+
+To be deployed on other EVM chains, such as:
+
+- Skale
+- Celo
+- Fantom
+- RSK
+
+To deploy to a new EVM chain, follow the [steps outlined here](docs/Deployment.md).
## Diagram
@@ -101,6 +390,12 @@ yarn test:ref
yarn coverage:ref
```
+To open the generated Hardhat coverage report locally after running `yarn coverage` or `yarn coverage:ref`:
+
+```bash
+open coverage/index.html
+```
+
To profile gas usage:
```bash
@@ -111,60 +406,36 @@ yarn profile
Seaport also includes a suite of fuzzing tests written in solidity with Foundry.
-To install Foundry (assuming a Linux or macOS system):
+To run tests with full traces and debugging with source, create an `.env` file with the following line:
```bash
-curl -L https://foundry.paradigm.xyz | bash
+FOUNDRY_PROFILE=debug
```
-This will download foundryup. To start Foundry, run:
-
-```bash
-foundryup
-```
-
-To install dependencies:
-
-```
-forge install
-```
+You may then run tests with `forge test`, optionally specifying a level of verbosity (anywhere from one to five `v`'s, eg, `-vvv`)
-To precompile contracts:
+This will compile tests and contracts without `via-ir` enabled, which is must faster, but will not exactly match the deployed bytecode.
-The optimized contracts are compiled using the IR pipeline, which can take a long time to compile. By default, the differential test suite deploys precompiled versions of both the optimized and reference contracts. Precompilation can be done by specifying specific Foundry profiles.
+To run tests against the actual bytecode intended to be deployed on networks, you will need to pre-compile the contracts, and remove the `FOUNDRY_PROFILE` variable from your `.env` file. **Note** that informative error traces may not be available, and the Forge debugger will not show the accompanying source code.
```bash
FOUNDRY_PROFILE=optimized forge build
FOUNDRY_PROFILE=reference forge build
```
-There are three Foundry profiles for running the test suites, which bypass the IR pipeline to speed up compilation. To run tests, run any of the following:
+To run Forge coverage tests and open the generated coverage report locally:
```bash
-FOUNDRY_PROFILE=test forge test # with 5000 fuzz runs
-FOUNDRY_PROFILE=lite forge test # with 1000 fuzz runs
-FOUNDRY_PROFILE=local-ffi forge test # compiles and deploys ReferenceConsideration normally, with 1000 fuzz runs
+brew install lcov
+SEAPORT_COVERAGE=true forge coverage --report summary --report lcov && genhtml lcov.info -o html --branch
+open html/index.htmlg
```
-You may wish to include a `.env` file that `export`s a specific profile when developing locally.
+**Note** that Forge does not yet ignore specific filepaths when running coverage tests.
-**Note** that stack+debug traces will not be available for precompiled contracts. To facilitate local development, specifying `FOUNDRY_PROFILE=local-ffi` will compile and deploy the reference implementation normally, allowing for stack+debug traces.
+For information on Foundry, including installation and testing, see the [Foundry Book](https://book.getfoundry.sh/).
-**Note** the `local-ffi` profile uses Forge's `ffi` flag. `ffi` can potentially be unsafe, as it allows Forge to execute arbitrary code. Use with caution, and always ensure you trust the code in this repository, especially when working on third-party forks.
-
-
-The following modifiers are also available:
-
-- Level 2 (-vv): Logs emitted during tests are also displayed.
-- Level 3 (-vvv): Stack traces for failing tests are also displayed.
-- Level 4 (-vvvv): Stack traces for all tests are displayed, and setup traces for failing tests are displayed.
-- Level 5 (-vvvvv): Stack traces and setup traces are always displayed.
-
-```bash
-FOUNDRY_PROFILE=test forge test -vv
-```
-
-For more information on foundry testing and use, see [Foundry Book installation instructions](https://book.getfoundry.sh/getting-started/installation.html).
+### Linting
To run lint checks:
@@ -192,16 +463,32 @@ When making a pull request, ensure that:
- All tests pass.
- Code coverage remains at 100% (coverage tests must currently be written in hardhat).
- All new code adheres to the style guide:
- - All lint checks pass.
- - Code is thoroughly commented with natspec where relevant.
+ - All lint checks pass.
+ - Code is thoroughly commented with natspec where relevant.
- If making a change to the contracts:
- - Gas snapshots are provided and demonstrate an improvement (or an acceptable deficit given other improvements).
- - Reference contracts are modified correspondingly if relevant.
- - New tests (ideally via foundry) are included for all new features or code paths.
+ - Gas snapshots are provided and demonstrate an improvement (or an acceptable deficit given other improvements).
+ - Reference contracts are modified correspondingly if relevant.
+ - New tests (ideally via foundry) are included for all new features or code paths.
- If making a modification to third-party dependencies, `yarn audit` passes.
- A descriptive summary of the PR has been provided.
## License
-[MIT](LICENSE) Copyright 2022 Ozone Networks, Inc.
-
+[MIT](LICENSE) Copyright 2023 Ozone Networks, Inc.
+
+[version-badge]: https://img.shields.io/github/package-json/v/ProjectOpenSea/seaport
+[version-link]: https://github.com/ProjectOpenSea/seaport/releases
+[ci-badge]: https://github.com/ProjectOpenSea/seaport/actions/workflows/test.yml/badge.svg
+[ci-link]: https://github.com/ProjectOpenSea/seaport/actions/workflows/test.yml
+[coverage-badge]: https://codecov.io/gh/ProjectOpenSea/seaport/branch/main/graph/badge.svg
+[coverage-link]: https://codecov.io/gh/ProjectOpenSea/seaport
+[license-badge]: https://img.shields.io/github/license/ProjectOpenSea/seaport
+[license-link]: https://github.com/ProjectOpenSea/seaport/blob/main/LICENSE
+[docs-badge]: https://img.shields.io/badge/Seaport-documentation-informational
+[docs-link]: https://github.com/ProjectOpenSea/seaport/tree/main/docs
+[discussions-badge]: https://img.shields.io/badge/Seaport-discussions-blueviolet
+[discussions-link]: https://github.com/ProjectOpenSea/seaport/discussions
+[js-library-badge]: https://img.shields.io/badge/Seaport.js-library-red
+[js-library-link]: https://github.com/ProjectOpenSea/seaport-js
+[discord-badge]: https://img.shields.io/static/v1?logo=discord&label=discord&message=Join&color=blue
+[discord-link]: https://discord.gg/ADXcTXpqry
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 000000000..4d89c9b08
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,40 @@
+coverage:
+ range: 100..100
+ round: down
+ precision: 2
+ status:
+ project:
+ default:
+ target: auto
+ threshold: 0%
+ base: auto
+ if_ci_failed: error
+
+flag_management:
+ default_rules: # the rules that will be followed for any flag added, generally
+ statuses:
+ - type: project
+ target: 100%
+ threshold: 0%
+ - type: patch
+ target: 100%
+ threshold: 0%
+ individual_flags: # exceptions to the default rules above, stated flag by flag
+ - name: foundry
+ statuses:
+ - type: project
+ target: 100%
+ threshold: 0%
+ - type: patch
+ target: 100%
+ threshold: 0%
+
+# Ignore folders that forge coverage is reporting on
+ignore:
+ - offerers
+ - script
+ - test
+ - contracts/test
+ - contracts/helpers
+ - contracts/zones
+ - contracts/lib/ConsiderationStructs.sol
diff --git a/config/.solcover-reference.js b/config/.solcover-reference.js
index 71ef3285d..a8e0bab93 100644
--- a/config/.solcover-reference.js
+++ b/config/.solcover-reference.js
@@ -5,12 +5,14 @@ module.exports = {
"conduit/lib/ConduitEnums.sol",
"conduit/lib/ConduitStructs.sol",
"Consideration.sol",
+ "helpers/PointerLibraries.sol",
"interfaces/AbridgedProxyInterfaces.sol",
"interfaces/AbridgedTokenInterfaces.sol",
"interfaces/ConduitControllerInterface.sol",
"interfaces/ConduitInterface.sol",
"interfaces/ConsiderationEventsAndErrors.sol",
"interfaces/ConsiderationInterface.sol",
+ "interfaces/ContractOffererInterface.sol",
"interfaces/EIP1271Interface.sol",
"interfaces/ZoneInterface.sol",
"lib/ConsiderationBase.sol",
@@ -25,8 +27,28 @@ module.exports = {
"test/ExcessReturnDataRecipient.sol",
"test/Reenterer.sol",
"test/TestERC1155.sol",
+ "test/TestERC1155Revert.sol",
"test/TestERC20.sol",
+ "test/TestERC20NotOk.sol",
"test/TestERC721.sol",
+ "test/TestERC721Revert.sol",
+ "test/TestContractOfferer.sol",
+ "test/TestContractOffererNativeToken.sol",
+ "test/TestInvalidContractOfferer.sol",
+ "test/TestInvalidContractOffererRatifyOrder.sol",
+ "test/TestBadContractOfferer.sol",
"test/TestZone.sol",
+ "test/TestPostExecution.sol",
+ "test/TestERC20Panic.sol",
+ "test/TestERC20Revert.sol",
+ "test/InvalidERC721Recipient.sol",
+ "test/ERC721ReceiverMock.sol",
+ "test/ConduitControllerMock.sol",
+ "test/ConduitMock.sol",
+ "test/ConduitMockErrors.sol",
+ "test/ConduitMockInvalidMagic.sol",
+ "test/ConduitMockRevertBytes.sol",
+ "test/ConduitMockRevertNoReason.sol",
+ "../reference/shim/Shim.sol",
],
};
diff --git a/config/.solcover.js b/config/.solcover.js
index 9cba82828..2a8e2f2fd 100644
--- a/config/.solcover.js
+++ b/config/.solcover.js
@@ -2,34 +2,52 @@ module.exports = {
skipFiles: [
"conduit/lib/ConduitEnums.sol",
"conduit/lib/ConduitStructs.sol",
+ "helpers/PointerLibraries.sol",
"interfaces/AbridgedProxyInterfaces.sol",
"interfaces/AbridgedTokenInterfaces.sol",
"interfaces/ConduitControllerInterface.sol",
"interfaces/ConduitInterface.sol",
"interfaces/ConsiderationEventsAndErrors.sol",
"interfaces/ConsiderationInterface.sol",
+ "interfaces/ContractOffererInterface.sol",
"interfaces/EIP1271Interface.sol",
"interfaces/SeaportInterface.sol",
"interfaces/ZoneInterface.sol",
"lib/ConsiderationConstants.sol",
"lib/ConsiderationEnums.sol",
"lib/ConsiderationStructs.sol",
- "reference/ReferenceConsideration.sol",
- "reference/conduit/ReferenceConduit.sol",
- "reference/conduit/ReferenceConduitController.sol",
- "reference/lib/ReferenceConsiderationBase.sol",
- "reference/lib/ReferenceConsiderationInternal.sol",
- "reference/lib/ReferenceConsiderationInternalView.sol",
- "reference/lib/ReferenceConsiderationPure.sol",
- "reference/lib/ReferenceTokenTransferrer.sol",
"test/EIP1271Wallet.sol",
"test/ExcessReturnDataRecipient.sol",
"test/ERC1155BatchRecipient.sol",
"test/Reenterer.sol",
"test/TestERC1155.sol",
+ "test/TestERC1155Revert.sol",
"test/TestERC20.sol",
+ "test/TestERC20NotOk.sol",
"test/TestERC721.sol",
+ "test/TestERC721Revert.sol",
+ "test/TestContractOfferer.sol",
+ "test/TestContractOffererNativeToken.sol",
+ "test/TestInvalidContractOfferer.sol",
+ "test/TestInvalidContractOffererRatifyOrder.sol",
+ "test/TestBadContractOfferer.sol",
+ "test/TestPostExecution.sol",
"test/TestZone.sol",
+ "test/TestERC20Panic.sol",
+ "test/TestERC20Revert.sol",
+ "test/InvalidERC721Recipient.sol",
+ "test/ERC721ReceiverMock.sol",
+ "test/ConduitControllerMock.sol",
+ "test/ConduitMock.sol",
+ "test/ConduitMockErrors.sol",
+ "test/ConduitMockInvalidMagic.sol",
+ "test/ConduitMockRevertBytes.sol",
+ "test/ConduitMockRevertNoReason.sol",
+ "zones/PausableZone.sol",
+ "zones/PausableZoneController.sol",
+ "zones/interfaces/PausableZoneControllerInterface.sol",
+ "zones/interfaces/PausableZoneEventsAndErrors.sol",
+ "zones/interfaces/PausableZoneInterface.sol",
],
configureYulOptimizer: true,
solcOptimizerDetails: {
diff --git a/contracts/Seaport.sol b/contracts/Seaport.sol
index ad036f657..c682a49c1 100644
--- a/contracts/Seaport.sol
+++ b/contracts/Seaport.sol
@@ -1,21 +1,22 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
import { Consideration } from "./lib/Consideration.sol";
/**
* @title Seaport
- * @custom:version 1.1
+ * @custom:version 1.2
* @author 0age (0age.eth)
* @custom:coauthor d1ll0n (d1ll0n.eth)
* @custom:coauthor transmissions11 (t11s.eth)
+ * @custom:coauthor James Wenzel (emo.eth)
* @custom:contributor Kartik (slokh.eth)
* @custom:contributor LeFevre (lefevre.eth)
* @custom:contributor Joseph Schiarizzi (CupOJoseph.eth)
* @custom:contributor Aspyn Palatnick (stuckinaboot.eth)
- * @custom:contributor James Wenzel (emo.eth)
* @custom:contributor Stephan Min (stephanm.eth)
* @custom:contributor Ryan Ghods (ralxz.eth)
+ * @custom:contributor Daniel Viau (snotrocket.eth)
* @custom:contributor hack3r-0m (hack3r-0m.eth)
* @custom:contributor Diego Estevez (antidiego.eth)
* @custom:contributor Chomtana (chomtana.eth)
@@ -56,6 +57,7 @@ import { Consideration } from "./lib/Consideration.sol";
* @custom:contributor blockdev (blockd3v.eth)
* @custom:contributor fiveoutofnine (fiveoutofnine.eth)
* @custom:contributor shuklaayush (shuklaayush.eth)
+ * @custom:contributor dravee (dravee.eth)
* @custom:contributor 0xPatissier
* @custom:contributor pcaversaccio
* @custom:contributor David Eiber
@@ -73,13 +75,12 @@ import { Consideration } from "./lib/Consideration.sol";
* @custom:contributor rfart(rfa)
* @custom:contributor Riley Holterhus
* @custom:contributor big-tech-sux
- * @notice Seaport is a generalized ETH/ERC20/ERC721/ERC1155 marketplace. It
- * minimizes external calls to the greatest extent possible and provides
- * lightweight methods for common routes as well as more flexible
- * methods for composing advanced orders or groups of orders. Each order
- * contains an arbitrary number of items that may be spent (the "offer")
- * along with an arbitrary number of items that must be received back by
- * the indicated recipients (the "consideration").
+ * @notice Seaport is a generalized native token/ERC20/ERC721/ERC1155
+ * marketplace with lightweight methods for common routes as well as
+ * more flexible methods for composing advanced orders or groups of
+ * orders. Each order contains an arbitrary number of items that may be
+ * spent (the "offer") along with an arbitrary number of items that must
+ * be received back by the indicated recipients (the "consideration").
*/
contract Seaport is Consideration {
/**
diff --git a/contracts/conduit/Conduit.sol b/contracts/conduit/Conduit.sol
index 411f77bc4..aa2ecae99 100644
--- a/contracts/conduit/Conduit.sol
+++ b/contracts/conduit/Conduit.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
@@ -7,13 +7,20 @@ import { ConduitItemType } from "./lib/ConduitEnums.sol";
import { TokenTransferrer } from "../lib/TokenTransferrer.sol";
-// prettier-ignore
import {
- ConduitTransfer,
- ConduitBatch1155Transfer
+ ConduitBatch1155Transfer,
+ ConduitTransfer
} from "./lib/ConduitStructs.sol";
-import "./lib/ConduitConstants.sol";
+import {
+ ChannelClosed_channel_ptr,
+ ChannelClosed_error_length,
+ ChannelClosed_error_ptr,
+ ChannelClosed_error_signature,
+ ChannelKey_channel_ptr,
+ ChannelKey_length,
+ ChannelKey_slot_ptr
+} from "./lib/ConduitConstants.sol";
/**
* @title Conduit
@@ -59,7 +66,11 @@ contract Conduit is ConduitInterface, TokenTransferrer {
// Next, set the caller as the argument.
mstore(ChannelClosed_channel_ptr, caller())
- // Finally, revert, returning full custom error with argument.
+ // Finally, revert, returning full custom error with argument
+ // data in memory.
+ // revert(abi.encodeWithSignature(
+ // "ChannelClosed(address)", caller()
+ // ))
revert(ChannelClosed_error_ptr, ChannelClosed_error_length)
}
}
@@ -90,12 +101,9 @@ contract Conduit is ConduitInterface, TokenTransferrer {
* @return magicValue A magic value indicating that the transfers were
* performed successfully.
*/
- function execute(ConduitTransfer[] calldata transfers)
- external
- override
- onlyOpenChannel
- returns (bytes4 magicValue)
- {
+ function execute(
+ ConduitTransfer[] calldata transfers
+ ) external override onlyOpenChannel returns (bytes4 magicValue) {
// Retrieve the total number of transfers and place on the stack.
uint256 totalStandardTransfers = transfers.length;
@@ -224,7 +232,7 @@ contract Conduit is ConduitInterface, TokenTransferrer {
} else if (item.itemType == ConduitItemType.ERC721) {
// Ensure that exactly one 721 item is being transferred.
if (item.amount != 1) {
- revert InvalidERC721TransferAmount();
+ revert InvalidERC721TransferAmount(item.amount);
}
// Transfer ERC721 token.
diff --git a/contracts/conduit/ConduitController.sol b/contracts/conduit/ConduitController.sol
index d3e0b711a..1b15fe5c0 100644
--- a/contracts/conduit/ConduitController.sol
+++ b/contracts/conduit/ConduitController.sol
@@ -1,9 +1,8 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-// prettier-ignore
import {
- ConduitControllerInterface
+ ConduitControllerInterface
} from "../interfaces/ConduitControllerInterface.sol";
import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
@@ -54,11 +53,10 @@ contract ConduitController is ConduitControllerInterface {
*
* @return conduit The address of the newly deployed conduit.
*/
- function createConduit(bytes32 conduitKey, address initialOwner)
- external
- override
- returns (address conduit)
- {
+ function createConduit(
+ bytes32 conduitKey,
+ address initialOwner
+ ) external override returns (address conduit) {
// Ensure that an initial owner has been supplied.
if (initialOwner == address(0)) {
revert InvalidInitialOwner();
@@ -201,10 +199,10 @@ contract ConduitController is ConduitControllerInterface {
* @param conduit The conduit for which to initiate ownership transfer.
* @param newPotentialOwner The new potential owner of the conduit.
*/
- function transferOwnership(address conduit, address newPotentialOwner)
- external
- override
- {
+ function transferOwnership(
+ address conduit,
+ address newPotentialOwner
+ ) external override {
// Ensure the caller is the current owner of the conduit in question.
_assertCallerIsConduitOwner(conduit);
@@ -288,12 +286,9 @@ contract ConduitController is ConduitControllerInterface {
*
* @return owner The owner of the supplied conduit.
*/
- function ownerOf(address conduit)
- external
- view
- override
- returns (address owner)
- {
+ function ownerOf(
+ address conduit
+ ) external view override returns (address owner) {
// Ensure that the conduit in question exists.
_assertConduitExists(conduit);
@@ -310,12 +305,9 @@ contract ConduitController is ConduitControllerInterface {
*
* @return conduitKey The conduit key used to deploy the supplied conduit.
*/
- function getKey(address conduit)
- external
- view
- override
- returns (bytes32 conduitKey)
- {
+ function getKey(
+ address conduit
+ ) external view override returns (bytes32 conduitKey) {
// Attempt to retrieve a conduit key for the conduit in question.
conduitKey = _conduits[conduit].key;
@@ -336,12 +328,9 @@ contract ConduitController is ConduitControllerInterface {
* @return exists A boolean indicating whether the derived conduit has been
* deployed or not.
*/
- function getConduit(bytes32 conduitKey)
- external
- view
- override
- returns (address conduit, bool exists)
- {
+ function getConduit(
+ bytes32 conduitKey
+ ) external view override returns (address conduit, bool exists) {
// Derive address from deployer, conduit key and creation code hash.
conduit = address(
uint160(
@@ -372,12 +361,9 @@ contract ConduitController is ConduitControllerInterface {
*
* @return potentialOwner The potential owner, if any, for the conduit.
*/
- function getPotentialOwner(address conduit)
- external
- view
- override
- returns (address potentialOwner)
- {
+ function getPotentialOwner(
+ address conduit
+ ) external view override returns (address potentialOwner) {
// Ensure that the conduit in question exists.
_assertConduitExists(conduit);
@@ -394,12 +380,10 @@ contract ConduitController is ConduitControllerInterface {
*
* @return isOpen The status of the channel on the given conduit.
*/
- function getChannelStatus(address conduit, address channel)
- external
- view
- override
- returns (bool isOpen)
- {
+ function getChannelStatus(
+ address conduit,
+ address channel
+ ) external view override returns (bool isOpen) {
// Ensure that the conduit in question exists.
_assertConduitExists(conduit);
@@ -414,12 +398,9 @@ contract ConduitController is ConduitControllerInterface {
*
* @return totalChannels The total number of open channels for the conduit.
*/
- function getTotalChannels(address conduit)
- external
- view
- override
- returns (uint256 totalChannels)
- {
+ function getTotalChannels(
+ address conduit
+ ) external view override returns (uint256 totalChannels) {
// Ensure that the conduit in question exists.
_assertConduitExists(conduit);
@@ -437,12 +418,10 @@ contract ConduitController is ConduitControllerInterface {
*
* @return channel The open channel, if any, at the specified channel index.
*/
- function getChannel(address conduit, uint256 channelIndex)
- external
- view
- override
- returns (address channel)
- {
+ function getChannel(
+ address conduit,
+ uint256 channelIndex
+ ) external view override returns (address channel) {
// Ensure that the conduit in question exists.
_assertConduitExists(conduit);
@@ -467,12 +446,9 @@ contract ConduitController is ConduitControllerInterface {
*
* @return channels An array of open channels on the given conduit.
*/
- function getChannels(address conduit)
- external
- view
- override
- returns (address[] memory channels)
- {
+ function getChannels(
+ address conduit
+ ) external view override returns (address[] memory channels) {
// Ensure that the conduit in question exists.
_assertConduitExists(conduit);
diff --git a/contracts/conduit/lib/ConduitConstants.sol b/contracts/conduit/lib/ConduitConstants.sol
index 2979289c4..42197c8e2 100644
--- a/contracts/conduit/lib/ConduitConstants.sol
+++ b/contracts/conduit/lib/ConduitConstants.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
// error ChannelClosed(address channel)
uint256 constant ChannelClosed_error_signature = (
diff --git a/contracts/conduit/lib/ConduitEnums.sol b/contracts/conduit/lib/ConduitEnums.sol
index 3d0243cd4..05a6587f5 100644
--- a/contracts/conduit/lib/ConduitEnums.sol
+++ b/contracts/conduit/lib/ConduitEnums.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
enum ConduitItemType {
NATIVE, // unused
diff --git a/contracts/conduit/lib/ConduitStructs.sol b/contracts/conduit/lib/ConduitStructs.sol
index d1b19f0c3..b7fc8322d 100644
--- a/contracts/conduit/lib/ConduitStructs.sol
+++ b/contracts/conduit/lib/ConduitStructs.sol
@@ -1,8 +1,12 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
import { ConduitItemType } from "./ConduitEnums.sol";
+/**
+ * @dev A ConduitTransfer is a struct that contains the information needed for a
+ * conduit to transfer an item from one address to another.
+ */
struct ConduitTransfer {
ConduitItemType itemType;
address token;
@@ -12,6 +16,11 @@ struct ConduitTransfer {
uint256 amount;
}
+/**
+ * @dev A ConduitBatch1155Transfer is a struct that contains the information
+ * needed for a conduit to transfer a batch of ERC-1155 tokens from one
+ * address to another.
+ */
struct ConduitBatch1155Transfer {
address token;
address from;
diff --git a/contracts/helpers/PointerLibraries.sol b/contracts/helpers/PointerLibraries.sol
new file mode 100644
index 000000000..7f927b564
--- /dev/null
+++ b/contracts/helpers/PointerLibraries.sol
@@ -0,0 +1,3095 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+type CalldataPointer is uint256;
+
+type ReturndataPointer is uint256;
+
+type MemoryPointer is uint256;
+
+using CalldataPointerLib for CalldataPointer global;
+using MemoryPointerLib for MemoryPointer global;
+using ReturndataPointerLib for ReturndataPointer global;
+
+using CalldataReaders for CalldataPointer global;
+using ReturndataReaders for ReturndataPointer global;
+using MemoryReaders for MemoryPointer global;
+using MemoryWriters for MemoryPointer global;
+
+CalldataPointer constant CalldataStart = CalldataPointer.wrap(0x04);
+MemoryPointer constant FreeMemoryPPtr = MemoryPointer.wrap(0x40);
+uint256 constant IdentityPrecompileAddress = 0x4;
+uint256 constant OffsetOrLengthMask = 0xffffffff;
+uint256 constant _OneWord = 0x20;
+uint256 constant _FreeMemoryPointerSlot = 0x40;
+
+/// @dev Allocates `size` bytes in memory by increasing the free memory pointer
+/// and returns the memory pointer to the first byte of the allocated region.
+// (Free functions cannot have visibility.)
+// solhint-disable-next-line func-visibility
+function malloc(uint256 size) pure returns (MemoryPointer mPtr) {
+ assembly {
+ mPtr := mload(_FreeMemoryPointerSlot)
+ mstore(_FreeMemoryPointerSlot, add(mPtr, size))
+ }
+}
+
+// (Free functions cannot have visibility.)
+// solhint-disable-next-line func-visibility
+function getFreeMemoryPointer() pure returns (MemoryPointer mPtr) {
+ mPtr = FreeMemoryPPtr.readMemoryPointer();
+}
+
+// (Free functions cannot have visibility.)
+// solhint-disable-next-line func-visibility
+function setFreeMemoryPointer(MemoryPointer mPtr) pure {
+ FreeMemoryPPtr.write(mPtr);
+}
+
+library CalldataPointerLib {
+ function lt(
+ CalldataPointer a,
+ CalldataPointer b
+ ) internal pure returns (bool c) {
+ assembly {
+ c := lt(a, b)
+ }
+ }
+
+ function gt(
+ CalldataPointer a,
+ CalldataPointer b
+ ) internal pure returns (bool c) {
+ assembly {
+ c := gt(a, b)
+ }
+ }
+
+ function eq(
+ CalldataPointer a,
+ CalldataPointer b
+ ) internal pure returns (bool c) {
+ assembly {
+ c := eq(a, b)
+ }
+ }
+
+ /// @dev Resolves an offset stored at `cdPtr + headOffset` to a calldata.
+ /// pointer `cdPtr` must point to some parent object with a dynamic
+ /// type's head stored at `cdPtr + headOffset`.
+ function pptr(
+ CalldataPointer cdPtr,
+ uint256 headOffset
+ ) internal pure returns (CalldataPointer cdPtrChild) {
+ cdPtrChild = cdPtr.offset(
+ cdPtr.offset(headOffset).readUint256() & OffsetOrLengthMask
+ );
+ }
+
+ /// @dev Resolves an offset stored at `cdPtr` to a calldata pointer.
+ /// `cdPtr` must point to some parent object with a dynamic type as its
+ /// first member, e.g. `struct { bytes data; }`
+ function pptr(
+ CalldataPointer cdPtr
+ ) internal pure returns (CalldataPointer cdPtrChild) {
+ cdPtrChild = cdPtr.offset(cdPtr.readUint256() & OffsetOrLengthMask);
+ }
+
+ /// @dev Returns the calldata pointer one word after `cdPtr`.
+ function next(
+ CalldataPointer cdPtr
+ ) internal pure returns (CalldataPointer cdPtrNext) {
+ assembly {
+ cdPtrNext := add(cdPtr, _OneWord)
+ }
+ }
+
+ /// @dev Returns the calldata pointer `_offset` bytes after `cdPtr`.
+ function offset(
+ CalldataPointer cdPtr,
+ uint256 _offset
+ ) internal pure returns (CalldataPointer cdPtrNext) {
+ assembly {
+ cdPtrNext := add(cdPtr, _offset)
+ }
+ }
+
+ /// @dev Copies `size` bytes from calldata starting at `src` to memory at
+ /// `dst`.
+ function copy(
+ CalldataPointer src,
+ MemoryPointer dst,
+ uint256 size
+ ) internal pure {
+ assembly {
+ calldatacopy(dst, src, size)
+ }
+ }
+}
+
+library ReturndataPointerLib {
+ function lt(
+ ReturndataPointer a,
+ ReturndataPointer b
+ ) internal pure returns (bool c) {
+ assembly {
+ c := lt(a, b)
+ }
+ }
+
+ function gt(
+ ReturndataPointer a,
+ ReturndataPointer b
+ ) internal pure returns (bool c) {
+ assembly {
+ c := gt(a, b)
+ }
+ }
+
+ function eq(
+ ReturndataPointer a,
+ ReturndataPointer b
+ ) internal pure returns (bool c) {
+ assembly {
+ c := eq(a, b)
+ }
+ }
+
+ /// @dev Resolves an offset stored at `rdPtr + headOffset` to a returndata
+ /// pointer. `rdPtr` must point to some parent object with a dynamic
+ /// type's head stored at `rdPtr + headOffset`.
+ function pptr(
+ ReturndataPointer rdPtr,
+ uint256 headOffset
+ ) internal pure returns (ReturndataPointer rdPtrChild) {
+ rdPtrChild = rdPtr.offset(
+ rdPtr.offset(headOffset).readUint256() & OffsetOrLengthMask
+ );
+ }
+
+ /// @dev Resolves an offset stored at `rdPtr` to a returndata pointer.
+ /// `rdPtr` must point to some parent object with a dynamic type as its
+ /// first member, e.g. `struct { bytes data; }`
+ function pptr(
+ ReturndataPointer rdPtr
+ ) internal pure returns (ReturndataPointer rdPtrChild) {
+ rdPtrChild = rdPtr.offset(rdPtr.readUint256() & OffsetOrLengthMask);
+ }
+
+ /// @dev Returns the returndata pointer one word after `cdPtr`.
+ function next(
+ ReturndataPointer rdPtr
+ ) internal pure returns (ReturndataPointer rdPtrNext) {
+ assembly {
+ rdPtrNext := add(rdPtr, _OneWord)
+ }
+ }
+
+ /// @dev Returns the returndata pointer `_offset` bytes after `cdPtr`.
+ function offset(
+ ReturndataPointer rdPtr,
+ uint256 _offset
+ ) internal pure returns (ReturndataPointer rdPtrNext) {
+ assembly {
+ rdPtrNext := add(rdPtr, _offset)
+ }
+ }
+
+ /// @dev Copies `size` bytes from returndata starting at `src` to memory at
+ /// `dst`.
+ function copy(
+ ReturndataPointer src,
+ MemoryPointer dst,
+ uint256 size
+ ) internal pure {
+ assembly {
+ returndatacopy(dst, src, size)
+ }
+ }
+}
+
+library MemoryPointerLib {
+ function copy(
+ MemoryPointer src,
+ MemoryPointer dst,
+ uint256 size
+ ) internal view {
+ assembly {
+ let success := staticcall(
+ gas(),
+ IdentityPrecompileAddress,
+ src,
+ size,
+ dst,
+ size
+ )
+ if or(iszero(returndatasize()), iszero(success)) {
+ revert(0, 0)
+ }
+ }
+ }
+
+ function lt(
+ MemoryPointer a,
+ MemoryPointer b
+ ) internal pure returns (bool c) {
+ assembly {
+ c := lt(a, b)
+ }
+ }
+
+ function gt(
+ MemoryPointer a,
+ MemoryPointer b
+ ) internal pure returns (bool c) {
+ assembly {
+ c := gt(a, b)
+ }
+ }
+
+ function eq(
+ MemoryPointer a,
+ MemoryPointer b
+ ) internal pure returns (bool c) {
+ assembly {
+ c := eq(a, b)
+ }
+ }
+
+ /// @dev Returns the memory pointer one word after `mPtr`.
+ function next(
+ MemoryPointer mPtr
+ ) internal pure returns (MemoryPointer mPtrNext) {
+ assembly {
+ mPtrNext := add(mPtr, _OneWord)
+ }
+ }
+
+ /// @dev Returns the memory pointer `_offset` bytes after `mPtr`.
+ function offset(
+ MemoryPointer mPtr,
+ uint256 _offset
+ ) internal pure returns (MemoryPointer mPtrNext) {
+ assembly {
+ mPtrNext := add(mPtr, _offset)
+ }
+ }
+
+ /// @dev Resolves a pointer pointer at `mPtr + headOffset` to a memory
+ /// pointer. `mPtr` must point to some parent object with a dynamic
+ /// type's pointer stored at `mPtr + headOffset`.
+ function pptr(
+ MemoryPointer mPtr,
+ uint256 headOffset
+ ) internal pure returns (MemoryPointer mPtrChild) {
+ mPtrChild = mPtr.offset(headOffset).readMemoryPointer();
+ }
+
+ /// @dev Resolves a pointer pointer stored at `mPtr` to a memory pointer.
+ /// `mPtr` must point to some parent object with a dynamic type as its
+ /// first member, e.g. `struct { bytes data; }`
+ function pptr(
+ MemoryPointer mPtr
+ ) internal pure returns (MemoryPointer mPtrChild) {
+ mPtrChild = mPtr.readMemoryPointer();
+ }
+}
+
+library CalldataReaders {
+ /// @dev Reads the value at `cdPtr` and applies a mask to return only the
+ /// last 4 bytes.
+ function readMaskedUint256(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint256 value) {
+ value = cdPtr.readUint256() & OffsetOrLengthMask;
+ }
+
+ /// @dev Reads the bool at `cdPtr` in calldata.
+ function readBool(
+ CalldataPointer cdPtr
+ ) internal pure returns (bool value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the address at `cdPtr` in calldata.
+ function readAddress(
+ CalldataPointer cdPtr
+ ) internal pure returns (address value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes1 at `cdPtr` in calldata.
+ function readBytes1(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes1 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes2 at `cdPtr` in calldata.
+ function readBytes2(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes2 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes3 at `cdPtr` in calldata.
+ function readBytes3(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes3 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes4 at `cdPtr` in calldata.
+ function readBytes4(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes4 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes5 at `cdPtr` in calldata.
+ function readBytes5(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes5 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes6 at `cdPtr` in calldata.
+ function readBytes6(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes6 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes7 at `cdPtr` in calldata.
+ function readBytes7(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes7 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes8 at `cdPtr` in calldata.
+ function readBytes8(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes8 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes9 at `cdPtr` in calldata.
+ function readBytes9(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes9 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes10 at `cdPtr` in calldata.
+ function readBytes10(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes10 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes11 at `cdPtr` in calldata.
+ function readBytes11(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes11 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes12 at `cdPtr` in calldata.
+ function readBytes12(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes12 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes13 at `cdPtr` in calldata.
+ function readBytes13(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes13 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes14 at `cdPtr` in calldata.
+ function readBytes14(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes14 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes15 at `cdPtr` in calldata.
+ function readBytes15(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes15 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes16 at `cdPtr` in calldata.
+ function readBytes16(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes16 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes17 at `cdPtr` in calldata.
+ function readBytes17(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes17 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes18 at `cdPtr` in calldata.
+ function readBytes18(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes18 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes19 at `cdPtr` in calldata.
+ function readBytes19(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes19 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes20 at `cdPtr` in calldata.
+ function readBytes20(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes20 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes21 at `cdPtr` in calldata.
+ function readBytes21(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes21 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes22 at `cdPtr` in calldata.
+ function readBytes22(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes22 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes23 at `cdPtr` in calldata.
+ function readBytes23(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes23 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes24 at `cdPtr` in calldata.
+ function readBytes24(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes24 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes25 at `cdPtr` in calldata.
+ function readBytes25(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes25 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes26 at `cdPtr` in calldata.
+ function readBytes26(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes26 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes27 at `cdPtr` in calldata.
+ function readBytes27(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes27 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes28 at `cdPtr` in calldata.
+ function readBytes28(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes28 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes29 at `cdPtr` in calldata.
+ function readBytes29(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes29 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes30 at `cdPtr` in calldata.
+ function readBytes30(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes30 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes31 at `cdPtr` in calldata.
+ function readBytes31(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes31 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the bytes32 at `cdPtr` in calldata.
+ function readBytes32(
+ CalldataPointer cdPtr
+ ) internal pure returns (bytes32 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint8 at `cdPtr` in calldata.
+ function readUint8(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint8 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint16 at `cdPtr` in calldata.
+ function readUint16(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint16 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint24 at `cdPtr` in calldata.
+ function readUint24(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint24 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint32 at `cdPtr` in calldata.
+ function readUint32(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint32 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint40 at `cdPtr` in calldata.
+ function readUint40(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint40 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint48 at `cdPtr` in calldata.
+ function readUint48(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint48 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint56 at `cdPtr` in calldata.
+ function readUint56(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint56 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint64 at `cdPtr` in calldata.
+ function readUint64(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint64 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint72 at `cdPtr` in calldata.
+ function readUint72(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint72 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint80 at `cdPtr` in calldata.
+ function readUint80(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint80 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint88 at `cdPtr` in calldata.
+ function readUint88(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint88 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint96 at `cdPtr` in calldata.
+ function readUint96(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint96 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint104 at `cdPtr` in calldata.
+ function readUint104(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint104 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint112 at `cdPtr` in calldata.
+ function readUint112(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint112 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint120 at `cdPtr` in calldata.
+ function readUint120(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint120 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint128 at `cdPtr` in calldata.
+ function readUint128(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint128 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint136 at `cdPtr` in calldata.
+ function readUint136(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint136 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint144 at `cdPtr` in calldata.
+ function readUint144(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint144 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint152 at `cdPtr` in calldata.
+ function readUint152(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint152 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint160 at `cdPtr` in calldata.
+ function readUint160(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint160 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint168 at `cdPtr` in calldata.
+ function readUint168(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint168 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint176 at `cdPtr` in calldata.
+ function readUint176(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint176 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint184 at `cdPtr` in calldata.
+ function readUint184(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint184 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint192 at `cdPtr` in calldata.
+ function readUint192(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint192 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint200 at `cdPtr` in calldata.
+ function readUint200(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint200 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint208 at `cdPtr` in calldata.
+ function readUint208(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint208 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint216 at `cdPtr` in calldata.
+ function readUint216(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint216 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint224 at `cdPtr` in calldata.
+ function readUint224(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint224 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint232 at `cdPtr` in calldata.
+ function readUint232(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint232 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint240 at `cdPtr` in calldata.
+ function readUint240(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint240 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint248 at `cdPtr` in calldata.
+ function readUint248(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint248 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the uint256 at `cdPtr` in calldata.
+ function readUint256(
+ CalldataPointer cdPtr
+ ) internal pure returns (uint256 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int8 at `cdPtr` in calldata.
+ function readInt8(
+ CalldataPointer cdPtr
+ ) internal pure returns (int8 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int16 at `cdPtr` in calldata.
+ function readInt16(
+ CalldataPointer cdPtr
+ ) internal pure returns (int16 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int24 at `cdPtr` in calldata.
+ function readInt24(
+ CalldataPointer cdPtr
+ ) internal pure returns (int24 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int32 at `cdPtr` in calldata.
+ function readInt32(
+ CalldataPointer cdPtr
+ ) internal pure returns (int32 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int40 at `cdPtr` in calldata.
+ function readInt40(
+ CalldataPointer cdPtr
+ ) internal pure returns (int40 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int48 at `cdPtr` in calldata.
+ function readInt48(
+ CalldataPointer cdPtr
+ ) internal pure returns (int48 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int56 at `cdPtr` in calldata.
+ function readInt56(
+ CalldataPointer cdPtr
+ ) internal pure returns (int56 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int64 at `cdPtr` in calldata.
+ function readInt64(
+ CalldataPointer cdPtr
+ ) internal pure returns (int64 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int72 at `cdPtr` in calldata.
+ function readInt72(
+ CalldataPointer cdPtr
+ ) internal pure returns (int72 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int80 at `cdPtr` in calldata.
+ function readInt80(
+ CalldataPointer cdPtr
+ ) internal pure returns (int80 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int88 at `cdPtr` in calldata.
+ function readInt88(
+ CalldataPointer cdPtr
+ ) internal pure returns (int88 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int96 at `cdPtr` in calldata.
+ function readInt96(
+ CalldataPointer cdPtr
+ ) internal pure returns (int96 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int104 at `cdPtr` in calldata.
+ function readInt104(
+ CalldataPointer cdPtr
+ ) internal pure returns (int104 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int112 at `cdPtr` in calldata.
+ function readInt112(
+ CalldataPointer cdPtr
+ ) internal pure returns (int112 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int120 at `cdPtr` in calldata.
+ function readInt120(
+ CalldataPointer cdPtr
+ ) internal pure returns (int120 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int128 at `cdPtr` in calldata.
+ function readInt128(
+ CalldataPointer cdPtr
+ ) internal pure returns (int128 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int136 at `cdPtr` in calldata.
+ function readInt136(
+ CalldataPointer cdPtr
+ ) internal pure returns (int136 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int144 at `cdPtr` in calldata.
+ function readInt144(
+ CalldataPointer cdPtr
+ ) internal pure returns (int144 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int152 at `cdPtr` in calldata.
+ function readInt152(
+ CalldataPointer cdPtr
+ ) internal pure returns (int152 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int160 at `cdPtr` in calldata.
+ function readInt160(
+ CalldataPointer cdPtr
+ ) internal pure returns (int160 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int168 at `cdPtr` in calldata.
+ function readInt168(
+ CalldataPointer cdPtr
+ ) internal pure returns (int168 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int176 at `cdPtr` in calldata.
+ function readInt176(
+ CalldataPointer cdPtr
+ ) internal pure returns (int176 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int184 at `cdPtr` in calldata.
+ function readInt184(
+ CalldataPointer cdPtr
+ ) internal pure returns (int184 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int192 at `cdPtr` in calldata.
+ function readInt192(
+ CalldataPointer cdPtr
+ ) internal pure returns (int192 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int200 at `cdPtr` in calldata.
+ function readInt200(
+ CalldataPointer cdPtr
+ ) internal pure returns (int200 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int208 at `cdPtr` in calldata.
+ function readInt208(
+ CalldataPointer cdPtr
+ ) internal pure returns (int208 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int216 at `cdPtr` in calldata.
+ function readInt216(
+ CalldataPointer cdPtr
+ ) internal pure returns (int216 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int224 at `cdPtr` in calldata.
+ function readInt224(
+ CalldataPointer cdPtr
+ ) internal pure returns (int224 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int232 at `cdPtr` in calldata.
+ function readInt232(
+ CalldataPointer cdPtr
+ ) internal pure returns (int232 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int240 at `cdPtr` in calldata.
+ function readInt240(
+ CalldataPointer cdPtr
+ ) internal pure returns (int240 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int248 at `cdPtr` in calldata.
+ function readInt248(
+ CalldataPointer cdPtr
+ ) internal pure returns (int248 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+
+ /// @dev Reads the int256 at `cdPtr` in calldata.
+ function readInt256(
+ CalldataPointer cdPtr
+ ) internal pure returns (int256 value) {
+ assembly {
+ value := calldataload(cdPtr)
+ }
+ }
+}
+
+library ReturndataReaders {
+ /// @dev Reads value at `rdPtr` & applies a mask to return only last 4 bytes
+ function readMaskedUint256(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint256 value) {
+ value = rdPtr.readUint256() & OffsetOrLengthMask;
+ }
+
+ /// @dev Reads the bool at `rdPtr` in returndata.
+ function readBool(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bool value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the address at `rdPtr` in returndata.
+ function readAddress(
+ ReturndataPointer rdPtr
+ ) internal pure returns (address value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes1 at `rdPtr` in returndata.
+ function readBytes1(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes1 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes2 at `rdPtr` in returndata.
+ function readBytes2(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes2 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes3 at `rdPtr` in returndata.
+ function readBytes3(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes3 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes4 at `rdPtr` in returndata.
+ function readBytes4(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes4 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes5 at `rdPtr` in returndata.
+ function readBytes5(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes5 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes6 at `rdPtr` in returndata.
+ function readBytes6(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes6 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes7 at `rdPtr` in returndata.
+ function readBytes7(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes7 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes8 at `rdPtr` in returndata.
+ function readBytes8(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes8 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes9 at `rdPtr` in returndata.
+ function readBytes9(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes9 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes10 at `rdPtr` in returndata.
+ function readBytes10(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes10 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes11 at `rdPtr` in returndata.
+ function readBytes11(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes11 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes12 at `rdPtr` in returndata.
+ function readBytes12(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes12 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes13 at `rdPtr` in returndata.
+ function readBytes13(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes13 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes14 at `rdPtr` in returndata.
+ function readBytes14(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes14 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes15 at `rdPtr` in returndata.
+ function readBytes15(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes15 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes16 at `rdPtr` in returndata.
+ function readBytes16(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes16 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes17 at `rdPtr` in returndata.
+ function readBytes17(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes17 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes18 at `rdPtr` in returndata.
+ function readBytes18(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes18 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes19 at `rdPtr` in returndata.
+ function readBytes19(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes19 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes20 at `rdPtr` in returndata.
+ function readBytes20(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes20 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes21 at `rdPtr` in returndata.
+ function readBytes21(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes21 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes22 at `rdPtr` in returndata.
+ function readBytes22(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes22 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes23 at `rdPtr` in returndata.
+ function readBytes23(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes23 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes24 at `rdPtr` in returndata.
+ function readBytes24(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes24 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes25 at `rdPtr` in returndata.
+ function readBytes25(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes25 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes26 at `rdPtr` in returndata.
+ function readBytes26(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes26 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes27 at `rdPtr` in returndata.
+ function readBytes27(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes27 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes28 at `rdPtr` in returndata.
+ function readBytes28(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes28 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes29 at `rdPtr` in returndata.
+ function readBytes29(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes29 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes30 at `rdPtr` in returndata.
+ function readBytes30(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes30 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes31 at `rdPtr` in returndata.
+ function readBytes31(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes31 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the bytes32 at `rdPtr` in returndata.
+ function readBytes32(
+ ReturndataPointer rdPtr
+ ) internal pure returns (bytes32 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint8 at `rdPtr` in returndata.
+ function readUint8(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint8 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint16 at `rdPtr` in returndata.
+ function readUint16(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint16 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint24 at `rdPtr` in returndata.
+ function readUint24(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint24 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint32 at `rdPtr` in returndata.
+ function readUint32(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint32 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint40 at `rdPtr` in returndata.
+ function readUint40(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint40 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint48 at `rdPtr` in returndata.
+ function readUint48(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint48 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint56 at `rdPtr` in returndata.
+ function readUint56(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint56 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint64 at `rdPtr` in returndata.
+ function readUint64(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint64 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint72 at `rdPtr` in returndata.
+ function readUint72(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint72 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint80 at `rdPtr` in returndata.
+ function readUint80(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint80 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint88 at `rdPtr` in returndata.
+ function readUint88(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint88 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint96 at `rdPtr` in returndata.
+ function readUint96(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint96 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint104 at `rdPtr` in returndata.
+ function readUint104(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint104 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint112 at `rdPtr` in returndata.
+ function readUint112(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint112 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint120 at `rdPtr` in returndata.
+ function readUint120(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint120 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint128 at `rdPtr` in returndata.
+ function readUint128(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint128 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint136 at `rdPtr` in returndata.
+ function readUint136(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint136 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint144 at `rdPtr` in returndata.
+ function readUint144(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint144 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint152 at `rdPtr` in returndata.
+ function readUint152(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint152 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint160 at `rdPtr` in returndata.
+ function readUint160(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint160 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint168 at `rdPtr` in returndata.
+ function readUint168(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint168 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint176 at `rdPtr` in returndata.
+ function readUint176(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint176 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint184 at `rdPtr` in returndata.
+ function readUint184(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint184 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint192 at `rdPtr` in returndata.
+ function readUint192(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint192 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint200 at `rdPtr` in returndata.
+ function readUint200(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint200 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint208 at `rdPtr` in returndata.
+ function readUint208(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint208 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint216 at `rdPtr` in returndata.
+ function readUint216(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint216 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint224 at `rdPtr` in returndata.
+ function readUint224(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint224 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint232 at `rdPtr` in returndata.
+ function readUint232(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint232 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint240 at `rdPtr` in returndata.
+ function readUint240(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint240 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint248 at `rdPtr` in returndata.
+ function readUint248(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint248 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the uint256 at `rdPtr` in returndata.
+ function readUint256(
+ ReturndataPointer rdPtr
+ ) internal pure returns (uint256 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int8 at `rdPtr` in returndata.
+ function readInt8(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int8 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int16 at `rdPtr` in returndata.
+ function readInt16(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int16 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int24 at `rdPtr` in returndata.
+ function readInt24(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int24 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int32 at `rdPtr` in returndata.
+ function readInt32(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int32 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int40 at `rdPtr` in returndata.
+ function readInt40(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int40 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int48 at `rdPtr` in returndata.
+ function readInt48(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int48 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int56 at `rdPtr` in returndata.
+ function readInt56(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int56 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int64 at `rdPtr` in returndata.
+ function readInt64(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int64 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int72 at `rdPtr` in returndata.
+ function readInt72(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int72 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int80 at `rdPtr` in returndata.
+ function readInt80(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int80 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int88 at `rdPtr` in returndata.
+ function readInt88(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int88 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int96 at `rdPtr` in returndata.
+ function readInt96(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int96 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int104 at `rdPtr` in returndata.
+ function readInt104(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int104 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int112 at `rdPtr` in returndata.
+ function readInt112(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int112 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int120 at `rdPtr` in returndata.
+ function readInt120(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int120 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int128 at `rdPtr` in returndata.
+ function readInt128(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int128 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int136 at `rdPtr` in returndata.
+ function readInt136(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int136 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int144 at `rdPtr` in returndata.
+ function readInt144(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int144 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int152 at `rdPtr` in returndata.
+ function readInt152(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int152 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int160 at `rdPtr` in returndata.
+ function readInt160(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int160 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int168 at `rdPtr` in returndata.
+ function readInt168(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int168 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int176 at `rdPtr` in returndata.
+ function readInt176(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int176 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int184 at `rdPtr` in returndata.
+ function readInt184(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int184 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int192 at `rdPtr` in returndata.
+ function readInt192(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int192 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int200 at `rdPtr` in returndata.
+ function readInt200(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int200 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int208 at `rdPtr` in returndata.
+ function readInt208(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int208 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int216 at `rdPtr` in returndata.
+ function readInt216(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int216 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int224 at `rdPtr` in returndata.
+ function readInt224(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int224 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int232 at `rdPtr` in returndata.
+ function readInt232(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int232 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int240 at `rdPtr` in returndata.
+ function readInt240(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int240 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int248 at `rdPtr` in returndata.
+ function readInt248(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int248 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+
+ /// @dev Reads the int256 at `rdPtr` in returndata.
+ function readInt256(
+ ReturndataPointer rdPtr
+ ) internal pure returns (int256 value) {
+ assembly {
+ returndatacopy(0, rdPtr, _OneWord)
+ value := mload(0)
+ }
+ }
+}
+
+library MemoryReaders {
+ /// @dev Reads the memory pointer at `mPtr` in memory.
+ function readMemoryPointer(
+ MemoryPointer mPtr
+ ) internal pure returns (MemoryPointer value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads value at `mPtr` & applies a mask to return only last 4 bytes
+ function readMaskedUint256(
+ MemoryPointer mPtr
+ ) internal pure returns (uint256 value) {
+ value = mPtr.readUint256() & OffsetOrLengthMask;
+ }
+
+ /// @dev Reads the bool at `mPtr` in memory.
+ function readBool(MemoryPointer mPtr) internal pure returns (bool value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the address at `mPtr` in memory.
+ function readAddress(
+ MemoryPointer mPtr
+ ) internal pure returns (address value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes1 at `mPtr` in memory.
+ function readBytes1(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes1 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes2 at `mPtr` in memory.
+ function readBytes2(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes2 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes3 at `mPtr` in memory.
+ function readBytes3(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes3 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes4 at `mPtr` in memory.
+ function readBytes4(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes4 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes5 at `mPtr` in memory.
+ function readBytes5(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes5 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes6 at `mPtr` in memory.
+ function readBytes6(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes6 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes7 at `mPtr` in memory.
+ function readBytes7(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes7 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes8 at `mPtr` in memory.
+ function readBytes8(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes8 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes9 at `mPtr` in memory.
+ function readBytes9(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes9 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes10 at `mPtr` in memory.
+ function readBytes10(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes10 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes11 at `mPtr` in memory.
+ function readBytes11(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes11 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes12 at `mPtr` in memory.
+ function readBytes12(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes12 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes13 at `mPtr` in memory.
+ function readBytes13(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes13 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes14 at `mPtr` in memory.
+ function readBytes14(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes14 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes15 at `mPtr` in memory.
+ function readBytes15(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes15 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes16 at `mPtr` in memory.
+ function readBytes16(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes16 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes17 at `mPtr` in memory.
+ function readBytes17(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes17 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes18 at `mPtr` in memory.
+ function readBytes18(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes18 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes19 at `mPtr` in memory.
+ function readBytes19(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes19 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes20 at `mPtr` in memory.
+ function readBytes20(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes20 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes21 at `mPtr` in memory.
+ function readBytes21(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes21 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes22 at `mPtr` in memory.
+ function readBytes22(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes22 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes23 at `mPtr` in memory.
+ function readBytes23(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes23 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes24 at `mPtr` in memory.
+ function readBytes24(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes24 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes25 at `mPtr` in memory.
+ function readBytes25(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes25 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes26 at `mPtr` in memory.
+ function readBytes26(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes26 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes27 at `mPtr` in memory.
+ function readBytes27(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes27 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes28 at `mPtr` in memory.
+ function readBytes28(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes28 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes29 at `mPtr` in memory.
+ function readBytes29(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes29 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes30 at `mPtr` in memory.
+ function readBytes30(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes30 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes31 at `mPtr` in memory.
+ function readBytes31(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes31 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the bytes32 at `mPtr` in memory.
+ function readBytes32(
+ MemoryPointer mPtr
+ ) internal pure returns (bytes32 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint8 at `mPtr` in memory.
+ function readUint8(MemoryPointer mPtr) internal pure returns (uint8 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint16 at `mPtr` in memory.
+ function readUint16(
+ MemoryPointer mPtr
+ ) internal pure returns (uint16 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint24 at `mPtr` in memory.
+ function readUint24(
+ MemoryPointer mPtr
+ ) internal pure returns (uint24 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint32 at `mPtr` in memory.
+ function readUint32(
+ MemoryPointer mPtr
+ ) internal pure returns (uint32 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint40 at `mPtr` in memory.
+ function readUint40(
+ MemoryPointer mPtr
+ ) internal pure returns (uint40 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint48 at `mPtr` in memory.
+ function readUint48(
+ MemoryPointer mPtr
+ ) internal pure returns (uint48 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint56 at `mPtr` in memory.
+ function readUint56(
+ MemoryPointer mPtr
+ ) internal pure returns (uint56 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint64 at `mPtr` in memory.
+ function readUint64(
+ MemoryPointer mPtr
+ ) internal pure returns (uint64 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint72 at `mPtr` in memory.
+ function readUint72(
+ MemoryPointer mPtr
+ ) internal pure returns (uint72 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint80 at `mPtr` in memory.
+ function readUint80(
+ MemoryPointer mPtr
+ ) internal pure returns (uint80 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint88 at `mPtr` in memory.
+ function readUint88(
+ MemoryPointer mPtr
+ ) internal pure returns (uint88 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint96 at `mPtr` in memory.
+ function readUint96(
+ MemoryPointer mPtr
+ ) internal pure returns (uint96 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint104 at `mPtr` in memory.
+ function readUint104(
+ MemoryPointer mPtr
+ ) internal pure returns (uint104 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint112 at `mPtr` in memory.
+ function readUint112(
+ MemoryPointer mPtr
+ ) internal pure returns (uint112 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint120 at `mPtr` in memory.
+ function readUint120(
+ MemoryPointer mPtr
+ ) internal pure returns (uint120 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint128 at `mPtr` in memory.
+ function readUint128(
+ MemoryPointer mPtr
+ ) internal pure returns (uint128 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint136 at `mPtr` in memory.
+ function readUint136(
+ MemoryPointer mPtr
+ ) internal pure returns (uint136 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint144 at `mPtr` in memory.
+ function readUint144(
+ MemoryPointer mPtr
+ ) internal pure returns (uint144 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint152 at `mPtr` in memory.
+ function readUint152(
+ MemoryPointer mPtr
+ ) internal pure returns (uint152 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint160 at `mPtr` in memory.
+ function readUint160(
+ MemoryPointer mPtr
+ ) internal pure returns (uint160 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint168 at `mPtr` in memory.
+ function readUint168(
+ MemoryPointer mPtr
+ ) internal pure returns (uint168 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint176 at `mPtr` in memory.
+ function readUint176(
+ MemoryPointer mPtr
+ ) internal pure returns (uint176 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint184 at `mPtr` in memory.
+ function readUint184(
+ MemoryPointer mPtr
+ ) internal pure returns (uint184 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint192 at `mPtr` in memory.
+ function readUint192(
+ MemoryPointer mPtr
+ ) internal pure returns (uint192 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint200 at `mPtr` in memory.
+ function readUint200(
+ MemoryPointer mPtr
+ ) internal pure returns (uint200 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint208 at `mPtr` in memory.
+ function readUint208(
+ MemoryPointer mPtr
+ ) internal pure returns (uint208 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint216 at `mPtr` in memory.
+ function readUint216(
+ MemoryPointer mPtr
+ ) internal pure returns (uint216 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint224 at `mPtr` in memory.
+ function readUint224(
+ MemoryPointer mPtr
+ ) internal pure returns (uint224 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint232 at `mPtr` in memory.
+ function readUint232(
+ MemoryPointer mPtr
+ ) internal pure returns (uint232 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint240 at `mPtr` in memory.
+ function readUint240(
+ MemoryPointer mPtr
+ ) internal pure returns (uint240 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint248 at `mPtr` in memory.
+ function readUint248(
+ MemoryPointer mPtr
+ ) internal pure returns (uint248 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the uint256 at `mPtr` in memory.
+ function readUint256(
+ MemoryPointer mPtr
+ ) internal pure returns (uint256 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int8 at `mPtr` in memory.
+ function readInt8(MemoryPointer mPtr) internal pure returns (int8 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int16 at `mPtr` in memory.
+ function readInt16(MemoryPointer mPtr) internal pure returns (int16 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int24 at `mPtr` in memory.
+ function readInt24(MemoryPointer mPtr) internal pure returns (int24 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int32 at `mPtr` in memory.
+ function readInt32(MemoryPointer mPtr) internal pure returns (int32 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int40 at `mPtr` in memory.
+ function readInt40(MemoryPointer mPtr) internal pure returns (int40 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int48 at `mPtr` in memory.
+ function readInt48(MemoryPointer mPtr) internal pure returns (int48 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int56 at `mPtr` in memory.
+ function readInt56(MemoryPointer mPtr) internal pure returns (int56 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int64 at `mPtr` in memory.
+ function readInt64(MemoryPointer mPtr) internal pure returns (int64 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int72 at `mPtr` in memory.
+ function readInt72(MemoryPointer mPtr) internal pure returns (int72 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int80 at `mPtr` in memory.
+ function readInt80(MemoryPointer mPtr) internal pure returns (int80 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int88 at `mPtr` in memory.
+ function readInt88(MemoryPointer mPtr) internal pure returns (int88 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int96 at `mPtr` in memory.
+ function readInt96(MemoryPointer mPtr) internal pure returns (int96 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int104 at `mPtr` in memory.
+ function readInt104(
+ MemoryPointer mPtr
+ ) internal pure returns (int104 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int112 at `mPtr` in memory.
+ function readInt112(
+ MemoryPointer mPtr
+ ) internal pure returns (int112 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int120 at `mPtr` in memory.
+ function readInt120(
+ MemoryPointer mPtr
+ ) internal pure returns (int120 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int128 at `mPtr` in memory.
+ function readInt128(
+ MemoryPointer mPtr
+ ) internal pure returns (int128 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int136 at `mPtr` in memory.
+ function readInt136(
+ MemoryPointer mPtr
+ ) internal pure returns (int136 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int144 at `mPtr` in memory.
+ function readInt144(
+ MemoryPointer mPtr
+ ) internal pure returns (int144 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int152 at `mPtr` in memory.
+ function readInt152(
+ MemoryPointer mPtr
+ ) internal pure returns (int152 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int160 at `mPtr` in memory.
+ function readInt160(
+ MemoryPointer mPtr
+ ) internal pure returns (int160 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int168 at `mPtr` in memory.
+ function readInt168(
+ MemoryPointer mPtr
+ ) internal pure returns (int168 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int176 at `mPtr` in memory.
+ function readInt176(
+ MemoryPointer mPtr
+ ) internal pure returns (int176 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int184 at `mPtr` in memory.
+ function readInt184(
+ MemoryPointer mPtr
+ ) internal pure returns (int184 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int192 at `mPtr` in memory.
+ function readInt192(
+ MemoryPointer mPtr
+ ) internal pure returns (int192 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int200 at `mPtr` in memory.
+ function readInt200(
+ MemoryPointer mPtr
+ ) internal pure returns (int200 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int208 at `mPtr` in memory.
+ function readInt208(
+ MemoryPointer mPtr
+ ) internal pure returns (int208 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int216 at `mPtr` in memory.
+ function readInt216(
+ MemoryPointer mPtr
+ ) internal pure returns (int216 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int224 at `mPtr` in memory.
+ function readInt224(
+ MemoryPointer mPtr
+ ) internal pure returns (int224 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int232 at `mPtr` in memory.
+ function readInt232(
+ MemoryPointer mPtr
+ ) internal pure returns (int232 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int240 at `mPtr` in memory.
+ function readInt240(
+ MemoryPointer mPtr
+ ) internal pure returns (int240 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int248 at `mPtr` in memory.
+ function readInt248(
+ MemoryPointer mPtr
+ ) internal pure returns (int248 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+
+ /// @dev Reads the int256 at `mPtr` in memory.
+ function readInt256(
+ MemoryPointer mPtr
+ ) internal pure returns (int256 value) {
+ assembly {
+ value := mload(mPtr)
+ }
+ }
+}
+
+library MemoryWriters {
+ /// @dev Writes `valuePtr` to memory at `mPtr`.
+ function write(MemoryPointer mPtr, MemoryPointer valuePtr) internal pure {
+ assembly {
+ mstore(mPtr, valuePtr)
+ }
+ }
+
+ /// @dev Writes a boolean `value` to `mPtr` in memory.
+ function write(MemoryPointer mPtr, bool value) internal pure {
+ assembly {
+ mstore(mPtr, value)
+ }
+ }
+
+ /// @dev Writes an address `value` to `mPtr` in memory.
+ function write(MemoryPointer mPtr, address value) internal pure {
+ assembly {
+ mstore(mPtr, value)
+ }
+ }
+
+ /// @dev Writes a bytes32 `value` to `mPtr` in memory.
+ /// Separate name to disambiguate literal write parameters.
+ function writeBytes32(MemoryPointer mPtr, bytes32 value) internal pure {
+ assembly {
+ mstore(mPtr, value)
+ }
+ }
+
+ /// @dev Writes a uint256 `value` to `mPtr` in memory.
+ function write(MemoryPointer mPtr, uint256 value) internal pure {
+ assembly {
+ mstore(mPtr, value)
+ }
+ }
+
+ /// @dev Writes an int256 `value` to `mPtr` in memory.
+ /// Separate name to disambiguate literal write parameters.
+ function writeInt(MemoryPointer mPtr, int256 value) internal pure {
+ assembly {
+ mstore(mPtr, value)
+ }
+ }
+}
diff --git a/contracts/helpers/TransferHelper.sol b/contracts/helpers/TransferHelper.sol
index 593fd967b..b08ec405e 100644
--- a/contracts/helpers/TransferHelper.sol
+++ b/contracts/helpers/TransferHelper.sol
@@ -1,38 +1,42 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-import "./TransferHelperStructs.sol";
+import { IERC721Receiver } from "../interfaces/IERC721Receiver.sol";
-import { TokenTransferrer } from "../lib/TokenTransferrer.sol";
+import {
+ TransferHelperItem,
+ TransferHelperItemsWithRecipient
+} from "./TransferHelperStructs.sol";
+
+import { ConduitItemType } from "../conduit/lib/ConduitEnums.sol";
import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
-// prettier-ignore
import {
ConduitControllerInterface
} from "../interfaces/ConduitControllerInterface.sol";
-import { Conduit } from "../conduit/Conduit.sol";
-
import { ConduitTransfer } from "../conduit/lib/ConduitStructs.sol";
-// prettier-ignore
import {
TransferHelperInterface
} from "../interfaces/TransferHelperInterface.sol";
+import { TransferHelperErrors } from "../interfaces/TransferHelperErrors.sol";
+
/**
* @title TransferHelper
- * @author stuckinaboot, stephankmin
+ * @author stephankmin, stuckinaboot, ryanio
* @notice TransferHelper is a utility contract for transferring
- * ERC20/ERC721/ERC1155 items in bulk to a specific recipient.
+ * ERC20/ERC721/ERC1155 items in bulk to specific recipients.
*/
-contract TransferHelper is TransferHelperInterface, TokenTransferrer {
+contract TransferHelper is TransferHelperInterface, TransferHelperErrors {
// Allow for interaction with the conduit controller.
ConduitControllerInterface internal immutable _CONDUIT_CONTROLLER;
- // Cache the conduit creation hash used by the conduit controller.
+ // Set conduit creation code and runtime code hashes as immutable arguments.
bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH;
+ bytes32 internal immutable _CONDUIT_RUNTIME_CODE_HASH;
/**
* @dev Set the supplied conduit controller and retrieve its
@@ -44,123 +48,297 @@ contract TransferHelper is TransferHelperInterface, TokenTransferrer {
* ERC20/721/1155 tokens.
*/
constructor(address conduitController) {
- // Get the conduit creation code hash from the supplied conduit
- // controller and set it as an immutable.
+ // Get the conduit creation code and runtime code hashes from the
+ // supplied conduit controller and set them as an immutable.
ConduitControllerInterface controller = ConduitControllerInterface(
conduitController
);
- (_CONDUIT_CREATION_CODE_HASH, ) = controller.getConduitCodeHashes();
+ (_CONDUIT_CREATION_CODE_HASH, _CONDUIT_RUNTIME_CODE_HASH) = controller
+ .getConduitCodeHashes();
// Set the supplied conduit controller as an immutable.
_CONDUIT_CONTROLLER = controller;
}
/**
- * @notice Transfer multiple items to a single recipient.
+ * @notice Transfer multiple ERC20/ERC721/ERC1155 items to
+ * specified recipients.
*
- * @param items The items to transfer.
- * @param recipient The address the items should be transferred to.
- * @param conduitKey The key of the conduit through which the bulk transfer
- * should occur.
+ * @param items The items to transfer to an intended recipient.
+ * @param conduitKey A mandatory conduit key referring to a conduit through
+ * which the bulk transfer should occur.
*
* @return magicValue A value indicating that the transfers were successful.
*/
function bulkTransfer(
- TransferHelperItem[] calldata items,
- address recipient,
+ TransferHelperItemsWithRecipient[] calldata items,
bytes32 conduitKey
) external override returns (bytes4 magicValue) {
- // Retrieve total number of transfers and place on stack.
- uint256 totalTransfers = items.length;
-
- // If no conduitKey is given, use TokenTransferrer to perform transfers.
+ // Ensure that a conduit key has been supplied.
if (conduitKey == bytes32(0)) {
- // Skip overflow checks: all for loops are indexed starting at zero.
- unchecked {
- // Iterate over each transfer.
- for (uint256 i = 0; i < totalTransfers; ++i) {
- // Retrieve the transfer in question.
- TransferHelperItem calldata item = items[i];
-
- // Perform a transfer based on the transfer's item type.
- // Revert if item being transferred is a native token.
- if (item.itemType == ConduitItemType.NATIVE) {
- revert InvalidItemType();
- } else if (item.itemType == ConduitItemType.ERC20) {
- _performERC20Transfer(
- item.token,
- msg.sender,
- recipient,
- item.amount
- );
- } else if (item.itemType == ConduitItemType.ERC721) {
- _performERC721Transfer(
- item.token,
- msg.sender,
- recipient,
- item.identifier
- );
- } else {
- _performERC1155Transfer(
- item.token,
- msg.sender,
- recipient,
- item.identifier,
- item.amount
- );
- }
- }
- }
+ revert InvalidConduit(conduitKey, address(0));
}
- // Otherwise, a conduitKey was provided.
- else {
- // Derive the conduit address from the deployer, conduit key
- // and creation code hash.
- address conduit = address(
- uint160(
- uint256(
- keccak256(
- abi.encodePacked(
- bytes1(0xff),
- address(_CONDUIT_CONTROLLER),
- conduitKey,
- _CONDUIT_CREATION_CODE_HASH
- )
+
+ // Use conduit derived from supplied conduit key to perform transfers.
+ _performTransfersWithConduit(items, conduitKey);
+
+ // Return a magic value indicating that the transfers were performed.
+ magicValue = this.bulkTransfer.selector;
+ }
+
+ /**
+ * @notice Perform multiple transfers to specified recipients via the
+ * conduit derived from the provided conduit key.
+ *
+ * @param transfers The items to transfer.
+ * @param conduitKey The conduit key referring to the conduit through
+ * which the bulk transfer should occur.
+ */
+ function _performTransfersWithConduit(
+ TransferHelperItemsWithRecipient[] calldata transfers,
+ bytes32 conduitKey
+ ) internal {
+ // Retrieve total number of transfers and place on stack.
+ uint256 numTransfers = transfers.length;
+
+ // Derive the conduit address from the deployer, conduit key
+ // and creation code hash.
+ address conduit = address(
+ uint160(
+ uint256(
+ keccak256(
+ abi.encodePacked(
+ bytes1(0xff),
+ address(_CONDUIT_CONTROLLER),
+ conduitKey,
+ _CONDUIT_CREATION_CODE_HASH
)
)
)
- );
+ )
+ );
- // Declare a new array to populate with each token transfer.
- ConduitTransfer[] memory conduitTransfers = new ConduitTransfer[](
- totalTransfers
- );
+ // Declare a variable to store the sum of all items across transfers.
+ uint256 sumOfItemsAcrossAllTransfers;
+
+ // Skip overflow checks: all for loops are indexed starting at zero.
+ unchecked {
+ // Iterate over each transfer.
+ for (uint256 i = 0; i < numTransfers; ++i) {
+ // Retrieve the transfer in question.
+ TransferHelperItemsWithRecipient calldata transfer = transfers[
+ i
+ ];
+
+ // Increment totalItems by the number of items in the transfer.
+ sumOfItemsAcrossAllTransfers += transfer.items.length;
+ }
+ }
+
+ // Declare a new array in memory with length totalItems to populate with
+ // each conduit transfer.
+ ConduitTransfer[] memory conduitTransfers = new ConduitTransfer[](
+ sumOfItemsAcrossAllTransfers
+ );
+
+ // Declare an index for storing ConduitTransfers in conduitTransfers.
+ uint256 itemIndex;
+
+ // Skip overflow checks: all for loops are indexed starting at zero.
+ unchecked {
+ // Iterate over each transfer.
+ for (uint256 i = 0; i < numTransfers; ++i) {
+ // Retrieve the transfer in question.
+ TransferHelperItemsWithRecipient calldata transfer = transfers[
+ i
+ ];
+
+ // Retrieve the items of the transfer in question.
+ TransferHelperItem[] calldata transferItems = transfer.items;
+
+ // Ensure recipient is not the zero address.
+ _checkRecipientIsNotZeroAddress(transfer.recipient);
- // Skip overflow checks: all for loops are indexed starting at zero.
- unchecked {
- // Iterate over each transfer.
- for (uint256 i = 0; i < totalTransfers; ++i) {
- // Retrieve the transfer in question.
- TransferHelperItem calldata item = items[i];
+ // Create a boolean indicating whether validateERC721Receiver
+ // is true and recipient is a contract.
+ bool callERC721Receiver = transfer.validateERC721Receiver &&
+ transfer.recipient.code.length != 0;
+
+ // Retrieve the total number of items in the transfer and
+ // place on stack.
+ uint256 numItemsInTransfer = transferItems.length;
+
+ // Iterate over each item in the transfer to create a
+ // corresponding ConduitTransfer.
+ for (uint256 j = 0; j < numItemsInTransfer; ++j) {
+ // Retrieve the item from the transfer.
+ TransferHelperItem calldata item = transferItems[j];
+
+ if (item.itemType == ConduitItemType.ERC20) {
+ // Ensure that the identifier of an ERC20 token is 0.
+ if (item.identifier != 0) {
+ revert InvalidERC20Identifier();
+ }
+ }
+
+ // If the item is an ERC721 token and
+ // callERC721Receiver is true...
+ if (item.itemType == ConduitItemType.ERC721) {
+ if (callERC721Receiver) {
+ // Check if the recipient implements
+ // onERC721Received for the given tokenId.
+ _checkERC721Receiver(
+ conduit,
+ transfer.recipient,
+ item.identifier
+ );
+ }
+ }
// Create a ConduitTransfer corresponding to each
// TransferHelperItem.
- conduitTransfers[i] = ConduitTransfer(
+ conduitTransfers[itemIndex] = ConduitTransfer(
item.itemType,
item.token,
msg.sender,
- recipient,
+ transfer.recipient,
item.identifier,
item.amount
);
+
+ // Increment the index for storing ConduitTransfers.
+ ++itemIndex;
+ }
+ }
+ }
+
+ // Attempt the external call to transfer tokens via the derived conduit.
+ try ConduitInterface(conduit).execute(conduitTransfers) returns (
+ bytes4 conduitMagicValue
+ ) {
+ // Check if the value returned from the external call matches
+ // the conduit `execute` selector.
+ if (conduitMagicValue != ConduitInterface.execute.selector) {
+ // If the external call fails, revert with the conduit key
+ // and conduit address.
+ revert InvalidConduit(conduitKey, conduit);
+ }
+ } catch Error(string memory reason) {
+ // Catch reverts with a provided reason string and
+ // revert with the reason, conduit key and conduit address.
+ revert ConduitErrorRevertString(reason, conduitKey, conduit);
+ } catch (bytes memory data) {
+ // Conduits will throw a custom error when attempting to transfer
+ // native token item types or an ERC721 item amount other than 1.
+ // Bubble up these custom errors when encountered. Note that the
+ // conduit itself will bubble up revert reasons from transfers as
+ // well, meaning that these errors are not necessarily indicative of
+ // an issue with the item type or amount in cases where the same
+ // custom error signature is encountered during a conduit transfer.
+
+ // Set initial value of first four bytes of revert data to the mask.
+ bytes4 customErrorSelector = bytes4(0xffffffff);
+
+ // Utilize assembly to read first four bytes (if present) directly.
+ assembly {
+ // Combine original mask with first four bytes of revert data.
+ customErrorSelector := and(
+ mload(add(data, 0x20)), // Data begins after length offset.
+ customErrorSelector
+ )
+ }
+
+ // Pass through the custom error in question if the revert data is
+ // the correct length and matches an expected custom error selector.
+ if (
+ data.length == 4 &&
+ customErrorSelector == InvalidItemType.selector
+ ) {
+ // "Bubble up" the revert reason.
+ assembly {
+ revert(add(data, 0x20), 0x04)
+ }
+ } else if (
+ data.length == 36 &&
+ customErrorSelector == InvalidERC721TransferAmount.selector
+ ) {
+ // "Bubble up" the revert reason.
+ assembly {
+ revert(add(data, 0x20), 0x24)
}
}
- // Call the conduit and execute bulk transfers.
- ConduitInterface(conduit).execute(conduitTransfers);
+ // Catch all other reverts from the external call to the conduit and
+ // include the conduit's raw revert reason as a data argument to a
+ // new custom error.
+ revert ConduitErrorRevertBytes(data, conduitKey, conduit);
}
+ }
- // Return a magic value indicating that the transfers were performed.
- magicValue = this.bulkTransfer.selector;
+ /**
+ * @notice An internal function to check if a recipient address implements
+ * onERC721Received for a given tokenId. Note that this check does
+ * not adhere to the safe transfer specification and is only meant
+ * to provide an additional layer of assurance that the recipient
+ * can receive the tokens — any hooks or post-transfer checks will
+ * fail and the caller will be the transfer helper rather than the
+ * ERC721 contract. Note that the conduit is set as the operator, as
+ * it will be the caller once the transfer is performed.
+ *
+ * @param conduit The conduit to provide as the operator when calling
+ * onERC721Received.
+ * @param recipient The ERC721 recipient on which to call onERC721Received.
+ * @param tokenId The ERC721 tokenId of the token being transferred.
+ */
+ function _checkERC721Receiver(
+ address conduit,
+ address recipient,
+ uint256 tokenId
+ ) internal {
+ // Check if recipient can receive ERC721 tokens.
+ try
+ IERC721Receiver(recipient).onERC721Received(
+ conduit,
+ msg.sender,
+ tokenId,
+ ""
+ )
+ returns (bytes4 selector) {
+ // Check if onERC721Received selector is valid.
+ if (selector != IERC721Receiver.onERC721Received.selector) {
+ // Revert if recipient cannot accept
+ // ERC721 tokens.
+ revert InvalidERC721Recipient(recipient);
+ }
+ } catch (bytes memory data) {
+ // "Bubble up" recipient's revert reason.
+ revert ERC721ReceiverErrorRevertBytes(
+ data,
+ recipient,
+ msg.sender,
+ tokenId
+ );
+ } catch Error(string memory reason) {
+ // "Bubble up" recipient's revert reason.
+ revert ERC721ReceiverErrorRevertString(
+ reason,
+ recipient,
+ msg.sender,
+ tokenId
+ );
+ }
+ }
+
+ /**
+ * @notice An internal function that reverts if the passed-in recipient
+ * is the zero address.
+ *
+ * @param recipient The recipient on which to perform the check.
+ */
+ function _checkRecipientIsNotZeroAddress(address recipient) internal pure {
+ // Revert if the recipient is the zero address.
+ if (recipient == address(0x0)) {
+ revert RecipientCannotBeZeroAddress();
+ }
}
}
diff --git a/contracts/helpers/TransferHelperStructs.sol b/contracts/helpers/TransferHelperStructs.sol
index 35aeec140..b58bee2c4 100644
--- a/contracts/helpers/TransferHelperStructs.sol
+++ b/contracts/helpers/TransferHelperStructs.sol
@@ -1,11 +1,29 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
import { ConduitItemType } from "../conduit/lib/ConduitEnums.sol";
+/**
+ * @dev A TransferHelperItem specifies the itemType (ERC20/ERC721/ERC1155),
+ * token address, token identifier, and amount of the token to be
+ * transferred via the TransferHelper. For ERC20 tokens, identifier
+ * must be 0. For ERC721 tokens, amount must be 1.
+ */
struct TransferHelperItem {
ConduitItemType itemType;
address token;
uint256 identifier;
uint256 amount;
}
+
+/**
+ * @dev A TransferHelperItemsWithRecipient specifies the tokens to transfer
+ * via the TransferHelper, their intended recipient, and a boolean flag
+ * indicating whether onERC721Received should be called on a recipient
+ * contract.
+ */
+struct TransferHelperItemsWithRecipient {
+ TransferHelperItem[] items;
+ address recipient;
+ bool validateERC721Receiver;
+}
diff --git a/contracts/interfaces/AbridgedTokenInterfaces.sol b/contracts/interfaces/AbridgedTokenInterfaces.sol
index fc39bdae0..914279c7b 100644
--- a/contracts/interfaces/AbridgedTokenInterfaces.sol
+++ b/contracts/interfaces/AbridgedTokenInterfaces.sol
@@ -1,23 +1,88 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
+/**
+ * @title ERC20Interface
+ * @notice Contains the minimum interfaces needed to interact with ERC20s.
+ */
interface ERC20Interface {
+ /**
+ * @dev Allows an operator to transfer tokens on behalf of an owner.
+ *
+ * @param from The address of the owner.
+ * @param to The address of the recipient.
+ * @param value The amount of tokens to transfer.
+ *
+ * @return success True if the transfer was successful.
+ */
function transferFrom(
- address,
- address,
- uint256
- ) external returns (bool);
+ address from,
+ address to,
+ uint256 value
+ ) external returns (bool success);
+
+ /**
+ * @dev Allows an operator to approve a spender to transfer tokens on behalf
+ * of a user.
+ *
+ * @param spender The address of the spender.
+ * @param value The amount of tokens to approve.
+ *
+ * @return success True if the approval was successful.
+ */
+ function approve(
+ address spender,
+ uint256 value
+ ) external returns (bool success);
}
+/**
+ * @title ERC721Interface
+ * @notice Contains the minimum interfaces needed to interact with ERC721s.
+ */
interface ERC721Interface {
- function transferFrom(
- address,
- address,
- uint256
- ) external;
+ /**
+ * @dev Allows an operator to transfer tokens on behalf of an owner.
+ *
+ * @param from The address of the owner.
+ * @param to The address of the recipient.
+ * @param tokenId The ID of the token to transfer.
+ */
+ function transferFrom(address from, address to, uint256 tokenId) external;
+
+ /**
+ * @dev Allows an owner to approve an operator to transfer all tokens on a
+ * contract on behalf of the owner.
+ *
+ * @param to The address of the operator.
+ * @param approved Whether the operator is approved.
+ */
+ function setApprovalForAll(address to, bool approved) external;
+
+ /**
+ * @dev Returns the owner of a given token ID.
+ *
+ * @param tokenId The token ID.
+ *
+ * @return owner The owner of the token.
+ */
+ function ownerOf(uint256 tokenId) external view returns (address owner);
}
+/**
+ * @title ERC1155Interface
+ * @notice Contains the minimum interfaces needed to interact with ERC1155s.
+ */
interface ERC1155Interface {
+ /**
+ * @dev Allows an operator to transfer tokens on behalf of an owner.
+ *
+ * @param from The address of the owner.
+ * @param to The address of the recipient.
+ * @param id The ID of the token(s) to transfer.
+ * @param amount The amount of tokens to transfer.
+ * @param data Additional data.
+ */
function safeTransferFrom(
address from,
address to,
@@ -26,6 +91,15 @@ interface ERC1155Interface {
bytes calldata data
) external;
+ /**
+ * @dev Allows an operator to transfer tokens on behalf of an owner.
+ *
+ * @param from The address of the owner.
+ * @param to The address of the recipient.
+ * @param ids The IDs of the token(s) to transfer.
+ * @param amounts The amounts of tokens to transfer.
+ * @param data Additional data.
+ */
function safeBatchTransferFrom(
address from,
address to,
@@ -33,4 +107,13 @@ interface ERC1155Interface {
uint256[] calldata amounts,
bytes calldata data
) external;
+
+ /**
+ * @dev Allows an owner to approve an operator to transfer all tokens on a
+ * contract on behalf of the owner.
+ *
+ * @param to The address of the operator.
+ * @param approved Whether the operator is approved.
+ */
+ function setApprovalForAll(address to, bool approved) external;
}
diff --git a/contracts/interfaces/AmountDerivationErrors.sol b/contracts/interfaces/AmountDerivationErrors.sol
index 718809956..31b62e5b8 100644
--- a/contracts/interfaces/AmountDerivationErrors.sol
+++ b/contracts/interfaces/AmountDerivationErrors.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
/**
* @title AmountDerivationErrors
diff --git a/contracts/interfaces/ConduitControllerInterface.sol b/contracts/interfaces/ConduitControllerInterface.sol
index 83561ae54..6de8f8885 100644
--- a/contracts/interfaces/ConduitControllerInterface.sol
+++ b/contracts/interfaces/ConduitControllerInterface.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
/**
* @title ConduitControllerInterface
@@ -130,9 +130,10 @@ interface ConduitControllerInterface {
*
* @return conduit The address of the newly deployed conduit.
*/
- function createConduit(bytes32 conduitKey, address initialOwner)
- external
- returns (address conduit);
+ function createConduit(
+ bytes32 conduitKey,
+ address initialOwner
+ ) external returns (address conduit);
/**
* @notice Open or close a channel on a given conduit, thereby allowing the
@@ -161,8 +162,10 @@ interface ConduitControllerInterface {
* @param conduit The conduit for which to initiate ownership transfer.
* @param newPotentialOwner The new potential owner of the conduit.
*/
- function transferOwnership(address conduit, address newPotentialOwner)
- external;
+ function transferOwnership(
+ address conduit,
+ address newPotentialOwner
+ ) external;
/**
* @notice Clear the currently set potential owner, if any, from a conduit.
@@ -212,10 +215,9 @@ interface ConduitControllerInterface {
* @return exists A boolean indicating whether the derived conduit has been
* deployed or not.
*/
- function getConduit(bytes32 conduitKey)
- external
- view
- returns (address conduit, bool exists);
+ function getConduit(
+ bytes32 conduitKey
+ ) external view returns (address conduit, bool exists);
/**
* @notice Retrieve the potential owner, if any, for a given conduit. The
@@ -227,10 +229,9 @@ interface ConduitControllerInterface {
*
* @return potentialOwner The potential owner, if any, for the conduit.
*/
- function getPotentialOwner(address conduit)
- external
- view
- returns (address potentialOwner);
+ function getPotentialOwner(
+ address conduit
+ ) external view returns (address potentialOwner);
/**
* @notice Retrieve the status (either open or closed) of a given channel on
@@ -241,10 +242,10 @@ interface ConduitControllerInterface {
*
* @return isOpen The status of the channel on the given conduit.
*/
- function getChannelStatus(address conduit, address channel)
- external
- view
- returns (bool isOpen);
+ function getChannelStatus(
+ address conduit,
+ address channel
+ ) external view returns (bool isOpen);
/**
* @notice Retrieve the total number of open channels for a given conduit.
@@ -253,10 +254,9 @@ interface ConduitControllerInterface {
*
* @return totalChannels The total number of open channels for the conduit.
*/
- function getTotalChannels(address conduit)
- external
- view
- returns (uint256 totalChannels);
+ function getTotalChannels(
+ address conduit
+ ) external view returns (uint256 totalChannels);
/**
* @notice Retrieve an open channel at a specific index for a given conduit.
@@ -268,10 +268,10 @@ interface ConduitControllerInterface {
*
* @return channel The open channel, if any, at the specified channel index.
*/
- function getChannel(address conduit, uint256 channelIndex)
- external
- view
- returns (address channel);
+ function getChannel(
+ address conduit,
+ uint256 channelIndex
+ ) external view returns (address channel);
/**
* @notice Retrieve all open channels for a given conduit. Note that calling
@@ -282,10 +282,9 @@ interface ConduitControllerInterface {
*
* @return channels An array of open channels on the given conduit.
*/
- function getChannels(address conduit)
- external
- view
- returns (address[] memory channels);
+ function getChannels(
+ address conduit
+ ) external view returns (address[] memory channels);
/**
* @dev Retrieve the conduit creation code and runtime code hashes.
diff --git a/contracts/interfaces/ConduitInterface.sol b/contracts/interfaces/ConduitInterface.sol
index d988fcc83..113b464ba 100644
--- a/contracts/interfaces/ConduitInterface.sol
+++ b/contracts/interfaces/ConduitInterface.sol
@@ -1,10 +1,9 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-// prettier-ignore
import {
- ConduitTransfer,
- ConduitBatch1155Transfer
+ ConduitBatch1155Transfer,
+ ConduitTransfer
} from "../conduit/lib/ConduitStructs.sol";
/**
@@ -55,9 +54,9 @@ interface ConduitInterface {
* @return magicValue A magic value indicating that the transfers were
* performed successfully.
*/
- function execute(ConduitTransfer[] calldata transfers)
- external
- returns (bytes4 magicValue);
+ function execute(
+ ConduitTransfer[] calldata transfers
+ ) external returns (bytes4 magicValue);
/**
* @notice Execute a sequence of batch 1155 transfers. Only a caller with an
diff --git a/contracts/interfaces/ConsiderationEventsAndErrors.sol b/contracts/interfaces/ConsiderationEventsAndErrors.sol
index 13682ecda..b13abf590 100644
--- a/contracts/interfaces/ConsiderationEventsAndErrors.sol
+++ b/contracts/interfaces/ConsiderationEventsAndErrors.sol
@@ -1,7 +1,11 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-import { SpentItem, ReceivedItem } from "../lib/ConsiderationStructs.sol";
+import {
+ OrderParameters,
+ ReceivedItem,
+ SpentItem
+} from "../lib/ConsiderationStructs.sol";
/**
* @title ConsiderationEventsAndErrors
@@ -51,21 +55,24 @@ interface ConsiderationEventsAndErrors {
* this event will not be emitted on partial fills even though they do
* validate the order as part of partial fulfillment.
*
- * @param orderHash The hash of the validated order.
- * @param offerer The offerer of the validated order.
- * @param zone The zone of the validated order.
+ * @param orderHash The hash of the validated order.
+ * @param orderParameters The parameters of the validated order.
*/
- event OrderValidated(
- bytes32 orderHash,
- address indexed offerer,
- address indexed zone
- );
+ event OrderValidated(bytes32 orderHash, OrderParameters orderParameters);
+
+ /**
+ * @dev Emit an event whenever one or more orders are matched using either
+ * matchOrders or matchAdvancedOrders.
+ *
+ * @param orderHashes The order hashes of the matched orders.
+ */
+ event OrdersMatched(bytes32[] orderHashes);
/**
* @dev Emit an event whenever a counter for a given offerer is incremented.
*
* @param newCounter The new counter for the offerer.
- * @param offerer The offerer in question.
+ * @param offerer The offerer in question.
*/
event CounterIncremented(uint256 newCounter, address indexed offerer);
@@ -80,8 +87,11 @@ interface ConsiderationEventsAndErrors {
/**
* @dev Revert with an error when attempting to fill an order outside the
* specified start time and end time.
+ *
+ * @param startTime The time at which the order becomes active.
+ * @param endTime The time at which the order becomes inactive.
*/
- error InvalidTime();
+ error InvalidTime(uint256 startTime, uint256 endTime);
/**
* @dev Revert with an error when attempting to fill an order referencing an
@@ -95,6 +105,15 @@ interface ConsiderationEventsAndErrors {
*/
error MissingOriginalConsiderationItems();
+ /**
+ * @dev Revert with an error when an order is validated and the length of
+ * the consideration array is not equal to the supplied total original
+ * consideration items value. This error is also thrown when contract
+ * orders supply a total original consideration items value that does
+ * not match the supplied consideration array length.
+ */
+ error ConsiderationLengthNotEqualToTotalOriginal();
+
/**
* @dev Revert with an error when a call to a conduit fails with revert data
* that is too expensive to return.
@@ -118,15 +137,15 @@ interface ConsiderationEventsAndErrors {
);
/**
- * @dev Revert with an error when insufficient ether is supplied as part of
- * msg.value when fulfilling orders.
+ * @dev Revert with an error when insufficient native tokens are supplied as
+ * part of msg.value when fulfilling orders.
*/
- error InsufficientEtherSupplied();
+ error InsufficientNativeTokensSupplied();
/**
- * @dev Revert with an error when an ether transfer reverts.
+ * @dev Revert with an error when a native token transfer reverts.
*/
- error EtherTransferGenericFailure(address account, uint256 amount);
+ error NativeTokenTransferGenericFailure(address account, uint256 amount);
/**
* @dev Revert with an error when a partial fill is attempted on an order
@@ -152,9 +171,10 @@ interface ConsiderationEventsAndErrors {
/**
* @dev Revert with an error when attempting to cancel an order as a caller
- * other than the indicated offerer or zone.
+ * other than the indicated offerer or zone or when attempting to
+ * cancel a contract order.
*/
- error InvalidCanceller();
+ error CannotCancelOrder();
/**
* @dev Revert with an error when supplying a fraction with a value of zero
@@ -184,7 +204,7 @@ interface ConsiderationEventsAndErrors {
/**
* @dev Revert with an error when attempting to fulfill an order with an
- * offer for ETH outside of matching orders.
+ * offer for a native token outside of matching orders.
*/
error InvalidNativeOfferItem();
}
diff --git a/contracts/interfaces/ConsiderationInterface.sol b/contracts/interfaces/ConsiderationInterface.sol
index 1c6d5c9fc..4132fad1b 100644
--- a/contracts/interfaces/ConsiderationInterface.sol
+++ b/contracts/interfaces/ConsiderationInterface.sol
@@ -1,27 +1,25 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-// prettier-ignore
import {
+ AdvancedOrder,
BasicOrderParameters,
- OrderComponents,
+ CriteriaResolver,
+ Execution,
Fulfillment,
FulfillmentComponent,
- Execution,
Order,
- AdvancedOrder,
- OrderStatus,
- CriteriaResolver
+ OrderComponents
} from "../lib/ConsiderationStructs.sol";
/**
* @title ConsiderationInterface
* @author 0age
- * @custom:version 1.1
- * @notice Consideration is a generalized ETH/ERC20/ERC721/ERC1155 marketplace.
- * It minimizes external calls to the greatest extent possible and
- * provides lightweight methods for common routes as well as more
- * flexible methods for composing advanced orders.
+ * @custom:version 1.2
+ * @notice Consideration is a generalized native token/ERC20/ERC721/ERC1155
+ * marketplace. It minimizes external calls to the greatest extent
+ * possible and provides lightweight methods for common routes as well
+ * as more flexible methods for composing advanced orders.
*
* @dev ConsiderationInterface contains all external function interfaces for
* Consideration.
@@ -42,10 +40,9 @@ interface ConsiderationInterface {
* @return fulfilled A boolean indicating whether the order has been
* successfully fulfilled.
*/
- function fulfillBasicOrder(BasicOrderParameters calldata parameters)
- external
- payable
- returns (bool fulfilled);
+ function fulfillBasicOrder(
+ BasicOrderParameters calldata parameters
+ ) external payable returns (bool fulfilled);
/**
* @notice Fulfill an order with an arbitrary number of items for offer and
@@ -69,10 +66,10 @@ interface ConsiderationInterface {
* @return fulfilled A boolean indicating whether the order has been
* successfully fulfilled.
*/
- function fulfillOrder(Order calldata order, bytes32 fulfillerConduitKey)
- external
- payable
- returns (bool fulfilled);
+ function fulfillOrder(
+ Order calldata order,
+ bytes32 fulfillerConduitKey
+ ) external payable returns (bool fulfilled);
/**
* @notice Fill an order, fully or partially, with an arbitrary number of
@@ -162,7 +159,9 @@ interface ConsiderationInterface {
* returned boolean was fulfillable or not.
* @return executions An array of elements indicating the sequence of
* transfers performed as part of matching the given
- * orders.
+ * orders. Note that unspent offer item amounts or
+ * native tokens will not be reflected as part of
+ * this array.
*/
function fulfillAvailableOrders(
Order[] calldata orders,
@@ -237,7 +236,9 @@ interface ConsiderationInterface {
* returned boolean was fulfillable or not.
* @return executions An array of elements indicating the sequence of
* transfers performed as part of matching the given
- * orders.
+ * orders. Note that unspent offer item amounts or
+ * native tokens will not be reflected as part of
+ * this array.
*/
function fulfillAvailableAdvancedOrders(
AdvancedOrder[] calldata advancedOrders,
@@ -254,11 +255,13 @@ interface ConsiderationInterface {
/**
* @notice Match an arbitrary number of orders, each with an arbitrary
- * number of items for offer and consideration along with as set of
+ * number of items for offer and consideration along with a set of
* fulfillments allocating offer components to consideration
* components. Note that this function does not support
* criteria-based or partial filling of orders (though filling the
- * remainder of a partially-filled order is supported).
+ * remainder of a partially-filled order is supported). Any unspent
+ * offer item amounts or native tokens will be transferred to the
+ * caller.
*
* @param orders The orders to match. Note that both the offerer and
* fulfiller on each order must first approve this
@@ -273,7 +276,9 @@ interface ConsiderationInterface {
*
* @return executions An array of elements indicating the sequence of
* transfers performed as part of matching the given
- * orders.
+ * orders. Note that unspent offer item amounts or
+ * native tokens will not be reflected as part of this
+ * array.
*/
function matchOrders(
Order[] calldata orders,
@@ -285,7 +290,10 @@ interface ConsiderationInterface {
* arbitrary number of items for offer and consideration, supplying
* criteria resolvers containing specific token identifiers and
* associated proofs as well as fulfillments allocating offer
- * components to consideration components.
+ * components to consideration components. Any unspent offer item
+ * amounts will be transferred to the designated recipient (with the
+ * null address signifying to use the caller) and any unspent native
+ * tokens will be returned to the caller.
*
* @param orders The advanced orders to match. Note that both the
* offerer and fulfiller on each order must first
@@ -311,15 +319,20 @@ interface ConsiderationInterface {
* to consideration components. Note that each
* consideration component must be fully met in
* order for the match operation to be valid.
+ * @param recipient The intended recipient for all unspent offer
+ * item amounts, or the caller if the null address
+ * is supplied.
*
* @return executions An array of elements indicating the sequence of
* transfers performed as part of matching the given
- * orders.
+ * orders. Note that unspent offer item amounts or native
+ * tokens will not be reflected as part of this array.
*/
function matchAdvancedOrders(
AdvancedOrder[] calldata orders,
CriteriaResolver[] calldata criteriaResolvers,
- Fulfillment[] calldata fulfillments
+ Fulfillment[] calldata fulfillments,
+ address recipient
) external payable returns (Execution[] memory executions);
/**
@@ -333,9 +346,9 @@ interface ConsiderationInterface {
* @return cancelled A boolean indicating whether the supplied orders have
* been successfully cancelled.
*/
- function cancel(OrderComponents[] calldata orders)
- external
- returns (bool cancelled);
+ function cancel(
+ OrderComponents[] calldata orders
+ ) external returns (bool cancelled);
/**
* @notice Validate an arbitrary number of orders, thereby registering their
@@ -352,9 +365,9 @@ interface ConsiderationInterface {
* @return validated A boolean indicating whether the supplied orders have
* been successfully validated.
*/
- function validate(Order[] calldata orders)
- external
- returns (bool validated);
+ function validate(
+ Order[] calldata orders
+ ) external returns (bool validated);
/**
* @notice Cancel all orders from a given offerer with a given zone in bulk
@@ -365,6 +378,28 @@ interface ConsiderationInterface {
*/
function incrementCounter() external returns (uint256 newCounter);
+ /**
+ * @notice Fulfill an order offering an ERC721 token by supplying Ether (or
+ * the native token for the given chain) as consideration for the
+ * order. An arbitrary number of "additional recipients" may also be
+ * supplied which will each receive native tokens from the fulfiller
+ * as consideration. Note that this function costs less gas than
+ * `fulfillBasicOrder` due to the zero bytes in the function
+ * selector (0x00000000) which also results in earlier function
+ * dispatch.
+ *
+ * @param parameters Additional information on the fulfilled order. Note
+ * that the offerer must first approve this contract (or
+ * their preferred conduit if indicated by the order) for
+ * their offered ERC721 token to be transferred.
+ *
+ * @return fulfilled A boolean indicating whether the order has been
+ * successfully fulfilled.
+ */
+ function fulfillBasicOrder_efficient_6GL6yc(
+ BasicOrderParameters calldata parameters
+ ) external payable returns (bool fulfilled);
+
/**
* @notice Retrieve the order hash for a given order.
*
@@ -372,10 +407,9 @@ interface ConsiderationInterface {
*
* @return orderHash The order hash.
*/
- function getOrderHash(OrderComponents calldata order)
- external
- view
- returns (bytes32 orderHash);
+ function getOrderHash(
+ OrderComponents calldata order
+ ) external view returns (bytes32 orderHash);
/**
* @notice Retrieve the status of a given order by hash, including whether
@@ -394,7 +428,9 @@ interface ConsiderationInterface {
* @return totalSize The total size of the order that is either filled or
* unfilled (i.e. the "denominator").
*/
- function getOrderStatus(bytes32 orderHash)
+ function getOrderStatus(
+ bytes32 orderHash
+ )
external
view
returns (
@@ -411,10 +447,9 @@ interface ConsiderationInterface {
*
* @return counter The current counter.
*/
- function getCounter(address offerer)
- external
- view
- returns (uint256 counter);
+ function getCounter(
+ address offerer
+ ) external view returns (uint256 counter);
/**
* @notice Retrieve configuration information for this contract.
@@ -432,6 +467,10 @@ interface ConsiderationInterface {
address conduitController
);
+ function getContractOffererNonce(
+ address contractOfferer
+ ) external view returns (uint256 nonce);
+
/**
* @notice Retrieve the name of this contract.
*
diff --git a/contracts/interfaces/ContractOffererInterface.sol b/contracts/interfaces/ContractOffererInterface.sol
new file mode 100644
index 000000000..e38823cfa
--- /dev/null
+++ b/contracts/interfaces/ContractOffererInterface.sol
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ ReceivedItem,
+ Schema,
+ SpentItem
+} from "../lib/ConsiderationStructs.sol";
+
+/**
+ * @title ContractOffererInterface
+ * @notice Contains the minimum interfaces needed to interact with a contract
+ * offerer.
+ */
+interface ContractOffererInterface {
+ /**
+ * @dev Generates an order with the specified minimum and maximum spent
+ * items, and optional context (supplied as extraData).
+ *
+ * @param fulfiller The address of the fulfiller.
+ * @param minimumReceived The minimum items that the caller is willing to
+ * receive.
+ * @param maximumSpent The maximum items the caller is willing to spend.
+ * @param context Additional context of the order.
+ *
+ * @return offer A tuple containing the offer items.
+ * @return consideration A tuple containing the consideration items.
+ */
+ function generateOrder(
+ address fulfiller,
+ SpentItem[] calldata minimumReceived,
+ SpentItem[] calldata maximumSpent,
+ bytes calldata context // encoded based on the schemaID
+ )
+ external
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration);
+
+ /**
+ * @dev Ratifies an order with the specified offer, consideration, and
+ * optional context (supplied as extraData).
+ *
+ * @param offer The offer items.
+ * @param consideration The consideration items.
+ * @param context Additional context of the order.
+ * @param orderHashes The hashes to ratify.
+ * @param contractNonce The nonce of the contract.
+ *
+ * @return ratifyOrderMagicValue The magic value returned by the contract
+ * offerer.
+ */
+ function ratifyOrder(
+ SpentItem[] calldata offer,
+ ReceivedItem[] calldata consideration,
+ bytes calldata context, // encoded based on the schemaID
+ bytes32[] calldata orderHashes,
+ uint256 contractNonce
+ ) external returns (bytes4 ratifyOrderMagicValue);
+
+ /**
+ * @dev View function to preview an order generated in response to a minimum
+ * set of received items, maximum set of spent items, and context
+ * (supplied as extraData).
+ *
+ * @param caller The address of the caller (e.g. Seaport).
+ * @param fulfiller The address of the fulfiller (e.g. the account
+ * calling Seaport).
+ * @param minimumReceived The minimum items that the caller is willing to
+ * receive.
+ * @param maximumSpent The maximum items the caller is willing to spend.
+ * @param context Additional context of the order.
+ *
+ * @return offer A tuple containing the offer items.
+ * @return consideration A tuple containing the consideration items.
+ */
+ function previewOrder(
+ address caller,
+ address fulfiller,
+ SpentItem[] calldata minimumReceived,
+ SpentItem[] calldata maximumSpent,
+ bytes calldata context // encoded based on the schemaID
+ )
+ external
+ view
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration);
+
+ /**
+ * @dev Gets the metadata for this contract offerer.
+ *
+ * @return name The name of the contract offerer.
+ * @return schemas The schemas supported by the contract offerer.
+ */
+ function getSeaportMetadata()
+ external
+ view
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ );
+
+ // Additional functions and/or events based on implemented schemaIDs
+}
diff --git a/contracts/interfaces/CriteriaResolutionErrors.sol b/contracts/interfaces/CriteriaResolutionErrors.sol
index dfbe88c26..641032eec 100644
--- a/contracts/interfaces/CriteriaResolutionErrors.sol
+++ b/contracts/interfaces/CriteriaResolutionErrors.sol
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
+
+import { Side } from "../lib/ConsiderationEnums.sol";
/**
* @title CriteriaResolutionErrors
@@ -11,20 +13,34 @@ interface CriteriaResolutionErrors {
/**
* @dev Revert with an error when providing a criteria resolver that refers
* to an order that has not been supplied.
+ *
+ * @param side The side of the order that was not supplied.
*/
- error OrderCriteriaResolverOutOfRange();
+ error OrderCriteriaResolverOutOfRange(Side side);
/**
* @dev Revert with an error if an offer item still has unresolved criteria
* after applying all criteria resolvers.
+ *
+ * @param orderIndex The index of the order that contains the offer item.
+ * @param offerIndex The index of the offer item that still has unresolved
+ * criteria.
*/
- error UnresolvedOfferCriteria();
+ error UnresolvedOfferCriteria(uint256 orderIndex, uint256 offerIndex);
/**
* @dev Revert with an error if a consideration item still has unresolved
* criteria after applying all criteria resolvers.
+ *
+ * @param orderIndex The index of the order that contains the
+ * consideration item.
+ * @param considerationIndex The index of the consideration item that still
+ * has unresolved criteria.
*/
- error UnresolvedConsiderationCriteria();
+ error UnresolvedConsiderationCriteria(
+ uint256 orderIndex,
+ uint256 considerationIndex
+ );
/**
* @dev Revert with an error when providing a criteria resolver that refers
diff --git a/contracts/interfaces/EIP1271Interface.sol b/contracts/interfaces/EIP1271Interface.sol
index 21c9cf1c6..cbc54ea4f 100644
--- a/contracts/interfaces/EIP1271Interface.sol
+++ b/contracts/interfaces/EIP1271Interface.sol
@@ -1,9 +1,22 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
+/**
+ * @title EIP1271Interface
+ * @notice Interface for the EIP-1271 standard signature validation method for
+ * contracts.
+ */
interface EIP1271Interface {
- function isValidSignature(bytes32 digest, bytes calldata signature)
- external
- view
- returns (bytes4);
+ /**
+ * @dev Validates a smart contract signature
+ *
+ * @param digest bytes32 The digest of the data to be signed.
+ * @param signature bytes The signature of the data to be validated.
+ *
+ * @return bytes4 The magic value, if the signature is valid.
+ */
+ function isValidSignature(
+ bytes32 digest,
+ bytes calldata signature
+ ) external view returns (bytes4);
}
diff --git a/contracts/interfaces/FulfillmentApplicationErrors.sol b/contracts/interfaces/FulfillmentApplicationErrors.sol
index 776a0f6be..6cd5bab8a 100644
--- a/contracts/interfaces/FulfillmentApplicationErrors.sol
+++ b/contracts/interfaces/FulfillmentApplicationErrors.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
import { Side } from "../lib/ConsiderationEnums.sol";
@@ -28,8 +28,13 @@ interface FulfillmentApplicationErrors {
* @dev Revert with an error when the initial offer item named by a
* fulfillment component does not match the type, token, identifier,
* or conduit preference of the initial consideration item.
+ *
+ * @param fulfillmentIndex The index of the fulfillment component that
+ * does not match the initial offer item.
*/
- error MismatchedFulfillmentOfferAndConsiderationComponents();
+ error MismatchedFulfillmentOfferAndConsiderationComponents(
+ uint256 fulfillmentIndex
+ );
/**
* @dev Revert with an error when an order or item index are out of range
diff --git a/contracts/interfaces/IERC721Receiver.sol b/contracts/interfaces/IERC721Receiver.sol
new file mode 100644
index 000000000..01027abd1
--- /dev/null
+++ b/contracts/interfaces/IERC721Receiver.sol
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+/**
+ * @title ERC721 token receiver interface
+ * @dev Interface for any contract that wants to support safeTransfers
+ * from ERC721 asset contracts.
+ */
+interface IERC721Receiver {
+ /**
+ * @dev Whenever an ERC721 token is transferred to this contract via
+ * safeTransferFrom, this function is called.
+ *
+ * @param operator The address of the operator.
+ * @param from The address of the sender.
+ * @param tokenId The ID of the ERC721.
+ * @param data Additional data.
+ *
+ * @return bytes4 The magic value, unless throwing.
+ */
+ function onERC721Received(
+ address operator,
+ address from,
+ uint256 tokenId,
+ bytes calldata data
+ ) external returns (bytes4);
+}
diff --git a/contracts/interfaces/ImmutableCreate2FactoryInterface.sol b/contracts/interfaces/ImmutableCreate2FactoryInterface.sol
index a8a34a590..a1bd08a0e 100644
--- a/contracts/interfaces/ImmutableCreate2FactoryInterface.sol
+++ b/contracts/interfaces/ImmutableCreate2FactoryInterface.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
/**
* @title ImmutableCreate2FactoryInterface
@@ -30,10 +30,10 @@ interface ImmutableCreate2FactoryInterface {
*
* @return deploymentAddress Address of the contract that will be created.
*/
- function safeCreate2(bytes32 salt, bytes calldata initializationCode)
- external
- payable
- returns (address deploymentAddress);
+ function safeCreate2(
+ bytes32 salt,
+ bytes calldata initializationCode
+ ) external payable returns (address deploymentAddress);
/**
* @dev Compute the address of the contract that will be created when
@@ -53,10 +53,10 @@ interface ImmutableCreate2FactoryInterface {
* or the null address if a contract already
* exists at that address.
*/
- function findCreate2Address(bytes32 salt, bytes calldata initCode)
- external
- view
- returns (address deploymentAddress);
+ function findCreate2Address(
+ bytes32 salt,
+ bytes calldata initCode
+ ) external view returns (address deploymentAddress);
/**
* @dev Compute the address of the contract that will be created when
@@ -77,10 +77,10 @@ interface ImmutableCreate2FactoryInterface {
* or the null address if a contract already
* exists at that address.
*/
- function findCreate2AddressViaHash(bytes32 salt, bytes32 initCodeHash)
- external
- view
- returns (address deploymentAddress);
+ function findCreate2AddressViaHash(
+ bytes32 salt,
+ bytes32 initCodeHash
+ ) external view returns (address deploymentAddress);
/**
* @dev Determine if a contract has already been deployed by the factory to
@@ -90,8 +90,7 @@ interface ImmutableCreate2FactoryInterface {
*
* @return True if the contract has been deployed, false otherwise.
*/
- function hasBeenDeployed(address deploymentAddress)
- external
- view
- returns (bool);
+ function hasBeenDeployed(
+ address deploymentAddress
+ ) external view returns (bool);
}
diff --git a/contracts/interfaces/ReentrancyErrors.sol b/contracts/interfaces/ReentrancyErrors.sol
index 042654f5c..9e3685f1d 100644
--- a/contracts/interfaces/ReentrancyErrors.sol
+++ b/contracts/interfaces/ReentrancyErrors.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
/**
* @title ReentrancyErrors
diff --git a/contracts/interfaces/SeaportInterface.sol b/contracts/interfaces/SeaportInterface.sol
index 6593f8658..f44a3d0d7 100644
--- a/contracts/interfaces/SeaportInterface.sol
+++ b/contracts/interfaces/SeaportInterface.sol
@@ -1,27 +1,25 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-// prettier-ignore
import {
+ AdvancedOrder,
BasicOrderParameters,
- OrderComponents,
+ CriteriaResolver,
+ Execution,
Fulfillment,
FulfillmentComponent,
- Execution,
Order,
- AdvancedOrder,
- OrderStatus,
- CriteriaResolver
+ OrderComponents
} from "../lib/ConsiderationStructs.sol";
/**
* @title SeaportInterface
* @author 0age
- * @custom:version 1.1
- * @notice Seaport is a generalized ETH/ERC20/ERC721/ERC1155 marketplace. It
- * minimizes external calls to the greatest extent possible and provides
- * lightweight methods for common routes as well as more flexible
- * methods for composing advanced orders.
+ * @custom:version 1.2
+ * @notice Seaport is a generalized native token/ERC20/ERC721/ERC1155
+ * marketplace. It minimizes external calls to the greatest extent
+ * possible and provides lightweight methods for common routes as well
+ * as more flexible methods for composing advanced orders.
*
* @dev SeaportInterface contains all external function interfaces for Seaport.
*/
@@ -41,10 +39,9 @@ interface SeaportInterface {
* @return fulfilled A boolean indicating whether the order has been
* successfully fulfilled.
*/
- function fulfillBasicOrder(BasicOrderParameters calldata parameters)
- external
- payable
- returns (bool fulfilled);
+ function fulfillBasicOrder(
+ BasicOrderParameters calldata parameters
+ ) external payable returns (bool fulfilled);
/**
* @notice Fulfill an order with an arbitrary number of items for offer and
@@ -68,10 +65,10 @@ interface SeaportInterface {
* @return fulfilled A boolean indicating whether the order has been
* successfully fulfilled.
*/
- function fulfillOrder(Order calldata order, bytes32 fulfillerConduitKey)
- external
- payable
- returns (bool fulfilled);
+ function fulfillOrder(
+ Order calldata order,
+ bytes32 fulfillerConduitKey
+ ) external payable returns (bool fulfilled);
/**
* @notice Fill an order, fully or partially, with an arbitrary number of
@@ -161,7 +158,9 @@ interface SeaportInterface {
* returned boolean was fulfillable or not.
* @return executions An array of elements indicating the sequence of
* transfers performed as part of matching the given
- * orders.
+ * orders. Note that unspent offer item amounts or
+ * native tokens will not be reflected as part of
+ * this array.
*/
function fulfillAvailableOrders(
Order[] calldata orders,
@@ -236,7 +235,9 @@ interface SeaportInterface {
* returned boolean was fulfillable or not.
* @return executions An array of elements indicating the sequence of
* transfers performed as part of matching the given
- * orders.
+ * orders. Note that unspent offer item amounts or
+ * native tokens will not be reflected as part of
+ * this array.
*/
function fulfillAvailableAdvancedOrders(
AdvancedOrder[] calldata advancedOrders,
@@ -253,11 +254,13 @@ interface SeaportInterface {
/**
* @notice Match an arbitrary number of orders, each with an arbitrary
- * number of items for offer and consideration along with as set of
+ * number of items for offer and consideration along with a set of
* fulfillments allocating offer components to consideration
* components. Note that this function does not support
* criteria-based or partial filling of orders (though filling the
- * remainder of a partially-filled order is supported).
+ * remainder of a partially-filled order is supported). Any unspent
+ * offer item amounts or native tokens will be transferred to the
+ * caller.
*
* @param orders The orders to match. Note that both the offerer and
* fulfiller on each order must first approve this
@@ -272,7 +275,9 @@ interface SeaportInterface {
*
* @return executions An array of elements indicating the sequence of
* transfers performed as part of matching the given
- * orders.
+ * orders. Note that unspent offer item amounts or
+ * native tokens will not be reflected as part of this
+ * array.
*/
function matchOrders(
Order[] calldata orders,
@@ -284,7 +289,10 @@ interface SeaportInterface {
* arbitrary number of items for offer and consideration, supplying
* criteria resolvers containing specific token identifiers and
* associated proofs as well as fulfillments allocating offer
- * components to consideration components.
+ * components to consideration components. Any unspent offer item
+ * amounts will be transferred to the designated recipient (with the
+ * null address signifying to use the caller) and any unspent native
+ * tokens will be returned to the caller.
*
* @param orders The advanced orders to match. Note that both the
* offerer and fulfiller on each order must first
@@ -310,15 +318,20 @@ interface SeaportInterface {
* to consideration components. Note that each
* consideration component must be fully met in
* order for the match operation to be valid.
+ * @param recipient The intended recipient for all unspent offer
+ * item amounts, or the caller if the null address
+ * is supplied.
*
* @return executions An array of elements indicating the sequence of
* transfers performed as part of matching the given
- * orders.
+ * orders. Note that unspent offer item amounts or native
+ * tokens will not be reflected as part of this array.
*/
function matchAdvancedOrders(
AdvancedOrder[] calldata orders,
CriteriaResolver[] calldata criteriaResolvers,
- Fulfillment[] calldata fulfillments
+ Fulfillment[] calldata fulfillments,
+ address recipient
) external payable returns (Execution[] memory executions);
/**
@@ -332,9 +345,9 @@ interface SeaportInterface {
* @return cancelled A boolean indicating whether the supplied orders have
* been successfully cancelled.
*/
- function cancel(OrderComponents[] calldata orders)
- external
- returns (bool cancelled);
+ function cancel(
+ OrderComponents[] calldata orders
+ ) external returns (bool cancelled);
/**
* @notice Validate an arbitrary number of orders, thereby registering their
@@ -351,9 +364,9 @@ interface SeaportInterface {
* @return validated A boolean indicating whether the supplied orders have
* been successfully validated.
*/
- function validate(Order[] calldata orders)
- external
- returns (bool validated);
+ function validate(
+ Order[] calldata orders
+ ) external returns (bool validated);
/**
* @notice Cancel all orders from a given offerer with a given zone in bulk
@@ -364,6 +377,28 @@ interface SeaportInterface {
*/
function incrementCounter() external returns (uint256 newCounter);
+ /**
+ * @notice Fulfill an order offering an ERC721 token by supplying Ether (or
+ * the native token for the given chain) as consideration for the
+ * order. An arbitrary number of "additional recipients" may also be
+ * supplied which will each receive native tokens from the fulfiller
+ * as consideration. Note that this function costs less gas than
+ * `fulfillBasicOrder` due to the zero bytes in the function
+ * selector (0x00000000) which also results in earlier function
+ * dispatch.
+ *
+ * @param parameters Additional information on the fulfilled order. Note
+ * that the offerer must first approve this contract (or
+ * their preferred conduit if indicated by the order) for
+ * their offered ERC721 token to be transferred.
+ *
+ * @return fulfilled A boolean indicating whether the order has been
+ * successfully fulfilled.
+ */
+ function fulfillBasicOrder_efficient_6GL6yc(
+ BasicOrderParameters calldata parameters
+ ) external payable returns (bool fulfilled);
+
/**
* @notice Retrieve the order hash for a given order.
*
@@ -371,10 +406,9 @@ interface SeaportInterface {
*
* @return orderHash The order hash.
*/
- function getOrderHash(OrderComponents calldata order)
- external
- view
- returns (bytes32 orderHash);
+ function getOrderHash(
+ OrderComponents calldata order
+ ) external view returns (bytes32 orderHash);
/**
* @notice Retrieve the status of a given order by hash, including whether
@@ -393,7 +427,9 @@ interface SeaportInterface {
* @return totalSize The total size of the order that is either filled or
* unfilled (i.e. the "denominator").
*/
- function getOrderStatus(bytes32 orderHash)
+ function getOrderStatus(
+ bytes32 orderHash
+ )
external
view
returns (
@@ -410,10 +446,9 @@ interface SeaportInterface {
*
* @return counter The current counter.
*/
- function getCounter(address offerer)
- external
- view
- returns (uint256 counter);
+ function getCounter(
+ address offerer
+ ) external view returns (uint256 counter);
/**
* @notice Retrieve configuration information for this contract.
@@ -431,6 +466,10 @@ interface SeaportInterface {
address conduitController
);
+ function getContractOffererNonce(
+ address contractOfferer
+ ) external view returns (uint256 nonce);
+
/**
* @notice Retrieve the name of this contract.
*
diff --git a/contracts/interfaces/SignatureVerificationErrors.sol b/contracts/interfaces/SignatureVerificationErrors.sol
index 90a2a4c97..f0fdd04c1 100644
--- a/contracts/interfaces/SignatureVerificationErrors.sol
+++ b/contracts/interfaces/SignatureVerificationErrors.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
/**
* @title SignatureVerificationErrors
diff --git a/contracts/interfaces/TokenTransferrerErrors.sol b/contracts/interfaces/TokenTransferrerErrors.sol
index 21887650b..954d85f12 100644
--- a/contracts/interfaces/TokenTransferrerErrors.sol
+++ b/contracts/interfaces/TokenTransferrerErrors.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
/**
* @title TokenTransferrerErrors
@@ -8,8 +8,10 @@ interface TokenTransferrerErrors {
/**
* @dev Revert with an error when an ERC721 transfer with amount other than
* one is attempted.
+ *
+ * @param amount The amount of the ERC721 tokens to transfer.
*/
- error InvalidERC721TransferAmount();
+ error InvalidERC721TransferAmount(uint256 amount);
/**
* @dev Revert with an error when attempting to fulfill an order where an
diff --git a/contracts/interfaces/TransferHelperErrors.sol b/contracts/interfaces/TransferHelperErrors.sol
new file mode 100644
index 000000000..88ffc106a
--- /dev/null
+++ b/contracts/interfaces/TransferHelperErrors.sol
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+/**
+ * @title TransferHelperErrors
+ */
+interface TransferHelperErrors {
+ /**
+ * @dev Revert with an error when attempting to execute transfers with a
+ * NATIVE itemType.
+ */
+ error InvalidItemType();
+
+ /**
+ * @dev Revert with an error when an ERC721 transfer with amount other than
+ * one is attempted.
+ *
+ * @param amount The amount of the ERC721 tokens to transfer.
+ */
+ error InvalidERC721TransferAmount(uint256 amount);
+
+ /**
+ * @dev Revert with an error when attempting to execute an ERC721 transfer
+ * to an invalid recipient.
+ */
+ error InvalidERC721Recipient(address recipient);
+
+ /**
+ * @dev Revert with an error when a call to an ERC721 receiver reverts with
+ * bytes data.
+ */
+ error ERC721ReceiverErrorRevertBytes(
+ bytes reason,
+ address receiver,
+ address sender,
+ uint256 identifier
+ );
+
+ /**
+ * @dev Revert with an error when a call to an ERC721 receiver reverts with
+ * string reason.
+ */
+ error ERC721ReceiverErrorRevertString(
+ string reason,
+ address receiver,
+ address sender,
+ uint256 identifier
+ );
+
+ /**
+ * @dev Revert with an error when an ERC20 token has an invalid identifier.
+ */
+ error InvalidERC20Identifier();
+
+ /**
+ * @dev Revert with an error if the recipient is the zero address.
+ */
+ error RecipientCannotBeZeroAddress();
+
+ /**
+ * @dev Revert with an error when attempting to fill an order referencing an
+ * invalid conduit (i.e. one that has not been deployed).
+ */
+ error InvalidConduit(bytes32 conduitKey, address conduit);
+
+ /**
+ * @dev Revert with an error when a call to a conduit reverts with a
+ * reason string.
+ */
+ error ConduitErrorRevertString(
+ string reason,
+ bytes32 conduitKey,
+ address conduit
+ );
+
+ /**
+ * @dev Revert with an error when a call to a conduit reverts with bytes
+ * data.
+ */
+ error ConduitErrorRevertBytes(
+ bytes reason,
+ bytes32 conduitKey,
+ address conduit
+ );
+}
diff --git a/contracts/interfaces/TransferHelperInterface.sol b/contracts/interfaces/TransferHelperInterface.sol
index c579868fd..c9478f32c 100644
--- a/contracts/interfaces/TransferHelperInterface.sol
+++ b/contracts/interfaces/TransferHelperInterface.sol
@@ -1,25 +1,19 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-import { TransferHelperItem } from "../helpers/TransferHelperStructs.sol";
+import {
+ TransferHelperItemsWithRecipient
+} from "../helpers/TransferHelperStructs.sol";
interface TransferHelperInterface {
- /**
- * @dev Revert with an error when attempting to execute transfers with a
- * NATIVE itemType.
- */
- error InvalidItemType();
-
/**
* @notice Transfer multiple items to a single recipient.
*
* @param items The items to transfer.
- * @param recipient The address the items should be transferred to.
* @param conduitKey The key of the conduit performing the bulk transfer.
*/
function bulkTransfer(
- TransferHelperItem[] calldata items,
- address recipient,
+ TransferHelperItemsWithRecipient[] calldata items,
bytes32 conduitKey
) external returns (bytes4);
}
diff --git a/contracts/interfaces/ZoneInteractionErrors.sol b/contracts/interfaces/ZoneInteractionErrors.sol
index f7b271c4c..786a2ca21 100644
--- a/contracts/interfaces/ZoneInteractionErrors.sol
+++ b/contracts/interfaces/ZoneInteractionErrors.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
/**
* @title ZoneInteractionErrors
@@ -11,9 +11,19 @@ interface ZoneInteractionErrors {
* @dev Revert with an error when attempting to fill an order that specifies
* a restricted submitter as its order type when not submitted by
* either the offerer or the order's zone or approved as valid by the
- * zone in question via a staticcall to `isValidOrder`.
+ * zone in question via a call to `isValidOrder`.
*
* @param orderHash The order hash for the invalid restricted order.
*/
error InvalidRestrictedOrder(bytes32 orderHash);
+
+ /**
+ * @dev Revert with an error when attempting to fill a contract order that
+ * fails to generate an order successfully, that does not adhere to the
+ * requirements for minimum spent or maximum received supplied by the
+ * fulfiller, or that fails the post-execution `ratifyOrder` check..
+ *
+ * @param orderHash The order hash for the invalid contract order.
+ */
+ error InvalidContractOrder(bytes32 orderHash);
}
diff --git a/contracts/interfaces/ZoneInterface.sol b/contracts/interfaces/ZoneInterface.sol
index 94dd037f2..d64a5d419 100644
--- a/contracts/interfaces/ZoneInterface.sol
+++ b/contracts/interfaces/ZoneInterface.sol
@@ -1,27 +1,37 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-// prettier-ignore
-import {
- AdvancedOrder,
- CriteriaResolver
-} from "../lib/ConsiderationStructs.sol";
+import { ZoneParameters, Schema } from "../lib/ConsiderationStructs.sol";
+/**
+ * @title ZoneInterface
+ * @notice Contains functions exposed by a zone.
+ */
interface ZoneInterface {
- // Called by Consideration whenever extraData is not provided by the caller.
- function isValidOrder(
- bytes32 orderHash,
- address caller,
- address offerer,
- bytes32 zoneHash
- ) external view returns (bytes4 validOrderMagicValue);
+ /**
+ * @dev Validates an order.
+ *
+ * @param zoneParameters The context about the order fulfillment and any
+ * supplied extraData.
+ *
+ * @return validOrderMagicValue The magic value that indicates a valid
+ * order.
+ */
+ function validateOrder(
+ ZoneParameters calldata zoneParameters
+ ) external returns (bytes4 validOrderMagicValue);
- // Called by Consideration whenever any extraData is provided by the caller.
- function isValidOrderIncludingExtraData(
- bytes32 orderHash,
- address caller,
- AdvancedOrder calldata order,
- bytes32[] calldata priorOrderHashes,
- CriteriaResolver[] calldata criteriaResolvers
- ) external view returns (bytes4 validOrderMagicValue);
+ /**
+ * @dev Returns the metadata for this zone.
+ *
+ * @return name The name of the zone.
+ * @return schemas The schemas that the zone implements.
+ */
+ function getSeaportMetadata()
+ external
+ view
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ );
}
diff --git a/contracts/interfaces/legacy/ConsiderationInterface1_1.sol b/contracts/interfaces/legacy/ConsiderationInterface1_1.sol
new file mode 100644
index 000000000..1590472bc
--- /dev/null
+++ b/contracts/interfaces/legacy/ConsiderationInterface1_1.sol
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.7;
+
+import {
+ BasicOrderParameters,
+ OrderComponents,
+ Fulfillment,
+ FulfillmentComponent,
+ Execution,
+ Order,
+ AdvancedOrder,
+ OrderStatus,
+ CriteriaResolver
+} from "../../lib/ConsiderationStructs.sol";
+
+/**
+ * @title ConsiderationInterface1.1
+ * @author 0age
+ * @custom:version 1.1
+ * *
+ * @dev This is the legacy ConsiderationInterface from Seaport 1.1
+ */
+interface ConsiderationInterface1_1 {
+ /**
+ * @notice Fulfill an order offering an ERC721 token by supplying Ether (or
+ * the native token for the given chain) as consideration for the
+ * order. An arbitrary number of "additional recipients" may also be
+ * supplied which will each receive native tokens from the fulfiller
+ * as consideration.
+ *
+ * @param parameters Additional information on the fulfilled order. Note
+ * that the offerer must first approve this contract (or
+ * their preferred conduit if indicated by the order) for
+ * their offered ERC721 token to be transferred.
+ *
+ * @return fulfilled A boolean indicating whether the order has been
+ * successfully fulfilled.
+ */
+ function fulfillBasicOrder(
+ BasicOrderParameters calldata parameters
+ ) external payable returns (bool fulfilled);
+
+ /**
+ * @notice Fulfill an order with an arbitrary number of items for offer and
+ * consideration. Note that this function does not support
+ * criteria-based orders or partial filling of orders (though
+ * filling the remainder of a partially-filled order is supported).
+ *
+ * @param order The order to fulfill. Note that both the
+ * offerer and the fulfiller must first approve
+ * this contract (or the corresponding conduit if
+ * indicated) to transfer any relevant tokens on
+ * their behalf and that contracts must implement
+ * `onERC1155Received` to receive ERC1155 tokens
+ * as consideration.
+ * @param fulfillerConduitKey A bytes32 value indicating what conduit, if
+ * any, to source the fulfiller's token approvals
+ * from. The zero hash signifies that no conduit
+ * should be used, with direct approvals set on
+ * Consideration.
+ *
+ * @return fulfilled A boolean indicating whether the order has been
+ * successfully fulfilled.
+ */
+ function fulfillOrder(
+ Order calldata order,
+ bytes32 fulfillerConduitKey
+ ) external payable returns (bool fulfilled);
+
+ /**
+ * @notice Fill an order, fully or partially, with an arbitrary number of
+ * items for offer and consideration alongside criteria resolvers
+ * containing specific token identifiers and associated proofs.
+ *
+ * @param advancedOrder The order to fulfill along with the fraction
+ * of the order to attempt to fill. Note that
+ * both the offerer and the fulfiller must first
+ * approve this contract (or their preferred
+ * conduit if indicated by the order) to transfer
+ * any relevant tokens on their behalf and that
+ * contracts must implement `onERC1155Received`
+ * to receive ERC1155 tokens as consideration.
+ * Also note that all offer and consideration
+ * components must have no remainder after
+ * multiplication of the respective amount with
+ * the supplied fraction for the partial fill to
+ * be considered valid.
+ * @param criteriaResolvers An array where each element contains a
+ * reference to a specific offer or
+ * consideration, a token identifier, and a proof
+ * that the supplied token identifier is
+ * contained in the merkle root held by the item
+ * in question's criteria element. Note that an
+ * empty criteria indicates that any
+ * (transferable) token identifier on the token
+ * in question is valid and that no associated
+ * proof needs to be supplied.
+ * @param fulfillerConduitKey A bytes32 value indicating what conduit, if
+ * any, to source the fulfiller's token approvals
+ * from. The zero hash signifies that no conduit
+ * should be used, with direct approvals set on
+ * Consideration.
+ * @param recipient The intended recipient for all received items,
+ * with `address(0)` indicating that the caller
+ * should receive the items.
+ *
+ * @return fulfilled A boolean indicating whether the order has been
+ * successfully fulfilled.
+ */
+ function fulfillAdvancedOrder(
+ AdvancedOrder calldata advancedOrder,
+ CriteriaResolver[] calldata criteriaResolvers,
+ bytes32 fulfillerConduitKey,
+ address recipient
+ ) external payable returns (bool fulfilled);
+
+ /**
+ * @notice Attempt to fill a group of orders, each with an arbitrary number
+ * of items for offer and consideration. Any order that is not
+ * currently active, has already been fully filled, or has been
+ * cancelled will be omitted. Remaining offer and consideration
+ * items will then be aggregated where possible as indicated by the
+ * supplied offer and consideration component arrays and aggregated
+ * items will be transferred to the fulfiller or to each intended
+ * recipient, respectively. Note that a failing item transfer or an
+ * issue with order formatting will cause the entire batch to fail.
+ * Note that this function does not support criteria-based orders or
+ * partial filling of orders (though filling the remainder of a
+ * partially-filled order is supported).
+ *
+ * @param orders The orders to fulfill. Note that both
+ * the offerer and the fulfiller must first
+ * approve this contract (or the
+ * corresponding conduit if indicated) to
+ * transfer any relevant tokens on their
+ * behalf and that contracts must implement
+ * `onERC1155Received` to receive ERC1155
+ * tokens as consideration.
+ * @param offerFulfillments An array of FulfillmentComponent arrays
+ * indicating which offer items to attempt
+ * to aggregate when preparing executions.
+ * @param considerationFulfillments An array of FulfillmentComponent arrays
+ * indicating which consideration items to
+ * attempt to aggregate when preparing
+ * executions.
+ * @param fulfillerConduitKey A bytes32 value indicating what conduit,
+ * if any, to source the fulfiller's token
+ * approvals from. The zero hash signifies
+ * that no conduit should be used, with
+ * direct approvals set on this contract.
+ * @param maximumFulfilled The maximum number of orders to fulfill.
+ *
+ * @return availableOrders An array of booleans indicating if each order
+ * with an index corresponding to the index of the
+ * returned boolean was fulfillable or not.
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function fulfillAvailableOrders(
+ Order[] calldata orders,
+ FulfillmentComponent[][] calldata offerFulfillments,
+ FulfillmentComponent[][] calldata considerationFulfillments,
+ bytes32 fulfillerConduitKey,
+ uint256 maximumFulfilled
+ )
+ external
+ payable
+ returns (bool[] memory availableOrders, Execution[] memory executions);
+
+ /**
+ * @notice Attempt to fill a group of orders, fully or partially, with an
+ * arbitrary number of items for offer and consideration per order
+ * alongside criteria resolvers containing specific token
+ * identifiers and associated proofs. Any order that is not
+ * currently active, has already been fully filled, or has been
+ * cancelled will be omitted. Remaining offer and consideration
+ * items will then be aggregated where possible as indicated by the
+ * supplied offer and consideration component arrays and aggregated
+ * items will be transferred to the fulfiller or to each intended
+ * recipient, respectively. Note that a failing item transfer or an
+ * issue with order formatting will cause the entire batch to fail.
+ *
+ * @param advancedOrders The orders to fulfill along with the
+ * fraction of those orders to attempt to
+ * fill. Note that both the offerer and the
+ * fulfiller must first approve this
+ * contract (or their preferred conduit if
+ * indicated by the order) to transfer any
+ * relevant tokens on their behalf and that
+ * contracts must implement
+ * `onERC1155Received` to enable receipt of
+ * ERC1155 tokens as consideration. Also
+ * note that all offer and consideration
+ * components must have no remainder after
+ * multiplication of the respective amount
+ * with the supplied fraction for an
+ * order's partial fill amount to be
+ * considered valid.
+ * @param criteriaResolvers An array where each element contains a
+ * reference to a specific offer or
+ * consideration, a token identifier, and a
+ * proof that the supplied token identifier
+ * is contained in the merkle root held by
+ * the item in question's criteria element.
+ * Note that an empty criteria indicates
+ * that any (transferable) token
+ * identifier on the token in question is
+ * valid and that no associated proof needs
+ * to be supplied.
+ * @param offerFulfillments An array of FulfillmentComponent arrays
+ * indicating which offer items to attempt
+ * to aggregate when preparing executions.
+ * @param considerationFulfillments An array of FulfillmentComponent arrays
+ * indicating which consideration items to
+ * attempt to aggregate when preparing
+ * executions.
+ * @param fulfillerConduitKey A bytes32 value indicating what conduit,
+ * if any, to source the fulfiller's token
+ * approvals from. The zero hash signifies
+ * that no conduit should be used, with
+ * direct approvals set on this contract.
+ * @param recipient The intended recipient for all received
+ * items, with `address(0)` indicating that
+ * the caller should receive the items.
+ * @param maximumFulfilled The maximum number of orders to fulfill.
+ *
+ * @return availableOrders An array of booleans indicating if each order
+ * with an index corresponding to the index of the
+ * returned boolean was fulfillable or not.
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function fulfillAvailableAdvancedOrders(
+ AdvancedOrder[] calldata advancedOrders,
+ CriteriaResolver[] calldata criteriaResolvers,
+ FulfillmentComponent[][] calldata offerFulfillments,
+ FulfillmentComponent[][] calldata considerationFulfillments,
+ bytes32 fulfillerConduitKey,
+ address recipient,
+ uint256 maximumFulfilled
+ )
+ external
+ payable
+ returns (bool[] memory availableOrders, Execution[] memory executions);
+
+ /**
+ * @notice Match an arbitrary number of orders, each with an arbitrary
+ * number of items for offer and consideration along with as set of
+ * fulfillments allocating offer components to consideration
+ * components. Note that this function does not support
+ * criteria-based or partial filling of orders (though filling the
+ * remainder of a partially-filled order is supported).
+ *
+ * @param orders The orders to match. Note that both the offerer and
+ * fulfiller on each order must first approve this
+ * contract (or their conduit if indicated by the order)
+ * to transfer any relevant tokens on their behalf and
+ * each consideration recipient must implement
+ * `onERC1155Received` to enable ERC1155 token receipt.
+ * @param fulfillments An array of elements allocating offer components to
+ * consideration components. Note that each
+ * consideration component must be fully met for the
+ * match operation to be valid.
+ *
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function matchOrders(
+ Order[] calldata orders,
+ Fulfillment[] calldata fulfillments
+ ) external payable returns (Execution[] memory executions);
+
+ /**
+ * @notice Match an arbitrary number of full or partial orders, each with an
+ * arbitrary number of items for offer and consideration, supplying
+ * criteria resolvers containing specific token identifiers and
+ * associated proofs as well as fulfillments allocating offer
+ * components to consideration components.
+ *
+ * @param orders The advanced orders to match. Note that both the
+ * offerer and fulfiller on each order must first
+ * approve this contract (or a preferred conduit if
+ * indicated by the order) to transfer any relevant
+ * tokens on their behalf and each consideration
+ * recipient must implement `onERC1155Received` in
+ * order to receive ERC1155 tokens. Also note that
+ * the offer and consideration components for each
+ * order must have no remainder after multiplying
+ * the respective amount with the supplied fraction
+ * in order for the group of partial fills to be
+ * considered valid.
+ * @param criteriaResolvers An array where each element contains a reference
+ * to a specific order as well as that order's
+ * offer or consideration, a token identifier, and
+ * a proof that the supplied token identifier is
+ * contained in the order's merkle root. Note that
+ * an empty root indicates that any (transferable)
+ * token identifier is valid and that no associated
+ * proof needs to be supplied.
+ * @param fulfillments An array of elements allocating offer components
+ * to consideration components. Note that each
+ * consideration component must be fully met in
+ * order for the match operation to be valid.
+ *
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function matchAdvancedOrders(
+ AdvancedOrder[] calldata orders,
+ CriteriaResolver[] calldata criteriaResolvers,
+ Fulfillment[] calldata fulfillments
+ ) external payable returns (Execution[] memory executions);
+
+ /**
+ * @notice Cancel an arbitrary number of orders. Note that only the offerer
+ * or the zone of a given order may cancel it. Callers should ensure
+ * that the intended order was cancelled by calling `getOrderStatus`
+ * and confirming that `isCancelled` returns `true`.
+ *
+ * @param orders The orders to cancel.
+ *
+ * @return cancelled A boolean indicating whether the supplied orders have
+ * been successfully cancelled.
+ */
+ function cancel(
+ OrderComponents[] calldata orders
+ ) external returns (bool cancelled);
+
+ /**
+ * @notice Validate an arbitrary number of orders, thereby registering their
+ * signatures as valid and allowing the fulfiller to skip signature
+ * verification on fulfillment. Note that validated orders may still
+ * be unfulfillable due to invalid item amounts or other factors;
+ * callers should determine whether validated orders are fulfillable
+ * by simulating the fulfillment call prior to execution. Also note
+ * that anyone can validate a signed order, but only the offerer can
+ * validate an order without supplying a signature.
+ *
+ * @param orders The orders to validate.
+ *
+ * @return validated A boolean indicating whether the supplied orders have
+ * been successfully validated.
+ */
+ function validate(
+ Order[] calldata orders
+ ) external returns (bool validated);
+
+ /**
+ * @notice Cancel all orders from a given offerer with a given zone in bulk
+ * by incrementing a counter. Note that only the offerer may
+ * increment the counter.
+ *
+ * @return newCounter The new counter.
+ */
+ function incrementCounter() external returns (uint256 newCounter);
+
+ /**
+ * @notice Retrieve the order hash for a given order.
+ *
+ * @param order The components of the order.
+ *
+ * @return orderHash The order hash.
+ */
+ function getOrderHash(
+ OrderComponents calldata order
+ ) external view returns (bytes32 orderHash);
+
+ /**
+ * @notice Retrieve the status of a given order by hash, including whether
+ * the order has been cancelled or validated and the fraction of the
+ * order that has been filled.
+ *
+ * @param orderHash The order hash in question.
+ *
+ * @return isValidated A boolean indicating whether the order in question
+ * has been validated (i.e. previously approved or
+ * partially filled).
+ * @return isCancelled A boolean indicating whether the order in question
+ * has been cancelled.
+ * @return totalFilled The total portion of the order that has been filled
+ * (i.e. the "numerator").
+ * @return totalSize The total size of the order that is either filled or
+ * unfilled (i.e. the "denominator").
+ */
+ function getOrderStatus(
+ bytes32 orderHash
+ )
+ external
+ view
+ returns (
+ bool isValidated,
+ bool isCancelled,
+ uint256 totalFilled,
+ uint256 totalSize
+ );
+
+ /**
+ * @notice Retrieve the current counter for a given offerer.
+ *
+ * @param offerer The offerer in question.
+ *
+ * @return counter The current counter.
+ */
+ function getCounter(
+ address offerer
+ ) external view returns (uint256 counter);
+
+ /**
+ * @notice Retrieve configuration information for this contract.
+ *
+ * @return version The contract version.
+ * @return domainSeparator The domain separator for this contract.
+ * @return conduitController The conduit Controller set for this contract.
+ */
+ function information()
+ external
+ view
+ returns (
+ string memory version,
+ bytes32 domainSeparator,
+ address conduitController
+ );
+
+ /**
+ * @notice Retrieve the name of this contract.
+ *
+ * @return contractName The name of this contract.
+ */
+ function name() external view returns (string memory contractName);
+}
diff --git a/contracts/interfaces/legacy/ZoneInterface1_1.sol b/contracts/interfaces/legacy/ZoneInterface1_1.sol
new file mode 100644
index 000000000..75e1d289a
--- /dev/null
+++ b/contracts/interfaces/legacy/ZoneInterface1_1.sol
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.7;
+
+import {
+ AdvancedOrder,
+ CriteriaResolver
+} from "../../lib/ConsiderationStructs.sol";
+
+/**
+ * @title ZoneInterface1.1
+ * @author 0age
+ * @custom:version 1.1
+ *
+ * @dev This is the legacy ZoneInterface from Seaport 1.1
+ */
+interface ZoneInterface1_1 {
+ // Called by Consideration whenever extraData is not provided by the caller.
+ function isValidOrder(
+ bytes32 orderHash,
+ address caller,
+ address offerer,
+ bytes32 zoneHash
+ ) external view returns (bytes4 validOrderMagicValue);
+
+ // Called by Consideration whenever any extraData is provided by the caller.
+ function isValidOrderIncludingExtraData(
+ bytes32 orderHash,
+ address caller,
+ AdvancedOrder calldata order,
+ bytes32[] calldata priorOrderHashes,
+ CriteriaResolver[] calldata criteriaResolvers
+ ) external view returns (bytes4 validOrderMagicValue);
+}
diff --git a/contracts/lib/AmountDeriver.sol b/contracts/lib/AmountDeriver.sol
index f35b2cb73..e03514af8 100644
--- a/contracts/lib/AmountDeriver.sol
+++ b/contracts/lib/AmountDeriver.sol
@@ -1,12 +1,15 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
-// prettier-ignore
import {
AmountDerivationErrors
} from "../interfaces/AmountDerivationErrors.sol";
-import "./ConsiderationConstants.sol";
+import {
+ Error_selector_offset,
+ InexactFraction_error_length,
+ InexactFraction_error_selector
+} from "./ConsiderationErrorConstants.sol";
/**
* @title AmountDeriver
@@ -123,8 +126,11 @@ contract AmountDeriver is AmountDerivationErrors {
// Ensure new value contains no remainder via mulmod operator.
// Credit to @hrkrshnn + @axic for proposing this optimal solution.
if mulmod(value, numerator, denominator) {
- mstore(0, InexactFraction_error_signature)
- revert(0, InexactFraction_error_len)
+ // Store left-padded selector with push4, mem[28:32] = selector
+ mstore(0, InexactFraction_error_selector)
+
+ // revert(abi.encodeWithSignature("InexactFraction()"))
+ revert(Error_selector_offset, InexactFraction_error_length)
}
}
diff --git a/contracts/lib/Assertions.sol b/contracts/lib/Assertions.sol
index ec10a11fe..d234f032e 100644
--- a/contracts/lib/Assertions.sol
+++ b/contracts/lib/Assertions.sol
@@ -1,18 +1,44 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
import { OrderParameters } from "./ConsiderationStructs.sol";
import { GettersAndDerivers } from "./GettersAndDerivers.sol";
-// prettier-ignore
import {
TokenTransferrerErrors
} from "../interfaces/TokenTransferrerErrors.sol";
import { CounterManager } from "./CounterManager.sol";
-import "./ConsiderationConstants.sol";
+import {
+ AdditionalRecipient_size_shift,
+ AddressDirtyUpperBitThreshold,
+ BasicOrder_additionalRecipients_head_cdPtr,
+ BasicOrder_additionalRecipients_head_ptr,
+ BasicOrder_additionalRecipients_length_cdPtr,
+ BasicOrder_basicOrderType_cdPtr,
+ BasicOrder_basicOrderType_range,
+ BasicOrder_considerationToken_cdPtr,
+ BasicOrder_offerer_cdPtr,
+ BasicOrder_offerToken_cdPtr,
+ BasicOrder_parameters_cdPtr,
+ BasicOrder_parameters_ptr,
+ BasicOrder_signature_cdPtr,
+ BasicOrder_signature_ptr,
+ BasicOrder_zone_cdPtr
+} from "./ConsiderationConstants.sol";
+
+import {
+ Error_selector_offset,
+ MissingItemAmount_error_length,
+ MissingItemAmount_error_selector
+} from "./ConsiderationErrorConstants.sol";
+
+import {
+ _revertInvalidBasicOrderParameterEncoding,
+ _revertMissingOriginalConsiderationItems
+} from "./ConsiderationErrors.sol";
/**
* @title Assertions
@@ -33,9 +59,9 @@ contract Assertions is
* that may optionally be used to transfer approved
* ERC20/721/1155 tokens.
*/
- constructor(address conduitController)
- GettersAndDerivers(conduitController)
- {}
+ constructor(
+ address conduitController
+ ) GettersAndDerivers(conduitController) {}
/**
* @dev Internal view function to ensure that the supplied consideration
@@ -81,7 +107,7 @@ contract Assertions is
) internal pure {
// Ensure supplied consideration array length is not less than original.
if (suppliedConsiderationItemTotal < originalConsiderationItemTotal) {
- revert MissingOriginalConsiderationItems();
+ _revertMissingOriginalConsiderationItems();
}
}
@@ -92,9 +118,14 @@ contract Assertions is
* @param amount The amount to check.
*/
function _assertNonZeroAmount(uint256 amount) internal pure {
- // Revert if the supplied amount is equal to zero.
- if (amount == 0) {
- revert MissingItemAmount();
+ assembly {
+ if iszero(amount) {
+ // Store left-padded selector with push4, mem[28:32] = selector
+ mstore(0, MissingItemAmount_error_selector)
+
+ // revert(abi.encodeWithSignature("MissingItemAmount()"))
+ revert(Error_selector_offset, MissingItemAmount_error_length)
+ }
}
}
@@ -119,54 +150,79 @@ contract Assertions is
* 2. Additional recipients arr offset == 0x240
* 3. Signature offset == 0x260 + (recipients.length * 0x40)
* 4. BasicOrderType between 0 and 23 (i.e. < 24)
+ * 5. Offerer, zone, offer token, and consideration token have no
+ * upper dirty bits — each argument is type(uint160).max or less
*/
validOffsets := and(
- // Order parameters at calldata 0x04 must have offset of 0x20.
- eq(
- calldataload(BasicOrder_parameters_cdPtr),
- BasicOrder_parameters_ptr
- ),
- // Additional recipients at cd 0x224 must have offset of 0x240.
- eq(
- calldataload(BasicOrder_additionalRecipients_head_cdPtr),
- BasicOrder_additionalRecipients_head_ptr
- )
- )
-
- validOffsets := and(
- validOffsets,
- eq(
- // Load signature offset from calldata 0x244.
- calldataload(BasicOrder_signature_cdPtr),
- // Derive expected offset as start of recipients + len * 64.
- add(
- BasicOrder_signature_ptr,
- mul(
- // Additional recipients length at calldata 0x264.
+ and(
+ and(
+ // Order parameters at cd 0x04 must have offset of 0x20.
+ eq(
+ calldataload(BasicOrder_parameters_cdPtr),
+ BasicOrder_parameters_ptr
+ ),
+ // Additional recipients (cd 0x224) arr offset == 0x240.
+ eq(
calldataload(
- BasicOrder_additionalRecipients_length_cdPtr
+ BasicOrder_additionalRecipients_head_cdPtr
),
- // Each additional recipient has a length of 0x40.
- AdditionalRecipients_size
+ BasicOrder_additionalRecipients_head_ptr
+ )
+ ),
+ // Signature offset == 0x260 + (recipients.length * 0x40).
+ eq(
+ // Load signature offset from calldata 0x244.
+ calldataload(BasicOrder_signature_cdPtr),
+ // Expected offset is start of recipients + len * 64.
+ add(
+ BasicOrder_signature_ptr,
+ shl(
+ // Each additional recipient has length of 0x40.
+ AdditionalRecipient_size_shift,
+ // Additional recipients length at cd 0x264.
+ calldataload(
+ BasicOrder_additionalRecipients_length_cdPtr
+ )
+ )
)
)
- )
- )
-
- validOffsets := and(
- validOffsets,
- lt(
- // BasicOrderType parameter at calldata offset 0x124.
- calldataload(BasicOrder_basicOrderType_cdPtr),
- // Value should be less than 24.
- BasicOrder_basicOrderType_range
+ ),
+ and(
+ // Ensure BasicOrderType parameter is less than 0x18.
+ lt(
+ // BasicOrderType parameter at calldata offset 0x124.
+ calldataload(BasicOrder_basicOrderType_cdPtr),
+ // Value should be less than 24.
+ BasicOrder_basicOrderType_range
+ ),
+ // Ensure no dirty upper bits are present on offerer, zone,
+ // offer token, or consideration token.
+ lt(
+ or(
+ or(
+ // Offerer parameter at calldata offset 0x84.
+ calldataload(BasicOrder_offerer_cdPtr),
+ // Zone parameter at calldata offset 0xa4.
+ calldataload(BasicOrder_zone_cdPtr)
+ ),
+ or(
+ // Offer token parameter at cd offset 0xc4.
+ calldataload(BasicOrder_offerToken_cdPtr),
+ // Consideration token parameter at offset 0x24.
+ calldataload(
+ BasicOrder_considerationToken_cdPtr
+ )
+ )
+ ),
+ AddressDirtyUpperBitThreshold
+ )
)
)
}
// Revert with an error if basic order parameter offsets are invalid.
if (!validOffsets) {
- revert InvalidBasicOrderParameterEncoding();
+ _revertInvalidBasicOrderParameterEncoding();
}
}
}
diff --git a/contracts/lib/BasicOrderFulfiller.sol b/contracts/lib/BasicOrderFulfiller.sol
index 62dcf1816..14aa0befc 100644
--- a/contracts/lib/BasicOrderFulfiller.sol
+++ b/contracts/lib/BasicOrderFulfiller.sol
@@ -1,28 +1,117 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
-import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
-
-// prettier-ignore
import {
- OrderType,
+ BasicOrderRouteType,
ItemType,
- BasicOrderRouteType
+ OrderType
} from "./ConsiderationEnums.sol";
-// prettier-ignore
-import {
- AdditionalRecipient,
- BasicOrderParameters,
- OfferItem,
- ConsiderationItem,
- SpentItem,
- ReceivedItem
-} from "./ConsiderationStructs.sol";
+import { BasicOrderParameters } from "./ConsiderationStructs.sol";
import { OrderValidator } from "./OrderValidator.sol";
-import "./ConsiderationConstants.sol";
+import {
+ _revertInsufficientNativeTokensSupplied,
+ _revertInvalidMsgValue,
+ _revertInvalidERC721TransferAmount,
+ _revertUnusedItemParameters
+} from "./ConsiderationErrors.sol";
+
+import {
+ AccumulatorDisarmed,
+ AdditionalRecipient_size_shift,
+ AdditionalRecipient_size,
+ BasicOrder_additionalRecipients_data_cdPtr,
+ BasicOrder_additionalRecipients_length_cdPtr,
+ BasicOrder_basicOrderType_cdPtr,
+ BasicOrder_common_params_size,
+ BasicOrder_considerationAmount_cdPtr,
+ BasicOrder_considerationHashesArray_ptr,
+ BasicOrder_considerationIdentifier_cdPtr,
+ BasicOrder_considerationItem_endAmount_ptr,
+ BasicOrder_considerationItem_identifier_ptr,
+ BasicOrder_considerationItem_itemType_ptr,
+ BasicOrder_considerationItem_startAmount_ptr,
+ BasicOrder_considerationItem_token_ptr,
+ BasicOrder_considerationItem_typeHash_ptr,
+ BasicOrder_considerationToken_cdPtr,
+ BasicOrder_endTime_cdPtr,
+ BasicOrder_fulfillerConduit_cdPtr,
+ BasicOrder_offerAmount_cdPtr,
+ BasicOrder_offeredItemByteMap,
+ BasicOrder_offerer_cdPtr,
+ BasicOrder_offererConduit_cdPtr,
+ BasicOrder_offerIdentifier_cdPtr,
+ BasicOrder_offerItem_endAmount_ptr,
+ BasicOrder_offerItem_itemType_ptr,
+ BasicOrder_offerItem_token_ptr,
+ BasicOrder_offerItem_typeHash_ptr,
+ BasicOrder_offerToken_cdPtr,
+ BasicOrder_order_considerationHashes_ptr,
+ BasicOrder_order_counter_ptr,
+ BasicOrder_order_offerer_ptr,
+ BasicOrder_order_offerHashes_ptr,
+ BasicOrder_order_orderType_ptr,
+ BasicOrder_order_startTime_ptr,
+ BasicOrder_order_typeHash_ptr,
+ BasicOrder_receivedItemByteMap,
+ BasicOrder_startTime_cdPtr,
+ BasicOrder_totalOriginalAdditionalRecipients_cdPtr,
+ BasicOrder_zone_cdPtr,
+ Common_token_offset,
+ Conduit_execute_ConduitTransfer_length_ptr,
+ Conduit_execute_ConduitTransfer_length,
+ Conduit_execute_ConduitTransfer_offset_ptr,
+ Conduit_execute_ConduitTransfer_ptr,
+ Conduit_execute_signature,
+ Conduit_execute_transferAmount_ptr,
+ Conduit_execute_transferIdentifier_ptr,
+ Conduit_execute_transferFrom_ptr,
+ Conduit_execute_transferItemType_ptr,
+ Conduit_execute_transferTo_ptr,
+ Conduit_execute_transferToken_ptr,
+ EIP712_ConsiderationItem_size,
+ EIP712_OfferItem_size,
+ EIP712_Order_size,
+ FiveWords,
+ FourWords,
+ FreeMemoryPointerSlot,
+ MaskOverLastTwentyBytes,
+ OneConduitExecute_size,
+ OneWord,
+ OneWordShift,
+ OrderFulfilled_baseOffset,
+ OrderFulfilled_baseSize,
+ OrderFulfilled_consideration_body_offset,
+ OrderFulfilled_consideration_head_offset,
+ OrderFulfilled_consideration_length_baseOffset,
+ OrderFulfilled_fulfiller_offset,
+ OrderFulfilled_offer_body_offset,
+ OrderFulfilled_offer_head_offset,
+ OrderFulfilled_offer_length_baseOffset,
+ OrderFulfilled_selector,
+ ReceivedItem_amount_offset,
+ ReceivedItem_size,
+ receivedItemsHash_ptr,
+ ThreeWords,
+ TwoWords,
+ ZeroSlot
+} from "./ConsiderationConstants.sol";
+
+import {
+ Error_selector_offset,
+ InvalidBasicOrderParameterEncoding_error_length,
+ InvalidBasicOrderParameterEncoding_error_selector,
+ InvalidTime_error_endTime_ptr,
+ InvalidTime_error_length,
+ InvalidTime_error_selector,
+ InvalidTime_error_startTime_ptr,
+ MissingOriginalConsiderationItems_error_length,
+ MissingOriginalConsiderationItems_error_selector,
+ UnusedItemParameters_error_length,
+ UnusedItemParameters_error_selector
+} from "./ConsiderationErrorConstants.sol";
/**
* @title BasicOrderFulfiller
@@ -77,6 +166,8 @@ contract BasicOrderFulfiller is OrderValidator {
// Declare additional recipient item type to derive from the route type.
ItemType additionalRecipientsItemType;
+ bytes32 orderHash;
+
// Utilize assembly to extract the order type and the basic order route.
assembly {
// Read basicOrderType from calldata.
@@ -88,7 +179,8 @@ contract BasicOrderFulfiller is OrderValidator {
// Divide basicOrderType by four to derive the route.
route := shr(2, basicOrderType)
- // If route > 1 additionalRecipient items are ERC20 (1) else Eth (0)
+ // If route > 1 additionalRecipient items are ERC20 (1) else native
+ // token (0).
additionalRecipientsItemType := gt(route, 1)
}
@@ -108,7 +200,7 @@ contract BasicOrderFulfiller is OrderValidator {
// Revert if msg.value has not been supplied as part of payable
// routes or has been supplied as part of non-payable routes.
if (!correctPayableStatus) {
- revert InvalidMsgValue(msg.value);
+ _revertInvalidMsgValue(msg.value);
}
}
@@ -138,25 +230,17 @@ contract BasicOrderFulfiller is OrderValidator {
)
// If route > 2, receivedItemType is route - 2. If route is 2,
- // the receivedItemType is ERC20 (1). Otherwise, it is Eth (0).
- receivedItemType := add(
- mul(sub(route, 2), gt(route, 2)),
- eq(route, 2)
- )
+ // the receivedItemType is ERC20 (1). Otherwise, it is native
+ // token (0).
+ receivedItemType := byte(route, BasicOrder_receivedItemByteMap)
// If route > 3, offeredItemType is ERC20 (1). Route is 2 or 3,
// offeredItemType = route. Route is 0 or 1, it is route + 2.
- offeredItemType := sub(
- add(route, mul(iszero(additionalRecipientsItemType), 2)),
- mul(
- offerTypeIsAdditionalRecipientsType,
- add(receivedItemType, 1)
- )
- )
+ offeredItemType := byte(route, BasicOrder_offeredItemByteMap)
}
// Derive & validate order using parameters and update order status.
- _prepareBasicFulfillmentFromCalldata(
+ orderHash = _prepareBasicFulfillmentFromCalldata(
parameters,
orderType,
receivedItemType,
@@ -175,38 +259,38 @@ contract BasicOrderFulfiller is OrderValidator {
conduitKey := calldataload(
add(
BasicOrder_offererConduit_cdPtr,
- mul(offerTypeIsAdditionalRecipientsType, OneWord)
+ shl(OneWordShift, offerTypeIsAdditionalRecipientsType)
)
)
}
// Transfer tokens based on the route.
if (additionalRecipientsItemType == ItemType.NATIVE) {
- // Ensure neither the token nor the identifier parameters are set.
- if (
- (uint160(parameters.considerationToken) |
- parameters.considerationIdentifier) != 0
- ) {
- revert UnusedItemParameters();
+ // Ensure neither consideration token nor identifier are set. Note
+ // that dirty upper bits in the consideration token will still cause
+ // this error to be thrown.
+ assembly {
+ if or(
+ calldataload(BasicOrder_considerationToken_cdPtr),
+ calldataload(BasicOrder_considerationIdentifier_cdPtr)
+ ) {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, UnusedItemParameters_error_selector)
+
+ // revert(abi.encodeWithSignature("UnusedItemParameters()"))
+ revert(
+ Error_selector_offset,
+ UnusedItemParameters_error_length
+ )
+ }
}
// Transfer the ERC721 or ERC1155 item, bypassing the accumulator.
- _transferIndividual721Or1155Item(
- offeredItemType,
- parameters.offerToken,
- parameters.offerer,
- msg.sender,
- parameters.offerIdentifier,
- parameters.offerAmount,
- conduitKey
- );
+ _transferIndividual721Or1155Item(offeredItemType, conduitKey);
// Transfer native to recipients, return excess to caller & wrap up.
- _transferEthAndFinalize(
- parameters.considerationAmount,
- parameters.offerer,
- parameters.additionalRecipients
- );
+ _transferNativeTokensAndFinalize();
} else {
// Initialize an accumulator array. From this point forward, no new
// memory regions can be safely allocated until the accumulator is
@@ -266,8 +350,6 @@ contract BasicOrderFulfiller is OrderValidator {
// Transfer ERC20 tokens to all recipients and wrap up.
_transferERC20AndFinalize(
- parameters.offerer,
- parameters,
offerTypeIsAdditionalRecipientsType,
accumulator
);
@@ -276,6 +358,9 @@ contract BasicOrderFulfiller is OrderValidator {
_triggerIfArmed(accumulator);
}
+ // Determine whether order is restricted and, if so, that it is valid.
+ _assertRestrictedBasicOrderValidity(orderHash, orderType, parameters);
+
// Clear the reentrancy guard.
_clearReentrancyGuard();
@@ -292,11 +377,8 @@ contract BasicOrderFulfiller is OrderValidator {
* offsets. Checking that the offsets were produced by default encoding
* will ensure that other functions using Solidity's calldata accessors
* (which calculate pointers from the stored offsets) are reading the
- * same data as the order hash is derived from. Also note that This
- * function accesses memory directly. It does not clear the expanded
- * memory regions used, nor does it update the free memory pointer, so
- * other direct memory access must not assume that unused memory is
- * empty.
+ * same data as the order hash is derived from. Also note that this
+ * function accesses memory directly.
*
* @param parameters The parameters of the basic order.
* @param orderType The order type.
@@ -309,6 +391,7 @@ contract BasicOrderFulfiller is OrderValidator {
* consideration item on the order.
* @param offeredItemType The item type of the offered item on
* the order.
+ * @return orderHash The calculated order hash.
*/
function _prepareBasicFulfillmentFromCalldata(
BasicOrderParameters calldata parameters,
@@ -317,28 +400,65 @@ contract BasicOrderFulfiller is OrderValidator {
ItemType additionalRecipientsItemType,
address additionalRecipientsToken,
ItemType offeredItemType
- ) internal {
+ ) internal returns (bytes32 orderHash) {
// Ensure this function cannot be triggered during a reentrant call.
- _setReentrancyGuard();
-
- // Ensure current timestamp falls between order start time and end time.
- _verifyTime(parameters.startTime, parameters.endTime, true);
+ _setReentrancyGuard(false); // Native tokens rejected during execution.
// Verify that calldata offsets for all dynamic types were produced by
- // default encoding. This ensures that the constants we use for calldata
+ // default encoding. This ensures that the constants used for calldata
// pointers to dynamic types are the same as those calculated by
// Solidity using their offsets. Also verify that the basic order type
// is within range.
_assertValidBasicOrderParameters();
- // Ensure supplied consideration array length is not less than original.
- _assertConsiderationLengthIsNotLessThanOriginalConsiderationLength(
- parameters.additionalRecipients.length,
- parameters.totalOriginalAdditionalRecipients
- );
+ // Check for invalid time and missing original consideration items.
+ // Utilize assembly so that constant calldata pointers can be applied.
+ assembly {
+ // Ensure current timestamp is between order start time & end time.
+ if or(
+ gt(calldataload(BasicOrder_startTime_cdPtr), timestamp()),
+ iszero(gt(calldataload(BasicOrder_endTime_cdPtr), timestamp()))
+ ) {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidTime_error_selector)
- // Declare stack element for the order hash.
- bytes32 orderHash;
+ // Store arguments.
+ mstore(
+ InvalidTime_error_startTime_ptr,
+ calldataload(BasicOrder_startTime_cdPtr)
+ )
+ mstore(
+ InvalidTime_error_endTime_ptr,
+ calldataload(BasicOrder_endTime_cdPtr)
+ )
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidTime(uint256,uint256)",
+ // startTime,
+ // endTime
+ // ))
+ revert(Error_selector_offset, InvalidTime_error_length)
+ }
+
+ // Ensure consideration array length isn't less than total original.
+ if lt(
+ calldataload(BasicOrder_additionalRecipients_length_cdPtr),
+ calldataload(BasicOrder_totalOriginalAdditionalRecipients_cdPtr)
+ ) {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, MissingOriginalConsiderationItems_error_selector)
+
+ // revert(abi.encodeWithSignature(
+ // "MissingOriginalConsiderationItems()"
+ // ))
+ revert(
+ Error_selector_offset,
+ MissingOriginalConsiderationItems_error_length
+ )
+ }
+ }
{
/**
@@ -422,7 +542,7 @@ contract BasicOrderFulfiller is OrderValidator {
// array.
let eventConsiderationArrPtr := add(
OrderFulfilled_consideration_length_baseOffset,
- mul(totalAdditionalRecipients, OneWord)
+ shl(OneWordShift, totalAdditionalRecipients)
)
// Set the length of the consideration array to the number of
@@ -430,12 +550,7 @@ contract BasicOrderFulfiller is OrderValidator {
// item.
mstore(
eventConsiderationArrPtr,
- add(
- calldataload(
- BasicOrder_additionalRecipients_length_cdPtr
- ),
- 1
- )
+ add(totalAdditionalRecipients, 1)
)
// Overwrite the consideration array pointer so it points to the
@@ -460,9 +575,9 @@ contract BasicOrderFulfiller is OrderValidator {
* 3. Calculate EIP712 ConsiderationItem hashes for original
* additional recipients and add a ReceivedItem for each to the
* consideration array in the OrderFulfilled event. The original
- * additional recipients are all the considerations signed by
- * the offerer aside from the primary consideration of the
- * order. Uses memory region from 0x80-0x160 as a buffer for
+ * additional recipients are all the consideration items signed
+ * by the offerer aside from the primary consideration items of
+ * the order. Uses memory region from 0x80-0x160 as a buffer for
* calculating EIP712 ConsiderationItem hashes.
*/
@@ -485,13 +600,16 @@ contract BasicOrderFulfiller is OrderValidator {
)
mstore(BasicOrder_considerationItem_identifier_ptr, 0)
+ // Declare a stack variable where all additional recipients will
+ // be combined to guard against providing dirty upper bits.
+ let combinedAdditionalRecipients
+
// Read length of the additionalRecipients array from calldata
// and iterate.
totalAdditionalRecipients := calldataload(
BasicOrder_totalOriginalAdditionalRecipients_cdPtr
)
let i := 0
- // prettier-ignore
for {} lt(i, totalAdditionalRecipients) {
i := add(i, 1)
} {
@@ -502,7 +620,7 @@ contract BasicOrderFulfiller is OrderValidator {
// Retrieve calldata pointer for additional recipient.
let additionalRecipientCdPtr := add(
BasicOrder_additionalRecipients_data_cdPtr,
- mul(AdditionalRecipients_size, i)
+ mul(AdditionalRecipient_size, i)
)
// Copy startAmount from calldata to the ConsiderationItem
@@ -518,7 +636,13 @@ contract BasicOrderFulfiller is OrderValidator {
calldatacopy(
BasicOrder_considerationItem_endAmount_ptr,
additionalRecipientCdPtr,
- AdditionalRecipients_size
+ AdditionalRecipient_size
+ )
+
+ // Include the recipient as part of combined recipients.
+ combinedAdditionalRecipients := or(
+ combinedAdditionalRecipients,
+ calldataload(add(additionalRecipientCdPtr, OneWord))
)
// Add 1 word to the pointer as part of each loop to reduce
@@ -585,7 +709,7 @@ contract BasicOrderFulfiller is OrderValidator {
receivedItemsHash_ptr,
keccak256(
BasicOrder_considerationHashesArray_ptr,
- mul(add(totalAdditionalRecipients, 1), OneWord)
+ shl(OneWordShift, add(totalAdditionalRecipients, 1))
)
)
@@ -600,14 +724,14 @@ contract BasicOrderFulfiller is OrderValidator {
totalAdditionalRecipients := calldataload(
BasicOrder_additionalRecipients_length_cdPtr
)
- // prettier-ignore
+
for {} lt(i, totalAdditionalRecipients) {
i := add(i, 1)
} {
// Retrieve calldata pointer for additional recipient.
let additionalRecipientCdPtr := add(
BasicOrder_additionalRecipients_data_cdPtr,
- mul(AdditionalRecipients_size, i)
+ mul(AdditionalRecipient_size, i)
)
// At this point, eventConsiderationArrPtr points to the
@@ -640,6 +764,27 @@ contract BasicOrderFulfiller is OrderValidator {
additionalRecipientCdPtr,
TwoWords
)
+
+ // Include the recipient as part of combined recipients.
+ combinedAdditionalRecipients := or(
+ combinedAdditionalRecipients,
+ calldataload(add(additionalRecipientCdPtr, OneWord))
+ )
+ }
+
+ // Ensure no dirty upper bits on combined additional recipients.
+ if gt(combinedAdditionalRecipients, MaskOverLastTwentyBytes) {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidBasicOrderParameterEncoding_error_selector)
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidBasicOrderParameterEncoding()"
+ // ))
+ revert(
+ Error_selector_offset,
+ InvalidBasicOrderParameterEncoding_error_length
+ )
}
}
}
@@ -712,11 +857,11 @@ contract BasicOrderFulfiller is OrderValidator {
*/
let eventConsiderationArrPtr := add(
OrderFulfilled_offer_length_baseOffset,
- mul(
+ shl(
+ OneWordShift,
calldataload(
BasicOrder_additionalRecipients_length_cdPtr
- ),
- OneWord
+ )
)
)
@@ -730,7 +875,7 @@ contract BasicOrderFulfiller is OrderValidator {
// offerAmount) from OrderParameters to (token, identifier,
// amount) in SpentItem struct.
calldatacopy(
- add(eventConsiderationArrPtr, AdditionalRecipients_size),
+ add(eventConsiderationArrPtr, AdditionalRecipient_size),
BasicOrder_offerToken_cdPtr,
ThreeWords
)
@@ -838,12 +983,12 @@ contract BasicOrderFulfiller is OrderValidator {
* - 0x140: recipient 0
*/
- // Derive pointer to start of OrderFulfilled event data
+ // Derive pointer to start of OrderFulfilled event data.
let eventDataPtr := add(
OrderFulfilled_baseOffset,
- mul(
- calldataload(BasicOrder_additionalRecipients_length_cdPtr),
- OneWord
+ shl(
+ OneWordShift,
+ calldataload(BasicOrder_additionalRecipients_length_cdPtr)
)
)
@@ -893,107 +1038,249 @@ contract BasicOrderFulfiller is OrderValidator {
// Restore the zero slot.
mstore(ZeroSlot, 0)
- }
- // Determine whether order is restricted and, if so, that it is valid.
- _assertRestrictedBasicOrderValidity(
- orderHash,
- parameters.zoneHash,
- orderType,
- parameters.offerer,
- parameters.zone
- );
+ // Update the free memory pointer so that event data is persisted.
+ mstore(FreeMemoryPointerSlot, add(eventDataPtr, dataSize))
+ }
// Verify and update the status of the derived order.
- _validateBasicOrderAndUpdateStatus(
- orderHash,
- parameters.offerer,
- parameters.signature
- );
+ _validateBasicOrderAndUpdateStatus(orderHash, parameters.signature);
+
+ // Return the derived order hash.
+ return orderHash;
+ }
+
+ /**
+ * @dev Internal function to transfer an individual ERC721 or ERC1155 item
+ * from a given originator to a given recipient. The accumulator will
+ * be bypassed, meaning that this function should be utilized in cases
+ * where multiple item transfers can be accumulated into a single
+ * conduit call. Sufficient approvals must be set, either on the
+ * respective conduit or on this contract. Note that this function may
+ * only be safely called as part of basic orders, as it assumes a
+ * specific calldata encoding structure that must first be validated.
+ *
+ * @param itemType The type of item to transfer, either ERC721 or ERC1155.
+ * @param conduitKey A bytes32 value indicating what corresponding conduit,
+ * if any, to source token approvals from. The zero hash
+ * signifies that no conduit should be used, with direct
+ * approvals set on this contract.
+ */
+ function _transferIndividual721Or1155Item(
+ ItemType itemType,
+ bytes32 conduitKey
+ ) internal {
+ // Retrieve token, from, identifier, and amount from calldata using
+ // fixed calldata offsets based on strict basic parameter encoding.
+ address token;
+ address from;
+ uint256 identifier;
+ uint256 amount;
+ assembly {
+ token := calldataload(BasicOrder_offerToken_cdPtr)
+ from := calldataload(BasicOrder_offerer_cdPtr)
+ identifier := calldataload(BasicOrder_offerIdentifier_cdPtr)
+ amount := calldataload(BasicOrder_offerAmount_cdPtr)
+ }
+
+ // Determine if the transfer is to be performed via a conduit.
+ if (conduitKey != bytes32(0)) {
+ // Use free memory pointer as calldata offset for the conduit call.
+ uint256 callDataOffset;
+
+ // Utilize assembly to place each argument in free memory.
+ assembly {
+ // Retrieve the free memory pointer and use it as the offset.
+ callDataOffset := mload(FreeMemoryPointerSlot)
+
+ // Write ConduitInterface.execute.selector to memory.
+ mstore(callDataOffset, Conduit_execute_signature)
+
+ // Write the offset to the ConduitTransfer array in memory.
+ mstore(
+ add(
+ callDataOffset,
+ Conduit_execute_ConduitTransfer_offset_ptr
+ ),
+ Conduit_execute_ConduitTransfer_ptr
+ )
+
+ // Write the length of the ConduitTransfer array to memory.
+ mstore(
+ add(
+ callDataOffset,
+ Conduit_execute_ConduitTransfer_length_ptr
+ ),
+ Conduit_execute_ConduitTransfer_length
+ )
+
+ // Write the item type to memory.
+ mstore(
+ add(callDataOffset, Conduit_execute_transferItemType_ptr),
+ itemType
+ )
+
+ // Write the token to memory.
+ mstore(
+ add(callDataOffset, Conduit_execute_transferToken_ptr),
+ token
+ )
+
+ // Write the transfer source to memory.
+ mstore(
+ add(callDataOffset, Conduit_execute_transferFrom_ptr),
+ from
+ )
+
+ // Write the transfer recipient (the caller) to memory.
+ mstore(
+ add(callDataOffset, Conduit_execute_transferTo_ptr),
+ caller()
+ )
+
+ // Write the token identifier to memory.
+ mstore(
+ add(callDataOffset, Conduit_execute_transferIdentifier_ptr),
+ identifier
+ )
+
+ // Write the transfer amount to memory.
+ mstore(
+ add(callDataOffset, Conduit_execute_transferAmount_ptr),
+ amount
+ )
+ }
+
+ // Perform the call to the conduit.
+ _callConduitUsingOffsets(
+ conduitKey,
+ callDataOffset,
+ OneConduitExecute_size
+ );
+ } else {
+ // Otherwise, determine whether it is an ERC721 or ERC1155 item.
+ if (itemType == ItemType.ERC721) {
+ // Ensure that exactly one 721 item is being transferred.
+ if (amount != 1) {
+ _revertInvalidERC721TransferAmount(amount);
+ }
+
+ // Perform transfer to caller via the token contract directly.
+ _performERC721Transfer(token, from, msg.sender, identifier);
+ } else {
+ // Perform transfer to caller via the token contract directly.
+ _performERC1155Transfer(
+ token,
+ from,
+ msg.sender,
+ identifier,
+ amount
+ );
+ }
+ }
}
/**
* @dev Internal function to transfer Ether (or other native tokens) to a
* given recipient as part of basic order fulfillment. Note that
* conduits are not utilized for native tokens as the transferred
- * amount must be provided as msg.value.
- *
- * @param amount The amount to transfer.
- * @param to The recipient of the native token transfer.
- * @param additionalRecipients The additional recipients of the order.
+ * amount must be provided as msg.value. Also note that this function
+ * may only be safely called as part of basic orders, as it assumes a
+ * specific calldata encoding structure that must first be validated.
*/
- function _transferEthAndFinalize(
- uint256 amount,
- address payable to,
- AdditionalRecipient[] calldata additionalRecipients
- ) internal {
- // Put ether value supplied by the caller on the stack.
- uint256 etherRemaining = msg.value;
+ function _transferNativeTokensAndFinalize() internal {
+ // Put native token value supplied by the caller on the stack.
+ uint256 nativeTokensRemaining = msg.value;
+
+ // Retrieve consideration amount, offerer, and total size of additional
+ // recipients data from calldata using fixed offsets and place on stack.
+ uint256 amount;
+ address payable to;
+ uint256 totalAdditionalRecipientsDataSize;
+ assembly {
+ amount := calldataload(BasicOrder_considerationAmount_cdPtr)
+ to := calldataload(BasicOrder_offerer_cdPtr)
+ totalAdditionalRecipientsDataSize := shl(
+ AdditionalRecipient_size_shift,
+ calldataload(BasicOrder_additionalRecipients_length_cdPtr)
+ )
+ }
- // Retrieve total number of additional recipients and place on stack.
- uint256 totalAdditionalRecipients = additionalRecipients.length;
+ uint256 additionalRecipientAmount;
+ address payable recipient;
// Skip overflow check as for loop is indexed starting at zero.
unchecked {
- // Iterate over each additional recipient.
- for (uint256 i = 0; i < totalAdditionalRecipients; ++i) {
- // Retrieve the additional recipient.
- AdditionalRecipient calldata additionalRecipient = (
- additionalRecipients[i]
- );
-
- // Read ether amount to transfer to recipient & place on stack.
- uint256 additionalRecipientAmount = additionalRecipient.amount;
+ // Iterate over additional recipient data by two-word element.
+ for (
+ uint256 i = 0;
+ i < totalAdditionalRecipientsDataSize;
+ i += AdditionalRecipient_size
+ ) {
+ assembly {
+ // Retrieve calldata pointer for additional recipient.
+ let additionalRecipientCdPtr := add(
+ BasicOrder_additionalRecipients_data_cdPtr,
+ i
+ )
- // Ensure that sufficient Ether is available.
- if (additionalRecipientAmount > etherRemaining) {
- revert InsufficientEtherSupplied();
+ additionalRecipientAmount := calldataload(
+ additionalRecipientCdPtr
+ )
+ recipient := calldataload(
+ add(OneWord, additionalRecipientCdPtr)
+ )
}
- // Transfer Ether to the additional recipient.
- _transferEth(
- additionalRecipient.recipient,
- additionalRecipientAmount
- );
+ // Ensure that sufficient native tokens are available.
+ if (additionalRecipientAmount > nativeTokensRemaining) {
+ _revertInsufficientNativeTokensSupplied();
+ }
- // Reduce ether value available. Skip underflow check as
+ // Reduce native token value available. Skip underflow check as
// subtracted value is confirmed above as less than remaining.
- etherRemaining -= additionalRecipientAmount;
+ nativeTokensRemaining -= additionalRecipientAmount;
+
+ // Transfer native tokens to the additional recipient.
+ _transferNativeTokens(recipient, additionalRecipientAmount);
}
}
- // Ensure that sufficient Ether is still available.
- if (amount > etherRemaining) {
- revert InsufficientEtherSupplied();
+ // Ensure that sufficient native tokens are still available.
+ if (amount > nativeTokensRemaining) {
+ _revertInsufficientNativeTokensSupplied();
}
- // Transfer Ether to the offerer.
- _transferEth(to, amount);
+ // Transfer native tokens to the offerer.
+ _transferNativeTokens(to, amount);
- // If any Ether remains after transfers, return it to the caller.
- if (etherRemaining > amount) {
- // Skip underflow check as etherRemaining > amount.
+ // If any native tokens remain after transfers, return to the caller.
+ if (nativeTokensRemaining > amount) {
+ // Skip underflow check as nativeTokensRemaining > amount.
unchecked {
- // Transfer remaining Ether to the caller.
- _transferEth(payable(msg.sender), etherRemaining - amount);
+ // Transfer remaining native tokens to the caller.
+ _transferNativeTokens(
+ payable(msg.sender),
+ nativeTokensRemaining - amount
+ );
}
}
}
/**
* @dev Internal function to transfer ERC20 tokens to a given recipient as
- * part of basic order fulfillment.
+ * part of basic order fulfillment. Note that this function may only be
+ * safely called as part of basic orders, as it assumes a specific
+ * calldata encoding structure that must first be validated. Also note
+ * that basic order parameters are retrieved using fixed offsets, this
+ * requires that strict basic order encoding has already been verified.
*
- * @param offerer The offerer of the fulfiller order.
- * @param parameters The basic order parameters.
* @param fromOfferer A boolean indicating whether to decrement amount from
* the offered amount.
* @param accumulator An open-ended array that collects transfers to execute
* against a given conduit in a single call.
*/
function _transferERC20AndFinalize(
- address offerer,
- BasicOrderParameters calldata parameters,
bool fromOfferer,
bytes memory accumulator
) internal {
@@ -1012,28 +1299,32 @@ contract BasicOrderFulfiller is OrderValidator {
// Set ERC20 token transfer variables based on fromOfferer boolean.
if (fromOfferer) {
- // Use offerer as from value and msg.sender as to value.
- from = offerer;
- to = msg.sender;
-
- // Use offer token and related values if token is from offerer.
- token = parameters.offerToken;
- identifier = parameters.offerIdentifier;
- amount = parameters.offerAmount;
+ // Use offerer as from value, msg.sender as to value, and offer
+ // token, identifier, & amount values if token is from offerer.
+ assembly {
+ from := calldataload(BasicOrder_offerer_cdPtr)
+ to := caller()
+ token := calldataload(BasicOrder_offerToken_cdPtr)
+ identifier := calldataload(BasicOrder_offerIdentifier_cdPtr)
+ amount := calldataload(BasicOrder_offerAmount_cdPtr)
+ }
} else {
- // Use msg.sender as from value and offerer as to value.
- from = msg.sender;
- to = offerer;
-
- // Otherwise, use consideration token and related values.
- token = parameters.considerationToken;
- identifier = parameters.considerationIdentifier;
- amount = parameters.considerationAmount;
+ // Otherwise, use msg.sender as from value, offerer as to value,
+ // and consideration token, identifier, and amount values.
+ assembly {
+ from := caller()
+ to := calldataload(BasicOrder_offerer_cdPtr)
+ token := calldataload(BasicOrder_considerationToken_cdPtr)
+ identifier := calldataload(
+ BasicOrder_considerationIdentifier_cdPtr
+ )
+ amount := calldataload(BasicOrder_considerationAmount_cdPtr)
+ }
}
// Ensure that no identifier is supplied.
if (identifier != 0) {
- revert UnusedItemParameters();
+ _revertUnusedItemParameters();
}
}
@@ -1046,24 +1337,39 @@ contract BasicOrderFulfiller is OrderValidator {
conduitKey := calldataload(
sub(
BasicOrder_fulfillerConduit_cdPtr,
- mul(fromOfferer, OneWord)
+ shl(OneWordShift, fromOfferer)
)
)
}
- // Retrieve total number of additional recipients and place on stack.
- uint256 totalAdditionalRecipients = (
- parameters.additionalRecipients.length
- );
+ // Retrieve total size of additional recipients data and place on stack.
+ uint256 totalAdditionalRecipientsDataSize;
+ assembly {
+ totalAdditionalRecipientsDataSize := shl(
+ AdditionalRecipient_size_shift,
+ calldataload(BasicOrder_additionalRecipients_length_cdPtr)
+ )
+ }
+
+ uint256 additionalRecipientAmount;
+ address recipient;
// Iterate over each additional recipient.
- for (uint256 i = 0; i < totalAdditionalRecipients; ) {
- // Retrieve the additional recipient.
- AdditionalRecipient calldata additionalRecipient = (
- parameters.additionalRecipients[i]
- );
+ for (uint256 i = 0; i < totalAdditionalRecipientsDataSize; ) {
+ assembly {
+ // Retrieve calldata pointer for additional recipient.
+ let additionalRecipientCdPtr := add(
+ BasicOrder_additionalRecipients_data_cdPtr,
+ i
+ )
- uint256 additionalRecipientAmount = additionalRecipient.amount;
+ additionalRecipientAmount := calldataload(
+ additionalRecipientCdPtr
+ )
+ recipient := calldataload(
+ add(OneWord, additionalRecipientCdPtr)
+ )
+ }
// Decrement the amount to transfer to fulfiller if indicated.
if (fromOfferer) {
@@ -1074,7 +1380,7 @@ contract BasicOrderFulfiller is OrderValidator {
_transferERC20(
token,
from,
- additionalRecipient.recipient,
+ recipient,
additionalRecipientAmount,
conduitKey,
accumulator
@@ -1082,7 +1388,7 @@ contract BasicOrderFulfiller is OrderValidator {
// Skip overflow check as for loop is indexed starting at zero.
unchecked {
- ++i;
+ i += AdditionalRecipient_size;
}
}
diff --git a/contracts/lib/Consideration.sol b/contracts/lib/Consideration.sol
index 765f6b38b..96d66a639 100644
--- a/contracts/lib/Consideration.sol
+++ b/contracts/lib/Consideration.sol
@@ -1,40 +1,55 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
-// prettier-ignore
import {
ConsiderationInterface
} from "../interfaces/ConsiderationInterface.sol";
-// prettier-ignore
import {
- OrderComponents,
- BasicOrderParameters,
- OrderParameters,
- Order,
AdvancedOrder,
- OrderStatus,
+ BasicOrderParameters,
CriteriaResolver,
+ Execution,
Fulfillment,
FulfillmentComponent,
- Execution
+ Order,
+ OrderComponents
} from "./ConsiderationStructs.sol";
import { OrderCombiner } from "./OrderCombiner.sol";
+import {
+ CalldataStart,
+ CalldataPointer
+} from "../helpers/PointerLibraries.sol";
+
+import {
+ Offset_fulfillAdvancedOrder_criteriaResolvers,
+ Offset_fulfillAvailableAdvancedOrders_cnsdrationFlflmnts,
+ Offset_fulfillAvailableAdvancedOrders_criteriaResolvers,
+ Offset_fulfillAvailableAdvancedOrders_offerFulfillments,
+ Offset_fulfillAvailableOrders_considerationFulfillments,
+ Offset_fulfillAvailableOrders_offerFulfillments,
+ Offset_matchAdvancedOrders_criteriaResolvers,
+ Offset_matchAdvancedOrders_fulfillments,
+ Offset_matchOrders_fulfillments,
+ OrderParameters_counter_offset
+} from "./ConsiderationConstants.sol";
+
/**
* @title Consideration
- * @author 0age
- * @custom:coauthor d1ll0n
- * @custom:coauthor transmissions11
- * @custom:version 1.1
- * @notice Consideration is a generalized ETH/ERC20/ERC721/ERC1155 marketplace.
- * It minimizes external calls to the greatest extent possible and
- * provides lightweight methods for common routes as well as more
- * flexible methods for composing advanced orders or groups of orders.
- * Each order contains an arbitrary number of items that may be spent
- * (the "offer") along with an arbitrary number of items that must be
- * received back by the indicated recipients (the "consideration").
+ * @author 0age (0age.eth)
+ * @custom:coauthor d1ll0n (d1ll0n.eth)
+ * @custom:coauthor transmissions11 (t11s.eth)
+ * @custom:coauthor James Wenzel (emo.eth)
+ * @custom:version 1.2
+ * @notice Consideration is a generalized native token/ERC20/ERC721/ERC1155
+ * marketplace that provides lightweight methods for common routes as
+ * well as more flexible methods for composing advanced orders or groups
+ * of orders. Each order contains an arbitrary number of items that may
+ * be spent (the "offer") along with an arbitrary number of items that
+ * must be received back by the indicated recipients (the
+ * "consideration").
*/
contract Consideration is ConsiderationInterface, OrderCombiner {
/**
@@ -47,6 +62,17 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
*/
constructor(address conduitController) OrderCombiner(conduitController) {}
+ /**
+ * @notice Accept native token transfers during execution that may then be
+ * used to facilitate native token transfers, where any tokens that
+ * remain will be transferred to the caller. Native tokens are only
+ * acceptable mid-fulfillment (and not during basic fulfillment).
+ */
+ receive() external payable {
+ // Ensure the reentrancy guard is currently set to accept native tokens.
+ _assertAcceptingNativeTokens();
+ }
+
/**
* @notice Fulfill an order offering an ERC20, ERC721, or ERC1155 item by
* supplying Ether (or other native tokens), ERC20 tokens, an ERC721
@@ -67,18 +93,49 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* this contract (or their chosen conduit if indicated)
* before any tokens can be transferred. Also note that
* contract recipients of ERC1155 consideration items must
- * implement `onERC1155Received` in order to receive those
- * items.
+ * implement `onERC1155Received` to receive those items.
*
* @return fulfilled A boolean indicating whether the order has been
* successfully fulfilled.
*/
- function fulfillBasicOrder(BasicOrderParameters calldata parameters)
- external
- payable
- override
- returns (bool fulfilled)
- {
+ function fulfillBasicOrder(
+ BasicOrderParameters calldata parameters
+ ) external payable override returns (bool fulfilled) {
+ // Validate and fulfill the basic order.
+ fulfilled = _validateAndFulfillBasicOrder(parameters);
+ }
+
+ /**
+ * @notice Fulfill an order offering an ERC20, ERC721, or ERC1155 item by
+ * supplying Ether (or other native tokens), ERC20 tokens, an ERC721
+ * item, or an ERC1155 item as consideration. Six permutations are
+ * supported: Native token to ERC721, Native token to ERC1155, ERC20
+ * to ERC721, ERC20 to ERC1155, ERC721 to ERC20, and ERC1155 to
+ * ERC20 (with native tokens supplied as msg.value). For an order to
+ * be eligible for fulfillment via this method, it must contain a
+ * single offer item (though that item may have a greater amount if
+ * the item is not an ERC721). An arbitrary number of "additional
+ * recipients" may also be supplied which will each receive native
+ * tokens or ERC20 items from the fulfiller as consideration. Refer
+ * to the documentation for a more comprehensive summary of how to
+ * utilize this method and what orders are compatible with it. Note
+ * that this function costs less gas than `fulfillBasicOrder` due to
+ * the zero bytes in the function selector (0x00000000) which also
+ * results in earlier function dispatch.
+ *
+ * @param parameters Additional information on the fulfilled order. Note
+ * that the offerer and the fulfiller must first approve
+ * this contract (or their chosen conduit if indicated)
+ * before any tokens can be transferred. Also note that
+ * contract recipients of ERC1155 consideration items must
+ * implement `onERC1155Received` to receive those items.
+ *
+ * @return fulfilled A boolean indicating whether the order has been
+ * successfully fulfilled.
+ */
+ function fulfillBasicOrder_efficient_6GL6yc(
+ BasicOrderParameters calldata parameters
+ ) external payable override returns (bool fulfilled) {
// Validate and fulfill the basic order.
fulfilled = _validateAndFulfillBasicOrder(parameters);
}
@@ -89,7 +146,7 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* criteria-based orders or partial filling of orders (though
* filling the remainder of a partially-filled order is supported).
*
- * @param order The order to fulfill. Note that both the
+ * @custom:param order The order to fulfill. Note that both the
* offerer and the fulfiller must first approve
* this contract (or the corresponding conduit if
* indicated) to transfer any relevant tokens on
@@ -100,20 +157,23 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* any, to source the fulfiller's token approvals
* from. The zero hash signifies that no conduit
* should be used (and direct approvals set on
- * Consideration).
+ * this contract).
*
* @return fulfilled A boolean indicating whether the order has been
* successfully fulfilled.
*/
- function fulfillOrder(Order calldata order, bytes32 fulfillerConduitKey)
- external
- payable
- override
- returns (bool fulfilled)
- {
+ function fulfillOrder(
+ /**
+ * @custom:name order
+ */
+ Order calldata,
+ bytes32 fulfillerConduitKey
+ ) external payable override returns (bool fulfilled) {
// Convert order to "advanced" order, then validate and fulfill it.
fulfilled = _validateAndFulfillAdvancedOrder(
- _convertOrderToAdvanced(order),
+ _toAdvancedOrderReturnType(_decodeOrderAsAdvancedOrder)(
+ CalldataStart.pptr()
+ ),
new CriteriaResolver[](0), // No criteria resolvers supplied.
fulfillerConduitKey,
msg.sender
@@ -125,53 +185,68 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* items for offer and consideration alongside criteria resolvers
* containing specific token identifiers and associated proofs.
*
- * @param advancedOrder The order to fulfill along with the fraction
- * of the order to attempt to fill. Note that
- * both the offerer and the fulfiller must first
- * approve this contract (or their conduit if
- * indicated by the order) to transfer any
- * relevant tokens on their behalf and that
- * contracts must implement `onERC1155Received`
- * to receive ERC1155 tokens as consideration.
- * Also note that all offer and consideration
- * components must have no remainder after
- * multiplication of the respective amount with
- * the supplied fraction for the partial fill to
- * be considered valid.
- * @param criteriaResolvers An array where each element contains a
- * reference to a specific offer or
- * consideration, a token identifier, and a proof
- * that the supplied token identifier is
- * contained in the merkle root held by the item
- * in question's criteria element. Note that an
- * empty criteria indicates that any
- * (transferable) token identifier on the token
- * in question is valid and that no associated
- * proof needs to be supplied.
- * @param fulfillerConduitKey A bytes32 value indicating what conduit, if
- * any, to source the fulfiller's token approvals
- * from. The zero hash signifies that no conduit
- * should be used (and direct approvals set on
- * Consideration).
- * @param recipient The intended recipient for all received items,
- * with `address(0)` indicating that the caller
- * should receive the items.
+ * @custom:param advancedOrder The order to fulfill along with the
+ * fraction of the order to attempt to fill.
+ * Note that both the offerer and the
+ * fulfiller must first approve this
+ * contract (or their conduit if indicated
+ * by the order) to transfer any relevant
+ * tokens on their behalf and that contracts
+ * must implement `onERC1155Received` to
+ * receive ERC1155 tokens as consideration.
+ * Also note that all offer and
+ * consideration components must have no
+ * remainder after multiplication of the
+ * respective amount with the supplied
+ * fraction for the partial fill to be
+ * considered valid.
+ * @custom:param criteriaResolvers An array where each element contains a
+ * reference to a specific offer or
+ * consideration, a token identifier, and a
+ * proof that the supplied token identifier
+ * is contained in the merkle root held by
+ * the item in question's criteria element.
+ * Note that an empty criteria indicates
+ * that any (transferable) token identifier
+ * on the token in question is valid and
+ * that no associated proof needs to be
+ * supplied.
+ * @param fulfillerConduitKey A bytes32 value indicating what conduit,
+ * if any, to source the fulfiller's token
+ * approvals from. The zero hash signifies
+ * that no conduit should be used (and
+ * direct approvals set on this contract).
+ * @param recipient The intended recipient for all received
+ * items, with `address(0)` indicating that
+ * the caller should receive the items.
*
* @return fulfilled A boolean indicating whether the order has been
* successfully fulfilled.
*/
function fulfillAdvancedOrder(
- AdvancedOrder calldata advancedOrder,
- CriteriaResolver[] calldata criteriaResolvers,
+ /**
+ * @custom:name advancedOrder
+ */
+ AdvancedOrder calldata,
+ /**
+ * @custom:name criteriaResolvers
+ */
+ CriteriaResolver[] calldata,
bytes32 fulfillerConduitKey,
address recipient
) external payable override returns (bool fulfilled) {
// Validate and fulfill the order.
fulfilled = _validateAndFulfillAdvancedOrder(
- advancedOrder,
- criteriaResolvers,
+ _toAdvancedOrderReturnType(_decodeAdvancedOrder)(
+ CalldataStart.pptr()
+ ),
+ _toCriteriaResolversReturnType(_decodeCriteriaResolvers)(
+ CalldataStart.pptr(
+ Offset_fulfillAdvancedOrder_criteriaResolvers
+ )
+ ),
fulfillerConduitKey,
- recipient == address(0) ? msg.sender : recipient
+ _substituteCallerForEmptyRecipient(recipient)
);
}
@@ -189,27 +264,37 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* partial filling of orders (though filling the remainder of a
* partially-filled order is supported).
*
- * @param orders The orders to fulfill. Note that both
- * the offerer and the fulfiller must first
- * approve this contract (or the
- * corresponding conduit if indicated) to
- * transfer any relevant tokens on their
- * behalf and that contracts must implement
- * `onERC1155Received` to receive ERC1155
- * tokens as consideration.
- * @param offerFulfillments An array of FulfillmentComponent arrays
- * indicating which offer items to attempt
- * to aggregate when preparing executions.
- * @param considerationFulfillments An array of FulfillmentComponent arrays
- * indicating which consideration items to
- * attempt to aggregate when preparing
- * executions.
- * @param fulfillerConduitKey A bytes32 value indicating what conduit,
- * if any, to source the fulfiller's token
- * approvals from. The zero hash signifies
- * that no conduit should be used (and
- * direct approvals set on Consideration).
- * @param maximumFulfilled The maximum number of orders to fulfill.
+ * @custom:param orders The orders to fulfill. Note that
+ * both the offerer and the
+ * fulfiller must first approve this
+ * contract (or the corresponding
+ * conduit if indicated) to transfer
+ * any relevant tokens on their
+ * behalf and that contracts must
+ * implement `onERC1155Received` to
+ * receive ERC1155 tokens as
+ * consideration.
+ * @custom:param offerFulfillments An array of FulfillmentComponent
+ * arrays indicating which offer
+ * items to attempt to aggregate
+ * when preparing executions. Note
+ * that any offer items not included
+ * as part of a fulfillment will be
+ * sent unaggregated to the caller.
+ * @custom:param considerationFulfillments An array of FulfillmentComponent
+ * arrays indicating which
+ * consideration items to attempt to
+ * aggregate when preparing
+ * executions.
+ * @param fulfillerConduitKey A bytes32 value indicating what
+ * conduit, if any, to source the
+ * fulfiller's token approvals from.
+ * The zero hash signifies that no
+ * conduit should be used (and
+ * direct approvals set on this
+ * contract).
+ * @param maximumFulfilled The maximum number of orders to
+ * fulfill.
*
* @return availableOrders An array of booleans indicating if each order
* with an index corresponding to the index of the
@@ -219,24 +304,50 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* orders.
*/
function fulfillAvailableOrders(
- Order[] calldata orders,
- FulfillmentComponent[][] calldata offerFulfillments,
- FulfillmentComponent[][] calldata considerationFulfillments,
+ /**
+ * @custom:name orders
+ */
+ Order[] calldata,
+ /**
+ * @custom:name offerFulfillments
+ */
+ FulfillmentComponent[][] calldata,
+ /**
+ * @custom:name considerationFulfillments
+ */
+ FulfillmentComponent[][] calldata,
bytes32 fulfillerConduitKey,
uint256 maximumFulfilled
)
external
payable
override
- returns (bool[] memory availableOrders, Execution[] memory executions)
+ returns (
+ bool[] memory /* availableOrders */,
+ Execution[] memory /* executions */
+ )
{
// Convert orders to "advanced" orders and fulfill all available orders.
return
_fulfillAvailableAdvancedOrders(
- _convertOrdersToAdvanced(orders), // Convert to advanced orders.
+ _toAdvancedOrdersReturnType(_decodeOrdersAsAdvancedOrders)(
+ CalldataStart.pptr()
+ ), // Convert to advanced orders.
new CriteriaResolver[](0), // No criteria resolvers supplied.
- offerFulfillments,
- considerationFulfillments,
+ _toNestedFulfillmentComponentsReturnType(
+ _decodeNestedFulfillmentComponents
+ )(
+ CalldataStart.pptr(
+ Offset_fulfillAvailableOrders_offerFulfillments
+ )
+ ),
+ _toNestedFulfillmentComponentsReturnType(
+ _decodeNestedFulfillmentComponents
+ )(
+ CalldataStart.pptr(
+ Offset_fulfillAvailableOrders_considerationFulfillments
+ )
+ ),
fulfillerConduitKey,
msg.sender,
maximumFulfilled
@@ -256,49 +367,63 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* recipient, respectively. Note that a failing item transfer or an
* issue with order formatting will cause the entire batch to fail.
*
- * @param advancedOrders The orders to fulfill along with the
- * fraction of those orders to attempt to
- * fill. Note that both the offerer and the
- * fulfiller must first approve this
- * contract (or their conduit if indicated
- * by the order) to transfer any relevant
- * tokens on their behalf and that
- * contracts must implement
- * `onERC1155Received` in order to receive
- * ERC1155 tokens as consideration. Also
- * note that all offer and consideration
- * components must have no remainder after
- * multiplication of the respective amount
- * with the supplied fraction for an
- * order's partial fill amount to be
- * considered valid.
- * @param criteriaResolvers An array where each element contains a
- * reference to a specific offer or
- * consideration, a token identifier, and a
- * proof that the supplied token identifier
- * is contained in the merkle root held by
- * the item in question's criteria element.
- * Note that an empty criteria indicates
- * that any (transferable) token
- * identifier on the token in question is
- * valid and that no associated proof needs
- * to be supplied.
- * @param offerFulfillments An array of FulfillmentComponent arrays
- * indicating which offer items to attempt
- * to aggregate when preparing executions.
- * @param considerationFulfillments An array of FulfillmentComponent arrays
- * indicating which consideration items to
- * attempt to aggregate when preparing
- * executions.
- * @param fulfillerConduitKey A bytes32 value indicating what conduit,
- * if any, to source the fulfiller's token
- * approvals from. The zero hash signifies
- * that no conduit should be used (and
- * direct approvals set on Consideration).
- * @param recipient The intended recipient for all received
- * items, with `address(0)` indicating that
- * the caller should receive the items.
- * @param maximumFulfilled The maximum number of orders to fulfill.
+ * @custom:param advancedOrders The orders to fulfill along with
+ * the fraction of those orders to
+ * attempt to fill. Note that both
+ * the offerer and the fulfiller
+ * must first approve this contract
+ * (or their conduit if indicated by
+ * the order) to transfer any
+ * relevant tokens on their behalf
+ * and that contracts must implement
+ * `onERC1155Received` to receive
+ * ERC1155 tokens as consideration.
+ * Also note that all offer and
+ * consideration components must
+ * have no remainder after
+ * multiplication of the respective
+ * amount with the supplied fraction
+ * for an order's partial fill
+ * amount to be considered valid.
+ * @custom:param criteriaResolvers An array where each element
+ * contains a reference to a
+ * specific offer or consideration,
+ * a token identifier, and a proof
+ * that the supplied token
+ * identifier is contained in the
+ * merkle root held by the item in
+ * question's criteria element. Note
+ * that an empty criteria indicates
+ * that any (transferable) token
+ * identifier on the token in
+ * question is valid and that no
+ * associated proof needs to be
+ * supplied.
+ * @custom:param offerFulfillments An array of FulfillmentComponent
+ * arrays indicating which offer
+ * items to attempt to aggregate
+ * when preparing executions. Note
+ * that any offer items not included
+ * as part of a fulfillment will be
+ * sent unaggregated to the caller.
+ * @custom:param considerationFulfillments An array of FulfillmentComponent
+ * arrays indicating which
+ * consideration items to attempt to
+ * aggregate when preparing
+ * executions.
+ * @param fulfillerConduitKey A bytes32 value indicating what
+ * conduit, if any, to source the
+ * fulfiller's token approvals from.
+ * The zero hash signifies that no
+ * conduit should be used (and
+ * direct approvals set on this
+ * contract).
+ * @param recipient The intended recipient for all
+ * received items, with `address(0)`
+ * indicating that the caller should
+ * receive the offer items.
+ * @param maximumFulfilled The maximum number of orders to
+ * fulfill.
*
* @return availableOrders An array of booleans indicating if each order
* with an index corresponding to the index of the
@@ -308,10 +433,22 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* orders.
*/
function fulfillAvailableAdvancedOrders(
- AdvancedOrder[] memory advancedOrders,
- CriteriaResolver[] calldata criteriaResolvers,
- FulfillmentComponent[][] calldata offerFulfillments,
- FulfillmentComponent[][] calldata considerationFulfillments,
+ /**
+ * @custom:name advancedOrders
+ */
+ AdvancedOrder[] calldata,
+ /**
+ * @custom:name criteriaResolvers
+ */
+ CriteriaResolver[] calldata,
+ /**
+ * @custom:name offerFulfillments
+ */
+ FulfillmentComponent[][] calldata,
+ /**
+ * @custom:name considerationFulfillments
+ */
+ FulfillmentComponent[][] calldata,
bytes32 fulfillerConduitKey,
address recipient,
uint256 maximumFulfilled
@@ -319,17 +456,38 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
external
payable
override
- returns (bool[] memory availableOrders, Execution[] memory executions)
+ returns (
+ bool[] memory /* availableOrders */,
+ Execution[] memory /* executions */
+ )
{
// Fulfill all available orders.
return
_fulfillAvailableAdvancedOrders(
- advancedOrders,
- criteriaResolvers,
- offerFulfillments,
- considerationFulfillments,
+ _toAdvancedOrdersReturnType(_decodeAdvancedOrders)(
+ CalldataStart.pptr()
+ ),
+ _toCriteriaResolversReturnType(_decodeCriteriaResolvers)(
+ CalldataStart.pptr(
+ Offset_fulfillAvailableAdvancedOrders_criteriaResolvers
+ )
+ ),
+ _toNestedFulfillmentComponentsReturnType(
+ _decodeNestedFulfillmentComponents
+ )(
+ CalldataStart.pptr(
+ Offset_fulfillAvailableAdvancedOrders_offerFulfillments
+ )
+ ),
+ _toNestedFulfillmentComponentsReturnType(
+ _decodeNestedFulfillmentComponents
+ )(
+ CalldataStart.pptr(
+ Offset_fulfillAvailableAdvancedOrders_cnsdrationFlflmnts
+ )
+ ),
fulfillerConduitKey,
- recipient == address(0) ? msg.sender : recipient,
+ _substituteCallerForEmptyRecipient(recipient),
maximumFulfilled
);
}
@@ -340,84 +498,135 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* fulfillments allocating offer components to consideration
* components. Note that this function does not support
* criteria-based or partial filling of orders (though filling the
- * remainder of a partially-filled order is supported).
- *
- * @param orders The orders to match. Note that both the offerer
- * and fulfiller on each order must first approve
- * this contract (or their conduit if indicated by
- * the order) to transfer any relevant tokens on
- * their behalf and each consideration recipient
- * must implement `onERC1155Received` in order to
- * receive ERC1155 tokens.
- * @param fulfillments An array of elements allocating offer components
- * to consideration components. Note that each
- * consideration component must be fully met in
- * order for the match operation to be valid.
+ * remainder of a partially-filled order is supported). Any unspent
+ * offer item amounts or native tokens will be transferred to the
+ * caller.
+ *
+ * @custom:param orders The orders to match. Note that both the
+ * offerer and fulfiller on each order must first
+ * approve this contract (or their conduit if
+ * indicated by the order) to transfer any
+ * relevant tokens on their behalf and each
+ * consideration recipient must implement
+ * `onERC1155Received` to receive ERC1155 tokens.
+ * @custom:param fulfillments An array of elements allocating offer
+ * components to consideration components. Note
+ * that each consideration component must be
+ * fully met for the match operation to be valid,
+ * and that any unspent offer items will be sent
+ * unaggregated to the caller.
*
* @return executions An array of elements indicating the sequence of
* transfers performed as part of matching the given
- * orders.
+ * orders. Note that unspent offer item amounts or native
+ * tokens will not be reflected as part of this array.
*/
function matchOrders(
- Order[] calldata orders,
- Fulfillment[] calldata fulfillments
- ) external payable override returns (Execution[] memory executions) {
+ /**
+ * @custom:name orders
+ */
+ Order[] calldata,
+ /**
+ * @custom:name fulfillments
+ */
+ Fulfillment[] calldata
+ ) external payable override returns (Execution[] memory /* executions */) {
// Convert to advanced, validate, and match orders using fulfillments.
return
_matchAdvancedOrders(
- _convertOrdersToAdvanced(orders),
+ _toAdvancedOrdersReturnType(_decodeOrdersAsAdvancedOrders)(
+ CalldataStart.pptr()
+ ),
new CriteriaResolver[](0), // No criteria resolvers supplied.
- fulfillments
+ _toFulfillmentsReturnType(_decodeFulfillments)(
+ CalldataStart.pptr(Offset_matchOrders_fulfillments)
+ ),
+ msg.sender
);
}
/**
- * @notice Match an arbitrary number of full or partial orders, each with an
- * arbitrary number of items for offer and consideration, supplying
- * criteria resolvers containing specific token identifiers and
- * associated proofs as well as fulfillments allocating offer
- * components to consideration components.
- *
- * @param advancedOrders The advanced orders to match. Note that both the
- * offerer and fulfiller on each order must first
- * approve this contract (or their conduit if
- * indicated by the order) to transfer any relevant
- * tokens on their behalf and each consideration
- * recipient must implement `onERC1155Received` in
- * order to receive ERC1155 tokens. Also note that
- * the offer and consideration components for each
- * order must have no remainder after multiplying
- * the respective amount with the supplied fraction
- * in order for the group of partial fills to be
- * considered valid.
- * @param criteriaResolvers An array where each element contains a reference
- * to a specific order as well as that order's
- * offer or consideration, a token identifier, and
- * a proof that the supplied token identifier is
- * contained in the order's merkle root. Note that
- * an empty root indicates that any (transferable)
- * token identifier is valid and that no associated
- * proof needs to be supplied.
- * @param fulfillments An array of elements allocating offer components
- * to consideration components. Note that each
- * consideration component must be fully met in
- * order for the match operation to be valid.
+ * @notice Match an arbitrary number of full, partial, or contract orders,
+ * each with an arbitrary number of items for offer and
+ * consideration, supplying criteria resolvers containing specific
+ * token identifiers and associated proofs as well as fulfillments
+ * allocating offer components to consideration components. Any
+ * unspent offer item amounts will be transferred to the designated
+ * recipient (with the null address signifying to use the caller)
+ * and any unspent native tokens will be returned to the caller.
+ *
+ * @custom:param advancedOrders The advanced orders to match. Note that
+ * both the offerer and fulfiller on each
+ * order must first approve this contract
+ * (or their conduit if indicated by the
+ * order) to transfer any relevant tokens on
+ * their behalf and each consideration
+ * recipient must implement
+ * `onERC1155Received` to receive ERC1155
+ * tokens. Also note that the offer and
+ * consideration components for each order
+ * must have no remainder after multiplying
+ * the respective amount with the supplied
+ * fraction for the group of partial fills
+ * to be considered valid.
+ * @custom:param criteriaResolvers An array where each element contains a
+ * reference to a specific offer or
+ * consideration, a token identifier, and a
+ * proof that the supplied token identifier
+ * is contained in the merkle root held by
+ * the item in question's criteria element.
+ * Note that an empty criteria indicates
+ * that any (transferable) token identifier
+ * on the token in question is valid and
+ * that no associated proof needs to be
+ * supplied.
+ * @custom:param fulfillments An array of elements allocating offer
+ * components to consideration components.
+ * Note that each consideration component
+ * must be fully met for the match operation
+ * to be valid, and that any unspent offer
+ * items will be sent unaggregated to the
+ * designated recipient.
+ * @param recipient The intended recipient for all unspent
+ * offer item amounts, or the caller if the
+ * null address is supplied.
*
* @return executions An array of elements indicating the sequence of
- * transfers performed as part of matching the given
- * orders.
+ * transfers performed as part of matching the given
+ * orders. Note that unspent offer item amounts or
+ * native tokens will not be reflected as part of this
+ * array.
*/
function matchAdvancedOrders(
- AdvancedOrder[] memory advancedOrders,
- CriteriaResolver[] calldata criteriaResolvers,
- Fulfillment[] calldata fulfillments
- ) external payable override returns (Execution[] memory executions) {
+ /**
+ * @custom:name advancedOrders
+ */
+ AdvancedOrder[] calldata,
+ /**
+ * @custom:name criteriaResolvers
+ */
+ CriteriaResolver[] calldata,
+ /**
+ * @custom:name fulfillments
+ */
+ Fulfillment[] calldata,
+ address recipient
+ ) external payable override returns (Execution[] memory /* executions */) {
// Validate and match the advanced orders using supplied fulfillments.
return
_matchAdvancedOrders(
- advancedOrders,
- criteriaResolvers,
- fulfillments
+ _toAdvancedOrdersReturnType(_decodeAdvancedOrders)(
+ CalldataStart.pptr()
+ ),
+ _toCriteriaResolversReturnType(_decodeCriteriaResolvers)(
+ CalldataStart.pptr(
+ Offset_matchAdvancedOrders_criteriaResolvers
+ )
+ ),
+ _toFulfillmentsReturnType(_decodeFulfillments)(
+ CalldataStart.pptr(Offset_matchAdvancedOrders_fulfillments)
+ ),
+ _substituteCallerForEmptyRecipient(recipient)
);
}
@@ -432,11 +641,9 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* @return cancelled A boolean indicating whether the supplied orders have
* been successfully cancelled.
*/
- function cancel(OrderComponents[] calldata orders)
- external
- override
- returns (bool cancelled)
- {
+ function cancel(
+ OrderComponents[] calldata orders
+ ) external override returns (bool cancelled) {
// Cancel the orders.
cancelled = _cancel(orders);
}
@@ -451,18 +658,19 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* that anyone can validate a signed order, but only the offerer can
* validate an order without supplying a signature.
*
- * @param orders The orders to validate.
+ * @custom:param orders The orders to validate.
*
* @return validated A boolean indicating whether the supplied orders have
* been successfully validated.
*/
- function validate(Order[] calldata orders)
- external
- override
- returns (bool validated)
- {
- // Validate the orders.
- validated = _validate(orders);
+ function validate(
+ /**
+ * @custom:name orders
+ */
+ Order[] calldata
+ ) external override returns (bool /* validated */) {
+ return
+ _validate(_toOrdersReturnType(_decodeOrders)(CalldataStart.pptr()));
}
/**
@@ -473,46 +681,44 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* @return newCounter The new counter.
*/
function incrementCounter() external override returns (uint256 newCounter) {
- // Increment current counter for the supplied offerer.
+ // Increment current counter for the supplied offerer. Note that the
+ // counter is incremented by a large, quasi-random interval.
newCounter = _incrementCounter();
}
/**
* @notice Retrieve the order hash for a given order.
*
- * @param order The components of the order.
+ * @custom:param order The components of the order.
*
* @return orderHash The order hash.
*/
- function getOrderHash(OrderComponents calldata order)
- external
- view
- override
- returns (bytes32 orderHash)
- {
+ function getOrderHash(
+ /**
+ * @custom:name order
+ */
+ OrderComponents calldata
+ ) external view override returns (bytes32 orderHash) {
+ CalldataPointer orderPointer = CalldataStart.pptr();
+
// Derive order hash by supplying order parameters along with counter.
orderHash = _deriveOrderHash(
- OrderParameters(
- order.offerer,
- order.zone,
- order.offer,
- order.consideration,
- order.orderType,
- order.startTime,
- order.endTime,
- order.zoneHash,
- order.salt,
- order.conduitKey,
- order.consideration.length
- ),
- order.counter
+ _toOrderParametersReturnType(
+ _decodeOrderComponentsAsOrderParameters
+ )(orderPointer),
+ // Read order counter
+ orderPointer.offset(OrderParameters_counter_offset).readUint256()
);
}
/**
* @notice Retrieve the status of a given order by hash, including whether
* the order has been cancelled or validated and the fraction of the
- * order that has been filled.
+ * order that has been filled. Since the _orderStatus[orderHash]
+ * does not get set for contract orders, getOrderStatus will always
+ * return (false, false, 0, 0) for those hashes. Note that this
+ * function is susceptible to view reentrancy and so should be used
+ * with care when calling from other contracts.
*
* @param orderHash The order hash in question.
*
@@ -526,7 +732,9 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
* @return totalSize The total size of the order that is either filled or
* unfilled (i.e. the "denominator").
*/
- function getOrderStatus(bytes32 orderHash)
+ function getOrderStatus(
+ bytes32 orderHash
+ )
external
view
override
@@ -548,12 +756,9 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
*
* @return counter The current counter.
*/
- function getCounter(address offerer)
- external
- view
- override
- returns (uint256 counter)
- {
+ function getCounter(
+ address offerer
+ ) external view override returns (uint256 counter) {
// Return the counter for the supplied offerer.
counter = _getCounter(offerer);
}
@@ -579,6 +784,21 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
return _information();
}
+ /**
+ * @dev Gets the contract offerer nonce for the specified contract offerer.
+ * Note that this function is susceptible to view reentrancy and so
+ * should be used with care when calling from other contracts.
+ *
+ * @param contractOfferer The contract offerer for which to get the nonce.
+ *
+ * @return nonce The contract offerer nonce.
+ */
+ function getContractOffererNonce(
+ address contractOfferer
+ ) external view override returns (uint256 nonce) {
+ nonce = _contractNonces[contractOfferer];
+ }
+
/**
* @notice Retrieve the name of this contract.
*
@@ -588,9 +808,9 @@ contract Consideration is ConsiderationInterface, OrderCombiner {
external
pure
override
- returns (string memory contractName)
+ returns (string memory /* contractName */)
{
// Return the name of the contract.
- contractName = _name();
+ return _name();
}
}
diff --git a/contracts/lib/ConsiderationBase.sol b/contracts/lib/ConsiderationBase.sol
index f7e46be2a..074fe2cf9 100644
--- a/contracts/lib/ConsiderationBase.sol
+++ b/contracts/lib/ConsiderationBase.sol
@@ -1,24 +1,45 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
-// prettier-ignore
import {
ConduitControllerInterface
} from "../interfaces/ConduitControllerInterface.sol";
-// prettier-ignore
import {
ConsiderationEventsAndErrors
} from "../interfaces/ConsiderationEventsAndErrors.sol";
-import "./ConsiderationConstants.sol";
+import {
+ EIP712_domainData_chainId_offset,
+ EIP712_domainData_nameHash_offset,
+ EIP712_domainData_size,
+ EIP712_domainData_verifyingContract_offset,
+ EIP712_domainData_versionHash_offset,
+ FreeMemoryPointerSlot,
+ NameLengthPtr,
+ NameWithLength,
+ OneWord,
+ OneWordShift,
+ Slot0x80,
+ ThreeWords,
+ ZeroSlot
+} from "./ConsiderationConstants.sol";
+
+import { ConsiderationDecoder } from "./ConsiderationDecoder.sol";
+import { ConsiderationEncoder } from "./ConsiderationEncoder.sol";
+
+import { TypehashDirectory } from "./TypehashDirectory.sol";
/**
* @title ConsiderationBase
* @author 0age
* @notice ConsiderationBase contains immutable constants and constructor logic.
*/
-contract ConsiderationBase is ConsiderationEventsAndErrors {
+contract ConsiderationBase is
+ ConsiderationDecoder,
+ ConsiderationEncoder,
+ ConsiderationEventsAndErrors
+{
// Precompute hashes, original chainId, and domain separator on deployment.
bytes32 internal immutable _NAME_HASH;
bytes32 internal immutable _VERSION_HASH;
@@ -32,6 +53,9 @@ contract ConsiderationBase is ConsiderationEventsAndErrors {
// Allow for interaction with the conduit controller.
ConduitControllerInterface internal immutable _CONDUIT_CONTROLLER;
+ // BulkOrder typehash storage
+ TypehashDirectory internal immutable _BULK_ORDER_TYPEHASH_DIRECTORY;
+
// Cache the conduit creation code hash used by the conduit controller.
bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH;
@@ -54,6 +78,8 @@ contract ConsiderationBase is ConsiderationEventsAndErrors {
_ORDER_TYPEHASH
) = _deriveTypehashes();
+ _BULK_ORDER_TYPEHASH_DIRECTORY = new TypehashDirectory();
+
// Store the current chainId and derive the current domain separator.
_CHAIN_ID = block.chainid;
_DOMAIN_SEPARATOR = _deriveDomainSeparator();
@@ -70,19 +96,48 @@ contract ConsiderationBase is ConsiderationEventsAndErrors {
/**
* @dev Internal view function to derive the EIP-712 domain separator.
*
- * @return The derived domain separator.
+ * @return domainSeparator The derived domain separator.
*/
- function _deriveDomainSeparator() internal view returns (bytes32) {
- // prettier-ignore
- return keccak256(
- abi.encode(
- _EIP_712_DOMAIN_TYPEHASH,
- _NAME_HASH,
- _VERSION_HASH,
- block.chainid,
- address(this)
- )
- );
+ function _deriveDomainSeparator()
+ internal
+ view
+ returns (bytes32 domainSeparator)
+ {
+ bytes32 typehash = _EIP_712_DOMAIN_TYPEHASH;
+ bytes32 nameHash = _NAME_HASH;
+ bytes32 versionHash = _VERSION_HASH;
+
+ // Leverage scratch space and other memory to perform an efficient hash.
+ assembly {
+ // Retrieve the free memory pointer; it will be replaced afterwards.
+ let freeMemoryPointer := mload(FreeMemoryPointerSlot)
+
+ // Retrieve value at 0x80; it will also be replaced afterwards.
+ let slot0x80 := mload(Slot0x80)
+
+ // Place typehash, name hash, and version hash at start of memory.
+ mstore(0, typehash)
+ mstore(EIP712_domainData_nameHash_offset, nameHash)
+ mstore(EIP712_domainData_versionHash_offset, versionHash)
+
+ // Place chainId in the next memory location.
+ mstore(EIP712_domainData_chainId_offset, chainid())
+
+ // Place the address of this contract in the next memory location.
+ mstore(EIP712_domainData_verifyingContract_offset, address())
+
+ // Hash relevant region of memory to derive the domain separator.
+ domainSeparator := keccak256(0, EIP712_domainData_size)
+
+ // Restore the free memory pointer.
+ mstore(FreeMemoryPointerSlot, freeMemoryPointer)
+
+ // Restore the zero slot to zero.
+ mstore(ZeroSlot, 0)
+
+ // Restore the value at 0x80.
+ mstore(Slot0x80, slot0x80)
+ }
}
/**
@@ -152,60 +207,56 @@ contract ConsiderationBase is ConsiderationEventsAndErrors {
nameHash = keccak256(bytes(_nameString()));
// Derive hash of the version string of the contract.
- versionHash = keccak256(bytes("1.1"));
+ versionHash = keccak256(bytes("1.2"));
// Construct the OfferItem type string.
- // prettier-ignore
- bytes memory offerItemTypeString = abi.encodePacked(
- "OfferItem(",
- "uint8 itemType,",
- "address token,",
- "uint256 identifierOrCriteria,",
- "uint256 startAmount,",
- "uint256 endAmount",
+ bytes memory offerItemTypeString = bytes(
+ "OfferItem("
+ "uint8 itemType,"
+ "address token,"
+ "uint256 identifierOrCriteria,"
+ "uint256 startAmount,"
+ "uint256 endAmount"
")"
);
// Construct the ConsiderationItem type string.
- // prettier-ignore
- bytes memory considerationItemTypeString = abi.encodePacked(
- "ConsiderationItem(",
- "uint8 itemType,",
- "address token,",
- "uint256 identifierOrCriteria,",
- "uint256 startAmount,",
- "uint256 endAmount,",
- "address recipient",
+ bytes memory considerationItemTypeString = bytes(
+ "ConsiderationItem("
+ "uint8 itemType,"
+ "address token,"
+ "uint256 identifierOrCriteria,"
+ "uint256 startAmount,"
+ "uint256 endAmount,"
+ "address recipient"
")"
);
// Construct the OrderComponents type string, not including the above.
- // prettier-ignore
- bytes memory orderComponentsPartialTypeString = abi.encodePacked(
- "OrderComponents(",
- "address offerer,",
- "address zone,",
- "OfferItem[] offer,",
- "ConsiderationItem[] consideration,",
- "uint8 orderType,",
- "uint256 startTime,",
- "uint256 endTime,",
- "bytes32 zoneHash,",
- "uint256 salt,",
- "bytes32 conduitKey,",
- "uint256 counter",
+ bytes memory orderComponentsPartialTypeString = bytes(
+ "OrderComponents("
+ "address offerer,"
+ "address zone,"
+ "OfferItem[] offer,"
+ "ConsiderationItem[] consideration,"
+ "uint8 orderType,"
+ "uint256 startTime,"
+ "uint256 endTime,"
+ "bytes32 zoneHash,"
+ "uint256 salt,"
+ "bytes32 conduitKey,"
+ "uint256 counter"
")"
);
// Construct the primary EIP-712 domain type string.
- // prettier-ignore
eip712DomainTypehash = keccak256(
- abi.encodePacked(
- "EIP712Domain(",
- "string name,",
- "string version,",
- "uint256 chainId,",
- "address verifyingContract",
+ bytes(
+ "EIP712Domain("
+ "string name,"
+ "string version,"
+ "uint256 chainId,"
+ "address verifyingContract"
")"
)
);
@@ -216,13 +267,24 @@ contract ConsiderationBase is ConsiderationEventsAndErrors {
// Derive ConsiderationItem type hash using corresponding type string.
considerationItemTypehash = keccak256(considerationItemTypeString);
- // Derive OrderItem type hash via combination of relevant type strings.
- orderTypehash = keccak256(
- abi.encodePacked(
- orderComponentsPartialTypeString,
- considerationItemTypeString,
- offerItemTypeString
- )
+ bytes memory orderTypeString = bytes.concat(
+ orderComponentsPartialTypeString,
+ considerationItemTypeString,
+ offerItemTypeString
);
+
+ // Derive OrderItem type hash via combination of relevant type strings.
+ orderTypehash = keccak256(orderTypeString);
+ }
+
+ function _lookupBulkOrderTypehash(
+ uint256 treeHeight
+ ) internal view returns (bytes32 typeHash) {
+ TypehashDirectory directory = _BULK_ORDER_TYPEHASH_DIRECTORY;
+ assembly {
+ let typeHashOffset := add(1, shl(OneWordShift, sub(treeHeight, 1)))
+ extcodecopy(directory, 0, typeHashOffset, OneWord)
+ typeHash := mload(0)
+ }
}
}
diff --git a/contracts/lib/ConsiderationConstants.sol b/contracts/lib/ConsiderationConstants.sol
index 12f4b0963..23848c64d 100644
--- a/contracts/lib/ConsiderationConstants.sol
+++ b/contracts/lib/ConsiderationConstants.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
/*
* -------------------------- Disambiguation & Other Notes ---------------------
@@ -8,7 +8,7 @@ pragma solidity >=0.8.7;
* offset or pointer to the body of a dynamic type. In calldata, the head
* is always an offset (relative to the parent object), while in memory,
* the head is always the pointer to the body. More information found here:
- * https://docs.soliditylang.org/en/v0.8.14/abi-spec.html#argument-encoding
+ * https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#argument-encoding
* - Note that the length of an array is separate from and precedes the
* head of the array.
*
@@ -38,30 +38,62 @@ pragma solidity >=0.8.7;
// Name is right padded, so it touches the length which is left padded. This
// enables writing both values at once. Length goes at byte 95 in memory, and
// name fills bytes 96-109, so both values can be written left-padded to 77.
-uint256 constant NameLengthPtr = 77;
+uint256 constant NameLengthPtr = 0x4D;
uint256 constant NameWithLength = 0x0d436F6E73696465726174696F6E;
-uint256 constant Version = 0x312e31;
-uint256 constant Version_length = 3;
-uint256 constant Version_shift = 0xe8;
+uint256 constant information_version_offset = 0;
+uint256 constant information_version_cd_offset = 0x60;
+uint256 constant information_domainSeparator_offset = 0x20;
+uint256 constant information_conduitController_offset = 0x40;
+uint256 constant information_versionLengthPtr = 0x63;
+uint256 constant information_versionWithLength = 0x03312e32; // 1.2
+uint256 constant information_length = 0xa0;
uint256 constant _NOT_ENTERED = 1;
uint256 constant _ENTERED = 2;
+uint256 constant _ENTERED_AND_ACCEPTING_NATIVE_TOKENS = 3;
+
+uint256 constant Offset_fulfillAdvancedOrder_criteriaResolvers = 0x20;
+uint256 constant Offset_fulfillAvailableOrders_offerFulfillments = 0x20;
+uint256 constant Offset_fulfillAvailableOrders_considerationFulfillments = 0x40;
+uint256 constant Offset_fulfillAvailableAdvancedOrders_criteriaResolvers = 0x20;
+uint256 constant Offset_fulfillAvailableAdvancedOrders_offerFulfillments = 0x40;
+uint256 constant Offset_fulfillAvailableAdvancedOrders_cnsdrationFlflmnts = (
+ 0x60
+);
+
+uint256 constant Offset_matchOrders_fulfillments = 0x20;
+
+uint256 constant Offset_matchAdvancedOrders_criteriaResolvers = 0x20;
+uint256 constant Offset_matchAdvancedOrders_fulfillments = 0x40;
// Common Offsets
// Offsets for identically positioned fields shared by:
// OfferItem, ConsiderationItem, SpentItem, ReceivedItem
+uint256 constant Selector_length = 0x4;
+
uint256 constant Common_token_offset = 0x20;
uint256 constant Common_identifier_offset = 0x40;
uint256 constant Common_amount_offset = 0x60;
+uint256 constant Common_endAmount_offset = 0x80;
+
+uint256 constant SpentItem_size = 0x80;
+uint256 constant SpentItem_size_shift = 0x7;
+
+uint256 constant OfferItem_size = 0xa0;
+uint256 constant OfferItem_size_with_length = 0xc0;
+uint256 constant ReceivedItem_size_excluding_recipient = 0x80;
uint256 constant ReceivedItem_size = 0xa0;
uint256 constant ReceivedItem_amount_offset = 0x60;
uint256 constant ReceivedItem_recipient_offset = 0x80;
uint256 constant ReceivedItem_CommonParams_size = 0x60;
+uint256 constant ConsiderationItem_size = 0xc0;
+uint256 constant ConsiderationItem_size_with_length = 0xe0;
+
uint256 constant ConsiderationItem_recipient_offset = 0xa0;
// Store the same constant in an abbreviated format for a line length fix.
uint256 constant ConsiderItem_recipient_offset = 0xa0;
@@ -69,39 +101,43 @@ uint256 constant ConsiderItem_recipient_offset = 0xa0;
uint256 constant Execution_offerer_offset = 0x20;
uint256 constant Execution_conduit_offset = 0x40;
-uint256 constant InvalidFulfillmentComponentData_error_signature = (
- 0x7fda727900000000000000000000000000000000000000000000000000000000
-);
-uint256 constant InvalidFulfillmentComponentData_error_len = 0x04;
-
-uint256 constant Panic_error_signature = (
- 0x4e487b7100000000000000000000000000000000000000000000000000000000
-);
-uint256 constant Panic_error_offset = 0x04;
-uint256 constant Panic_error_length = 0x24;
-uint256 constant Panic_arithmetic = 0x11;
-
-uint256 constant MissingItemAmount_error_signature = (
- 0x91b3e51400000000000000000000000000000000000000000000000000000000
-);
-uint256 constant MissingItemAmount_error_len = 0x04;
-
+// uint256 constant OrderParameters_offerer_offset = 0x00;
+uint256 constant OrderParameters_zone_offset = 0x20;
uint256 constant OrderParameters_offer_head_offset = 0x40;
uint256 constant OrderParameters_consideration_head_offset = 0x60;
+// uint256 constant OrderParameters_orderType_offset = 0x80;
+uint256 constant OrderParameters_startTime_offset = 0xa0;
+uint256 constant OrderParameters_endTime_offset = 0xc0;
+uint256 constant OrderParameters_zoneHash_offset = 0xe0;
+// uint256 constant OrderParameters_salt_offset = 0x100;
uint256 constant OrderParameters_conduit_offset = 0x120;
uint256 constant OrderParameters_counter_offset = 0x140;
uint256 constant Fulfillment_itemIndex_offset = 0x20;
+uint256 constant AdvancedOrder_head_size = 0xa0;
uint256 constant AdvancedOrder_numerator_offset = 0x20;
+uint256 constant AdvancedOrder_denominator_offset = 0x40;
+uint256 constant AdvancedOrder_signature_offset = 0x60;
+uint256 constant AdvancedOrder_extraData_offset = 0x80;
+
+uint256 constant OrderStatus_ValidatedAndNotCancelled = 1;
+uint256 constant OrderStatus_filledNumerator_offset = 0x10;
+uint256 constant OrderStatus_filledDenominator_offset = 0x88;
-uint256 constant AlmostOneWord = 0x1f;
+uint256 constant ThirtyOneBytes = 0x1f;
uint256 constant OneWord = 0x20;
uint256 constant TwoWords = 0x40;
uint256 constant ThreeWords = 0x60;
uint256 constant FourWords = 0x80;
uint256 constant FiveWords = 0xa0;
+uint256 constant OneWordShift = 0x5;
+uint256 constant TwoWordsShift = 0x6;
+
+uint256 constant SixtyThreeBytes = 0x3f;
+uint256 constant OnlyFullWordMask = 0xffffffe0;
+
uint256 constant FreeMemoryPointerSlot = 0x40;
uint256 constant ZeroSlot = 0x60;
uint256 constant DefaultFreeMemoryPointer = 0x80;
@@ -109,19 +145,47 @@ uint256 constant DefaultFreeMemoryPointer = 0x80;
uint256 constant Slot0x80 = 0x80;
uint256 constant Slot0xA0 = 0xa0;
-uint256 constant BasicOrder_endAmount_cdPtr = 0x104;
+// uint256 constant BasicOrder_endAmount_cdPtr = 0x104;
uint256 constant BasicOrder_common_params_size = 0xa0;
uint256 constant BasicOrder_considerationHashesArray_ptr = 0x160;
+uint256 constant BasicOrder_receivedItemByteMap = (
+ 0x0000010102030000000000000000000000000000000000000000000000000000
+);
+uint256 constant BasicOrder_offeredItemByteMap = (
+ 0x0203020301010000000000000000000000000000000000000000000000000000
+);
+
+bytes32 constant OrdersMatchedTopic0 = (
+ 0x4b9f2d36e1b4c93de62cc077b00b1a91d84b6c31b4a14e012718dcca230689e7
+);
uint256 constant EIP712_Order_size = 0x180;
uint256 constant EIP712_OfferItem_size = 0xc0;
uint256 constant EIP712_ConsiderationItem_size = 0xe0;
-uint256 constant AdditionalRecipients_size = 0x40;
+uint256 constant AdditionalRecipient_size = 0x40;
+uint256 constant AdditionalRecipient_size_shift = 0x6;
uint256 constant EIP712_DomainSeparator_offset = 0x02;
uint256 constant EIP712_OrderHash_offset = 0x22;
uint256 constant EIP712_DigestPayload_size = 0x42;
+uint256 constant EIP712_domainData_nameHash_offset = 0x20;
+uint256 constant EIP712_domainData_versionHash_offset = 0x40;
+uint256 constant EIP712_domainData_chainId_offset = 0x60;
+uint256 constant EIP712_domainData_verifyingContract_offset = 0x80;
+uint256 constant EIP712_domainData_size = 0xa0;
+
+// Minimum BulkOrder proof size: 64 bytes for signature + 3 for key + 32 for 1
+// sibling. Maximum BulkOrder proof size: 65 bytes for signature + 3 for key +
+// 768 for 24 siblings.
+
+uint256 constant BulkOrderProof_minSize = 0x63;
+uint256 constant BulkOrderProof_rangeSize = 0x2e2;
+uint256 constant BulkOrderProof_lengthAdjustmentBeforeMask = 0x1d;
+uint256 constant BulkOrderProof_lengthRangeAfterMask = 0x2;
+uint256 constant BulkOrderProof_keyShift = 0xe8;
+uint256 constant BulkOrderProof_keySize = 0x3;
+
uint256 constant receivedItemsHash_ptr = 0x60;
/*
@@ -172,6 +236,11 @@ uint256 constant OrderFulfilled_baseOffset = 0x180;
uint256 constant OrderFulfilled_consideration_length_baseOffset = 0x2a0;
uint256 constant OrderFulfilled_offer_length_baseOffset = 0x200;
+// Related constants used for restricted order checks on basic orders.
+uint256 constant OrderFulfilled_baseDataSize = 0x160;
+// uint256 constant ValidateOrder_offerDataOffset = 0x184;
+// uint256 constant RatifyOrder_offerDataOffset = 0xc4;
+
// uint256 constant OrderFulfilled_orderHash_offset = 0x00;
uint256 constant OrderFulfilled_fulfiller_offset = 0x20;
uint256 constant OrderFulfilled_offer_head_offset = 0x40;
@@ -182,16 +251,16 @@ uint256 constant OrderFulfilled_consideration_body_offset = 0x120;
// BasicOrderParameters
uint256 constant BasicOrder_parameters_cdPtr = 0x04;
uint256 constant BasicOrder_considerationToken_cdPtr = 0x24;
-// uint256 constant BasicOrder_considerationIdentifier_cdPtr = 0x44;
+uint256 constant BasicOrder_considerationIdentifier_cdPtr = 0x44;
uint256 constant BasicOrder_considerationAmount_cdPtr = 0x64;
uint256 constant BasicOrder_offerer_cdPtr = 0x84;
uint256 constant BasicOrder_zone_cdPtr = 0xa4;
uint256 constant BasicOrder_offerToken_cdPtr = 0xc4;
-// uint256 constant BasicOrder_offerIdentifier_cdPtr = 0xe4;
+uint256 constant BasicOrder_offerIdentifier_cdPtr = 0xe4;
uint256 constant BasicOrder_offerAmount_cdPtr = 0x104;
uint256 constant BasicOrder_basicOrderType_cdPtr = 0x124;
uint256 constant BasicOrder_startTime_cdPtr = 0x144;
-// uint256 constant BasicOrder_endTime_cdPtr = 0x164;
+uint256 constant BasicOrder_endTime_cdPtr = 0x164;
// uint256 constant BasicOrder_zoneHash_cdPtr = 0x184;
// uint256 constant BasicOrder_salt_cdPtr = 0x1a4;
uint256 constant BasicOrder_offererConduit_cdPtr = 0x1c4;
@@ -201,9 +270,7 @@ uint256 constant BasicOrder_additionalRecipients_head_cdPtr = 0x224;
uint256 constant BasicOrder_signature_cdPtr = 0x244;
uint256 constant BasicOrder_additionalRecipients_length_cdPtr = 0x264;
uint256 constant BasicOrder_additionalRecipients_data_cdPtr = 0x284;
-
uint256 constant BasicOrder_parameters_ptr = 0x20;
-
uint256 constant BasicOrder_basicOrderType_range = 0x18; // 24 values
/*
@@ -235,7 +302,7 @@ uint256 constant BasicOrder_considerationItem_endAmount_ptr = 0x120;
* - 0x100: startAmount
* - 0x120: endAmount
*/
-uint256 constant BasicOrder_offerItem_typeHash_ptr = DefaultFreeMemoryPointer;
+uint256 constant BasicOrder_offerItem_typeHash_ptr = 0x80;
uint256 constant BasicOrder_offerItem_itemType_ptr = 0xa0;
uint256 constant BasicOrder_offerItem_token_ptr = 0xc0;
// uint256 constant BasicOrder_offerItem_identifier_ptr = 0xe0;
@@ -272,6 +339,11 @@ uint256 constant BasicOrder_order_startTime_ptr = 0x140;
uint256 constant BasicOrder_order_counter_ptr = 0x1e0;
uint256 constant BasicOrder_additionalRecipients_head_ptr = 0x240;
uint256 constant BasicOrder_signature_ptr = 0x260;
+uint256 constant BasicOrder_startTimeThroughZoneHash_size = 0x60;
+
+uint256 constant ContractOrder_orderHash_offerer_shift = 0x60;
+
+uint256 constant Counter_blockhash_shift = 0x80;
// Signature-related
bytes32 constant EIP2098_allButHighestBitMask = (
@@ -287,28 +359,18 @@ uint256 constant ECDSA_signature_v_offset = 0x60;
bytes32 constant EIP1271_isValidSignature_selector = (
0x1626ba7e00000000000000000000000000000000000000000000000000000000
);
-uint256 constant EIP1271_isValidSignature_signatureHead_negativeOffset = 0x20;
uint256 constant EIP1271_isValidSignature_digest_negativeOffset = 0x40;
uint256 constant EIP1271_isValidSignature_selector_negativeOffset = 0x44;
uint256 constant EIP1271_isValidSignature_calldata_baseLength = 0x64;
-
uint256 constant EIP1271_isValidSignature_signature_head_offset = 0x40;
-// abi.encodeWithSignature("NoContract(address)")
-uint256 constant NoContract_error_signature = (
- 0x5f15d67200000000000000000000000000000000000000000000000000000000
-);
-uint256 constant NoContract_error_sig_ptr = 0x0;
-uint256 constant NoContract_error_token_ptr = 0x4;
-uint256 constant NoContract_error_length = 0x24; // 4 + 32 == 36
-
uint256 constant EIP_712_PREFIX = (
0x1901000000000000000000000000000000000000000000000000000000000000
);
uint256 constant ExtraGasBuffer = 0x20;
-uint256 constant CostPerWord = 3;
-uint256 constant MemoryExpansionCoefficient = 0x200; // 512
+uint256 constant CostPerWord = 0x3;
+uint256 constant MemoryExpansionCoefficientShift = 0x9;
uint256 constant Create2AddressDerivation_ptr = 0x0b;
uint256 constant Create2AddressDerivation_length = 0x55;
@@ -316,11 +378,12 @@ uint256 constant Create2AddressDerivation_length = 0x55;
uint256 constant MaskOverByteTwelve = (
0x0000000000000000000000ff0000000000000000000000000000000000000000
);
-
uint256 constant MaskOverLastTwentyBytes = (
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
);
-
+uint256 constant AddressDirtyUpperBitThreshold = (
+ 0x0000000000000000000000010000000000000000000000000000000000000000
+);
uint256 constant MaskOverFirstFourBytes = (
0xffffffff00000000000000000000000000000000000000000000000000000000
);
@@ -334,7 +397,6 @@ uint256 constant MaxUint120 = 0xffffffffffffffffffffffffffffff;
uint256 constant Conduit_execute_ConduitTransfer_ptr = 0x20;
uint256 constant Conduit_execute_ConduitTransfer_length = 0x01;
-
uint256 constant Conduit_execute_ConduitTransfer_offset_ptr = 0x04;
uint256 constant Conduit_execute_ConduitTransfer_length_ptr = 0x24;
uint256 constant Conduit_execute_transferItemType_ptr = 0x44;
@@ -353,10 +415,9 @@ uint256 constant Accumulator_conduitKey_ptr = 0x20;
uint256 constant Accumulator_selector_ptr = 0x40;
uint256 constant Accumulator_array_offset_ptr = 0x44;
uint256 constant Accumulator_array_length_ptr = 0x64;
-
uint256 constant Accumulator_itemSizeOffsetDifference = 0x3c;
-
uint256 constant Accumulator_array_offset = 0x20;
+
uint256 constant Conduit_transferItem_size = 0xc0;
uint256 constant Conduit_transferItem_token_ptr = 0x20;
uint256 constant Conduit_transferItem_from_ptr = 0x40;
@@ -364,49 +425,88 @@ uint256 constant Conduit_transferItem_to_ptr = 0x60;
uint256 constant Conduit_transferItem_identifier_ptr = 0x80;
uint256 constant Conduit_transferItem_amount_ptr = 0xa0;
-// Declare constant for errors related to amount derivation.
-// error InexactFraction() @ AmountDerivationErrors.sol
-uint256 constant InexactFraction_error_signature = (
- 0xc63cf08900000000000000000000000000000000000000000000000000000000
-);
-uint256 constant InexactFraction_error_len = 0x04;
-
-// Declare constant for errors related to signature verification.
-uint256 constant Ecrecover_precompile = 1;
+uint256 constant Ecrecover_precompile = 0x1;
uint256 constant Ecrecover_args_size = 0x80;
uint256 constant Signature_lower_v = 27;
-// error BadSignatureV(uint8) @ SignatureVerificationErrors.sol
-uint256 constant BadSignatureV_error_signature = (
- 0x1f003d0a00000000000000000000000000000000000000000000000000000000
+// Bitmask that only gives a non-zero value if masked with a non-match selector.
+uint256 constant NonMatchSelector_MagicMask = (
+ 0x4000000000000000000000000000000000000000000000000000000000
);
-uint256 constant BadSignatureV_error_offset = 0x04;
-uint256 constant BadSignatureV_error_length = 0x24;
-// error InvalidSigner() @ SignatureVerificationErrors.sol
-uint256 constant InvalidSigner_error_signature = (
- 0x815e1d6400000000000000000000000000000000000000000000000000000000
+// First bit indicates that a NATIVE offer items has been used and the 231st bit
+// indicates that a non match selector has been called.
+uint256 constant NonMatchSelector_InvalidErrorValue = (
+ 0x4000000000000000000000000000000000000000000000000000000001
);
-uint256 constant InvalidSigner_error_length = 0x04;
-// error InvalidSignature() @ SignatureVerificationErrors.sol
-uint256 constant InvalidSignature_error_signature = (
- 0x8baa579f00000000000000000000000000000000000000000000000000000000
+/**
+ * @dev Selector and offsets for generateOrder
+ *
+ * function generateOrder(
+ * address fulfiller,
+ * SpentItem[] calldata minimumReceived,
+ * SpentItem[] calldata maximumSpent,
+ * bytes calldata context
+ * )
+ */
+uint256 constant generateOrder_selector = 0x98919765;
+uint256 constant generateOrder_selector_offset = 0x1c;
+uint256 constant generateOrder_head_offset = 0x04;
+uint256 constant generateOrder_minimumReceived_head_offset = 0x20;
+uint256 constant generateOrder_maximumSpent_head_offset = 0x40;
+uint256 constant generateOrder_context_head_offset = 0x60;
+uint256 constant generateOrder_base_tail_offset = 0x80;
+uint256 constant generateOrder_maximum_returndatasize = 0xffff;
+
+uint256 constant ratifyOrder_selector = 0xf4dd92ce;
+uint256 constant ratifyOrder_selector_offset = 0x1c;
+uint256 constant ratifyOrder_head_offset = 0x04;
+// uint256 constant ratifyOrder_offer_head_offset = 0x00;
+uint256 constant ratifyOrder_consideration_head_offset = 0x20;
+uint256 constant ratifyOrder_context_head_offset = 0x40;
+uint256 constant ratifyOrder_orderHashes_head_offset = 0x60;
+uint256 constant ratifyOrder_contractNonce_offset = 0x80;
+uint256 constant ratifyOrder_base_tail_offset = 0xa0;
+
+uint256 constant validateOrder_selector = 0x17b1f942;
+uint256 constant validateOrder_selector_offset = 0x1c;
+uint256 constant validateOrder_head_offset = 0x04;
+uint256 constant validateOrder_zoneParameters_offset = 0x20;
+
+// uint256 constant ZoneParameters_orderHash_offset = 0x00;
+uint256 constant ZoneParameters_fulfiller_offset = 0x20;
+uint256 constant ZoneParameters_offerer_offset = 0x40;
+uint256 constant ZoneParameters_offer_head_offset = 0x60;
+uint256 constant ZoneParameters_consideration_head_offset = 0x80;
+uint256 constant ZoneParameters_extraData_head_offset = 0xa0;
+uint256 constant ZoneParameters_orderHashes_head_offset = 0xc0;
+uint256 constant ZoneParameters_startTime_offset = 0xe0;
+uint256 constant ZoneParameters_endTime_offset = 0x100;
+uint256 constant ZoneParameters_zoneHash_offset = 0x120;
+uint256 constant ZoneParameters_base_tail_offset = 0x140;
+uint256 constant ZoneParameters_selectorAndPointer_length = 0x24;
+uint256 constant ZoneParameters_basicOrderFixedElements_length = 0x64;
+
+// ConsiderationDecoder Constants
+uint256 constant OrderParameters_head_size = 0x0160;
+uint256 constant OrderParameters_totalOriginalConsiderationItems_offset = (
+ 0x0140
);
-uint256 constant InvalidSignature_error_length = 0x04;
+uint256 constant AdvancedOrderPlusOrderParameters_head_size = 0x0200;
-// error BadContractSignature() @ SignatureVerificationErrors.sol
-uint256 constant BadContractSignature_error_signature = (
- 0x4f7fb80d00000000000000000000000000000000000000000000000000000000
-);
-uint256 constant BadContractSignature_error_length = 0x04;
+uint256 constant Order_signature_offset = 0x20;
+uint256 constant Order_head_size = 0x40;
+
+uint256 constant AdvancedOrder_fixed_segment_0 = 0x40;
+
+uint256 constant CriteriaResolver_head_size = 0xa0;
+uint256 constant CriteriaResolver_fixed_segment_0 = 0x80;
+uint256 constant CriteriaResolver_criteriaProof_offset = 0x80;
-uint256 constant NumBitsAfterSelector = 0xe0;
+uint256 constant FulfillmentComponent_mem_tail_size = 0x40;
+uint256 constant FulfillmentComponent_mem_tail_size_shift = 0x6;
+uint256 constant Fulfillment_head_size = 0x40;
+uint256 constant Fulfillment_considerationComponents_offset = 0x20;
-// 69 is the lowest modulus for which the remainder
-// of every selector other than the two match functions
-// is greater than those of the match functions.
-uint256 constant NonMatchSelector_MagicModulus = 69;
-// Of the two match function selectors, the highest
-// remainder modulo 69 is 29.
-uint256 constant NonMatchSelector_MagicRemainder = 0x1d;
+uint256 constant OrderComponents_OrderParameters_common_head_size = 0x0140;
diff --git a/contracts/lib/ConsiderationDecoder.sol b/contracts/lib/ConsiderationDecoder.sol
new file mode 100644
index 000000000..9d1ce5c84
--- /dev/null
+++ b/contracts/lib/ConsiderationDecoder.sol
@@ -0,0 +1,1351 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.17;
+
+import {
+ AdvancedOrder,
+ ConsiderationItem,
+ CriteriaResolver,
+ Fulfillment,
+ FulfillmentComponent,
+ OfferItem,
+ Order,
+ OrderParameters,
+ ReceivedItem
+} from "./ConsiderationStructs.sol";
+
+import {
+ AdvancedOrder_denominator_offset,
+ AdvancedOrder_extraData_offset,
+ AdvancedOrder_fixed_segment_0,
+ AdvancedOrder_head_size,
+ AdvancedOrder_numerator_offset,
+ AdvancedOrder_signature_offset,
+ AdvancedOrderPlusOrderParameters_head_size,
+ Common_amount_offset,
+ Common_endAmount_offset,
+ ConsiderationItem_size_with_length,
+ ConsiderationItem_size,
+ CriteriaResolver_criteriaProof_offset,
+ CriteriaResolver_fixed_segment_0,
+ CriteriaResolver_head_size,
+ FourWords,
+ FreeMemoryPointerSlot,
+ Fulfillment_considerationComponents_offset,
+ Fulfillment_head_size,
+ FulfillmentComponent_mem_tail_size_shift,
+ FulfillmentComponent_mem_tail_size,
+ generateOrder_maximum_returndatasize,
+ OfferItem_size_with_length,
+ OfferItem_size,
+ OneWord,
+ OneWordShift,
+ OnlyFullWordMask,
+ Order_head_size,
+ Order_signature_offset,
+ OrderComponents_OrderParameters_common_head_size,
+ OrderParameters_consideration_head_offset,
+ OrderParameters_head_size,
+ OrderParameters_offer_head_offset,
+ OrderParameters_totalOriginalConsiderationItems_offset,
+ ReceivedItem_recipient_offset,
+ ReceivedItem_size,
+ ReceivedItem_size_excluding_recipient,
+ SpentItem_size_shift,
+ SpentItem_size,
+ ThirtyOneBytes,
+ TwoWords
+} from "./ConsiderationConstants.sol";
+
+import {
+ CalldataPointer,
+ malloc,
+ MemoryPointer,
+ OffsetOrLengthMask
+} from "../helpers/PointerLibraries.sol";
+
+contract ConsiderationDecoder {
+ /**
+ * @dev Takes a bytes array from calldata and copies it into memory.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the bytes array in
+ * calldata which contains the length of the array.
+ *
+ * @return mPtrLength A memory pointer to the start of the bytes array in
+ * memory which contains the length of the array.
+ */
+ function _decodeBytes(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ assembly {
+ // Get the current free memory pointer.
+ mPtrLength := mload(FreeMemoryPointerSlot)
+
+ // Derive the size of the bytes array, rounding up to nearest word
+ // and adding a word for the length field. Note: masking
+ // `calldataload(cdPtrLength)` is redundant here.
+ let size := add(
+ and(
+ add(calldataload(cdPtrLength), ThirtyOneBytes),
+ OnlyFullWordMask
+ ),
+ OneWord
+ )
+
+ // Copy bytes from calldata into memory based on pointers and size.
+ calldatacopy(mPtrLength, cdPtrLength, size)
+
+ // Store the masked value in memory. Note: the value of `size` is at
+ // least 32, meaning the calldatacopy above will at least write to
+ // `[mPtrLength, mPtrLength + 32)`.
+ mstore(
+ mPtrLength,
+ and(calldataload(cdPtrLength), OffsetOrLengthMask)
+ )
+
+ // Update free memory pointer based on the size of the bytes array.
+ mstore(FreeMemoryPointerSlot, add(mPtrLength, size))
+ }
+ }
+
+ /**
+ * @dev Takes an offer array from calldata and copies it into memory.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the offer array
+ * in calldata which contains the length of the array.
+ *
+ * @return mPtrLength A memory pointer to the start of the offer array in
+ * memory which contains the length of the array.
+ */
+ function _decodeOffer(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ assembly {
+ // Retrieve length of array, masking to prevent potential overflow.
+ let arrLength := and(calldataload(cdPtrLength), OffsetOrLengthMask)
+
+ // Get the current free memory pointer.
+ mPtrLength := mload(FreeMemoryPointerSlot)
+
+ // Write the array length to memory.
+ mstore(mPtrLength, arrLength)
+
+ // Derive the head by adding one word to the length pointer.
+ let mPtrHead := add(mPtrLength, OneWord)
+
+ // Derive the tail by adding one word per element (note that structs
+ // are written to memory with an offset per struct element).
+ let mPtrTail := add(mPtrHead, shl(OneWordShift, arrLength))
+
+ // Track the next tail, beginning with the initial tail value.
+ let mPtrTailNext := mPtrTail
+
+ // Copy all offer array data into memory at the tail pointer.
+ calldatacopy(
+ mPtrTail,
+ add(cdPtrLength, OneWord),
+ mul(arrLength, OfferItem_size)
+ )
+
+ // Track the next head pointer, starting with initial head value.
+ let mPtrHeadNext := mPtrHead
+
+ // Iterate over each head pointer until it reaches the tail.
+ for {
+
+ } lt(mPtrHeadNext, mPtrTail) {
+
+ } {
+ // Write the next tail pointer to next head pointer in memory.
+ mstore(mPtrHeadNext, mPtrTailNext)
+
+ // Increment the next head pointer by one word.
+ mPtrHeadNext := add(mPtrHeadNext, OneWord)
+
+ // Increment the next tail pointer by the size of an offer item.
+ mPtrTailNext := add(mPtrTailNext, OfferItem_size)
+ }
+
+ // Update free memory pointer to allocate memory up to end of tail.
+ mstore(FreeMemoryPointerSlot, mPtrTailNext)
+ }
+ }
+
+ /**
+ * @dev Takes a consideration array from calldata and copies it into memory.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the consideration
+ * array in calldata which contains the length of the
+ * array.
+ *
+ * @return mPtrLength A memory pointer to the start of the consideration
+ * array in memory which contains the length of the
+ * array.
+ */
+ function _decodeConsideration(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ assembly {
+ // Retrieve length of array, masking to prevent potential overflow.
+ let arrLength := and(calldataload(cdPtrLength), OffsetOrLengthMask)
+
+ // Get the current free memory pointer.
+ mPtrLength := mload(FreeMemoryPointerSlot)
+
+ // Write the array length to memory.
+ mstore(mPtrLength, arrLength)
+
+ // Derive the head by adding one word to the length pointer.
+ let mPtrHead := add(mPtrLength, OneWord)
+
+ // Derive the tail by adding one word per element (note that structs
+ // are written to memory with an offset per struct element).
+ let mPtrTail := add(mPtrHead, shl(OneWordShift, arrLength))
+
+ // Track the next tail, beginning with the initial tail value.
+ let mPtrTailNext := mPtrTail
+
+ // Copy all consideration array data into memory at tail pointer.
+ calldatacopy(
+ mPtrTail,
+ add(cdPtrLength, OneWord),
+ mul(arrLength, ConsiderationItem_size)
+ )
+
+ // Track the next head pointer, starting with initial head value.
+ let mPtrHeadNext := mPtrHead
+
+ // Iterate over each head pointer until it reaches the tail.
+ for {
+
+ } lt(mPtrHeadNext, mPtrTail) {
+
+ } {
+ // Write the next tail pointer to next head pointer in memory.
+ mstore(mPtrHeadNext, mPtrTailNext)
+
+ // Increment the next head pointer by one word.
+ mPtrHeadNext := add(mPtrHeadNext, OneWord)
+
+ // Increment next tail pointer by size of a consideration item.
+ mPtrTailNext := add(mPtrTailNext, ConsiderationItem_size)
+ }
+
+ // Update free memory pointer to allocate memory up to end of tail.
+ mstore(FreeMemoryPointerSlot, mPtrTailNext)
+ }
+ }
+
+ /**
+ * @dev Takes a calldata pointer and memory pointer and copies a referenced
+ * OrderParameters struct and associated offer and consideration data
+ * to memory.
+ *
+ * @param cdPtr A calldata pointer for the OrderParameters struct.
+ * @param mPtr A memory pointer to the OrderParameters struct head.
+ */
+ function _decodeOrderParametersTo(
+ CalldataPointer cdPtr,
+ MemoryPointer mPtr
+ ) internal pure {
+ // Copy the full OrderParameters head from calldata to memory.
+ cdPtr.copy(mPtr, OrderParameters_head_size);
+
+ // Resolve the offer calldata offset, use that to decode and copy offer
+ // from calldata, and write resultant memory offset to head in memory.
+ mPtr.offset(OrderParameters_offer_head_offset).write(
+ _decodeOffer(cdPtr.pptr(OrderParameters_offer_head_offset))
+ );
+
+ // Resolve consideration calldata offset, use that to copy consideration
+ // from calldata, and write resultant memory offset to head in memory.
+ mPtr.offset(OrderParameters_consideration_head_offset).write(
+ _decodeConsideration(
+ cdPtr.pptr(OrderParameters_consideration_head_offset)
+ )
+ );
+ }
+
+ /**
+ * @dev Takes a calldata pointer to an OrderParameters struct and copies the
+ * decoded struct to memory.
+ *
+ * @param cdPtr A calldata pointer for the OrderParameters struct.
+ *
+ * @return mPtr A memory pointer to the OrderParameters struct head.
+ */
+ function _decodeOrderParameters(
+ CalldataPointer cdPtr
+ ) internal pure returns (MemoryPointer mPtr) {
+ // Allocate required memory for the OrderParameters head (offer and
+ // consideration are allocated independently).
+ mPtr = malloc(OrderParameters_head_size);
+
+ // Decode and copy the order parameters to the newly allocated memory.
+ _decodeOrderParametersTo(cdPtr, mPtr);
+ }
+
+ /**
+ * @dev Takes a calldata pointer to an Order struct and copies the decoded
+ * struct to memory.
+ *
+ * @param cdPtr A calldata pointer for the Order struct.
+ *
+ * @return mPtr A memory pointer to the Order struct head.
+ */
+ function _decodeOrder(
+ CalldataPointer cdPtr
+ ) internal pure returns (MemoryPointer mPtr) {
+ // Allocate required memory for the Order head (OrderParameters and
+ // signature are allocated independently).
+ mPtr = malloc(Order_head_size);
+
+ // Resolve OrderParameters calldata offset, use it to decode and copy
+ // from calldata, and write resultant memory offset to head in memory.
+ mPtr.write(_decodeOrderParameters(cdPtr.pptr()));
+
+ // Resolve signature calldata offset, use that to decode and copy from
+ // calldata, and write resultant memory offset to head in memory.
+ mPtr.offset(Order_signature_offset).write(
+ _decodeBytes(cdPtr.pptr(Order_signature_offset))
+ );
+ }
+
+ /**
+ * @dev Takes a calldata pointer to an AdvancedOrder struct and copies the
+ * decoded struct to memory.
+ *
+ * @param cdPtr A calldata pointer for the AdvancedOrder struct.
+ *
+ * @return mPtr A memory pointer to the AdvancedOrder struct head.
+ */
+ function _decodeAdvancedOrder(
+ CalldataPointer cdPtr
+ ) internal pure returns (MemoryPointer mPtr) {
+ // Allocate memory for AdvancedOrder head and OrderParameters head.
+ mPtr = malloc(AdvancedOrderPlusOrderParameters_head_size);
+
+ // Use numerator + denominator calldata offset to decode and copy
+ // from calldata and write resultant memory offset to head in memory.
+ cdPtr.offset(AdvancedOrder_numerator_offset).copy(
+ mPtr.offset(AdvancedOrder_numerator_offset),
+ AdvancedOrder_fixed_segment_0
+ );
+
+ // Get pointer to memory immediately after advanced order.
+ MemoryPointer mPtrParameters = mPtr.offset(AdvancedOrder_head_size);
+
+ // Write pptr for advanced order parameters to memory.
+ mPtr.write(mPtrParameters);
+
+ // Resolve OrderParameters calldata pointer & write to allocated region.
+ _decodeOrderParametersTo(cdPtr.pptr(), mPtrParameters);
+
+ // Resolve signature calldata offset, use that to decode and copy from
+ // calldata, and write resultant memory offset to head in memory.
+ mPtr.offset(AdvancedOrder_signature_offset).write(
+ _decodeBytes(cdPtr.pptr(AdvancedOrder_signature_offset))
+ );
+
+ // Resolve extraData calldata offset, use that to decode and copy from
+ // calldata, and write resultant memory offset to head in memory.
+ mPtr.offset(AdvancedOrder_extraData_offset).write(
+ _decodeBytes(cdPtr.pptr(AdvancedOrder_extraData_offset))
+ );
+ }
+
+ /**
+ * @dev Allocates a single word of empty bytes in memory and returns the
+ * pointer to that memory region.
+ *
+ * @return mPtr The memory pointer to the new empty word in memory.
+ */
+ function _getEmptyBytesOrArray()
+ internal
+ pure
+ returns (MemoryPointer mPtr)
+ {
+ mPtr = malloc(OneWord);
+ mPtr.write(0);
+ }
+
+ /**
+ * @dev Takes a calldata pointer to an Order struct and copies the decoded
+ * struct to memory as an AdvancedOrder.
+ *
+ * @param cdPtr A calldata pointer for the Order struct.
+ *
+ * @return mPtr A memory pointer to the AdvancedOrder struct head.
+ */
+ function _decodeOrderAsAdvancedOrder(
+ CalldataPointer cdPtr
+ ) internal pure returns (MemoryPointer mPtr) {
+ // Allocate memory for AdvancedOrder head and OrderParameters head.
+ mPtr = malloc(AdvancedOrderPlusOrderParameters_head_size);
+
+ // Get pointer to memory immediately after advanced order.
+ MemoryPointer mPtrParameters = mPtr.offset(AdvancedOrder_head_size);
+
+ // Write pptr for advanced order parameters.
+ mPtr.write(mPtrParameters);
+
+ // Resolve OrderParameters calldata pointer & write to allocated region.
+ _decodeOrderParametersTo(cdPtr.pptr(), mPtrParameters);
+
+ // Write default Order numerator and denominator values (i.e. 1/1).
+ mPtr.offset(AdvancedOrder_numerator_offset).write(1);
+ mPtr.offset(AdvancedOrder_denominator_offset).write(1);
+
+ // Resolve signature calldata offset, use that to decode and copy from
+ // calldata, and write resultant memory offset to head in memory.
+ mPtr.offset(AdvancedOrder_signature_offset).write(
+ _decodeBytes(cdPtr.pptr(Order_signature_offset))
+ );
+
+ // Resolve extraData calldata offset, use that to decode and copy from
+ // calldata, and write resultant memory offset to head in memory.
+ mPtr.offset(AdvancedOrder_extraData_offset).write(
+ _getEmptyBytesOrArray()
+ );
+ }
+
+ /**
+ * @dev Takes a calldata pointer to an array of Order structs and copies the
+ * decoded array to memory as an array of AdvancedOrder structs.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the orders array in
+ * calldata which contains the length of the array.
+ *
+ * @return mPtrLength A memory pointer to the start of the array of advanced
+ * orders in memory which contains length of the array.
+ */
+ function _decodeOrdersAsAdvancedOrders(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ // Retrieve length of array, masking to prevent potential overflow.
+ uint256 arrLength = cdPtrLength.readMaskedUint256();
+
+ unchecked {
+ // Derive offset to the tail based on one word per array element.
+ uint256 tailOffset = arrLength << OneWordShift;
+
+ // Add one additional word for the length and allocate memory.
+ mPtrLength = malloc(tailOffset + OneWord);
+
+ // Write the length of the array to memory.
+ mPtrLength.write(arrLength);
+
+ // Advance to first memory & calldata pointers (e.g. after length).
+ MemoryPointer mPtrHead = mPtrLength.next();
+ CalldataPointer cdPtrHead = cdPtrLength.next();
+
+ // Iterate over each pointer, word by word, until tail is reached.
+ for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
+ // Resolve Order calldata offset, use it to decode and copy from
+ // calldata, and write resultant AdvancedOrder offset to memory.
+ mPtrHead.offset(offset).write(
+ _decodeOrderAsAdvancedOrder(cdPtrHead.pptr(offset))
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Takes a calldata pointer to a criteria proof, or an array bytes32
+ * types, and copies the decoded proof to memory.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the criteria proof
+ * in calldata which contains the length of the array.
+ *
+ * @return mPtrLength A memory pointer to the start of the criteria proof
+ * in memory which contains length of the array.
+ */
+ function _decodeCriteriaProof(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ // Retrieve length of array, masking to prevent potential overflow.
+ uint256 arrLength = cdPtrLength.readMaskedUint256();
+
+ unchecked {
+ // Derive array size based on one word per array element and length.
+ uint256 arrSize = (arrLength + 1) << OneWordShift;
+
+ // Allocate memory equal to the array size.
+ mPtrLength = malloc(arrSize);
+
+ // Copy the array from calldata into memory.
+ cdPtrLength.copy(mPtrLength, arrSize);
+ }
+ }
+
+ /**
+ * @dev Takes a calldata pointer to a CriteriaResolver struct and copies the
+ * decoded struct to memory.
+ *
+ * @param cdPtr A calldata pointer for the CriteriaResolver struct.
+ *
+ * @return mPtr A memory pointer to the CriteriaResolver struct head.
+ */
+ function _decodeCriteriaResolver(
+ CalldataPointer cdPtr
+ ) internal pure returns (MemoryPointer mPtr) {
+ // Allocate required memory for the CriteriaResolver head (the criteria
+ // proof bytes32 array is allocated independently).
+ mPtr = malloc(CriteriaResolver_head_size);
+
+ // Decode and copy order index, side, index, and identifier from
+ // calldata and write resultant memory offset to head in memory.
+ cdPtr.copy(mPtr, CriteriaResolver_fixed_segment_0);
+
+ // Resolve criteria proof calldata offset, use it to decode and copy
+ // from calldata, and write resultant memory offset to head in memory.
+ mPtr.offset(CriteriaResolver_criteriaProof_offset).write(
+ _decodeCriteriaProof(
+ cdPtr.pptr(CriteriaResolver_criteriaProof_offset)
+ )
+ );
+ }
+
+ /**
+ * @dev Takes an array of criteria resolvers from calldata and copies it
+ * into memory.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the criteria
+ * resolver array in calldata which contains the length
+ * of the array.
+ *
+ * @return mPtrLength A memory pointer to the start of the criteria resolver
+ * array in memory which contains the length of the
+ * array.
+ */
+ function _decodeCriteriaResolvers(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ // Retrieve length of array, masking to prevent potential overflow.
+ uint256 arrLength = cdPtrLength.readMaskedUint256();
+
+ unchecked {
+ // Derive offset to the tail based on one word per array element.
+ uint256 tailOffset = arrLength << OneWordShift;
+
+ // Add one additional word for the length and allocate memory.
+ mPtrLength = malloc(tailOffset + OneWord);
+
+ // Write the length of the array to memory.
+ mPtrLength.write(arrLength);
+
+ // Advance to first memory & calldata pointers (e.g. after length).
+ MemoryPointer mPtrHead = mPtrLength.next();
+ CalldataPointer cdPtrHead = cdPtrLength.next();
+
+ // Iterate over each pointer, word by word, until tail is reached.
+ for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
+ // Resolve CriteriaResolver calldata offset, use it to decode
+ // and copy from calldata, and write resultant memory offset.
+ mPtrHead.offset(offset).write(
+ _decodeCriteriaResolver(cdPtrHead.pptr(offset))
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Takes an array of orders from calldata and copies it into memory.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the orders array in
+ * calldata which contains the length of the array.
+ *
+ * @return mPtrLength A memory pointer to the start of the orders array
+ * in memory which contains the length of the array.
+ */
+ function _decodeOrders(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ // Retrieve length of array, masking to prevent potential overflow.
+ uint256 arrLength = cdPtrLength.readMaskedUint256();
+
+ unchecked {
+ // Derive offset to the tail based on one word per array element.
+ uint256 tailOffset = arrLength << OneWordShift;
+
+ // Add one additional word for the length and allocate memory.
+ mPtrLength = malloc(tailOffset + OneWord);
+
+ // Write the length of the array to memory.
+ mPtrLength.write(arrLength);
+
+ // Advance to first memory & calldata pointers (e.g. after length).
+ MemoryPointer mPtrHead = mPtrLength.next();
+ CalldataPointer cdPtrHead = cdPtrLength.next();
+
+ // Iterate over each pointer, word by word, until tail is reached.
+ for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
+ // Resolve Order calldata offset, use it to decode and copy
+ // from calldata, and write resultant memory offset.
+ mPtrHead.offset(offset).write(
+ _decodeOrder(cdPtrHead.pptr(offset))
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Takes an array of fulfillment components from calldata and copies it
+ * into memory.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the fulfillment
+ * components array in calldata which contains the length
+ * of the array.
+ *
+ * @return mPtrLength A memory pointer to the start of the fulfillment
+ * components array in memory which contains the length
+ * of the array.
+ */
+ function _decodeFulfillmentComponents(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ assembly {
+ let arrLength := and(calldataload(cdPtrLength), OffsetOrLengthMask)
+
+ // Get the current free memory pointer.
+ mPtrLength := mload(FreeMemoryPointerSlot)
+
+ mstore(mPtrLength, arrLength)
+ let mPtrHead := add(mPtrLength, OneWord)
+ let mPtrTail := add(mPtrHead, shl(OneWordShift, arrLength))
+ let mPtrTailNext := mPtrTail
+ calldatacopy(
+ mPtrTail,
+ add(cdPtrLength, OneWord),
+ shl(FulfillmentComponent_mem_tail_size_shift, arrLength)
+ )
+ let mPtrHeadNext := mPtrHead
+ for {
+
+ } lt(mPtrHeadNext, mPtrTail) {
+
+ } {
+ mstore(mPtrHeadNext, mPtrTailNext)
+ mPtrHeadNext := add(mPtrHeadNext, OneWord)
+ mPtrTailNext := add(
+ mPtrTailNext,
+ FulfillmentComponent_mem_tail_size
+ )
+ }
+
+ // Update the free memory pointer.
+ mstore(FreeMemoryPointerSlot, mPtrTailNext)
+ }
+ }
+
+ /**
+ * @dev Takes a nested array of fulfillment components from calldata and
+ * copies it into memory.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the nested
+ * fulfillment components array in calldata which
+ * contains the length of the array.
+ *
+ * @return mPtrLength A memory pointer to the start of the nested
+ * fulfillment components array in memory which
+ * contains the length of the array.
+ */
+ function _decodeNestedFulfillmentComponents(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ // Retrieve length of array, masking to prevent potential overflow.
+ uint256 arrLength = cdPtrLength.readMaskedUint256();
+
+ unchecked {
+ // Derive offset to the tail based on one word per array element.
+ uint256 tailOffset = arrLength << OneWordShift;
+
+ // Add one additional word for the length and allocate memory.
+ mPtrLength = malloc(tailOffset + OneWord);
+
+ // Write the length of the array to memory.
+ mPtrLength.write(arrLength);
+
+ // Advance to first memory & calldata pointers (e.g. after length).
+ MemoryPointer mPtrHead = mPtrLength.next();
+ CalldataPointer cdPtrHead = cdPtrLength.next();
+
+ // Iterate over each pointer, word by word, until tail is reached.
+ for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
+ // Resolve FulfillmentComponents array calldata offset, use it
+ // to decode and copy from calldata, and write memory offset.
+ mPtrHead.offset(offset).write(
+ _decodeFulfillmentComponents(cdPtrHead.pptr(offset))
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Takes an array of advanced orders from calldata and copies it into
+ * memory.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the advanced orders
+ * array in calldata which contains the length of the
+ * array.
+ *
+ * @return mPtrLength A memory pointer to the start of the advanced orders
+ * array in memory which contains the length of the
+ * array.
+ */
+ function _decodeAdvancedOrders(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ // Retrieve length of array, masking to prevent potential overflow.
+ uint256 arrLength = cdPtrLength.readMaskedUint256();
+
+ unchecked {
+ // Derive offset to the tail based on one word per array element.
+ uint256 tailOffset = arrLength << OneWordShift;
+
+ // Add one additional word for the length and allocate memory.
+ mPtrLength = malloc(tailOffset + OneWord);
+
+ // Write the length of the array to memory.
+ mPtrLength.write(arrLength);
+
+ // Advance to first memory & calldata pointers (e.g. after length).
+ MemoryPointer mPtrHead = mPtrLength.next();
+ CalldataPointer cdPtrHead = cdPtrLength.next();
+
+ // Iterate over each pointer, word by word, until tail is reached.
+ for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
+ // Resolve AdvancedOrder calldata offset, use it to decode and
+ // copy from calldata, and write resultant memory offset.
+ mPtrHead.offset(offset).write(
+ _decodeAdvancedOrder(cdPtrHead.pptr(offset))
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Takes a calldata pointer to a Fulfillment struct and copies the
+ * decoded struct to memory.
+ *
+ * @param cdPtr A calldata pointer for the Fulfillment struct.
+ *
+ * @return mPtr A memory pointer to the Fulfillment struct head.
+ */
+ function _decodeFulfillment(
+ CalldataPointer cdPtr
+ ) internal pure returns (MemoryPointer mPtr) {
+ // Allocate required memory for the Fulfillment head (the fulfillment
+ // components arrays are allocated independently).
+ mPtr = malloc(Fulfillment_head_size);
+
+ // Resolve offerComponents calldata offset, use it to decode and copy
+ // from calldata, and write resultant memory offset to head in memory.
+ mPtr.write(_decodeFulfillmentComponents(cdPtr.pptr()));
+
+ // Resolve considerationComponents calldata offset, use it to decode and
+ // copy from calldata, and write resultant memory offset to memory head.
+ mPtr.offset(Fulfillment_considerationComponents_offset).write(
+ _decodeFulfillmentComponents(
+ cdPtr.pptr(Fulfillment_considerationComponents_offset)
+ )
+ );
+ }
+
+ /**
+ * @dev Takes an array of fulfillments from calldata and copies it into
+ * memory.
+ *
+ * @param cdPtrLength A calldata pointer to the start of the fulfillments
+ * array in calldata which contains the length of the
+ * array.
+ *
+ * @return mPtrLength A memory pointer to the start of the fulfillments
+ * array in memory which contains the length of the
+ * array.
+ */
+ function _decodeFulfillments(
+ CalldataPointer cdPtrLength
+ ) internal pure returns (MemoryPointer mPtrLength) {
+ // Retrieve length of array, masking to prevent potential overflow.
+ uint256 arrLength = cdPtrLength.readMaskedUint256();
+
+ unchecked {
+ // Derive offset to the tail based on one word per array element.
+ uint256 tailOffset = arrLength << OneWordShift;
+
+ // Add one additional word for the length and allocate memory.
+ mPtrLength = malloc(tailOffset + OneWord);
+
+ // Write the length of the array to memory.
+ mPtrLength.write(arrLength);
+
+ // Advance to first memory & calldata pointers (e.g. after length).
+ MemoryPointer mPtrHead = mPtrLength.next();
+ CalldataPointer cdPtrHead = cdPtrLength.next();
+
+ // Iterate over each pointer, word by word, until tail is reached.
+ for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
+ // Resolve Fulfillment calldata offset, use it to decode and
+ // copy from calldata, and write resultant memory offset.
+ mPtrHead.offset(offset).write(
+ _decodeFulfillment(cdPtrHead.pptr(offset))
+ );
+ }
+ }
+ }
+
+ /**
+ * @dev Takes a calldata pointer to an OrderComponents struct and copies the
+ * decoded struct to memory as an OrderParameters struct (with the
+ * totalOriginalConsiderationItems value set equal to the length of the
+ * supplied consideration array).
+ *
+ * @param cdPtr A calldata pointer for the OrderComponents struct.
+ *
+ * @return mPtr A memory pointer to the OrderParameters struct head.
+ */
+ function _decodeOrderComponentsAsOrderParameters(
+ CalldataPointer cdPtr
+ ) internal pure returns (MemoryPointer mPtr) {
+ // Allocate memory for the OrderParameters head.
+ mPtr = malloc(OrderParameters_head_size);
+
+ // Copy the full OrderComponents head from calldata to memory.
+ cdPtr.copy(mPtr, OrderComponents_OrderParameters_common_head_size);
+
+ // Resolve the offer calldata offset, use that to decode and copy offer
+ // from calldata, and write resultant memory offset to head in memory.
+ mPtr.offset(OrderParameters_offer_head_offset).write(
+ _decodeOffer(cdPtr.pptr(OrderParameters_offer_head_offset))
+ );
+
+ // Resolve consideration calldata offset, use that to copy consideration
+ // from calldata, and write resultant memory offset to head in memory.
+ MemoryPointer consideration = _decodeConsideration(
+ cdPtr.pptr(OrderParameters_consideration_head_offset)
+ );
+ mPtr.offset(OrderParameters_consideration_head_offset).write(
+ consideration
+ );
+
+ // Write masked consideration length to totalOriginalConsiderationItems.
+ mPtr
+ .offset(OrderParameters_totalOriginalConsiderationItems_offset)
+ .write(consideration.readUint256());
+ }
+
+ /**
+ * @dev Decodes the returndata from a call to generateOrder, or returns
+ * empty arrays and a boolean signifying that the returndata does not
+ * adhere to a valid encoding scheme if it cannot be decoded.
+ *
+ * @return invalidEncoding A boolean signifying whether the returndata has
+ * an invalid encoding.
+ * @return offer The decoded offer array.
+ * @return consideration The decoded consideration array.
+ */
+ function _decodeGenerateOrderReturndata()
+ internal
+ pure
+ returns (
+ uint256 invalidEncoding,
+ MemoryPointer offer,
+ MemoryPointer consideration
+ )
+ {
+ assembly {
+ // Check that returndatasize is at least four words: offerOffset,
+ // considerationOffset, offerLength, & considerationLength
+ invalidEncoding := lt(returndatasize(), FourWords)
+
+ let offsetOffer
+ let offsetConsideration
+ let offerLength
+ let considerationLength
+
+ // Proceed if enough returndata is present to continue evaluation.
+ if iszero(invalidEncoding) {
+ // Copy first two words of returndata (the offsets to offer and
+ // consideration array lengths) to scratch space.
+ returndatacopy(0, 0, TwoWords)
+ offsetOffer := mload(0)
+ offsetConsideration := mload(OneWord)
+
+ // If valid length, check that offsets are within returndata.
+ let invalidOfferOffset := gt(offsetOffer, returndatasize())
+ let invalidConsiderationOffset := gt(
+ offsetConsideration,
+ returndatasize()
+ )
+
+ // Only proceed if length (and thus encoding) is valid so far.
+ invalidEncoding := or(
+ invalidOfferOffset,
+ invalidConsiderationOffset
+ )
+ if iszero(invalidEncoding) {
+ // Copy length of offer array to scratch space.
+ returndatacopy(0, offsetOffer, OneWord)
+ offerLength := mload(0)
+
+ // Copy length of consideration array to scratch space.
+ returndatacopy(OneWord, offsetConsideration, OneWord)
+ considerationLength := mload(OneWord)
+
+ {
+ // Calculate total size of offer & consideration arrays.
+ let totalOfferSize := shl(
+ SpentItem_size_shift,
+ offerLength
+ )
+ let totalConsiderationSize := mul(
+ ReceivedItem_size,
+ considerationLength
+ )
+
+ // Add 4 words to total size to cover the offset and
+ // length fields of the two arrays.
+ let totalSize := add(
+ FourWords,
+ add(totalOfferSize, totalConsiderationSize)
+ )
+ // Don't continue if returndatasize exceeds 65535 bytes
+ // or is greater than the calculated size.
+ invalidEncoding := or(
+ gt(
+ or(offerLength, considerationLength),
+ generateOrder_maximum_returndatasize
+ ),
+ gt(totalSize, returndatasize())
+ )
+
+ // Set first word of scratch space to 0 so length of
+ // offer/consideration are set to 0 on invalid encoding.
+ mstore(0, 0)
+ }
+ }
+ }
+
+ if iszero(invalidEncoding) {
+ offer := copySpentItemsAsOfferItems(
+ add(offsetOffer, OneWord),
+ offerLength
+ )
+
+ consideration := copyReceivedItemsAsConsiderationItems(
+ add(offsetConsideration, OneWord),
+ considerationLength
+ )
+ }
+
+ function copySpentItemsAsOfferItems(rdPtrHead, length)
+ -> mPtrLength
+ {
+ // Retrieve the current free memory pointer.
+ mPtrLength := mload(FreeMemoryPointerSlot)
+
+ // Allocate memory for the array.
+ mstore(
+ FreeMemoryPointerSlot,
+ add(
+ mPtrLength,
+ add(OneWord, mul(length, OfferItem_size_with_length))
+ )
+ )
+
+ // Write the length of the array to the start of free memory.
+ mstore(mPtrLength, length)
+
+ // Use offset from length to minimize stack depth.
+ let headOffsetFromLength := OneWord
+ let headSizeWithLength := shl(OneWordShift, add(1, length))
+ let mPtrTailNext := add(mPtrLength, headSizeWithLength)
+
+ // Iterate over each element.
+ for {
+
+ } lt(headOffsetFromLength, headSizeWithLength) {
+
+ } {
+ // Write the memory pointer to the accompanying head offset.
+ mstore(add(mPtrLength, headOffsetFromLength), mPtrTailNext)
+
+ // Copy itemType, token, identifier and amount.
+ returndatacopy(mPtrTailNext, rdPtrHead, SpentItem_size)
+
+ // Copy amount to endAmount.
+ mstore(
+ add(mPtrTailNext, Common_endAmount_offset),
+ mload(add(mPtrTailNext, Common_amount_offset))
+ )
+
+ // Update read pointer, next tail pointer, and head offset.
+ rdPtrHead := add(rdPtrHead, SpentItem_size)
+ mPtrTailNext := add(mPtrTailNext, OfferItem_size)
+ headOffsetFromLength := add(headOffsetFromLength, OneWord)
+ }
+ }
+
+ function copyReceivedItemsAsConsiderationItems(rdPtrHead, length)
+ -> mPtrLength
+ {
+ // Retrieve the current free memory pointer.
+ mPtrLength := mload(FreeMemoryPointerSlot)
+
+ // Allocate memory for the array.
+ mstore(
+ FreeMemoryPointerSlot,
+ add(
+ mPtrLength,
+ add(
+ OneWord,
+ mul(length, ConsiderationItem_size_with_length)
+ )
+ )
+ )
+
+ // Write the length of the array to the start of free memory.
+ mstore(mPtrLength, length)
+
+ // Use offset from length to minimize stack depth.
+ let headOffsetFromLength := OneWord
+ let headSizeWithLength := shl(OneWordShift, add(1, length))
+ let mPtrTailNext := add(mPtrLength, headSizeWithLength)
+
+ // Iterate over each element.
+ for {
+
+ } lt(headOffsetFromLength, headSizeWithLength) {
+
+ } {
+ // Write the memory pointer to the accompanying head offset.
+ mstore(add(mPtrLength, headOffsetFromLength), mPtrTailNext)
+
+ // Copy itemType, token, identifier and amount.
+ returndatacopy(
+ mPtrTailNext,
+ rdPtrHead,
+ ReceivedItem_size_excluding_recipient
+ )
+
+ // Copy amount and recipient.
+ returndatacopy(
+ add(mPtrTailNext, Common_endAmount_offset),
+ add(rdPtrHead, Common_amount_offset),
+ TwoWords
+ )
+
+ // Update read pointer, next tail pointer, and head offset.
+ rdPtrHead := add(rdPtrHead, ReceivedItem_size)
+ mPtrTailNext := add(mPtrTailNext, ConsiderationItem_size)
+ headOffsetFromLength := add(headOffsetFromLength, OneWord)
+ }
+ }
+ }
+ }
+
+ /**
+ * @dev Converts a function returning _decodeGenerateOrderReturndata types
+ * into a function returning offer and consideration types.
+ *
+ * @param inFn The input function, taking no arguments and returning an
+ * error buffer, spent item array, and received item array.
+ *
+ * @return outFn The output function, taking no arguments and returning an
+ * error buffer, offer array, and consideration array.
+ */
+ function _convertGetGeneratedOrderResult(
+ function()
+ internal
+ pure
+ returns (uint256, MemoryPointer, MemoryPointer) inFn
+ )
+ internal
+ pure
+ returns (
+ function()
+ internal
+ pure
+ returns (
+ uint256,
+ OfferItem[] memory,
+ ConsiderationItem[] memory
+ ) outFn
+ )
+ {
+ assembly {
+ outFn := inFn
+ }
+ }
+
+ /**
+ * @dev Converts a function taking ReceivedItem, address, bytes32, and bytes
+ * types (e.g. the _transfer function) into a function taking
+ * OfferItem, address, bytes32, and bytes types.
+ *
+ * @param inFn The input function, taking ReceivedItem, address, bytes32,
+ * and bytes types (e.g. the _transfer function).
+ *
+ * @return outFn The output function, taking OfferItem, address, bytes32,
+ * and bytes types.
+ */
+ function _toOfferItemInput(
+ function(ReceivedItem memory, address, bytes32, bytes memory)
+ internal inFn
+ )
+ internal
+ pure
+ returns (
+ function(OfferItem memory, address, bytes32, bytes memory)
+ internal outFn
+ )
+ {
+ assembly {
+ outFn := inFn
+ }
+ }
+
+ /**
+ * @dev Converts a function taking ReceivedItem, address, bytes32, and bytes
+ * types (e.g. the _transfer function) into a function taking
+ * ConsiderationItem, address, bytes32, and bytes types.
+ *
+ * @param inFn The input function, taking ReceivedItem, address, bytes32,
+ * and bytes types (e.g. the _transfer function).
+ *
+ * @return outFn The output function, taking ConsiderationItem, address,
+ * bytes32, and bytes types.
+ */
+ function _toConsiderationItemInput(
+ function(ReceivedItem memory, address, bytes32, bytes memory)
+ internal inFn
+ )
+ internal
+ pure
+ returns (
+ function(ConsiderationItem memory, address, bytes32, bytes memory)
+ internal outFn
+ )
+ {
+ assembly {
+ outFn := inFn
+ }
+ }
+
+ /**
+ * @dev Converts a function taking a calldata pointer and returning a memory
+ * pointer into a function taking that calldata pointer and returning
+ * an OrderParameters type.
+ *
+ * @param inFn The input function, taking an arbitrary calldata pointer and
+ * returning an arbitrary memory pointer.
+ *
+ * @return outFn The output function, taking an arbitrary calldata pointer
+ * and returning an OrderParameters type.
+ */
+ function _toOrderParametersReturnType(
+ function(CalldataPointer) internal pure returns (MemoryPointer) inFn
+ )
+ internal
+ pure
+ returns (
+ function(CalldataPointer)
+ internal
+ pure
+ returns (OrderParameters memory) outFn
+ )
+ {
+ assembly {
+ outFn := inFn
+ }
+ }
+
+ /**
+ * @dev Converts a function taking a calldata pointer and returning a memory
+ * pointer into a function taking that calldata pointer and returning
+ * an AdvancedOrder type.
+ *
+ * @param inFn The input function, taking an arbitrary calldata pointer and
+ * returning an arbitrary memory pointer.
+ *
+ * @return outFn The output function, taking an arbitrary calldata pointer
+ * and returning an AdvancedOrder type.
+ */
+ function _toAdvancedOrderReturnType(
+ function(CalldataPointer) internal pure returns (MemoryPointer) inFn
+ )
+ internal
+ pure
+ returns (
+ function(CalldataPointer)
+ internal
+ pure
+ returns (AdvancedOrder memory) outFn
+ )
+ {
+ assembly {
+ outFn := inFn
+ }
+ }
+
+ /**
+ * @dev Converts a function taking a calldata pointer and returning a memory
+ * pointer into a function taking that calldata pointer and returning
+ * a dynamic array of CriteriaResolver types.
+ *
+ * @param inFn The input function, taking an arbitrary calldata pointer and
+ * returning an arbitrary memory pointer.
+ *
+ * @return outFn The output function, taking an arbitrary calldata pointer
+ * and returning a dynamic array of CriteriaResolver types.
+ */
+ function _toCriteriaResolversReturnType(
+ function(CalldataPointer) internal pure returns (MemoryPointer) inFn
+ )
+ internal
+ pure
+ returns (
+ function(CalldataPointer)
+ internal
+ pure
+ returns (CriteriaResolver[] memory) outFn
+ )
+ {
+ assembly {
+ outFn := inFn
+ }
+ }
+
+ /**
+ * @dev Converts a function taking a calldata pointer and returning a memory
+ * pointer into a function taking that calldata pointer and returning
+ * a dynamic array of Order types.
+ *
+ * @param inFn The input function, taking an arbitrary calldata pointer and
+ * returning an arbitrary memory pointer.
+ *
+ * @return outFn The output function, taking an arbitrary calldata pointer
+ * and returning a dynamic array of Order types.
+ */
+ function _toOrdersReturnType(
+ function(CalldataPointer) internal pure returns (MemoryPointer) inFn
+ )
+ internal
+ pure
+ returns (
+ function(CalldataPointer)
+ internal
+ pure
+ returns (Order[] memory) outFn
+ )
+ {
+ assembly {
+ outFn := inFn
+ }
+ }
+
+ /**
+ * @dev Converts a function taking a calldata pointer and returning a memory
+ * pointer into a function taking that calldata pointer and returning
+ * a nested dynamic array of dynamic arrays of FulfillmentComponent
+ * types.
+ *
+ * @param inFn The input function, taking an arbitrary calldata pointer and
+ * returning an arbitrary memory pointer.
+ *
+ * @return outFn The output function, taking an arbitrary calldata pointer
+ * and returning a nested dynamic array of dynamic arrays of
+ * FulfillmentComponent types.
+ */
+ function _toNestedFulfillmentComponentsReturnType(
+ function(CalldataPointer) internal pure returns (MemoryPointer) inFn
+ )
+ internal
+ pure
+ returns (
+ function(CalldataPointer)
+ internal
+ pure
+ returns (FulfillmentComponent[][] memory) outFn
+ )
+ {
+ assembly {
+ outFn := inFn
+ }
+ }
+
+ /**
+ * @dev Converts a function taking a calldata pointer and returning a memory
+ * pointer into a function taking that calldata pointer and returning
+ * a dynamic array of AdvancedOrder types.
+ *
+ * @param inFn The input function, taking an arbitrary calldata pointer and
+ * returning an arbitrary memory pointer.
+ *
+ * @return outFn The output function, taking an arbitrary calldata pointer
+ * and returning a dynamic array of AdvancedOrder types.
+ */
+ function _toAdvancedOrdersReturnType(
+ function(CalldataPointer) internal pure returns (MemoryPointer) inFn
+ )
+ internal
+ pure
+ returns (
+ function(CalldataPointer)
+ internal
+ pure
+ returns (AdvancedOrder[] memory) outFn
+ )
+ {
+ assembly {
+ outFn := inFn
+ }
+ }
+
+ /**
+ * @dev Converts a function taking a calldata pointer and returning a memory
+ * pointer into a function taking that calldata pointer and returning
+ * a dynamic array of Fulfillment types.
+ *
+ * @param inFn The input function, taking an arbitrary calldata pointer and
+ * returning an arbitrary memory pointer.
+ *
+ * @return outFn The output function, taking an arbitrary calldata pointer
+ * and returning a dynamic array of Fulfillment types.
+ */
+ function _toFulfillmentsReturnType(
+ function(CalldataPointer) internal pure returns (MemoryPointer) inFn
+ )
+ internal
+ pure
+ returns (
+ function(CalldataPointer)
+ internal
+ pure
+ returns (Fulfillment[] memory) outFn
+ )
+ {
+ assembly {
+ outFn := inFn
+ }
+ }
+
+ /**
+ * @dev Converts an offer item into a received item, applying a given
+ * recipient.
+ *
+ * @param offerItem The offer item.
+ * @param recipient The recipient.
+ *
+ * @return receivedItem The received item.
+ */
+ function _convertOfferItemToReceivedItemWithRecipient(
+ OfferItem memory offerItem,
+ address recipient
+ ) internal pure returns (ReceivedItem memory receivedItem) {
+ assembly {
+ receivedItem := offerItem
+ mstore(add(receivedItem, ReceivedItem_recipient_offset), recipient)
+ }
+ }
+}
diff --git a/contracts/lib/ConsiderationEncoder.sol b/contracts/lib/ConsiderationEncoder.sol
new file mode 100644
index 000000000..cf74ed796
--- /dev/null
+++ b/contracts/lib/ConsiderationEncoder.sol
@@ -0,0 +1,746 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.17;
+
+import {
+ BasicOrder_additionalRecipients_length_cdPtr,
+ BasicOrder_common_params_size,
+ BasicOrder_startTime_cdPtr,
+ BasicOrder_startTimeThroughZoneHash_size,
+ Common_amount_offset,
+ Common_identifier_offset,
+ Common_token_offset,
+ generateOrder_base_tail_offset,
+ generateOrder_context_head_offset,
+ generateOrder_head_offset,
+ generateOrder_maximumSpent_head_offset,
+ generateOrder_minimumReceived_head_offset,
+ generateOrder_selector_offset,
+ generateOrder_selector,
+ OneWord,
+ OneWordShift,
+ OnlyFullWordMask,
+ OrderFulfilled_baseDataSize,
+ OrderFulfilled_offer_length_baseOffset,
+ OrderParameters_consideration_head_offset,
+ OrderParameters_endTime_offset,
+ OrderParameters_offer_head_offset,
+ OrderParameters_startTime_offset,
+ OrderParameters_zoneHash_offset,
+ ratifyOrder_base_tail_offset,
+ ratifyOrder_consideration_head_offset,
+ ratifyOrder_context_head_offset,
+ ratifyOrder_contractNonce_offset,
+ ratifyOrder_head_offset,
+ ratifyOrder_orderHashes_head_offset,
+ ratifyOrder_selector_offset,
+ ratifyOrder_selector,
+ ReceivedItem_size,
+ Selector_length,
+ SixtyThreeBytes,
+ SpentItem_size_shift,
+ SpentItem_size,
+ validateOrder_head_offset,
+ validateOrder_selector_offset,
+ validateOrder_selector,
+ validateOrder_zoneParameters_offset,
+ ZoneParameters_base_tail_offset,
+ ZoneParameters_basicOrderFixedElements_length,
+ ZoneParameters_consideration_head_offset,
+ ZoneParameters_endTime_offset,
+ ZoneParameters_extraData_head_offset,
+ ZoneParameters_fulfiller_offset,
+ ZoneParameters_offer_head_offset,
+ ZoneParameters_offerer_offset,
+ ZoneParameters_orderHashes_head_offset,
+ ZoneParameters_selectorAndPointer_length,
+ ZoneParameters_startTime_offset,
+ ZoneParameters_zoneHash_offset
+} from "./ConsiderationConstants.sol";
+
+import {
+ BasicOrderParameters,
+ OrderParameters
+} from "./ConsiderationStructs.sol";
+
+import {
+ CalldataPointer,
+ getFreeMemoryPointer,
+ MemoryPointer
+} from "../helpers/PointerLibraries.sol";
+
+contract ConsiderationEncoder {
+ /**
+ * @dev Takes a bytes array and casts it to a memory pointer.
+ *
+ * @param obj A bytes array in memory.
+ *
+ * @return ptr A memory pointer to the start of the bytes array in memory.
+ */
+ function toMemoryPointer(
+ bytes memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Takes an array of bytes32 types and casts it to a memory pointer.
+ *
+ * @param obj An array of bytes32 types in memory.
+ *
+ * @return ptr A memory pointer to the start of the array of bytes32 types
+ * in memory.
+ */
+ function toMemoryPointer(
+ bytes32[] memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Takes a bytes array in memory and copies it to a new location in
+ * memory.
+ *
+ * @param src A memory pointer referencing the bytes array to be copied (and
+ * pointing to the length of the bytes array).
+ * @param src A memory pointer referencing the location in memory to copy
+ * the bytes array to (and pointing to the length of the copied
+ * bytes array).
+ *
+ * @return size The size of the bytes array.
+ */
+ function _encodeBytes(
+ MemoryPointer src,
+ MemoryPointer dst
+ ) internal view returns (uint256 size) {
+ unchecked {
+ // Mask the length of the bytes array to protect against overflow
+ // and round up to the nearest word.
+ // Note: `size` also includes the 1 word that stores the length.
+ size = (src.readUint256() + SixtyThreeBytes) & OnlyFullWordMask;
+
+ // Copy the bytes array to the new memory location.
+ src.copy(dst, size);
+ }
+ }
+
+ /**
+ * @dev Takes an OrderParameters struct and a context bytes array in memory
+ * and encodes it as `generateOrder` calldata.
+ *
+ * @param orderParameters The OrderParameters struct used to construct the
+ * encoded `generateOrder` calldata.
+ * @param context The context bytes array used to construct the
+ * encoded `generateOrder` calldata.
+ *
+ * @return dst A memory pointer referencing the encoded `generateOrder`
+ * calldata.
+ * @return size The size of the bytes array.
+ */
+ function _encodeGenerateOrder(
+ OrderParameters memory orderParameters,
+ bytes memory context
+ ) internal view returns (MemoryPointer dst, uint256 size) {
+ // Get the memory pointer for the OrderParameters struct.
+ MemoryPointer src = orderParameters.toMemoryPointer();
+
+ // Get free memory pointer to write calldata to.
+ dst = getFreeMemoryPointer();
+
+ // Write generateOrder selector and get pointer to start of calldata.
+ dst.write(generateOrder_selector);
+ dst = dst.offset(generateOrder_selector_offset);
+
+ // Get pointer to the beginning of the encoded data.
+ MemoryPointer dstHead = dst.offset(generateOrder_head_offset);
+
+ // Write `fulfiller` to calldata.
+ dstHead.write(msg.sender);
+
+ // Initialize tail offset, used to populate the minimumReceived array.
+ uint256 tailOffset = generateOrder_base_tail_offset;
+
+ // Write offset to minimumReceived.
+ dstHead.offset(generateOrder_minimumReceived_head_offset).write(
+ tailOffset
+ );
+
+ // Get memory pointer to `orderParameters.offer.length`.
+ MemoryPointer srcOfferPointer = src
+ .offset(OrderParameters_offer_head_offset)
+ .readMemoryPointer();
+
+ // Encode the offer array as a `SpentItem[]`.
+ uint256 minimumReceivedSize = _encodeSpentItems(
+ srcOfferPointer,
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment tail offset, now used to populate maximumSpent array.
+ tailOffset += minimumReceivedSize;
+ }
+
+ // Write offset to maximumSpent.
+ dstHead.offset(generateOrder_maximumSpent_head_offset).write(
+ tailOffset
+ );
+
+ // Get memory pointer to `orderParameters.consideration.length`.
+ MemoryPointer srcConsiderationPointer = src
+ .offset(OrderParameters_consideration_head_offset)
+ .readMemoryPointer();
+
+ // Encode the consideration array as a `SpentItem[]`.
+ uint256 maximumSpentSize = _encodeSpentItems(
+ srcConsiderationPointer,
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment tail offset, now used to populate context array.
+ tailOffset += maximumSpentSize;
+ }
+
+ // Write offset to context.
+ dstHead.offset(generateOrder_context_head_offset).write(tailOffset);
+
+ // Get memory pointer to context.
+ MemoryPointer srcContext = toMemoryPointer(context);
+
+ // Encode context as a bytes array.
+ uint256 contextSize = _encodeBytes(
+ srcContext,
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment the tail offset, now used to determine final size.
+ tailOffset += contextSize;
+
+ // Derive the final size by including the selector.
+ size = Selector_length + tailOffset;
+ }
+ }
+
+ /**
+ * @dev Takes an order hash (e.g. offerer shifted 96 bits to the left XOR'd
+ * with the contract nonce in the case of contract orders), an
+ * OrderParameters struct, context bytes array, and an array of order
+ * hashes for each order included as part of the current fulfillment
+ * and encodes it as `ratifyOrder` calldata.
+ *
+ * @param orderHash The order hash (e.g. shl(0x60, offerer) ^ nonce).
+ * @param orderParameters The OrderParameters struct used to construct the
+ * encoded `ratifyOrder` calldata.
+ * @param context The context bytes array used to construct the
+ * encoded `ratifyOrder` calldata.
+ * @param orderHashes An array of bytes32 values representing the order
+ * hashes of all orders included as part of the
+ * current fulfillment.
+ * @param shiftedOfferer The offerer for the order, shifted 96 bits to the
+ * left.
+ *
+ * @return dst A memory pointer referencing the encoded `ratifyOrder`
+ * calldata.
+ * @return size The size of the bytes array.
+ */
+ function _encodeRatifyOrder(
+ bytes32 orderHash, // e.g. shl(0x60, offerer) ^ contract nonce
+ OrderParameters memory orderParameters,
+ bytes memory context, // encoded based on the schemaID
+ bytes32[] memory orderHashes,
+ uint256 shiftedOfferer
+ ) internal view returns (MemoryPointer dst, uint256 size) {
+ // Get free memory pointer to write calldata to. This isn't allocated as
+ // it is only used for a single function call.
+ dst = getFreeMemoryPointer();
+
+ // Write ratifyOrder selector and get pointer to start of calldata.
+ dst.write(ratifyOrder_selector);
+ dst = dst.offset(ratifyOrder_selector_offset);
+
+ // Get pointer to the beginning of the encoded data.
+ MemoryPointer dstHead = dst.offset(ratifyOrder_head_offset);
+
+ // Write contractNonce to calldata via xor(orderHash, shiftedOfferer).
+ dstHead.offset(ratifyOrder_contractNonce_offset).write(
+ uint256(orderHash) ^ shiftedOfferer
+ );
+
+ // Initialize tail offset, used to populate the offer array.
+ uint256 tailOffset = ratifyOrder_base_tail_offset;
+ MemoryPointer src = orderParameters.toMemoryPointer();
+
+ // Write offset to `offer`.
+ dstHead.write(tailOffset);
+
+ // Get memory pointer to `orderParameters.offer.length`.
+ MemoryPointer srcOfferPointer = src
+ .offset(OrderParameters_offer_head_offset)
+ .readMemoryPointer();
+
+ // Encode the offer array as a `SpentItem[]`.
+ uint256 offerSize = _encodeSpentItems(
+ srcOfferPointer,
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment tail offset, now used to populate consideration array.
+ tailOffset += offerSize;
+ }
+
+ // Write offset to consideration.
+ dstHead.offset(ratifyOrder_consideration_head_offset).write(tailOffset);
+
+ // Get pointer to `orderParameters.consideration.length`.
+ MemoryPointer srcConsiderationPointer = src
+ .offset(OrderParameters_consideration_head_offset)
+ .readMemoryPointer();
+
+ // Encode the consideration array as a `ReceivedItem[]`.
+ uint256 considerationSize = _encodeConsiderationAsReceivedItems(
+ srcConsiderationPointer,
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment tail offset, now used to populate context array.
+ tailOffset += considerationSize;
+ }
+
+ // Write offset to context.
+ dstHead.offset(ratifyOrder_context_head_offset).write(tailOffset);
+
+ // Encode context.
+ uint256 contextSize = _encodeBytes(
+ toMemoryPointer(context),
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment tail offset, now used to populate orderHashes array.
+ tailOffset += contextSize;
+ }
+
+ // Write offset to orderHashes.
+ dstHead.offset(ratifyOrder_orderHashes_head_offset).write(tailOffset);
+
+ // Encode orderHashes.
+ uint256 orderHashesSize = _encodeOrderHashes(
+ toMemoryPointer(orderHashes),
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment the tail offset, now used to determine final size.
+ tailOffset += orderHashesSize;
+
+ // Derive the final size by including the selector.
+ size = Selector_length + tailOffset;
+ }
+ }
+
+ /**
+ * @dev Takes an order hash, OrderParameters struct, extraData bytes array,
+ * and array of order hashes for each order included as part of the
+ * current fulfillment and encodes it as `validateOrder` calldata.
+ * Note that future, new versions of this contract may end up writing
+ * to a memory region that might have been potentially dirtied by the
+ * accumulator. Since the book-keeping for the accumulator does not
+ * update the free memory pointer, it will be necessary to ensure that
+ * all bytes in the memory in the range [dst, dst+size) are fully
+ * updated/written to in this function.
+ *
+ * @param orderHash The order hash.
+ * @param orderParameters The OrderParameters struct used to construct the
+ * encoded `validateOrder` calldata.
+ * @param extraData The extraData bytes array used to construct the
+ * encoded `validateOrder` calldata.
+ * @param orderHashes An array of bytes32 values representing the order
+ * hashes of all orders included as part of the
+ * current fulfillment.
+ *
+ * @return dst A memory pointer referencing the encoded `validateOrder`
+ * calldata.
+ * @return size The size of the bytes array.
+ */
+ function _encodeValidateOrder(
+ bytes32 orderHash,
+ OrderParameters memory orderParameters,
+ bytes memory extraData,
+ bytes32[] memory orderHashes
+ ) internal view returns (MemoryPointer dst, uint256 size) {
+ // Get free memory pointer to write calldata to. This isn't allocated as
+ // it is only used for a single function call.
+ dst = getFreeMemoryPointer();
+
+ // Write validateOrder selector and get pointer to start of calldata.
+ dst.write(validateOrder_selector);
+ dst = dst.offset(validateOrder_selector_offset);
+
+ // Get pointer to the beginning of the encoded data.
+ MemoryPointer dstHead = dst.offset(validateOrder_head_offset);
+
+ // Write offset to zoneParameters to start of calldata.
+ dstHead.write(validateOrder_zoneParameters_offset);
+
+ // Reuse `dstHead` as pointer to zoneParameters.
+ dstHead = dstHead.offset(validateOrder_zoneParameters_offset);
+
+ // Write orderHash and fulfiller to zoneParameters.
+ dstHead.writeBytes32(orderHash);
+ dstHead.offset(ZoneParameters_fulfiller_offset).write(msg.sender);
+
+ // Get the memory pointer to the order parameters struct.
+ MemoryPointer src = orderParameters.toMemoryPointer();
+
+ // Copy offerer, startTime, endTime and zoneHash to zoneParameters.
+ dstHead.offset(ZoneParameters_offerer_offset).write(src.readUint256());
+ dstHead.offset(ZoneParameters_startTime_offset).write(
+ src.offset(OrderParameters_startTime_offset).readUint256()
+ );
+ dstHead.offset(ZoneParameters_endTime_offset).write(
+ src.offset(OrderParameters_endTime_offset).readUint256()
+ );
+ dstHead.offset(ZoneParameters_zoneHash_offset).write(
+ src.offset(OrderParameters_zoneHash_offset).readUint256()
+ );
+
+ // Initialize tail offset, used to populate the offer array.
+ uint256 tailOffset = ZoneParameters_base_tail_offset;
+
+ // Write offset to `offer`.
+ dstHead.offset(ZoneParameters_offer_head_offset).write(tailOffset);
+
+ // Get pointer to `orderParameters.offer.length`.
+ MemoryPointer srcOfferPointer = src
+ .offset(OrderParameters_offer_head_offset)
+ .readMemoryPointer();
+
+ // Encode the offer array as a `SpentItem[]`.
+ uint256 offerSize = _encodeSpentItems(
+ srcOfferPointer,
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment tail offset, now used to populate consideration array.
+ tailOffset += offerSize;
+ }
+
+ // Write offset to consideration.
+ dstHead.offset(ZoneParameters_consideration_head_offset).write(
+ tailOffset
+ );
+
+ // Get pointer to `orderParameters.consideration.length`.
+ MemoryPointer srcConsiderationPointer = src
+ .offset(OrderParameters_consideration_head_offset)
+ .readMemoryPointer();
+
+ // Encode the consideration array as a `ReceivedItem[]`.
+ uint256 considerationSize = _encodeConsiderationAsReceivedItems(
+ srcConsiderationPointer,
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment tail offset, now used to populate extraData array.
+ tailOffset += considerationSize;
+ }
+
+ // Write offset to extraData.
+ dstHead.offset(ZoneParameters_extraData_head_offset).write(tailOffset);
+ // Copy extraData.
+ uint256 extraDataSize = _encodeBytes(
+ toMemoryPointer(extraData),
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment tail offset, now used to populate orderHashes array.
+ tailOffset += extraDataSize;
+ }
+
+ // Write offset to orderHashes.
+ dstHead.offset(ZoneParameters_orderHashes_head_offset).write(
+ tailOffset
+ );
+
+ // Encode the order hashes array.
+ uint256 orderHashesSize = _encodeOrderHashes(
+ toMemoryPointer(orderHashes),
+ dstHead.offset(tailOffset)
+ );
+
+ unchecked {
+ // Increment the tail offset, now used to determine final size.
+ tailOffset += orderHashesSize;
+
+ // Derive final size including selector and ZoneParameters pointer.
+ size = ZoneParameters_selectorAndPointer_length + tailOffset;
+ }
+ }
+
+ /**
+ * @dev Takes an order hash and BasicOrderParameters struct (from calldata)
+ * and encodes it as `validateOrder` calldata.
+ *
+ * @param orderHash The order hash.
+ * @param parameters The BasicOrderParameters struct used to construct the
+ * encoded `validateOrder` calldata.
+ *
+ * @return dst A memory pointer referencing the encoded `validateOrder`
+ * calldata.
+ * @return size The size of the bytes array.
+ */
+ function _encodeValidateBasicOrder(
+ bytes32 orderHash,
+ BasicOrderParameters calldata parameters
+ ) internal view returns (MemoryPointer dst, uint256 size) {
+ // Get free memory pointer to write calldata to. This isn't allocated as
+ // it is only used for a single function call.
+ dst = getFreeMemoryPointer();
+
+ // Write validateOrder selector and get pointer to start of calldata.
+ dst.write(validateOrder_selector);
+ dst = dst.offset(validateOrder_selector_offset);
+
+ // Get pointer to the beginning of the encoded data.
+ MemoryPointer dstHead = dst.offset(validateOrder_head_offset);
+
+ // Write offset to zoneParameters to start of calldata.
+ dstHead.write(validateOrder_zoneParameters_offset);
+
+ // Reuse `dstHead` as pointer to zoneParameters.
+ dstHead = dstHead.offset(validateOrder_zoneParameters_offset);
+
+ // Write offerer, orderHash and fulfiller to zoneParameters.
+ dstHead.writeBytes32(orderHash);
+ dstHead.offset(ZoneParameters_fulfiller_offset).write(msg.sender);
+ dstHead.offset(ZoneParameters_offerer_offset).write(parameters.offerer);
+
+ // Copy startTime, endTime and zoneHash to zoneParameters.
+ CalldataPointer.wrap(BasicOrder_startTime_cdPtr).copy(
+ dstHead.offset(ZoneParameters_startTime_offset),
+ BasicOrder_startTimeThroughZoneHash_size
+ );
+
+ // Initialize tail offset, used for the offer + consideration arrays.
+ uint256 tailOffset = ZoneParameters_base_tail_offset;
+
+ // Write offset to offer from event data into target calldata.
+ dstHead.offset(ZoneParameters_offer_head_offset).write(tailOffset);
+
+ unchecked {
+ // Write consideration offset next (located 5 words after offer).
+ dstHead.offset(ZoneParameters_consideration_head_offset).write(
+ tailOffset + BasicOrder_common_params_size
+ );
+
+ // Retrieve the offset to the length of additional recipients.
+ uint256 additionalRecipientsLength = CalldataPointer
+ .wrap(BasicOrder_additionalRecipients_length_cdPtr)
+ .readUint256();
+
+ // Derive offset to event data using base offset & total recipients.
+ uint256 offerDataOffset = OrderFulfilled_offer_length_baseOffset +
+ additionalRecipientsLength *
+ OneWord;
+
+ // Derive size of offer and consideration data.
+ // 2 words (lengths) + 4 (offer data) + 5 (consideration 1) + 5 * ar
+ uint256 offerAndConsiderationSize = OrderFulfilled_baseDataSize +
+ (additionalRecipientsLength * ReceivedItem_size);
+
+ // Copy offer and consideration data from event data to calldata.
+ MemoryPointer.wrap(offerDataOffset).copy(
+ dstHead.offset(tailOffset),
+ offerAndConsiderationSize
+ );
+
+ // Increment tail offset, now used to populate extraData array.
+ tailOffset += offerAndConsiderationSize;
+ }
+
+ // Write empty bytes for extraData.
+ dstHead.offset(ZoneParameters_extraData_head_offset).write(tailOffset);
+ dstHead.offset(tailOffset).write(0);
+
+ unchecked {
+ // Increment tail offset, now used to populate orderHashes array.
+ tailOffset += OneWord;
+ }
+
+ // Write offset to orderHashes.
+ dstHead.offset(ZoneParameters_orderHashes_head_offset).write(
+ tailOffset
+ );
+
+ // Write length = 1 to the orderHashes array.
+ dstHead.offset(tailOffset).write(1);
+
+ unchecked {
+ // Write the single order hash to the orderHashes array.
+ dstHead.offset(tailOffset + OneWord).writeBytes32(orderHash);
+
+ // Final size: selector, ZoneParameters pointer, orderHashes & tail.
+ size = ZoneParameters_basicOrderFixedElements_length + tailOffset;
+ }
+ }
+
+ /**
+ * @dev Takes a memory pointer to an array of bytes32 values representing
+ * the order hashes included as part of the fulfillment and a memory
+ * pointer to a location to copy it to, and copies the source data to
+ * the destination in memory.
+ *
+ * @param srcLength A memory pointer referencing the order hashes array to
+ * be copied (and pointing to the length of the array).
+ * @param dstLength A memory pointer referencing the location in memory to
+ * copy the orderHashes array to (and pointing to the
+ * length of the copied array).
+ *
+ * @return size The size of the order hashes array (including the length).
+ */
+ function _encodeOrderHashes(
+ MemoryPointer srcLength,
+ MemoryPointer dstLength
+ ) internal view returns (uint256 size) {
+ // Read length of the array from source and write to destination.
+ uint256 length = srcLength.readUint256();
+ dstLength.write(length);
+
+ unchecked {
+ // Determine head & tail size as one word per element in the array.
+ uint256 headAndTailSize = length << OneWordShift;
+
+ // Copy the tail starting from the next element of the source to the
+ // next element of the destination.
+ srcLength.next().copy(dstLength.next(), headAndTailSize);
+
+ // Set size to the length of the tail plus one word for length.
+ size = headAndTailSize + OneWord;
+ }
+ }
+
+ /**
+ * @dev Takes a memory pointer to an offer or consideration array and a
+ * memory pointer to a location to copy it to, and copies the source
+ * data to the destination in memory as a SpentItem array.
+ *
+ * @param srcLength A memory pointer referencing the offer or consideration
+ * array to be copied as a SpentItem array (and pointing to
+ * the length of the original array).
+ * @param dstLength A memory pointer referencing the location in memory to
+ * copy the offer array to (and pointing to the length of
+ * the copied array).
+ *
+ * @return size The size of the SpentItem array (including the length).
+ */
+ function _encodeSpentItems(
+ MemoryPointer srcLength,
+ MemoryPointer dstLength
+ ) internal pure returns (uint256 size) {
+ assembly {
+ // Read length of the array from source and write to destination.
+ let length := mload(srcLength)
+ mstore(dstLength, length)
+
+ // Get pointer to first item's head position in the array,
+ // containing the item's pointer in memory. The head pointer will be
+ // incremented until it reaches the tail position (start of the
+ // array data).
+ let mPtrHead := add(srcLength, OneWord)
+
+ // Position in memory to write next item for calldata. Since
+ // SpentItem has a fixed length, the array elements do not contain
+ // head elements in calldata, they are concatenated together after
+ // the array length.
+ let cdPtrData := add(dstLength, OneWord)
+
+ // Pointer to end of array head in memory.
+ let mPtrHeadEnd := add(mPtrHead, shl(OneWordShift, length))
+
+ for {
+
+ } lt(mPtrHead, mPtrHeadEnd) {
+
+ } {
+ // Read pointer to data for array element from head position.
+ let mPtrTail := mload(mPtrHead)
+
+ // Copy itemType, token, identifier, amount to calldata.
+ mstore(cdPtrData, mload(mPtrTail))
+ mstore(
+ add(cdPtrData, Common_token_offset),
+ mload(add(mPtrTail, Common_token_offset))
+ )
+ mstore(
+ add(cdPtrData, Common_identifier_offset),
+ mload(add(mPtrTail, Common_identifier_offset))
+ )
+ mstore(
+ add(cdPtrData, Common_amount_offset),
+ mload(add(mPtrTail, Common_amount_offset))
+ )
+
+ mPtrHead := add(mPtrHead, OneWord)
+ cdPtrData := add(cdPtrData, SpentItem_size)
+ }
+
+ size := add(OneWord, shl(SpentItem_size_shift, length))
+ }
+ }
+
+ /**
+ * @dev Takes a memory pointer to an consideration array and a memory
+ * pointer to a location to copy it to, and copies the source data to
+ * the destination in memory as a ReceivedItem array.
+ *
+ * @param srcLength A memory pointer referencing the consideration array to
+ * be copied as a ReceivedItem array (and pointing to the
+ * length of the original array).
+ * @param dstLength A memory pointer referencing the location in memory to
+ * copy the consideration array to as a ReceivedItem array
+ * (and pointing to the length of the new array).
+ *
+ * @return size The size of the ReceivedItem array (including the length).
+ */
+ function _encodeConsiderationAsReceivedItems(
+ MemoryPointer srcLength,
+ MemoryPointer dstLength
+ ) internal view returns (uint256 size) {
+ unchecked {
+ // Read length of the array from source and write to destination.
+ uint256 length = srcLength.readUint256();
+ dstLength.write(length);
+
+ // Get pointer to first item's head position in the array,
+ // containing the item's pointer in memory. The head pointer will be
+ // incremented until it reaches the tail position (start of the
+ // array data).
+ MemoryPointer srcHead = srcLength.next();
+ MemoryPointer srcHeadEnd = srcHead.offset(length << OneWordShift);
+
+ // Position in memory to write next item for calldata. Since
+ // ReceivedItem has a fixed length, the array elements do not
+ // contain offsets in calldata, they are concatenated together after
+ // the array length.
+ MemoryPointer dstHead = dstLength.next();
+ while (srcHead.lt(srcHeadEnd)) {
+ MemoryPointer srcTail = srcHead.pptr();
+ srcTail.copy(dstHead, ReceivedItem_size);
+ srcHead = srcHead.next();
+ dstHead = dstHead.offset(ReceivedItem_size);
+ }
+
+ size = OneWord + (length * ReceivedItem_size);
+ }
+ }
+}
diff --git a/contracts/lib/ConsiderationEnums.sol b/contracts/lib/ConsiderationEnums.sol
index c8797f204..261ae6b3c 100644
--- a/contracts/lib/ConsiderationEnums.sol
+++ b/contracts/lib/ConsiderationEnums.sol
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-// prettier-ignore
enum OrderType {
// 0: no partial fills, anyone can execute
FULL_OPEN,
@@ -13,10 +12,12 @@ enum OrderType {
FULL_RESTRICTED,
// 3: partial fills supported, only offerer or zone can execute
- PARTIAL_RESTRICTED
+ PARTIAL_RESTRICTED,
+
+ // 4: contract order type
+ CONTRACT
}
-// prettier-ignore
enum BasicOrderType {
// 0: no partial fills, anyone can execute
ETH_TO_ERC721_FULL_OPEN,
@@ -91,7 +92,6 @@ enum BasicOrderType {
ERC1155_TO_ERC20_PARTIAL_RESTRICTED
}
-// prettier-ignore
enum BasicOrderRouteType {
// 0: provide Ether (or other native token) to receive offered ERC721 item.
ETH_TO_ERC721,
@@ -112,7 +112,6 @@ enum BasicOrderRouteType {
ERC1155_TO_ERC20
}
-// prettier-ignore
enum ItemType {
// 0: ETH on mainnet, MATIC on polygon, etc.
NATIVE,
@@ -133,7 +132,6 @@ enum ItemType {
ERC1155_WITH_CRITERIA
}
-// prettier-ignore
enum Side {
// 0: Items that can be spent
OFFER,
diff --git a/contracts/lib/ConsiderationErrorConstants.sol b/contracts/lib/ConsiderationErrorConstants.sol
new file mode 100644
index 000000000..62615a635
--- /dev/null
+++ b/contracts/lib/ConsiderationErrorConstants.sol
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+uint256 constant Error_selector_offset = 0x1c;
+
+/*
+ * error MissingFulfillmentComponentOnAggregation(uint8 side)
+ * - Defined in FulfillmentApplicationErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: side
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant MissingFulfillmentComponentOnAggregation_error_selector = (
+ 0x375c24c1
+);
+uint256 constant MissingFulfillmentComponentOnAggregation_error_side_ptr = 0x20;
+uint256 constant MissingFulfillmentComponentOnAggregation_error_length = 0x24;
+
+/*
+ * error OfferAndConsiderationRequiredOnFulfillment()
+ * - Defined in FulfillmentApplicationErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant OfferAndConsiderationRequiredOnFulfillment_error_selector = (
+ 0x98e9db6e
+);
+uint256 constant OfferAndConsiderationRequiredOnFulfillment_error_length = 0x04;
+
+/*
+ * error MismatchedFulfillmentOfferAndConsiderationComponents(
+ * uint256 fulfillmentIndex
+ * )
+ * - Defined in FulfillmentApplicationErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: fulfillmentIndex
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant MismatchedOfferAndConsiderationComponents_error_selector = (
+ 0xbced929d
+);
+uint256 constant MismatchedOfferAndConsiderationComponents_error_idx_ptr = 0x20;
+uint256 constant MismatchedOfferAndConsiderationComponents_error_length = 0x24;
+
+/*
+ * error InvalidFulfillmentComponentData()
+ * - Defined in FulfillmentApplicationErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant InvalidFulfillmentComponentData_error_selector = 0x7fda7279;
+uint256 constant InvalidFulfillmentComponentData_error_length = 0x04;
+
+/*
+ * error InexactFraction()
+ * - Defined in AmountDerivationErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant InexactFraction_error_selector = 0xc63cf089;
+uint256 constant InexactFraction_error_length = 0x04;
+
+/*
+ * error OrderCriteriaResolverOutOfRange(uint8 side)
+ * - Defined in CriteriaResolutionErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: side
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant OrderCriteriaResolverOutOfRange_error_selector = 0x133c37c6;
+uint256 constant OrderCriteriaResolverOutOfRange_error_side_ptr = 0x20;
+uint256 constant OrderCriteriaResolverOutOfRange_error_length = 0x24;
+
+/*
+ * error UnresolvedOfferCriteria(uint256 orderIndex, uint256 offerIndex)
+ * - Defined in CriteriaResolutionErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: orderIndex
+ * - 0x40: offerIndex
+ * Revert buffer is memory[0x1c:0x60]
+ */
+uint256 constant UnresolvedOfferCriteria_error_selector = 0xd6929332;
+uint256 constant UnresolvedOfferCriteria_error_orderIndex_ptr = 0x20;
+uint256 constant UnresolvedOfferCriteria_error_offerIndex_ptr = 0x40;
+uint256 constant UnresolvedOfferCriteria_error_length = 0x44;
+
+/*
+ * error UnresolvedConsiderationCriteria(
+ * uint256 orderIndex,
+ * uint256 considerationIndex
+ * )
+ * - Defined in CriteriaResolutionErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: orderIndex
+ * - 0x40: considerationIndex
+ * Revert buffer is memory[0x1c:0x60]
+ */
+uint256 constant UnresolvedConsiderationCriteria_error_selector = 0xa8930e9a;
+uint256 constant UnresolvedConsiderationCriteria_error_orderIndex_ptr = 0x20;
+uint256 constant UnresolvedConsiderationCriteria_error_considerationIdx_ptr = (
+ 0x40
+);
+uint256 constant UnresolvedConsiderationCriteria_error_length = 0x44;
+
+/*
+ * error OfferCriteriaResolverOutOfRange()
+ * - Defined in CriteriaResolutionErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant OfferCriteriaResolverOutOfRange_error_selector = 0xbfb3f8ce;
+// uint256 constant OfferCriteriaResolverOutOfRange_error_length = 0x04;
+
+/*
+ * error ConsiderationCriteriaResolverOutOfRange()
+ * - Defined in CriteriaResolutionErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant ConsiderationCriteriaResolverOutOfRange_error_selector = (
+ 0x6088d7de
+);
+uint256 constant ConsiderationCriteriaResolverOutOfRange_err_selector = (
+ 0x6088d7de
+);
+// uint256 constant ConsiderationCriteriaResolverOutOfRange_error_length = 0x04;
+
+/*
+ * error CriteriaNotEnabledForItem()
+ * - Defined in CriteriaResolutionErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant CriteriaNotEnabledForItem_error_selector = 0x94eb6af6;
+uint256 constant CriteriaNotEnabledForItem_error_length = 0x04;
+
+/*
+ * error InvalidProof()
+ * - Defined in CriteriaResolutionErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant InvalidProof_error_selector = 0x09bde339;
+uint256 constant InvalidProof_error_length = 0x04;
+
+/*
+ * error InvalidRestrictedOrder(bytes32 orderHash)
+ * - Defined in ZoneInteractionErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: orderHash
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant InvalidRestrictedOrder_error_selector = 0xfb5014fc;
+uint256 constant InvalidRestrictedOrder_error_orderHash_ptr = 0x20;
+uint256 constant InvalidRestrictedOrder_error_length = 0x24;
+
+/*
+ * error InvalidContractOrder(bytes32 orderHash)
+ * - Defined in ZoneInteractionErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: orderHash
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant InvalidContractOrder_error_selector = 0x93979285;
+uint256 constant InvalidContractOrder_error_orderHash_ptr = 0x20;
+uint256 constant InvalidContractOrder_error_length = 0x24;
+
+/*
+ * error BadSignatureV(uint8 v)
+ * - Defined in SignatureVerificationErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: v
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant BadSignatureV_error_selector = 0x1f003d0a;
+uint256 constant BadSignatureV_error_v_ptr = 0x20;
+uint256 constant BadSignatureV_error_length = 0x24;
+
+/*
+ * error InvalidSigner()
+ * - Defined in SignatureVerificationErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant InvalidSigner_error_selector = 0x815e1d64;
+uint256 constant InvalidSigner_error_length = 0x04;
+
+/*
+ * error InvalidSignature()
+ * - Defined in SignatureVerificationErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant InvalidSignature_error_selector = 0x8baa579f;
+uint256 constant InvalidSignature_error_length = 0x04;
+
+/*
+ * error BadContractSignature()
+ * - Defined in SignatureVerificationErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant BadContractSignature_error_selector = 0x4f7fb80d;
+uint256 constant BadContractSignature_error_length = 0x04;
+
+/*
+ * error InvalidERC721TransferAmount(uint256 amount)
+ * - Defined in TokenTransferrerErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: amount
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant InvalidERC721TransferAmount_error_selector = 0x69f95827;
+uint256 constant InvalidERC721TransferAmount_error_amount_ptr = 0x20;
+uint256 constant InvalidERC721TransferAmount_error_length = 0x24;
+
+/*
+ * error MissingItemAmount()
+ * - Defined in TokenTransferrerErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant MissingItemAmount_error_selector = 0x91b3e514;
+uint256 constant MissingItemAmount_error_length = 0x04;
+
+/*
+ * error UnusedItemParameters()
+ * - Defined in TokenTransferrerErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant UnusedItemParameters_error_selector = 0x6ab37ce7;
+uint256 constant UnusedItemParameters_error_length = 0x04;
+
+/*
+ * error NoReentrantCalls()
+ * - Defined in ReentrancyErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant NoReentrantCalls_error_selector = 0x7fa8a987;
+uint256 constant NoReentrantCalls_error_length = 0x04;
+
+/*
+ * error OrderAlreadyFilled(bytes32 orderHash)
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: orderHash
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant OrderAlreadyFilled_error_selector = 0x10fda3e1;
+uint256 constant OrderAlreadyFilled_error_orderHash_ptr = 0x20;
+uint256 constant OrderAlreadyFilled_error_length = 0x24;
+
+/*
+ * error InvalidTime(uint256 startTime, uint256 endTime)
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: startTime
+ * - 0x40: endTime
+ * Revert buffer is memory[0x1c:0x60]
+ */
+uint256 constant InvalidTime_error_selector = 0x21ccfeb7;
+uint256 constant InvalidTime_error_startTime_ptr = 0x20;
+uint256 constant InvalidTime_error_endTime_ptr = 0x40;
+uint256 constant InvalidTime_error_length = 0x44;
+
+/*
+ * error InvalidConduit(bytes32 conduitKey, address conduit)
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: conduitKey
+ * - 0x40: conduit
+ * Revert buffer is memory[0x1c:0x60]
+ */
+uint256 constant InvalidConduit_error_selector = 0x1cf99b26;
+uint256 constant InvalidConduit_error_conduitKey_ptr = 0x20;
+uint256 constant InvalidConduit_error_conduit_ptr = 0x40;
+uint256 constant InvalidConduit_error_length = 0x44;
+
+/*
+ * error MissingOriginalConsiderationItems()
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant MissingOriginalConsiderationItems_error_selector = 0x466aa616;
+uint256 constant MissingOriginalConsiderationItems_error_length = 0x04;
+
+/*
+ * error InvalidCallToConduit(address conduit)
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: conduit
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant InvalidCallToConduit_error_selector = 0xd13d53d4;
+uint256 constant InvalidCallToConduit_error_conduit_ptr = 0x20;
+uint256 constant InvalidCallToConduit_error_length = 0x24;
+
+/*
+ * error ConsiderationNotMet(
+ * uint256 orderIndex,
+ * uint256 considerationIndex,
+ * uint256 shortfallAmount
+ * )
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: orderIndex
+ * - 0x40: considerationIndex
+ * - 0x60: shortfallAmount
+ * Revert buffer is memory[0x1c:0x80]
+ */
+uint256 constant ConsiderationNotMet_error_selector = 0xa5f54208;
+uint256 constant ConsiderationNotMet_error_orderIndex_ptr = 0x20;
+uint256 constant ConsiderationNotMet_error_considerationIndex_ptr = 0x40;
+uint256 constant ConsiderationNotMet_error_shortfallAmount_ptr = 0x60;
+uint256 constant ConsiderationNotMet_error_length = 0x64;
+
+/*
+ * error InsufficientNativeTokensSupplied()
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant InsufficientNativeTokensSupplied_error_selector = 0x8ffff980;
+uint256 constant InsufficientNativeTokensSupplied_error_length = 0x04;
+
+/*
+ * error NativeTokenTransferGenericFailure(address account, uint256 amount)
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: account
+ * - 0x40: amount
+ * Revert buffer is memory[0x1c:0x60]
+ */
+uint256 constant NativeTokenTransferGenericFailure_error_selector = 0xbc806b96;
+uint256 constant NativeTokenTransferGenericFailure_error_account_ptr = 0x20;
+uint256 constant NativeTokenTransferGenericFailure_error_amount_ptr = 0x40;
+uint256 constant NativeTokenTransferGenericFailure_error_length = 0x44;
+
+/*
+ * error PartialFillsNotEnabledForOrder()
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant PartialFillsNotEnabledForOrder_error_selector = 0xa11b63ff;
+uint256 constant PartialFillsNotEnabledForOrder_error_length = 0x04;
+
+/*
+ * error OrderIsCancelled(bytes32 orderHash)
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: orderHash
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant OrderIsCancelled_error_selector = 0x1a515574;
+uint256 constant OrderIsCancelled_error_orderHash_ptr = 0x20;
+uint256 constant OrderIsCancelled_error_length = 0x24;
+
+/*
+ * error OrderPartiallyFilled(bytes32 orderHash)
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: orderHash
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant OrderPartiallyFilled_error_selector = 0xee9e0e63;
+uint256 constant OrderPartiallyFilled_error_orderHash_ptr = 0x20;
+uint256 constant OrderPartiallyFilled_error_length = 0x24;
+
+/*
+ * error CannotCancelOrder()
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant CannotCancelOrder_error_selector = 0xfed398fc;
+uint256 constant CannotCancelOrder_error_length = 0x04;
+
+/*
+ * error BadFraction()
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant BadFraction_error_selector = 0x5a052b32;
+uint256 constant BadFraction_error_length = 0x04;
+
+/*
+ * error InvalidMsgValue(uint256 value)
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: value
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant InvalidMsgValue_error_selector = 0xa61be9f0;
+uint256 constant InvalidMsgValue_error_value_ptr = 0x20;
+uint256 constant InvalidMsgValue_error_length = 0x24;
+
+/*
+ * error InvalidBasicOrderParameterEncoding()
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant InvalidBasicOrderParameterEncoding_error_selector = 0x39f3e3fd;
+uint256 constant InvalidBasicOrderParameterEncoding_error_length = 0x04;
+
+/*
+ * error NoSpecifiedOrdersAvailable()
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant NoSpecifiedOrdersAvailable_error_selector = 0xd5da9a1b;
+uint256 constant NoSpecifiedOrdersAvailable_error_length = 0x04;
+
+/*
+ * error InvalidNativeOfferItem()
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant InvalidNativeOfferItem_error_selector = 0x12d3f5a3;
+uint256 constant InvalidNativeOfferItem_error_length = 0x04;
+
+/*
+ * error ConsiderationLengthNotEqualToTotalOriginal()
+ * - Defined in ConsiderationEventsAndErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * Revert buffer is memory[0x1c:0x20]
+ */
+uint256 constant ConsiderationLengthNotEqualToTotalOriginal_error_selector = (
+ 0x2165628a
+);
+uint256 constant ConsiderationLengthNotEqualToTotalOriginal_error_length = 0x04;
+
+/*
+ * error Panic(uint256 code)
+ * - Built-in Solidity error
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: code
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant Panic_error_selector = 0x4e487b71;
+uint256 constant Panic_error_code_ptr = 0x20;
+uint256 constant Panic_error_length = 0x24;
+
+uint256 constant Panic_arithmetic = 0x11;
+// uint256 constant Panic_resource = 0x41;
diff --git a/contracts/lib/ConsiderationErrors.sol b/contracts/lib/ConsiderationErrors.sol
new file mode 100644
index 000000000..f5f7f1d42
--- /dev/null
+++ b/contracts/lib/ConsiderationErrors.sol
@@ -0,0 +1,712 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.17;
+
+import { Side } from "./ConsiderationEnums.sol";
+
+import {
+ BadFraction_error_length,
+ BadFraction_error_selector,
+ CannotCancelOrder_error_length,
+ CannotCancelOrder_error_selector,
+ ConsiderationLengthNotEqualToTotalOriginal_error_length,
+ ConsiderationLengthNotEqualToTotalOriginal_error_selector,
+ ConsiderationNotMet_error_considerationIndex_ptr,
+ ConsiderationNotMet_error_length,
+ ConsiderationNotMet_error_orderIndex_ptr,
+ ConsiderationNotMet_error_selector,
+ ConsiderationNotMet_error_shortfallAmount_ptr,
+ CriteriaNotEnabledForItem_error_length,
+ CriteriaNotEnabledForItem_error_selector,
+ Error_selector_offset,
+ InsufficientNativeTokensSupplied_error_length,
+ InsufficientNativeTokensSupplied_error_selector,
+ InvalidBasicOrderParameterEncoding_error_length,
+ InvalidBasicOrderParameterEncoding_error_selector,
+ InvalidCallToConduit_error_conduit_ptr,
+ InvalidCallToConduit_error_length,
+ InvalidCallToConduit_error_selector,
+ InvalidConduit_error_conduit_ptr,
+ InvalidConduit_error_conduitKey_ptr,
+ InvalidConduit_error_length,
+ InvalidConduit_error_selector,
+ InvalidContractOrder_error_length,
+ InvalidContractOrder_error_orderHash_ptr,
+ InvalidContractOrder_error_selector,
+ InvalidERC721TransferAmount_error_amount_ptr,
+ InvalidERC721TransferAmount_error_length,
+ InvalidERC721TransferAmount_error_selector,
+ InvalidMsgValue_error_length,
+ InvalidMsgValue_error_selector,
+ InvalidMsgValue_error_value_ptr,
+ InvalidNativeOfferItem_error_length,
+ InvalidNativeOfferItem_error_selector,
+ InvalidProof_error_length,
+ InvalidProof_error_selector,
+ InvalidTime_error_endTime_ptr,
+ InvalidTime_error_length,
+ InvalidTime_error_selector,
+ InvalidTime_error_startTime_ptr,
+ MismatchedOfferAndConsiderationComponents_error_idx_ptr,
+ MismatchedOfferAndConsiderationComponents_error_length,
+ MismatchedOfferAndConsiderationComponents_error_selector,
+ MissingFulfillmentComponentOnAggregation_error_length,
+ MissingFulfillmentComponentOnAggregation_error_selector,
+ MissingFulfillmentComponentOnAggregation_error_side_ptr,
+ MissingOriginalConsiderationItems_error_length,
+ MissingOriginalConsiderationItems_error_selector,
+ NoReentrantCalls_error_length,
+ NoReentrantCalls_error_selector,
+ NoSpecifiedOrdersAvailable_error_length,
+ NoSpecifiedOrdersAvailable_error_selector,
+ OfferAndConsiderationRequiredOnFulfillment_error_length,
+ OfferAndConsiderationRequiredOnFulfillment_error_selector,
+ OrderAlreadyFilled_error_length,
+ OrderAlreadyFilled_error_orderHash_ptr,
+ OrderAlreadyFilled_error_selector,
+ OrderCriteriaResolverOutOfRange_error_length,
+ OrderCriteriaResolverOutOfRange_error_selector,
+ OrderCriteriaResolverOutOfRange_error_side_ptr,
+ OrderIsCancelled_error_length,
+ OrderIsCancelled_error_orderHash_ptr,
+ OrderIsCancelled_error_selector,
+ OrderPartiallyFilled_error_length,
+ OrderPartiallyFilled_error_orderHash_ptr,
+ OrderPartiallyFilled_error_selector,
+ PartialFillsNotEnabledForOrder_error_length,
+ PartialFillsNotEnabledForOrder_error_selector,
+ UnresolvedConsiderationCriteria_error_considerationIdx_ptr,
+ UnresolvedConsiderationCriteria_error_length,
+ UnresolvedConsiderationCriteria_error_orderIndex_ptr,
+ UnresolvedConsiderationCriteria_error_selector,
+ UnresolvedOfferCriteria_error_length,
+ UnresolvedOfferCriteria_error_offerIndex_ptr,
+ UnresolvedOfferCriteria_error_orderIndex_ptr,
+ UnresolvedOfferCriteria_error_selector,
+ UnusedItemParameters_error_length,
+ UnusedItemParameters_error_selector
+} from "./ConsiderationErrorConstants.sol";
+
+/**
+ * @dev Reverts the current transaction with a "BadFraction" error message.
+ */
+function _revertBadFraction() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, BadFraction_error_selector)
+
+ // revert(abi.encodeWithSignature("BadFraction()"))
+ revert(Error_selector_offset, BadFraction_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with a "ConsiderationNotMet" error
+ * message, including the provided order index, consideration index, and
+ * shortfall amount.
+ *
+ * @param orderIndex The index of the order that did not meet the
+ * consideration criteria.
+ * @param considerationIndex The index of the consideration item that did not
+ * meet its criteria.
+ * @param shortfallAmount The amount by which the consideration criteria were
+ * not met.
+ */
+function _revertConsiderationNotMet(
+ uint256 orderIndex,
+ uint256 considerationIndex,
+ uint256 shortfallAmount
+) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, ConsiderationNotMet_error_selector)
+
+ // Store arguments.
+ mstore(ConsiderationNotMet_error_orderIndex_ptr, orderIndex)
+ mstore(
+ ConsiderationNotMet_error_considerationIndex_ptr,
+ considerationIndex
+ )
+ mstore(ConsiderationNotMet_error_shortfallAmount_ptr, shortfallAmount)
+
+ // revert(abi.encodeWithSignature(
+ // "ConsiderationNotMet(uint256,uint256,uint256)",
+ // orderIndex,
+ // considerationIndex,
+ // shortfallAmount
+ // ))
+ revert(Error_selector_offset, ConsiderationNotMet_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with a "CriteriaNotEnabledForItem" error
+ * message.
+ */
+function _revertCriteriaNotEnabledForItem() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, CriteriaNotEnabledForItem_error_selector)
+
+ // revert(abi.encodeWithSignature("CriteriaNotEnabledForItem()"))
+ revert(Error_selector_offset, CriteriaNotEnabledForItem_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an
+ * "InsufficientNativeTokensSupplied" error message.
+ */
+function _revertInsufficientNativeTokensSupplied() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InsufficientNativeTokensSupplied_error_selector)
+
+ // revert(abi.encodeWithSignature("InsufficientNativeTokensSupplied()"))
+ revert(
+ Error_selector_offset,
+ InsufficientNativeTokensSupplied_error_length
+ )
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an
+ * "InvalidBasicOrderParameterEncoding" error message.
+ */
+function _revertInvalidBasicOrderParameterEncoding() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidBasicOrderParameterEncoding_error_selector)
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidBasicOrderParameterEncoding()"
+ // ))
+ revert(
+ Error_selector_offset,
+ InvalidBasicOrderParameterEncoding_error_length
+ )
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an "InvalidCallToConduit" error
+ * message, including the provided address of the conduit that was called
+ * improperly.
+ *
+ * @param conduit The address of the conduit that was called improperly.
+ */
+function _revertInvalidCallToConduit(address conduit) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidCallToConduit_error_selector)
+
+ // Store argument.
+ mstore(InvalidCallToConduit_error_conduit_ptr, conduit)
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidCallToConduit(address)",
+ // conduit
+ // ))
+ revert(Error_selector_offset, InvalidCallToConduit_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an "CannotCancelOrder" error
+ * message.
+ */
+function _revertCannotCancelOrder() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, CannotCancelOrder_error_selector)
+
+ // revert(abi.encodeWithSignature("CannotCancelOrder()"))
+ revert(Error_selector_offset, CannotCancelOrder_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an "InvalidConduit" error message,
+ * including the provided key and address of the invalid conduit.
+ *
+ * @param conduitKey The key of the invalid conduit.
+ * @param conduit The address of the invalid conduit.
+ */
+function _revertInvalidConduit(bytes32 conduitKey, address conduit) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidConduit_error_selector)
+
+ // Store arguments.
+ mstore(InvalidConduit_error_conduitKey_ptr, conduitKey)
+ mstore(InvalidConduit_error_conduit_ptr, conduit)
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidConduit(bytes32,address)",
+ // conduitKey,
+ // conduit
+ // ))
+ revert(Error_selector_offset, InvalidConduit_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an "InvalidERC721TransferAmount"
+ * error message.
+ *
+ * @param amount The invalid amount.
+ */
+function _revertInvalidERC721TransferAmount(uint256 amount) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidERC721TransferAmount_error_selector)
+
+ // Store argument.
+ mstore(InvalidERC721TransferAmount_error_amount_ptr, amount)
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidERC721TransferAmount(uint256)",
+ // amount
+ // ))
+ revert(Error_selector_offset, InvalidERC721TransferAmount_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an "InvalidMsgValue" error message,
+ * including the invalid value that was sent in the transaction's
+ * `msg.value` field.
+ *
+ * @param value The invalid value that was sent in the transaction's `msg.value`
+ * field.
+ */
+function _revertInvalidMsgValue(uint256 value) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidMsgValue_error_selector)
+
+ // Store argument.
+ mstore(InvalidMsgValue_error_value_ptr, value)
+
+ // revert(abi.encodeWithSignature("InvalidMsgValue(uint256)", value))
+ revert(Error_selector_offset, InvalidMsgValue_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an "InvalidNativeOfferItem" error
+ * message.
+ */
+function _revertInvalidNativeOfferItem() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidNativeOfferItem_error_selector)
+
+ // revert(abi.encodeWithSignature("InvalidNativeOfferItem()"))
+ revert(Error_selector_offset, InvalidNativeOfferItem_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an "InvalidProof" error message.
+ */
+function _revertInvalidProof() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidProof_error_selector)
+
+ // revert(abi.encodeWithSignature("InvalidProof()"))
+ revert(Error_selector_offset, InvalidProof_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an "InvalidContractOrder" error
+ * message.
+ *
+ * @param orderHash The hash of the contract order that caused the error.
+ */
+function _revertInvalidContractOrder(bytes32 orderHash) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidContractOrder_error_selector)
+
+ // Store arguments.
+ mstore(InvalidContractOrder_error_orderHash_ptr, orderHash)
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidContractOrder(bytes32)",
+ // orderHash
+ // ))
+ revert(Error_selector_offset, InvalidContractOrder_error_length)
+ }
+}
+
+/**
+ * @dev Reverts the current transaction with an "InvalidTime" error message.
+ *
+ * @param startTime The time at which the order becomes active.
+ * @param endTime The time at which the order becomes inactive.
+ */
+function _revertInvalidTime(uint256 startTime, uint256 endTime) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, InvalidTime_error_selector)
+
+ // Store arguments.
+ mstore(InvalidTime_error_startTime_ptr, startTime)
+ mstore(InvalidTime_error_endTime_ptr, endTime)
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidTime(uint256,uint256)",
+ // startTime,
+ // endTime
+ // ))
+ revert(Error_selector_offset, InvalidTime_error_length)
+ }
+}
+
+/**
+ * @dev Reverts execution with a
+ * "MismatchedFulfillmentOfferAndConsiderationComponents" error message.
+ *
+ * @param fulfillmentIndex The index of the fulfillment that caused the
+ * error.
+ */
+function _revertMismatchedFulfillmentOfferAndConsiderationComponents(
+ uint256 fulfillmentIndex
+) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, MismatchedOfferAndConsiderationComponents_error_selector)
+
+ // Store fulfillment index argument.
+ mstore(
+ MismatchedOfferAndConsiderationComponents_error_idx_ptr,
+ fulfillmentIndex
+ )
+
+ // revert(abi.encodeWithSignature(
+ // "MismatchedFulfillmentOfferAndConsiderationComponents(uint256)",
+ // fulfillmentIndex
+ // ))
+ revert(
+ Error_selector_offset,
+ MismatchedOfferAndConsiderationComponents_error_length
+ )
+ }
+}
+
+/**
+ * @dev Reverts execution with a "MissingFulfillmentComponentOnAggregation"
+ * error message.
+ *
+ * @param side The side of the fulfillment component that is missing (0 for
+ * offer, 1 for consideration).
+ *
+ */
+function _revertMissingFulfillmentComponentOnAggregation(Side side) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, MissingFulfillmentComponentOnAggregation_error_selector)
+
+ // Store argument.
+ mstore(MissingFulfillmentComponentOnAggregation_error_side_ptr, side)
+
+ // revert(abi.encodeWithSignature(
+ // "MissingFulfillmentComponentOnAggregation(uint8)",
+ // side
+ // ))
+ revert(
+ Error_selector_offset,
+ MissingFulfillmentComponentOnAggregation_error_length
+ )
+ }
+}
+
+/**
+ * @dev Reverts execution with a "MissingOriginalConsiderationItems" error
+ * message.
+ */
+function _revertMissingOriginalConsiderationItems() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, MissingOriginalConsiderationItems_error_selector)
+
+ // revert(abi.encodeWithSignature(
+ // "MissingOriginalConsiderationItems()"
+ // ))
+ revert(
+ Error_selector_offset,
+ MissingOriginalConsiderationItems_error_length
+ )
+ }
+}
+
+/**
+ * @dev Reverts execution with a "NoReentrantCalls" error message.
+ */
+function _revertNoReentrantCalls() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, NoReentrantCalls_error_selector)
+
+ // revert(abi.encodeWithSignature("NoReentrantCalls()"))
+ revert(Error_selector_offset, NoReentrantCalls_error_length)
+ }
+}
+
+/**
+ * @dev Reverts execution with a "NoSpecifiedOrdersAvailable" error message.
+ */
+function _revertNoSpecifiedOrdersAvailable() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, NoSpecifiedOrdersAvailable_error_selector)
+
+ // revert(abi.encodeWithSignature("NoSpecifiedOrdersAvailable()"))
+ revert(Error_selector_offset, NoSpecifiedOrdersAvailable_error_length)
+ }
+}
+
+/**
+ * @dev Reverts execution with a "OfferAndConsiderationRequiredOnFulfillment"
+ * error message.
+ */
+function _revertOfferAndConsiderationRequiredOnFulfillment() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, OfferAndConsiderationRequiredOnFulfillment_error_selector)
+
+ // revert(abi.encodeWithSignature(
+ // "OfferAndConsiderationRequiredOnFulfillment()"
+ // ))
+ revert(
+ Error_selector_offset,
+ OfferAndConsiderationRequiredOnFulfillment_error_length
+ )
+ }
+}
+
+/**
+ * @dev Reverts execution with an "OrderAlreadyFilled" error message.
+ *
+ * @param orderHash The hash of the order that has already been filled.
+ */
+function _revertOrderAlreadyFilled(bytes32 orderHash) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, OrderAlreadyFilled_error_selector)
+
+ // Store argument.
+ mstore(OrderAlreadyFilled_error_orderHash_ptr, orderHash)
+
+ // revert(abi.encodeWithSignature(
+ // "OrderAlreadyFilled(bytes32)",
+ // orderHash
+ // ))
+ revert(Error_selector_offset, OrderAlreadyFilled_error_length)
+ }
+}
+
+/**
+ * @dev Reverts execution with an "OrderCriteriaResolverOutOfRange" error
+ * message.
+ *
+ * @param side The side of the criteria that is missing (0 for offer, 1 for
+ * consideration).
+ *
+ */
+function _revertOrderCriteriaResolverOutOfRange(Side side) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, OrderCriteriaResolverOutOfRange_error_selector)
+
+ // Store argument.
+ mstore(OrderCriteriaResolverOutOfRange_error_side_ptr, side)
+
+ // revert(abi.encodeWithSignature(
+ // "OrderCriteriaResolverOutOfRange(uint8)",
+ // side
+ // ))
+ revert(
+ Error_selector_offset,
+ OrderCriteriaResolverOutOfRange_error_length
+ )
+ }
+}
+
+/**
+ * @dev Reverts execution with an "OrderIsCancelled" error message.
+ *
+ * @param orderHash The hash of the order that has already been cancelled.
+ */
+function _revertOrderIsCancelled(bytes32 orderHash) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, OrderIsCancelled_error_selector)
+
+ // Store argument.
+ mstore(OrderIsCancelled_error_orderHash_ptr, orderHash)
+
+ // revert(abi.encodeWithSignature(
+ // "OrderIsCancelled(bytes32)",
+ // orderHash
+ // ))
+ revert(Error_selector_offset, OrderIsCancelled_error_length)
+ }
+}
+
+/**
+ * @dev Reverts execution with an "OrderPartiallyFilled" error message.
+ *
+ * @param orderHash The hash of the order that has already been partially
+ * filled.
+ */
+function _revertOrderPartiallyFilled(bytes32 orderHash) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, OrderPartiallyFilled_error_selector)
+
+ // Store argument.
+ mstore(OrderPartiallyFilled_error_orderHash_ptr, orderHash)
+
+ // revert(abi.encodeWithSignature(
+ // "OrderPartiallyFilled(bytes32)",
+ // orderHash
+ // ))
+ revert(Error_selector_offset, OrderPartiallyFilled_error_length)
+ }
+}
+
+/**
+ * @dev Reverts execution with a "PartialFillsNotEnabledForOrder" error message.
+ */
+function _revertPartialFillsNotEnabledForOrder() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, PartialFillsNotEnabledForOrder_error_selector)
+
+ // revert(abi.encodeWithSignature("PartialFillsNotEnabledForOrder()"))
+ revert(
+ Error_selector_offset,
+ PartialFillsNotEnabledForOrder_error_length
+ )
+ }
+}
+
+/**
+ * @dev Reverts execution with an "UnresolvedConsiderationCriteria" error
+ * message.
+ */
+function _revertUnresolvedConsiderationCriteria(
+ uint256 orderIndex,
+ uint256 considerationIndex
+) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, UnresolvedConsiderationCriteria_error_selector)
+
+ // Store orderIndex and considerationIndex arguments.
+ mstore(UnresolvedConsiderationCriteria_error_orderIndex_ptr, orderIndex)
+ mstore(
+ UnresolvedConsiderationCriteria_error_considerationIdx_ptr,
+ considerationIndex
+ )
+
+ // revert(abi.encodeWithSignature(
+ // "UnresolvedConsiderationCriteria(uint256, uint256)",
+ // orderIndex,
+ // considerationIndex
+ // ))
+ revert(
+ Error_selector_offset,
+ UnresolvedConsiderationCriteria_error_length
+ )
+ }
+}
+
+/**
+ * @dev Reverts execution with an "UnresolvedOfferCriteria" error message.
+ */
+function _revertUnresolvedOfferCriteria(
+ uint256 orderIndex,
+ uint256 offerIndex
+) pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, UnresolvedOfferCriteria_error_selector)
+
+ // Store arguments.
+ mstore(UnresolvedOfferCriteria_error_orderIndex_ptr, orderIndex)
+ mstore(UnresolvedOfferCriteria_error_offerIndex_ptr, offerIndex)
+
+ // revert(abi.encodeWithSignature(
+ // "UnresolvedOfferCriteria(uint256, uint256)",
+ // orderIndex,
+ // offerIndex
+ // ))
+ revert(Error_selector_offset, UnresolvedOfferCriteria_error_length)
+ }
+}
+
+/**
+ * @dev Reverts execution with an "UnusedItemParameters" error message.
+ */
+function _revertUnusedItemParameters() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, UnusedItemParameters_error_selector)
+
+ // revert(abi.encodeWithSignature("UnusedItemParameters()"))
+ revert(Error_selector_offset, UnusedItemParameters_error_length)
+ }
+}
+
+/**
+ * @dev Reverts execution with a "ConsiderationLengthNotEqualToTotalOriginal"
+ * error message.
+ */
+function _revertConsiderationLengthNotEqualToTotalOriginal() pure {
+ assembly {
+ // Store left-padded selector with push4 (reduces bytecode),
+ // mem[28:32] = selector
+ mstore(0, ConsiderationLengthNotEqualToTotalOriginal_error_selector)
+
+ // revert(abi.encodeWithSignature(
+ // "ConsiderationLengthNotEqualToTotalOriginal()"
+ // ))
+ revert(
+ Error_selector_offset,
+ ConsiderationLengthNotEqualToTotalOriginal_error_length
+ )
+ }
+}
diff --git a/contracts/lib/ConsiderationStructs.sol b/contracts/lib/ConsiderationStructs.sol
index 064d01d9a..04cd4aaf0 100644
--- a/contracts/lib/ConsiderationStructs.sol
+++ b/contracts/lib/ConsiderationStructs.sol
@@ -1,14 +1,18 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-// prettier-ignore
import {
- OrderType,
BasicOrderType,
ItemType,
+ OrderType,
Side
} from "./ConsiderationEnums.sol";
+import {
+ CalldataPointer,
+ MemoryPointer
+} from "../helpers/PointerLibraries.sol";
+
/**
* @dev An order contains eleven components: an offerer, a zone (or account that
* can cancel the order or restrict who can fulfill the order depending on
@@ -163,9 +167,9 @@ struct Order {
* @dev Advanced orders include a numerator (i.e. a fraction to attempt to fill)
* and a denominator (the total size of the order) in addition to the
* signature and other order parameters. It also supports an optional field
- * for supplying extra data; this data will be included in a staticcall to
- * `isValidOrderIncludingExtraData` on the zone for the order if the order
- * type is restricted and the offerer or zone are not the caller.
+ * for supplying extra data; this data will be provided to the zone if the
+ * order type is restricted and the zone is not the caller, or will be
+ * provided to the offerer as context for contract order types.
*/
struct AdvancedOrder {
OrderParameters parameters;
@@ -242,3 +246,534 @@ struct Execution {
address offerer;
bytes32 conduitKey;
}
+
+/**
+ * @dev Restricted orders are validated post-execution by calling validateOrder
+ * on the zone. This struct provides context about the order fulfillment
+ * and any supplied extraData, as well as all order hashes fulfilled in a
+ * call to a match or fulfillAvailable method.
+ */
+struct ZoneParameters {
+ bytes32 orderHash;
+ address fulfiller;
+ address offerer;
+ SpentItem[] offer;
+ ReceivedItem[] consideration;
+ bytes extraData;
+ bytes32[] orderHashes;
+ uint256 startTime;
+ uint256 endTime;
+ bytes32 zoneHash;
+}
+
+/**
+ * @dev Zones and contract offerers can communicate which schemas they implement
+ * along with any associated metadata related to each schema.
+ */
+struct Schema {
+ uint256 id;
+ bytes metadata;
+}
+
+using StructPointers for OrderComponents global;
+using StructPointers for OfferItem global;
+using StructPointers for ConsiderationItem global;
+using StructPointers for SpentItem global;
+using StructPointers for ReceivedItem global;
+using StructPointers for BasicOrderParameters global;
+using StructPointers for AdditionalRecipient global;
+using StructPointers for OrderParameters global;
+using StructPointers for Order global;
+using StructPointers for AdvancedOrder global;
+using StructPointers for OrderStatus global;
+using StructPointers for CriteriaResolver global;
+using StructPointers for Fulfillment global;
+using StructPointers for FulfillmentComponent global;
+using StructPointers for Execution global;
+using StructPointers for ZoneParameters global;
+
+/**
+ * @dev This library provides a set of functions for converting structs to
+ * pointers.
+ */
+library StructPointers {
+ /**
+ * @dev Get a MemoryPointer from OrderComponents.
+ *
+ * @param obj The OrderComponents object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ OrderComponents memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from OrderComponents.
+ *
+ * @param obj The OrderComponents object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ OrderComponents calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from OfferItem.
+ *
+ * @param obj The OfferItem object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ OfferItem memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from OfferItem.
+ *
+ * @param obj The OfferItem object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ OfferItem calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from ConsiderationItem.
+ *
+ * @param obj The ConsiderationItem object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ ConsiderationItem memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from ConsiderationItem.
+ *
+ * @param obj The ConsiderationItem object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ ConsiderationItem calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from SpentItem.
+ *
+ * @param obj The SpentItem object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ SpentItem memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from SpentItem.
+ *
+ * @param obj The SpentItem object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ SpentItem calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from ReceivedItem.
+ *
+ * @param obj The ReceivedItem object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ ReceivedItem memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from ReceivedItem.
+ *
+ * @param obj The ReceivedItem object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ ReceivedItem calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from BasicOrderParameters.
+ *
+ * @param obj The BasicOrderParameters object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ BasicOrderParameters memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from BasicOrderParameters.
+ *
+ * @param obj The BasicOrderParameters object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ BasicOrderParameters calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from AdditionalRecipient.
+ *
+ * @param obj The AdditionalRecipient object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ AdditionalRecipient memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from AdditionalRecipient.
+ *
+ * @param obj The AdditionalRecipient object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ AdditionalRecipient calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from OrderParameters.
+ *
+ * @param obj The OrderParameters object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ OrderParameters memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from OrderParameters.
+ *
+ * @param obj The OrderParameters object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ OrderParameters calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from Order.
+ *
+ * @param obj The Order object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ Order memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from Order.
+ *
+ * @param obj The Order object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ Order calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from AdvancedOrder.
+ *
+ * @param obj The AdvancedOrder object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ AdvancedOrder memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from AdvancedOrder.
+ *
+ * @param obj The AdvancedOrder object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ AdvancedOrder calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from OrderStatus.
+ *
+ * @param obj The OrderStatus object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ OrderStatus memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from OrderStatus.
+ *
+ * @param obj The OrderStatus object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ OrderStatus calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from CriteriaResolver.
+ *
+ * @param obj The CriteriaResolver object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ CriteriaResolver memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from CriteriaResolver.
+ *
+ * @param obj The CriteriaResolver object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ CriteriaResolver calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from Fulfillment.
+ *
+ * @param obj The Fulfillment object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ Fulfillment memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from Fulfillment.
+ *
+ * @param obj The Fulfillment object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ Fulfillment calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from FulfillmentComponent.
+ *
+ * @param obj The FulfillmentComponent object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ FulfillmentComponent memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from FulfillmentComponent.
+ *
+ * @param obj The FulfillmentComponent object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ FulfillmentComponent calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from Execution.
+ *
+ * @param obj The Execution object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ Execution memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from Execution.
+ *
+ * @param obj The Execution object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ Execution calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a MemoryPointer from ZoneParameters.
+ *
+ * @param obj The ZoneParameters object.
+ *
+ * @return ptr The MemoryPointer.
+ */
+ function toMemoryPointer(
+ ZoneParameters memory obj
+ ) internal pure returns (MemoryPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+
+ /**
+ * @dev Get a CalldataPointer from ZoneParameters.
+ *
+ * @param obj The ZoneParameters object.
+ *
+ * @return ptr The CalldataPointer.
+ */
+ function toCalldataPointer(
+ ZoneParameters calldata obj
+ ) internal pure returns (CalldataPointer ptr) {
+ assembly {
+ ptr := obj
+ }
+ }
+}
diff --git a/contracts/lib/CounterManager.sol b/contracts/lib/CounterManager.sol
index 69532b391..710411a17 100644
--- a/contracts/lib/CounterManager.sol
+++ b/contracts/lib/CounterManager.sol
@@ -1,13 +1,18 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
-// prettier-ignore
import {
ConsiderationEventsAndErrors
} from "../interfaces/ConsiderationEventsAndErrors.sol";
import { ReentrancyGuard } from "./ReentrancyGuard.sol";
+import {
+ Counter_blockhash_shift,
+ OneWord,
+ TwoWords
+} from "./ConsiderationConstants.sol";
+
/**
* @title CounterManager
* @author 0age
@@ -19,9 +24,13 @@ contract CounterManager is ConsiderationEventsAndErrors, ReentrancyGuard {
mapping(address => uint256) private _counters;
/**
- * @dev Internal function to cancel all orders from a given offerer with a
- * given zone in bulk by incrementing a counter. Note that only the
- * offerer may increment the counter.
+ * @dev Internal function to cancel all orders from a given offerer in bulk
+ * by incrementing a counter by a large, quasi-random interval. Note
+ * that only the offerer may increment the counter. Note that the
+ * counter is incremented by a large, quasi-random interval, which
+ * makes it infeasible to "activate" signed orders by incrementing the
+ * counter. This activation functionality can be achieved instead with
+ * restricted orders or contract orders.
*
* @return newCounter The new counter.
*/
@@ -29,10 +38,29 @@ contract CounterManager is ConsiderationEventsAndErrors, ReentrancyGuard {
// Ensure that the reentrancy guard is not currently set.
_assertNonReentrant();
- // Skip overflow check as counter cannot be incremented that far.
- unchecked {
- // Increment current counter for the supplied offerer.
- newCounter = ++_counters[msg.sender];
+ // Utilize assembly to access counters storage mapping directly. Skip
+ // overflow check as counter cannot be incremented that far.
+ assembly {
+ // Use second half of previous block hash as a quasi-random number.
+ let quasiRandomNumber := shr(
+ Counter_blockhash_shift,
+ blockhash(sub(number(), 1))
+ )
+
+ // Write the caller to scratch space.
+ mstore(0, caller())
+
+ // Write the storage slot for _counters to scratch space.
+ mstore(OneWord, _counters.slot)
+
+ // Derive the storage pointer for the counter value.
+ let storagePointer := keccak256(0, TwoWords)
+
+ // Derive new counter value using random number and original value.
+ newCounter := add(quasiRandomNumber, sload(storagePointer))
+
+ // Store the updated counter value.
+ sstore(storagePointer, newCounter)
}
// Emit an event containing the new counter.
@@ -47,11 +75,9 @@ contract CounterManager is ConsiderationEventsAndErrors, ReentrancyGuard {
*
* @return currentCounter The current counter.
*/
- function _getCounter(address offerer)
- internal
- view
- returns (uint256 currentCounter)
- {
+ function _getCounter(
+ address offerer
+ ) internal view returns (uint256 currentCounter) {
// Return the counter for the supplied offerer.
currentCounter = _counters[offerer];
}
diff --git a/contracts/lib/CriteriaResolution.sol b/contracts/lib/CriteriaResolution.sol
index 6c814958a..076c3852d 100644
--- a/contracts/lib/CriteriaResolution.sol
+++ b/contracts/lib/CriteriaResolution.sol
@@ -1,24 +1,42 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
import { ItemType, Side } from "./ConsiderationEnums.sol";
-// prettier-ignore
import {
- OfferItem,
- ConsiderationItem,
- OrderParameters,
AdvancedOrder,
- CriteriaResolver
+ CriteriaResolver,
+ MemoryPointer,
+ OfferItem,
+ OrderParameters
} from "./ConsiderationStructs.sol";
-import "./ConsiderationConstants.sol";
+import {
+ _revertCriteriaNotEnabledForItem,
+ _revertInvalidProof,
+ _revertOrderCriteriaResolverOutOfRange,
+ _revertUnresolvedConsiderationCriteria,
+ _revertUnresolvedOfferCriteria
+} from "./ConsiderationErrors.sol";
-// prettier-ignore
import {
CriteriaResolutionErrors
} from "../interfaces/CriteriaResolutionErrors.sol";
+import {
+ OneWord,
+ OneWordShift,
+ OrderParameters_consideration_head_offset,
+ Selector_length,
+ TwoWords
+} from "./ConsiderationConstants.sol";
+
+import {
+ ConsiderationCriteriaResolverOutOfRange_err_selector,
+ Error_selector_offset,
+ OfferCriteriaResolverOutOfRange_error_selector
+} from "./ConsiderationErrorConstants.sol";
+
/**
* @title CriteriaResolution
* @author 0age
@@ -64,104 +82,86 @@ contract CriteriaResolution is CriteriaResolutionErrors {
// Ensure that the order index is in range.
if (orderIndex >= totalAdvancedOrders) {
- revert OrderCriteriaResolverOutOfRange();
+ _revertOrderCriteriaResolverOutOfRange(
+ criteriaResolver.side
+ );
}
+ // Retrieve the referenced advanced order.
+ AdvancedOrder memory advancedOrder = advancedOrders[orderIndex];
+
// Skip criteria resolution for order if not fulfilled.
- if (advancedOrders[orderIndex].numerator == 0) {
+ if (advancedOrder.numerator == 0) {
continue;
}
// Retrieve the parameters for the order.
OrderParameters memory orderParameters = (
- advancedOrders[orderIndex].parameters
+ advancedOrder.parameters
);
- // Read component index from memory and place it on the stack.
- uint256 componentIndex = criteriaResolver.index;
+ {
+ // Get a pointer to the list of items to give to
+ // _updateCriteriaItem. If the resolver refers to a
+ // consideration item, this array pointer will be replaced
+ // with the consideration array.
+ OfferItem[] memory items = orderParameters.offer;
- // Declare values for item's type and criteria.
- ItemType itemType;
- uint256 identifierOrCriteria;
+ // Read component index from memory and place it on stack.
+ uint256 componentIndex = criteriaResolver.index;
- // If the criteria resolver refers to an offer item...
- if (criteriaResolver.side == Side.OFFER) {
- // Retrieve the offer.
- OfferItem[] memory offer = orderParameters.offer;
-
- // Ensure that the component index is in range.
- if (componentIndex >= offer.length) {
- revert OfferCriteriaResolverOutOfRange();
- }
-
- // Retrieve relevant item using the component index.
- OfferItem memory offerItem = offer[componentIndex];
-
- // Read item type and criteria from memory & place on stack.
- itemType = offerItem.itemType;
- identifierOrCriteria = offerItem.identifierOrCriteria;
-
- // Optimistically update item type to remove criteria usage.
- // Use assembly to operate on ItemType enum as a number.
- ItemType newItemType;
- assembly {
- // Item type 4 becomes 2 and item type 5 becomes 3.
- newItemType := sub(3, eq(itemType, 4))
- }
- offerItem.itemType = newItemType;
-
- // Optimistically update identifier w/ supplied identifier.
- offerItem.identifierOrCriteria = criteriaResolver
- .identifier;
- } else {
- // Otherwise, the resolver refers to a consideration item.
- ConsiderationItem[] memory consideration = (
- orderParameters.consideration
+ // Get error selector for `OfferCriteriaResolverOutOfRange`.
+ uint256 errorSelector = (
+ OfferCriteriaResolverOutOfRange_error_selector
);
- // Ensure that the component index is in range.
- if (componentIndex >= consideration.length) {
- revert ConsiderationCriteriaResolverOutOfRange();
+ // If the resolver refers to a consideration item...
+ if (criteriaResolver.side != Side.OFFER) {
+ // Get the pointer to `orderParameters.consideration`
+ // Using the array directly has a significant impact on
+ // the optimized compiler output.
+ MemoryPointer considerationPtr = orderParameters
+ .toMemoryPointer()
+ .pptr(OrderParameters_consideration_head_offset);
+
+ // Replace the items pointer with a pointer to the
+ // consideration array.
+ assembly {
+ items := considerationPtr
+ }
+
+ // Replace the error selector with the selector for
+ // `ConsiderationCriteriaResolverOutOfRange`.
+ errorSelector = (
+ ConsiderationCriteriaResolverOutOfRange_err_selector
+ );
}
- // Retrieve relevant item using order and component index.
- ConsiderationItem memory considerationItem = (
- consideration[componentIndex]
- );
-
- // Read item type and criteria from memory & place on stack.
- itemType = considerationItem.itemType;
- identifierOrCriteria = (
- considerationItem.identifierOrCriteria
- );
-
- // Optimistically update item type to remove criteria usage.
- // Use assembly to operate on ItemType enum as a number.
- ItemType newItemType;
- assembly {
- // Item type 4 becomes 2 and item type 5 becomes 3.
- newItemType := sub(3, eq(itemType, 4))
+ // Ensure that the component index is in range.
+ if (componentIndex >= items.length) {
+ assembly {
+ // Revert with either
+ // `OfferCriteriaResolverOutOfRange()` or
+ // `ConsiderationCriteriaResolverOutOfRange()`,
+ // depending on whether the resolver refers to a
+ // consideration item.
+ mstore(0, errorSelector)
+ // revert(abi.encodeWithSignature(
+ // "OfferCriteriaResolverOutOfRange()"
+ // ))
+ // or
+ // revert(abi.encodeWithSignature(
+ // "ConsiderationCriteriaResolverOutOfRange()"
+ // ))
+ revert(Error_selector_offset, Selector_length)
+ }
}
- considerationItem.itemType = newItemType;
- // Optimistically update identifier w/ supplied identifier.
- considerationItem.identifierOrCriteria = (
- criteriaResolver.identifier
- );
- }
-
- // Ensure the specified item type indicates criteria usage.
- if (!_isItemWithCriteria(itemType)) {
- revert CriteriaNotEnabledForItem();
- }
-
- // If criteria is not 0 (i.e. a collection-wide offer)...
- if (identifierOrCriteria != uint256(0)) {
- // Verify identifier inclusion in criteria root using proof.
- _verifyProof(
- criteriaResolver.identifier,
- identifierOrCriteria,
- criteriaResolver.criteriaProof
+ // Apply the criteria resolver to the item in question.
+ _updateCriteriaItem(
+ items,
+ componentIndex,
+ criteriaResolver
);
}
}
@@ -192,7 +192,7 @@ contract CriteriaResolution is CriteriaResolutionErrors {
orderParameters.consideration[j].itemType
)
) {
- revert UnresolvedConsiderationCriteria();
+ _revertUnresolvedConsiderationCriteria(i, j);
}
}
@@ -205,13 +205,64 @@ contract CriteriaResolution is CriteriaResolutionErrors {
if (
_isItemWithCriteria(orderParameters.offer[j].itemType)
) {
- revert UnresolvedOfferCriteria();
+ _revertUnresolvedOfferCriteria(i, j);
}
}
}
}
}
+ /**
+ * @dev Internal pure function to update a criteria item.
+ *
+ * @param offer The offer containing the item to update.
+ * @param componentIndex The index of the item to update.
+ * @param criteriaResolver The criteria resolver to use to update the item.
+ */
+ function _updateCriteriaItem(
+ OfferItem[] memory offer,
+ uint256 componentIndex,
+ CriteriaResolver memory criteriaResolver
+ ) internal pure {
+ // Retrieve relevant item using the component index.
+ OfferItem memory offerItem = offer[componentIndex];
+
+ // Read item type and criteria from memory & place on stack.
+ ItemType itemType = offerItem.itemType;
+
+ // Ensure the specified item type indicates criteria usage.
+ if (!_isItemWithCriteria(itemType)) {
+ _revertCriteriaNotEnabledForItem();
+ }
+
+ uint256 identifierOrCriteria = offerItem.identifierOrCriteria;
+
+ // If criteria is not 0 (i.e. a collection-wide criteria-based item)...
+ if (identifierOrCriteria != uint256(0)) {
+ // Verify identifier inclusion in criteria root using proof.
+ _verifyProof(
+ criteriaResolver.identifier,
+ identifierOrCriteria,
+ criteriaResolver.criteriaProof
+ );
+ } else if (criteriaResolver.criteriaProof.length != 0) {
+ // Revert if non-empty proof is supplied for a collection-wide item.
+ _revertInvalidProof();
+ }
+
+ // Update item type to remove criteria usage.
+ // Use assembly to operate on ItemType enum as a number.
+ ItemType newItemType;
+ assembly {
+ // Item type 4 becomes 2 and item type 5 becomes 3.
+ newItemType := sub(3, eq(itemType, 4))
+ }
+ offerItem.itemType = newItemType;
+
+ // Update identifier w/ supplied identifier.
+ offerItem.identifierOrCriteria = criteriaResolver.identifier;
+ }
+
/**
* @dev Internal pure function to check whether a given item type represents
* a criteria-based ERC721 or ERC1155 item (e.g. an item that can be
@@ -223,11 +274,9 @@ contract CriteriaResolution is CriteriaResolutionErrors {
* @return withCriteria A boolean indicating that the item type in question
* represents a criteria-based item.
*/
- function _isItemWithCriteria(ItemType itemType)
- internal
- pure
- returns (bool withCriteria)
- {
+ function _isItemWithCriteria(
+ ItemType itemType
+ ) internal pure returns (bool withCriteria) {
// ERC721WithCriteria is ItemType 4. ERC1155WithCriteria is ItemType 5.
assembly {
withCriteria := gt(itemType, 3)
@@ -258,14 +307,13 @@ contract CriteriaResolution is CriteriaResolutionErrors {
// Derive the hash of the leaf to use as the initial proof element.
let computedHash := keccak256(0, OneWord)
- // Based on: https://github.com/Rari-Capital/solmate/blob/v7/src/utils/MerkleProof.sol
// Get memory start location of the first element in proof array.
let data := add(proof, OneWord)
// Iterate over each proof element to compute the root hash.
for {
// Left shift by 5 is equivalent to multiplying by 0x20.
- let end := add(data, shl(5, mload(proof)))
+ let end := add(data, shl(OneWordShift, mload(proof)))
} lt(data, end) {
// Increment by one word at a time.
data := add(data, OneWord)
@@ -276,7 +324,7 @@ contract CriteriaResolution is CriteriaResolutionErrors {
// Sort proof elements and place them in scratch space.
// Slot of `computedHash` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
- let scratch := shl(5, gt(computedHash, loadedData))
+ let scratch := shl(OneWordShift, gt(computedHash, loadedData))
// Store elements to hash contiguously in scratch space. Scratch
// space is 64 bytes (0x00 - 0x3f) & both elements are 32 bytes.
@@ -293,7 +341,7 @@ contract CriteriaResolution is CriteriaResolutionErrors {
// Revert if computed hash does not equal supplied root.
if (!isValid) {
- revert InvalidProof();
+ _revertInvalidProof();
}
}
}
diff --git a/contracts/lib/Executor.sol b/contracts/lib/Executor.sol
index 6e030b29b..68b207e56 100644
--- a/contracts/lib/Executor.sol
+++ b/contracts/lib/Executor.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
@@ -13,7 +13,40 @@ import { Verifiers } from "./Verifiers.sol";
import { TokenTransferrer } from "./TokenTransferrer.sol";
-import "./ConsiderationConstants.sol";
+import {
+ Accumulator_array_length_ptr,
+ Accumulator_array_offset_ptr,
+ Accumulator_array_offset,
+ Accumulator_conduitKey_ptr,
+ Accumulator_itemSizeOffsetDifference,
+ Accumulator_selector_ptr,
+ AccumulatorArmed,
+ AccumulatorDisarmed,
+ Conduit_transferItem_amount_ptr,
+ Conduit_transferItem_from_ptr,
+ Conduit_transferItem_identifier_ptr,
+ Conduit_transferItem_size,
+ Conduit_transferItem_to_ptr,
+ Conduit_transferItem_token_ptr,
+ FreeMemoryPointerSlot,
+ OneWord,
+ TwoWords
+} from "./ConsiderationConstants.sol";
+
+import {
+ Error_selector_offset,
+ NativeTokenTransferGenericFailure_error_account_ptr,
+ NativeTokenTransferGenericFailure_error_amount_ptr,
+ NativeTokenTransferGenericFailure_error_length,
+ NativeTokenTransferGenericFailure_error_selector
+} from "./ConsiderationErrorConstants.sol";
+
+import {
+ _revertInvalidCallToConduit,
+ _revertInvalidConduit,
+ _revertInvalidERC721TransferAmount,
+ _revertUnusedItemParameters
+} from "./ConsiderationErrors.sol";
/**
* @title Executor
@@ -56,15 +89,15 @@ contract Executor is Verifiers, TokenTransferrer {
if (item.itemType == ItemType.NATIVE) {
// Ensure neither the token nor the identifier parameters are set.
if ((uint160(item.token) | item.identifier) != 0) {
- revert UnusedItemParameters();
+ _revertUnusedItemParameters();
}
// transfer the native tokens to the recipient.
- _transferEth(item.recipient, item.amount);
+ _transferNativeTokens(item.recipient, item.amount);
} else if (item.itemType == ItemType.ERC20) {
// Ensure that no identifier is supplied.
if (item.identifier != 0) {
- revert UnusedItemParameters();
+ _revertUnusedItemParameters();
}
// Transfer ERC20 tokens from the source to the recipient.
@@ -101,122 +134,6 @@ contract Executor is Verifiers, TokenTransferrer {
}
}
- /**
- * @dev Internal function to transfer an individual ERC721 or ERC1155 item
- * from a given originator to a given recipient. The accumulator will
- * be bypassed, meaning that this function should be utilized in cases
- * where multiple item transfers can be accumulated into a single
- * conduit call. Sufficient approvals must be set, either on the
- * respective conduit or on this contract itself.
- *
- * @param itemType The type of item to transfer, either ERC721 or ERC1155.
- * @param token The token to transfer.
- * @param from The originator of the transfer.
- * @param to The recipient of the transfer.
- * @param identifier The tokenId to transfer.
- * @param amount The amount to transfer.
- * @param conduitKey A bytes32 value indicating what corresponding conduit,
- * if any, to source token approvals from. The zero hash
- * signifies that no conduit should be used, with direct
- * approvals set on this contract.
- */
- function _transferIndividual721Or1155Item(
- ItemType itemType,
- address token,
- address from,
- address to,
- uint256 identifier,
- uint256 amount,
- bytes32 conduitKey
- ) internal {
- // Determine if the transfer is to be performed via a conduit.
- if (conduitKey != bytes32(0)) {
- // Use free memory pointer as calldata offset for the conduit call.
- uint256 callDataOffset;
-
- // Utilize assembly to place each argument in free memory.
- assembly {
- // Retrieve the free memory pointer and use it as the offset.
- callDataOffset := mload(FreeMemoryPointerSlot)
-
- // Write ConduitInterface.execute.selector to memory.
- mstore(callDataOffset, Conduit_execute_signature)
-
- // Write the offset to the ConduitTransfer array in memory.
- mstore(
- add(
- callDataOffset,
- Conduit_execute_ConduitTransfer_offset_ptr
- ),
- Conduit_execute_ConduitTransfer_ptr
- )
-
- // Write the length of the ConduitTransfer array to memory.
- mstore(
- add(
- callDataOffset,
- Conduit_execute_ConduitTransfer_length_ptr
- ),
- Conduit_execute_ConduitTransfer_length
- )
-
- // Write the item type to memory.
- mstore(
- add(callDataOffset, Conduit_execute_transferItemType_ptr),
- itemType
- )
-
- // Write the token to memory.
- mstore(
- add(callDataOffset, Conduit_execute_transferToken_ptr),
- token
- )
-
- // Write the transfer source to memory.
- mstore(
- add(callDataOffset, Conduit_execute_transferFrom_ptr),
- from
- )
-
- // Write the transfer recipient to memory.
- mstore(add(callDataOffset, Conduit_execute_transferTo_ptr), to)
-
- // Write the token identifier to memory.
- mstore(
- add(callDataOffset, Conduit_execute_transferIdentifier_ptr),
- identifier
- )
-
- // Write the transfer amount to memory.
- mstore(
- add(callDataOffset, Conduit_execute_transferAmount_ptr),
- amount
- )
- }
-
- // Perform the call to the conduit.
- _callConduitUsingOffsets(
- conduitKey,
- callDataOffset,
- OneConduitExecute_size
- );
- } else {
- // Otherwise, determine whether it is an ERC721 or ERC1155 item.
- if (itemType == ItemType.ERC721) {
- // Ensure that exactly one 721 item is being transferred.
- if (amount != 1) {
- revert InvalidERC721TransferAmount();
- }
-
- // Perform transfer via the token contract directly.
- _performERC721Transfer(token, from, to, identifier);
- } else {
- // Perform transfer via the token contract directly.
- _performERC1155Transfer(token, from, to, identifier, amount);
- }
- }
- }
-
/**
* @dev Internal function to transfer Ether or other native tokens to a
* given recipient.
@@ -224,7 +141,10 @@ contract Executor is Verifiers, TokenTransferrer {
* @param to The recipient of the transfer.
* @param amount The amount to transfer.
*/
- function _transferEth(address payable to, uint256 amount) internal {
+ function _transferNativeTokens(
+ address payable to,
+ uint256 amount
+ ) internal {
// Ensure that the supplied amount is non-zero.
_assertNonZeroAmount(amount);
@@ -232,7 +152,7 @@ contract Executor is Verifiers, TokenTransferrer {
bool success;
assembly {
- // Transfer the ETH and store if it succeeded or not.
+ // Transfer the native token and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
@@ -242,7 +162,27 @@ contract Executor is Verifiers, TokenTransferrer {
_revertWithReasonIfOneIsReturned();
// Otherwise, revert with a generic error message.
- revert EtherTransferGenericFailure(to, amount);
+ assembly {
+ // Store left-padded selector with push4, mem[28:32] = selector
+ mstore(0, NativeTokenTransferGenericFailure_error_selector)
+
+ // Write `to` and `amount` arguments.
+ mstore(NativeTokenTransferGenericFailure_error_account_ptr, to)
+ mstore(
+ NativeTokenTransferGenericFailure_error_amount_ptr,
+ amount
+ )
+
+ // revert(abi.encodeWithSignature(
+ // "NativeTokenTransferGenericFailure(address,uint256)",
+ // to,
+ // amount
+ // ))
+ revert(
+ Error_selector_offset,
+ NativeTokenTransferGenericFailure_error_length
+ )
+ }
}
}
@@ -328,7 +268,7 @@ contract Executor is Verifiers, TokenTransferrer {
if (conduitKey == bytes32(0)) {
// Ensure that exactly one 721 item is being transferred.
if (amount != 1) {
- revert InvalidERC721TransferAmount();
+ _revertInvalidERC721TransferAmount(amount);
}
// Perform transfer via the token contract directly.
@@ -537,12 +477,12 @@ contract Executor is Verifiers, TokenTransferrer {
_revertWithReasonIfOneIsReturned();
// Otherwise, revert with a generic error.
- revert InvalidCallToConduit(conduit);
+ _revertInvalidCallToConduit(conduit);
}
// Ensure result was extracted and matches EIP-1271 magic value.
if (result != ConduitInterface.execute.selector) {
- revert InvalidConduit(conduitKey, conduit);
+ _revertInvalidConduit(conduitKey, conduit);
}
}
@@ -556,11 +496,9 @@ contract Executor is Verifiers, TokenTransferrer {
* @return accumulatorConduitKey The conduit key currently set for the
* accumulator.
*/
- function _getAccumulatorConduitKey(bytes memory accumulator)
- internal
- pure
- returns (bytes32 accumulatorConduitKey)
- {
+ function _getAccumulatorConduitKey(
+ bytes memory accumulator
+ ) internal pure returns (bytes32 accumulatorConduitKey) {
// Retrieve the current conduit key from the accumulator.
assembly {
accumulatorConduitKey := mload(
diff --git a/contracts/lib/FulfillmentApplier.sol b/contracts/lib/FulfillmentApplier.sol
index 02c0957f5..86ee261e6 100644
--- a/contracts/lib/FulfillmentApplier.sol
+++ b/contracts/lib/FulfillmentApplier.sol
@@ -1,26 +1,55 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
-import { ItemType, Side } from "./ConsiderationEnums.sol";
+import { Side } from "./ConsiderationEnums.sol";
-// prettier-ignore
import {
- OfferItem,
- ConsiderationItem,
- ReceivedItem,
- OrderParameters,
AdvancedOrder,
Execution,
- FulfillmentComponent
+ FulfillmentComponent,
+ ReceivedItem
} from "./ConsiderationStructs.sol";
-import "./ConsiderationConstants.sol";
+import {
+ _revertMismatchedFulfillmentOfferAndConsiderationComponents,
+ _revertMissingFulfillmentComponentOnAggregation,
+ _revertOfferAndConsiderationRequiredOnFulfillment
+} from "./ConsiderationErrors.sol";
-// prettier-ignore
import {
FulfillmentApplicationErrors
} from "../interfaces/FulfillmentApplicationErrors.sol";
+import {
+ AdvancedOrder_numerator_offset,
+ Common_amount_offset,
+ Common_identifier_offset,
+ Common_token_offset,
+ Execution_conduit_offset,
+ Execution_offerer_offset,
+ Fulfillment_itemIndex_offset,
+ OneWord,
+ OneWordShift,
+ OrderParameters_conduit_offset,
+ OrderParameters_consideration_head_offset,
+ OrderParameters_offer_head_offset,
+ ReceivedItem_CommonParams_size,
+ ReceivedItem_recipient_offset,
+ ReceivedItem_size
+} from "./ConsiderationConstants.sol";
+
+import {
+ Error_selector_offset,
+ InvalidFulfillmentComponentData_error_length,
+ InvalidFulfillmentComponentData_error_selector,
+ MissingItemAmount_error_length,
+ MissingItemAmount_error_selector,
+ Panic_arithmetic,
+ Panic_error_code_ptr,
+ Panic_error_length,
+ Panic_error_selector
+} from "./ConsiderationErrorConstants.sol";
+
/**
* @title FulfillmentApplier
* @author 0age
@@ -42,19 +71,22 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
* Note that each consideration amount must
* be zero in order for the match operation
* to be valid.
+ * @param fulfillmentIndex The index of the fulfillment being
+ * applied.
*
* @return execution The transfer performed as a result of the fulfillment.
*/
function _applyFulfillment(
AdvancedOrder[] memory advancedOrders,
- FulfillmentComponent[] calldata offerComponents,
- FulfillmentComponent[] calldata considerationComponents
+ FulfillmentComponent[] memory offerComponents,
+ FulfillmentComponent[] memory considerationComponents,
+ uint256 fulfillmentIndex
) internal pure returns (Execution memory execution) {
// Ensure 1+ of both offer and consideration components are supplied.
if (
offerComponents.length == 0 || considerationComponents.length == 0
) {
- revert OfferAndConsiderationRequiredOnFulfillment();
+ _revertOfferAndConsiderationRequiredOnFulfillment();
}
// Declare a new Execution struct.
@@ -70,6 +102,15 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
// Retrieve the consideration item from the execution struct.
ReceivedItem memory considerationItem = considerationExecution.item;
+ // Skip aggregating offer items if no consideration items are available.
+ if (considerationItem.amount == 0) {
+ // Set the offerer and recipient to null address if execution
+ // amount is zero. This will cause the execution item to be skipped.
+ considerationExecution.offerer = address(0);
+ considerationExecution.item.recipient = payable(0);
+ return considerationExecution;
+ }
+
// Recipient does not need to be specified because it will always be set
// to that of the consideration.
// Validate & aggregate offer items to Execution object.
@@ -79,13 +120,19 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
execution
);
- // Ensure offer and consideration share types, tokens and identifiers.
+ // Ensure offer & consideration item types, tokens, & identifiers match.
+ // (a != b || c != d || e != f) == (((a ^ b) | (c ^ d) | (e ^ f)) != 0),
+ // but the second expression requires less gas to evaluate.
if (
- execution.item.itemType != considerationItem.itemType ||
- execution.item.token != considerationItem.token ||
- execution.item.identifier != considerationItem.identifier
+ ((uint8(execution.item.itemType) ^
+ uint8(considerationItem.itemType)) |
+ (uint160(execution.item.token) ^
+ uint160(considerationItem.token)) |
+ (execution.item.identifier ^ considerationItem.identifier)) != 0
) {
- revert MismatchedFulfillmentOfferAndConsiderationComponents();
+ _revertMismatchedFulfillmentOfferAndConsiderationComponents(
+ fulfillmentIndex
+ );
}
// If total consideration amount exceeds the offer amount...
@@ -105,9 +152,6 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
.startAmount = (considerationItem.amount -
execution.item.amount);
}
-
- // Reduce total consideration amount to equal the offer amount.
- considerationItem.amount = execution.item.amount;
} else {
// Retrieve the first offer component from the fulfillment.
FulfillmentComponent memory targetComponent = offerComponents[0];
@@ -166,7 +210,7 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
// Retrieve fulfillment components array length and place on stack.
// Ensure at least one fulfillment component has been supplied.
if (fulfillmentComponents.length == 0) {
- revert MissingFulfillmentComponentOnAggregation(side);
+ _revertMissingFulfillmentComponentOnAggregation(side);
}
// If the fulfillment components are offer components...
@@ -223,257 +267,198 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
Execution memory execution
) internal pure {
assembly {
- // Declare function for reverts on invalid fulfillment data.
- function throwInvalidFulfillmentComponentData() {
- // Store the InvalidFulfillmentComponentData error signature.
- mstore(0, InvalidFulfillmentComponentData_error_signature)
-
- // Return, supplying InvalidFulfillmentComponentData signature.
- revert(0, InvalidFulfillmentComponentData_error_len)
- }
-
- // Declare function for reverts due to arithmetic overflows.
- function throwOverflow() {
- // Store the Panic error signature.
- mstore(0, Panic_error_signature)
-
- // Store the arithmetic (0x11) panic code as initial argument.
- mstore(Panic_error_offset, Panic_arithmetic)
-
- // Return, supplying Panic signature and arithmetic code.
- revert(0, Panic_error_length)
- }
-
- // Get position in offerComponents head.
- let fulfillmentHeadPtr := add(offerComponents, OneWord)
-
- // Retrieve the order index using the fulfillment pointer.
- let orderIndex := mload(mload(fulfillmentHeadPtr))
-
- // Ensure that the order index is not out of range.
- if iszero(lt(orderIndex, mload(advancedOrders))) {
- throwInvalidFulfillmentComponentData()
- }
-
- // Read advancedOrders[orderIndex] pointer from its array head.
- let orderPtr := mload(
- // Calculate head position of advancedOrders[orderIndex].
- add(add(advancedOrders, OneWord), mul(orderIndex, OneWord))
- )
-
- // Read the pointer to OrderParameters from the AdvancedOrder.
- let paramsPtr := mload(orderPtr)
-
- // Load the offer array pointer.
- let offerArrPtr := mload(
- add(paramsPtr, OrderParameters_offer_head_offset)
- )
-
- // Retrieve item index using an offset of the fulfillment pointer.
- let itemIndex := mload(
- add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset)
- )
-
- // Only continue if the fulfillment is not invalid.
- if iszero(lt(itemIndex, mload(offerArrPtr))) {
- throwInvalidFulfillmentComponentData()
- }
-
- // Retrieve consideration item pointer using the item index.
- let offerItemPtr := mload(
- add(
- // Get pointer to beginning of receivedItem.
- add(offerArrPtr, OneWord),
- // Calculate offset to pointer for desired order.
- mul(itemIndex, OneWord)
- )
- )
-
// Declare a variable for the final aggregated item amount.
- let amount := 0
-
- // Create variable to track errors encountered with amount.
- let errorBuffer := 0
+ let amount
- // Only add offer amount to execution amount on a nonzero numerator.
- if mload(add(orderPtr, AdvancedOrder_numerator_offset)) {
- // Retrieve amount pointer using consideration item pointer.
- let amountPtr := add(offerItemPtr, Common_amount_offset)
+ // Declare a variable to track errors encountered with amount.
+ let errorBuffer
- // Set the amount.
- amount := mload(amountPtr)
+ // Declare a variable for the hash of itemType, token, identifier
+ let dataHash
- // Zero out amount on item to indicate it is credited.
- mstore(amountPtr, 0)
+ for {
+ // Create variable to track position in offerComponents head.
+ let fulfillmentHeadPtr := offerComponents
- // Buffer indicating whether issues were found.
- errorBuffer := iszero(amount)
- }
+ // Get position one word past last element in head of array.
+ let endPtr := add(
+ offerComponents,
+ shl(OneWordShift, mload(offerComponents))
+ )
+ } lt(fulfillmentHeadPtr, endPtr) {
- // Retrieve the received item pointer.
- let receivedItemPtr := mload(execution)
-
- // Set the item type on the received item.
- mstore(receivedItemPtr, mload(offerItemPtr))
-
- // Set the token on the received item.
- mstore(
- add(receivedItemPtr, Common_token_offset),
- mload(add(offerItemPtr, Common_token_offset))
- )
-
- // Set the identifier on the received item.
- mstore(
- add(receivedItemPtr, Common_identifier_offset),
- mload(add(offerItemPtr, Common_identifier_offset))
- )
-
- // Set the offerer on returned execution using order pointer.
- mstore(add(execution, Execution_offerer_offset), mload(paramsPtr))
-
- // Set conduitKey on returned execution via offset of order pointer.
- mstore(
- add(execution, Execution_conduit_offset),
- mload(add(paramsPtr, OrderParameters_conduit_offset))
- )
-
- // Calculate the hash of (itemType, token, identifier).
- let dataHash := keccak256(
- receivedItemPtr,
- ReceivedItem_CommonParams_size
- )
-
- // Get position one word past last element in head of array.
- let endPtr := add(
- offerComponents,
- mul(mload(offerComponents), OneWord)
- )
-
- // Iterate over remaining offer components.
- // prettier-ignore
- for {} lt(fulfillmentHeadPtr, endPtr) {} {
- // Increment the pointer to the fulfillment head by one word.
+ } {
+ // Increment position in considerationComponents head.
fulfillmentHeadPtr := add(fulfillmentHeadPtr, OneWord)
- // Get the order index using the fulfillment pointer.
- orderIndex := mload(mload(fulfillmentHeadPtr))
+ // Retrieve the order index using the fulfillment pointer.
+ let orderIndex := mload(mload(fulfillmentHeadPtr))
- // Ensure the order index is in range.
+ // Ensure that the order index is not out of range.
if iszero(lt(orderIndex, mload(advancedOrders))) {
- throwInvalidFulfillmentComponentData()
+ throwInvalidFulfillmentComponentData()
}
- // Get pointer to AdvancedOrder element.
- orderPtr := mload(
+ // Read advancedOrders[orderIndex] pointer from its array head.
+ let orderPtr := mload(
+ // Calculate head position of advancedOrders[orderIndex].
add(
add(advancedOrders, OneWord),
- mul(orderIndex, OneWord)
+ shl(OneWordShift, orderIndex)
)
)
- // Only continue if numerator is not zero.
- if iszero(mload(
- add(orderPtr, AdvancedOrder_numerator_offset)
- )) {
- continue
- }
-
// Read the pointer to OrderParameters from the AdvancedOrder.
- paramsPtr := mload(orderPtr)
+ let paramsPtr := mload(orderPtr)
- // Load offer array pointer.
- offerArrPtr := mload(
- add(
- paramsPtr,
- OrderParameters_offer_head_offset
- )
+ // Retrieve item index using an offset of fulfillment pointer.
+ let itemIndex := mload(
+ add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset)
)
- // Get the item index using the fulfillment pointer.
- itemIndex := mload(add(mload(fulfillmentHeadPtr), OneWord))
+ let offerItemPtr
+ {
+ // Load the offer array pointer.
+ let offerArrPtr := mload(
+ add(paramsPtr, OrderParameters_offer_head_offset)
+ )
- // Throw if itemIndex is out of the range of array.
- if iszero(
- lt(itemIndex, mload(offerArrPtr))
- ) {
- throwInvalidFulfillmentComponentData()
+ // If the offer item index is out of range or the numerator
+ // is zero, skip this item.
+ if or(
+ iszero(lt(itemIndex, mload(offerArrPtr))),
+ iszero(
+ mload(add(orderPtr, AdvancedOrder_numerator_offset))
+ )
+ ) {
+ continue
+ }
+
+ // Retrieve offer item pointer using the item index.
+ offerItemPtr := mload(
+ add(
+ // Get pointer to beginning of receivedItem.
+ add(offerArrPtr, OneWord),
+ // Calculate offset to pointer for desired order.
+ shl(OneWordShift, itemIndex)
+ )
+ )
}
- // Retrieve offer item pointer using index.
- offerItemPtr := mload(
- add(
- // Get pointer to beginning of receivedItem.
- add(offerArrPtr, OneWord),
- // Use offset to pointer for desired order.
- mul(itemIndex, OneWord)
+ // Declare a separate scope for the amount update.
+ {
+ // Retrieve amount pointer using consideration item pointer.
+ let amountPtr := add(offerItemPtr, Common_amount_offset)
+
+ // Add offer item amount to execution amount.
+ let newAmount := add(amount, mload(amountPtr))
+
+ // Update error buffer:
+ // 1 = zero amount, 2 = overflow, 3 = both.
+ errorBuffer := or(
+ errorBuffer,
+ or(
+ shl(1, lt(newAmount, amount)),
+ iszero(mload(amountPtr))
+ )
)
- )
- // Retrieve amount pointer using offer item pointer.
- let amountPtr := add(
- offerItemPtr,
- Common_amount_offset
- )
+ // Update the amount to the new, summed amount.
+ amount := newAmount
- // Add offer amount to execution amount.
- let newAmount := add(amount, mload(amountPtr))
+ // Zero out amount on original item to indicate it is spent.
+ mstore(amountPtr, 0)
+ }
- // Update error buffer: 1 = zero amount, 2 = overflow, 3 = both.
- errorBuffer := or(
- errorBuffer,
- or(
- shl(1, lt(newAmount, amount)),
- iszero(mload(amountPtr))
- )
- )
+ // Retrieve ReceivedItem pointer from Execution.
+ let receivedItem := mload(execution)
+
+ // Check if this is the first valid fulfillment item
+ switch iszero(dataHash)
+ case 1 {
+ // On first valid item, populate the received item in
+ // memory for later comparison.
+
+ // Set the item type on the received item.
+ mstore(receivedItem, mload(offerItemPtr))
+
+ // Set the token on the received item.
+ mstore(
+ add(receivedItem, Common_token_offset),
+ mload(add(offerItemPtr, Common_token_offset))
+ )
+
+ // Set the identifier on the received item.
+ mstore(
+ add(receivedItem, Common_identifier_offset),
+ mload(add(offerItemPtr, Common_identifier_offset))
+ )
+
+ // Set offerer on returned execution using order pointer.
+ mstore(
+ add(execution, Execution_offerer_offset),
+ mload(paramsPtr)
+ )
- // Update the amount to the new, summed amount.
- amount := newAmount
-
- // Zero out amount on original item to indicate it is credited.
- mstore(amountPtr, 0)
-
- // Ensure the indicated item matches original item.
- if iszero(
- and(
- and(
- // The offerer must match on both items.
- eq(
- mload(paramsPtr),
- mload(
- add(execution, Execution_offerer_offset)
- )
- ),
- // The conduit key must match on both items.
- eq(
- mload(
- add(
- paramsPtr,
- OrderParameters_conduit_offset
- )
- ),
- mload(
- add(
- execution,
- Execution_conduit_offset
- )
- )
- )
+ // Set execution conduitKey via order pointer offset.
+ mstore(
+ add(execution, Execution_conduit_offset),
+ mload(add(paramsPtr, OrderParameters_conduit_offset))
+ )
+
+ // Calculate the hash of (itemType, token, identifier).
+ dataHash := keccak256(
+ receivedItem,
+ ReceivedItem_CommonParams_size
+ )
+
+ // If component index > 0, swap component pointer with
+ // pointer to first component so that any remainder after
+ // fulfillment can be added back to the first item.
+ let firstFulfillmentHeadPtr := add(offerComponents, OneWord)
+ if xor(firstFulfillmentHeadPtr, fulfillmentHeadPtr) {
+ let firstFulfillmentPtr := mload(
+ firstFulfillmentHeadPtr
+ )
+ let fulfillmentPtr := mload(fulfillmentHeadPtr)
+ mstore(firstFulfillmentHeadPtr, fulfillmentPtr)
+ }
+ }
+ default {
+ // Compare every subsequent item to the first
+ if or(
+ or(
+ // The offerer must match on both items.
+ xor(
+ mload(paramsPtr),
+ mload(add(execution, Execution_offerer_offset))
+ ),
+ // The conduit key must match on both items.
+ xor(
+ mload(
+ add(
+ paramsPtr,
+ OrderParameters_conduit_offset
+ )
+ ),
+ mload(add(execution, Execution_conduit_offset))
+ )
),
// The itemType, token, and identifier must match.
- eq(
+ xor(
dataHash,
keccak256(
offerItemPtr,
ReceivedItem_CommonParams_size
)
)
- )
- ) {
- // Throw if any of the requirements are not met.
- throwInvalidFulfillmentComponentData()
+ ) {
+ // Throw if any of the requirements are not met.
+ throwInvalidFulfillmentComponentData()
+ }
}
}
+
// Write final amount to execution.
mstore(add(mload(execution), Common_amount_offset), amount)
@@ -481,17 +466,45 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
if errorBuffer {
// If errorBuffer is 1, an item had an amount of zero.
if eq(errorBuffer, 1) {
- // Store the MissingItemAmount error signature.
- mstore(0, MissingItemAmount_error_signature)
-
- // Return, supplying MissingItemAmount signature.
- revert(0, MissingItemAmount_error_len)
+ // Store left-padded selector with push4 (reduces bytecode)
+ // mem[28:32] = selector
+ mstore(0, MissingItemAmount_error_selector)
+
+ // revert(abi.encodeWithSignature("MissingItemAmount()"))
+ revert(
+ Error_selector_offset,
+ MissingItemAmount_error_length
+ )
}
// If errorBuffer is not 1 or 0, the sum overflowed.
// Panic!
throwOverflow()
}
+
+ // Declare function for reverts on invalid fulfillment data.
+ function throwInvalidFulfillmentComponentData() {
+ // Store left-padded selector (uses push4 and reduces code size)
+ mstore(0, InvalidFulfillmentComponentData_error_selector)
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidFulfillmentComponentData()"
+ // ))
+ revert(
+ Error_selector_offset,
+ InvalidFulfillmentComponentData_error_length
+ )
+ }
+
+ // Declare function for reverts due to arithmetic overflows.
+ function throwOverflow() {
+ // Store the Panic error signature.
+ mstore(0, Panic_error_selector)
+ // Store the arithmetic (0x11) panic code.
+ mstore(Panic_error_code_ptr, Panic_arithmetic)
+ // revert(abi.encodeWithSignature("Panic(uint256)", 0x11))
+ revert(Error_selector_offset, Panic_error_length)
+ }
}
}
@@ -499,6 +512,12 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
* @dev Internal pure function to aggregate a group of consideration items
* using supplied directives on which component items are candidates
* for aggregation, skipping items on orders that are not available.
+ * Note that this function depends on memory layout affected by an
+ * earlier call to _validateOrdersAndPrepareToFulfill. The memory for
+ * the consideration arrays needs to be updated before calling
+ * _aggregateValidFulfillmentConsiderationItems.
+ * _validateOrdersAndPrepareToFulfill is called in _matchAdvancedOrders
+ * and _fulfillAvailableAdvancedOrders in the current version.
*
* @param advancedOrders The orders to aggregate consideration
* items from.
@@ -506,7 +525,7 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
* indicating the order index and item index
* of each candidate consideration item for
* aggregation.
- * @param execution The execution to apply the aggregation to.
+ * @param execution The execution to apply the aggregation to.
*/
function _aggregateValidFulfillmentConsiderationItems(
AdvancedOrder[] memory advancedOrders,
@@ -515,248 +534,195 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
) internal pure {
// Utilize assembly in order to efficiently aggregate the items.
assembly {
- // Declare function for reverts on invalid fulfillment data.
- function throwInvalidFulfillmentComponentData() {
- // Store the InvalidFulfillmentComponentData error signature.
- mstore(0, InvalidFulfillmentComponentData_error_signature)
-
- // Return, supplying InvalidFulfillmentComponentData signature.
- revert(0, InvalidFulfillmentComponentData_error_len)
- }
-
- // Declare function for reverts due to arithmetic overflows.
- function throwOverflow() {
- // Store the Panic error signature.
- mstore(0, Panic_error_signature)
-
- // Store the arithmetic (0x11) panic code as initial argument.
- mstore(Panic_error_offset, Panic_arithmetic)
-
- // Return, supplying Panic signature and arithmetic code.
- revert(0, Panic_error_length)
- }
-
- // Get position in considerationComponents head.
- let fulfillmentHeadPtr := add(considerationComponents, OneWord)
-
- // Retrieve the order index using the fulfillment pointer.
- let orderIndex := mload(mload(fulfillmentHeadPtr))
-
- // Ensure that the order index is not out of range.
- if iszero(lt(orderIndex, mload(advancedOrders))) {
- throwInvalidFulfillmentComponentData()
- }
-
- // Read advancedOrders[orderIndex] pointer from its array head.
- let orderPtr := mload(
- // Calculate head position of advancedOrders[orderIndex].
- add(add(advancedOrders, OneWord), mul(orderIndex, OneWord))
- )
-
- // Load consideration array pointer.
- let considerationArrPtr := mload(
- add(
- // Read pointer to OrderParameters from the AdvancedOrder.
- mload(orderPtr),
- OrderParameters_consideration_head_offset
- )
- )
-
- // Retrieve item index using an offset of the fulfillment pointer.
- let itemIndex := mload(
- add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset)
- )
-
- // Ensure that the order index is not out of range.
- if iszero(lt(itemIndex, mload(considerationArrPtr))) {
- throwInvalidFulfillmentComponentData()
- }
-
- // Retrieve consideration item pointer using the item index.
- let considerationItemPtr := mload(
- add(
- // Get pointer to beginning of receivedItem.
- add(considerationArrPtr, OneWord),
- // Calculate offset to pointer for desired order.
- mul(itemIndex, OneWord)
- )
- )
-
// Declare a variable for the final aggregated item amount.
- let amount := 0
+ let amount
// Create variable to track errors encountered with amount.
- let errorBuffer := 0
+ let errorBuffer
- // Only add consideration amount to execution amount if numerator is
- // greater than zero.
- if mload(add(orderPtr, AdvancedOrder_numerator_offset)) {
- // Retrieve amount pointer using consideration item pointer.
- let amountPtr := add(considerationItemPtr, Common_amount_offset)
+ // Declare variable for hash(itemType, token, identifier, recipient)
+ let dataHash
- // Set the amount.
- amount := mload(amountPtr)
-
- // Set error bit if amount is zero.
- errorBuffer := iszero(amount)
-
- // Zero out amount on item to indicate it is credited.
- mstore(amountPtr, 0)
- }
+ for {
+ // Track position in considerationComponents head.
+ let fulfillmentHeadPtr := considerationComponents
- // Retrieve ReceivedItem pointer from Execution.
- let receivedItem := mload(execution)
-
- // Set the item type on the received item.
- mstore(receivedItem, mload(considerationItemPtr))
-
- // Set the token on the received item.
- mstore(
- add(receivedItem, Common_token_offset),
- mload(add(considerationItemPtr, Common_token_offset))
- )
-
- // Set the identifier on the received item.
- mstore(
- add(receivedItem, Common_identifier_offset),
- mload(add(considerationItemPtr, Common_identifier_offset))
- )
-
- // Set the recipient on the received item.
- mstore(
- add(receivedItem, ReceivedItem_recipient_offset),
- mload(
- add(
- considerationItemPtr,
- ConsiderationItem_recipient_offset
- )
+ // Get position one word past last element in head of array.
+ let endPtr := add(
+ considerationComponents,
+ shl(OneWordShift, mload(considerationComponents))
)
- )
-
- // Calculate the hash of (itemType, token, identifier).
- let dataHash := keccak256(
- receivedItem,
- ReceivedItem_CommonParams_size
- )
-
- // Get position one word past last element in head of array.
- let endPtr := add(
- considerationComponents,
- mul(mload(considerationComponents), OneWord)
- )
-
- // Iterate over remaining offer components.
- // prettier-ignore
- for {} lt(fulfillmentHeadPtr, endPtr) {} {
+ } lt(fulfillmentHeadPtr, endPtr) {
+
+ } {
// Increment position in considerationComponents head.
fulfillmentHeadPtr := add(fulfillmentHeadPtr, OneWord)
- // Get the order index using the fulfillment pointer.
- orderIndex := mload(mload(fulfillmentHeadPtr))
+ // Retrieve the order index using the fulfillment pointer.
+ let orderIndex := mload(mload(fulfillmentHeadPtr))
- // Ensure the order index is in range.
+ // Ensure that the order index is not out of range.
if iszero(lt(orderIndex, mload(advancedOrders))) {
- throwInvalidFulfillmentComponentData()
+ throwInvalidFulfillmentComponentData()
}
- // Get pointer to AdvancedOrder element.
- orderPtr := mload(
+ // Read advancedOrders[orderIndex] pointer from its array head.
+ let orderPtr := mload(
+ // Calculate head position of advancedOrders[orderIndex].
add(
add(advancedOrders, OneWord),
- mul(orderIndex, OneWord)
+ shl(OneWordShift, orderIndex)
)
)
- // Only continue if numerator is not zero.
- if iszero(
- mload(add(orderPtr, AdvancedOrder_numerator_offset))
- ) {
- continue
+ // Retrieve item index using an offset of fulfillment pointer.
+ let itemIndex := mload(
+ add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset)
+ )
+
+ let considerationItemPtr
+ {
+ // Load consideration array pointer.
+ let considerationArrPtr := mload(
+ add(
+ // Read OrderParameters pointer from AdvancedOrder.
+ mload(orderPtr),
+ OrderParameters_consideration_head_offset
+ )
+ )
+
+ // If the consideration item index is out of range or the
+ // numerator is zero, skip this item.
+ if or(
+ iszero(lt(itemIndex, mload(considerationArrPtr))),
+ iszero(
+ mload(add(orderPtr, AdvancedOrder_numerator_offset))
+ )
+ ) {
+ continue
+ }
+
+ // Retrieve consideration item pointer using the item index.
+ considerationItemPtr := mload(
+ add(
+ // Get pointer to beginning of receivedItem.
+ add(considerationArrPtr, OneWord),
+ // Calculate offset to pointer for desired order.
+ shl(OneWordShift, itemIndex)
+ )
+ )
}
- // Load consideration array pointer from OrderParameters.
- considerationArrPtr := mload(
- add(
- // Get pointer to OrderParameters from AdvancedOrder.
- mload(orderPtr),
- OrderParameters_consideration_head_offset
+ // Declare a separate scope for the amount update
+ {
+ // Retrieve amount pointer using consideration item pointer.
+ let amountPtr := add(
+ considerationItemPtr,
+ Common_amount_offset
)
- )
- // Get the item index using the fulfillment pointer.
- itemIndex := mload(add(mload(fulfillmentHeadPtr), OneWord))
+ // Add consideration item amount to execution amount.
+ let newAmount := add(amount, mload(amountPtr))
- // Check if itemIndex is within the range of array.
- if iszero(lt(itemIndex, mload(considerationArrPtr))) {
- throwInvalidFulfillmentComponentData()
+ // Update error buffer:
+ // 1 = zero amount, 2 = overflow, 3 = both.
+ errorBuffer := or(
+ errorBuffer,
+ or(
+ shl(1, lt(newAmount, amount)),
+ iszero(mload(amountPtr))
+ )
+ )
+
+ // Update the amount to the new, summed amount.
+ amount := newAmount
+
+ // Zero out original item amount to indicate it is credited.
+ mstore(amountPtr, 0)
}
- // Retrieve consideration item pointer using index.
- considerationItemPtr := mload(
- add(
- // Get pointer to beginning of receivedItem.
- add(considerationArrPtr, OneWord),
- // Use offset to pointer for desired order.
- mul(itemIndex, OneWord)
- )
- )
+ // Retrieve ReceivedItem pointer from Execution.
+ let receivedItem := mload(execution)
- // Retrieve amount pointer using consideration item pointer.
- let amountPtr := add(
- considerationItemPtr,
- Common_amount_offset
- )
+ switch iszero(dataHash)
+ case 1 {
+ // On first valid item, populate the received item in
+ // memory for later comparison.
- // Add offer amount to execution amount.
- let newAmount := add(amount, mload(amountPtr))
+ // Set the item type on the received item.
+ mstore(receivedItem, mload(considerationItemPtr))
- // Update error buffer: 1 = zero amount, 2 = overflow, 3 = both.
- errorBuffer := or(
- errorBuffer,
- or(
- shl(1, lt(newAmount, amount)),
- iszero(mload(amountPtr))
- )
- )
+ // Set the token on the received item.
+ mstore(
+ add(receivedItem, Common_token_offset),
+ mload(add(considerationItemPtr, Common_token_offset))
+ )
- // Update the amount to the new, summed amount.
- amount := newAmount
-
- // Zero out amount on original item to indicate it is credited.
- mstore(amountPtr, 0)
-
- // Ensure the indicated item matches original item.
- if iszero(
- and(
- // Item recipients must match.
- eq(
- mload(
- add(
- considerationItemPtr,
- ConsiderItem_recipient_offset
- )
- ),
- mload(
- add(
- receivedItem,
- ReceivedItem_recipient_offset
- )
+ // Set the identifier on the received item.
+ mstore(
+ add(receivedItem, Common_identifier_offset),
+ mload(
+ add(considerationItemPtr, Common_identifier_offset)
+ )
+ )
+
+ // Set the recipient on the received item.
+ // Note that this depends on the memory layout affected by
+ // _validateOrdersAndPrepareToFulfill.
+ mstore(
+ add(receivedItem, ReceivedItem_recipient_offset),
+ mload(
+ add(
+ considerationItemPtr,
+ ReceivedItem_recipient_offset
)
- ),
- // The itemType, token, identifier must match.
- eq(
- dataHash,
- keccak256(
- considerationItemPtr,
- ReceivedItem_CommonParams_size
- )
)
)
- ) {
- // Throw if any of the requirements are not met.
- throwInvalidFulfillmentComponentData()
+
+ // Calculate the hash of (itemType, token, identifier,
+ // recipient). This is run after amount is set to zero, so
+ // there will be one blank word after identifier included in
+ // the hash buffer.
+ dataHash := keccak256(
+ considerationItemPtr,
+ ReceivedItem_size
+ )
+
+ // If component index > 0, swap component pointer with
+ // pointer to first component so that any remainder after
+ // fulfillment can be added back to the first item.
+ let firstFulfillmentHeadPtr := add(
+ considerationComponents,
+ OneWord
+ )
+ if xor(firstFulfillmentHeadPtr, fulfillmentHeadPtr) {
+ let firstFulfillmentPtr := mload(
+ firstFulfillmentHeadPtr
+ )
+ let fulfillmentPtr := mload(fulfillmentHeadPtr)
+ mstore(firstFulfillmentHeadPtr, fulfillmentPtr)
+ }
+ }
+ default {
+ // Compare every subsequent item to the first
+ // The itemType, token, identifier and recipient must match.
+ if xor(
+ dataHash,
+ // Calculate the hash of (itemType, token, identifier,
+ // recipient). This is run after amount is set to zero,
+ // so there will be one blank word after identifier
+ // included in the hash buffer.
+ keccak256(considerationItemPtr, ReceivedItem_size)
+ ) {
+ // Throw if any of the requirements are not met.
+ throwInvalidFulfillmentComponentData()
+ }
}
}
+
+ // Retrieve ReceivedItem pointer from Execution.
+ let receivedItem := mload(execution)
+
// Write final amount to execution.
mstore(add(receivedItem, Common_amount_offset), amount)
@@ -764,17 +730,44 @@ contract FulfillmentApplier is FulfillmentApplicationErrors {
if errorBuffer {
// If errorBuffer is 1, an item had an amount of zero.
if eq(errorBuffer, 1) {
- // Store the MissingItemAmount error signature.
- mstore(0, MissingItemAmount_error_signature)
+ // Store left-padded selector with push4, mem[28:32]
+ mstore(0, MissingItemAmount_error_selector)
- // Return, supplying MissingItemAmount signature.
- revert(0, MissingItemAmount_error_len)
+ // revert(abi.encodeWithSignature("MissingItemAmount()"))
+ revert(
+ Error_selector_offset,
+ MissingItemAmount_error_length
+ )
}
- // If errorBuffer is not 1 or 0, the sum overflowed.
+ // If errorBuffer is not 1 or 0, `amount` overflowed.
// Panic!
throwOverflow()
}
+
+ // Declare function for reverts on invalid fulfillment data.
+ function throwInvalidFulfillmentComponentData() {
+ // Store the InvalidFulfillmentComponentData error signature.
+ mstore(0, InvalidFulfillmentComponentData_error_selector)
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidFulfillmentComponentData()"
+ // ))
+ revert(
+ Error_selector_offset,
+ InvalidFulfillmentComponentData_error_length
+ )
+ }
+
+ // Declare function for reverts due to arithmetic overflows.
+ function throwOverflow() {
+ // Store the Panic error signature.
+ mstore(0, Panic_error_selector)
+ // Store the arithmetic (0x11) panic code.
+ mstore(Panic_error_code_ptr, Panic_arithmetic)
+ // revert(abi.encodeWithSignature("Panic(uint256)", 0x11))
+ revert(Error_selector_offset, Panic_error_length)
+ }
}
}
}
diff --git a/contracts/lib/GettersAndDerivers.sol b/contracts/lib/GettersAndDerivers.sol
index 4b0d16357..46ff51f3a 100644
--- a/contracts/lib/GettersAndDerivers.sol
+++ b/contracts/lib/GettersAndDerivers.sol
@@ -1,11 +1,37 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
import { OrderParameters } from "./ConsiderationStructs.sol";
import { ConsiderationBase } from "./ConsiderationBase.sol";
-import "./ConsiderationConstants.sol";
+import {
+ Create2AddressDerivation_length,
+ Create2AddressDerivation_ptr,
+ EIP_712_PREFIX,
+ EIP712_ConsiderationItem_size,
+ EIP712_DigestPayload_size,
+ EIP712_DomainSeparator_offset,
+ EIP712_OfferItem_size,
+ EIP712_Order_size,
+ EIP712_OrderHash_offset,
+ FreeMemoryPointerSlot,
+ information_conduitController_offset,
+ information_domainSeparator_offset,
+ information_length,
+ information_version_cd_offset,
+ information_version_offset,
+ information_versionLengthPtr,
+ information_versionWithLength,
+ MaskOverByteTwelve,
+ MaskOverLastTwentyBytes,
+ OneWord,
+ OneWordShift,
+ OrderParameters_consideration_head_offset,
+ OrderParameters_counter_offset,
+ OrderParameters_offer_head_offset,
+ TwoWords
+} from "./ConsiderationConstants.sol";
/**
* @title GettersAndDerivers
@@ -22,9 +48,9 @@ contract GettersAndDerivers is ConsiderationBase {
* that may optionally be used to transfer approved
* ERC20/721/1155 tokens.
*/
- constructor(address conduitController)
- ConsiderationBase(conduitController)
- {}
+ constructor(
+ address conduitController
+ ) ConsiderationBase(conduitController) {}
/**
* @dev Internal view function to derive the order hash for a given order.
@@ -33,7 +59,7 @@ contract GettersAndDerivers is ConsiderationBase {
* caller.
*
* @param orderParameters The parameters of the order to hash.
- * @param counter The counter of the order to hash.
+ * @param counter The counter of the order to hash.
*
* @return orderHash The hash.
*/
@@ -76,7 +102,6 @@ contract GettersAndDerivers is ConsiderationBase {
offerArrPtr := add(offerArrPtr, OneWord)
// Iterate over the offer items.
- // prettier-ignore
for { let i := 0 } lt(i, offerLength) {
i := add(i, 1)
} {
@@ -104,7 +129,7 @@ contract GettersAndDerivers is ConsiderationBase {
// Derive the offer hash using the hashes of each item.
offerHash := keccak256(
mload(FreeMemoryPointerSlot),
- mul(offerLength, OneWord)
+ shl(OneWordShift, offerLength)
)
}
@@ -131,7 +156,6 @@ contract GettersAndDerivers is ConsiderationBase {
)
// Iterate over the consideration items (not including tips).
- // prettier-ignore
for { let i := 0 } lt(i, originalConsiderationLength) {
i := add(i, 1)
} {
@@ -162,7 +186,7 @@ contract GettersAndDerivers is ConsiderationBase {
// Derive the consideration hash using the hashes of each item.
considerationHash := keccak256(
mload(FreeMemoryPointerSlot),
- mul(originalConsiderationLength, OneWord)
+ shl(OneWordShift, originalConsiderationLength)
)
}
@@ -242,11 +266,9 @@ contract GettersAndDerivers is ConsiderationBase {
* @return conduit The address of the conduit associated with the given
* conduit key.
*/
- function _deriveConduit(bytes32 conduitKey)
- internal
- view
- returns (address conduit)
- {
+ function _deriveConduit(
+ bytes32 conduitKey
+ ) internal view returns (address conduit) {
// Read conduit controller address from runtime and place on the stack.
address conduitController = address(_CONDUIT_CONTROLLER);
@@ -295,7 +317,6 @@ contract GettersAndDerivers is ConsiderationBase {
* @return The domain separator.
*/
function _domainSeparator() internal view returns (bytes32) {
- // prettier-ignore
return block.chainid == _CHAIN_ID
? _DOMAIN_SEPARATOR
: _deriveDomainSeparator();
@@ -305,31 +326,32 @@ contract GettersAndDerivers is ConsiderationBase {
* @dev Internal view function to retrieve configuration information for
* this contract.
*
- * @return version The contract version.
- * @return domainSeparator The domain separator for this contract.
- * @return conduitController The conduit Controller set for this contract.
+ * @return The contract version.
+ * @return The domain separator for this contract.
+ * @return The conduit Controller set for this contract.
*/
function _information()
internal
view
returns (
- string memory version,
- bytes32 domainSeparator,
- address conduitController
+ string memory /* version */,
+ bytes32 /* domainSeparator */,
+ address /* conduitController */
)
{
// Derive the domain separator.
- domainSeparator = _domainSeparator();
+ bytes32 domainSeparator = _domainSeparator();
// Declare variable as immutables cannot be accessed within assembly.
- conduitController = address(_CONDUIT_CONTROLLER);
-
- // Allocate a string with the intended length.
- version = new string(Version_length);
+ address conduitController = address(_CONDUIT_CONTROLLER);
- // Set the version as data on the newly allocated string.
+ // Return the version, domain separator, and conduit controller.
assembly {
- mstore(add(version, OneWord), shl(Version_shift, Version))
+ mstore(information_version_offset, information_version_cd_offset)
+ mstore(information_domainSeparator_offset, domainSeparator)
+ mstore(information_conduitController_offset, conduitController)
+ mstore(information_versionLengthPtr, information_versionWithLength)
+ return(information_version_offset, information_length)
}
}
@@ -342,11 +364,10 @@ contract GettersAndDerivers is ConsiderationBase {
*
* @return value The hash.
*/
- function _deriveEIP712Digest(bytes32 domainSeparator, bytes32 orderHash)
- internal
- pure
- returns (bytes32 value)
- {
+ function _deriveEIP712Digest(
+ bytes32 domainSeparator,
+ bytes32 orderHash
+ ) internal pure returns (bytes32 value) {
// Leverage scratch space to perform an efficient hash.
assembly {
// Place the EIP-712 prefix at the start of scratch space.
diff --git a/contracts/lib/LowLevelHelpers.sol b/contracts/lib/LowLevelHelpers.sol
index c3bba2398..199c44784 100644
--- a/contracts/lib/LowLevelHelpers.sol
+++ b/contracts/lib/LowLevelHelpers.sol
@@ -1,7 +1,15 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
-import "./ConsiderationConstants.sol";
+import {
+ CostPerWord,
+ ExtraGasBuffer,
+ FreeMemoryPointerSlot,
+ MemoryExpansionCoefficientShift,
+ OneWord,
+ OneWordShift,
+ ThirtyOneBytes
+} from "./ConsiderationConstants.sol";
/**
* @title LowLevelHelpers
@@ -10,34 +18,6 @@ import "./ConsiderationConstants.sol";
* operations.
*/
contract LowLevelHelpers {
- /**
- * @dev Internal view function to staticcall an arbitrary target with given
- * calldata. Note that no data is written to memory and no contract
- * size check is performed.
- *
- * @param target The account to staticcall.
- * @param callData The calldata to supply when staticcalling the target.
- *
- * @return success The status of the staticcall to the target.
- */
- function _staticcall(address target, bytes memory callData)
- internal
- view
- returns (bool success)
- {
- assembly {
- // Perform the staticcall.
- success := staticcall(
- gas(),
- target,
- add(callData, OneWord),
- mload(callData),
- 0,
- 0
- )
- }
- }
-
/**
* @dev Internal view function to revert and pass along the revert reason if
* data was returned by the last call and that the size of that data
@@ -51,15 +31,18 @@ contract LowLevelHelpers {
// Ensure that sufficient gas is available to copy returndata
// while expanding memory where necessary. Start by computing
// the word size of returndata and allocated memory.
- let returnDataWords := div(
- add(returndatasize(), AlmostOneWord),
- OneWord
+ let returnDataWords := shr(
+ OneWordShift,
+ add(returndatasize(), ThirtyOneBytes)
)
// Note: use the free memory pointer in place of msize() to work
// around a Yul warning that prevents accessing msize directly
// when the IR pipeline is activated.
- let msizeWords := div(mload(FreeMemoryPointerSlot), OneWord)
+ let msizeWords := shr(
+ OneWordShift,
+ mload(FreeMemoryPointerSlot)
+ )
// Next, compute the cost of the returndatacopy.
let cost := mul(CostPerWord, returnDataWords)
@@ -70,12 +53,12 @@ contract LowLevelHelpers {
cost,
add(
mul(sub(returnDataWords, msizeWords), CostPerWord),
- div(
+ shr(
+ MemoryExpansionCoefficientShift,
sub(
mul(returnDataWords, returnDataWords),
mul(msizeWords, msizeWords)
- ),
- MemoryExpansionCoefficient
+ )
)
)
)
@@ -95,31 +78,54 @@ contract LowLevelHelpers {
}
/**
- * @dev Internal pure function to determine if the first word of returndata
- * matches an expected magic value.
+ * @dev Internal view function to branchlessly select either the caller (if
+ * a supplied recipient is equal to zero) or the supplied recipient (if
+ * that recipient is a nonzero value).
*
- * @param expected The expected magic value.
+ * @param recipient The supplied recipient.
*
- * @return A boolean indicating whether the expected value matches the one
- * located in the first word of returndata.
+ * @return updatedRecipient The updated recipient.
*/
- function _doesNotMatchMagic(bytes4 expected) internal pure returns (bool) {
- // Declare a variable for the value held by the return data buffer.
- bytes4 result;
-
- // Utilize assembly in order to read directly from returndata buffer.
+ function _substituteCallerForEmptyRecipient(
+ address recipient
+ ) internal view returns (address updatedRecipient) {
+ // Utilize assembly to perform a branchless operation on the recipient.
assembly {
- // Only put result on stack if return data is exactly one word.
- if eq(returndatasize(), OneWord) {
- // Copy the word directly from return data into scratch space.
- returndatacopy(0, 0, OneWord)
+ // Add caller to recipient if recipient equals 0; otherwise add 0.
+ updatedRecipient := add(recipient, mul(iszero(recipient), caller()))
+ }
+ }
- // Take value from scratch space and place it on the stack.
- result := mload(0)
- }
+ /**
+ * @dev Internal pure function to cast a `bool` value to a `uint256` value.
+ *
+ * @param b The `bool` value to cast.
+ *
+ * @return u The `uint256` value.
+ */
+ function _cast(bool b) internal pure returns (uint256 u) {
+ assembly {
+ u := b
}
+ }
- // Return a boolean indicating whether expected and located value match.
- return result != expected;
+ /**
+ * @dev Internal pure function to compare two addresses without first
+ * masking them. Note that dirty upper bits will cause otherwise equal
+ * addresses to be recognized as unequal.
+ *
+ * @param a The first address.
+ * @param b The second address
+ *
+ * @return areEqual A boolean representing whether the addresses are equal.
+ */
+ function _unmaskedAddressComparison(
+ address a,
+ address b
+ ) internal pure returns (bool areEqual) {
+ // Utilize assembly to perform the comparison without masking.
+ assembly {
+ areEqual := eq(a, b)
+ }
}
}
diff --git a/contracts/lib/OrderCombiner.sol b/contracts/lib/OrderCombiner.sol
index 7de31ccef..fc47d4b07 100644
--- a/contracts/lib/OrderCombiner.sol
+++ b/contracts/lib/OrderCombiner.sol
@@ -1,27 +1,43 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
-import { Side, ItemType } from "./ConsiderationEnums.sol";
+import { Side, ItemType, OrderType } from "./ConsiderationEnums.sol";
-// prettier-ignore
import {
- OfferItem,
+ AdvancedOrder,
ConsiderationItem,
- ReceivedItem,
- OrderParameters,
+ CriteriaResolver,
+ Execution,
Fulfillment,
FulfillmentComponent,
- Execution,
- Order,
- AdvancedOrder,
- CriteriaResolver
+ OfferItem,
+ OrderParameters,
+ ReceivedItem
} from "./ConsiderationStructs.sol";
import { OrderFulfiller } from "./OrderFulfiller.sol";
import { FulfillmentApplier } from "./FulfillmentApplier.sol";
-import "./ConsiderationConstants.sol";
+import {
+ _revertConsiderationNotMet,
+ _revertInsufficientNativeTokensSupplied,
+ _revertInvalidNativeOfferItem,
+ _revertNoSpecifiedOrdersAvailable
+} from "./ConsiderationErrors.sol";
+
+import {
+ AccumulatorDisarmed,
+ ConsiderationItem_recipient_offset,
+ NonMatchSelector_InvalidErrorValue,
+ NonMatchSelector_MagicMask,
+ OneWord,
+ OneWordShift,
+ OrdersMatchedTopic0,
+ ReceivedItem_amount_offset,
+ ReceivedItem_recipient_offset,
+ TwoWords
+} from "./ConsiderationConstants.sol";
/**
* @title OrderCombiner
@@ -107,17 +123,20 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
function _fulfillAvailableAdvancedOrders(
AdvancedOrder[] memory advancedOrders,
CriteriaResolver[] memory criteriaResolvers,
- FulfillmentComponent[][] calldata offerFulfillments,
- FulfillmentComponent[][] calldata considerationFulfillments,
+ FulfillmentComponent[][] memory offerFulfillments,
+ FulfillmentComponent[][] memory considerationFulfillments,
bytes32 fulfillerConduitKey,
address recipient,
uint256 maximumFulfilled
)
internal
- returns (bool[] memory availableOrders, Execution[] memory executions)
+ returns (
+ bool[] memory /* availableOrders */,
+ Execution[] memory /* executions */
+ )
{
// Validate orders, apply amounts, & determine if they utilize conduits.
- _validateOrdersAndPrepareToFulfill(
+ bytes32[] memory orderHashes = _validateOrdersAndPrepareToFulfill(
advancedOrders,
criteriaResolvers,
false, // Signifies that invalid orders should NOT revert.
@@ -126,22 +145,24 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
);
// Aggregate used offer and consideration items and execute transfers.
- (availableOrders, executions) = _executeAvailableFulfillments(
- advancedOrders,
- offerFulfillments,
- considerationFulfillments,
- fulfillerConduitKey,
- recipient
- );
-
- // Return order fulfillment details and executions.
- return (availableOrders, executions);
+ return
+ _executeAvailableFulfillments(
+ advancedOrders,
+ offerFulfillments,
+ considerationFulfillments,
+ fulfillerConduitKey,
+ recipient,
+ orderHashes
+ );
}
/**
* @dev Internal function to validate a group of orders, update their
* statuses, reduce amounts by their previously filled fractions, apply
- * criteria resolvers, and emit OrderFulfilled events.
+ * criteria resolvers, and emit OrderFulfilled events. Note that this
+ * function needs to be called before
+ * _aggregateValidFulfillmentConsiderationItems to set the memory
+ * layout that _aggregateValidFulfillmentConsiderationItems depends on.
*
* @param advancedOrders The advanced orders to validate and reduce by
* their previously filled amounts.
@@ -157,7 +178,11 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
* order being invalid; setting this to false will
* instead cause the invalid order to be skipped.
* @param maximumFulfilled The maximum number of orders to fulfill.
- * @param recipient The intended recipient for all received items.
+ * @param recipient The intended recipient for all items that do not
+ * already have a designated recipient and are not
+ * already used as part of a provided fulfillment.
+ *
+ * @return orderHashes The hashes of the orders being fulfilled.
*/
function _validateOrdersAndPrepareToFulfill(
AdvancedOrder[] memory advancedOrders,
@@ -165,64 +190,69 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
bool revertOnInvalid,
uint256 maximumFulfilled,
address recipient
- ) internal {
+ ) internal returns (bytes32[] memory orderHashes) {
// Ensure this function cannot be triggered during a reentrant call.
- _setReentrancyGuard();
-
- // Read length of orders array and place on the stack.
- uint256 totalOrders = advancedOrders.length;
-
- // Track the order hash for each order being fulfilled.
- bytes32[] memory orderHashes = new bytes32[](totalOrders);
-
- // Override orderHashes length to zero after memory has been allocated.
- assembly {
- mstore(orderHashes, 0)
- }
+ _setReentrancyGuard(true); // Native tokens accepted during execution.
// Declare an error buffer indicating status of any native offer items.
- // {00} == 0 => In a match function, no native offer items: allow.
- // {01} == 1 => In a match function, some native offer items: allow.
- // {10} == 2 => Not in a match function, no native offer items: allow.
- // {11} == 3 => Not in a match function, some native offer items: THROW.
+ // Native tokens may only be provided as part of contract orders or when
+ // fulfilling via matchOrders or matchAdvancedOrders; if bits indicating
+ // these conditions are not met have been set, throw.
uint256 invalidNativeOfferItemErrorBuffer;
// Use assembly to set the value for the second bit of the error buffer.
assembly {
- // Use the second bit of the error buffer to indicate whether the
- // current function is not matchAdvancedOrders or matchOrders.
- invalidNativeOfferItemErrorBuffer := shl(
- 1,
- gt(
- // Take the remainder of the selector modulo a magic value.
- mod(
- shr(NumBitsAfterSelector, calldataload(0)),
- NonMatchSelector_MagicModulus
- ),
- // Check if remainder is higher than the greatest remainder
- // of the two match selectors modulo the magic value.
- NonMatchSelector_MagicRemainder
- )
+ /**
+ * Use the 231st bit of the error buffer to indicate whether the
+ * current function is not matchAdvancedOrders or matchOrders.
+ *
+ * sig func
+ * -----------------------------------------------------------------
+ * 1010100000010111010001000 0 000100 matchOrders
+ * 1111001011010001001010110 0 010010 matchAdvancedOrders
+ * 1110110110011000101001010 1 110100 fulfillAvailableOrders
+ * 1000011100100000000110110 1 000001 fulfillAvailableAdvancedOrders
+ * ^ 7th bit
+ */
+ invalidNativeOfferItemErrorBuffer := and(
+ NonMatchSelector_MagicMask,
+ calldataload(0)
)
}
+ // Declare variables for later use.
+ AdvancedOrder memory advancedOrder;
+ uint256 terminalMemoryOffset;
+
+ unchecked {
+ // Read length of orders array and place on the stack.
+ uint256 totalOrders = advancedOrders.length;
+
+ // Track the order hash for each order being fulfilled.
+ orderHashes = new bytes32[](totalOrders);
+
+ // Determine the memory offset to terminate on during loops.
+ terminalMemoryOffset = (totalOrders + 1) << OneWordShift;
+ }
+
// Skip overflow checks as all for loops are indexed starting at zero.
unchecked {
+ // Declare inner variables.
+ OfferItem[] memory offer;
+ ConsiderationItem[] memory consideration;
+
// Iterate over each order.
- for (uint256 i = 0; i < totalOrders; ++i) {
- // Retrieve the current order.
- AdvancedOrder memory advancedOrder = advancedOrders[i];
+ for (uint256 i = OneWord; i < terminalMemoryOffset; i += OneWord) {
+ // Retrieve order using assembly to bypass out-of-range check.
+ assembly {
+ advancedOrder := mload(add(advancedOrders, i))
+ }
// Determine if max number orders have already been fulfilled.
if (maximumFulfilled == 0) {
// Mark fill fraction as zero as the order will not be used.
advancedOrder.numerator = 0;
- // Update the length of the orderHashes array.
- assembly {
- mstore(orderHashes, add(i, 1))
- }
-
// Continue iterating through the remaining orders.
continue;
}
@@ -234,16 +264,9 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
uint256 denominator
) = _validateOrderAndUpdateStatus(
advancedOrder,
- criteriaResolvers,
- revertOnInvalid,
- orderHashes
+ revertOnInvalid
);
- // Update the length of the orderHashes array.
- assembly {
- mstore(orderHashes, add(i, 1))
- }
-
// Do not track hash or adjust prices if order is not fulfilled.
if (numerator == 0) {
// Mark fill fraction as zero if the order is not fulfilled.
@@ -254,12 +277,14 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
}
// Otherwise, track the order hash in question.
- orderHashes[i] = orderHash;
+ assembly {
+ mstore(add(orderHashes, i), orderHash)
+ }
// Decrement the number of fulfilled orders.
// Skip underflow check as the condition before
// implies that maximumFulfilled > 0.
- maximumFulfilled--;
+ --maximumFulfilled;
// Place the start time for the order on the stack.
uint256 startTime = advancedOrder.parameters.startTime;
@@ -268,23 +293,40 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
uint256 endTime = advancedOrder.parameters.endTime;
// Retrieve array of offer items for the order in question.
- OfferItem[] memory offer = advancedOrder.parameters.offer;
+ offer = advancedOrder.parameters.offer;
// Read length of offer array and place on the stack.
uint256 totalOfferItems = offer.length;
+ {
+ // Create a variable indicating if the order is not a
+ // contract order. Cache in scratch space to avoid stack
+ // depth errors.
+ OrderType orderType = advancedOrder.parameters.orderType;
+ assembly {
+ // Note that this check requires that there are no order
+ // types beyond the current set (0-4). It will need to
+ // be modified if more order types are added.
+ let isNonContract := lt(orderType, 4)
+ mstore(0, isNonContract)
+ }
+ }
+
// Iterate over each offer item on the order.
for (uint256 j = 0; j < totalOfferItems; ++j) {
// Retrieve the offer item.
OfferItem memory offerItem = offer[j];
- assembly {
- // If the offer item is for the native token, set the
- // first bit of the error buffer to true.
- invalidNativeOfferItemErrorBuffer := or(
- invalidNativeOfferItemErrorBuffer,
- iszero(mload(offerItem))
- )
+ {
+ assembly {
+ // If the offer item is for the native token and the
+ // order type is not a contract order type, set the
+ // first bit of the error buffer to true.
+ invalidNativeOfferItemErrorBuffer := or(
+ invalidNativeOfferItemErrorBuffer,
+ lt(mload(offerItem), mload(0))
+ )
+ }
}
// Apply order fill fraction to offer item end amount.
@@ -307,23 +349,23 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
);
}
- // Update end amount in memory to match the derived amount.
- offerItem.endAmount = endAmount;
-
// Adjust offer amount using current time; round down.
- offerItem.startAmount = _locateCurrentAmount(
+ uint256 currentAmount = _locateCurrentAmount(
offerItem.startAmount,
- offerItem.endAmount,
+ endAmount,
startTime,
endTime,
false // round down
);
+
+ // Update amounts in memory to match the current amount.
+ // Note that the end amount is used to track spent amounts.
+ offerItem.startAmount = currentAmount;
+ offerItem.endAmount = currentAmount;
}
// Retrieve array of consideration items for order in question.
- ConsiderationItem[] memory consideration = (
- advancedOrder.parameters.consideration
- );
+ consideration = (advancedOrder.parameters.consideration);
// Read length of consideration array and place on the stack.
uint256 totalConsiderationItems = consideration.length;
@@ -358,22 +400,32 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
);
}
- // Update end amount in memory to match the derived amount.
- considerationItem.endAmount = endAmount;
-
// Adjust consideration amount using current time; round up.
- considerationItem.startAmount = (
+ uint256 currentAmount = (
_locateCurrentAmount(
considerationItem.startAmount,
- considerationItem.endAmount,
+ endAmount,
startTime,
endTime,
true // round up
)
);
- // Utilize assembly to manually "shift" the recipient value.
+ considerationItem.startAmount = currentAmount;
+
+ // Utilize assembly to manually "shift" the recipient value,
+ // then to copy the start amount to the recipient.
+ // Note that this sets up the memory layout that is
+ // subsequently relied upon by
+ // _aggregateValidFulfillmentConsiderationItems.
assembly {
+ // Derive the pointer to the recipient using the item
+ // pointer along with the offset to the recipient.
+ let considerationItemRecipientPtr := add(
+ considerationItem,
+ ConsiderationItem_recipient_offset // recipient
+ )
+
// Write recipient to endAmount, as endAmount is not
// used from this point on and can be repurposed to fit
// the layout of a ReceivedItem.
@@ -382,24 +434,28 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
considerationItem,
ReceivedItem_recipient_offset // old endAmount
),
- mload(
- add(
- considerationItem,
- ConsiderationItem_recipient_offset
- )
- )
+ mload(considerationItemRecipientPtr)
)
+
+ // Write startAmount to recipient, as recipient is not
+ // used from this point on and can be repurposed to
+ // track received amounts.
+ mstore(considerationItemRecipientPtr, currentAmount)
}
}
}
}
- // If the first bit is set, a native offer item was encountered. If the
- // second bit is set in the error buffer, the current function is not
- // matchOrders or matchAdvancedOrders. If the value is three, both the
- // first and second bits were set; in that case, revert with an error.
- if (invalidNativeOfferItemErrorBuffer == 3) {
- revert InvalidNativeOfferItem();
+ // If the first bit is set, a native offer item was encountered on an
+ // order that is not a contract order. If the 231st bit is set in the
+ // error buffer, the current function is not matchOrders or
+ // matchAdvancedOrders. If the value is 1 + (1 << 230), then both the
+ // 1st and 231st bits were set; in that case, revert with an error.
+ if (
+ invalidNativeOfferItemErrorBuffer ==
+ NonMatchSelector_InvalidErrorValue
+ ) {
+ _revertInvalidNativeOfferItem();
}
// Apply criteria resolvers to each order as applicable.
@@ -408,21 +464,32 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
// Emit an event for each order signifying that it has been fulfilled.
// Skip overflow checks as all for loops are indexed starting at zero.
unchecked {
+ bytes32 orderHash;
+
// Iterate over each order.
- for (uint256 i = 0; i < totalOrders; ++i) {
+ for (uint256 i = OneWord; i < terminalMemoryOffset; i += OneWord) {
+ assembly {
+ orderHash := mload(add(orderHashes, i))
+ }
+
// Do not emit an event if no order hash is present.
- if (orderHashes[i] == bytes32(0)) {
+ if (orderHash == bytes32(0)) {
continue;
}
+ // Retrieve order using assembly to bypass out-of-range check.
+ assembly {
+ advancedOrder := mload(add(advancedOrders, i))
+ }
+
// Retrieve parameters for the order in question.
OrderParameters memory orderParameters = (
- advancedOrders[i].parameters
+ advancedOrder.parameters
);
// Emit an OrderFulfilled event.
_emitOrderFulfilledEvent(
- orderHashes[i],
+ orderHash,
orderParameters.offerer,
orderParameters.zone,
recipient,
@@ -473,8 +540,11 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
* approvals from. The zero hash signifies
* that no conduit should be used, with
* direct approvals set on Consideration.
- * @param recipient The intended recipient for all received
- * items.
+ * @param recipient The intended recipient for all items
+ * that do not already have a designated
+ * recipient and are not already used as
+ * part of a provided fulfillment.
+ * @param orderHashes An array of order hashes for each order.
*
* @return availableOrders An array of booleans indicating if each order
* with an index corresponding to the index of the
@@ -488,7 +558,8 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
FulfillmentComponent[][] memory offerFulfillments,
FulfillmentComponent[][] memory considerationFulfillments,
bytes32 fulfillerConduitKey,
- address recipient
+ address recipient,
+ bytes32[] memory orderHashes
)
internal
returns (bool[] memory availableOrders, Execution[] memory executions)
@@ -512,49 +583,52 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
uint256 totalFilteredExecutions = 0;
// Iterate over each offer fulfillment.
- for (uint256 i = 0; i < totalOfferFulfillments; ++i) {
- /// Retrieve the offer fulfillment components in question.
- FulfillmentComponent[] memory components = (
- offerFulfillments[i]
- );
-
+ for (uint256 i = 0; i < totalOfferFulfillments; ) {
// Derive aggregated execution corresponding with fulfillment.
Execution memory execution = _aggregateAvailable(
advancedOrders,
Side.OFFER,
- components,
+ offerFulfillments[i],
fulfillerConduitKey,
recipient
);
// If offerer and recipient on the execution are the same...
- if (execution.item.recipient == execution.offerer) {
+ if (
+ _unmaskedAddressComparison(
+ execution.item.recipient,
+ execution.offerer
+ )
+ ) {
// Increment total filtered executions.
++totalFilteredExecutions;
} else {
// Otherwise, assign the execution to the executions array.
executions[i - totalFilteredExecutions] = execution;
}
+
+ // Increment iterator.
+ ++i;
}
// Iterate over each consideration fulfillment.
- for (uint256 i = 0; i < totalConsiderationFulfillments; ++i) {
- /// Retrieve consideration fulfillment components in question.
- FulfillmentComponent[] memory components = (
- considerationFulfillments[i]
- );
-
+ for (uint256 i = 0; i < totalConsiderationFulfillments; ) {
// Derive aggregated execution corresponding with fulfillment.
Execution memory execution = _aggregateAvailable(
advancedOrders,
Side.CONSIDERATION,
- components,
+ considerationFulfillments[i],
fulfillerConduitKey,
address(0) // unused
);
// If offerer and recipient on the execution are the same...
- if (execution.item.recipient == execution.offerer) {
+ if (
+ _unmaskedAddressComparison(
+ execution.item.recipient,
+ execution.offerer
+ )
+ ) {
// Increment total filtered executions.
++totalFilteredExecutions;
} else {
@@ -563,6 +637,9 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
i + totalOfferFulfillments - totalFilteredExecutions
] = execution;
}
+
+ // Increment iterator.
+ ++i;
}
// If some number of executions have been filtered...
@@ -579,13 +656,15 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
// Revert if no orders are available.
if (executions.length == 0) {
- revert NoSpecifiedOrdersAvailable();
+ _revertNoSpecifiedOrdersAvailable();
}
// Perform final checks and return.
availableOrders = _performFinalChecksAndExecuteOrders(
advancedOrders,
- executions
+ executions,
+ orderHashes,
+ recipient
);
return (availableOrders, executions);
@@ -600,62 +679,29 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
* @param executions An array of elements indicating the sequence of
* transfers to perform when fulfilling the given
* orders.
+ * @param orderHashes An array of order hashes for each order.
+ * @param recipient The intended recipient for all items that do
+ * not already have a designated recipient and are
+ * not used as part of a provided fulfillment.
*
- * @return availableOrders An array of booleans indicating if each order
- * with an index corresponding to the index of the
- * returned boolean was fulfillable or not.
+ * @return availableOrders An array of booleans indicating if each order
+ * with an index corresponding to the index of the
+ * returned boolean was fulfillable or not.
*/
function _performFinalChecksAndExecuteOrders(
AdvancedOrder[] memory advancedOrders,
- Execution[] memory executions
- ) internal returns (bool[] memory availableOrders) {
+ Execution[] memory executions,
+ bytes32[] memory orderHashes,
+ address recipient
+ ) internal returns (bool[] memory /* availableOrders */) {
+ // Declare a variable for the available native token balance.
+ uint256 nativeTokenBalance;
+
// Retrieve the length of the advanced orders array and place on stack.
uint256 totalOrders = advancedOrders.length;
// Initialize array for tracking available orders.
- availableOrders = new bool[](totalOrders);
-
- // Skip overflow checks as all for loops are indexed starting at zero.
- unchecked {
- // Iterate over orders to ensure all considerations are met.
- for (uint256 i = 0; i < totalOrders; ++i) {
- // Retrieve the order in question.
- AdvancedOrder memory advancedOrder = advancedOrders[i];
-
- // Skip consideration item checks for order if not fulfilled.
- if (advancedOrder.numerator == 0) {
- // Note: orders do not need to be marked as unavailable as a
- // new memory region has been allocated. Review carefully if
- // altering compiler version or managing memory manually.
- continue;
- }
-
- // Mark the order as available.
- availableOrders[i] = true;
-
- // Retrieve consideration items to ensure they are fulfilled.
- ConsiderationItem[] memory consideration = (
- advancedOrder.parameters.consideration
- );
-
- // Read length of consideration array and place on the stack.
- uint256 totalConsiderationItems = consideration.length;
-
- // Iterate over each consideration item to ensure it is met.
- for (uint256 j = 0; j < totalConsiderationItems; ++j) {
- // Retrieve remaining amount on the consideration item.
- uint256 unmetAmount = consideration[j].startAmount;
-
- // Revert if the remaining amount is not zero.
- if (unmetAmount != 0) {
- revert ConsiderationNotMet(i, j, unmetAmount);
- }
- }
- }
- }
-
- // Put ether value supplied by the caller on the stack.
- uint256 etherRemaining = msg.value;
+ bool[] memory availableOrders = new bool[](totalOrders);
// Initialize an accumulator array. From this point forward, no new
// memory regions can be safely allocated until the accumulator is no
@@ -675,14 +721,14 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
// If execution transfers native tokens, reduce value available.
if (item.itemType == ItemType.NATIVE) {
- // Ensure that sufficient native tokens are still available.
- if (item.amount > etherRemaining) {
- revert InsufficientEtherSupplied();
+ // Get the current available balance of native tokens.
+ assembly {
+ nativeTokenBalance := selfbalance()
}
- // Skip underflow check as amount is less than ether remaining.
- unchecked {
- etherRemaining -= item.amount;
+ // Ensure that sufficient native tokens are still available.
+ if (item.amount > nativeTokenBalance) {
+ _revertInsufficientNativeTokensSupplied();
}
}
@@ -700,19 +746,168 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
}
}
+ // Skip overflow checks as all for loops are indexed starting at zero.
+ unchecked {
+ // duplicate recipient address to stack to avoid stack-too-deep
+ address _recipient = recipient;
+
+ // Iterate over orders to ensure all consideration items are met.
+ for (uint256 i = 0; i < totalOrders; ++i) {
+ // Retrieve the order in question.
+ AdvancedOrder memory advancedOrder = advancedOrders[i];
+
+ // Skip consideration item checks for order if not fulfilled.
+ if (advancedOrder.numerator == 0) {
+ // This is required because the current memory region, which
+ // was previously used by the accumulator, might be dirty.
+ availableOrders[i] = false;
+ continue;
+ }
+
+ // Mark the order as available.
+ availableOrders[i] = true;
+
+ // Retrieve the order parameters.
+ OrderParameters memory parameters = advancedOrder.parameters;
+
+ {
+ // Retrieve offer items.
+ OfferItem[] memory offer = parameters.offer;
+
+ // Read length of offer array & place on the stack.
+ uint256 totalOfferItems = offer.length;
+
+ // Iterate over each offer item to restore it.
+ for (uint256 j = 0; j < totalOfferItems; ++j) {
+ OfferItem memory offerItem = offer[j];
+ // Retrieve original amount on the offer item.
+ uint256 originalAmount = offerItem.endAmount;
+ // Retrieve remaining amount on the offer item.
+ uint256 unspentAmount = offerItem.startAmount;
+
+ // Transfer to recipient if unspent amount is not zero.
+ // Note that the transfer will not be reflected in the
+ // executions array.
+ if (unspentAmount != 0) {
+ _transfer(
+ _convertOfferItemToReceivedItemWithRecipient(
+ offerItem,
+ _recipient
+ ),
+ parameters.offerer,
+ parameters.conduitKey,
+ accumulator
+ );
+ }
+
+ // Restore original amount on the offer item.
+ offerItem.startAmount = originalAmount;
+ }
+ }
+
+ {
+ // Retrieve consideration items & ensure they are fulfilled.
+ ConsiderationItem[] memory consideration = (
+ parameters.consideration
+ );
+
+ // Read length of consideration array & place on the stack.
+ uint256 totalConsiderationItems = consideration.length;
+
+ // Iterate over each consideration item to ensure it is met.
+ for (uint256 j = 0; j < totalConsiderationItems; ++j) {
+ ConsiderationItem memory considerationItem = (
+ consideration[j]
+ );
+
+ // Retrieve remaining amount on the consideration item.
+ uint256 unmetAmount = considerationItem.startAmount;
+
+ // Revert if the remaining amount is not zero.
+ if (unmetAmount != 0) {
+ _revertConsiderationNotMet(i, j, unmetAmount);
+ }
+
+ // Utilize assembly to restore the original value.
+ assembly {
+ // Write recipient to startAmount.
+ mstore(
+ add(
+ considerationItem,
+ ReceivedItem_amount_offset
+ ),
+ mload(
+ add(
+ considerationItem,
+ ConsiderationItem_recipient_offset
+ )
+ )
+ )
+ }
+ }
+ }
+
+ // Check restricted orders and contract orders.
+ _assertRestrictedAdvancedOrderValidity(
+ advancedOrder,
+ orderHashes,
+ orderHashes[i]
+ );
+ }
+ }
+
// Trigger any remaining accumulated transfers via call to the conduit.
_triggerIfArmed(accumulator);
- // If any ether remains after fulfillments, return it to the caller.
- if (etherRemaining != 0) {
- _transferEth(payable(msg.sender), etherRemaining);
+ // Determine whether any native token balance remains.
+ assembly {
+ nativeTokenBalance := selfbalance()
+ }
+
+ // Return any remaining native token balance to the caller.
+ if (nativeTokenBalance != 0) {
+ _transferNativeTokens(payable(msg.sender), nativeTokenBalance);
}
// Clear the reentrancy guard.
_clearReentrancyGuard();
// Return the array containing available orders.
- return (availableOrders);
+ return availableOrders;
+ }
+
+ /**
+ * @dev Internal function to emit an OrdersMatched event using the same
+ * memory region as the existing order hash array.
+ *
+ * @param orderHashes An array of order hashes to include as an argument for
+ * the OrdersMatched event.
+ */
+ function _emitOrdersMatched(bytes32[] memory orderHashes) internal {
+ assembly {
+ // Load the array length from memory.
+ let length := mload(orderHashes)
+
+ // Get the full size of the event data - one word for the offset,
+ // one for the array length and one per hash.
+ let dataSize := add(TwoWords, shl(OneWordShift, length))
+
+ // Get pointer to start of data, reusing word before array length
+ // for the offset.
+ let dataPointer := sub(orderHashes, OneWord)
+
+ // Cache the existing word in memory at the offset pointer.
+ let cache := mload(dataPointer)
+
+ // Write an offset of 32.
+ mstore(dataPointer, OneWord)
+
+ // Emit the OrdersMatched event.
+ log1(dataPointer, dataSize, OrdersMatchedTopic0)
+
+ // Restore the cached word.
+ mstore(dataPointer, cache)
+ }
}
/**
@@ -746,6 +941,8 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
* to consideration components. Note that each
* consideration component must be fully met in
* order for the match operation to be valid.
+ * @param recipient The intended recipient for all unspent offer
+ * item amounts.
*
* @return executions An array of elements indicating the sequence of
* transfers performed as part of matching the given
@@ -754,19 +951,29 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
function _matchAdvancedOrders(
AdvancedOrder[] memory advancedOrders,
CriteriaResolver[] memory criteriaResolvers,
- Fulfillment[] calldata fulfillments
- ) internal returns (Execution[] memory executions) {
+ Fulfillment[] memory fulfillments,
+ address recipient
+ ) internal returns (Execution[] memory /* executions */) {
// Validate orders, update order status, and determine item amounts.
- _validateOrdersAndPrepareToFulfill(
+ bytes32[] memory orderHashes = _validateOrdersAndPrepareToFulfill(
advancedOrders,
criteriaResolvers,
true, // Signifies that invalid orders should revert.
advancedOrders.length,
- address(0) // OrderFulfilled event has no recipient when matching.
+ recipient
);
- // Fulfill the orders using the supplied fulfillments.
- return _fulfillAdvancedOrders(advancedOrders, fulfillments);
+ // Emit OrdersMatched event, providing an array of matched order hashes.
+ _emitOrdersMatched(orderHashes);
+
+ // Fulfill the orders using the supplied fulfillments and recipient.
+ return
+ _fulfillAdvancedOrders(
+ advancedOrders,
+ fulfillments,
+ orderHashes,
+ recipient
+ );
}
/**
@@ -781,14 +988,20 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
* that the final amount of each consideration
* component must be zero for a match operation to
* be considered valid.
+ * @param orderHashes An array of order hashes for each order.
+ * @param recipient The intended recipient for all items that do
+ * not already have a designated recipient and are
+ * not used as part of a provided fulfillment.
*
- * @return executions An array of elements indicating the sequence of
- * transfers performed as part of matching the given
- * orders.
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the
+ * given orders.
*/
function _fulfillAdvancedOrders(
AdvancedOrder[] memory advancedOrders,
- Fulfillment[] calldata fulfillments
+ Fulfillment[] memory fulfillments,
+ bytes32[] memory orderHashes,
+ address recipient
) internal returns (Execution[] memory executions) {
// Retrieve fulfillments array length and place on the stack.
uint256 totalFulfillments = fulfillments.length;
@@ -804,17 +1017,23 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
// Iterate over each fulfillment.
for (uint256 i = 0; i < totalFulfillments; ++i) {
/// Retrieve the fulfillment in question.
- Fulfillment calldata fulfillment = fulfillments[i];
+ Fulfillment memory fulfillment = fulfillments[i];
// Derive the execution corresponding with the fulfillment.
Execution memory execution = _applyFulfillment(
advancedOrders,
fulfillment.offerComponents,
- fulfillment.considerationComponents
+ fulfillment.considerationComponents,
+ i
);
// If offerer and recipient on the execution are the same...
- if (execution.item.recipient == execution.offerer) {
+ if (
+ _unmaskedAddressComparison(
+ execution.item.recipient,
+ execution.offerer
+ )
+ ) {
// Increment total filtered executions.
++totalFilteredExecutions;
} else {
@@ -836,9 +1055,14 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
}
// Perform final checks and execute orders.
- _performFinalChecksAndExecuteOrders(advancedOrders, executions);
+ _performFinalChecksAndExecuteOrders(
+ advancedOrders,
+ executions,
+ orderHashes,
+ recipient
+ );
// Return the executions array.
- return (executions);
+ return executions;
}
}
diff --git a/contracts/lib/OrderFulfiller.sol b/contracts/lib/OrderFulfiller.sol
index 4a7fe6a21..405508355 100644
--- a/contracts/lib/OrderFulfiller.sol
+++ b/contracts/lib/OrderFulfiller.sol
@@ -1,18 +1,16 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
-import { ItemType } from "./ConsiderationEnums.sol";
+import { ItemType, OrderType } from "./ConsiderationEnums.sol";
-// prettier-ignore
import {
- OfferItem,
+ AdvancedOrder,
ConsiderationItem,
- SpentItem,
- ReceivedItem,
+ CriteriaResolver,
+ OfferItem,
OrderParameters,
- Order,
- AdvancedOrder,
- CriteriaResolver
+ ReceivedItem,
+ SpentItem
} from "./ConsiderationStructs.sol";
import { BasicOrderFulfiller } from "./BasicOrderFulfiller.sol";
@@ -21,7 +19,17 @@ import { CriteriaResolution } from "./CriteriaResolution.sol";
import { AmountDeriver } from "./AmountDeriver.sol";
-import "./ConsiderationConstants.sol";
+import {
+ _revertInsufficientNativeTokensSupplied,
+ _revertInvalidNativeOfferItem
+} from "./ConsiderationErrors.sol";
+
+import {
+ AccumulatorDisarmed,
+ ConsiderationItem_recipient_offset,
+ ReceivedItem_amount_offset,
+ ReceivedItem_recipient_offset
+} from "./ConsiderationConstants.sol";
/**
* @title OrderFulfiller
@@ -43,9 +51,9 @@ contract OrderFulfiller is
* that may optionally be used to transfer approved
* ERC20/721/1155 tokens.
*/
- constructor(address conduitController)
- BasicOrderFulfiller(conduitController)
- {}
+ constructor(
+ address conduitController
+ ) BasicOrderFulfiller(conduitController) {}
/**
* @dev Internal function to validate an order and update its status, adjust
@@ -80,22 +88,17 @@ contract OrderFulfiller is
address recipient
) internal returns (bool) {
// Ensure this function cannot be triggered during a reentrant call.
- _setReentrancyGuard();
-
- // Declare empty bytes32 array (unused, will remain empty).
- bytes32[] memory priorOrderHashes;
+ _setReentrancyGuard(
+ // Native tokens accepted during execution for contract order types.
+ advancedOrder.parameters.orderType == OrderType.CONTRACT
+ );
// Validate order, update status, and determine fraction to fill.
(
bytes32 orderHash,
uint256 fillNumerator,
uint256 fillDenominator
- ) = _validateOrderAndUpdateStatus(
- advancedOrder,
- criteriaResolvers,
- true,
- priorOrderHashes
- );
+ ) = _validateOrderAndUpdateStatus(advancedOrder, true);
// Create an array with length 1 containing the order.
AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](1);
@@ -118,6 +121,17 @@ contract OrderFulfiller is
recipient
);
+ // Declare empty bytes32 array and populate with the order hash.
+ bytes32[] memory orderHashes = new bytes32[](1);
+ orderHashes[0] = orderHash;
+
+ // Ensure restricted orders have a valid submitter or pass a zone check.
+ _assertRestrictedAdvancedOrderValidity(
+ advancedOrders[0],
+ orderHashes,
+ orderHash
+ );
+
// Emit an event signifying that the order has been fulfilled.
_emitOrderFulfilledEvent(
orderHash,
@@ -170,10 +184,10 @@ contract OrderFulfiller is
// As of solidity 0.6.0, inline assembly cannot directly access function
// definitions, but can still access locally scoped function variables.
- // This means that in order to recast the type of a function, we need to
- // create a local variable to reference the internal function definition
- // (using the same type) and a local variable with the desired type,
- // and then cast the original function pointer to the desired type.
+ // This means that a local variable to reference the internal function
+ // definition (using the same type), along with a local variable with
+ // the desired type, must first be created. Then, the original function
+ // pointer can be recast to the desired type.
/**
* Repurpose existing OfferItem memory regions on the offer array for
@@ -190,36 +204,26 @@ contract OrderFulfiller is
// Declare a nested scope to minimize stack depth.
unchecked {
- // Declare a virtual function pointer taking an OfferItem argument.
- function(OfferItem memory, address, bytes32, bytes memory)
- internal _transferOfferItem;
-
- {
- // Assign _transfer function to a new function pointer (it takes
- // a ReceivedItem as its initial argument)
- function(ReceivedItem memory, address, bytes32, bytes memory)
- internal _transferReceivedItem = _transfer;
-
- // Utilize assembly to override the virtual function pointer.
- assembly {
- // Cast initial ReceivedItem type to an OfferItem type.
- _transferOfferItem := _transferReceivedItem
- }
- }
-
// Read offer array length from memory and place on stack.
uint256 totalOfferItems = orderParameters.offer.length;
+ // Create a variable to indicate whether the order has any
+ // native offer items
+ uint256 anyNativeItems;
+
// Iterate over each offer on the order.
// Skip overflow check as for loop is indexed starting at zero.
for (uint256 i = 0; i < totalOfferItems; ++i) {
// Retrieve the offer item.
OfferItem memory offerItem = orderParameters.offer[i];
- // Offer items for the native token can not be received
- // outside of a match order function.
- if (offerItem.itemType == ItemType.NATIVE) {
- revert InvalidNativeOfferItem();
+ // Offer items for the native token can not be received outside
+ // of a match order function except as part of a contract order.
+ {
+ ItemType itemType = offerItem.itemType;
+ assembly {
+ anyNativeItems := or(anyNativeItems, iszero(itemType))
+ }
}
// Declare an additional nested scope to minimize stack depth.
@@ -252,17 +256,36 @@ contract OrderFulfiller is
}
// Transfer the item from the offerer to the recipient.
- _transferOfferItem(
+ _toOfferItemInput(_transfer)(
offerItem,
orderParameters.offerer,
orderParameters.conduitKey,
accumulator
);
}
+
+ // If a non-contract order has native offer items, throw with an
+ // `InvalidNativeOfferItem` custom error.
+ {
+ OrderType orderType = orderParameters.orderType;
+ uint256 invalidNativeOfferItem;
+ assembly {
+ invalidNativeOfferItem := and(
+ // Note that this check requires that there are no order
+ // types beyond the current set (0-4). It will need to
+ // be modified if more order types are added.
+ lt(orderType, 4),
+ anyNativeItems
+ )
+ }
+ if (invalidNativeOfferItem != 0) {
+ _revertInvalidNativeOfferItem();
+ }
+ }
}
- // Put ether value supplied by the caller on the stack.
- uint256 etherRemaining = msg.value;
+ // Declare a variable for the available native token balance.
+ uint256 nativeTokenBalance;
/**
* Repurpose existing ConsiderationItem memory regions on the
@@ -281,22 +304,6 @@ contract OrderFulfiller is
// Declare a nested scope to minimize stack depth.
unchecked {
- // Declare virtual function pointer with ConsiderationItem argument.
- function(ConsiderationItem memory, address, bytes32, bytes memory)
- internal _transferConsiderationItem;
- {
- // Reassign _transfer function to a new function pointer (it
- // takes a ReceivedItem as its initial argument).
- function(ReceivedItem memory, address, bytes32, bytes memory)
- internal _transferReceivedItem = _transfer;
-
- // Utilize assembly to override the virtual function pointer.
- assembly {
- // Cast ReceivedItem type to ConsiderationItem type.
- _transferConsiderationItem := _transferReceivedItem
- }
- }
-
// Read consideration array length from memory and place on stack.
uint256 totalConsiderationItems = orderParameters
.consideration
@@ -343,17 +350,19 @@ contract OrderFulfiller is
// Reduce available value if offer spent ETH or a native token.
if (considerationItem.itemType == ItemType.NATIVE) {
- // Ensure that sufficient native tokens are still available.
- if (amount > etherRemaining) {
- revert InsufficientEtherSupplied();
+ // Get the current available balance of native tokens.
+ assembly {
+ nativeTokenBalance := selfbalance()
}
- // Skip underflow check as a comparison has just been made.
- etherRemaining -= amount;
+ // Ensure that sufficient native tokens are still available.
+ if (amount > nativeTokenBalance) {
+ _revertInsufficientNativeTokensSupplied();
+ }
}
// Transfer item from caller to recipient specified by the item.
- _transferConsiderationItem(
+ _toConsiderationItemInput(_transfer)(
considerationItem,
msg.sender,
fulfillerConduitKey,
@@ -365,10 +374,14 @@ contract OrderFulfiller is
// Trigger any remaining accumulated transfers via call to the conduit.
_triggerIfArmed(accumulator);
- // If any ether remains after fulfillments...
- if (etherRemaining != 0) {
- // return it to the caller.
- _transferEth(payable(msg.sender), etherRemaining);
+ // Determine whether any native token balance remains.
+ assembly {
+ nativeTokenBalance := selfbalance()
+ }
+
+ // Return any remaining native token balance to the caller.
+ if (nativeTokenBalance != 0) {
+ _transferNativeTokens(payable(msg.sender), nativeTokenBalance);
}
}
@@ -380,7 +393,7 @@ contract OrderFulfiller is
* @param orderHash The order hash.
* @param offerer The offerer for the order.
* @param zone The zone for the order.
- * @param fulfiller The fulfiller of the order, or the null address if
+ * @param recipient The recipient of the order, or the null address if
* the order was fulfilled via order matching.
* @param offer The offer items for the order.
* @param consideration The consideration items for the order.
@@ -389,7 +402,7 @@ contract OrderFulfiller is
bytes32 orderHash,
address offerer,
address zone,
- address fulfiller,
+ address recipient,
OfferItem[] memory offer,
ConsiderationItem[] memory consideration
) internal {
@@ -410,64 +423,9 @@ contract OrderFulfiller is
orderHash,
offerer,
zone,
- fulfiller,
+ recipient,
spentItems,
receivedItems
);
}
-
- /**
- * @dev Internal pure function to convert an order to an advanced order with
- * numerator and denominator of 1 and empty extraData.
- *
- * @param order The order to convert.
- *
- * @return advancedOrder The new advanced order.
- */
- function _convertOrderToAdvanced(Order calldata order)
- internal
- pure
- returns (AdvancedOrder memory advancedOrder)
- {
- // Convert to partial order (1/1 or full fill) and return new value.
- advancedOrder = AdvancedOrder(
- order.parameters,
- 1,
- 1,
- order.signature,
- ""
- );
- }
-
- /**
- * @dev Internal pure function to convert an array of orders to an array of
- * advanced orders with numerator and denominator of 1.
- *
- * @param orders The orders to convert.
- *
- * @return advancedOrders The new array of partial orders.
- */
- function _convertOrdersToAdvanced(Order[] calldata orders)
- internal
- pure
- returns (AdvancedOrder[] memory advancedOrders)
- {
- // Read the number of orders from calldata and place on the stack.
- uint256 totalOrders = orders.length;
-
- // Allocate new empty array for each partial order in memory.
- advancedOrders = new AdvancedOrder[](totalOrders);
-
- // Skip overflow check as the index for the loop starts at zero.
- unchecked {
- // Iterate over the given orders.
- for (uint256 i = 0; i < totalOrders; ++i) {
- // Convert to partial order (1/1 or full fill) and update array.
- advancedOrders[i] = _convertOrderToAdvanced(orders[i]);
- }
- }
-
- // Return the array of advanced orders.
- return advancedOrders;
- }
}
diff --git a/contracts/lib/OrderValidator.sol b/contracts/lib/OrderValidator.sol
index 70e05f049..afc927d25 100644
--- a/contracts/lib/OrderValidator.sol
+++ b/contracts/lib/OrderValidator.sol
@@ -1,24 +1,56 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
import { OrderType } from "./ConsiderationEnums.sol";
-// prettier-ignore
import {
- OrderParameters,
- Order,
AdvancedOrder,
+ ConsiderationItem,
+ OfferItem,
+ Order,
OrderComponents,
- OrderStatus,
- CriteriaResolver
+ OrderParameters,
+ OrderStatus
} from "./ConsiderationStructs.sol";
-import "./ConsiderationConstants.sol";
+import {
+ _revertBadFraction,
+ _revertCannotCancelOrder,
+ _revertConsiderationLengthNotEqualToTotalOriginal,
+ _revertInvalidContractOrder,
+ _revertPartialFillsNotEnabledForOrder
+} from "./ConsiderationErrors.sol";
import { Executor } from "./Executor.sol";
import { ZoneInteraction } from "./ZoneInteraction.sol";
+import { MemoryPointer } from "../helpers/PointerLibraries.sol";
+
+import {
+ AdvancedOrder_denominator_offset,
+ AdvancedOrder_numerator_offset,
+ BasicOrder_offerer_cdPtr,
+ Common_amount_offset,
+ Common_endAmount_offset,
+ Common_identifier_offset,
+ Common_token_offset,
+ ConsiderItem_recipient_offset,
+ ContractOrder_orderHash_offerer_shift,
+ MaxUint120,
+ OrderStatus_filledDenominator_offset,
+ OrderStatus_filledNumerator_offset,
+ OrderStatus_ValidatedAndNotCancelled
+} from "./ConsiderationConstants.sol";
+
+import {
+ Error_selector_offset,
+ Panic_arithmetic,
+ Panic_error_code_ptr,
+ Panic_error_length,
+ Panic_error_selector
+} from "./ConsiderationErrorConstants.sol";
+
/**
* @title OrderValidator
* @author 0age
@@ -29,6 +61,9 @@ contract OrderValidator is Executor, ZoneInteraction {
// Track status of each order (validated, cancelled, and fraction filled).
mapping(bytes32 => OrderStatus) private _orderStatus;
+ // Track nonces for contract offerers.
+ mapping(address => uint256) internal _contractNonces;
+
/**
* @dev Derive and set hashes, reference chainId, and associated domain
* separator during deployment.
@@ -41,17 +76,25 @@ contract OrderValidator is Executor, ZoneInteraction {
/**
* @dev Internal function to verify and update the status of a basic order.
+ * Note that this function may only be safely called as part of basic
+ * orders, as it assumes a specific calldata encoding structure that
+ * must first be validated.
*
* @param orderHash The hash of the order.
- * @param offerer The offerer of the order.
* @param signature A signature from the offerer indicating that the order
* has been approved.
*/
function _validateBasicOrderAndUpdateStatus(
bytes32 orderHash,
- address offerer,
- bytes memory signature
+ bytes calldata signature
) internal {
+ // Retrieve offerer directly using fixed calldata offset based on strict
+ // basic parameter encoding.
+ address offerer;
+ assembly {
+ offerer := calldataload(BasicOrder_offerer_cdPtr)
+ }
+
// Retrieve the order status for the given order hash.
OrderStatus storage orderStatus = _orderStatus[orderHash];
@@ -84,37 +127,20 @@ contract OrderValidator is Executor, ZoneInteraction {
* fill. Note that all offer and consideration
* amounts must divide with no remainder in order
* for a partial fill to be valid.
- * @param criteriaResolvers An array where each element contains a reference
- * to a specific offer or consideration, a token
- * identifier, and a proof that the supplied token
- * identifier is contained in the order's merkle
- * root. Note that a criteria of zero indicates
- * that any (transferable) token identifier is
- * valid and that no proof needs to be supplied.
* @param revertOnInvalid A boolean indicating whether to revert if the
* order is invalid due to the time or status.
- * @param priorOrderHashes The order hashes of each order supplied prior to
- * the current order as part of a "match" variety
- * of order fulfillment (e.g. this array will be
- * empty for single or "fulfill available").
*
* @return orderHash The order hash.
- * @return newNumerator A value indicating the portion of the order that
+ * @return numerator A value indicating the portion of the order that
* will be filled.
- * @return newDenominator A value indicating the total size of the order.
+ * @return denominator A value indicating the total size of the order.
*/
function _validateOrderAndUpdateStatus(
AdvancedOrder memory advancedOrder,
- CriteriaResolver[] memory criteriaResolvers,
- bool revertOnInvalid,
- bytes32[] memory priorOrderHashes
+ bool revertOnInvalid
)
internal
- returns (
- bytes32 orderHash,
- uint256 newNumerator,
- uint256 newDenominator
- )
+ returns (bytes32 orderHash, uint256 numerator, uint256 denominator)
{
// Retrieve the parameters for the order.
OrderParameters memory orderParameters = advancedOrder.parameters;
@@ -132,38 +158,74 @@ contract OrderValidator is Executor, ZoneInteraction {
}
// Read numerator and denominator from memory and place on the stack.
- uint256 numerator = uint256(advancedOrder.numerator);
- uint256 denominator = uint256(advancedOrder.denominator);
+ // Note that overflowed values are masked.
+ assembly {
+ numerator := and(
+ mload(add(advancedOrder, AdvancedOrder_numerator_offset)),
+ MaxUint120
+ )
+
+ denominator := and(
+ mload(add(advancedOrder, AdvancedOrder_denominator_offset)),
+ MaxUint120
+ )
+ }
- // Ensure that the supplied numerator and denominator are valid.
- if (numerator > denominator || numerator == 0) {
- revert BadFraction();
+ // Declare variable for tracking the validity of the supplied fraction.
+ bool invalidFraction;
+
+ // If the order is a contract order, return the generated order.
+ if (orderParameters.orderType == OrderType.CONTRACT) {
+ // Ensure that the numerator and denominator are both equal to 1.
+ assembly {
+ // (1 ^ nd =/= 0) => (nd =/= 1) => (n =/= 1) || (d =/= 1)
+ // It's important that the values are 120-bit masked before
+ // multiplication is applied. Otherwise, the last implication
+ // above is not correct (mod 2^256).
+ invalidFraction := xor(mul(numerator, denominator), 1)
+ }
+
+ // Revert if the supplied numerator and denominator are not valid.
+ if (invalidFraction) {
+ _revertBadFraction();
+ }
+
+ // Return the generated order based on the order params and the
+ // provided extra data. If revertOnInvalid is true, the function
+ // will revert if the input is invalid.
+ return
+ _getGeneratedOrder(
+ orderParameters,
+ advancedOrder.extraData,
+ revertOnInvalid
+ );
+ }
+
+ // Ensure numerator does not exceed denominator and is not zero.
+ assembly {
+ invalidFraction := or(gt(numerator, denominator), iszero(numerator))
+ }
+
+ // Revert if the supplied numerator and denominator are not valid.
+ if (invalidFraction) {
+ _revertBadFraction();
}
// If attempting partial fill (n < d) check order type & ensure support.
if (
- numerator < denominator &&
- _doesNotSupportPartialFills(orderParameters.orderType)
+ _doesNotSupportPartialFills(
+ orderParameters.orderType,
+ numerator,
+ denominator
+ )
) {
// Revert if partial fill was attempted on an unsupported order.
- revert PartialFillsNotEnabledForOrder();
+ _revertPartialFillsNotEnabledForOrder();
}
// Retrieve current counter & use it w/ parameters to derive order hash.
orderHash = _assertConsiderationLengthAndGetOrderHash(orderParameters);
- // Ensure restricted orders have a valid submitter or pass a zone check.
- _assertRestrictedAdvancedOrderValidity(
- advancedOrder,
- criteriaResolvers,
- priorOrderHashes,
- orderHash,
- orderParameters.zoneHash,
- orderParameters.orderType,
- orderParameters.offerer,
- orderParameters.zone
- );
-
// Retrieve the order status using the derived order hash.
OrderStatus storage orderStatus = _orderStatus[orderHash];
@@ -189,43 +251,79 @@ contract OrderValidator is Executor, ZoneInteraction {
);
}
- // Read filled amount as numerator and denominator and put on the stack.
- uint256 filledNumerator = orderStatus.numerator;
- uint256 filledDenominator = orderStatus.denominator;
-
- // If order (orderStatus) currently has a non-zero denominator it is
- // partially filled.
- if (filledDenominator != 0) {
- // If denominator of 1 supplied, fill all remaining amount on order.
- if (denominator == 1) {
- // Scale numerator & denominator to match current denominator.
- numerator = filledDenominator;
- denominator = filledDenominator;
- }
- // Otherwise, if supplied denominator differs from current one...
- else if (filledDenominator != denominator) {
- // scale current numerator by the supplied denominator, then...
- filledNumerator *= denominator;
-
- // the supplied numerator & denominator by current denominator.
- numerator *= filledDenominator;
- denominator *= filledDenominator;
- }
+ assembly {
+ let orderStatusSlot := orderStatus.slot
+ // Read filled amount as numerator and denominator and put on stack.
+ let filledNumerator := sload(orderStatusSlot)
+ let filledDenominator := shr(
+ OrderStatus_filledDenominator_offset,
+ filledNumerator
+ )
- // Once adjusted, if current+supplied numerator exceeds denominator:
- if (filledNumerator + numerator > denominator) {
- // Skip underflow check: denominator >= orderStatus.numerator
- unchecked {
- // Reduce current numerator so it + supplied = denominator.
- numerator = denominator - filledNumerator;
+ for {
+
+ } 1 {
+
+ } {
+ if iszero(filledDenominator) {
+ filledNumerator := numerator
+
+ break
}
- }
- // Increment the filled numerator by the new numerator.
- filledNumerator += numerator;
+ // Shift and mask to calculate the current filled numerator.
+ filledNumerator := and(
+ shr(OrderStatus_filledNumerator_offset, filledNumerator),
+ MaxUint120
+ )
+
+ // If denominator of 1 supplied, fill entire remaining amount.
+ if eq(denominator, 1) {
+ numerator := sub(filledDenominator, filledNumerator)
+ denominator := filledDenominator
+ filledNumerator := filledDenominator
+
+ break
+ }
+
+ // If supplied denominator equals to the current one:
+ if eq(denominator, filledDenominator) {
+ // Increment the filled numerator by the new numerator.
+ filledNumerator := add(numerator, filledNumerator)
+
+ // Once adjusted, if current + supplied numerator exceeds
+ // the denominator:
+ let carry := mul(
+ sub(filledNumerator, denominator),
+ gt(filledNumerator, denominator)
+ )
+
+ numerator := sub(numerator, carry)
+
+ filledNumerator := sub(filledNumerator, carry)
+
+ break
+ }
+
+ // Otherwise, if supplied denominator differs from current one:
+ filledNumerator := mul(filledNumerator, denominator)
+ numerator := mul(numerator, filledDenominator)
+ denominator := mul(denominator, filledDenominator)
+
+ // Increment the filled numerator by the new numerator.
+ filledNumerator := add(numerator, filledNumerator)
+
+ // Once adjusted, if current + supplied numerator exceeds
+ // denominator:
+ let carry := mul(
+ sub(filledNumerator, denominator),
+ gt(filledNumerator, denominator)
+ )
+
+ numerator := sub(numerator, carry)
+
+ filledNumerator := sub(filledNumerator, carry)
- // Use assembly to ensure fractional amounts are below max uint120.
- assembly {
// Check filledNumerator and denominator for uint120 overflow.
if or(
gt(filledNumerator, MaxUint120),
@@ -263,34 +361,305 @@ contract OrderValidator is Executor, ZoneInteraction {
gt(denominator, MaxUint120)
) {
// Store the Panic error signature.
- mstore(0, Panic_error_signature)
+ mstore(0, Panic_error_selector)
+ // Store the arithmetic (0x11) panic code.
+ mstore(Panic_error_code_ptr, Panic_arithmetic)
+
+ // revert(abi.encodeWithSignature(
+ // "Panic(uint256)", 0x11
+ // ))
+ revert(Error_selector_offset, Panic_error_length)
+ }
+ }
- // Set arithmetic (0x11) panic code as initial argument.
- mstore(Panic_error_offset, Panic_arithmetic)
+ break
+ }
- // Return, supplying Panic signature & arithmetic code.
- revert(0, Panic_error_length)
- }
+ // Update order status and fill amount, packing struct values.
+ // [denominator: 15 bytes] [numerator: 15 bytes]
+ // [isCancelled: 1 byte] [isValidated: 1 byte]
+ sstore(
+ orderStatusSlot,
+ or(
+ OrderStatus_ValidatedAndNotCancelled,
+ or(
+ shl(
+ OrderStatus_filledNumerator_offset,
+ filledNumerator
+ ),
+ shl(OrderStatus_filledDenominator_offset, denominator)
+ )
+ )
+ )
+ }
+ }
+
+ /**
+ * @dev Internal pure function to check the compatibility of two offer
+ * or consideration items for contract orders. Note that the itemType
+ * and identifier are reset in cases where criteria = 0 (collection-
+ * wide offers), which means that a contract offerer has full latitude
+ * to choose any identifier it wants mid-flight, in contrast to the
+ * normal behavior, where the fulfiller can pick which identifier to
+ * receive by providing a CriteriaResolver.
+ *
+ * @param originalItem The original offer or consideration item.
+ * @param newItem The new offer or consideration item.
+ *
+ * @return isInvalid Error buffer indicating if items are incompatible.
+ */
+ function _compareItems(
+ MemoryPointer originalItem,
+ MemoryPointer newItem
+ ) internal pure returns (uint256 isInvalid) {
+ assembly {
+ let itemType := mload(originalItem)
+ let identifier := mload(add(originalItem, Common_identifier_offset))
+
+ // Set returned identifier for criteria-based items w/ criteria = 0.
+ if and(gt(itemType, 3), iszero(identifier)) {
+ // replace item type
+ itemType := sub(3, eq(itemType, 4))
+ identifier := mload(add(newItem, Common_identifier_offset))
+ }
+
+ let originalAmount := mload(add(originalItem, Common_amount_offset))
+ let newAmount := mload(add(newItem, Common_amount_offset))
+
+ isInvalid := iszero(
+ and(
+ // originalItem.token == newItem.token &&
+ // originalItem.itemType == newItem.itemType
+ and(
+ eq(
+ mload(add(originalItem, Common_token_offset)),
+ mload(add(newItem, Common_token_offset))
+ ),
+ eq(itemType, mload(newItem))
+ ),
+ // originalItem.identifier == newItem.identifier &&
+ // originalItem.startAmount == originalItem.endAmount
+ and(
+ eq(
+ identifier,
+ mload(add(newItem, Common_identifier_offset))
+ ),
+ eq(
+ originalAmount,
+ mload(add(originalItem, Common_endAmount_offset))
+ )
+ )
+ )
+ )
+ }
+ }
+
+ /**
+ * @dev Internal pure function to check the compatibility of two recipients
+ * on consideration items for contract orders. This check is skipped if
+ * no recipient is originally supplied.
+ *
+ * @param originalRecipient The original consideration item recipient.
+ * @param newRecipient The new consideration item recipient.
+ *
+ * @return isInvalid Error buffer indicating if recipients are incompatible.
+ */
+ function _checkRecipients(
+ address originalRecipient,
+ address newRecipient
+ ) internal pure returns (uint256 isInvalid) {
+ assembly {
+ isInvalid := iszero(
+ or(
+ iszero(originalRecipient),
+ eq(newRecipient, originalRecipient)
+ )
+ )
+ }
+ }
+
+ /**
+ * @dev Internal function to generate a contract order. When a
+ * collection-wide criteria-based item (criteria = 0) is provided as an
+ * input to a contract order, the contract offerer has full latitude to
+ * choose any identifier it wants mid-flight, which differs from the
+ * usual behavior. For regular criteria-based orders with
+ * identifierOrCriteria = 0, the fulfiller can pick which identifier to
+ * receive by providing a CriteriaResolver. For contract offers with
+ * identifierOrCriteria = 0, Seaport does not expect a corresponding
+ * CriteriaResolver, and will revert if one is provided.
+ *
+ * @param orderParameters The parameters for the order.
+ * @param context The context for generating the order.
+ * @param revertOnInvalid Whether to revert on invalid input.
+ *
+ * @return orderHash The order hash.
+ * @return numerator The numerator.
+ * @return denominator The denominator.
+ */
+ function _getGeneratedOrder(
+ OrderParameters memory orderParameters,
+ bytes memory context,
+ bool revertOnInvalid
+ )
+ internal
+ returns (bytes32 orderHash, uint256 numerator, uint256 denominator)
+ {
+ // Ensure that consideration array length is equal to the total original
+ // consideration items value.
+ if (
+ orderParameters.consideration.length !=
+ orderParameters.totalOriginalConsiderationItems
+ ) {
+ _revertConsiderationLengthNotEqualToTotalOriginal();
+ }
+
+ {
+ address offerer = orderParameters.offerer;
+ bool success;
+ (MemoryPointer cdPtr, uint256 size) = _encodeGenerateOrder(
+ orderParameters,
+ context
+ );
+ assembly {
+ success := call(gas(), offerer, 0, cdPtr, size, 0, 0)
+ }
+
+ {
+ // Note: overflow impossible; nonce can't increment that high.
+ uint256 contractNonce;
+ unchecked {
+ // Note: nonce will be incremented even for skipped orders,
+ // and even if generateOrder's return data does not satisfy
+ // all the constraints. This is the case when errorBuffer
+ // != 0 and revertOnInvalid == false.
+ contractNonce = _contractNonces[offerer]++;
+ }
+
+ assembly {
+ // Shift offerer address up 96 bytes and combine with nonce.
+ orderHash := xor(
+ contractNonce,
+ shl(ContractOrder_orderHash_offerer_shift, offerer)
+ )
}
}
- // Skip overflow check: checked above unless numerator is reduced.
- unchecked {
- // Update order status and fill amount, packing struct values.
- orderStatus.isValidated = true;
- orderStatus.isCancelled = false;
- orderStatus.numerator = uint120(filledNumerator);
- orderStatus.denominator = uint120(denominator);
+
+ // Revert or skip if the call to generate the contract order failed.
+ if (!success) {
+ return _revertOrReturnEmpty(revertOnInvalid, orderHash);
}
- } else {
- // Update order status and fill amount, packing struct values.
- orderStatus.isValidated = true;
- orderStatus.isCancelled = false;
- orderStatus.numerator = uint120(numerator);
- orderStatus.denominator = uint120(denominator);
}
- // Return order hash, a modified numerator, and a modified denominator.
- return (orderHash, numerator, denominator);
+ // Decode the returned contract order and/or update the error buffer.
+ (
+ uint256 errorBuffer,
+ OfferItem[] memory offer,
+ ConsiderationItem[] memory consideration
+ ) = _convertGetGeneratedOrderResult(_decodeGenerateOrderReturndata)();
+
+ // Revert or skip if the returndata could not be decoded correctly.
+ if (errorBuffer != 0) {
+ return _revertOrReturnEmpty(revertOnInvalid, orderHash);
+ }
+
+ {
+ // Designate lengths.
+ uint256 originalOfferLength = orderParameters.offer.length;
+ uint256 newOfferLength = offer.length;
+
+ // Explicitly specified offer items cannot be removed.
+ if (originalOfferLength > newOfferLength) {
+ return _revertOrReturnEmpty(revertOnInvalid, orderHash);
+ }
+
+ // Iterate over each specified offer (e.g. minimumReceived) item.
+ for (uint256 i = 0; i < originalOfferLength; ) {
+ // Retrieve the pointer to the originally supplied item.
+ MemoryPointer mPtrOriginal = orderParameters
+ .offer[i]
+ .toMemoryPointer();
+
+ // Retrieve the pointer to the newly returned item.
+ MemoryPointer mPtrNew = offer[i].toMemoryPointer();
+
+ // Compare the items and update the error buffer accordingly.
+ errorBuffer |=
+ _cast(
+ mPtrOriginal
+ .offset(Common_amount_offset)
+ .readUint256() >
+ mPtrNew.offset(Common_amount_offset).readUint256()
+ ) |
+ _compareItems(mPtrOriginal, mPtrNew);
+
+ // Increment the array (cannot overflow as index starts at 0).
+ unchecked {
+ ++i;
+ }
+ }
+
+ // Assign the returned offer item in place of the original item.
+ orderParameters.offer = offer;
+ }
+
+ {
+ // Designate lengths & memory locations.
+ ConsiderationItem[] memory originalConsiderationArray = (
+ orderParameters.consideration
+ );
+ uint256 newConsiderationLength = consideration.length;
+
+ // New consideration items cannot be created.
+ if (newConsiderationLength > originalConsiderationArray.length) {
+ return _revertOrReturnEmpty(revertOnInvalid, orderHash);
+ }
+
+ // Iterate over returned consideration & do not exceed maximumSpent.
+ for (uint256 i = 0; i < newConsiderationLength; ) {
+ // Retrieve the pointer to the originally supplied item.
+ MemoryPointer mPtrOriginal = originalConsiderationArray[i]
+ .toMemoryPointer();
+
+ // Retrieve the pointer to the newly returned item.
+ MemoryPointer mPtrNew = consideration[i].toMemoryPointer();
+
+ // Compare the items and update the error buffer accordingly
+ // and ensure that the recipients are equal when provided.
+ errorBuffer |=
+ _cast(
+ mPtrNew.offset(Common_amount_offset).readUint256() >
+ mPtrOriginal
+ .offset(Common_amount_offset)
+ .readUint256()
+ ) |
+ _compareItems(mPtrOriginal, mPtrNew) |
+ _checkRecipients(
+ mPtrOriginal
+ .offset(ConsiderItem_recipient_offset)
+ .readAddress(),
+ mPtrNew
+ .offset(ConsiderItem_recipient_offset)
+ .readAddress()
+ );
+
+ // Increment the array (cannot overflow as index starts at 0).
+ unchecked {
+ ++i;
+ }
+ }
+
+ // Assign returned consideration item in place of the original item.
+ orderParameters.consideration = consideration;
+ }
+
+ // Revert or skip if any item comparison failed.
+ if (errorBuffer != 0) {
+ return _revertOrReturnEmpty(revertOnInvalid, orderHash);
+ }
+
+ // Return order hash and full fill amount (numerator & denominator = 1).
+ return (orderHash, 1, 1);
}
/**
@@ -298,23 +667,24 @@ contract OrderValidator is Executor, ZoneInteraction {
* only the offerer or the zone of a given order may cancel it. Callers
* should ensure that the intended order was cancelled by calling
* `getOrderStatus` and confirming that `isCancelled` returns `true`.
+ * Also note that contract orders are not cancellable.
*
* @param orders The orders to cancel.
*
* @return cancelled A boolean indicating whether the supplied orders were
* successfully cancelled.
*/
- function _cancel(OrderComponents[] calldata orders)
- internal
- returns (bool cancelled)
- {
+ function _cancel(
+ OrderComponents[] calldata orders
+ ) internal returns (bool cancelled) {
// Ensure that the reentrancy guard is not currently set.
_assertNonReentrant();
// Declare variables outside of the loop.
OrderStatus storage orderStatus;
- address offerer;
- address zone;
+
+ // Declare a variable for tracking invariants in the loop.
+ bool anyInvalidCallerOrContractOrder;
// Skip overflow check as for loop is indexed starting at zero.
unchecked {
@@ -326,29 +696,30 @@ contract OrderValidator is Executor, ZoneInteraction {
// Retrieve the order.
OrderComponents calldata order = orders[i];
- offerer = order.offerer;
- zone = order.zone;
-
- // Ensure caller is either offerer or zone of the order.
- if (msg.sender != offerer && msg.sender != zone) {
- revert InvalidCanceller();
+ address offerer = order.offerer;
+ address zone = order.zone;
+ OrderType orderType = order.orderType;
+
+ assembly {
+ // If caller is neither the offerer nor zone, or a contract
+ // order is present, flag anyInvalidCallerOrContractOrder.
+ anyInvalidCallerOrContractOrder := or(
+ anyInvalidCallerOrContractOrder,
+ // orderType == CONTRACT ||
+ // !(caller == offerer || caller == zone)
+ or(
+ eq(orderType, 4),
+ iszero(
+ or(eq(caller(), offerer), eq(caller(), zone))
+ )
+ )
+ )
}
- // Derive order hash using the order parameters and the counter.
bytes32 orderHash = _deriveOrderHash(
- OrderParameters(
- offerer,
- zone,
- order.offer,
- order.consideration,
- order.orderType,
- order.startTime,
- order.endTime,
- order.zoneHash,
- order.salt,
- order.conduitKey,
- order.consideration.length
- ),
+ _toOrderParametersReturnType(
+ _decodeOrderComponentsAsOrderParameters
+ )(order.toCalldataPointer()),
order.counter
);
@@ -367,6 +738,10 @@ contract OrderValidator is Executor, ZoneInteraction {
}
}
+ if (anyInvalidCallerOrContractOrder) {
+ _revertCannotCancelOrder();
+ }
+
// Return a boolean indicating that orders were successfully cancelled.
cancelled = true;
}
@@ -386,10 +761,9 @@ contract OrderValidator is Executor, ZoneInteraction {
* @return validated A boolean indicating whether the supplied orders were
* successfully validated.
*/
- function _validate(Order[] calldata orders)
- internal
- returns (bool validated)
- {
+ function _validate(
+ Order[] memory orders
+ ) internal returns (bool validated) {
// Ensure that the reentrancy guard is not currently set.
_assertNonReentrant();
@@ -404,12 +778,17 @@ contract OrderValidator is Executor, ZoneInteraction {
uint256 totalOrders = orders.length;
// Iterate over each order.
- for (uint256 i = 0; i < totalOrders; ) {
+ for (uint256 i = 0; i < totalOrders; ++i) {
// Retrieve the order.
- Order calldata order = orders[i];
+ Order memory order = orders[i];
// Retrieve the order parameters.
- OrderParameters calldata orderParameters = order.parameters;
+ OrderParameters memory orderParameters = order.parameters;
+
+ // Skip contract orders.
+ if (orderParameters.orderType == OrderType.CONTRACT) {
+ continue;
+ }
// Move offerer from memory to the stack.
offerer = orderParameters.offerer;
@@ -432,6 +811,15 @@ contract OrderValidator is Executor, ZoneInteraction {
// If the order has not already been validated...
if (!orderStatus.isValidated) {
+ // Ensure that consideration array length is equal to the
+ // total original consideration items value.
+ if (
+ orderParameters.consideration.length !=
+ orderParameters.totalOriginalConsiderationItems
+ ) {
+ _revertConsiderationLengthNotEqualToTotalOriginal();
+ }
+
// Verify the supplied signature.
_verifySignature(offerer, orderHash, order.signature);
@@ -439,15 +827,8 @@ contract OrderValidator is Executor, ZoneInteraction {
orderStatus.isValidated = true;
// Emit an event signifying the order has been validated.
- emit OrderValidated(
- orderHash,
- offerer,
- orderParameters.zone
- );
+ emit OrderValidated(orderHash, orderParameters);
}
-
- // Increment counter inside body of the loop for gas efficiency.
- ++i;
}
}
@@ -472,7 +853,9 @@ contract OrderValidator is Executor, ZoneInteraction {
* @return totalSize The total size of the order that is either filled or
* unfilled (i.e. the "denominator").
*/
- function _getOrderStatus(bytes32 orderHash)
+ function _getOrderStatus(
+ bytes32 orderHash
+ )
internal
view
returns (
@@ -494,26 +877,58 @@ contract OrderValidator is Executor, ZoneInteraction {
);
}
+ /**
+ * @dev Internal pure function to either revert or return an empty tuple
+ * depending on the value of `revertOnInvalid`.
+ *
+ * @param revertOnInvalid Whether to revert on invalid input.
+ * @param contractOrderHash The contract order hash.
+ *
+ * @return orderHash The order hash.
+ * @return numerator The numerator.
+ * @return denominator The denominator.
+ */
+ function _revertOrReturnEmpty(
+ bool revertOnInvalid,
+ bytes32 contractOrderHash
+ )
+ internal
+ pure
+ returns (bytes32 orderHash, uint256 numerator, uint256 denominator)
+ {
+ if (revertOnInvalid) {
+ _revertInvalidContractOrder(contractOrderHash);
+ }
+
+ return (contractOrderHash, 0, 0);
+ }
+
/**
* @dev Internal pure function to check whether a given order type indicates
* that partial fills are not supported (e.g. only "full fills" are
* allowed for the order in question).
*
- * @param orderType The order type in question.
+ * @param orderType The order type in question.
+ * @param numerator The numerator in question.
+ * @param denominator The denominator in question.
*
* @return isFullOrder A boolean indicating whether the order type only
* supports full fills.
*/
- function _doesNotSupportPartialFills(OrderType orderType)
- internal
- pure
- returns (bool isFullOrder)
- {
+ function _doesNotSupportPartialFills(
+ OrderType orderType,
+ uint256 numerator,
+ uint256 denominator
+ ) internal pure returns (bool isFullOrder) {
// The "full" order types are even, while "partial" order types are odd.
- // Bitwise and by 1 is equivalent to modulo by 2, but 2 gas cheaper.
+ // Bitwise and by 1 is equivalent to modulo by 2, but 2 gas cheaper. The
+ // check is only necessary if numerator is less than denominator.
assembly {
// Equivalent to `uint256(orderType) & 1 == 0`.
- isFullOrder := iszero(and(orderType, 1))
+ isFullOrder := and(
+ lt(numerator, denominator),
+ iszero(and(orderType, 1))
+ )
}
}
}
diff --git a/contracts/lib/ReentrancyGuard.sol b/contracts/lib/ReentrancyGuard.sol
index 69169fe0a..35aca6ad2 100644
--- a/contracts/lib/ReentrancyGuard.sol
+++ b/contracts/lib/ReentrancyGuard.sol
@@ -1,9 +1,20 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
import { ReentrancyErrors } from "../interfaces/ReentrancyErrors.sol";
-import "./ConsiderationConstants.sol";
+import { LowLevelHelpers } from "./LowLevelHelpers.sol";
+
+import {
+ _revertInvalidMsgValue,
+ _revertNoReentrantCalls
+} from "./ConsiderationErrors.sol";
+
+import {
+ _ENTERED_AND_ACCEPTING_NATIVE_TOKENS,
+ _ENTERED,
+ _NOT_ENTERED
+} from "./ConsiderationConstants.sol";
/**
* @title ReentrancyGuard
@@ -11,7 +22,7 @@ import "./ConsiderationConstants.sol";
* @notice ReentrancyGuard contains a storage variable and related functionality
* for protecting against reentrancy.
*/
-contract ReentrancyGuard is ReentrancyErrors {
+contract ReentrancyGuard is ReentrancyErrors, LowLevelHelpers {
// Prevent reentrant calls on protected functions.
uint256 private _reentrancyGuard;
@@ -24,16 +35,25 @@ contract ReentrancyGuard is ReentrancyErrors {
}
/**
- * @dev Internal function to ensure that the sentinel value for the
- * reentrancy guard is not currently set and, if not, to set the
- * sentinel value for the reentrancy guard.
+ * @dev Internal function to ensure that a sentinel value for the reentrancy
+ * guard is not currently set and, if not, to set a sentinel value for
+ * the reentrancy guard based on whether or not native tokens may be
+ * received during execution or not.
+ *
+ * @param acceptNativeTokens A boolean indicating whether native tokens may
+ * be received during execution or not.
*/
- function _setReentrancyGuard() internal {
+ function _setReentrancyGuard(bool acceptNativeTokens) internal {
// Ensure that the reentrancy guard is not already set.
_assertNonReentrant();
- // Set the reentrancy guard.
- _reentrancyGuard = _ENTERED;
+ // Set the reentrancy guard. A value of 2 indicates that native tokens
+ // may not be accepted during execution, whereas a value of 3 indicates
+ // that they will be accepted (with any remaining native tokens returned
+ // to the caller).
+ unchecked {
+ _reentrancyGuard = _ENTERED + _cast(acceptNativeTokens);
+ }
}
/**
@@ -45,13 +65,24 @@ contract ReentrancyGuard is ReentrancyErrors {
}
/**
- * @dev Internal view function to ensure that the sentinel value for the
+ * @dev Internal view function to ensure that a sentinel value for the
reentrancy guard is not currently set.
*/
function _assertNonReentrant() internal view {
// Ensure that the reentrancy guard is not currently set.
if (_reentrancyGuard != _NOT_ENTERED) {
- revert NoReentrantCalls();
+ _revertNoReentrantCalls();
+ }
+ }
+
+ /**
+ * @dev Internal view function to ensure that the sentinel value indicating
+ * native tokens may be received during execution is currently set.
+ */
+ function _assertAcceptingNativeTokens() internal view {
+ // Ensure that the reentrancy guard is not currently set.
+ if (_reentrancyGuard != _ENTERED_AND_ACCEPTING_NATIVE_TOKENS) {
+ _revertInvalidMsgValue(msg.value);
}
}
}
diff --git a/contracts/lib/SignatureVerification.sol b/contracts/lib/SignatureVerification.sol
index 023d3c25f..a30ab656c 100644
--- a/contracts/lib/SignatureVerification.sol
+++ b/contracts/lib/SignatureVerification.sol
@@ -1,16 +1,42 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
-import { EIP1271Interface } from "../interfaces/EIP1271Interface.sol";
-
-// prettier-ignore
import {
SignatureVerificationErrors
} from "../interfaces/SignatureVerificationErrors.sol";
import { LowLevelHelpers } from "./LowLevelHelpers.sol";
-import "./ConsiderationConstants.sol";
+import {
+ ECDSA_MaxLength,
+ ECDSA_signature_s_offset,
+ ECDSA_signature_v_offset,
+ ECDSA_twentySeventhAndTwentyEighthBytesSet,
+ Ecrecover_args_size,
+ Ecrecover_precompile,
+ EIP1271_isValidSignature_calldata_baseLength,
+ EIP1271_isValidSignature_digest_negativeOffset,
+ EIP1271_isValidSignature_selector_negativeOffset,
+ EIP1271_isValidSignature_selector,
+ EIP1271_isValidSignature_signature_head_offset,
+ EIP2098_allButHighestBitMask,
+ MaxUint8,
+ OneWord,
+ Signature_lower_v
+} from "./ConsiderationConstants.sol";
+
+import {
+ BadContractSignature_error_length,
+ BadContractSignature_error_selector,
+ BadSignatureV_error_length,
+ BadSignatureV_error_selector,
+ BadSignatureV_error_v_ptr,
+ Error_selector_offset,
+ InvalidSignature_error_length,
+ InvalidSignature_error_selector,
+ InvalidSigner_error_length,
+ InvalidSigner_error_selector
+} from "./ConsiderationErrorConstants.sol";
/**
* @title SignatureVerification
@@ -24,14 +50,19 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers {
* is not 64 or 65 bytes or if the recovered signer does not match the
* supplied signer.
*
- * @param signer The signer for the order.
- * @param digest The digest to verify the signature against.
- * @param signature A signature from the signer indicating that the order
- * has been approved.
+ * @param signer The signer for the order.
+ * @param digest The digest to verify signature against.
+ * @param originalDigest The original digest to verify signature
+ * against.
+ * @param originalSignatureLength The original signature length.
+ * @param signature A signature from the signer indicating
+ * that the order has been approved.
*/
function _assertValidSignature(
address signer,
bytes32 digest,
+ bytes32 originalDigest,
+ uint256 originalSignatureLength,
bytes memory signature
) internal view {
// Declare value for ecrecover equality or 1271 call success status.
@@ -42,9 +73,6 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers {
// Ensure that first word of scratch space is empty.
mstore(0, 0)
- // Declare value for v signature parameter.
- let v
-
// Get the length of the signature.
let signatureLength := mload(signature)
@@ -79,7 +107,7 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers {
// signature is 65 bytes, this will be the real `v` value.
// If not, it will need to be modified - doing it this way
// saves an extra condition.
- v := byte(
+ let v := byte(
0,
mload(add(signature, ECDSA_signature_v_offset))
)
@@ -151,6 +179,9 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers {
// If the signature was not verified with ecrecover, try EIP1271.
if iszero(success) {
+ // Reset the original signature length.
+ mstore(signature, originalSignatureLength)
+
// Temporarily overwrite the word before the signature length
// and use it as the head of the signature input to
// `isValidSignature`, which has a value of 64.
@@ -168,20 +199,25 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers {
// Cache the value currently stored at the selector pointer.
let cachedWordOverwrittenBySelector := mload(selectorPtr)
- // Get pointer to use for `digest` input to `isValidSignature`.
- let digestPtr := sub(
- signature,
- EIP1271_isValidSignature_digest_negativeOffset
- )
-
// Cache the value currently stored at the digest pointer.
- let cachedWordOverwrittenByDigest := mload(digestPtr)
+ let cachedWordOverwrittenByDigest := mload(
+ sub(
+ signature,
+ EIP1271_isValidSignature_digest_negativeOffset
+ )
+ )
// Write the selector first, since it overlaps the digest.
mstore(selectorPtr, EIP1271_isValidSignature_selector)
- // Next, write the digest.
- mstore(digestPtr, digest)
+ // Next, write the original digest.
+ mstore(
+ sub(
+ signature,
+ EIP1271_isValidSignature_digest_negativeOffset
+ ),
+ originalDigest
+ )
// Call signer with `isValidSignature` to validate signature.
success := staticcall(
@@ -189,7 +225,7 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers {
signer,
selectorPtr,
add(
- signatureLength,
+ originalSignatureLength,
EIP1271_isValidSignature_calldata_baseLength
),
0,
@@ -204,30 +240,82 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers {
// Revert with bad 1271 signature if signer has code.
if extcodesize(signer) {
// Bad contract signature.
- mstore(0, BadContractSignature_error_signature)
- revert(0, BadContractSignature_error_length)
+ // Store left-padded selector with push4, mem[28:32]
+ mstore(0, BadContractSignature_error_selector)
+
+ // revert(abi.encodeWithSignature(
+ // "BadContractSignature()"
+ // ))
+ revert(
+ Error_selector_offset,
+ BadContractSignature_error_length
+ )
}
// Check if signature length was invalid.
if gt(sub(ECDSA_MaxLength, signatureLength), 1) {
// Revert with generic invalid signature error.
- mstore(0, InvalidSignature_error_signature)
- revert(0, InvalidSignature_error_length)
+ // Store left-padded selector with push4, mem[28:32]
+ mstore(0, InvalidSignature_error_selector)
+
+ // revert(abi.encodeWithSignature(
+ // "InvalidSignature()"
+ // ))
+ revert(
+ Error_selector_offset,
+ InvalidSignature_error_length
+ )
}
// Check if v was invalid.
- if iszero(
- byte(v, ECDSA_twentySeventhAndTwentyEighthBytesSet)
+ if and(
+ eq(signatureLength, ECDSA_MaxLength),
+ iszero(
+ byte(
+ byte(
+ 0,
+ mload(
+ add(
+ signature,
+ ECDSA_signature_v_offset
+ )
+ )
+ ),
+ ECDSA_twentySeventhAndTwentyEighthBytesSet
+ )
+ )
) {
// Revert with invalid v value.
- mstore(0, BadSignatureV_error_signature)
- mstore(BadSignatureV_error_offset, v)
- revert(0, BadSignatureV_error_length)
+ // Store left-padded selector with push4, mem[28:32]
+ mstore(0, BadSignatureV_error_selector)
+ mstore(
+ BadSignatureV_error_v_ptr,
+ byte(
+ 0,
+ mload(
+ add(signature, ECDSA_signature_v_offset)
+ )
+ )
+ )
+
+ // revert(abi.encodeWithSignature(
+ // "BadSignatureV(uint8)", v
+ // ))
+ revert(
+ Error_selector_offset,
+ BadSignatureV_error_length
+ )
}
// Revert with generic invalid signer error message.
- mstore(0, InvalidSigner_error_signature)
- revert(0, InvalidSigner_error_length)
+ // Store left-padded selector with push4, mem[28:32]
+ mstore(0, InvalidSigner_error_selector)
+
+ // revert(abi.encodeWithSignature("InvalidSigner()"))
+ revert(
+ Error_selector_offset,
+ InvalidSigner_error_length
+ )
}
}
@@ -235,7 +323,13 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers {
// signature head.
mstore(wordBeforeSignaturePtr, cachedWordBeforeSignature)
mstore(selectorPtr, cachedWordOverwrittenBySelector)
- mstore(digestPtr, cachedWordOverwrittenByDigest)
+ mstore(
+ sub(
+ signature,
+ EIP1271_isValidSignature_digest_negativeOffset
+ ),
+ cachedWordOverwrittenByDigest
+ )
}
}
@@ -246,8 +340,10 @@ contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers {
// Otherwise, revert with error indicating bad contract signature.
assembly {
- mstore(0, BadContractSignature_error_signature)
- revert(0, BadContractSignature_error_length)
+ // Store left-padded selector with push4, mem[28:32] = selector
+ mstore(0, BadContractSignature_error_selector)
+ // revert(abi.encodeWithSignature("BadContractSignature()"))
+ revert(Error_selector_offset, BadContractSignature_error_length)
}
}
}
diff --git a/contracts/lib/TokenTransferrer.sol b/contracts/lib/TokenTransferrer.sol
index 197a28355..05f1e7a0b 100644
--- a/contracts/lib/TokenTransferrer.sol
+++ b/contracts/lib/TokenTransferrer.sol
@@ -1,9 +1,84 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
-import "./TokenTransferrerConstants.sol";
+import {
+ BadReturnValueFromERC20OnTransfer_error_amount_ptr,
+ BadReturnValueFromERC20OnTransfer_error_from_ptr,
+ BadReturnValueFromERC20OnTransfer_error_length,
+ BadReturnValueFromERC20OnTransfer_error_selector,
+ BadReturnValueFromERC20OnTransfer_error_to_ptr,
+ BadReturnValueFromERC20OnTransfer_error_token_ptr,
+ BatchTransfer1155Params_amounts_head_ptr,
+ BatchTransfer1155Params_calldata_baseSize,
+ BatchTransfer1155Params_data_head_ptr,
+ BatchTransfer1155Params_data_length_basePtr,
+ BatchTransfer1155Params_ids_head_ptr,
+ BatchTransfer1155Params_ids_length_offset,
+ BatchTransfer1155Params_ids_length_ptr,
+ BatchTransfer1155Params_ptr,
+ ConduitBatch1155Transfer_amounts_length_baseOffset,
+ ConduitBatch1155Transfer_from_offset,
+ ConduitBatch1155Transfer_ids_head_offset,
+ ConduitBatch1155Transfer_ids_length_offset,
+ ConduitBatch1155Transfer_usable_head_size,
+ ConduitBatchTransfer_amounts_head_offset,
+ CostPerWord,
+ DefaultFreeMemoryPointer,
+ ERC1155_safeBatchTransferFrom_signature,
+ ERC1155_safeTransferFrom_amount_ptr,
+ ERC1155_safeTransferFrom_data_length_offset,
+ ERC1155_safeTransferFrom_data_length_ptr,
+ ERC1155_safeTransferFrom_data_offset_ptr,
+ ERC1155_safeTransferFrom_from_ptr,
+ ERC1155_safeTransferFrom_id_ptr,
+ ERC1155_safeTransferFrom_length,
+ ERC1155_safeTransferFrom_sig_ptr,
+ ERC1155_safeTransferFrom_signature,
+ ERC1155_safeTransferFrom_to_ptr,
+ ERC1155BatchTransferGenericFailure_error_signature,
+ ERC1155BatchTransferGenericFailure_ids_offset,
+ ERC1155BatchTransferGenericFailure_token_ptr,
+ ERC20_transferFrom_amount_ptr,
+ ERC20_transferFrom_from_ptr,
+ ERC20_transferFrom_length,
+ ERC20_transferFrom_sig_ptr,
+ ERC20_transferFrom_signature,
+ ERC20_transferFrom_to_ptr,
+ ERC721_transferFrom_from_ptr,
+ ERC721_transferFrom_id_ptr,
+ ERC721_transferFrom_length,
+ ERC721_transferFrom_sig_ptr,
+ ERC721_transferFrom_signature,
+ ERC721_transferFrom_to_ptr,
+ ExtraGasBuffer,
+ FreeMemoryPointerSlot,
+ Generic_error_selector_offset,
+ Invalid1155BatchTransferEncoding_length,
+ Invalid1155BatchTransferEncoding_ptr,
+ Invalid1155BatchTransferEncoding_selector,
+ MemoryExpansionCoefficientShift,
+ NoContract_error_account_ptr,
+ NoContract_error_length,
+ NoContract_error_selector,
+ OneWord,
+ OneWordShift,
+ Slot0x80,
+ Slot0xA0,
+ Slot0xC0,
+ ThirtyOneBytes,
+ TokenTransferGenericFailure_err_identifier_ptr,
+ TokenTransferGenericFailure_error_amount_ptr,
+ TokenTransferGenericFailure_error_from_ptr,
+ TokenTransferGenericFailure_error_identifier_ptr,
+ TokenTransferGenericFailure_error_length,
+ TokenTransferGenericFailure_error_selector,
+ TokenTransferGenericFailure_error_to_ptr,
+ TokenTransferGenericFailure_error_token_ptr,
+ TwoWords,
+ TwoWordsShift,
+ ZeroSlot
+} from "./TokenTransferrerConstants.sol";
-// prettier-ignore
import {
TokenTransferrerErrors
} from "../interfaces/TokenTransferrerErrors.sol";
@@ -100,16 +175,16 @@ contract TokenTransferrer is TokenTransferrerErrors {
// necessary. Start by computing the word size
// of returndata and allocated memory. Round up
// to the nearest full word.
- let returnDataWords := div(
- add(returndatasize(), AlmostOneWord),
- OneWord
+ let returnDataWords := shr(
+ OneWordShift,
+ add(returndatasize(), ThirtyOneBytes)
)
// Note: use the free memory pointer in place of
// msize() to work around a Yul warning that
// prevents accessing msize directly when the IR
// pipeline is activated.
- let msizeWords := div(memPointer, OneWord)
+ let msizeWords := shr(OneWordShift, memPointer)
// Next, compute the cost of the returndatacopy.
let cost := mul(CostPerWord, returnDataWords)
@@ -126,15 +201,15 @@ contract TokenTransferrer is TokenTransferrerErrors {
),
CostPerWord
),
- div(
+ shr(
+ MemoryExpansionCoefficientShift,
sub(
mul(
returnDataWords,
returnDataWords
),
mul(msizeWords, msizeWords)
- ),
- MemoryExpansionCoefficient
+ )
)
)
)
@@ -154,10 +229,10 @@ contract TokenTransferrer is TokenTransferrerErrors {
}
}
- // Otherwise revert with a generic error message.
+ // Store left-padded selector with push4, mem[28:32]
mstore(
- TokenTransferGenericFailure_error_sig_ptr,
- TokenTransferGenericFailure_error_signature
+ 0,
+ TokenTransferGenericFailure_error_selector
)
mstore(
TokenTransferGenericFailure_error_token_ptr,
@@ -168,22 +243,33 @@ contract TokenTransferrer is TokenTransferrerErrors {
from
)
mstore(TokenTransferGenericFailure_error_to_ptr, to)
- mstore(TokenTransferGenericFailure_error_id_ptr, 0)
+ mstore(
+ TokenTransferGenericFailure_err_identifier_ptr,
+ 0
+ )
mstore(
TokenTransferGenericFailure_error_amount_ptr,
amount
)
+
+ // revert(abi.encodeWithSignature(
+ // "TokenTransferGenericFailure(
+ // address,address,address,uint256,uint256
+ // )", token, from, to, identifier, amount
+ // ))
revert(
- TokenTransferGenericFailure_error_sig_ptr,
+ Generic_error_selector_offset,
TokenTransferGenericFailure_error_length
)
}
// Otherwise revert with a message about the token
// returning false or non-compliant return values.
+
+ // Store left-padded selector with push4, mem[28:32]
mstore(
- BadReturnValueFromERC20OnTransfer_error_sig_ptr,
- BadReturnValueFromERC20OnTransfer_error_signature
+ 0,
+ BadReturnValueFromERC20OnTransfer_error_selector
)
mstore(
BadReturnValueFromERC20OnTransfer_error_token_ptr,
@@ -201,16 +287,30 @@ contract TokenTransferrer is TokenTransferrerErrors {
BadReturnValueFromERC20OnTransfer_error_amount_ptr,
amount
)
+
+ // revert(abi.encodeWithSignature(
+ // "BadReturnValueFromERC20OnTransfer(
+ // address,address,address,uint256
+ // )", token, from, to, amount
+ // ))
revert(
- BadReturnValueFromERC20OnTransfer_error_sig_ptr,
+ Generic_error_selector_offset,
BadReturnValueFromERC20OnTransfer_error_length
)
}
// Otherwise, revert with error about token not having code:
- mstore(NoContract_error_sig_ptr, NoContract_error_signature)
- mstore(NoContract_error_token_ptr, token)
- revert(NoContract_error_sig_ptr, NoContract_error_length)
+ // Store left-padded selector with push4, mem[28:32]
+ mstore(0, NoContract_error_selector)
+ mstore(NoContract_error_account_ptr, token)
+
+ // revert(abi.encodeWithSignature(
+ // "NoContract(address)", account
+ // ))
+ revert(
+ Generic_error_selector_offset,
+ NoContract_error_length
+ )
}
// Otherwise, the token just returned no data despite the call
@@ -248,9 +348,14 @@ contract TokenTransferrer is TokenTransferrerErrors {
assembly {
// If the token has no code, revert.
if iszero(extcodesize(token)) {
- mstore(NoContract_error_sig_ptr, NoContract_error_signature)
- mstore(NoContract_error_token_ptr, token)
- revert(NoContract_error_sig_ptr, NoContract_error_length)
+ // Store left-padded selector with push4, mem[28:32] = selector
+ mstore(0, NoContract_error_selector)
+ mstore(NoContract_error_account_ptr, token)
+
+ // revert(abi.encodeWithSignature(
+ // "NoContract(address)", account
+ // ))
+ revert(Generic_error_selector_offset, NoContract_error_length)
}
// The free memory pointer memory slot will be used when populating
@@ -283,15 +388,15 @@ contract TokenTransferrer is TokenTransferrerErrors {
// returndata while expanding memory where necessary. Start
// by computing word size of returndata & allocated memory.
// Round up to the nearest full word.
- let returnDataWords := div(
- add(returndatasize(), AlmostOneWord),
- OneWord
+ let returnDataWords := shr(
+ OneWordShift,
+ add(returndatasize(), ThirtyOneBytes)
)
// Note: use the free memory pointer in place of msize() to
// work around a Yul warning that prevents accessing msize
// directly when the IR pipeline is activated.
- let msizeWords := div(memPointer, OneWord)
+ let msizeWords := shr(OneWordShift, memPointer)
// Next, compute the cost of the returndatacopy.
let cost := mul(CostPerWord, returnDataWords)
@@ -305,12 +410,12 @@ contract TokenTransferrer is TokenTransferrerErrors {
sub(returnDataWords, msizeWords),
CostPerWord
),
- div(
+ shr(
+ MemoryExpansionCoefficientShift,
sub(
mul(returnDataWords, returnDataWords),
mul(msizeWords, msizeWords)
- ),
- MemoryExpansionCoefficient
+ )
)
)
)
@@ -329,17 +434,24 @@ contract TokenTransferrer is TokenTransferrerErrors {
}
// Otherwise revert with a generic error message.
- mstore(
- TokenTransferGenericFailure_error_sig_ptr,
- TokenTransferGenericFailure_error_signature
- )
+ // Store left-padded selector with push4, mem[28:32] = selector
+ mstore(0, TokenTransferGenericFailure_error_selector)
mstore(TokenTransferGenericFailure_error_token_ptr, token)
mstore(TokenTransferGenericFailure_error_from_ptr, from)
mstore(TokenTransferGenericFailure_error_to_ptr, to)
- mstore(TokenTransferGenericFailure_error_id_ptr, identifier)
+ mstore(
+ TokenTransferGenericFailure_error_identifier_ptr,
+ identifier
+ )
mstore(TokenTransferGenericFailure_error_amount_ptr, 1)
+
+ // revert(abi.encodeWithSignature(
+ // "TokenTransferGenericFailure(
+ // address,address,address,uint256,uint256
+ // )", token, from, to, identifier, amount
+ // ))
revert(
- TokenTransferGenericFailure_error_sig_ptr,
+ Generic_error_selector_offset,
TokenTransferGenericFailure_error_length
)
}
@@ -376,9 +488,14 @@ contract TokenTransferrer is TokenTransferrerErrors {
assembly {
// If the token has no code, revert.
if iszero(extcodesize(token)) {
- mstore(NoContract_error_sig_ptr, NoContract_error_signature)
- mstore(NoContract_error_token_ptr, token)
- revert(NoContract_error_sig_ptr, NoContract_error_length)
+ // Store left-padded selector with push4, mem[28:32] = selector
+ mstore(0, NoContract_error_selector)
+ mstore(NoContract_error_account_ptr, token)
+
+ // revert(abi.encodeWithSignature(
+ // "NoContract(address)", account
+ // ))
+ revert(Generic_error_selector_offset, NoContract_error_length)
}
// The following memory slots will be used when populating call data
@@ -423,15 +540,15 @@ contract TokenTransferrer is TokenTransferrerErrors {
// returndata while expanding memory where necessary. Start
// by computing word size of returndata & allocated memory.
// Round up to the nearest full word.
- let returnDataWords := div(
- add(returndatasize(), AlmostOneWord),
- OneWord
+ let returnDataWords := shr(
+ OneWordShift,
+ add(returndatasize(), ThirtyOneBytes)
)
// Note: use the free memory pointer in place of msize() to
// work around a Yul warning that prevents accessing msize
// directly when the IR pipeline is activated.
- let msizeWords := div(memPointer, OneWord)
+ let msizeWords := shr(OneWordShift, memPointer)
// Next, compute the cost of the returndatacopy.
let cost := mul(CostPerWord, returnDataWords)
@@ -445,12 +562,12 @@ contract TokenTransferrer is TokenTransferrerErrors {
sub(returnDataWords, msizeWords),
CostPerWord
),
- div(
+ shr(
+ MemoryExpansionCoefficientShift,
sub(
mul(returnDataWords, returnDataWords),
mul(msizeWords, msizeWords)
- ),
- MemoryExpansionCoefficient
+ )
)
)
)
@@ -469,17 +586,25 @@ contract TokenTransferrer is TokenTransferrerErrors {
}
// Otherwise revert with a generic error message.
- mstore(
- TokenTransferGenericFailure_error_sig_ptr,
- TokenTransferGenericFailure_error_signature
- )
+
+ // Store left-padded selector with push4, mem[28:32] = selector
+ mstore(0, TokenTransferGenericFailure_error_selector)
mstore(TokenTransferGenericFailure_error_token_ptr, token)
mstore(TokenTransferGenericFailure_error_from_ptr, from)
mstore(TokenTransferGenericFailure_error_to_ptr, to)
- mstore(TokenTransferGenericFailure_error_id_ptr, identifier)
+ mstore(
+ TokenTransferGenericFailure_error_identifier_ptr,
+ identifier
+ )
mstore(TokenTransferGenericFailure_error_amount_ptr, amount)
+
+ // revert(abi.encodeWithSignature(
+ // "TokenTransferGenericFailure(
+ // address,address,address,uint256,uint256
+ // )", token, from, to, identifier, amount
+ // ))
revert(
- TokenTransferGenericFailure_error_sig_ptr,
+ Generic_error_selector_offset,
TokenTransferGenericFailure_error_length
)
}
@@ -553,9 +678,17 @@ contract TokenTransferrer is TokenTransferrerErrors {
// If the token has no code, revert.
if iszero(extcodesize(token)) {
- mstore(NoContract_error_sig_ptr, NoContract_error_signature)
- mstore(NoContract_error_token_ptr, token)
- revert(NoContract_error_sig_ptr, NoContract_error_length)
+ // Store left-padded selector with push4, mem[28:32]
+ mstore(0, NoContract_error_selector)
+ mstore(NoContract_error_account_ptr, token)
+
+ // revert(abi.encodeWithSignature(
+ // "NoContract(address)", account
+ // ))
+ revert(
+ Generic_error_selector_offset,
+ NoContract_error_length
+ )
}
// Get the total number of supplied ids.
@@ -566,7 +699,7 @@ contract TokenTransferrer is TokenTransferrerErrors {
// Determine the expected offset for the amounts array.
let expectedAmountsOffset := add(
ConduitBatch1155Transfer_amounts_length_baseOffset,
- mul(idsLength, OneWord)
+ shl(OneWordShift, idsLength)
)
// Validate struct encoding.
@@ -604,10 +737,15 @@ contract TokenTransferrer is TokenTransferrerErrors {
// Revert with an error if the encoding is not valid.
if invalidEncoding {
+ // Store left-padded selector with push4, mem[28:32]
mstore(
Invalid1155BatchTransferEncoding_ptr,
Invalid1155BatchTransferEncoding_selector
)
+
+ // revert(abi.encodeWithSignature(
+ // "Invalid1155BatchTransferEncoding()"
+ // ))
revert(
Invalid1155BatchTransferEncoding_ptr,
Invalid1155BatchTransferEncoding_length
@@ -626,7 +764,10 @@ contract TokenTransferrer is TokenTransferrerErrors {
// Determine size of calldata required for ids and amounts. Note
// that the size includes both lengths as well as the data.
- let idsAndAmountsSize := add(TwoWords, mul(idsLength, TwoWords))
+ let idsAndAmountsSize := add(
+ TwoWords,
+ shl(TwoWordsShift, idsLength)
+ )
// Update the offset for the data array in memory.
mstore(
@@ -679,9 +820,9 @@ contract TokenTransferrer is TokenTransferrerErrors {
// returndata while expanding memory where necessary.
// Start by computing word size of returndata and
// allocated memory. Round up to the nearest full word.
- let returnDataWords := div(
- add(returndatasize(), AlmostOneWord),
- OneWord
+ let returnDataWords := shr(
+ OneWordShift,
+ add(returndatasize(), ThirtyOneBytes)
)
// Note: use transferDataSize in place of msize() to
@@ -692,7 +833,7 @@ contract TokenTransferrer is TokenTransferrerErrors {
// manually and does not update it, and transferDataSize
// should be the largest memory value used (unless a
// previous batch was larger).
- let msizeWords := div(transferDataSize, OneWord)
+ let msizeWords := shr(OneWordShift, transferDataSize)
// Next, compute the cost of the returndatacopy.
let cost := mul(CostPerWord, returnDataWords)
@@ -706,15 +847,15 @@ contract TokenTransferrer is TokenTransferrerErrors {
sub(returnDataWords, msizeWords),
CostPerWord
),
- div(
+ shr(
+ MemoryExpansionCoefficientShift,
sub(
mul(
returnDataWords,
returnDataWords
),
mul(msizeWords, msizeWords)
- ),
- MemoryExpansionCoefficient
+ )
)
)
)
diff --git a/contracts/lib/TokenTransferrerConstants.sol b/contracts/lib/TokenTransferrerConstants.sol
index adb01c58d..111a756c9 100644
--- a/contracts/lib/TokenTransferrerConstants.sol
+++ b/contracts/lib/TokenTransferrerConstants.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
/*
* -------------------------- Disambiguation & Other Notes ---------------------
@@ -8,7 +8,7 @@ pragma solidity >=0.8.7;
* offset or pointer to the body of a dynamic type. In calldata, the head
* is always an offset (relative to the parent object), while in memory,
* the head is always the pointer to the body. More information found here:
- * https://docs.soliditylang.org/en/v0.8.14/abi-spec.html#argument-encoding
+ * https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#argument-encoding
* - Note that the length of an array is separate from and precedes the
* head of the array.
*
@@ -33,11 +33,14 @@ pragma solidity >=0.8.7;
* codebase but have been left in for readability.
*/
-uint256 constant AlmostOneWord = 0x1f;
+uint256 constant ThirtyOneBytes = 0x1f;
uint256 constant OneWord = 0x20;
uint256 constant TwoWords = 0x40;
uint256 constant ThreeWords = 0x60;
+uint256 constant OneWordShift = 0x5;
+uint256 constant TwoWordsShift = 0x6;
+
uint256 constant FreeMemoryPointerSlot = 0x40;
uint256 constant ZeroSlot = 0x60;
uint256 constant DefaultFreeMemoryPointer = 0x80;
@@ -46,6 +49,8 @@ uint256 constant Slot0x80 = 0x80;
uint256 constant Slot0xA0 = 0xa0;
uint256 constant Slot0xC0 = 0xc0;
+uint256 constant Generic_error_selector_offset = 0x1c;
+
// abi.encodeWithSignature("transferFrom(address,address,uint256)")
uint256 constant ERC20_transferFrom_signature = (
0x23b872dd00000000000000000000000000000000000000000000000000000000
@@ -79,59 +84,61 @@ uint256 constant ERC1155_safeBatchTransferFrom_signature = (
0x2eb2c2d600000000000000000000000000000000000000000000000000000000
);
-bytes4 constant ERC1155_safeBatchTransferFrom_selector = bytes4(
- bytes32(ERC1155_safeBatchTransferFrom_signature)
-);
+// bytes4 constant ERC1155_safeBatchTransferFrom_selector = bytes4(
+// bytes32(ERC1155_safeBatchTransferFrom_signature)
+// );
-uint256 constant ERC721_transferFrom_signature = ERC20_transferFrom_signature;
+uint256 constant ERC721_transferFrom_signature = (
+ 0x23b872dd00000000000000000000000000000000000000000000000000000000
+);
uint256 constant ERC721_transferFrom_sig_ptr = 0x0;
uint256 constant ERC721_transferFrom_from_ptr = 0x04;
uint256 constant ERC721_transferFrom_to_ptr = 0x24;
uint256 constant ERC721_transferFrom_id_ptr = 0x44;
uint256 constant ERC721_transferFrom_length = 0x64; // 4 + 32 * 3 == 100
-// abi.encodeWithSignature("NoContract(address)")
-uint256 constant NoContract_error_signature = (
- 0x5f15d67200000000000000000000000000000000000000000000000000000000
-);
-uint256 constant NoContract_error_sig_ptr = 0x0;
-uint256 constant NoContract_error_token_ptr = 0x4;
-uint256 constant NoContract_error_length = 0x24; // 4 + 32 == 36
+/*
+ * error NoContract(address account)
+ * - Defined in TokenTransferrerErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x00: account
+ * Revert buffer is memory[0x1c:0x40]
+ */
+uint256 constant NoContract_error_selector = 0x5f15d672;
+uint256 constant NoContract_error_account_ptr = 0x20;
+uint256 constant NoContract_error_length = 0x24;
-// abi.encodeWithSignature(
-// "TokenTransferGenericFailure(address,address,address,uint256,uint256)"
-// )
-uint256 constant TokenTransferGenericFailure_error_signature = (
- 0xf486bc8700000000000000000000000000000000000000000000000000000000
-);
-uint256 constant TokenTransferGenericFailure_error_sig_ptr = 0x0;
-uint256 constant TokenTransferGenericFailure_error_token_ptr = 0x4;
-uint256 constant TokenTransferGenericFailure_error_from_ptr = 0x24;
-uint256 constant TokenTransferGenericFailure_error_to_ptr = 0x44;
-uint256 constant TokenTransferGenericFailure_error_id_ptr = 0x64;
-uint256 constant TokenTransferGenericFailure_error_amount_ptr = 0x84;
-
-// 4 + 32 * 5 == 164
+/*
+ * error TokenTransferGenericFailure(
+ * address token,
+ * address from,
+ * address to,
+ * uint256 identifier,
+ * uint256 amount
+ * )
+ * - Defined in TokenTransferrerErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x20: token
+ * - 0x40: from
+ * - 0x60: to
+ * - 0x80: identifier
+ * - 0xa0: amount
+ * Revert buffer is memory[0x1c:0xc0]
+ */
+uint256 constant TokenTransferGenericFailure_error_selector = 0xf486bc87;
+uint256 constant TokenTransferGenericFailure_error_token_ptr = 0x20;
+uint256 constant TokenTransferGenericFailure_error_from_ptr = 0x40;
+uint256 constant TokenTransferGenericFailure_error_to_ptr = 0x60;
+uint256 constant TokenTransferGenericFailure_error_identifier_ptr = 0x80;
+uint256 constant TokenTransferGenericFailure_err_identifier_ptr = 0x80;
+uint256 constant TokenTransferGenericFailure_error_amount_ptr = 0xa0;
uint256 constant TokenTransferGenericFailure_error_length = 0xa4;
-// abi.encodeWithSignature(
-// "BadReturnValueFromERC20OnTransfer(address,address,address,uint256)"
-// )
-uint256 constant BadReturnValueFromERC20OnTransfer_error_signature = (
- 0x9889192300000000000000000000000000000000000000000000000000000000
-);
-uint256 constant BadReturnValueFromERC20OnTransfer_error_sig_ptr = 0x0;
-uint256 constant BadReturnValueFromERC20OnTransfer_error_token_ptr = 0x4;
-uint256 constant BadReturnValueFromERC20OnTransfer_error_from_ptr = 0x24;
-uint256 constant BadReturnValueFromERC20OnTransfer_error_to_ptr = 0x44;
-uint256 constant BadReturnValueFromERC20OnTransfer_error_amount_ptr = 0x64;
-
-// 4 + 32 * 4 == 132
-uint256 constant BadReturnValueFromERC20OnTransfer_error_length = 0x84;
-
uint256 constant ExtraGasBuffer = 0x20;
-uint256 constant CostPerWord = 3;
-uint256 constant MemoryExpansionCoefficient = 0x200;
+uint256 constant CostPerWord = 0x3;
+uint256 constant MemoryExpansionCoefficientShift = 0x9;
// Values are offset by 32 bytes in order to write the token to the beginning
// in the event of a revert
@@ -145,17 +152,17 @@ uint256 constant BatchTransfer1155Params_calldata_baseSize = 0xc4;
uint256 constant BatchTransfer1155Params_ids_length_ptr = 0xc4;
uint256 constant BatchTransfer1155Params_ids_length_offset = 0xa0;
-uint256 constant BatchTransfer1155Params_amounts_length_baseOffset = 0xc0;
-uint256 constant BatchTransfer1155Params_data_length_baseOffset = 0xe0;
+// uint256 constant BatchTransfer1155Params_amounts_length_baseOffset = 0xc0;
+// uint256 constant BatchTransfer1155Params_data_length_baseOffset = 0xe0;
uint256 constant ConduitBatch1155Transfer_usable_head_size = 0x80;
uint256 constant ConduitBatch1155Transfer_from_offset = 0x20;
uint256 constant ConduitBatch1155Transfer_ids_head_offset = 0x60;
-uint256 constant ConduitBatch1155Transfer_amounts_head_offset = 0x80;
+// uint256 constant ConduitBatch1155Transfer_amounts_head_offset = 0x80;
uint256 constant ConduitBatch1155Transfer_ids_length_offset = 0xa0;
uint256 constant ConduitBatch1155Transfer_amounts_length_baseOffset = 0xc0;
-uint256 constant ConduitBatch1155Transfer_calldata_baseSize = 0xc0;
+// uint256 constant ConduitBatch1155Transfer_calldata_baseSize = 0xc0;
// Note: abbreviated version of above constant to adhere to line length limit.
uint256 constant ConduitBatchTransfer_amounts_head_offset = 0x80;
@@ -171,3 +178,23 @@ uint256 constant ERC1155BatchTransferGenericFailure_error_signature = (
);
uint256 constant ERC1155BatchTransferGenericFailure_token_ptr = 0x04;
uint256 constant ERC1155BatchTransferGenericFailure_ids_offset = 0xc0;
+
+/*
+ * error BadReturnValueFromERC20OnTransfer(
+ * address token, address from, address to, uint256 amount
+ * )
+ * - Defined in TokenTransferrerErrors.sol
+ * Memory layout:
+ * - 0x00: Left-padded selector (data begins at 0x1c)
+ * - 0x00: token
+ * - 0x20: from
+ * - 0x40: to
+ * - 0x60: amount
+ * Revert buffer is memory[0x1c:0xa0]
+ */
+uint256 constant BadReturnValueFromERC20OnTransfer_error_selector = 0x98891923;
+uint256 constant BadReturnValueFromERC20OnTransfer_error_token_ptr = 0x20;
+uint256 constant BadReturnValueFromERC20OnTransfer_error_from_ptr = 0x40;
+uint256 constant BadReturnValueFromERC20OnTransfer_error_to_ptr = 0x60;
+uint256 constant BadReturnValueFromERC20OnTransfer_error_amount_ptr = 0x80;
+uint256 constant BadReturnValueFromERC20OnTransfer_error_length = 0x84;
diff --git a/contracts/lib/TypehashDirectory.sol b/contracts/lib/TypehashDirectory.sol
new file mode 100644
index 000000000..9fd733109
--- /dev/null
+++ b/contracts/lib/TypehashDirectory.sol
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.17;
+
+import {
+ FreeMemoryPointerSlot,
+ OneWord,
+ OneWordShift,
+ ThirtyOneBytes
+} from "../lib/ConsiderationConstants.sol";
+
+/**
+ * @title TypehashDirectory
+ * @notice The typehash directory contains 24 bulk order EIP-712 typehashes,
+ * depending on the height of the tree in each bulk order payload, as
+ * its runtime code (with an invalid opcode prefix so that the contract
+ * cannot be called normally). This runtime code is designed to be read
+ * from by Seaport using `extcodecopy` while verifying bulk signatures.
+ */
+contract TypehashDirectory {
+ // Encodes "[2]" for use in deriving typehashes.
+ bytes3 internal constant twoSubstring = 0x5B325D;
+ uint256 internal constant twoSubstringLength = 0x3;
+
+ // Dictates maximum bulk order group size; 24 => 2^24 => 16,777,216 orders.
+ uint256 internal constant MaxTreeHeight = 0x18;
+
+ uint256 internal constant InvalidOpcode = 0xfe;
+
+ /**
+ * @dev Derive 24 bulk order EIP-712 typehashes, one for each supported
+ * tree height from 1 to 24, and write them to runtime code.
+ */
+ constructor() {
+ // Declare an array where each type hash will be written.
+ bytes32[] memory typeHashes = new bytes32[](MaxTreeHeight);
+
+ // Derive a string of 24 "[2]" substrings.
+ bytes memory brackets = getMaxTreeBrackets(MaxTreeHeight);
+
+ // Derive a string of subtypes for the order parameters.
+ bytes memory subTypes = getTreeSubTypes();
+
+ // Cache memory pointer before each loop so memory doesn't expand by the
+ // full string size on each loop.
+ uint256 freeMemoryPointer;
+ assembly {
+ freeMemoryPointer := mload(FreeMemoryPointerSlot)
+ }
+
+ // Iterate over each tree height.
+ for (uint256 i = 0; i < MaxTreeHeight; ) {
+ // The actual height is one greater than its respective index.
+ uint256 height = i + 1;
+
+ // Slice brackets length to size needed for `height`.
+ assembly {
+ mstore(brackets, mul(twoSubstringLength, height))
+ }
+
+ // Encode the type string for the BulkOrder struct.
+ bytes memory bulkOrderTypeString = bytes.concat(
+ "BulkOrder(OrderComponents",
+ brackets,
+ " tree)",
+ subTypes
+ );
+
+ // Derive EIP712 type hash.
+ bytes32 typeHash = keccak256(bulkOrderTypeString);
+ typeHashes[i] = typeHash;
+
+ // Reset the free memory pointer.
+ assembly {
+ mstore(FreeMemoryPointerSlot, freeMemoryPointer)
+ }
+
+ unchecked {
+ ++i;
+ }
+ }
+
+ assembly {
+ // Overwrite length with zero to give the contract an INVALID prefix
+ // and deploy the type hashes array as a contract.
+ mstore(typeHashes, InvalidOpcode)
+
+ return(
+ add(typeHashes, ThirtyOneBytes),
+ add(shl(OneWordShift, MaxTreeHeight), 1)
+ )
+ }
+ }
+
+ /**
+ * @dev Internal pure function that returns a string of "[2]" substrings,
+ * with a number of substrings equal to the provided height.
+ *
+ * @param maxHeight The number of "[2]" substrings to include.
+ *
+ * @return A bytes array representing the string.
+ */
+ function getMaxTreeBrackets(
+ uint256 maxHeight
+ ) internal pure returns (bytes memory) {
+ bytes memory suffixes = new bytes(twoSubstringLength * maxHeight);
+ assembly {
+ // Retrieve the pointer to the array head.
+ let ptr := add(suffixes, OneWord)
+
+ // Derive the terminal pointer.
+ let endPtr := add(ptr, mul(maxHeight, twoSubstringLength))
+
+ // Iterate over each pointer until terminal pointer is reached.
+ for {
+
+ } lt(ptr, endPtr) {
+ ptr := add(ptr, twoSubstringLength)
+ } {
+ // Insert "[2]" substring directly at current pointer location.
+ mstore(ptr, twoSubstring)
+ }
+ }
+
+ // Return the fully populated array of substrings.
+ return suffixes;
+ }
+
+ /**
+ * @dev Internal pure function that returns a string of subtypes used in
+ * generating bulk order EIP-712 typehashes.
+ *
+ * @return A bytes array representing the string.
+ */
+ function getTreeSubTypes() internal pure returns (bytes memory) {
+ // Construct the OfferItem type string.
+ bytes memory offerItemTypeString = bytes(
+ "OfferItem("
+ "uint8 itemType,"
+ "address token,"
+ "uint256 identifierOrCriteria,"
+ "uint256 startAmount,"
+ "uint256 endAmount"
+ ")"
+ );
+
+ // Construct the ConsiderationItem type string.
+ bytes memory considerationItemTypeString = bytes(
+ "ConsiderationItem("
+ "uint8 itemType,"
+ "address token,"
+ "uint256 identifierOrCriteria,"
+ "uint256 startAmount,"
+ "uint256 endAmount,"
+ "address recipient"
+ ")"
+ );
+
+ // Construct the OrderComponents type string, not including the above.
+ bytes memory orderComponentsPartialTypeString = bytes(
+ "OrderComponents("
+ "address offerer,"
+ "address zone,"
+ "OfferItem[] offer,"
+ "ConsiderationItem[] consideration,"
+ "uint8 orderType,"
+ "uint256 startTime,"
+ "uint256 endTime,"
+ "bytes32 zoneHash,"
+ "uint256 salt,"
+ "bytes32 conduitKey,"
+ "uint256 counter"
+ ")"
+ );
+
+ // Return the combined string.
+ return
+ bytes.concat(
+ considerationItemTypeString,
+ offerItemTypeString,
+ orderComponentsPartialTypeString
+ );
+ }
+}
diff --git a/contracts/lib/Verifiers.sol b/contracts/lib/Verifiers.sol
index baba8da90..65fd80f0e 100644
--- a/contracts/lib/Verifiers.sol
+++ b/contracts/lib/Verifiers.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
+pragma solidity 0.8.17;
import { OrderStatus } from "./ConsiderationStructs.sol";
@@ -7,6 +7,27 @@ import { Assertions } from "./Assertions.sol";
import { SignatureVerification } from "./SignatureVerification.sol";
+import {
+ _revertInvalidTime,
+ _revertOrderAlreadyFilled,
+ _revertOrderIsCancelled,
+ _revertOrderPartiallyFilled
+} from "./ConsiderationErrors.sol";
+
+import {
+ BulkOrderProof_keyShift,
+ BulkOrderProof_keySize,
+ BulkOrderProof_lengthAdjustmentBeforeMask,
+ BulkOrderProof_lengthRangeAfterMask,
+ BulkOrderProof_minSize,
+ BulkOrderProof_rangeSize,
+ ECDSA_MaxLength,
+ OneWord,
+ OneWordShift,
+ ThirtyOneBytes,
+ TwoWords
+} from "./ConsiderationConstants.sol";
+
/**
* @title Verifiers
* @author 0age
@@ -39,26 +60,25 @@ contract Verifiers is Assertions, SignatureVerification {
uint256 endTime,
bool revertOnInvalid
) internal view returns (bool valid) {
- // Revert if order's timespan hasn't started yet or has already ended.
- if (startTime > block.timestamp || endTime <= block.timestamp) {
- // Only revert if revertOnInvalid has been supplied as true.
- if (revertOnInvalid) {
- revert InvalidTime();
- }
-
- // Return false as the order is invalid.
- return false;
+ // Mark as valid if order has started and has not already ended.
+ assembly {
+ valid := and(
+ iszero(gt(startTime, timestamp())),
+ gt(endTime, timestamp())
+ )
}
- // Return true as the order time is valid.
- valid = true;
+ // Only revert on invalid if revertOnInvalid has been supplied as true.
+ if (revertOnInvalid && !valid) {
+ _revertInvalidTime(startTime, endTime);
+ }
}
/**
* @dev Internal view function to verify the signature of an order. An
* ERC-1271 fallback will be attempted if either the signature length
- * is not 32 or 33 bytes or if the recovered signer does not match the
- * supplied offerer. Note that in cases where a 32 or 33 byte signature
+ * is not 64 or 65 bytes or if the recovered signer does not match the
+ * supplied offerer. Note that in cases where a 64 or 65 byte signature
* is supplied, only standard ECDSA signatures that recover to a
* non-zero address are supported.
*
@@ -73,15 +93,144 @@ contract Verifiers is Assertions, SignatureVerification {
bytes memory signature
) internal view {
// Skip signature verification if the offerer is the caller.
- if (offerer == msg.sender) {
+ if (_unmaskedAddressComparison(offerer, msg.sender)) {
return;
}
- // Derive EIP-712 digest using the domain separator and the order hash.
- bytes32 digest = _deriveEIP712Digest(_domainSeparator(), orderHash);
+ bytes32 domainSeparator = _domainSeparator();
+
+ // Derive original EIP-712 digest using domain separator and order hash.
+ bytes32 originalDigest = _deriveEIP712Digest(
+ domainSeparator,
+ orderHash
+ );
+
+ uint256 originalSignatureLength = signature.length;
+
+ bytes32 digest;
+ if (_isValidBulkOrderSize(originalSignatureLength)) {
+ // Rederive order hash and digest using bulk order proof.
+ (orderHash) = _computeBulkOrderProof(signature, orderHash);
+ digest = _deriveEIP712Digest(domainSeparator, orderHash);
+ } else {
+ digest = originalDigest;
+ }
// Ensure that the signature for the digest is valid for the offerer.
- _assertValidSignature(offerer, digest, signature);
+ _assertValidSignature(
+ offerer,
+ digest,
+ originalDigest,
+ originalSignatureLength,
+ signature
+ );
+ }
+
+ /**
+ * @dev Determines whether the specified bulk order size is valid.
+ *
+ * @param signatureLength The signature length of the bulk order to check.
+ *
+ * @return validLength True if bulk order size is valid, false otherwise.
+ */
+ function _isValidBulkOrderSize(
+ uint256 signatureLength
+ ) internal pure returns (bool validLength) {
+ // Utilize assembly to validate the length; the equivalent logic is
+ // (64 + x) + 3 + 32y where (0 <= x <= 1) and (1 <= y <= 24).
+ assembly {
+ validLength := and(
+ lt(
+ sub(signatureLength, BulkOrderProof_minSize),
+ BulkOrderProof_rangeSize
+ ),
+ lt(
+ and(
+ add(
+ signatureLength,
+ BulkOrderProof_lengthAdjustmentBeforeMask
+ ),
+ ThirtyOneBytes
+ ),
+ BulkOrderProof_lengthRangeAfterMask
+ )
+ )
+ }
+ }
+
+ /**
+ * @dev Computes the bulk order hash for the specified proof and leaf. Note
+ * that if an index that exceeds the number of orders in the bulk order
+ * payload will instead "wrap around" and refer to an earlier index.
+ *
+ * @param proofAndSignature The proof and signature of the bulk order.
+ * @param leaf The leaf of the bulk order tree.
+ *
+ * @return bulkOrderHash The bulk order hash.
+ */
+ function _computeBulkOrderProof(
+ bytes memory proofAndSignature,
+ bytes32 leaf
+ ) internal view returns (bytes32 bulkOrderHash) {
+ // Declare arguments for the root hash and the height of the proof.
+ bytes32 root;
+ uint256 height;
+
+ // Utilize assembly to efficiently derive the root hash using the proof.
+ assembly {
+ // Retrieve the length of the proof, key, and signature combined.
+ let fullLength := mload(proofAndSignature)
+
+ // If proofAndSignature has odd length, it is a compact signature
+ // with 64 bytes.
+ let signatureLength := sub(ECDSA_MaxLength, and(fullLength, 1))
+
+ // Derive height (or depth of tree) with signature and proof length.
+ height := shr(OneWordShift, sub(fullLength, signatureLength))
+
+ // Update the length in memory to only include the signature.
+ mstore(proofAndSignature, signatureLength)
+
+ // Derive the pointer for the key using the signature length.
+ let keyPtr := add(proofAndSignature, add(OneWord, signatureLength))
+
+ // Retrieve the three-byte key using the derived pointer.
+ let key := shr(BulkOrderProof_keyShift, mload(keyPtr))
+
+ /// Retrieve pointer to first proof element by applying a constant
+ // for the key size to the derived key pointer.
+ let proof := add(keyPtr, BulkOrderProof_keySize)
+
+ // Compute level 1.
+ let scratchPtr1 := shl(OneWordShift, and(key, 1))
+ mstore(scratchPtr1, leaf)
+ mstore(xor(scratchPtr1, OneWord), mload(proof))
+
+ // Compute remaining proofs.
+ for {
+ let i := 1
+ } lt(i, height) {
+ i := add(i, 1)
+ } {
+ proof := add(proof, OneWord)
+ let scratchPtr := shl(OneWordShift, and(shr(i, key), 1))
+ mstore(scratchPtr, keccak256(0, TwoWords))
+ mstore(xor(scratchPtr, OneWord), mload(proof))
+ }
+
+ // Compute root hash.
+ root := keccak256(0, TwoWords)
+ }
+
+ // Retrieve appropriate typehash from runtime storage based on height.
+ bytes32 rootTypeHash = _lookupBulkOrderTypehash(height);
+
+ // Use the typehash and the root hash to derive final bulk order hash.
+ assembly {
+ mstore(0, rootTypeHash)
+ mstore(OneWord, root)
+ bulkOrderHash := keccak256(0, TwoWords)
+ }
}
/**
@@ -109,7 +258,7 @@ contract Verifiers is Assertions, SignatureVerification {
if (orderStatus.isCancelled) {
// Only revert if revertOnInvalid has been supplied as true.
if (revertOnInvalid) {
- revert OrderIsCancelled(orderHash);
+ _revertOrderIsCancelled(orderHash);
}
// Return false as the order status is invalid.
@@ -124,13 +273,13 @@ contract Verifiers is Assertions, SignatureVerification {
// ensure the order has not been partially filled when not allowed.
if (onlyAllowUnused) {
// Always revert on partial fills when onlyAllowUnused is true.
- revert OrderPartiallyFilled(orderHash);
+ _revertOrderPartiallyFilled(orderHash);
}
// Otherwise, ensure that order has not been entirely filled.
else if (orderStatusNumerator >= orderStatus.denominator) {
// Only revert if revertOnInvalid has been supplied as true.
if (revertOnInvalid) {
- revert OrderAlreadyFilled(orderHash);
+ _revertOrderAlreadyFilled(orderHash);
}
// Return false as the order status is invalid.
diff --git a/contracts/lib/ZoneInteraction.sol b/contracts/lib/ZoneInteraction.sol
index fbe88a514..26868af40 100644
--- a/contracts/lib/ZoneInteraction.sol
+++ b/contracts/lib/ZoneInteraction.sol
@@ -1,175 +1,261 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.13;
-
-import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
+pragma solidity 0.8.17;
import { OrderType } from "./ConsiderationEnums.sol";
-// prettier-ignore
-import { AdvancedOrder, CriteriaResolver } from "./ConsiderationStructs.sol";
+import {
+ AdvancedOrder,
+ BasicOrderParameters,
+ OrderParameters
+} from "./ConsiderationStructs.sol";
+
+import { ZoneInteractionErrors } from "../interfaces/ZoneInteractionErrors.sol";
-import "./ConsiderationConstants.sol";
+import { LowLevelHelpers } from "./LowLevelHelpers.sol";
+
+import { ConsiderationEncoder } from "./ConsiderationEncoder.sol";
+
+import { MemoryPointer } from "../helpers/PointerLibraries.sol";
-// prettier-ignore
import {
- ZoneInteractionErrors
-} from "../interfaces/ZoneInteractionErrors.sol";
+ ContractOrder_orderHash_offerer_shift,
+ MaskOverFirstFourBytes,
+ OneWord,
+ OrderParameters_zone_offset
+} from "./ConsiderationConstants.sol";
-import { LowLevelHelpers } from "./LowLevelHelpers.sol";
+import {
+ Error_selector_offset,
+ InvalidContractOrder_error_selector,
+ InvalidRestrictedOrder_error_length,
+ InvalidRestrictedOrder_error_orderHash_ptr,
+ InvalidRestrictedOrder_error_selector
+} from "./ConsiderationErrorConstants.sol";
/**
* @title ZoneInteraction
* @author 0age
* @notice ZoneInteraction contains logic related to interacting with zones.
*/
-contract ZoneInteraction is ZoneInteractionErrors, LowLevelHelpers {
+contract ZoneInteraction is
+ ConsiderationEncoder,
+ ZoneInteractionErrors,
+ LowLevelHelpers
+{
/**
- * @dev Internal view function to determine if an order has a restricted
- * order type and, if so, to ensure that either the offerer or the zone
- * are the fulfiller or that a staticcall to `isValidOrder` on the zone
- * returns a magic value indicating that the order is currently valid.
+ * @dev Internal function to determine if an order has a restricted order
+ * type and, if so, to ensure that either the zone is the caller or
+ * that a call to `validateOrder` on the zone returns a magic value
+ * indicating that the order is currently valid. Note that contract
+ * orders are not accessible via the basic fulfillment method.
*
- * @param orderHash The hash of the order.
- * @param zoneHash The hash to provide upon calling the zone.
- * @param orderType The type of the order.
- * @param offerer The offerer in question.
- * @param zone The zone in question.
+ * @param orderHash The hash of the order.
+ * @param orderType The order type.
+ * @param parameters The parameters of the basic order.
*/
function _assertRestrictedBasicOrderValidity(
bytes32 orderHash,
- bytes32 zoneHash,
OrderType orderType,
- address offerer,
- address zone
- ) internal view {
- // Order type 2-3 require zone or offerer be caller or zone to approve.
- if (
- uint256(orderType) > 1 &&
- msg.sender != zone &&
- msg.sender != offerer
- ) {
- // Perform minimal staticcall to the zone.
- _callIsValidOrder(zone, orderHash, offerer, zoneHash);
- }
- }
-
- function _callIsValidOrder(
- address zone,
- bytes32 orderHash,
- address offerer,
- bytes32 zoneHash
- ) internal view {
- // Perform minimal staticcall to the zone.
- bool success = _staticcall(
- zone,
- abi.encodeWithSelector(
- ZoneInterface.isValidOrder.selector,
+ BasicOrderParameters calldata parameters
+ ) internal {
+ // Order type 2-3 require zone be caller or zone to approve.
+ // Note that in cases where fulfiller == zone, the restricted order
+ // validation will be skipped.
+ if (_isRestrictedAndCallerNotZone(orderType, parameters.zone)) {
+ // Encode the `validateOrder` call in memory.
+ (MemoryPointer callData, uint256 size) = _encodeValidateBasicOrder(
orderHash,
- msg.sender,
- offerer,
- zoneHash
- )
- );
+ parameters
+ );
- // Ensure call was successful and returned the correct magic value.
- _assertIsValidOrderStaticcallSuccess(success, orderHash);
+ // Perform `validateOrder` call and ensure magic value was returned.
+ _callAndCheckStatus(
+ parameters.zone,
+ orderHash,
+ callData,
+ size,
+ InvalidRestrictedOrder_error_selector
+ );
+ }
}
/**
- * @dev Internal view function to determine whether an order is a restricted
- * order and, if so, to ensure that it was either submitted by the
- * offerer or the zone for the order, or that the zone returns the
- * expected magic value upon performing a staticcall to `isValidOrder`
- * or `isValidOrderIncludingExtraData` depending on whether the order
- * fulfillment specifies extra data or criteria resolvers.
+ * @dev Internal function to determine the post-execution validity of
+ * restricted and contract orders. Restricted orders where the caller
+ * is not the zone must successfully call `validateOrder` with the
+ * correct magic value returned. Contract orders must successfully call
+ * `ratifyOrder` with the correct magic value returned.
*
- * @param advancedOrder The advanced order in question.
- * @param criteriaResolvers An array where each element contains a reference
- * to a specific offer or consideration, a token
- * identifier, and a proof that the supplied token
- * identifier is contained in the order's merkle
- * root. Note that a criteria of zero indicates
- * that any (transferable) token identifier is
- * valid and that no proof needs to be supplied.
- * @param priorOrderHashes The order hashes of each order supplied prior to
- * the current order as part of a "match" variety
- * of order fulfillment (e.g. this array will be
- * empty for single or "fulfill available").
- * @param orderHash The hash of the order.
- * @param zoneHash The hash to provide upon calling the zone.
- * @param orderType The type of the order.
- * @param offerer The offerer in question.
- * @param zone The zone in question.
+ * @param advancedOrder The advanced order in question.
+ * @param orderHashes The order hashes of each order included as part of
+ * the current fulfillment.
+ * @param orderHash The hash of the order.
*/
function _assertRestrictedAdvancedOrderValidity(
AdvancedOrder memory advancedOrder,
- CriteriaResolver[] memory criteriaResolvers,
- bytes32[] memory priorOrderHashes,
- bytes32 orderHash,
- bytes32 zoneHash,
- OrderType orderType,
- address offerer,
- address zone
- ) internal view {
- // Order type 2-3 require zone or offerer be caller or zone to approve.
+ bytes32[] memory orderHashes,
+ bytes32 orderHash
+ ) internal {
+ // Declare variables that will be assigned based on the order type.
+ address target;
+ uint256 errorSelector;
+ MemoryPointer callData;
+ uint256 size;
+
+ // Retrieve the parameters of the order in question.
+ OrderParameters memory parameters = advancedOrder.parameters;
+
+ // OrderType 2-3 require zone to be caller or approve via validateOrder.
if (
- uint256(orderType) > 1 &&
- msg.sender != zone &&
- msg.sender != offerer
+ _isRestrictedAndCallerNotZone(parameters.orderType, parameters.zone)
) {
- // If no extraData or criteria resolvers are supplied...
- if (
- advancedOrder.extraData.length == 0 &&
- criteriaResolvers.length == 0
- ) {
- // Perform minimal staticcall to the zone.
- _callIsValidOrder(zone, orderHash, offerer, zoneHash);
- } else {
- // Otherwise, extra data or criteria resolvers were supplied; in
- // that event, perform a more verbose staticcall to the zone.
- bool success = _staticcall(
- zone,
- abi.encodeWithSelector(
- ZoneInterface.isValidOrderIncludingExtraData.selector,
- orderHash,
- msg.sender,
- advancedOrder,
- priorOrderHashes,
- criteriaResolvers
- )
- );
-
- // Ensure call was successful and returned correct magic value.
- _assertIsValidOrderStaticcallSuccess(success, orderHash);
+ // Encode the `validateOrder` call in memory.
+ (callData, size) = _encodeValidateOrder(
+ orderHash,
+ parameters,
+ advancedOrder.extraData,
+ orderHashes
+ );
+
+ // Set the target to the zone.
+ target = (
+ parameters
+ .toMemoryPointer()
+ .offset(OrderParameters_zone_offset)
+ .readAddress()
+ );
+
+ // Set the restricted-order-specific error selector.
+ errorSelector = InvalidRestrictedOrder_error_selector;
+ } else if (parameters.orderType == OrderType.CONTRACT) {
+ // Set the target to the offerer (note the offerer has no offset).
+ target = parameters.toMemoryPointer().readAddress();
+
+ // Shift the target 96 bits to the left.
+ uint256 shiftedOfferer;
+ assembly {
+ shiftedOfferer := shl(
+ ContractOrder_orderHash_offerer_shift,
+ target
+ )
}
+
+ // Encode the `ratifyOrder` call in memory.
+ (callData, size) = _encodeRatifyOrder(
+ orderHash,
+ parameters,
+ advancedOrder.extraData,
+ orderHashes,
+ shiftedOfferer
+ );
+
+ // Set the contract-order-specific error selector.
+ errorSelector = InvalidContractOrder_error_selector;
+ } else {
+ return;
}
+
+ // Perform call and ensure a corresponding magic value was returned.
+ _callAndCheckStatus(target, orderHash, callData, size, errorSelector);
}
/**
- * @dev Internal view function to ensure that a staticcall to `isValidOrder`
- * or `isValidOrderIncludingExtraData` as part of validating a
- * restricted order that was not submitted by the named offerer or zone
- * was successful and returned the required magic value.
+ * @dev Determines whether the specified order type is restricted and the
+ * caller is not the specified zone.
+ *
+ * @param orderType The type of the order to check.
+ * @param zone The address of the zone to check against.
*
- * @param success A boolean indicating the status of the staticcall.
- * @param orderHash The order hash of the order in question.
+ * @return mustValidate True if the order type is restricted and the caller
+ * is not the specified zone, false otherwise.
*/
- function _assertIsValidOrderStaticcallSuccess(
- bool success,
- bytes32 orderHash
- ) internal view {
- // If the call failed...
+ function _isRestrictedAndCallerNotZone(
+ OrderType orderType,
+ address zone
+ ) internal view returns (bool mustValidate) {
+ assembly {
+ mustValidate := and(
+ // Note that this check requires that there are no order types
+ // beyond the current set (0-4). It will need to be modified if
+ // more order types are added.
+ and(lt(orderType, 4), gt(orderType, 1)),
+ iszero(eq(caller(), zone))
+ )
+ }
+ }
+
+ /**
+ * @dev Calls the specified target with the given data and checks the status
+ * of the call. Revert reasons will be "bubbled up" if one is returned,
+ * otherwise reverting calls will throw a generic error based on the
+ * supplied error handler.
+ *
+ * @param target The address of the contract to call.
+ * @param orderHash The hash of the order associated with the call.
+ * @param callData The data to pass to the contract call.
+ * @param size The size of calldata.
+ * @param errorSelector The error handling function to call if the call
+ * fails or the magic value does not match.
+ */
+ function _callAndCheckStatus(
+ address target,
+ bytes32 orderHash,
+ MemoryPointer callData,
+ uint256 size,
+ uint256 errorSelector
+ ) internal {
+ bool success;
+ bool magicMatch;
+ assembly {
+ // Get magic value from the selector at start of provided calldata.
+ let magic := and(mload(callData), MaskOverFirstFourBytes)
+
+ // Clear the start of scratch space.
+ mstore(0, 0)
+
+ // Perform call, placing result in the first word of scratch space.
+ success := call(gas(), target, 0, callData, size, 0, OneWord)
+
+ // Determine if returned magic value matches the calldata selector.
+ magicMatch := eq(magic, mload(0))
+ }
+
+ // Revert if the call was not successful.
if (!success) {
// Revert and pass reason along if one was returned.
_revertWithReasonIfOneIsReturned();
- // Otherwise, revert with a generic error message.
- revert InvalidRestrictedOrder(orderHash);
+ // If no reason was returned, revert with supplied error selector.
+ assembly {
+ mstore(0, errorSelector)
+ mstore(InvalidRestrictedOrder_error_orderHash_ptr, orderHash)
+ // revert(abi.encodeWithSelector(
+ // "InvalidRestrictedOrder(bytes32)",
+ // orderHash
+ // ))
+ revert(
+ Error_selector_offset,
+ InvalidRestrictedOrder_error_length
+ )
+ }
}
- // Ensure result was extracted and matches isValidOrder magic value.
- if (_doesNotMatchMagic(ZoneInterface.isValidOrder.selector)) {
- revert InvalidRestrictedOrder(orderHash);
+ // Revert if the correct magic value was not returned.
+ if (!magicMatch) {
+ // Revert with a generic error message.
+ assembly {
+ mstore(0, errorSelector)
+ mstore(InvalidRestrictedOrder_error_orderHash_ptr, orderHash)
+
+ // revert(abi.encodeWithSelector(
+ // "InvalidRestrictedOrder(bytes32)",
+ // orderHash
+ // ))
+ revert(
+ Error_selector_offset,
+ InvalidRestrictedOrder_error_length
+ )
+ }
}
}
}
diff --git a/contracts/test/ConduitControllerMock.sol b/contracts/test/ConduitControllerMock.sol
new file mode 100644
index 000000000..a14d164cd
--- /dev/null
+++ b/contracts/test/ConduitControllerMock.sol
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ ConduitControllerInterface
+} from "../interfaces/ConduitControllerInterface.sol";
+
+import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
+
+import { ConduitMock } from "../test/ConduitMock.sol";
+
+import { ConduitMockInvalidMagic } from "../test/ConduitMockInvalidMagic.sol";
+
+import {
+ ConduitMockRevertNoReason
+} from "../test/ConduitMockRevertNoReason.sol";
+
+import { ConduitMockRevertBytes } from "../test/ConduitMockRevertBytes.sol";
+
+contract ConduitControllerMock is ConduitControllerInterface {
+ // Register keys, owners, new potential owners, and channels by conduit.
+ mapping(address => ConduitProperties) internal _conduits;
+
+ // Set conduit creation code and runtime code hashes as immutable arguments.
+ bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH;
+ bytes32 internal immutable _CONDUIT_RUNTIME_CODE_HASH;
+
+ uint256 private conduitNum;
+
+ /**
+ * @dev Initialize contract by deploying a conduit and setting the creation
+ * code and runtime code hashes as immutable arguments.
+ */
+ constructor(uint256 _conduitNum) {
+ conduitNum = _conduitNum;
+
+ bytes32 creationCodeHash;
+ bytes32 runtimeCodeHash;
+
+ if (conduitNum == 0) {
+ creationCodeHash = keccak256(type(ConduitMock).creationCode);
+ ConduitMock zeroConduit = new ConduitMock{ salt: bytes32(0) }();
+ runtimeCodeHash = address(zeroConduit).codehash;
+ } else if (conduitNum == 1) {
+ creationCodeHash = keccak256(
+ type(ConduitMockRevertNoReason).creationCode
+ );
+ ConduitMockRevertNoReason zeroConduit = new ConduitMockRevertNoReason{
+ salt: bytes32(0)
+ }();
+ runtimeCodeHash = address(zeroConduit).codehash;
+ } else if (conduitNum == 2) {
+ creationCodeHash = keccak256(
+ type(ConduitMockInvalidMagic).creationCode
+ );
+ ConduitMockInvalidMagic zeroConduit = new ConduitMockInvalidMagic{
+ salt: bytes32(0)
+ }();
+ runtimeCodeHash = address(zeroConduit).codehash;
+ } else if (conduitNum == 3) {
+ creationCodeHash = keccak256(
+ type(ConduitMockRevertBytes).creationCode
+ );
+ ConduitMockRevertBytes zeroConduit = new ConduitMockRevertBytes{
+ salt: bytes32(0)
+ }();
+ runtimeCodeHash = address(zeroConduit).codehash;
+ }
+ _CONDUIT_CREATION_CODE_HASH = creationCodeHash;
+ _CONDUIT_RUNTIME_CODE_HASH = runtimeCodeHash;
+ }
+
+ /**
+ * @notice Deploy a new conduit using a supplied conduit key and assigning
+ * an initial owner for the deployed conduit. Note that the first
+ * twenty bytes of the supplied conduit key must match the caller
+ * and that a new conduit cannot be created if one has already been
+ * deployed using the same conduit key.
+ *
+ * @param conduitKey The conduit key used to deploy the conduit. Note that
+ * the first twenty bytes of the conduit key must match
+ * the caller of this contract.
+ * @param initialOwner The initial owner to set for the new conduit.
+ *
+ * @return conduit The address of the newly deployed conduit.
+ */
+ function createConduit(
+ bytes32 conduitKey,
+ address initialOwner
+ ) external override returns (address conduit) {
+ // Ensure that an initial owner has been supplied.
+ if (initialOwner == address(0)) {
+ revert InvalidInitialOwner();
+ }
+
+ // If the first 20 bytes of the conduit key do not match the caller...
+ if (address(uint160(bytes20(conduitKey))) != msg.sender) {
+ // Revert with an error indicating that the creator is invalid.
+ revert InvalidCreator();
+ }
+
+ // Derive address from deployer, conduit key and creation code hash.
+ conduit = address(
+ uint160(
+ uint256(
+ keccak256(
+ abi.encodePacked(
+ bytes1(0xff),
+ address(this),
+ conduitKey,
+ _CONDUIT_CREATION_CODE_HASH
+ )
+ )
+ )
+ )
+ );
+
+ // If derived conduit exists, as evidenced by comparing runtime code...
+ if (conduit.codehash == _CONDUIT_RUNTIME_CODE_HASH) {
+ // Revert with an error indicating that the conduit already exists.
+ revert ConduitAlreadyExists(conduit);
+ }
+
+ // Deploy the conduit via CREATE2 using the conduit key as the salt.
+ if (conduitNum == 0) {
+ new ConduitMock{ salt: conduitKey }();
+ } else if (conduitNum == 1) {
+ new ConduitMockRevertNoReason{ salt: conduitKey }();
+ } else if (conduitNum == 2) {
+ new ConduitMockInvalidMagic{ salt: conduitKey }();
+ } else if (conduitNum == 3) {
+ new ConduitMockRevertBytes{ salt: conduitKey }();
+ }
+ // Initialize storage variable referencing conduit properties.
+ ConduitProperties storage conduitProperties = _conduits[conduit];
+
+ // Set the supplied initial owner as the owner of the conduit.
+ conduitProperties.owner = initialOwner;
+
+ // Set conduit key used to deploy the conduit to enable reverse lookup.
+ conduitProperties.key = conduitKey;
+
+ // Emit an event indicating that the conduit has been deployed.
+ emit NewConduit(conduit, conduitKey);
+
+ // Emit an event indicating that conduit ownership has been assigned.
+ emit OwnershipTransferred(conduit, address(0), initialOwner);
+ }
+
+ /**
+ * @notice Open or close a channel on a given conduit, thereby allowing the
+ * specified account to execute transfers against that conduit.
+ * Extreme care must be taken when updating channels, as malicious
+ * or vulnerable channels can transfer any ERC20, ERC721 and ERC1155
+ * tokens where the token holder has granted the conduit approval.
+ * Only the owner of the conduit in question may call this function.
+ *
+ * @param conduit The conduit for which to open or close the channel.
+ * @param channel The channel to open or close on the conduit.
+ * @param isOpen A boolean indicating whether to open or close the channel.
+ */
+ function updateChannel(
+ address conduit,
+ address channel,
+ bool isOpen
+ ) external override {
+ // Ensure the caller is the current owner of the conduit in question.
+ _assertCallerIsConduitOwner(conduit);
+
+ // Call the conduit, updating the channel.
+ ConduitInterface(conduit).updateChannel(channel, isOpen);
+
+ // Retrieve storage region where channels for the conduit are tracked.
+ ConduitProperties storage conduitProperties = _conduits[conduit];
+
+ // Retrieve the index, if one currently exists, for the updated channel.
+ uint256 channelIndexPlusOne = (
+ conduitProperties.channelIndexesPlusOne[channel]
+ );
+
+ // Determine whether the updated channel is already tracked as open.
+ bool channelPreviouslyOpen = channelIndexPlusOne != 0;
+
+ // If the channel has been set to open and was previously closed...
+ if (isOpen && !channelPreviouslyOpen) {
+ // Add the channel to the channels array for the conduit.
+ conduitProperties.channels.push(channel);
+
+ // Add new open channel length to associated mapping as index + 1.
+ conduitProperties.channelIndexesPlusOne[channel] = (
+ conduitProperties.channels.length
+ );
+ } else if (!isOpen && channelPreviouslyOpen) {
+ // Set a previously open channel as closed via "swap & pop" method.
+ // Decrement located index to get the index of the closed channel.
+ uint256 removedChannelIndex;
+
+ // Skip underflow check as channelPreviouslyOpen being true ensures
+ // that channelIndexPlusOne is nonzero.
+ unchecked {
+ removedChannelIndex = channelIndexPlusOne - 1;
+ }
+
+ // Use length of channels array to determine index of last channel.
+ uint256 finalChannelIndex = conduitProperties.channels.length - 1;
+
+ // If closed channel is not last channel in the channels array...
+ if (finalChannelIndex != removedChannelIndex) {
+ // Retrieve the final channel and place the value on the stack.
+ address finalChannel = (
+ conduitProperties.channels[finalChannelIndex]
+ );
+
+ // Overwrite the removed channel using the final channel value.
+ conduitProperties.channels[removedChannelIndex] = finalChannel;
+
+ // Update final index in associated mapping to removed index.
+ conduitProperties.channelIndexesPlusOne[finalChannel] = (
+ channelIndexPlusOne
+ );
+ }
+
+ // Remove the last channel from the channels array for the conduit.
+ conduitProperties.channels.pop();
+
+ // Remove the closed channel from associated mapping of indexes.
+ delete conduitProperties.channelIndexesPlusOne[channel];
+ }
+ }
+
+ /**
+ * @notice Initiate conduit ownership transfer by assigning a new potential
+ * owner for the given conduit. Once set, the new potential owner
+ * may call `acceptOwnership` to claim ownership of the conduit.
+ * Only the owner of the conduit in question may call this function.
+ *
+ * @param conduit The conduit for which to initiate ownership transfer.
+ * @param newPotentialOwner The new potential owner of the conduit.
+ */
+ function transferOwnership(
+ address conduit,
+ address newPotentialOwner
+ ) external override {
+ // Ensure the caller is the current owner of the conduit in question.
+ _assertCallerIsConduitOwner(conduit);
+
+ // Ensure the new potential owner is not an invalid address.
+ if (newPotentialOwner == address(0)) {
+ revert NewPotentialOwnerIsZeroAddress(conduit);
+ }
+
+ // Ensure the new potential owner is not already set.
+ if (newPotentialOwner == _conduits[conduit].potentialOwner) {
+ revert NewPotentialOwnerAlreadySet(conduit, newPotentialOwner);
+ }
+
+ // Emit an event indicating that the potential owner has been updated.
+ emit PotentialOwnerUpdated(newPotentialOwner);
+
+ // Set the new potential owner as the potential owner of the conduit.
+ _conduits[conduit].potentialOwner = newPotentialOwner;
+ }
+
+ /**
+ * @notice Clear the currently set potential owner, if any, from a conduit.
+ * Only the owner of the conduit in question may call this function.
+ *
+ * @param conduit The conduit for which to cancel ownership transfer.
+ */
+ function cancelOwnershipTransfer(address conduit) external override {
+ // Ensure the caller is the current owner of the conduit in question.
+ _assertCallerIsConduitOwner(conduit);
+
+ // Ensure that ownership transfer is currently possible.
+ if (_conduits[conduit].potentialOwner == address(0)) {
+ revert NoPotentialOwnerCurrentlySet(conduit);
+ }
+
+ // Emit an event indicating that the potential owner has been cleared.
+ emit PotentialOwnerUpdated(address(0));
+
+ // Clear the current new potential owner from the conduit.
+ _conduits[conduit].potentialOwner = address(0);
+ }
+
+ /**
+ * @notice Accept ownership of a supplied conduit. Only accounts that the
+ * current owner has set as the new potential owner may call this
+ * function.
+ *
+ * @param conduit The conduit for which to accept ownership.
+ */
+ function acceptOwnership(address conduit) external override {
+ // Ensure that the conduit in question exists.
+ _assertConduitExists(conduit);
+
+ // If caller does not match current potential owner of the conduit...
+ if (msg.sender != _conduits[conduit].potentialOwner) {
+ // Revert, indicating that caller is not current potential owner.
+ revert CallerIsNotNewPotentialOwner(conduit);
+ }
+
+ // Emit an event indicating that the potential owner has been cleared.
+ emit PotentialOwnerUpdated(address(0));
+
+ // Clear the current new potential owner from the conduit.
+ _conduits[conduit].potentialOwner = address(0);
+
+ // Emit an event indicating conduit ownership has been transferred.
+ emit OwnershipTransferred(
+ conduit,
+ _conduits[conduit].owner,
+ msg.sender
+ );
+
+ // Set the caller as the owner of the conduit.
+ _conduits[conduit].owner = msg.sender;
+ }
+
+ /**
+ * @notice Retrieve the current owner of a deployed conduit.
+ *
+ * @param conduit The conduit for which to retrieve the associated owner.
+ *
+ * @return owner The owner of the supplied conduit.
+ */
+ function ownerOf(
+ address conduit
+ ) external view override returns (address owner) {
+ // Ensure that the conduit in question exists.
+ _assertConduitExists(conduit);
+
+ // Retrieve the current owner of the conduit in question.
+ owner = _conduits[conduit].owner;
+ }
+
+ /**
+ * @notice Retrieve the conduit key for a deployed conduit via reverse
+ * lookup.
+ *
+ * @param conduit The conduit for which to retrieve the associated conduit
+ * key.
+ *
+ * @return conduitKey The conduit key used to deploy the supplied conduit.
+ */
+ function getKey(
+ address conduit
+ ) external view override returns (bytes32 conduitKey) {
+ // Attempt to retrieve a conduit key for the conduit in question.
+ conduitKey = _conduits[conduit].key;
+
+ // Revert if no conduit key was located.
+ if (conduitKey == bytes32(0)) {
+ revert NoConduit();
+ }
+ }
+
+ /**
+ * @notice Derive the conduit associated with a given conduit key and
+ * determine whether that conduit exists (i.e. whether it has been
+ * deployed).
+ *
+ * @param conduitKey The conduit key used to derive the conduit.
+ *
+ * @return conduit The derived address of the conduit.
+ * @return exists A boolean indicating whether the derived conduit has been
+ * deployed or not.
+ */
+ function getConduit(
+ bytes32 conduitKey
+ ) external view override returns (address conduit, bool exists) {
+ // Derive address from deployer, conduit key and creation code hash.
+ conduit = address(
+ uint160(
+ uint256(
+ keccak256(
+ abi.encodePacked(
+ bytes1(0xff),
+ address(this),
+ conduitKey,
+ _CONDUIT_CREATION_CODE_HASH
+ )
+ )
+ )
+ )
+ );
+
+ // Determine whether conduit exists by retrieving its runtime code.
+ exists = (conduit.codehash == _CONDUIT_RUNTIME_CODE_HASH);
+ }
+
+ /**
+ * @notice Retrieve the potential owner, if any, for a given conduit. The
+ * current owner may set a new potential owner via
+ * `transferOwnership` and that owner may then accept ownership of
+ * the conduit in question via `acceptOwnership`.
+ *
+ * @param conduit The conduit for which to retrieve the potential owner.
+ *
+ * @return potentialOwner The potential owner, if any, for the conduit.
+ */
+ function getPotentialOwner(
+ address conduit
+ ) external view override returns (address potentialOwner) {
+ // Ensure that the conduit in question exists.
+ _assertConduitExists(conduit);
+
+ // Retrieve the current potential owner of the conduit in question.
+ potentialOwner = _conduits[conduit].potentialOwner;
+ }
+
+ /**
+ * @notice Retrieve the status (either open or closed) of a given channel on
+ * a conduit.
+ *
+ * @param conduit The conduit for which to retrieve the channel status.
+ * @param channel The channel for which to retrieve the status.
+ *
+ * @return isOpen The status of the channel on the given conduit.
+ */
+ function getChannelStatus(
+ address conduit,
+ address channel
+ ) external view override returns (bool isOpen) {
+ // Ensure that the conduit in question exists.
+ _assertConduitExists(conduit);
+
+ // Retrieve the current channel status for the conduit in question.
+ isOpen = _conduits[conduit].channelIndexesPlusOne[channel] != 0;
+ }
+
+ /**
+ * @notice Retrieve the total number of open channels for a given conduit.
+ *
+ * @param conduit The conduit for which to retrieve the total channel count.
+ *
+ * @return totalChannels The total number of open channels for the conduit.
+ */
+ function getTotalChannels(
+ address conduit
+ ) external view override returns (uint256 totalChannels) {
+ // Ensure that the conduit in question exists.
+ _assertConduitExists(conduit);
+
+ // Retrieve the total open channel count for the conduit in question.
+ totalChannels = _conduits[conduit].channels.length;
+ }
+
+ /**
+ * @notice Retrieve an open channel at a specific index for a given conduit.
+ * Note that the index of a channel can change as a result of other
+ * channels being closed on the conduit.
+ *
+ * @param conduit The conduit for which to retrieve the open channel.
+ * @param channelIndex The index of the channel in question.
+ *
+ * @return channel The open channel, if any, at the specified channel index.
+ */
+ function getChannel(
+ address conduit,
+ uint256 channelIndex
+ ) external view override returns (address channel) {
+ // Ensure that the conduit in question exists.
+ _assertConduitExists(conduit);
+
+ // Retrieve the total open channel count for the conduit in question.
+ uint256 totalChannels = _conduits[conduit].channels.length;
+
+ // Ensure that the supplied index is within range.
+ if (channelIndex >= totalChannels) {
+ revert ChannelOutOfRange(conduit);
+ }
+
+ // Retrieve the channel at the given index.
+ channel = _conduits[conduit].channels[channelIndex];
+ }
+
+ /**
+ * @notice Retrieve all open channels for a given conduit. Note that calling
+ * this function for a conduit with many channels will revert with
+ * an out-of-gas error.
+ *
+ * @param conduit The conduit for which to retrieve open channels.
+ *
+ * @return channels An array of open channels on the given conduit.
+ */
+ function getChannels(
+ address conduit
+ ) external view override returns (address[] memory channels) {
+ // Ensure that the conduit in question exists.
+ _assertConduitExists(conduit);
+
+ // Retrieve all of the open channels on the conduit in question.
+ channels = _conduits[conduit].channels;
+ }
+
+ /**
+ * @dev Retrieve the conduit creation code and runtime code hashes.
+ */
+ function getConduitCodeHashes()
+ external
+ view
+ override
+ returns (bytes32 creationCodeHash, bytes32 runtimeCodeHash)
+ {
+ // Retrieve the conduit creation code hash from runtime.
+ creationCodeHash = _CONDUIT_CREATION_CODE_HASH;
+
+ // Retrieve the conduit runtime code hash from runtime.
+ runtimeCodeHash = _CONDUIT_RUNTIME_CODE_HASH;
+ }
+
+ /**
+ * @dev Private view function to revert if the caller is not the owner of a
+ * given conduit.
+ *
+ * @param conduit The conduit for which to assert ownership.
+ */
+ function _assertCallerIsConduitOwner(address conduit) private view {
+ // Ensure that the conduit in question exists.
+ _assertConduitExists(conduit);
+
+ // If the caller does not match the current owner of the conduit...
+ if (msg.sender != _conduits[conduit].owner) {
+ // Revert, indicating that the caller is not the owner.
+ revert CallerIsNotOwner(conduit);
+ }
+ }
+
+ /**
+ * @dev Private view function to revert if a given conduit does not exist.
+ *
+ * @param conduit The conduit for which to assert existence.
+ */
+ function _assertConduitExists(address conduit) private view {
+ // Attempt to retrieve a conduit key for the conduit in question.
+ if (_conduits[conduit].key == bytes32(0)) {
+ // Revert if no conduit key was located.
+ revert NoConduit();
+ }
+ }
+}
diff --git a/contracts/test/ConduitMock.sol b/contracts/test/ConduitMock.sol
new file mode 100644
index 000000000..aad1342e1
--- /dev/null
+++ b/contracts/test/ConduitMock.sol
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
+
+import {
+ ConduitBatch1155Transfer,
+ ConduitTransfer
+} from "../conduit/lib/ConduitStructs.sol";
+
+contract ConduitMock is ConduitInterface {
+ constructor() {}
+
+ function execute(
+ ConduitTransfer[] calldata /* transfers */
+ ) external pure override returns (bytes4) {
+ // Return the valid magic value.
+ return 0x4ce34aa2;
+ }
+
+ function executeBatch1155(
+ ConduitBatch1155Transfer[] calldata /* batch1155Transfers */
+ ) external view override returns (bytes4 magicValue) {}
+
+ function executeWithBatch1155(
+ ConduitTransfer[] calldata /* standardTransfers */,
+ ConduitBatch1155Transfer[] calldata /* batch1155Transfers */
+ ) external view override returns (bytes4 magicValue) {}
+
+ function updateChannel(address channel, bool isOpen) external override {}
+}
diff --git a/contracts/test/ConduitMockInvalidMagic.sol b/contracts/test/ConduitMockInvalidMagic.sol
new file mode 100644
index 000000000..0a822677c
--- /dev/null
+++ b/contracts/test/ConduitMockInvalidMagic.sol
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
+
+import {
+ ConduitBatch1155Transfer,
+ ConduitTransfer
+} from "../conduit/lib/ConduitStructs.sol";
+
+contract ConduitMockInvalidMagic is ConduitInterface {
+ constructor() {}
+
+ function execute(
+ ConduitTransfer[] calldata /* transfers */
+ ) external pure override returns (bytes4) {
+ return 0xabcd0000;
+ }
+
+ function executeBatch1155(
+ ConduitBatch1155Transfer[] calldata /* batch1155Transfers */
+ ) external view override returns (bytes4 magicValue) {}
+
+ function executeWithBatch1155(
+ ConduitTransfer[] calldata /* standardTransfers */,
+ ConduitBatch1155Transfer[] calldata /* batch1155Transfers */
+ ) external view override returns (bytes4 magicValue) {}
+
+ function updateChannel(address channel, bool isOpen) external override {}
+}
diff --git a/contracts/test/ConduitMockRevertBytes.sol b/contracts/test/ConduitMockRevertBytes.sol
new file mode 100644
index 000000000..38e97dfa9
--- /dev/null
+++ b/contracts/test/ConduitMockRevertBytes.sol
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
+
+import {
+ ConduitBatch1155Transfer,
+ ConduitTransfer
+} from "../conduit/lib/ConduitStructs.sol";
+
+contract ConduitMockRevertBytes is ConduitInterface {
+ constructor() {}
+
+ error CustomError();
+
+ function execute(
+ ConduitTransfer[] calldata /* transfers */
+ ) external pure override returns (bytes4) {
+ revert CustomError();
+ }
+
+ function executeBatch1155(
+ ConduitBatch1155Transfer[] calldata /* batch1155Transfers */
+ ) external view override returns (bytes4 magicValue) {}
+
+ function executeWithBatch1155(
+ ConduitTransfer[] calldata /* standardTransfers */,
+ ConduitBatch1155Transfer[] calldata /* batch1155Transfers */
+ ) external view override returns (bytes4 magicValue) {}
+
+ function updateChannel(address channel, bool isOpen) external override {}
+}
diff --git a/contracts/test/ConduitMockRevertNoReason.sol b/contracts/test/ConduitMockRevertNoReason.sol
new file mode 100644
index 000000000..c11155e19
--- /dev/null
+++ b/contracts/test/ConduitMockRevertNoReason.sol
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
+
+import {
+ ConduitBatch1155Transfer,
+ ConduitTransfer
+} from "../conduit/lib/ConduitStructs.sol";
+
+contract ConduitMockRevertNoReason is ConduitInterface {
+ constructor() {}
+
+ function execute(
+ ConduitTransfer[] calldata /* transfers */
+ ) external pure override returns (bytes4) {
+ // Revert without reason string.
+ revert();
+ }
+
+ function executeBatch1155(
+ ConduitBatch1155Transfer[] calldata /* batch1155Transfers */
+ ) external view override returns (bytes4 magicValue) {}
+
+ function executeWithBatch1155(
+ ConduitTransfer[] calldata /* standardTransfers */,
+ ConduitBatch1155Transfer[] calldata /* batch1155Transfers */
+ ) external view override returns (bytes4 magicValue) {}
+
+ function updateChannel(address channel, bool isOpen) external override {}
+}
diff --git a/contracts/test/EIP1271Wallet.sol b/contracts/test/EIP1271Wallet.sol
index b0e5b50a1..997e6768d 100644
--- a/contracts/test/EIP1271Wallet.sol
+++ b/contracts/test/EIP1271Wallet.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
interface ERC20ApprovalInterface {
function approve(address, uint256) external returns (bool);
@@ -58,11 +58,10 @@ contract EIP1271Wallet {
token.setApprovalForAll(operator, true);
}
- function isValidSignature(bytes32 digest, bytes memory signature)
- external
- view
- returns (bytes4)
- {
+ function isValidSignature(
+ bytes32 digest,
+ bytes memory signature
+ ) external view returns (bytes4) {
if (digestApproved[digest]) {
return _EIP_1271_MAGIC_VALUE;
}
diff --git a/contracts/test/ERC1155BatchRecipient.sol b/contracts/test/ERC1155BatchRecipient.sol
index 2704c169d..360ad9d1d 100644
--- a/contracts/test/ERC1155BatchRecipient.sol
+++ b/contracts/test/ERC1155BatchRecipient.sol
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
contract ERC1155BatchRecipient {
error UnexpectedBatchData();
diff --git a/contracts/test/ERC721ReceiverMock.sol b/contracts/test/ERC721ReceiverMock.sol
new file mode 100644
index 000000000..b6c409e9c
--- /dev/null
+++ b/contracts/test/ERC721ReceiverMock.sol
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { IERC721Receiver } from "../interfaces/IERC721Receiver.sol";
+
+contract ERC721ReceiverMock is IERC721Receiver {
+ enum Error {
+ None,
+ RevertWithMessage,
+ RevertWithoutMessage,
+ Panic
+ }
+
+ bytes4 private immutable _retval;
+ Error private immutable _error;
+
+ event Received(
+ address operator,
+ address from,
+ uint256 tokenId,
+ bytes data,
+ uint256 gas
+ );
+
+ constructor(bytes4 retval, Error error) {
+ _retval = retval;
+ _error = error;
+ }
+
+ function onERC721Received(
+ address operator,
+ address from,
+ uint256 tokenId,
+ bytes memory data
+ ) public override returns (bytes4) {
+ if (_error == Error.RevertWithMessage) {
+ revert("ERC721ReceiverMock: reverting");
+ } else if (_error == Error.RevertWithoutMessage) {
+ revert();
+ } else if (_error == Error.Panic) {
+ uint256 a = uint256(0) / uint256(0);
+ a;
+ }
+ emit Received(operator, from, tokenId, data, gasleft());
+ return _retval;
+ }
+}
diff --git a/contracts/test/ExcessReturnDataRecipient.sol b/contracts/test/ExcessReturnDataRecipient.sol
index 7196920ab..8d7fd3363 100644
--- a/contracts/test/ExcessReturnDataRecipient.sol
+++ b/contracts/test/ExcessReturnDataRecipient.sol
@@ -1,5 +1,5 @@
-//SPDX-License-Identifier: Unlicense
-pragma solidity >=0.8.7;
+// SPDX-License-Identifier: Unlicense
+pragma solidity ^0.8.13;
contract ExcessReturnDataRecipient {
uint256 private revertDataSize;
diff --git a/contracts/test/InvalidERC721Recipient.sol b/contracts/test/InvalidERC721Recipient.sol
new file mode 100644
index 000000000..66a4b2d42
--- /dev/null
+++ b/contracts/test/InvalidERC721Recipient.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+interface IERC721Receiver {
+ function onERC721Received(
+ address,
+ address,
+ uint256,
+ bytes calldata
+ ) external returns (bytes4);
+}
+
+contract InvalidERC721Recipient is IERC721Receiver {
+ function onERC721Received(
+ address /* operator */,
+ address /* from */,
+ uint256 /* tokenId */,
+ bytes calldata /* data */
+ ) external pure override returns (bytes4) {
+ return 0xabcd0000;
+ }
+}
diff --git a/contracts/test/InvalidEthRecipient.sol b/contracts/test/InvalidEthRecipient.sol
new file mode 100644
index 000000000..831fc9a01
--- /dev/null
+++ b/contracts/test/InvalidEthRecipient.sol
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+contract InvalidEthRecipient {
+ receive() external payable {
+ revert(
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ );
+ }
+}
diff --git a/contracts/test/Reenterer.sol b/contracts/test/Reenterer.sol
index 18d7eda0d..03eb2239d 100644
--- a/contracts/test/Reenterer.sol
+++ b/contracts/test/Reenterer.sol
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
contract Reenterer {
+ bool public isPrepared;
address public target;
uint256 public msgValue;
bytes public callData;
@@ -16,20 +17,24 @@ contract Reenterer {
target = targetToUse;
msgValue = msgValueToUse;
callData = callDataToUse;
+ isPrepared = true;
}
receive() external payable {
- (bool success, bytes memory returnData) = target.call{
- value: msgValue
- }(callData);
+ if (isPrepared) {
+ (bool success, bytes memory returnData) = target.call{
+ value: msgValue
+ }(callData);
- if (!success) {
- assembly {
- returndatacopy(0, 0, returndatasize())
- revert(0, returndatasize())
+ if (!success) {
+ assembly {
+ returndatacopy(0, 0, returndatasize())
+ revert(0, returndatasize())
+ }
}
- }
+ emit Reentered(returnData);
- emit Reentered(returnData);
+ isPrepared = false;
+ }
}
}
diff --git a/contracts/test/TestBadContractOfferer.sol b/contracts/test/TestBadContractOfferer.sol
new file mode 100644
index 000000000..8b2272eae
--- /dev/null
+++ b/contracts/test/TestBadContractOfferer.sol
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ERC721Interface } from "../interfaces/AbridgedTokenInterfaces.sol";
+
+import {
+ ContractOffererInterface
+} from "../interfaces/ContractOffererInterface.sol";
+
+import { ItemType } from "../lib/ConsiderationEnums.sol";
+
+import {
+ ReceivedItem,
+ Schema,
+ SpentItem
+} from "../lib/ConsiderationStructs.sol";
+
+contract TestBadContractOfferer is ContractOffererInterface {
+ error IntentionalRevert();
+
+ address private immutable seaport;
+ ERC721Interface token;
+
+ constructor(address _seaport, ERC721Interface _token) {
+ seaport = _seaport;
+ token = _token;
+ ERC721Interface(token).setApprovalForAll(seaport, true);
+ }
+
+ receive() external payable {}
+
+ /**
+ * @dev Generates an order with the specified minimum and maximum spent items,
+ * and the optional extra data.
+ *
+ * @param a Fulfiller, unused here.
+ * @param b The minimum items that the caller is willing to
+ * receive.
+ * @param c maximumSent, unused here.
+ * @param d context, unused here.
+ *
+ * @return offer A tuple containing the offer items.
+ * @return consideration A tuple containing the consideration items.
+ */
+ function generateOrder(
+ address a,
+ SpentItem[] calldata b,
+ SpentItem[] calldata c,
+ bytes calldata d
+ )
+ external
+ virtual
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ return previewOrder(a, a, b, c, d);
+ }
+
+ /**
+ * @dev View function to preview an order generated in response to a minimum
+ * set of received items, maximum set of spent items, and context
+ * (supplied as extraData).
+ *
+ * @param - caller, unused here.
+ * @param - fulfiller, unused here.
+ * @param minimumReceived The minimum received set.
+ * @param - maximumSpent, unused here.
+ * @param - context, unused here.
+ *
+ * @return offer The offer for the order.
+ * @return consideration The consideration for the order.
+ */
+ function previewOrder(
+ address,
+ address,
+ SpentItem[] calldata minimumReceived,
+ SpentItem[] calldata maximumSpent,
+ bytes calldata
+ )
+ public
+ view
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ if (minimumReceived[0].identifier == 1) {
+ offer = minimumReceived;
+ consideration = new ReceivedItem[](1);
+ consideration[0] = ReceivedItem({
+ itemType: ItemType.NATIVE,
+ token: address(0),
+ identifier: 0,
+ amount: 100,
+ recipient: payable(address(this))
+ });
+ return (offer, consideration);
+ } else if (minimumReceived[0].identifier == 2) {
+ // return nothing
+ assembly {
+ return(0, 0)
+ }
+ } else if (minimumReceived[0].identifier == 3) {
+ revert IntentionalRevert();
+ } else {
+ // return garbage
+ bytes32 h1 = keccak256(abi.encode(minimumReceived));
+ bytes32 h2 = keccak256(abi.encode(maximumSpent));
+ assembly {
+ mstore(0x00, h1)
+ mstore(0x20, h2)
+ return(0, 0x100)
+ }
+ }
+ }
+
+ function ratifyOrder(
+ SpentItem[] calldata /* offer */,
+ ReceivedItem[] calldata /* consideration */,
+ bytes calldata /* context */,
+ bytes32[] calldata /* orderHashes */,
+ uint256 /* contractNonce */
+ ) external pure override returns (bytes4 /* ratifyOrderMagicValue */) {
+ return TestBadContractOfferer.ratifyOrder.selector;
+ }
+
+ /**
+ * @dev Returns the metadata for this contract offerer.
+ */
+ function getSeaportMetadata()
+ external
+ pure
+ override
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ )
+ {
+ schemas = new Schema[](1);
+ schemas[0].id = 1337;
+ schemas[0].metadata = new bytes(0);
+
+ return ("TestBadContractOfferer", schemas);
+ }
+}
diff --git a/contracts/test/TestContractOfferer.sol b/contracts/test/TestContractOfferer.sol
new file mode 100644
index 000000000..4840b16e8
--- /dev/null
+++ b/contracts/test/TestContractOfferer.sol
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ ERC20Interface,
+ ERC721Interface,
+ ERC1155Interface
+} from "../interfaces/AbridgedTokenInterfaces.sol";
+
+import {
+ ContractOffererInterface
+} from "../interfaces/ContractOffererInterface.sol";
+
+import { ItemType } from "../lib/ConsiderationEnums.sol";
+
+import {
+ ReceivedItem,
+ Schema,
+ SpentItem
+} from "../lib/ConsiderationStructs.sol";
+
+/**
+ * @title TestContractOfferer
+ * @author 0age
+ * @notice TestContractOfferer is a maximally simple contract offerer. It offers
+ * a single item and expects to receive back another single item, and
+ * ignores all parameters supplied to it when previewing or generating
+ * an order. The offered item is placed into this contract as part of
+ * deployment and the corresponding token approvals are set for Seaport.
+ */
+contract TestContractOfferer is ContractOffererInterface {
+ error OrderUnavailable();
+
+ address private immutable _SEAPORT;
+
+ SpentItem private _available;
+ SpentItem private _required;
+
+ bool public ready;
+ bool public fulfilled;
+
+ uint256 public extraAvailable;
+ uint256 public extraRequired;
+
+ constructor(address seaport) {
+ // Set immutable values and storage variables.
+ _SEAPORT = seaport;
+ fulfilled = false;
+ ready = false;
+ extraAvailable = 0;
+ extraRequired = 0;
+ }
+
+ receive() external payable {}
+
+ /// In case of criteria based orders and non-wildcard items, the member
+ /// `available.identifier` would correspond to the `identifierOrCriteria`
+ /// i.e., the merkle-root.
+ /// @param identifier corresponds to the actual token-id that gets transferred.
+ function activateWithCriteria(
+ SpentItem memory available,
+ SpentItem memory required,
+ uint256 identifier
+ ) public {
+ if (ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ if (available.itemType == ItemType.ERC721_WITH_CRITERIA) {
+ ERC721Interface token = ERC721Interface(available.token);
+
+ token.transferFrom(msg.sender, address(this), identifier);
+
+ token.setApprovalForAll(_SEAPORT, true);
+ } else if (available.itemType == ItemType.ERC1155_WITH_CRITERIA) {
+ ERC1155Interface token = ERC1155Interface(available.token);
+
+ token.safeTransferFrom(
+ msg.sender,
+ address(this),
+ identifier,
+ available.amount,
+ ""
+ );
+
+ token.setApprovalForAll(_SEAPORT, true);
+ }
+
+ // Set storage variables.
+ _available = available;
+ _required = required;
+ ready = true;
+ }
+
+ function activate(
+ SpentItem memory available,
+ SpentItem memory required
+ ) public payable {
+ if (ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ // Retrieve the offered item and set associated approvals.
+ if (available.itemType == ItemType.NATIVE) {
+ available.amount = address(this).balance;
+ } else if (available.itemType == ItemType.ERC20) {
+ ERC20Interface token = ERC20Interface(available.token);
+
+ token.transferFrom(msg.sender, address(this), available.amount);
+
+ token.approve(_SEAPORT, available.amount);
+ } else if (available.itemType == ItemType.ERC721) {
+ ERC721Interface token = ERC721Interface(available.token);
+
+ token.transferFrom(msg.sender, address(this), available.identifier);
+
+ token.setApprovalForAll(_SEAPORT, true);
+ } else if (available.itemType == ItemType.ERC1155) {
+ ERC1155Interface token = ERC1155Interface(available.token);
+
+ token.safeTransferFrom(
+ msg.sender,
+ address(this),
+ available.identifier,
+ available.amount,
+ ""
+ );
+
+ token.setApprovalForAll(_SEAPORT, true);
+ }
+
+ // Set storage variables.
+ _available = available;
+ _required = required;
+ ready = true;
+ }
+
+ function extendAvailable() public {
+ if (!ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ extraAvailable++;
+
+ _available.amount /= 2;
+ }
+
+ function extendRequired() public {
+ if (!ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ extraRequired++;
+ }
+
+ function generateOrder(
+ address,
+ SpentItem[] calldata,
+ SpentItem[] calldata,
+ bytes calldata context
+ )
+ external
+ virtual
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ // Ensure the caller is Seaport & the order has not yet been fulfilled.
+ if (
+ !ready ||
+ fulfilled ||
+ msg.sender != _SEAPORT ||
+ context.length % 32 != 0
+ ) {
+ revert OrderUnavailable();
+ }
+
+ // Set the offer and consideration that were supplied during deployment.
+ offer = new SpentItem[](1 + extraAvailable);
+ consideration = new ReceivedItem[](1 + extraRequired);
+
+ for (uint256 i = 0; i < 1 + extraAvailable; ++i) {
+ offer[i] = _available;
+ }
+
+ for (uint256 i = 0; i < 1 + extraRequired; ++i) {
+ consideration[i] = ReceivedItem({
+ itemType: _required.itemType,
+ token: _required.token,
+ identifier: _required.identifier,
+ amount: _required.amount,
+ recipient: payable(address(this))
+ });
+ }
+
+ // Update storage to reflect that the order has been fulfilled.
+ fulfilled = true;
+ }
+
+ function previewOrder(
+ address caller,
+ address,
+ SpentItem[] calldata,
+ SpentItem[] calldata,
+ bytes calldata context
+ )
+ external
+ view
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ // Ensure the caller is Seaport & the order has not yet been fulfilled.
+ if (
+ !ready ||
+ fulfilled ||
+ caller != _SEAPORT ||
+ context.length % 32 != 0
+ ) {
+ revert OrderUnavailable();
+ }
+
+ // Set the offer and consideration that were supplied during deployment.
+ offer = new SpentItem[](1 + extraAvailable);
+ consideration = new ReceivedItem[](1 + extraRequired);
+
+ for (uint256 i = 0; i < 1 + extraAvailable; ++i) {
+ offer[i] = _available;
+ }
+
+ for (uint256 i = 0; i < 1 + extraRequired; ++i) {
+ consideration[i] = ReceivedItem({
+ itemType: _required.itemType,
+ token: _required.token,
+ identifier: _required.identifier,
+ amount: _required.amount,
+ recipient: payable(address(this))
+ });
+ }
+ }
+
+ function getInventory()
+ external
+ view
+ returns (SpentItem[] memory offerable, SpentItem[] memory receivable)
+ {
+ // Set offerable and receivable supplied at deployment if unfulfilled.
+ if (!ready || fulfilled) {
+ offerable = new SpentItem[](0);
+
+ receivable = new SpentItem[](0);
+ } else {
+ offerable = new SpentItem[](1 + extraAvailable);
+ for (uint256 i = 0; i < 1 + extraAvailable; ++i) {
+ offerable[i] = _available;
+ }
+
+ receivable = new SpentItem[](1 + extraRequired);
+ for (uint256 i = 0; i < 1 + extraRequired; ++i) {
+ receivable[i] = _required;
+ }
+ }
+ }
+
+ function ratifyOrder(
+ SpentItem[] calldata /* offer */,
+ ReceivedItem[] calldata /* consideration */,
+ bytes calldata context,
+ bytes32[] calldata orderHashes,
+ uint256 /* contractNonce */
+ )
+ external
+ pure
+ virtual
+ override
+ returns (bytes4 /* ratifyOrderMagicValue */)
+ {
+ if (context.length > 32 && context.length % 32 == 0) {
+ bytes32[] memory expectedOrderHashes = abi.decode(
+ context,
+ (bytes32[])
+ );
+
+ uint256 expectedLength = expectedOrderHashes.length;
+
+ if (expectedLength != orderHashes.length) {
+ revert("Revert on unexpected order hashes length");
+ }
+
+ for (uint256 i = 0; i < expectedLength; ++i) {
+ if (expectedOrderHashes[i] != orderHashes[i]) {
+ revert("Revert on unexpected order hash");
+ }
+ }
+ }
+
+ return ContractOffererInterface.ratifyOrder.selector;
+ }
+
+ function onERC1155Received(
+ address,
+ address,
+ uint256,
+ uint256,
+ bytes calldata
+ ) external pure returns (bytes4) {
+ return bytes4(0xf23a6e61);
+ }
+
+ /**
+ * @dev Returns the metadata for this contract offerer.
+ */
+ function getSeaportMetadata()
+ external
+ pure
+ override
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ )
+ {
+ schemas = new Schema[](1);
+ schemas[0].id = 1337;
+ schemas[0].metadata = new bytes(0);
+
+ return ("TestContractOfferer", schemas);
+ }
+}
diff --git a/contracts/test/TestContractOffererNativeToken.sol b/contracts/test/TestContractOffererNativeToken.sol
new file mode 100644
index 000000000..b1955fc6b
--- /dev/null
+++ b/contracts/test/TestContractOffererNativeToken.sol
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ ERC721Interface,
+ ERC1155Interface
+} from "../interfaces/AbridgedTokenInterfaces.sol";
+
+import {
+ ContractOffererInterface
+} from "../interfaces/ContractOffererInterface.sol";
+
+import { ItemType } from "../lib/ConsiderationEnums.sol";
+
+import {
+ ReceivedItem,
+ Schema,
+ SpentItem
+} from "../lib/ConsiderationStructs.sol";
+
+/**
+ * @title TestContractOffererNativeToken
+ */
+contract TestContractOffererNativeToken is ContractOffererInterface {
+ error OrderUnavailable();
+
+ address private immutable _SEAPORT;
+
+ SpentItem private _available;
+ SpentItem private _required;
+
+ bool public ready;
+ bool public fulfilled;
+
+ uint256 public extraAvailable;
+ uint256 public extraRequired;
+
+ constructor(address seaport) {
+ // Set immutable values and storage variables.
+ _SEAPORT = seaport;
+ fulfilled = false;
+ ready = false;
+ extraAvailable = 0;
+ extraRequired = 0;
+ }
+
+ receive() external payable {}
+
+ function activate(
+ SpentItem memory available,
+ SpentItem memory required
+ ) public payable {
+ if (ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ // Set storage variables.
+ _available = available;
+ _required = required;
+ ready = true;
+ }
+
+ /// In case of criteria based orders and non-wildcard items, the member
+ /// `available.identifier` would correspond to the `identifierOrCriteria`
+ /// i.e., the merkle-root.
+ /// @param identifier corresponds to the actual token-id that gets transferred.
+ function activateWithCriteria(
+ SpentItem memory available,
+ SpentItem memory required,
+ uint256 identifier
+ ) public {
+ if (ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ if (available.itemType == ItemType.ERC721_WITH_CRITERIA) {
+ ERC721Interface token = ERC721Interface(available.token);
+
+ token.transferFrom(msg.sender, address(this), identifier);
+
+ token.setApprovalForAll(_SEAPORT, true);
+ } else if (available.itemType == ItemType.ERC1155_WITH_CRITERIA) {
+ ERC1155Interface token = ERC1155Interface(available.token);
+
+ token.safeTransferFrom(
+ msg.sender,
+ address(this),
+ identifier,
+ available.amount,
+ ""
+ );
+
+ token.setApprovalForAll(_SEAPORT, true);
+ }
+
+ // Set storage variables.
+ _available = available;
+ _required = required;
+ ready = true;
+ }
+
+ function extendAvailable() public {
+ if (!ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ extraAvailable++;
+
+ _available.amount /= 2;
+ }
+
+ function extendRequired() public {
+ if (!ready || fulfilled) {
+ revert OrderUnavailable();
+ }
+
+ extraRequired++;
+ }
+
+ function generateOrder(
+ address,
+ SpentItem[] calldata minimumReceived,
+ SpentItem[] calldata maximumSpent,
+ bytes calldata /* context */
+ )
+ external
+ virtual
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ // Set the offer and consideration that were supplied during deployment.
+ offer = new SpentItem[](1);
+ consideration = new ReceivedItem[](1);
+
+ // Send eth to Seaport.
+ (bool success, ) = _SEAPORT.call{ value: minimumReceived[0].amount }(
+ ""
+ );
+
+ // Revert if transaction fails.
+ if (!success) {
+ assembly {
+ returndatacopy(0, 0, returndatasize())
+ revert(0, returndatasize())
+ }
+ }
+
+ // Set the offer item as the _available item in storage.
+ offer[0] = minimumReceived[0];
+
+ // Set the erc721 consideration item.
+ consideration[0] = ReceivedItem({
+ itemType: ItemType.ERC721,
+ token: maximumSpent[0].token,
+ identifier: maximumSpent[0].identifier,
+ amount: maximumSpent[0].amount,
+ recipient: payable(address(this))
+ });
+
+ // Update storage to reflect that the order has been fulfilled.
+ fulfilled = true;
+ }
+
+ function previewOrder(
+ address caller,
+ address,
+ SpentItem[] calldata,
+ SpentItem[] calldata,
+ bytes calldata context
+ )
+ external
+ view
+ override
+ returns (SpentItem[] memory offer, ReceivedItem[] memory consideration)
+ {
+ // Ensure the caller is Seaport & the order has not yet been fulfilled.
+ if (!ready || fulfilled || caller != _SEAPORT || context.length != 0) {
+ revert OrderUnavailable();
+ }
+
+ // Set the offer and consideration that were supplied during deployment.
+ offer = new SpentItem[](1 + extraAvailable);
+ consideration = new ReceivedItem[](1 + extraRequired);
+
+ for (uint256 i = 0; i < 1 + extraAvailable; ++i) {
+ offer[i] = _available;
+ }
+
+ for (uint256 i = 0; i < 1 + extraRequired; ++i) {
+ consideration[i] = ReceivedItem({
+ itemType: _required.itemType,
+ token: _required.token,
+ identifier: _required.identifier,
+ amount: _required.amount,
+ recipient: payable(address(this))
+ });
+ }
+ }
+
+ function getInventory()
+ external
+ view
+ returns (SpentItem[] memory offerable, SpentItem[] memory receivable)
+ {
+ // Set offerable and receivable supplied at deployment if unfulfilled.
+ if (!ready || fulfilled) {
+ offerable = new SpentItem[](0);
+
+ receivable = new SpentItem[](0);
+ } else {
+ offerable = new SpentItem[](1 + extraAvailable);
+ for (uint256 i = 0; i < 1 + extraAvailable; ++i) {
+ offerable[i] = _available;
+ }
+
+ receivable = new SpentItem[](1 + extraRequired);
+ for (uint256 i = 0; i < 1 + extraRequired; ++i) {
+ receivable[i] = _required;
+ }
+ }
+ }
+
+ function ratifyOrder(
+ SpentItem[] calldata /* offer */,
+ ReceivedItem[] calldata /* consideration */,
+ bytes calldata /* context */,
+ bytes32[] calldata /* orderHashes */,
+ uint256 /* contractNonce */
+ )
+ external
+ pure
+ virtual
+ override
+ returns (bytes4 /* ratifyOrderMagicValue */)
+ {
+ return ContractOffererInterface.ratifyOrder.selector;
+ }
+
+ function onERC1155Received(
+ address,
+ address,
+ uint256,
+ uint256,
+ bytes calldata
+ ) external pure returns (bytes4) {
+ return bytes4(0xf23a6e61);
+ }
+
+ /**
+ * @dev Returns the metadata for this contract offerer.
+ */
+ function getSeaportMetadata()
+ external
+ pure
+ override
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ )
+ {
+ schemas = new Schema[](1);
+ schemas[0].id = 1337;
+ schemas[0].metadata = new bytes(0);
+
+ return ("TestContractOffererNativeToken", schemas);
+ }
+}
diff --git a/contracts/test/TestERC1155.sol b/contracts/test/TestERC1155.sol
index 959dad790..ff967bec2 100644
--- a/contracts/test/TestERC1155.sol
+++ b/contracts/test/TestERC1155.sol
@@ -1,7 +1,7 @@
-//SPDX-License-Identifier: Unlicense
-pragma solidity >=0.8.7;
+// SPDX-License-Identifier: Unlicense
+pragma solidity ^0.8.13;
-import "@rari-capital/solmate/src/tokens/ERC1155.sol";
+import { ERC1155 } from "@rari-capital/solmate/src/tokens/ERC1155.sol";
// Used for minting test ERC1155s in our tests
contract TestERC1155 is ERC1155 {
diff --git a/contracts/test/TestERC1155Revert.sol b/contracts/test/TestERC1155Revert.sol
new file mode 100644
index 000000000..ac5742489
--- /dev/null
+++ b/contracts/test/TestERC1155Revert.sol
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity ^0.8.0;
+
+contract TestERC1155Revert {
+ function safeTransferFrom(
+ address /* from */,
+ address /* to */,
+ uint256 /* id */,
+ uint256 /* amount */,
+ bytes calldata /* data */
+ ) public pure {
+ revert(
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ );
+ }
+
+ function safeBatchTransferFrom(
+ address /* from */,
+ address /* to */,
+ uint256[] memory /* ids */,
+ uint256[] memory /* values */,
+ bytes memory /* data */
+ ) public pure {
+ revert("Some ERC1155 revert message for batch transfers");
+ }
+
+ function getRevertData() public pure returns (bytes memory) {
+ assembly {
+ mstore(0x40, 0)
+ mstore(0, shl(20, 1))
+ mstore(add(0x20, shl(20, 1)), 1)
+ return(0, add(0x20, shl(20, 1)))
+ }
+ }
+}
diff --git a/contracts/test/TestERC20.sol b/contracts/test/TestERC20.sol
index 9113ecf68..ea4abd901 100644
--- a/contracts/test/TestERC20.sol
+++ b/contracts/test/TestERC20.sol
@@ -1,7 +1,7 @@
-//SPDX-License-Identifier: Unlicense
-pragma solidity >=0.8.7;
+// SPDX-License-Identifier: Unlicense
+pragma solidity ^0.8.13;
-import "@rari-capital/solmate/src/tokens/ERC20.sol";
+import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
// Used for minting test ERC20s in our tests
contract TestERC20 is ERC20("Test20", "TST20", 18) {
diff --git a/contracts/test/TestERC20NotOk.sol b/contracts/test/TestERC20NotOk.sol
new file mode 100644
index 000000000..5d794f4df
--- /dev/null
+++ b/contracts/test/TestERC20NotOk.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity ^0.8.13;
+
+import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
+
+// Used for minting test ERC20s in our tests.
+contract TestERC20NotOk is ERC20("Test20NotOk", "TST20NO", 18) {
+ bool public notOk;
+
+ function mint(address to, uint256 amount) external returns (bool) {
+ _mint(to, amount);
+ return true;
+ }
+
+ function transferFrom(
+ address /* from */,
+ address /* to */,
+ uint256 /* amount */
+ ) public pure override returns (bool) {
+ return false;
+ }
+}
diff --git a/contracts/test/TestERC20Panic.sol b/contracts/test/TestERC20Panic.sol
new file mode 100644
index 000000000..1b204b256
--- /dev/null
+++ b/contracts/test/TestERC20Panic.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
+
+contract TestERC20Panic is ERC20("TestPanic", "PANIC", 18) {
+ function mint(address to, uint256 amount) external returns (bool) {
+ _mint(to, amount);
+ return true;
+ }
+
+ function transferFrom(
+ address /* from */,
+ address /* to */,
+ uint256 /* amount */
+ ) public pure override returns (bool) {
+ uint256 a = uint256(0) / uint256(0);
+ a;
+
+ return true;
+ }
+}
diff --git a/contracts/test/TestERC20Revert.sol b/contracts/test/TestERC20Revert.sol
new file mode 100644
index 000000000..37a01b137
--- /dev/null
+++ b/contracts/test/TestERC20Revert.sol
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
+
+contract TestERC20Revert is ERC20("TestRevert", "REVERT", 18) {
+ function mint(address to, uint256 amount) external {
+ _mint(to, amount);
+ }
+
+ function transferFrom(
+ address /* from */,
+ address /* to */,
+ uint256 /* amount */
+ ) public pure override returns (bool) {
+ revert(
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ );
+ }
+}
diff --git a/contracts/test/TestERC721.sol b/contracts/test/TestERC721.sol
index 5e9e143ee..e207d9ae1 100644
--- a/contracts/test/TestERC721.sol
+++ b/contracts/test/TestERC721.sol
@@ -1,7 +1,7 @@
-//SPDX-License-Identifier: Unlicense
-pragma solidity >=0.8.7;
+// SPDX-License-Identifier: Unlicense
+pragma solidity ^0.8.13;
-import "@rari-capital/solmate/src/tokens/ERC721.sol";
+import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol";
// Used for minting test ERC721s in our tests
contract TestERC721 is ERC721("Test721", "TST721") {
diff --git a/contracts/test/TestERC721Revert.sol b/contracts/test/TestERC721Revert.sol
new file mode 100644
index 000000000..8d820122f
--- /dev/null
+++ b/contracts/test/TestERC721Revert.sol
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity ^0.8.0;
+
+contract TestERC721Revert {
+ function transferFrom(
+ address /* from */,
+ address /* to */,
+ uint256 /* amount */
+ ) public pure {
+ revert(
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ );
+ }
+}
diff --git a/contracts/test/TestInvalidContractOfferer.sol b/contracts/test/TestInvalidContractOfferer.sol
new file mode 100644
index 000000000..627849945
--- /dev/null
+++ b/contracts/test/TestInvalidContractOfferer.sol
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ReceivedItem, SpentItem } from "../lib/ConsiderationStructs.sol";
+
+import { TestContractOfferer } from "./TestContractOfferer.sol";
+
+contract TestInvalidContractOfferer is TestContractOfferer {
+ error RevertWithData(bytes revertData);
+
+ constructor(address seaport) TestContractOfferer(seaport) {}
+
+ function generateOrder(
+ address,
+ SpentItem[] calldata,
+ SpentItem[] calldata,
+ bytes calldata context
+ )
+ external
+ pure
+ override
+ returns (SpentItem[] memory, ReceivedItem[] memory)
+ {
+ revert RevertWithData(context);
+ }
+}
diff --git a/contracts/test/TestInvalidContractOffererRatifyOrder.sol b/contracts/test/TestInvalidContractOffererRatifyOrder.sol
new file mode 100644
index 000000000..c9a81c84e
--- /dev/null
+++ b/contracts/test/TestInvalidContractOffererRatifyOrder.sol
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ReceivedItem, SpentItem } from "../lib/ConsiderationStructs.sol";
+
+import { TestContractOfferer } from "./TestContractOfferer.sol";
+
+contract TestInvalidContractOffererRatifyOrder is TestContractOfferer {
+ constructor(address seaport) TestContractOfferer(seaport) {}
+
+ function ratifyOrder(
+ SpentItem[] calldata,
+ ReceivedItem[] calldata,
+ bytes calldata,
+ bytes32[] calldata,
+ uint256
+ ) external pure override returns (bytes4) {
+ return bytes4(keccak256("throw"));
+ }
+}
diff --git a/contracts/test/TestPostExecution.sol b/contracts/test/TestPostExecution.sol
new file mode 100644
index 000000000..34dd469ce
--- /dev/null
+++ b/contracts/test/TestPostExecution.sol
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
+
+import { ERC721Interface } from "../interfaces/AbridgedTokenInterfaces.sol";
+
+import { ItemType } from "../lib/ConsiderationEnums.sol";
+
+import {
+ ReceivedItem,
+ Schema,
+ ZoneParameters
+} from "../lib/ConsiderationStructs.sol";
+
+contract TestPostExecution is ZoneInterface {
+ function validateOrder(
+ ZoneParameters calldata zoneParameters
+ ) external view override returns (bytes4 validOrderMagicValue) {
+ if (zoneParameters.consideration.length == 0) {
+ revert("No consideration items supplied");
+ }
+
+ ReceivedItem memory receivedItem = zoneParameters.consideration[0];
+
+ address currentOwner;
+ try
+ ERC721Interface(receivedItem.token).ownerOf(receivedItem.identifier)
+ returns (address owner) {
+ currentOwner = owner;
+ } catch {
+ revert("Unsupported consideration token type (must implement 721)");
+ }
+
+ if (receivedItem.itemType != ItemType.ERC721) {
+ revert("Validity check performed with unsupported item type");
+ }
+
+ // Note that endAmount has been repurposed as recipient; this interface
+ // still needs to be modified to return spent / received items.
+ if (receivedItem.amount != 1) {
+ // Note that this is currently failing in the matchOrder case.
+ revert("Returned item amount incorrectly modified");
+ }
+
+ if (currentOwner != receivedItem.recipient) {
+ revert("Validity check performed prior to execution");
+ }
+
+ validOrderMagicValue = ZoneInterface.validateOrder.selector;
+ }
+
+ /**
+ * @dev Returns the metadata for this zone.
+ */
+ function getSeaportMetadata()
+ external
+ pure
+ override
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ )
+ {
+ schemas = new Schema[](1);
+ schemas[0].id = 3003;
+ schemas[0].metadata = new bytes(0);
+
+ return ("TestPostExecution", schemas);
+ }
+}
diff --git a/contracts/test/TestZone.sol b/contracts/test/TestZone.sol
index 45f520be8..ca78521fc 100644
--- a/contracts/test/TestZone.sol
+++ b/contracts/test/TestZone.sol
@@ -1,61 +1,71 @@
// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.7;
+pragma solidity ^0.8.13;
import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
-// prettier-ignore
-import {
- AdvancedOrder,
- CriteriaResolver
-} from "../lib/ConsiderationStructs.sol";
+import { Schema, ZoneParameters } from "../lib/ConsiderationStructs.sol";
contract TestZone is ZoneInterface {
- function isValidOrder(
- bytes32 orderHash,
- address caller,
- address offerer,
- bytes32 zoneHash
+ function validateOrder(
+ ZoneParameters calldata zoneParameters
) external pure override returns (bytes4 validOrderMagicValue) {
- orderHash;
- caller;
- offerer;
-
- if (zoneHash == bytes32(uint256(1))) {
- revert("Revert on zone hash 1");
- } else if (zoneHash == bytes32(uint256(2))) {
+ if (zoneParameters.extraData.length == 0) {
+ if (zoneParameters.zoneHash == bytes32(uint256(1))) {
+ revert("Revert on zone hash 1");
+ } else if (zoneParameters.zoneHash == bytes32(uint256(2))) {
+ assembly {
+ revert(0, 0)
+ }
+ }
+ } else if (zoneParameters.extraData.length == 4) {
+ revert("Revert on extraData length 4");
+ } else if (zoneParameters.extraData.length == 5) {
assembly {
revert(0, 0)
}
- }
+ } else if (
+ zoneParameters.extraData.length > 32 &&
+ zoneParameters.extraData.length % 32 == 0
+ ) {
+ bytes32[] memory expectedOrderHashes = abi.decode(
+ zoneParameters.extraData,
+ (bytes32[])
+ );
- validOrderMagicValue = zoneHash != bytes32(uint256(3))
- ? ZoneInterface.isValidOrder.selector
- : bytes4(0xffffffff);
- }
+ uint256 expectedLength = expectedOrderHashes.length;
- function isValidOrderIncludingExtraData(
- bytes32 orderHash,
- address caller,
- AdvancedOrder calldata order,
- bytes32[] calldata priorOrderHashes,
- CriteriaResolver[] calldata criteriaResolvers
- ) external pure override returns (bytes4 validOrderMagicValue) {
- orderHash;
- caller;
- order;
- priorOrderHashes;
- criteriaResolvers;
+ if (expectedLength != zoneParameters.orderHashes.length) {
+ revert("Revert on unexpected order hashes length");
+ }
- if (order.extraData.length == 4) {
- revert("Revert on extraData length 4");
- } else if (order.extraData.length == 5) {
- assembly {
- revert(0, 0)
+ for (uint256 i = 0; i < expectedLength; ++i) {
+ if (expectedOrderHashes[i] != zoneParameters.orderHashes[i]) {
+ revert("Revert on unexpected order hash");
+ }
}
}
- validOrderMagicValue = order.parameters.zoneHash != bytes32(uint256(3))
- ? ZoneInterface.isValidOrder.selector
+ validOrderMagicValue = zoneParameters.zoneHash != bytes32(uint256(3))
+ ? ZoneInterface.validateOrder.selector
: bytes4(0xffffffff);
}
+
+ /**
+ * @dev Returns the metadata for this zone.
+ */
+ function getSeaportMetadata()
+ external
+ pure
+ override
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ )
+ {
+ schemas = new Schema[](1);
+ schemas[0].id = 3003;
+ schemas[0].metadata = new bytes(0);
+
+ return ("TestZone", schemas);
+ }
}
diff --git a/contracts/zones/PausableZone.sol b/contracts/zones/PausableZone.sol
new file mode 100644
index 000000000..6abf08d1d
--- /dev/null
+++ b/contracts/zones/PausableZone.sol
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
+
+import {
+ PausableZoneEventsAndErrors
+} from "./interfaces/PausableZoneEventsAndErrors.sol";
+
+import { SeaportInterface } from "../interfaces/SeaportInterface.sol";
+
+import {
+ AdvancedOrder,
+ CriteriaResolver,
+ Execution,
+ Fulfillment,
+ Order,
+ OrderComponents,
+ Schema,
+ ZoneParameters
+} from "../lib/ConsiderationStructs.sol";
+
+import { PausableZoneInterface } from "./interfaces/PausableZoneInterface.sol";
+
+/**
+ * @title PausableZone
+ * @author cupOJoseph, BCLeFevre, ryanio
+ * @notice PausableZone is a simple zone implementation that approves every
+ * order. It can be self-destructed by its controller to pause
+ * restricted orders that have it set as their zone. Note that this zone
+ * cannot execute orders that return native tokens to the fulfiller.
+ */
+contract PausableZone is
+ PausableZoneEventsAndErrors,
+ ZoneInterface,
+ PausableZoneInterface
+{
+ // Set an immutable controller that can pause the zone & update an operator.
+ address internal immutable _controller;
+
+ // Set an operator that can instruct the zone to cancel or execute orders.
+ address public operator;
+
+ /**
+ * @dev Ensure that the caller is either the operator or controller.
+ */
+ modifier isOperator() {
+ // Ensure that the caller is either the operator or the controller.
+ if (msg.sender != operator && msg.sender != _controller) {
+ revert InvalidOperator();
+ }
+
+ // Continue with function execution.
+ _;
+ }
+
+ /**
+ * @dev Ensure that the caller is the controller.
+ */
+ modifier isController() {
+ // Ensure that the caller is the controller.
+ if (msg.sender != _controller) {
+ revert InvalidController();
+ }
+
+ // Continue with function execution.
+ _;
+ }
+
+ /**
+ * @notice Set the deployer as the controller of the zone.
+ */
+ constructor() {
+ // Set the controller to the deployer.
+ _controller = msg.sender;
+
+ // Emit an event signifying that the zone is unpaused.
+ emit Unpaused();
+ }
+
+ /**
+ * @notice Cancel an arbitrary number of orders that have agreed to use the
+ * contract as their zone.
+ *
+ * @param seaport The Seaport address.
+ * @param orders The orders to cancel.
+ *
+ * @return cancelled A boolean indicating whether the supplied orders have
+ * been successfully cancelled.
+ */
+ function cancelOrders(
+ SeaportInterface seaport,
+ OrderComponents[] calldata orders
+ ) external override isOperator returns (bool cancelled) {
+ // Call cancel on Seaport and return its boolean value.
+ cancelled = seaport.cancel(orders);
+ }
+
+ /**
+ * @notice Pause this contract, safely stopping orders from using
+ * the contract as a zone. Restricted orders with this address as a
+ * zone will not be fulfillable unless the zone is redeployed to the
+ * same address.
+ */
+ function pause(address payee) external override isController {
+ // Emit an event signifying that the zone is paused.
+ emit Paused();
+
+ // Destroy the zone, sending any native tokens to the transaction
+ // submitter.
+ selfdestruct(payable(payee));
+ }
+
+ /**
+ * @notice Assign the given address with the ability to operate the zone.
+ *
+ * @param operatorToAssign The address to assign as the operator.
+ */
+ function assignOperator(
+ address operatorToAssign
+ ) external override isController {
+ // Ensure the operator being assigned is not the null address.
+ if (operatorToAssign == address(0)) {
+ revert PauserCanNotBeSetAsZero();
+ }
+
+ // Set the given address as the new operator.
+ operator = operatorToAssign;
+
+ // Emit an event indicating the operator has been updated.
+ emit OperatorUpdated(operatorToAssign);
+ }
+
+ /**
+ * @notice Execute an arbitrary number of matched orders, each with
+ * an arbitrary number of items for offer and consideration
+ * along with a set of fulfillments allocating offer components
+ * to consideration components. Note that this call will revert if
+ * excess native tokens are returned by Seaport.
+ *
+ * @param seaport The Seaport address.
+ * @param orders The orders to match.
+ * @param fulfillments An array of elements allocating offer components
+ * to consideration components.
+ *
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function executeMatchOrders(
+ SeaportInterface seaport,
+ Order[] calldata orders,
+ Fulfillment[] calldata fulfillments
+ )
+ external
+ payable
+ override
+ isOperator
+ returns (Execution[] memory executions)
+ {
+ // Call matchOrders on Seaport and return the sequence of transfers
+ // performed as part of matching the given orders.
+ executions = seaport.matchOrders{ value: msg.value }(
+ orders,
+ fulfillments
+ );
+ }
+
+ /**
+ * @notice Execute an arbitrary number of matched advanced orders,
+ * each with an arbitrary number of items for offer and
+ * consideration along with a set of fulfillments allocating
+ * offer components to consideration components. Note that this call
+ * will revert if excess native tokens are returned by Seaport.
+ *
+ * @param seaport The Seaport address.
+ * @param orders The orders to match.
+ * @param criteriaResolvers An array where each element contains a reference
+ * to a specific order as well as that order's
+ * offer or consideration, a token identifier, and
+ * a proof that the supplied token identifier is
+ * contained in the order's merkle root.
+ * @param fulfillments An array of elements allocating offer components
+ * to consideration components.
+ *
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function executeMatchAdvancedOrders(
+ SeaportInterface seaport,
+ AdvancedOrder[] calldata orders,
+ CriteriaResolver[] calldata criteriaResolvers,
+ Fulfillment[] calldata fulfillments
+ )
+ external
+ payable
+ override
+ isOperator
+ returns (Execution[] memory executions)
+ {
+ // Call matchAdvancedOrders on Seaport and return the sequence of
+ // transfers performed as part of matching the given orders.
+ executions = seaport.matchAdvancedOrders{ value: msg.value }(
+ orders,
+ criteriaResolvers,
+ fulfillments,
+ msg.sender
+ );
+ }
+
+ /**
+ * @notice Check if a given order including extraData is currently valid.
+ *
+ * @dev This function is called by Seaport whenever any extraData is
+ * provided by the caller.
+ *
+ * @custom:param zoneParameters A struct that provides context about the
+ * order fulfillment and any supplied
+ * extraData, as well as all order hashes
+ * fulfilled in a call to a match or
+ * fulfillAvailable method.
+ *
+ * @return validOrderMagicValue A magic value indicating if the order is
+ * currently valid.
+ */
+ function validateOrder(
+ /**
+ * @custom:name zoneParameters
+ */
+ ZoneParameters calldata
+ ) external pure override returns (bytes4 validOrderMagicValue) {
+ // Return the selector of isValidOrder as the magic value.
+ validOrderMagicValue = ZoneInterface.validateOrder.selector;
+ }
+
+ /**
+ * @dev Returns the metadata for this zone.
+ */
+ function getSeaportMetadata()
+ external
+ pure
+ override
+ returns (
+ string memory name,
+ Schema[] memory schemas // map to Seaport Improvement Proposal IDs
+ )
+ {
+ schemas = new Schema[](1);
+ schemas[0].id = 3003;
+ schemas[0].metadata = new bytes(0);
+
+ return ("PausableZone", schemas);
+ }
+}
diff --git a/contracts/zones/PausableZoneController.sol b/contracts/zones/PausableZoneController.sol
new file mode 100644
index 000000000..14662e518
--- /dev/null
+++ b/contracts/zones/PausableZoneController.sol
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { PausableZone } from "./PausableZone.sol";
+
+import {
+ PausableZoneControllerInterface
+} from "./interfaces/PausableZoneControllerInterface.sol";
+
+import {
+ PausableZoneEventsAndErrors
+} from "./interfaces/PausableZoneEventsAndErrors.sol";
+
+import {
+ AdvancedOrder,
+ CriteriaResolver,
+ Execution,
+ Fulfillment,
+ Order,
+ OrderComponents
+} from "../lib/ConsiderationStructs.sol";
+
+import { SeaportInterface } from "../interfaces/SeaportInterface.sol";
+
+/**
+ * @title PausableZoneController
+ * @author cupOJoseph, BCLeFevre, stuckinaboot, stephankmin
+ * @notice PausableZoneController enables deploying, pausing and executing
+ * orders on PausableZones. This deployer is designed to be owned
+ * by a gnosis safe, DAO, or trusted party.
+ */
+contract PausableZoneController is
+ PausableZoneControllerInterface,
+ PausableZoneEventsAndErrors
+{
+ // Set the owner that can deploy, pause and execute orders on PausableZones.
+ address internal _owner;
+
+ // Set the address of the new potential owner of the zone.
+ address private _potentialOwner;
+
+ // Set the address with the ability to pause the zone.
+ address internal _pauser;
+
+ // Set the immutable zone creation code hash.
+ bytes32 public immutable zoneCreationCode;
+
+ /**
+ * @dev Throws if called by any account other than the owner or pauser.
+ */
+ modifier isPauser() {
+ if (msg.sender != _pauser && msg.sender != _owner) {
+ revert InvalidPauser();
+ }
+ _;
+ }
+
+ /**
+ * @notice Set the owner of the controller and store
+ * the zone creation code.
+ *
+ * @param ownerAddress The deployer to be set as the owner.
+ */
+ constructor(address ownerAddress) {
+ // Set the owner address as the owner.
+ _owner = ownerAddress;
+
+ // Hash and store the zone creation code.
+ zoneCreationCode = keccak256(type(PausableZone).creationCode);
+ }
+
+ /**
+ * @notice Deploy a PausableZone to a precomputed address.
+ *
+ * @param salt The salt to be used to derive the zone address
+ *
+ * @return derivedAddress The derived address for the zone.
+ */
+ function createZone(
+ bytes32 salt
+ ) external override returns (address derivedAddress) {
+ // Ensure the caller is the owner.
+ if (msg.sender != _owner) {
+ revert CallerIsNotOwner();
+ }
+
+ // Derive the PausableZone address.
+ // This expression demonstrates address computation but is not required.
+ derivedAddress = address(
+ uint160(
+ uint256(
+ keccak256(
+ abi.encodePacked(
+ bytes1(0xff),
+ address(this),
+ salt,
+ zoneCreationCode
+ )
+ )
+ )
+ )
+ );
+
+ // Revert if a zone is currently deployed to the derived address.
+ if (derivedAddress.code.length != 0) {
+ revert ZoneAlreadyExists(derivedAddress);
+ }
+
+ // Deploy the zone using the supplied salt.
+ new PausableZone{ salt: salt }();
+
+ // Emit an event signifying that the zone was created.
+ emit ZoneCreated(derivedAddress, salt);
+ }
+
+ /**
+ * @notice Pause orders on a given zone.
+ *
+ * @param zone The address of the zone to be paused.
+ *
+ * @return success A boolean indicating the zone has been paused.
+ */
+ function pause(
+ address zone
+ ) external override isPauser returns (bool success) {
+ // Call pause on the given zone.
+ PausableZone(zone).pause(msg.sender);
+
+ // Return a boolean indicating the pause was successful.
+ success = true;
+ }
+
+ /**
+ * @notice Cancel Seaport orders on a given zone.
+ *
+ * @param pausableZoneAddress The zone that manages the
+ * orders to be cancelled.
+ * @param seaportAddress The Seaport address.
+ * @param orders The orders to cancel.
+ */
+ function cancelOrders(
+ address pausableZoneAddress,
+ SeaportInterface seaportAddress,
+ OrderComponents[] calldata orders
+ ) external override {
+ // Ensure the caller is the owner.
+ if (msg.sender != _owner) {
+ revert CallerIsNotOwner();
+ }
+
+ // Create a zone object from the zone address.
+ PausableZone zone = PausableZone(pausableZoneAddress);
+
+ // Call cancelOrders on the given zone.
+ zone.cancelOrders(seaportAddress, orders);
+ }
+
+ /**
+ * @notice Execute an arbitrary number of matched orders on a given zone.
+ *
+ * @param pausableZoneAddress The zone that manages the orders
+ * to be cancelled.
+ * @param seaportAddress The Seaport address.
+ * @param orders The orders to match.
+ * @param fulfillments An array of elements allocating offer
+ * components to consideration components.
+ *
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function executeMatchOrders(
+ address pausableZoneAddress,
+ SeaportInterface seaportAddress,
+ Order[] calldata orders,
+ Fulfillment[] calldata fulfillments
+ ) external payable override returns (Execution[] memory executions) {
+ // Ensure the caller is the owner.
+ if (msg.sender != _owner) {
+ revert CallerIsNotOwner();
+ }
+
+ // Create a zone object from the zone address.
+ PausableZone zone = PausableZone(pausableZoneAddress);
+
+ // Call executeMatchOrders on the given zone and return the sequence
+ // of transfers performed as part of matching the given orders.
+ executions = zone.executeMatchOrders{ value: msg.value }(
+ seaportAddress,
+ orders,
+ fulfillments
+ );
+ }
+
+ /**
+ * @notice Execute an arbitrary number of matched advanced orders on a given
+ * zone.
+ *
+ * @param pausableZoneAddress The zone that manages the orders to be
+ * cancelled.
+ * @param seaportAddress The Seaport address.
+ * @param orders The orders to match.
+ * @param criteriaResolvers An array where each element contains a
+ * reference to a specific order as well as that
+ * order's offer or consideration, a token
+ * identifier, and a proof that the supplied
+ * token identifier is contained in the
+ * order's merkle root.
+ * @param fulfillments An array of elements allocating offer
+ * components to consideration components.
+ *
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function executeMatchAdvancedOrders(
+ address pausableZoneAddress,
+ SeaportInterface seaportAddress,
+ AdvancedOrder[] calldata orders,
+ CriteriaResolver[] calldata criteriaResolvers,
+ Fulfillment[] calldata fulfillments
+ ) external payable override returns (Execution[] memory executions) {
+ // Ensure the caller is the owner.
+ if (msg.sender != _owner) {
+ revert CallerIsNotOwner();
+ }
+
+ // Create a zone object from the zone address.
+ PausableZone zone = PausableZone(pausableZoneAddress);
+
+ // Call executeMatchOrders on the given zone and return the sequence
+ // of transfers performed as part of matching the given orders.
+ executions = zone.executeMatchAdvancedOrders{ value: msg.value }(
+ seaportAddress,
+ orders,
+ criteriaResolvers,
+ fulfillments
+ );
+ }
+
+ /**
+ * @notice Initiate Zone ownership transfer by assigning a new potential
+ * owner this contract. Once set, the new potential owner
+ * may call `acceptOwnership` to claim ownership.
+ * Only the owner in question may call this function.
+ *
+ * @param newPotentialOwner The address for which to initiate ownership
+ * transfer to.
+ */
+ function transferOwnership(address newPotentialOwner) external override {
+ // Ensure the caller is the owner.
+ if (msg.sender != _owner) {
+ revert CallerIsNotOwner();
+ }
+ // Ensure the new potential owner is not an invalid address.
+ if (newPotentialOwner == address(0)) {
+ revert OwnerCanNotBeSetAsZero();
+ }
+
+ // Emit an event indicating that the potential owner has been updated.
+ emit PotentialOwnerUpdated(newPotentialOwner);
+
+ // Set the new potential owner as the potential owner.
+ _potentialOwner = newPotentialOwner;
+ }
+
+ /**
+ * @notice Clear the currently set potential owner, if any.
+ * Only the owner of this contract may call this function.
+ */
+ function cancelOwnershipTransfer() external override {
+ // Ensure the caller is the current owner.
+ if (msg.sender != _owner) {
+ revert CallerIsNotOwner();
+ }
+
+ // Emit an event indicating that the potential owner has been cleared.
+ emit PotentialOwnerUpdated(address(0));
+
+ // Clear the current new potential owner.
+ delete _potentialOwner;
+ }
+
+ /**
+ * @notice Accept ownership of this contract. Only the account that the
+ * current owner has set as the new potential owner may call this
+ * function.
+ */
+ function acceptOwnership() external override {
+ // Ensure the caller is the potential owner.
+ if (msg.sender != _potentialOwner) {
+ revert CallerIsNotPotentialOwner();
+ }
+
+ // Emit an event indicating that the potential owner has been cleared.
+ emit PotentialOwnerUpdated(address(0));
+
+ // Clear the current new potential owner
+ delete _potentialOwner;
+
+ // Emit an event indicating ownership has been transferred.
+ emit OwnershipTransferred(_owner, msg.sender);
+
+ // Set the caller as the owner of this contract.
+ _owner = msg.sender;
+ }
+
+ /**
+ * @notice Assign the given address with the ability to pause the zone.
+ *
+ * @param pauserToAssign The address to assign the pauser role.
+ */
+ function assignPauser(address pauserToAssign) external override {
+ // Ensure the caller is the owner.
+ if (msg.sender != _owner) {
+ revert CallerIsNotOwner();
+ }
+ // Ensure the pauser to assign is not an invalid address.
+ if (pauserToAssign == address(0)) {
+ revert PauserCanNotBeSetAsZero();
+ }
+
+ // Set the given account as the pauser.
+ _pauser = pauserToAssign;
+
+ // Emit an event indicating the pauser has been assigned.
+ emit PauserUpdated(pauserToAssign);
+ }
+
+ /**
+ * @notice Assign the given address with the ability to operate the
+ * given zone.
+ *
+ * @param pausableZoneAddress The zone address to assign operator role.
+ * @param operatorToAssign The address to assign as operator.
+ */
+ function assignOperator(
+ address pausableZoneAddress,
+ address operatorToAssign
+ ) external override {
+ // Ensure the caller is the owner.
+ if (msg.sender != _owner) {
+ revert CallerIsNotOwner();
+ }
+ // Create a zone object from the zone address.
+ PausableZone zone = PausableZone(pausableZoneAddress);
+
+ // Call assignOperator on the zone by passing in the given
+ // operator address.
+ zone.assignOperator(operatorToAssign);
+ }
+
+ /**
+ * @notice An external view function that returns the owner.
+ *
+ * @return The address of the owner.
+ */
+ function owner() external view override returns (address) {
+ return _owner;
+ }
+
+ /**
+ * @notice An external view function that return the potential owner.
+ *
+ * @return The address of the potential owner.
+ */
+ function potentialOwner() external view override returns (address) {
+ return _potentialOwner;
+ }
+
+ /**
+ * @notice An external view function that returns the pauser.
+ *
+ * @return The address of the pauser.
+ */
+ function pauser() external view override returns (address) {
+ return _pauser;
+ }
+}
diff --git a/contracts/zones/interfaces/PausableZoneControllerInterface.sol b/contracts/zones/interfaces/PausableZoneControllerInterface.sol
new file mode 100644
index 000000000..8c115556c
--- /dev/null
+++ b/contracts/zones/interfaces/PausableZoneControllerInterface.sol
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import {
+ AdvancedOrder,
+ CriteriaResolver,
+ Execution,
+ Fulfillment,
+ Order,
+ OrderComponents
+} from "../../lib/ConsiderationStructs.sol";
+
+import { SeaportInterface } from "../../interfaces/SeaportInterface.sol";
+
+/**
+ * @title PausableZoneController
+ * @author cupOJoseph, BCLeFevre, stuckinaboot
+ * @notice PausableZoneController enables deploying, pausing and executing
+ * orders on PausableZones. This deployer is designed to be owned
+ * by a gnosis safe, DAO, or trusted party.
+ */
+interface PausableZoneControllerInterface {
+ /**
+ * @notice Deploy a PausableZone to a precomputed address.
+ *
+ * @param salt The salt to be used to derive the zone address
+ *
+ * @return derivedAddress The derived address for the zone.
+ */
+ function createZone(bytes32 salt) external returns (address derivedAddress);
+
+ /**
+ * @notice Pause orders on a given zone.
+ *
+ * @param zone The address of the zone to be paused.
+ *
+ * @return success A boolean indicating the zone has been paused.
+ */
+ function pause(address zone) external returns (bool success);
+
+ /**
+ * @notice Cancel Seaport offers on a given zone.
+ *
+ * @param pausableZoneAddress The zone that manages the orders to be
+ * cancelled.
+ * @param seaportAddress The Seaport address.
+ * @param orders The orders to cancel.
+ */
+ function cancelOrders(
+ address pausableZoneAddress,
+ SeaportInterface seaportAddress,
+ OrderComponents[] calldata orders
+ ) external;
+
+ /**
+ * @notice Execute an arbitrary number of matched orders on a given zone.
+ *
+ * @param pausableZoneAddress The zone that manages the orders to be
+ * cancelled.
+ * @param seaportAddress The Seaport address.
+ * @param orders The orders to match.
+ * @param fulfillments An array of elements allocating offer
+ * components to consideration components.
+ *
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function executeMatchOrders(
+ address pausableZoneAddress,
+ SeaportInterface seaportAddress,
+ Order[] calldata orders,
+ Fulfillment[] calldata fulfillments
+ ) external payable returns (Execution[] memory executions);
+
+ /**
+ * @notice Execute an arbitrary number of matched advanced orders on a
+ * given zone.
+ *
+ * @param pausableZoneAddress The zone that manages the orders to be
+ * cancelled.
+ * @param seaportAddress The Seaport address.
+ * @param orders The orders to match.
+ * @param criteriaResolvers An array where each element contains a
+ * reference to a specific order as well as
+ * that order's offer or consideration,
+ * a token identifier, and a proof that
+ * the supplied token identifier is
+ * contained in the order's merkle root.
+ * @param fulfillments An array of elements allocating offer
+ * components to consideration components.
+ *
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function executeMatchAdvancedOrders(
+ address pausableZoneAddress,
+ SeaportInterface seaportAddress,
+ AdvancedOrder[] calldata orders,
+ CriteriaResolver[] calldata criteriaResolvers,
+ Fulfillment[] calldata fulfillments
+ ) external payable returns (Execution[] memory executions);
+
+ /**
+ * @notice Initiate Zone ownership transfer by assigning a new potential
+ * owner this contract. Once set, the new potential owner
+ * may call `acceptOwnership` to claim ownership.
+ * Only the owner in question may call this function.
+ *
+ * @param newPotentialOwner The address for which to initiate ownership
+ * transfer to.
+ */
+ function transferOwnership(address newPotentialOwner) external;
+
+ /**
+ * @notice Clear the currently set potential owner, if any.
+ * Only the owner of this contract may call this function.
+ */
+ function cancelOwnershipTransfer() external;
+
+ /**
+ * @notice Accept ownership of this contract. Only the account that the
+ * current owner has set as the new potential owner may call this
+ * function.
+ */
+ function acceptOwnership() external;
+
+ /**
+ * @notice Assign the given address with the ability to pause the zone.
+ *
+ * @param pauserToAssign The address to assign the pauser role.
+ */
+ function assignPauser(address pauserToAssign) external;
+
+ /**
+ * @notice Assign the given address with the ability to operate the
+ * given zone.
+ *
+ * @param pausableZoneAddress The zone address to assign operator role.
+ * @param operatorToAssign The address to assign as operator.
+ */
+ function assignOperator(
+ address pausableZoneAddress,
+ address operatorToAssign
+ ) external;
+
+ /**
+ * @notice An external view function that returns the owner.
+ *
+ * @return The address of the owner.
+ */
+ function owner() external view returns (address);
+
+ /**
+ * @notice An external view function that return the potential owner.
+ *
+ * @return The address of the potential owner.
+ */
+ function potentialOwner() external view returns (address);
+
+ /**
+ * @notice An external view function that returns the pauser.
+ *
+ * @return The address of the pauser.
+ */
+ function pauser() external view returns (address);
+}
diff --git a/contracts/zones/interfaces/PausableZoneEventsAndErrors.sol b/contracts/zones/interfaces/PausableZoneEventsAndErrors.sol
new file mode 100644
index 000000000..459343631
--- /dev/null
+++ b/contracts/zones/interfaces/PausableZoneEventsAndErrors.sol
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+/**
+ * @notice PausableZoneEventsAndErrors contains errors and events
+ * related to zone interaction.
+ */
+interface PausableZoneEventsAndErrors {
+ /**
+ * @dev Emit an event whenever a zone is successfully paused.
+ */
+ event Paused();
+
+ /**
+ * @dev Emit an event whenever a zone is successfully unpaused (created).
+ */
+ event Unpaused();
+
+ /**
+ * @dev Emit an event whenever a zone owner registers a new potential
+ * owner for that zone.
+ *
+ * @param newPotentialOwner The new potential owner of the zone.
+ */
+ event PotentialOwnerUpdated(address newPotentialOwner);
+
+ /**
+ * @dev Emit an event whenever zone ownership is transferred.
+ *
+ * @param previousOwner The previous owner of the zone.
+ * @param newOwner The new owner of the zone.
+ */
+ event OwnershipTransferred(address previousOwner, address newOwner);
+
+ /**
+ * @dev Emit an event whenever a new zone is created.
+ *
+ * @param zone The address of the zone.
+ * @param salt The salt used to deploy the zone.
+ */
+ event ZoneCreated(address zone, bytes32 salt);
+
+ /**
+ * @dev Emit an event whenever a zone owner assigns a new pauser
+ *
+ * @param newPauser The new pausear of the zone.
+ */
+ event PauserUpdated(address newPauser);
+
+ /**
+ * @dev Emit an event whenever a zone owner assigns a new operator
+ *
+ * @param newOperator The new operator of the zone.
+ */
+ event OperatorUpdated(address newOperator);
+
+ /**
+ * @dev Revert with an error when attempting to pause the zone
+ * while the caller is not the owner or pauser of the zone.
+ */
+ error InvalidPauser();
+
+ /**
+ * @dev Revert with an error when attempting to call an operation
+ * while the caller is not the controller or operator of the zone.
+ */
+ error InvalidOperator();
+
+ /**
+ * @dev Revert with an error when attempting to pause the zone or update the
+ * operator while the caller is not the controller of the zone.
+ */
+ error InvalidController();
+ /**
+ * @dev Revert with an error when attempting to deploy a zone that is
+ * currently deployed.
+ */
+ error ZoneAlreadyExists(address zone);
+
+ /**
+ * @dev Revert with an error when the caller does not have the _owner role
+ *
+ */
+ error CallerIsNotOwner();
+
+ /**
+ * @dev Revert with an error when the caller does not have the operator role
+ *
+ */
+ error CallerIsNotOperator();
+
+ /**
+ * @dev Revert with an error when attempting to set the new potential owner
+ * as the 0 address.
+ *
+ */
+ error OwnerCanNotBeSetAsZero();
+
+ /**
+ * @dev Revert with an error when attempting to set the new potential pauser
+ * as the 0 address.
+ *
+ */
+ error PauserCanNotBeSetAsZero();
+
+ /**
+ * @dev Revert with an error when the caller does not have
+ * the potentialOwner role.
+ */
+ error CallerIsNotPotentialOwner();
+}
diff --git a/contracts/zones/interfaces/PausableZoneInterface.sol b/contracts/zones/interfaces/PausableZoneInterface.sol
new file mode 100644
index 000000000..42b5dfc0e
--- /dev/null
+++ b/contracts/zones/interfaces/PausableZoneInterface.sol
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+import { SeaportInterface } from "../../interfaces/SeaportInterface.sol";
+
+import {
+ AdvancedOrder,
+ CriteriaResolver,
+ Execution,
+ Fulfillment,
+ Order,
+ OrderComponents
+} from "../../lib/ConsiderationStructs.sol";
+
+/**
+ * @title PausableZone
+ * @author cupOJoseph, BCLeFevre, ryanio
+ * @notice PausableZone is a simple zone implementation that approves every
+ * order. It can be self-destructed by its controller to pause
+ * restricted orders that have it set as their zone.
+ */
+interface PausableZoneInterface {
+ /**
+ * @notice Cancel an arbitrary number of orders that have agreed to use the
+ * contract as their zone.
+ *
+ * @param seaport The Seaport address.
+ * @param orders The orders to cancel.
+ *
+ * @return cancelled A boolean indicating whether the supplied orders have
+ * been successfully cancelled.
+ */
+ function cancelOrders(
+ SeaportInterface seaport,
+ OrderComponents[] calldata orders
+ ) external returns (bool cancelled);
+
+ /**
+ * @notice Execute an arbitrary number of matched orders, each with
+ * an arbitrary number of items for offer and consideration
+ * along with a set of fulfillments allocating offer components
+ * to consideration components.
+ *
+ * @param seaport The Seaport address.
+ * @param orders The orders to match.
+ * @param fulfillments An array of elements allocating offer components
+ * to consideration components.
+ *
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function executeMatchOrders(
+ SeaportInterface seaport,
+ Order[] calldata orders,
+ Fulfillment[] calldata fulfillments
+ ) external payable returns (Execution[] memory executions);
+
+ /**
+ * @notice Execute an arbitrary number of matched advanced orders,
+ * each with an arbitrary number of items for offer and
+ * consideration along with a set of fulfillments allocating
+ * offer components to consideration components.
+ *
+ * @param seaport The Seaport address.
+ * @param orders The orders to match.
+ * @param criteriaResolvers An array where each element contains a reference
+ * to a specific order as well as that order's
+ * offer or consideration, a token identifier, and
+ * a proof that the supplied token identifier is
+ * contained in the order's merkle root.
+ * @param fulfillments An array of elements allocating offer components
+ * to consideration components.
+ *
+ * @return executions An array of elements indicating the sequence of
+ * transfers performed as part of matching the given
+ * orders.
+ */
+ function executeMatchAdvancedOrders(
+ SeaportInterface seaport,
+ AdvancedOrder[] calldata orders,
+ CriteriaResolver[] calldata criteriaResolvers,
+ Fulfillment[] calldata fulfillments
+ ) external payable returns (Execution[] memory executions);
+
+ /**
+ * @notice Pause this contract, safely stopping orders from using
+ * the contract as a zone. Restricted orders with this address as a
+ * zone will not be fulfillable unless the zone is redeployed to the
+ * same address.
+ */
+ function pause(address payee) external;
+
+ /**
+ * @notice Assign the given address with the ability to operate the zone.
+ *
+ * @param operatorToAssign The address to assign as the operator.
+ */
+ function assignOperator(address operatorToAssign) external;
+}
diff --git a/diagrams/Seaport.drawio.svg b/diagrams/Seaport.drawio.svg
index f9c8a13e0..95de479fc 100644
--- a/diagrams/Seaport.drawio.svg
+++ b/diagrams/Seaport.drawio.svg
@@ -1,4 +1,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/Deployment.md b/docs/Deployment.md
new file mode 100644
index 000000000..4226d2711
--- /dev/null
+++ b/docs/Deployment.md
@@ -0,0 +1,120 @@
+# Deploying Seaport
+
+Seaport 1.1, 1.2, and the ConduitController can each be deployed to their respective canonical deployment address on all EVM chains using the CREATE2 Factory.
+
+## Relevant Addresses
+
+
+
+
Name
+
Address
+
+
+
KEYLESS_CREATE2_DEPLOYER_ADDRESS
+
0x4c8D290a1B368ac4728d83a9e8321fC3af2b39b1
+
+
+
KEYLESS_CREATE2_ADDRESS
+
0x7A0D94F55792C434d74a40883C6ed8545E406D12
+
+
+
INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_ADDRESS
+
0xcfA3A7637547094fF06246817a35B8333C315196
+
+
+
IMMUTABLE_CREATE2_FACTORY_ADDRESS
+
0x0000000000ffe8b47b3e2130213b802212439497
+
+
+
ConduitController
+
0x00000000F9490004C11Cef243f5400493c00Ad63
+
+
+
Seaport 1.1
+
0x00000000006c3852cbEf3e08E8dF289169EdE581
+
+
+
Seaport 1.2
+
0x00000000000006c7676171937C444f6BDe3D6282
+
+
+
+---
+
+## Setting up Factory on a New Chain
+
+If there is no `IMMUTABLE_CREATE2_FACTORY_ADDRESS` on the chain, deploy this first with foundry.
+
+1. Send 0.01 Ether to the `KEYLESS_CREATE2_DEPLOYER_ADDRESS`
+2. Create the `KEYLESS_CREATE2_ADDRESS` by submitting this pre-signed transaction:
+
+```
+cast publish --rpc-url ${RPC_URL} 0xf87e8085174876e800830186a08080ad601f80600e600039806000f350fe60003681823780368234f58015156014578182fd5b80825250506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222
+```
+
+3. Create the `INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_ADDRESS` by submitting:
+
+```
+cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x7a0d94f55792c434d74a40883c6ed8545e406d12 0x608060405234801561001057600080fd5b50610833806100206000396000f3fe60806040526004361061003f5760003560e01c806308508b8f1461004457806364e030871461009857806385cf97ab14610138578063a49a7c90146101bc575b600080fd5b34801561005057600080fd5b506100846004803603602081101561006757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166101ec565b604080519115158252519081900360200190f35b61010f600480360360408110156100ae57600080fd5b813591908101906040810160208201356401000000008111156100d057600080fd5b8201836020820111156100e257600080fd5b8035906020019184600183028401116401000000008311171561010457600080fd5b509092509050610217565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b34801561014457600080fd5b5061010f6004803603604081101561015b57600080fd5b8135919081019060408101602082013564010000000081111561017d57600080fd5b82018360208201111561018f57600080fd5b803590602001918460018302840111640100000000831117156101b157600080fd5b509092509050610592565b3480156101c857600080fd5b5061010f600480360360408110156101df57600080fd5b508035906020013561069e565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b600083606081901c33148061024c57507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008116155b6102a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260458152602001806107746045913960600191505060405180910390fd5b606084848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250604051855195965090943094508b93508692506020918201918291908401908083835b6020831061033557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016102f8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018281037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183528085528251928201929092207fff000000000000000000000000000000000000000000000000000000000000008383015260609890981b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260358201969096526055808201979097528251808203909701875260750182525084519484019490942073ffffffffffffffffffffffffffffffffffffffff81166000908152938490529390922054929350505060ff16156104a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180610735603f913960400191505060405180910390fd5b81602001825188818334f5955050508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161461053a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260468152602001806107b96046913960600191505060405180910390fd5b50505073ffffffffffffffffffffffffffffffffffffffff8116600090815260208190526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559392505050565b6000308484846040516020018083838082843760408051919093018181037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001825280845281516020928301207fff000000000000000000000000000000000000000000000000000000000000008383015260609990991b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166021820152603581019790975260558088019890985282518088039098018852607590960182525085519585019590952073ffffffffffffffffffffffffffffffffffffffff81166000908152948590529490932054939450505060ff909116159050610697575060005b9392505050565b604080517fff000000000000000000000000000000000000000000000000000000000000006020808301919091523060601b6021830152603582018590526055808301859052835180840390910181526075909201835281519181019190912073ffffffffffffffffffffffffffffffffffffffff81166000908152918290529190205460ff161561072e575060005b9291505056fe496e76616c696420636f6e7472616374206372656174696f6e202d20636f6e74726163742068617320616c7265616479206265656e206465706c6f7965642e496e76616c69642073616c74202d206669727374203230206279746573206f66207468652073616c74206d757374206d617463682063616c6c696e6720616464726573732e4661696c656420746f206465706c6f7920636f6e7472616374207573696e672070726f76696465642073616c7420616e6420696e697469616c697a6174696f6e20636f64652ea265627a7a723058202bdc55310d97c4088f18acf04253db593f0914059f0c781a9df3624dcef0d1cf64736f6c634300050a0032
+```
+
+4. Create the `IMMUTABLE_CREATE2_FACTORY_ADDRESS` contract at `0x0000000000ffe8b47b3e2130213b802212439497` by submitting:
+
+```
+cast send --rpc-url ${RPC_URL} --private-key ${PK} 0xcfa3a7637547094ff06246817a35b8333c315196 0x64e030870000000000000000000000000000000000000000f4b0218f13a6440a6f02000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000853608060405234801561001057600080fd5b50610833806100206000396000f3fe60806040526004361061003f5760003560e01c806308508b8f1461004457806364e030871461009857806385cf97ab14610138578063a49a7c90146101bc575b600080fd5b34801561005057600080fd5b506100846004803603602081101561006757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166101ec565b604080519115158252519081900360200190f35b61010f600480360360408110156100ae57600080fd5b813591908101906040810160208201356401000000008111156100d057600080fd5b8201836020820111156100e257600080fd5b8035906020019184600183028401116401000000008311171561010457600080fd5b509092509050610217565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b34801561014457600080fd5b5061010f6004803603604081101561015b57600080fd5b8135919081019060408101602082013564010000000081111561017d57600080fd5b82018360208201111561018f57600080fd5b803590602001918460018302840111640100000000831117156101b157600080fd5b509092509050610592565b3480156101c857600080fd5b5061010f600480360360408110156101df57600080fd5b508035906020013561069e565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b600083606081901c33148061024c57507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008116155b6102a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260458152602001806107746045913960600191505060405180910390fd5b606084848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250604051855195965090943094508b93508692506020918201918291908401908083835b6020831061033557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016102f8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018281037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183528085528251928201929092207fff000000000000000000000000000000000000000000000000000000000000008383015260609890981b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260358201969096526055808201979097528251808203909701875260750182525084519484019490942073ffffffffffffffffffffffffffffffffffffffff81166000908152938490529390922054929350505060ff16156104a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180610735603f913960400191505060405180910390fd5b81602001825188818334f5955050508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161461053a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260468152602001806107b96046913960600191505060405180910390fd5b50505073ffffffffffffffffffffffffffffffffffffffff8116600090815260208190526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559392505050565b6000308484846040516020018083838082843760408051919093018181037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001825280845281516020928301207fff000000000000000000000000000000000000000000000000000000000000008383015260609990991b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166021820152603581019790975260558088019890985282518088039098018852607590960182525085519585019590952073ffffffffffffffffffffffffffffffffffffffff81166000908152948590529490932054939450505060ff909116159050610697575060005b9392505050565b604080517fff000000000000000000000000000000000000000000000000000000000000006020808301919091523060601b6021830152603582018590526055808301859052835180840390910181526075909201835281519181019190912073ffffffffffffffffffffffffffffffffffffffff81166000908152918290529190205460ff161561072e575060005b9291505056fe496e76616c696420636f6e7472616374206372656174696f6e202d20636f6e74726163742068617320616c7265616479206265656e206465706c6f7965642e496e76616c69642073616c74202d206669727374203230206279746573206f66207468652073616c74206d757374206d617463682063616c6c696e6720616464726573732e4661696c656420746f206465706c6f7920636f6e7472616374207573696e672070726f76696465642073616c7420616e6420696e697469616c697a6174696f6e20636f64652ea265627a7a723058202bdc55310d97c4088f18acf04253db593f0914059f0c781a9df3624dcef0d1cf64736f6c634300050a003200000000000000000000000000
+```
+
+## Deploying Seaport and ConduitController
+
+Once the `IMMUTABLE_CREATE2_FACTORY_ADDRESS` exists, begin to deploy the contracts:
+
+1. Deploy the `ConduitController` contract by submitting:
+
+```
+cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x0000000000ffe8b47b3e2130213b802212439497 0x64e030870000000000000000000000000000000000000000dc0ef3c792976604960400000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000302760c08060405234620000ea57600090610c9f906001600160401b03603f8301601f1916820181811183821017620000da575b604052828252620023889160208101908484833951902060805260405192839281840192831184841017620000ca575b8339039082f58015620000ba575b6001600160a01b03163f60a05260405161227490816200011482396080518181816102b101528181610bcc0152610d06015260a0518181816102d401528181610c620152610da90152f35b620000c462000106565b6200006f565b620000d4620000ef565b62000061565b620000e4620000ef565b62000031565b600080fd5b50634e487b7160e01b600052604160045260246000fd5b506040513d6000823e3d90fdfe60806040526004361015610013575b600080fd5b60003560e01c8063027cc7641461012b5780630a96ad391461012257806313ad9cab1461011957806314afd79e1461011057806333bc8572146101075780634e3f9580146100fe57806351710e45146100f55780636d435421146100ec5780636e9bfd9f146100e3578063794593bc146100da5780637b37e561146100d15780638b9e028b146100c8578063906c87cc146100bf576393790f44146100b757600080fd5b61000e61126e565b5061000e6111fa565b5061000e61113c565b5061000e610fc8565b5061000e610c8a565b5061000e610b3c565b5061000e6109bf565b5061000e610765565b5061000e6106f3565b5061000e61064f565b5061000e6105db565b5061000e6102fa565b5061000e61027b565b5061000e61017a565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361000e57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576101b2610134565b602435906101bf81611574565b73ffffffffffffffffffffffffffffffffffffffff80911691600083815280602052600360408220015482101561023f5790600360408361023b9661020a9552806020522001611400565b90549060031b1c166040519182918291909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0390f35b602484604051907f6ceb340b0000000000000000000000000000000000000000000000000000000082526004820152fd5b600091031261000e57565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57604080517f000000000000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006020820152f35b503461000e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57610332610134565b61033a610157565b90604435918215918215840361000e5761035381611505565b73ffffffffffffffffffffffffffffffffffffffff811690813b1561000e576040517fc4e8fcb500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528515156024820152610401926000908290604490829084905af180156105ce575b6105b5575b5073ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b92600484019261043183859073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b5491821590806105ae575b1561048157505050600361047d92930161045682826114ce565b54929073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b555b005b91949391816105a5575b5061049257005b6104df61047d938560037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600098019201916104ce83546113a4565b90808203610504575b505050611447565b9073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b6105766105449161053b61051b61059c9588611400565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b92839187611400565b90919082549060031b9173ffffffffffffffffffffffffffffffffffffffff9283811b93849216901b16911916179055565b859073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b553880806104d7565b9050153861048b565b508061043c565b806105c26105c892611335565b80610270565b386103da565b6105d6611397565b6103d5565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020610615610134565b61061e81611574565b73ffffffffffffffffffffffffffffffffffffffff8091166000526000825260016040600020015416604051908152f35b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760206106e861068c610134565b73ffffffffffffffffffffffffffffffffffffffff6106a9610157565b916106b381611574565b166000526000835260046040600020019073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b541515604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5773ffffffffffffffffffffffffffffffffffffffff610740610134565b61074981611574565b1660005260006020526020600360406000200154604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5761079d610134565b6107a681611574565b61080c6107f360026107d88473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b33036109765761047f9060007f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da81604051a2610896600261086d8373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b017fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b73ffffffffffffffffffffffffffffffffffffffff3390806108dd60016107d88673ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b169083167fc8894f26f396ce8c004245c8b7cd1b92103a6e4302fcbab883987149ac01b7ec6000604051a46001610935339273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b019073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6040517f88c3a11500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576109f7610134565b6109ff610157565b90610a0981611505565b73ffffffffffffffffffffffffffffffffffffffff808316908115610b095750610a5b6107f360026107d88573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b8114610ab95761093561047f93926002927f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da6000604051a273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b506040517fcbc080ca00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b82602491604051917fa388d263000000000000000000000000000000000000000000000000000000008352166004820152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576040517fff00000000000000000000000000000000000000000000000000000000000000602082019081523060601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260043560358301527f0000000000000000000000000000000000000000000000000000000000000000605583015273ffffffffffffffffffffffffffffffffffffffff91610c3b81607581015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611356565b519020604080519290911673ffffffffffffffffffffffffffffffffffffffff811683523f7f000000000000000000000000000000000000000000000000000000000000000014602083015290f35b503461000e576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57600435610cc6610157565b73ffffffffffffffffffffffffffffffffffffffff91828216908115610f9f57338160601c03610f7657610da46107f386516020810190610d8881610c0f7f0000000000000000000000000000000000000000000000000000000000000000883087917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000605594927fff00000000000000000000000000000000000000000000000000000000000000855260601b166001840152601583015260358201520190565b51902073ffffffffffffffffffffffffffffffffffffffff1690565b92833f7f000000000000000000000000000000000000000000000000000000000000000014610f3057947f4397af6128d529b8ae0442f99db1296d5136062597a15bbc61c1b2a6431a7d15610eca838060009961023b989796865180610c9f8082019082821067ffffffffffffffff831117610f23575b6115a0833903908df515610f16575b610e9c610e578973ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b91600183019073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b55835173ffffffffffffffffffffffffffffffffffffffff8716815260208101919091529081906040820190565b0390a15194859483167fc8894f26f396ce8c004245c8b7cd1b92103a6e4302fcbab883987149ac01b7ec8287a473ffffffffffffffffffffffffffffffffffffffff1682526020820190565b610f1e611397565b610e2a565b610f2b611305565b610e1b565b85517f6328ccb200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602490fd5b600485517fcb6e5344000000000000000000000000000000000000000000000000000000008152fd5b600485517f99faaa04000000000000000000000000000000000000000000000000000000008152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57611000610134565b61100981611505565b73ffffffffffffffffffffffffffffffffffffffff9081811660009281845283602052600260408520015416156110ba575061108e600291837f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da81604051a273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b017fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055604051f35b602490604051907f6b0136160000000000000000000000000000000000000000000000000000000082526004820152fd5b6020908160408183019282815285518094520193019160005b828110611112575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611104565b503461000e576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57611175610134565b9061117f82611574565b73ffffffffffffffffffffffffffffffffffffffff91826000911681528082526003604082200192604051908193808654938481520195845280842093915b8383106111e15761023b866111d5818a0382611356565b604051918291826110eb565b84548116875295810195600194850194909201916111be565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020611234610134565b61123d81611574565b73ffffffffffffffffffffffffffffffffffffffff8091166000526000825260026040600020015416604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5773ffffffffffffffffffffffffffffffffffffffff6112bb610134565b16600052600060205260406000205480156112db57602090604051908152f35b60046040517f4ca82090000000000000000000000000000000000000000000000000000000008152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161134957604052565b611351611305565b604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761134957604052565b506040513d6000823e3d90fd5b600181106113d1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80548210156114185760005260206000200190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8054801561149f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019061147c8282611400565b73ffffffffffffffffffffffffffffffffffffffff82549160031b1b1916905555565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b906105446114f692805490680100000000000000008210156114f8575b600182018155611400565b565b611500611305565b6114eb565b61150e81611574565b73ffffffffffffffffffffffffffffffffffffffff809116908160005260006020526001604060002001541633036115435750565b602490604051907fd4ed9a170000000000000000000000000000000000000000000000000000000082526004820152fd5b73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002054156112db5756fe60a080604052346100235733608052610c7690816100298239608051816103c50152f35b600080fdfe60806040526004361015610013575b600080fd5b6000803560e01c9081634ce34aa21461006657508063899e104c1461005d5780638df25d92146100545763c4e8fcb51461004c57600080fd5b61000e610362565b5061000e61027f565b5061000e6101ab565b346101465760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101465760043567ffffffffffffffff8111610142576100b5903690600401610149565b9133815280602052604081205415610116575b8281106100fa576040517f4ce34aa2000000000000000000000000000000000000000000000000000000008152602090f35b8061011061010b6001938686610532565b6105c4565b016100c8565b807f93daadf2000000000000000000000000000000000000000000000000000000006024925233600452fd5b5080fd5b80fd5b9181601f8401121561000e5782359167ffffffffffffffff831161000e5760208085019460c0850201011161000e57565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e576101fc903690600401610149565b9160243590811161000e5761021590369060040161017a565b919092600033815280602052604081205415610116575b8181106102685761023d8486610acb565b6040517f899e104c000000000000000000000000000000000000000000000000000000008152602090f35b8061027961010b6001938587610532565b0161022c565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043567ffffffffffffffff811161000e576102cf90369060040161017a565b33600052600060205260406000205415610316576102ec91610acb565b60206040517f8df25d92000000000000000000000000000000000000000000000000000000008152f35b7f93daadf2000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043561039e81610344565b6024359081151580830361000e5773ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000000000000000000000000000000000001633036105085761041f6104188473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b5460ff1690565b1515146104b657816104a6846104767fae63067d43ac07563b7eb8db6595635fc77f1578a2a5ea06ba91b63e2afa37e29573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405193151584521691602090a2005b506040517f924e341e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9190911660048201529015156024820152604490fd5b60046040517f6d5769be000000000000000000000000000000000000000000000000000000008152fd5b91908110156105425760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6004111561057b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b35600481101561000e5790565b356105c181610344565b90565b60016105cf826105aa565b6105d881610571565b0361061357806105ed602061061193016105b7565b906105fa604082016105b7565b60a0610608606084016105b7565b92013592610712565b565b600261061e826105aa565b61062781610571565b0361069657600160a08201350361066c5780610648602061061193016105b7565b90610655604082016105b7565b6080610663606084016105b7565b92013592610882565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b60036106a1826105aa565b6106aa81610571565b036106e857806106bf602061061193016105b7565b6106cb604083016105b7565b6106d7606084016105b7565b90608060a085013594013592610990565b60046040517f7932f1fc000000000000000000000000000000000000000000000000000000008152fd5b9092604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d151581161561077c575b505050505050604052606052565b80863b15151661076e579087959691156107bc57602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b156107f657506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d610835575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c908060030291808211610869575b505060205a91011061086057856107fc565b833d81803e3d90fd5b8080600392028380020360091c9203020101868061084e565b9092813b1561096257604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156108db5750505050604052606052565b8593943d61091e575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211610949575b505060205a91011061086057856108e4565b8080600392028380020360091c92030201018680610937565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b15610a9d57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af115610a0d57505050505060805260a05260c052604052606052565b89949550883d610a50575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c908060030291808211610a84575b505060205a910110610a7b5786610a18565b843d81803e3d90fd5b8080600392028380020360091c92030201018780610a69565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b90816020907f2eb2c2d600000000000000000000000000000000000000000000000000000000825260005b838110610b095750505050506080604052565b8435820194853590813b156109625760a09182880192833560059181831b948b60c08097608094818301868501351490606085013514169201013584141615610c165789019a890160243760061b9360e0850160a452610104850194600086526040019060c437600080858982865af115610b8a5750505050600101610af6565b869394503d610bcb575b507fafc445e20000000000000000000000000000000000000000000000000000000060005260045260645260849081510190526000fd5b84601f3d01821c911c90600381810292808311610bff575b505050835a910110610bf55784610b94565b3d6000803e3d6000fd5b8080028380020360091c9203020101858080610be3565b7feba2084c0000000000000000000000000000000000000000000000000000000060005260046000fdfea2646970667358221220c5c8d054d9d5df7c3530eab1c32506aad1fcb6772c1457f0da5443ad9e91b4a364736f6c634300080e0033a264697066735822122031e2de61a9e35e9e87d5eef6a36b045a0bab54c4031fd01a0f8138afce3cec3164736f6c634300080e003360a080604052346100235733608052610c7690816100298239608051816103c50152f35b600080fdfe60806040526004361015610013575b600080fd5b6000803560e01c9081634ce34aa21461006657508063899e104c1461005d5780638df25d92146100545763c4e8fcb51461004c57600080fd5b61000e610362565b5061000e61027f565b5061000e6101ab565b346101465760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101465760043567ffffffffffffffff8111610142576100b5903690600401610149565b9133815280602052604081205415610116575b8281106100fa576040517f4ce34aa2000000000000000000000000000000000000000000000000000000008152602090f35b8061011061010b6001938686610532565b6105c4565b016100c8565b807f93daadf2000000000000000000000000000000000000000000000000000000006024925233600452fd5b5080fd5b80fd5b9181601f8401121561000e5782359167ffffffffffffffff831161000e5760208085019460c0850201011161000e57565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e576101fc903690600401610149565b9160243590811161000e5761021590369060040161017a565b919092600033815280602052604081205415610116575b8181106102685761023d8486610acb565b6040517f899e104c000000000000000000000000000000000000000000000000000000008152602090f35b8061027961010b6001938587610532565b0161022c565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043567ffffffffffffffff811161000e576102cf90369060040161017a565b33600052600060205260406000205415610316576102ec91610acb565b60206040517f8df25d92000000000000000000000000000000000000000000000000000000008152f35b7f93daadf2000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043561039e81610344565b6024359081151580830361000e5773ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000000000000000000000000000000000001633036105085761041f6104188473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b5460ff1690565b1515146104b657816104a6846104767fae63067d43ac07563b7eb8db6595635fc77f1578a2a5ea06ba91b63e2afa37e29573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405193151584521691602090a2005b506040517f924e341e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9190911660048201529015156024820152604490fd5b60046040517f6d5769be000000000000000000000000000000000000000000000000000000008152fd5b91908110156105425760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6004111561057b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b35600481101561000e5790565b356105c181610344565b90565b60016105cf826105aa565b6105d881610571565b0361061357806105ed602061061193016105b7565b906105fa604082016105b7565b60a0610608606084016105b7565b92013592610712565b565b600261061e826105aa565b61062781610571565b0361069657600160a08201350361066c5780610648602061061193016105b7565b90610655604082016105b7565b6080610663606084016105b7565b92013592610882565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b60036106a1826105aa565b6106aa81610571565b036106e857806106bf602061061193016105b7565b6106cb604083016105b7565b6106d7606084016105b7565b90608060a085013594013592610990565b60046040517f7932f1fc000000000000000000000000000000000000000000000000000000008152fd5b9092604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d151581161561077c575b505050505050604052606052565b80863b15151661076e579087959691156107bc57602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b156107f657506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d610835575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c908060030291808211610869575b505060205a91011061086057856107fc565b833d81803e3d90fd5b8080600392028380020360091c9203020101868061084e565b9092813b1561096257604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156108db5750505050604052606052565b8593943d61091e575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211610949575b505060205a91011061086057856108e4565b8080600392028380020360091c92030201018680610937565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b15610a9d57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af115610a0d57505050505060805260a05260c052604052606052565b89949550883d610a50575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c908060030291808211610a84575b505060205a910110610a7b5786610a18565b843d81803e3d90fd5b8080600392028380020360091c92030201018780610a69565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b90816020907f2eb2c2d600000000000000000000000000000000000000000000000000000000825260005b838110610b095750505050506080604052565b8435820194853590813b156109625760a09182880192833560059181831b948b60c08097608094818301868501351490606085013514169201013584141615610c165789019a890160243760061b9360e0850160a452610104850194600086526040019060c437600080858982865af115610b8a5750505050600101610af6565b869394503d610bcb575b507fafc445e20000000000000000000000000000000000000000000000000000000060005260045260645260849081510190526000fd5b84601f3d01821c911c90600381810292808311610bff575b505050835a910110610bf55784610b94565b3d6000803e3d6000fd5b8080028380020360091c9203020101858080610be3565b7feba2084c0000000000000000000000000000000000000000000000000000000060005260046000fdfea2646970667358221220c5c8d054d9d5df7c3530eab1c32506aad1fcb6772c1457f0da5443ad9e91b4a364736f6c634300080e003300000000000000000000000000000000000000000000000000
+```
+
+2. Deploy the `Seaport 1.1` contract by submitting:
+
+```
+cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x0000000000ffe8b47b3e2130213b802212439497 0x64e030870000000000000000000000000000000000000000a39d1860ddeb0e016b0900000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000670b6101c060405234620000b9576200001f6200001962000114565b62000151565b604051615f7e90816200076d82396080518161282c015260a05181612852015260c05181612809015260e051818181611758015261269701526101005181818161162401526126e60152610120518181816117f40152612734015261014051816127b7015261016051816127dd015261018051818181611003015281816122f4015261246a01526101a05181818161233201526124a80152f35b600080fd5b604081019081106001600160401b03821117620000da57604052565b634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b03821190821017620000da57604052565b620066eb60208138039182604051938492620001318285620000f0565b833981010312620000b957516001600160a01b0381168103620000b95790565b604060049162000160620002e3565b610120526101005260e05260c05260a05260805246610140526200018362000237565b610160526001600160a01b03166101808190528151630a96ad3960e01b815292839182905afa90811562000203575b600091620001cd575b506101a052620001cb6001600055565b565b620001f3915060403d8111620001fb575b620001ea8183620000f0565b81019062000213565b5038620001bb565b503d620001de565b6200020d6200022a565b620001b2565b9190826040910312620000b9576020825192015190565b506040513d6000823e3d90fd5b60c05160805160a0516040519160208301938452604083015260608201524660808201523060a082015260a0815260c0810181811060018060401b03821117620000da5760405251902090565b604051906200029382620000be565b6003825262312e3160e81b6020830152565b90815180926000905b828210620002cb575011620002c1570190565b6000828201520190565b915080602080928401015181850152018391620002ae565b620002ed62000747565b8051602080920120916200030062000284565b8281519101209160405181810192816200032b85600a906909ecccccae492e8cada560b31b81520190565b6e1d5a5b9d0e081a5d195b551e5c194b608a1b8152600f016d1859191c995cdcc81d1bdad95b8b60921b8152600e017f75696e74323536206964656e7469666965724f7243726974657269612c0000008152601d017f75696e74323536207374617274416d6f756e742c0000000000000000000000008152601401701d5a5b9d0c8d4d88195b99105b5bdd5b9d607a1b8152601101602960f81b81526001010392601f19938481018452620003e19084620000f0565b60405171086dedce6d2c8cae4c2e8d2dedc92e8cada560731b8282019081529481601287016e1d5a5b9d0e081a5d195b551e5c194b608a1b8152600f016d1859191c995cdcc81d1bdad95b8b60921b8152600e017f75696e74323536206964656e7469666965724f7243726974657269612c0000008152601d017f75696e74323536207374617274416d6f756e742c0000000000000000000000008152601401711d5a5b9d0c8d4d88195b99105b5bdd5b9d0b60721b8152601201701859191c995cdcc81c9958da5c1a595b9d607a1b8152601101602960f81b8152600101038181018352620004d29083620000f0565b6040519283818101620004fc906010906f09ee4c8cae486dedae0dedccadce8e6560831b81520190565b6f1859191c995cdcc81bd999995c995c8b60821b81526010016c1859191c995cdcc81e9bdb994b609a1b8152600d017113d999995c925d195b56d7481bd999995c8b60721b81526012017f436f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f8152611b8b60f21b60208201526022016f1d5a5b9d0e081bdc99195c951e5c194b60821b8152601001711d5a5b9d0c8d4d881cdd185c9d151a5b594b60721b81526012016f1d5a5b9d0c8d4d88195b99151a5b594b60821b815260100170189e5d195ccccc881e9bdb9952185cda0b607a1b81526011016c1d5a5b9d0c8d4d881cd85b1d0b609a1b8152600d017f6279746573333220636f6e647569744b65792c0000000000000000000000000081526013016e3ab4b73a191a9b1031b7bab73a32b960891b8152600f01602960f81b81526001010382810185526200064e9085620000f0565b6040516c08a92a06e626488dedac2d2dc5609b1b8282019081529080600d83016b1cdd1c9a5b99c81b985b594b60a21b8152600c016e1cdd1c9a5b99c81d995c9cda5bdb8b608a1b8152600f016f1d5a5b9d0c8d4d8818da185a5b92590b60821b81526010017f6164647265737320766572696679696e67436f6e7472616374000000000000008152601901602960f81b8152600101038481018252620006f69082620000f0565b5190209786519020968351902095604051938492830195866200071991620002a5565b6200072491620002a5565b6200072f91620002a5565b039081018252620007419082620000f0565b51902090565b604051906200075682620000be565b600782526614d9585c1bdc9d60ca1b602083015256fe60806040526004361015610013575b600080fd5b60003560e01c806306fdde031461013f57806346423aa71461013657806355944a421461012d5780635b34b9661461012457806379df72bd1461011b57806387201b41146101125780638814773214610109578063a817440414610100578063b3a34c4c146100f7578063e7acab24146100ee578063ed98a574146100e5578063f07ec373146100dc578063f47b7740146100d3578063fb0f3ee1146100ca5763fd9f1e10146100c257600080fd5b61000e61132d565b5061000e61102c565b5061000e610f8b565b5061000e610f46565b5061000e610eb5565b5061000e610e07565b5061000e610da3565b5061000e610d32565b5061000e610be3565b5061000e610b0f565b5061000e610994565b5061000e61092f565b5061000e61089e565b5061000e6101c1565b5061000e610199565b91908251928382526000905b8482106101815750601f8460209495601f199311610174575b0116010190565b600085828601015261016d565b90602090818082850101519082860101520190610154565b503461000e57600060031936011261000e57602080526707536561706f727460475260606020f35b503461000e57602060031936011261000e57600435600052600260205260806040600020546040519060ff81161515825260ff8160081c16151560208301526effffffffffffffffffffffffffffff8160101c16604083015260881c6060820152f35b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60a0810190811067ffffffffffffffff82111761027057604052565b610278610224565b604052565b60c0810190811067ffffffffffffffff82111761027057604052565b6020810190811067ffffffffffffffff82111761027057604052565b6040810190811067ffffffffffffffff82111761027057604052565b90601f601f19910116810190811067ffffffffffffffff82111761027057604052565b60405190610160820182811067ffffffffffffffff82111761027057604052565b6040519061032282610254565b565b60209067ffffffffffffffff811161033e575b60051b0190565b610346610224565b610337565b6001600160a01b0381160361000e57565b60a435906103228261034b565b35906103228261034b565b3590600682101561000e57565b92919261038d82610324565b60409461039c865192836102d1565b819584835260208093019160a080960285019481861161000e57925b8584106103c85750505050505050565b868483031261000e5784879184516103df81610254565b6103e887610374565b8152828701356103f78161034b565b83820152858701358682015260608088013590820152608080880135908201528152019301926103b8565b9080601f8301121561000e5781602061043d93359101610381565b90565b92919261044c82610324565b60409461045b865192836102d1565b819584835260208093019160c080960285019481861161000e57925b8584106104875750505050505050565b868483031261000e57848791845161049e8161027d565b6104a787610374565b8152828701356104b68161034b565b838201528587013586820152606080880135908201526080808801359082015260a080880135906104e68261034b565b820152815201930192610477565b9080601f8301121561000e5781602061043d93359101610440565b6004111561000e57565b35906103228261050f565b9190916101608184031261000e5761053a6102f4565b9261054482610369565b845261055260208301610369565b602085015267ffffffffffffffff90604083013582811161000e5781610579918501610422565b6040860152606083013591821161000e576105959183016104f4565b60608401526105a660808201610519565b608084015260a081013560a084015260c081013560c084015260e081013560e0840152610100808201359084015261012080820135908401526101408091013590830152565b35906effffffffffffffffffffffffffffff8216820361000e57565b92919267ffffffffffffffff8211610650575b604051916106336020601f19601f84011601846102d1565b82948184528183011161000e578281602093846000960137010152565b610658610224565b61061b565b9080601f8301121561000e5781602061043d93359101610608565b91909160a08184031261000e5761068d610315565b9267ffffffffffffffff823581811161000e57826106ac918501610524565b85526106ba602084016105ec565b60208601526106cb604084016105ec565b6040860152606083013581811161000e57826106e891850161065d565b6060860152608083013590811161000e57610703920161065d565b6080830152565b9080601f8301121561000e5781359061072282610324565b9261073060405194856102d1565b828452602092838086019160051b8301019280841161000e57848301915b84831061075e5750505050505090565b823567ffffffffffffffff811161000e57869161078084848094890101610678565b81520192019161074e565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600611156107f657565b6103226107bc565b608090805161080c816107ec565b8352816001600160a01b03918260208201511660208601526040810151604086015260608101516060860152015116910152565b90815180825260208080930193019160005b828110610860575050505090565b909192938260e0600192604088516108798382516107fe565b808501516001600160a01b031660a0840152015160c082015201950193929101610852565b50606060031936011261000e5767ffffffffffffffff60043581811161000e576108cc90369060040161070a565b9060243581811161000e576108e590369060040161078b565b60443592831161000e5761092b9361091161090761091795369060040161078b565b9490933691611bff565b90613e21565b604051918291602083526020830190610840565b0390f35b503461000e57600060031936011261000e57610949615017565b3360005260016020526020604060002060018154018091556040518181527f721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f833392a2604051908152f35b503461000e5760031960208136011261000e5760043567ffffffffffffffff811161000e576101608160040192823603011261000e576109d38261152d565b916109e06024830161152d565b906109ee6044840182611cfc565b6064850192916109fe8484611d50565b92909360848801610a0e90611dae565b95610a1891611d50565b969050610a236102f4565b6001600160a01b0390991689526001600160a01b031660208901523690610a4992610381565b60408701523690610a5992610440565b6060850152610a6b9060808501611db8565b60a482013560a084015260c482013560c084015260e482013560e08401526101048201356101008401526101248201356101208401526101408301526101440135610ab59161268a565b604051908152602090f35b9092916040820191604081528451809352606081019260208096019060005b818110610af95750505061043d9394818403910152610840565b8251151586529487019491870191600101610adf565b5060e060031936011261000e5767ffffffffffffffff60043581811161000e57610b3d90369060040161070a565b60243582811161000e57610b5590369060040161078b565b909160443584811161000e57610b6f90369060040161078b565b9060643595861161000e57610b8b610ba496369060040161078b565b929091610b9661035c565b9560c4359760843596611cc2565b9061092b60405192839283610ac0565b602060031982011261000e576004359067ffffffffffffffff821161000e57610bdf9160040161078b565b9091565b503461000e57610bf236610bb4565b610bfa615017565b60005b818110610c105760405160018152602090f35b80610c1e6001928486613f13565b610c2881806146ae565b610c318161152d565b91610c44610c3f3684610524565b614fa9565b91610c59836000526002602052604060002090565b610c6381856155a2565b50610c76610c72825460ff1690565b1590565b610c86575b505050505001610bfd565b7ffde361574a066b44b3b5fe98a87108b7565e327327954c4faeea56a4e6491a0a92610d2592610d01610d0793610cd6610ccf610cc86020968781019061158b565b3691610608565b898b615303565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b0161152d565b6040519384526001600160a01b039081169416929081906020820190565b0390a33880808080610c7b565b50604060031936011261000e5767ffffffffffffffff60043581811161000e57610d6090369060040161078b565b60249291923591821161000e5761092b92610d8d610d8561091794369060040161078b565b939092614750565b60405190610d9a82610299565b60008252613e21565b5060031960408136011261000e576004359067ffffffffffffffff821161000e57604090823603011261000e57610dfd610de16020926004016146e1565b60405190610dee82610299565b600082523391602435916141fd565b6040519015158152f35b5060031960808136011261000e576004359067ffffffffffffffff9081831161000e5760a090833603011261000e5760243590811161000e5761092b91610e55610e9692369060040161078b565b90606435610e628161034b565b6001600160a01b038116610ea85750610e90610e8433945b3690600401610678565b91604435933691611bff565b906141fd565b60405190151581529081906020820190565b610e84610e909194610e7a565b5060a060031936011261000e5767ffffffffffffffff60043581811161000e57610ee390369060040161078b565b9060243583811161000e57610efc90369060040161078b565b91909260443594851161000e57610f25610f1d610ba496369060040161078b565b929093614750565b9160405193610f3385610299565b6000855260843595339560643595612a0b565b503461000e57602060031936011261000e576020610f83600435610f698161034b565b6001600160a01b0316600052600160205260406000205490565b604051908152f35b503461000e57600060031936011261000e57610ff3610fa86127b4565b60405190610fb5826102b5565b600382527f312e3100000000000000000000000000000000000000000000000000000000006020830152604051928392606084526060840190610148565b9060208301526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660408301520390f35b5060031960208136011261000e5760043567ffffffffffffffff811161000e576102408160040192823603011261000e5761012435908160021c926001841193341585036112f85784936003821160028314916110d183600286117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe870102018815926001820185028460011b880103998a92600360a088026024013593168a6115dc565b6110e38260051b6101c40135986107ec565b156111b5575050506111036110f78261152d565b6001600160a01b031690565b6001600160a01b0390811660248401351761118b5761115f60449461115a6111759761116b9461113560a4890161152d565b9060648901946111448661152d565b9060e48b01359360c48c01359333931691611dcf565b61152d565b91610204840190611537565b93909201356119df565b61117f6001600055565b60405160018152602090f35b60046040517f6ab37ce7000000000000000000000000000000000000000000000000000000008152fd5b9194509161121e6110f7606461122396611228996111d1611514565b8a819b996111df839b6107ec565b1561122d5750610d01916111f560a4850161152d565b61120086860161152d565b9060e48601359160c4870135916001600160a01b03339216906120c8565b611ac5565b6122c4565b611175565b611236816107ec565b6003810361127d57506112789161124f60a4850161152d565b61125a86860161152d565b9060e48601359160c4870135916001600160a01b03339216906121be565b610d01565b806112896004926107ec565b036112c3576112789161129b8861152d565b6112a686860161152d565b6044860135916001600160a01b03602488013592169033906120c8565b611278916112d08861152d565b6112db86860161152d565b6044860135916001600160a01b03602488013592169033906121be565b6040517fa61be9f0000000000000000000000000000000000000000000000000000000008152346004820152602490fd5b0390fd5b503461000e5761133c36610bb4565b611344615017565b60005b81811061135a5760405160018152602090f35b611365818385614fe2565b61136e8161152d565b60209061137c82840161152d565b6001600160a01b0391828116938433141580611508575b6114de576040956113a681880182611cfc565b6060808401926113b68486611d50565b90916080948a8689016113c890611dae565b976113d3908a611d50565b9a90506113de6102f4565b6001600160a01b03909c168c526001600160a01b03909116908b0152369061140592610381565b8c890152369061141492610440565b9086015284019061142491611db8565b60a0808201359084015260c0808201359084015260e08082013590840152610100808201359084015261012080820135908401526101409182840152013561146b9161268a565b93611480856000526002602052604060002090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000166101001790555193845216917f6bacc01dbe442496068f7d234edd811f1a5f833243e0aec824f86ab861f3c90d90602090a3600101611347565b60046040517f80ec7374000000000000000000000000000000000000000000000000000000008152fd5b50838316331415611393565b60405190611521826102b5565b60208083523683820137565b3561043d8161034b565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e57602001918160061b3603831361000e57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e5760200191813603831361000e57565b9591906115e7615008565b6115fb610140880135610120890135615296565b50611604611927565b611622611615610200890189611537565b6101e08a013591506118f6565b7f00000000000000000000000000000000000000000000000000000000000000006080528160a0526060602460c037604060646101203760e06080908120610160526001610264359081016102a060059290921b918201526102c081019384526024906102e00137610160928460a0528560c052600060e05260005b8394610204358210156116fb5790604060a0600193602090818560061b6102840161010037838560061b6102840161012037019660e0608020885201968888528960c08201526101008360061b610284019101370193929361169e565b5090929350969590966001610204350160051b610160206060525b83610264358210156117495790604060a060019301958787528860c08201526101008360061b6102840191013701611716565b505093509490506103229391507f00000000000000000000000000000000000000000000000000000000000000006080528260a052606060c460c03760206101046101203760c0608020600052602060002060e05260016102643560051b610200015261022092836102643560051b0152606060c46102406102643560051b01376118ee610cc8608435936117f1856001600160a01b03166000526001602052604060002090565b547f00000000000000000000000000000000000000000000000000000000000000006080526040608460a03760605161010052846101205260a0610144610140376101e0526101809485608020956102643560051b0190868252336101a06102643560051b015260806101c06102643560051b01526101206101e06102643560051b01527f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f3160a4359260a061026435026101e00190a360006060526118e56060820161115a6118bf8261152d565b966118cc6080860161152d565b906001600160a01b03809916906101608701358b61569d565b9581019061158b565b9216906147dc565b106118fd57565b60046040517f466aa616000000000000000000000000000000000000000000000000000000008152fd5b601861012435106102643560061b61026001610244351461024061022435146020600435141616161561195657565b60046040517f39f3e3fd000000000000000000000000000000000000000000000000000000008152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90156119b95790565b61043d611980565b91908110156119d2575b60061b0190565b6119da611980565b6119cb565b919234936000915b808310611a4257505050828211611a185781611a0291611e97565b808211611a0d575050565b610322910333611e97565b60046040517f1a783b8d000000000000000000000000000000000000000000000000000000008152fd5b909194611a508683856119c1565b90813590808211611a1857611a748260206001950135611a6f8161034b565b611e97565b03950191906119e7565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818110611ab9570390565b611ac1611a7e565b0390565b90939291908115611b85579333611ade60a0830161152d565b60e08301359260c08101355b61118b578460051b6101e40335946102008201611b078184611537565b93905060005b848110611b24575050505050956103229596611f2c565b8989858e611b3c85611b368989611537565b906119c1565b803592611b6a575b91611b649391611b5d6110f7602060019998960161152d565b908c611f2c565b01611b0d565b92909493919b8c611b7a91611aae565b9b9193949092611b44565b933394611b918261152d565b6040830135926020810135611aea565b81601f8201121561000e57803591611bb883610324565b92611bc660405194856102d1565b808452602092838086019260051b82010192831161000e578301905b828210611bf0575050505090565b81358152908301908301611be2565b909291611c0b84610324565b91604094611c1b865194856102d1565b839581855260208095019160051b83019380851161000e5783925b858410611c465750505050505050565b67ffffffffffffffff90843582811161000e5786019060a08285031261000e578451611c7181610254565b8235815289830135600281101561000e578a82015285830135868201526060808401359082015260808084013594851161000e57611cb3868c96879601611ba1565b90820152815201930192611c36565b90611cf090610bdf9a99989796959493986001600160a01b03811615600014611cf6575033985b3691611bff565b90612a0b565b98611ce9565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e576020019160a082023603831361000e57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561000e570180359067ffffffffffffffff821161000e576020019160c082023603831361000e57565b600411156107f657565b3561043d8161050f565b6004821015611dc45752565b611dcc6107bc565b52565b949290959391841515600014611e3b5761032296604051967f4ce34aa2000000000000000000000000000000000000000000000000000000008852602060048901526001602489015260448801526064870152608486015260a485015260c484015260e4830152612451565b9291946002919450611e4c816107ec565b03611e8b57600103611e61576103229361504d565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b9291906103229461515b565b90611ea181611efb565b600080808084865af115611eb3575050565b60449250611ebf612895565b6001600160a01b03604051927f470c7c1d0000000000000000000000000000000000000000000000000000000084521660048301526024820152fd5b15611f0257565b60046040517f91b3e514000000000000000000000000000000000000000000000000000000008152fd5b929193949094611f3b83611efb565b611f4581836122b1565b806120ba575050604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d1515811615611fb4575b505050505050604052606052565b80863b151516611fa657908795969115611ff457602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b1561202e57506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d61206d575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c9080600302918082116120a1575b505060205a9101106120985785612034565b833d81803e3d90fd5b8080600392028380020360091c92030201018680612086565b9061032295929493916125c0565b959092949391936120d981836122b1565b806120f0575050600103611e61576103229361504d565b9060649593916000979593975060208251146000146121ab5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c0868201600181510180915261214b565b9590919293946121cd86611efb565b6121d781836122b1565b806121e75750506103229461515b565b906064959694939291602082511460001461229e5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c0868201600181510180915261223e565b906020820151036122bf5750565b610322905b60408082510361244d57602082015160c06064840151026044019180519260206001600160a01b036000928184927f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001783528684527f000000000000000000000000000000000000000000000000000000000000000086526055600b201696855281805284880182885af190519015612402577fffffffff000000000000000000000000000000000000000000000000000000007f4ce34aa2000000000000000000000000000000000000000000000000000000009116036123c05750505060209052565b517f1cf99b2600000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03919091166024820152604490fd5b611329848361240f612895565b517fd13d53d40000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201529081906024820190565b5050565b6040519160206001600160a01b036101046000938285937f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001784528685527f00000000000000000000000000000000000000000000000000000000000000006040526055600b20169660405282805282875af190519015612574577fffffffff000000000000000000000000000000000000000000000000000000007f4ce34aa200000000000000000000000000000000000000000000000000000000911603612530575050565b6040517f1cf99b2600000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03919091166024820152604490fd5b61132983612580612895565b6040517fd13d53d40000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201529081906024820190565b9060649492939160208251146000146126775760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280878401525b02019260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe484015260048301526024820152600060448201520152565b5060c08582016001815101809152612615565b91909161014081018051917f0000000000000000000000000000000000000000000000000000000000000000604051604083018051928351926020809501906000915b868684106127915750505050506040519160051b8220917f00000000000000000000000000000000000000000000000000000000000000009093606086019481865101906000915b8a831061276d575050505050601f198660051b604051209401978851907f00000000000000000000000000000000000000000000000000000000000000008a5282519383528451958552865261018089209852525252565b838082601f19600194510180519089815260e0812087525201920192019190612715565b8082601f19600194510180519088815260c08120875252019201920191906126cd565b467f0000000000000000000000000000000000000000000000000000000000000000036127ff577f000000000000000000000000000000000000000000000000000000000000000090565b60405160208101907f000000000000000000000000000000000000000000000000000000000000000082527f000000000000000000000000000000000000000000000000000000000000000060408201527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260a0815261288f8161027d565b51902090565b3d61289c57565b601f3d0160051c60405160051c9080600302918082116128cf575b505060205a9101106128c557565b3d6000803e3d6000fd5b8080600392028380020360091c920302010138806128b7565b919082604091031261000e576040516040810181811067ffffffffffffffff821117612922575b6040526020808294803584520135910152565b61292a610224565b61290f565b92919261293b82610324565b60409261294a845192836102d1565b819581835260208093019160061b84019381851161000e57915b84831061297357505050505050565b83869161298084866128e8565b815201920191612964565b9291909261299884610324565b916129a660405193846102d1565b829480845260208094019060051b83019282841161000e5780915b8483106129d057505050505050565b823567ffffffffffffffff811161000e57820184601f8201121561000e578691612a00868385809535910161292f565b8152019201916129c1565b96989792612a268a612a359695612a2d95949998998b612c40565b369161298b565b93369161298b565b908251825191612a4d612a48848461314b565b61366d565b9760009586915b848310612b47575050506000935b838510612abf57505050505080612ab4575b50825115612a8a5782612a8691613b15565b9190565b60046040517fd5da9a1b000000000000000000000000000000000000000000000000000000008152fd5b835103835238612a74565b909192939488612ada84612ad38986612c1e565b518a613745565b8051608001516001600160a01b03166001600160a01b03612b086110f760208501516001600160a01b031690565b911603612b225750506001809101955b0193929190612a62565b8791612b4191612b3a85896001979c01038093612c1e565b528b612c1e565b50612b18565b9091968a612b6583612b5e8b879b98999a9b612c1e565b518c6136c9565b8051608001516001600160a01b03166001600160a01b03612b936110f760208501516001600160a01b031690565b911603612bb05750506001809101975b0191909594939295612a54565b8991612bcd91612bc6856001969d038093612c1e565b528d612c1e565b50612ba3565b90612bdd82610324565b612bea60405191826102d1565b828152601f19612bfa8294610324565b0190602036910137565b602090805115612c12570190565b612c1a611980565b0190565b6020918151811015612c33575b60051b010190565b612c3b611980565b612c2b565b93929091612c4c615008565b845192612c5884612bd3565b9160008352601d604560003560e01c061160011b9060005b868110612d30575050600314612d0657612c8a9086613266565b60005b838110612c9c57505050509050565b80612ca960019284612c1e565b5115612d0157612cfb612cbc8289612c1e565b5151612cc88386612c1e565b519086612cdc82516001600160a01b031690565b60208301516001600160a01b03169060606040850151940151946145e5565b01612c8d565b612cfb565b60046040517f12d3f5a3000000000000000000000000000000000000000000000000000000008152fd5b612d3a818a612c1e565b51918015612ebf57612d4d868685614cb3565b9290916001850189528215612eab57907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91612d89868b612c1e565b52019380519260a084015193604060c08201519101518051908560005b838110612e405750505050606080935101519485519560005b878110612dd85750505050505050506001905b01612c70565b808760a0612de860019486612c1e565b5188612e2489898d6080860197612e01895187836131fa565b918701958651908a518214600014612e30575050508085525b80885284516131a0565b90520151905201612dbf565b612e39926131fa565b8552612e1a565b612e4a8184612c1e565b519b8c5115179b86868b60808401938451612e669085896131fa565b60608192019586519881518a1460001499612e919760019b612e9b575050508187525b52845161315f565b9052018690612da6565b612ea4926131fa565b8752612e89565b509360019392506000915060200152612dd2565b91906000602060019301528181018652612dd2565b612edc615008565b805192612ee884612bd3565b92600091828552601d6045843560e01c061160011b90835b878110612f90575050600314612d0657612f1a9083613266565b838110612f275750505050565b80612f3460019285612c1e565b5115612f8b57612f85612f478285612c1e565b5151612f538387612c1e565b5190612f6681516001600160a01b031690565b60208201516001600160a01b0316906060604084015193015193614513565b01612f1a565b612f85565b612f9a8187612c1e565b51918581156130fb5750612faf888685614ee0565b929091600185018b528883156130e95750907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91612fed868d612c1e565b52019380519260a084015191604060c0860151950151805190858c5b83811061308f5750505050606090510151938451948a5b86811061303857505050505050506001905b01612f00565b8061304560019284612c1e565b5160a0608082019189613083888b61305f87518d866131fa565b60608601948d8651908a518214600014612e305750505080855280885284516131a0565b90520151905201613020565b6130998184612c1e565b519b8c5115179b868a89608084019384516130b59085896131fa565b60608192019586519881518a14600014996130df9760019b612e9b5750505081875252845161315f565b9052018690613009565b92505093600193925060200152613032565b6020600193929401528181018852613032565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482118115151661313f570290565b613147611a7e565b0290565b81198111613157570190565b612c1a611a7e565b909283820361316e5750505090565b82939161318a613196946131909303954203918287039061310e565b9261310e565b9061314b565b9081049015150290565b90928382036131af5750505090565b926131906131cd9261318a856001969703964203918288039061310e565b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830104019015150290565b9190918281146132435782818309613219576132159161310e565b0490565b7fc63cf0890000000000000000000000000000000000000000000000000000000060005260046000fd5b50905090565b600211156107f657565b5161043d816107ec565b611dcc826107ec565b815181519260005b8281106133a45750505060005b82811061328757505050565b6132918183612c1e565b516132c56132b160208301516effffffffffffffffffffffffffffff1690565b6effffffffffffffffffffffffffffff1690565b1561339b5751606081018051519060005b828110613354575050506040809101908151519160005b83811061330257505050506001905b0161327b565b61331f613319613313838551612c1e565b51613253565b60031090565b61332b576001016132ed565b600483517fa6cfc673000000000000000000000000000000000000000000000000000000008152fd5b613365613319613313838551612c1e565b613371576001016132d6565b60046040517fff75a340000000000000000000000000000000000000000000000000000000008152fd5b506001906132fc565b6133ae8183612c1e565b5180519086821015613565576020916133e56132b1846133ce848b612c1e565b5101516effffffffffffffffffffffffffffff1690565b1561355a576133f49087612c1e565b515191604092838301519183015161340b81613249565b61341481613249565b6134e55783015180518210156134bc579061342e91612c1e565b5191600383519361343e856107ec565b84906134558482019160048351981485039061325d565b606085015190525b11156134935750906001929181613478575b50505b0161326e565b61348c91608060608301519201519161358f565b388061346f565b600490517f94eb6af6000000000000000000000000000000000000000000000000000000008152fd5b600484517fbfb3f8ce000000000000000000000000000000000000000000000000000000008152fd5b929060608094015180518210156135315760039161350291612c1e565b5193845194613510866107ec565b85916135278583019260048451991486039061325d565b850151905261345d565b600483517f6088d7de000000000000000000000000000000000000000000000000000000008152fd5b505050600190613472565b60046040517f869586c4000000000000000000000000000000000000000000000000000000008152fd5b91909160009081526020808220928181019282825192600593841b0101915b8285106135eb575050505050036135c157565b60046040517f09bde339000000000000000000000000000000000000000000000000000000008152fd5b8451808711821b968752958418959095526040812094938301936135ae565b604051906060820182811067ffffffffffffffff821117613660575b8060405260408361363683610254565b6000928381528360808301528360a08301528360c08301528360e083015281528260208201520152565b613668610224565b613626565b9061367782610324565b61368460405191826102d1565b828152601f196136948294610324565b019060005b8281106136a557505050565b6020906136b061360a565b82828501015201613699565b906002821015611dc45752565b9092916136d461360a565b93805115613714576136f6926001600160a01b038693166080845101526137e9565b81516060810151156137055750565b60806000918260208601520152565b60246040517f375c24c100000000000000000000000000000000000000000000000000000000815260006004820152fd5b92919061375061360a565b9381511561378d576137639185916139aa565b60208301903382526040840152825190606082015115613781575050565b60009182608092520152565b60246040517f375c24c100000000000000000000000000000000000000000000000000000000815260016004820152fd5b507f7fda72790000000000000000000000000000000000000000000000000000000060005260046000fd5b92919260208201906020825151825181101561399d575b60051b82010151928351926020604085015181835101518151811015613990575b60051b01015160009460208697015161397a575b9061012060609260408b5193805185526020810151602086015201516040840152805160208c0152015160408a01522091805160051b01905b8181106138c1575050505060608293945101526138885750565b60011461389757610322611a7e565b7f91b3e5140000000000000000000000000000000000000000000000000000000060005260046000fd5b60209095949501906020825151855181101561396d575b60051b85010151602081015115613964575160606020604083015181865101518151811015613957575b60051b01015196818801519081158a8381011060011b17179801966000828201522084149060408a0151610120820151149060208b015190511416161561394a575b9061386e565b6139526137be565b613944565b61395f6137be565b613902565b50949394613944565b6139756137be565b6138d8565b6060820180516000909152801597509550613835565b6139986137be565b613821565b6139a56137be565b613800565b9291602080830194855151918151831015613b08575b80600593841b8301015194606093828588510151818b5101518151811015613afb575b831b010151926000968188990151613ae6575b51948451865281850151828701526040850151604087015260a0809501519a608087019b8c52878720948051851b01905b818110613a4257505050505050508394955001526138885750565b83909a999a01908c848351518551811015613ad9575b871b850101518581015115613acf578a869151015181855101518151811015613ac2575b881b0101518a81019b8d8d518091019e8f9115911060011b17179c9b60009052888b822089149251910151141615613ab5575b90613a27565b613abd6137be565b613aaf565b613aca6137be565b613a7c565b5050999899613aaf565b613ae16137be565b613a58565b848701805160009091528015995097506139f6565b613b036137be565b6139e3565b613b106137be565b6139c0565b908151613b2181612bd3565b9260005b828110613be5575050503490613b39611514565b9080519060005b828110613b7457505050613b53906122c4565b80613b64575b5061043d6001600055565b613b6e9033611e97565b38613b59565b613b7e8183612c1e565b518051908151613b8d816107ec565b613b96816107ec565b15613bca575b8560019392826040613bbb6020613bc49601516001600160a01b031690565b91015191613cae565b01613b40565b9560608293920181815111611a185751900395909190613b9c565b613bef8183612c1e565b51613c0f6132b160208301516effffffffffffffffffffffffffffff1690565b15613ca557613c27613c218388612c1e565b60019052565b606080915101519081519160005b838110613c4a57505050506001905b01613b25565b82613c558284612c1e565b51015180613c665750600101613c35565b6040517fa5f542080000000000000000000000000000000000000000000000000000000081526004810187905260248101929092526044820152606490fd5b50600190613c44565b9290918351613cbc816107ec565b613cc5816107ec565b613d1a57505050613ce36110f760208301516001600160a01b031690565b6001600160a01b03604083015191161761118b57806060613d1160806103229401516001600160a01b031690565b91015190611e97565b90919260018151613d2a816107ec565b613d33816107ec565b03613d8357604081015161118b5761032293613d5960208301516001600160a01b031690565b906001600160a01b036060613d7860808601516001600160a01b031690565b940151931691611f2c565b9260028451613d91816107ec565b613d9a816107ec565b03613de05783613db760206103229601516001600160a01b031690565b60808201516001600160a01b0316926001600160a01b03606060408501519401519416916120c8565b83613df860206103229601516001600160a01b031690565b60808201516001600160a01b0316926001600160a01b03606060408501519401519416916121be565b90613e33909493929482519083612ed4565b613e3c8261366d565b9160009485915b808310613e705750505090613e619184829495613e65575b50613b15565b5090565b825103825238613e5b565b909195613e7e878385613f13565b613ea4613e8b8280611537565b90613e9b60209485810190611537565b92909189613f6c565b906001600160a01b03613ed96110f7613ec960808651016001600160a01b0390511690565b938501516001600160a01b031690565b911603613ef057506001809101965b019190613e43565b96613f0d8298600193830390613f06828a612c1e565b5287612c1e565b50613ee8565b9190811015613f54575b60051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18136030182121561000e570190565b613f5c611980565b613f1d565b61043d9036906128e8565b92909391613f7861360a565b948115801561415e575b61413457613f8e61360a565b613fa381613f9d36888861292f565b886139aa565b5191613fba87613fb436848661292f565b886137e9565b613fc48751613253565b835190613fd0826107ec565b613fd9826107ec565b613fe2816107ec565b148015906140fc575b80156140e9575b6140bf5761043d9561406f95608095896060948588019687518784510151106000146140825750505061403161402c8593614057936119b0565b613f61565b60208361404a8d828a5191510151900396845190612c1e565b5151015191015190612c1e565b5101528651015190525b01516001600160a01b031690565b6080835101906001600160a01b03169052565b86979694506140b1935061404a856140a161402c6020956040956119b0565b9451015188518551910397612c1e565b510152519086510152614061565b60046040517f09cfb455000000000000000000000000000000000000000000000000000000008152fd5b5060408751015160408401511415613ff2565b508651602001516001600160a01b03166001600160a01b0361412b6110f760208701516001600160a01b031690565b91161415613feb565b60046040517f98e9db6e000000000000000000000000000000000000000000000000000000008152fd5b508315613f82565b6040519061417382610254565b604051608083610160830167ffffffffffffffff8111848210176141f0575b6040526000808452806020850152606093846040820152848082015281848201528160a08201528160c08201528160e08201528161010082015281610120820152816101408201528252806020830152604082015282808201520152565b6141f8610224565b614192565b909291614208615017565b600260005561421784836148c0565b9490919260405195614228876102b5565b6001875260005b6020808210156142515790602091614245614166565b90828b0101520161422f565b505061428583959761428061429e9a61428e97998351156142ba575b60208401528251156142ad575b82613266565b612c04565b515195866142c7565b81516001600160a01b0316612cdc565b6142a86001600055565b600190565b6142b5611980565b61427a565b6142c2611980565b61426d565b939192909360a093848201519360c0830151966142e2611514565b96604092838601908151519160005b8381106143d7575050505034986060809601978851519860005b8a8110614338575050505050505050505050614326906122c4565b8061432e5750565b6103229033611e97565b614343818351612c1e565b51898101805161435d87878d8c60808801958651906144a1565b8092528783015190528151614371816107ec565b61437a816107ec565b15614397575b50906143918d8c6001943390613cae565b0161430b565b90919e9d8082116143ae579d9e9d039c908a614380565b600489517f1a783b8d000000000000000000000000000000000000000000000000000000008152fd5b6143e2818351612c1e565b5180516143ee816107ec565b6143f7816107ec565b15614441579061443b8d8f93868f8d6144236001988e936060870193845195608089019687519061446a565b9052528c610120613bbb82516001600160a01b031690565b016142f1565b600488517f12d3f5a3000000000000000000000000000000000000000000000000000000008152fd5b90939084810361448057505061043d93506131fa565b938361449561043d979661449b9496866131fa565b936131fa565b9061315f565b9093908481036144b757505061043d93506131fa565b938361449561043d97966144cc9496866131fa565b906131a0565b90815180825260208080930193019160005b8281106144f2575050505090565b909192938260a08261450760019489516107fe565b019501939291016144e4565b91939290936040805193608091828601918652602090600082880152838188015285518093528160a088019601936000915b84831061459a5750505050505091614595827f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31948380950360608501526001600160a01b038091169716956144d2565b0390a3565b90919293949684836001928a5180516145b2816107ec565b8252808401516001600160a01b031684830152858101518683015260609081015190820152019801959493019190614545565b92909493916040918251946080918287019187526001600160a01b0394856020921682890152838189015286518093528160a089019701936000915b84831061466a57505050505050828285949361459593867f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f319896036060870152169716956144d2565b90919293949784836001928b518051614682816107ec565b8252808401518c1684830152858101518683015260609081015190820152019901959493019190614621565b9035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea18136030182121561000e570190565b6146e9614166565b506147336147056146fa83806146ae565b92602081019061158b565b61471c6040519461471586610254565b3690610524565b845260016020850152600160408501523691610608565b606082015260405161474481610299565b60008152608082015290565b61475982610324565b9161476760405193846102d1565b808352601f1961477682610324565b0160005b8181106147c557505060005b8181106147935750505090565b806147a96147a46001938587613f13565b6146e1565b6147b38287612c1e565b526147be8186612c1e565b5001614786565b6020906147d0614166565b8282880101520161477a565b929190836000526002602052604060002091825460ff8160081c1661487b576effffffffffffffffffffffffffffff8160101c1661484a579460ff7101000000000000000000000000000001000195961615614839575b50505055565b61484292615303565b388080614833565b602486604051907fee9e0e630000000000000000000000000000000000000000000000000000000082526004820152fd5b602486604051907f1a5155740000000000000000000000000000000000000000000000000000000082526004820152fd5b90805b6148b7575090565b809106806148af565b90918151926148db610c7260a086015160c087015190615296565b614ca7576148fe6132b160208501516effffffffffffffffffffffffffffff1690565b9361491e6132b160408601516effffffffffffffffffffffffffffff1690565b948581118015614c9f575b614c755785811080614c5d575b614c335761498261494683614fa9565b9360e0840151608085015161495a81611da4565b85516001600160a01b0316918761497b60208901516001600160a01b031690565b948b615cc1565b614996836000526002602052604060002090565b916149a4610c7284866155a2565b614c23578254958460ff881615614bfc575b5050506effffffffffffffffffffffffffffff90818660101c169560881c96871515600014614b7f5760018103614b4757505085945b856149f7888361314b565b11614b3d575b86614a079161314b565b8082871183831117614ad6575b5090614a8f818493614a4e614ad19660017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b84547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff16911660101b70ffffffffffffffffffffffffffffff000016178355565b815470ffffffffffffffffffffffffffffffffff1690861660881b7fffffffffffffffffffffffffffffff000000000000000000000000000000000016179055565b929190565b9690614ae987614aef92989594986148ac565b826148ac565b80150180809204970492049480861181841117614b0e57909138614a14565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80860396506149fd565b959096868103614b58575b506149ec565b614b7281614b6c89614b78959b9a9b61310e565b9861310e565b9761310e565b9438614b52565b9550955090614ad191614bb78260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b81547fffffffffffffffffffffffffffffff00000000000000000000000000000000ff1687821660101b70ffffffffffffffffffffffffffffff000016178255614a8f565b6060614c12614c1b94516001600160a01b031690565b92015191615303565b3880846149b6565b5050509150915090600090600090565b60046040517fa11b63ff000000000000000000000000000000000000000000000000000000008152fd5b5060016080830151614c6e81611da4565b1615614936565b60046040517f5a052b32000000000000000000000000000000000000000000000000000000008152fd5b508015614929565b50600092508291508190565b919290928251614ccf610c7260a083015160c0840151906152df565b614ed057614cf26132b160208601516effffffffffffffffffffffffffffff1690565b614d116132b160408701516effffffffffffffffffffffffffffff1690565b958682118015614ec8575b614c755786821080614eb0575b614c3357614d7d90614d3a84614fa9565b9460e0850151608086015190614d4f82611da4565b87614d6188516001600160a01b031690565b93614d7660208a01516001600160a01b031690565b958c615da2565b614d91836000526002602052604060002090565b91614d9f610c728486615645565b614c23578254958460ff881615614e92575b5050506effffffffffffffffffffffffffffff90818660101c169560881c96871515600014614b7f5760018103614e6657505085945b85614df2888361314b565b11614e5c575b86614e029161314b565b8082871183821117614e48575090614a8f818493614a4e614ad19660017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b969050614aef614ae98789989594986148ac565b8086039650614df8565b959096868103614e77575b50614de7565b614b7281614b6c89614e8b959b9a9b61310e565b9438614e71565b6060614c12614ea894516001600160a01b031690565b388084614db1565b5060016080840151614ec181611da4565b1615614d29565b508115614d1c565b5050915050600090600090600090565b919290928251614efc610c7260a083015160c084015190615296565b614ed057614f1f6132b160208601516effffffffffffffffffffffffffffff1690565b614f3e6132b160408701516effffffffffffffffffffffffffffff1690565b958682118015614fa1575b614c755786821080614f89575b614c3357614f6790614d3a84614fa9565b614f7b836000526002602052604060002090565b91614d9f610c7284866155a2565b5060016080840151614f9a81611da4565b1615614f56565b508115614f49565b61043d90614fc2606082015151610140830151906118f6565b80516001600160a01b03166000908152600160205260409020549061268a565b909161043d92811015614ffb575b60051b8101906146ae565b615003611980565b614ff0565b615010615017565b6002600055565b60016000540361502357565b60046040517f7fa8a987000000000000000000000000000000000000000000000000000000008152fd5b9092813b1561512d57604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156150a65750505050604052606052565b8593943d6150e9575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211615114575b505060205a91011061209857856150af565b8080600392028380020360091c92030201018680615102565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b1561526857604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af1156151d857505050505060805260a05260c052604052606052565b89949550883d61521b575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c90806003029180821161524f575b505060205a91011061524657866151e3565b843d81803e3d90fd5b8080600392028380020360091c92030201018780615234565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b42109081156152d4575b506152aa57600190565b60046040517f6f7eac26000000000000000000000000000000000000000000000000000000008152fd5b9050421015386152a0565b42109081156152f8575b506152f357600190565b600090565b9050421015386152e9565b9091336001600160a01b0383161461559d5761531d6127b4565b926000937f190100000000000000000000000000000000000000000000000000000000000085526002526022526042832090836022528380528392815191601f198101805184604103918860018411938415615532575b508514851515169788156153c3575b5050505050505050156153935750565b60049061539e612895565b7f4f7fb80d000000000000000000000000000000000000000000000000000000008152fd5b909192939495969750604082527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8501937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0855196019660208b60648a519b7f1626ba7e000000000000000000000000000000000000000000000000000000009d8e8b528c520188845afa998a615469575b505050505252523880808080808080615383565b8b51036154765780615455565b908a913b61550a576154e257640101000000821a156154b757807f815e1d640000000000000000000000000000000000000000000000000000000060049252fd5b6024917f1f003d0a000000000000000000000000000000000000000000000000000000008252600452fd5b807f8baa579f0000000000000000000000000000000000000000000000000000000060049252fd5b6004827f4f7fb80d000000000000000000000000000000000000000000000000000000008152fd5b9850506040840180519060608601518b1a99615569575b89865288835260208b60808560015afa5083835287865252885138615374565b9850601b8160ff1c01987f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82168152615549565b505050565b905460ff8160081c16615614576effffffffffffffffffffffffffffff8160101c1690816155d3575b505050600190565b60881c11156155e35780806155cb565b602490604051907f10fda3e10000000000000000000000000000000000000000000000000000000082526004820152fd5b602482604051907f1a5155740000000000000000000000000000000000000000000000000000000082526004820152fd5b906000905460ff8160081c16615694576effffffffffffffffffffffffffffff8160101c16908161567a575b50505050600190565b60881c111561568a578080615671565b6155e35750600090565b50905050600090565b90929160019060048110156156fd575b11806156ea575b806156d7575b6156c5575b50505050565b6156ce9361570a565b388080806156bf565b506001600160a01b0382163314156156ba565b506001600160a01b0384163314156156b4565b6157056107bc565b6156ad565b6000919290829161032295604051906001600160a01b0360208301937f0e1d31dc00000000000000000000000000000000000000000000000000000000855288602485015233604485015216606483015260848201526084815261576d8161027d565b51915afa615e78565b90815180825260208080930193019160005b828110615796575050505090565b909192938260a0600192875180516157ad816107ec565b8252808401516001600160a01b03168483015260408082015190830152606080820151908301526080908101519082015201950193929101615788565b90815180825260208080930193019160005b82811061580a575050505090565b909192938260c060019287518051615821816107ec565b8252808401516001600160a01b039081168584015260408083015190840152606080830151908401526080808301519084015260a0918201511690820152019501939291016157fc565b906004821015611dc45752565b6060519081815260208091019160809160005b828110615899575050505090565b83518552938101939281019260010161588b565b90815180825260208080930193019160005b8281106158cd575050505090565b8351855293810193928101926001016158bf565b90815180825260208092019182818360051b85019501936000915b84831061590c5750505050505090565b909192939495848061595e83856001950387528a518051825261593584820151858401906136bc565b60408082015190830152606080820151908301526080809101519160a0809282015201906158ad565b98019301930191949392906158fc565b92615b02906001600160a01b0361043d9694615b0f94875216602086015260a06040860152805160a080870152610140906159b482880182516001600160a01b03169052565b6080615af1615a286159f38a6159dc6020870151610160809301906001600160a01b03169052565b6040860151906101808d01526102a08c0190615776565b60608501517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec08c8303016101a08d01526157ea565b615a3a838501516101c08c019061586b565b60a08401516101e08b015260c08401516102008b015260e08401516102208b015261010094858501516102408c015261012094858101516102608d015201516102808b0152615aa1602087015160c08c01906effffffffffffffffffffffffffffff169052565b60408601516effffffffffffffffffffffffffffff1660e08b015260608601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6095868c840301908c0152610148565b930151918784030190870152610148565b8381036060850152615878565b9160808184039101526158e1565b939061043d95936001600160a01b03615b0f94615cb393885216602087015260a06040870152805160a08088015261014090615b6482890182516001600160a01b03169052565b6080615ca2615bd8615ba38b6020860151615b8d61016091828401906001600160a01b03169052565b61018060408801519201526102a08d0190615776565b60608501518c82037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec0016101a08e01526157ea565b615bea838501516101c08d019061586b565b60a08401516101e08c015260c08401516102008c015260e08401516102208c015261010094858501516102408d0152610120948c6102608783015191015201516102808c0152615c52602087015160c08d01906effffffffffffffffffffffffffffff169052565b60408601516effffffffffffffffffffffffffffff1660e08c015260608601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6095868d840301908d0152610148565b930151918884030190880152610148565b9084820360608601526158ad565b909591929493600190615cd381611da4565b1180615d8f575b80615d7c575b615ced575b505050505050565b6080810151511580615d73575b15615d155750615d0a945061570a565b388080808080615ce5565b6000935083929450615d6061576d615d6e9760405192839160208301957f33131570000000000000000000000000000000000000000000000000000000008752338b6024860161596e565b03601f1981018352826102d1565b615d0a565b50855115615cfa565b506001600160a01b038416331415615ce0565b506001600160a01b038216331415615cda565b919692939594600190615db481611da4565b1180615e65575b80615e52575b615dcf575b50505050505050565b6080820151511580615e49575b15615df9575050615ded945061570a565b38808080808080615dc6565b600094508493955061576d615e4497615d6060405193849260208401967f33131570000000000000000000000000000000000000000000000000000000008852338c60248701615b1d565b615ded565b50805115615ddc565b506001600160a01b038516331415615dc1565b506001600160a01b038316331415615dbb565b15615f0f577f0e1d31dc000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000600060203d14615f04575b1603615ed35750565b602490604051907ffb5014fc0000000000000000000000000000000000000000000000000000000082526004820152fd5b602081803e51615eca565b602490615f1a612895565b604051907ffb5014fc0000000000000000000000000000000000000000000000000000000082526004820152fdfea26469706673582212200d53e9d4f26a00cc6af37b012c26f8d770777dfea74c99c52ea7d855f909a12a64736f6c634300080e003300000000000000000000000000000000f9490004c11cef243f5400493c00ad63000000000000000000000000000000000000000000
+```
+
+3. Deploy the `Seaport 1.2` contract by submitting:
+
+```
+cast send --rpc-url ${RPC_URL} --private-key ${PK} 0x0000000000ffe8b47b3e2130213b802212439497 0x64e03087000000000000000000000000000000000000000007cfc129aa3aa9006db365ff000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000068786101e060405234620000c3576200001f620000196200012e565b6200016b565b604051615c0190816200076e823960805181612622015260a05181612646015260c051816125ff015260e0518181816113b80152612430015261010051818181611257015261247f01526101205181818161145e01526124eb015261014051816125ac015261016051816125d3015261018051818181610f1f01528181612144015261226501526101a0518161559c01526101c05181818161218201526122a30152f35b600080fd5b50634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b03821117620000fb57604052565b62000105620000c8565b604052565b601f909101601f19168101906001600160401b03821190821017620000fb57604052565b62006858602081380391826040519384926200014b82856200010a565b833981010312620000c357516001600160a01b0381168103620000c35790565b60406004916200017a62000638565b610120526101005260e05260c05260a0526080528151806104e980820190828210600180881b038311176200029d575b6200636f833903906000f080156200028d575b6101a0524661014052620001fd60c0519060805160a0516040519360005281602052604052466060523060805260a0600020926040526000606052608052565b610160526001600160a01b03166101808190528151630a96ad3960e01b815292839182905afa9081156200027d575b60009162000247575b506101c052620002456001600055565b565b6200026d915060403d811162000275575b6200026481836200010a565b810190620002ba565b503862000235565b503d62000258565b62000287620002ad565b6200022c565b62000297620002ad565b620001bd565b620002a7620000c8565b620001aa565b506040513d6000823e3d90fd5b9190826040910312620000c3576020825192015190565b60405190620002e082620000df565b60038252565b6040519060a082016001600160401b038111838210176200030b575b604052606a8252565b62000315620000c8565b62000302565b6040519060c082016001600160401b03811183821017620003e5575b6040526084825263656e742960e01b60a0837f436f6e73696465726174696f6e4974656d2875696e7438206974656d5479706560208201527f2c6164647265737320746f6b656e2c75696e74323536206964656e746966696560408201527f724f7243726974657269612c75696e74323536207374617274416d6f756e742c60608201527f75696e7432353620656e64416d6f756e742c616464726573732072656369706960808201520152565b620003ef620000c8565b62000337565b6040519061010082016001600160401b0381118382101762000525575b60405260d482527f4b65792c75696e7432353620636f756e7465722900000000000000000000000060e0837f4f72646572436f6d706f6e656e74732861646472657373206f6666657265722c60208201527f61646472657373207a6f6e652c4f666665724974656d5b5d206f666665722c4360408201527f6f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f6e60608201527f2c75696e7438206f72646572547970652c75696e74323536207374617274546960808201527f6d652c75696e7432353620656e6454696d652c62797465733332207a6f6e654860a08201527f6173682c75696e743235362073616c742c6279746573333220636f6e6475697460c08201520152565b6200052f620000c8565b62000412565b60405190608082016001600160401b03811183821017620005c1575b6040526052825271766572696679696e67436f6e74726163742960701b6060837f454950373132446f6d61696e28737472696e67206e616d652c737472696e672060208201527f76657273696f6e2c75696e7432353620636861696e49642c616464726573732060408201520152565b620005cb620000c8565b62000551565b9081519160005b838110620005ea575050016000815290565b8060208092840101518185015201620005d8565b620006296200062294936200062262000245946040519788956020870190620005d1565b90620005d1565b03601f1981018452836200010a565b6040516200064681620000df565b600781526614d9585c1bdc9d60ca1b6020918201527f32b5c112df393a49218d7552f96b2eeb829dfb4272f4f24eef510a586b85feef9162000687620002d1565b828101906218971960e91b825251902091620006a2620002e6565b818101927f4f666665724974656d2875696e7438206974656d547970652c6164647265737384527f20746f6b656e2c75696e74323536206964656e7469666965724f72437269746560408301527f7269612c75696e74323536207374617274416d6f756e742c75696e7432353620606083015269656e64416d6f756e742960b01b6080830152620007326200031b565b926200076562000741620003f5565b936200074c62000535565b83815191012096815190209580518482012095620005fe565b80519101209056fe60806040526004361015610023575b361561001957600080fd5b610021614fef565b005b60003560e01c80156100eb57806306fdde031461016957806346423aa7146101605780635b34b9661461015757806379df72bd1461014e57806387201b4114610145578063881477321461013c578063a817440414610133578063a900866b1461012a578063b3a34c4c14610121578063e7acab2414610118578063ed98a5741461010f578063f07ec37314610106578063f2d12b12146100fd578063f47b7740146100f4578063fb0f3ee1146100eb5763fd9f1e100361000e576100e6610f50565b61000e565b506100e66101c8565b506100e6610ec8565b506100e6610df2565b506100e6610d8a565b506100e6610cc2565b506100e6610c05565b506100e6610b81565b506100e6610b17565b506100e6610a60565b506100e66108d6565b506100e66107c6565b506100e661059d565b506100e66104f5565b506100e6610474565b506100e661042e565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc906020828201126101c3576004359167ffffffffffffffff83116101c35782610240920301126101c35760040190565b600080fd5b506101d236610172565b6101243590600382169160021c91600183119234158403610420575b60038111907f0203020301010000000000000000000000000000000000000000000000000000811a9061024c8260a0850260240135887d010102030000000000000000000000000000000000000000000000000000851a888a61121a565b928060051b6101c4013596610260816106a8565b6102b3575050604435602435176102a55761028b9461027e916115b5565b61028661166d565b61569e565b6102956001600055565b60405160018152602090f35b0390f35b636ab37ce76000526004601cfd5b610286925061028b969161032a916102c96111a8565b9384836102d682956106a8565b6002810361032f5750610325918a6102f060a082016111bf565b6102fc606083016111bf565b60c060e08401359301359173ffffffffffffffffffffffffffffffffffffffff33921690611efe565b611738565b612105565b610338816106a8565b600381036103875750610325918a61035260a082016111bf565b61035e606083016111bf565b60c060e08401359301359173ffffffffffffffffffffffffffffffffffffffff33921690611fff565b806103936004926106a8565b036103dc57610325918a6103a6816111bf565b6103b2606083016111bf565b9073ffffffffffffffffffffffffffffffffffffffff602060408501359401359216903390611efe565b610325918a6103ea816111bf565b6103f6606083016111bf565b9073ffffffffffffffffffffffffffffffffffffffff602060408501359401359216903390611fff565b61042934611d42565b6101ee565b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357602080526707536561706f727460475260606020f35b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357600435600052600260205260806040600020546040519060ff81161515825260ff8160081c16151560208301526effffffffffffffffffffffffffffff8160101c16604083015260881c6060820152f35b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35761052d614fd5565b3360005260016020526020604060002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff43014060801c018091556040518181527f721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f833392a2604051908152f35b50346101c3577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6020813601126101c3576004359067ffffffffffffffff82116101c3576101609082360301126101c35761061263ffffffff6020921661014461060982600401611cd6565b91013590612423565b604051908152f35b9181601f840112156101c35782359167ffffffffffffffff83116101c3576020808501948460051b0101116101c357565b73ffffffffffffffffffffffffffffffffffffffff8116036101c357565b60a435906106768261064b565b565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600611156106b257565b610676610678565b60809080516106c8816106a8565b83528173ffffffffffffffffffffffffffffffffffffffff918260208201511660208601526040810151604086015260608101516060860152015116910152565b90815180825260208080930193019160005b828110610729575050505090565b909192938260e0600192604088516107428382516106ba565b8085015173ffffffffffffffffffffffffffffffffffffffff1660a0840152015160c08201520195019392910161071b565b9092916040820191604081528451809352606081019260208096019060005b8181106107b0575050506107ad9394818403910152610709565b90565b8251151586529487019491870191600101610793565b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35767ffffffffffffffff6004358181116101c35761081290369060040161061a565b50506024358181116101c35761082c90369060040161061a565b50506044358181116101c35761084690369060040161061a565b50506064359081116101c35761086090369060040161061a565b505061087961086d610669565b60c43590608435611813565b906102a160405192839283610774565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101c3576004359067ffffffffffffffff82116101c3576108d29160040161061a565b9091565b50346101c3576108e536610889565b505060046108fb63ffffffff8235168201611aba565b90610904614fd5565b81519060005b82811061091d5760405160018152602090f35b8061092a600192866129f9565b51805184608082015161093c816129a5565b610945816129a5565b14610a4857805173ffffffffffffffffffffffffffffffffffffffff1661096b826147c6565b90610980826000526002602052604060002090565b61098a81846155c2565b5061099d610999825460ff1690565b1590565b6109ae575b50505050505b0161090a565b6109f4610a1f928460207ff280791efe782edcf06ce15c8f4dff17601db3b88eb3805a0db7d77faf757f04986060890151516101408a015103610a3b575b0151916151f2565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b610a2e60405192839283614e9e565b0390a138808080806109a2565b610a43614cb0565b6109ec565b50506109a8565b9060206107ad928181520190610709565b5060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35760043567ffffffffffffffff8082116101c357610aab368360040161061a565b50506024359081116101c3576102a191610b0391610acc368260040161061a565b5050610afb610ae463ffffffff809416600401615b97565b92610aed6110db565b926000845216600401611c52565b903392613bcc565b604051918291602083526020830190610709565b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35773ffffffffffffffffffffffffffffffffffffffff600435610b688161064b565b1660005260036020526020604060002054604051908152f35b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6040813601126101c3576004359067ffffffffffffffff82116101c35760409082360301126101c357610bfb610be363ffffffff602093166004016119cd565b610beb6110db565b9060008252339160243591613f74565b6040519015158152f35b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6080813601126101c3576004359067ffffffffffffffff908183116101c35760a09083360301126101c3576024359081116101c3576102a191610cb091610c71368260040161061a565b5050610ca060643592610c838461064b565b610c9663ffffffff80921660040161186c565b9216600401611a2d565b9133811502019160443591613f74565b60405190151581529081906020820190565b5060a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357600467ffffffffffffffff81358181116101c357610d0d3682850161061a565b5050602435908282116101c357610d263683860161061a565b50506044359283116101c357610d7b61087994610d453686830161061a565b5050610d5963ffffffff8094168201615b97565b92610d7381610d666110db565b9660008852168301611b44565b951601611b44565b608435933393606435936126d4565b50346101c35760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c3576020610612600435610dcb8161064b565b73ffffffffffffffffffffffffffffffffffffffff16600052600160205260406000205490565b5060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c35767ffffffffffffffff600480358281116101c357610e3d3682840161061a565b5050602435908382116101c357610e563683850161061a565b50506044359384116101c3576102a193610eb0610ebc94610e793684830161061a565b5050610e9f610ea860643595610e8e8761064b565b63ffffffff92838092168501611bf5565b97168301611a2d565b931601611c52565b91338115020192613bcc565b60405191829182610a4f565b50346101c35760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c357610f006125a7565b606060005260205273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166040526303312e3260635260a06000f35b50346101c357610f5f36610889565b90610f68614fd5565b600091825b818110610f925783610f855760405160018152602090f35b610f8d614d6b565b610295565b80610fa06001928486614d10565b94610faa866111bf565b907f6bacc01dbe442496068f7d234edd811f1a5f833243e0aec824f86ab861f3c90d611075611006610fde60208b016111bf565b93610feb60808c01614d5e565b60048633148833141715911417179961014061060982611cd6565b9261104a61101e856000526002602052604060002090565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016610100179055565b60405193845273ffffffffffffffffffffffffffffffffffffffff9081169416929081906020820190565b0390a301610f6d565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519060a0820182811067ffffffffffffffff8211176110ce57604052565b6110d661107e565b604052565b604051906020820182811067ffffffffffffffff8211176110ce57604052565b604051906040820182811067ffffffffffffffff8211176110ce57604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176110ce57604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff811161119b575b01160190565b6111a361107e565b611195565b6111b06110fb565b90602082526020828136910137565b356107ad8161064b565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101c3570180359067ffffffffffffffff82116101c3576020019181360383136101c357565b959392919094611228614fa7565b61123061155f565b6101643561014435428211154282111761154b57505061020435610264351061153d5793907f00000000000000000000000000000000000000000000000000000000000000006080528060a0526060602460c037604060646101203760e06080908120610160526001610264359081016102a060059290921b918201526102c081019283526024906102e00137610160948360a0528460c052600060e05260009260005b83610204358210156113315790604060019261010060a060208560061b9a818c610284018537858c61028401610120376102a48c0135179d019860e06080208a5201988a8a528b60c08401526102840191013701969392966112d4565b5096509192979690976001610204350160051b610160206060525b836102643588101561138957906102a460a060019301958787528860c082015260408a60061b91610100836102840191013701351796019561134c565b50925095945095925073ffffffffffffffffffffffffffffffffffffffff91501161152f576107ad91611528917f00000000000000000000000000000000000000000000000000000000000000006080528060a052606060c460c03760206101046101203760c0608020600052602060002060e05260016102643560051b610200015261022090816102643560051b0152606060c46102406102643560051b013761036060843561145a8173ffffffffffffffffffffffffffffffffffffffff166000526001602052604060002090565b54967f00000000000000000000000000000000000000000000000000000000000000006080526040608460a037606051610100526101205260a0610144610140376101e09687526101809687608020976102643560051b0191888352336101a06102643560051b015260806101c06102643560051b0152610120826102643560051b01527f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f3160a06102643502938460a435940190a360006060526102643560051b01016040528101906111c9565b9083614371565b6339f3e3fd6000526004601cfd5b63466aa6166000526004601cfd5b6321ccfeb76000526020526040526044601cfd5b7401000000000000000000000000000000000000000060243560c4351760a43560843517171060186101243510166102643560061b61026001610244351461024061022435146020600435141616161561152f57565b608435916101043560e43560c4358315611627579461067695604051957f4ce34aa200000000000000000000000000000000000000000000000000000000875260206004880152600160248801526044870152606486015260848501523360a485015260c484015260e483015261223e565b925092806116366002926106a8565b0361166057928360016106769503611651575b503391615004565b61165a90611d31565b38611649565b91906106769333916150e3565b3460643560006102643560061b815b8181106116bd575050508181116116b0575b61169a81608435611d62565b8082116116a5575050565b610676910333611d62565b6116b8611d22565b61168e565b806102840135948086116116e657906116e08660409303966102a4830135611d62565b0161167c565b638ffff98084526004601cfd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9190820391821161173057565b6106766116f3565b919082156117d95760843592610104353360c43560e4355b6117cc575b8360051b6101e40335936102643560061b9060005b82811061177f57505050956106769596611dae565b87876102848301358c856117ab575b918493916117a5936102a46040970135908a611dae565b0161176a565b9891816117bf60409695936117a595611723565b9a9193509193945061178e565b6117d4611d53565b611755565b3392606435608435602435604435611750565b60209067ffffffffffffffff8111611806575b60051b0190565b61180e61107e565b6117ff565b906108d2929163ffffffff9161182f8360043516600401611bf5565b926118408160243516600401611a2d565b6118606118538360443516600401611b44565b9260643516600401611b44565b923381150201946126d4565b90604051610200810160405260806118c68294604060208201602086013760a084018085526118a563ffffffff918284351684016118f5565b6118b68160608401351683016118cb565b60608601528382013516016118cb565b910152565b9060206040519263ffffffff813563ffffffe0601f82011692848401908737168452830101604052565b6118c660609161016081853763ffffffff611917816040840135168301611927565b604086015283820135160161197a565b90641fffffffe082359263ffffffff841660405194818652602093849160051b168601019283928160a0809402910185378086015b83811061196c5750505050604052565b84815293820193810161195c565b90641fffffffe082359263ffffffff841660405194818652602093849160051b168601019283928160c0809402910185378086015b8381106119bf5750505050604052565b8481529382019381016119af565b906040516102008101604052611a13819360a083018084526119f963ffffffff918284351684016118f5565b6001602085015260016040850152602082013516016118cb565b606082015260806040519160208301604052600083520152565b803591600592641fffffffe081851b16604080519060209384848401018252829663ffffffff809216845260005b858110611a6e5750505050505050909150565b8083888093850101351683018551908360a091828401895287608093848484018737820135160101908d60018884351601901b8851928184018a52833782015282828801015201611a5b565b908135641fffffffe08160051b166040805160209384848301018352819663ffffffff809216835260005b858110611af55750505050505050565b808388809385010135168301611b34838851928984016101a085018b52611b2581848b81860135168501016118f5565b8452878a8201351601016118cb565b8382015282828701015201611ae5565b90813591641fffffffe08360051b166040516020928383830101604052819563ffffffff809116835260005b848110611b7f57505050505050565b80611b9587848180958801013516860101611ba1565b82828701015201611b70565b90813591604080519363ffffffff81168552602080641fffffffe08360051b168701019381643fffffffc0869460061b16910185378086015b828110611be75750505052565b848152938301938101611bda565b90813591641fffffffe08360051b166040516020928383830101604052819563ffffffff809116835260005b848110611c3057505050505050565b80611c468784818095880101351686010161186c565b82828701015201611c21565b908135641fffffffe08160051b166040805160209384848301018352819663ffffffff809216835260005b858110611c8d5750505050505050565b808388809385010135168301611cc6838851928984018a52611cb782898184013516830101611ba1565b8452878a820135160101611ba1565b8382015282828701015201611c7d565b9060405161016081016040528092611d16610140918281853763ffffffff611d05816040840135168301611927565b60408601526060820135160161197a565b80606084015251910152565b50638ffff9806000526004601cfd5b6369f958276000526020526024601cfd5b63a61be9f06000526020526024601cfd5b50636ab37ce76000526004601cfd5b611d6b82611d99565b600080808085855af115611d7d575050565b611d85612681565b63bc806b966000526020526040526044601cfd5b15611da057565b6391b3e5146000526004601cfd5b929193949094611dbd83611d99565b611dc781836120f2565b80611ef0575050604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d1515811615611e36575b505050505050604052606052565b80863b151516611e2857908795969115611e5b5786635f15d67287526020526024601cfd5b959192939515611e80575063988919238594526020526040526060526080526084601cfd5b3d611ea3575b5063f486bc87845260205260405260605260805260a05260a4601cfd5b601f3d0160051c9060051c908060030291808211611ed7575b505060205a910110611ece5785611e86565b833d81803e3d90fd5b8080600392028380020360091c92030201018680611ebc565b906106769592949391612359565b919395909294611f0e81836120f2565b80611f375750508460016106769603611f28575b50615004565b611f3190611d31565b38611f22565b815160649693959394929190602003611fec5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c08682016001815101809152611f8c565b95909192939461200e86611d99565b61201881836120f2565b80612028575050610676946150e3565b90606495969493929160208251146000146120df5760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280888401525b02019360037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe48501526004840152602483015260448201520152565b5060c0868201600181510180915261207f565b906020820151036121005750565b610676905b90604082510361223a5760208201519160c06064820151026044019260405193602073ffffffffffffffffffffffffffffffffffffffff6000928184927f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001783528584527f00000000000000000000000000000000000000000000000000000000000000006040526055600b2016976040528180526040860182895af190805191156122215750937f4ce34aa2000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000060209596160361221557505052565b61221e91612345565b52565b63d13d53d48691612230612681565b526020526024601cfd5b9050565b9060405190602073ffffffffffffffffffffffffffffffffffffffff6101046000938285937f00000000000000000000000000000000000000000000000000000000000000001674ff00000000000000000000000000000000000000001784528785527f00000000000000000000000000000000000000000000000000000000000000006040526055600b20169560405282805282865af1908051911561233657507fffffffff000000000000000000000000000000000000000000000000000000007f4ce34aa20000000000000000000000000000000000000000000000000000000091160361232d575050565b61067691612345565b63d13d53d49150612230612681565b631cf99b266000526020526040526044601cfd5b9060649492939160208251146000146124105760c0906001906040845260208401527f4ce34aa20000000000000000000000000000000000000000000000000000000060408401526020604484015280878401525b02019260017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc48501527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe484015260048301526024820152600060448201520152565b5060c085820160018151018091526123ae565b91909161014081018051917f0000000000000000000000000000000000000000000000000000000000000000604051604083018051928351926020809501906000915b868684106125665750505050506040519160051b8220917f00000000000000000000000000000000000000000000000000000000000000009093606086019481865101906000915b8a83106125245750505050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08660051b604051209401978851907f00000000000000000000000000000000000000000000000000000000000000008a5282519383528451958552865261018089209852525252565b8380827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600194510180519089815260e08120875252019201920191906124ae565b80827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0600194510180519088815260c0812087525201920192019190612466565b6000467f0000000000000000000000000000000000000000000000000000000000000000036125f557507f000000000000000000000000000000000000000000000000000000000000000090565b60405190608051907f000000000000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006020527f0000000000000000000000000000000000000000000000000000000000000000604052466060523060805260a081209260405260605260805290565b3d61268857565b601f3d0160051c60405160051c9080600302918082116126bb575b505060205a9101106126b157565b3d6000803e3d6000fd5b8080600392028380020360091c920302010138806126a3565b9196948094966126e49284612a1b565b9186519581516126fc6126f7828a612efa565b61339b565b976000998a905b8282106128015750506000925b828410612753575050505050936127379394868297612748575b5081511561273b57613748565b9190565b6127436133fe565b613748565b82510382523861272a565b90919293998961276e83612767888f6129f9565b5189613478565b80516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff6127c0602084015173ffffffffffffffffffffffffffffffffffffffff1690565b9116036127dc5750506001809101945b01929190999399612710565b86916127fb916127f485886001979b010380936129f9565b528c6129f9565b506127d0565b90949a8a61281e8a6128178986999798996129f9565b518a61340d565b80516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff612870602084015173ffffffffffffffffffffffffffffffffffffffff1690565b91160361288e5750506001809101955b01909a949a93929193612703565b87916128ab916128a4856001969b0380936129f9565b528d6129f9565b50612880565b6128b96110ae565b90604051610160810181811067ffffffffffffffff821117612938575b604052600080825280602083015260609182604082015282808201528160808201528160a08201528160c08201528160e08201528161010082015281610120820152816101408201528452806020850152604084015280808401526080830152565b61294061107e565b6128d6565b61294d6110fb565b600181529060203681840137565b9061296d612968836117ec565b61111b565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061299b82946117ec565b0190602036910137565b600511156106b257565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020908051156129ed570190565b6129f56129af565b0190565b6020918151811015612a0e575b60051b010190565b612a166129af565b612a06565b90919392612a27614fb6565b6000357c40000000000000000000000000000000000000000000000000000000001692612a526128b1565b50825193612a5f8561295b565b96600180960160051b9560205b878110612b2f57505050907c4000000000000000000000000000000000000000000000000000000001612aa59214612b22575b8361301b565b60205b838110612ab55750505050565b806020918701518015612b1c57612b1690828601515185612aea825173ffffffffffffffffffffffffffffffffffffffff1690565b8287015173ffffffffffffffffffffffffffffffffffffffff165b906060604085015194015194614296565b01612aa8565b50612b16565b612b2a612ff8565b612a9f565b80870151928015612cbf57612b43846146cc565b95918d82949215612cab5790857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff93920152019481519260a08401519360c081015160408201518051906004608080950151612b9e816129a5565b106000528960005b838110612c42575050505060608095510151958651958960005b888110612bdb57505050505050505050506020905b01612a6c565b87612be6828c6129f9565b5160a088820191612c2089898d612bff87518983612fc5565b908b86019889519089518214600014612c32575050508088525b8751612f53565b80945201908151905252018a90612bc0565b612c3b92612fc5565b8852612c19565b87612c4d82856129f9565b519e8f600051905110179e612c8f878d8a8401938c6060612c7087518984612fc5565b92019687519087518214600014612c9b575050508086525b8551612f07565b80925252018a90612ba6565b612ca492612fc5565b8652612c88565b505094506020809392506000910152612bd5565b92906000602080930152612bd5565b90919392612cda614fb6565b6000357c40000000000000000000000000000000000000000000000000000000001692612d056128b1565b50825193612d128561295b565b96600180960160051b9560205b878110612da857505050907c4000000000000000000000000000000000000000000000000000000001612d579214612b22578361301b565b60205b838110612d675750505050565b806020918701518015612da257612d9c90828601515185612aea825173ffffffffffffffffffffffffffffffffffffffff1690565b01612d5a565b50612d9c565b80870151928015612ed857612dbc84614478565b95918d82949215612ec45790857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff93920152019481519260a08401519360c081015160408201518051906004608080950151612e17816129a5565b106000528960005b838110612e8a575050505060608095510151958651958960005b888110612e5457505050505050505050506020905b01612d1f565b87612e5f828c6129f9565b5160a088820191612e7889898d612bff87518983612fc5565b80945201908151905252018a90612e39565b87612e9582856129f9565b519e8f600051905110179e612eb8878d8a8401938c6060612c7087518984612fc5565b80925252018a90612e1f565b505094506020809392506000910152612e4e565b92906000602080930152612e4e565b8181029291811591840414171561173057565b9190820180921161173057565b929092838103612f175750505090565b612f2d83612f3393039342039182850390612ee7565b93612ee7565b8201809211612f46575b81049015150290565b612f4e6116f3565b612f3d565b919092838303612f635750505090565b600192612f7c83612f8293039342039182850390612ee7565b94612ee7565b8301809311612fb8575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830104019015150290565b612fc06116f3565b612f8c565b919091828114612ff25782818309612fe457612fe091612ee7565b0490565b63c63cf0896000526004601cfd5b50905090565b506312d3f5a36000526004601cfd5b600211156106b257565b516107ad816106a8565b815181519260005b8281106131245750505060005b82811061303c57505050565b61304681836129f9565b5161307a61306660208301516effffffffffffffffffffffffffffff1690565b6effffffffffffffffffffffffffffff1690565b1561311b5751606081018051519060005b8281106130ed575050506040018051519060005b8281106130b3575050506001905b01613030565b806130d36130cd6130c760019486516129f9565b51613011565b60031090565b6130de575b0161309f565b6130e8818661321e565b6130d8565b806131016130cd6130c760019486516129f9565b61310c575b0161308b565b613116818761320a565b613106565b506001906130ad565b61312e81836129f9565b516131438151878110156131de575b866129f9565b51602090613165613066838301516effffffffffffffffffffffffffffff1690565b156131d357519060409081830151918401519263bfb3f8ce9185015161318a81613007565b61319381613007565b6131c0575b5081518310156131b75750916131b1916001949361323b565b01613023565b6000526004601cfd5b9050606091500151636088d7de38613198565b5050506001906131b1565b6131f460208401516131ef81613007565b6131f9565b61313d565b63133c37c66000526020526024601cfd5b63a8930e9a6000526020526040526044601cfd5b63d69293326000526020526040526044601cfd5b61221e826106a8565b90613245916129f9565b51805191613252836106a8565b60038311156132b157613292826004604060609501958651801515600014613298576132889087870151906080880151916132ce565b1460030390613232565b01519052565b5060808501515115613288576132ac6132bf565b613288565b6394eb6af66000526004601cfd5b506309bde3396000526004601cfd5b916000928352602090818420918082019181815191600592831b0101905b81841061330c5750505050036132ff5750565b6309bde33990526004601cfd5b8351808611821b958652948318949094526040862093928201926132ec565b6133336110ae565b906000825260006020830152600060408301526000606083015260006080830152565b604051906060820182811067ffffffffffffffff82111761338e575b604052600060408361338261332b565b81528260208201520152565b61339661107e565b613372565b906133a8612968836117ec565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06133d682946117ec565b019060005b8281106133e757505050565b6020906133f2613356565b828285010152016133db565b5063d5da9a1b6000526004601cfd5b909291613418613356565b93805115613465576134479273ffffffffffffffffffffffffffffffffffffffff8693166080845101526134f6565b81516060810151156134565750565b60806000918260208601520152565b63375c24c160005260006020526024601cfd5b929190613483613356565b938151156134c05761349691859161363d565b602083019033825260408401528251906060820151156134b4575050565b60009182608092520152565b63375c24c160005260016020526024601cfd5b50637fda72796000526004601cfd5b50634e487b7160005260116020526024601cfd5b9092919260009081928290828351905b8160051b8501811061353557505050505060608293945101526135265750565b600114611da0576106766134e2565b602090969596019060208251518451811015613630575b60051b840101518051906020845101516020604084015192015115825182101517613625579060209160051b0101519660609081890151998a81019a15908b1060011b171798976000828201528b5187156001146135d857502085189060408b0151610120820151189060208c015190511817176135cb575b90613506565b6135d36134d3565b6135c5565b929061012092949750806040915185526020810151602086015201516040840152805160208d0152015160408b015220926020850182811861361b575b506135c5565b8251905238613615565b5050509594956135c5565b6136386134d3565b61354c565b9092919260009081928291808051600590811b82015b80841061366f5750505050505060608293945101526135265750565b60209796978094019380855151875181101561373b575b841b87010151908086510151916060928284835101519201511582518210151761372f576000918391871b010151928301998a519b8c81019c15908d1060011b17179a99528b5188156001146136ef57505060a090208614613653576136ea6134d3565b613653565b8251815281830151818301526040808401519082015260808084015191015260a090912096508301848118613725575b50613653565b845190523861371f565b50505050969596613653565b6137436134d3565b613686565b9290918351916137578361295b565b946137606111a8565b9480519060005b8281106138ee5750505060005b8481106137ab57505050505061378990612105565b478061379b575b506107ad6001600055565b6137a59033611d62565b38613790565b6137b581836129f9565b516137d561306660208301516effffffffffffffffffffffffffffff1690565b156138d8576137ed6137e7838a6129f9565b60019052565b8051604081015180519060005b828110613873575050506060809101519081519160005b83811061383b5750505050906138356001928661382e84826129f9565b519161577d565b01613774565b80613848600192846129f9565b5160a085820191825180613862575b500151905201613811565b61386d90858c61397a565b38613857565b808b613881600193856129f9565b518a6080820151926060830192835161389f575b50505052016137fa565b6138d0926138ac91613968565b895173ffffffffffffffffffffffffffffffffffffffff166101208b015191613991565b8a8e38613895565b508060006138e86001938a6129f9565b52613835565b80613949896138ff600194866129f9565b51805190815161390e816106a8565b613917816106a8565b1561394f575b6040613940602083015173ffffffffffffffffffffffffffffffffffffffff1690565b91015191613991565b01613767565b476060830151111561391d57613963611d22565b61391d565b919061397261332b565b506080830152565b63a5f542086000526020526040526060526064601cfd5b929190835161399f816106a8565b6139a8816106a8565b613a4b57505050806139f06139d7602061067694015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff6040830151911617613a3e575b6060613a35608083015173ffffffffffffffffffffffffffffffffffffffff1690565b91015190611d62565b613a46611d53565b613a12565b60018451613a58816106a8565b613a61816106a8565b03613ae15792610676936040820151613ad4575b602082015173ffffffffffffffffffffffffffffffffffffffff169073ffffffffffffffffffffffffffffffffffffffff6060613ac9608086015173ffffffffffffffffffffffffffffffffffffffff1690565b940151931691611dae565b613adc611d53565b613a75565b60028451613aee816106a8565b613af7816106a8565b03613b645783613b21602061067696015173ffffffffffffffffffffffffffffffffffffffff1690565b608082015173ffffffffffffffffffffffffffffffffffffffff169273ffffffffffffffffffffffffffffffffffffffff60606040850151940151941691611efe565b83613b89602061067696015173ffffffffffffffffffffffffffffffffffffffff1690565b608082015173ffffffffffffffffffffffffffffffffffffffff169273ffffffffffffffffffffffffffffffffffffffff60606040850151940151941691611fff565b9193929081613bde9184519085612cce565b805160051b604001927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082018051907f4b9f2d36e1b4c93de62cc077b00b1a91d84b6c31b4a14e012718dcca230689e760209687835282a152855195613c438761339b565b9460009788915b818310613c78575050505092613c699386829697613c6d575b50613748565b5090565b825103825238613c63565b90919298613c9884613c8a818d6129f9565b518481519101519088613d26565b80516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff613ce98584015173ffffffffffffffffffffffffffffffffffffffff1690565b911603613d0357506001809101935b019190989298613c4a565b93613d208295600193830390613d19828d6129f9565b528a6129f9565b50613cf8565b909192613d31613356565b938351158015613f13575b613f06575b613d49613356565b90613d5582828661363d565b81519460609384870193845115613eee575092859288836107ad9996613d828360809a97613e859c6134f6565b613d8c8351613011565b613d95816106a8565b885190613da1826106a8565b613daa826106a8565b60ff85519273ffffffffffffffffffffffffffffffffffffffff8c604080613dec6139d760208a015173ffffffffffffffffffffffffffffffffffffffff1690565b613e106139d7602086015173ffffffffffffffffffffffffffffffffffffffff1690565b189701519101511894169218161717613edf575b50835182518601511015613ea557505090602083613e59613e47613e66956129df565b5193518c5183015185519103976129f9565b51510151910151906129f9565b5101525b015173ffffffffffffffffffffffffffffffffffffffff1690565b60808351019073ffffffffffffffffffffffffffffffffffffffff169052565b8495939492509060206040613e5985613ec0613ed1966129df565b5194510151885185519103976129f9565b510152519086510152613e6a565b613ee890613f1c565b38613e24565b97505050505050506080600091826020850152015290565b613f0e613f2d565b613d41565b50805115613d3c565b63bced929d6000526020526024601cfd5b506398e9db6e6000526004601cfd5b613f446110fb565b90600182528160005b60209081811015613f6f57602091613f636128b1565b90828501015201613f4d565b505050565b9261400e613fda9261404695613fa460046080835101516005811015614055575b613f9e816129a5565b14614fc5565b613fec84613fb183614478565b9098829a9296613fbf613f3c565b96613fc9886129df565b52613fd3876129df565b508661301b565b613fe3856129df565b51519889614062565b614008613ff7612945565b9183614002846129df565b526129df565b5161577d565b815173ffffffffffffffffffffffffffffffffffffffff16602083015173ffffffffffffffffffffffffffffffffffffffff16612b05565b6140506001600055565b600190565b61405d610678565b613f95565b60a08082015160c083015197969095939161407b6111a8565b9689604086019384515190600095865b8c898d86841061417b5750505050505050506080926004848701516140af816129a5565b101661416e575b6060809501968751519760005b8981106140f257505050505050505050506140df919250612105565b47806140e85750565b6106769033611d62565b8061414e8c8f8b8b8b8f93614123908c8c61411060019c8e516129f9565b5196870195865195880195865190614224565b8092528b83015190528151614137816106a8565b614140816106a8565b15614154575b503390613991565b016140c3565b4710614161575b38614146565b614169611d22565b61415b565b614176612ff8565b6140b6565b99856141e29392869798999c6141bd6141978860019a516129f9565b519485516141a4816106a8565b15179e8d606087019384519560808901968751906141ed565b9052528c610120613940825173ffffffffffffffffffffffffffffffffffffffff1690565b01908d93929161408b565b9093908481036142035750506107ad9350612fc5565b93836142186107ad979661421e949686612fc5565b93612fc5565b90612f07565b90939084810361423a5750506107ad9350612fc5565b93836142186107ad979661424f949686612fc5565b90612f53565b90815180825260208080930193019160005b828110614275575050505090565b909192938260a08261428a60019489516106ba565b01950193929101614267565b929094939160409182519460809182870191875273ffffffffffffffffffffffffffffffffffffffff94856020921682890152838189015286518093528160a089019701936000915b84831061432d57505050505050828285949361432893867f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31989603606087015216971695614255565b0390a3565b90919293949784836001928b518051614345816106a8565b8252808401518c16848301528581015186830152606090810151908201520199019594930191906142df565b9092916000938285526002602052604085209283549260ff8460081c16614453576effffffffffffffffffffffffffffff8460101c166144425760ff8416156143d8575b505071010000000000000000000000000000010001909255509091506106769050565b6143e46129688261115f565b92818452368282011161443e57926201000194926106769798602084614436957fffffffffffffffffffffffffffffff00000000000000000000000000000000009883870137840101526084356151f2565b9185946143b5565b8780fd5b5063ee9e0e6386526020526024601cfd5b50631a51557486526020526024601cfd5b90805b61446f575090565b80910680614467565b80519061449161099960a084015160c0850151906151dd565b6146bf576effffffffffffffffffffffffffffff9260209284848401511693856040850151169360808301600481516144c9816129a5565b6144d2816129a5565b1461468c5786158688111761467f575b51916144ed836129a5565b60018093161586881016614672575b614505846147c6565b9761451a896000526002602052604060002090565b94614528610999878c6155c2565b614663578554938a60ff86161561462f575b5050508260881c8481159061455c575b505050508460881b9060101b17179055565b989798939091929361461f5760101c821688851461460b578189146145ed5788829102970297029587019686881187890302809103970391818711828411176145a7575b808061454a565b909591966145be6145b8848a614464565b82614464565b801501808092049804920495808711908311176145db57806145a0565b601190634e487b71600052526024601cfd5b9250505084959401948486118587030280910395033880808061454a565b93975095505050830393833880808061454a565b505050508394933880808061454a565b606061465261465b945173ffffffffffffffffffffffffffffffffffffffff1690565b920151916151f2565b38808a61453a565b50600097508796505050505050565b61467a614832565b6144fc565b614687614823565b6144e2565b5091936080939650600191506146ab9502186146b2575b015190614841565b9192909190565b6146ba614823565b6146a3565b5050600090600090600090565b8051906146e961099960a084015160c08501514210904210151690565b6146bf576effffffffffffffffffffffffffffff926020928484840151169385604085015116936080830160048151614721816129a5565b61472a816129a5565b1461479a5786158688111761478d575b5191614745836129a5565b60018093161586881016614780575b61475d846147c6565b97614772896000526002602052604060002090565b94614528610999878c615625565b614788614832565b614754565b614795614823565b61473a565b5091936080939650600191506146ab9502186147b9575b015190614a00565b6147c1614823565b6147b1565b6060810151516101408201511161153d578061481d73ffffffffffffffffffffffffffffffffffffffff6107ad93511673ffffffffffffffffffffffffffffffffffffffff16600052600160205260406000205490565b90612423565b50635a052b326000526004601cfd5b5063a11b63ff6000526004601cfd5b6060906040828201805151610140840151036149f3575b60008061488361487c865173ffffffffffffffffffffffffffffffffffffffff1690565b9786614bc4565b9082895af1936148b38673ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b958654906001978883019055821b1894156149e5575b6148d1615a9f565b94909195866149d7575b01805151825181116149c9575b6000905b89818310614993575050505281519083519180518311614985575b91906000925b888385106149325750505050505261492457918190565b61492d81614f96565b918190565b909192939661494188846129f9565b5161497961494f8a8a6129f9565b518681015187840151106149638285614cbf565b179260a080910151910151908091149015171590565b1717960192919061490d565b61498e87614f96565b614907565b9091976149a18985516129f9565b516149bf6149af8b886129f9565b5188830151898201511092614cbf565b17179701906148ec565b6149d288614f96565b6148e8565b6149e088614f96565b6148db565b6149ee85614f96565b6148c9565b6149fb614cb0565b614858565b60608082019182515161014082015103614b5c575b614a3d614a36825173ffffffffffffffffffffffffffffffffffffffff1690565b9482614bc4565b929060008094819282895af193614a748673ffffffffffffffffffffffffffffffffffffffff166000526003602052604060002090565b958654906001978883019055831b189415614b5257614a91615a9f565b93919485614b115760400180515182518111614b445787905b8a818310614b1e575050505281519083519180518311614b1157919086925b89838510614ae857505050505052614ae15750918190565b9150918190565b9091929396614af788846129f9565b51614b0561494f8a8a6129f9565b17179601929190614ac9565b5050505050509150918190565b909197614b2c8985516129f9565b51614b3a6149af8b886129f9565b1717970190614aaa565b505050505050509150918190565b5050509150918190565b614b64614cb0565b614a15565b91909160408051936020928360e083028701018352818652839160010160051b92838701915b848410614b9e57505050505050565b60c060a0879285878c01528460808083893e606083019088013e01930193019291614b8f565b9190608490614c2b604051916398919765835260a0601c84019633602086015260806040860152614c176060614c01604084015185890190614c55565b9283608001828901520151838388010190614c55565b018094608082016080820152010190614c30565b010190565b8051603f0163ffffffe0169291610676918491905b829060045afa153d15176101c357565b9081519081815260209283808083019301918460051b0101915b84838210614c82575050505060071b0190565b8160809251805185528281015183860152604080820151908601526060809101519085015201910190614c6f565b50632165628a6000526004601cfd5b90815191604081015180156003851116614cfc575b6020809160608401516080850151149060408601511416948451149301519101511416161590565b506040820151600490931460030392614cd4565b9190811015614d51575b60051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea1813603018212156101c3570190565b614d596129af565b614d1a565b3560058110156101c35790565b5063fed398fc6000526004601cfd5b90815180825260208080930193019160005b828110614d9a575050505090565b909192938260a060019287518051614db1816106a8565b82528084015173ffffffffffffffffffffffffffffffffffffffff168483015260408082015190830152606080820151908301526080908101519082015201950193929101614d8c565b90815180825260208080930193019160005b828110614e1b575050505090565b909192938260c060019287518051614e32816106a8565b82528084015173ffffffffffffffffffffffffffffffffffffffff9081168584015260408083015190840152606080830151908401526080808301519084015260a091820151169082015201950193929101614e0d565b906005821015614e965752565b61221e610678565b90815260406020820152614ecb60408201835173ffffffffffffffffffffffffffffffffffffffff169052565b602082015173ffffffffffffffffffffffffffffffffffffffff1660608201526101806040830151614f42614f0e610160928360808701526101a0860190614d7a565b60608601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08683030160a0870152614dfb565b93614f55608082015160c0860190614e89565b60a081015160e085015260c081015191610100928386015260e082015192610120938487015282015192610140938487015282015190850152015191015290565b63939792856000526020526024601cfd5b614faf614fd5565b6002600055565b614fbe614fd5565b6003600055565b614fcd614fd5565b600201600055565b600160005403614fe157565b637fa8a9876000526004601cfd5b600360005403614ffb57565b61067634611d42565b929091833b156150d157604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652816004528260245283604452858060648180855af11561505e5750505050604052606052565b85853d615085575b5063f486bc879052602052604052606052608052600160a05260a4601cfd5b601f3d0160051c9060051c9080600302918082116150b8575b505060205a9101106150b05785615066565b3d81803e3d90fd5b8080600392028380020360091c9203020101868061509e565b83635f15d6726000526020526024601cfd5b9392919091843b156151cb57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528160045282602452836044528460645260a06084528960a452898060c48180855af11561516257505050505060805260a05260c052604052606052565b89893d615187575b5063f486bc87905260205260405260605260805260a05260a4601cfd5b601f3d0160051c9060051c9080600302918082116151b2575b505060205a9101106150b0578661516a565b8080600392028380020360091c920302010187806151a0565b84635f15d6726000526020526024601cfd5b9190428111428411151692831561154b575050565b929190338414615373576152046125a7565b9361524182867f19010000000000000000000000000000000000000000000000000000000000006000526002526022526042600020906000602252565b908351926002601f601d860116106102e27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9d860110166000146153655760018085169081604103927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf600593880101831c93808952880160209384820151928560238560e81c94019460e31c1690815285845191185283925b8684106153455750505050509661533f9161067697986152fe60406000209261556d565b600052526040600020907f19010000000000000000000000000000000000000000000000000000000000006000526002526022526042600020906000602252565b90615379565b85859101938684821c841b166040600020815287865191185201926152da565b506106769495508190615379565b50509050565b909291926000948580528051957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082018051918860410390809160018111968715615503575b5050508514851515169788156153f5575b5050505050505050156153df57565b6153e7612681565b634f7fb80d6000526004601cfd5b909192939495809798508452604082527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8401938451957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08201976020600060648b519c7f1626ba7e000000000000000000000000000000000000000000000000000000009e8f8c528d520189845afa9a8b6154a1575b505050505052525238808080808080806153d0565b600051036154af578061548c565b3b6153e7576154f557606001906041640101000000835160001a1a159114166154e05763815e1d646000526004601cfd5b631f003d0a6000525160001a6020526024601cfd5b638baa579f6000526004601cfd5b9091925060408601908151926060880151851a9061553b575b8752845260208360808660015afa508484528a865252513880806153bf565b50601b8360ff1c017f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416835261551c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6020910160051b60010160007f00000000000000000000000000000000000000000000000000000000000000003c60005190565b905460ff8160081c16615613576effffffffffffffffffffffffffffff8160101c1690816155f3575b505050600190565b60881c1115615604575b80806155eb565b61560d9061568d565b386155fd565b50631a5155746000526020526024601cfd5b906000905460ff8160081c16615684576effffffffffffffffffffffffffffff8160101c16908161565a575b50505050600190565b60881c111561566a578080615651565b615675575b50600090565b61567e9061568d565b3861566f565b50905050600090565b6310fda3e16000526020526024601cfd5b9190608082019081356156b08161064b565b33141590600460018211911016166156c757505050565b610676926156f56139d76060604051956317b1f94287526020808801528460408801523382880152016111bf565b6080840152606061014461012085013761014060a08401526101e060c0840152615778601c6103246102643561574160a08202918261016001906101808a019060051b61020001614c45565b6102a0810160e08801528461032082890160006102e08201526102c084016101008b015260016103008201520152019401926111bf565b6159e3565b919082519060808201918251926005841015615838575b6157c5602083019473ffffffffffffffffffffffffffffffffffffffff865116331415906004600182119110161690565b156157ed5750906157df91608061067696015190856158c9565b91519263fb5014fc93615a4b565b600491949350516157fd816129a5565b615806816129a5565b0361583257610676936158269184519460808660601b9301519085615845565b91639397928593615a4b565b50505050565b615840610678565b615794565b9493919260c060a4946158b5614c2b946040519663f4dd92ce8852601c88019a1860a088015260a0602088015261589f60606158886040840151878b0190614c55565b928360a00160408b0152015185838a01019061599b565b019160a083016060880152838388010190614c30565b01809460a08201608082015201019061597f565b9392614c2b906101649392604051936317b1f9428552601c85019760208087015260408601523360608601528151608086015260a082015161012086015260c082015190610140918287015260e08301516101608701528160a087015261596f60408401519361595a606061594461018097888c0190614c55565b9283870160c08c0152015186838b01019061599b565b019183830160e0890152848389010190614c30565b0194859182016101008201520101905b6129f5602092839283815180845260051b948593019101614c45565b8051908183526020928380808401938560051b01019101915b8181106159c55750505060a0020190565b60a090818481835160045afa153d15176101c35785019201916159b4565b6020909391937fffffffff00000000000000000000000000000000000000000000000000000000845116926000948580938180525af1908251149015615a3c5715615a2c575050565b63fb5014fc90526020526024601cfd5b5063fb5014fc90612230612681565b602090949391947fffffffff00000000000000000000000000000000000000000000000000000000845116926000948580938180525af1908251149015615a96571561223057505050565b50612230612681565b60009081906080803d109060009081908280918515615b42575b8515615aca575b5050505050929190565b91939750919550602094939480920196604051918360c08302840101604052818352839160010160051b98898401905b8a8410615b1f5750505050615b1493949596509501614b69565b913880808080615ac0565b60a083879284878901528181863e60608501518286015201920193019290615afa565b9450909150604081803e5190602051913d81113d8411179485615ab95794508093506020915060003e60005191602082813e602051903d8260a0028560071b0186011161ffff83861711179460008052615ab9565b908135641fffffffe08160051b169060405191602091828285010160405263ffffffff809116845260005b828110615bd25750929450505050565b80615be885848180958c010135168a01016119cd565b82828801015201615bc256fea164736f6c6343000811000a60406080815234610081576100156103a0604052565b6018806080526103003660a03761002a6101fe565b61003261027d565b9280519160005b84811061004c5760fe608052610301609ff35b8061005860019261011f565b6003028352610067878461016e565b60208151910120610077826101d5565b5284845201610039565b600080fd5b50634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b038211176100b857604052565b6100c0610086565b604052565b60c081019081106001600160401b038211176100b857604052565b61010081019081106001600160401b038211176100b857604052565b601f909101601f19168101906001600160401b038211908210176100b857604052565b906001820180921161012d57565b634e487b7160e01b600052601160045260246000fd5b9081519160005b83811061015b575050016000815290565b806020809284010151818501520161014a565b6101d3906101c56101b2949360066040519687947f42756c6b4f72646572284f72646572436f6d706f6e656e74730000000000000060208701526039860190610143565b6520747265652960d01b81520190610143565b03601f1981018452836100fc565b565b6080518110156101e85760051b60a00190565b634e487b7160e01b600052603260045260246000fd5b604051608081016001600160401b0381118282101761024b575b6040526048815260208101606036823760688201905b81811061023a57505090565b625b325d60e81b815260030161022e565b610253610086565b610218565b6101c561027794936102776101d3946040519788956020870190610143565b90610143565b6104e56040805161028d8161009d565b606a81527f4f666665724974656d2875696e7438206974656d547970652c6164647265737360208201527f20746f6b656e2c75696e74323536206964656e7469666965724f724372697465828201527f7269612c75696e74323536207374617274416d6f756e742c75696e7432353620606082015269656e64416d6f756e742960b01b60808201528151610320816100c5565b608481527f436f6e73696465726174696f6e4974656d2875696e7438206974656d5479706560208201527f2c6164647265737320746f6b656e2c75696e74323536206964656e7469666965838201527f724f7243726974657269612c75696e74323536207374617274416d6f756e742c60608201527f75696e7432353620656e64416d6f756e742c6164647265737320726563697069608082015263656e742960e01b60a08201527f61646472657373207a6f6e652c4f666665724974656d5b5d206f666665722c438351936103f5856100e0565b60d485527f4f72646572436f6d706f6e656e74732861646472657373206f6666657265722c60208601528401527f6f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f6e60608401527f2c75696e7438206f72646572547970652c75696e74323536207374617274546960808401527f6d652c75696e7432353620656e6454696d652c62797465733332207a6f6e654860a08401527f6173682c75696e743235362073616c742c6279746573333220636f6e6475697460c08401527f4b65792c75696e7432353620636f756e7465722900000000000000000000000060e0840152610258565b9056fe00000000000000000000000000000000f9490004c11cef243f5400493c00ad630000000000000000
+```
+
+4. Validate deployments were successful by checking that `Seaport` is returned:
+
+```
+cast --to-ascii $(cast call --rpc-url ${RPC_URL} 0x00000000006c3852cbEf3e08E8dF289169EdE581 'name()')
+cast --to-ascii $(cast call --rpc-url ${RPC_URL} 0x00000000000006c7676171937C444f6BDe3D6282 'name()')
+```
+
+## Verifying Seaport and ConduitController
+After `Seaport` and `ConduitController` are deployed, they are verified as follows:
+
+1. Ensure that `EXPLORER_API_KEY` and `NETWORK_RPC` are set in `.env` appropriately.
+2. Navigate to the `1.1` release tag.
+
+3. Verify `ConduitController` by calling:
+
+```
+npx hardhat verify --network verificationNetwork "0x00000000F9490004C11Cef243f5400493c00Ad63"
+```
+
+4. Verify `Seaport 1.1` by calling:
+
+```
+npx hardhat verify --network verificationNetwork "0x00000000006c3852cbEf3e08E8dF289169EdE581" "0x00000000F9490004C11Cef243f5400493c00Ad63"
+```
+
+5. Navigate to the `1.2` release tag.
+
+6. Verify `Seaport 1.2` by calling:
+
+```
+npx hardhat verify --network verificationNetwork "0x00000000000006c7676171937C444f6BDe3D6282" "0x00000000F9490004C11Cef243f5400493c00Ad63"
+```
\ No newline at end of file
diff --git a/docs/FunctionSignatures.md b/docs/FunctionSignatures.md
new file mode 100644
index 000000000..cc7e950c9
--- /dev/null
+++ b/docs/FunctionSignatures.md
@@ -0,0 +1,64 @@
+# Seaport Function Signatures
+
+