From 68978f0e2cdee315ea77c9f4bda42bd285ec00af Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 10:41:52 +1000 Subject: [PATCH 01/11] Delete files no longer used --- contracts/access/IERC173.sol | 18 - .../create2/OwnableCreate2Deployer.sol | 51 - contracts/games/gems/GemGame.sol | 61 -- contracts/games/gems/README.md | 35 - contracts/staking/StakeHolderERC20.sol | 73 -- contracts/staking/StakeHolderERC20V2.sol | 74 -- contracts/staking/StakeHolderNative.sol | 6 +- contracts/staking/StakeHolderWIMX.sol | 13 +- .../token/erc721/abstract/ERC721Hybrid.sol | 497 ---------- .../erc721/abstract/ERC721HybridPermit.sol | 158 ---- .../abstract/ImmutableERC721HybridBase.sol | 157 ---- .../token/erc721/erc721psi/ERC721Psi.sol | 467 ---------- .../erc721/erc721psi/ERC721PsiBurnable.sol | 85 -- .../token/erc721/preset/ImmutableERC721.sol | 129 --- .../trading/seaport16/ImmutableSeaport.sol | 675 -------------- .../seaport16/conduit/ConduitController.sol | 5 - .../interfaces/ImmutableSeaportEvents.sol | 14 - .../validators/ReadOnlyOrderValidator.sol | 5 - .../seaport16/validators/SeaportValidator.sol | 5 - .../validators/SeaportValidatorHelper.sol | 5 - .../v3/ImmutableSignedZoneV3.sol | 879 ------------------ .../zones/immutable-signed-zone/v3/README.md | 61 -- .../v3/ZoneAccessControl.sol | 54 -- .../v3/interfaces/ITransferValidator.sol | 17 - .../v3/interfaces/SIP5EventsAndErrors.sol | 15 - .../v3/interfaces/SIP5Interface.sol | 24 - .../v3/interfaces/SIP6EventsAndErrors.sol | 16 - .../v3/interfaces/SIP6Interface.sol | 11 - .../v3/interfaces/SIP7EventsAndErrors.sol | 133 --- .../v3/interfaces/SIP7Interface.sol | 73 -- .../ZoneAccessControlEventsAndErrors.sol | 14 - 31 files changed, 12 insertions(+), 3818 deletions(-) delete mode 100644 contracts/access/IERC173.sol delete mode 100644 contracts/deployer/create2/OwnableCreate2Deployer.sol delete mode 100644 contracts/games/gems/GemGame.sol delete mode 100644 contracts/games/gems/README.md delete mode 100644 contracts/staking/StakeHolderERC20.sol delete mode 100644 contracts/staking/StakeHolderERC20V2.sol delete mode 100644 contracts/token/erc721/abstract/ERC721Hybrid.sol delete mode 100644 contracts/token/erc721/abstract/ERC721HybridPermit.sol delete mode 100644 contracts/token/erc721/abstract/ImmutableERC721HybridBase.sol delete mode 100644 contracts/token/erc721/erc721psi/ERC721Psi.sol delete mode 100644 contracts/token/erc721/erc721psi/ERC721PsiBurnable.sol delete mode 100644 contracts/token/erc721/preset/ImmutableERC721.sol delete mode 100644 contracts/trading/seaport16/ImmutableSeaport.sol delete mode 100644 contracts/trading/seaport16/conduit/ConduitController.sol delete mode 100644 contracts/trading/seaport16/interfaces/ImmutableSeaportEvents.sol delete mode 100644 contracts/trading/seaport16/validators/ReadOnlyOrderValidator.sol delete mode 100644 contracts/trading/seaport16/validators/SeaportValidator.sol delete mode 100644 contracts/trading/seaport16/validators/SeaportValidatorHelper.sol delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.sol delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/README.md delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/ZoneAccessControl.sol delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/ITransferValidator.sol delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP5EventsAndErrors.sol delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP5Interface.sol delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP6EventsAndErrors.sol delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP6Interface.sol delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7EventsAndErrors.sol delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7Interface.sol delete mode 100644 contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/ZoneAccessControlEventsAndErrors.sol diff --git a/contracts/access/IERC173.sol b/contracts/access/IERC173.sol deleted file mode 100644 index 08bfa4df..00000000 --- a/contracts/access/IERC173.sol +++ /dev/null @@ -1,18 +0,0 @@ -//SPDX-License-Identifier: Apache 2.0 -pragma solidity ^0.8.0; - -import {IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; - -interface IERC173 is IERC165 { - /// @dev This emits when ownership of a contract changes. - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /// @notice Get the address of the owner - /// @return The address of the owner. - function owner() external view returns (address); - - /// @notice Set the address of the new owner of the contract - /// @dev Set _newOwner to address(0) to renounce any ownership. - /// @param _newOwner The address of the new owner of the contract - function transferOwnership(address _newOwner) external; -} diff --git a/contracts/deployer/create2/OwnableCreate2Deployer.sol b/contracts/deployer/create2/OwnableCreate2Deployer.sol deleted file mode 100644 index 4a75c015..00000000 --- a/contracts/deployer/create2/OwnableCreate2Deployer.sol +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {Deployer} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Deployer.sol"; -import {Create2} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create2.sol"; - -/** - * @title OwnableCreate2Deployer - * @notice Deploys and initializes contracts using the `CREATE2` opcode. The contract exposes two functions, {deploy} and {deployAndInit}. - * {deploy} deploys a contract using the `CREATE2` opcode, and {deployAndInit} additionally initializes the contract using provided data. - * The latter offers a way of ensuring that the constructor arguments do not affect the deployment address. - * - * @dev This contract extends the {Deployer} contract from the Axelar SDK, by adding basic access control to the deployment functions. - * The contract has an owner, which is the only entity that can deploy new contracts. - * - * @dev The contract deploys a contract with the same bytecode, salt, and sender to the same address. - * The address where the contract will be deployed can be found using {deployedAddress}. - */ -contract OwnableCreate2Deployer is Ownable, Create2, Deployer { - constructor(address owner) Ownable() { - transferOwnership(owner); - } - - /** - * @dev Deploys a contract using the `CREATE2` opcode. - * This function is called by {deploy} and {deployAndInit} external functions in the {Deployer} contract. - * This function can only be called by the owner of this contract, hence the external {deploy} and {deployAndInit} functions can only be called by the owner. - * The address where the contract will be deployed can be found using the {deployedAddress} function. - * @param bytecode The bytecode of the contract to be deployed - * @param deploySalt A salt which is a hash of the salt provided by the sender and the sender's address. - * @return The address of the deployed contract - */ - // Slither 0.10.4 is mistakenly seeing this as dead code. It is called from Deployer.deploy - // slither-disable-next-line dead-code - function _deploy(bytes memory bytecode, bytes32 deploySalt) internal override onlyOwner returns (address) { - return _create2(bytecode, deploySalt); - } - - /** - * @dev Returns the address where a contract will be stored if deployed via {deploy} or {deployAndInit}. - * This function is called by the {deployedAddress} external functions in the {Deployer} contract. - * @param bytecode The bytecode of the contract to be deployed - * @param deploySalt A salt which is a hash of the sender's address and the `salt` provided by the sender, when calling the {deployedAddress} function. - * @return The predicted deployment address of the contract - */ - function _deployedAddress(bytes memory bytecode, bytes32 deploySalt) internal view override returns (address) { - return _create2Address(bytecode, deploySalt); - } -} diff --git a/contracts/games/gems/GemGame.sol b/contracts/games/gems/GemGame.sol deleted file mode 100644 index bc86775c..00000000 --- a/contracts/games/gems/GemGame.sol +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.19 <0.8.29; - -import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; -import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; - -error Unauthorized(); -error ContractPaused(); - -/** - * @title GemGame - A simple contract that emits an event for the purpose of indexing off-chain - * @author Immutable - * @dev The GemGame contract is not designed to be upgradeable or extended - */ -contract GemGame is AccessControl, Pausable { - /// @notice Indicates that an account has earned a gem - event GemEarned(address indexed account, uint256 timestamp); - - /// @notice Role to allow pausing the contract - bytes32 private constant _PAUSE = keccak256("PAUSE"); - - /// @notice Role to allow unpausing the contract - bytes32 private constant _UNPAUSE = keccak256("UNPAUSE"); - - /** - * @notice Sets the DEFAULT_ADMIN, PAUSE and UNPAUSE roles - * @param _admin The address for the admin role - * @param _pauser The address for the pauser role - * @param _unpauser The address for the unpauser role - */ - constructor(address _admin, address _pauser, address _unpauser) { - _grantRole(DEFAULT_ADMIN_ROLE, _admin); - _grantRole(_PAUSE, _pauser); - _grantRole(_UNPAUSE, _unpauser); - } - - /** - * @notice Pauses the contract - */ - function pause() external { - if (!hasRole(_PAUSE, msg.sender)) revert Unauthorized(); - _pause(); - } - - /** - * @notice Unpauses the contract - */ - function unpause() external { - if (!hasRole(_UNPAUSE, msg.sender)) revert Unauthorized(); - _unpause(); - } - - /** - * @notice Function that emits a `GemEarned` event - */ - function earnGem() external { - if (paused()) revert ContractPaused(); - emit GemEarned(msg.sender, block.timestamp); - } -} diff --git a/contracts/games/gems/README.md b/contracts/games/gems/README.md deleted file mode 100644 index f95c5c11..00000000 --- a/contracts/games/gems/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Gem (GM) Game - -The GemGame contract emits a single event for the purpose of indexing off-chain. - -## Immutable Contract Addresses - -| Environment/Network | Deployment Address | Commit Hash | -|--------------------------|--------------------|-------------| -| imtbl-zkevm-testnet | 0xe19453c43b35563B8105F8B88DeEDcde999671Cb | [97f00aa69c7cfadddc67ca271593eaa0b1eac940](https://github.com/immutable/contracts/tree/97f00aa69c7cfadddc67ca271593eaa0b1eac940/contracts/games/gems) | -| imtbl-zkevm-mainnet | 0x3f04d7a7297d5535595ee0a30071008b54e62a03 | [97f00aa69c7cfadddc67ca271593eaa0b1eac940](https://github.com/immutable/contracts/tree/97f00aa69c7cfadddc67ca271593eaa0b1eac940/contracts/games/gems) | - -# Status - -Contract threat models and audits: - -| Description | Date |Version Audited | Link to Report | -|---------------------------|------------------|-----------------|----------------| -| Internal audit | April 15, 2024 | [97f00aa69c7cfadddc67ca271593eaa0b1eac940](https://github.com/immutable/contracts/tree/97f00aa69c7cfadddc67ca271593eaa0b1eac940/contracts/games/gems) | [202404-internal-audit-gm-game](../../../../audits/games/gems/202404-internal-audit-gm-game.pdf) | | -| Not audited and no threat model | - | - | - | - - - -# Deployment - -**Deploy and verify using CREATE3 factory contract:** - -This repo includes a script for deploying via a CREATE3 factory contract. The script is defined as a test contract as per the examples [here](https://book.getfoundry.sh/reference/forge/forge-script#examples) and can be found in `./script/games/gems/DeployGemGame.sol`. - -See the `.env.example` for required environment variables. - -```sh -forge script script/games/gems/DeployGemGame.sol --tc DeployGemGame --sig "deploy()" -vvv --rpc-url {rpc-url} --broadcast --verifier-url https://explorer.immutable.com/api --verifier blockscout --verify --gas-price 10gwei -``` - -Optionally, you can also specify `--ledger` or `--trezor` for hardware deployments. See docs [here](https://book.getfoundry.sh/reference/forge/forge-script#wallet-options---hardware-wallet). diff --git a/contracts/staking/StakeHolderERC20.sol b/contracts/staking/StakeHolderERC20.sol deleted file mode 100644 index 868da95a..00000000 --- a/contracts/staking/StakeHolderERC20.sol +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.19 <0.8.29; - -import {IERC20Upgradeable} from "openzeppelin-contracts-upgradeable-4.9.3/token/ERC20/IERC20Upgradeable.sol"; -import { - SafeERC20Upgradeable -} from "openzeppelin-contracts-upgradeable-4.9.3/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import {IStakeHolder, StakeHolderBase} from "./StakeHolderBase.sol"; - -/** - * @title StakeHolderERC20: allows anyone to stake any amount of an ERC20 token and to then remove all or part of that stake. - * @dev The StakeHolderERC20 contract is designed to be upgradeable. - */ -contract StakeHolderERC20 is StakeHolderBase { - using SafeERC20Upgradeable for IERC20Upgradeable; - - /// @notice The token used for staking. - IERC20Upgradeable internal token; - - /** - * @notice Initialises the upgradeable contract, setting up admin accounts. - * @param _roleAdmin the address to grant `DEFAULT_ADMIN_ROLE` to - * @param _upgradeAdmin the address to grant `UPGRADE_ROLE` to - * @param _distributeAdmin the address to grant `DISTRIBUTE_ROLE` to. - * @param _token the token to use for staking. - */ - function initialize(address _roleAdmin, address _upgradeAdmin, address _distributeAdmin, address _token) - public - initializer - { - __StakeHolderERC20_init(_roleAdmin, _upgradeAdmin, _distributeAdmin, _token); - } - - function __StakeHolderERC20_init( - address _roleAdmin, - address _upgradeAdmin, - address _distributeAdmin, - address _token - ) internal onlyInitializing { - __StakeHolderBase_init(_roleAdmin, _upgradeAdmin, _distributeAdmin); - token = IERC20Upgradeable(_token); - } - - /** - * @inheritdoc IStakeHolder - */ - function getToken() external view returns (address) { - return address(token); - } - - /** - * @inheritdoc StakeHolderBase - */ - function _sendValue(address _to, uint256 _amount) internal override { - token.safeTransfer(_to, _amount); - } - - /** - * @inheritdoc StakeHolderBase - */ - function _checksAndTransfer(uint256 _amount) internal override { - if (msg.value != 0) { - revert NonPayable(); - } - token.safeTransferFrom(msg.sender, address(this), _amount); - } - - /// @notice storage gap for additional variables for upgrades - // slither-disable-start unused-state - uint256[50] private __StakeHolderERC20Gap; - // slither-disable-end unused-state -} diff --git a/contracts/staking/StakeHolderERC20V2.sol b/contracts/staking/StakeHolderERC20V2.sol deleted file mode 100644 index ebb9d17f..00000000 --- a/contracts/staking/StakeHolderERC20V2.sol +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.19 <0.8.29; - -import {IERC20Upgradeable} from "openzeppelin-contracts-upgradeable-4.9.3/token/ERC20/IERC20Upgradeable.sol"; -import { - SafeERC20Upgradeable -} from "openzeppelin-contracts-upgradeable-4.9.3/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import {IStakeHolder, StakeHolderBase, StakeHolderBaseV2} from "./StakeHolderBaseV2.sol"; - -/** - * @title StakeHolderERC20V2: allows anyone to stake any amount of an ERC20 token and to then remove all or part of that stake. - * @dev The StakeHolderERC20 contract is designed to be upgradeable. - * @dev This contract is the same as StakeHolderERC20, with the exception that it derives from StakeHolderBaseV2. - */ -contract StakeHolderERC20V2 is StakeHolderBaseV2 { - using SafeERC20Upgradeable for IERC20Upgradeable; - - /// @notice The token used for staking. - IERC20Upgradeable internal token; - - /** - * @notice Initialises the upgradeable contract, setting up admin accounts. - * @param _roleAdmin the address to grant `DEFAULT_ADMIN_ROLE` to - * @param _upgradeAdmin the address to grant `UPGRADE_ROLE` to - * @param _distributeAdmin the address to grant `DISTRIBUTE_ROLE` to. - * @param _token the token to use for staking. - */ - function initialize(address _roleAdmin, address _upgradeAdmin, address _distributeAdmin, address _token) - public - initializer - { - __StakeHolderERC20_init(_roleAdmin, _upgradeAdmin, _distributeAdmin, _token); - } - - function __StakeHolderERC20_init( - address _roleAdmin, - address _upgradeAdmin, - address _distributeAdmin, - address _token - ) internal onlyInitializing { - __StakeHolderBase_init(_roleAdmin, _upgradeAdmin, _distributeAdmin); - token = IERC20Upgradeable(_token); - } - - /** - * @inheritdoc IStakeHolder - */ - function getToken() external view returns (address) { - return address(token); - } - - /** - * @inheritdoc StakeHolderBase - */ - function _sendValue(address _to, uint256 _amount) internal override { - token.safeTransfer(_to, _amount); - } - - /** - * @inheritdoc StakeHolderBase - */ - function _checksAndTransfer(uint256 _amount) internal override { - if (msg.value != 0) { - revert NonPayable(); - } - token.safeTransferFrom(msg.sender, address(this), _amount); - } - - /// @notice storage gap for additional variables for upgrades - // slither-disable-start unused-state - uint256[50] private __StakeHolderERC20Gap; - // slither-disable-end unused-state -} diff --git a/contracts/staking/StakeHolderNative.sol b/contracts/staking/StakeHolderNative.sol index b7c33078..55fee4c1 100644 --- a/contracts/staking/StakeHolderNative.sol +++ b/contracts/staking/StakeHolderNative.sol @@ -1,4 +1,4 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 +// Copyright (c) Immutable Pty Ltd 2018 - 2025 // SPDX-License-Identifier: Apache 2 pragma solidity >=0.8.19 <0.8.29; @@ -36,7 +36,8 @@ contract StakeHolderNative is StakeHolderBase { // Look for revert reason and bubble it up if present. // Revert reasons should contain an error selector, which is four bytes long. if (returndata.length >= 4) { - assembly ("memory-safe") { + // solhint-disable-next-line no-inline-assembly + assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } @@ -58,6 +59,7 @@ contract StakeHolderNative is StakeHolderBase { /// @notice storage gap for additional variables for upgrades // slither-disable-start unused-state + // solhint-disable-next-line var-name-mixedcase uint256[50] private __StakeHolderNativeGap; // slither-disable-end unused-state } diff --git a/contracts/staking/StakeHolderWIMX.sol b/contracts/staking/StakeHolderWIMX.sol index 1b92ac6f..25396ecc 100644 --- a/contracts/staking/StakeHolderWIMX.sol +++ b/contracts/staking/StakeHolderWIMX.sol @@ -1,4 +1,4 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 +// Copyright (c) Immutable Pty Ltd 2018 - 2025 // SPDX-License-Identifier: Apache 2 pragma solidity >=0.8.19 <0.8.29; @@ -21,10 +21,12 @@ contract StakeHolderWIMX is StakeHolderNative { * @param _distributeAdmin the address to grant `DISTRIBUTE_ROLE` to * @param _wIMXToken The address of the WIMX contract. */ - function initialize(address _roleAdmin, address _upgradeAdmin, address _distributeAdmin, address _wIMXToken) - public - initializer - { + function initialize( + address _roleAdmin, + address _upgradeAdmin, + address _distributeAdmin, + address _wIMXToken + ) public initializer { __StakeHolderBase_init(_roleAdmin, _upgradeAdmin, _distributeAdmin); wIMX = IWIMX(_wIMXToken); } @@ -63,6 +65,7 @@ contract StakeHolderWIMX is StakeHolderNative { /// @notice storage gap for additional variables for upgrades // slither-disable-start unused-state + // solhint-disable-next-line var-name-mixedcase uint256[50] private __StakeHolderWIMXGap; // slither-disable-end unused-state } diff --git a/contracts/token/erc721/abstract/ERC721Hybrid.sol b/contracts/token/erc721/abstract/ERC721Hybrid.sol deleted file mode 100644 index 02f234e7..00000000 --- a/contracts/token/erc721/abstract/ERC721Hybrid.sol +++ /dev/null @@ -1,497 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; -import {ERC721Psi, ERC721PsiBurnable} from "../erc721psi/ERC721PsiBurnable.sol"; -// Errors -import {IImmutableERC721Errors} from "../../../errors/Errors.sol"; - -// forge-lint: disable-start(pascal-case-struct) - -/* -This contract allows for minting with one of two strategies: -- ERC721: minting with specified tokenIDs (inefficient) -- ERC721Psi: minting in batches with consecutive tokenIDs (efficient) - -All other ERC721 functions are supported, with routing logic depending on the tokenId. -*/ - -abstract contract ERC721Hybrid is ERC721PsiBurnable, ERC721, IImmutableERC721Errors { - using BitMaps for BitMaps.BitMap; - - /// @notice The total number of tokens minted by ID, used in totalSupply() - uint256 private _idMintTotalSupply = 0; - - /// @notice A mapping of tokens ids before the threshold that have been burned to prevent re-minting - BitMaps.BitMap private _burnedTokens; - - /** - * @notice A singular batch transfer request. The length of the tos and tokenIds must be matching - * batch transfers will transfer the specified ids to their matching address via index. - * - */ - struct TransferRequest { - address from; - address[] tos; - uint256[] tokenIds; - } - - /// @notice A singular safe burn request. - struct IDBurn { - address owner; - uint256[] tokenIds; - } - - /// @notice A singular Mint by quantity request - struct Mint { - address to; - uint256 quantity; - } - - /// @notice A singular Mint by id request - struct IDMint { - address to; - uint256[] tokenIds; - } - - constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) ERC721Psi(name_, symbol_) {} - - /** - * @notice allows caller to burn multiple tokens by id - * @param tokenIDs an array of token ids - */ - function burnBatch(uint256[] calldata tokenIDs) external { - for (uint256 i = 0; i < tokenIDs.length; i++) { - burn(tokenIDs[i]); - } - } - - /** - * @notice burns the specified token id - * @param tokenId the id of the token to burn - */ - function burn(uint256 tokenId) public virtual { - if (!_isApprovedOrOwner(_msgSender(), tokenId)) { - revert IImmutableERC721NotOwnerOrOperator(tokenId); - } - _burn(tokenId); - } - - /** - * @notice Burn a token, checking the owner of the token against the parameter first. - * @param owner the owner of the token - * @param tokenId the id of the token to burn - */ - function safeBurn(address owner, uint256 tokenId) public virtual { - address currentOwner = ownerOf(tokenId); - if (currentOwner != owner) { - revert IImmutableERC721MismatchedTokenOwner(tokenId, currentOwner); - } - - burn(tokenId); - } - - /** - * @notice returns the threshold that divides tokens that are minted by id and - * minted by quantity - */ - function mintBatchByQuantityThreshold() public pure virtual returns (uint256) { - return 2 ** 128; - } - - /** - * @notice checks to see if tokenID exists in the collection - * @param tokenId the id of the token to check - * - */ - function exists(uint256 tokenId) public view virtual returns (bool) { - return _exists(tokenId); - } - - /** - * @notice Overwritten functions with combined implementations, supply for the collection is summed as they - * are tracked differently by each minting strategy - */ - function balanceOf(address owner) public view virtual override(ERC721, ERC721Psi) returns (uint256) { - return ERC721.balanceOf(owner) + ERC721Psi.balanceOf(owner); - } - - /* @notice Overwritten functions with combined implementations, supply for the collection is summed as they - * are tracked differently by each minting strategy - */ - function totalSupply() public view override(ERC721PsiBurnable) returns (uint256) { - return ERC721PsiBurnable.totalSupply() + _idMintTotalSupply; - } - - /** - * @notice refer to erc721 or erc721psi - */ - function ownerOf(uint256 tokenId) public view virtual override(ERC721, ERC721Psi) returns (address) { - if (tokenId < mintBatchByQuantityThreshold()) { - return ERC721.ownerOf(tokenId); - } - return ERC721Psi.ownerOf(tokenId); - } - - /** - * @notice Overwritten functions with direct routing. The metadata of the collect remains the same regardless - * of the minting strategy used for the tokenID - */ - - /** - * @inheritdoc ERC721 - */ - function tokenURI(uint256 tokenId) public view virtual override(ERC721, ERC721Psi) returns (string memory) { - return ERC721.tokenURI(tokenId); - } - - /** - * @inheritdoc ERC721 - */ - function name() public view virtual override(ERC721, ERC721Psi) returns (string memory) { - return ERC721.name(); - } - - /** - * @inheritdoc ERC721 - */ - function symbol() public view virtual override(ERC721, ERC721Psi) returns (string memory) { - return ERC721.symbol(); - } - - /** - * @inheritdoc ERC721 - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721Psi, ERC721) returns (bool) { - return ERC721.supportsInterface(interfaceId); - } - - /** - * @inheritdoc ERC721 - */ - function setApprovalForAll(address operator, bool approved) public virtual override(ERC721, ERC721Psi) { - return ERC721.setApprovalForAll(operator, approved); - } - - /** - * @inheritdoc ERC721 - */ - function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override(ERC721, ERC721Psi) { - safeTransferFrom(from, to, tokenId, ""); - } - - /** - * @notice refer to erc721 or erc721psi - */ - function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) - public - virtual - override(ERC721, ERC721Psi) - { - if (tokenId < mintBatchByQuantityThreshold()) { - return ERC721.safeTransferFrom(from, to, tokenId, _data); - } - return ERC721Psi.safeTransferFrom(from, to, tokenId, _data); - } - - /** - * @notice refer to erc721 or erc721psi - */ - function isApprovedForAll(address owner, address operator) - public - view - virtual - override(ERC721, ERC721Psi) - returns (bool) - { - return ERC721.isApprovedForAll(owner, operator); - } - - /** - * @notice refer to erc721 or erc721psi - */ - function getApproved(uint256 tokenId) public view virtual override(ERC721, ERC721Psi) returns (address) { - if (tokenId < mintBatchByQuantityThreshold()) { - return ERC721.getApproved(tokenId); - } - return ERC721Psi.getApproved(tokenId); - } - - /** - * @notice refer to erc721 or erc721psi - */ - function approve(address to, uint256 tokenId) public virtual override(ERC721, ERC721Psi) { - if (tokenId < mintBatchByQuantityThreshold()) { - return ERC721.approve(to, tokenId); - } - return ERC721Psi.approve(to, tokenId); - } - - /** - * @notice refer to erc721 or erc721psi - */ - function transferFrom(address from, address to, uint256 tokenId) public virtual override(ERC721, ERC721Psi) { - if (tokenId < mintBatchByQuantityThreshold()) { - return ERC721.transferFrom(from, to, tokenId); - } - return ERC721Psi.transferFrom(from, to, tokenId); - } - - /** - * @notice mints number of tokens specified to the address given via erc721psi - * @param to the address to mint to - * @param quantity the number of tokens to mint - */ - function _mintByQuantity(address to, uint256 quantity) internal { - ERC721Psi._mint(to, quantity); - } - - /** - * @notice safe mints number of tokens specified to the address given via erc721psi - * @param to the address to mint to - * @param quantity the number of tokens to mint - */ - function _safeMintByQuantity(address to, uint256 quantity) internal { - ERC721Psi._safeMint(to, quantity); - } - - /** - * @notice mints number of tokens specified to a multiple specified addresses via erc721psi - * @param mints an array of mint requests - */ - function _mintBatchByQuantity(Mint[] calldata mints) internal { - for (uint256 i = 0; i < mints.length; i++) { - Mint calldata m = mints[i]; - _mintByQuantity(m.to, m.quantity); - } - } - - /** - * @notice safe mints number of tokens specified to a multiple specified addresses via erc721psi - * @param mints an array of mint requests - */ - function _safeMintBatchByQuantity(Mint[] calldata mints) internal { - for (uint256 i = 0; i < mints.length; i++) { - Mint calldata m = mints[i]; - _safeMintByQuantity(m.to, m.quantity); - } - } - - /** - * @notice safe mints number of tokens specified to a multiple specified addresses via erc721 - * @param to the address to mint to - * @param tokenId the id of the token to mint - */ - function _mintByID(address to, uint256 tokenId) internal { - if (tokenId >= mintBatchByQuantityThreshold()) { - revert IImmutableERC721IDAboveThreshold(tokenId); - } - - if (_burnedTokens.get(tokenId)) { - revert IImmutableERC721TokenAlreadyBurned(tokenId); - } - - _idMintTotalSupply++; - ERC721._mint(to, tokenId); - } - - /** - * @notice safe mints number of tokens specified to a multiple specified addresses via erc721 - * @param to the address to mint to - * @param tokenId the id of the token to mint - */ - function _safeMintByID(address to, uint256 tokenId) internal { - if (tokenId >= mintBatchByQuantityThreshold()) { - revert IImmutableERC721IDAboveThreshold(tokenId); - } - - if (_burnedTokens.get(tokenId)) { - revert IImmutableERC721TokenAlreadyBurned(tokenId); - } - - _idMintTotalSupply++; - ERC721._safeMint(to, tokenId); - } - - /** - * @notice mints multiple tokens by id to a specified address via erc721 - * @param to the address to mint to - * @param tokenIds the ids of the tokens to mint - */ - function _mintBatchByID(address to, uint256[] calldata tokenIds) internal { - for (uint256 i = 0; i < tokenIds.length; i++) { - _mintByID(to, tokenIds[i]); - } - } - - /** - * @notice safe mints multiple tokens by id to a specified address via erc721 - * @param to the address to mint to - * @param tokenIds the ids of the tokens to mint - * - */ - function _safeMintBatchByID(address to, uint256[] calldata tokenIds) internal { - for (uint256 i = 0; i < tokenIds.length; i++) { - _safeMintByID(to, tokenIds[i]); - } - } - - /** - * @notice mints multiple tokens by id to multiple specified addresses via erc721 - * @param mints an array of mint requests - */ - function _mintBatchByIDToMultiple(IDMint[] calldata mints) internal { - for (uint256 i = 0; i < mints.length; i++) { - IDMint calldata m = mints[i]; - _mintBatchByID(m.to, m.tokenIds); - } - } - - /** - * @notice safe mints multiple tokens by id to multiple specified addresses via erc721 - * @param mints an array of mint requests - */ - function _safeMintBatchByIDToMultiple(IDMint[] calldata mints) internal { - for (uint256 i = 0; i < mints.length; i++) { - IDMint calldata m = mints[i]; - _safeMintBatchByID(m.to, m.tokenIds); - } - } - - /** - * @notice batch burn a tokens by id, checking the owner of the token against the parameter first. - * @param burns array of burn requests - */ - function _safeBurnBatch(IDBurn[] calldata burns) internal { - for (uint256 i = 0; i < burns.length; i++) { - IDBurn calldata b = burns[i]; - for (uint256 j = 0; j < b.tokenIds.length; j++) { - safeBurn(b.owner, b.tokenIds[j]); - } - } - } - - /** - * @notice refer to erc721 or erc721psi - */ - function _transfer(address from, address to, uint256 tokenId) internal virtual override(ERC721, ERC721Psi) { - if (tokenId < mintBatchByQuantityThreshold()) { - ERC721._transfer(from, to, tokenId); - } else { - ERC721Psi._transfer(from, to, tokenId); - } - } - - /** - * @notice burn a token by id, if the token is below the threshold it is burned via erc721 - * additional tracking is added for erc721 to prevent re-minting. Refer to erc721 or erc721psi - * @param tokenId the id of the token to burn - */ - function _burn(uint256 tokenId) internal virtual override(ERC721, ERC721PsiBurnable) { - if (tokenId < mintBatchByQuantityThreshold()) { - ERC721._burn(tokenId); - _burnedTokens.set(tokenId); - // slither-disable-next-line costly-loop - _idMintTotalSupply--; - } else { - ERC721PsiBurnable._burn(tokenId); - } - } - - /** - * @notice refer to erc721 or erc721psi - */ - function _approve(address to, uint256 tokenId) internal virtual override(ERC721, ERC721Psi) { - if (tokenId < mintBatchByQuantityThreshold()) { - return ERC721._approve(to, tokenId); - } - return ERC721Psi._approve(to, tokenId); - } - - /** - * @notice refer to erc721 or erc721psi - */ - function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) - internal - virtual - override(ERC721, ERC721Psi) - { - if (tokenId < mintBatchByQuantityThreshold()) { - return ERC721._safeTransfer(from, to, tokenId, _data); - } - return ERC721Psi._safeTransfer(from, to, tokenId, _data); - } - - /** - * @notice methods below are overwritten to always invoke the erc721 equivalent due to linearisation - * they do not get invoked explicitly by any external minting methods in this contract and are only overwritten to satisfy - * the compiler - */ - - /** - * @notice overriding erc721 and erc721psi _safemint, super calls the `_safeMint` method of - * the erc721 implementation due to inheritance linearisation. Refer to erc721 - */ - // slither-disable-next-line dead-code - function _safeMint(address to, uint256 tokenId) internal virtual override(ERC721, ERC721Psi) { - super._safeMint(to, tokenId); - } - - /** - * @notice overriding erc721 and erc721psi _safemint, super calls the `_safeMint` method of - * the erc721 implementation due to inheritance linearisation. Refer to erc721 - */ - function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual override(ERC721, ERC721Psi) { - super._safeMint(to, tokenId, _data); - } - - /** - * @notice overriding erc721 and erc721psi _safemint, super calls the `_mint` method of - * the erc721 implementation due to inheritance linearisation. Refer to erc721 - */ - function _mint(address to, uint256 tokenId) internal virtual override(ERC721, ERC721Psi) { - super._mint(to, tokenId); - } - - /** - * @notice refer to erc721 or erc721psi - */ - function _isApprovedOrOwner(address spender, uint256 tokenId) - internal - view - virtual - override(ERC721, ERC721Psi) - returns (bool) - { - if (tokenId < mintBatchByQuantityThreshold()) { - return ERC721._isApprovedOrOwner(spender, tokenId); - } - return ERC721Psi._isApprovedOrOwner(spender, tokenId); - } - - /** - * @notice refer to erc721 or erc721psi - */ - function _exists(uint256 tokenId) internal view virtual override(ERC721, ERC721PsiBurnable) returns (bool) { - if (tokenId < mintBatchByQuantityThreshold()) { - return ERC721._ownerOf(tokenId) != address(0) && (!_burnedTokens.get(tokenId)); - } - return ERC721PsiBurnable._exists(tokenId); - } - - /// @notice returns the startTokenID for the minting by quantity section of the contract - function _startTokenId() internal pure virtual override(ERC721Psi) returns (uint256) { - return mintBatchByQuantityThreshold(); - } - - /** - * @inheritdoc ERC721 - */ - // slither-disable-next-line dead-code - function _baseURI() internal view virtual override(ERC721, ERC721Psi) returns (string memory) { - return ERC721._baseURI(); - } -} -// forge-lint: disable-end(pascal-case-struct) diff --git a/contracts/token/erc721/abstract/ERC721HybridPermit.sol b/contracts/token/erc721/abstract/ERC721HybridPermit.sol deleted file mode 100644 index bd3652b4..00000000 --- a/contracts/token/erc721/abstract/ERC721HybridPermit.sol +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; -import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import {BytesLib} from "solidity-bytes-utils/contracts/BytesLib.sol"; -import {IERC4494} from "./IERC4494.sol"; -import {ERC721Hybrid} from "./ERC721Hybrid.sol"; - -/** - * @title ERC721HybridPermit: An extension of the ERC721Hybrid NFT standard that supports off-chain approval via permits. - * @dev This contract implements ERC-4494 as well, allowing tokens to be approved via off-chain signed messages. - */ -abstract contract ERC721HybridPermit is ERC721Hybrid, IERC4494, EIP712 { - /** - * @notice mapping used to keep track of nonces of each token ID for validating - * signatures - */ - mapping(uint256 tokenId => uint256 nonce) private _nonces; - - /** - * @dev the unique identifier for the permit struct to be EIP 712 compliant - */ - bytes32 private constant _PERMIT_TYPEHASH = keccak256( - abi.encodePacked("Permit(", "address spender," "uint256 tokenId," "uint256 nonce," "uint256 deadline" ")") - ); - - constructor(string memory name, string memory symbol) ERC721Hybrid(name, symbol) EIP712(name, "1") {} - - /** - * @notice Function to approve by way of owner signature - * @param spender the address to approve - * @param tokenId the index of the NFT to approve the spender on - * @param deadline a timestamp expiry for the permit - * @param sig a traditional or EIP-2098 signature - */ - function permit(address spender, uint256 tokenId, uint256 deadline, bytes memory sig) external override { - _permit(spender, tokenId, deadline, sig); - } - - /** - * @notice Returns the current nonce of a given token ID. - * @param tokenId The ID of the token for which to retrieve the nonce. - * @return Current nonce of the given token. - */ - function nonces(uint256 tokenId) external view returns (uint256) { - return _nonces[tokenId]; - } - - /** - * @notice Returns the domain separator used in the encoding of the signature for permits, as defined by EIP-712 - * @return the bytes32 domain separator - */ - function DOMAIN_SEPARATOR() external view override returns (bytes32) { - return _domainSeparatorV4(); - } - - /** - * @notice Overrides supportsInterface from IERC165 and ERC721Hybrid to add support for IERC4494. - * @param interfaceId The interface identifier, which is a 4-byte selector. - * @return True if the contract implements `interfaceId` and the call doesn't revert, otherwise false. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721Hybrid) returns (bool) { - return interfaceId == type(IERC4494).interfaceId // 0x5604e225 - || super.supportsInterface(interfaceId); - } - - /** - * @notice Overrides the _transfer method from ERC721Hybrid to increment the nonce after a successful transfer. - * @param from The address from which the token is being transferred. - * @param to The address to which the token is being transferred. - * @param tokenId The ID of the token being transferred. - */ - function _transfer(address from, address to, uint256 tokenId) internal virtual override(ERC721Hybrid) { - _nonces[tokenId]++; - super._transfer(from, to, tokenId); - } - - function _permit(address spender, uint256 tokenId, uint256 deadline, bytes memory sig) internal virtual { - if (deadline < block.timestamp) { - revert PermitExpired(); - } - - bytes32 digest = _buildPermitDigest(spender, tokenId, deadline); - - // smart contract wallet signature validation - if (_isValidERC1271Signature(ownerOf(tokenId), digest, sig)) { - _approve(spender, tokenId); - return; - } - - address recoveredSigner = address(0); - - // EOA signature validation - if (sig.length == 64) { - // ERC2098 Sig - recoveredSigner = - ECDSA.recover(digest, bytes32(BytesLib.slice(sig, 0, 32)), bytes32(BytesLib.slice(sig, 32, 64))); - } else if (sig.length == 65) { - // typical EDCSA Sig - recoveredSigner = ECDSA.recover(digest, sig); - } else { - revert InvalidSignature(); - } - - if (_isValidEOASignature(recoveredSigner, tokenId)) { - _approve(spender, tokenId); - } else { - revert InvalidSignature(); - } - } - - /** - * @notice Builds the EIP-712 compliant digest for the permit. - * @param spender The address which is approved to spend the token. - * @param tokenId The ID of the token for which the permit is being generated. - * @param deadline The deadline until which the permit is valid. - * @return A bytes32 digest, EIP-712 compliant, that serves as a unique identifier for the permit. - */ - function _buildPermitDigest(address spender, uint256 tokenId, uint256 deadline) internal view returns (bytes32) { - return _hashTypedDataV4(keccak256(abi.encode(_PERMIT_TYPEHASH, spender, tokenId, _nonces[tokenId], deadline))); - } - - /** - * @notice Checks if a given signature is valid according to EIP-1271. - * @param recoveredSigner The address which purports to have signed the message. - * @param tokenId The token id. - * @return True if the signature is from an approved operator or owner, otherwise false. - */ - function _isValidEOASignature(address recoveredSigner, uint256 tokenId) private view returns (bool) { - return recoveredSigner != address(0) && _isApprovedOrOwner(recoveredSigner, tokenId); - } - - /** - * @notice Checks if a given signature is valid according to EIP-1271. - * @param spender The address which purports to have signed the message. - * @param digest The EIP-712 compliant digest that was signed. - * @param sig The actual signature bytes. - * @return True if the signature is valid according to EIP-1271, otherwise false. - */ - function _isValidERC1271Signature(address spender, bytes32 digest, bytes memory sig) private view returns (bool) { - // slither-disable-next-line low-level-calls - (bool success, bytes memory res) = - spender.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, digest, sig)); - - if (success && res.length == 32) { - bytes4 decodedRes = abi.decode(res, (bytes4)); - if (decodedRes == IERC1271.isValidSignature.selector) { - return true; - } - } - - return false; - } -} diff --git a/contracts/token/erc721/abstract/ImmutableERC721HybridBase.sol b/contracts/token/erc721/abstract/ImmutableERC721HybridBase.sol deleted file mode 100644 index 62411b9d..00000000 --- a/contracts/token/erc721/abstract/ImmutableERC721HybridBase.sol +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {AccessControlEnumerable, MintingAccessControl} from "../../../access/MintingAccessControl.sol"; -import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol"; -import {OperatorAllowlistEnforced} from "../../../allowlist/OperatorAllowlistEnforced.sol"; -import {ERC721HybridPermit} from "./ERC721HybridPermit.sol"; -import {ERC721Hybrid} from "./ERC721Hybrid.sol"; - -abstract contract ImmutableERC721HybridBase is - OperatorAllowlistEnforced, - MintingAccessControl, - ERC2981, - ERC721HybridPermit -{ - /// @notice Contract level metadata - string public contractURI; - - /// @notice Common URIs for individual token URIs - string public baseURI; - - /** - * @notice Grants `DEFAULT_ADMIN_ROLE` to the supplied `owner` address - * @param owner_ The address to grant the `DEFAULT_ADMIN_ROLE` to - * @param name_ The name of the collection - * @param symbol_ The symbol of the collection - * @param baseURI_ The base URI for the collection - * @param contractURI_ The contract URI for the collection - * @param operatorAllowlist_ The address of the operator allowlist - * @param receiver_ The address of the royalty receiver - * @param feeNumerator_ The royalty fee numerator - */ - constructor( - address owner_, - string memory name_, - string memory symbol_, - string memory baseURI_, - string memory contractURI_, - address operatorAllowlist_, - address receiver_, - uint96 feeNumerator_ - ) ERC721HybridPermit(name_, symbol_) { - // Initialize state variables - _grantRole(DEFAULT_ADMIN_ROLE, owner_); - _setDefaultRoyalty(receiver_, feeNumerator_); - _setOperatorAllowlistRegistry(operatorAllowlist_); - baseURI = baseURI_; - contractURI = contractURI_; - } - - /// @dev Returns the supported interfaces - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721HybridPermit, ERC2981, AccessControlEnumerable) - returns (bool) - { - return super.supportsInterface(interfaceId); - } - - /// @notice Returns the baseURI of the collection - function _baseURI() internal view virtual override returns (string memory) { - return baseURI; - } - - /** - * @notice Allows admin to set the base URI - * @param baseURI_ The base URI to set - */ - function setBaseURI(string memory baseURI_) public onlyRole(DEFAULT_ADMIN_ROLE) { - baseURI = baseURI_; - } - - /** - * @notice sets the contract uri for the collection. Permissioned to only the admin role - * @param _contractURI the new baseURI to set - */ - function setContractURI(string memory _contractURI) public onlyRole(DEFAULT_ADMIN_ROLE) { - contractURI = _contractURI; - } - - /** - * @inheritdoc ERC721Hybrid - * @dev Note it will validate the operator in the allowlist - */ - function setApprovalForAll(address operator, bool approved) - public - virtual - override(ERC721Hybrid) - validateApproval(operator) - { - super.setApprovalForAll(operator, approved); - } - - /** - * @inheritdoc ERC721Hybrid - * @dev Note it will validate the to address in the allowlist - */ - function _approve(address to, uint256 tokenId) internal virtual override(ERC721Hybrid) validateApproval(to) { - super._approve(to, tokenId); - } - - /** - * @inheritdoc ERC721HybridPermit - * @dev Note it will validate the from and to address in the allowlist - */ - function _transfer(address from, address to, uint256 tokenId) - internal - virtual - override(ERC721HybridPermit) - validateTransfer(from, to) - { - super._transfer(from, to, tokenId); - } - - /** - * @notice Set the default royalty receiver address - * @param receiver the address to receive the royalty - * @param feeNumerator the royalty fee numerator - * @dev This can only be called by the an admin. See ERC2981 for more details on _setDefaultRoyalty - */ - function setDefaultRoyaltyReceiver(address receiver, uint96 feeNumerator) public onlyRole(DEFAULT_ADMIN_ROLE) { - _setDefaultRoyalty(receiver, feeNumerator); - } - - /** - * @notice Set the royalty receiver address for a specific tokenId - * @param tokenId the token to set the royalty for - * @param receiver the address to receive the royalty - * @param feeNumerator the royalty fee numerator - * @dev This can only be called by the a minter. See ERC2981 for more details on _setTokenRoyalty - */ - function setNFTRoyaltyReceiver(uint256 tokenId, address receiver, uint96 feeNumerator) - public - onlyRole(MINTER_ROLE) - { - _setTokenRoyalty(tokenId, receiver, feeNumerator); - } - - /** - * @notice Set the royalty receiver address for a list of tokenId - * @param tokenIds the list of tokens to set the royalty for - * @param receiver the address to receive the royalty - * @param feeNumerator the royalty fee numerator - * @dev This can only be called by the a minter. See ERC2981 for more details on _setTokenRoyalty - */ - function setNFTRoyaltyReceiverBatch(uint256[] calldata tokenIds, address receiver, uint96 feeNumerator) - public - onlyRole(MINTER_ROLE) - { - for (uint256 i = 0; i < tokenIds.length; i++) { - _setTokenRoyalty(tokenIds[i], receiver, feeNumerator); - } - } -} diff --git a/contracts/token/erc721/erc721psi/ERC721Psi.sol b/contracts/token/erc721/erc721psi/ERC721Psi.sol deleted file mode 100644 index 80b41326..00000000 --- a/contracts/token/erc721/erc721psi/ERC721Psi.sol +++ /dev/null @@ -1,467 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * ______ _____ _____ ______ ___ __ _ _ _ - * | ____| __ \ / ____|____ |__ \/_ | || || | - * | |__ | |__) | | / / ) || | \| |/ | - * | __| | _ /| | / / / / | |\_ _/ - * | |____| | \ \| |____ / / / /_ | | | | - * |______|_| \_\\_____|/_/ |____||_| |_| - * - * - github: https://github.com/estarriolvetch/ERC721Psi - * - npm: https://www.npmjs.com/package/erc721psi - */ -// forge-lint: disable-start(all) -pragma solidity >=0.8.19 <0.8.29; - -import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; -import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; -import "@openzeppelin/contracts/utils/Context.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; -import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import "@openzeppelin/contracts/utils/Address.sol"; -import "@openzeppelin/contracts/utils/StorageSlot.sol"; -import "solidity-bits/contracts/BitMaps.sol"; - -contract ERC721Psi is Context, ERC165, IERC721, IERC721Metadata { - using Address for address; - using Strings for uint256; - using BitMaps for BitMaps.BitMap; - - BitMaps.BitMap private _batchHead; - - string private _name; - string private _symbol; - - // Mapping from token ID to owner address - mapping(uint256 => address) internal _owners; - uint256 private _currentIndex; - - mapping(uint256 => address) private _tokenApprovals; - mapping(address => mapping(address => bool)) private _operatorApprovals; - - // The mask of the lower 160 bits for addresses. - uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1; - // The `Transfer` event signature is given by: - // `keccak256(bytes("Transfer(address,address,uint256)"))`. - bytes32 private constant _TRANSFER_EVENT_SIGNATURE = - 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; - - /** - * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. - */ - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - _currentIndex = _startTokenId(); - } - - /** - * @dev Returns the starting token ID. - * To change the starting token ID, please override this function. - */ - function _startTokenId() internal pure virtual returns (uint256) { - // It will become modifiable in the future versions - return 0; - } - - /** - * @dev Returns the next token ID to be minted. - */ - function _nextTokenId() internal view virtual returns (uint256) { - return _currentIndex; - } - - /** - * @dev Returns the total amount of tokens minted in the contract. - */ - function _totalMinted() internal view virtual returns (uint256) { - return _currentIndex - _startTokenId(); - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { - return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId - || super.supportsInterface(interfaceId); - } - - /** - * @dev See {IERC721-balanceOf}. - */ - function balanceOf(address owner) public view virtual override returns (uint256) { - require(owner != address(0), "ERC721Psi: balance query for the zero address"); - - uint256 count = 0; - for (uint256 i = _startTokenId(); i < _nextTokenId(); ++i) { - if (_exists(i)) { - if (owner == ownerOf(i)) { - ++count; - } - } - } - return count; - } - - /** - * @dev See {IERC721-ownerOf}. - */ - function ownerOf(uint256 tokenId) public view virtual override returns (address) { - (address owner,) = _ownerAndBatchHeadOf(tokenId); - return owner; - } - - function _ownerAndBatchHeadOf(uint256 tokenId) internal view returns (address owner, uint256 tokenIdBatchHead) { - require(_exists(tokenId), "ERC721Psi: owner query for nonexistent token"); - tokenIdBatchHead = _getBatchHead(tokenId); - owner = _owners[tokenIdBatchHead]; - } - - /** - * @dev See {IERC721Metadata-name}. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev See {IERC721Metadata-symbol}. - */ - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - /** - * @dev See {IERC721Metadata-tokenURI}. - */ - function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { - require(_exists(tokenId), "ERC721Psi: URI query for nonexistent token"); - - string memory baseURI = _baseURI(); - return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; - } - - /** - * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each - * token will be the concatenation of the `baseURI` and the `tokenId`. Empty - * by default, can be overriden in child contracts. - */ - // slither-disable-next-line dead-code - function _baseURI() internal view virtual returns (string memory) { - return ""; - } - - /** - * @dev See {IERC721-approve}. - */ - function approve(address to, uint256 tokenId) public virtual override { - address owner = ownerOf(tokenId); - require(to != owner, "ERC721Psi: approval to current owner"); - - require( - _msgSender() == owner || isApprovedForAll(owner, _msgSender()), - "ERC721Psi: approve caller is not owner nor approved for all" - ); - - _approve(to, tokenId); - } - - /** - * @dev See {IERC721-getApproved}. - */ - function getApproved(uint256 tokenId) public view virtual override returns (address) { - require(_exists(tokenId), "ERC721Psi: approved query for nonexistent token"); - - return _tokenApprovals[tokenId]; - } - - /** - * @dev See {IERC721-setApprovalForAll}. - */ - function setApprovalForAll(address operator, bool approved) public virtual override { - require(operator != _msgSender(), "ERC721Psi: approve to caller"); - - _operatorApprovals[_msgSender()][operator] = approved; - emit ApprovalForAll(_msgSender(), operator, approved); - } - - /** - * @dev See {IERC721-isApprovedForAll}. - */ - function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { - return _operatorApprovals[owner][operator]; - } - - /** - * @dev See {IERC721-transferFrom}. - */ - function transferFrom(address from, address to, uint256 tokenId) public virtual override { - require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Psi: transfer caller is not owner nor approved"); - - _transfer(from, to, tokenId); - } - - /** - * @dev See {IERC721-safeTransferFrom}. - */ - function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { - safeTransferFrom(from, to, tokenId, ""); - } - - /** - * @dev See {IERC721-safeTransferFrom}. - */ - function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { - require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Psi: transfer caller is not owner nor approved"); - _safeTransfer(from, to, tokenId, _data); - } - - /** - * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients - * are aware of the ERC721 protocol to prevent tokens from being forever locked. - * - * `_data` is additional data, it has no specified format and it is sent in call to `to`. - * - * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. - * implement alternative mechanisms to perform token transfer, such as signature-based. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `tokenId` token must exist and be owned by `from`. - * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - * - * Emits a {Transfer} event. - */ - function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual { - _transfer(from, to, tokenId); - require( - _checkOnERC721Received(from, to, tokenId, 1, _data), "ERC721Psi: transfer to non ERC721Receiver implementer" - ); - } - - /** - * @dev Returns whether `tokenId` exists. - * - * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. - * - * Tokens start existing when they are minted (`_mint`). - */ - function _exists(uint256 tokenId) internal view virtual returns (bool) { - return tokenId < _nextTokenId() && _startTokenId() <= tokenId; - } - - /** - * @dev Returns whether `spender` is allowed to manage `tokenId`. - * - * Requirements: - * - * - `tokenId` must exist. - */ - function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { - require(_exists(tokenId), "ERC721Psi: operator query for nonexistent token"); - address owner = ownerOf(tokenId); - return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); - } - - /** - * @dev Safely mints `quantity` tokens and transfers them to `to`. - * - * Requirements: - * - * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called for each safe transfer. - * - `quantity` must be greater than 0. - * - * Emits a {Transfer} event. - */ - function _safeMint(address to, uint256 quantity) internal virtual { - ERC721Psi._safeMint(to, quantity, ""); - } - - function _safeMint(address to, uint256 quantity, bytes memory _data) internal virtual { - uint256 nextTokenId = _nextTokenId(); - // need to specify the specific implementation to avoid calling the - // mint method of erc721 due to matching func signatures - ERC721Psi._mint(to, quantity); - require( - _checkOnERC721Received(address(0), to, nextTokenId, quantity, _data), - "ERC721Psi: transfer to non ERC721Receiver implementer" - ); - } - - function _mint(address to, uint256 quantity) internal virtual { - uint256 nextTokenId = _nextTokenId(); - - require(quantity > 0, "ERC721Psi: quantity must be greater 0"); - require(to != address(0), "ERC721Psi: mint to the zero address"); - - _beforeTokenTransfers(address(0), to, nextTokenId, quantity); - _currentIndex += quantity; - _owners[nextTokenId] = to; - _batchHead.set(nextTokenId); - - uint256 toMasked; - uint256 end = nextTokenId + quantity; - - // Use assembly to loop and emit the `Transfer` event for gas savings. - // The duplicated `log4` removes an extra check and reduces stack juggling. - // The assembly, together with the surrounding Solidity code, have been - // delicately arranged to nudge the compiler into producing optimized opcodes. - assembly ("memory-safe") { - // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean. - toMasked := and(to, _BITMASK_ADDRESS) - // Emit the `Transfer` event. - log4( - 0, // Start of data (0, since no data). - 0, // End of data (0, since no data). - _TRANSFER_EVENT_SIGNATURE, // Signature. - 0, // `address(0)`. - toMasked, // `to`. - nextTokenId // `tokenId`. - ) - - // The `iszero(eq(,))` check ensures that large values of `quantity` - // that overflows uint256 will make the loop run out of gas. - // The compiler will optimize the `iszero` away for performance. - for { - let tokenId := add(nextTokenId, 1) - } iszero(eq(tokenId, end)) { - tokenId := add(tokenId, 1) - } { - // Emit the `Transfer` event. Similar to above. - log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId) - } - } - - _afterTokenTransfers(address(0), to, nextTokenId, quantity); - } - - /** - * @dev Transfers `tokenId` from `from` to `to`. - * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - `tokenId` token must be owned by `from`. - * - * Emits a {Transfer} event. - */ - function _transfer(address from, address to, uint256 tokenId) internal virtual { - (address owner, uint256 tokenIdBatchHead) = _ownerAndBatchHeadOf(tokenId); - - require(owner == from, "ERC721Psi: transfer of token that is not own"); - require(to != address(0), "ERC721Psi: transfer to the zero address"); - - _beforeTokenTransfers(from, to, tokenId, 1); - - // Clear approvals from the previous owner - _approve(address(0), tokenId); - - uint256 subsequentTokenId = tokenId + 1; - - if (!_batchHead.get(subsequentTokenId) && subsequentTokenId < _nextTokenId()) { - _owners[subsequentTokenId] = from; - _batchHead.set(subsequentTokenId); - } - - _owners[tokenId] = to; - if (tokenId != tokenIdBatchHead) { - _batchHead.set(tokenId); - } - - emit Transfer(from, to, tokenId); - - _afterTokenTransfers(from, to, tokenId, 1); - } - - /** - * @dev Approve `to` to operate on `tokenId` - * - * Emits a {Approval} event. - */ - function _approve(address to, uint256 tokenId) internal virtual { - _tokenApprovals[tokenId] = to; - emit Approval(ownerOf(tokenId), to, tokenId); - } - - /** - * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. - * The call is not executed if the target address is not a contract. - * - * @param from address representing the previous owner of the given token ID - * @param to target address that will receive the tokens - * @param startTokenId uint256 the first ID of the tokens to be transferred - * @param quantity uint256 amount of the tokens to be transfered. - * @param _data bytes optional data to send along with the call - * @return r bool whether the call correctly returned the expected magic value - */ - function _checkOnERC721Received( - address from, - address to, - uint256 startTokenId, - uint256 quantity, - bytes memory _data - ) private returns (bool r) { - if (to.isContract()) { - r = true; - for (uint256 tokenId = startTokenId; tokenId < startTokenId + quantity; tokenId++) { - // slither-disable-start calls-loop - try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { - r = r && retval == IERC721Receiver.onERC721Received.selector; - } catch (bytes memory reason) { - if (reason.length == 0) { - revert("ERC721Psi: transfer to non ERC721Receiver implementer"); - } else { - assembly ("memory-safe") { - revert(add(32, reason), mload(reason)) - } - } - } - // slither-disable-end calls-loop - } - return r; - } else { - return true; - } - } - - function _getBatchHead(uint256 tokenId) internal view returns (uint256 tokenIdBatchHead) { - tokenIdBatchHead = _batchHead.scanForward(tokenId); - } - - function totalSupply() public view virtual returns (uint256) { - return _totalMinted(); - } - - /** - * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting. - * - * startTokenId - the first token id to be transferred - * quantity - the amount to be transferred - * - * Calling conditions: - * - * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be - * transferred to `to`. - * - When `from` is zero, `tokenId` will be minted for `to`. - */ - function _beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} - - /** - * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes - * minting. - * - * startTokenId - the first token id to be transferred - * quantity - the amount to be transferred - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero. - * - `from` and `to` are never both zero. - */ - function _afterTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} -} -// forge-lint: disable-end(all) diff --git a/contracts/token/erc721/erc721psi/ERC721PsiBurnable.sol b/contracts/token/erc721/erc721psi/ERC721PsiBurnable.sol deleted file mode 100644 index fa46873c..00000000 --- a/contracts/token/erc721/erc721psi/ERC721PsiBurnable.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: MIT -/** - * ______ _____ _____ ______ ___ __ _ _ _ - * | ____| __ \ / ____|____ |__ \/_ | || || | - * | |__ | |__) | | / / ) || | \| |/ | - * | __| | _ /| | / / / / | |\_ _/ - * | |____| | \ \| |____ / / / /_ | | | | - * |______|_| \_\\_____|/_/ |____||_| |_| - */ -pragma solidity >=0.8.19 <0.8.29; - -import {BitMaps} from "solidity-bits/contracts/BitMaps.sol"; -import {ERC721Psi} from "./ERC721Psi.sol"; - -abstract contract ERC721PsiBurnable is ERC721Psi { - using BitMaps for BitMaps.BitMap; - - BitMaps.BitMap private _burnedToken; - - /** - * @dev Destroys `tokenId`. - * The approval is cleared when the token is burned. - * - * Requirements: - * - * - `tokenId` must exist. - * - * Emits a {Transfer} event. - */ - function _burn(uint256 tokenId) internal virtual { - address from = ownerOf(tokenId); - _beforeTokenTransfers(from, address(0), tokenId, 1); - _burnedToken.set(tokenId); - - emit Transfer(from, address(0), tokenId); - - _afterTokenTransfers(from, address(0), tokenId, 1); - } - - /** - * @dev Returns whether `tokenId` exists. - * - * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. - * - * Tokens start existing when they are minted (`_mint`), - * and stop existing when they are burned (`_burn`). - */ - function _exists(uint256 tokenId) internal view virtual override returns (bool) { - if (_burnedToken.get(tokenId)) { - return false; - } - return super._exists(tokenId); - } - - /** - * @dev See {IERC721Enumerable-totalSupply}. - */ - function totalSupply() public view virtual override returns (uint256) { - return _totalMinted() - _burned(); - } - - /** - * @dev Returns number of token burned. - */ - function _burned() internal view returns (uint256 burned) { - uint256 startBucket = _startTokenId() >> 8; - uint256 lastBucket = (_nextTokenId() >> 8) + 1; - - for (uint256 i = startBucket; i < lastBucket; i++) { - uint256 bucket = _burnedToken.getBucket(i); - burned += _popcount(bucket); - } - } - - /** - * @dev Returns number of set bits. - */ - function _popcount(uint256 x) private pure returns (uint256 count) { - unchecked { - for (count = 0; x != 0; count++) { - x &= x - 1; - } - } - } -} diff --git a/contracts/token/erc721/preset/ImmutableERC721.sol b/contracts/token/erc721/preset/ImmutableERC721.sol deleted file mode 100644 index c248a546..00000000 --- a/contracts/token/erc721/preset/ImmutableERC721.sol +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {ImmutableERC721HybridBase} from "../abstract/ImmutableERC721HybridBase.sol"; - -contract ImmutableERC721 is ImmutableERC721HybridBase { - /// ===== Constructor ===== - /** - * @notice Grants `DEFAULT_ADMIN_ROLE` to the supplied `owner` address - * @param owner_ The address to grant the `DEFAULT_ADMIN_ROLE` to - * @param name_ The name of the collection - * @param symbol_ The symbol of the collection - * @param baseURI_ The base URI for the collection - * @param contractURI_ The contract URI for the collection - * @param operatorAllowlist_ The address of the operator allowlist - * @param royaltyReceiver_ The address of the royalty receiver - * @param feeNumerator_ The royalty fee numerator - * @dev the royalty receiver and amount (this can not be changed once set) - */ - constructor( - address owner_, - string memory name_, - string memory symbol_, - string memory baseURI_, - string memory contractURI_, - address operatorAllowlist_, - address royaltyReceiver_, - uint96 feeNumerator_ - ) - ImmutableERC721HybridBase( - owner_, name_, symbol_, baseURI_, contractURI_, operatorAllowlist_, royaltyReceiver_, feeNumerator_ - ) - {} - - /** - * @notice Allows minter to mint a token by ID to a specified address - * @param to the address to mint the token to - * @param tokenId the ID of the token to mint - */ - function mint(address to, uint256 tokenId) external onlyRole(MINTER_ROLE) { - _mintByID(to, tokenId); - } - - /** - * @notice Allows minter to mint a token by ID to a specified address with hooks and checks - * @param to the address to mint the token to - * @param tokenId the ID of the token to mint - */ - function safeMint(address to, uint256 tokenId) external onlyRole(MINTER_ROLE) { - _safeMintByID(to, tokenId); - } - - /** - * @notice Allows minter to mint a number of tokens sequentially to a specified address - * @param to the address to mint the token to - * @param quantity the number of tokens to mint - */ - function mintByQuantity(address to, uint256 quantity) external onlyRole(MINTER_ROLE) { - _mintByQuantity(to, quantity); - } - - /** - * @notice Allows minter to mint a number of tokens sequentially to a specified address with hooks - * and checks - * @param to the address to mint the token to - * @param quantity the number of tokens to mint - */ - function safeMintByQuantity(address to, uint256 quantity) external onlyRole(MINTER_ROLE) { - _safeMintByQuantity(to, quantity); - } - - /** - * @notice Allows minter to mint a number of tokens sequentially to a number of specified addresses - * @param mints the list of Mint struct containing the to, and the number of tokens to mint - */ - function mintBatchByQuantity(Mint[] calldata mints) external onlyRole(MINTER_ROLE) { - _mintBatchByQuantity(mints); - } - - /** - * @notice Allows minter to safe mint a number of tokens sequentially to a number of specified addresses - * @param mints the list of Mint struct containing the to, and the number of tokens to mint - */ - function safeMintBatchByQuantity(Mint[] calldata mints) external onlyRole(MINTER_ROLE) { - _safeMintBatchByQuantity(mints); - } - - /** - * @notice Allows minter to safe mint a number of tokens by ID to a number of specified - * addresses with hooks and checks. Check ERC721Hybrid for details on _mintBatchByIDToMultiple - * @param mints the list of IDMint struct containing the to, and tokenIds - */ - function mintBatch(IDMint[] calldata mints) external onlyRole(MINTER_ROLE) { - _mintBatchByIDToMultiple(mints); - } - - /** - * @notice Allows minter to safe mint a number of tokens by ID to a number of specified - * addresses with hooks and checks. Check ERC721Hybrid for details on _safeMintBatchByIDToMultiple - * @param mints the list of IDMint struct containing the to, and tokenIds - */ - function safeMintBatch(IDMint[] calldata mints) external onlyRole(MINTER_ROLE) { - _safeMintBatchByIDToMultiple(mints); - } - - /** - * @notice Allows caller to a burn a number of tokens by ID from a specified address - * @param burns the IDBurn struct containing the to, and tokenIds - */ - function safeBurnBatch(IDBurn[] calldata burns) external { - _safeBurnBatch(burns); - } - - /** - * @notice Allows caller to a transfer a number of tokens by ID from a specified - * address to a number of specified addresses - * @param tr the TransferRequest struct containing the from, tos, and tokenIds - */ - function safeTransferFromBatch(TransferRequest calldata tr) external { - if (tr.tokenIds.length != tr.tos.length) { - revert IImmutableERC721MismatchedTransferLengths(); - } - - for (uint256 i = 0; i < tr.tokenIds.length; i++) { - safeTransferFrom(tr.from, tr.tos[i], tr.tokenIds[i]); - } - } -} diff --git a/contracts/trading/seaport16/ImmutableSeaport.sol b/contracts/trading/seaport16/ImmutableSeaport.sol deleted file mode 100644 index 622c3b18..00000000 --- a/contracts/trading/seaport16/ImmutableSeaport.sol +++ /dev/null @@ -1,675 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.24; - -import {Consideration} from "seaport-core-16/src/lib/Consideration.sol"; -import { - AdvancedOrder, - BasicOrderParameters, - CriteriaResolver, - Execution, - Fulfillment, - FulfillmentComponent, - Order -} from "seaport-types-16/src/lib/ConsiderationStructs.sol"; -import {OrderType} from "seaport-types-16/src/lib/ConsiderationEnums.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {ImmutableSeaportEvents} from "./interfaces/ImmutableSeaportEvents.sol"; - -/** - * @title ImmutableSeaport - * @custom:version 1.6 - * @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 ImmutableSeaport is Consideration, Ownable, ImmutableSeaportEvents { - // Mapping to store valid ImmutableZones - this allows for multiple Zones - // to be active at the same time, and can be expired or added on demand. - mapping(address => bool) public allowedZones; - - error OrderNotRestricted(uint8 orderType); - error AllowedZoneAlreadySet(address zone, bool allowed); - error InvalidZone(address zone); - - /** - * @notice Derive and set hashes, reference chainId, and associated domain - * separator during deployment. - * - * @param conduitController A contract that deploys conduits, or proxies - * that may optionally be used to transfer approved - * ERC20/721/1155 tokens. - * @param owner The address of the owner of this contract. Specified in the - * constructor to be CREATE2 / CREATE3 compatible. - */ - constructor(address conduitController, address owner) Consideration(conduitController) Ownable() { - require(owner != address(0), "ImmutableSeaport: owner is the zero address"); - // Transfer ownership to the address specified in the constructor - _transferOwnership(owner); - } - - /** - * @dev Set the validity of a zone for use during fulfillment. - */ - function setAllowedZone(address zone, bool allowed) external onlyOwner { - require(zone != address(0), "ImmutableSeaport: zone is the zero address"); - - if (allowedZones[zone] == allowed) { - revert AllowedZoneAlreadySet(zone, allowed); - } - - allowedZones[zone] = allowed; - emit AllowedZoneSet(zone, allowed); - } - - /** - * @dev Internal pure function to retrieve and return the name of this - * contract. - * - * @return The name of this contract. - */ - function _name() internal pure override returns (string memory) { - // Return the name of the contract. - return "ImmutableSeaport"; - } - - /** - * @dev Internal pure function to retrieve the name of this contract as a - * string that will be used to derive the name hash in the constructor. - * - * @return The name of this contract as a string. - */ - // slither-disable-next-line dead-code - function _nameString() internal pure override returns (string memory) { - // Return the name of the contract. - return "ImmutableSeaport"; - } - - /** - * @dev Helper function to revert any basic order that has an invalid zone. - * - * @param parameters The basic order parameters. - */ - function _rejectBasicOrderIfZoneInvalid(BasicOrderParameters calldata parameters) internal view { - // Basic order types (modulo 4): 0 = FULL_OPEN, 1 = PARTIAL_OPEN, 2 = FULL_RESTRICTED, 3 = PARTIAL_RESTRICTED. Only restricted orders (types 2 and 3) are allowed - if (uint256(parameters.basicOrderType) % 4 != 2 && uint256(parameters.basicOrderType) % 4 != 3) { - revert OrderNotRestricted(uint8(uint256(parameters.basicOrderType) % 4)); - } - _rejectIfZoneInvalid(parameters.zone); - } - - /** - * @dev Helper function to revert any order that has an invalid zone. - * - * @param order The order. - */ - function _rejectOrderIfZoneInvalid(Order memory order) internal view { - if ( - order.parameters.orderType != OrderType.FULL_RESTRICTED - && order.parameters.orderType != OrderType.PARTIAL_RESTRICTED - ) { - revert OrderNotRestricted(uint8(order.parameters.orderType)); - } - _rejectIfZoneInvalid(order.parameters.zone); - } - - /** - * @dev Helper function to revert any advanced order that has an invalid zone. - * - * @param advancedOrder The advanced order. - */ - function _rejectAdvancedOrderIfZoneInvalid(AdvancedOrder memory advancedOrder) internal view { - if ( - advancedOrder.parameters.orderType != OrderType.FULL_RESTRICTED - && advancedOrder.parameters.orderType != OrderType.PARTIAL_RESTRICTED - ) { - revert OrderNotRestricted(uint8(advancedOrder.parameters.orderType)); - } - _rejectIfZoneInvalid(advancedOrder.parameters.zone); - } - - /** - * @dev Helper function to revert if the zone is not allowed. - * - * @param zone The zone to check. - */ - function _rejectIfZoneInvalid(address zone) internal view { - if (!allowedZones[zone]) { - revert InvalidZone(zone); - } - } - - /** - * @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. - * - * @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(BasicOrderParameters calldata parameters) - public - payable - virtual - override - returns (bool fulfilled) - { - _rejectBasicOrderIfZoneInvalid(parameters); - - return super.fulfillBasicOrder(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) - public - payable - virtual - override - returns (bool fulfilled) - { - _rejectBasicOrderIfZoneInvalid(parameters); - - return super.fulfillBasicOrder_efficient_6GL6yc(parameters); - } - - /** - * @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). - * - * @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 - * 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 (and direct approvals set on - * this contract). - * - * @return fulfilled A boolean indicating whether the order has been - * successfully fulfilled. - */ - function fulfillOrder( - /** - * @custom:name order - */ - Order calldata order, - bytes32 fulfillerConduitKey - ) - public - payable - virtual - override - returns (bool fulfilled) - { - _rejectOrderIfZoneInvalid(order); - - return super.fulfillOrder(order, fulfillerConduitKey); - } - - /** - * @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. - * - * @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( - /** - * @custom:name advancedOrder - */ - AdvancedOrder calldata advancedOrder, - /** - * @custom:name criteriaResolvers - */ - CriteriaResolver[] calldata criteriaResolvers, - bytes32 fulfillerConduitKey, - address recipient - ) public payable virtual override returns (bool fulfilled) { - _rejectAdvancedOrderIfZoneInvalid(advancedOrder); - - return super.fulfillAdvancedOrder(advancedOrder, criteriaResolvers, fulfillerConduitKey, recipient); - } - - /** - * @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). - * - * @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 - * 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( - /** - * @custom:name orders - */ - Order[] calldata orders, - /** - * @custom:name offerFulfillments - */ - FulfillmentComponent[][] calldata offerFulfillments, - /** - * @custom:name considerationFulfillments - */ - FulfillmentComponent[][] calldata considerationFulfillments, - bytes32 fulfillerConduitKey, - uint256 maximumFulfilled - ) - public - payable - virtual - override - returns ( - bool[] memory, /* availableOrders */ - Execution[] memory /* executions */ - ) - { - uint256 numberOfOrders = orders.length; - for (uint256 i = 0; i < numberOfOrders; i++) { - Order memory order = orders[i]; - _rejectOrderIfZoneInvalid(order); - } - - return super.fulfillAvailableOrders( - orders, offerFulfillments, considerationFulfillments, fulfillerConduitKey, maximumFulfilled - ); - } - - /** - * @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. - * - * @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 - * 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( - /** - * @custom:name advancedOrders - */ - AdvancedOrder[] calldata advancedOrders, - /** - * @custom:name criteriaResolvers - */ - CriteriaResolver[] calldata criteriaResolvers, - /** - * @custom:name offerFulfillments - */ - FulfillmentComponent[][] calldata offerFulfillments, - /** - * @custom:name considerationFulfillments - */ - FulfillmentComponent[][] calldata considerationFulfillments, - bytes32 fulfillerConduitKey, - address recipient, - uint256 maximumFulfilled - ) - public - payable - virtual - override - returns ( - bool[] memory, /* availableOrders */ - Execution[] memory /* executions */ - ) - { - uint256 numberOfAdvancedOrders = advancedOrders.length; - for (uint256 i = 0; i < numberOfAdvancedOrders; i++) { - AdvancedOrder memory advancedOrder = advancedOrders[i]; - _rejectAdvancedOrderIfZoneInvalid(advancedOrder); - } - - return super.fulfillAvailableAdvancedOrders( - advancedOrders, - criteriaResolvers, - offerFulfillments, - considerationFulfillments, - fulfillerConduitKey, - recipient, - maximumFulfilled - ); - } - - /** - * @notice Match an arbitrary number of 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 function does not support - * criteria-based or partial filling of orders (though filling the - * 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. Note that unspent offer item amounts or native - * tokens will not be reflected as part of this array. - */ - function matchOrders( - /** - * @custom:name orders - */ - Order[] calldata orders, - /** - * @custom:name fulfillments - */ - Fulfillment[] calldata fulfillments - ) - public - payable - virtual - override - returns ( - Execution[] memory /* executions */ - ) - { - uint256 numberOfOrders = orders.length; - for (uint256 i = 0; i < numberOfOrders; i++) { - Order memory order = orders[i]; - _rejectOrderIfZoneInvalid(order); - } - - return super.matchOrders(orders, fulfillments); - } - - /** - * @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. Note that unspent offer item amounts or - * native tokens will not be reflected as part of this - * array. - */ - function matchAdvancedOrders( - /** - * @custom:name advancedOrders - */ - AdvancedOrder[] calldata advancedOrders, - /** - * @custom:name criteriaResolvers - */ - CriteriaResolver[] calldata criteriaResolvers, - /** - * @custom:name fulfillments - */ - Fulfillment[] calldata fulfillments, - address recipient - ) - public - payable - virtual - override - returns ( - Execution[] memory /* executions */ - ) - { - uint256 numberOfAdvancedOrders = advancedOrders.length; - for (uint256 i = 0; i < numberOfAdvancedOrders; i++) { - AdvancedOrder memory advancedOrder = advancedOrders[i]; - _rejectAdvancedOrderIfZoneInvalid(advancedOrder); - } - - return super.matchAdvancedOrders(advancedOrders, criteriaResolvers, fulfillments, recipient); - } -} diff --git a/contracts/trading/seaport16/conduit/ConduitController.sol b/contracts/trading/seaport16/conduit/ConduitController.sol deleted file mode 100644 index a3b0eaac..00000000 --- a/contracts/trading/seaport16/conduit/ConduitController.sol +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -// forge-lint: disable-next-line(unused-import) -import {ConduitController} from "seaport-core-16/src/conduit/ConduitController.sol"; diff --git a/contracts/trading/seaport16/interfaces/ImmutableSeaportEvents.sol b/contracts/trading/seaport16/interfaces/ImmutableSeaportEvents.sol deleted file mode 100644 index 9b67db9a..00000000 --- a/contracts/trading/seaport16/interfaces/ImmutableSeaportEvents.sol +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; - -/** - * @notice ImmutableSeaportEvents contains events - * related to the ImmutableSeaport contract - */ -interface ImmutableSeaportEvents { - /** - * @dev Emit an event when an allowed zone status is updated - */ - event AllowedZoneSet(address zoneAddress, bool allowed); -} diff --git a/contracts/trading/seaport16/validators/ReadOnlyOrderValidator.sol b/contracts/trading/seaport16/validators/ReadOnlyOrderValidator.sol deleted file mode 100644 index 74de5531..00000000 --- a/contracts/trading/seaport16/validators/ReadOnlyOrderValidator.sol +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: MIT -// solhint-disable -pragma solidity ^0.8.17; - -import {ReadOnlyOrderValidator} from "seaport-16/contracts/helpers/order-validator/lib/ReadOnlyOrderValidator.sol"; diff --git a/contracts/trading/seaport16/validators/SeaportValidator.sol b/contracts/trading/seaport16/validators/SeaportValidator.sol deleted file mode 100644 index 196db81b..00000000 --- a/contracts/trading/seaport16/validators/SeaportValidator.sol +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: MIT -// solhint-disable -pragma solidity ^0.8.17; - -import {SeaportValidator} from "seaport-16/contracts/helpers/order-validator/SeaportValidator.sol"; diff --git a/contracts/trading/seaport16/validators/SeaportValidatorHelper.sol b/contracts/trading/seaport16/validators/SeaportValidatorHelper.sol deleted file mode 100644 index 8f565398..00000000 --- a/contracts/trading/seaport16/validators/SeaportValidatorHelper.sol +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: MIT -// solhint-disable -pragma solidity ^0.8.17; - -import {SeaportValidatorHelper} from "seaport-16/contracts/helpers/order-validator/lib/SeaportValidatorHelper.sol"; diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.sol b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.sol deleted file mode 100644 index 193f2b13..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.sol +++ /dev/null @@ -1,879 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.20; - -import {ITransferValidator} from "@limitbreak/creator-token-standards/src/interfaces/ITransferValidator.sol"; -import {AccessControlEnumerable} from "openzeppelin-contracts-5.0.2/access/extensions/AccessControlEnumerable.sol"; -import {ECDSA} from "openzeppelin-contracts-5.0.2/utils/cryptography/ECDSA.sol"; -import {MessageHashUtils} from "openzeppelin-contracts-5.0.2/utils/cryptography/MessageHashUtils.sol"; -import {ERC165} from "openzeppelin-contracts-5.0.2/utils/introspection/ERC165.sol"; -import {Math} from "openzeppelin-contracts-5.0.2/utils/math/Math.sol"; -import {ZoneInterface} from "seaport-types-16/src/interfaces/ZoneInterface.sol"; -import {ZoneParameters, Schema, ReceivedItem} from "seaport-types-16/src/lib/ConsiderationStructs.sol"; -import {ZoneAccessControl} from "./ZoneAccessControl.sol"; -import {SIP5Interface} from "./interfaces/SIP5Interface.sol"; -import {SIP6Interface} from "./interfaces/SIP6Interface.sol"; -import {SIP7Interface} from "./interfaces/SIP7Interface.sol"; - -/** - * @title ImmutableSignedZoneV3 - * @author Immutable - * @notice ImmutableSignedZoneV3 is a zone implementation based on the - * SIP-7 standard https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md - * implementing substandards 1, 3, 4, 6, 7 and 8. - * - * The contract is not upgradable. If the contract needs to be changed a new version - * should be deployed, and the old version should be removed from the Seaport contract - * zone allowlist. - */ -contract ImmutableSignedZoneV3 is - ERC165, - ZoneAccessControl, - ZoneInterface, - SIP5Interface, - SIP6Interface, - SIP7Interface -{ - /// @dev The EIP-712 domain type hash. - bytes32 private constant _EIP_712_DOMAIN_TYPEHASH = keccak256( - abi.encodePacked( - "EIP712Domain(", "string name,", "string version,", "uint256 chainId,", "address verifyingContract", ")" - ) - ); - - /// @dev The EIP-712 domain version value. - bytes32 private constant _VERSION_HASH = keccak256(bytes("3.0")); - - /// @dev The EIP-712 signed order type hash. - bytes32 private constant _SIGNED_ORDER_TYPEHASH = keccak256( - abi.encodePacked( - "SignedOrder(", "address fulfiller,", "uint64 expiration,", "bytes32 orderHash,", "bytes context", ")" - ) - ); - - /// @dev The chain ID on which the contract was deployed. - uint256 private immutable _CHAIN_ID = block.chainid; - - /// @dev The domain separator used for signing. - bytes32 private immutable _DOMAIN_SEPARATOR; - - /// @dev The accepted SIP-6 version. - uint8 private constant _ACCEPTED_SIP6_VERSION = 0; - - /// @dev The name for this zone returned in getSeaportMetadata(). - string private _ZONE_NAME; - - bytes32 private immutable _NAME_HASH; - - /// @dev The Seaport contract address. - address private immutable _SEAPORT; - - /// @dev The allowed signers. - mapping(address => SignerInfo) private _signers; - - /// @dev The API endpoint where orders for this zone can be signed. - string private _apiEndpoint; - - /// @dev The documentationURI. - string private _documentationURI; - - /** - * @notice Constructor to deploy the contract. - * - * @param zoneName The name for the zone returned in getSeaportMetadata(). - * @param seaport The Seaport contract address. - * @param apiEndpoint The API endpoint where orders for this zone can be signed. - * Request and response payloads are defined in SIP-7. - * @param documentationURI The documentation URI. - * @param owner The address of the owner of this contract. Specified in the - * constructor to be CREATE2 / CREATE3 compatible. - */ - constructor( - string memory zoneName, - address seaport, - string memory apiEndpoint, - string memory documentationURI, - address owner - ) ZoneAccessControl(owner) { - // Set the zone name. - _ZONE_NAME = zoneName; - - // Set name hash. - _NAME_HASH = keccak256(bytes(zoneName)); - - // Set the Seaport contract address. - if (seaport == address(0)) { - revert SeaportCannotBeZeroAddress(); - } - _SEAPORT = seaport; - - // Set the API endpoint. - _apiEndpoint = apiEndpoint; - - // Set the documentation URI. - _documentationURI = documentationURI; - - // Derive and set the domain separator. - _DOMAIN_SEPARATOR = _deriveDomainSeparator(); - - // Emit an event to signal a SIP-5 contract has been deployed. - emit SeaportCompatibleContractDeployed(); - } - - /** - * @notice Check if a given signer is active. - * - * @param signer The signer address to check. - * @return True if the signer is active, false otherwise. - */ - function isActiveSigner(address signer) external view returns (bool) { - return _signers[signer].active; - } - - /** - * @notice Add a new signer to the zone. - * - * @param signer The new signer address to add. - */ - function addSigner(address signer) external override onlyRole(ZONE_MANAGER_ROLE) { - // Do not allow the zero address to be added as a signer. - if (signer == address(0)) { - revert SignerCannotBeZeroAddress(); - } - - // Revert if the signer is already active. - if (_signers[signer].active) { - revert SignerAlreadyActive(signer); - } - - // Revert if the signer was previously authorized. - // Specified in SIP-7 to prevent compromised signer from being - // cycled back into use. - if (_signers[signer].previouslyActive) { - revert SignerCannotBeReauthorized(signer); - } - - // Set the signer info. - _signers[signer] = SignerInfo({active: true, previouslyActive: true}); - - // Emit an event that the signer was added. - emit SignerAdded(signer); - } - - /** - * @notice Remove an active signer from the zone. - * - * @param signer The signer address to remove. - */ - function removeSigner(address signer) external override onlyRole(ZONE_MANAGER_ROLE) { - // Revert if the signer is not active. - if (!_signers[signer].active) { - revert SignerNotActive(signer); - } - - // Set the signer's active status to false. - _signers[signer].active = false; - - // Emit an event that the signer was removed. - emit SignerRemoved(signer); - } - - /** - * @notice Update the API endpoint returned by this zone. - * - * @param newApiEndpoint The new API endpoint. - */ - function updateAPIEndpoint(string calldata newApiEndpoint) external override onlyRole(ZONE_MANAGER_ROLE) { - _apiEndpoint = newApiEndpoint; - } - - /** - * @notice Update the documentation URI returned by this zone. - * - * @param newDocumentationURI The new documentation URI. - */ - function updateDocumentationURI(string calldata newDocumentationURI) external override onlyRole(ZONE_MANAGER_ROLE) { - _documentationURI = newDocumentationURI; - } - - /** - * @dev Returns Seaport metadata for this contract, returning the - * contract name and supported schemas. - * - * @return name The contract name. - * @return schemas The supported SIPs. - */ - function getSeaportMetadata() - external - view - override(SIP5Interface, ZoneInterface) - returns (string memory name, Schema[] memory schemas) - { - name = _ZONE_NAME; - - // supported SIP (7) - schemas = new Schema[](1); - schemas[0].id = 7; - schemas[0].metadata = - abi.encode(_domainSeparator(), _apiEndpoint, _getSupportedSubstandards(), _documentationURI); - } - - /** - * @notice Returns signing information about the zone. - * - * @return domainSeparator The domain separator used for signing. - * @return apiEndpoint The API endpoint to get signatures for orders. - * @return substandards The supported substandards. - * @return documentationURI The documentation URI. - */ - function sip7Information() - external - view - override - returns ( - bytes32 domainSeparator, - string memory apiEndpoint, - uint256[] memory substandards, - string memory documentationURI - ) - { - domainSeparator = _domainSeparator(); - apiEndpoint = _apiEndpoint; - - substandards = _getSupportedSubstandards(); - - documentationURI = _documentationURI; - } - - /** - * @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, before tokens have been transferred. - * - * @param zoneParameters The zone parameters containing data related to - * the fulfilment execution. - * @return authorizedOrderMagicValue A magic value indicating if the order - * is currently valid. - */ - function authorizeOrder(ZoneParameters calldata zoneParameters) - external - override - returns (bytes4 authorizedOrderMagicValue) - { - // Revert if the caller is not the Seaport contract. - if (msg.sender != _SEAPORT) { - revert CallerNotSeaport(); - } - - // Put the extraData and orderHash on the stack for cheaper access. - bytes calldata extraData = zoneParameters.extraData; - bytes32 orderHash = zoneParameters.orderHash; - - // Revert with an error if the extraData is empty. - if (extraData.length == 0) { - revert InvalidExtraData("extraData is empty", orderHash); - } - - // We expect the extraData to conform with SIP-6 as well as SIP-7 - // Therefore all SIP-7 related data is offset by one byte - // SIP-7 specifically requires SIP-6 as a prerequisite. - - // Revert with an error if the extraData does not have valid length. - if (extraData.length < 93) { - revert InvalidExtraData("extraData length must be at least 93 bytes", orderHash); - } - - // Revert if SIP-6 version is not accepted (0). - if (uint8(extraData[0]) != _ACCEPTED_SIP6_VERSION) { - revert UnsupportedExtraDataVersion(uint8(extraData[0])); - } - - // extraData bytes 1-20: expected fulfiller. - // (zero address means not restricted). - address expectedFulfiller = address(bytes20(extraData[1:21])); - - // extraData bytes 21-28: expiration timestamp. - uint64 expiration = uint64(bytes8(extraData[21:29])); - - // extraData bytes 29-92: signature. - // (strictly requires 64 byte compact sig, ERC2098). - bytes calldata signature = extraData[29:93]; - - // extraData bytes 93-end: context (optional, variable length). - bytes calldata context = extraData[93:]; - - // Revert if expired. - if (block.timestamp > expiration) { - revert SignatureExpired(block.timestamp, expiration, orderHash); - } - - // Put fulfiller on the stack for more efficient access. - address actualFulfiller = zoneParameters.fulfiller; - - // Revert unless: - // - expected fulfiller is 0 address (any fulfiller) OR - // - expected fulfiller is the same as actual fulfiller. - if (expectedFulfiller != address(0) && expectedFulfiller != actualFulfiller) { - revert InvalidFulfiller(expectedFulfiller, actualFulfiller, orderHash); - } - - // Revert if no spent items are provided. - if (zoneParameters.offer.length == 0) { - revert NoSpentItems(orderHash); - } - - // Revert if no received items are provided. - if (zoneParameters.consideration.length == 0) { - revert NoReceivedItems(orderHash); - } - - // Validate supported substandards - before hook validation. - _validateSubstandards(context, zoneParameters, true); - - // Derive the signedOrder hash. - bytes32 signedOrderHash = _deriveSignedOrderHash(expectedFulfiller, expiration, orderHash, context); - - // Derive the EIP-712 digest using the domain separator and signedOrder - // hash through openzepplin helper. - bytes32 digest = MessageHashUtils.toTypedDataHash(_domainSeparator(), signedOrderHash); - - // Recover the signer address from the digest and signature. - // Pass in R and VS from compact signature (ERC2098). - address recoveredSigner = ECDSA.recover(digest, bytes32(signature[0:32]), bytes32(signature[32:64])); - - // Revert if the signer is not active. - // This also reverts if the digest constructed on serverside is incorrect. - if (!_signers[recoveredSigner].active) { - revert SignerNotActive(recoveredSigner); - } - - // Pre hook validation completes and passes with no reverts, return valid. - authorizedOrderMagicValue = ZoneInterface.authorizeOrder.selector; - } - - /** - * @notice Validates a fulfilment execution. - * - * @dev This function is called by Seaport whenever any extraData is - * provided by the caller, after tokens have been transferred. Note - * that this function omits redundant validation that is already performed - * in the authorizeOrder function which is called by Seaport before - * tokens are transferred. - * - * @param zoneParameters The zone parameters containing data related to - * the fulfilment execution. - * @return validOrderMagicValue A magic value indicating if the order is - * currently valid. - */ - function validateOrder(ZoneParameters calldata zoneParameters) - external - override - returns (bytes4 validOrderMagicValue) - { - // Revert if the caller is not the Seaport contract. - if (msg.sender != _SEAPORT) { - revert CallerNotSeaport(); - } - - // Put the extraData and orderHash on the stack for cheaper access. - bytes calldata extraData = zoneParameters.extraData; - - // extraData bytes 93-end: context (optional, variable length). - // extraData length is guaranteed by the authorizeOrder function. - bytes calldata context = extraData[93:]; - - // Validate supported substandards - after hook validation. - _validateSubstandards(context, zoneParameters, false); - - // Pre hook validation completes and passes with no reverts, return valid. - validOrderMagicValue = ZoneInterface.validateOrder.selector; - } - - /** - * @notice ERC-165 interface support. - * - * @param interfaceId The interface ID to check for support. - */ - function supportsInterface(bytes4 interfaceId) - public - view - override(ERC165, ZoneInterface, AccessControlEnumerable) - returns (bool) - { - return interfaceId == type(ZoneInterface).interfaceId || interfaceId == type(SIP5Interface).interfaceId - || interfaceId == type(SIP7Interface).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev Internal view function to get the EIP-712 domain separator. If the - * chainId matches the chainId set on deployment, the cached domain - * separator will be returned; otherwise, it will be derived from - * scratch. - * - * @return The domain separator. - */ - function _domainSeparator() internal view returns (bytes32) { - return block.chainid == _CHAIN_ID ? _DOMAIN_SEPARATOR : _deriveDomainSeparator(); - } - - /** - * @dev Internal view function to derive the EIP-712 domain separator. - * - * @return domainSeparator The derived domain separator. - */ - function _deriveDomainSeparator() internal view returns (bytes32 domainSeparator) { - return keccak256(abi.encode(_EIP_712_DOMAIN_TYPEHASH, _NAME_HASH, _VERSION_HASH, block.chainid, address(this))); - } - - /** - * @dev Get the supported substandards of the contract. - * - * @return substandards Array of substandards supported. - */ - function _getSupportedSubstandards() internal pure returns (uint256[] memory substandards) { - // support substandards 1, 3, 4, 6, 7 and 8 - substandards = new uint256[](6); - substandards[0] = 1; - substandards[1] = 3; - substandards[2] = 4; - substandards[3] = 6; - substandards[4] = 7; - substandards[5] = 8; - } - - /** - * @dev Derive the signedOrder hash from the orderHash and expiration. - * - * @param fulfiller The expected fulfiller address. - * @param expiration The signature expiration timestamp. - * @param orderHash The order hash. - * @param context The optional variable-length context. - * @return signedOrderHash The signedOrder hash. - */ - function _deriveSignedOrderHash(address fulfiller, uint64 expiration, bytes32 orderHash, bytes calldata context) - internal - pure - returns (bytes32 signedOrderHash) - { - // Derive the signed order hash. - signedOrderHash = - keccak256(abi.encode(_SIGNED_ORDER_TYPEHASH, fulfiller, expiration, orderHash, keccak256(context))); - } - - /** - * @dev Validate substandards 1, 3, 4, 6, 7 and 8 based on context. - * - * @param context Bytes payload of context. - * @param zoneParameters The zone parameters. - * @param before Whether validation is occurring in before or after hook. - */ - function _validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - internal - { - uint256 startIndex = 0; - uint256 contextLength = context.length; - - // The ImmutableSignedZoneV3 contract enforces at least - // one of the supported substandards is present in the context. - if (contextLength == 0) { - revert InvalidExtraData("invalid context, no substandards present", zoneParameters.orderHash); - } - - // Each _validateSubstandard* function returns the length of the substandard - // segment (0 if the substandard was not matched). - startIndex = _validateSubstandard1(context[startIndex:], zoneParameters, before) + startIndex; - - if (startIndex == contextLength) return; - startIndex = _validateSubstandard3(context[startIndex:], zoneParameters, before) + startIndex; - - if (startIndex == contextLength) return; - startIndex = _validateSubstandard4(context[startIndex:], zoneParameters, before) + startIndex; - - if (startIndex == contextLength) return; - startIndex = _validateSubstandard6(context[startIndex:], zoneParameters, before) + startIndex; - - if (startIndex == contextLength) return; - startIndex = _validateSubstandard7(context[startIndex:], zoneParameters, before) + startIndex; - - if (startIndex == contextLength) return; - startIndex = _validateSubstandard8(context[startIndex:], zoneParameters, before) + startIndex; - - if (startIndex != contextLength) { - revert InvalidExtraData("invalid context, unexpected context length", zoneParameters.orderHash); - } - } - - /** - * @dev Validates substandard 1. This substandard is used to validate that the server's - * specified first received item identifier matches the actual first received - * item identifier. - * - * @param context Bytes payload of context, 0 indexed to start of substandard segment. - * @param zoneParameters The zone parameters. - * @param before Whether validation is occurring in before or after hook. - * @return Length of substandard segment. - */ - function _validateSubstandard1(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - internal - pure - returns (uint256) - { - if (uint8(context[0]) != 1) { - return 0; - } - - if (context.length < 33) { - revert InvalidExtraData("invalid substandard 1 data length", zoneParameters.orderHash); - } - - // Only perform validation in before hook. - if (before) { - // zoneParameters.consideration.length >= 1 is guaranteed by the authorizeOrder function. - if (uint256(bytes32(context[1:33])) != zoneParameters.consideration[0].identifier) { - revert Substandard1Violation( - zoneParameters.orderHash, - zoneParameters.consideration[0].identifier, - uint256(bytes32(context[1:33])) - ); - } - } - - return 33; - } - - /** - * @dev Validates substandard 3. This substandard is used to validate that the server's - * specified received items matches the actual received items. This substandard - * should be used when the server is able to accurately determine the received items - * for an exact fulfilment scenario. This substandard should NOT be used for fulfilments - * where the received items cannot be accurately known in advance, as is the case in - * best-efforts partial fulfilment scenarios. - * - * @param context Bytes payload of context, 0 indexed to start of substandard segment. - * @param zoneParameters The zone parameters. - * @param before Whether validation is occurring in before or after hook. - * @return Length of substandard segment. - */ - function _validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - internal - pure - returns (uint256) - { - if (uint8(context[0]) != 3) { - return 0; - } - - if (context.length < 33) { - revert InvalidExtraData("invalid substandard 3 data length", zoneParameters.orderHash); - } - - // Only perform validation in before hook. - if (before) { - if (_deriveReceivedItemsHash(zoneParameters.consideration, 1, 1) != bytes32(context[1:33])) { - revert Substandard3Violation(zoneParameters.orderHash); - } - } - - return 33; - } - - /** - * @dev Validates substandard 4. This substandard is used to validate that the server's - * specified orders that must be bundled with the fulfilment are present. This is useful - * for scenarios where the fulfiller desires a bundled fulfilment to revert if part of - * bundle is not available for fulfilment. - * - * @param context Bytes payload of context, 0 indexed to start of substandard segment. - * @param zoneParameters The zone parameters. - * @param before Whether validation is occurring in before or after hook. - * @return Length of substandard segment. - */ - function _validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - internal - pure - returns (uint256) - { - if (uint8(context[0]) != 4) { - return 0; - } - - // substandard ID + array offset + array length. - if (context.length < 65) { - revert InvalidExtraData("invalid substandard 4 data length", zoneParameters.orderHash); - } - - uint256 expectedOrderHashesSize = uint256(bytes32(context[33:65])); - uint256 substandardIndexEnd = 64 + (expectedOrderHashesSize * 32); - - // substandard ID + array offset + array length + array data. - if (context.length < substandardIndexEnd + 1) { - revert InvalidExtraData("invalid substandard 4 data length", zoneParameters.orderHash); - } - - // Only perform validation in after hook. Note that zoneParameters.orderHashes is only fully - // populated in the after hook (validateOrder call). - if (!before) { - bytes32[] memory expectedOrderHashes = abi.decode(context[1:substandardIndexEnd + 1], (bytes32[])); - - // revert if any order hashes in substandard data are not present in zoneParameters.orderHashes. - if (!_bytes32ArrayIncludes(zoneParameters.orderHashes, expectedOrderHashes)) { - revert Substandard4Violation(zoneParameters.orderHashes, expectedOrderHashes, zoneParameters.orderHash); - } - } - - return substandardIndexEnd + 1; - } - - /** - * @dev Validates substandard 6. This substandard a variation on substandard 3 to support - * that supports fulfilments where server cannot accurately determine expected received - * items in advance, as is the case in best-efforts partial fulfilment scenarios. - * - * @param context Bytes payload of context, 0 indexed to start of substandard segment. - * @param zoneParameters The zone parameters. - * @param before Whether validation is occurring in before or after hook. - * @return Length of substandard segment. - */ - function _validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - internal - pure - returns (uint256) - { - if (uint8(context[0]) != 6) { - return 0; - } - - if (context.length < 65) { - revert InvalidExtraData("invalid substandard 6 data length", zoneParameters.orderHash); - } - - // Only perform validation in before hook. - if (before) { - // The first 32 bytes are the original first offer item amount. - uint256 originalFirstOfferItemAmount = uint256(bytes32(context[1:33])); - // The next 32 bytes are the hash of the received items that were expected - // derived based on an assumption of full fulfilment (i.e. numerator = denominator = 1). - bytes32 expectedReceivedItemsHash = bytes32(context[33:65]); - - // To support partial fulfilment scenarios, we must scale the actual received item amounts - // to match the expected received items hash based on full fulfilment (i.e. numerator = denominator = 1). - // - // actualAmount = originalAmount * numerator / denominator - // originalAmount = actualAmount * denominator / numerator - // - // The numerator and denominator values are inferred from the actual and original (extracted - // from context) amounts of the first offer item. - if ( - _deriveReceivedItemsHash( - zoneParameters.consideration, originalFirstOfferItemAmount, zoneParameters.offer[0].amount - ) != expectedReceivedItemsHash - ) { - revert Substandard6Violation( - zoneParameters.offer[0].amount, originalFirstOfferItemAmount, zoneParameters.orderHash - ); - } - } - - return 65; - } - - /** - * @dev Validates substandard 7. This substandard is a superset of substandard 1. It - * additionally calls the creator token standard transfer validator before and - * after token transfer hooks for a specified operator. - * - * @param context Bytes payload of context, 0 indexed to start of substandard segment. - * @param zoneParameters The zone parameters. - * @param before Whether validation is occurring in before or after hook. - * @return Length of substandard segment. - */ - function _validateSubstandard7(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - internal - returns (uint256) - { - if (uint8(context[0]) != 7) { - return 0; - } - - if (context.length < 73) { - revert InvalidExtraData("invalid substandard 7 data length", zoneParameters.orderHash); - } - - // Only perform identifier validation in before hook. - if (before) { - // zoneParameters.consideration.length >= 1 is guaranteed by the authorizeOrder function. - if (uint256(bytes32(context[1:33])) != zoneParameters.consideration[0].identifier) { - revert Substandard7IdentifierViolation( - zoneParameters.orderHash, - zoneParameters.consideration[0].identifier, - uint256(bytes32(context[1:33])) - ); - } - } - - // This zone assumes that either the first consideration item or the first offer item is an ERC721 or ERC1155 token. - // slither-disable-next-line uninitialized-local - address token; - // zoneParameters.consideration.length >= 1 is guaranteed by the authorizeOrder function. - if (uint256(zoneParameters.consideration[0].itemType) > 1) { - token = zoneParameters.consideration[0].token; - // zoneParameters.offer.length >= 1 is guaranteed by the authorizeOrder function. - } else if (uint256(zoneParameters.offer[0].itemType) > 1) { - token = zoneParameters.offer[0].token; - } else { - revert Substandard7UnexpectedItemTypeViolation(zoneParameters.orderHash); - } - - address registry = address(bytes20(context[33:53])); - - if (before) { - ITransferValidator(registry).beforeAuthorizedTransfer(address(bytes20(context[53:73])), token); - } else { - ITransferValidator(registry).afterAuthorizedTransfer(token); - } - - return 73; - } - - /** - * @dev Validates substandard 8. This substandard is a superset of substandard 1. It - * additionally calls the creator token standard transfer validator before and - * after token transfer hooks. - * - * @param context Bytes payload of context, 0 indexed to start of substandard segment. - * @param zoneParameters The zone parameters. - * @param before Whether validation is occurring in before or after hook. - * @return Length of substandard segment. - */ - function _validateSubstandard8(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - internal - returns (uint256) - { - if (uint8(context[0]) != 8) { - return 0; - } - - if (context.length < 53) { - revert InvalidExtraData("invalid substandard 8 data length", zoneParameters.orderHash); - } - - // Only perform identifier validation in before hook. - if (before) { - if (uint256(bytes32(context[1:33])) != zoneParameters.consideration[0].identifier) { - revert Substandard8IdentifierViolation( - zoneParameters.orderHash, - zoneParameters.consideration[0].identifier, - uint256(bytes32(context[1:33])) - ); - } - } - - // This zone assumes that either the first consideration item or the first offer item is an ERC721 or ERC1155 token. - // slither-disable-next-line uninitialized-local - address token; - // slither-disable-next-line uninitialized-local - uint256 tokenId; - // zoneParameters.consideration.length >= 1 is guaranteed by the authorizeOrder function. - if (uint256(zoneParameters.consideration[0].itemType) > 1) { - token = zoneParameters.consideration[0].token; - tokenId = zoneParameters.consideration[0].identifier; - // zoneParameters.offer.length >= 1 is guaranteed by the authorizeOrder function. - } else if (uint256(zoneParameters.offer[0].itemType) > 1) { - token = zoneParameters.offer[0].token; - tokenId = zoneParameters.offer[0].identifier; - } else { - revert Substandard8UnexpectedItemTypeViolation(zoneParameters.orderHash); - } - - address registry = address(bytes20(context[33:53])); - - if (before) { - ITransferValidator(registry).beforeAuthorizedTransfer(token, tokenId); - } else { - ITransferValidator(registry).afterAuthorizedTransfer(token, tokenId); - } - - return 53; - } - - /** - * @dev Derive the received items hash based on received item array. - * - * @param receivedItems Actual received item array. - * @param scalingFactorNumerator Scaling factor numerator. - * @param scalingFactorDenominator Scaling factor denominator. - * @return receivedItemsHash Hash of received items. - */ - function _deriveReceivedItemsHash( - ReceivedItem[] calldata receivedItems, - uint256 scalingFactorNumerator, - uint256 scalingFactorDenominator - ) internal pure returns (bytes32) { - uint256 numberOfItems = receivedItems.length; - bytes memory receivedItemsHash = new bytes(0); // Explicitly initialize to empty bytes - - for (uint256 i; i < numberOfItems; i++) { - receivedItemsHash = abi.encodePacked( - receivedItemsHash, - receivedItems[i].itemType, - receivedItems[i].token, - receivedItems[i].identifier, - Math.mulDiv(receivedItems[i].amount, scalingFactorNumerator, scalingFactorDenominator), - receivedItems[i].recipient - ); - } - - return keccak256(receivedItemsHash); - } - - /** - * @dev Helper function to check if every element of values exists in sourceArray - * optimised for performance checking arrays sized 0-15. - * - * @param sourceArray Source array. - * @param values Values array. - * @return True if all elements in values exist in sourceArray. - */ - function _bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) - internal - pure - returns (bool) - { - // cache the length in memory for loop optimisation - uint256 sourceArraySize = sourceArray.length; - uint256 valuesSize = values.length; - - // we can assume all items are unique - // therefore if values is bigger than superset sourceArray, return false - if (valuesSize > sourceArraySize) { - return false; - } - - // Iterate through each element and compare them - for (uint256 i = 0; i < valuesSize;) { - bool found = false; - bytes32 item = values[i]; - for (uint256 j = 0; j < sourceArraySize;) { - if (item == sourceArray[j]) { - // if item from values is in sourceArray, break - found = true; - break; - } - unchecked { - j++; - } - } - if (!found) { - // if any item from values is not found in sourceArray, return false - return false; - } - unchecked { - i++; - } - } - - // All elements from values exist in sourceArray - return true; - } -} diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/README.md b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/README.md deleted file mode 100644 index 0e9ea585..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# Immutable Signed Zone (v3) - -The Immutable Signed Zone contract is a [Seaport Zone](https://docs.opensea.io/docs/seaport-hooks#zone-hooks) that implements [SIP-7 (Interface for Server-Signed Orders)](https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md) with support for [substandards](https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md#substandards) 1, 3, 4, 6, 7 and 8. - -This zone is used by Immutable to enable: - -* Enforcement of protocol, royalty and ecosystem fees -* Off-chain order cancellation - -# Status - -Contract threat models and audits: - -| Description | Date | Version Audited | Link to Report | -| -------------- | ---------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| Threat Model | 2024-04-15 | V2 | [202404-threat-model-immutable-signed-zone-v2.md](../../../../../../audits/trading/202404-threat-model-immutable-signed-zone-v2.md) | -| Internal Audit | 2024-05-02 | V2 | [202405-internal-audit-immutable-signed-zone-v2.pdf](../../../../../../audits/trading/202405-internal-audit-immutable-signed-zone-v2.pdf) | - -## ImmutableSignedZoneV2 - -| Location | Date | Version Deployed | Address | -| ----------------------- | ------------ | ---------------- | ------- | -| Immutable zkEVM Testnet | Not deployed | - | - | -| Immutable zkEVM Mainnet | Not deployed | - | - | - -## Architecture - -The trading system on the Immutable platform is shown in the diagram below. - -```mermaid -flowchart LR - client[Client] <-- 1. POST .../fulfillment-data ---> ob[Immutable Off-Chain\nOrderbook] - client -- 2. fulfillAdvancedOrder ---> seaport[ImmutableSeaport.sol] - seaport -- 3. authorizeOrder --> Zone - seaport -- 4a. transferFrom --> erc20[IERC20.sol] - seaport -- 4b. transferFrom --> erc721[IERC721.sol] - seaport -- 4c. safeTransferFrom --> erc1155[IERC1155.sol] - seaport -- 5. validateOrder --> Zone - subgraph Zone - direction TB - zone[ImmutableSignedZoneV3.sol] --> AccessControlEnumerable.sol - end -``` - -The sequence of events is as follows: - -1. The client makes a HTTP `POST .../fulfillment-data` request to the Immutable Orderbook, which will construct and sign an `extraData` payload to return to the client -2. The client calls `fulfillAdvancedOrder` or `fulfillAvailableAdvancedOrders` on `ImmutableSeaport.sol` to fulfill an order -3. `ImmutableSeaport.sol` executes the fufilment by transferring items between parties -4. `ImmutableSeaport.sol` calls `authorizeOrder` on `ImmutableSignedZoneV3.sol`, passing it the fulfilment execution details as well as the `extraData` parameter -5. `ImmutableSignedZoneV3.sol` authorizes the fulfilment execution details using the `extraData` payload, reverting if expectations are not met -6. `ImmutableSeaport.sol` calls `validateOrder` on `ImmutableSignedZoneV3.sol`, passing it the fulfilment execution details as well as the `extraData` parameter -7. `ImmutableSignedZoneV3.sol` validates the fulfilment execution details using the `extraData` payload, reverting if expectations are not met - -## Differences compared to ImmutableSignedZone (v2) - -The contract was developed based on ImmutableSignedZoneV2, with the addition of: - - Support for the Seaport 1.6 Zone interface `authorizeOrder` function - - SIP-7 Substandard 1 - - SIP-7 Substandard 7 -- SIP-7 Substandard 8 diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/ZoneAccessControl.sol b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/ZoneAccessControl.sol deleted file mode 100644 index 25cdeb11..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/ZoneAccessControl.sol +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.20; - -import {AccessControl} from "openzeppelin-contracts-5.0.2/access/AccessControl.sol"; -import {IAccessControl} from "openzeppelin-contracts-5.0.2/access/IAccessControl.sol"; -import {AccessControlEnumerable} from "openzeppelin-contracts-5.0.2/access/extensions/AccessControlEnumerable.sol"; -import {ZoneAccessControlEventsAndErrors} from "./interfaces/ZoneAccessControlEventsAndErrors.sol"; - -/** - * @notice ZoneAccessControl encapsulates access control functionality for the zone. - */ -abstract contract ZoneAccessControl is AccessControlEnumerable, ZoneAccessControlEventsAndErrors { - /// @dev Zone manager manages the zone. - // forge-lint: disable-next-line(unsafe-typecast) - bytes32 public constant ZONE_MANAGER_ROLE = bytes32("ZONE_MANAGER"); - - /** - * @notice Constructor to setup initial default admin. - * - * @param owner The address to assign the DEFAULT_ADMIN_ROLE. - */ - constructor(address owner) { - require(owner != address(0), "ZoneAccessControl: owner is the zero address"); - // Grant admin role to the specified owner. - _grantRole(DEFAULT_ADMIN_ROLE, owner); - } - - /** - * @inheritdoc AccessControl - */ - function revokeRole(bytes32 role, address account) - public - override(AccessControl, IAccessControl) - onlyRole(getRoleAdmin(role)) - { - if (role == DEFAULT_ADMIN_ROLE && super.getRoleMemberCount(DEFAULT_ADMIN_ROLE) == 1) { - revert LastDefaultAdminRole(account); - } - - super.revokeRole(role, account); - } - - /** - * @inheritdoc AccessControl - */ - function renounceRole(bytes32 role, address callerConfirmation) public override(AccessControl, IAccessControl) { - if (role == DEFAULT_ADMIN_ROLE && super.getRoleMemberCount(DEFAULT_ADMIN_ROLE) == 1) { - revert LastDefaultAdminRole(callerConfirmation); - } - - super.renounceRole(role, callerConfirmation); - } -} diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/ITransferValidator.sol b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/ITransferValidator.sol deleted file mode 100644 index a109426b..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/ITransferValidator.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -interface ITransferValidator { - function applyCollectionTransferPolicy(address caller, address from, address to) external view; - function validateTransfer(address caller, address from, address to) external view; - function validateTransfer(address caller, address from, address to, uint256 tokenId) external view; - function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) external; - - function beforeAuthorizedTransfer(address operator, address token, uint256 tokenId) external; - function afterAuthorizedTransfer(address token, uint256 tokenId) external; - function beforeAuthorizedTransfer(address operator, address token) external; - function afterAuthorizedTransfer(address token) external; - function beforeAuthorizedTransfer(address token, uint256 tokenId) external; - function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) external; - function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external; -} diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP5EventsAndErrors.sol b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP5EventsAndErrors.sol deleted file mode 100644 index 97334d48..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP5EventsAndErrors.sol +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 - -pragma solidity ^0.8.17; - -/** - * @notice SIP5EventsAndErrors contains errors and events - * related to zone interaction as specified in the SIP-5. - */ -interface SIP5EventsAndErrors { - /** - * @dev An event that is emitted when a SIP-5 compatible contract is deployed. - */ - event SeaportCompatibleContractDeployed(); -} diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP5Interface.sol b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP5Interface.sol deleted file mode 100644 index a7f82219..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP5Interface.sol +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 - -pragma solidity ^0.8.17; - -import {Schema} from "seaport-types-16/src/lib/ConsiderationStructs.sol"; -import {SIP5EventsAndErrors} from "./SIP5EventsAndErrors.sol"; - -/** - * @dev SIP-5: Contract Metadata Interface for Seaport Contracts - * https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-5.md - */ -// This contract name re-use is OK because the SIP5Interface is an interface and not a deployable contract. -// slither-disable-next-line name-reused -interface SIP5Interface is SIP5EventsAndErrors { - /** - * @dev Returns Seaport metadata for this contract, returning the - * contract name and supported schemas. - * - * @return name The contract name - * @return schemas The supported SIPs - */ - function getSeaportMetadata() external view returns (string memory name, Schema[] memory schemas); -} diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP6EventsAndErrors.sol b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP6EventsAndErrors.sol deleted file mode 100644 index d313057e..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP6EventsAndErrors.sol +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; - -/** - * @notice SIP6EventsAndErrors contains errors and events - * related to zone interaction as specified in the SIP-6. - */ -// This contract name re-use is OK because the SIP6EventsAndErrors is an interface and not a deployable contract. -// slither-disable-next-line name-reused -interface SIP6EventsAndErrors { - /** - * @dev Revert with an error if SIP-6 version byte is not supported. - */ - error UnsupportedExtraDataVersion(uint8 version); -} diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP6Interface.sol b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP6Interface.sol deleted file mode 100644 index 094e56ea..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP6Interface.sol +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; - -import {SIP6EventsAndErrors} from "./SIP6EventsAndErrors.sol"; - -/** - * @dev SIP-6: Multi-Zone ExtraData - * https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-6.md - */ -interface SIP6Interface is SIP6EventsAndErrors {} diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7EventsAndErrors.sol b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7EventsAndErrors.sol deleted file mode 100644 index 31138850..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7EventsAndErrors.sol +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 - -pragma solidity ^0.8.17; - -/** - * @notice SIP7EventsAndErrors contains errors and events - * related to zone interaction as specified in the SIP-7. - */ -// This contract name re-use is OK because the SIP7EventsAndErrors is an interface and not a deployable contract. -// slither-disable-next-line name-reused -interface SIP7EventsAndErrors { - /** - * @dev Emit an event when a new signer is added. - */ - event SignerAdded(address signer); - - /** - * @dev Emit an event when a signer is removed. - */ - event SignerRemoved(address signer); - - /** - * @dev Revert with an error if trying to add a signer that is - * already active. - */ - error SignerAlreadyActive(address signer); - - /** - * @dev Revert with an error if trying to remove a signer that is - * not active. - */ - error SignerNotActive(address signer); - - /** - * @dev Revert with an error if a new signer is the zero address. - */ - error SignerCannotBeZeroAddress(); - - /** - * @dev Revert with an error if a removed signer is trying to be - * reauthorized. - */ - error SignerCannotBeReauthorized(address signer); - - /** - * @dev Revert with an error when the signature has expired. - */ - error SignatureExpired(uint256 currentTimestamp, uint256 expiration, bytes32 orderHash); - - /** - * @dev Revert with an error if the fulfiller does not match. - */ - error InvalidFulfiller(address expectedFulfiller, address actualFulfiller, bytes32 orderHash); - - /** - * @dev Revert with an error if supplied order extraData is invalid - * or improperly formatted. - */ - error InvalidExtraData(string reason, bytes32 orderHash); - - /** - * @dev Revert with an error if the Seaport address is the zero address. - * This is a custom error that is not part of the SIP-7 spec. - */ - error SeaportCannotBeZeroAddress(); - - /** - * @dev Revert with an error if the caller is not the Seaport contract. - * This is a custom error that is not part of the SIP-7 spec. - */ - error CallerNotSeaport(); - - /** - * @dev Revert with an error if no spent items are provided. - * This is a custom error that is not part of the SIP-7 spec. - */ - error NoSpentItems(bytes32 orderHash); - - /** - * @dev Revert with an error if no received items are provided. - * This is a custom error that is not part of the SIP-7 spec. - */ - error NoReceivedItems(bytes32 orderHash); - - /** - * @dev Revert with an error if substandard 1 validation fails. - * This is a custom error that is not part of the SIP-7 spec. - */ - error Substandard1Violation(bytes32 orderHash, uint256 actualIdentifier, uint256 expectedIdentifier); - - /** - * @dev Revert with an error if substandard 3 validation fails. - * This is a custom error that is not part of the SIP-7 spec. - */ - error Substandard3Violation(bytes32 orderHash); - - /** - * @dev Revert with an error if substandard 4 validation fails. - * This is a custom error that is not part of the SIP-7 spec. - */ - error Substandard4Violation(bytes32[] actualOrderHashes, bytes32[] expectedOrderHashes, bytes32 orderHash); - - /** - * @dev Revert with an error if substandard 6 validation fails. - * This is a custom error that is not part of the SIP-7 spec. - */ - error Substandard6Violation(uint256 actualSpentItemAmount, uint256 originalSpentItemAmount, bytes32 orderHash); - - /** - * @dev Revert with an error if substandard 7 identifier validation fails. - * This is a custom error that is not part of the SIP-7 spec. - */ - error Substandard7IdentifierViolation(bytes32 orderHash, uint256 actualIdentifier, uint256 expectedIdentifier); - - /** - * @dev Revert with an error if substandard 7 item type expectations are not met. - * This is a custom error that is not part of the SIP-7 spec. - */ - error Substandard7UnexpectedItemTypeViolation(bytes32 orderHash); - - /** - * @dev Revert with an error if substandard 8 identifier validation fails. - * This is a custom error that is not part of the SIP-7 spec. - */ - error Substandard8IdentifierViolation(bytes32 orderHash, uint256 actualIdentifier, uint256 expectedIdentifier); - - /** - * @dev Revert with an error if substandard 8 item type expectations are not met. - * This is a custom error that is not part of the SIP-7 spec. - */ - error Substandard8UnexpectedItemTypeViolation(bytes32 orderHash); -} diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7Interface.sol b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7Interface.sol deleted file mode 100644 index a026e19a..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7Interface.sol +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 - -pragma solidity ^0.8.17; - -import {SIP7EventsAndErrors} from "./SIP7EventsAndErrors.sol"; - -/** - * @title SIP7Interface - * @author ryanio, Immutable - * @notice ImmutableSignedZone is an implementation of SIP-7 that requires orders - * to be signed by an approved signer. - * https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md - * - */ -// This contract name re-use is OK because the SIP7Interface is an interface and not a deployable contract. -// slither-disable-next-line name-reused -interface SIP7Interface is SIP7EventsAndErrors { - /** - * @dev The struct for storing signer info. - */ - struct SignerInfo { - /// @dev If the signer is currently active. - bool active; - /// @dev If the signer has been active before. - bool previouslyActive; - } - - /** - * @notice Add a new signer to the zone. - * - * @param signer The new signer address to add. - */ - function addSigner(address signer) external; - - /** - * @notice Remove an active signer from the zone. - * - * @param signer The signer address to remove. - */ - function removeSigner(address signer) external; - - /** - * @notice Update the API endpoint returned by this zone. - * - * @param newApiEndpoint The new API endpoint. - */ - function updateAPIEndpoint(string calldata newApiEndpoint) external; - - /** - * @notice Update the documentation URI returned by this zone. - * - * @param newDocumentationURI The new documentation URI. - */ - function updateDocumentationURI(string calldata newDocumentationURI) external; - - /** - * @notice Returns signing information about the zone. - * - * @return domainSeparator The domain separator used for signing. - * @return apiEndpoint The API endpoint to get signatures for orders - * using this zone. - */ - function sip7Information() - external - view - returns ( - bytes32 domainSeparator, - string memory apiEndpoint, - uint256[] memory substandards, - string memory documentationURI - ); -} diff --git a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/ZoneAccessControlEventsAndErrors.sol b/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/ZoneAccessControlEventsAndErrors.sol deleted file mode 100644 index 9dfd38ca..00000000 --- a/contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/ZoneAccessControlEventsAndErrors.sol +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; - -/** - * @notice ZoneAccessControlEventsAndErrors contains errors and events - * related to zone access control. - */ -interface ZoneAccessControlEventsAndErrors { - /** - * @dev Revert with an error if revoking last DEFAULT_ADMIN_ROLE. - */ - error LastDefaultAdminRole(address account); -} From aae01e4d7206672fdf56b28b49ef66ca10ac430b Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 10:46:14 +1000 Subject: [PATCH 02/11] Revert changes --- contracts/staking/StakeHolderNative.sol | 7 +++---- contracts/staking/StakeHolderWIMX.sol | 14 ++++++-------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/contracts/staking/StakeHolderNative.sol b/contracts/staking/StakeHolderNative.sol index 55fee4c1..adf2a1a4 100644 --- a/contracts/staking/StakeHolderNative.sol +++ b/contracts/staking/StakeHolderNative.sol @@ -1,4 +1,4 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2025 +// Copyright (c) Immutable Pty Ltd 2018 - 2026 // SPDX-License-Identifier: Apache 2 pragma solidity >=0.8.19 <0.8.29; @@ -36,8 +36,8 @@ contract StakeHolderNative is StakeHolderBase { // Look for revert reason and bubble it up if present. // Revert reasons should contain an error selector, which is four bytes long. if (returndata.length >= 4) { - // solhint-disable-next-line no-inline-assembly - assembly { + assembly ("memory-safe") + { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } @@ -59,7 +59,6 @@ contract StakeHolderNative is StakeHolderBase { /// @notice storage gap for additional variables for upgrades // slither-disable-start unused-state - // solhint-disable-next-line var-name-mixedcase uint256[50] private __StakeHolderNativeGap; // slither-disable-end unused-state } diff --git a/contracts/staking/StakeHolderWIMX.sol b/contracts/staking/StakeHolderWIMX.sol index 25396ecc..e4c30f95 100644 --- a/contracts/staking/StakeHolderWIMX.sol +++ b/contracts/staking/StakeHolderWIMX.sol @@ -1,4 +1,4 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2025 +// Copyright (c) Immutable Pty Ltd 2018 - 2026 // SPDX-License-Identifier: Apache 2 pragma solidity >=0.8.19 <0.8.29; @@ -21,12 +21,11 @@ contract StakeHolderWIMX is StakeHolderNative { * @param _distributeAdmin the address to grant `DISTRIBUTE_ROLE` to * @param _wIMXToken The address of the WIMX contract. */ - function initialize( - address _roleAdmin, - address _upgradeAdmin, - address _distributeAdmin, - address _wIMXToken - ) public initializer { + function initialize(address _roleAdmin, address _upgradeAdmin, address _distributeAdmin, address _wIMXToken + ) + public + initializer + { __StakeHolderBase_init(_roleAdmin, _upgradeAdmin, _distributeAdmin); wIMX = IWIMX(_wIMXToken); } @@ -65,7 +64,6 @@ contract StakeHolderWIMX is StakeHolderNative { /// @notice storage gap for additional variables for upgrades // slither-disable-start unused-state - // solhint-disable-next-line var-name-mixedcase uint256[50] private __StakeHolderWIMXGap; // slither-disable-end unused-state } From b34a7ce8b60f3d8c29d4b8e378ebb190fcf9ce2a Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 10:47:19 +1000 Subject: [PATCH 03/11] Fix formatting --- contracts/staking/StakeHolderNative.sol | 3 +-- contracts/staking/StakeHolderWIMX.sol | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/contracts/staking/StakeHolderNative.sol b/contracts/staking/StakeHolderNative.sol index adf2a1a4..b7c33078 100644 --- a/contracts/staking/StakeHolderNative.sol +++ b/contracts/staking/StakeHolderNative.sol @@ -36,8 +36,7 @@ contract StakeHolderNative is StakeHolderBase { // Look for revert reason and bubble it up if present. // Revert reasons should contain an error selector, which is four bytes long. if (returndata.length >= 4) { - assembly ("memory-safe") - { + assembly ("memory-safe") { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } diff --git a/contracts/staking/StakeHolderWIMX.sol b/contracts/staking/StakeHolderWIMX.sol index e4c30f95..1b92ac6f 100644 --- a/contracts/staking/StakeHolderWIMX.sol +++ b/contracts/staking/StakeHolderWIMX.sol @@ -21,10 +21,9 @@ contract StakeHolderWIMX is StakeHolderNative { * @param _distributeAdmin the address to grant `DISTRIBUTE_ROLE` to * @param _wIMXToken The address of the WIMX contract. */ - function initialize(address _roleAdmin, address _upgradeAdmin, address _distributeAdmin, address _wIMXToken - ) - public - initializer + function initialize(address _roleAdmin, address _upgradeAdmin, address _distributeAdmin, address _wIMXToken) + public + initializer { __StakeHolderBase_init(_roleAdmin, _upgradeAdmin, _distributeAdmin); wIMX = IWIMX(_wIMXToken); From caf5ae04aafbf19b1e594100cd65d8b538f41b1b Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 10:48:17 +1000 Subject: [PATCH 04/11] Remove test code and scripts associated with remove contracts --- .../ImmutableERC721ByQuantityPerf.t.sol | 42 - ...ImmutableERC721ByQuantityPerfPrefill.t.sol | 16 - script/games/gems/DeployGemGame.sol | 119 - script/staking/StakeHolderScriptERC20.t.sol | 387 ---- .../seaport16/DeployConduitController.s.sol | 90 - .../seaport16/DeployImmutableSeaport.s.sol | 59 - .../DeployImmutableSignedZoneV3.s.sol | 72 - script/trading/seaport16/README.md | 20 - script/trading/seaport16/deploy.sh | 104 - ...listImmutableERC721TransferApprovals.t.sol | 6 +- .../OperatorAllowlistUpgradeable.t.sol | 6 +- test/deployer/AccessControlledDeployer.t.sol | 92 +- test/deployer/create2/Create2Utils.sol | 22 - .../create2/OwnableCreate2Deployer.t.sol | 181 -- test/games/gems/GemGame.t.sol | 66 - test/staking/StakeHolderBase.t.sol | 25 - test/staking/StakeHolderConfigERC20.t.sol | 35 - test/staking/StakeHolderConfigERC20V2.t.sol | 54 - test/staking/StakeHolderConfigNative.t.sol | 34 - test/staking/StakeHolderInitBase.t.sol | 4 - test/staking/StakeHolderInitERC20.t.sol | 12 - test/staking/StakeHolderInitERC20V2.t.sol | 13 - test/staking/StakeHolderInitNative.t.sol | 12 - .../staking/StakeHolderOperationalERC20.t.sol | 66 - .../StakeHolderOperationalERC20V2.t.sol | 32 - test/staking/StakeHolderTimeDelayERC20.t.sol | 47 - test/staking/StakeHolderUpgradeForkTest.t.sol | 114 - .../erc721/ERC721ConfigByQuantityV1.t.sol | 26 - .../ERC721OperationalByQuantityV1.t.sol | 35 - .../seaport16/ImmutableSeaportBase.t.sol | 69 - .../seaport16/ImmutableSeaportConfig.t.sol | 42 - .../seaport16/ImmutableSeaportHarness.t.sol | 22 - .../ImmutableSeaportOperational.t.sol | 244 -- ...utableSeaportSignedZoneV3Integration.t.sol | 902 -------- .../ImmutableSeaportTestHelper.t.sol | 216 -- test/trading/seaport16/README.md | 10 - .../utils/MockTransferValidator.t.sol | 252 --- .../seaport16/utils/SigningTestHelper.t.sol | 21 - .../v3/IImmutableSignedZoneV3Harness.t.sol | 67 - .../v3/ImmutableSignedZoneV3.t.sol | 1956 ----------------- .../v3/ImmutableSignedZoneV3Harness.t.sol | 107 - .../zones/immutable-signed-zone/v3/README.md | 147 -- 42 files changed, 11 insertions(+), 5835 deletions(-) delete mode 100644 perfTest/token/erc721/ImmutableERC721ByQuantityPerf.t.sol delete mode 100644 perfTest/token/erc721/ImmutableERC721ByQuantityPerfPrefill.t.sol delete mode 100644 script/games/gems/DeployGemGame.sol delete mode 100644 script/staking/StakeHolderScriptERC20.t.sol delete mode 100644 script/trading/seaport16/DeployConduitController.s.sol delete mode 100644 script/trading/seaport16/DeployImmutableSeaport.s.sol delete mode 100644 script/trading/seaport16/DeployImmutableSignedZoneV3.s.sol delete mode 100644 script/trading/seaport16/README.md delete mode 100755 script/trading/seaport16/deploy.sh delete mode 100644 test/deployer/create2/Create2Utils.sol delete mode 100644 test/deployer/create2/OwnableCreate2Deployer.t.sol delete mode 100644 test/games/gems/GemGame.t.sol delete mode 100644 test/staking/StakeHolderConfigERC20.t.sol delete mode 100644 test/staking/StakeHolderConfigERC20V2.t.sol delete mode 100644 test/staking/StakeHolderConfigNative.t.sol delete mode 100644 test/staking/StakeHolderInitERC20.t.sol delete mode 100644 test/staking/StakeHolderInitERC20V2.t.sol delete mode 100644 test/staking/StakeHolderInitNative.t.sol delete mode 100644 test/staking/StakeHolderOperationalERC20.t.sol delete mode 100644 test/staking/StakeHolderOperationalERC20V2.t.sol delete mode 100644 test/staking/StakeHolderTimeDelayERC20.t.sol delete mode 100644 test/staking/StakeHolderUpgradeForkTest.t.sol delete mode 100644 test/token/erc721/ERC721ConfigByQuantityV1.t.sol delete mode 100644 test/token/erc721/ERC721OperationalByQuantityV1.t.sol delete mode 100644 test/trading/seaport16/ImmutableSeaportBase.t.sol delete mode 100644 test/trading/seaport16/ImmutableSeaportConfig.t.sol delete mode 100644 test/trading/seaport16/ImmutableSeaportHarness.t.sol delete mode 100644 test/trading/seaport16/ImmutableSeaportOperational.t.sol delete mode 100644 test/trading/seaport16/ImmutableSeaportSignedZoneV3Integration.t.sol delete mode 100644 test/trading/seaport16/ImmutableSeaportTestHelper.t.sol delete mode 100644 test/trading/seaport16/README.md delete mode 100644 test/trading/seaport16/utils/MockTransferValidator.t.sol delete mode 100644 test/trading/seaport16/utils/SigningTestHelper.t.sol delete mode 100644 test/trading/seaport16/zones/immutable-signed-zone/v3/IImmutableSignedZoneV3Harness.t.sol delete mode 100644 test/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.t.sol delete mode 100644 test/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3Harness.t.sol delete mode 100644 test/trading/seaport16/zones/immutable-signed-zone/v3/README.md diff --git a/perfTest/token/erc721/ImmutableERC721ByQuantityPerf.t.sol b/perfTest/token/erc721/ImmutableERC721ByQuantityPerf.t.sol deleted file mode 100644 index 6c55dc36..00000000 --- a/perfTest/token/erc721/ImmutableERC721ByQuantityPerf.t.sol +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 - -pragma solidity >=0.8.19 <0.8.29; - -import {Test} from "forge-std/Test.sol"; -import {ERC721ByQuantityPerfTest} from "./ERC721ByQuantityPerf.t.sol"; -import {ImmutableERC721} from "../../../contracts/token/erc721/preset/ImmutableERC721.sol"; -import {IImmutableERC721ByQuantity} from "../../../contracts/token/erc721/interfaces/IImmutableERC721ByQuantity.sol"; -import {IImmutableERC721} from "../../../contracts/token/erc721/interfaces/IImmutableERC721.sol"; - -/** - * Contract for ERC 721 by quantity performance tests, for ImmutableERC721.sol (that is, v1). - */ -contract ImmutableERC721ByQuantityPerfTest is ERC721ByQuantityPerfTest { - function setUpStart() public virtual override { - super.setUpStart(); - - ImmutableERC721 immutableERC721 = new ImmutableERC721( - owner, name, symbol, baseURI, contractURI, address(allowlist), feeReceiver, feeNumerator - ); - - // ImmutableERC721 does not implement the interface, and hence must be cast to the - // interface type. - erc721BQ = IImmutableERC721ByQuantity(address(immutableERC721)); - erc721 = IImmutableERC721(address(immutableERC721)); - - vm.prank(owner); - erc721.grantMinterRole(minter); - - // Mint the first NFT to prefillUser1 - vm.recordLogs(); - vm.prank(minter); - erc721BQ.safeMintByQuantity(prefillUser1, 1); - Vm.Log[] memory entries = vm.getRecordedLogs(); - // Expect 1 of - // event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); - assertEq(entries.length, 1, "More logs than expected"); - firstNftId = uint256(entries[0].topics[3]); - //emit log_named_bytes32("First NFT ID", bytes32(firstNftId)); - } -} diff --git a/perfTest/token/erc721/ImmutableERC721ByQuantityPerfPrefill.t.sol b/perfTest/token/erc721/ImmutableERC721ByQuantityPerfPrefill.t.sol deleted file mode 100644 index 2596a006..00000000 --- a/perfTest/token/erc721/ImmutableERC721ByQuantityPerfPrefill.t.sol +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 - -pragma solidity >=0.8.19 <0.8.29; - -import {ImmutableERC721ByQuantityPerfTest} from "./ImmutableERC721ByQuantityPerf.t.sol"; - -/** - * ImmutableERC721ByQuantityPerfTest, but prefilling the contract with data. - */ -contract ImmutableERC721ByQuantityPerfPrefillTest is ImmutableERC721ByQuantityPerfTest { - function setUpStart() public override { - super.setUpStart(); - prefillWithNfts(); - } -} diff --git a/script/games/gems/DeployGemGame.sol b/script/games/gems/DeployGemGame.sol deleted file mode 100644 index ab170b1a..00000000 --- a/script/games/gems/DeployGemGame.sol +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2.0 - -pragma solidity ^0.8.20; - -import {Test} from "forge-std/Test.sol"; -import {GemGame} from "../../../contracts/games/gems/GemGame.sol"; - -/** - * @title IDeployer Interface - * @notice This interface defines the contract responsible for deploying and optionally initializing new contracts - * via a specified deployment method. - * @dev Credit to axelarnetwork https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/blob/main/contracts/interfaces/IDeployer.sol - */ -interface IDeployer { - function deploy(bytes memory bytecode, bytes32 salt) external payable returns (address deployedAddress_); - function deployAndInit(bytes memory bytecode, bytes32 salt, bytes calldata init) - external - payable - returns (address deployedAddress_); - function deployedAddress(bytes calldata bytecode, address sender, bytes32 salt) - external - view - returns (address deployedAddress_); -} - -struct DeploymentArgs { - address signer; - address factory; - string salt; -} - -struct GemGameContractArgs { - address defaultAdmin; - address pauser; - address unpauser; -} - -contract DeployGemGame is Test { - event GemEarned(address indexed account, uint256 timestamp); - - function testDeploy() external { - /// @dev Fork the Immutable zkEVM testnet for this test - string memory rpcURL = "https://rpc.testnet.immutable.com"; - vm.createSelectFork(rpcURL); - - /// @dev These are Immutable zkEVM testnet values where necessary - DeploymentArgs memory deploymentArgs = DeploymentArgs({ - signer: 0xdDA0d9448Ebe3eA43aFecE5Fa6401F5795c19333, - factory: 0x37a59A845Bb6eD2034098af8738fbFFB9D589610, - salt: "salty" - }); - - GemGameContractArgs memory gemGameContractArgs = GemGameContractArgs({ - pauser: makeAddr("pause"), unpauser: makeAddr("unpause"), defaultAdmin: makeAddr("admin") - }); - - // Run deployment against forked testnet - GemGame deployedGemGameContract = _deploy(deploymentArgs, gemGameContractArgs); - - assertEq(true, deployedGemGameContract.hasRole(keccak256("PAUSE"), gemGameContractArgs.pauser)); - assertEq(true, deployedGemGameContract.hasRole(keccak256("UNPAUSE"), gemGameContractArgs.unpauser)); - assertEq( - true, - deployedGemGameContract.hasRole( - deployedGemGameContract.DEFAULT_ADMIN_ROLE(), gemGameContractArgs.defaultAdmin - ) - ); - - // The DEFAULT_ADMIN_ROLE should be revoked from the deployer account - assertEq( - false, deployedGemGameContract.hasRole(deployedGemGameContract.DEFAULT_ADMIN_ROLE(), deploymentArgs.signer) - ); - - // Earn a gem - vm.expectEmit(true, true, false, false); - emit GemEarned(address(this), block.timestamp); - deployedGemGameContract.earnGem(); - } - - function deploy() external { - address signer = vm.envAddress("DEPLOYER_ADDRESS"); - address factory = vm.envAddress("OWNABLE_CREATE3_FACTORY_ADDRESS"); - address defaultAdmin = vm.envAddress("DEFAULT_ADMIN"); - address pauser = vm.envAddress("PAUSER"); - address unpauser = vm.envAddress("UNPAUSER"); - string memory salt = vm.envString("GEM_GAME_SALT"); - - DeploymentArgs memory deploymentArgs = DeploymentArgs({signer: signer, factory: factory, salt: salt}); - - GemGameContractArgs memory gemGameContractArgs = - GemGameContractArgs({defaultAdmin: defaultAdmin, pauser: pauser, unpauser: unpauser}); - - _deploy(deploymentArgs, gemGameContractArgs); - } - - function _deploy(DeploymentArgs memory deploymentArgs, GemGameContractArgs memory gemGameContractArgs) - internal - returns (GemGame gemGameContract) - { - IDeployer ownableCreate3 = IDeployer(deploymentArgs.factory); - - // Create deployment bytecode and encode constructor args - bytes memory deploymentBytecode = abi.encodePacked( - type(GemGame).creationCode, - abi.encode(gemGameContractArgs.defaultAdmin, gemGameContractArgs.pauser, gemGameContractArgs.unpauser) - ); - - bytes32 saltBytes = keccak256(abi.encode(deploymentArgs.salt)); - - /// @dev Deploy the contract via the Ownable CREATE3 factory - vm.startBroadcast(deploymentArgs.signer); - - address gemGameContractAddress = ownableCreate3.deploy(deploymentBytecode, saltBytes); - gemGameContract = GemGame(gemGameContractAddress); - - vm.stopBroadcast(); - } -} diff --git a/script/staking/StakeHolderScriptERC20.t.sol b/script/staking/StakeHolderScriptERC20.t.sol deleted file mode 100644 index 31e0497c..00000000 --- a/script/staking/StakeHolderScriptERC20.t.sol +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.20; - -import {Test} from "forge-std/Test.sol"; -import {console} from "forge-std/console.sol"; -import {ERC1967Proxy} from "openzeppelin-contracts-4.9.3/proxy/ERC1967/ERC1967Proxy.sol"; -import {TimelockController} from "openzeppelin-contracts-4.9.3/governance/TimelockController.sol"; -import {ERC20PresetFixedSupply} from "openzeppelin-contracts-4.9.3/token/ERC20/presets/ERC20PresetFixedSupply.sol"; -import {IERC20} from "openzeppelin-contracts-4.9.3/token/ERC20/IERC20.sol"; - -import {IStakeHolder} from "../../contracts/staking/IStakeHolder.sol"; -import {StakeHolderERC20V2} from "../../contracts/staking/StakeHolderERC20V2.sol"; -import {OwnableCreate3Deployer} from "../../contracts/deployer/create3/OwnableCreate3Deployer.sol"; - -/** - * @title IDeployer Interface - * @notice This interface defines the contract responsible for deploying and optionally initializing new contracts - * via a specified deployment method. - * @dev Credit to axelarnetwork https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/blob/main/contracts/interfaces/IDeployer.sol - */ -interface IDeployer { - function deploy(bytes memory bytecode, bytes32 salt) external payable returns (address deployedAddress_); - function deployAndInit(bytes memory bytecode, bytes32 salt, bytes calldata init) - external - payable - returns (address deployedAddress_); - function deployedAddress(bytes calldata bytecode, address sender, bytes32 salt) - external - view - returns (address deployedAddress_); -} - -// Args needed for compex deployment using CREATE3 and a TimelockController -struct ComplexDeploymentArgs { - address signer; - address factory; - string salt; -} - -struct ComplexStakeHolderContractArgs { - address distributeAdmin; - address token; -} - -struct ComplexTimelockContractArgs { - uint256 timeDelayInSeconds; - address proposerAdmin; - address executorAdmin; -} - -// Args needed for simple deployment -struct SimpleDeploymentArgs { - address deployer; -} - -struct SimpleStakeHolderContractArgs { - address roleAdmin; - address upgradeAdmin; - address distributeAdmin; - address token; -} - -/** - * @notice Deployment script and test code for the deployment script. - * @dev testDeploy is the test. - * @dev deploy() is the function the script should call. - * For more details on deployment see ../../contracts/staking/README.md - */ -contract StakeHolderScriptERC20 is Test { - /** - * Deploy the OwnableCreate3Deployer needed for the complex deployment. - */ - function deployDeployer() external { - address signer = vm.envAddress("DEPLOYER_ADDRESS"); - _deployDeployer(signer); - } - - /** - * Deploy StakeHolderERC20V2 using Create3, with the TimelockController. - */ - function deployComplex() external { - address signer = vm.envAddress("DEPLOYER_ADDRESS"); - address factory = vm.envAddress("OWNABLE_CREATE3_FACTORY_ADDRESS"); - address distributeAdmin = vm.envAddress("DISTRIBUTE_ADMIN"); - address token = vm.envAddress("ERC20_STAKING_TOKEN"); - uint256 timeDelayInSeconds = vm.envUint("TIMELOCK_DELAY_SECONDS"); - address proposerAdmin = vm.envAddress("TIMELOCK_PROPOSER_ADMIN"); - address executorAdmin = vm.envAddress("TIMELOCK_EXECUTOR_ADMIN"); - string memory salt = vm.envString("SALT"); - - ComplexDeploymentArgs memory deploymentArgs = - ComplexDeploymentArgs({signer: signer, factory: factory, salt: salt}); - - ComplexStakeHolderContractArgs memory stakeHolderArgs = - ComplexStakeHolderContractArgs({distributeAdmin: distributeAdmin, token: token}); - - ComplexTimelockContractArgs memory timelockArgs = ComplexTimelockContractArgs({ - timeDelayInSeconds: timeDelayInSeconds, proposerAdmin: proposerAdmin, executorAdmin: executorAdmin - }); - _deployComplex(deploymentArgs, stakeHolderArgs, timelockArgs); - } - - /** - * Deploy StakeHolderERC20V2 using an EOA. - */ - function deploySimple() external { - address deployer = vm.envAddress("DEPLOYER_ADDRESS"); - address roleAdmin = vm.envAddress("ROLE_ADMIN"); - address upgradeAdmin = vm.envAddress("UPGRADE_ADMIN"); - address distributeAdmin = vm.envAddress("DISTRIBUTE_ADMIN"); - address token = vm.envAddress("ERC20_STAKING_TOKEN"); - - SimpleDeploymentArgs memory deploymentArgs = SimpleDeploymentArgs({deployer: deployer}); - - SimpleStakeHolderContractArgs memory stakeHolderArgs = SimpleStakeHolderContractArgs({ - roleAdmin: roleAdmin, upgradeAdmin: upgradeAdmin, distributeAdmin: distributeAdmin, token: token - }); - _deploySimple(deploymentArgs, stakeHolderArgs); - } - - function stake() external { - address stakeHolder = vm.envAddress("STAKE_HOLDER_CONTRACT"); - address staker = vm.envAddress("STAKER_ADDRESS"); - uint256 amount = vm.envUint("STAKER_AMOUNT"); - _stake(IStakeHolder(stakeHolder), staker, amount); - } - - function unstake() external { - address stakeHolder = vm.envAddress("STAKE_HOLDER_CONTRACT"); - address staker = vm.envAddress("STAKER_ADDRESS"); - uint256 amount = vm.envUint("STAKER_AMOUNT"); - _unstake(IStakeHolder(stakeHolder), staker, amount); - } - - /** - * Deploy the OwnableCreate3Deployer contract. Set the owner to the - * contract deployer. - */ - function _deployDeployer(address _deployer) private { - vm.startBroadcast(_deployer); - new OwnableCreate3Deployer(_deployer); - vm.stopBroadcast(); - } - - /** - * Deploy StakeHolderERC20V2 using Create3, with the TimelockController. - */ - function _deployComplex( - ComplexDeploymentArgs memory deploymentArgs, - ComplexStakeHolderContractArgs memory stakeHolderArgs, - ComplexTimelockContractArgs memory timelockArgs - ) private returns (StakeHolderERC20V2 stakeHolderContract, TimelockController timelockController) { - IDeployer ownableCreate3 = IDeployer(deploymentArgs.factory); - - bytes32 salt1 = keccak256(abi.encode(deploymentArgs.salt)); - bytes32 salt2 = keccak256(abi.encode(salt1)); - bytes32 salt3 = keccak256(abi.encode(salt2)); - - // Deploy TimelockController via the Ownable Create3 factory. - address timelockAddress; - bytes memory deploymentBytecode; - { - address[] memory proposers = new address[](1); - proposers[0] = timelockArgs.proposerAdmin; - address[] memory executors = new address[](1); - executors[0] = timelockArgs.executorAdmin; - // Create deployment bytecode and encode constructor args - deploymentBytecode = abi.encodePacked( - type(TimelockController).creationCode, - abi.encode(timelockArgs.timeDelayInSeconds, proposers, executors, address(0)) - ); - /// @dev Deploy the contract via the Ownable CREATE3 factory - vm.startBroadcast(deploymentArgs.signer); - timelockAddress = ownableCreate3.deploy(deploymentBytecode, salt1); - vm.stopBroadcast(); - } - - // Deploy StakeHolderERC20V2 via the Ownable Create3 factory. - // Create deployment bytecode and encode constructor args - deploymentBytecode = abi.encodePacked(type(StakeHolderERC20V2).creationCode); - /// @dev Deploy the contract via the Ownable CREATE3 factory - vm.startBroadcast(deploymentArgs.signer); - address stakeHolderImplAddress = ownableCreate3.deploy(deploymentBytecode, salt2); - vm.stopBroadcast(); - - // Deploy ERC1967Proxy via the Ownable Create3 factory. - // Create init data for the ERC1967 Proxy - bytes memory initData = abi.encodeWithSelector( - StakeHolderERC20V2.initialize.selector, - timelockAddress, // roleAdmin - timelockAddress, // upgradeAdmin - stakeHolderArgs.distributeAdmin, - stakeHolderArgs.token - ); - // Create deployment bytecode and encode constructor args - deploymentBytecode = - abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(stakeHolderImplAddress, initData)); - /// @dev Deploy the contract via the Ownable CREATE3 factory - vm.startBroadcast(deploymentArgs.signer); - address stakeHolderContractAddress = ownableCreate3.deploy(deploymentBytecode, salt3); - vm.stopBroadcast(); - - stakeHolderContract = StakeHolderERC20V2(stakeHolderContractAddress); - timelockController = TimelockController(payable(timelockAddress)); - } - - /** - * Deploy StakeHolderERC20V2 using an EOA and no time lock. - */ - function _deploySimple( - SimpleDeploymentArgs memory deploymentArgs, - SimpleStakeHolderContractArgs memory stakeHolderArgs - ) private returns (StakeHolderERC20V2 stakeHolderContract) { - bytes memory initData = abi.encodeWithSelector( - StakeHolderERC20V2.initialize.selector, - stakeHolderArgs.roleAdmin, - stakeHolderArgs.upgradeAdmin, - stakeHolderArgs.distributeAdmin, - stakeHolderArgs.token - ); - - vm.startBroadcast(deploymentArgs.deployer); - StakeHolderERC20V2 impl = new StakeHolderERC20V2(); - vm.stopBroadcast(); - vm.startBroadcast(deploymentArgs.deployer); - ERC1967Proxy proxy = new ERC1967Proxy(address(impl), initData); - vm.stopBroadcast(); - - stakeHolderContract = StakeHolderERC20V2(address(proxy)); - } - - function _stake(IStakeHolder _stakeHolder, address _staker, uint256 _amount) private { - address tokenAddress = _stakeHolder.getToken(); - IERC20 erc20 = IERC20(tokenAddress); - - uint256 bal = erc20.balanceOf(_staker); - console.log("Balance is: %x", bal); - console.log("Amount is: %x", _amount); - if (bal < _amount) { - revert("Insufficient balance"); - } - - vm.startBroadcast(_staker); - erc20.approve(address(_stakeHolder), _amount); - _stakeHolder.stake(_amount); - vm.stopBroadcast(); - } - - function _unstake(IStakeHolder _stakeHolder, address _staker, uint256 _amount) private { - vm.startBroadcast(_staker); - _stakeHolder.unstake(_amount); - vm.stopBroadcast(); - } - - function testComplex() external { - /// @dev Fork the Immutable zkEVM testnet for this test - string memory rpcURL = "https://rpc.testnet.immutable.com"; - vm.createSelectFork(rpcURL); - - address bank = makeAddr("bank"); - vm.startBroadcast(bank); - ERC20PresetFixedSupply erc20 = new ERC20PresetFixedSupply("Name", "SYM", 1000 ether, bank); - vm.stopBroadcast(); - - /// @dev These are Immutable zkEVM testnet values where necessary - address immTestNetCreate3 = 0x37a59A845Bb6eD2034098af8738fbFFB9D589610; - ComplexDeploymentArgs memory deploymentArgs = ComplexDeploymentArgs({ - signer: 0xdDA0d9448Ebe3eA43aFecE5Fa6401F5795c19333, factory: immTestNetCreate3, salt: "salt" - }); - - address distributeAdmin = makeAddr("distribute"); - ComplexStakeHolderContractArgs memory stakeHolderArgs = - ComplexStakeHolderContractArgs({distributeAdmin: distributeAdmin, token: address(erc20)}); - - uint256 delay = 604800; // 604800 seconds = 1 week - address proposer = makeAddr("proposer"); - address executor = makeAddr("executor"); - - ComplexTimelockContractArgs memory timelockArgs = - ComplexTimelockContractArgs({timeDelayInSeconds: delay, proposerAdmin: proposer, executorAdmin: executor}); - - // Run deployment against forked testnet - StakeHolderERC20V2 stakeHolder; - TimelockController timelockController; - (stakeHolder, timelockController) = _deployComplex(deploymentArgs, stakeHolderArgs, timelockArgs); - - _commonTest( - true, - IStakeHolder(stakeHolder), - address(timelockController), - bank, - immTestNetCreate3, - address(0), - address(0), - distributeAdmin - ); - - assertTrue( - timelockController.hasRole(timelockController.PROPOSER_ROLE(), proposer), "Proposer not set correcrly" - ); - assertTrue( - timelockController.hasRole(timelockController.EXECUTOR_ROLE(), executor), "Executor not set correcrly" - ); - assertEq(timelockController.getMinDelay(), delay, "Delay not set correctly"); - } - - function testSimple() external { - /// @dev Fork the Immutable zkEVM testnet for this test - string memory rpcURL = "https://rpc.testnet.immutable.com"; - vm.createSelectFork(rpcURL); - - address deployer = makeAddr("deployer"); - address bank = makeAddr("bank"); - - vm.startBroadcast(deployer); - ERC20PresetFixedSupply erc20 = new ERC20PresetFixedSupply("Name", "SYM", 1000 ether, bank); - vm.stopBroadcast(); - - /// @dev These are Immutable zkEVM testnet values where necessary - SimpleDeploymentArgs memory deploymentArgs = SimpleDeploymentArgs({deployer: deployer}); - - address roleAdmin = makeAddr("role"); - address upgradeAdmin = makeAddr("upgrade"); - address distributeAdmin = makeAddr("distribute"); - - SimpleStakeHolderContractArgs memory stakeHolderContractArgs = SimpleStakeHolderContractArgs({ - roleAdmin: roleAdmin, upgradeAdmin: upgradeAdmin, distributeAdmin: distributeAdmin, token: address(erc20) - }); - - // Run deployment against forked testnet - StakeHolderERC20V2 stakeHolder = _deploySimple(deploymentArgs, stakeHolderContractArgs); - - _commonTest( - false, IStakeHolder(stakeHolder), address(0), bank, deployer, roleAdmin, upgradeAdmin, distributeAdmin - ); - } - - function _commonTest( - bool _isComplex, - IStakeHolder _stakeHolder, - address _timelockControl, - address _bank, - address _deployer, - address _roleAdmin, - address _upgradeAdmin, - address _distributeAdmin - ) private { - address roleAdmin = _isComplex ? _timelockControl : _roleAdmin; - address upgradeAdmin = _isComplex ? _timelockControl : _upgradeAdmin; - - address tokenAddress = _stakeHolder.getToken(); - IERC20 erc20 = IERC20(tokenAddress); - - // Post deployment checks - { - StakeHolderERC20V2 temp = new StakeHolderERC20V2(); - bytes32 defaultAdminRole = temp.DEFAULT_ADMIN_ROLE(); - assertTrue( - _stakeHolder.hasRole(_stakeHolder.UPGRADE_ROLE(), upgradeAdmin), - "Upgrade admin should have upgrade role" - ); - assertTrue(_stakeHolder.hasRole(defaultAdminRole, roleAdmin), "Role admin should have default admin role"); - assertTrue( - _stakeHolder.hasRole(_stakeHolder.DISTRIBUTE_ROLE(), _distributeAdmin), - "Distribute admin should have distribute role" - ); - // The DEFAULT_ADMIN_ROLE should be revoked from the deployer account - assertFalse(_stakeHolder.hasRole(defaultAdminRole, _deployer), "msg.sender should not be an admin"); - } - - address user1 = makeAddr("user1"); - vm.startBroadcast(_bank); - bool success = erc20.transfer(user1, 100 ether); - vm.stopBroadcast(); - require(success, "ERC20 transfer unexpectedly failed"); - - _stake(_stakeHolder, user1, 10 ether); - - assertEq(erc20.balanceOf(user1), 90 ether, "User1 balance after stake"); - assertEq(erc20.balanceOf(address(_stakeHolder)), 10 ether, "StakeHolder balance after stake"); - - _unstake(_stakeHolder, user1, 7 ether); - assertEq(erc20.balanceOf(user1), 97 ether, "User1 balance after unstake"); - assertEq(erc20.balanceOf(address(_stakeHolder)), 3 ether, "StakeHolder balance after unstake"); - } -} diff --git a/script/trading/seaport16/DeployConduitController.s.sol b/script/trading/seaport16/DeployConduitController.s.sol deleted file mode 100644 index 0af646c2..00000000 --- a/script/trading/seaport16/DeployConduitController.s.sol +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity 0.8.24; - -import {console} from "forge-std/Console.sol"; -import {Script} from "forge-std/Script.sol"; - -/** - * @title DeployConduitController - * @notice This script deploys the ConduitController contract following the strategy - * outlined here: https://github.com/ProjectOpenSea/seaport/blob/main/docs/Deployment.md. - */ -contract DeployConduitController is Script { - address private constant KEYLESS_CREATE2_DEPLOYER_ADDRESS = 0x4c8D290a1B368ac4728d83a9e8321fC3af2b39b1; - address private constant KEYLESS_CREATE2_ADDRESS = 0x7A0D94F55792C434d74a40883C6ed8545E406D12; - address private constant INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_ADDRESS = 0xcfA3A7637547094fF06246817a35B8333C315196; - address private constant IMMUTABLE_CREATE2_FACTORY_ADDRESS = 0x0000000000FFe8B47B3e2130213B802212439497; - address private constant CONDUIT_CONTROLLER_ADDRESS = 0x00000000F9490004C11Cef243f5400493c00Ad63; - - bytes private constant CREATE_KEYLESS_CREATE2_RAW_SIGNED_TX = - hex"f87e8085174876e800830186a08080ad601f80600e600039806000f350fe60003681823780368234f58015156014578182fd5b80825250506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222"; - - bytes private constant CREATE_INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_RAW_TX = - hex"608060405234801561001057600080fd5b50610833806100206000396000f3fe60806040526004361061003f5760003560e01c806308508b8f1461004457806364e030871461009857806385cf97ab14610138578063a49a7c90146101bc575b600080fd5b34801561005057600080fd5b506100846004803603602081101561006757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166101ec565b604080519115158252519081900360200190f35b61010f600480360360408110156100ae57600080fd5b813591908101906040810160208201356401000000008111156100d057600080fd5b8201836020820111156100e257600080fd5b8035906020019184600183028401116401000000008311171561010457600080fd5b509092509050610217565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b34801561014457600080fd5b5061010f6004803603604081101561015b57600080fd5b8135919081019060408101602082013564010000000081111561017d57600080fd5b82018360208201111561018f57600080fd5b803590602001918460018302840111640100000000831117156101b157600080fd5b509092509050610592565b3480156101c857600080fd5b5061010f600480360360408110156101df57600080fd5b508035906020013561069e565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b600083606081901c33148061024c57507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008116155b6102a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260458152602001806107746045913960600191505060405180910390fd5b606084848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250604051855195965090943094508b93508692506020918201918291908401908083835b6020831061033557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016102f8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018281037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183528085528251928201929092207fff000000000000000000000000000000000000000000000000000000000000008383015260609890981b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260358201969096526055808201979097528251808203909701875260750182525084519484019490942073ffffffffffffffffffffffffffffffffffffffff81166000908152938490529390922054929350505060ff16156104a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180610735603f913960400191505060405180910390fd5b81602001825188818334f5955050508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161461053a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260468152602001806107b96046913960600191505060405180910390fd5b50505073ffffffffffffffffffffffffffffffffffffffff8116600090815260208190526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559392505050565b6000308484846040516020018083838082843760408051919093018181037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001825280845281516020928301207fff000000000000000000000000000000000000000000000000000000000000008383015260609990991b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166021820152603581019790975260558088019890985282518088039098018852607590960182525085519585019590952073ffffffffffffffffffffffffffffffffffffffff81166000908152948590529490932054939450505060ff909116159050610697575060005b9392505050565b604080517fff000000000000000000000000000000000000000000000000000000000000006020808301919091523060601b6021830152603582018590526055808301859052835180840390910181526075909201835281519181019190912073ffffffffffffffffffffffffffffffffffffffff81166000908152918290529190205460ff161561072e575060005b9291505056fe496e76616c696420636f6e7472616374206372656174696f6e202d20636f6e74726163742068617320616c7265616479206265656e206465706c6f7965642e496e76616c69642073616c74202d206669727374203230206279746573206f66207468652073616c74206d757374206d617463682063616c6c696e6720616464726573732e4661696c656420746f206465706c6f7920636f6e7472616374207573696e672070726f76696465642073616c7420616e6420696e697469616c697a6174696f6e20636f64652ea265627a7a723058202bdc55310d97c4088f18acf04253db593f0914059f0c781a9df3624dcef0d1cf64736f6c634300050a0032"; - - bytes private constant CREATE_IMMUTABLE_CREATE2_FACTORY_RAW_TX = - hex"64e030870000000000000000000000000000000000000000f4b0218f13a6440a6f02000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000853608060405234801561001057600080fd5b50610833806100206000396000f3fe60806040526004361061003f5760003560e01c806308508b8f1461004457806364e030871461009857806385cf97ab14610138578063a49a7c90146101bc575b600080fd5b34801561005057600080fd5b506100846004803603602081101561006757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166101ec565b604080519115158252519081900360200190f35b61010f600480360360408110156100ae57600080fd5b813591908101906040810160208201356401000000008111156100d057600080fd5b8201836020820111156100e257600080fd5b8035906020019184600183028401116401000000008311171561010457600080fd5b509092509050610217565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b34801561014457600080fd5b5061010f6004803603604081101561015b57600080fd5b8135919081019060408101602082013564010000000081111561017d57600080fd5b82018360208201111561018f57600080fd5b803590602001918460018302840111640100000000831117156101b157600080fd5b509092509050610592565b3480156101c857600080fd5b5061010f600480360360408110156101df57600080fd5b508035906020013561069e565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b600083606081901c33148061024c57507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008116155b6102a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260458152602001806107746045913960600191505060405180910390fd5b606084848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250604051855195965090943094508b93508692506020918201918291908401908083835b6020831061033557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016102f8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018281037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183528085528251928201929092207fff000000000000000000000000000000000000000000000000000000000000008383015260609890981b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260358201969096526055808201979097528251808203909701875260750182525084519484019490942073ffffffffffffffffffffffffffffffffffffffff81166000908152938490529390922054929350505060ff16156104a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180610735603f913960400191505060405180910390fd5b81602001825188818334f5955050508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161461053a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260468152602001806107b96046913960600191505060405180910390fd5b50505073ffffffffffffffffffffffffffffffffffffffff8116600090815260208190526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559392505050565b6000308484846040516020018083838082843760408051919093018181037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001825280845281516020928301207fff000000000000000000000000000000000000000000000000000000000000008383015260609990991b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166021820152603581019790975260558088019890985282518088039098018852607590960182525085519585019590952073ffffffffffffffffffffffffffffffffffffffff81166000908152948590529490932054939450505060ff909116159050610697575060005b9392505050565b604080517fff000000000000000000000000000000000000000000000000000000000000006020808301919091523060601b6021830152603582018590526055808301859052835180840390910181526075909201835281519181019190912073ffffffffffffffffffffffffffffffffffffffff81166000908152918290529190205460ff161561072e575060005b9291505056fe496e76616c696420636f6e7472616374206372656174696f6e202d20636f6e74726163742068617320616c7265616479206265656e206465706c6f7965642e496e76616c69642073616c74202d206669727374203230206279746573206f66207468652073616c74206d757374206d617463682063616c6c696e6720616464726573732e4661696c656420746f206465706c6f7920636f6e7472616374207573696e672070726f76696465642073616c7420616e6420696e697469616c697a6174696f6e20636f64652ea265627a7a723058202bdc55310d97c4088f18acf04253db593f0914059f0c781a9df3624dcef0d1cf64736f6c634300050a003200000000000000000000000000"; - - bytes private constant CREATE_CONDUIT_CONTROLLER_RAW_TX = - hex"64e030870000000000000000000000000000000000000000dc0ef3c792976604960400000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000302760c08060405234620000ea57600090610c9f906001600160401b03603f8301601f1916820181811183821017620000da575b604052828252620023889160208101908484833951902060805260405192839281840192831184841017620000ca575b8339039082f58015620000ba575b6001600160a01b03163f60a05260405161227490816200011482396080518181816102b101528181610bcc0152610d06015260a0518181816102d401528181610c620152610da90152f35b620000c462000106565b6200006f565b620000d4620000ef565b62000061565b620000e4620000ef565b62000031565b600080fd5b50634e487b7160e01b600052604160045260246000fd5b506040513d6000823e3d90fdfe60806040526004361015610013575b600080fd5b60003560e01c8063027cc7641461012b5780630a96ad391461012257806313ad9cab1461011957806314afd79e1461011057806333bc8572146101075780634e3f9580146100fe57806351710e45146100f55780636d435421146100ec5780636e9bfd9f146100e3578063794593bc146100da5780637b37e561146100d15780638b9e028b146100c8578063906c87cc146100bf576393790f44146100b757600080fd5b61000e61126e565b5061000e6111fa565b5061000e61113c565b5061000e610fc8565b5061000e610c8a565b5061000e610b3c565b5061000e6109bf565b5061000e610765565b5061000e6106f3565b5061000e61064f565b5061000e6105db565b5061000e6102fa565b5061000e61027b565b5061000e61017a565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361000e57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576101b2610134565b602435906101bf81611574565b73ffffffffffffffffffffffffffffffffffffffff80911691600083815280602052600360408220015482101561023f5790600360408361023b9661020a9552806020522001611400565b90549060031b1c166040519182918291909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0390f35b602484604051907f6ceb340b0000000000000000000000000000000000000000000000000000000082526004820152fd5b600091031261000e57565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57604080517f000000000000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006020820152f35b503461000e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57610332610134565b61033a610157565b90604435918215918215840361000e5761035381611505565b73ffffffffffffffffffffffffffffffffffffffff811690813b1561000e576040517fc4e8fcb500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528515156024820152610401926000908290604490829084905af180156105ce575b6105b5575b5073ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b92600484019261043183859073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b5491821590806105ae575b1561048157505050600361047d92930161045682826114ce565b54929073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b555b005b91949391816105a5575b5061049257005b6104df61047d938560037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600098019201916104ce83546113a4565b90808203610504575b505050611447565b9073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b6105766105449161053b61051b61059c9588611400565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b92839187611400565b90919082549060031b9173ffffffffffffffffffffffffffffffffffffffff9283811b93849216901b16911916179055565b859073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b553880806104d7565b9050153861048b565b508061043c565b806105c26105c892611335565b80610270565b386103da565b6105d6611397565b6103d5565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020610615610134565b61061e81611574565b73ffffffffffffffffffffffffffffffffffffffff8091166000526000825260016040600020015416604051908152f35b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760206106e861068c610134565b73ffffffffffffffffffffffffffffffffffffffff6106a9610157565b916106b381611574565b166000526000835260046040600020019073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b541515604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5773ffffffffffffffffffffffffffffffffffffffff610740610134565b61074981611574565b1660005260006020526020600360406000200154604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5761079d610134565b6107a681611574565b61080c6107f360026107d88473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b33036109765761047f9060007f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da81604051a2610896600261086d8373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b017fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b73ffffffffffffffffffffffffffffffffffffffff3390806108dd60016107d88673ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b169083167fc8894f26f396ce8c004245c8b7cd1b92103a6e4302fcbab883987149ac01b7ec6000604051a46001610935339273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b019073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6040517f88c3a11500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152602490fd5b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576109f7610134565b6109ff610157565b90610a0981611505565b73ffffffffffffffffffffffffffffffffffffffff808316908115610b095750610a5b6107f360026107d88573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b8114610ab95761093561047f93926002927f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da6000604051a273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b506040517fcbc080ca00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b82602491604051917fa388d263000000000000000000000000000000000000000000000000000000008352166004820152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576040517fff00000000000000000000000000000000000000000000000000000000000000602082019081523060601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260043560358301527f0000000000000000000000000000000000000000000000000000000000000000605583015273ffffffffffffffffffffffffffffffffffffffff91610c3b81607581015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611356565b519020604080519290911673ffffffffffffffffffffffffffffffffffffffff811683523f7f000000000000000000000000000000000000000000000000000000000000000014602083015290f35b503461000e576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57600435610cc6610157565b73ffffffffffffffffffffffffffffffffffffffff91828216908115610f9f57338160601c03610f7657610da46107f386516020810190610d8881610c0f7f0000000000000000000000000000000000000000000000000000000000000000883087917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000605594927fff00000000000000000000000000000000000000000000000000000000000000855260601b166001840152601583015260358201520190565b51902073ffffffffffffffffffffffffffffffffffffffff1690565b92833f7f000000000000000000000000000000000000000000000000000000000000000014610f3057947f4397af6128d529b8ae0442f99db1296d5136062597a15bbc61c1b2a6431a7d15610eca838060009961023b989796865180610c9f8082019082821067ffffffffffffffff831117610f23575b6115a0833903908df515610f16575b610e9c610e578973ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b91600183019073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b55835173ffffffffffffffffffffffffffffffffffffffff8716815260208101919091529081906040820190565b0390a15194859483167fc8894f26f396ce8c004245c8b7cd1b92103a6e4302fcbab883987149ac01b7ec8287a473ffffffffffffffffffffffffffffffffffffffff1682526020820190565b610f1e611397565b610e2a565b610f2b611305565b610e1b565b85517f6328ccb200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602490fd5b600485517fcb6e5344000000000000000000000000000000000000000000000000000000008152fd5b600485517f99faaa04000000000000000000000000000000000000000000000000000000008152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57611000610134565b61100981611505565b73ffffffffffffffffffffffffffffffffffffffff9081811660009281845283602052600260408520015416156110ba575061108e600291837f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da81604051a273ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b017fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055604051f35b602490604051907f6b0136160000000000000000000000000000000000000000000000000000000082526004820152fd5b6020908160408183019282815285518094520193019160005b828110611112575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611104565b503461000e576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57611175610134565b9061117f82611574565b73ffffffffffffffffffffffffffffffffffffffff91826000911681528082526003604082200192604051908193808654938481520195845280842093915b8383106111e15761023b866111d5818a0382611356565b604051918291826110eb565b84548116875295810195600194850194909201916111be565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020611234610134565b61123d81611574565b73ffffffffffffffffffffffffffffffffffffffff8091166000526000825260026040600020015416604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5773ffffffffffffffffffffffffffffffffffffffff6112bb610134565b16600052600060205260406000205480156112db57602090604051908152f35b60046040517f4ca82090000000000000000000000000000000000000000000000000000000008152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161134957604052565b611351611305565b604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761134957604052565b506040513d6000823e3d90fd5b600181106113d1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80548210156114185760005260206000200190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8054801561149f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019061147c8282611400565b73ffffffffffffffffffffffffffffffffffffffff82549160031b1b1916905555565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b906105446114f692805490680100000000000000008210156114f8575b600182018155611400565b565b611500611305565b6114eb565b61150e81611574565b73ffffffffffffffffffffffffffffffffffffffff809116908160005260006020526001604060002001541633036115435750565b602490604051907fd4ed9a170000000000000000000000000000000000000000000000000000000082526004820152fd5b73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002054156112db5756fe60a080604052346100235733608052610c7690816100298239608051816103c50152f35b600080fdfe60806040526004361015610013575b600080fd5b6000803560e01c9081634ce34aa21461006657508063899e104c1461005d5780638df25d92146100545763c4e8fcb51461004c57600080fd5b61000e610362565b5061000e61027f565b5061000e6101ab565b346101465760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101465760043567ffffffffffffffff8111610142576100b5903690600401610149565b9133815280602052604081205415610116575b8281106100fa576040517f4ce34aa2000000000000000000000000000000000000000000000000000000008152602090f35b8061011061010b6001938686610532565b6105c4565b016100c8565b807f93daadf2000000000000000000000000000000000000000000000000000000006024925233600452fd5b5080fd5b80fd5b9181601f8401121561000e5782359167ffffffffffffffff831161000e5760208085019460c0850201011161000e57565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e576101fc903690600401610149565b9160243590811161000e5761021590369060040161017a565b919092600033815280602052604081205415610116575b8181106102685761023d8486610acb565b6040517f899e104c000000000000000000000000000000000000000000000000000000008152602090f35b8061027961010b6001938587610532565b0161022c565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043567ffffffffffffffff811161000e576102cf90369060040161017a565b33600052600060205260406000205415610316576102ec91610acb565b60206040517f8df25d92000000000000000000000000000000000000000000000000000000008152f35b7f93daadf2000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043561039e81610344565b6024359081151580830361000e5773ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000000000000000000000000000000000001633036105085761041f6104188473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b5460ff1690565b1515146104b657816104a6846104767fae63067d43ac07563b7eb8db6595635fc77f1578a2a5ea06ba91b63e2afa37e29573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405193151584521691602090a2005b506040517f924e341e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9190911660048201529015156024820152604490fd5b60046040517f6d5769be000000000000000000000000000000000000000000000000000000008152fd5b91908110156105425760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6004111561057b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b35600481101561000e5790565b356105c181610344565b90565b60016105cf826105aa565b6105d881610571565b0361061357806105ed602061061193016105b7565b906105fa604082016105b7565b60a0610608606084016105b7565b92013592610712565b565b600261061e826105aa565b61062781610571565b0361069657600160a08201350361066c5780610648602061061193016105b7565b90610655604082016105b7565b6080610663606084016105b7565b92013592610882565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b60036106a1826105aa565b6106aa81610571565b036106e857806106bf602061061193016105b7565b6106cb604083016105b7565b6106d7606084016105b7565b90608060a085013594013592610990565b60046040517f7932f1fc000000000000000000000000000000000000000000000000000000008152fd5b9092604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d151581161561077c575b505050505050604052606052565b80863b15151661076e579087959691156107bc57602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b156107f657506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d610835575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c908060030291808211610869575b505060205a91011061086057856107fc565b833d81803e3d90fd5b8080600392028380020360091c9203020101868061084e565b9092813b1561096257604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156108db5750505050604052606052565b8593943d61091e575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211610949575b505060205a91011061086057856108e4565b8080600392028380020360091c92030201018680610937565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b15610a9d57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af115610a0d57505050505060805260a05260c052604052606052565b89949550883d610a50575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c908060030291808211610a84575b505060205a910110610a7b5786610a18565b843d81803e3d90fd5b8080600392028380020360091c92030201018780610a69565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b90816020907f2eb2c2d600000000000000000000000000000000000000000000000000000000825260005b838110610b095750505050506080604052565b8435820194853590813b156109625760a09182880192833560059181831b948b60c08097608094818301868501351490606085013514169201013584141615610c165789019a890160243760061b9360e0850160a452610104850194600086526040019060c437600080858982865af115610b8a5750505050600101610af6565b869394503d610bcb575b507fafc445e20000000000000000000000000000000000000000000000000000000060005260045260645260849081510190526000fd5b84601f3d01821c911c90600381810292808311610bff575b505050835a910110610bf55784610b94565b3d6000803e3d6000fd5b8080028380020360091c9203020101858080610be3565b7feba2084c0000000000000000000000000000000000000000000000000000000060005260046000fdfea2646970667358221220c5c8d054d9d5df7c3530eab1c32506aad1fcb6772c1457f0da5443ad9e91b4a364736f6c634300080e0033a264697066735822122031e2de61a9e35e9e87d5eef6a36b045a0bab54c4031fd01a0f8138afce3cec3164736f6c634300080e003360a080604052346100235733608052610c7690816100298239608051816103c50152f35b600080fdfe60806040526004361015610013575b600080fd5b6000803560e01c9081634ce34aa21461006657508063899e104c1461005d5780638df25d92146100545763c4e8fcb51461004c57600080fd5b61000e610362565b5061000e61027f565b5061000e6101ab565b346101465760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101465760043567ffffffffffffffff8111610142576100b5903690600401610149565b9133815280602052604081205415610116575b8281106100fa576040517f4ce34aa2000000000000000000000000000000000000000000000000000000008152602090f35b8061011061010b6001938686610532565b6105c4565b016100c8565b807f93daadf2000000000000000000000000000000000000000000000000000000006024925233600452fd5b5080fd5b80fd5b9181601f8401121561000e5782359167ffffffffffffffff831161000e5760208085019460c0850201011161000e57565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e576101fc903690600401610149565b9160243590811161000e5761021590369060040161017a565b919092600033815280602052604081205415610116575b8181106102685761023d8486610acb565b6040517f899e104c000000000000000000000000000000000000000000000000000000008152602090f35b8061027961010b6001938587610532565b0161022c565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043567ffffffffffffffff811161000e576102cf90369060040161017a565b33600052600060205260406000205415610316576102ec91610acb565b60206040517f8df25d92000000000000000000000000000000000000000000000000000000008152f35b7f93daadf2000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043561039e81610344565b6024359081151580830361000e5773ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000000000000000000000000000000000001633036105085761041f6104188473ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b5460ff1690565b1515146104b657816104a6846104767fae63067d43ac07563b7eb8db6595635fc77f1578a2a5ea06ba91b63e2afa37e29573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405193151584521691602090a2005b506040517f924e341e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9190911660048201529015156024820152604490fd5b60046040517f6d5769be000000000000000000000000000000000000000000000000000000008152fd5b91908110156105425760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6004111561057b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b35600481101561000e5790565b356105c181610344565b90565b60016105cf826105aa565b6105d881610571565b0361061357806105ed602061061193016105b7565b906105fa604082016105b7565b60a0610608606084016105b7565b92013592610712565b565b600261061e826105aa565b61062781610571565b0361069657600160a08201350361066c5780610648602061061193016105b7565b90610655604082016105b7565b6080610663606084016105b7565b92013592610882565b60046040517fefcc00b1000000000000000000000000000000000000000000000000000000008152fd5b60036106a1826105aa565b6106aa81610571565b036106e857806106bf602061061193016105b7565b6106cb604083016105b7565b6106d7606084016105b7565b90608060a085013594013592610990565b60046040517f7932f1fc000000000000000000000000000000000000000000000000000000008152fd5b9092604051926000947f23b872dd00000000000000000000000000000000000000000000000000000000865280600452816024528260445260208660648180885af1803d15601f3d1160018a51141617163d151581161561077c575b505050505050604052606052565b80863b15151661076e579087959691156107bc57602486887f5f15d672000000000000000000000000000000000000000000000000000000008252600452fd5b156107f657506084947f98891923000000000000000000000000000000000000000000000000000000008552600452602452604452606452fd5b3d610835575b5060a4947ff486bc8700000000000000000000000000000000000000000000000000000000855260045260245260445281606452608452fd5b601f3d0160051c9060051c908060030291808211610869575b505060205a91011061086057856107fc565b833d81803e3d90fd5b8080600392028380020360091c9203020101868061084e565b9092813b1561096257604051926000947f23b872dd000000000000000000000000000000000000000000000000000000008652806004528160245282604452858060648180885af1156108db5750505050604052606052565b8593943d61091e575b5060a4947ff486bc870000000000000000000000000000000000000000000000000000000085526004526024526044526064526001608452fd5b601f3d0160051c9060051c908060030291808211610949575b505060205a91011061086057856108e4565b8080600392028380020360091c92030201018680610937565b507f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b929093833b15610a9d57604051936080519160a0519360c051956000987ff242432a000000000000000000000000000000000000000000000000000000008a528060045281602452826044528360645260a06084528960a452898060c48180895af115610a0d57505050505060805260a05260c052604052606052565b89949550883d610a50575b5060a4957ff486bc87000000000000000000000000000000000000000000000000000000008652600452602452604452606452608452fd5b601f3d0160051c9060051c908060030291808211610a84575b505060205a910110610a7b5786610a18565b843d81803e3d90fd5b8080600392028380020360091c92030201018780610a69565b837f5f15d6720000000000000000000000000000000000000000000000000000000060005260045260246000fd5b90816020907f2eb2c2d600000000000000000000000000000000000000000000000000000000825260005b838110610b095750505050506080604052565b8435820194853590813b156109625760a09182880192833560059181831b948b60c08097608094818301868501351490606085013514169201013584141615610c165789019a890160243760061b9360e0850160a452610104850194600086526040019060c437600080858982865af115610b8a5750505050600101610af6565b869394503d610bcb575b507fafc445e20000000000000000000000000000000000000000000000000000000060005260045260645260849081510190526000fd5b84601f3d01821c911c90600381810292808311610bff575b505050835a910110610bf55784610b94565b3d6000803e3d6000fd5b8080028380020360091c9203020101858080610be3565b7feba2084c0000000000000000000000000000000000000000000000000000000060005260046000fdfea2646970667358221220c5c8d054d9d5df7c3530eab1c32506aad1fcb6772c1457f0da5443ad9e91b4a364736f6c634300080e003300000000000000000000000000000000000000000000000000"; - - function run() external { - vm.startBroadcast(); - - if (KEYLESS_CREATE2_ADDRESS.code.length == 0) { - console.log("Deploying KEYLESS_CREATE2"); - // Creates KEYLESS_CREATE2 contract using 0x4c8D290a1B368ac4728d83a9e8321fC3af2b39b1 as the deployer. This account must be funded with at least 0.01 ether. - require( - KEYLESS_CREATE2_DEPLOYER_ADDRESS.balance >= 0.01 ether, - string.concat( - "keylessCreate2Deployer balance must be at least ", - vm.toString(uint256(0.01 ether)), - " but is only ", - vm.toString(KEYLESS_CREATE2_DEPLOYER_ADDRESS.balance) - ) - ); - vm.broadcastRawTransaction(CREATE_KEYLESS_CREATE2_RAW_SIGNED_TX); - } else { - console.log("Skipping KEYLESS_CREATE2, already exists"); - } - - _createIfNotExist( - "INEFFICIENT_IMMUTABLE_CREATE2_FACTORY", - INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_ADDRESS, - KEYLESS_CREATE2_ADDRESS, - CREATE_INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_RAW_TX - ); - _createIfNotExist( - "IMMUTABLE_CREATE2_FACTORY", - IMMUTABLE_CREATE2_FACTORY_ADDRESS, - INEFFICIENT_IMMUTABLE_CREATE2_FACTORY_ADDRESS, - CREATE_IMMUTABLE_CREATE2_FACTORY_RAW_TX - ); - _createIfNotExist( - "ConduitController", - CONDUIT_CONTROLLER_ADDRESS, - IMMUTABLE_CREATE2_FACTORY_ADDRESS, - CREATE_CONDUIT_CONTROLLER_RAW_TX - ); - - vm.stopBroadcast(); - } - - function _createIfNotExist( - string memory targetName, - address targetAddress, - address deployer, - bytes memory creationTx - ) private { - if (targetAddress.code.length == 0) { - console.log("Deploying", targetName); - (bool success,) = deployer.call(creationTx); - require(success, string.concat("Failed to create ", targetName)); - } else { - console.log(string.concat("Skipping ", targetName, ", already exists")); - } - } -} - -// forge script script/trading/seaport16/DeployConduitController.s.sol --rpc-url "https://rpc.testnet.immutable.com" -vvvv --priority-gas-price 10000000000 --with-gas-price 11000000000 --private-key=xx diff --git a/script/trading/seaport16/DeployImmutableSeaport.s.sol b/script/trading/seaport16/DeployImmutableSeaport.s.sol deleted file mode 100644 index 45502ccb..00000000 --- a/script/trading/seaport16/DeployImmutableSeaport.s.sol +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity 0.8.24; - -import {console} from "forge-std/console.sol"; -import {Script} from "forge-std/Script.sol"; -import {ImmutableSeaport} from "../../../contracts/trading/seaport16/ImmutableSeaport.sol"; -import {AccessControlledDeployer} from "../../../contracts/deployer/AccessControlledDeployer.sol"; -import {IDeployer} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IDeployer.sol"; - -/** - * @title DeployImmutableSeaport - * @notice This script deploys the ImmutableSeaport contract via CREATE2. - * @dev This script assumes that the ConduitController contract has already been deployed. - */ -contract DeployImmutableSeaport is Script { - address private constant ACCESS_CONTROLLED_DEPLOYER_ADDRESS = 0x0B5B1d92259b13D516cCd5a6E63d7D94Ea2A4836; - address private constant CREATE2_DEPLOYER_ADDRESS = 0x9df760a54b3B00cC8B3d70A37d45fa97cCfdb4Db; - address private constant CONDUIT_CONTROLLER_ADDRESS = 0x00000000F9490004C11Cef243f5400493c00Ad63; - address private constant IMMUTABLE_SEAPORT_ADDRESS = 0xbE737Cf2C122F83d1610C1224f7B99ca9d0E09f6; - bytes32 private constant IMMUTABLE_SEAPORT_DEPLOYMENT_SALT = keccak256(abi.encodePacked("immutable-seaport16")); - address private constant SEAPORT_INITIAL_OWNER = 0xdDA0d9448Ebe3eA43aFecE5Fa6401F5795c19333; // Immutable Deployer - - function run() external { - AccessControlledDeployer deployer = AccessControlledDeployer(ACCESS_CONTROLLED_DEPLOYER_ADDRESS); - IDeployer create2Deployer = IDeployer(CREATE2_DEPLOYER_ADDRESS); - - // Check supplied immutableSeaportAddress matches the expected address based on current creationCode - bytes memory immutableSeaportDeploymentBytecode = abi.encodePacked( - type(ImmutableSeaport).creationCode, abi.encode(CONDUIT_CONTROLLER_ADDRESS, SEAPORT_INITIAL_OWNER) - ); - address expectedImmutableSeaportAddress = create2Deployer.deployedAddress( - immutableSeaportDeploymentBytecode, ACCESS_CONTROLLED_DEPLOYER_ADDRESS, IMMUTABLE_SEAPORT_DEPLOYMENT_SALT - ); - console.log("Expected ImmutableSeaport address: %s", expectedImmutableSeaportAddress); - require( - expectedImmutableSeaportAddress == IMMUTABLE_SEAPORT_ADDRESS, "Expected ImmutableSeaport address mismatch" - ); - - vm.startBroadcast(); - - // Deploy ImmutableSeaport if it doesn't already exist - if (IMMUTABLE_SEAPORT_ADDRESS.code.length == 0) { - console.log("Deploying ImmutableSeaport"); - address deployedImmutableSeaportAddress = - deployer.deploy(create2Deployer, immutableSeaportDeploymentBytecode, IMMUTABLE_SEAPORT_DEPLOYMENT_SALT); - require( - deployedImmutableSeaportAddress == IMMUTABLE_SEAPORT_ADDRESS, - "Deployed ImmutableSeaport address mismatch" - ); - } else { - console.log("Skipping ImmutableSeaport, already exists"); - } - - vm.stopBroadcast(); - } -} - -// forge script script/trading/seaport16/DeployImmutableSeaport.s.sol --rpc-url "https://rpc.testnet.immutable.com" -vvvv --priority-gas-price 10000000000 --with-gas-price 11000000000 --private-key=xx diff --git a/script/trading/seaport16/DeployImmutableSignedZoneV3.s.sol b/script/trading/seaport16/DeployImmutableSignedZoneV3.s.sol deleted file mode 100644 index eff1fae0..00000000 --- a/script/trading/seaport16/DeployImmutableSignedZoneV3.s.sol +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity 0.8.24; - -import {console} from "forge-std/console.sol"; -import {Script} from "forge-std/Script.sol"; -import {ImmutableSeaport} from "../../../contracts/trading/seaport16/ImmutableSeaport.sol"; -import { - ImmutableSignedZoneV3 -} from "../../../contracts/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.sol"; -import {AccessControlledDeployer} from "../../../contracts/deployer/AccessControlledDeployer.sol"; -import {IDeployer} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IDeployer.sol"; - -/** - * @title DeployImmutableSignedZoneV3 - * @notice This script deploys the ImmutableSignedZoneV3 contract via CREATE2. - * @dev This script assumes that the ImmutableSeaport contract has already been deployed. - */ -contract DeployImmutableSignedZoneV3 is Script { - address private constant ACCESS_CONTROLLED_DEPLOYER_ADDRESS = 0x0B5B1d92259b13D516cCd5a6E63d7D94Ea2A4836; - address private constant CREATE2_DEPLOYER_ADDRESS = 0x9df760a54b3B00cC8B3d70A37d45fa97cCfdb4Db; - address private constant CONDUIT_CONTROLLER_ADDRESS = 0x00000000F9490004C11Cef243f5400493c00Ad63; - address private constant IMMUTABLE_SEAPORT_ADDRESS = 0xbE737Cf2C122F83d1610C1224f7B99ca9d0E09f6; - bytes32 private constant IMMUTABLE_SEAPORT_DEPLOYMENT_SALT = keccak256(abi.encodePacked("immutable-seaport16")); - address private constant ZONE_ADDRESS = 0x18C12fb9c4f6165196c895aDF84230828A85B247; - bytes32 private constant ZONE_DEPLOYMENT_SALT = keccak256(abi.encodePacked("immutable-signed-zone-v3")); - address private constant SEAPORT_INITIAL_OWNER = 0xdDA0d9448Ebe3eA43aFecE5Fa6401F5795c19333; // Immutable Deployer - address private constant ZONE_INITIAL_OWNER = 0xdDA0d9448Ebe3eA43aFecE5Fa6401F5795c19333; // Immutable Deployer - - function run() external { - AccessControlledDeployer deployer = AccessControlledDeployer(ACCESS_CONTROLLED_DEPLOYER_ADDRESS); - IDeployer create2Deployer = IDeployer(CREATE2_DEPLOYER_ADDRESS); - - // Check supplied immutableSeaportAddress matches the expected address based on current creationCode - bytes memory immutableSeaportDeploymentBytecode = abi.encodePacked( - type(ImmutableSeaport).creationCode, abi.encode(CONDUIT_CONTROLLER_ADDRESS, SEAPORT_INITIAL_OWNER) - ); - address expectedIMMUTABLE_SEAPORT_ADDRESS = create2Deployer.deployedAddress( - immutableSeaportDeploymentBytecode, ACCESS_CONTROLLED_DEPLOYER_ADDRESS, IMMUTABLE_SEAPORT_DEPLOYMENT_SALT - ); - console.log("Expected ImmutableSeaport address: %s", expectedIMMUTABLE_SEAPORT_ADDRESS); - require( - expectedIMMUTABLE_SEAPORT_ADDRESS == IMMUTABLE_SEAPORT_ADDRESS, "Expected ImmutableSeaport address mismatch" - ); - - // Check supplied ZONE_ADDRESS matches the expected address based on current creationCode - bytes memory zoneDeploymentBytecode = abi.encodePacked( - type(ImmutableSignedZoneV3).creationCode, - abi.encode("ImmutableSignedZone", IMMUTABLE_SEAPORT_ADDRESS, "", "", ZONE_INITIAL_OWNER) - ); - address expectedZoneAddress = create2Deployer.deployedAddress( - zoneDeploymentBytecode, ACCESS_CONTROLLED_DEPLOYER_ADDRESS, ZONE_DEPLOYMENT_SALT - ); - console.log("Expected ImmutableSignedZoneV3 address: %s", expectedZoneAddress); - require(expectedZoneAddress == ZONE_ADDRESS, "Expected ImmutableSignedZoneV3 address mismatch"); - - vm.startBroadcast(); - - // Deploy zone if it doesn't already exist - if (ZONE_ADDRESS.code.length == 0) { - console.log("Deploying ImmutableSignedZoneV3"); - address deployedZoneAddress = deployer.deploy(create2Deployer, zoneDeploymentBytecode, ZONE_DEPLOYMENT_SALT); - require(deployedZoneAddress == ZONE_ADDRESS, "Deployed ImmutableSignedZoneV3 address mismatch"); - } else { - console.log("Skipping ImmutableSignedZoneV3, already exists"); - } - - vm.stopBroadcast(); - } -} - -// forge script script/trading/seaport16/DeployImmutableSignedZoneV3.s.sol --rpc-url "https://rpc.testnet.immutable.com" -vvvv --priority-gas-price 10000000000 --with-gas-price 11000000000 --private-key=xx diff --git a/script/trading/seaport16/README.md b/script/trading/seaport16/README.md deleted file mode 100644 index 558f8c36..00000000 --- a/script/trading/seaport16/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Seaport Deployment Scripts - -This directory contains scripts for deploying a set of contracts required to support a Seaport 1.6 trading system. It assumes a ledger wallet is used. - -## Environment Variables - -The following environment variables must be specified for all scripts. They can be supplied vai the environment or a `.env` file. - -* `DRY_RUN`: `true` or `false`. -* `IMMUTABLE_NETWORK`: Must be `mainnet` for Immutable zkEVM Mainnet or `testnet` for Testnet. -* `HD_PATH`: Hierarchical Deterministic path for the ledger wallet. Should of the form `HD_PATH="m/44'/60'/0'/0/0"`. -* `BLOCKSCOUT_APIKEY`: API key for verifying contracts on Blockscout. The key for use with Immutable zkEVM Mainnet will be different to the one used for Testnet. API keys for Immtuable zkEVM Mainnet can be obtained in the [block explorer](https://explorer.immutable.com/account/api-key). - -## Deployment - -Deploy via the following command: - -`./script/trading/seaport16/deploy.sh ` - -where `CONTRACT_TO_DEPLOY` is one of `ConduitController`, `ImmutableSeaport` or `ImmutableSignedZoneV3`. diff --git a/script/trading/seaport16/deploy.sh b/script/trading/seaport16/deploy.sh deleted file mode 100755 index e510d41a..00000000 --- a/script/trading/seaport16/deploy.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/bash - -# Load the .env file if it exists -if [ -f .env ] -then - set -a; source .env; set +a -fi - -if [ -z "${DRY_RUN}" ]; then - echo "Error: DRY_RUN variable is not set" - exit 1 -fi - -if [[ "$DRY_RUN" == "true" ]]; then - echo "Dry run mode" -elif [[ "$DRY_RUN" == "false" ]]; then - echo "Broadcast mode" -else - echo "Error: DRY_RUN must be either true or false" - exit 1 -fi - -if [ -z "${IMMUTABLE_NETWORK}" ]; then - echo "Error: IMMUTABLE_NETWORK variable is not set" - exit 1 -fi - -if [[ "$IMMUTABLE_NETWORK" == "mainnet" ]]; then - echo Immutable zkEVM Mainnet Configuration - IMMUTABLE_RPC=https://rpc.immutable.com - BLOCKSCOUT_URI=https://explorer.immutable.com/api? -elif [[ "$IMMUTABLE_NETWORK" == "testnet" ]]; then - echo Immutable zkEVM Testnet Configuration - IMMUTABLE_RPC=https://rpc.testnet.immutable.com - BLOCKSCOUT_URI=https://explorer.testnet.immutable.com/api? -else - echo "Error: IMMUTABLE_NETWORK must be either mainnet or testnet" - exit 1 -fi - -if [ -z "${HD_PATH}" ]; then - echo "Error: HD_PATH environment variable is not set" - exit 1 -fi - -if [ -z "${BLOCKSCOUT_APIKEY}" ]; then - echo "Error: BLOCKSCOUT_APIKEY environment variable is not set" - exit 1 -fi - -# Check if an argument was provided -if [ -z "$1" ]; then - echo "Error: No argument provided." - echo "Usage: $0 " - exit 1 -fi - -contract_to_deploy="$1" -if [[ "$contract_to_deploy" == "ConduitController" ]]; then - script=script/trading/seaport16/DeployConduitController.s.sol -elif [[ "$contract_to_deploy" == "ImmutableSeaport" ]]; then - script=script/trading/seaport16/DeployImmutableSeaport.s.sol -elif [[ "$contract_to_deploy" == "ImmutableSignedZoneV3" ]]; then - script=script/trading/seaport16/DeployImmutableSignedZoneV3.s.sol -else - echo "Error: contract to deploy must be either ConduitController, ImmutableSeaport or ImmutableSignedZoneV3" - exit 1 -fi - -echo "Configuration" -echo " DRY_RUN: $DRY_RUN" -echo " IMMUTABLE_RPC: $IMMUTABLE_RPC" -echo " BLOCKSCOUT_URI: $BLOCKSCOUT_URI" -echo " BLOCKSCOUT_APIKEY: $BLOCKSCOUT_APIKEY" -echo " Script to execute: $script" - -# NOTE WELL --------------------------------------------- -# Add resume option if the script fails part way through: -# --resume \ -# To record the transactions but not execute them, remove the --broadcast line. -# NOTE WELL --------------------------------------------- -if [[ "$DRY_RUN" == "true" ]]; then - forge script \ - --rpc-url $IMMUTABLE_RPC \ - --priority-gas-price 10000000000 \ - --with-gas-price 10000000100 \ - -vvvv \ - --ledger \ - --hd-paths "$HD_PATH" \ - $script -else - forge script \ - --rpc-url $IMMUTABLE_RPC \ - --priority-gas-price 10000000000 \ - --with-gas-price 10000000100 \ - -vvvv \ - --broadcast \ - --verify \ - --verifier blockscout \ - --verifier-url $BLOCKSCOUT_URI$BLOCKSCOUT_APIKEY \ - --ledger \ - --hd-paths "$HD_PATH" \ - $script -fi diff --git a/test/allowlist/AllowlistImmutableERC721TransferApprovals.t.sol b/test/allowlist/AllowlistImmutableERC721TransferApprovals.t.sol index 24d2f63f..e9c7863a 100644 --- a/test/allowlist/AllowlistImmutableERC721TransferApprovals.t.sol +++ b/test/allowlist/AllowlistImmutableERC721TransferApprovals.t.sol @@ -4,7 +4,7 @@ import {Test} from "forge-std/Test.sol"; import {MockWallet} from "../../contracts/mocks/MockWallet.sol"; import {MockFactory} from "../../contracts/mocks/MockFactory.sol"; -import {ImmutableERC721} from "../../contracts/token/erc721/preset/ImmutableERC721.sol"; +import {ImmutableERC721V2} from "../../contracts/token/erc721/preset/ImmutableERC721V2.sol"; import {OperatorAllowlistUpgradeable} from "../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; import {DeployOperatorAllowlist} from "../utils/DeployAllowlistProxy.sol"; import {DeploySCWallet} from "../utils/DeploySCW.sol"; @@ -15,7 +15,7 @@ import {MockOnReceive} from "../../contracts/mocks/MockOnReceive.sol"; contract AllowlistERC721TransferApprovals is Test { OperatorAllowlistUpgradeable public allowlist; - ImmutableERC721 public immutableERC721; + ImmutableERC721V2 public immutableERC721; DeploySCWallet public deploySCWScript; DeployMockMarketPlace public deployMockMarketPlaceScript; MockMarketplace public mockMarketPlace; @@ -41,7 +41,7 @@ contract AllowlistERC721TransferApprovals is Test { allowlist = OperatorAllowlistUpgradeable(proxyAddr); - immutableERC721 = new ImmutableERC721( + immutableERC721 = new ImmutableERC721V2( admin, "test", "USDC", "test-base-uri", "test-contract-uri", address(allowlist), feeReceiver, 0 ); diff --git a/test/allowlist/OperatorAllowlistUpgradeable.t.sol b/test/allowlist/OperatorAllowlistUpgradeable.t.sol index 7fc1de94..fbef7a33 100644 --- a/test/allowlist/OperatorAllowlistUpgradeable.t.sol +++ b/test/allowlist/OperatorAllowlistUpgradeable.t.sol @@ -5,14 +5,14 @@ pragma solidity >=0.8.19 <0.8.29; import {Test} from "forge-std/Test.sol"; import {OperatorAllowlistUpgradeable} from "../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; import {MockOperatorAllowlistUpgradeable} from "./MockOAL.sol"; -import {ImmutableERC721} from "../../contracts/token/erc721/preset/ImmutableERC721.sol"; +import {ImmutableERC721V2} from "../../contracts/token/erc721/preset/ImmutableERC721V2.sol"; import {DeployOperatorAllowlist} from "../utils/DeployAllowlistProxy.sol"; import {DeploySCWallet} from "../utils/DeploySCW.sol"; import {IWalletProxy} from "../../contracts/allowlist/IWalletProxy.sol"; contract OperatorAllowlistTest is Test, OperatorAllowlistUpgradeable { OperatorAllowlistUpgradeable public allowlist; - ImmutableERC721 public immutableERC721; + ImmutableERC721V2 public immutableERC721; MockOperatorAllowlistUpgradeable public oalV2; DeploySCWallet public deploySCWScript; @@ -34,7 +34,7 @@ contract OperatorAllowlistTest is Test, OperatorAllowlistUpgradeable { allowlist = OperatorAllowlistUpgradeable(proxyAddr); - immutableERC721 = new ImmutableERC721( + immutableERC721 = new ImmutableERC721V2( admin, "test", "USDC", "test-base-uri", "test-contract-uri", address(allowlist), feeReceiver, 0 ); diff --git a/test/deployer/AccessControlledDeployer.t.sol b/test/deployer/AccessControlledDeployer.t.sol index f4aeec85..66c182ef 100644 --- a/test/deployer/AccessControlledDeployer.t.sol +++ b/test/deployer/AccessControlledDeployer.t.sol @@ -12,14 +12,12 @@ import { ERC20MintableBurnableInit } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/test/token/ERC20MintableBurnableInit.sol"; -import {OwnableCreate2Deployer} from "../../contracts/deployer/create2/OwnableCreate2Deployer.sol"; import {AccessControlledDeployer} from "../../contracts/deployer/AccessControlledDeployer.sol"; import {OwnableCreate3Deployer} from "../../contracts/deployer/create3/OwnableCreate3Deployer.sol"; -import {Create2Utils} from "./create2/Create2Utils.sol"; import {Create3Utils} from "./create3/Create3Utils.sol"; -contract AccessControlledDeployerTest is Test, Create2Utils, Create3Utils { +contract AccessControlledDeployerTest is Test, Create3Utils { address private roleAdmin = makeAddr("admin"); address private ownershipManager = makeAddr("ownershipManager"); address private pauser = makeAddr("pauser"); @@ -108,49 +106,12 @@ contract AccessControlledDeployerTest is Test, Create2Utils, Create3Utils { assertFalse(rbacDeployer.hasRole(rbacDeployer.OWNERSHIP_MANAGER_ROLE(), ownershipManager)); } - function test_RevertIf_TransferDeployerOwnership_ByNonAdmin() public { - OwnableCreate2Deployer create2Deployer = new OwnableCreate2Deployer(address(rbacDeployer)); - vm.expectRevert(); - rbacDeployer.transferOwnershipOfDeployer(create2Deployer, makeAddr("newOwner2")); - } - - function test_RevertIf_TransferDeployerOwnership_ByRoleAdmin() public { - OwnableCreate2Deployer create2Deployer = new OwnableCreate2Deployer(address(rbacDeployer)); - vm.startPrank(roleAdmin); - vm.expectRevert(); - rbacDeployer.transferOwnershipOfDeployer(create2Deployer, makeAddr("newOwner2")); - } - - function test_RevertIf_TransferDeployerOwnership_WithZeroOwnerAddress() public { - OwnableCreate2Deployer create2Deployer = new OwnableCreate2Deployer(address(rbacDeployer)); - vm.startPrank(ownershipManager); - vm.expectRevert(ZeroAddress.selector); - rbacDeployer.transferOwnershipOfDeployer(create2Deployer, address(0)); - } - - function test_RevertIf_TransferDeployerOwnership_WhenNotCurrentOwner() public { - OwnableCreate2Deployer create2Deployer = new OwnableCreate2Deployer(makeAddr("currentOwner")); - vm.startPrank(ownershipManager); - vm.expectRevert(NotOwnerOfDeployer.selector); - rbacDeployer.transferOwnershipOfDeployer(create2Deployer, makeAddr("newOwner2")); - } - function test_RevertIf_TransferDeployerOwnership_WithZeroDeployerAddress() public { vm.startPrank(ownershipManager); vm.expectRevert(ZeroAddress.selector); rbacDeployer.transferOwnershipOfDeployer(Ownable(address(0)), makeAddr("newOwner2")); } - function test_TransferDeployerOwnership_ForOwnableCreate2Deployer() public { - OwnableCreate2Deployer create2Deployer = new OwnableCreate2Deployer(address(rbacDeployer)); - assertTrue(create2Deployer.owner() == address(rbacDeployer)); - - address newOwner = makeAddr("newOwner"); - vm.startPrank(ownershipManager); - rbacDeployer.transferOwnershipOfDeployer(create2Deployer, newOwner); - assertTrue(create2Deployer.owner() == newOwner); - } - function test_TransferDeployerOwnership_ForOwnableCreate3Deployer() public { OwnableCreate3Deployer create3Deployer = new OwnableCreate3Deployer(address(rbacDeployer)); assertTrue(create3Deployer.owner() == address(rbacDeployer)); @@ -300,53 +261,6 @@ contract AccessControlledDeployerTest is Test, Create2Utils, Create3Utils { rbacDeployer.deploy(IDeployer(address(0)), new bytes(0), bytes32(0)); } - function test_Deploy_UsingCreate2() public { - OwnableCreate2Deployer create2Deployer = new OwnableCreate2Deployer(address(rbacDeployer)); - bytes memory erc20MintableBytecode = - abi.encodePacked(type(ERC20MintableBurnable).creationCode, abi.encode("Test Token", "TEST", 10)); - bytes32 erc20MintableSalt = createSaltFromKey("erc20-mintable-burnable-v1", address(rbacDeployer)); - - address expectedAddress = predictCreate2Address( - erc20MintableBytecode, address(create2Deployer), address(rbacDeployer), erc20MintableSalt - ); - - vm.startPrank(authDeployers[0]); - vm.expectEmit(); - emit Deployed(expectedAddress, address(rbacDeployer), erc20MintableSalt, keccak256(erc20MintableBytecode)); - address deployedAddress = rbacDeployer.deploy(create2Deployer, erc20MintableBytecode, erc20MintableSalt); - ERC20MintableBurnable deployed = ERC20MintableBurnable(deployedAddress); - - assertEq(deployedAddress, expectedAddress, "deployed address does not match expected"); - assertEq(deployed.name(), "Test Token", "deployed contract does not match expected"); - assertEq(deployed.symbol(), "TEST", "deployed contract does not match expected"); - assertEq(deployed.decimals(), 10, "deployed contract does not match expected"); - } - - function test_DeployAndInit_UsingCreate2() public { - OwnableCreate2Deployer create2Deployer = new OwnableCreate2Deployer(address(rbacDeployer)); - bytes memory mintableInitBytecode = - abi.encodePacked(type(ERC20MintableBurnableInit).creationCode, abi.encode(10)); - - bytes32 mintableInitSalt = createSaltFromKey("erc20-mintable-burnable-init-v1", address(rbacDeployer)); - - address expectedAddress = predictCreate2Address( - mintableInitBytecode, address(create2Deployer), address(rbacDeployer), mintableInitSalt - ); - - bytes memory initPayload = abi.encodeWithSelector(ERC20MintableBurnableInit.init.selector, "Test Token", "TEST"); - vm.startPrank(authDeployers[0]); - vm.expectEmit(); - emit Deployed(expectedAddress, address(rbacDeployer), mintableInitSalt, keccak256(mintableInitBytecode)); - address deployedAddress = - rbacDeployer.deployAndInit(create2Deployer, mintableInitBytecode, mintableInitSalt, initPayload); - ERC20MintableBurnableInit deployed = ERC20MintableBurnableInit(deployedAddress); - - assertEq(deployedAddress, expectedAddress, "deployed address does not match expected"); - assertEq(deployed.name(), "Test Token", "deployed contract does not match expected"); - assertEq(deployed.symbol(), "TEST", "deployed contract does not match expected"); - assertEq(deployed.decimals(), 10, "deployed contract does not match expected"); - } - function test_Deploy_UsingCreate3() public { OwnableCreate3Deployer create3Deployer = new OwnableCreate3Deployer(address(rbacDeployer)); bytes memory erc20MintableBytecode = @@ -402,4 +316,8 @@ contract AccessControlledDeployerTest is Test, Create2Utils, Create3Utils { vm.expectRevert("Pausable: paused"); rbacDeployer.deployAndInit(IDeployer(address(0)), new bytes(0), bytes32(0), new bytes(0)); } + + function createSaltFromKey(string memory key, address owner) private pure returns (bytes32) { + return keccak256(abi.encode(address(owner), key)); + } } diff --git a/test/deployer/create2/Create2Utils.sol b/test/deployer/create2/Create2Utils.sol deleted file mode 100644 index 646c1c03..00000000 --- a/test/deployer/create2/Create2Utils.sol +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {Test} from "forge-std/Test.sol"; - -contract Create2Utils is Test { - function predictCreate2Address(bytes memory _bytecode, address _deployer, address _sender, bytes32 _salt) - public - pure - returns (address) - { - bytes32 deploySalt = keccak256(abi.encode(_sender, _salt)); - return address( - uint160(uint256(keccak256(abi.encodePacked(hex"ff", address(_deployer), deploySalt, keccak256(_bytecode))))) - ); - } - - function createSaltFromKey(string memory key, address owner) public pure returns (bytes32) { - return keccak256(abi.encode(address(owner), key)); - } -} diff --git a/test/deployer/create2/OwnableCreate2Deployer.t.sol b/test/deployer/create2/OwnableCreate2Deployer.t.sol deleted file mode 100644 index 8798d9ca..00000000 --- a/test/deployer/create2/OwnableCreate2Deployer.t.sol +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {Test} from "forge-std/Test.sol"; -import {IDeploy} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IDeploy.sol"; -import {ERC20Mock} from "@openzeppelin/contracts/mocks/ERC20Mock.sol"; -import { - ERC20MintableBurnable -} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/test/token/ERC20MintableBurnable.sol"; -import { - ERC20MintableBurnableInit -} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/test/token/ERC20MintableBurnableInit.sol"; -import {ContractAddress} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/ContractAddress.sol"; - -import {OwnableCreate2Deployer} from "../../../contracts/deployer/create2/OwnableCreate2Deployer.sol"; -import {Create2Utils} from "./Create2Utils.sol"; - -contract OwnableCreate2DeployerTest is Test, Create2Utils { - OwnableCreate2Deployer private factory; - bytes private erc20MockBytecode; - bytes32 private erc20MockSalt; - address private factoryOwner; - - event Deployed(address indexed deployedAddress, address indexed sender, bytes32 indexed salt, bytes32 bytecodeHash); - - using ContractAddress for address; - - function setUp() public { - factoryOwner = address(0x123); - factory = new OwnableCreate2Deployer(factoryOwner); - - erc20MockBytecode = type(ERC20Mock).creationCode; - erc20MockSalt = Create2Utils.createSaltFromKey("erc20-mock-v1", factoryOwner); - - vm.startPrank(factoryOwner); - } - - /// @dev deploying with empty bytecode should revert - function test_RevertIf_DeployWithEmptyByteCode() public { - vm.expectRevert(IDeploy.EmptyBytecode.selector); - factory.deploy("", erc20MockSalt); - } - - /// @dev only the owner should be able to deploy - function test_RevertIf_DeployWithNonOwner() public { - vm.stopPrank(); - - address nonOwner = address(0x1); - vm.startPrank(nonOwner); - vm.expectRevert("Ownable: caller is not the owner"); - factory.deploy(erc20MockBytecode, erc20MockSalt); - } - - /// @dev attempting to deploy a second contract with the same bytecode, salt and sender should revert - function test_RevertIf_DeployAlreadyDeployedCreate2Contract() public { - factory.deploy(erc20MockBytecode, erc20MockSalt); - - vm.expectRevert(IDeploy.AlreadyDeployed.selector); - factory.deploy(erc20MockBytecode, erc20MockSalt); - } - - /// @dev ensure contracts are deployed at the expected address - function test_deploy_DeploysContractAtExpectedAddress() public { - address expectedAddress = Create2Utils.predictCreate2Address( - erc20MockBytecode, address(factory), address(factoryOwner), erc20MockSalt - ); - - /// forward the nonce of the owner and the factory, to confirm they doesn't influence address - vm.setNonce(factoryOwner, vm.getNonce(factoryOwner) + 10); - vm.setNonce(address(factory), vm.getNonce(address(factory)) + 20); - - vm.expectEmit(); - emit Deployed(expectedAddress, address(factoryOwner), erc20MockSalt, keccak256(erc20MockBytecode)); - address deployedAddress = factory.deploy(erc20MockBytecode, erc20MockSalt); - - assertTrue(deployedAddress.isContract(), "contract not deployed"); - assertEq(deployedAddress, expectedAddress, "deployed address does not match expected address"); - } - - function test_deploy_DeploysContractWithConstructor() public { - bytes memory erc20MintableBytecode = - abi.encodePacked(type(ERC20MintableBurnable).creationCode, abi.encode("Test Token", "TEST", 18)); - bytes32 erc20MintableSalt = Create2Utils.createSaltFromKey("erc20-mintable-burnable-v1", factoryOwner); - - address expectedAddress = Create2Utils.predictCreate2Address( - erc20MintableBytecode, address(factory), address(factoryOwner), erc20MintableSalt - ); - - vm.expectEmit(); - emit Deployed(expectedAddress, address(factoryOwner), erc20MintableSalt, keccak256(erc20MintableBytecode)); - address deployedAddress = factory.deploy(erc20MintableBytecode, erc20MintableSalt); - ERC20MintableBurnable deployed = ERC20MintableBurnable(deployedAddress); - - assertEq(deployedAddress, expectedAddress, "deployed address does not match expected"); - assertEq(deployed.name(), "Test Token", "deployed contract does not match expected"); - assertEq(deployed.symbol(), "TEST", "deployed contract does not match expected"); - assertEq(deployed.decimals(), 18, "deployed contract does not match expected"); - } - - function test_deploy_DeploysSameContractToDifferentAddresses_GivenDifferentSalts() public { - address deployed1 = factory.deploy(erc20MockBytecode, erc20MockSalt); - - bytes32 newSalt = Create2Utils.createSaltFromKey("create2-deployer-test-v2", factoryOwner); - address deployed2 = factory.deploy(erc20MockBytecode, newSalt); - - assertEq(deployed1.code, deployed2.code, "bytecodes of deployed contracts do not match"); - assertNotEq(deployed1, deployed2, "deployed contracts should not have the same address"); - } - - function test_deploy_DeploysContractChangedOwner() public { - address newOwner = address(0x1); - - factory.transferOwnership(newOwner); - assertEq(factory.owner(), newOwner, "owner did not change as expected"); - - // check that the old owner cannot deploy - vm.expectRevert("Ownable: caller is not the owner"); - factory.deploy(erc20MockBytecode, erc20MockSalt); - - // test that the new owner can deploy - vm.startPrank(newOwner); - address expectedAddress = - Create2Utils.predictCreate2Address(erc20MockBytecode, address(factory), address(newOwner), erc20MockSalt); - - vm.expectEmit(); - emit Deployed(expectedAddress, address(newOwner), erc20MockSalt, keccak256(erc20MockBytecode)); - address deployedAddress = factory.deploy(erc20MockBytecode, erc20MockSalt); - - assertTrue(deployedAddress.isContract(), "contract not deployed"); - assertEq(deployedAddress, expectedAddress, "deployed address does not match expected address"); - } - - /** - * deployAndInit - */ - function test_RevertIf_DeployAndInitWithNonOwner() public { - vm.stopPrank(); - - address nonOwner = address(0x1); - vm.startPrank(nonOwner); - vm.expectRevert("Ownable: caller is not the owner"); - factory.deployAndInit(erc20MockBytecode, erc20MockSalt, ""); - } - - function test_deployAndInit_DeploysAndInitsContract() public { - bytes memory mintableInitBytecode = - abi.encodePacked(type(ERC20MintableBurnableInit).creationCode, abi.encode(18)); - - bytes32 mintableInitSalt = Create2Utils.createSaltFromKey("erc20-mintable-burnable-init-v1", factoryOwner); - - address expectedAddress = Create2Utils.predictCreate2Address( - mintableInitBytecode, address(factory), address(factoryOwner), mintableInitSalt - ); - - bytes memory initPayload = abi.encodeWithSelector(ERC20MintableBurnableInit.init.selector, "Test Token", "TEST"); - vm.expectEmit(); - emit Deployed(expectedAddress, address(factoryOwner), mintableInitSalt, keccak256(mintableInitBytecode)); - address deployedAddress = factory.deployAndInit(mintableInitBytecode, mintableInitSalt, initPayload); - ERC20MintableBurnableInit deployed = ERC20MintableBurnableInit(deployedAddress); - - assertEq(deployedAddress, expectedAddress, "deployed address does not match expected"); - assertEq(deployed.name(), "Test Token", "deployed contract does not match expected"); - assertEq(deployed.symbol(), "TEST", "deployed contract does not match expected"); - } - - /** - * deployedAddress - */ - function test_deployedAddress_ReturnsPredictedAddress() public { - address deployAddress = factory.deployedAddress(erc20MockBytecode, address(factoryOwner), erc20MockSalt); - - address predictedAddress = Create2Utils.predictCreate2Address( - erc20MockBytecode, address(factory), address(factoryOwner), erc20MockSalt - ); - address deployedAddress = factory.deploy(erc20MockBytecode, erc20MockSalt); - - assertEq(deployAddress, predictedAddress, "deployment address did not match predicted address"); - assertEq(deployAddress, deployedAddress, "deployment address did not match deployed address"); - } -} diff --git a/test/games/gems/GemGame.t.sol b/test/games/gems/GemGame.t.sol deleted file mode 100644 index aa7a82a2..00000000 --- a/test/games/gems/GemGame.t.sol +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {Test} from "forge-std/Test.sol"; -import {GemGame, Unauthorized, ContractPaused} from "../../../contracts/games/gems/GemGame.sol"; - -contract GemGameTest is Test { - event GemEarned(address indexed account, uint256 timestamp); - - GemGame private _gemGame; - - function setUp() public { - _gemGame = new GemGame(address(this), address(this), address(this)); - } - - function testEarnGemEmitsGemEarnedEvent() public { - vm.expectEmit(true, true, false, false); - emit GemEarned(address(this), block.timestamp); - _gemGame.earnGem(); - } - - function testEarnGemContractPausedReverts() public { - // pause the contract - _gemGame.pause(); - - // attempt to earn a gem - vm.expectRevert(ContractPaused.selector); - _gemGame.earnGem(); - } - - function testPausePausesContract() public { - // pause the contract - _gemGame.pause(); - - assertEq(_gemGame.paused(), true, "GemGame should be paused"); - } - - function testPauseWithoutPauseRoleReverts() public { - // revoke the pause role - _gemGame.revokeRole(keccak256("PAUSE"), address(this)); - - // attempt to pause - vm.expectRevert(Unauthorized.selector); - _gemGame.pause(); - } - - function testUnpauseUnpausesContract() public { - // pause the contract - _gemGame.pause(); - - // unpause the contract - _gemGame.unpause(); - - assertEq(_gemGame.paused(), false, "GemGame should be unpaused"); - } - - function testUnpauseWithoutPauseRoleReverts() public { - // revoke the unpause role - _gemGame.revokeRole(keccak256("UNPAUSE"), address(this)); - - // attempt to unpause - vm.expectRevert(Unauthorized.selector); - _gemGame.unpause(); - } -} diff --git a/test/staking/StakeHolderBase.t.sol b/test/staking/StakeHolderBase.t.sol index e5722a5b..156398d9 100644 --- a/test/staking/StakeHolderBase.t.sol +++ b/test/staking/StakeHolderBase.t.sol @@ -6,14 +6,11 @@ pragma solidity >=0.8.19 <0.8.29; import {Test} from "forge-std/Test.sol"; import {IStakeHolder} from "../../contracts/staking/IStakeHolder.sol"; import {StakeHolderBaseV2} from "../../contracts/staking/StakeHolderBaseV2.sol"; -import {StakeHolderERC20} from "../../contracts/staking/StakeHolderERC20.sol"; -import {StakeHolderERC20V2} from "../../contracts/staking/StakeHolderERC20V2.sol"; import {StakeHolderNative} from "../../contracts/staking/StakeHolderNative.sol"; import {StakeHolderNativeV2} from "../../contracts/staking/StakeHolderNativeV2.sol"; import {StakeHolderWIMX} from "../../contracts/staking/StakeHolderWIMX.sol"; import {StakeHolderWIMXV2} from "../../contracts/staking/StakeHolderWIMXV2.sol"; import {WIMX} from "../../contracts/staking/WIMX.sol"; -import {ERC20PresetFixedSupply} from "openzeppelin-contracts-4.9.3/token/ERC20/presets/ERC20PresetFixedSupply.sol"; import {ERC1967Proxy} from "openzeppelin-contracts-4.9.3/proxy/ERC1967/ERC1967Proxy.sol"; abstract contract StakeHolderBaseTest is Test { @@ -32,7 +29,6 @@ abstract contract StakeHolderBaseTest is Test { address public staker3; address public bank; - ERC20PresetFixedSupply erc20; WIMX wimxErc20; function setUp() public virtual { @@ -51,13 +47,8 @@ abstract contract StakeHolderBaseTest is Test { distributeRole = temp.DISTRIBUTE_ROLE(); } - function deployERC20() internal { - erc20 = new ERC20PresetFixedSupply("Name", "SYM", 1000 ether, bank); - } - function deployWIMX() internal { wimxErc20 = new WIMX(); - erc20 = ERC20PresetFixedSupply(address(wimxErc20)); } function deployStakeHolderNativeV1() internal { @@ -68,15 +59,6 @@ abstract contract StakeHolderBaseTest is Test { stakeHolder = IStakeHolder(address(proxy)); } - function deployStakeHolderERC20V1() internal { - StakeHolderERC20 impl = new StakeHolderERC20(); - bytes memory initData = abi.encodeWithSelector( - StakeHolderERC20.initialize.selector, roleAdmin, upgradeAdmin, distributeAdmin, address(erc20) - ); - ERC1967Proxy proxy = new ERC1967Proxy(address(impl), initData); - stakeHolder = IStakeHolder(address(proxy)); - } - function deployStakeHolderWIMXV1() internal { StakeHolderWIMX impl = new StakeHolderWIMX(); bytes memory initData = abi.encodeWithSelector( @@ -93,13 +75,6 @@ abstract contract StakeHolderBaseTest is Test { StakeHolderNativeV2(address(stakeHolder)).upgradeToAndCall(address(implV2), upgradeData); } - function upgradeToStakeHolderERC20V2() internal { - StakeHolderERC20V2 implV2 = new StakeHolderERC20V2(); - bytes memory upgradeData = abi.encodeWithSelector(StakeHolderBaseV2.upgradeStorage.selector, bytes("NotUsed")); - vm.prank(upgradeAdmin); - StakeHolderERC20V2(address(stakeHolder)).upgradeToAndCall(address(implV2), upgradeData); - } - function upgradeToStakeHolderWIMXV2() internal { StakeHolderWIMXV2 implV2 = new StakeHolderWIMXV2(); bytes memory upgradeData = abi.encodeWithSelector(StakeHolderBaseV2.upgradeStorage.selector, bytes("NotUsed")); diff --git a/test/staking/StakeHolderConfigERC20.t.sol b/test/staking/StakeHolderConfigERC20.t.sol deleted file mode 100644 index eff02fff..00000000 --- a/test/staking/StakeHolderConfigERC20.t.sol +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {StakeHolderERC20} from "../../contracts/staking/StakeHolderERC20.sol"; -import {IStakeHolder} from "../../contracts/staking/IStakeHolder.sol"; -import {StakeHolderBase} from "../../contracts/staking/StakeHolderBase.sol"; -import {StakeHolderConfigBaseTest} from "./StakeHolderConfigBase.t.sol"; - -contract StakeHolderERC20V2a is StakeHolderERC20 { - function upgradeStorage( - bytes memory /* _data */ - ) - external - override(StakeHolderBase) - { - version = 2; - } -} - -contract StakeHolderConfigERC20Test is StakeHolderConfigBaseTest { - function setUp() public override { - super.setUp(); - deployERC20(); - deployStakeHolderERC20V1(); - } - - function _deployV1() internal override returns (IStakeHolder) { - return IStakeHolder(address(new StakeHolderERC20())); - } - - function _deployV2() internal override returns (IStakeHolder) { - return IStakeHolder(address(new StakeHolderERC20V2a())); - } -} diff --git a/test/staking/StakeHolderConfigERC20V2.t.sol b/test/staking/StakeHolderConfigERC20V2.t.sol deleted file mode 100644 index 8b421ba4..00000000 --- a/test/staking/StakeHolderConfigERC20V2.t.sol +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {StakeHolderERC20} from "../../contracts/staking/StakeHolderERC20.sol"; -import {StakeHolderERC20V2} from "../../contracts/staking/StakeHolderERC20V2.sol"; -import {IStakeHolder} from "../../contracts/staking/IStakeHolder.sol"; -import {StakeHolderBaseV2} from "../../contracts/staking/StakeHolderBaseV2.sol"; -import {StakeHolderConfigBaseTestV2} from "./StakeHolderConfigBaseV2.t.sol"; -import {ERC1967Proxy} from "openzeppelin-contracts-4.9.3/proxy/ERC1967/ERC1967Proxy.sol"; - -contract StakeHolderERC20V3a is StakeHolderERC20V2 { - function upgradeStorage( - bytes memory /* _data */ - ) - external - override(StakeHolderBaseV2) - { - version = 3; - } -} - -contract StakeHolderConfigERC20TestV2 is StakeHolderConfigBaseTestV2 { - function setUp() public override { - super.setUp(); - deployERC20(); - deployStakeHolderERC20V1(); - upgradeToStakeHolderERC20V2(); - } - - function testDeployStakeHolderERC20V2() public { - // Check that V2 can be installed from scratch: that is, without upgrading form V1. - StakeHolderERC20V2 impl = new StakeHolderERC20V2(); - bytes memory initData = abi.encodeWithSelector( - StakeHolderERC20V2.initialize.selector, roleAdmin, upgradeAdmin, distributeAdmin, erc20 - ); - ERC1967Proxy proxy = new ERC1967Proxy(address(impl), initData); - IStakeHolder stakeHolderV2 = IStakeHolder(address(proxy)); - - assertEq(stakeHolderV2.version(), 2, "Incorrect version"); - } - - function _deployV1() internal override returns (IStakeHolder) { - return IStakeHolder(address(new StakeHolderERC20())); - } - - function _deployV2() internal override returns (IStakeHolder) { - return IStakeHolder(address(new StakeHolderERC20V2())); - } - - function _deployV3() internal override returns (IStakeHolder) { - return IStakeHolder(address(new StakeHolderERC20V3a())); - } -} diff --git a/test/staking/StakeHolderConfigNative.t.sol b/test/staking/StakeHolderConfigNative.t.sol deleted file mode 100644 index a206c6d6..00000000 --- a/test/staking/StakeHolderConfigNative.t.sol +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {StakeHolderNative} from "../../contracts/staking/StakeHolderNative.sol"; -import {IStakeHolder} from "../../contracts/staking/IStakeHolder.sol"; -import {StakeHolderBase} from "../../contracts/staking/StakeHolderBase.sol"; -import {StakeHolderConfigBaseTest} from "./StakeHolderConfigBase.t.sol"; - -contract StakeHolderNativeV2a is StakeHolderNative { - function upgradeStorage( - bytes memory /* _data */ - ) - external - override(StakeHolderBase) - { - version = 2; - } -} - -contract StakeHolderConfigNativeTest is StakeHolderConfigBaseTest { - function setUp() public override { - super.setUp(); - deployStakeHolderNativeV1(); - } - - function _deployV1() internal override returns (IStakeHolder) { - return IStakeHolder(address(new StakeHolderNative())); - } - - function _deployV2() internal override returns (IStakeHolder) { - return IStakeHolder(address(new StakeHolderNativeV2a())); - } -} diff --git a/test/staking/StakeHolderInitBase.t.sol b/test/staking/StakeHolderInitBase.t.sol index 83dec739..98773121 100644 --- a/test/staking/StakeHolderInitBase.t.sol +++ b/test/staking/StakeHolderInitBase.t.sol @@ -14,10 +14,6 @@ abstract contract StakeHolderInitBaseTest is StakeHolderBaseTest { assertEq(stakeHolder.getNumStakers(), 0, "Expect no stakers at deployment time"); } - function testGetToken() public view { - assertEq(stakeHolder.getToken(), address(erc20), "Incorrect token address returned"); - } - function testAdmins() public view { assertEq(stakeHolder.getRoleMemberCount(defaultAdminRole), 1, "Expect one role admin"); assertEq(stakeHolder.getRoleMemberCount(upgradeRole), 1, "Expect one upgrade admin"); diff --git a/test/staking/StakeHolderInitERC20.t.sol b/test/staking/StakeHolderInitERC20.t.sol deleted file mode 100644 index 4bf684ec..00000000 --- a/test/staking/StakeHolderInitERC20.t.sol +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {StakeHolderInitBaseTest} from "./StakeHolderInitBase.t.sol"; - -contract StakeHolderInitERC20Test is StakeHolderInitBaseTest { - function setUp() public override { - super.setUp(); - deployStakeHolderERC20V1(); - } -} diff --git a/test/staking/StakeHolderInitERC20V2.t.sol b/test/staking/StakeHolderInitERC20V2.t.sol deleted file mode 100644 index ca935fd3..00000000 --- a/test/staking/StakeHolderInitERC20V2.t.sol +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {StakeHolderInitBaseTestV2} from "./StakeHolderInitBaseV2.t.sol"; - -contract StakeHolderInitERC20TestV2 is StakeHolderInitBaseTestV2 { - function setUp() public override { - super.setUp(); - deployStakeHolderERC20V1(); - upgradeToStakeHolderERC20V2(); - } -} diff --git a/test/staking/StakeHolderInitNative.t.sol b/test/staking/StakeHolderInitNative.t.sol deleted file mode 100644 index 40102cec..00000000 --- a/test/staking/StakeHolderInitNative.t.sol +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {StakeHolderInitBaseTest} from "./StakeHolderInitBase.t.sol"; - -contract StakeHolderInitNativeTest is StakeHolderInitBaseTest { - function setUp() public override { - super.setUp(); - deployStakeHolderNativeV1(); - } -} diff --git a/test/staking/StakeHolderOperationalERC20.t.sol b/test/staking/StakeHolderOperationalERC20.t.sol deleted file mode 100644 index 4068d7a7..00000000 --- a/test/staking/StakeHolderOperationalERC20.t.sol +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {IStakeHolder} from "../../contracts/staking/IStakeHolder.sol"; -import {StakeHolderOperationalBaseTest} from "./StakeHolderOperationalBase.t.sol"; - -contract StakeHolderOperationalERC20Test is StakeHolderOperationalBaseTest { - function setUp() public virtual override { - super.setUp(); - deployERC20(); - deployStakeHolderERC20V1(); - } - - function testStakeWithValue() public { - uint256 amount = 100 ether; - vm.deal(staker1, amount); - _deal(staker1, amount); - - vm.prank(staker1); - erc20.approve(address(stakeHolder), amount); - vm.expectRevert(abi.encodeWithSelector(IStakeHolder.NonPayable.selector)); - vm.prank(staker1); - stakeHolder.stake{value: amount}(amount); - } - - function _deal(address _to, uint256 _amount) internal override { - vm.prank(bank); - bool success = erc20.transfer(_to, _amount); - require(success, "Unexpected ERC 20 transfer failure"); - } - - function _addStake(address _staker, uint256 _amount, bool _hasError, bytes memory _error) internal override { - vm.prank(_staker); - erc20.approve(address(stakeHolder), _amount); - if (_hasError) { - vm.expectRevert(_error); - } - vm.prank(_staker); - stakeHolder.stake(_amount); - } - - function _distributeRewards( - address _distributor, - uint256 _total, - IStakeHolder.AccountAmount[] memory _accountAmounts, - bool _hasError, - bytes memory _error - ) internal override { - vm.prank(_distributor); - erc20.approve(address(stakeHolder), _total); - if (_hasError) { - vm.expectRevert(_error); - } - vm.prank(_distributor); - stakeHolder.distributeRewards(_accountAmounts); - } - - function _getBalanceStaker(address _staker) internal view override returns (uint256) { - return erc20.balanceOf(_staker); - } - - function _getBalanceStakeHolderContract() internal view override returns (uint256) { - return erc20.balanceOf(address(stakeHolder)); - } -} diff --git a/test/staking/StakeHolderOperationalERC20V2.t.sol b/test/staking/StakeHolderOperationalERC20V2.t.sol deleted file mode 100644 index 4ce54674..00000000 --- a/test/staking/StakeHolderOperationalERC20V2.t.sol +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {IStakeHolder} from "../../contracts/staking/IStakeHolder.sol"; -import {IStakeHolderV2} from "../../contracts/staking/IStakeHolderV2.sol"; -import {StakeHolderBaseTest} from "./StakeHolderBase.t.sol"; -import {StakeHolderOperationalBaseTestV2} from "./StakeHolderOperationalBaseV2.t.sol"; -import {StakeHolderOperationalERC20Test} from "./StakeHolderOperationalERC20.t.sol"; - -contract StakeHolderOperationalERC20TestV2 is StakeHolderOperationalERC20Test, StakeHolderOperationalBaseTestV2 { - function setUp() public override(StakeHolderOperationalERC20Test, StakeHolderBaseTest) { - StakeHolderOperationalERC20Test.setUp(); - upgradeToStakeHolderERC20V2(); - } - - function _stakeFor( - address _distributor, - uint256 _total, - IStakeHolder.AccountAmount[] memory _accountAmounts, - bool _hasError, - bytes memory _error - ) internal override { - vm.prank(_distributor); - erc20.approve(address(stakeHolder), _total); - if (_hasError) { - vm.expectRevert(_error); - } - vm.prank(_distributor); - IStakeHolderV2(address(stakeHolder)).stakeFor(_accountAmounts); - } -} diff --git a/test/staking/StakeHolderTimeDelayERC20.t.sol b/test/staking/StakeHolderTimeDelayERC20.t.sol deleted file mode 100644 index dc83c9e7..00000000 --- a/test/staking/StakeHolderTimeDelayERC20.t.sol +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {StakeHolderERC20} from "../../contracts/staking/StakeHolderERC20.sol"; -import {StakeHolderERC20V2} from "../../contracts/staking/StakeHolderERC20V2.sol"; -import {IStakeHolder} from "../../contracts/staking/IStakeHolder.sol"; -import {StakeHolderTimeDelayBaseTest} from "./StakeHolderTimeDelayBase.t.sol"; -import {ERC1967Proxy} from "openzeppelin-contracts-4.9.3/proxy/ERC1967/ERC1967Proxy.sol"; -import {StakeHolderBaseV2} from "../../contracts/staking/StakeHolderBaseV2.sol"; - -contract StakeHolderERC20V3a is StakeHolderERC20V2 { - function upgradeStorage( - bytes memory /* _data */ - ) - external - override(StakeHolderBaseV2) - { - version = 3; - } -} - -contract StakeHolderTimeDelayERC20Test is StakeHolderTimeDelayBaseTest { - function setUp() public override { - super.setUp(); - deployERC20(); - - StakeHolderERC20 impl = new StakeHolderERC20(); - bytes memory initData = abi.encodeWithSelector( - StakeHolderERC20.initialize.selector, - address(stakeHolderTimeDelay), - address(stakeHolderTimeDelay), - distributeAdmin, - address(erc20) - ); - ERC1967Proxy proxy = new ERC1967Proxy(address(impl), initData); - stakeHolder = IStakeHolder(address(proxy)); - } - - function _deployV2() internal override returns (IStakeHolder) { - return IStakeHolder(address(new StakeHolderERC20V2())); - } - - function _deployV3() internal override returns (IStakeHolder) { - return IStakeHolder(address(new StakeHolderERC20V3a())); - } -} diff --git a/test/staking/StakeHolderUpgradeForkTest.t.sol b/test/staking/StakeHolderUpgradeForkTest.t.sol deleted file mode 100644 index 391d1914..00000000 --- a/test/staking/StakeHolderUpgradeForkTest.t.sol +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {Test} from "forge-std/Test.sol"; -import {StakeHolderWIMX} from "../../contracts/staking/StakeHolderWIMX.sol"; -import {StakeHolderWIMXV2} from "../../contracts/staking/StakeHolderWIMXV2.sol"; -import {TimelockController} from "openzeppelin-contracts-4.9.3/governance/TimelockController.sol"; -import {UUPSUpgradeable} from "openzeppelin-contracts-upgradeable-4.9.3/proxy/utils/UUPSUpgradeable.sol"; -import {StakeHolderBase} from "../../contracts/staking/StakeHolderBase.sol"; - -contract StakeHolderUpgradeForkTest is Test { - string constant MAINNET_RPC_URL = "https://rpc.immutable.com/"; - address constant STAKE_HOLDER_PROXY = 0xb6c2aA8690C8Ab6AC380a0bb798Ab0debe5C4C38; - address constant TIMELOCK_CONTROLLER = 0x994a66607f947A47F33C2fA80e0470C03C30e289; - address constant WIMX = 0x3A0C2Ba54D6CBd3121F01b96dFd20e99D1696C9D; - - bytes32 constant PROPOSER_ROLE = 0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1; - bytes32 constant EXECUTOR_ROLE = 0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63; - - address constant PROPOSER = 0xaA53161A1fD22b258c89bA76B4bA11019034612D; - address constant EXECUTOR = 0xaA53161A1fD22b258c89bA76B4bA11019034612D; - - uint256 constant TIMELOCK_DELAY = 604800; - - uint256 constant STAKERS_TO_CHECK = 100; - - StakeHolderWIMX stakeHolder; - TimelockController stakeHolderTimeDelay; - - // Put the variables below into storage so we don't need to worry about - // stack depth issues. - address stakingTokenAddress; - uint256 numStakers; - address[] stakers; - uint256[] stakersBalances; - address stakingTokenAddress2; - - function setUp() public { - uint256 mainnetFork = vm.createFork(MAINNET_RPC_URL); - vm.selectFork(mainnetFork); - stakeHolder = StakeHolderWIMX(payable(STAKE_HOLDER_PROXY)); - stakeHolderTimeDelay = TimelockController(payable(TIMELOCK_CONTROLLER)); - } - - function testUpgradeToV2() public { - if (stakeHolder.version() != 0) { - // Don't run the rest of this test after the upgrade happens. - // It would fail if we were to run the test. - return; - } - //console.log("Executing Staking Contract Upgrade Fork Test"); - - stakingTokenAddress = stakeHolder.getToken(); - assertEq(WIMX, stakingTokenAddress, "WIMX address incorrect prior to upgrade"); - - numStakers = stakeHolder.getNumStakers(); - assertGe(numStakers, 822, "Wrong number of stakers"); - { - address[] memory stakersMem = stakeHolder.getStakers(0, STAKERS_TO_CHECK); - for (uint256 i = 0; i < STAKERS_TO_CHECK; i++) { - stakers.push(stakersMem[i]); - } - } - for (uint256 i = 0; i < stakers.length; i++) { - stakersBalances.push(stakeHolder.getBalance(stakers[i])); - } - - uint256 delay = stakeHolderTimeDelay.getMinDelay(); - assertEq(delay, TIMELOCK_DELAY, "Unexpected timelock delay"); - assertTrue(stakeHolderTimeDelay.hasRole(PROPOSER_ROLE, PROPOSER), "Proposer does not have proposer role"); - assertTrue(stakeHolderTimeDelay.hasRole(EXECUTOR_ROLE, EXECUTOR), "Executor does not have executor role"); - - StakeHolderWIMXV2 v2Impl = new StakeHolderWIMXV2(); - - bytes memory callData = abi.encodeWithSelector(StakeHolderBase.upgradeStorage.selector, bytes("")); - bytes memory upgradeCall = - abi.encodeWithSelector(UUPSUpgradeable.upgradeToAndCall.selector, address(v2Impl), callData); - - address target = address(stakeHolder); - uint256 value = 0; - bytes memory data = upgradeCall; - bytes32 predecessor = bytes32(0); - bytes32 salt = bytes32(uint256(1)); - uint256 theDelay = delay; - - uint256 timeNow = block.timestamp; - - vm.prank(PROPOSER); - stakeHolderTimeDelay.schedule(target, value, data, predecessor, salt, theDelay); - - vm.warp(timeNow + delay); - - vm.prank(EXECUTOR); - stakeHolderTimeDelay.execute(target, value, data, predecessor, salt); - - assertEq(stakeHolder.version(), 2, "Upgrade did not upgrade version 2"); - - stakingTokenAddress2 = stakeHolder.getToken(); - assertEq(stakingTokenAddress, stakingTokenAddress2, "Staking token address incorrect after upgrade"); - - uint256 numStakers2 = stakeHolder.getNumStakers(); - assertGe(numStakers2, numStakers, "After upgrade: Wrong number of stakers"); - address[] memory stakers2 = stakeHolder.getStakers(0, stakers.length); - - for (uint256 i = 0; i < stakers.length; i++) { - assertEq(stakers[i], stakers2[i], "Stakers arrays don't match"); - } - for (uint256 i = 0; i < stakers.length; i++) { - uint256 bal = stakeHolder.getBalance(stakers[i]); - assertEq(bal, stakersBalances[i], "Balance changed"); - } - } -} diff --git a/test/token/erc721/ERC721ConfigByQuantityV1.t.sol b/test/token/erc721/ERC721ConfigByQuantityV1.t.sol deleted file mode 100644 index 5286adff..00000000 --- a/test/token/erc721/ERC721ConfigByQuantityV1.t.sol +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {ERC721ConfigByQuantityBaseTest} from "./ERC721ConfigByQuantityBase.t.sol"; -import {ImmutableERC721} from "../../../contracts/token/erc721/preset/ImmutableERC721.sol"; -import {IImmutableERC721ByQuantity} from "../../../contracts/token/erc721/interfaces/IImmutableERC721ByQuantity.sol"; -import {IImmutableERC721} from "../../../contracts/token/erc721/interfaces/IImmutableERC721.sol"; - -contract ERC721ConfigByQuantityV1Test is ERC721ConfigByQuantityBaseTest { - function setUp() public virtual override { - super.setUp(); - - ImmutableERC721 immutableERC721 = new ImmutableERC721( - owner, name, symbol, baseURI, contractURI, address(allowlist), feeReceiver, feeNumerator - ); - - // ImmutableERC721 does not implement the interface, and hence must be cast to the - // interface type. - erc721 = IImmutableERC721(address(immutableERC721)); - erc721BQ = IImmutableERC721ByQuantity(address(immutableERC721)); - - vm.prank(owner); - erc721.grantMinterRole(minter); - } -} diff --git a/test/token/erc721/ERC721OperationalByQuantityV1.t.sol b/test/token/erc721/ERC721OperationalByQuantityV1.t.sol deleted file mode 100644 index 38697cc1..00000000 --- a/test/token/erc721/ERC721OperationalByQuantityV1.t.sol +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -import {ERC721OperationalByQuantityBaseTest} from "./ERC721OperationalByQuantityBase.t.sol"; -import {ImmutableERC721} from "../../../contracts/token/erc721/preset/ImmutableERC721.sol"; -import {IImmutableERC721ByQuantity} from "../../../contracts/token/erc721/interfaces/IImmutableERC721ByQuantity.sol"; -import {IImmutableERC721} from "../../../contracts/token/erc721/interfaces/IImmutableERC721.sol"; -import {IImmutableERC721Errors} from "../../../contracts/errors/Errors.sol"; - -// Test the original ImmutableERC721 contract: Operational tests -contract ERC721OperationalByQuantityV1Test is ERC721OperationalByQuantityBaseTest { - function setUp() public virtual override { - super.setUp(); - - ImmutableERC721 immutableERC721 = new ImmutableERC721( - owner, name, symbol, baseURI, contractURI, address(allowlist), feeReceiver, feeNumerator - ); - - // ImmutableERC721 does not implement the interface, and hence must be cast to the - // interface type. - erc721BQ = IImmutableERC721ByQuantity(address(immutableERC721)); - erc721 = IImmutableERC721(address(immutableERC721)); - - vm.prank(owner); - erc721.grantMinterRole(minter); - } - - function notOwnedRevertError(uint256 _tokenIdToBeBurned) public pure override returns (bytes memory) { - return - abi.encodeWithSelector( - IImmutableERC721Errors.IImmutableERC721NotOwnerOrOperator.selector, _tokenIdToBeBurned - ); - } -} diff --git a/test/trading/seaport16/ImmutableSeaportBase.t.sol b/test/trading/seaport16/ImmutableSeaportBase.t.sol deleted file mode 100644 index 021f1163..00000000 --- a/test/trading/seaport16/ImmutableSeaportBase.t.sol +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.13; - -import {Test} from "forge-std/Test.sol"; -import {ImmutableSeaport} from "../../../contracts/trading/seaport16/ImmutableSeaport.sol"; -import { - ImmutableSignedZoneV3 -} from "../../../contracts/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.sol"; - -import {ConduitController} from "seaport-core-16/src/conduit/ConduitController.sol"; -import {Conduit} from "seaport-core-16/src/conduit/Conduit.sol"; - -abstract contract ImmutableSeaportBaseTest is Test { - event AllowedZoneSet(address zoneAddress, bool allowed); - - ImmutableSeaport public immutableSeaport; - ImmutableSignedZoneV3 public immutableSignedZone; - ConduitController public conduitController; - Conduit public conduit; - bytes32 public conduitKey; - address public conduitAddress; - address public owner; - address public zoneManager; - address public immutableSigner; - uint256 public immutableSignerPkey; - address public buyer; - address public seller; - uint256 public buyerPkey; - uint256 public sellerPkey; - - function setUp() public virtual { - // Set up chain ID - //uint256 chainId = block.chainid; - - // Create test addresses - owner = makeAddr("owner"); - zoneManager = makeAddr("zoneManager"); - (immutableSigner, immutableSignerPkey) = makeAddrAndKey("immutableSigner"); - (buyer, buyerPkey) = makeAddrAndKey("buyer"); - (seller, sellerPkey) = makeAddrAndKey("seller"); - - // Deploy contracts - - // 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. - conduitKey = bytes32(uint256(uint160(owner)) << (256 - 160)); - conduitController = new ConduitController(); - vm.prank(owner); - conduitController.createConduit(conduitKey, owner); - bool exists; - (conduitAddress, exists) = conduitController.getConduit(conduitKey); - assertTrue(exists, "Condiut contract does not exist"); - conduit = Conduit(conduitAddress); - - immutableSeaport = new ImmutableSeaport(address(conduitController), owner); - - immutableSignedZone = new ImmutableSignedZoneV3("ImmutableSignedZone", address(immutableSeaport), "", "", owner); - bytes32 zoneManagerRole = immutableSignedZone.ZONE_MANAGER_ROLE(); - vm.prank(owner); - immutableSignedZone.grantRole(zoneManagerRole, zoneManager); - vm.prank(zoneManager); - immutableSignedZone.addSigner(immutableSigner); - - vm.prank(owner); - immutableSeaport.setAllowedZone(address(immutableSignedZone), true); - vm.prank(owner); - conduitController.updateChannel(conduitAddress, address(immutableSeaport), true); - } -} diff --git a/test/trading/seaport16/ImmutableSeaportConfig.t.sol b/test/trading/seaport16/ImmutableSeaportConfig.t.sol deleted file mode 100644 index f5ec8845..00000000 --- a/test/trading/seaport16/ImmutableSeaportConfig.t.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {ImmutableSeaportBaseTest} from "./ImmutableSeaportBase.t.sol"; -import {ImmutableSeaport} from "../../../contracts/trading/seaport16/ImmutableSeaport.sol"; - -contract ImmutableSeaportConfigTest is ImmutableSeaportBaseTest { - function testEmitsAllowedZoneSetEvent() public { - address zone = makeAddr("zone"); - bool allowed = true; - - vm.prank(owner); - vm.expectEmit(true, true, true, true); - emit AllowedZoneSet(zone, allowed); - immutableSeaport.setAllowedZone(zone, allowed); - } - - function testRejectZeroAddressZone() public { - vm.prank(owner); - vm.expectRevert("ImmutableSeaport: zone is the zero address"); - immutableSeaport.setAllowedZone(address(0), true); - } - - function testRejectAllowedZoneAlreadySetToTrue() public { - address zone = makeAddr("zone"); - - vm.prank(owner); - immutableSeaport.setAllowedZone(zone, true); - - vm.prank(owner); - vm.expectRevert(abi.encodeWithSelector(ImmutableSeaport.AllowedZoneAlreadySet.selector, zone, true)); - immutableSeaport.setAllowedZone(zone, true); - } - - function testRejectAllowedZoneAlreadySetToFalse() public { - address zone = makeAddr("zone"); - - vm.prank(owner); - vm.expectRevert(abi.encodeWithSelector(ImmutableSeaport.AllowedZoneAlreadySet.selector, zone, false)); - immutableSeaport.setAllowedZone(zone, false); - } -} diff --git a/test/trading/seaport16/ImmutableSeaportHarness.t.sol b/test/trading/seaport16/ImmutableSeaportHarness.t.sol deleted file mode 100644 index 317d0b45..00000000 --- a/test/trading/seaport16/ImmutableSeaportHarness.t.sol +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; - -import {ImmutableSeaport} from "../../../contracts/trading/seaport16/ImmutableSeaport.sol"; - -contract ImmutableSeaportHarness is ImmutableSeaport { - constructor(address conduitController, address owner) ImmutableSeaport(conduitController, owner) {} - - function exposed_domainSeparator() external view returns (bytes32) { - return _domainSeparator(); - } - - function exposed_deriveEIP712Digest(bytes32 domainSeparator, bytes32 orderHash) - external - pure - returns (bytes32 value) - { - return _deriveEIP712Digest(domainSeparator, orderHash); - } -} - diff --git a/test/trading/seaport16/ImmutableSeaportOperational.t.sol b/test/trading/seaport16/ImmutableSeaportOperational.t.sol deleted file mode 100644 index 4d29abdb..00000000 --- a/test/trading/seaport16/ImmutableSeaportOperational.t.sol +++ /dev/null @@ -1,244 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {ImmutableSeaportBaseTest} from "./ImmutableSeaportBase.t.sol"; -import {ImmutableSeaportTestHelper} from "./ImmutableSeaportTestHelper.t.sol"; -import {ImmutableSeaport} from "../../../contracts/trading/seaport16/ImmutableSeaport.sol"; -import { - SIP7EventsAndErrors -} from "../../../contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7EventsAndErrors.sol"; - -import { - OrderParameters, - OrderComponents, - AdvancedOrder, - CriteriaResolver -} from "seaport-types-16/src/lib/ConsiderationStructs.sol"; -import {OrderType} from "seaport-types-16/src/lib/ConsiderationEnums.sol"; - -import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; - -contract TestERC721 is ERC721("Test721", "TST721") { - function mint(address to, uint256 tokenId) public returns (bool) { - _mint(to, tokenId); - return true; - } - - function tokenURI(uint256) public pure override returns (string memory) { - return "tokenURI"; - } -} - -// A wallet rather than an EOA needs to be used for the seller because code in forge detects -// the seller as a contract when created it is created with makeAddr. -contract SellerWallet { - bytes4 private constant SELECTOR_ERC1271_BYTES_BYTES = 0x20c13b0b; - bytes4 private constant SELECTOR_ERC1271_BYTES32_BYTES = 0x1626ba7e; - - function isValidSignature( - bytes calldata, - /*_data */ - bytes calldata /*_signatures*/ - ) - external - pure - returns (bytes4) - { - // if (_signatureValidationInternal(_subDigest(keccak256(_data)), _signatures)) { - return SELECTOR_ERC1271_BYTES_BYTES; - // } - // return 0; - } - - function isValidSignature( - bytes32, - /*_hash*/ - bytes calldata /*_signatures*/ - ) - external - pure - returns (bytes4) - { - // if (_signatureValidationInternal(_subDigest(_hash), _signatures)) { - return SELECTOR_ERC1271_BYTES32_BYTES; - // } - // return 0; - } - - function setApprovalForAll(address _erc721, address _seaport) external { - ERC721(_erc721).setApprovalForAll(_seaport, true); - } - - receive() external payable {} -} - -contract ImmutableSeaportOperationalTest is ImmutableSeaportBaseTest, ImmutableSeaportTestHelper { - SellerWallet public sellerWallet; - TestERC721 public erc721; - uint256 public nftId; - - function setUp() public override { - super.setUp(); - _setFulfillerAndZone(buyer, address(immutableSignedZone)); - sellerWallet = new SellerWallet(); - nftId = 1; - vm.deal(buyer, 10 ether); - } - - function testFulfillFullRestrictedOrder() public { - _checkFulfill(OrderType.FULL_RESTRICTED); - } - - function testFulfillPartialRestrictedOrder() public { - _checkFulfill(OrderType.PARTIAL_RESTRICTED); - } - - function testRejectUnsupportedZones() public { - // Create order with random zone - address randomZone = makeAddr("randomZone"); - AdvancedOrder memory order = _prepareCheckFulfill(randomZone); - - vm.prank(buyer); - vm.expectRevert(abi.encodeWithSelector(ImmutableSeaport.InvalidZone.selector, randomZone)); - immutableSeaport.fulfillAdvancedOrder{value: 10 ether}(order, new CriteriaResolver[](0), conduitKey, buyer); - } - - function testRejectFullOpenOrder() public { - AdvancedOrder memory order = _prepareCheckFulfill(OrderType.FULL_OPEN); - - vm.prank(buyer); - vm.expectRevert( - abi.encodeWithSelector(ImmutableSeaport.OrderNotRestricted.selector, uint8(OrderType.FULL_OPEN)) - ); - immutableSeaport.fulfillAdvancedOrder{value: 10 ether}(order, new CriteriaResolver[](0), conduitKey, buyer); - } - - function testRejectDisabledZone() public { - AdvancedOrder memory order = _prepareCheckFulfill(); - - vm.prank(owner); - immutableSeaport.setAllowedZone(address(immutableSignedZone), false); - - vm.prank(buyer); - vm.expectRevert(abi.encodeWithSelector(ImmutableSeaport.InvalidZone.selector, address(immutableSignedZone))); - immutableSeaport.fulfillAdvancedOrder{value: 10 ether}(order, new CriteriaResolver[](0), conduitKey, buyer); - } - - function testRejectWrongSigner() public { - uint256 wrongSigner = 1; - AdvancedOrder memory order = _prepareCheckFulfill(wrongSigner); - - // The algorithm inside fulfillAdvancedOrder uses ecRecover to determine the signer. If the - // information going in is wrong, then the wrong signer will be derived. - address derivedBadSigner = 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf; - - vm.prank(buyer); - vm.expectRevert(abi.encodeWithSelector(SIP7EventsAndErrors.SignerNotActive.selector, derivedBadSigner)); - immutableSeaport.fulfillAdvancedOrder{value: 10 ether}(order, new CriteriaResolver[](0), conduitKey, buyer); - } - - function testRejectInvalidExtraData() public { - AdvancedOrder memory order = _prepareCheckFulfillWithBadExtraData(); - - // The algorithm inside fulfillAdvancedOrder uses ecRecover to determine the signer. If the - // information going in is wrong, then the wrong signer will be derived. - address derivedBadSigner = 0xbB5E3df75ae272CcEb6CEB0F4A4BCfd3AfE3f27D; - - vm.prank(buyer); - vm.expectRevert(abi.encodeWithSelector(SIP7EventsAndErrors.SignerNotActive.selector, derivedBadSigner)); - immutableSeaport.fulfillAdvancedOrder{value: 10 ether}(order, new CriteriaResolver[](0), conduitKey, buyer); - } - - function _checkFulfill(OrderType _orderType) internal { - AdvancedOrder memory order = _prepareCheckFulfill(_orderType); - - // Record balances before - uint256 sellerBalanceBefore = address(sellerWallet).balance; - uint256 buyerBalanceBefore = address(buyer).balance; - - // Fulfill order - vm.prank(buyer); - immutableSeaport.fulfillAdvancedOrder{value: 10 ether}(order, new CriteriaResolver[](0), conduitKey, buyer); - - // Verify results - assertEq(erc721.ownerOf(nftId), buyer, "Owner of NFT not buyer"); - assertEq(address(sellerWallet).balance, sellerBalanceBefore + 10 ether, "Seller incorrect final balance"); - assertEq(address(buyer).balance, buyerBalanceBefore - 10 ether, "Buyer incorrect final balance"); - } - - function _prepareCheckFulfill() internal returns (AdvancedOrder memory) { - return - _prepareCheckFulfill(OrderType.PARTIAL_RESTRICTED, address(immutableSignedZone), immutableSignerPkey, false); - } - - function _prepareCheckFulfill(OrderType _orderType) internal returns (AdvancedOrder memory) { - return _prepareCheckFulfill(_orderType, address(immutableSignedZone), immutableSignerPkey, false); - } - - function _prepareCheckFulfill(address _zone) internal returns (AdvancedOrder memory) { - return _prepareCheckFulfill(OrderType.PARTIAL_RESTRICTED, _zone, immutableSignerPkey, false); - } - - function _prepareCheckFulfill(uint256 _signer) internal returns (AdvancedOrder memory) { - return _prepareCheckFulfill(OrderType.PARTIAL_RESTRICTED, address(immutableSignedZone), _signer, false); - } - - function _prepareCheckFulfillWithBadExtraData() internal returns (AdvancedOrder memory) { - return - _prepareCheckFulfill(OrderType.PARTIAL_RESTRICTED, address(immutableSignedZone), immutableSignerPkey, true); - } - - function _prepareCheckFulfill(OrderType _orderType, address _zone, uint256 _signer, bool _useBadExtraData) - internal - returns (AdvancedOrder memory) - { - // Deploy test ERC721 - erc721 = new TestERC721(); - erc721.mint(address(sellerWallet), nftId); - sellerWallet.setApprovalForAll(address(erc721), conduitAddress); - uint64 expiration = uint64(block.timestamp + 90); - - // Create order - OrderParameters memory orderParams = OrderParameters({ - offerer: address(sellerWallet), - zone: _zone, - offer: _createOfferItems(address(erc721), nftId), - consideration: _createConsiderationItems(address(sellerWallet), 10 ether), - orderType: _orderType, - startTime: 0, - endTime: expiration, - zoneHash: bytes32(0), - salt: 0, - conduitKey: conduitKey, - totalOriginalConsiderationItems: 1 - }); - - OrderComponents memory orderComponents = OrderComponents({ - offerer: orderParams.offerer, - zone: orderParams.zone, - offer: orderParams.offer, - consideration: orderParams.consideration, - orderType: orderParams.orderType, - startTime: orderParams.startTime, - endTime: orderParams.endTime, - zoneHash: orderParams.zoneHash, - salt: orderParams.salt, - conduitKey: orderParams.conduitKey, - counter: immutableSeaport.getCounter(orderParams.offerer) - }); - - bytes32 orderHash = immutableSeaport.getOrderHash(orderComponents); - bytes memory extraData = - _generateSip7Signature(orderHash, buyer, _signer, expiration, orderParams.consideration); - if (_useBadExtraData) { - orderParams.consideration[0].recipient = payable(buyer); - extraData = _generateSip7Signature(orderHash, buyer, _signer, expiration, orderParams.consideration); - } - bytes memory signature = _signOrder(sellerPkey, orderHash); - - AdvancedOrder memory order = AdvancedOrder({ - parameters: orderParams, numerator: 1, denominator: 1, signature: signature, extraData: extraData - }); - return order; - } -} diff --git a/test/trading/seaport16/ImmutableSeaportSignedZoneV3Integration.t.sol b/test/trading/seaport16/ImmutableSeaportSignedZoneV3Integration.t.sol deleted file mode 100644 index df104efa..00000000 --- a/test/trading/seaport16/ImmutableSeaportSignedZoneV3Integration.t.sol +++ /dev/null @@ -1,902 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 - -pragma solidity ^0.8.17; - -import {Test} from "forge-std/Test.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {ItemType, OrderType} from "seaport-types-16/src/lib/ConsiderationEnums.sol"; -import { - AdvancedOrder, - ConsiderationItem, - CriteriaResolver, - OrderComponents, - OfferItem, - OrderParameters, - ReceivedItem -} from "seaport-types-16/src/lib/ConsiderationStructs.sol"; -import {ConduitController} from "../../../contracts/trading/seaport16/conduit/ConduitController.sol"; -import {ImmutableSeaportHarness} from "./ImmutableSeaportHarness.t.sol"; -import {IImmutableERC1155} from "../seaport/utils/IImmutableERC1155.t.sol"; -import {IImmutableERC721} from "../seaport/utils/IImmutableERC721.t.sol"; -import {IOperatorAllowlistUpgradeable} from "../seaport/utils/IOperatorAllowlistUpgradeable.t.sol"; -import {SigningTestHelper} from "../seaport/utils/SigningTestHelper.t.sol"; -import {IImmutableSignedZoneV3Harness} from "./zones/immutable-signed-zone/v3/IImmutableSignedZoneV3Harness.t.sol"; - -// forge-lint: disable-start(erc20-unchecked-transfer) - -contract ImmutableSeaportSignedZoneV3IntegrationTest is Test, SigningTestHelper { - // Foundry artifacts allow the test to deploy contracts separately that aren't compatible with - // the solidity version compiler that the test and its dependencies resolve to. - string private constant OPERATOR_ALLOWLIST_ARTIFACT = - "./foundry-out/OperatorAllowlistUpgradeable.sol/OperatorAllowlistUpgradeable.json"; - string private constant ERC1155_ARTIFACT = "./foundry-out/ImmutableERC1155.sol/ImmutableERC1155.json"; - string private constant ERC20_ARTIFACT = - "./foundry-out/ImmutableERC20FixedSupplyNoBurn.sol/ImmutableERC20FixedSupplyNoBurn.json"; - string private constant ERC721_ARTIFACT = "./foundry-out/ImmutableERC721.sol/ImmutableERC721.json"; - string private constant ZONE_ARTIFACT = - "./foundry-out/ImmutableSignedZoneV3Harness.t.sol/ImmutableSignedZoneV3Harness.json"; - - address private immutable OWNER = makeAddr("owner"); - address private immutable ZONE_MANAGER = makeAddr("zone_manager"); - address private immutable SIGNER; - uint256 private immutable SIGNER_PRIVATE_KEY; - address private immutable FULFILLER = makeAddr("fulfiller"); - address private immutable FULFILLER_TWO = makeAddr("fulfiller_two"); - address private immutable OFFERER; - uint256 private immutable OFFERER_PRIVATE_KEY; - address private immutable PROTOCOL_FEE_RECEIVER = makeAddr("protocol_fee_receiver"); - address private immutable ROYALTY_FEE_RECEIVER = makeAddr("royalty_fee_receiver"); - address private immutable ECOSYSTEM_FEE_RECEIVER = makeAddr("ecosystem_fee_receiver"); - - ImmutableSeaportHarness private seaport; - IImmutableSignedZoneV3Harness private zone; - IERC20 private erc20Token; - IImmutableERC1155 private erc1155Token; - IImmutableERC721 private erc721Token; - - constructor() { - (SIGNER, SIGNER_PRIVATE_KEY) = makeAddrAndKey("signer"); - (OFFERER, OFFERER_PRIVATE_KEY) = makeAddrAndKey("offerer"); - } - - function setUp() public { - // operator allowlist - IOperatorAllowlistUpgradeable operatorAllowlist = - IOperatorAllowlistUpgradeable(deployCode(OPERATOR_ALLOWLIST_ARTIFACT)); - operatorAllowlist.initialize(OWNER, OWNER, OWNER); - - // tokens - erc20Token = - IERC20(deployCode(ERC20_ARTIFACT, abi.encode("TestERC20", "ERC20", type(uint256).max, OWNER, OWNER))); - erc721Token = IImmutableERC721( - deployCode( - ERC721_ARTIFACT, - abi.encode( - OWNER, "TestERC721", "ERC721", "", "", address(operatorAllowlist), ROYALTY_FEE_RECEIVER, uint96(100) - ) - ) - ); - vm.prank(OWNER); - erc721Token.grantMinterRole(OWNER); - erc1155Token = IImmutableERC1155( - deployCode( - ERC1155_ARTIFACT, - abi.encode(OWNER, "TestERC1155", "", "", address(operatorAllowlist), ROYALTY_FEE_RECEIVER, uint96(100)) - ) - ); - vm.prank(OWNER); - erc1155Token.grantMinterRole(OWNER); - - // seaport - ConduitController conduitController = new ConduitController(); - seaport = new ImmutableSeaportHarness(address(conduitController), OWNER); - - // zone - zone = IImmutableSignedZoneV3Harness( - deployCode( - ZONE_ARTIFACT, - abi.encode( - "MyZoneName", address(seaport), "https://www.immutable.com", "https://www.immutable.com/docs", OWNER - ) - ) - ); - vm.prank(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, ZONE_MANAGER); - vm.prank(ZONE_MANAGER); - zone.addSigner(SIGNER); - - // set allowed zone - vm.prank(OWNER); - seaport.setAllowedZone(address(zone), true); - - // operator allowlist addresses - address[] memory allowlistAddress = new address[](1); - allowlistAddress[0] = address(seaport); - vm.prank(OWNER); - operatorAllowlist.addAddressesToAllowlist(allowlistAddress); - } - - function test_fulfillAdvancedOrder_withCompleteFulfilment() public { - // offer items - OfferItem[] memory offerItems = new OfferItem[](1); - offerItems[0] = OfferItem({ - itemType: ItemType.ERC721, - token: address(erc721Token), - identifierOrCriteria: uint256(50), - startAmount: uint256(1), - endAmount: uint256(1) - }); - - // consideration items - ConsiderationItem[] memory originalConsiderationItems = new ConsiderationItem[](1); - // original item - originalConsiderationItems[0] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(200_000_000_000_000_000_000), // 200^18 - endAmount: uint256(200_000_000_000_000_000_000), // 200^18 - recipient: payable(OFFERER) - }); - - ConsiderationItem[] memory considerationItems = new ConsiderationItem[](4); - considerationItems[0] = originalConsiderationItems[0]; - // protocol fee - 2% - considerationItems[1] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(4_000_000_000_000_000_000), - endAmount: uint256(4_000_000_000_000_000_000), - recipient: payable(PROTOCOL_FEE_RECEIVER) - }); - // royalty fee - 1% - considerationItems[2] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(2_000_000_000_000_000_000), - endAmount: uint256(2_000_000_000_000_000_000), - recipient: payable(ROYALTY_FEE_RECEIVER) - }); - // ecosystem fee - 3% - considerationItems[3] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(6_000_000_000_000_000_000), - endAmount: uint256(6_000_000_000_000_000_000), - recipient: payable(ECOSYSTEM_FEE_RECEIVER) - }); - - // order - OrderParameters memory orderParameters = OrderParameters({ - offerer: OFFERER, - zone: address(zone), - offer: offerItems, - consideration: considerationItems, - orderType: OrderType.FULL_RESTRICTED, - startTime: uint256(0), - endTime: uint256(5000), - zoneHash: bytes32(0), - salt: uint256(123), - conduitKey: bytes32(0), - totalOriginalConsiderationItems: uint256(1) - }); - - // order hash - bytes32 orderHash = seaport.getOrderHash( - OrderComponents({ - offerer: orderParameters.offerer, - zone: orderParameters.zone, - offer: orderParameters.offer, - consideration: originalConsiderationItems, - orderType: orderParameters.orderType, - startTime: orderParameters.startTime, - endTime: orderParameters.endTime, - zoneHash: orderParameters.zoneHash, - salt: orderParameters.salt, - conduitKey: orderParameters.conduitKey, - counter: seaport.getCounter(orderParameters.offerer) - }) - ); - - // order signature - bytes memory orderSignature; - { - bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); - orderSignature = _sign(OFFERER_PRIVATE_KEY, orderDigest); - } - - // extra data - bytes memory extraData; - { - ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); - expectedReceivedItems[0] = ReceivedItem({ - itemType: considerationItems[0].itemType, - token: considerationItems[0].token, - identifier: considerationItems[0].identifierOrCriteria, - amount: considerationItems[0].startAmount, - recipient: considerationItems[0].recipient - }); - expectedReceivedItems[1] = ReceivedItem({ - itemType: considerationItems[1].itemType, - token: considerationItems[1].token, - identifier: considerationItems[1].identifierOrCriteria, - amount: considerationItems[1].startAmount, - recipient: considerationItems[1].recipient - }); - expectedReceivedItems[2] = ReceivedItem({ - itemType: considerationItems[2].itemType, - token: considerationItems[2].token, - identifier: considerationItems[2].identifierOrCriteria, - amount: considerationItems[2].startAmount, - recipient: considerationItems[2].recipient - }); - expectedReceivedItems[3] = ReceivedItem({ - itemType: considerationItems[3].itemType, - token: considerationItems[3].token, - identifier: considerationItems[3].identifierOrCriteria, - amount: considerationItems[3].startAmount, - recipient: considerationItems[3].recipient - }); - bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); - bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); - bytes32 eip712SignedOrderHash = - zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); - extraData = abi.encodePacked( - bytes1(0), - FULFILLER, - uint64(4000), - _signCompact( - SIGNER_PRIVATE_KEY, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) - ), - context - ); - } - - // advanced order - AdvancedOrder memory advancedOrder = AdvancedOrder({ - parameters: orderParameters, - numerator: uint120(1), - denominator: uint120(1), - signature: orderSignature, - extraData: extraData - }); - - // mints - vm.prank(OWNER); - bool success = erc20Token.transfer( - FULFILLER, - (considerationItems[0].startAmount + considerationItems[1].startAmount + considerationItems[2].startAmount - + considerationItems[3].startAmount) - ); - require(success, "Unexpectedly, ERC20 transfer failed"); - vm.prank(OWNER); - erc721Token.safeMint(OFFERER, offerItems[0].identifierOrCriteria); - - // approvals - vm.prank(OFFERER); - erc721Token.setApprovalForAll(address(seaport), true); - vm.prank(FULFILLER); - erc20Token.approve(address(seaport), type(uint256).max); - - // fulfillment - vm.prank(FULFILLER); - seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); - - // assertions - assertEq(erc721Token.balanceOf(OFFERER), 0); - assertEq(erc721Token.balanceOf(FULFILLER), offerItems[0].startAmount); - assertEq(erc20Token.balanceOf(OFFERER), considerationItems[0].startAmount); - assertEq(erc20Token.balanceOf(FULFILLER), 0); - assertEq(erc20Token.balanceOf(PROTOCOL_FEE_RECEIVER), considerationItems[1].startAmount); - assertEq(erc20Token.balanceOf(ROYALTY_FEE_RECEIVER), considerationItems[2].startAmount); - assertEq(erc20Token.balanceOf(ECOSYSTEM_FEE_RECEIVER), considerationItems[3].startAmount); - } - - function test_fulfillAdvancedOrder_withPartialFill() public { - // offer items - OfferItem[] memory offerItems = new OfferItem[](1); - offerItems[0] = OfferItem({ - itemType: ItemType.ERC1155, - token: address(erc1155Token), - identifierOrCriteria: uint256(50), - startAmount: uint256(100), - endAmount: uint256(100) - }); - - // consideration items - ConsiderationItem[] memory originalConsiderationItems = new ConsiderationItem[](1); - // original item - originalConsiderationItems[0] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(200_000_000_000_000_000_000), // 200^18 - endAmount: uint256(200_000_000_000_000_000_000), // 200^18 - recipient: payable(OFFERER) - }); - - ConsiderationItem[] memory considerationItems = new ConsiderationItem[](4); - considerationItems[0] = originalConsiderationItems[0]; - // protocol fee - 2% - considerationItems[1] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(4_000_000_000_000_000_000), - endAmount: uint256(4_000_000_000_000_000_000), - recipient: payable(PROTOCOL_FEE_RECEIVER) - }); - // royalty fee - 1% - considerationItems[2] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(2_000_000_000_000_000_000), - endAmount: uint256(2_000_000_000_000_000_000), - recipient: payable(ROYALTY_FEE_RECEIVER) - }); - // ecosystem fee - 3% - considerationItems[3] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(6_000_000_000_000_000_000), - endAmount: uint256(6_000_000_000_000_000_000), - recipient: payable(ECOSYSTEM_FEE_RECEIVER) - }); - - // order - OrderParameters memory orderParameters = OrderParameters({ - offerer: OFFERER, - zone: address(zone), - offer: offerItems, - consideration: considerationItems, - orderType: OrderType.PARTIAL_RESTRICTED, - startTime: uint256(0), - endTime: uint256(5000), - zoneHash: bytes32(0), - salt: uint256(123), - conduitKey: bytes32(0), - totalOriginalConsiderationItems: uint256(1) - }); - - // order hash - bytes32 orderHash = seaport.getOrderHash( - OrderComponents({ - offerer: orderParameters.offerer, - zone: orderParameters.zone, - offer: orderParameters.offer, - consideration: originalConsiderationItems, - orderType: orderParameters.orderType, - startTime: orderParameters.startTime, - endTime: orderParameters.endTime, - zoneHash: orderParameters.zoneHash, - salt: orderParameters.salt, - conduitKey: orderParameters.conduitKey, - counter: seaport.getCounter(orderParameters.offerer) - }) - ); - - // order signature - bytes memory orderSignature; - { - bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); - orderSignature = _sign(OFFERER_PRIVATE_KEY, orderDigest); - } - - // extra data - bytes memory extraData; - { - ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); - expectedReceivedItems[0] = ReceivedItem({ - itemType: considerationItems[0].itemType, - token: considerationItems[0].token, - identifier: considerationItems[0].identifierOrCriteria, - amount: considerationItems[0].startAmount, - recipient: considerationItems[0].recipient - }); - expectedReceivedItems[1] = ReceivedItem({ - itemType: considerationItems[1].itemType, - token: considerationItems[1].token, - identifier: considerationItems[1].identifierOrCriteria, - amount: considerationItems[1].startAmount, - recipient: considerationItems[1].recipient - }); - expectedReceivedItems[2] = ReceivedItem({ - itemType: considerationItems[2].itemType, - token: considerationItems[2].token, - identifier: considerationItems[2].identifierOrCriteria, - amount: considerationItems[2].startAmount, - recipient: considerationItems[2].recipient - }); - expectedReceivedItems[3] = ReceivedItem({ - itemType: considerationItems[3].itemType, - token: considerationItems[3].token, - identifier: considerationItems[3].identifierOrCriteria, - amount: considerationItems[3].startAmount, - recipient: considerationItems[3].recipient - }); - bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); - bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); - bytes32 eip712SignedOrderHash = - zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); - extraData = abi.encodePacked( - bytes1(0), - FULFILLER, - uint64(4000), - _signCompact( - SIGNER_PRIVATE_KEY, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) - ), - context - ); - } - - // advanced order, fill 1/100th of the order - AdvancedOrder memory advancedOrder = AdvancedOrder({ - parameters: orderParameters, - numerator: uint120(1), - denominator: uint120(100), - signature: orderSignature, - extraData: extraData - }); - - // mints - vm.prank(OWNER); - bool success = erc20Token.transfer( - FULFILLER, - (considerationItems[0].startAmount - + considerationItems[1].startAmount - + considerationItems[2].startAmount - + considerationItems[3].startAmount) / 100 - ); - require(success, "Unexpectedly, ERC20 transfer failed"); - vm.prank(OWNER); - erc1155Token.safeMint(OFFERER, offerItems[0].identifierOrCriteria, offerItems[0].startAmount, new bytes(0)); - - // approvals - vm.prank(OFFERER); - erc1155Token.setApprovalForAll(address(seaport), true); - vm.prank(FULFILLER); - erc20Token.approve(address(seaport), type(uint256).max); - - // fulfillment - vm.prank(FULFILLER); - seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); - - // assertions - assertEq( - erc1155Token.balanceOf(OFFERER, offerItems[0].identifierOrCriteria), offerItems[0].startAmount * 99 / 100 - ); - assertEq( - erc1155Token.balanceOf(FULFILLER, offerItems[0].identifierOrCriteria), offerItems[0].startAmount * 1 / 100 - ); - assertEq(erc20Token.balanceOf(OFFERER), considerationItems[0].startAmount / 100); - assertEq(erc20Token.balanceOf(FULFILLER), 0); - assertEq(erc20Token.balanceOf(PROTOCOL_FEE_RECEIVER), considerationItems[1].startAmount / 100); - assertEq(erc20Token.balanceOf(ROYALTY_FEE_RECEIVER), considerationItems[2].startAmount / 100); - assertEq(erc20Token.balanceOf(ECOSYSTEM_FEE_RECEIVER), considerationItems[3].startAmount / 100); - } - - function test_fulfillAdvancedOrder_withMultiplePartialFills() public { - // offer items - OfferItem[] memory offerItems = new OfferItem[](1); - offerItems[0] = OfferItem({ - itemType: ItemType.ERC1155, - token: address(erc1155Token), - identifierOrCriteria: uint256(50), - startAmount: uint256(100), - endAmount: uint256(100) - }); - - // consideration items - ConsiderationItem[] memory originalConsiderationItems = new ConsiderationItem[](1); - // original item - originalConsiderationItems[0] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(200_000_000_000_000_000_000), // 200^18 - endAmount: uint256(200_000_000_000_000_000_000), // 200^18 - recipient: payable(OFFERER) - }); - - ConsiderationItem[] memory considerationItems = new ConsiderationItem[](4); - considerationItems[0] = originalConsiderationItems[0]; - // protocol fee - 2% - considerationItems[1] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(4_000_000_000_000_000_000), - endAmount: uint256(4_000_000_000_000_000_000), - recipient: payable(PROTOCOL_FEE_RECEIVER) - }); - // royalty fee - 1% - considerationItems[2] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(2_000_000_000_000_000_000), - endAmount: uint256(2_000_000_000_000_000_000), - recipient: payable(ROYALTY_FEE_RECEIVER) - }); - // ecosystem fee - 3% - considerationItems[3] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(6_000_000_000_000_000_000), - endAmount: uint256(6_000_000_000_000_000_000), - recipient: payable(ECOSYSTEM_FEE_RECEIVER) - }); - - // order - OrderParameters memory orderParameters = OrderParameters({ - offerer: OFFERER, - zone: address(zone), - offer: offerItems, - consideration: considerationItems, - orderType: OrderType.PARTIAL_RESTRICTED, - startTime: uint256(0), - endTime: uint256(5000), - zoneHash: bytes32(0), - salt: uint256(123), - conduitKey: bytes32(0), - totalOriginalConsiderationItems: uint256(1) - }); - - // order hash - bytes32 orderHash = seaport.getOrderHash( - OrderComponents({ - offerer: orderParameters.offerer, - zone: orderParameters.zone, - offer: orderParameters.offer, - consideration: originalConsiderationItems, - orderType: orderParameters.orderType, - startTime: orderParameters.startTime, - endTime: orderParameters.endTime, - zoneHash: orderParameters.zoneHash, - salt: orderParameters.salt, - conduitKey: orderParameters.conduitKey, - counter: seaport.getCounter(orderParameters.offerer) - }) - ); - - // order signature - bytes memory orderSignature; - { - bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); - orderSignature = _sign(OFFERER_PRIVATE_KEY, orderDigest); - } - - // extra data - bytes memory extraData; - { - ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); - expectedReceivedItems[0] = ReceivedItem({ - itemType: considerationItems[0].itemType, - token: considerationItems[0].token, - identifier: considerationItems[0].identifierOrCriteria, - amount: considerationItems[0].startAmount, - recipient: considerationItems[0].recipient - }); - expectedReceivedItems[1] = ReceivedItem({ - itemType: considerationItems[1].itemType, - token: considerationItems[1].token, - identifier: considerationItems[1].identifierOrCriteria, - amount: considerationItems[1].startAmount, - recipient: considerationItems[1].recipient - }); - expectedReceivedItems[2] = ReceivedItem({ - itemType: considerationItems[2].itemType, - token: considerationItems[2].token, - identifier: considerationItems[2].identifierOrCriteria, - amount: considerationItems[2].startAmount, - recipient: considerationItems[2].recipient - }); - expectedReceivedItems[3] = ReceivedItem({ - itemType: considerationItems[3].itemType, - token: considerationItems[3].token, - identifier: considerationItems[3].identifierOrCriteria, - amount: considerationItems[3].startAmount, - recipient: considerationItems[3].recipient - }); - bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); - bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); - bytes32 eip712SignedOrderHash = - zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); - extraData = abi.encodePacked( - bytes1(0), - FULFILLER, - uint64(4000), - _signCompact( - SIGNER_PRIVATE_KEY, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) - ), - context - ); - } - - // advanced order, fill 1/100th of the order - AdvancedOrder memory advancedOrder = AdvancedOrder({ - parameters: orderParameters, - numerator: uint120(1), - denominator: uint120(100), - signature: orderSignature, - extraData: extraData - }); - - // mints - vm.prank(OWNER); - bool success = erc20Token.transfer( - FULFILLER, - (considerationItems[0].startAmount - + considerationItems[1].startAmount - + considerationItems[2].startAmount - + considerationItems[3].startAmount) * 2 / 100 - ); - require(success, "Unexpectedly, ERC20 transfer failed"); - vm.prank(OWNER); - erc1155Token.safeMint(OFFERER, offerItems[0].identifierOrCriteria, offerItems[0].startAmount, new bytes(0)); - - // approvals - vm.prank(OFFERER); - erc1155Token.setApprovalForAll(address(seaport), true); - vm.prank(FULFILLER); - erc20Token.approve(address(seaport), type(uint256).max); - - // fulfill twice - vm.prank(FULFILLER); - seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); - vm.prank(FULFILLER); - seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); - - // assertions - assertEq( - erc1155Token.balanceOf(OFFERER, offerItems[0].identifierOrCriteria), offerItems[0].startAmount * 98 / 100 - ); - assertEq( - erc1155Token.balanceOf(FULFILLER, offerItems[0].identifierOrCriteria), offerItems[0].startAmount * 2 / 100 - ); - assertEq(erc20Token.balanceOf(OFFERER), considerationItems[0].startAmount * 2 / 100); - assertEq(erc20Token.balanceOf(FULFILLER), 0); - assertEq(erc20Token.balanceOf(PROTOCOL_FEE_RECEIVER), considerationItems[1].startAmount * 2 / 100); - assertEq(erc20Token.balanceOf(ROYALTY_FEE_RECEIVER), considerationItems[2].startAmount * 2 / 100); - assertEq(erc20Token.balanceOf(ECOSYSTEM_FEE_RECEIVER), considerationItems[3].startAmount * 2 / 100); - } - - function test_fulfillAdvancedOrder_withOverfilling() public { - // offer items - OfferItem[] memory offerItems = new OfferItem[](1); - offerItems[0] = OfferItem({ - itemType: ItemType.ERC1155, - token: address(erc1155Token), - identifierOrCriteria: uint256(50), - startAmount: uint256(100), - endAmount: uint256(100) - }); - - // consideration items - ConsiderationItem[] memory originalConsiderationItems = new ConsiderationItem[](1); - // original item - originalConsiderationItems[0] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(200_000_000_000_000_000_000), // 200^18 - endAmount: uint256(200_000_000_000_000_000_000), // 200^18 - recipient: payable(OFFERER) - }); - - ConsiderationItem[] memory considerationItems = new ConsiderationItem[](4); - considerationItems[0] = originalConsiderationItems[0]; - // protocol fee - 2% - considerationItems[1] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(4_000_000_000_000_000_000), - endAmount: uint256(4_000_000_000_000_000_000), - recipient: payable(PROTOCOL_FEE_RECEIVER) - }); - // royalty fee - 1% - considerationItems[2] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(2_000_000_000_000_000_000), - endAmount: uint256(2_000_000_000_000_000_000), - recipient: payable(ROYALTY_FEE_RECEIVER) - }); - // ecosystem fee - 3% - considerationItems[3] = ConsiderationItem({ - itemType: ItemType.ERC20, - token: address(erc20Token), - identifierOrCriteria: uint256(0), - startAmount: uint256(6_000_000_000_000_000_000), - endAmount: uint256(6_000_000_000_000_000_000), - recipient: payable(ECOSYSTEM_FEE_RECEIVER) - }); - - // order - OrderParameters memory orderParameters = OrderParameters({ - offerer: OFFERER, - zone: address(zone), - offer: offerItems, - consideration: considerationItems, - orderType: OrderType.PARTIAL_RESTRICTED, - startTime: uint256(0), - endTime: uint256(5000), - zoneHash: bytes32(0), - salt: uint256(123), - conduitKey: bytes32(0), - totalOriginalConsiderationItems: uint256(1) - }); - - // order hash - bytes32 orderHash = seaport.getOrderHash( - OrderComponents({ - offerer: orderParameters.offerer, - zone: orderParameters.zone, - offer: orderParameters.offer, - consideration: originalConsiderationItems, - orderType: orderParameters.orderType, - startTime: orderParameters.startTime, - endTime: orderParameters.endTime, - zoneHash: orderParameters.zoneHash, - salt: orderParameters.salt, - conduitKey: orderParameters.conduitKey, - counter: seaport.getCounter(orderParameters.offerer) - }) - ); - - // order signature - bytes memory orderSignature; - { - bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); - orderSignature = _sign(OFFERER_PRIVATE_KEY, orderDigest); - } - - // substandard 6 data expected received items - ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); - expectedReceivedItems[0] = ReceivedItem({ - itemType: considerationItems[0].itemType, - token: considerationItems[0].token, - identifier: considerationItems[0].identifierOrCriteria, - amount: considerationItems[0].startAmount, - recipient: considerationItems[0].recipient - }); - expectedReceivedItems[1] = ReceivedItem({ - itemType: considerationItems[1].itemType, - token: considerationItems[1].token, - identifier: considerationItems[1].identifierOrCriteria, - amount: considerationItems[1].startAmount, - recipient: considerationItems[1].recipient - }); - expectedReceivedItems[2] = ReceivedItem({ - itemType: considerationItems[2].itemType, - token: considerationItems[2].token, - identifier: considerationItems[2].identifierOrCriteria, - amount: considerationItems[2].startAmount, - recipient: considerationItems[2].recipient - }); - expectedReceivedItems[3] = ReceivedItem({ - itemType: considerationItems[3].itemType, - token: considerationItems[3].token, - identifier: considerationItems[3].identifierOrCriteria, - amount: considerationItems[3].startAmount, - recipient: considerationItems[3].recipient - }); - - // extra data - bytes memory extraData1; - bytes memory extraData2; - { - bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); - bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); - bytes32 eip712SignedOrderHash = - zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); - extraData1 = abi.encodePacked( - bytes1(0), - FULFILLER, - uint64(4000), - _signCompact( - SIGNER_PRIVATE_KEY, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) - ), - context - ); - } - { - bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); - bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); - bytes32 eip712SignedOrderHash = - zone.exposed_deriveSignedOrderHash(FULFILLER_TWO, uint64(4000), orderHash, context); - extraData2 = abi.encodePacked( - bytes1(0), - FULFILLER_TWO, - uint64(4000), - _signCompact( - SIGNER_PRIVATE_KEY, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) - ), - context - ); - } - - // advanced order, fill 1/2 of the order - AdvancedOrder memory advancedOrder1 = AdvancedOrder({ - parameters: orderParameters, - numerator: uint120(50), - denominator: uint120(100), - signature: orderSignature, - extraData: extraData1 - }); - - // advanced order, attempt to fill the whole order - AdvancedOrder memory advancedOrder2 = AdvancedOrder({ - parameters: orderParameters, - numerator: uint120(1), - denominator: uint120(1), - signature: orderSignature, - extraData: extraData2 - }); - - // mints - vm.prank(OWNER); - erc20Token.transfer( - FULFILLER, - (considerationItems[0].startAmount - + considerationItems[1].startAmount - + considerationItems[2].startAmount - + considerationItems[3].startAmount) / 2 - ); - vm.prank(OWNER); - erc20Token.transfer( - FULFILLER_TWO, - (considerationItems[0].startAmount + considerationItems[1].startAmount + considerationItems[2].startAmount - + considerationItems[3].startAmount) - ); - vm.prank(OWNER); - erc1155Token.safeMint(OFFERER, offerItems[0].identifierOrCriteria, offerItems[0].startAmount, new bytes(0)); - - // approvals - vm.prank(OFFERER); - erc1155Token.setApprovalForAll(address(seaport), true); - vm.prank(FULFILLER); - erc20Token.approve(address(seaport), type(uint256).max); - vm.prank(FULFILLER_TWO); - erc20Token.approve(address(seaport), type(uint256).max); - - // fulfill twice - vm.prank(FULFILLER); - seaport.fulfillAdvancedOrder(advancedOrder1, new CriteriaResolver[](0), bytes32(0), FULFILLER); - vm.prank(FULFILLER_TWO); - seaport.fulfillAdvancedOrder(advancedOrder2, new CriteriaResolver[](0), bytes32(0), FULFILLER_TWO); - - // assertions - assertEq(erc1155Token.balanceOf(OFFERER, offerItems[0].identifierOrCriteria), 0); - assertEq(erc1155Token.balanceOf(FULFILLER, offerItems[0].identifierOrCriteria), offerItems[0].startAmount / 2); - assertEq( - erc1155Token.balanceOf(FULFILLER_TWO, offerItems[0].identifierOrCriteria), offerItems[0].startAmount / 2 - ); - assertEq(erc20Token.balanceOf(OFFERER), considerationItems[0].startAmount); - assertEq(erc20Token.balanceOf(FULFILLER), 0); - assertEq( - erc20Token.balanceOf(FULFILLER_TWO), - (considerationItems[0].startAmount - + considerationItems[1].startAmount - + considerationItems[2].startAmount - + considerationItems[3].startAmount) / 2 - ); - assertEq(erc20Token.balanceOf(PROTOCOL_FEE_RECEIVER), considerationItems[1].startAmount); - assertEq(erc20Token.balanceOf(ROYALTY_FEE_RECEIVER), considerationItems[2].startAmount); - assertEq(erc20Token.balanceOf(ECOSYSTEM_FEE_RECEIVER), considerationItems[3].startAmount); - } -} - -// forge-lint: disable-end(erc20-unchecked-transfer) diff --git a/test/trading/seaport16/ImmutableSeaportTestHelper.t.sol b/test/trading/seaport16/ImmutableSeaportTestHelper.t.sol deleted file mode 100644 index a1ab1a37..00000000 --- a/test/trading/seaport16/ImmutableSeaportTestHelper.t.sol +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {Test} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {SigningTestHelper} from "./utils/SigningTestHelper.t.sol"; -import {ItemType} from "seaport-types-16/src/lib/ConsiderationEnums.sol"; -import { - ZoneParameters, - ConsiderationItem, - OfferItem, - ReceivedItem, - SpentItem -} from "seaport-types-16/src/lib/ConsiderationStructs.sol"; -import {Math} from "openzeppelin-contracts-5.0.2/utils/math/Math.sol"; - -abstract contract ImmutableSeaportTestHelper is Test, SigningTestHelper { - string public constant ZONE_NAME = "ImmutableSignedZone"; - string public constant VERSION = "3.0"; - - address private theFulfiller; - - address private theZone; - - function _setFulfillerAndZone(address _fulfiller, address _zone) internal { - theFulfiller = _fulfiller; - theZone = _zone; - } - - // Helper functions - function _createZoneParameters(bytes memory _extraData) internal returns (ZoneParameters memory) { - bytes32 orderHash = keccak256("0x1234"); - return _createZoneParameters(_extraData, orderHash, _createMockConsideration(10)); - } - - function _createZoneParameters(bytes memory _extraData, bytes32 _orderHash) - internal - returns (ZoneParameters memory) - { - return _createZoneParameters(_extraData, _orderHash, _createMockConsideration(10)); - } - - function _createZoneParameters(bytes memory _extraData, bytes32 _orderHash, ReceivedItem[] memory _consideration) - internal - view - returns (ZoneParameters memory) - { - bytes32[] memory orderHashes = new bytes32[](1); - orderHashes[0] = _orderHash; - return _createZoneParameters(_extraData, _orderHash, orderHashes, _consideration); - } - - function _createZoneParameters( - bytes memory _extraData, - bytes32 _orderHash, - bytes32[] memory _orderHashes, - ReceivedItem[] memory _consideration - ) internal view returns (ZoneParameters memory) { - return ZoneParameters({ - orderHash: _orderHash, - fulfiller: theFulfiller, - offerer: address(0), - offer: new SpentItem[](0), - consideration: _consideration, - extraData: _extraData, - orderHashes: _orderHashes, - startTime: 0, - endTime: 0, - zoneHash: bytes32(0) - }); - } - - function _createMockConsideration(uint256 count) internal returns (ReceivedItem[] memory) { - ReceivedItem[] memory consideration = new ReceivedItem[](count); - for (uint256 i = 0; i < count; i++) { - address payable recipient = payable(makeAddr(string(abi.encodePacked("recipient", vm.toString(i))))); - address payable token = payable(makeAddr(string(abi.encodePacked("token", vm.toString(i))))); - consideration[i] = ReceivedItem({ - itemType: ItemType.NATIVE, token: token, identifier: 123, amount: 12, recipient: recipient - }); - } - return consideration; - } - - function _convertConsiderationToReceivedItems(ConsiderationItem[] memory _items) - internal - pure - returns (ReceivedItem[] memory) - { - ReceivedItem[] memory receivedItems = new ReceivedItem[](_items.length); - for (uint256 i = 0; i < _items.length; i++) { - receivedItems[i] = ReceivedItem({ - itemType: _items[i].itemType, - token: _items[i].token, - identifier: _items[i].identifierOrCriteria, - amount: _items[i].startAmount, - recipient: _items[i].recipient - }); - } - return receivedItems; - } - - function _createConsiderationItems(address recipient, uint256 amount) - internal - pure - returns (ConsiderationItem[] memory) - { - ConsiderationItem[] memory consideration = new ConsiderationItem[](1); - consideration[0] = ConsiderationItem({ - itemType: ItemType.NATIVE, - token: address(0), - identifierOrCriteria: 0, - startAmount: amount, - endAmount: amount, - recipient: payable(recipient) - }); - return consideration; - } - - function _deriveReceivedItemsHash(ReceivedItem[] calldata receivedItems) public pure returns (bytes32) { - return _deriveReceivedItemsHash(receivedItems, 1, 1); - } - - function _deriveReceivedItemsHash( - ReceivedItem[] calldata receivedItems, - uint256 scalingFactorNumerator, - uint256 scalingFactorDenominator - ) public pure returns (bytes32) { - uint256 numberOfItems = receivedItems.length; - bytes memory receivedItemsHash = new bytes(0); // Explicitly initialize to empty bytes - - for (uint256 i; i < numberOfItems; i++) { - receivedItemsHash = abi.encodePacked( - receivedItemsHash, - receivedItems[i].itemType, - receivedItems[i].token, - receivedItems[i].identifier, - Math.mulDiv(receivedItems[i].amount, scalingFactorNumerator, scalingFactorDenominator), - receivedItems[i].recipient - ); - } - - return keccak256(receivedItemsHash); - } - - function _signOrder( - uint256, - /* _signerPkey */ - bytes32 /* _orderHash */ - ) - internal - pure - returns (bytes memory) - { - // For the purposes of testing, the offerer wallet will always return valid for signature checks - return abi.encodePacked("Hello!"); - } - - function _signSIP7Order(uint256 _signerPkey, bytes32 orderHash, uint64 expiration, bytes memory context) - internal - view - returns (bytes memory) - { - uint256 chainId = block.chainid; - bytes32 domainSeparator = keccak256( - abi.encode( - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), - keccak256(bytes(ZONE_NAME)), - keccak256(bytes(VERSION)), - chainId, - theZone - ) - ); - - bytes32 structHash = keccak256( - abi.encode( - keccak256("SignedOrder(address fulfiller,uint64 expiration,bytes32 orderHash,bytes context)"), - theFulfiller, - expiration, - orderHash, - keccak256(context) - ) - ); - - bytes32 digest = ECDSA.toTypedDataHash(domainSeparator, structHash); - - return _signCompact(_signerPkey, digest); - } - - // Helper functions - function _createOfferItems(address token, uint256 tokenId) internal pure returns (OfferItem[] memory) { - OfferItem[] memory offer = new OfferItem[](1); - offer[0] = OfferItem({ - itemType: ItemType.ERC721, token: token, identifierOrCriteria: tokenId, startAmount: 1, endAmount: 1 - }); - return offer; - } - - function _generateSip7Signature( - bytes32 orderHash, - address fulfiller, - uint256 signerPkey, - uint64 _expiration, - ConsiderationItem[] memory _consideration - ) internal view returns (bytes memory) { - ReceivedItem[] memory receivedItems = _convertConsiderationToReceivedItems(_consideration); - bytes32 substandard3Data = this._deriveReceivedItemsHash(receivedItems); - bytes32[] memory orderHashes = new bytes32[](1); - orderHashes[0] = orderHash; - bytes memory substandard4Data = abi.encode(orderHashes); - bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data, bytes1(0x04), substandard4Data); - - bytes memory signature = _signSIP7Order(signerPkey, orderHash, _expiration, context); - return abi.encodePacked(bytes1(0), fulfiller, _expiration, signature, context); - } -} diff --git a/test/trading/seaport16/README.md b/test/trading/seaport16/README.md deleted file mode 100644 index 39c2f0da..00000000 --- a/test/trading/seaport16/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Seaport 1.6 Test Code - -The code in this directory is the same as the Seaport 1.5 tests (in [../seaport](../seaport/)), with the following exceptions: - -* All references to Immutable's Seaport implementation are to the 1.6 implementation. -* All references to seaport, seaport-core, and seaport-types are to seaport-16, seaport-core-16, and seaport-types-16. -* All references to test/trading/seaport/utils has been updated to use the version in the seaport 1.5 directory. These files have not changed as they do not reference Seaport. -* Immutable signed zone v2 has been renamed to v3. - - diff --git a/test/trading/seaport16/utils/MockTransferValidator.t.sol b/test/trading/seaport16/utils/MockTransferValidator.t.sol deleted file mode 100644 index c31f6c49..00000000 --- a/test/trading/seaport16/utils/MockTransferValidator.t.sol +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; - -import {ITransferValidator} from "@limitbreak/creator-token-standards/src/interfaces/ITransferValidator.sol"; - -contract MockTransferValidator is ITransferValidator { - bool private _shouldRevertApplyCollectionTransferPolicy = false; - address private _revertApplyCollectionTransferPolicyCaller; - address private _revertApplyCollectionTransferPolicyFrom; - address private _revertApplyCollectionTransferPolicyTo; - - bool private _shouldRevertValidateTransfer = false; - address private _revertValidateTransferCaller; - address private _revertValidateTransferFrom; - address private _revertValidateTransferTo; - - bool private _shouldRevertValidateTransferWithTokenId = false; - address private _revertValidateTransferWithTokenIdCaller; - address private _revertValidateTransferWithTokenIdFrom; - address private _revertValidateTransferWithTokenIdTo; - uint256 private _revertValidateTransferWithTokenIdTokenId; - - bool private _shouldRevertValidateTransferWithTokenIdAndAmount = false; - address private _revertValidateTransferWithTokenIdAndAmountCaller; - address private _revertValidateTransferWithTokenIdAndAmountFrom; - address private _revertValidateTransferWithTokenIdAndAmountTo; - uint256 private _revertValidateTransferWithTokenIdAndAmountTokenId; - uint256 private _revertValidateTransferWithTokenIdAndAmountAmount; - - bool private _shouldRevertBeforeAuthorizedTransferWithOperatorAndTokenId = false; - address private _revertBeforeAuthorizedTransferWithOperatorAndTokenIdOperator; - address private _revertBeforeAuthorizedTransferWithOperatorAndTokenIdToken; - uint256 private _revertBeforeAuthorizedTransferWithOperatorAndTokenIdTokenId; - - bool private _shouldRevertAfterAuthorizedTransferWithTokenId = false; - address private _revertAfterAuthorizedTransferWithTokenIdToken; - uint256 private _revertAfterAuthorizedTransferWithTokenIdTokenId; - - bool private _shouldRevertBeforeAuthorizedTransferWithOperator = false; - address private _revertBeforeAuthorizedTransferWithOperatorOperator; - address private _revertBeforeAuthorizedTransferWithOperatorToken; - - bool private _shouldRevertAfterAuthorizedTransfer = false; - address private _revertAfterAuthorizedTransferToken; - - bool private _shouldRevertBeforeAuthorizedTransferWithTokenId = false; - address private _revertBeforeAuthorizedTransferWithTokenIdToken; - uint256 private _revertBeforeAuthorizedTransferWithTokenIdTokenId; - - bool private _shouldRevertBeforeAuthorizedTransferWithAmount = false; - address private _revertBeforeAuthorizedTransferWithAmountToken; - uint256 private _revertBeforeAuthorizedTransferWithAmountTokenId; - uint256 private _revertBeforeAuthorizedTransferWithAmountAmount; - - bool private _shouldRevertAfterAuthorizedTransferWithAmount = false; - address private _revertAfterAuthorizedTransferWithAmountToken; - uint256 private _revertAfterAuthorizedTransferWithAmountTokenId; - - function revertApplyCollectionTransferPolicy(address caller, address from, address to) public { - _shouldRevertApplyCollectionTransferPolicy = true; - _revertApplyCollectionTransferPolicyCaller = caller; - _revertApplyCollectionTransferPolicyFrom = from; - _revertApplyCollectionTransferPolicyTo = to; - } - - function revertValidateTransfer(address caller, address from, address to) public { - _shouldRevertValidateTransfer = true; - _revertValidateTransferCaller = caller; - _revertValidateTransferFrom = from; - _revertValidateTransferTo = to; - } - - function revertValidateTransferWithTokenId(address caller, address from, address to, uint256 tokenId) public { - _shouldRevertValidateTransferWithTokenId = true; - _revertValidateTransferWithTokenIdCaller = caller; - _revertValidateTransferWithTokenIdFrom = from; - _revertValidateTransferWithTokenIdTo = to; - _revertValidateTransferWithTokenIdTokenId = tokenId; - } - - function revertValidateTransferWithTokenIdAndAmount( - address caller, - address from, - address to, - uint256 tokenId, - uint256 amount - ) public { - _shouldRevertValidateTransferWithTokenIdAndAmount = true; - _revertValidateTransferWithTokenIdAndAmountCaller = caller; - _revertValidateTransferWithTokenIdAndAmountFrom = from; - _revertValidateTransferWithTokenIdAndAmountTo = to; - _revertValidateTransferWithTokenIdAndAmountTokenId = tokenId; - _revertValidateTransferWithTokenIdAndAmountAmount = amount; - } - - function revertBeforeAuthorizedTransferWithOperatorAndTokenId(address operator, address token, uint256 tokenId) - public - { - _shouldRevertBeforeAuthorizedTransferWithOperatorAndTokenId = true; - _revertBeforeAuthorizedTransferWithOperatorAndTokenIdOperator = operator; - _revertBeforeAuthorizedTransferWithOperatorAndTokenIdToken = token; - _revertBeforeAuthorizedTransferWithOperatorAndTokenIdTokenId = tokenId; - } - - function revertAfterAuthorizedTransferWithTokenId(address token, uint256 tokenId) public { - _shouldRevertAfterAuthorizedTransferWithTokenId = true; - _revertAfterAuthorizedTransferWithTokenIdToken = token; - _revertAfterAuthorizedTransferWithTokenIdTokenId = tokenId; - } - - function revertBeforeAuthorizedTransferWithOperator(address operator, address token) public { - _shouldRevertBeforeAuthorizedTransferWithOperator = true; - _revertBeforeAuthorizedTransferWithOperatorOperator = operator; - _revertBeforeAuthorizedTransferWithOperatorToken = token; - } - - function revertAfterAuthorizedTransfer(address token) public { - _shouldRevertAfterAuthorizedTransfer = true; - _revertAfterAuthorizedTransferToken = token; - } - - function revertBeforeAuthorizedTransferWithTokenId(address token, uint256 tokenId) public { - _shouldRevertBeforeAuthorizedTransferWithTokenId = true; - _revertBeforeAuthorizedTransferWithTokenIdToken = token; - _revertBeforeAuthorizedTransferWithTokenIdTokenId = tokenId; - } - - function revertBeforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) public { - _shouldRevertBeforeAuthorizedTransferWithAmount = true; - _revertBeforeAuthorizedTransferWithAmountToken = token; - _revertBeforeAuthorizedTransferWithAmountTokenId = tokenId; - _revertBeforeAuthorizedTransferWithAmountAmount = amount; - } - - function revertAfterAuthorizedTransferWithAmount(address token, uint256 tokenId) public { - _shouldRevertAfterAuthorizedTransferWithAmount = true; - _revertAfterAuthorizedTransferWithAmountToken = token; - _revertAfterAuthorizedTransferWithAmountTokenId = tokenId; - } - - function applyCollectionTransferPolicy(address caller, address from, address to) external view override { - if ( - _shouldRevertApplyCollectionTransferPolicy && caller == _revertApplyCollectionTransferPolicyCaller - && from == _revertApplyCollectionTransferPolicyFrom && to == _revertApplyCollectionTransferPolicyTo - ) { - revert MockTransferValidatorRevert("applyCollectionTransferPolicy(address caller, address from, address to)"); - } - } - - function validateTransfer(address caller, address from, address to) external view override { - if ( - _shouldRevertValidateTransfer && caller == _revertValidateTransferCaller - && from == _revertValidateTransferFrom && to == _revertValidateTransferTo - ) { - revert MockTransferValidatorRevert("validateTransfer(address caller, address from, address to)"); - } - } - - function validateTransfer(address caller, address from, address to, uint256 tokenId) external view override { - if ( - _shouldRevertValidateTransferWithTokenId && caller == _revertValidateTransferWithTokenIdCaller - && from == _revertValidateTransferWithTokenIdFrom && to == _revertValidateTransferWithTokenIdTo - && tokenId == _revertValidateTransferWithTokenIdTokenId - ) { - revert MockTransferValidatorRevert("validateTransfer(address caller, address from, address to, uint256 tokenId)"); - } - } - - function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) - external - view - override - { - if ( - _shouldRevertValidateTransferWithTokenIdAndAmount - && caller == _revertValidateTransferWithTokenIdAndAmountCaller - && from == _revertValidateTransferWithTokenIdAndAmountFrom - && to == _revertValidateTransferWithTokenIdAndAmountTo - && tokenId == _revertValidateTransferWithTokenIdAndAmountTokenId - && amount == _revertValidateTransferWithTokenIdAndAmountAmount - ) { - revert MockTransferValidatorRevert("validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount)"); - } - } - - function beforeAuthorizedTransfer(address operator, address token, uint256 tokenId) external view override { - if ( - _shouldRevertBeforeAuthorizedTransferWithOperatorAndTokenId - && operator == _revertBeforeAuthorizedTransferWithOperatorAndTokenIdOperator - && token == _revertBeforeAuthorizedTransferWithOperatorAndTokenIdToken - && tokenId == _revertBeforeAuthorizedTransferWithOperatorAndTokenIdTokenId - ) { - revert MockTransferValidatorRevert("beforeAuthorizedTransfer(address operator, address token, uint256 tokenId)"); - } - } - - function afterAuthorizedTransfer(address token, uint256 tokenId) external view override { - if ( - _shouldRevertAfterAuthorizedTransferWithTokenId && token == _revertAfterAuthorizedTransferWithTokenIdToken - && tokenId == _revertAfterAuthorizedTransferWithTokenIdTokenId - ) { - revert MockTransferValidatorRevert("afterAuthorizedTransfer(address token, uint256 tokenId)"); - } - } - - function beforeAuthorizedTransfer(address operator, address token) external view override { - if ( - _shouldRevertBeforeAuthorizedTransferWithOperator - && operator == _revertBeforeAuthorizedTransferWithOperatorOperator - && token == _revertBeforeAuthorizedTransferWithOperatorToken - ) { - revert MockTransferValidatorRevert("beforeAuthorizedTransfer(address operator, address token)"); - } - } - - function afterAuthorizedTransfer(address token) external view override { - if (_shouldRevertAfterAuthorizedTransfer && token == _revertAfterAuthorizedTransferToken) { - revert MockTransferValidatorRevert("afterAuthorizedTransfer(address token)"); - } - } - - function beforeAuthorizedTransfer(address token, uint256 tokenId) external view override { - if ( - _shouldRevertBeforeAuthorizedTransferWithTokenId && token == _revertBeforeAuthorizedTransferWithTokenIdToken - && tokenId == _revertBeforeAuthorizedTransferWithTokenIdTokenId - ) { - revert MockTransferValidatorRevert("beforeAuthorizedTransfer(address token, uint256 tokenId)"); - } - } - - function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) external view override { - if ( - _shouldRevertBeforeAuthorizedTransferWithAmount && token == _revertBeforeAuthorizedTransferWithAmountToken - && tokenId == _revertBeforeAuthorizedTransferWithAmountTokenId - && amount == _revertBeforeAuthorizedTransferWithAmountAmount - ) { - revert MockTransferValidatorRevert("beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount)"); - } - } - - function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external view override { - if ( - _shouldRevertAfterAuthorizedTransferWithAmount && token == _revertAfterAuthorizedTransferWithAmountToken - && tokenId == _revertAfterAuthorizedTransferWithAmountTokenId - ) { - revert MockTransferValidatorRevert("afterAuthorizedTransferWithAmount(address token, uint256 tokenId)"); - } - } -} - -error MockTransferValidatorRevert(string functionDefinition); diff --git a/test/trading/seaport16/utils/SigningTestHelper.t.sol b/test/trading/seaport16/utils/SigningTestHelper.t.sol deleted file mode 100644 index 217d5f07..00000000 --- a/test/trading/seaport16/utils/SigningTestHelper.t.sol +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; - -import {Test} from "forge-std/Test.sol"; - -abstract contract SigningTestHelper is Test { - function _sign(uint256 signerPrivateKey, bytes32 signatureDigest) internal pure returns (bytes memory) { - (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, signatureDigest); - return abi.encodePacked(r, s, v); - } - - function _signCompact(uint256 signerPrivateKey, bytes32 signatureDigest) internal pure returns (bytes memory) { - (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, signatureDigest); - if (v != 27) { - // then left-most bit of s has to be flipped to 1. - s = s | bytes32(uint256(1) << 255); - } - return abi.encodePacked(r, s); - } -} diff --git a/test/trading/seaport16/zones/immutable-signed-zone/v3/IImmutableSignedZoneV3Harness.t.sol b/test/trading/seaport16/zones/immutable-signed-zone/v3/IImmutableSignedZoneV3Harness.t.sol deleted file mode 100644 index 40fffc00..00000000 --- a/test/trading/seaport16/zones/immutable-signed-zone/v3/IImmutableSignedZoneV3Harness.t.sol +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; - -import {ZoneInterface} from "seaport-16/contracts/interfaces/ZoneInterface.sol"; -import {ReceivedItem, ZoneParameters} from "seaport-types-16/src/lib/ConsiderationStructs.sol"; -import { - SIP7Interface -} from "../../../../../../contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7Interface.sol"; - -interface IImmutableSignedZoneV3Harness is ZoneInterface, SIP7Interface { - function grantRole(bytes32 role, address account) external; - - function DEFAULT_ADMIN_ROLE() external view returns (bytes32); - - function ZONE_MANAGER_ROLE() external view returns (bytes32); - - function exposed_domainSeparator() external view returns (bytes32); - - function exposed_deriveDomainSeparator() external view returns (bytes32 domainSeparator); - - function exposed_getSupportedSubstandards() external pure returns (uint256[] memory substandards); - - function exposed_deriveSignedOrderHash( - address fulfiller, - uint64 expiration, - bytes32 orderHash, - bytes calldata context - ) external view returns (bytes32 signedOrderHash); - - function exposed_validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external; - - function exposed_validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - pure - returns (uint256); - - function exposed_validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - pure - returns (uint256); - - function exposed_validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - pure - returns (uint256); - - function exposed_validateSubstandard7(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - returns (uint256); - - function exposed_validateSubstandard8(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - returns (uint256); - - function exposed_deriveReceivedItemsHash( - ReceivedItem[] calldata receivedItems, - uint256 scalingFactorNumerator, - uint256 scalingFactorDenominator - ) external pure returns (bytes32); - - function exposed_bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) - external - pure - returns (bool); -} diff --git a/test/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.t.sol b/test/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.t.sol deleted file mode 100644 index 19ae3daa..00000000 --- a/test/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.t.sol +++ /dev/null @@ -1,1956 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; - -import {Test} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {ItemType} from "seaport-types-16/src/lib/ConsiderationEnums.sol"; -import {ReceivedItem, Schema, SpentItem, ZoneParameters} from "seaport-types-16/src/lib/ConsiderationStructs.sol"; -import { - ImmutableSignedZoneV3 -} from "../../../../../../contracts/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.sol"; -import { - SIP5EventsAndErrors -} from "../../../../../../contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP5EventsAndErrors.sol"; -import { - SIP6EventsAndErrors -} from "../../../../../../contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP6EventsAndErrors.sol"; -import { - SIP7EventsAndErrors -} from "../../../../../../contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/SIP7EventsAndErrors.sol"; -import { - ZoneAccessControlEventsAndErrors -} from "../../../../../../contracts/trading/seaport16/zones/immutable-signed-zone/v3/interfaces/ZoneAccessControlEventsAndErrors.sol"; -import {SigningTestHelper} from "../../../../seaport/utils/SigningTestHelper.t.sol"; -import {ImmutableSignedZoneV3Harness} from "./ImmutableSignedZoneV3Harness.t.sol"; -import {MockTransferValidator, MockTransferValidatorRevert} from "../../../utils/MockTransferValidator.t.sol"; - -contract ImmutableSignedZoneV3Test is - Test, - SigningTestHelper, - ZoneAccessControlEventsAndErrors, - SIP5EventsAndErrors, - SIP6EventsAndErrors, - SIP7EventsAndErrors -{ - uint64 private constant DEFAULT_EXPIRATION = 100; - address private immutable OWNER = makeAddr("owner"); - address private immutable FULFILLER = makeAddr("fulfiller"); - address private immutable OFFERER = makeAddr("offerer"); - address private immutable SIGNER; - uint256 private immutable SIGNER_PRIVATE_KEY; - address private immutable SEAPORT = makeAddr("seaport"); - - // OpenZeppelin v5 access/IAccessControl.sol - error AccessControlUnauthorizedAccount(address account, bytes32 neededRole); - error AccessControlBadConfirmation(); - - constructor() { - (SIGNER, SIGNER_PRIVATE_KEY) = makeAddrAndKey("signer"); - } - - /* constructor */ - - function test_contructor_grantsAdminRoleToOwner() public { - address owner = makeAddr("owner"); - ImmutableSignedZoneV3 zone = new ImmutableSignedZoneV3( - "MyZoneName", SEAPORT, "https://www.immutable.com", "https://www.immutable.com/docs", owner - ); - bool ownerHasAdminRole = zone.hasRole(zone.DEFAULT_ADMIN_ROLE(), owner); - assertTrue(ownerHasAdminRole); - } - - function test_contructor_revertsIfOwnerIsTheZeroAddress() public { - vm.expectRevert("ZoneAccessControl: owner is the zero address"); - new ImmutableSignedZoneV3( - "MyZoneName", SEAPORT, "https://www.immutable.com", "https://www.immutable.com/docs", address(0) - ); - } - - function test_contructor_emitsSeaportCompatibleContractDeployedEvent() public { - vm.expectEmit(); - emit SeaportCompatibleContractDeployed(); - new ImmutableSignedZoneV3( - "MyZoneName", SEAPORT, "https://www.immutable.com", "https://www.immutable.com/docs", makeAddr("owner") - ); - } - - /* grantRole */ - - function test_grantRole_revertsIfCalledByNonAdminRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - address nonAdmin = makeAddr("non_admin"); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.expectRevert( - abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, nonAdmin, zone.DEFAULT_ADMIN_ROLE()) - ); - vm.prank(nonAdmin); - zone.grantRole(managerRole, OWNER); - } - - function test_grantRole_grantsIfCalledByAdminRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - address newManager = makeAddr("new_manager"); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, newManager); - bool newManagerHasManagerRole = zone.hasRole(managerRole, newManager); - assertTrue(newManagerHasManagerRole); - } - - /* revokeRole */ - - function test_revokeRole_revertsIfCalledByNonAdminRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - address managerOne = makeAddr("manager_one"); - address managerTwo = makeAddr("manager_two"); - vm.prank(OWNER); - zone.grantRole(managerRole, managerOne); - vm.prank(OWNER); - zone.grantRole(managerRole, managerTwo); - vm.expectRevert( - abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, managerOne, zone.DEFAULT_ADMIN_ROLE()) - ); - vm.prank(managerOne); - zone.revokeRole(managerRole, managerTwo); - } - - function test_revokeRole_revertsIfRevokingLastDefaultAdminRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 adminRole = zone.DEFAULT_ADMIN_ROLE(); - vm.expectRevert(abi.encodeWithSelector(LastDefaultAdminRole.selector, OWNER)); - vm.prank(OWNER); - zone.revokeRole(adminRole, OWNER); - } - - function test_revokeRole_revokesIfRevokingNonLastDefaultAdminRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 adminRole = zone.DEFAULT_ADMIN_ROLE(); - address newAdmin = makeAddr("new_admin"); - vm.prank(OWNER); - zone.grantRole(adminRole, newAdmin); - vm.prank(OWNER); - zone.revokeRole(adminRole, OWNER); - bool ownerHasAdminRole = zone.hasRole(adminRole, OWNER); - assertFalse(ownerHasAdminRole); - } - - function test_revokeRole_revokesIfRevokingLastNonDefaultAdminRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.revokeRole(managerRole, OWNER); - bool ownerHasManagerRole = zone.hasRole(managerRole, OWNER); - uint256 managerCount = zone.getRoleMemberCount(managerRole); - assertFalse(ownerHasManagerRole); - assertEq(managerCount, 0); - } - - /* renounceRole */ - - function test_renounceRole_revertsIfCallerDoesNotMatchCallerConfirmationAddress() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - address newManager = makeAddr("new_manager"); - vm.prank(OWNER); - zone.grantRole(managerRole, newManager); - vm.expectRevert(abi.encodeWithSelector(AccessControlBadConfirmation.selector)); - vm.prank(newManager); - zone.renounceRole(managerRole, makeAddr("random")); - } - - function test_renounceRole_revertsIfRenouncingLastDefaultAdminRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 adminRole = zone.DEFAULT_ADMIN_ROLE(); - vm.expectRevert(abi.encodeWithSelector(LastDefaultAdminRole.selector, OWNER)); - vm.prank(OWNER); - zone.renounceRole(adminRole, OWNER); - } - - function test_renounceRole_revokesIfRenouncingNonLastDefaultAdminRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 adminRole = zone.DEFAULT_ADMIN_ROLE(); - address newAdmin = makeAddr("new_admin"); - vm.prank(OWNER); - zone.grantRole(adminRole, newAdmin); - vm.prank(OWNER); - zone.renounceRole(adminRole, OWNER); - bool ownerHasAdminRole = zone.hasRole(adminRole, OWNER); - assertFalse(ownerHasAdminRole); - } - - function test_renounceRole_revokesIfRenouncingLastNonDefaultAdminRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.renounceRole(managerRole, OWNER); - bool ownerHasManagerRole = zone.hasRole(managerRole, OWNER); - uint256 managerCount = zone.getRoleMemberCount(managerRole); - assertFalse(ownerHasManagerRole); - assertEq(managerCount, 0); - } - - /* isActiveSigner */ - - function test_isActiveSigner_returnsTrueIfSignerIsActive() public { - address signer = makeAddr("signer"); - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(signer); - bool isActive = zone.isActiveSigner(signer); - assertTrue(isActive); - } - - function test_isActiveSigner_returnsFalseIfSignerIsNotActive() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bool isActive = zone.isActiveSigner(makeAddr("signer")); - assertFalse(isActive); - } - - /* addSigner */ - - function test_addSigner_revertsIfCalledByNonZoneManagerRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - vm.expectRevert( - abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, OWNER, zone.ZONE_MANAGER_ROLE()) - ); - vm.prank(OWNER); - zone.addSigner(makeAddr("signer_to_add")); - } - - function test_addSigner_revertsIfSignerIsTheZeroAddress() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.expectRevert(abi.encodeWithSelector(SignerCannotBeZeroAddress.selector)); - vm.prank(OWNER); - zone.addSigner(address(0)); - } - - function test_addSigner_emitsSignerAddedEvent() public { - address signerToAdd = makeAddr("signer_to_add"); - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.expectEmit(address(zone)); - emit SignerAdded(signerToAdd); - vm.prank(OWNER); - zone.addSigner(signerToAdd); - } - - function test_addSigner_revertsIfSignerAlreadyActive() public { - address signerToAdd = makeAddr("signer_to_add"); - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(signerToAdd); - vm.expectRevert(abi.encodeWithSelector(SignerAlreadyActive.selector, signerToAdd)); - vm.prank(OWNER); - zone.addSigner(signerToAdd); - } - - function test_addSigner_revertsIfSignerWasPreviouslyActive() public { - address signerToAdd = makeAddr("signer_to_add"); - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(signerToAdd); - vm.prank(OWNER); - zone.removeSigner(signerToAdd); - vm.expectRevert(abi.encodeWithSelector(SignerCannotBeReauthorized.selector, signerToAdd)); - vm.prank(OWNER); - zone.addSigner(signerToAdd); - } - - /* removeSigner */ - - function test_removeSigner_revertsIfCalledByNonZoneManagerRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - vm.expectRevert( - abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, OWNER, zone.ZONE_MANAGER_ROLE()) - ); - vm.prank(OWNER); - zone.removeSigner(makeAddr("signer_to_remove")); - } - - function test_removeSigner_revertsIfSignerNotActive() public { - address signerToRemove = makeAddr("signer_to_remove"); - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.expectRevert(abi.encodeWithSelector(SignerNotActive.selector, signerToRemove)); - vm.prank(OWNER); - zone.removeSigner(signerToRemove); - } - - function test_removeSigner_emitsSignerRemovedEvent() public { - address signerToRemove = makeAddr("signer_to_remove"); - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(signerToRemove); - vm.expectEmit(address(zone)); - emit SignerRemoved(signerToRemove); - vm.prank(OWNER); - zone.removeSigner(signerToRemove); - } - - /* updateAPIEndpoint */ - - function test_updateAPIEndpoint_revertsIfCalledByNonZoneManagerRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - vm.expectRevert( - abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, OWNER, zone.ZONE_MANAGER_ROLE()) - ); - vm.prank(OWNER); - zone.updateAPIEndpoint("https://www.new-immutable.com"); - } - - function test_updateAPIEndpoint_updatesAPIEndpointIfCalledByZoneManagerRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - string memory expectedApiEndpoint = "https://www.new-immutable.com"; - vm.prank(OWNER); - zone.updateAPIEndpoint(expectedApiEndpoint); - (, Schema[] memory schemas) = zone.getSeaportMetadata(); - (, string memory apiEndpoint,,) = abi.decode(schemas[0].metadata, (bytes32, string, uint256[], string)); - assertEq(apiEndpoint, expectedApiEndpoint); - } - - /* updateDocumentationURI */ - - function test_updateDocumentationURI_revertsIfCalledByNonZoneManagerRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - vm.expectRevert( - abi.encodeWithSelector(AccessControlUnauthorizedAccount.selector, OWNER, zone.ZONE_MANAGER_ROLE()) - ); - vm.prank(OWNER); - zone.updateDocumentationURI("https://www.new-immutable.com/docs"); - } - - function test_updateDocumentationURI_updatesDocumentationURIIfCalledByZoneManagerRole() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - string memory expectedDocumentationURI = "https://www.new-immutable.com/docs"; - vm.prank(OWNER); - zone.updateDocumentationURI(expectedDocumentationURI); - (, Schema[] memory schemas) = zone.getSeaportMetadata(); - (,,, string memory documentationURI) = abi.decode(schemas[0].metadata, (bytes32, string, uint256[], string)); - assertEq(documentationURI, expectedDocumentationURI); - } - - /* getSeaportMetadata */ - - function test_getSeaportMetadata() public { - string memory expectedZoneName = "MyZoneName"; - string memory expectedApiEndpoint = "https://www.immutable.com"; - string memory expectedDocumentationURI = "https://www.immutable.com/docs"; - - ImmutableSignedZoneV3Harness zone = new ImmutableSignedZoneV3Harness( - expectedZoneName, SEAPORT, expectedApiEndpoint, expectedDocumentationURI, OWNER - ); - - bytes32 expectedDomainSeparator = zone.exposed_deriveDomainSeparator(); - uint256[] memory expectedSubstandards = zone.exposed_getSupportedSubstandards(); - - (string memory name, Schema[] memory schemas) = zone.getSeaportMetadata(); - ( - bytes32 domainSeparator, - string memory apiEndpoint, - uint256[] memory substandards, - string memory documentationURI - ) = abi.decode(schemas[0].metadata, (bytes32, string, uint256[], string)); - - assertEq(name, expectedZoneName); - assertEq(schemas.length, 1); - assertEq(schemas[0].id, 7); - assertEq(domainSeparator, expectedDomainSeparator); - assertEq(apiEndpoint, expectedApiEndpoint); - assertEq(substandards, expectedSubstandards); - assertEq(documentationURI, expectedDocumentationURI); - } - - /* sip7Information */ - - function test_sip7Information() public { - string memory expectedApiEndpoint = "https://www.immutable.com"; - string memory expectedDocumentationURI = "https://www.immutable.com/docs"; - - ImmutableSignedZoneV3Harness zone = new ImmutableSignedZoneV3Harness( - "MyZoneName", SEAPORT, expectedApiEndpoint, expectedDocumentationURI, OWNER - ); - - bytes32 expectedDomainSeparator = zone.exposed_deriveDomainSeparator(); - uint256[] memory expectedSubstandards = zone.exposed_getSupportedSubstandards(); - - ( - bytes32 domainSeparator, - string memory apiEndpoint, - uint256[] memory substandards, - string memory documentationURI - ) = zone.sip7Information(); - - assertEq(domainSeparator, expectedDomainSeparator); - assertEq(apiEndpoint, expectedApiEndpoint); - assertEq(substandards, expectedSubstandards); - assertEq(documentationURI, expectedDocumentationURI); - } - - /* authorizeOrder */ - - function test_authorizeOrder_revertsIfSeaportNotCaller() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(SIGNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - abi.encodePacked(bytes1(0x01), zoneParameters.consideration[0].identifier) - ); - - vm.expectRevert(abi.encodeWithSelector(CallerNotSeaport.selector)); - vm.prank(makeAddr("not_seaport")); - zone.authorizeOrder(zoneParameters); - } - - function test_authorizeOrder_allowedIfSeaportIsCaller() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(SIGNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - abi.encodePacked(bytes1(0x01), zoneParameters.consideration[0].identifier) - ); - - vm.prank(SEAPORT); - zone.authorizeOrder(zoneParameters); - } - - function test_authorizeOrder_revertsIfEmptyExtraData() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = new bytes(0); - vm.expectRevert( - abi.encodeWithSelector(InvalidExtraData.selector, "extraData is empty", zoneParameters.orderHash) - ); - vm.prank(SEAPORT); - zone.authorizeOrder(zoneParameters); - } - - function test_authorizeOrder_revertsIfExtraDataLengthIsLessThan93() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = bytes(hex"01"); - vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, "extraData length must be at least 93 bytes", zoneParameters.orderHash - ) - ); - vm.prank(SEAPORT); - zone.authorizeOrder(zoneParameters); - } - - function test_authorizeOrder_revertsIfExtraDataVersionIsNotSupported() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = bytes( - hex"01f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000660f3027d9ef9e6e50a74cc24433373b9cdd97693a02adcc94e562bb59a5af68190ecaea4414dcbe74618f6c77d11cbcf4a8345bbdf46e665249904925c95929ba6606638b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8" - ); - vm.expectRevert(abi.encodeWithSelector(UnsupportedExtraDataVersion.selector, uint8(1))); - vm.prank(SEAPORT); - zone.authorizeOrder(zoneParameters); - } - - function test_authorizeOrder_revertsIfSignatureHasExpired() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - new bytes(0) - ); - - vm.expectRevert(abi.encodeWithSelector(SignatureExpired.selector, 1000, 100, zoneParameters.orderHash)); - // set current block.timestamp to be 1000 - vm.warp(1000); - vm.prank(SEAPORT); - zone.authorizeOrder(zoneParameters); - } - - function test_authorizeOrder_revertsIfActualFulfillerDoesNotMatchExpectedFulfiller() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.fulfiller = makeAddr("random"); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, FULFILLER, DEFAULT_EXPIRATION, zoneParameters.orderHash, new bytes(0) - ); - - vm.expectRevert( - abi.encodeWithSelector( - InvalidFulfiller.selector, FULFILLER, zoneParameters.fulfiller, zoneParameters.orderHash - ) - ); - vm.prank(SEAPORT); - zone.authorizeOrder(zoneParameters); - } - - function test_authorizeOrder_revertsIfNoSpentItems() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.offer = new SpentItem[](0); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - new bytes(0) - ); - - vm.expectRevert(abi.encodeWithSelector(NoSpentItems.selector, zoneParameters.orderHash)); - vm.prank(SEAPORT); - zone.authorizeOrder(zoneParameters); - } - - function test_authorizeOrder_revertsIfNoReceivedItems() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.consideration = new ReceivedItem[](0); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - new bytes(0) - ); - - vm.expectRevert(abi.encodeWithSelector(NoReceivedItems.selector, zoneParameters.orderHash)); - vm.prank(SEAPORT); - zone.authorizeOrder(zoneParameters); - } - - function test_authorizeOrder_revertsIfSignerIsNotActive() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - // no signer added - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - abi.encodePacked(bytes1(0x01), zoneParameters.consideration[0].identifier) - ); - - vm.expectRevert( - abi.encodeWithSelector(SignerNotActive.selector, address(0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2)) - ); - vm.prank(SEAPORT); - zone.authorizeOrder(zoneParameters); - } - - function test_authorizeOrder_returnsMagicValueOnSuccessfulValidation() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(SIGNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - abi.encodePacked(bytes1(0x01), zoneParameters.consideration[0].identifier) - ); - - vm.prank(SEAPORT); - assertEq(zone.authorizeOrder(zoneParameters), bytes4(0x01e4d72a)); - } - - /* validateOrder */ - - function test_validateOrder_revertsIfSeaportNotCaller() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(SIGNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - abi.encodePacked(bytes1(0x01), zoneParameters.consideration[0].identifier) - ); - - vm.expectRevert(abi.encodeWithSelector(CallerNotSeaport.selector)); - vm.prank(makeAddr("not_seaport")); - zone.validateOrder(zoneParameters); - } - - function test_validateOrder_allowedIfSeaportIsCaller() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(SIGNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - abi.encodePacked(bytes1(0x01), zoneParameters.consideration[0].identifier) - ); - - vm.prank(SEAPORT); - zone.validateOrder(zoneParameters); - } - - function test_validateOrder_revertsIfContextIsEmpty() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(SIGNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - new bytes(0) - ); - - vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, "invalid context, no substandards present", zoneParameters.orderHash - ) - ); - vm.prank(SEAPORT); - zone.validateOrder(zoneParameters); - } - - function test_validateOrder_returnsMagicValueOnSuccessfulValidation() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); - vm.prank(OWNER); - zone.grantRole(managerRole, OWNER); - vm.prank(OWNER); - zone.addSigner(SIGNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - zoneParameters.extraData = _buildExtraData( - zone, - SIGNER_PRIVATE_KEY, - zoneParameters.fulfiller, - DEFAULT_EXPIRATION, - zoneParameters.orderHash, - abi.encodePacked(bytes1(0x01), zoneParameters.consideration[0].identifier) - ); - - vm.prank(SEAPORT); - assertEq(zone.validateOrder(zoneParameters), bytes4(0x17b1f942)); - } - - /* supportsInterface */ - - function test_supportsInterface() public { - ImmutableSignedZoneV3 zone = _newZone(OWNER); - assertTrue(zone.supportsInterface(0x01ffc9a7)); // ERC165 interface - assertFalse(zone.supportsInterface(0xffffffff)); // ERC165 compliance - assertTrue(zone.supportsInterface(0x2e778efc)); // SIP-5 interface - assertTrue(zone.supportsInterface(0x39dd6933)); // SIP-5 compliance - ZoneInterface - } - - /* _domainSeparator */ - - function test_domainSeparator_returnsCachedDomainSeparatorWhenChainIDMatchesValueSetOnDeployment() public { - address deployer = makeAddr("deployer"); - vm.startPrank(deployer); - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - vm.stopPrank(); - - bytes32 domainSeparator = zone.exposed_domainSeparator(); - assertEq(domainSeparator, bytes32(0x7946de4e45fec74bd718eeaf23c0d6e9ee4a66d31dcaa416695b27a794624c58)); - } - - function test_domainSeparator_returnsUpdatedDomainSeparatorIfChainIDIsDifferentFromValueSetOnDeployment() public { - address deployer = makeAddr("deployer"); - vm.startPrank(deployer); - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - vm.stopPrank(); - - bytes32 domainSeparatorCached = zone.exposed_domainSeparator(); - vm.chainId(31338); - bytes32 domainSeparatorDerived = zone.exposed_domainSeparator(); - - assertNotEq(domainSeparatorCached, domainSeparatorDerived); - assertEq(domainSeparatorDerived, bytes32(0xf0f125b29a5274c4bcd4a916a5363c1c79472ac09bca6f09dbb97a23a3e6bc8f)); - } - - /* _deriveDomainSeparator */ - - function test_deriveDomainSeparator_returnsDomainSeparatorForChainID() public { - address deployer = makeAddr("deployer"); - vm.startPrank(deployer); - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - vm.stopPrank(); - - bytes32 domainSeparator = zone.exposed_deriveDomainSeparator(); - assertEq(domainSeparator, bytes32(0x7946de4e45fec74bd718eeaf23c0d6e9ee4a66d31dcaa416695b27a794624c58)); - } - - /* _getSupportedSubstandards */ - - function test_getSupportedSubstandards() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - uint256[] memory supportedSubstandards = zone.exposed_getSupportedSubstandards(); - assertEq(supportedSubstandards.length, 6); - assertEq(supportedSubstandards[0], 1); - assertEq(supportedSubstandards[1], 3); - assertEq(supportedSubstandards[2], 4); - assertEq(supportedSubstandards[3], 6); - assertEq(supportedSubstandards[4], 7); - assertEq(supportedSubstandards[5], 8); - } - - /* _deriveSignedOrderHash */ - - function test_deriveSignedOrderHash_returnsHashOfSignedOrder() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - address fulfiller = 0x71458637cD221877830A21F543E8b731e93C3627; - uint64 expiration = 1234995; - bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); - bytes memory context = hex"9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91"; - bytes32 derivedSignedOrderHash = zone.exposed_deriveSignedOrderHash(fulfiller, expiration, orderHash, context); - assertEq(derivedSignedOrderHash, 0x40c87207c5a0c362da24cb974859c70655de00fee9400f3a805ac360b90bd8c5); - } - - /* _validateSubstandards */ - - function test_validateSubstandards_revertsIfEmptyContext() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = new bytes(0); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, "invalid context, no substandards present", zoneParameters.orderHash - ) - ); - - zone.exposed_validateSubstandards(context, zoneParameters, true); - } - - function test_validateSubstandards_beforeHookSubstandard1() public { - _test_validateSubstandards_substandard1(true); - } - - function test_validateSubstandards_afterHookSubstandard1() public { - _test_validateSubstandards_substandard1(false); - } - - function _test_validateSubstandards_substandard1(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x01), zoneParameters.consideration[0].identifier); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - zone.exposed_validateSubstandards(context, zoneParameters, before); - } - - function test_validateSubstandards_beforeHookSubstandard3() public { - _test_validateSubstandards_substandard3(true); - } - - function test_validateSubstandards_afterHookSubstandard3() public { - _test_validateSubstandards_substandard3(false); - } - - function _test_validateSubstandards_substandard3(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes32 substandard3Data = zone.exposed_deriveReceivedItemsHash(zoneParameters.consideration, 1, 1); - bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - zone.exposed_validateSubstandards(context, zoneParameters, before); - } - - function test_validateSubstandards_beforeHookSubstandard4() public { - _test_validateSubstandards_substandard4(true); - } - - function test_validateSubstandards_afterHookSubstandard4() public { - _test_validateSubstandards_substandard4(false); - } - - function _test_validateSubstandards_substandard4(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory substandard4Data = abi.encode(zoneParameters.orderHashes); - bytes memory context = abi.encodePacked(bytes1(0x04), substandard4Data); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - zone.exposed_validateSubstandards(context, zoneParameters, before); - } - - function test_validateSubstandards_beforeHookSubstandard6() public { - _test_validateSubstandards_substandard6(true); - } - - function test_validateSubstandards_afterHookSubstandard6() public { - _test_validateSubstandards_substandard6(false); - } - - function _test_validateSubstandards_substandard6(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x2), identifier: 222, amount: 10, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(zoneParameters.consideration, 100, 10); - bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), substandard6Data); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - zone.exposed_validateSubstandards(context, zoneParameters, before); - } - - function test_validateSubstandards_beforeHookSubstandard7() public { - _test_validateSubstandards_substandard7(true); - } - - function test_validateSubstandards_afterHookSubstandard7() public { - _test_validateSubstandards_substandard7(false); - } - - function _test_validateSubstandards_substandard7(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - MockTransferValidator transferValidator = new MockTransferValidator(); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - SpentItem memory spentItem = - SpentItem({itemType: ItemType.ERC20, token: address(0x9), identifier: 0, amount: 100}); - spentItems[0] = spentItem; - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC721, token: address(0x8), identifier: 222, amount: 1, recipient: payable(address(0x3)) - }); - receivedItems[0] = receivedItem; - zoneParameters.consideration = receivedItems; - - bytes memory context = abi.encodePacked( - bytes1(0x07), zoneParameters.consideration[0].identifier, address(transferValidator), address(0x7) - ); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - zone.exposed_validateSubstandards(context, zoneParameters, before); - } - - function test_validateSubstandards_beforeHookSubstandard8() public { - _test_validateSubstandards_substandard8(true); - } - - function test_validateSubstandards_afterHookSubstandard8() public { - _test_validateSubstandards_substandard8(false); - } - - function _test_validateSubstandards_substandard8(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - MockTransferValidator transferValidator = new MockTransferValidator(); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - SpentItem memory spentItem = - SpentItem({itemType: ItemType.ERC20, token: address(0x9), identifier: 0, amount: 100}); - spentItems[0] = spentItem; - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC721, token: address(0x8), identifier: 222, amount: 1, recipient: payable(address(0x3)) - }); - receivedItems[0] = receivedItem; - zoneParameters.consideration = receivedItems; - - bytes memory context = - abi.encodePacked(bytes1(0x08), zoneParameters.consideration[0].identifier, address(transferValidator)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - zone.exposed_validateSubstandards(context, zoneParameters, before); - } - - function test_validateSubstandards_beforeHookMultipleSubstandardsInCorrectOrder() public { - _test_validateSubstandards_multipleSubstandardsInCorrectOrder(true); - } - - function test_validateSubstandards_afterHookMultipleSubstandardsInCorrectOrder() public { - _test_validateSubstandards_multipleSubstandardsInCorrectOrder(false); - } - - function _test_validateSubstandards_multipleSubstandardsInCorrectOrder(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - bytes32 substandard3Data = zone.exposed_deriveReceivedItemsHash(zoneParameters.consideration, 1, 1); - bytes memory substandard4Data = abi.encode(zoneParameters.orderHashes); - bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data, bytes1(0x04), substandard4Data); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - zone.exposed_validateSubstandards(context, zoneParameters, before); - } - - function test_validateSubstandards_beforeHookSubstandards3Then6() public { - _test_validateSubstandards_substandards3Then6(true); - } - - function test_validateSubstandards_afterHookSubstandards3Then6() public { - _test_validateSubstandards_substandards3Then6(false); - } - - function _test_validateSubstandards_substandards3Then6(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC1155, token: address(0x5), identifier: 222, amount: 10}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x4), identifier: 0, amount: 20, recipient: payable(address(0x3)) - }); - receivedItems[0] = receivedItem; - zoneParameters.consideration = receivedItems; - - bytes32 substandard3Data = zone.exposed_deriveReceivedItemsHash(zoneParameters.consideration, 1, 1); - bytes memory substandard6Data = abi.encodePacked(uint256(10), substandard3Data); - bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data, bytes1(0x06), substandard6Data); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - zone.exposed_validateSubstandards(context, zoneParameters, before); - } - - function test_validateSubstandards_beforeHookManySubstandards() public { - _test_validateSubstandards_manySubstandards(true); - } - - function test_validateSubstandards_afterHookManySubstandards() public { - _test_validateSubstandards_manySubstandards(false); - } - - function _test_validateSubstandards_manySubstandards(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC1155, token: address(0x5), identifier: 222, amount: 10}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x4), identifier: 0, amount: 20, recipient: payable(address(0x3)) - }); - receivedItems[0] = receivedItem; - zoneParameters.consideration = receivedItems; - - bytes memory substandard1Data = abi.encodePacked(uint256(0)); - bytes32 substandard3Data = zone.exposed_deriveReceivedItemsHash(zoneParameters.consideration, 1, 1); - bytes memory substandard4Data = abi.encode(zoneParameters.orderHashes); - bytes memory substandard6Data = abi.encodePacked(uint256(10), substandard3Data); - bytes memory context = abi.encodePacked( - bytes1(0x01), - substandard1Data, - bytes1(0x03), - substandard3Data, - bytes1(0x04), - substandard4Data, - bytes1(0x06), - substandard6Data - ); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - zone.exposed_validateSubstandards(context, zoneParameters, before); - } - - function test_validateSubstandards_revertsOnMultipleSubstandardsInIncorrectOrder() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x2), identifier: 222, amount: 10, recipient: payable(address(0x3)) - }); - receivedItems[0] = receivedItem; - zoneParameters.consideration = receivedItems; - - bytes32 substandard3Data = zone.exposed_deriveReceivedItemsHash(zoneParameters.consideration, 1, 1); - bytes memory substandard4Data = abi.encode(zoneParameters.orderHashes); - bytes memory context = abi.encodePacked(bytes1(0x04), substandard4Data, bytes1(0x03), substandard3Data); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, "invalid context, unexpected context length", zoneParameters.orderHash - ) - ); - zone.exposed_validateSubstandards(context, zoneParameters, true); - } - - /* _validateSubstandard1 */ - - function test_validateSubstandard1_returnsZeroLengthIfNotSubstandard1() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x03)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard1(context, zoneParameters, true); - assertEq(substandardLengthResult, 0); - } - - function test_validateSubstandard1_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x01), bytes10(0)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, "invalid substandard 1 data length", zoneParameters.orderHash - ) - ); - zone.exposed_validateSubstandard1(context, zoneParameters, true); - } - - function test_validateSubstandard1_revertsIfFirstReceivedItemIdentifierNotEqualToIdentifierInContext() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC20, token: address(0x45), identifier: 0, amount: 200}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC721, token: address(0x2), identifier: 45, amount: 1, recipient: payable(address(0x3)) - }); - receivedItems[0] = receivedItem; - zoneParameters.consideration = receivedItems; - - bytes memory context = abi.encodePacked(bytes1(0x01), uint256(46)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert(abi.encodeWithSelector(Substandard1Violation.selector, zoneParameters.orderHash, 45, 46)); - zone.exposed_validateSubstandard1(context, zoneParameters, true); - } - - function test_validateSubstandard1_beforeHookReturns33OnSuccess() public { - _test_validateSubstandard1_returns33OnSuccess(true); - } - - function test_validateSubstandard1_afterHookReturns33OnSuccess() public { - _test_validateSubstandard1_returns33OnSuccess(false); - } - - function _test_validateSubstandard1_returns33OnSuccess(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC20, token: address(0x45), identifier: 0, amount: 200}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC721, token: address(0x2), identifier: 45, amount: 1, recipient: payable(address(0x3)) - }); - receivedItems[0] = receivedItem; - zoneParameters.consideration = receivedItems; - - bytes memory context = abi.encodePacked(bytes1(0x01), uint256(45)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard1(context, zoneParameters, before); - assertEq(substandardLengthResult, 33); - } - - /* _validateSubstandard3 */ - - function test_validateSubstandard3_returnsZeroLengthIfNotSubstandard3() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x04)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard3(context, zoneParameters, true); - assertEq(substandardLengthResult, 0); - } - - function test_validateSubstandard3_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x03), bytes10(0)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, "invalid substandard 3 data length", zoneParameters.orderHash - ) - ); - zone.exposed_validateSubstandard3(context, zoneParameters, true); - } - - function test_validateSubstandard3_revertsIfDerivedReceivedItemsHashNotEqualToHashInContext() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x03), bytes32(0)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert(abi.encodeWithSelector(Substandard3Violation.selector, zoneParameters.orderHash)); - zone.exposed_validateSubstandard3(context, zoneParameters, true); - } - - function test_validateSubstandard3_beforeHookReturns33OnSuccess() public { - _test_validateSubstandard3_returns33OnSuccess(true); - } - - function test_validateSubstandard3_afterHookReturns33OnSuccess() public { - _test_validateSubstandard3_returns33OnSuccess(false); - } - - function _test_validateSubstandard3_returns33OnSuccess(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - bytes32 substandard3Data = zone.exposed_deriveReceivedItemsHash(zoneParameters.consideration, 1, 1); - bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard3(context, zoneParameters, before); - assertEq(substandardLengthResult, 33); - } - - /* _validateSubstandard4 */ - - function test_validateSubstandard4_returnsZeroLengthIfNotSubstandard4() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x02)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard4(context, zoneParameters, true); - assertEq(substandardLengthResult, 0); - } - - function test_validateSubstandard4_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - bytes memory context = abi.encodePacked(bytes1(0x04), bytes10(0)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, "invalid substandard 4 data length", zoneParameters.orderHash - ) - ); - zone.exposed_validateSubstandard4(context, zoneParameters, true); - } - - function test_validateSubstandard4_revertsIfExpectedOrderHashesAreNotPresent() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - bytes32[] memory orderHashes = new bytes32[](1); - orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); - zoneParameters.orderHashes = orderHashes; - - bytes32[] memory expectedOrderHashes = new bytes32[](1); - expectedOrderHashes[0] = bytes32(0x17d4cf2b6c174a86b533210b50ba676a82e5ab1e2e89ea538f0a43a37f92fcbf); - - bytes memory context = abi.encodePacked(bytes1(0x04), abi.encode(expectedOrderHashes)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector( - Substandard4Violation.selector, - zoneParameters.orderHashes, - expectedOrderHashes, - zoneParameters.orderHash - ) - ); - zone.exposed_validateSubstandard4(context, zoneParameters, false); - } - - function test_validateSubstandard4_beforeHookReturnsLengthOfSubstandardSegmentOnSuccess() public { - _test_validateSubstandard4_returnsLengthOfSubstandardSegmentOnSuccess(true); - } - - function test_validateSubstandard4_afterHookReturnsLengthOfSubstandardSegmentOnSuccess() public { - _test_validateSubstandard4_returnsLengthOfSubstandardSegmentOnSuccess(false); - } - - function _test_validateSubstandard4_returnsLengthOfSubstandardSegmentOnSuccess(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x04), abi.encode(zoneParameters.orderHashes)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard4(context, zoneParameters, before); - // bytes1 + bytes32 + bytes32 + bytes32 = 97 - assertEq(substandardLengthResult, 97); - } - - /* _validateSubstandard6 */ - - function test_validateSubstandard6_returnsZeroLengthIfNotSubstandard6() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x04)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard6(context, zoneParameters, true); - assertEq(substandardLengthResult, 0); - } - - function test_validateSubstandard6_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x06), bytes10(0)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, "invalid substandard 6 data length", zoneParameters.orderHash - ) - ); - zone.exposed_validateSubstandard6(context, zoneParameters, true); - } - - function test_validateSubstandard6_revertsIfDerivedReceivedItemsHashesIsNotEqualToHashesInContext() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), bytes32(uint256(0x123456))); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector( - Substandard6Violation.selector, zoneParameters.offer[0].amount, 100, zoneParameters.orderHash - ) - ); - zone.exposed_validateSubstandard6(context, zoneParameters, true); - } - - function test_validateSubstandard6_beforeHookReturnsLengthOfSubstandardSegmentOnSuccess() public { - _test_validateSubstandard6_returnsLengthOfSubstandardSegmentOnSuccess(true); - } - - function test_validateSubstandard6_afterHookReturnsLengthOfSubstandardSegmentOnSuccess() public { - _test_validateSubstandard6_returnsLengthOfSubstandardSegmentOnSuccess(false); - } - - function _test_validateSubstandard6_returnsLengthOfSubstandardSegmentOnSuccess(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x2), identifier: 222, amount: 10, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(zoneParameters.consideration, 100, 10); - bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), substandard6Data); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard6(context, zoneParameters, before); - // bytes1 + uint256 + bytes32 = 65 - assertEq(substandardLengthResult, 65); - } - - /* _validateSubstandard7 */ - - function test_validateSubstandard7_returnsZeroLengthIfNotSubstandard7() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - bytes memory context = abi.encodePacked(bytes1(0x04)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard7(context, zoneParameters, true); - assertEq(substandardLengthResult, 0); - } - - function test_validateSubstandard7_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - bytes memory context = abi.encodePacked(bytes1(0x07), bytes10(0)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, "invalid substandard 7 data length", zoneParameters.orderHash - ) - ); - zone.exposed_validateSubstandard7(context, zoneParameters, true); - } - - function test_validateSubstandard7_revertsIfFirstReceivedItemIdentifierNotEqualToIdentifierInContext() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC20, token: address(0x55), identifier: 0, amount: 100}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC721, token: address(0x2), identifier: 45, amount: 1, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes memory context = abi.encodePacked(bytes1(0x07), uint256(46), address(0x6), address(0x7)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector(Substandard7IdentifierViolation.selector, zoneParameters.orderHash, 45, 46) - ); - zone.exposed_validateSubstandard7(context, zoneParameters, true); - } - - function test_validateSubstandard7_revertsIfItemTypeRequirementsAreNotMet() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC20, token: address(0x2), identifier: 0, amount: 50}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x9), identifier: 0, amount: 100, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes memory context = abi.encodePacked(bytes1(0x07), uint256(0), address(0x6), address(0x7)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector(Substandard7UnexpectedItemTypeViolation.selector, zoneParameters.orderHash) - ); - zone.exposed_validateSubstandard7(context, zoneParameters, true); - } - - function test_validateSubstandard7_revertsIfTransferValidatorBeforeAuthorizedTransferReverts() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - MockTransferValidator transferValidator = new MockTransferValidator(); - address operator = makeAddr("operator"); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x8), identifier: 222, amount: 1}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x9), identifier: 0, amount: 100, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes memory context = abi.encodePacked( - bytes1(0x07), zoneParameters.consideration[0].identifier, address(transferValidator), operator - ); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - transferValidator.revertBeforeAuthorizedTransferWithOperator(operator, zoneParameters.offer[0].token); - vm.expectRevert( - abi.encodeWithSelector( - MockTransferValidatorRevert.selector, "beforeAuthorizedTransfer(address operator, address token)" - ) - ); - zone.exposed_validateSubstandard7(context, zoneParameters, true); - } - - function test_validateSubstandard7_revertsIfTransferValidatorAfterAuthorizedTransferReverts() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - MockTransferValidator transferValidator = new MockTransferValidator(); - address operator = makeAddr("operator"); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x8), identifier: 222, amount: 1}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x9), identifier: 0, amount: 100, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes memory context = abi.encodePacked( - bytes1(0x07), zoneParameters.consideration[0].identifier, address(transferValidator), operator - ); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - transferValidator.revertAfterAuthorizedTransfer(zoneParameters.offer[0].token); - vm.expectRevert( - abi.encodeWithSelector(MockTransferValidatorRevert.selector, "afterAuthorizedTransfer(address token)") - ); - zone.exposed_validateSubstandard7(context, zoneParameters, false); - } - - function test_validateSubstandard7_beforeHookReturns73OnSuccess() public { - _test_validateSubstandard7_returns73OnSuccess(true); - } - - function test_validateSubstandard7_afterHookReturns73OnSuccess() public { - _test_validateSubstandard7_returns73OnSuccess(false); - } - - function _test_validateSubstandard7_returns73OnSuccess(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - MockTransferValidator transferValidator = new MockTransferValidator(); - address operator = makeAddr("operator"); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x8), identifier: 222, amount: 1}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x9), identifier: 0, amount: 100, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes memory context = abi.encodePacked( - bytes1(0x07), zoneParameters.consideration[0].identifier, address(transferValidator), operator - ); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard7(context, zoneParameters, before); - assertEq(substandardLengthResult, 73); - } - - /* _validateSubstandard8 */ - - function test_validateSubstandard8_returnsZeroLengthIfNotSubstandard8() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - bytes memory context = abi.encodePacked(bytes1(0x04)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard8(context, zoneParameters, true); - assertEq(substandardLengthResult, 0); - } - - function test_validateSubstandard8_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - bytes memory context = abi.encodePacked(bytes1(0x08), bytes10(0)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, "invalid substandard 8 data length", zoneParameters.orderHash - ) - ); - zone.exposed_validateSubstandard8(context, zoneParameters, true); - } - - function test_validateSubstandard8_revertsIfFirstReceivedItemIdentifierNotEqualToIdentifierInContext() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC20, token: address(0x2), identifier: 0, amount: 50}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC721, token: address(0x5), identifier: 45, amount: 1, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes memory context = abi.encodePacked(bytes1(0x08), uint256(46), address(0x6)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector(Substandard8IdentifierViolation.selector, zoneParameters.orderHash, 45, 46) - ); - zone.exposed_validateSubstandard8(context, zoneParameters, true); - } - - function test_validateSubstandard8_revertsIfItemTypeRequirementsAreNotMet() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC20, token: address(0x2), identifier: 0, amount: 50}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x9), identifier: 0, amount: 100, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes memory context = abi.encodePacked(bytes1(0x08), uint256(0), address(0x6)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - vm.expectRevert( - abi.encodeWithSelector(Substandard8UnexpectedItemTypeViolation.selector, zoneParameters.orderHash) - ); - zone.exposed_validateSubstandard8(context, zoneParameters, true); - } - - function test_validateSubstandard8_revertsIfTransferValidatorBeforeAuthorizedTransferReverts() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - MockTransferValidator transferValidator = new MockTransferValidator(); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x8), identifier: 222, amount: 1}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x9), identifier: 0, amount: 100, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes memory context = - abi.encodePacked(bytes1(0x08), zoneParameters.consideration[0].identifier, address(transferValidator)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - transferValidator.revertBeforeAuthorizedTransferWithTokenId(address(0x8), zoneParameters.offer[0].identifier); - vm.expectRevert( - abi.encodeWithSelector( - MockTransferValidatorRevert.selector, "beforeAuthorizedTransfer(address token, uint256 tokenId)" - ) - ); - zone.exposed_validateSubstandard8(context, zoneParameters, true); - } - - function test_validateSubstandard8_revertsIfTransferValidatorAfterAuthorizedTransferReverts() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - MockTransferValidator transferValidator = new MockTransferValidator(); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x8), identifier: 222, amount: 1}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x9), identifier: 0, amount: 100, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes memory context = - abi.encodePacked(bytes1(0x08), zoneParameters.consideration[0].identifier, address(transferValidator)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - transferValidator.revertAfterAuthorizedTransferWithTokenId(address(0x8), zoneParameters.offer[0].identifier); - vm.expectRevert( - abi.encodeWithSelector( - MockTransferValidatorRevert.selector, "afterAuthorizedTransfer(address token, uint256 tokenId)" - ) - ); - zone.exposed_validateSubstandard8(context, zoneParameters, false); - } - - function test_validateSubstandard8_beforeHookReturns53OnSuccess() public { - _test_validateSubstandard8_returns53OnSuccess(true); - } - - function test_validateSubstandard8_afterHookReturns53OnSuccess() public { - _test_validateSubstandard8_returns53OnSuccess(false); - } - - function _test_validateSubstandard8_returns53OnSuccess(bool before) private { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - MockTransferValidator transferValidator = new MockTransferValidator(); - - ZoneParameters memory zoneParameters = _defaultBaseZoneParameters(); - - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x8), identifier: 222, amount: 1}); - zoneParameters.offer = spentItems; - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x9), identifier: 0, amount: 100, recipient: payable(address(0x3)) - }); - zoneParameters.consideration = receivedItems; - - bytes memory context = - abi.encodePacked(bytes1(0x08), zoneParameters.consideration[0].identifier, address(transferValidator)); - zoneParameters.extraData = _buildExtraData( - zone, SIGNER_PRIVATE_KEY, zoneParameters.fulfiller, DEFAULT_EXPIRATION, zoneParameters.orderHash, context - ); - - uint256 substandardLengthResult = zone.exposed_validateSubstandard8(context, zoneParameters, before); - assertEq(substandardLengthResult, 53); - } - - /* _deriveReceivedItemsHash */ - - function test_deriveReceivedItemsHash_returnsHashIfNoReceivedItems() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ReceivedItem[] memory receivedItems = new ReceivedItem[](0); - - bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, 0, 0); - assertEq(receivedItemsHash, bytes32(0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)); - } - - function test_deriveReceivedItemsHash_returnsHashForValidReceivedItems() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - ReceivedItem[] memory receivedItems = new ReceivedItem[](2); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x2), identifier: 222, amount: 10, recipient: payable(address(0x3)) - }); - receivedItems[1] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x2), identifier: 199, amount: 10, recipient: payable(address(0x3)) - }); - - // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10)); - bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10); - assertEq(receivedItemsHash, bytes32(0x8f5c27e415d7805dea8816d4030dc2c0ce11f8f48a0adcde373021dec7b41aad)); - } - - function test_deriveReceivedItemsHash_returnsHashForReceivedItemWithAVeryLargeAmount() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x2), identifier: 222, amount: 10, recipient: payable(address(0x3)) - }); - - // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, type(uint256).max, 100)); - bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, type(uint256).max, 100); - assertEq(receivedItemsHash, bytes32(0xdb99f7eb854f29cd6f8faedea38d7da25073ef9876653ff45ab5c10e51f8ce4f)); - } - - /* _bytes32ArrayIncludes */ - - function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayIsSmallerThanValuesArray() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - bytes32[] memory sourceArray = new bytes32[](1); - bytes32[] memory valuesArray = new bytes32[](2); - - bool includesEmptySource = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); - assertFalse(includesEmptySource); - } - - function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayDoesNotIncludeValuesArray() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - bytes32[] memory sourceArray = new bytes32[](2); - sourceArray[0] = bytes32(uint256(1)); - sourceArray[1] = bytes32(uint256(2)); - bytes32[] memory valuesArray = new bytes32[](2); - valuesArray[0] = bytes32(uint256(3)); - valuesArray[1] = bytes32(uint256(4)); - - bool includes = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); - assertFalse(includes); - } - - function test_bytes32ArrayIncludes_returnsTrueIfSourceArrayEqualsValuesArray() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - bytes32[] memory sourceArray = new bytes32[](2); - sourceArray[0] = bytes32(uint256(1)); - sourceArray[1] = bytes32(uint256(2)); - bytes32[] memory valuesArray = new bytes32[](2); - valuesArray[0] = bytes32(uint256(1)); - valuesArray[1] = bytes32(uint256(2)); - - bool includes = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); - assertTrue(includes); - } - - function test_bytes32ArrayIncludes_returnsTrueIfValuesArrayIsASubsetOfSourceArray() public { - ImmutableSignedZoneV3Harness zone = _newZoneHarness(OWNER); - - bytes32[] memory sourceArray = new bytes32[](4); - sourceArray[0] = bytes32(uint256(1)); - sourceArray[1] = bytes32(uint256(2)); - sourceArray[2] = bytes32(uint256(3)); - sourceArray[3] = bytes32(uint256(4)); - bytes32[] memory valuesArray = new bytes32[](2); - valuesArray[0] = bytes32(uint256(1)); - valuesArray[1] = bytes32(uint256(2)); - - bool includes = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); - assertTrue(includes); - } - - /* helper functions */ - - function _newZone(address owner) private returns (ImmutableSignedZoneV3) { - return new ImmutableSignedZoneV3( - "MyZoneName", SEAPORT, "https://www.immutable.com", "https://www.immutable.com/docs", owner - ); - } - - function _newZoneHarness(address owner) private returns (ImmutableSignedZoneV3Harness) { - return new ImmutableSignedZoneV3Harness( - "MyZoneName", SEAPORT, "https://www.immutable.com", "https://www.immutable.com/docs", owner - ); - } - - function _defaultBaseZoneParameters() private view returns (ZoneParameters memory) { - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x66), identifier: 111, amount: 1}); - - ReceivedItem[] memory receivedItems = new ReceivedItem[](1); - receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC20, token: address(0x77), identifier: 0, amount: 10, recipient: payable(address(0x3)) - }); - - bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); - bytes32[] memory orderHashes = new bytes32[](1); - orderHashes[0] = orderHash; - - ZoneParameters memory zoneParameters = ZoneParameters({ - orderHash: orderHash, - fulfiller: FULFILLER, - offerer: OFFERER, - offer: spentItems, - consideration: receivedItems, - extraData: new bytes(0), - orderHashes: orderHashes, - startTime: 0, - endTime: 0, - zoneHash: bytes32(0) - }); - - return zoneParameters; - } - - function _buildExtraData( - ImmutableSignedZoneV3Harness zone, - uint256 signerPrivateKey, - address fulfiller, - uint64 expiration, - bytes32 orderHash, - bytes memory context - ) private view returns (bytes memory) { - bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(fulfiller, expiration, orderHash, context); - bytes memory extraData = abi.encodePacked( - bytes1(0), - fulfiller, - expiration, - _signCompact( - signerPrivateKey, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) - ), - context - ); - return extraData; - } -} diff --git a/test/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3Harness.t.sol b/test/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3Harness.t.sol deleted file mode 100644 index 9f4d380b..00000000 --- a/test/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3Harness.t.sol +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2026 -// SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; - -import {ReceivedItem, ZoneParameters} from "seaport-types-16/src/lib/ConsiderationStructs.sol"; -import { - ImmutableSignedZoneV3 -} from "../../../../../../contracts/trading/seaport16/zones/immutable-signed-zone/v3/ImmutableSignedZoneV3.sol"; - -contract ImmutableSignedZoneV3Harness is ImmutableSignedZoneV3 { - constructor( - string memory zoneName, - address seaport, - string memory apiEndpoint, - string memory documentationURI, - address owner - ) ImmutableSignedZoneV3(zoneName, seaport, apiEndpoint, documentationURI, owner) {} - - function exposed_domainSeparator() external view returns (bytes32) { - return _domainSeparator(); - } - - function exposed_deriveDomainSeparator() external view returns (bytes32 domainSeparator) { - return _deriveDomainSeparator(); - } - - function exposed_getSupportedSubstandards() external pure returns (uint256[] memory substandards) { - return _getSupportedSubstandards(); - } - - function exposed_deriveSignedOrderHash( - address fulfiller, - uint64 expiration, - bytes32 orderHash, - bytes calldata context - ) external pure returns (bytes32 signedOrderHash) { - return _deriveSignedOrderHash(fulfiller, expiration, orderHash, context); - } - - function exposed_validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - { - return _validateSubstandards(context, zoneParameters, before); - } - - function exposed_validateSubstandard1(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - pure - returns (uint256) - { - return _validateSubstandard1(context, zoneParameters, before); - } - - function exposed_validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - pure - returns (uint256) - { - return _validateSubstandard3(context, zoneParameters, before); - } - - function exposed_validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - pure - returns (uint256) - { - return _validateSubstandard4(context, zoneParameters, before); - } - - function exposed_validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - pure - returns (uint256) - { - return _validateSubstandard6(context, zoneParameters, before); - } - - function exposed_validateSubstandard7(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - returns (uint256) - { - return _validateSubstandard7(context, zoneParameters, before); - } - - function exposed_validateSubstandard8(bytes calldata context, ZoneParameters calldata zoneParameters, bool before) - external - returns (uint256) - { - return _validateSubstandard8(context, zoneParameters, before); - } - - function exposed_deriveReceivedItemsHash( - ReceivedItem[] calldata receivedItems, - uint256 scalingFactorNumerator, - uint256 scalingFactorDenominator - ) external pure returns (bytes32) { - return _deriveReceivedItemsHash(receivedItems, scalingFactorNumerator, scalingFactorDenominator); - } - - function exposed_bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) - external - pure - returns (bool) - { - return _bytes32ArrayIncludes(sourceArray, values); - } -} diff --git a/test/trading/seaport16/zones/immutable-signed-zone/v3/README.md b/test/trading/seaport16/zones/immutable-signed-zone/v3/README.md deleted file mode 100644 index 4a005428..00000000 --- a/test/trading/seaport16/zones/immutable-signed-zone/v3/README.md +++ /dev/null @@ -1,147 +0,0 @@ -# Test Plan for Immutable Signed Zone (v3) - -## ImmutableSignedZoneV3.sol - -Constructor tests: - -| Test name | Description | Happy Case | Implemented | -| ------------------------------------------------------------- | ------------------------------------------------------------- | ---------- | ----------- | -| `test_contructor_grantsAdminRoleToOwner` | Check `DEFAULT_ADMIN_ROLE` is granted to the specified owner. | Yes | Yes | -| `test_contructor_revertsIfOwnerIsTheZeroAddress` | Check specified owner is not the zero address. | No | Yes | -| `test_contructor_emitsSeaportCompatibleContractDeployedEvent` | Emits `SeaportCompatibleContractDeployed` event. | Yes | Yes | - -Control function tests: - -| Test name | Description | Happy Case | Implemented | -| ------------------------------------------------------------------------------ | ----------------------------------------------- | ---------- | ----------- | -| `test_grantRole_revertsIfCalledByNonAdminRole` | Grant role without authorization | No | Yes | -| `test_grantRole_grantsIfCalledByAdminRole` | Grant role with authorization | Yes | Yes | -| `test_revokeRole_revertsIfCalledByNonAdminRole` | Revoke role without authorization | No | Yes | -| `test_revokeRole_revertsIfRevokingLastDefaultAdminRole` | Revoke last `DEFAULT_ADMIN_ROLE`. | No | Yes | -| `test_revokeRole_revokesIfRevokingNonLastDefaultAdminRole` | Revoke non-last `DEFAULT_ADMIN_ROLE`. | Yes | Yes | -| `test_revokeRole_revokesIfRevokingLastNonDefaultAdminRole` | Revoke last non-`DEFAULT_ADMIN_ROLE`. | Yes | Yes | -| `test_renounceRole_revertsIfCallerDoesNotMatchCallerConfirmationAddress` | Renounce role without authorization | No | Yes | -| `test_renounceRole_revertsIfRenouncingLastDefaultAdminRole` | Renounce last `DEFAULT_ADMIN_ROLE`. | No | Yes | -| `test_renounceRole_revokesIfRenouncingNonLastDefaultAdminRole` | Renounce non-last `DEFAULT_ADMIN_ROLE`. | Yes | Yes | -| `test_renounceRole_revokesIfRenouncingLastNonDefaultAdminRole` | Renounce last non-`DEFAULT_ADMIN_ROLE`. | Yes | Yes | -| `test_isActiveSigner_returnsTrueIfSignerIsActive` | Check active signer. | Yes | Yes | -| `test_isActiveSigner_returnsFalseIfSignerIsNotActive` | Check inactive signer. | Yes | Yes | -| `test_addSigner_revertsIfCalledByNonZoneManagerRole` | Add signer without authorization. | No | Yes | -| `test_addSigner_revertsIfSignerIsTheZeroAddress` | Add zero address as signer. | No | Yes | -| `test_addSigner_emitsSignerAddedEvent` | Emits `SignerAdded` event. | Yes | Yes | -| `test_addSigner_revertsIfSignerAlreadyActive` | Add an already active signer. | No | Yes | -| `test_addSigner_revertsIfSignerWasPreviouslyActive` | Add a previously active signer. | No | Yes | -| `test_removeSigner_revertsIfCalledByNonZoneManagerRole` | Remove signer without authorization. | Yes | Yes | -| `test_removeSigner_revertsIfSignerNotActive` | Remove a signer that is not active. | No | Yes | -| `test_removeSigner_emitsSignerRemovedEvent` | Emits `SignerRemoved` event. | Yes | Yes | -| `test_updateAPIEndpoint_revertsIfCalledByNonZoneManagerRole` | Update API endpoint without authorization. | No | Yes | -| `test_updateAPIEndpoint_updatesAPIEndpointIfCalledByZoneManagerRole` | Update API endpoint with authorization. | Yes | Yes | -| `test_updateDocumentationURI_revertsIfCalledByNonZoneManagerRole` | Update documentation URI without authorization. | No | Yes | -| `test_updateDocumentationURI_updatesDocumentationURIIfCalledByZoneManagerRole` | Update documentation URI with authorization. | Yes | Yes | - -Operational function tests: - -| Test name | Description | Happy Case | Implemented | -| --------------------------------------------------------------------------- | --------------------------------------------------- | ---------- | ----------- | -| `test_getSeaportMetadata` | Retrieve metadata describing the Zone. | Yes | Yes | -| `test_sip7Information` | Retrieve SIP-7 specific information. | Yes | Yes | -| `test_supportsInterface` | ERC165 support. | Yes | Yes | -| `test_authorizeOrder_revertsIfSeaportNotCaller` | `authorizeOrder` caller not Seaport. | No | Yes | -| `test_authorizeOrder_allowedIfSeaportIsCaller` | `authorizeOrder` caller is Seaport. | Yes | Yes | -| `test_authorizeOrder_revertsIfEmptyExtraData` | Authorize order with empty `extraData`. | No | Yes | -| `test_authorizeOrder_revertsIfExtraDataLengthIsLessThan93` | Authorize order with unexpected `extraData` length. | No | Yes | -| `test_authorizeOrder_revertsIfExtraDataVersionIsNotSupported` | Authorize order with unexpected SIP-6 version byte. | No | Yes | -| `test_authorizeOrder_revertsIfSignatureHasExpired` | Authorize order with an expired signature. | No | Yes | -| `test_authorizeOrder_revertsIfActualFulfillerDoesNotMatchExpectedFulfiller` | Authorize order with unexpected fufiller. | No | Yes | -| `test_authorizeOrder_revertsIfNoSpentItems` | Authorize order with no spent items. | No | Yes | -| `test_authorizeOrder_revertsIfNoReceivedItems` | Authorize order with no received items. | No | Yes | -| `test_authorizeOrder_revertsIfSignerIsNotActive` | Authorize order with inactive signer. | No | Yes | -| `test_authorizeOrder_returnsMagicValueOnSuccessfulValidation` | Authorize order successfully. | Yes | Yes | -| `test_validateOrder_revertsIfSeaportNotCaller` | `validateOrder` caller not Seaport. | No | Yes | -| `test_validateOrder_allowedIfSeaportIsCaller` | `validateOrder` caller is Seaport. | Yes | Yes | -| `test_validateOrder_revertsIfContextIsEmpty` | Validate order with an empty context. | No | Yes | -| `test_validateOrder_returnsMagicValueOnSuccessfulValidation` | Validate order successfully. | Yes | Yes | - -Internal operational function tests: - -| Test name | Description | Happy Case | Implemented | -| ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | ---------- | ----------- | -| `test_domainSeparator_returnsCachedDomainSeparatorWhenChainIDMatchesValueSetOnDeployment` | Domain separator basic test. | Yes | Yes | -| `test_domainSeparator_returnsUpdatedDomainSeparatorIfChainIDIsDifferentFromValueSetOnDeployment` | Domain separator changes when chain ID changes. | Yes | Yes | -| `test_deriveDomainSeparator_returnsDomainSeparatorForChainID` | Domain separator derivation. | Yes | Yes | -| `test_getSupportedSubstandards` | Retrieve Zone's supported substandards. | Yes | Yes | -| `test_deriveSignedOrderHash_returnsHashOfSignedOrder` | Signed order hash derivation. | Yes | Yes | -| `test_validateSubstandards_revertsIfEmptyContext` | Empty context without substandards. | No | Yes | -| `test_validateSubstandards_beforeHookSubstandard1` | Context with substandard 1 in before hook. | Yes | Yes | -| `test_validateSubstandards_afterHookSubstandard1` | Context with substandard 1 in after hook. | Yes | Yes | -| `test_validateSubstandards_beforeHookSubstandard3` | Context with substandard 3 in before hook. | Yes | Yes | -| `test_validateSubstandards_afterHookSubstandard3` | Context with substandard 3 in after hook. | Yes | Yes | -| `test_validateSubstandards_beforeHookSubstandard4` | Context with substandard 4 in before hook. | Yes | Yes | -| `test_validateSubstandards_afterHookSubstandard4` | Context with substandard 4 in after hook. | Yes | Yes | -| `test_validateSubstandards_beforeHookSubstandard6` | Context with substandard 6 in before hook. | Yes | Yes | -| `test_validateSubstandards_afterHookSubstandard6` | Context with substandard 6 in after hook. | Yes | Yes | -| `test_validateSubstandards_beforeHookSubstandard7` | Context with substandard 7 in before hook. | Yes | Yes | -| `test_validateSubstandards_afterHookSubstandard7` | Context with substandard 7 in after hook. | Yes | Yes | -| `test_validateSubstandards_beforeHookSubstandard8` | Context with substandard 8 in before hook. | Yes | Yes | -| `test_validateSubstandards_afterHookSubstandard8` | Context with substandard 8 in after hook. | Yes | Yes | -| `test_validateSubstandards_beforeHookMultipleSubstandardsInCorrectOrder` | Context with multiple substandards in before hook. | Yes | Yes | -| `test_validateSubstandards_afterHookMultipleSubstandardsInCorrectOrder` | Context with multiple substandards in after hook. | Yes | Yes | -| `test_validateSubstandards_beforeHookSubstandards3Then6` | Context with substandards 3 and 6, but not 4 in before hook. | Yes | Yes | -| `test_validateSubstandards_beforeHookSubstandards3Then6` | Context with substandards 3 and 6, but not 4 in after hook. | Yes | Yes | -| `test_validateSubstandards_beforeHookManySubstandards` | Context with many substandards in before hook. | Yes | Yes | -| `test_validateSubstandards_afterHookManySubstandards` | Context with many substandards in after hook. | Yes | Yes | -| `test_validateSubstandards_revertsOnMultipleSubstandardsInIncorrectOrder` | Context with multiple substandards out of order. | No | Yes | -| `test_validateSubstandard1_returnsZeroLengthIfNotSubstandard1` | Substandard 1 validation skips when version byte is not 1. | Yes | Yes | -| `test_validateSubstandard1_revertsIfContextLengthIsInvalid` | Substandard 1 validation with invalid data. | No | Yes | -| `test_validateSubstandard1_revertsIfFirstReceivedItemIdentifierNotEqualToIdentifierInContext` | Substandard 1 validation when first received item identifier doesn't match expected identifier. | No | Yes | -| `test_validateSubstandard1_beforeHookReturns33OnSuccess` | Substandard 1 validation when first received item identifier matches expected identifier. | Yes | Yes | -| `test_validateSubstandard1_afterHookReturns33OnSuccess` | Substandard 1 success in after hook. | Yes | Yes | -| `test_validateSubstandard3_returnsZeroLengthIfNotSubstandard3` | Substandard 3 validation skips when version byte is not 3. | Yes | Yes | -| `test_validateSubstandard3_revertsIfContextLengthIsInvalid` | Substandard 3 validation with invalid data. | No | Yes | -| `test_validateSubstandard3_revertsIfDerivedReceivedItemsHashNotEqualToHashInContext` | Substandard 3 validation when derived hash doesn't match expected hash. | No | Yes | -| `test_validateSubstandard3_beforeHookReturns33OnSuccess` | Substandard 3 validation when derived hash matches expected hash. | Yes | Yes | -| `test_validateSubstandard3_afterHookReturns33OnSuccess` | Substandard 3 success in after hook. | Yes | Yes | -| `test_validateSubstandard4_returnsZeroLengthIfNotSubstandard4` | Substandard 4 validation skips when version byte is not 4. | Yes | Yes | -| `test_validateSubstandard4_revertsIfContextLengthIsInvalid` | Substandard 4 validation with invalid data. | No | Yes | -| `test_validateSubstandard4_revertsIfExpectedOrderHashesAreNotPresent` | Substandard 4 validation when required order hashes are not present. | No | Yes | -| `test_validateSubstandard4_beforeHookReturnsLengthOfSubstandardSegmentOnSuccess` | Substandard 4 success in before hook. | Yes | Yes | -| `test_validateSubstandard4_afterHookReturnsLengthOfSubstandardSegmentOnSuccess` | Substandard 4 validation when required order hashes are present. | Yes | Yes | -| `test_validateSubstandard6_returnsZeroLengthIfNotSubstandard6` | Substandard 6 validation skips when version byte is not 6. | Yes | Yes | -| `test_validateSubstandard6_revertsIfContextLengthIsInvalid` | Substandard 6 validation with invalid data. | No | Yes | -| `test_validateSubstandard6_revertsIfDerivedReceivedItemsHashesIsNotEqualToHashesInContext` | Substandard 6 validation when derived hash doesn't match expected hash. | No | Yes | -| `test_validateSubstandard6_beforeHookReturnsLengthOfSubstandardSegmentOnSuccess` | Substandard 6 validation when derived hash matches expected hash. | Yes | Yes | -| `test_validateSubstandard6_afterHookReturnsLengthOfSubstandardSegmentOnSuccess` | Substandard 6 success in after hook. | Yes | Yes | -| `test_validateSubstandard7_returnsZeroLengthIfNotSubstandard7` | Substandard 7 validation skips when version byte is not 7. | Yes | Yes | -| `test_validateSubstandard7_revertsIfContextLengthIsInvalid` | Substandard 7 validation with invalid data. | No | Yes | -| `test_validateSubstandard7_revertsIfFirstReceivedItemIdentifierNotEqualToIdentifierInContext` | Substandard 7 validation when first received item identifier doesn't match expected identifier. | No | Yes | -| `test_validateSubstandard7_revertsIfItemTypeRequirementsAreNotMet` | Substandard 7 validation when item type requirements are not met. | No | Yes | -| `test_validateSubstandard7_revertsIfTransferValidatorBeforeAuthorizedTransferReverts` | Substandard 7 validation when `ITransferValidator.beforeAuthorizedTransfer` reverts | No | Yes | -| `test_validateSubstandard7_revertsIfTransferValidatorAfterAuthorizedTransferReverts` | Substandard 7 validation when `ITransferValidator.afterAuthorizedTransfer` reverts | No | Yes | -| `test_validateSubstandard7_beforeHookReturns73OnSuccess` | Substandard 7 validations are successful in before hook. | Yes | Yes | -| `test_validateSubstandard7_afterHookReturns73OnSuccess` | Substandard 7 validations are successful in after hook. | Yes | Yes | -| `test_validateSubstandard8_returnsZeroLengthIfNotSubstandard8` | Substandard 8 validation skips when version byte is not 8. | Yes | Yes | -| `test_validateSubstandard8_revertsIfContextLengthIsInvalid` | Substandard 8 validation with invalid data. | No | Yes | -| `test_validateSubstandard8_revertsIfFirstReceivedItemIdentifierNotEqualToIdentifierInContext` | Substandard 8 validation when first received item identifier doesn't match expected identifier. | No | Yes | -| `test_validateSubstandard8_revertsIfItemTypeRequirementsAreNotMet` | Substandard 8 validation when item type requirements are not met. | No | Yes | -| `test_validateSubstandard8_revertsIfTransferValidatorBeforeAuthorizedTransferReverts` | Substandard 8 validation when `ITransferValidator.beforeAuthorizedTransfer` reverts | No | Yes | -| `test_validateSubstandard8_revertsIfTransferValidatorAfterAuthorizedTransferReverts` | Substandard 8 validation when `ITransferValidator.afterAuthorizedTransfer` reverts | No | Yes | -| `test_validateSubstandard8_beforeHookReturns53OnSuccess` | Substandard 8 validations are successful in before hook. | Yes | Yes | -| `test_validateSubstandard8_afterHookReturns53OnSuccess` | Substandard 8 validations are successful in after hook. | Yes | Yes | -| `test_deriveReceivedItemsHash_returnsHashIfNoReceivedItems` | Received items derivation with not items. | Yes | Yes | -| `test_deriveReceivedItemsHash_returnsHashForValidReceivedItems` | Received items derivation with some items. | Yes | Yes | -| `test_deriveReceivedItemsHash_returnsHashForReceivedItemWithAVeryLargeAmount` | Received items derivation with scaling factor forcing `> uint256` intermediate calcualtions. | Yes | Yes | -| `test_bytes32ArrayIncludes_returnsFalseIfSourceArrayIsSmallerThanValuesArray` | `byte32` array inclusion check when more values than in source. | Yes | Yes | -| `test_bytes32ArrayIncludes_returnsFalseIfSourceArrayDoesNotIncludeValuesArray` | `byte32` array inclusion check when values are not present in source. | Yes | Yes | -| `test_bytes32ArrayIncludes_returnsTrueIfSourceArrayEqualsValuesArray` | `byte32` array inclusion check when source and values are identical. | Yes | Yes | -| `test_bytes32ArrayIncludes_returnsTrueIfValuesArrayIsASubsetOfSourceArray` | `byte32` array inclusion check when values are present in source. | Yes | Yes | - -Integration tests: - -All of these tests are in [test/trading/seaport16/ImmutableSeaportSignedZoneV3Integration.t.sol](../../../ImmutableSeaportSignedZoneV3Integration.t.sol). - -| Test name | Description | Happy Case | Implemented | -| ---------------------------------------------------- | ------------------------------- | ---------- | ----------- | -| `test_fulfillAdvancedOrder_withCompleteFulfilment` | Full fulfilment. | Yes | Yes | -| `test_fulfillAdvancedOrder_withPartialFill` | Partial fulfilment. | Yes | Yes | -| `test_fulfillAdvancedOrder_withMultiplePartialFills` | Sequential partial fulfilments. | Yes | Yes | -| `test_fulfillAdvancedOrder_withOverfilling` | Over fulfilment. | Yes | Yes | From 3eefcd43c4fe3b45e35974076b41585b4050afff Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 10:54:08 +1000 Subject: [PATCH 05/11] Move errors --- .gitmodules | 7 ------- contracts/payment-splitter/PaymentSplitter.sol | 2 +- .../PaymentSplitterErrors.sol | 0 foundry.lock | 12 ------------ lib/immutable-seaport-1.6.0+im4 | 1 - lib/immutable-seaport-core-1.6.0+im2 | 1 - 6 files changed, 1 insertion(+), 22 deletions(-) rename contracts/{errors => payment-splitter}/PaymentSplitterErrors.sol (100%) delete mode 160000 lib/immutable-seaport-1.6.0+im4 delete mode 160000 lib/immutable-seaport-core-1.6.0+im2 diff --git a/.gitmodules b/.gitmodules index 87086272..e4dc50d1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,13 +26,6 @@ path = lib/axelar-gmp-sdk-solidity url = https://github.com/axelarnetwork/axelar-gmp-sdk-solidity -[submodule "lib/immutable-seaport-core-1.6.0+im2"] - path = lib/immutable-seaport-core-1.6.0+im2 - url = https://github.com/immutable/seaport-core - -[submodule "lib/immutable-seaport-1.6.0+im4"] - path = lib/immutable-seaport-1.6.0+im4 - url = https://github.com/immutable/seaport [submodule "lib/creator-token-standards"] path = lib/creator-token-standards url = https://github.com/limitbreakinc/creator-token-standards diff --git a/contracts/payment-splitter/PaymentSplitter.sol b/contracts/payment-splitter/PaymentSplitter.sol index 6447ca2e..508e38e9 100644 --- a/contracts/payment-splitter/PaymentSplitter.sol +++ b/contracts/payment-splitter/PaymentSplitter.sol @@ -5,7 +5,7 @@ pragma solidity >=0.8.19 <0.8.29; import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; -import {IPaymentSplitterErrors} from "../errors/PaymentSplitterErrors.sol"; +import {IPaymentSplitterErrors} from "./PaymentSplitterErrors.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; /** diff --git a/contracts/errors/PaymentSplitterErrors.sol b/contracts/payment-splitter/PaymentSplitterErrors.sol similarity index 100% rename from contracts/errors/PaymentSplitterErrors.sol rename to contracts/payment-splitter/PaymentSplitterErrors.sol diff --git a/foundry.lock b/foundry.lock index 40585dd2..d22c3861 100644 --- a/foundry.lock +++ b/foundry.lock @@ -23,24 +23,12 @@ "rev": "ae061dc008105dd8d05937df9ad9a676f878cbf9" } }, - "lib/immutable-seaport-1.6.0+im4": { - "tag": { - "name": "1.6.0+im4", - "rev": "e058101dbe69b403352598ed989c3afd845e9793" - } - }, "lib/immutable-seaport-core-1.5.0+im1": { "tag": { "name": "1.5.0+im.1", "rev": "33e9030f308500b422926a1be12d7a1e4d6adc06" } }, - "lib/immutable-seaport-core-1.6.0+im2": { - "tag": { - "name": "1.6.0+im2", - "rev": "9ad91d82609e937a9ba3ef330df396b05f384e44" - } - }, "lib/openzeppelin-contracts-4.9.3": { "tag": { "name": "v4.9.3", diff --git a/lib/immutable-seaport-1.6.0+im4 b/lib/immutable-seaport-1.6.0+im4 deleted file mode 160000 index e058101d..00000000 --- a/lib/immutable-seaport-1.6.0+im4 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e058101dbe69b403352598ed989c3afd845e9793 diff --git a/lib/immutable-seaport-core-1.6.0+im2 b/lib/immutable-seaport-core-1.6.0+im2 deleted file mode 160000 index 9ad91d82..00000000 --- a/lib/immutable-seaport-core-1.6.0+im2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9ad91d82609e937a9ba3ef330df396b05f384e44 From 489df508dc89dc2c14c8e97fdd8c4b6f5b22861a Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 11:03:39 +1000 Subject: [PATCH 06/11] Split out errors --- .../allowlist/OperatorAllowlistEnforced.sol | 2 +- .../OperatorAllowlistEnforcementErrors.sol | 23 ++++++++ contracts/errors/Errors.sol | 59 ------------------- .../token/erc1155/abstract/ERC1155Permit.sol | 2 +- .../interfaces/IImmutableERC1155Errors.sol | 10 ++++ .../token/erc721/abstract/ERC721Permit.sol | 2 +- test/token/erc1155/ImmutableERC1155.t.sol | 2 +- ...utableSeaportSignedZoneV2Integration.t.sol | 2 +- 8 files changed, 38 insertions(+), 64 deletions(-) create mode 100644 contracts/allowlist/OperatorAllowlistEnforcementErrors.sol delete mode 100644 contracts/errors/Errors.sol create mode 100644 contracts/token/erc1155/interfaces/IImmutableERC1155Errors.sol diff --git a/contracts/allowlist/OperatorAllowlistEnforced.sol b/contracts/allowlist/OperatorAllowlistEnforced.sol index 4079488b..47978906 100644 --- a/contracts/allowlist/OperatorAllowlistEnforced.sol +++ b/contracts/allowlist/OperatorAllowlistEnforced.sol @@ -5,7 +5,7 @@ pragma solidity >=0.8.19 <0.8.29; import {IOperatorAllowlist} from "./IOperatorAllowlist.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import {OperatorAllowlistEnforcementErrors} from "../errors/Errors.sol"; +import {OperatorAllowlistEnforcementErrors} from "./OperatorAllowlistEnforcementErrors.sol"; // Disable this code size warning as there is no plan to change this logic. // forge-lint: disable-start(unwrapped-modifier-logic) diff --git a/contracts/allowlist/OperatorAllowlistEnforcementErrors.sol b/contracts/allowlist/OperatorAllowlistEnforcementErrors.sol new file mode 100644 index 00000000..9e0cccf5 --- /dev/null +++ b/contracts/allowlist/OperatorAllowlistEnforcementErrors.sol @@ -0,0 +1,23 @@ +//SPDX-License-Identifier: Apache 2.0 +pragma solidity >=0.8.19 <0.8.29; + + +interface OperatorAllowlistEnforcementErrors { + /// @dev Error thrown when the operatorAllowlist address does not implement the IOperatorAllowlist interface + error AllowlistDoesNotImplementIOperatorAllowlist(); + + /// @dev Error thrown when calling address is not OperatorAllowlist + error CallerNotInAllowlist(address caller); + + /// @dev Error thrown when 'from' address is not OperatorAllowlist + error TransferFromNotInAllowlist(address from); + + /// @dev Error thrown when 'to' address is not OperatorAllowlist + error TransferToNotInAllowlist(address to); + + /// @dev Error thrown when approve target is not OperatorAllowlist + error ApproveTargetNotInAllowlist(address target); + + /// @dev Error thrown when approve target is not OperatorAllowlist + error ApproverNotInAllowlist(address approver); +} diff --git a/contracts/errors/Errors.sol b/contracts/errors/Errors.sol deleted file mode 100644 index 72f512f5..00000000 --- a/contracts/errors/Errors.sol +++ /dev/null @@ -1,59 +0,0 @@ -//SPDX-License-Identifier: Apache 2.0 -pragma solidity >=0.8.19 <0.8.29; - -interface IImmutableERC721Errors { - /// @dev Caller tried to mint an already burned token - error IImmutableERC721TokenAlreadyBurned(uint256 tokenId); - - /// @dev Caller tried to mint an already burned token - error IImmutableERC721SendingToZerothAddress(); - - /// @dev Caller tried to mint an already burned token - error IImmutableERC721MismatchedTransferLengths(); - - /// @dev Caller tried to mint a tokenid that is above the hybrid threshold - error IImmutableERC721IDAboveThreshold(uint256 tokenId); - - /// @dev Caller is not approved or owner - error IImmutableERC721NotOwnerOrOperator(uint256 tokenId); - - /// @dev Current token owner is not what was expected - error IImmutableERC721MismatchedTokenOwner(uint256 tokenId, address currentOwner); - - /// @dev Signer is zeroth address - error SignerCannotBeZerothAddress(); - - /// @dev Deadline exceeded for permit - error PermitExpired(); - - /// @dev Derived signature is invalid (EIP721 and EIP1271) - error InvalidSignature(); -} - -interface OperatorAllowlistEnforcementErrors { - /// @dev Error thrown when the operatorAllowlist address does not implement the IOperatorAllowlist interface - error AllowlistDoesNotImplementIOperatorAllowlist(); - - /// @dev Error thrown when calling address is not OperatorAllowlist - error CallerNotInAllowlist(address caller); - - /// @dev Error thrown when 'from' address is not OperatorAllowlist - error TransferFromNotInAllowlist(address from); - - /// @dev Error thrown when 'to' address is not OperatorAllowlist - error TransferToNotInAllowlist(address to); - - /// @dev Error thrown when approve target is not OperatorAllowlist - error ApproveTargetNotInAllowlist(address target); - - /// @dev Error thrown when approve target is not OperatorAllowlist - error ApproverNotInAllowlist(address approver); -} - -interface IImmutableERC1155Errors { - /// @dev Deadline exceeded for permit - error PermitExpired(); - - /// @dev Derived signature is invalid (EIP721 and EIP1271) - error InvalidSignature(); -} diff --git a/contracts/token/erc1155/abstract/ERC1155Permit.sol b/contracts/token/erc1155/abstract/ERC1155Permit.sol index 6eeee94d..aa82232a 100644 --- a/contracts/token/erc1155/abstract/ERC1155Permit.sol +++ b/contracts/token/erc1155/abstract/ERC1155Permit.sol @@ -7,7 +7,7 @@ import {EIP712, ECDSA} from "@openzeppelin/contracts/utils/cryptography/EIP712.s import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import {BytesLib} from "solidity-bytes-utils/contracts/BytesLib.sol"; import {IERC1155Permit} from "./IERC1155Permit.sol"; -import {IImmutableERC1155Errors} from "../../../errors/Errors.sol"; +import {IImmutableERC1155Errors} from "../interfaces/IImmutableERC1155Errors.sol"; abstract contract ERC1155Permit is ERC1155Burnable, EIP712, IERC1155Permit, IImmutableERC1155Errors { bytes32 private immutable _PERMIT_TYPEHASH = diff --git a/contracts/token/erc1155/interfaces/IImmutableERC1155Errors.sol b/contracts/token/erc1155/interfaces/IImmutableERC1155Errors.sol new file mode 100644 index 00000000..06f92c92 --- /dev/null +++ b/contracts/token/erc1155/interfaces/IImmutableERC1155Errors.sol @@ -0,0 +1,10 @@ +//SPDX-License-Identifier: Apache 2.0 +pragma solidity >=0.8.19 <0.8.29; + +interface IImmutableERC1155Errors { + /// @dev Deadline exceeded for permit + error PermitExpired(); + + /// @dev Derived signature is invalid (EIP721 and EIP1271) + error InvalidSignature(); +} diff --git a/contracts/token/erc721/abstract/ERC721Permit.sol b/contracts/token/erc721/abstract/ERC721Permit.sol index e2001ad5..ebb125af 100644 --- a/contracts/token/erc721/abstract/ERC721Permit.sol +++ b/contracts/token/erc721/abstract/ERC721Permit.sol @@ -9,7 +9,7 @@ import {BytesLib} from "solidity-bytes-utils/contracts/BytesLib.sol"; import {IERC4494} from "./IERC4494.sol"; import {ERC721, ERC721Burnable, IERC165} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; // Errors -import {IImmutableERC721Errors} from "../../../errors/Errors.sol"; +import {IImmutableERC721Errors} from "../interfaces/IImmutableERC721Errors.sol"; /** * @title ERC721Permit: An extension of the ERC721Burnable NFT standard that supports off-chain approval via permits. diff --git a/test/token/erc1155/ImmutableERC1155.t.sol b/test/token/erc1155/ImmutableERC1155.t.sol index 803bcbc2..dbe2714a 100644 --- a/test/token/erc1155/ImmutableERC1155.t.sol +++ b/test/token/erc1155/ImmutableERC1155.t.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.19 <0.8.29; import {Test} from "forge-std/Test.sol"; import {ImmutableERC1155} from "../../../contracts/token/erc1155/preset/ImmutableERC1155.sol"; -import {IImmutableERC1155Errors} from "../../../contracts/errors/Errors.sol"; +import {IImmutableERC1155Errors} from "../../../contracts/token/erc1155/interfaces/IImmutableERC1155Errors.sol"; import {OperatorAllowlistUpgradeable} from "../../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; import {Sign} from "../../utils/Sign.sol"; import {DeployOperatorAllowlist} from "../../utils/DeployAllowlistProxy.sol"; diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 8676e33b..1b8c22b7 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -34,7 +34,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test, SigningTestHelper string private constant ERC1155_ARTIFACT = "./foundry-out/ImmutableERC1155.sol/ImmutableERC1155.json"; string private constant ERC20_ARTIFACT = "./foundry-out/ImmutableERC20FixedSupplyNoBurn.sol/ImmutableERC20FixedSupplyNoBurn.json"; - string private constant ERC721_ARTIFACT = "./foundry-out/ImmutableERC721.sol/ImmutableERC721.json"; + string private constant ERC721_ARTIFACT = "./foundry-out/ImmutableERC721V2.sol/ImmutableERC721V2.json"; string private constant ZONE_ARTIFACT = "./foundry-out/ImmutableSignedZoneV2Harness.t.sol/ImmutableSignedZoneV2Harness.json"; From a658e140271298dc0a8c4fa94cfc4217d82ad2d8 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 11:11:45 +1000 Subject: [PATCH 07/11] Move mocks to test/utils --- ...wlistImmutableERC721MintByIDTransferApprovals.t.sol | 10 +++++----- .../AllowlistImmutableERC721TransferApprovals.t.sol | 10 +++++----- test/token/erc1155/ImmutableERC1155.t.sol | 6 +++--- test/token/erc1155/ImmutableERC1155Costs.t.sol | 4 ++-- test/token/erc721/ERC721OperationalBase.t.sol | 2 +- .../token/erc721/ERC721OperationalByQuantityBase.t.sol | 2 +- test/utils/DeployMockMarketPlace.sol | 2 +- test/utils/DeploySCW.sol | 4 ++-- {contracts => test/utils}/mocks/MockDisguisedEOA.sol | 0 {contracts => test/utils}/mocks/MockEIP1271Wallet.sol | 0 {contracts => test/utils}/mocks/MockFactory.sol | 0 {contracts => test/utils}/mocks/MockMarketplace.sol | 0 {contracts => test/utils}/mocks/MockOnReceive.sol | 0 {contracts => test/utils}/mocks/MockWallet.sol | 0 {contracts => test/utils}/mocks/MockWalletFactory.sol | 0 15 files changed, 20 insertions(+), 20 deletions(-) rename {contracts => test/utils}/mocks/MockDisguisedEOA.sol (100%) rename {contracts => test/utils}/mocks/MockEIP1271Wallet.sol (100%) rename {contracts => test/utils}/mocks/MockFactory.sol (100%) rename {contracts => test/utils}/mocks/MockMarketplace.sol (100%) rename {contracts => test/utils}/mocks/MockOnReceive.sol (100%) rename {contracts => test/utils}/mocks/MockWallet.sol (100%) rename {contracts => test/utils}/mocks/MockWalletFactory.sol (100%) diff --git a/test/allowlist/AllowlistImmutableERC721MintByIDTransferApprovals.t.sol b/test/allowlist/AllowlistImmutableERC721MintByIDTransferApprovals.t.sol index 436ca3e7..7c2a6b1a 100644 --- a/test/allowlist/AllowlistImmutableERC721MintByIDTransferApprovals.t.sol +++ b/test/allowlist/AllowlistImmutableERC721MintByIDTransferApprovals.t.sol @@ -2,16 +2,16 @@ pragma solidity >=0.8.19 <0.8.29; import {Test} from "forge-std/Test.sol"; -import {MockWallet} from "../../contracts/mocks/MockWallet.sol"; -import {MockFactory} from "../../contracts/mocks/MockFactory.sol"; +import {MockWallet} from "../utils/mocks/MockWallet.sol"; +import {MockFactory} from "../utils/mocks/MockFactory.sol"; import {ImmutableERC721MintByID} from "../../contracts/token/erc721/preset/ImmutableERC721MintByID.sol"; import {OperatorAllowlistUpgradeable} from "../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; import {DeployOperatorAllowlist} from "../utils/DeployAllowlistProxy.sol"; import {DeploySCWallet} from "../utils/DeploySCW.sol"; import {DeployMockMarketPlace} from "../utils/DeployMockMarketPlace.sol"; -import {MockMarketplace} from "../../contracts/mocks/MockMarketplace.sol"; -import {MockDisguisedEOA} from "../../contracts/mocks/MockDisguisedEOA.sol"; -import {MockOnReceive} from "../../contracts/mocks/MockOnReceive.sol"; +import {MockMarketplace} from "../utils/mocks/MockMarketplace.sol"; +import {MockDisguisedEOA} from "../utils/mocks/MockDisguisedEOA.sol"; +import {MockOnReceive} from "../utils/mocks/MockOnReceive.sol"; contract AllowlistERC721TransferApprovals is Test { OperatorAllowlistUpgradeable public allowlist; diff --git a/test/allowlist/AllowlistImmutableERC721TransferApprovals.t.sol b/test/allowlist/AllowlistImmutableERC721TransferApprovals.t.sol index e9c7863a..9dddc0ad 100644 --- a/test/allowlist/AllowlistImmutableERC721TransferApprovals.t.sol +++ b/test/allowlist/AllowlistImmutableERC721TransferApprovals.t.sol @@ -2,16 +2,16 @@ pragma solidity >=0.8.19 <0.8.29; import {Test} from "forge-std/Test.sol"; -import {MockWallet} from "../../contracts/mocks/MockWallet.sol"; -import {MockFactory} from "../../contracts/mocks/MockFactory.sol"; +import {MockWallet} from "../utils/mocks/MockWallet.sol"; +import {MockFactory} from "../utils/mocks/MockFactory.sol"; import {ImmutableERC721V2} from "../../contracts/token/erc721/preset/ImmutableERC721V2.sol"; import {OperatorAllowlistUpgradeable} from "../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; import {DeployOperatorAllowlist} from "../utils/DeployAllowlistProxy.sol"; import {DeploySCWallet} from "../utils/DeploySCW.sol"; import {DeployMockMarketPlace} from "../utils/DeployMockMarketPlace.sol"; -import {MockMarketplace} from "../../contracts/mocks/MockMarketplace.sol"; -import {MockDisguisedEOA} from "../../contracts/mocks/MockDisguisedEOA.sol"; -import {MockOnReceive} from "../../contracts/mocks/MockOnReceive.sol"; +import {MockMarketplace} from "../utils/mocks/MockMarketplace.sol"; +import {MockDisguisedEOA} from "../utils/mocks/MockDisguisedEOA.sol"; +import {MockOnReceive} from "../utils/mocks/MockOnReceive.sol"; contract AllowlistERC721TransferApprovals is Test { OperatorAllowlistUpgradeable public allowlist; diff --git a/test/token/erc1155/ImmutableERC1155.t.sol b/test/token/erc1155/ImmutableERC1155.t.sol index dbe2714a..4755ec02 100644 --- a/test/token/erc1155/ImmutableERC1155.t.sol +++ b/test/token/erc1155/ImmutableERC1155.t.sol @@ -8,9 +8,9 @@ import {IImmutableERC1155Errors} from "../../../contracts/token/erc1155/interfac import {OperatorAllowlistUpgradeable} from "../../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; import {Sign} from "../../utils/Sign.sol"; import {DeployOperatorAllowlist} from "../../utils/DeployAllowlistProxy.sol"; -import {MockWallet} from "../../../contracts/mocks/MockWallet.sol"; -import {MockWalletFactory} from "../../../contracts/mocks/MockWalletFactory.sol"; -import {MockEIP1271Wallet} from "../../../contracts/mocks/MockEIP1271Wallet.sol"; +import {MockWallet} from "../../utils/mocks/MockWallet.sol"; +import {MockWalletFactory} from "../../utils/mocks/MockWalletFactory.sol"; +import {MockEIP1271Wallet} from "../../utils/mocks/MockEIP1271Wallet.sol"; contract ImmutableERC1155Test is Test { ImmutableERC1155 public immutableERC1155; diff --git a/test/token/erc1155/ImmutableERC1155Costs.t.sol b/test/token/erc1155/ImmutableERC1155Costs.t.sol index d56db1ab..28892acf 100644 --- a/test/token/erc1155/ImmutableERC1155Costs.t.sol +++ b/test/token/erc1155/ImmutableERC1155Costs.t.sol @@ -7,8 +7,8 @@ import {ImmutableERC1155} from "../../../contracts/token/erc1155/preset/Immutabl import {OperatorAllowlistUpgradeable} from "../../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; import {Sign} from "../../utils/Sign.sol"; import {DeployOperatorAllowlist} from "../../utils/DeployAllowlistProxy.sol"; -import {MockWallet} from "../../../contracts/mocks/MockWallet.sol"; -import {MockWalletFactory} from "../../../contracts/mocks/MockWalletFactory.sol"; +import {MockWallet} from "../../utils/mocks/MockWallet.sol"; +import {MockWalletFactory} from "../../utils/mocks/MockWalletFactory.sol"; contract ImmutableERC1155Costs is Test { ImmutableERC1155 public immutableERC1155; diff --git a/test/token/erc721/ERC721OperationalBase.t.sol b/test/token/erc721/ERC721OperationalBase.t.sol index 2be1661c..09a1463d 100644 --- a/test/token/erc721/ERC721OperationalBase.t.sol +++ b/test/token/erc721/ERC721OperationalBase.t.sol @@ -8,7 +8,7 @@ import { IImmutableERC721Structs, IImmutableERC721Errors } from "../../../contracts/token/erc721/interfaces/IImmutableERC721.sol"; -import {MockEIP1271Wallet} from "../../../contracts/mocks/MockEIP1271Wallet.sol"; +import {MockEIP1271Wallet} from "../../utils/mocks/MockEIP1271Wallet.sol"; abstract contract ERC721OperationalBaseTest is ERC721BaseTest { function testMint() public { diff --git a/test/token/erc721/ERC721OperationalByQuantityBase.t.sol b/test/token/erc721/ERC721OperationalByQuantityBase.t.sol index 619f5644..d39c0924 100644 --- a/test/token/erc721/ERC721OperationalByQuantityBase.t.sol +++ b/test/token/erc721/ERC721OperationalByQuantityBase.t.sol @@ -8,7 +8,7 @@ import { IImmutableERC721, IImmutableERC721Errors } from "../../../contracts/token/erc721/interfaces/IImmutableERC721.sol"; -import {MockEIP1271Wallet} from "../../../contracts/mocks/MockEIP1271Wallet.sol"; +import {MockEIP1271Wallet} from "../../utils/mocks/MockEIP1271Wallet.sol"; // Test the original ImmutableERC721 contract: Operational tests abstract contract ERC721OperationalByQuantityBaseTest is ERC721OperationalBaseTest { diff --git a/test/utils/DeployMockMarketPlace.sol b/test/utils/DeployMockMarketPlace.sol index 1c7895d4..91678136 100644 --- a/test/utils/DeployMockMarketPlace.sol +++ b/test/utils/DeployMockMarketPlace.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache 2.0 pragma solidity >=0.8.19 <0.8.29; -import {MockMarketplace} from "../../contracts/mocks/MockMarketplace.sol"; +import {MockMarketplace} from "./mocks/MockMarketplace.sol"; /// Deploys the OperatorAllowlistUpgradeable contract behind an ERC1967 Proxy and returns the address of the proxy contract DeployMockMarketPlace { diff --git a/test/utils/DeploySCW.sol b/test/utils/DeploySCW.sol index f974d106..58559706 100644 --- a/test/utils/DeploySCW.sol +++ b/test/utils/DeploySCW.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Apache 2.0 pragma solidity >=0.8.19 <0.8.29; -import {MockWallet} from "../../contracts/mocks/MockWallet.sol"; -import {MockWalletFactory} from "../../contracts/mocks/MockWalletFactory.sol"; +import {MockWallet} from "./mocks/MockWallet.sol"; +import {MockWalletFactory} from "./mocks/MockWalletFactory.sol"; contract DeploySCWallet { MockWallet public mockWalletModule; diff --git a/contracts/mocks/MockDisguisedEOA.sol b/test/utils/mocks/MockDisguisedEOA.sol similarity index 100% rename from contracts/mocks/MockDisguisedEOA.sol rename to test/utils/mocks/MockDisguisedEOA.sol diff --git a/contracts/mocks/MockEIP1271Wallet.sol b/test/utils/mocks/MockEIP1271Wallet.sol similarity index 100% rename from contracts/mocks/MockEIP1271Wallet.sol rename to test/utils/mocks/MockEIP1271Wallet.sol diff --git a/contracts/mocks/MockFactory.sol b/test/utils/mocks/MockFactory.sol similarity index 100% rename from contracts/mocks/MockFactory.sol rename to test/utils/mocks/MockFactory.sol diff --git a/contracts/mocks/MockMarketplace.sol b/test/utils/mocks/MockMarketplace.sol similarity index 100% rename from contracts/mocks/MockMarketplace.sol rename to test/utils/mocks/MockMarketplace.sol diff --git a/contracts/mocks/MockOnReceive.sol b/test/utils/mocks/MockOnReceive.sol similarity index 100% rename from contracts/mocks/MockOnReceive.sol rename to test/utils/mocks/MockOnReceive.sol diff --git a/contracts/mocks/MockWallet.sol b/test/utils/mocks/MockWallet.sol similarity index 100% rename from contracts/mocks/MockWallet.sol rename to test/utils/mocks/MockWallet.sol diff --git a/contracts/mocks/MockWalletFactory.sol b/test/utils/mocks/MockWalletFactory.sol similarity index 100% rename from contracts/mocks/MockWalletFactory.sol rename to test/utils/mocks/MockWalletFactory.sol From 679f73e75823b5fc5830dd2a6dcccfdea9c71605 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 11:12:39 +1000 Subject: [PATCH 08/11] Fix format --- contracts/allowlist/OperatorAllowlistEnforcementErrors.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/allowlist/OperatorAllowlistEnforcementErrors.sol b/contracts/allowlist/OperatorAllowlistEnforcementErrors.sol index 9e0cccf5..ec4dead4 100644 --- a/contracts/allowlist/OperatorAllowlistEnforcementErrors.sol +++ b/contracts/allowlist/OperatorAllowlistEnforcementErrors.sol @@ -1,7 +1,6 @@ //SPDX-License-Identifier: Apache 2.0 pragma solidity >=0.8.19 <0.8.29; - interface OperatorAllowlistEnforcementErrors { /// @dev Error thrown when the operatorAllowlist address does not implement the IOperatorAllowlist interface error AllowlistDoesNotImplementIOperatorAllowlist(); From a850e38b158865b6de6323a900f853897c1166ad Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 11:15:33 +1000 Subject: [PATCH 09/11] Remove import for ERC721c --- .gitmodules | 3 --- foundry.lock | 6 ------ lib/creator-token-standards | 1 - 3 files changed, 10 deletions(-) delete mode 160000 lib/creator-token-standards diff --git a/.gitmodules b/.gitmodules index e4dc50d1..c3c030c6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,6 +26,3 @@ path = lib/axelar-gmp-sdk-solidity url = https://github.com/axelarnetwork/axelar-gmp-sdk-solidity -[submodule "lib/creator-token-standards"] - path = lib/creator-token-standards - url = https://github.com/limitbreakinc/creator-token-standards diff --git a/foundry.lock b/foundry.lock index d22c3861..42f86d02 100644 --- a/foundry.lock +++ b/foundry.lock @@ -5,12 +5,6 @@ "rev": "3f6ae1a1d22590e1c9b6af66781adc72148ee447" } }, - "lib/creator-token-standards": { - "tag": { - "name": "v5.0.0", - "rev": "980a63b33591d568b6e04b45f37deba05a55f787" - } - }, "lib/forge-std": { "tag": { "name": "v1.11.0", diff --git a/lib/creator-token-standards b/lib/creator-token-standards deleted file mode 160000 index 980a63b3..00000000 --- a/lib/creator-token-standards +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 980a63b33591d568b6e04b45f37deba05a55f787 From 0cede2ebb515cecdbbfea5f6d095ac6aeba12fd4 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 11:20:20 +1000 Subject: [PATCH 10/11] Remove unused library --- .gitmodules | 3 --- foundry.lock | 6 ------ lib/solidity-bits | 1 - 3 files changed, 10 deletions(-) delete mode 160000 lib/solidity-bits diff --git a/.gitmodules b/.gitmodules index c3c030c6..b7a56c37 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std -[submodule "lib/solidity-bits"] - path = lib/solidity-bits - url = https://github.com/estarriolvetch/solidity-bits [submodule "lib/solidity-bytes-utils"] path = lib/solidity-bytes-utils url = https://github.com/GNSPS/solidity-bytes-utils diff --git a/foundry.lock b/foundry.lock index 42f86d02..3a7137b9 100644 --- a/foundry.lock +++ b/foundry.lock @@ -41,12 +41,6 @@ "rev": "3d4c0d5741b131c231e558d7a6213392ab3672a5" } }, - "lib/solidity-bits": { - "tag": { - "name": "v0.4.0", - "rev": "c243a888782b61542da380ac92e218c676427b50" - } - }, "lib/solidity-bytes-utils": { "tag": { "name": "v0.8.0", diff --git a/lib/solidity-bits b/lib/solidity-bits deleted file mode 160000 index c243a888..00000000 --- a/lib/solidity-bits +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c243a888782b61542da380ac92e218c676427b50 From fe14749f17d618f1a3682d706ad0786181f18392 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 26 May 2026 11:27:56 +1000 Subject: [PATCH 11/11] Updated documentation to match that PSI v1 has been removed --- contracts/token/erc721/erc721psi/README.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/contracts/token/erc721/erc721psi/README.md b/contracts/token/erc721/erc721psi/README.md index c30f09e8..198f2b97 100644 --- a/contracts/token/erc721/erc721psi/README.md +++ b/contracts/token/erc721/erc721psi/README.md @@ -1,18 +1,12 @@ # Immutable ERC721 Mint by Quantity Implementation Contracts -The ERC721Psi code in this directory was forked from https://github.com/estarriolvetch/ERC721Psi, with the changes listed in the ERC721Psi Changelog section below. +The ERC721PsiV2 is based upon https://github.com/estarriolvetch/ERC721Psi. It slightly increases the minting gas usage but reduces the gas usage for most other functions. -The ERC721PsiV2 leverages the ERC721Psi code, slightly increasing the minting gas usage but reducing the gas usage for most other functions. The differences between the ERC721Psi and ERC721PsiV2 are listed in the section ERC721PsiV2 and ERC721Psi Differences section. - -## ERC721Psi Differences From Upstream +Differences from https://github.com/estarriolvetch/ERC721Psi : - ERC721Psi: changed `_safeMint(address to, uint256 quantity) internal virtual` to call `ERC721PSI._mint` explicitly to avoid calling ERC721 methods when both are imported in a child contract - ERC721Psi: changed `_safeMint(address to, uint256 quantity, bytes memory _data` to call `ERC721PSI._mint` explicitly to avoid calling ERC721 methods when both are imported in a child contract - - -## ERC721PsiV2 and ERC721Psi Differences - - Switched from `solidity-bits'` `BitMaps` implementation to using the `TokenGroup` struct. In `ERC721Psi`, `BitMaps` are used as arrays of bits that have to be traversed. One `TokenGroup` struct holds the token ids for a 256 NFTs. The first NFT in a group is at a multiple of 256. The owner of an NFT for a `TokenGroup` is the `defaultOwner` specified in the `TokenGroup` struct, unless the owner is specified in the `tokenOwners` map. The result of this change is that the owner of a token can be determined using a deterministic amount of gas for `ERC721PsiV2`. - In `ERC721Psi`, newly minted NFTs are minted to the next available token id. In `ERC721PsiV2`, newly minted NFTs are minted to the next token id that is a multiple of 256. This means that each new mint by quantity is minted to a new token group. - For `ERC721PsiV2`, when the mint by quantity request is not a multiple of 256 NFTs, there are unused token ids. These token ids are added to a `burned` map in the `TokenGroup`, thus making those token ids unavailable.