├── .prettierignore ├── .gitattributes ├── .gitignore ├── foundry.toml ├── .gitmodules ├── src ├── marketplaces │ ├── wyvern │ │ ├── interfaces │ │ │ ├── IWyvernProxyRegistry.sol │ │ │ ├── IERC1155.sol │ │ │ ├── IAtomicizer.sol │ │ │ ├── IERC721.sol │ │ │ └── WyvernInterface.sol │ │ ├── lib │ │ │ ├── WyvernEnums.sol │ │ │ ├── WyvernStructs.sol │ │ │ ├── ArrayUtils.sol │ │ │ └── WyvernTypeHashes.sol │ │ └── WyvernConfig.sol │ ├── foundation │ │ ├── interfaces │ │ │ └── IFoundation.sol │ │ └── FoundationConfig.sol │ ├── X2Y2 │ │ ├── interfaces │ │ │ ├── IX2Y2Marketplace.sol │ │ │ └── MarketConstants.sol │ │ ├── lib │ │ │ ├── X2Y2TypeHashes.sol │ │ │ ├── Strings.sol │ │ │ └── ECDSA.sol │ │ └── X2Y2Config.sol │ ├── looksRare │ │ ├── interfaces │ │ │ ├── ICurrencyManager.sol │ │ │ └── ILooksRareExchange.sol │ │ ├── lib │ │ │ ├── LooksRareTypeHashes.sol │ │ │ └── OrderTypes.sol │ │ └── LooksRareConfig.sol │ ├── sudoswap │ │ ├── interfaces │ │ │ ├── IPair.sol │ │ │ ├── IRouter.sol │ │ │ └── IPairFactory.sol │ │ └── SudoswapConfig.sol │ └── seaport │ │ ├── lib │ │ ├── ConsiderationEnums.sol │ │ ├── ConsiderationStructs.sol │ │ └── ConsiderationTypeHashes.sol │ │ └── interfaces │ │ └── ConsiderationInterface.sol ├── Types.sol └── BaseMarketConfig.sol ├── .vscode └── settings.json ├── test ├── tokens │ ├── TestERC20.sol │ ├── TestERC721.sol │ └── TestERC1155.sol └── utils │ └── BaseOrderTest.sol ├── .github └── workflows │ ├── tests.yml │ └── testAndbuildResults.yml ├── package.json ├── LICENSE ├── README.md ├── script └── runTest.js └── yarn.lock /.prettierignore: -------------------------------------------------------------------------------- 1 | lib/solmate 2 | lib/forge-std -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | .gas-snapshot linguist-language=Julia -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /cache 2 | /node_modules 3 | /out 4 | 5 | .DS_Store 6 | results/results.tex 7 | results/results-direct.tex 8 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | solc = "0.8.14" 3 | bytecode_hash = "none" 4 | optimizer_runs = 1000000 5 | ignored_error_codes = [5667, 9302] -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "lib/solmate"] 5 | path = lib/solmate 6 | url = https://github.com/rari-capital/solmate 7 | -------------------------------------------------------------------------------- /src/marketplaces/wyvern/interfaces/IWyvernProxyRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.13; 3 | 4 | interface IWyvernProxyRegistry { 5 | function registerProxy() external returns (address); 6 | 7 | function proxies(address) external returns (address); 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "solidity.packageDefaultDependenciesContractsDirectory": "src", 3 | "solidity.packageDefaultDependenciesDirectory": "lib", 4 | "solidity.compileUsingRemoteVersion": "v0.8.14", 5 | "search.exclude": { "lib": true }, 6 | "files.associations": { 7 | ".gas-snapshot": "julia" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/marketplaces/wyvern/interfaces/IERC1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.13; 3 | 4 | interface IERC1155 { 5 | function safeTransferFrom( 6 | address from, 7 | address to, 8 | uint256 id, 9 | uint256 amount, 10 | bytes memory data 11 | ) external; 12 | } 13 | -------------------------------------------------------------------------------- /test/tokens/TestERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.14; 3 | 4 | import "solmate/tokens/ERC20.sol"; 5 | 6 | contract TestERC20 is ERC20("Test20", "TST20", 18) { 7 | function mint(address to, uint256 amount) external returns (bool) { 8 | _mint(to, amount); 9 | return true; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/marketplaces/wyvern/interfaces/IAtomicizer.sol: -------------------------------------------------------------------------------- 1 | /** 2 | *Submitted for verification at Etherscan.io on 2018-03-08 3 | */ 4 | 5 | pragma solidity >=0.4.13; 6 | 7 | interface IAtomicizer { 8 | function atomicize( 9 | address[] calldata addrs, 10 | uint256[] calldata values, 11 | uint256[] calldata calldataLengths, 12 | bytes calldata calldatas 13 | ) external; 14 | } 15 | -------------------------------------------------------------------------------- /src/marketplaces/wyvern/interfaces/IERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.13; 3 | 4 | interface IERC721 { 5 | function safeTransferFrom( 6 | address from, 7 | address to, 8 | uint256 tokenId 9 | ) external; 10 | 11 | function transferFrom( 12 | address from, 13 | address to, 14 | uint256 tokenId 15 | ) external; 16 | } 17 | -------------------------------------------------------------------------------- /test/tokens/TestERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.14; 3 | 4 | import "solmate/tokens/ERC721.sol"; 5 | 6 | contract TestERC721 is ERC721("Test721", "TST721") { 7 | function mint(address to, uint256 tokenId) public returns (bool) { 8 | _mint(to, tokenId); 9 | return true; 10 | } 11 | 12 | function tokenURI(uint256) public pure override returns (string memory) { 13 | return "tokenURI"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/marketplaces/foundation/interfaces/IFoundation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | interface IFoundation { 6 | function setBuyPrice( 7 | address nftContract, 8 | uint256 tokenId, 9 | uint256 price 10 | ) external; 11 | 12 | function buyV2( 13 | address nftContract, 14 | uint256 tokenId, 15 | uint256 maxPrice, 16 | address payable buyReferrer 17 | ) external payable; 18 | } 19 | -------------------------------------------------------------------------------- /test/tokens/TestERC1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.14; 3 | 4 | import "solmate/tokens/ERC1155.sol"; 5 | 6 | contract TestERC1155 is ERC1155 { 7 | function mint( 8 | address to, 9 | uint256 tokenId, 10 | uint256 amount 11 | ) public returns (bool) { 12 | _mint(to, tokenId, amount, ""); 13 | return true; 14 | } 15 | 16 | function uri(uint256) public pure override returns (string memory) { 17 | return "uri"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/marketplaces/X2Y2/interfaces/IX2Y2Marketplace.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import { Market } from "./MarketConstants.sol"; 4 | 5 | interface IX2Y2Marketplace { 6 | function run1( 7 | Market.Order memory order, 8 | Market.SettleShared memory shared, 9 | Market.SettleDetail memory detail 10 | ) external returns (uint256); 11 | 12 | function run(Market.RunInput memory input) external payable; 13 | 14 | function updateSigners(address[] memory toAdd, address[] memory toRemove) 15 | external; 16 | } 17 | -------------------------------------------------------------------------------- /src/marketplaces/looksRare/interfaces/ICurrencyManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface ICurrencyManager { 5 | function addCurrency(address currency) external; 6 | 7 | function removeCurrency(address currency) external; 8 | 9 | function isCurrencyWhitelisted(address currency) 10 | external 11 | view 12 | returns (bool); 13 | 14 | function viewWhitelistedCurrencies(uint256 cursor, uint256 size) 15 | external 16 | view 17 | returns (address[] memory, uint256); 18 | 19 | function viewCountWhitelistedCurrencies() external view returns (uint256); 20 | } 21 | -------------------------------------------------------------------------------- /src/marketplaces/sudoswap/interfaces/IPair.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | interface IPair { 4 | 5 | function swapNFTsForToken( 6 | uint256[] calldata nftIds, 7 | uint256 minExpectedTokenOutput, 8 | address payable tokenRecipient, 9 | bool isRouter, 10 | address routerCaller 11 | ) external returns (uint256 outputAmount); 12 | 13 | function swapTokenForSpecificNFTs( 14 | uint256[] calldata nftIds, 15 | uint256 maxExpectedTokenInput, 16 | address nftRecipient, 17 | bool isRouter, 18 | address routerCaller 19 | ) external payable returns (uint256 inputAmount); 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: pull_request_target 4 | 5 | jobs: 6 | tests: 7 | name: Run Tests 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | 12 | - name: Install Foundry 13 | uses: onbjerg/foundry-toolchain@v1 14 | with: 15 | version: nightly 16 | 17 | - name: Install dependencies 18 | run: forge install 19 | 20 | - name: Run tests 21 | run: forge test --fork-url ${{ secrets.ETH_RPC_URL }} -vv 22 | lint: 23 | name: Run Lint 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v2 27 | - uses: actions/setup-node@v2 28 | with: 29 | node-version: 12.x 30 | - run: yarn install --frozen-lockfile 31 | - run: yarn lint:check -------------------------------------------------------------------------------- /src/marketplaces/wyvern/lib/WyvernEnums.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.13; 3 | 4 | /* Fee method: protocol fee or split fee. */ 5 | enum FeeMethod { 6 | ProtocolFee, 7 | SplitFee 8 | } 9 | 10 | /** 11 | * Side: buy or sell. 12 | */ 13 | enum Side { 14 | Buy, 15 | Sell 16 | } 17 | 18 | /** 19 | * Currently supported kinds of sale: fixed price, Dutch auction. 20 | * English auctions cannot be supported without stronger escrow guarantees. 21 | * Future interesting options: Vickrey auction, nonlinear Dutch auctions. 22 | */ 23 | enum SaleKind { 24 | FixedPrice, 25 | DutchAuction 26 | } 27 | 28 | /* Delegate call could be used to atomically transfer multiple assets owned by the proxy contract with one order. */ 29 | enum HowToCall { 30 | Call, 31 | DelegateCall 32 | } 33 | -------------------------------------------------------------------------------- /src/marketplaces/looksRare/interfaces/ILooksRareExchange.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import { OrderTypes } from "../lib/OrderTypes.sol"; 5 | 6 | interface ILooksRareExchange { 7 | function matchAskWithTakerBidUsingETHAndWETH( 8 | OrderTypes.TakerOrder calldata takerBid, 9 | OrderTypes.MakerOrder calldata makerAsk 10 | ) external payable; 11 | 12 | function matchAskWithTakerBid( 13 | OrderTypes.TakerOrder calldata takerBid, 14 | OrderTypes.MakerOrder calldata makerAsk 15 | ) external; 16 | 17 | function matchBidWithTakerAsk( 18 | OrderTypes.TakerOrder calldata takerAsk, 19 | OrderTypes.MakerOrder calldata makerBid 20 | ) external; 21 | 22 | function updateProtocolFeeRecipient(address _protocolFeeRecipient) external; 23 | } 24 | -------------------------------------------------------------------------------- /src/Types.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | type TestItemETH is uint256; 5 | 6 | struct TestItem721 { 7 | address token; 8 | uint256 identifier; 9 | } 10 | 11 | struct TestItem1155 { 12 | address token; 13 | uint256 identifier; 14 | uint256 amount; 15 | } 16 | 17 | struct TestItem20 { 18 | address token; 19 | uint256 amount; 20 | } 21 | 22 | struct TestCallParameters { 23 | address target; 24 | uint256 value; 25 | bytes data; 26 | } 27 | 28 | struct SetupCall { 29 | address sender; 30 | address target; 31 | bytes data; 32 | } 33 | 34 | struct TestOrderContext { 35 | bool listOnChain; 36 | address offerer; 37 | address fulfiller; 38 | } 39 | 40 | struct TestOrderPayload { 41 | // Call needed to submit order on-chain without signature 42 | TestCallParameters submitOrder; 43 | // Call needed to actually execute the order 44 | TestCallParameters executeOrder; 45 | } 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "marketplace-benchmarks", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "description": "Marketplace Benchmarking for various NFT marketplaces", 6 | "files": [ 7 | "src/**/*.sol" 8 | ], 9 | "devDependencies": { 10 | "prettier": "^2.3.1", 11 | "prettier-plugin-solidity": "^1.0.0-beta.13" 12 | }, 13 | "scripts": { 14 | "lint:check": "prettier --check **.sol && prettier --check **.js", 15 | "lint:fix": "prettier --write **.sol && prettier --write **.js", 16 | "test:generate": "node script/runTest.js" 17 | }, 18 | "prettier": { 19 | "overrides": [ 20 | { 21 | "files": [ 22 | "*.sol", 23 | "*.js" 24 | ], 25 | "options": { 26 | "tabWidth": 4, 27 | "printWidth": 80, 28 | "bracketSpacing": true 29 | } 30 | } 31 | ] 32 | }, 33 | "dependencies": { 34 | "child_process": "^1.0.2", 35 | "color-interpolate": "^1.0.5", 36 | "color-parse": "^1.4.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 t11s 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/marketplaces/wyvern/interfaces/WyvernInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.13; 3 | 4 | import { FeeMethod, Side, SaleKind, HowToCall } from "../lib/WyvernEnums.sol"; 5 | 6 | interface WyvernInterface { 7 | function atomicMatch_( 8 | address[14] calldata addrs, 9 | uint256[18] calldata uints, 10 | uint8[8] calldata feeMethodsSidesKindsHowToCalls, 11 | bytes calldata calldataBuy, 12 | bytes calldata calldataSell, 13 | bytes calldata replacementPatternBuy, 14 | bytes calldata replacementPatternSell, 15 | bytes calldata staticExtradataBuy, 16 | bytes calldata staticExtradataSell, 17 | uint8[2] calldata vs, 18 | bytes32[5] calldata rssMetadata 19 | ) external payable; 20 | 21 | function approveOrder_( 22 | address[7] calldata addrs, 23 | uint256[9] calldata uints, 24 | FeeMethod feeMethod, 25 | Side side, 26 | SaleKind saleKind, 27 | HowToCall howToCall, 28 | bytes calldata _calldata, 29 | bytes calldata replacementPattern, 30 | bytes calldata staticExtradata, 31 | bool orderbookInclusionDesired 32 | ) external; 33 | } 34 | -------------------------------------------------------------------------------- /src/marketplaces/sudoswap/interfaces/IRouter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | interface IRouter { 4 | struct PairSwapSpecific { 5 | address pair; 6 | uint256[] nftIds; 7 | } 8 | 9 | function swapETHForSpecificNFTs( 10 | PairSwapSpecific[] calldata swapList, 11 | address payable ethRecipient, 12 | address nftRecipient, 13 | uint256 deadline 14 | ) external payable returns (uint256 remainingValue); 15 | 16 | function swapERC20ForSpecificNFTs( 17 | PairSwapSpecific[] calldata swapList, 18 | uint256 inputAmount, 19 | address nftRecipient, 20 | uint256 deadline 21 | ) external returns (uint256 remainingValue); 22 | 23 | function swapNFTsForToken( 24 | PairSwapSpecific[] calldata swapList, 25 | uint256 minOutput, 26 | address tokenRecipient, 27 | uint256 deadline 28 | ) external returns (uint256 outputAmount); 29 | 30 | function depositNFTs( 31 | address _nft, 32 | uint256[] calldata ids, 33 | address recipient 34 | ) external; 35 | 36 | function depositNFTs( 37 | address _nft, 38 | uint256[] calldata ids, 39 | address[] calldata recipients 40 | ) external; 41 | } 42 | -------------------------------------------------------------------------------- /src/marketplaces/looksRare/lib/LooksRareTypeHashes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import { OrderTypes } from "./OrderTypes.sol"; 5 | 6 | contract LooksRareTypeHashes { 7 | using OrderTypes for OrderTypes.MakerOrder; 8 | using OrderTypes for OrderTypes.TakerOrder; 9 | 10 | bytes32 public constant DOMAIN_SEPARATOR = 11 | keccak256( 12 | abi.encode( 13 | 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") 14 | 0xda9101ba92939daf4bb2e18cd5f942363b9297fbc3232c9dd964abb1fb70ed71, // keccak256("LooksRareExchange") 15 | 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6, // keccak256(bytes("1")) for versionId = 1 16 | 1, 17 | 0x59728544B08AB483533076417FbBB2fD0B17CE3a // mainnet LooksRare exchange address 18 | ) 19 | ); 20 | 21 | function _deriveOrderDigest(OrderTypes.MakerOrder memory makerOrder) 22 | internal 23 | pure 24 | returns (bytes32) 25 | { 26 | bytes32 orderHash = makerOrder.hash(); 27 | return 28 | keccak256( 29 | abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, orderHash) 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/marketplaces/sudoswap/interfaces/IPairFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import { IPair } from "./IPair.sol"; 4 | 5 | interface IPairFactory { 6 | enum PoolType { 7 | TOKEN, 8 | NFT, 9 | TRADE 10 | } 11 | 12 | function createPairETH( 13 | address _nft, 14 | address _bondingCurve, 15 | address payable _assetRecipient, 16 | PoolType _poolType, 17 | uint128 _delta, 18 | uint96 _fee, 19 | uint128 _spotPrice, 20 | uint256[] calldata _initialNFTIDs 21 | ) external payable returns (IPair pair); 22 | 23 | struct CreateERC20PairParams { 24 | address token; 25 | address nft; 26 | address bondingCurve; 27 | address payable assetRecipient; 28 | PoolType poolType; 29 | uint128 delta; 30 | uint96 fee; 31 | uint128 spotPrice; 32 | uint256[] initialNFTIDs; 33 | uint256 initialTokenBalance; 34 | } 35 | 36 | function createPairERC20(CreateERC20PairParams calldata params) 37 | external 38 | returns (IPair pair); 39 | 40 | function owner() external view returns (address); 41 | 42 | function changeProtocolFeeMultiplier(uint256 _protocolFeeMultiplier) 43 | external; 44 | 45 | function setRouterAllowed(address _router, bool isAllowed) 46 | external; 47 | } 48 | -------------------------------------------------------------------------------- /src/marketplaces/X2Y2/lib/X2Y2TypeHashes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import { Market } from "../interfaces/MarketConstants.sol"; 5 | import { ECDSA } from "./ECDSA.sol"; 6 | 7 | contract X2Y2TypeHashes { 8 | function _deriveOrderDigest(Market.Order memory order) 9 | internal 10 | pure 11 | returns (bytes32) 12 | { 13 | bytes32 orderHash = keccak256( 14 | abi.encode( 15 | order.salt, 16 | order.user, 17 | order.network, 18 | order.intent, 19 | order.delegateType, 20 | order.deadline, 21 | order.currency, 22 | order.dataMask, 23 | order.items.length, 24 | order.items 25 | ) 26 | ); 27 | return ECDSA.toEthSignedMessageHash(orderHash); 28 | } 29 | 30 | function _deriveInputDigest(Market.RunInput memory input) 31 | internal 32 | pure 33 | returns (bytes32) 34 | { 35 | return 36 | keccak256( 37 | abi.encode(input.shared, input.details.length, input.details) 38 | ); 39 | } 40 | 41 | function _hashItem(Market.Order memory order, Market.OrderItem memory item) 42 | internal 43 | view 44 | virtual 45 | returns (bytes32) 46 | { 47 | return 48 | keccak256( 49 | abi.encode( 50 | order.salt, 51 | order.user, 52 | order.network, 53 | order.intent, 54 | order.delegateType, 55 | order.deadline, 56 | order.currency, 57 | order.dataMask, 58 | item 59 | ) 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/testAndbuildResults.yml: -------------------------------------------------------------------------------- 1 | name: Test and build results 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | tests: 9 | name: Run Tests 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Checkout results branch 15 | uses: actions/checkout@v3 16 | with: 17 | ref: results 18 | path: results 19 | 20 | - name: Install Foundry 21 | uses: onbjerg/foundry-toolchain@v1 22 | with: 23 | version: nightly 24 | 25 | - name: Install dependencies 26 | run: forge install 27 | - run: yarn install --frozen-lockfile 28 | 29 | - name: Run tests 30 | run: yarn test:generate ${{ secrets.ETH_RPC_URL }} 31 | 32 | - name: Process latex EOA 33 | uses: dante-ev/latex-action@latest 34 | with: 35 | root_file: results.tex 36 | compiler: lualatex 37 | working_directory: results 38 | args: -interaction=nonstopmode -shell-escape 39 | 40 | - name: Process latex direct 41 | uses: dante-ev/latex-action@latest 42 | with: 43 | root_file: results-direct.tex 44 | compiler: lualatex 45 | working_directory: results 46 | args: -interaction=nonstopmode -shell-escape 47 | 48 | - name: Save results 49 | run: | 50 | cd results 51 | if [[ `git status --porcelain` ]]; then 52 | git config --global user.name 'GitHub Actions Bot' 53 | git config --global user.email '<>' 54 | git add *.pdf 55 | git commit -m "Autogenerated pdf" 56 | git push 57 | fi 58 | 59 | lint: 60 | name: Run Lint 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v2 64 | - uses: actions/setup-node@v2 65 | with: 66 | node-version: 12.x 67 | - run: yarn install --frozen-lockfile 68 | - run: yarn lint:check 69 | 70 | -------------------------------------------------------------------------------- /src/marketplaces/X2Y2/lib/Strings.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) 3 | 4 | pragma solidity ^0.8.0; 5 | 6 | /** 7 | * @dev String operations. 8 | */ 9 | library Strings { 10 | bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; 11 | 12 | /** 13 | * @dev Converts a `uint256` to its ASCII `string` decimal representation. 14 | */ 15 | function toString(uint256 value) internal pure returns (string memory) { 16 | // Inspired by OraclizeAPI's implementation - MIT licence 17 | // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol 18 | 19 | if (value == 0) { 20 | return "0"; 21 | } 22 | uint256 temp = value; 23 | uint256 digits; 24 | while (temp != 0) { 25 | digits++; 26 | temp /= 10; 27 | } 28 | bytes memory buffer = new bytes(digits); 29 | while (value != 0) { 30 | digits -= 1; 31 | buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); 32 | value /= 10; 33 | } 34 | return string(buffer); 35 | } 36 | 37 | /** 38 | * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. 39 | */ 40 | function toHexString(uint256 value) internal pure returns (string memory) { 41 | if (value == 0) { 42 | return "0x00"; 43 | } 44 | uint256 temp = value; 45 | uint256 length = 0; 46 | while (temp != 0) { 47 | length++; 48 | temp >>= 8; 49 | } 50 | return toHexString(value, length); 51 | } 52 | 53 | /** 54 | * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. 55 | */ 56 | function toHexString(uint256 value, uint256 length) 57 | internal 58 | pure 59 | returns (string memory) 60 | { 61 | bytes memory buffer = new bytes(2 * length + 2); 62 | buffer[0] = "0"; 63 | buffer[1] = "x"; 64 | for (uint256 i = 2 * length + 1; i > 1; --i) { 65 | buffer[i] = _HEX_SYMBOLS[value & 0xf]; 66 | value >>= 4; 67 | } 68 | require(value == 0, "Strings: hex length insufficient"); 69 | return string(buffer); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/marketplaces/wyvern/lib/WyvernStructs.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.13; 3 | 4 | import "./WyvernEnums.sol"; 5 | 6 | /* An ECDSA signature. */ 7 | struct Sig { 8 | /* v parameter */ 9 | uint8 v; 10 | /* r parameter */ 11 | bytes32 r; 12 | /* s parameter */ 13 | bytes32 s; 14 | } 15 | 16 | /* An order on the exchange. */ 17 | struct Order { 18 | /* Exchange address, intended as a versioning mechanism. */ 19 | address exchange; 20 | /* Order maker address. */ 21 | address maker; 22 | /* Order taker address, if specified. */ 23 | address taker; 24 | /* Maker relayer fee of the order, unused for taker order. */ 25 | uint256 makerRelayerFee; 26 | /* Taker relayer fee of the order, or maximum taker fee for a taker order. */ 27 | uint256 takerRelayerFee; 28 | /* Maker protocol fee of the order, unused for taker order. */ 29 | uint256 makerProtocolFee; 30 | /* Taker protocol fee of the order, or maximum taker fee for a taker order. */ 31 | uint256 takerProtocolFee; 32 | /* Order fee recipient or zero address for taker order. */ 33 | address feeRecipient; 34 | /* Fee method (protocol token or split fee). */ 35 | FeeMethod feeMethod; 36 | /* Side (buy/sell). */ 37 | Side side; 38 | /* Kind of sale. */ 39 | SaleKind saleKind; 40 | /* Target. */ 41 | address target; 42 | /* HowToCall. */ 43 | HowToCall howToCall; 44 | /* Calldata. */ 45 | bytes _calldata; 46 | /* Calldata replacement pattern, or an empty byte array for no replacement. */ 47 | bytes replacementPattern; 48 | /* Static call target, zero-address for no static call. */ 49 | address staticTarget; 50 | /* Static call extra data. */ 51 | bytes staticExtradata; 52 | /* Token used to pay for the order, or the zero-address as a sentinel value for Ether. */ 53 | address paymentToken; 54 | /* Base price of the order (in paymentTokens). */ 55 | uint256 basePrice; 56 | /* Auction extra parameter - minimum bid increment for English auctions, starting/ending price difference. */ 57 | uint256 extra; 58 | /* Listing timestamp. */ 59 | uint256 listingTime; 60 | /* Expiration timestamp - 0 for no expiry. */ 61 | uint256 expirationTime; 62 | /* Order salt, used to prevent duplicate hashes. */ 63 | uint256 salt; 64 | /* NOTE: uint counter is an additional component of the order but is read from storage */ 65 | } 66 | -------------------------------------------------------------------------------- /src/marketplaces/X2Y2/interfaces/MarketConstants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicensed 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | library Market { 6 | uint256 constant INTENT_SELL = 1; 7 | uint256 constant INTENT_AUCTION = 2; 8 | uint256 constant INTENT_BUY = 3; 9 | 10 | uint8 constant SIGN_V1 = 1; 11 | uint8 constant SIGN_V3 = 3; 12 | 13 | struct OrderItem { 14 | uint256 price; 15 | bytes data; 16 | } 17 | 18 | struct Order { 19 | uint256 salt; 20 | address user; 21 | uint256 network; 22 | uint256 intent; 23 | uint256 delegateType; 24 | uint256 deadline; 25 | address currency; 26 | bytes dataMask; 27 | OrderItem[] items; 28 | // signature 29 | bytes32 r; 30 | bytes32 s; 31 | uint8 v; 32 | uint8 signVersion; 33 | } 34 | 35 | struct Fee { 36 | uint256 percentage; 37 | address to; 38 | } 39 | 40 | struct SettleDetail { 41 | Market.Op op; 42 | uint256 orderIdx; 43 | uint256 itemIdx; 44 | uint256 price; 45 | bytes32 itemHash; 46 | address executionDelegate; 47 | bytes dataReplacement; 48 | uint256 bidIncentivePct; 49 | uint256 aucMinIncrementPct; 50 | uint256 aucIncDurationSecs; 51 | Fee[] fees; 52 | } 53 | 54 | struct SettleShared { 55 | uint256 salt; 56 | uint256 deadline; 57 | uint256 amountToEth; 58 | uint256 amountToWeth; 59 | address user; 60 | bool canFail; 61 | } 62 | 63 | struct RunInput { 64 | Order[] orders; 65 | SettleDetail[] details; 66 | SettleShared shared; 67 | // signature 68 | bytes32 r; 69 | bytes32 s; 70 | uint8 v; 71 | } 72 | 73 | struct OngoingAuction { 74 | uint256 price; 75 | uint256 netPrice; 76 | uint256 endAt; 77 | address bidder; 78 | } 79 | 80 | enum InvStatus { 81 | NEW, 82 | AUCTION, 83 | COMPLETE, 84 | CANCELLED, 85 | REFUNDED 86 | } 87 | 88 | enum Op { 89 | INVALID, 90 | // off-chain 91 | COMPLETE_SELL_OFFER, 92 | COMPLETE_BUY_OFFER, 93 | CANCEL_OFFER, 94 | // auction 95 | BID, 96 | COMPLETE_AUCTION, 97 | REFUND_AUCTION, 98 | REFUND_AUCTION_STUCK_ITEM 99 | } 100 | 101 | enum DelegationType { 102 | INVALID, 103 | ERC721, 104 | ERC1155 105 | } 106 | 107 | struct Pair { 108 | address token; 109 | uint256 tokenId; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/marketplaces/looksRare/lib/OrderTypes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | /** 5 | * @title OrderTypes 6 | * @notice This library contains order types for the LooksRare exchange. 7 | */ 8 | library OrderTypes { 9 | // keccak256("MakerOrder(bool isOrderAsk,address signer,address collection,uint256 price,uint256 tokenId,uint256 amount,address strategy,address currency,uint256 nonce,uint256 startTime,uint256 endTime,uint256 minPercentageToAsk,bytes params)") 10 | bytes32 internal constant MAKER_ORDER_HASH = 11 | 0x40261ade532fa1d2c7293df30aaadb9b3c616fae525a0b56d3d411c841a85028; 12 | 13 | struct MakerOrder { 14 | bool isOrderAsk; // true --> ask / false --> bid 15 | address signer; // signer of the maker order 16 | address collection; // collection address 17 | uint256 price; // price (used as ) 18 | uint256 tokenId; // id of the token 19 | uint256 amount; // amount of tokens to sell/purchase (must be 1 for ERC721, 1+ for ERC1155) 20 | address strategy; // strategy for trade execution (e.g., DutchAuction, StandardSaleForFixedPrice) 21 | address currency; // currency (e.g., WETH) 22 | uint256 nonce; // order nonce (must be unique unless new maker order is meant to override existing one e.g., lower ask price) 23 | uint256 startTime; // startTime in timestamp 24 | uint256 endTime; // endTime in timestamp 25 | uint256 minPercentageToAsk; // slippage protection (9000 --> 90% of the final price must return to ask) 26 | bytes params; // additional parameters 27 | uint8 v; // v: parameter (27 or 28) 28 | bytes32 r; // r: parameter 29 | bytes32 s; // s: parameter 30 | } 31 | 32 | struct TakerOrder { 33 | bool isOrderAsk; // true --> ask / false --> bid 34 | address taker; // msg.sender 35 | uint256 price; // final price for the purchase 36 | uint256 tokenId; 37 | uint256 minPercentageToAsk; // // slippage protection (9000 --> 90% of the final price must return to ask) 38 | bytes params; // other params (e.g., tokenId) 39 | } 40 | 41 | function hash(MakerOrder memory makerOrder) 42 | internal 43 | pure 44 | returns (bytes32) 45 | { 46 | return 47 | keccak256( 48 | abi.encode( 49 | MAKER_ORDER_HASH, 50 | makerOrder.isOrderAsk, 51 | makerOrder.signer, 52 | makerOrder.collection, 53 | makerOrder.price, 54 | makerOrder.tokenId, 55 | makerOrder.amount, 56 | makerOrder.strategy, 57 | makerOrder.currency, 58 | makerOrder.nonce, 59 | makerOrder.startTime, 60 | makerOrder.endTime, 61 | makerOrder.minPercentageToAsk, 62 | keccak256(makerOrder.params) 63 | ) 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Marketplace Benchmarks 2 | Marketplace Benchmarks is a repository which runs a variety of generalized tests on NFT marketplaces to benchmark them for gas efficiency. View benchmark results [here](../results/results.pdf) for EOA calls and [here](../results/results-direct.pdf) for direct calls. 3 | 4 | ### Setup 5 | 6 | #### Install Foundry 7 | To install Foundry (assuming a Linux or macOS system): 8 | 9 | ```bash 10 | curl -L https://foundry.paradigm.xyz | bash 11 | ``` 12 | 13 | This will download foundryup. To start Foundry, run: 14 | 15 | ```bash 16 | foundryup 17 | ``` 18 | 19 | To install dependencies: 20 | 21 | ```bash 22 | forge install 23 | ``` 24 | 25 | ### Run Tests 26 | Tests are all run against mainnet deployments of active marketplaces, as such, a Mainnet Ethereum RPC is required. This will log gas snapshots for each individual test operation. 27 | ```bash 28 | forge test --fork-url -vv 29 | ``` 30 | 31 | ### Adding A Marketplace 32 | 1. Create a marketplace config in `/src/marketplaces` 33 | 2. Integrate into [`GenericMarketplaceTest`](test/GenericMarketplaceTest.t.sol) 34 | - Import your marketplace config 35 | - Create a global variable for your marketpalce config 36 | - Deploy and set your marketplace config in the constructor 37 | - Create a new test named `test` which calls `benchmarkMarket(BaseMarketConfig config)` with your marketplace config. 38 | 39 | #### Marketplace Config 40 | A marketplace config must inherits [`BaseMarketConfig`](src/BaseMarketConfig.sol#L53-L254). See [`SeaportConfig`](src/marketplaces/seaport/SeaportConfig.sol) for reference. 41 | 42 | ##### *Required Functions* 43 | - `beforeAllPrepareMarketplace(address seller, address buyer)` - This function must set the [approval targets](src/BaseMarketConfig.sol#L14-L26) for the marketplace. These addresses will be used prior to each test to reset buyer/seller approvals. 44 | - `name()` - This function must return the name of the marketplace to use in benchmarking results 45 | - `market()` - This function must return the address of the marketplace. It is used to reset the marketplace storage between tests. 46 | 47 | ##### *Optional Functions* 48 | There are a variety of different types of tests which your market can support by implementing any of the functions defined in the `Test Payload Calls` section of [`BaseMarketConfig`](src/BaseMarketConfig.sol). Tests that use unimplemented payload calls will show up as incompatable with your marketplace. 49 | 50 | `beforeAllPrepareMarketplaceCall` is an optional setup function which allows for any arbitrary calls to be sent from any address. For example: it is used to deploy Wyvern proxies for the buyer and seller prior to benchmarking Wyvern. 51 | 52 | ### Adding A Test 53 | Anyone can add a generalized test to this repository which will enable for checkpointing different functionalities across marketplaces. 54 | 55 | #### Creating the Generalized Test 56 | Generalized tests are written in [`GenericMarketplaceTest`](test/GenericMarketplaceTest.t.sol). Use the simple [ERC721->Ether](test/GenericMarketplaceTest.t.sol#L78-L110) test as a reference. 57 | 58 | 1. Ensure that the buyer (Bob) and seller (Alice) have sufficient funds for the transaction. Each user is dealt enough ETH prior to the test. 59 | 2. Find an appropriate payload call in [`BaseMarketConfig`](src/BaseMarketConfig.sol). If none exists, you may create a new one. 60 | 3. Use `_benchmarkCallWithParams` to call the payload. 61 | 4. Add assertions throughout the test to ensure that the marketplace is actually doing what you expect it to. 62 | -------------------------------------------------------------------------------- /src/marketplaces/foundation/FoundationConfig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | pragma solidity >=0.8.7; 3 | 4 | import { BaseMarketConfig } from "../../BaseMarketConfig.sol"; 5 | import { IFoundation } from "./interfaces/IFoundation.sol"; 6 | import { TestCallParameters, TestOrderContext, TestOrderPayload, TestItem721, TestItem1155, TestItem20 } from "../../Types.sol"; 7 | 8 | contract FoundationConfig is BaseMarketConfig { 9 | function name() external pure override returns (string memory) { 10 | return "Foundation"; 11 | } 12 | 13 | function market() public pure override returns (address) { 14 | return address(foundation); 15 | } 16 | 17 | IFoundation internal constant foundation = 18 | IFoundation(0xcDA72070E455bb31C7690a170224Ce43623d0B6f); 19 | 20 | function beforeAllPrepareMarketplace(address, address) external override { 21 | // ERC-20 n/a but currently required by the test suite 22 | buyerNftApprovalTarget = sellerNftApprovalTarget = buyerErc20ApprovalTarget = sellerErc20ApprovalTarget = address( 23 | foundation 24 | ); 25 | } 26 | 27 | function getPayload_BuyOfferedERC721WithEther( 28 | TestOrderContext calldata context, 29 | TestItem721 calldata nft, 30 | uint256 ethAmount 31 | ) external view override returns (TestOrderPayload memory execution) { 32 | if (!context.listOnChain) { 33 | _notImplemented(); 34 | } 35 | 36 | execution.submitOrder = TestCallParameters( 37 | address(foundation), 38 | 0, 39 | abi.encodeWithSelector( 40 | IFoundation.setBuyPrice.selector, 41 | nft.token, 42 | nft.identifier, 43 | ethAmount 44 | ) 45 | ); 46 | execution.executeOrder = TestCallParameters( 47 | address(foundation), 48 | ethAmount, 49 | abi.encodeWithSelector( 50 | IFoundation.buyV2.selector, 51 | nft.token, 52 | nft.identifier, 53 | ethAmount, 54 | address(0) 55 | ) 56 | ); 57 | } 58 | 59 | function getPayload_BuyOfferedERC721WithEtherOneFeeRecipient( 60 | TestOrderContext calldata context, 61 | TestItem721 memory nft, 62 | uint256 priceEthAmount, 63 | address feeRecipient, 64 | uint256 feeEthAmount 65 | ) external view override returns (TestOrderPayload memory execution) { 66 | if (!context.listOnChain) { 67 | _notImplemented(); 68 | } 69 | 70 | execution.submitOrder = TestCallParameters( 71 | address(foundation), 72 | 0, 73 | abi.encodeWithSelector( 74 | IFoundation.setBuyPrice.selector, 75 | nft.token, 76 | nft.identifier, 77 | priceEthAmount 78 | ) 79 | ); 80 | execution.executeOrder = TestCallParameters( 81 | address(foundation), 82 | priceEthAmount, 83 | abi.encodeWithSelector( 84 | IFoundation.buyV2.selector, 85 | nft.token, 86 | nft.identifier, 87 | priceEthAmount, 88 | feeRecipient 89 | ) 90 | ); 91 | } 92 | 93 | function getPayload_BuyOfferedERC721WithEtherTwoFeeRecipient( 94 | TestOrderContext calldata context, 95 | TestItem721 memory nft, 96 | uint256 priceEthAmount, 97 | address feeRecipient1, 98 | uint256 feeEthAmount1, 99 | address feeRecipient2, 100 | uint256 feeEthAmount2 101 | ) external view override returns (TestOrderPayload memory execution) { 102 | if (!context.listOnChain) { 103 | _notImplemented(); 104 | } 105 | 106 | // TODO: pending sell referrer support 107 | _notImplemented(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/marketplaces/seaport/lib/ConsiderationEnums.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | // prettier-ignore 5 | enum OrderType { 6 | // 0: no partial fills, anyone can execute 7 | FULL_OPEN, 8 | 9 | // 1: partial fills supported, anyone can execute 10 | PARTIAL_OPEN, 11 | 12 | // 2: no partial fills, only offerer or zone can execute 13 | FULL_RESTRICTED, 14 | 15 | // 3: partial fills supported, only offerer or zone can execute 16 | PARTIAL_RESTRICTED 17 | } 18 | 19 | // prettier-ignore 20 | enum BasicOrderType { 21 | // 0: no partial fills, anyone can execute 22 | ETH_TO_ERC721_FULL_OPEN, 23 | 24 | // 1: partial fills supported, anyone can execute 25 | ETH_TO_ERC721_PARTIAL_OPEN, 26 | 27 | // 2: no partial fills, only offerer or zone can execute 28 | ETH_TO_ERC721_FULL_RESTRICTED, 29 | 30 | // 3: partial fills supported, only offerer or zone can execute 31 | ETH_TO_ERC721_PARTIAL_RESTRICTED, 32 | 33 | // 4: no partial fills, anyone can execute 34 | ETH_TO_ERC1155_FULL_OPEN, 35 | 36 | // 5: partial fills supported, anyone can execute 37 | ETH_TO_ERC1155_PARTIAL_OPEN, 38 | 39 | // 6: no partial fills, only offerer or zone can execute 40 | ETH_TO_ERC1155_FULL_RESTRICTED, 41 | 42 | // 7: partial fills supported, only offerer or zone can execute 43 | ETH_TO_ERC1155_PARTIAL_RESTRICTED, 44 | 45 | // 8: no partial fills, anyone can execute 46 | ERC20_TO_ERC721_FULL_OPEN, 47 | 48 | // 9: partial fills supported, anyone can execute 49 | ERC20_TO_ERC721_PARTIAL_OPEN, 50 | 51 | // 10: no partial fills, only offerer or zone can execute 52 | ERC20_TO_ERC721_FULL_RESTRICTED, 53 | 54 | // 11: partial fills supported, only offerer or zone can execute 55 | ERC20_TO_ERC721_PARTIAL_RESTRICTED, 56 | 57 | // 12: no partial fills, anyone can execute 58 | ERC20_TO_ERC1155_FULL_OPEN, 59 | 60 | // 13: partial fills supported, anyone can execute 61 | ERC20_TO_ERC1155_PARTIAL_OPEN, 62 | 63 | // 14: no partial fills, only offerer or zone can execute 64 | ERC20_TO_ERC1155_FULL_RESTRICTED, 65 | 66 | // 15: partial fills supported, only offerer or zone can execute 67 | ERC20_TO_ERC1155_PARTIAL_RESTRICTED, 68 | 69 | // 16: no partial fills, anyone can execute 70 | ERC721_TO_ERC20_FULL_OPEN, 71 | 72 | // 17: partial fills supported, anyone can execute 73 | ERC721_TO_ERC20_PARTIAL_OPEN, 74 | 75 | // 18: no partial fills, only offerer or zone can execute 76 | ERC721_TO_ERC20_FULL_RESTRICTED, 77 | 78 | // 19: partial fills supported, only offerer or zone can execute 79 | ERC721_TO_ERC20_PARTIAL_RESTRICTED, 80 | 81 | // 20: no partial fills, anyone can execute 82 | ERC1155_TO_ERC20_FULL_OPEN, 83 | 84 | // 21: partial fills supported, anyone can execute 85 | ERC1155_TO_ERC20_PARTIAL_OPEN, 86 | 87 | // 22: no partial fills, only offerer or zone can execute 88 | ERC1155_TO_ERC20_FULL_RESTRICTED, 89 | 90 | // 23: partial fills supported, only offerer or zone can execute 91 | ERC1155_TO_ERC20_PARTIAL_RESTRICTED 92 | } 93 | 94 | // prettier-ignore 95 | enum BasicOrderRouteType { 96 | // 0: provide Ether (or other native token) to receive offered ERC721 item. 97 | ETH_TO_ERC721, 98 | 99 | // 1: provide Ether (or other native token) to receive offered ERC1155 item. 100 | ETH_TO_ERC1155, 101 | 102 | // 2: provide ERC20 item to receive offered ERC721 item. 103 | ERC20_TO_ERC721, 104 | 105 | // 3: provide ERC20 item to receive offered ERC1155 item. 106 | ERC20_TO_ERC1155, 107 | 108 | // 4: provide ERC721 item to receive offered ERC20 item. 109 | ERC721_TO_ERC20, 110 | 111 | // 5: provide ERC1155 item to receive offered ERC20 item. 112 | ERC1155_TO_ERC20 113 | } 114 | 115 | // prettier-ignore 116 | enum ItemType { 117 | // 0: ETH on mainnet, MATIC on polygon, etc. 118 | NATIVE, 119 | 120 | // 1: ERC20 items (ERC777 and ERC20 analogues could also technically work) 121 | ERC20, 122 | 123 | // 2: ERC721 items 124 | ERC721, 125 | 126 | // 3: ERC1155 items 127 | ERC1155, 128 | 129 | // 4: ERC721 items where a number of tokenIds are supported 130 | ERC721_WITH_CRITERIA, 131 | 132 | // 5: ERC1155 items where a number of ids are supported 133 | ERC1155_WITH_CRITERIA 134 | } 135 | 136 | // prettier-ignore 137 | enum Side { 138 | // 0: Items that can be spent 139 | OFFER, 140 | 141 | // 1: Items that must be received 142 | CONSIDERATION 143 | } 144 | -------------------------------------------------------------------------------- /src/marketplaces/wyvern/lib/ArrayUtils.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.13; 3 | 4 | library ArrayUtils { 5 | function guardedArrayReplace( 6 | bytes memory array, 7 | bytes memory desired, 8 | bytes memory mask 9 | ) internal pure { 10 | require(array.length == desired.length); 11 | require(array.length == mask.length); 12 | 13 | uint256 words = array.length / 0x20; 14 | uint256 index = words * 0x20; 15 | assert(index / 0x20 == words); 16 | uint256 i; 17 | 18 | for (i = 0; i < words; i++) { 19 | /* Conceptually: array[i] = (!mask[i] && array[i]) || (mask[i] && desired[i]), bitwise in word chunks. */ 20 | assembly { 21 | let commonIndex := mul(0x20, add(1, i)) 22 | let maskValue := mload(add(mask, commonIndex)) 23 | mstore( 24 | add(array, commonIndex), 25 | or( 26 | and(not(maskValue), mload(add(array, commonIndex))), 27 | and(maskValue, mload(add(desired, commonIndex))) 28 | ) 29 | ) 30 | } 31 | } 32 | 33 | /* Deal with the last section of the byte array. */ 34 | if (words > 0) { 35 | /* This overlaps with bytes already set but is still more efficient than iterating through each of the remaining bytes individually. */ 36 | i = words; 37 | assembly { 38 | let commonIndex := mul(0x20, add(1, i)) 39 | let maskValue := mload(add(mask, commonIndex)) 40 | mstore( 41 | add(array, commonIndex), 42 | or( 43 | and(not(maskValue), mload(add(array, commonIndex))), 44 | and(maskValue, mload(add(desired, commonIndex))) 45 | ) 46 | ) 47 | } 48 | } else { 49 | /* If the byte array is shorter than a word, we must unfortunately do the whole thing bytewise. 50 | (bounds checks could still probably be optimized away in assembly, but this is a rare case) */ 51 | for (i = index; i < array.length; i++) { 52 | array[i] = 53 | ((mask[i] ^ 0xff) & array[i]) | 54 | (mask[i] & desired[i]); 55 | } 56 | } 57 | } 58 | 59 | /** 60 | * Test if two arrays are equal 61 | * @param a First array 62 | * @param b Second array 63 | * @return Whether or not all bytes in the arrays are equal 64 | */ 65 | function arrayEq(bytes memory a, bytes memory b) 66 | internal 67 | pure 68 | returns (bool) 69 | { 70 | return keccak256(a) == keccak256(b); 71 | } 72 | 73 | /** 74 | * Unsafe write address into a memory location using entire word 75 | * 76 | * @param index Memory location 77 | * @param source uint to write 78 | * @return End memory index 79 | */ 80 | function unsafeWriteAddressWord(uint256 index, address source) 81 | internal 82 | pure 83 | returns (uint256) 84 | { 85 | assembly { 86 | mstore(index, source) 87 | index := add(index, 0x20) 88 | } 89 | return index; 90 | } 91 | 92 | /** 93 | * Unsafe write uint into a memory location 94 | * 95 | * @param index Memory location 96 | * @param source uint to write 97 | * @return End memory index 98 | */ 99 | function unsafeWriteUint(uint256 index, uint256 source) 100 | internal 101 | pure 102 | returns (uint256) 103 | { 104 | assembly { 105 | mstore(index, source) 106 | index := add(index, 0x20) 107 | } 108 | return index; 109 | } 110 | 111 | /** 112 | * Unsafe write uint8 into a memory location 113 | * 114 | * @param index Memory location 115 | * @param source uint8 to write 116 | * @return End memory index 117 | */ 118 | function unsafeWriteUint8(uint256 index, uint8 source) 119 | internal 120 | pure 121 | returns (uint256) 122 | { 123 | assembly { 124 | mstore8(index, source) 125 | index := add(index, 0x1) 126 | } 127 | return index; 128 | } 129 | 130 | /** 131 | * Unsafe write uint8 into a memory location using entire word 132 | * 133 | * @param index Memory location 134 | * @param source uint to write 135 | * @return End memory index 136 | */ 137 | function unsafeWriteUint8Word(uint256 index, uint8 source) 138 | internal 139 | pure 140 | returns (uint256) 141 | { 142 | assembly { 143 | mstore(index, source) 144 | index := add(index, 0x20) 145 | } 146 | return index; 147 | } 148 | 149 | /** 150 | * Unsafe write bytes32 into a memory location using entire word 151 | * 152 | * @param index Memory location 153 | * @param source uint to write 154 | * @return End memory index 155 | */ 156 | function unsafeWriteBytes32(uint256 index, bytes32 source) 157 | internal 158 | pure 159 | returns (uint256) 160 | { 161 | assembly { 162 | mstore(index, source) 163 | index := add(index, 0x20) 164 | } 165 | return index; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/marketplaces/wyvern/lib/WyvernTypeHashes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.13; 3 | 4 | import "./WyvernStructs.sol"; 5 | import "./ArrayUtils.sol"; 6 | 7 | uint256 constant EIP712_Order_size = 0x180; 8 | uint256 constant EIP712_DomainSeparator_offset = 0x02; 9 | uint256 constant EIP712_OrderHash_offset = 0x22; 10 | uint256 constant EIP712_DigestPayload_size = 0x42; 11 | uint256 constant EIP_712_PREFIX = ( 12 | 0x1901000000000000000000000000000000000000000000000000000000000000 13 | ); 14 | 15 | contract WyvernTypeHashes { 16 | bytes32 internal constant _NAME_HASH = 17 | 0x9a2ed463836165738cfa54208ff6e7847fd08cbaac309aac057086cb0a144d13; 18 | bytes32 internal constant _VERSION_HASH = 19 | 0xe2fd538c762ee69cab09ccd70e2438075b7004dd87577dc3937e9fcc8174bb64; 20 | bytes32 internal constant _EIP_712_DOMAIN_TYPEHASH = 21 | 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; 22 | bytes32 internal constant _ORDER_TYPEHASH = 23 | 0xdba08a88a748f356e8faf8578488343eab21b1741728779c9dcfdc782bc800f8; 24 | bytes32 internal immutable _DOMAIN_SEPARATOR; 25 | address internal constant wyvernAddress = 26 | 0x7f268357A8c2552623316e2562D90e642bB538E5; 27 | 28 | constructor() { 29 | _DOMAIN_SEPARATOR = _deriveDomainSeparator(); 30 | } 31 | 32 | /** 33 | * @dev Derive the domain separator for EIP-712 signatures. 34 | * @return The domain separator. 35 | */ 36 | function _deriveDomainSeparator() internal pure returns (bytes32) { 37 | return 38 | keccak256( 39 | abi.encode( 40 | _EIP_712_DOMAIN_TYPEHASH, 41 | _NAME_HASH, 42 | _VERSION_HASH, 43 | 1, 44 | wyvernAddress 45 | ) 46 | ); 47 | } 48 | 49 | function _deriveEIP712Digest(bytes32 orderHash) 50 | internal 51 | view 52 | returns (bytes32 value) 53 | { 54 | bytes32 domainSeparator = _DOMAIN_SEPARATOR; 55 | // Leverage scratch space to perform an efficient hash. 56 | assembly { 57 | // Place the EIP-712 prefix at the start of scratch space. 58 | mstore(0, EIP_712_PREFIX) 59 | 60 | // Place the domain separator in the next region of scratch space. 61 | mstore(EIP712_DomainSeparator_offset, domainSeparator) 62 | 63 | // Place the order hash in scratch space, spilling into the first 64 | // two bytes of the free memory pointer — this should never be set 65 | // as memory cannot be expanded to that size, and will be zeroed out 66 | // after the hash is performed. 67 | mstore(EIP712_OrderHash_offset, orderHash) 68 | 69 | // Hash the relevant region (65 bytes). 70 | value := keccak256(0, EIP712_DigestPayload_size) 71 | 72 | // Clear out the dirtied bits in the memory pointer. 73 | mstore(EIP712_OrderHash_offset, 0) 74 | } 75 | } 76 | 77 | function hashOrder(Order memory order, uint256 counter) 78 | internal 79 | pure 80 | returns (bytes32 hash) 81 | { 82 | /* Unfortunately abi.encodePacked doesn't work here, stack size constraints. */ 83 | uint256 size = 800; 84 | bytes memory array = new bytes(size); 85 | uint256 index; 86 | assembly { 87 | index := add(array, 0x20) 88 | } 89 | index = ArrayUtils.unsafeWriteBytes32(index, _ORDER_TYPEHASH); 90 | index = ArrayUtils.unsafeWriteAddressWord(index, order.exchange); 91 | index = ArrayUtils.unsafeWriteAddressWord(index, order.maker); 92 | index = ArrayUtils.unsafeWriteAddressWord(index, order.taker); 93 | index = ArrayUtils.unsafeWriteUint(index, order.makerRelayerFee); 94 | index = ArrayUtils.unsafeWriteUint(index, order.takerRelayerFee); 95 | index = ArrayUtils.unsafeWriteUint(index, order.makerProtocolFee); 96 | index = ArrayUtils.unsafeWriteUint(index, order.takerProtocolFee); 97 | index = ArrayUtils.unsafeWriteAddressWord(index, order.feeRecipient); 98 | index = ArrayUtils.unsafeWriteUint8Word(index, uint8(order.feeMethod)); 99 | index = ArrayUtils.unsafeWriteUint8Word(index, uint8(order.side)); 100 | index = ArrayUtils.unsafeWriteUint8Word(index, uint8(order.saleKind)); 101 | index = ArrayUtils.unsafeWriteAddressWord(index, order.target); 102 | index = ArrayUtils.unsafeWriteUint8Word(index, uint8(order.howToCall)); 103 | index = ArrayUtils.unsafeWriteBytes32( 104 | index, 105 | keccak256(order._calldata) 106 | ); 107 | index = ArrayUtils.unsafeWriteBytes32( 108 | index, 109 | keccak256(order.replacementPattern) 110 | ); 111 | index = ArrayUtils.unsafeWriteAddressWord(index, order.staticTarget); 112 | index = ArrayUtils.unsafeWriteBytes32( 113 | index, 114 | keccak256(order.staticExtradata) 115 | ); 116 | index = ArrayUtils.unsafeWriteAddressWord(index, order.paymentToken); 117 | index = ArrayUtils.unsafeWriteUint(index, order.basePrice); 118 | index = ArrayUtils.unsafeWriteUint(index, order.extra); 119 | index = ArrayUtils.unsafeWriteUint(index, order.listingTime); 120 | index = ArrayUtils.unsafeWriteUint(index, order.expirationTime); 121 | index = ArrayUtils.unsafeWriteUint(index, order.salt); 122 | index = ArrayUtils.unsafeWriteUint(index, counter); 123 | assembly { 124 | hash := keccak256(add(array, 0x20), size) 125 | } 126 | return hash; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /script/runTest.js: -------------------------------------------------------------------------------- 1 | const { exec } = require("child_process"); 2 | const interpolate = require("color-interpolate"); 3 | const parse = require("color-parse"); 4 | const colormap = interpolate(["green", "red"]); 5 | const fs = require("fs"); 6 | const RPC = process.argv[2]; 7 | 8 | if (RPC === undefined || RPC == "") throw Error("RPC not set"); 9 | 10 | // Execute forge tests and capture `stdout` 11 | exec( 12 | `forge clean && forge test --fork-url ${RPC} -vv`, 13 | (error, stdout, stderr) => { 14 | if (error) { 15 | throw Error("Forge test failed"); 16 | } 17 | 18 | /// {market:{testName:{actionName:{gas,direct}}}} 19 | let eoaTests = {}; 20 | eoaTests.results = {}; 21 | parseOutput(eoaTests, stdout, false); 22 | const eoaLatex = generateLatex( 23 | eoaTests.results, 24 | "Benchmark Tests (EOA)" 25 | ); 26 | fs.writeFileSync("./results/results.tex", eoaLatex); 27 | 28 | /// {market:{testName:{actionName:{gas,direct}}}} 29 | let directTests = {}; 30 | directTests.results = {}; 31 | parseOutput(directTests, stdout, true); 32 | const directLatex = generateLatex( 33 | directTests.results, 34 | "Benchmark Tests (Direct)" 35 | ); 36 | fs.writeFileSync("./results/results-direct.tex", directLatex); 37 | } 38 | ); 39 | 40 | /** 41 | * Parses the entire `stdout` from forge. Sets the values in `tests` 42 | * @param {*} tests The variable which holds test results 43 | * @param {*} stdout The output from running forge on the market-benchmark tests 44 | * @param {*} showDirect Show direct contract interactions (as opposed to EOA) 45 | */ 46 | function parseOutput(tests, stdout, showDirect = false) { 47 | const outputLines = stdout.split("\n"); 48 | let doNextLine = false; 49 | for (let outputLine of outputLines) { 50 | outputLine = outputLine.trim(); 51 | 52 | if (outputLine == "") { 53 | doNextLine = false; 54 | } else if (doNextLine) { 55 | parseTestLine(tests, outputLine, showDirect); 56 | } else if (outputLine.includes("Logs:")) { 57 | doNextLine = true; 58 | } 59 | } 60 | } 61 | 62 | /** 63 | * Parses a line of text from the forge output. Sets corresponding keys in `tests` 64 | */ 65 | function parseTestLine(tests, testLine, showDirect) { 66 | const marketName = testLine.split("]")[0].substring(1); 67 | const testName = testLine.split(")")[0].split("(")[1]; 68 | const actionName = testLine 69 | .split(")")[1] 70 | .split("(")[0] 71 | .split("--")[0] 72 | .trim(); 73 | const direct = testLine.includes("(direct)"); 74 | const gasUsage = 75 | testLine.split("gas:").length > 1 76 | ? testLine.split("gas:")[1].trim() 77 | : 0; 78 | if ( 79 | (showDirect && !direct && gasUsage != 0) || 80 | (!showDirect && direct && gasUsage != 0) 81 | ) 82 | return; // Skip unwanted results 83 | addTestResults(tests, marketName, testName, actionName, gasUsage); 84 | } 85 | 86 | function addTestResults(tests, market, testName, actionName, gasUsage) { 87 | if (!tests.results.hasOwnProperty(market)) { 88 | tests.results[market] = {}; 89 | } 90 | if (!tests.results[market].hasOwnProperty(testName)) { 91 | tests.results[market][testName] = {}; 92 | } 93 | if (actionName == "") return; 94 | tests.results[market][testName][actionName] = { gasUsage }; 95 | } 96 | 97 | /** 98 | * Generates the latex from the global dictionary `tests` 99 | * @returns String containing latex 100 | */ 101 | function generateLatex(tests, title) { 102 | let latex = ""; 103 | const markets = Object.keys(tests); 104 | const testNames = Object.keys(tests[markets[0]]); 105 | 106 | latex += 107 | "\\documentclass[border = 4pt]{standalone}\n\\usepackage{emoji}\n\\usepackage{xcolor}\n\\usepackage{multirow}\n\\begin{document}" + 108 | `\n\\setemojifont{TwemojiMozilla}\n\\begin{tabular}{ |c|c|${"c|".repeat( 109 | markets.length 110 | )} } \n\\hline\n\\multicolumn{${ 111 | 2 + markets.length 112 | }}{|c|}{${title}} \\\\ \n` + 113 | "\\hline \n Test Name & Action Name "; 114 | 115 | for (const market of markets) { 116 | latex += `& ${market} `; 117 | } 118 | 119 | latex += "\\\\ \n\\hline\\hline\n"; 120 | 121 | for (const testName of testNames) { 122 | let actionNames = []; 123 | for (const market of markets) { 124 | const tempActionNames = Object.keys(tests[market][testName]); 125 | if (actionNames.length < tempActionNames.length) 126 | actionNames = tempActionNames; 127 | } 128 | if (actionNames.length == 0) { 129 | actionNames = [""]; 130 | } 131 | 132 | latex += `\\multirow{${actionNames.length}}{18em}{${testName}}`; 133 | for (const actionName of actionNames) { 134 | latex += `& ${actionName} `; 135 | 136 | let gasValues = []; 137 | for (const market of markets) { 138 | if (tests[market][testName][actionName] === undefined) { 139 | gasValues.push(0); 140 | } else { 141 | gasValues.push( 142 | parseInt(tests[market][testName][actionName].gasUsage) 143 | ); 144 | } 145 | } 146 | const maxGas = Math.max(...gasValues); 147 | const minGas = Math.min.apply(null, gasValues.filter(Boolean)); 148 | 149 | for (const gasValue of gasValues) { 150 | const color = getColor(minGas, maxGas, gasValue); 151 | if (gasValue == 0) { 152 | latex += `& \\emoji{cross-mark} `; 153 | } else { 154 | const percentChange = Math.round( 155 | ((gasValue - maxGas) * 100.0) / maxGas 156 | ); 157 | latex += 158 | `& \\color[RGB]{${color.values[0]},${color.values[1]},${color.values[2]}} ${gasValue}` + 159 | (percentChange != 0 ? `(${percentChange}\\%)` : ``); // Only show percent change if not 0 160 | } 161 | } 162 | 163 | latex += "\\\\\n"; 164 | latex += `\\cline{2-${2 + markets.length}}`; 165 | } 166 | latex += "\\cline{0-1} \n"; 167 | } 168 | 169 | latex += "\\end{tabular}\n\\end{document}"; 170 | 171 | return latex; 172 | } 173 | 174 | /** 175 | * Generate interpolated color between green and red (green lowest gas, and red highest) 176 | * @param {*} minGas The minimum gas used for the test 177 | * @param {*} maxGas The maximum gas used for the test 178 | * @param {*} gas The gas used by a market for this test 179 | * @returns The color to display market results as for the test 180 | */ 181 | function getColor(minGas, maxGas, gas) { 182 | let color; 183 | if (minGas == maxGas) { 184 | color = colormap(0); 185 | } else if (!Number.isFinite(minGas)) { 186 | color = colormap(0); 187 | } else { 188 | color = colormap(((gas - minGas) * 1.0) / (maxGas - minGas)); 189 | } 190 | 191 | const parsed = parse(color); 192 | return parsed; 193 | } 194 | -------------------------------------------------------------------------------- /test/utils/BaseOrderTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.14; 3 | import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; 4 | import { stdStorage, StdStorage } from "forge-std/Test.sol"; 5 | import { TestERC1155 } from "../tokens/TestERC1155.sol"; 6 | import { TestERC20 } from "../tokens/TestERC20.sol"; 7 | import { TestERC721 } from "../tokens/TestERC721.sol"; 8 | 9 | contract BaseOrderTest is DSTestPlus { 10 | using stdStorage for StdStorage; 11 | StdStorage stdstore; 12 | 13 | uint256 constant MAX_INT = ~uint256(0); 14 | 15 | uint256 internal alicePk = 0xa11ce; 16 | uint256 internal bobPk = 0xb0b; 17 | uint256 internal calPk = 0xca1; 18 | uint256 internal feeReciever1Pk = 0xfee1; 19 | uint256 internal feeReciever2Pk = 0xfee2; 20 | address payable internal alice = payable(hevm.addr(alicePk)); 21 | address payable internal bob = payable(hevm.addr(bobPk)); 22 | address payable internal cal = payable(hevm.addr(calPk)); 23 | address payable internal feeReciever1 = payable(hevm.addr(feeReciever1Pk)); 24 | address payable internal feeReciever2 = payable(hevm.addr(feeReciever2Pk)); 25 | 26 | TestERC20 internal token1; 27 | TestERC20 internal token2; 28 | TestERC20 internal token3; 29 | 30 | TestERC721 internal test721_1; 31 | TestERC721 internal test721_2; 32 | TestERC721 internal test721_3; 33 | 34 | TestERC1155 internal test1155_1; 35 | TestERC1155 internal test1155_2; 36 | TestERC1155 internal test1155_3; 37 | 38 | address[] allTokens; 39 | TestERC20[] erc20s; 40 | address[] erc20Addresses; 41 | TestERC721[] erc721s; 42 | address[] erc721Addresses; 43 | TestERC1155[] erc1155s; 44 | address[] accounts; 45 | mapping(address => uint256) internal privateKeys; 46 | 47 | mapping(bytes32 => bool) originalMarketWriteSlots; 48 | 49 | event Transfer(address indexed from, address indexed to, uint256 value); 50 | 51 | event TransferSingle( 52 | address indexed operator, 53 | address indexed from, 54 | address indexed to, 55 | uint256 id, 56 | uint256 value 57 | ); 58 | 59 | struct RestoreERC20Balance { 60 | address token; 61 | address who; 62 | } 63 | 64 | function setUp() public virtual { 65 | hevm.label(alice, "alice"); 66 | hevm.label(bob, "bob"); 67 | hevm.label(cal, "cal"); 68 | hevm.label(address(this), "testContract"); 69 | 70 | privateKeys[alice] = alicePk; 71 | privateKeys[bob] = bobPk; 72 | privateKeys[cal] = calPk; 73 | 74 | _deployTestTokenContracts(); 75 | accounts = [alice, bob, cal, address(this)]; 76 | erc20s = [token1, token2, token3]; 77 | erc20Addresses = [address(token1), address(token2), address(token3)]; 78 | erc721s = [test721_1, test721_2, test721_3]; 79 | erc721Addresses = [address(test721_1), address(test721_2), address(test721_3)]; 80 | erc1155s = [test1155_1, test1155_2, test1155_3]; 81 | allTokens = [ 82 | address(token1), 83 | address(token2), 84 | address(token3), 85 | address(test721_1), 86 | address(test721_2), 87 | address(test721_3), 88 | address(test1155_1), 89 | address(test1155_2), 90 | address(test1155_3) 91 | ]; 92 | } 93 | 94 | /** 95 | * @dev deploy test token contracts 96 | */ 97 | function _deployTestTokenContracts() internal { 98 | token1 = new TestERC20(); 99 | token2 = new TestERC20(); 100 | token3 = new TestERC20(); 101 | test721_1 = new TestERC721(); 102 | test721_2 = new TestERC721(); 103 | test721_3 = new TestERC721(); 104 | test1155_1 = new TestERC1155(); 105 | test1155_2 = new TestERC1155(); 106 | test1155_3 = new TestERC1155(); 107 | hevm.label(address(token1), "token1"); 108 | hevm.label(address(test721_1), "test721_1"); 109 | hevm.label(address(test1155_1), "test1155_1"); 110 | hevm.label(address(feeReciever1), "feeReciever1"); 111 | hevm.label(address(feeReciever2), "feeReciever2"); 112 | } 113 | 114 | function _setApprovals( 115 | address _owner, 116 | address _erc20Target, 117 | address _erc721Target, 118 | address _erc1155Target 119 | ) internal { 120 | hevm.startPrank(_owner); 121 | for (uint256 i = 0; i < erc20s.length; i++) { 122 | erc20s[i].approve(_erc20Target, MAX_INT); 123 | } 124 | for (uint256 i = 0; i < erc721s.length; i++) { 125 | erc721s[i].setApprovalForAll(_erc721Target, true); 126 | } 127 | for (uint256 i = 0; i < erc1155s.length; i++) { 128 | erc1155s[i].setApprovalForAll( 129 | _erc1155Target != address(0) ? _erc1155Target : _erc721Target, 130 | true 131 | ); 132 | } 133 | 134 | hevm.stopPrank(); 135 | } 136 | 137 | /** 138 | * @dev reset written token storage slots to 0 and reinitialize uint128(MAX_INT) 139 | * erc20 balances for 3 test accounts and this 140 | */ 141 | function _resetStorageAndEth(address market) internal { 142 | _resetTokensStorage(); 143 | _restoreEthBalances(); 144 | _resetMarketStorage(market); 145 | hevm.record(); 146 | } 147 | 148 | function _restoreEthBalances() internal { 149 | for (uint256 i = 0; i < accounts.length; i++) { 150 | hevm.deal(accounts[i], uint128(MAX_INT)); 151 | } 152 | hevm.deal(feeReciever1, 0); 153 | hevm.deal(feeReciever2, 0); 154 | } 155 | 156 | /** 157 | * @dev Reset market storage between runs to allow for duplicate orders 158 | */ 159 | function _resetMarketStorage(address market) internal { 160 | if (!originalMarketWriteSlots[0]) { 161 | (, bytes32[] memory writeSlots1) = hevm.accesses(market); 162 | for (uint256 i = 0; i < writeSlots1.length; i++) { 163 | originalMarketWriteSlots[writeSlots1[i]] = true; 164 | } 165 | originalMarketWriteSlots[0] = true; 166 | } 167 | (, bytes32[] memory writeSlots) = hevm.accesses(market); 168 | for (uint256 i = 0; i < writeSlots.length; i++) { 169 | if (originalMarketWriteSlots[writeSlots[i]]) continue; 170 | hevm.store(market, writeSlots[i], bytes32(0)); 171 | } 172 | } 173 | 174 | function _resetTokensStorage() internal { 175 | for (uint256 i = 0; i < allTokens.length; i++) { 176 | _resetStorage(allTokens[i]); 177 | } 178 | } 179 | 180 | /** 181 | * @dev reset all storage written at an address thus far to 0; will overwrite totalSupply()for ERC20s but that should be fine 182 | * with the goal of resetting the balances and owners of tokens - but note: should be careful about approvals, etc 183 | * 184 | * note: must be called in conjunction with vm.record() 185 | */ 186 | function _resetStorage(address _addr) internal { 187 | (, bytes32[] memory writeSlots) = hevm.accesses(_addr); 188 | for (uint256 i = 0; i < writeSlots.length; i++) { 189 | hevm.store(_addr, writeSlots[i], bytes32(0)); 190 | } 191 | } 192 | 193 | receive() external payable virtual {} 194 | } 195 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@solidity-parser/parser@^0.14.0": 6 | version "0.14.2" 7 | resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.2.tgz#2d8f2bddb217621df882ceeae7d7b42ae8664db3" 8 | integrity sha512-10cr0s+MtRtqjEw0WFJrm2rwULN30xx7btd/v9cmqME2617/2M5MbHDkFIGIGTa7lwNw4bN9mVGfhlLzrYw8pA== 9 | dependencies: 10 | antlr4ts "^0.5.0-alpha.4" 11 | 12 | almost-equal@^1.1.0: 13 | version "1.1.0" 14 | resolved "https://registry.yarnpkg.com/almost-equal/-/almost-equal-1.1.0.tgz#f851c631138757994276aa2efbe8dfa3066cccdd" 15 | integrity sha512-0V/PkoculFl5+0Lp47JoxUcO0xSxhIBvm+BxHdD/OgXNmdRpRHCFnKVuUoWyS9EzQP+otSGv0m9Lb4yVkQBn2A== 16 | 17 | ansi-regex@^5.0.1: 18 | version "5.0.1" 19 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 20 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 21 | 22 | antlr4ts@^0.5.0-alpha.4: 23 | version "0.5.0-alpha.4" 24 | resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" 25 | integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== 26 | 27 | child_process@^1.0.2: 28 | version "1.0.2" 29 | resolved "https://registry.yarnpkg.com/child_process/-/child_process-1.0.2.tgz#b1f7e7fc73d25e7fd1d455adc94e143830182b5a" 30 | integrity sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g== 31 | 32 | clamp@^1.0.1: 33 | version "1.0.1" 34 | resolved "https://registry.yarnpkg.com/clamp/-/clamp-1.0.1.tgz#66a0e64011816e37196828fdc8c8c147312c8634" 35 | integrity sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA== 36 | 37 | color-interpolate@^1.0.5: 38 | version "1.0.5" 39 | resolved "https://registry.yarnpkg.com/color-interpolate/-/color-interpolate-1.0.5.tgz#d5710ce4244bd8b9feeda003f409edd4073b6217" 40 | integrity sha512-EcWwYtBJdbeHyYq/y5QwVWLBUm4s7+8K37ycgO9OdY6YuAEa0ywAY+ItlAxE1UO5bXW4ugxNhStTV3rsTZ35ZA== 41 | dependencies: 42 | clamp "^1.0.1" 43 | color-parse "^1.2.0" 44 | color-space "^1.14.3" 45 | lerp "^1.0.3" 46 | 47 | color-name@^1.0.0: 48 | version "1.1.4" 49 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 50 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 51 | 52 | color-parse@^1.2.0, color-parse@^1.4.2: 53 | version "1.4.2" 54 | resolved "https://registry.yarnpkg.com/color-parse/-/color-parse-1.4.2.tgz#78651f5d34df1a57f997643d86f7f87268ad4eb5" 55 | integrity sha512-RI7s49/8yqDj3fECFZjUI1Yi0z/Gq1py43oNJivAIIDSyJiOZLfYCRQEgn8HEVAj++PcRe8AnL2XF0fRJ3BTnA== 56 | dependencies: 57 | color-name "^1.0.0" 58 | 59 | color-space@^1.14.3: 60 | version "1.16.0" 61 | resolved "https://registry.yarnpkg.com/color-space/-/color-space-1.16.0.tgz#611781bca41cd8582a1466fd9e28a7d3d89772a2" 62 | integrity sha512-A6WMiFzunQ8KEPFmj02OnnoUnqhmSaHaZ/0LVFcPTdlvm8+3aMJ5x1HRHy3bDHPkovkf4sS0f4wsVvwk71fKkg== 63 | dependencies: 64 | hsluv "^0.0.3" 65 | mumath "^3.3.4" 66 | 67 | emoji-regex@^10.0.0: 68 | version "10.1.0" 69 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.1.0.tgz#d50e383743c0f7a5945c47087295afc112e3cf66" 70 | integrity sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg== 71 | 72 | emoji-regex@^8.0.0: 73 | version "8.0.0" 74 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 75 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 76 | 77 | escape-string-regexp@^4.0.0: 78 | version "4.0.0" 79 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 80 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 81 | 82 | hsluv@^0.0.3: 83 | version "0.0.3" 84 | resolved "https://registry.yarnpkg.com/hsluv/-/hsluv-0.0.3.tgz#829107dafb4a9f8b52a1809ed02e091eade6754c" 85 | integrity sha512-08iL2VyCRbkQKBySkSh6m8zMUa3sADAxGVWs3Z1aPcUkTJeK0ETG4Fc27tEmQBGUAXZjIsXOZqBvacuVNSC/fQ== 86 | 87 | is-fullwidth-code-point@^3.0.0: 88 | version "3.0.0" 89 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 90 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 91 | 92 | lerp@^1.0.3: 93 | version "1.0.3" 94 | resolved "https://registry.yarnpkg.com/lerp/-/lerp-1.0.3.tgz#a18c8968f917896de15ccfcc28d55a6b731e776e" 95 | integrity sha512-70Rh4rCkJDvwWiTsyZ1HmJGvnyfFah4m6iTux29XmasRiZPDBpT9Cfa4ai73+uLZxnlKruUS62jj2lb11wURiA== 96 | 97 | lru-cache@^6.0.0: 98 | version "6.0.0" 99 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 100 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 101 | dependencies: 102 | yallist "^4.0.0" 103 | 104 | mumath@^3.3.4: 105 | version "3.3.4" 106 | resolved "https://registry.yarnpkg.com/mumath/-/mumath-3.3.4.tgz#48d4a0f0fd8cad4e7b32096ee89b161a63d30bbf" 107 | integrity sha512-VAFIOG6rsxoc7q/IaY3jdjmrsuX9f15KlRLYTHmixASBZkZEKC1IFqE2BC5CdhXmK6WLM1Re33z//AGmeRI6FA== 108 | dependencies: 109 | almost-equal "^1.1.0" 110 | 111 | prettier-plugin-solidity@^1.0.0-beta.13: 112 | version "1.0.0-beta.19" 113 | resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz#7c3607fc4028f5e6a425259ff03e45eedf733df3" 114 | integrity sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g== 115 | dependencies: 116 | "@solidity-parser/parser" "^0.14.0" 117 | emoji-regex "^10.0.0" 118 | escape-string-regexp "^4.0.0" 119 | semver "^7.3.5" 120 | solidity-comments-extractor "^0.0.7" 121 | string-width "^4.2.3" 122 | 123 | prettier@^2.3.1: 124 | version "2.7.1" 125 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" 126 | integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== 127 | 128 | semver@^7.3.5: 129 | version "7.3.7" 130 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" 131 | integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== 132 | dependencies: 133 | lru-cache "^6.0.0" 134 | 135 | solidity-comments-extractor@^0.0.7: 136 | version "0.0.7" 137 | resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" 138 | integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== 139 | 140 | string-width@^4.2.3: 141 | version "4.2.3" 142 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 143 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 144 | dependencies: 145 | emoji-regex "^8.0.0" 146 | is-fullwidth-code-point "^3.0.0" 147 | strip-ansi "^6.0.1" 148 | 149 | strip-ansi@^6.0.1: 150 | version "6.0.1" 151 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 152 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 153 | dependencies: 154 | ansi-regex "^5.0.1" 155 | 156 | yallist@^4.0.0: 157 | version "4.0.0" 158 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 159 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 160 | -------------------------------------------------------------------------------- /src/marketplaces/seaport/lib/ConsiderationStructs.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | // prettier-ignore 5 | import { 6 | OrderType, 7 | BasicOrderType, 8 | ItemType, 9 | Side, 10 | BasicOrderRouteType 11 | } from "./ConsiderationEnums.sol"; 12 | 13 | /** 14 | * @dev An order contains eleven components: an offerer, a zone (or account that 15 | * can cancel the order or restrict who can fulfill the order depending on 16 | * the type), the order type (specifying partial fill support as well as 17 | * restricted order status), the start and end time, a hash that will be 18 | * provided to the zone when validating restricted orders, a salt, a key 19 | * corresponding to a given conduit, a counter, and an arbitrary number of 20 | * offer items that can be spent along with consideration items that must 21 | * be received by their respective recipient. 22 | */ 23 | struct OrderComponents { 24 | address offerer; 25 | address zone; 26 | OfferItem[] offer; 27 | ConsiderationItem[] consideration; 28 | OrderType orderType; 29 | uint256 startTime; 30 | uint256 endTime; 31 | bytes32 zoneHash; 32 | uint256 salt; 33 | bytes32 conduitKey; 34 | uint256 counter; 35 | } 36 | 37 | /** 38 | * @dev An offer item has five components: an item type (ETH or other native 39 | * tokens, ERC20, ERC721, and ERC1155, as well as criteria-based ERC721 and 40 | * ERC1155), a token address, a dual-purpose "identifierOrCriteria" 41 | * component that will either represent a tokenId or a merkle root 42 | * depending on the item type, and a start and end amount that support 43 | * increasing or decreasing amounts over the duration of the respective 44 | * order. 45 | */ 46 | struct OfferItem { 47 | ItemType itemType; 48 | address token; 49 | uint256 identifierOrCriteria; 50 | uint256 startAmount; 51 | uint256 endAmount; 52 | } 53 | 54 | /** 55 | * @dev A consideration item has the same five components as an offer item and 56 | * an additional sixth component designating the required recipient of the 57 | * item. 58 | */ 59 | struct ConsiderationItem { 60 | ItemType itemType; 61 | address token; 62 | uint256 identifierOrCriteria; 63 | uint256 startAmount; 64 | uint256 endAmount; 65 | address payable recipient; 66 | } 67 | 68 | /** 69 | * @dev A spent item is translated from a utilized offer item and has four 70 | * components: an item type (ETH or other native tokens, ERC20, ERC721, and 71 | * ERC1155), a token address, a tokenId, and an amount. 72 | */ 73 | struct SpentItem { 74 | ItemType itemType; 75 | address token; 76 | uint256 identifier; 77 | uint256 amount; 78 | } 79 | 80 | /** 81 | * @dev A received item is translated from a utilized consideration item and has 82 | * the same four components as a spent item, as well as an additional fifth 83 | * component designating the required recipient of the item. 84 | */ 85 | struct ReceivedItem { 86 | ItemType itemType; 87 | address token; 88 | uint256 identifier; 89 | uint256 amount; 90 | address payable recipient; 91 | } 92 | 93 | /** 94 | * @dev For basic orders involving ETH / native / ERC20 <=> ERC721 / ERC1155 95 | * matching, a group of six functions may be called that only requires a 96 | * subset of the usual order arguments. Note the use of a "basicOrderType" 97 | * enum; this represents both the usual order type as well as the "route" 98 | * of the basic order (a simple derivation function for the basic order 99 | * type is `basicOrderType = orderType + (4 * basicOrderRoute)`.) 100 | */ 101 | struct BasicOrderParameters { 102 | // calldata offset 103 | address considerationToken; // 0x24 104 | uint256 considerationIdentifier; // 0x44 105 | uint256 considerationAmount; // 0x64 106 | address payable offerer; // 0x84 107 | address zone; // 0xa4 108 | address offerToken; // 0xc4 109 | uint256 offerIdentifier; // 0xe4 110 | uint256 offerAmount; // 0x104 111 | BasicOrderType basicOrderType; // 0x124 112 | uint256 startTime; // 0x144 113 | uint256 endTime; // 0x164 114 | bytes32 zoneHash; // 0x184 115 | uint256 salt; // 0x1a4 116 | bytes32 offererConduitKey; // 0x1c4 117 | bytes32 fulfillerConduitKey; // 0x1e4 118 | uint256 totalOriginalAdditionalRecipients; // 0x204 119 | AdditionalRecipient[] additionalRecipients; // 0x224 120 | bytes signature; // 0x244 121 | // Total length, excluding dynamic array data: 0x264 (580) 122 | } 123 | 124 | /** 125 | * @dev Basic orders can supply any number of additional recipients, with the 126 | * implied assumption that they are supplied from the offered ETH (or other 127 | * native token) or ERC20 token for the order. 128 | */ 129 | struct AdditionalRecipient { 130 | uint256 amount; 131 | address payable recipient; 132 | } 133 | 134 | /** 135 | * @dev The full set of order components, with the exception of the counter, 136 | * must be supplied when fulfilling more sophisticated orders or groups of 137 | * orders. The total number of original consideration items must also be 138 | * supplied, as the caller may specify additional consideration items. 139 | */ 140 | struct OrderParameters { 141 | address offerer; // 0x00 142 | address zone; // 0x20 143 | OfferItem[] offer; // 0x40 144 | ConsiderationItem[] consideration; // 0x60 145 | OrderType orderType; // 0x80 146 | uint256 startTime; // 0xa0 147 | uint256 endTime; // 0xc0 148 | bytes32 zoneHash; // 0xe0 149 | uint256 salt; // 0x100 150 | bytes32 conduitKey; // 0x120 151 | uint256 totalOriginalConsiderationItems; // 0x140 152 | // offer.length // 0x160 153 | } 154 | 155 | /** 156 | * @dev Orders require a signature in addition to the other order parameters. 157 | */ 158 | struct Order { 159 | OrderParameters parameters; 160 | bytes signature; 161 | } 162 | 163 | /** 164 | * @dev Advanced orders include a numerator (i.e. a fraction to attempt to fill) 165 | * and a denominator (the total size of the order) in addition to the 166 | * signature and other order parameters. It also supports an optional field 167 | * for supplying extra data; this data will be included in a staticcall to 168 | * `isValidOrderIncludingExtraData` on the zone for the order if the order 169 | * type is restricted and the offerer or zone are not the caller. 170 | */ 171 | struct AdvancedOrder { 172 | OrderParameters parameters; 173 | uint120 numerator; 174 | uint120 denominator; 175 | bytes signature; 176 | bytes extraData; 177 | } 178 | 179 | /** 180 | * @dev Orders can be validated (either explicitly via `validate`, or as a 181 | * consequence of a full or partial fill), specifically cancelled (they can 182 | * also be cancelled in bulk via incrementing a per-zone counter), and 183 | * partially or fully filled (with the fraction filled represented by a 184 | * numerator and denominator). 185 | */ 186 | struct OrderStatus { 187 | bool isValidated; 188 | bool isCancelled; 189 | uint120 numerator; 190 | uint120 denominator; 191 | } 192 | 193 | /** 194 | * @dev A criteria resolver specifies an order, side (offer vs. consideration), 195 | * and item index. It then provides a chosen identifier (i.e. tokenId) 196 | * alongside a merkle proof demonstrating the identifier meets the required 197 | * criteria. 198 | */ 199 | struct CriteriaResolver { 200 | uint256 orderIndex; 201 | Side side; 202 | uint256 index; 203 | uint256 identifier; 204 | bytes32[] criteriaProof; 205 | } 206 | 207 | /** 208 | * @dev A fulfillment is applied to a group of orders. It decrements a series of 209 | * offer and consideration items, then generates a single execution 210 | * element. A given fulfillment can be applied to as many offer and 211 | * consideration items as desired, but must contain at least one offer and 212 | * at least one consideration that match. The fulfillment must also remain 213 | * consistent on all key parameters across all offer items (same offerer, 214 | * token, type, tokenId, and conduit preference) as well as across all 215 | * consideration items (token, type, tokenId, and recipient). 216 | */ 217 | struct Fulfillment { 218 | FulfillmentComponent[] offerComponents; 219 | FulfillmentComponent[] considerationComponents; 220 | } 221 | 222 | /** 223 | * @dev Each fulfillment component contains one index referencing a specific 224 | * order and another referencing a specific offer or consideration item. 225 | */ 226 | struct FulfillmentComponent { 227 | uint256 orderIndex; 228 | uint256 itemIndex; 229 | } 230 | 231 | /** 232 | * @dev An execution is triggered once all consideration items have been zeroed 233 | * out. It sends the item in question from the offerer to the item's 234 | * recipient, optionally sourcing approvals from either this contract 235 | * directly or from the offerer's chosen conduit if one is specified. An 236 | * execution is not provided as an argument, but rather is derived via 237 | * orders, criteria resolvers, and fulfillments (where the total number of 238 | * executions will be less than or equal to the total number of indicated 239 | * fulfillments) and returned as part of `matchOrders`. 240 | */ 241 | struct Execution { 242 | ReceivedItem item; 243 | address offerer; 244 | bytes32 conduitKey; 245 | } 246 | -------------------------------------------------------------------------------- /src/marketplaces/seaport/lib/ConsiderationTypeHashes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | import "./ConsiderationStructs.sol"; 5 | 6 | uint256 constant EIP712_Order_size = 0x180; 7 | uint256 constant EIP712_OfferItem_size = 0xc0; 8 | uint256 constant EIP712_ConsiderationItem_size = 0xe0; 9 | uint256 constant EIP712_DomainSeparator_offset = 0x02; 10 | uint256 constant EIP712_OrderHash_offset = 0x22; 11 | uint256 constant EIP712_DigestPayload_size = 0x42; 12 | uint256 constant EIP_712_PREFIX = ( 13 | 0x1901000000000000000000000000000000000000000000000000000000000000 14 | ); 15 | 16 | contract ConsiderationTypeHashes { 17 | bytes32 internal immutable _NAME_HASH; 18 | bytes32 internal immutable _VERSION_HASH; 19 | bytes32 internal immutable _EIP_712_DOMAIN_TYPEHASH; 20 | bytes32 internal immutable _OFFER_ITEM_TYPEHASH; 21 | bytes32 internal immutable _CONSIDERATION_ITEM_TYPEHASH; 22 | bytes32 internal immutable _ORDER_TYPEHASH; 23 | bytes32 internal immutable _DOMAIN_SEPARATOR; 24 | address internal constant seaportAddress = 25 | address(0x00000000006c3852cbEf3e08E8dF289169EdE581); 26 | 27 | constructor() { 28 | // Derive hash of the name of the contract. 29 | _NAME_HASH = keccak256(bytes("Seaport")); 30 | 31 | // Derive hash of the version string of the contract. 32 | _VERSION_HASH = keccak256(bytes("1.1")); 33 | 34 | bytes memory offerItemTypeString = abi.encodePacked( 35 | "OfferItem(", 36 | "uint8 itemType,", 37 | "address token,", 38 | "uint256 identifierOrCriteria,", 39 | "uint256 startAmount,", 40 | "uint256 endAmount", 41 | ")" 42 | ); 43 | 44 | // Construct the ConsiderationItem type string. 45 | // prettier-ignore 46 | bytes memory considerationItemTypeString = abi.encodePacked( 47 | "ConsiderationItem(", 48 | "uint8 itemType,", 49 | "address token,", 50 | "uint256 identifierOrCriteria,", 51 | "uint256 startAmount,", 52 | "uint256 endAmount,", 53 | "address recipient", 54 | ")" 55 | ); 56 | 57 | // Construct the OrderComponents type string, not including the above. 58 | // prettier-ignore 59 | bytes memory orderComponentsPartialTypeString = abi.encodePacked( 60 | "OrderComponents(", 61 | "address offerer,", 62 | "address zone,", 63 | "OfferItem[] offer,", 64 | "ConsiderationItem[] consideration,", 65 | "uint8 orderType,", 66 | "uint256 startTime,", 67 | "uint256 endTime,", 68 | "bytes32 zoneHash,", 69 | "uint256 salt,", 70 | "bytes32 conduitKey,", 71 | "uint256 counter", 72 | ")" 73 | ); 74 | // Derive the OfferItem type hash using the corresponding type string. 75 | bytes32 offerItemTypehash = keccak256(offerItemTypeString); 76 | 77 | // Derive ConsiderationItem type hash using corresponding type string. 78 | bytes32 considerationItemTypehash = keccak256( 79 | considerationItemTypeString 80 | ); 81 | 82 | // Construct the primary EIP-712 domain type string. 83 | // prettier-ignore 84 | _EIP_712_DOMAIN_TYPEHASH = keccak256( 85 | abi.encodePacked( 86 | "EIP712Domain(", 87 | "string name,", 88 | "string version,", 89 | "uint256 chainId,", 90 | "address verifyingContract", 91 | ")" 92 | ) 93 | ); 94 | 95 | _OFFER_ITEM_TYPEHASH = offerItemTypehash; 96 | _CONSIDERATION_ITEM_TYPEHASH = considerationItemTypehash; 97 | 98 | // Derive OrderItem type hash via combination of relevant type strings. 99 | _ORDER_TYPEHASH = keccak256( 100 | abi.encodePacked( 101 | orderComponentsPartialTypeString, 102 | considerationItemTypeString, 103 | offerItemTypeString 104 | ) 105 | ); 106 | 107 | _DOMAIN_SEPARATOR = _deriveDomainSeparator(); 108 | } 109 | 110 | /** 111 | * @dev Internal view function to derive the EIP-712 domain separator. 112 | * 113 | * @return The derived domain separator. 114 | */ 115 | function _deriveDomainSeparator() internal view returns (bytes32) { 116 | // prettier-ignore 117 | return keccak256( 118 | abi.encode( 119 | _EIP_712_DOMAIN_TYPEHASH, 120 | _NAME_HASH, 121 | _VERSION_HASH, 122 | 1, 123 | seaportAddress 124 | ) 125 | ); 126 | } 127 | 128 | /** 129 | * @dev Internal pure function to efficiently derive an digest to sign for 130 | * an order in accordance with EIP-712. 131 | * 132 | * @param orderHash The order hash. 133 | * 134 | * @return value The hash. 135 | */ 136 | function _deriveEIP712Digest(bytes32 orderHash) 137 | internal 138 | view 139 | returns (bytes32 value) 140 | { 141 | bytes32 domainSeparator = _DOMAIN_SEPARATOR; 142 | // Leverage scratch space to perform an efficient hash. 143 | assembly { 144 | // Place the EIP-712 prefix at the start of scratch space. 145 | mstore(0, EIP_712_PREFIX) 146 | 147 | // Place the domain separator in the next region of scratch space. 148 | mstore(EIP712_DomainSeparator_offset, domainSeparator) 149 | 150 | // Place the order hash in scratch space, spilling into the first 151 | // two bytes of the free memory pointer — this should never be set 152 | // as memory cannot be expanded to that size, and will be zeroed out 153 | // after the hash is performed. 154 | mstore(EIP712_OrderHash_offset, orderHash) 155 | 156 | // Hash the relevant region (65 bytes). 157 | value := keccak256(0, EIP712_DigestPayload_size) 158 | 159 | // Clear out the dirtied bits in the memory pointer. 160 | mstore(EIP712_OrderHash_offset, 0) 161 | } 162 | } 163 | 164 | /** 165 | * @dev Internal view function to derive the EIP-712 hash for an offer item. 166 | * 167 | * @param offerItem The offered item to hash. 168 | * 169 | * @return The hash. 170 | */ 171 | function _hashOfferItem(OfferItem memory offerItem) 172 | internal 173 | view 174 | returns (bytes32) 175 | { 176 | return 177 | keccak256( 178 | abi.encode( 179 | _OFFER_ITEM_TYPEHASH, 180 | offerItem.itemType, 181 | offerItem.token, 182 | offerItem.identifierOrCriteria, 183 | offerItem.startAmount, 184 | offerItem.endAmount 185 | ) 186 | ); 187 | } 188 | 189 | /** 190 | * @dev Internal view function to derive the EIP-712 hash for a consideration item. 191 | * 192 | * @param considerationItem The consideration item to hash. 193 | * 194 | * @return The hash. 195 | */ 196 | function _hashConsiderationItem(ConsiderationItem memory considerationItem) 197 | internal 198 | view 199 | returns (bytes32) 200 | { 201 | return 202 | keccak256( 203 | abi.encode( 204 | _CONSIDERATION_ITEM_TYPEHASH, 205 | considerationItem.itemType, 206 | considerationItem.token, 207 | considerationItem.identifierOrCriteria, 208 | considerationItem.startAmount, 209 | considerationItem.endAmount, 210 | considerationItem.recipient 211 | ) 212 | ); 213 | } 214 | 215 | /** 216 | * @dev Internal view function to derive the order hash for a given order. 217 | * Note that only the original consideration items are included in the 218 | * order hash, as additional consideration items may be supplied by the 219 | * caller. 220 | * 221 | * @param orderParameters The parameters of the order to hash. 222 | * @param counter The counter of the order to hash. 223 | * 224 | * @return orderHash The hash. 225 | */ 226 | function _deriveOrderHash( 227 | OrderParameters memory orderParameters, 228 | uint256 counter 229 | ) internal view returns (bytes32 orderHash) { 230 | // Designate new memory regions for offer and consideration item hashes. 231 | bytes32[] memory offerHashes = new bytes32[]( 232 | orderParameters.offer.length 233 | ); 234 | bytes32[] memory considerationHashes = new bytes32[]( 235 | orderParameters.totalOriginalConsiderationItems 236 | ); 237 | 238 | // Iterate over each offer on the order. 239 | for (uint256 i = 0; i < orderParameters.offer.length; ++i) { 240 | // Hash the offer and place the result into memory. 241 | offerHashes[i] = _hashOfferItem(orderParameters.offer[i]); 242 | } 243 | 244 | // Iterate over each consideration on the order. 245 | for ( 246 | uint256 i = 0; 247 | i < orderParameters.totalOriginalConsiderationItems; 248 | ++i 249 | ) { 250 | // Hash the consideration and place the result into memory. 251 | considerationHashes[i] = _hashConsiderationItem( 252 | orderParameters.consideration[i] 253 | ); 254 | } 255 | 256 | // Derive and return the order hash as specified by EIP-712. 257 | 258 | return 259 | keccak256( 260 | abi.encode( 261 | _ORDER_TYPEHASH, 262 | orderParameters.offerer, 263 | orderParameters.zone, 264 | keccak256(abi.encodePacked(offerHashes)), 265 | keccak256(abi.encodePacked(considerationHashes)), 266 | orderParameters.orderType, 267 | orderParameters.startTime, 268 | orderParameters.endTime, 269 | orderParameters.zoneHash, 270 | orderParameters.salt, 271 | orderParameters.conduitKey, 272 | counter 273 | ) 274 | ); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/marketplaces/X2Y2/lib/ECDSA.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts v4.4.1 (utils/cryptography/ECDSA.sol) 3 | 4 | pragma solidity ^0.8.0; 5 | 6 | import "./Strings.sol"; 7 | 8 | /** 9 | * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. 10 | * 11 | * These functions can be used to verify that a message was signed by the holder 12 | * of the private keys of a given address. 13 | */ 14 | library ECDSA { 15 | enum RecoverError { 16 | NoError, 17 | InvalidSignature, 18 | InvalidSignatureLength, 19 | InvalidSignatureS, 20 | InvalidSignatureV 21 | } 22 | 23 | function _throwError(RecoverError error) private pure { 24 | if (error == RecoverError.NoError) { 25 | return; // no error: do nothing 26 | } else if (error == RecoverError.InvalidSignature) { 27 | revert("ECDSA: invalid signature"); 28 | } else if (error == RecoverError.InvalidSignatureLength) { 29 | revert("ECDSA: invalid signature length"); 30 | } else if (error == RecoverError.InvalidSignatureS) { 31 | revert("ECDSA: invalid signature 's' value"); 32 | } else if (error == RecoverError.InvalidSignatureV) { 33 | revert("ECDSA: invalid signature 'v' value"); 34 | } 35 | } 36 | 37 | /** 38 | * @dev Returns the address that signed a hashed message (`hash`) with 39 | * `signature` or error string. This address can then be used for verification purposes. 40 | * 41 | * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: 42 | * this function rejects them by requiring the `s` value to be in the lower 43 | * half order, and the `v` value to be either 27 or 28. 44 | * 45 | * IMPORTANT: `hash` _must_ be the result of a hash operation for the 46 | * verification to be secure: it is possible to craft signatures that 47 | * recover to arbitrary addresses for non-hashed data. A safe way to ensure 48 | * this is by receiving a hash of the original message (which may otherwise 49 | * be too long), and then calling {toEthSignedMessageHash} on it. 50 | * 51 | * Documentation for signature generation: 52 | * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] 53 | * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] 54 | * 55 | * _Available since v4.3._ 56 | */ 57 | function tryRecover(bytes32 hash, bytes memory signature) 58 | internal 59 | pure 60 | returns (address, RecoverError) 61 | { 62 | // Check the signature length 63 | // - case 65: r,s,v signature (standard) 64 | // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ 65 | if (signature.length == 65) { 66 | bytes32 r; 67 | bytes32 s; 68 | uint8 v; 69 | // ecrecover takes the signature parameters, and the only way to get them 70 | // currently is to use assembly. 71 | assembly { 72 | r := mload(add(signature, 0x20)) 73 | s := mload(add(signature, 0x40)) 74 | v := byte(0, mload(add(signature, 0x60))) 75 | } 76 | return tryRecover(hash, v, r, s); 77 | } else if (signature.length == 64) { 78 | bytes32 r; 79 | bytes32 vs; 80 | // ecrecover takes the signature parameters, and the only way to get them 81 | // currently is to use assembly. 82 | assembly { 83 | r := mload(add(signature, 0x20)) 84 | vs := mload(add(signature, 0x40)) 85 | } 86 | return tryRecover(hash, r, vs); 87 | } else { 88 | return (address(0), RecoverError.InvalidSignatureLength); 89 | } 90 | } 91 | 92 | /** 93 | * @dev Returns the address that signed a hashed message (`hash`) with 94 | * `signature`. This address can then be used for verification purposes. 95 | * 96 | * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: 97 | * this function rejects them by requiring the `s` value to be in the lower 98 | * half order, and the `v` value to be either 27 or 28. 99 | * 100 | * IMPORTANT: `hash` _must_ be the result of a hash operation for the 101 | * verification to be secure: it is possible to craft signatures that 102 | * recover to arbitrary addresses for non-hashed data. A safe way to ensure 103 | * this is by receiving a hash of the original message (which may otherwise 104 | * be too long), and then calling {toEthSignedMessageHash} on it. 105 | */ 106 | function recover(bytes32 hash, bytes memory signature) 107 | internal 108 | pure 109 | returns (address) 110 | { 111 | (address recovered, RecoverError error) = tryRecover(hash, signature); 112 | _throwError(error); 113 | return recovered; 114 | } 115 | 116 | /** 117 | * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. 118 | * 119 | * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] 120 | * 121 | * _Available since v4.3._ 122 | */ 123 | function tryRecover( 124 | bytes32 hash, 125 | bytes32 r, 126 | bytes32 vs 127 | ) internal pure returns (address, RecoverError) { 128 | bytes32 s; 129 | uint8 v; 130 | assembly { 131 | s := and( 132 | vs, 133 | 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 134 | ) 135 | v := add(shr(255, vs), 27) 136 | } 137 | return tryRecover(hash, v, r, s); 138 | } 139 | 140 | /** 141 | * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. 142 | * 143 | * _Available since v4.2._ 144 | */ 145 | function recover( 146 | bytes32 hash, 147 | bytes32 r, 148 | bytes32 vs 149 | ) internal pure returns (address) { 150 | (address recovered, RecoverError error) = tryRecover(hash, r, vs); 151 | _throwError(error); 152 | return recovered; 153 | } 154 | 155 | /** 156 | * @dev Overload of {ECDSA-tryRecover} that receives the `v`, 157 | * `r` and `s` signature fields separately. 158 | * 159 | * _Available since v4.3._ 160 | */ 161 | function tryRecover( 162 | bytes32 hash, 163 | uint8 v, 164 | bytes32 r, 165 | bytes32 s 166 | ) internal pure returns (address, RecoverError) { 167 | // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature 168 | // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines 169 | // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most 170 | // signatures from current libraries generate a unique signature with an s-value in the lower half order. 171 | // 172 | // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value 173 | // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or 174 | // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept 175 | // these malleable signatures as well. 176 | if ( 177 | uint256(s) > 178 | 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 179 | ) { 180 | return (address(0), RecoverError.InvalidSignatureS); 181 | } 182 | if (v != 27 && v != 28) { 183 | return (address(0), RecoverError.InvalidSignatureV); 184 | } 185 | 186 | // If the signature is valid (and not malleable), return the signer address 187 | address signer = ecrecover(hash, v, r, s); 188 | if (signer == address(0)) { 189 | return (address(0), RecoverError.InvalidSignature); 190 | } 191 | 192 | return (signer, RecoverError.NoError); 193 | } 194 | 195 | /** 196 | * @dev Overload of {ECDSA-recover} that receives the `v`, 197 | * `r` and `s` signature fields separately. 198 | */ 199 | function recover( 200 | bytes32 hash, 201 | uint8 v, 202 | bytes32 r, 203 | bytes32 s 204 | ) internal pure returns (address) { 205 | (address recovered, RecoverError error) = tryRecover(hash, v, r, s); 206 | _throwError(error); 207 | return recovered; 208 | } 209 | 210 | /** 211 | * @dev Returns an Ethereum Signed Message, created from a `hash`. This 212 | * produces hash corresponding to the one signed with the 213 | * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] 214 | * JSON-RPC method as part of EIP-191. 215 | * 216 | * See {recover}. 217 | */ 218 | function toEthSignedMessageHash(bytes32 hash) 219 | internal 220 | pure 221 | returns (bytes32) 222 | { 223 | // 32 is the length in bytes of hash, 224 | // enforced by the type signature above 225 | return 226 | keccak256( 227 | abi.encodePacked("\x19Ethereum Signed Message:\n32", hash) 228 | ); 229 | } 230 | 231 | /** 232 | * @dev Returns an Ethereum Signed Message, created from `s`. This 233 | * produces hash corresponding to the one signed with the 234 | * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] 235 | * JSON-RPC method as part of EIP-191. 236 | * 237 | * See {recover}. 238 | */ 239 | function toEthSignedMessageHash(bytes memory s) 240 | internal 241 | pure 242 | returns (bytes32) 243 | { 244 | return 245 | keccak256( 246 | abi.encodePacked( 247 | "\x19Ethereum Signed Message:\n", 248 | Strings.toString(s.length), 249 | s 250 | ) 251 | ); 252 | } 253 | 254 | /** 255 | * @dev Returns an Ethereum Signed Typed Data, created from a 256 | * `domainSeparator` and a `structHash`. This produces hash corresponding 257 | * to the one signed with the 258 | * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] 259 | * JSON-RPC method as part of EIP-712. 260 | * 261 | * See {recover}. 262 | */ 263 | function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) 264 | internal 265 | pure 266 | returns (bytes32) 267 | { 268 | return 269 | keccak256( 270 | abi.encodePacked("\x19\x01", domainSeparator, structHash) 271 | ); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/marketplaces/looksRare/LooksRareConfig.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import { LooksRareTypeHashes } from "./lib/LooksRareTypeHashes.sol"; 4 | import { OrderTypes } from "./lib/OrderTypes.sol"; 5 | import { ILooksRareExchange } from "./interfaces/ILooksRareExchange.sol"; 6 | import { ICurrencyManager } from "./interfaces/ICurrencyManager.sol"; 7 | import { BaseMarketConfig } from "../../BaseMarketConfig.sol"; 8 | import { TestCallParameters, TestOrderContext, TestOrderPayload, TestItem721, TestItem1155, TestItem20, SetupCall } from "../../Types.sol"; 9 | 10 | contract LooksRareConfig is BaseMarketConfig, LooksRareTypeHashes { 11 | function name() external pure override returns (string memory) { 12 | return "LooksRare"; 13 | } 14 | 15 | function market() public pure override returns (address) { 16 | return address(looksRare); 17 | } 18 | 19 | address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 20 | 21 | ILooksRareExchange internal constant looksRare = 22 | ILooksRareExchange(0x59728544B08AB483533076417FbBB2fD0B17CE3a); 23 | address internal constant looksRareOwner = 24 | 0xA6a96Fa698A6d5aFCEf6E8EFeACAAe7EE43f2486; 25 | 26 | address internal constant fixedPriceStrategy = 27 | 0x56244Bb70CbD3EA9Dc8007399F61dFC065190031; 28 | 29 | ICurrencyManager internal constant currencyManager = 30 | ICurrencyManager(0xC881ADdf409eE2C4b6bBc8B607c2C5CAFaB93d25); 31 | address internal constant currencyManagerOwner = 32 | 0xAa27e4FCCcBF24B1f745cf5b2ECee018E91a5e5e; 33 | 34 | /*////////////////////////////////////////////////////////////// 35 | Generic Helpers 36 | //////////////////////////////////////////////////////////////*/ 37 | 38 | function buildMakerOrder( 39 | bool isOrderAsk, 40 | address maker, 41 | address fungableToken, 42 | uint256 fungableAmount, 43 | address nftToken, 44 | uint256 nftAmount, 45 | uint256 nftTokenId 46 | ) internal view returns (OrderTypes.MakerOrder memory makerOrder) { 47 | makerOrder = OrderTypes.MakerOrder( 48 | isOrderAsk, 49 | maker, 50 | nftToken, 51 | fungableAmount, 52 | nftTokenId, 53 | nftAmount, 54 | fixedPriceStrategy, 55 | fungableToken, 56 | 0, 57 | block.timestamp, 58 | block.timestamp + 1, 59 | 0, 60 | "", 61 | 0, 62 | 0, 63 | 0 64 | ); 65 | (uint8 v, bytes32 r, bytes32 s) = _sign( 66 | maker, 67 | _deriveOrderDigest(makerOrder) 68 | ); 69 | makerOrder.v = v; 70 | makerOrder.r = r; 71 | makerOrder.s = s; 72 | } 73 | 74 | function buildTakerOrder( 75 | address taker, 76 | OrderTypes.MakerOrder memory makerOrder 77 | ) internal pure returns (OrderTypes.TakerOrder memory) { 78 | return 79 | OrderTypes.TakerOrder( 80 | !makerOrder.isOrderAsk, 81 | taker, 82 | makerOrder.price, 83 | makerOrder.tokenId, 84 | 0, 85 | "" 86 | ); 87 | } 88 | 89 | /*////////////////////////////////////////////////////////////// 90 | Setup 91 | //////////////////////////////////////////////////////////////*/ 92 | 93 | function beforeAllPrepareMarketplace(address, address) external override { 94 | buyerNftApprovalTarget = sellerNftApprovalTarget = 0xf42aa99F011A1fA7CDA90E5E98b277E306BcA83e; // ERC721 transfer manager 95 | buyerErc1155ApprovalTarget = sellerErc1155ApprovalTarget = 0xFED24eC7E22f573c2e08AEF55aA6797Ca2b3A051; // ERC1155 transfer manager 96 | buyerErc20ApprovalTarget = sellerErc20ApprovalTarget = address( 97 | looksRare 98 | ); 99 | } 100 | 101 | function beforeAllPrepareMarketplaceCall( 102 | address, 103 | address, 104 | address[] calldata erc20Tokens, 105 | address[] calldata 106 | ) external pure override returns (SetupCall[] memory) { 107 | SetupCall[] memory setupCalls = new SetupCall[](erc20Tokens.length + 1); 108 | for (uint256 i = 0; i < erc20Tokens.length; i++) { 109 | // Whitelist necessary ERC-20 tokens 110 | setupCalls[i] = SetupCall( 111 | currencyManagerOwner, 112 | address(currencyManager), 113 | abi.encodeWithSelector( 114 | ICurrencyManager.addCurrency.selector, 115 | erc20Tokens[i] 116 | ) 117 | ); 118 | } 119 | 120 | // Remove protocol fee 121 | setupCalls[erc20Tokens.length] = SetupCall( 122 | looksRareOwner, 123 | address(looksRare), 124 | abi.encodeWithSelector( 125 | ILooksRareExchange.updateProtocolFeeRecipient.selector, 126 | address(0) 127 | ) 128 | ); 129 | 130 | return setupCalls; 131 | } 132 | 133 | /*////////////////////////////////////////////////////////////// 134 | Test Payload Calls 135 | //////////////////////////////////////////////////////////////*/ 136 | 137 | function getPayload_BuyOfferedERC721WithEther( 138 | TestOrderContext calldata context, 139 | TestItem721 memory nft, 140 | uint256 ethAmount 141 | ) external view override returns (TestOrderPayload memory execution) { 142 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 143 | true, 144 | context.offerer, 145 | WETH, 146 | ethAmount, 147 | nft.token, 148 | 1, 149 | nft.identifier 150 | ); 151 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 152 | context.fulfiller, 153 | makerOrder 154 | ); 155 | 156 | if (context.listOnChain) { 157 | _notImplemented(); 158 | } 159 | execution.executeOrder = TestCallParameters( 160 | address(looksRare), 161 | ethAmount, 162 | abi.encodeWithSelector( 163 | ILooksRareExchange.matchAskWithTakerBidUsingETHAndWETH.selector, 164 | takerOrder, 165 | makerOrder 166 | ) 167 | ); 168 | } 169 | 170 | function getPayload_BuyOfferedERC1155WithEther( 171 | TestOrderContext calldata context, 172 | TestItem1155 calldata nft, 173 | uint256 ethAmount 174 | ) external view override returns (TestOrderPayload memory execution) { 175 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 176 | true, 177 | context.offerer, 178 | WETH, 179 | ethAmount, 180 | nft.token, 181 | nft.amount, 182 | nft.identifier 183 | ); 184 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 185 | context.fulfiller, 186 | makerOrder 187 | ); 188 | 189 | if (context.listOnChain) { 190 | _notImplemented(); 191 | } 192 | execution.executeOrder = TestCallParameters( 193 | address(looksRare), 194 | ethAmount, 195 | abi.encodeWithSelector( 196 | ILooksRareExchange.matchAskWithTakerBidUsingETHAndWETH.selector, 197 | takerOrder, 198 | makerOrder 199 | ) 200 | ); 201 | } 202 | 203 | function getPayload_BuyOfferedERC721WithERC20( 204 | TestOrderContext calldata context, 205 | TestItem721 calldata nft, 206 | TestItem20 calldata erc20 207 | ) external view override returns (TestOrderPayload memory execution) { 208 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 209 | true, 210 | context.offerer, 211 | erc20.token, 212 | erc20.amount, 213 | nft.token, 214 | 1, 215 | nft.identifier 216 | ); 217 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 218 | context.fulfiller, 219 | makerOrder 220 | ); 221 | 222 | if (context.listOnChain) { 223 | _notImplemented(); 224 | } 225 | 226 | execution.executeOrder = TestCallParameters( 227 | address(looksRare), 228 | 0, 229 | abi.encodeWithSelector( 230 | ILooksRareExchange.matchAskWithTakerBid.selector, 231 | takerOrder, 232 | makerOrder 233 | ) 234 | ); 235 | } 236 | 237 | function getPayload_BuyOfferedERC1155WithERC20( 238 | TestOrderContext calldata context, 239 | TestItem1155 calldata nft, 240 | TestItem20 calldata erc20 241 | ) external view override returns (TestOrderPayload memory execution) { 242 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 243 | true, 244 | context.offerer, 245 | erc20.token, 246 | erc20.amount, 247 | nft.token, 248 | nft.amount, 249 | nft.identifier 250 | ); 251 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 252 | context.fulfiller, 253 | makerOrder 254 | ); 255 | 256 | if (context.listOnChain) { 257 | _notImplemented(); 258 | } 259 | 260 | execution.executeOrder = TestCallParameters( 261 | address(looksRare), 262 | 0, 263 | abi.encodeWithSelector( 264 | ILooksRareExchange.matchAskWithTakerBid.selector, 265 | takerOrder, 266 | makerOrder 267 | ) 268 | ); 269 | } 270 | 271 | function getPayload_BuyOfferedERC20WithERC721( 272 | TestOrderContext calldata context, 273 | TestItem20 calldata erc20, 274 | TestItem721 calldata nft 275 | ) external view override returns (TestOrderPayload memory execution) { 276 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 277 | false, 278 | context.offerer, 279 | erc20.token, 280 | erc20.amount, 281 | nft.token, 282 | 1, 283 | nft.identifier 284 | ); 285 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 286 | context.fulfiller, 287 | makerOrder 288 | ); 289 | 290 | if (context.listOnChain) { 291 | _notImplemented(); 292 | } 293 | 294 | execution.executeOrder = TestCallParameters( 295 | address(looksRare), 296 | 0, 297 | abi.encodeWithSelector( 298 | ILooksRareExchange.matchBidWithTakerAsk.selector, 299 | takerOrder, 300 | makerOrder 301 | ) 302 | ); 303 | } 304 | 305 | function getPayload_BuyOfferedERC20WithERC1155( 306 | TestOrderContext calldata context, 307 | TestItem20 calldata erc20, 308 | TestItem1155 calldata nft 309 | ) external view override returns (TestOrderPayload memory execution) { 310 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 311 | false, 312 | context.offerer, 313 | erc20.token, 314 | erc20.amount, 315 | nft.token, 316 | nft.amount, 317 | nft.identifier 318 | ); 319 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 320 | context.fulfiller, 321 | makerOrder 322 | ); 323 | 324 | if (context.listOnChain) { 325 | _notImplemented(); 326 | } 327 | 328 | execution.executeOrder = TestCallParameters( 329 | address(looksRare), 330 | 0, 331 | abi.encodeWithSelector( 332 | ILooksRareExchange.matchBidWithTakerAsk.selector, 333 | takerOrder, 334 | makerOrder 335 | ) 336 | ); 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /src/marketplaces/sudoswap/SudoswapConfig.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "solmate/tokens/ERC20.sol"; 4 | 5 | import { BaseMarketConfig } from "../../BaseMarketConfig.sol"; 6 | import { SetupCall, TestCallParameters, TestOrderContext, TestOrderPayload, TestItem721, TestItem1155, TestItem20 } from "../../Types.sol"; 7 | import { IPair } from "./interfaces/IPair.sol"; 8 | import { IRouter } from "./interfaces/IRouter.sol"; 9 | import { IPairFactory } from "./interfaces/IPairFactory.sol"; 10 | 11 | contract SudoswapConfig is BaseMarketConfig { 12 | IPairFactory constant PAIR_FACTORY = 13 | IPairFactory(0xb16c1342E617A5B6E4b631EB114483FDB289c0A4); 14 | IRouter constant ROUTER = 15 | IRouter(0x5ba23BEAb987463a64BD05575D3D4a947DfDe32E); 16 | address constant LINEAR_CURVE = 0x5B6aC51d9B1CeDE0068a1B26533CAce807f883Ee; 17 | 18 | uint128 constant DELTA = 1; 19 | uint128 constant NFT_PRICE = 100; // the price at which NFTs are being bought/sold 20 | uint128 constant TOKEN_POOL_SPOT_PRICE = NFT_PRICE; 21 | uint128 constant NFT_POOL_SPOT_PRICE = TOKEN_POOL_SPOT_PRICE - DELTA; 22 | 23 | IPair erc20TokenPool; // owned by seller, who sells ERC20 to buy ERC721 24 | IPair ethNftPool; // owned by seller, who sells ERC721 to buy ETH 25 | IPair erc20NftPool; // owned by seller, who sells ERC721 to buy ERC20 26 | address erc20Address; 27 | address erc721Address; 28 | address currentMarket; // the current address that stores the listed ERC721 29 | 30 | IPair[10] ethNftPoolsForDistinct; 31 | 32 | function name() external pure override returns (string memory) { 33 | return "sudoswap"; 34 | } 35 | 36 | function market() public view override returns (address) { 37 | return currentMarket; 38 | } 39 | 40 | function beforeAllPrepareMarketplace(address, address) external override { 41 | buyerNftApprovalTarget = sellerNftApprovalTarget = buyerErc20ApprovalTarget = sellerErc20ApprovalTarget = address( 42 | ROUTER 43 | ); 44 | } 45 | 46 | function beforeAllPrepareMarketplaceCall( 47 | address seller, 48 | address, 49 | address[] calldata erc20Addresses, 50 | address[] calldata erc721Addresses 51 | ) external override returns (SetupCall[] memory setupCalls) { 52 | // record tokens 53 | erc20Address = erc20Addresses[0]; 54 | erc721Address = erc721Addresses[0]; 55 | 56 | // set protocol fee to zero 57 | setupCalls = new SetupCall[](2); 58 | setupCalls[0] = SetupCall({ 59 | sender: PAIR_FACTORY.owner(), 60 | target: address(PAIR_FACTORY), 61 | data: abi.encodeWithSelector( 62 | IPairFactory.changeProtocolFeeMultiplier.selector, 63 | 0 64 | ) 65 | }); 66 | 67 | // whitelist the new router 68 | setupCalls[1] = SetupCall({ 69 | sender: PAIR_FACTORY.owner(), 70 | target: address(PAIR_FACTORY), 71 | data: abi.encodeWithSelector( 72 | IPairFactory.setRouterAllowed.selector, 73 | address(ROUTER), 74 | true 75 | ) 76 | }); 77 | 78 | // deploy pools 79 | uint256[] memory empty; 80 | 81 | erc20TokenPool = PAIR_FACTORY.createPairERC20( 82 | IPairFactory.CreateERC20PairParams({ 83 | token: erc20Addresses[0], 84 | nft: erc721Addresses[0], 85 | bondingCurve: LINEAR_CURVE, 86 | assetRecipient: payable(seller), 87 | poolType: IPairFactory.PoolType.TOKEN, 88 | delta: DELTA, 89 | fee: 0, 90 | spotPrice: TOKEN_POOL_SPOT_PRICE, 91 | initialNFTIDs: empty, 92 | initialTokenBalance: 0 93 | }) 94 | ); 95 | 96 | ethNftPool = PAIR_FACTORY.createPairETH( 97 | erc721Addresses[0], 98 | LINEAR_CURVE, 99 | payable(seller), 100 | IPairFactory.PoolType.NFT, 101 | DELTA, 102 | 0, 103 | NFT_POOL_SPOT_PRICE, 104 | empty 105 | ); 106 | 107 | for (uint256 i = 0; i < 10; i++) { 108 | ethNftPoolsForDistinct[i] = PAIR_FACTORY.createPairETH( 109 | erc721Addresses[0], 110 | LINEAR_CURVE, 111 | payable(seller), 112 | IPairFactory.PoolType.NFT, 113 | DELTA, 114 | 0, 115 | NFT_POOL_SPOT_PRICE + uint128(i), 116 | empty 117 | ); 118 | } 119 | 120 | erc20NftPool = PAIR_FACTORY.createPairERC20( 121 | IPairFactory.CreateERC20PairParams({ 122 | token: erc20Addresses[0], 123 | nft: erc721Addresses[0], 124 | bondingCurve: LINEAR_CURVE, 125 | assetRecipient: payable(seller), 126 | poolType: IPairFactory.PoolType.NFT, 127 | delta: DELTA, 128 | fee: 0, 129 | spotPrice: NFT_POOL_SPOT_PRICE, 130 | initialNFTIDs: empty, 131 | initialTokenBalance: 0 132 | }) 133 | ); 134 | } 135 | 136 | function getPayload_BuyOfferedERC721WithEther( 137 | TestOrderContext calldata context, 138 | TestItem721 memory nft, 139 | uint256 ethAmount 140 | ) external override returns (TestOrderPayload memory execution) { 141 | if (!context.listOnChain || nft.token != erc721Address) 142 | _notImplemented(); 143 | 144 | // update market address so tests know where the ERC721 will be escrowed 145 | currentMarket = address(ethNftPool); 146 | 147 | // construct submitOrder payload 148 | // offerer transfers ERC721 to ethNftPool 149 | execution.submitOrder = TestCallParameters({ 150 | target: nft.token, 151 | value: 0, 152 | data: abi.encodeWithSignature( 153 | "safeTransferFrom(address,address,uint256)", 154 | context.offerer, 155 | address(ethNftPool), 156 | nft.identifier 157 | ) 158 | }); 159 | 160 | // construct executeOrder payload 161 | // fulfiller calls pair directly to swap 162 | uint256[] memory nftIds = new uint256[](1); 163 | nftIds[0] = nft.identifier; 164 | execution.executeOrder = TestCallParameters({ 165 | target: address(ethNftPool), 166 | value: ethAmount, 167 | data: abi.encodeWithSelector( 168 | IPair.swapTokenForSpecificNFTs.selector, 169 | nftIds, 170 | type(uint256).max, 171 | context.fulfiller, 172 | false, 173 | address(0) 174 | ) 175 | }); 176 | } 177 | 178 | function getPayload_BuyOfferedERC721WithERC20( 179 | TestOrderContext calldata context, 180 | TestItem721 calldata nft, 181 | TestItem20 calldata erc20 182 | ) external override returns (TestOrderPayload memory execution) { 183 | if ( 184 | !context.listOnChain || 185 | nft.token != erc721Address || 186 | erc20.token != erc20Address || 187 | erc20.amount != NFT_PRICE 188 | ) _notImplemented(); 189 | 190 | // update market address so tests know where the ERC721 will be escrowed 191 | currentMarket = address(erc20NftPool); 192 | 193 | // construct submitOrder payload 194 | // offerer transfers ERC721 to erc20NftPool 195 | execution.submitOrder = TestCallParameters({ 196 | target: nft.token, 197 | value: 0, 198 | data: abi.encodeWithSignature( 199 | "safeTransferFrom(address,address,uint256)", 200 | context.offerer, 201 | address(erc20NftPool), 202 | nft.identifier 203 | ) 204 | }); 205 | 206 | // construct executeOrder payload 207 | // fulfiller calls router 208 | uint256[] memory nftIds = new uint256[](1); 209 | nftIds[0] = nft.identifier; 210 | IRouter.PairSwapSpecific[] 211 | memory swapList = new IRouter.PairSwapSpecific[](1); 212 | swapList[0] = IRouter.PairSwapSpecific({ 213 | pair: address(erc20NftPool), 214 | nftIds: nftIds 215 | }); 216 | execution.executeOrder = TestCallParameters({ 217 | target: address(ROUTER), 218 | value: 0, 219 | data: abi.encodeWithSelector( 220 | IRouter.swapERC20ForSpecificNFTs.selector, 221 | swapList, 222 | erc20.amount, 223 | context.fulfiller, 224 | type(uint256).max 225 | ) 226 | }); 227 | } 228 | 229 | function getPayload_BuyOfferedERC20WithERC721( 230 | TestOrderContext calldata context, 231 | TestItem20 calldata erc20, 232 | TestItem721 calldata nft 233 | ) external override returns (TestOrderPayload memory execution) { 234 | if ( 235 | !context.listOnChain || 236 | nft.token != erc721Address || 237 | erc20.token != erc20Address || 238 | erc20.amount != NFT_PRICE 239 | ) _notImplemented(); 240 | 241 | // update market address so tests know where the ERC20 will be escrowed 242 | currentMarket = address(erc20TokenPool); 243 | 244 | // construct submitOrder payload 245 | // offerer transfers ERC20 to erc20TokenPool 246 | execution.submitOrder = TestCallParameters({ 247 | target: erc20.token, 248 | value: 0, 249 | data: abi.encodeWithSelector( 250 | ERC20.transfer.selector, 251 | address(erc20TokenPool), 252 | erc20.amount 253 | ) 254 | }); 255 | 256 | // construct executeOrder payload 257 | // fulfiller calls router 258 | uint256[] memory nftIds = new uint256[](1); 259 | nftIds[0] = nft.identifier; 260 | IRouter.PairSwapSpecific[] 261 | memory swapList = new IRouter.PairSwapSpecific[](1); 262 | swapList[0] = IRouter.PairSwapSpecific({ 263 | pair: address(erc20TokenPool), 264 | nftIds: nftIds 265 | }); 266 | execution.executeOrder = TestCallParameters({ 267 | target: address(ROUTER), 268 | value: 0, 269 | data: abi.encodeWithSelector( 270 | IRouter.swapNFTsForToken.selector, 271 | swapList, 272 | 0, 273 | context.fulfiller, 274 | type(uint256).max 275 | ) 276 | }); 277 | } 278 | 279 | function getPayload_BuyOfferedManyERC721WithEther( 280 | TestOrderContext calldata context, 281 | TestItem721[] calldata nfts, 282 | uint256 ethAmount 283 | ) external override returns (TestOrderPayload memory execution) { 284 | if (!context.listOnChain) _notImplemented(); 285 | 286 | // update market address so tests know where the ERC721 will be escrowed 287 | currentMarket = address(ethNftPool); 288 | 289 | address nftAddress = nfts[0].token; 290 | 291 | uint256[] memory ids = new uint256[](nfts.length); 292 | for (uint256 i = 0; i < nfts.length; i++) { 293 | ids[i] = nfts[i].identifier; 294 | } 295 | 296 | // construct submitOrder payload 297 | // offerer transfers 10 ERC721s to ethNftPool 298 | execution.submitOrder = TestCallParameters({ 299 | target: address(ROUTER), 300 | value: 0, 301 | data: abi.encodeWithSignature( 302 | "depositNFTs(address,uint256[],address)", 303 | nftAddress, 304 | ids, 305 | address(ethNftPool) 306 | ) 307 | }); 308 | 309 | // construct executeOrder payload 310 | // fulfiller calls pair directly to swap 311 | execution.executeOrder = TestCallParameters({ 312 | target: address(ethNftPool), 313 | value: ethAmount, 314 | data: abi.encodeWithSelector( 315 | IPair.swapTokenForSpecificNFTs.selector, 316 | ids, 317 | type(uint256).max, 318 | context.fulfiller, 319 | false, 320 | address(0) 321 | ) 322 | }); 323 | } 324 | 325 | function getPayload_BuyOfferedManyERC721WithEtherDistinctOrders( 326 | TestOrderContext[] calldata contexts, 327 | TestItem721[] calldata nfts, 328 | uint256[] calldata ethAmounts 329 | ) external view override returns (TestOrderPayload memory execution) { 330 | 331 | if (!contexts[0].listOnChain) _notImplemented(); 332 | 333 | address[] memory pools = new address[](nfts.length); 334 | uint256[] memory ids = new uint256[](nfts.length); 335 | 336 | for (uint256 i = 0; i < nfts.length; i++) { 337 | pools[i] = address(ethNftPoolsForDistinct[i]); 338 | ids[i] = nfts[i].identifier; 339 | } 340 | 341 | execution.submitOrder = TestCallParameters({ 342 | target: address(ROUTER), 343 | value: 0, 344 | data: abi.encodeWithSignature( 345 | "depositNFTs(address,uint256[],address[])", 346 | nfts[0].token, 347 | ids, 348 | pools 349 | ) 350 | }); 351 | 352 | // construct executeOrder payload 353 | // fulfiller calls router 354 | IRouter.PairSwapSpecific[] 355 | memory swapList = new IRouter.PairSwapSpecific[](nfts.length); 356 | 357 | for (uint256 i = 0; i < nfts.length; i++) { 358 | uint256[] memory singleId = new uint256[](1); 359 | singleId[0] = nfts[i].identifier; 360 | swapList[i] = IRouter.PairSwapSpecific({ 361 | pair: address(ethNftPoolsForDistinct[i]), 362 | nftIds: singleId 363 | }); 364 | } 365 | 366 | uint256 totalEthAmount = 0; 367 | for (uint256 i = 0; i < ethAmounts.length; i++) { 368 | totalEthAmount += ethAmounts[i]; 369 | } 370 | 371 | execution.executeOrder = TestCallParameters({ 372 | target: address(ROUTER), 373 | value: totalEthAmount, 374 | data: abi.encodeWithSelector( 375 | IRouter.swapETHForSpecificNFTs.selector, 376 | swapList, 377 | contexts[0].offerer, 378 | contexts[0].fulfiller, 379 | type(uint256).max 380 | ) 381 | }); 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /src/marketplaces/X2Y2/X2Y2Config.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import { IX2Y2Marketplace } from "./interfaces/IX2Y2Marketplace.sol"; 4 | import { BaseMarketConfig } from "../../BaseMarketConfig.sol"; 5 | import { Market } from "./interfaces/MarketConstants.sol"; 6 | import { X2Y2TypeHashes } from "./lib/X2Y2TypeHashes.sol"; 7 | import { SetupCall, TestCallParameters, TestOrderContext, TestOrderPayload, TestItem721, TestItem1155, TestItem20 } from "../../Types.sol"; 8 | 9 | contract X2Y2Config is BaseMarketConfig, X2Y2TypeHashes { 10 | IX2Y2Marketplace internal constant X2Y2 = 11 | IX2Y2Marketplace(0x74312363e45DCaBA76c59ec49a7Aa8A65a67EeD3); 12 | 13 | address internal constant X2Y2Owner = 14 | 0x5D7CcA9Fb832BBD99C8bD720EbdA39B028648301; 15 | 16 | address internal constant erc721Delegate = 17 | 0xF849de01B080aDC3A814FaBE1E2087475cF2E354; 18 | 19 | address internal X2Y2Signer; 20 | 21 | function name() external pure override returns (string memory) { 22 | return "X2Y2"; 23 | } 24 | 25 | function market() public pure override returns (address) { 26 | return address(X2Y2); 27 | } 28 | 29 | function beforeAllPrepareMarketplace(address, address) external override { 30 | buyerNftApprovalTarget = sellerNftApprovalTarget = erc721Delegate; 31 | buyerErc20ApprovalTarget = sellerErc20ApprovalTarget = address(X2Y2); 32 | } 33 | 34 | function beforeAllPrepareMarketplaceCall( 35 | address seller, 36 | address, 37 | address[] calldata, 38 | address[] calldata 39 | ) external override returns (SetupCall[] memory) { 40 | SetupCall[] memory setupCalls = new SetupCall[](1); 41 | 42 | address[] memory removeSigners = new address[](0); 43 | address[] memory addSigners = new address[](1); 44 | addSigners[0] = seller; 45 | 46 | // Set seller as a signer for X2Y2 47 | setupCalls[0] = SetupCall( 48 | X2Y2Owner, 49 | address(X2Y2), 50 | abi.encodeWithSelector( 51 | IX2Y2Marketplace.updateSigners.selector, 52 | addSigners, 53 | removeSigners 54 | ) 55 | ); 56 | 57 | X2Y2Signer = seller; 58 | 59 | return setupCalls; 60 | } 61 | 62 | function encodeFillOrderDistinctOrders( 63 | TestOrderContext[] calldata contexts, 64 | TestItem721[] memory nfts, 65 | uint256[] memory prices, 66 | address currency, 67 | uint256 intent 68 | ) internal view returns (bytes memory payload, uint256 ethSum) { 69 | Market.RunInput memory input; 70 | 71 | input.shared.user = contexts[0].fulfiller; 72 | input.shared.deadline = block.timestamp + 1; 73 | 74 | Market.Order[] memory orders = new Market.Order[](nfts.length); 75 | Market.SettleDetail[] memory details = new Market.SettleDetail[]( 76 | nfts.length 77 | ); 78 | 79 | for (uint256 i = 0; i < nfts.length; i++) { 80 | { 81 | ethSum += prices[i]; 82 | } 83 | { 84 | orders[i].user = contexts[i].offerer; 85 | orders[i].network = 1; 86 | orders[i].intent = intent; 87 | orders[i].delegateType = 1; 88 | orders[i].deadline = block.timestamp + 1; 89 | orders[i].currency = currency; 90 | 91 | Market.OrderItem[] memory items = new Market.OrderItem[](1); 92 | 93 | Market.Pair[] memory itemPairs = new Market.Pair[](1); 94 | itemPairs[0] = Market.Pair(nfts[i].token, nfts[i].identifier); 95 | 96 | items[0] = Market.OrderItem(prices[i], abi.encode(itemPairs)); 97 | 98 | orders[i].items = items; 99 | 100 | (orders[i].v, orders[i].r, orders[i].s) = _sign( 101 | contexts[i].offerer, 102 | _deriveOrderDigest(orders[i]) 103 | ); 104 | orders[i].signVersion = Market.SIGN_V1; 105 | } 106 | { 107 | details[i].op = intent == Market.INTENT_SELL 108 | ? Market.Op.COMPLETE_SELL_OFFER 109 | : Market.Op.COMPLETE_BUY_OFFER; 110 | details[i].orderIdx = i; 111 | details[i].itemIdx = 0; 112 | details[i].price = prices[i]; 113 | details[i].itemHash = _hashItem(orders[i], orders[i].items[0]); 114 | details[i].executionDelegate = erc721Delegate; 115 | } 116 | } 117 | 118 | input.orders = orders; 119 | input.details = details; 120 | 121 | (input.v, input.r, input.s) = _sign( 122 | X2Y2Signer, 123 | _deriveInputDigest(input) 124 | ); 125 | 126 | payload = abi.encodeWithSelector(IX2Y2Marketplace.run.selector, input); 127 | } 128 | 129 | function encodeFillOrder( 130 | address offerer, 131 | address fulfiller, 132 | TestItem721[] memory nfts, 133 | uint256 price, 134 | address currency, 135 | uint256 intent, 136 | Market.Fee[] memory fees 137 | ) internal view returns (bytes memory) { 138 | Market.RunInput memory input; 139 | 140 | input.shared.user = fulfiller; 141 | input.shared.deadline = block.timestamp + 1; 142 | 143 | Market.Order[] memory orders = new Market.Order[](1); 144 | orders[0].user = offerer; 145 | orders[0].network = 1; 146 | orders[0].intent = intent; 147 | orders[0].delegateType = 1; 148 | orders[0].deadline = block.timestamp + 1; 149 | orders[0].currency = currency; 150 | 151 | Market.OrderItem[] memory items = new Market.OrderItem[](1); 152 | 153 | Market.Pair[] memory itemPairs = new Market.Pair[](nfts.length); 154 | 155 | for (uint256 i = 0; i < nfts.length; i++) { 156 | itemPairs[i] = Market.Pair(nfts[i].token, nfts[i].identifier); 157 | } 158 | 159 | items[0] = Market.OrderItem(price, abi.encode(itemPairs)); 160 | 161 | orders[0].items = items; 162 | 163 | (orders[0].v, orders[0].r, orders[0].s) = _sign( 164 | offerer, 165 | _deriveOrderDigest(orders[0]) 166 | ); 167 | orders[0].signVersion = Market.SIGN_V1; 168 | 169 | input.orders = orders; 170 | 171 | Market.SettleDetail[] memory details = new Market.SettleDetail[](1); 172 | details[0].op = intent == Market.INTENT_SELL 173 | ? Market.Op.COMPLETE_SELL_OFFER 174 | : Market.Op.COMPLETE_BUY_OFFER; 175 | details[0].orderIdx = 0; 176 | details[0].itemIdx = 0; 177 | details[0].price = price; 178 | details[0].fees = fees; 179 | details[0].itemHash = _hashItem(orders[0], orders[0].items[0]); 180 | details[0].executionDelegate = erc721Delegate; 181 | input.details = details; 182 | 183 | (input.v, input.r, input.s) = _sign( 184 | X2Y2Signer, 185 | _deriveInputDigest(input) 186 | ); 187 | 188 | return abi.encodeWithSelector(IX2Y2Marketplace.run.selector, input); 189 | } 190 | 191 | function getPayload_BuyOfferedERC721WithEther( 192 | TestOrderContext calldata context, 193 | TestItem721 calldata nft, 194 | uint256 ethAmount 195 | ) external view override returns (TestOrderPayload memory execution) { 196 | if (context.listOnChain) { 197 | _notImplemented(); 198 | } 199 | 200 | TestItem721[] memory nfts = new TestItem721[](1); 201 | nfts[0] = nft; 202 | 203 | Market.Fee[] memory fees = new Market.Fee[](0); 204 | 205 | bytes memory payload = encodeFillOrder( 206 | context.offerer, 207 | context.fulfiller, 208 | nfts, 209 | ethAmount, 210 | address(0), 211 | Market.INTENT_SELL, 212 | fees 213 | ); 214 | 215 | execution.executeOrder = TestCallParameters( 216 | address(X2Y2), 217 | ethAmount, 218 | payload 219 | ); 220 | } 221 | 222 | function getPayload_BuyOfferedERC721WithERC20( 223 | TestOrderContext calldata context, 224 | TestItem721 calldata nft, 225 | TestItem20 calldata erc20 226 | ) external view override returns (TestOrderPayload memory execution) { 227 | if (context.listOnChain) { 228 | _notImplemented(); 229 | } 230 | 231 | TestItem721[] memory nfts = new TestItem721[](1); 232 | nfts[0] = nft; 233 | 234 | Market.Fee[] memory fees = new Market.Fee[](0); 235 | 236 | bytes memory payload = encodeFillOrder( 237 | context.offerer, 238 | context.fulfiller, 239 | nfts, 240 | erc20.amount, 241 | erc20.token, 242 | Market.INTENT_SELL, 243 | fees 244 | ); 245 | 246 | execution.executeOrder = TestCallParameters(address(X2Y2), 0, payload); 247 | } 248 | 249 | function getPayload_BuyOfferedERC20WithERC721( 250 | TestOrderContext calldata context, 251 | TestItem20 calldata erc20, 252 | TestItem721 calldata nft 253 | ) external view override returns (TestOrderPayload memory execution) { 254 | if (context.listOnChain) { 255 | _notImplemented(); 256 | } 257 | 258 | TestItem721[] memory nfts = new TestItem721[](1); 259 | nfts[0] = nft; 260 | 261 | Market.Fee[] memory fees = new Market.Fee[](0); 262 | 263 | bytes memory payload = encodeFillOrder( 264 | context.offerer, 265 | context.fulfiller, 266 | nfts, 267 | erc20.amount, 268 | erc20.token, 269 | Market.INTENT_BUY, 270 | fees 271 | ); 272 | 273 | execution.executeOrder = TestCallParameters(address(X2Y2), 0, payload); 274 | } 275 | 276 | function getPayload_BuyOfferedERC721WithEtherOneFeeRecipient( 277 | TestOrderContext calldata context, 278 | TestItem721 memory nft, 279 | uint256 priceEthAmount, 280 | address feeRecipient, 281 | uint256 feeEthAmount 282 | ) external view override returns (TestOrderPayload memory execution) { 283 | if (context.listOnChain) { 284 | _notImplemented(); 285 | } 286 | 287 | TestItem721[] memory nfts = new TestItem721[](1); 288 | nfts[0] = nft; 289 | 290 | Market.Fee[] memory fees = new Market.Fee[](1); 291 | fees[0] = Market.Fee( 292 | (feeEthAmount * 1000000) / (priceEthAmount + feeEthAmount) + 1, 293 | feeRecipient 294 | ); 295 | 296 | bytes memory payload = encodeFillOrder( 297 | context.offerer, 298 | context.fulfiller, 299 | nfts, 300 | priceEthAmount + feeEthAmount, 301 | address(0), 302 | Market.INTENT_SELL, 303 | fees 304 | ); 305 | 306 | execution.executeOrder = TestCallParameters( 307 | address(X2Y2), 308 | priceEthAmount + feeEthAmount, 309 | payload 310 | ); 311 | } 312 | 313 | function getPayload_BuyOfferedERC721WithEtherTwoFeeRecipient( 314 | TestOrderContext calldata context, 315 | TestItem721 memory nft, 316 | uint256 priceEthAmount, 317 | address feeRecipient1, 318 | uint256 feeEthAmount1, 319 | address feeRecipient2, 320 | uint256 feeEthAmount2 321 | ) external view override returns (TestOrderPayload memory execution) { 322 | if (context.listOnChain) { 323 | _notImplemented(); 324 | } 325 | 326 | TestItem721[] memory nfts = new TestItem721[](1); 327 | nfts[0] = nft; 328 | 329 | Market.Fee[] memory fees = new Market.Fee[](2); 330 | fees[0] = Market.Fee( 331 | (feeEthAmount1 * 1000000) / 332 | (priceEthAmount + feeEthAmount1 + feeEthAmount2) + 333 | 1, 334 | feeRecipient1 335 | ); 336 | fees[1] = Market.Fee( 337 | (feeEthAmount2 * 1000000) / 338 | (priceEthAmount + feeEthAmount1 + feeEthAmount2) + 339 | 1, 340 | feeRecipient2 341 | ); 342 | 343 | bytes memory payload = encodeFillOrder( 344 | context.offerer, 345 | context.fulfiller, 346 | nfts, 347 | priceEthAmount + feeEthAmount1 + feeEthAmount2, 348 | address(0), 349 | Market.INTENT_SELL, 350 | fees 351 | ); 352 | 353 | execution.executeOrder = TestCallParameters( 354 | address(X2Y2), 355 | priceEthAmount + feeEthAmount1 + feeEthAmount2, 356 | payload 357 | ); 358 | } 359 | 360 | function getPayload_BuyOfferedManyERC721WithEther( 361 | TestOrderContext calldata context, 362 | TestItem721[] calldata nfts, 363 | uint256 ethAmount 364 | ) external view override returns (TestOrderPayload memory execution) { 365 | if (context.listOnChain) { 366 | _notImplemented(); 367 | } 368 | 369 | Market.Fee[] memory fees = new Market.Fee[](0); 370 | 371 | bytes memory payload = encodeFillOrder( 372 | context.offerer, 373 | context.fulfiller, 374 | nfts, 375 | ethAmount, 376 | address(0), 377 | Market.INTENT_SELL, 378 | fees 379 | ); 380 | 381 | execution.executeOrder = TestCallParameters( 382 | address(X2Y2), 383 | ethAmount, 384 | payload 385 | ); 386 | } 387 | 388 | function getPayload_BuyOfferedManyERC721WithEtherDistinctOrders( 389 | TestOrderContext[] calldata contexts, 390 | TestItem721[] calldata nfts, 391 | uint256[] calldata ethAmounts 392 | ) external view override returns (TestOrderPayload memory execution) { 393 | (bytes memory payload, uint256 ethSum) = encodeFillOrderDistinctOrders( 394 | contexts, 395 | nfts, 396 | ethAmounts, 397 | address(0), 398 | Market.INTENT_SELL 399 | ); 400 | 401 | execution.executeOrder = TestCallParameters( 402 | address(X2Y2), 403 | ethSum, 404 | payload 405 | ); 406 | } 407 | 408 | function getPayload_BuyOfferedManyERC721WithErc20DistinctOrders( 409 | TestOrderContext[] calldata contexts, 410 | address erc20Address, 411 | TestItem721[] calldata nfts, 412 | uint256[] calldata erc20Amounts 413 | ) external view override returns (TestOrderPayload memory execution) { 414 | (bytes memory payload, ) = encodeFillOrderDistinctOrders( 415 | contexts, 416 | nfts, 417 | erc20Amounts, 418 | erc20Address, 419 | Market.INTENT_SELL 420 | ); 421 | 422 | execution.executeOrder = TestCallParameters(address(X2Y2), 0, payload); 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /src/BaseMarketConfig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | import { SetupCall, TestOrderPayload, TestOrderContext, TestCallParameters, TestItem20, TestItem721, TestItem1155 } from "./Types.sol"; 5 | 6 | abstract contract BaseMarketConfig { 7 | /** 8 | * @dev Market name used in results 9 | */ 10 | function name() external pure virtual returns (string memory); 11 | 12 | function market() public view virtual returns (address); 13 | 14 | /** 15 | * @dev Address that should be approved for nft tokens 16 | * (ERC721 and ERC1155). Should be set during `beforeAllPrepareMarketplace`. 17 | */ 18 | address public sellerNftApprovalTarget; 19 | address public buyerNftApprovalTarget; 20 | 21 | /** 22 | * @dev Address that should be approved for ERC1155 tokens. Only set if 23 | * different than ERC721 which is defined above. Set during `beforeAllPrepareMarketplace`. 24 | */ 25 | address public sellerErc1155ApprovalTarget; 26 | address public buyerErc1155ApprovalTarget; 27 | 28 | /** 29 | * @dev Address that should be approved for erc20 tokens. 30 | * Should be set during `beforeAllPrepareMarketplace`. 31 | */ 32 | address public sellerErc20ApprovalTarget; 33 | address public buyerErc20ApprovalTarget; 34 | 35 | /** 36 | * @dev Get calldata to call from test prior to starting tests 37 | * (used by wyvern to create proxies) 38 | * @param seller The seller address used for testing the marketplace 39 | * @param buyer The buyer address used for testing the marketplace 40 | * @return From address, to address, and calldata 41 | */ 42 | function beforeAllPrepareMarketplaceCall( 43 | address seller, 44 | address buyer, 45 | address[] calldata erc20Tokens, 46 | address[] calldata erc721Tokens 47 | ) external virtual returns (SetupCall[] memory) { 48 | SetupCall[] memory empty = new SetupCall[](0); 49 | return empty; 50 | } 51 | 52 | /** 53 | * @dev Final setup prior to starting tests 54 | * @param seller The seller address used for testing the marketplace 55 | * @param buyer The buyer address used for testing the marketplace 56 | */ 57 | function beforeAllPrepareMarketplace(address seller, address buyer) 58 | external 59 | virtual; 60 | 61 | /*////////////////////////////////////////////////////////////// 62 | Test Payload Calls 63 | //////////////////////////////////////////////////////////////*/ 64 | 65 | /** 66 | * @dev Get call parameters to execute an order selling a 721 token for Ether. 67 | * If `context.listOnChain` is true and marketplace does not support on-chain 68 | * listing, this function must revert with NotImplemented. 69 | * @param context Order context, including the buyer and seller and whether the 70 | * order should be listed on chain. 71 | * @param nft Address and ID for ERC721 token to be sold. 72 | * @param ethAmount Amount of Ether to be received for the NFT. 73 | */ 74 | function getPayload_BuyOfferedERC721WithEther( 75 | TestOrderContext calldata context, 76 | TestItem721 calldata nft, 77 | uint256 ethAmount 78 | ) external virtual returns (TestOrderPayload memory execution) { 79 | _notImplemented(); 80 | } 81 | 82 | /** 83 | * @dev Get call parameters to execute an order selling an 1155 token for Ether. 84 | * If `context.listOnChain` is true and marketplace does not support on-chain 85 | * listing, this function must revert with NotImplemented. 86 | * @param context Order context, including the buyer and seller and whether the 87 | * order should be listed on chain. 88 | * @param nft Address, ID and amount for ERC1155 token to be sold. 89 | * @param ethAmount Amount of Ether to be received for the NFT. 90 | */ 91 | function getPayload_BuyOfferedERC1155WithEther( 92 | TestOrderContext calldata context, 93 | TestItem1155 calldata nft, 94 | uint256 ethAmount 95 | ) external view virtual returns (TestOrderPayload memory execution) { 96 | _notImplemented(); 97 | } 98 | 99 | /** 100 | * @dev Get call parameters to execute an order selling a 721 token for an ERC20. 101 | * If `context.listOnChain` is true and marketplace does not support on-chain 102 | * listing, this function must revert with NotImplemented. 103 | * @param context Order context, including the buyer and seller and whether the 104 | * order should be listed on chain. 105 | * @param nft Address and ID of 721 token to be sold. 106 | * @param erc20 Address and amount for ERC20 to be received for nft. 107 | */ 108 | function getPayload_BuyOfferedERC721WithERC20( 109 | TestOrderContext calldata context, 110 | TestItem721 calldata nft, 111 | TestItem20 calldata erc20 112 | ) external virtual returns (TestOrderPayload memory execution) { 113 | _notImplemented(); 114 | } 115 | 116 | /** 117 | * @dev Get call parameters to execute an order selling an 1155 token for an ERC20. 118 | * If `context.listOnChain` is true and marketplace does not support on-chain 119 | * listing, this function must revert with NotImplemented. 120 | * @param context Order context, including the buyer and seller and whether the 121 | * order should be listed on chain. 122 | * @param nft Address, ID and amount for ERC1155 token to be sold. 123 | * @param erc20 Address and amount for ERC20 to be received for nft. 124 | */ 125 | function getPayload_BuyOfferedERC1155WithERC20( 126 | TestOrderContext calldata context, 127 | TestItem1155 calldata nft, 128 | TestItem20 calldata erc20 129 | ) external view virtual returns (TestOrderPayload memory execution) { 130 | _notImplemented(); 131 | } 132 | 133 | /** 134 | * @dev Get call parameters to execute an order selling an ERC20 token for an ERC721. 135 | * If `context.listOnChain` is true and marketplace does not support on-chain 136 | * listing, this function must revert with NotImplemented. 137 | * @param context Order context, including the buyer and seller and whether the 138 | * order should be listed on chain. 139 | * @param erc20 Address and amount for ERC20 to be sold. 140 | * @param nft Address and ID for 721 token to be received for ERC20. 141 | */ 142 | function getPayload_BuyOfferedERC20WithERC721( 143 | TestOrderContext calldata context, 144 | TestItem20 calldata erc20, 145 | TestItem721 calldata nft 146 | ) external virtual returns (TestOrderPayload memory execution) { 147 | _notImplemented(); 148 | } 149 | 150 | /** 151 | * @dev Get call parameters to execute an order selling an ERC20 token for an ERC1155. 152 | * If `context.listOnChain` is true and marketplace does not support on-chain 153 | * listing, this function must revert with NotImplemented. 154 | * @param context Order context, including the buyer and seller and whether the 155 | * order should be listed on chain. 156 | * @param erc20 Address and amount for ERC20 to be sold. 157 | * @param nft Address, ID and amount for 1155 token to be received for ERC20. 158 | */ 159 | function getPayload_BuyOfferedERC20WithERC1155( 160 | TestOrderContext calldata context, 161 | TestItem20 calldata erc20, 162 | TestItem1155 calldata nft 163 | ) external view virtual returns (TestOrderPayload memory execution) { 164 | _notImplemented(); 165 | } 166 | 167 | /** 168 | * @dev Get call parameters to execute an order selling an ERC721 token for an ERC1155. 169 | * If `context.listOnChain` is true and marketplace does not support on-chain 170 | * listing, this function must revert with NotImplemented. 171 | * @param context Order context, including the buyer and seller and whether the 172 | * order should be listed on chain. 173 | * @param sellNft Address and ID of 721 token to be sold. 174 | * @param buyNft Address, ID and amount of 1155 token to be received for ERC721. 175 | */ 176 | function getPayload_BuyOfferedERC721WithERC1155( 177 | TestOrderContext calldata context, 178 | TestItem721 calldata sellNft, 179 | TestItem1155 calldata buyNft 180 | ) external view virtual returns (TestOrderPayload memory execution) { 181 | _notImplemented(); 182 | } 183 | 184 | /** 185 | * @dev Get call parameters to execute an order selling an ERC1155 token for an ERC721. 186 | * If `context.listOnChain` is true and marketplace does not support on-chain 187 | * listing, this function must revert with NotImplemented. 188 | * @param context Order context, including the buyer and seller and whether the 189 | * order should be listed on chain. 190 | * @param sellNft Address and ID of 1155 token to be sold. 191 | * @param buyNft Address, ID and amount of 721 token to be received for ERC1155. 192 | */ 193 | function getPayload_BuyOfferedERC1155WithERC721( 194 | TestOrderContext calldata context, 195 | TestItem1155 calldata sellNft, 196 | TestItem721 calldata buyNft 197 | ) external view virtual returns (TestOrderPayload memory execution) { 198 | _notImplemented(); 199 | } 200 | 201 | /** 202 | * @dev Get call parameters to execute an order selling a 721 token for Ether with one fee recipient. 203 | * If `context.listOnChain` is true and marketplace does not support on-chain 204 | * listing, this function must revert with NotImplemented. 205 | * @param context Order context, including the buyer and seller and whether the 206 | * order should be listed on chain. 207 | * @param nft Address and ID for ERC721 token to be sold. 208 | * @param priceEthAmount Amount of Ether to be received for the NFT. 209 | * @param feeRecipient Address to send fee to. 210 | * @param feeEthAmount Amount of Ether to send for fee. 211 | */ 212 | function getPayload_BuyOfferedERC721WithEtherOneFeeRecipient( 213 | TestOrderContext calldata context, 214 | TestItem721 memory nft, 215 | uint256 priceEthAmount, 216 | address feeRecipient, 217 | uint256 feeEthAmount 218 | ) external view virtual returns (TestOrderPayload memory execution) { 219 | _notImplemented(); 220 | } 221 | 222 | /** 223 | * @dev Get call parameters to execute an order selling a 721 token for Ether with two fee recipients. 224 | * If `context.listOnChain` is true and marketplace does not support on-chain 225 | * listing, this function must revert with NotImplemented. 226 | * @param context Order context, including the buyer and seller and whether the 227 | * order should be listed on chain. 228 | * @param nft Address and ID for ERC721 token to be sold. 229 | * @param priceEthAmount Amount of Ether to be received for the NFT. 230 | * @param feeRecipient1 Address to send first fee to. 231 | * @param feeEthAmount1 Amount of Ether to send for first fee. 232 | * @param feeRecipient2 Address to send second fee to. 233 | * @param feeEthAmount2 Amount of Ether to send for second fee. 234 | */ 235 | function getPayload_BuyOfferedERC721WithEtherTwoFeeRecipient( 236 | TestOrderContext calldata context, 237 | TestItem721 memory nft, 238 | uint256 priceEthAmount, 239 | address feeRecipient1, 240 | uint256 feeEthAmount1, 241 | address feeRecipient2, 242 | uint256 feeEthAmount2 243 | ) external view virtual returns (TestOrderPayload memory execution) { 244 | _notImplemented(); 245 | } 246 | 247 | /** 248 | * @dev Get call parameters to execute an order selling many 721 tokens for Ether. 249 | * If `context.listOnChain` is true and marketplace does not support on-chain 250 | * listing, this function must revert with NotImplemented. 251 | * @param context Order context, including the buyer and seller and whether the 252 | * order should be listed on chain. 253 | * @param nfts Array of Address and ID for ERC721 tokens to be sold. 254 | * @param ethAmount Amount of Ether to be received for the NFT. 255 | */ 256 | function getPayload_BuyOfferedManyERC721WithEther( 257 | TestOrderContext calldata context, 258 | TestItem721[] calldata nfts, 259 | uint256 ethAmount 260 | ) external virtual returns (TestOrderPayload memory execution) { 261 | _notImplemented(); 262 | } 263 | 264 | /** 265 | * @dev Get call parameters to execute an order "sweeping the floor" buy filling 10 distinct 266 | * ERC-721->ETH orders at once. Same seller on each order. If the market does not support the 267 | * order type, must revert with NotImplemented. 268 | * @param contexts Array of contexts for each order 269 | * @param nfts Array of NFTs for each order 270 | * @param ethAmounts Array of Ether emounts to be received for the NFTs in each order 271 | */ 272 | function getPayload_BuyOfferedManyERC721WithEtherDistinctOrders( 273 | TestOrderContext[] calldata contexts, 274 | TestItem721[] calldata nfts, 275 | uint256[] calldata ethAmounts 276 | ) external view virtual returns (TestOrderPayload memory execution) { 277 | _notImplemented(); 278 | } 279 | 280 | /** 281 | * @dev Get call parameters to execute an order "sweeping the floor" buy filling 10 distinct 282 | * ERC-721->ETH orders at once. Same seller on each order. If the market does not support the 283 | * order type, must revert with NotImplemented. 284 | * @param contexts Array of contexts for each order 285 | * @param erc20Address The erc20 address to use across orders 286 | * @param nfts Array of NFTs for each order 287 | * @param erc20Amounts Array of Erc20 amounts to be received for the NFTs in each order 288 | */ 289 | function getPayload_BuyOfferedManyERC721WithErc20DistinctOrders( 290 | TestOrderContext[] calldata contexts, 291 | address erc20Address, 292 | TestItem721[] calldata nfts, 293 | uint256[] calldata erc20Amounts 294 | ) external view virtual returns (TestOrderPayload memory execution) { 295 | _notImplemented(); 296 | } 297 | 298 | /*////////////////////////////////////////////////////////////// 299 | Helpers 300 | //////////////////////////////////////////////////////////////*/ 301 | ITestRunner private _tester; 302 | error NotImplemented(); 303 | 304 | /** 305 | * @dev Revert if the type of requested order is impossible 306 | * to execute for a marketplace. 307 | */ 308 | function _notImplemented() internal pure { 309 | revert NotImplemented(); 310 | } 311 | 312 | constructor() { 313 | _tester = ITestRunner(msg.sender); 314 | } 315 | 316 | /** 317 | * @dev Request a signature from the testing contract. 318 | */ 319 | function _sign(address signer, bytes32 digest) 320 | internal 321 | view 322 | returns ( 323 | uint8, 324 | bytes32, 325 | bytes32 326 | ) 327 | { 328 | return _tester.signDigest(signer, digest); 329 | } 330 | } 331 | 332 | interface ITestRunner { 333 | function signDigest(address signer, bytes32 digest) 334 | external 335 | view 336 | returns ( 337 | uint8, 338 | bytes32, 339 | bytes32 340 | ); 341 | } 342 | -------------------------------------------------------------------------------- /src/marketplaces/seaport/interfaces/ConsiderationInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | // prettier-ignore 5 | import { 6 | BasicOrderParameters, 7 | OrderComponents, 8 | Fulfillment, 9 | FulfillmentComponent, 10 | Execution, 11 | Order, 12 | AdvancedOrder, 13 | OrderStatus, 14 | CriteriaResolver 15 | } from "../lib/ConsiderationStructs.sol"; 16 | 17 | /** 18 | * @title ConsiderationInterface 19 | * @author 0age 20 | * @custom:version 1.1 21 | * @notice Consideration is a generalized ETH/ERC20/ERC721/ERC1155 marketplace. 22 | * It minimizes external calls to the greatest extent possible and 23 | * provides lightweight methods for common routes as well as more 24 | * flexible methods for composing advanced orders. 25 | * 26 | * @dev ConsiderationInterface contains all external function interfaces for 27 | * Consideration. 28 | */ 29 | interface ConsiderationInterface { 30 | /** 31 | * @notice Fulfill an order offering an ERC721 token by supplying Ether (or 32 | * the native token for the given chain) as consideration for the 33 | * order. An arbitrary number of "additional recipients" may also be 34 | * supplied which will each receive native tokens from the fulfiller 35 | * as consideration. 36 | * 37 | * @param parameters Additional information on the fulfilled order. Note 38 | * that the offerer must first approve this contract (or 39 | * their preferred conduit if indicated by the order) for 40 | * their offered ERC721 token to be transferred. 41 | * 42 | * @return fulfilled A boolean indicating whether the order has been 43 | * successfully fulfilled. 44 | */ 45 | function fulfillBasicOrder(BasicOrderParameters calldata parameters) 46 | external 47 | payable 48 | returns (bool fulfilled); 49 | 50 | /** 51 | * @notice Fulfill an order with an arbitrary number of items for offer and 52 | * consideration. Note that this function does not support 53 | * criteria-based orders or partial filling of orders (though 54 | * filling the remainder of a partially-filled order is supported). 55 | * 56 | * @param order The order to fulfill. Note that both the 57 | * offerer and the fulfiller must first approve 58 | * this contract (or the corresponding conduit if 59 | * indicated) to transfer any relevant tokens on 60 | * their behalf and that contracts must implement 61 | * `onERC1155Received` to receive ERC1155 tokens 62 | * as consideration. 63 | * @param fulfillerConduitKey A bytes32 value indicating what conduit, if 64 | * any, to source the fulfiller's token approvals 65 | * from. The zero hash signifies that no conduit 66 | * should be used, with direct approvals set on 67 | * Consideration. 68 | * 69 | * @return fulfilled A boolean indicating whether the order has been 70 | * successfully fulfilled. 71 | */ 72 | function fulfillOrder(Order calldata order, bytes32 fulfillerConduitKey) 73 | external 74 | payable 75 | returns (bool fulfilled); 76 | 77 | /** 78 | * @notice Fill an order, fully or partially, with an arbitrary number of 79 | * items for offer and consideration alongside criteria resolvers 80 | * containing specific token identifiers and associated proofs. 81 | * 82 | * @param advancedOrder The order to fulfill along with the fraction 83 | * of the order to attempt to fill. Note that 84 | * both the offerer and the fulfiller must first 85 | * approve this contract (or their preferred 86 | * conduit if indicated by the order) to transfer 87 | * any relevant tokens on their behalf and that 88 | * contracts must implement `onERC1155Received` 89 | * to receive ERC1155 tokens as consideration. 90 | * Also note that all offer and consideration 91 | * components must have no remainder after 92 | * multiplication of the respective amount with 93 | * the supplied fraction for the partial fill to 94 | * be considered valid. 95 | * @param criteriaResolvers An array where each element contains a 96 | * reference to a specific offer or 97 | * consideration, a token identifier, and a proof 98 | * that the supplied token identifier is 99 | * contained in the merkle root held by the item 100 | * in question's criteria element. Note that an 101 | * empty criteria indicates that any 102 | * (transferable) token identifier on the token 103 | * in question is valid and that no associated 104 | * proof needs to be supplied. 105 | * @param fulfillerConduitKey A bytes32 value indicating what conduit, if 106 | * any, to source the fulfiller's token approvals 107 | * from. The zero hash signifies that no conduit 108 | * should be used, with direct approvals set on 109 | * Consideration. 110 | * @param recipient The intended recipient for all received items, 111 | * with `address(0)` indicating that the caller 112 | * should receive the items. 113 | * 114 | * @return fulfilled A boolean indicating whether the order has been 115 | * successfully fulfilled. 116 | */ 117 | function fulfillAdvancedOrder( 118 | AdvancedOrder calldata advancedOrder, 119 | CriteriaResolver[] calldata criteriaResolvers, 120 | bytes32 fulfillerConduitKey, 121 | address recipient 122 | ) external payable returns (bool fulfilled); 123 | 124 | /** 125 | * @notice Attempt to fill a group of orders, each with an arbitrary number 126 | * of items for offer and consideration. Any order that is not 127 | * currently active, has already been fully filled, or has been 128 | * cancelled will be omitted. Remaining offer and consideration 129 | * items will then be aggregated where possible as indicated by the 130 | * supplied offer and consideration component arrays and aggregated 131 | * items will be transferred to the fulfiller or to each intended 132 | * recipient, respectively. Note that a failing item transfer or an 133 | * issue with order formatting will cause the entire batch to fail. 134 | * Note that this function does not support criteria-based orders or 135 | * partial filling of orders (though filling the remainder of a 136 | * partially-filled order is supported). 137 | * 138 | * @param orders The orders to fulfill. Note that both 139 | * the offerer and the fulfiller must first 140 | * approve this contract (or the 141 | * corresponding conduit if indicated) to 142 | * transfer any relevant tokens on their 143 | * behalf and that contracts must implement 144 | * `onERC1155Received` to receive ERC1155 145 | * tokens as consideration. 146 | * @param offerFulfillments An array of FulfillmentComponent arrays 147 | * indicating which offer items to attempt 148 | * to aggregate when preparing executions. 149 | * @param considerationFulfillments An array of FulfillmentComponent arrays 150 | * indicating which consideration items to 151 | * attempt to aggregate when preparing 152 | * executions. 153 | * @param fulfillerConduitKey A bytes32 value indicating what conduit, 154 | * if any, to source the fulfiller's token 155 | * approvals from. The zero hash signifies 156 | * that no conduit should be used, with 157 | * direct approvals set on this contract. 158 | * @param maximumFulfilled The maximum number of orders to fulfill. 159 | * 160 | * @return availableOrders An array of booleans indicating if each order 161 | * with an index corresponding to the index of the 162 | * returned boolean was fulfillable or not. 163 | * @return executions An array of elements indicating the sequence of 164 | * transfers performed as part of matching the given 165 | * orders. 166 | */ 167 | function fulfillAvailableOrders( 168 | Order[] calldata orders, 169 | FulfillmentComponent[][] calldata offerFulfillments, 170 | FulfillmentComponent[][] calldata considerationFulfillments, 171 | bytes32 fulfillerConduitKey, 172 | uint256 maximumFulfilled 173 | ) 174 | external 175 | payable 176 | returns (bool[] memory availableOrders, Execution[] memory executions); 177 | 178 | /** 179 | * @notice Attempt to fill a group of orders, fully or partially, with an 180 | * arbitrary number of items for offer and consideration per order 181 | * alongside criteria resolvers containing specific token 182 | * identifiers and associated proofs. Any order that is not 183 | * currently active, has already been fully filled, or has been 184 | * cancelled will be omitted. Remaining offer and consideration 185 | * items will then be aggregated where possible as indicated by the 186 | * supplied offer and consideration component arrays and aggregated 187 | * items will be transferred to the fulfiller or to each intended 188 | * recipient, respectively. Note that a failing item transfer or an 189 | * issue with order formatting will cause the entire batch to fail. 190 | * 191 | * @param advancedOrders The orders to fulfill along with the 192 | * fraction of those orders to attempt to 193 | * fill. Note that both the offerer and the 194 | * fulfiller must first approve this 195 | * contract (or their preferred conduit if 196 | * indicated by the order) to transfer any 197 | * relevant tokens on their behalf and that 198 | * contracts must implement 199 | * `onERC1155Received` to enable receipt of 200 | * ERC1155 tokens as consideration. Also 201 | * note that all offer and consideration 202 | * components must have no remainder after 203 | * multiplication of the respective amount 204 | * with the supplied fraction for an 205 | * order's partial fill amount to be 206 | * considered valid. 207 | * @param criteriaResolvers An array where each element contains a 208 | * reference to a specific offer or 209 | * consideration, a token identifier, and a 210 | * proof that the supplied token identifier 211 | * is contained in the merkle root held by 212 | * the item in question's criteria element. 213 | * Note that an empty criteria indicates 214 | * that any (transferable) token 215 | * identifier on the token in question is 216 | * valid and that no associated proof needs 217 | * to be supplied. 218 | * @param offerFulfillments An array of FulfillmentComponent arrays 219 | * indicating which offer items to attempt 220 | * to aggregate when preparing executions. 221 | * @param considerationFulfillments An array of FulfillmentComponent arrays 222 | * indicating which consideration items to 223 | * attempt to aggregate when preparing 224 | * executions. 225 | * @param fulfillerConduitKey A bytes32 value indicating what conduit, 226 | * if any, to source the fulfiller's token 227 | * approvals from. The zero hash signifies 228 | * that no conduit should be used, with 229 | * direct approvals set on this contract. 230 | * @param recipient The intended recipient for all received 231 | * items, with `address(0)` indicating that 232 | * the caller should receive the items. 233 | * @param maximumFulfilled The maximum number of orders to fulfill. 234 | * 235 | * @return availableOrders An array of booleans indicating if each order 236 | * with an index corresponding to the index of the 237 | * returned boolean was fulfillable or not. 238 | * @return executions An array of elements indicating the sequence of 239 | * transfers performed as part of matching the given 240 | * orders. 241 | */ 242 | function fulfillAvailableAdvancedOrders( 243 | AdvancedOrder[] calldata advancedOrders, 244 | CriteriaResolver[] calldata criteriaResolvers, 245 | FulfillmentComponent[][] calldata offerFulfillments, 246 | FulfillmentComponent[][] calldata considerationFulfillments, 247 | bytes32 fulfillerConduitKey, 248 | address recipient, 249 | uint256 maximumFulfilled 250 | ) 251 | external 252 | payable 253 | returns (bool[] memory availableOrders, Execution[] memory executions); 254 | 255 | /** 256 | * @notice Match an arbitrary number of orders, each with an arbitrary 257 | * number of items for offer and consideration along with as set of 258 | * fulfillments allocating offer components to consideration 259 | * components. Note that this function does not support 260 | * criteria-based or partial filling of orders (though filling the 261 | * remainder of a partially-filled order is supported). 262 | * 263 | * @param orders The orders to match. Note that both the offerer and 264 | * fulfiller on each order must first approve this 265 | * contract (or their conduit if indicated by the order) 266 | * to transfer any relevant tokens on their behalf and 267 | * each consideration recipient must implement 268 | * `onERC1155Received` to enable ERC1155 token receipt. 269 | * @param fulfillments An array of elements allocating offer components to 270 | * consideration components. Note that each 271 | * consideration component must be fully met for the 272 | * match operation to be valid. 273 | * 274 | * @return executions An array of elements indicating the sequence of 275 | * transfers performed as part of matching the given 276 | * orders. 277 | */ 278 | function matchOrders( 279 | Order[] calldata orders, 280 | Fulfillment[] calldata fulfillments 281 | ) external payable returns (Execution[] memory executions); 282 | 283 | /** 284 | * @notice Match an arbitrary number of full or partial orders, each with an 285 | * arbitrary number of items for offer and consideration, supplying 286 | * criteria resolvers containing specific token identifiers and 287 | * associated proofs as well as fulfillments allocating offer 288 | * components to consideration components. 289 | * 290 | * @param orders The advanced orders to match. Note that both the 291 | * offerer and fulfiller on each order must first 292 | * approve this contract (or a preferred conduit if 293 | * indicated by the order) to transfer any relevant 294 | * tokens on their behalf and each consideration 295 | * recipient must implement `onERC1155Received` in 296 | * order to receive ERC1155 tokens. Also note that 297 | * the offer and consideration components for each 298 | * order must have no remainder after multiplying 299 | * the respective amount with the supplied fraction 300 | * in order for the group of partial fills to be 301 | * considered valid. 302 | * @param criteriaResolvers An array where each element contains a reference 303 | * to a specific order as well as that order's 304 | * offer or consideration, a token identifier, and 305 | * a proof that the supplied token identifier is 306 | * contained in the order's merkle root. Note that 307 | * an empty root indicates that any (transferable) 308 | * token identifier is valid and that no associated 309 | * proof needs to be supplied. 310 | * @param fulfillments An array of elements allocating offer components 311 | * to consideration components. Note that each 312 | * consideration component must be fully met in 313 | * order for the match operation to be valid. 314 | * 315 | * @return executions An array of elements indicating the sequence of 316 | * transfers performed as part of matching the given 317 | * orders. 318 | */ 319 | function matchAdvancedOrders( 320 | AdvancedOrder[] calldata orders, 321 | CriteriaResolver[] calldata criteriaResolvers, 322 | Fulfillment[] calldata fulfillments 323 | ) external payable returns (Execution[] memory executions); 324 | 325 | /** 326 | * @notice Cancel an arbitrary number of orders. Note that only the offerer 327 | * or the zone of a given order may cancel it. Callers should ensure 328 | * that the intended order was cancelled by calling `getOrderStatus` 329 | * and confirming that `isCancelled` returns `true`. 330 | * 331 | * @param orders The orders to cancel. 332 | * 333 | * @return cancelled A boolean indicating whether the supplied orders have 334 | * been successfully cancelled. 335 | */ 336 | function cancel(OrderComponents[] calldata orders) 337 | external 338 | returns (bool cancelled); 339 | 340 | /** 341 | * @notice Validate an arbitrary number of orders, thereby registering their 342 | * signatures as valid and allowing the fulfiller to skip signature 343 | * verification on fulfillment. Note that validated orders may still 344 | * be unfulfillable due to invalid item amounts or other factors; 345 | * callers should determine whether validated orders are fulfillable 346 | * by simulating the fulfillment call prior to execution. Also note 347 | * that anyone can validate a signed order, but only the offerer can 348 | * validate an order without supplying a signature. 349 | * 350 | * @param orders The orders to validate. 351 | * 352 | * @return validated A boolean indicating whether the supplied orders have 353 | * been successfully validated. 354 | */ 355 | function validate(Order[] calldata orders) 356 | external 357 | returns (bool validated); 358 | 359 | /** 360 | * @notice Cancel all orders from a given offerer with a given zone in bulk 361 | * by incrementing a counter. Note that only the offerer may 362 | * increment the counter. 363 | * 364 | * @return newCounter The new counter. 365 | */ 366 | function incrementCounter() external returns (uint256 newCounter); 367 | 368 | /** 369 | * @notice Retrieve the order hash for a given order. 370 | * 371 | * @param order The components of the order. 372 | * 373 | * @return orderHash The order hash. 374 | */ 375 | function getOrderHash(OrderComponents calldata order) 376 | external 377 | view 378 | returns (bytes32 orderHash); 379 | 380 | /** 381 | * @notice Retrieve the status of a given order by hash, including whether 382 | * the order has been cancelled or validated and the fraction of the 383 | * order that has been filled. 384 | * 385 | * @param orderHash The order hash in question. 386 | * 387 | * @return isValidated A boolean indicating whether the order in question 388 | * has been validated (i.e. previously approved or 389 | * partially filled). 390 | * @return isCancelled A boolean indicating whether the order in question 391 | * has been cancelled. 392 | * @return totalFilled The total portion of the order that has been filled 393 | * (i.e. the "numerator"). 394 | * @return totalSize The total size of the order that is either filled or 395 | * unfilled (i.e. the "denominator"). 396 | */ 397 | function getOrderStatus(bytes32 orderHash) 398 | external 399 | view 400 | returns ( 401 | bool isValidated, 402 | bool isCancelled, 403 | uint256 totalFilled, 404 | uint256 totalSize 405 | ); 406 | 407 | /** 408 | * @notice Retrieve the current counter for a given offerer. 409 | * 410 | * @param offerer The offerer in question. 411 | * 412 | * @return counter The current counter. 413 | */ 414 | function getCounter(address offerer) 415 | external 416 | view 417 | returns (uint256 counter); 418 | 419 | /** 420 | * @notice Retrieve configuration information for this contract. 421 | * 422 | * @return version The contract version. 423 | * @return domainSeparator The domain separator for this contract. 424 | * @return conduitController The conduit Controller set for this contract. 425 | */ 426 | function information() 427 | external 428 | view 429 | returns ( 430 | string memory version, 431 | bytes32 domainSeparator, 432 | address conduitController 433 | ); 434 | 435 | /** 436 | * @notice Retrieve the name of this contract. 437 | * 438 | * @return contractName The name of this contract. 439 | */ 440 | function name() external view returns (string memory contractName); 441 | } 442 | -------------------------------------------------------------------------------- /src/marketplaces/wyvern/WyvernConfig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | import { BaseMarketConfig } from "../../BaseMarketConfig.sol"; 5 | import { SetupCall, TestCallParameters, TestOrderContext, TestOrderPayload, TestItem721, TestItem1155, TestItem20 } from "../../Types.sol"; 6 | import { WyvernInterface as IWyvern } from "./interfaces/WyvernInterface.sol"; 7 | import { IWyvernProxyRegistry } from "./interfaces/IWyvernProxyRegistry.sol"; 8 | import { IERC721 } from "./interfaces/IERC721.sol"; 9 | import { IERC1155 } from "./interfaces/IERC1155.sol"; 10 | import { IAtomicizer } from "./interfaces/IAtomicizer.sol"; 11 | import "./lib/WyvernStructs.sol"; 12 | import "./lib/WyvernEnums.sol"; 13 | import "./lib/WyvernTypeHashes.sol"; 14 | 15 | contract WyvernConfig is BaseMarketConfig, WyvernTypeHashes { 16 | function name() external pure override returns (string memory) { 17 | return "Wyvern"; 18 | } 19 | 20 | function market() public pure override returns (address) { 21 | return address(wyvern); 22 | } 23 | 24 | bytes internal constant EMPTY_BYTES = bytes(""); 25 | 26 | address internal constant NULL_ADDRESS = 27 | 0x0000000000000000000000000000000000000000; 28 | 29 | address internal constant DEFAULT_FEE_RECIPIENT = 30 | 0x0000000000000000000000000000000000000001; 31 | 32 | address internal constant ATOMICIZER = 33 | 0xC99f70bFD82fb7c8f8191fdfbFB735606b15e5c5; 34 | 35 | IWyvern internal constant wyvern = 36 | IWyvern(0x7f268357A8c2552623316e2562D90e642bB538E5); 37 | 38 | IWyvernProxyRegistry internal constant proxyRegistry = 39 | IWyvernProxyRegistry(0xa5409ec958C83C3f309868babACA7c86DCB077c1); 40 | 41 | Sig internal EMPTY_SIG = Sig(0, 0, 0); 42 | 43 | /*////////////////////////////////////////////////////////////// 44 | Generic Helpers 45 | //////////////////////////////////////////////////////////////*/ 46 | 47 | function encodeAtomicMatch( 48 | Order memory buyOrder, 49 | Order memory sellOrder, 50 | Sig memory buySignature, 51 | Sig memory sellSignature 52 | ) internal pure returns (bytes memory) { 53 | return 54 | abi.encodeWithSelector( 55 | IWyvern.atomicMatch_.selector, 56 | [ 57 | buyOrder.exchange, 58 | buyOrder.maker, 59 | buyOrder.taker, 60 | buyOrder.feeRecipient, 61 | buyOrder.target, 62 | NULL_ADDRESS, 63 | buyOrder.paymentToken, 64 | sellOrder.exchange, 65 | sellOrder.maker, 66 | sellOrder.taker, 67 | sellOrder.feeRecipient, 68 | sellOrder.target, 69 | NULL_ADDRESS, 70 | sellOrder.paymentToken 71 | ], 72 | [ 73 | buyOrder.makerRelayerFee, 74 | 0, 75 | 0, 76 | 0, 77 | buyOrder.basePrice, 78 | 0, 79 | buyOrder.listingTime, 80 | buyOrder.expirationTime, 81 | buyOrder.salt, 82 | sellOrder.makerRelayerFee, 83 | 0, 84 | 0, 85 | 0, 86 | sellOrder.basePrice, 87 | 0, 88 | sellOrder.listingTime, 89 | sellOrder.expirationTime, 90 | sellOrder.salt 91 | ], 92 | [ 93 | uint8(buyOrder.feeMethod), 94 | uint8(buyOrder.side), 95 | uint8(buyOrder.saleKind), 96 | uint8(buyOrder.howToCall), 97 | uint8(sellOrder.feeMethod), 98 | uint8(sellOrder.side), 99 | uint8(sellOrder.saleKind), 100 | uint8(sellOrder.howToCall) 101 | ], 102 | buyOrder._calldata, 103 | sellOrder._calldata, 104 | buyOrder.replacementPattern, 105 | sellOrder.replacementPattern, 106 | EMPTY_BYTES, 107 | EMPTY_BYTES, 108 | [buySignature.v, sellSignature.v], 109 | [ 110 | buySignature.r, 111 | buySignature.s, 112 | sellSignature.r, 113 | sellSignature.s, 114 | bytes32(0) 115 | ] 116 | ); 117 | } 118 | 119 | function encodeApproveOrder(Order memory order) 120 | internal 121 | pure 122 | returns (bytes memory) 123 | { 124 | return 125 | abi.encodeWithSelector( 126 | IWyvern.approveOrder_.selector, 127 | [ 128 | order.exchange, 129 | order.maker, 130 | order.taker, 131 | order.feeRecipient, 132 | order.target, 133 | NULL_ADDRESS, 134 | order.paymentToken 135 | ], 136 | [ 137 | order.makerRelayerFee, 138 | 0, 139 | 0, 140 | 0, 141 | order.basePrice, 142 | 0, 143 | order.listingTime, 144 | order.expirationTime, 145 | order.salt 146 | ], 147 | order.feeMethod, 148 | order.side, 149 | order.saleKind, 150 | order.howToCall, 151 | order._calldata, 152 | order.replacementPattern, 153 | EMPTY_BYTES, 154 | true 155 | ); 156 | } 157 | 158 | function buildOrder( 159 | address maker, 160 | address taker, 161 | address paymentToken, 162 | uint256 price, 163 | address target, 164 | address feeRecipient, 165 | uint256 fee, 166 | Side side, 167 | HowToCall howToCall, 168 | bytes memory _calldata, 169 | bytes memory replacementPattern 170 | ) internal view returns (Order memory order, Sig memory signature) { 171 | order.exchange = address(wyvern); 172 | order.maker = maker; 173 | order.taker = taker; 174 | order.makerRelayerFee = fee; 175 | order.feeRecipient = feeRecipient; 176 | order.feeMethod = FeeMethod.SplitFee; 177 | order.side = side; 178 | order.saleKind = SaleKind.FixedPrice; 179 | order.target = target; 180 | order.howToCall = howToCall; 181 | order._calldata = _calldata; 182 | order.replacementPattern = replacementPattern; 183 | order.paymentToken = paymentToken; 184 | order.basePrice = price; 185 | bytes32 digest = _deriveEIP712Digest(hashOrder(order, 0)); 186 | (signature.v, signature.r, signature.s) = _sign(maker, digest); 187 | } 188 | 189 | /*////////////////////////////////////////////////////////////// 190 | ERC721 Helpers 191 | //////////////////////////////////////////////////////////////*/ 192 | 193 | function encodeERC721TransferFrom( 194 | address maker, 195 | address taker, 196 | uint256 identifier 197 | ) internal pure returns (bytes memory) { 198 | return 199 | abi.encodeWithSelector( 200 | IERC721.transferFrom.selector, 201 | maker, 202 | taker, 203 | identifier 204 | ); 205 | } 206 | 207 | function encodeERC721ReplacementPatternSell() 208 | internal 209 | pure 210 | returns (bytes memory) 211 | { 212 | return 213 | abi.encodeWithSelector( 214 | bytes4(0x00000000), 215 | 0x0000000000000000000000000000000000000000000000000000000000000000, 216 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 217 | 0x0000000000000000000000000000000000000000000000000000000000000000 218 | ); 219 | } 220 | 221 | function encodeERC721ReplacementPatternBuy() 222 | internal 223 | pure 224 | returns (bytes memory) 225 | { 226 | return 227 | abi.encodeWithSelector( 228 | bytes4(0x00000000), 229 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 230 | 0x0000000000000000000000000000000000000000000000000000000000000000, 231 | 0x0000000000000000000000000000000000000000000000000000000000000000 232 | ); 233 | } 234 | 235 | function encodeERC721BatchTransferFrom( 236 | address maker, 237 | address taker, 238 | TestItem721[] memory nfts 239 | ) internal view returns (bytes memory) { 240 | address[] memory targets = new address[](nfts.length); 241 | uint256[] memory values = new uint256[](nfts.length); 242 | bytes[] memory calldatas = new bytes[](nfts.length); 243 | uint256[] memory calldataLengths = new uint256[](nfts.length); 244 | uint256 calldataLengthSum = 0; 245 | 246 | for (uint256 i = 0; i < nfts.length; i++) { 247 | calldatas[i] = encodeERC721TransferFrom( 248 | maker, 249 | taker, 250 | nfts[i].identifier 251 | ); 252 | calldataLengths[i] = calldatas[i].length; 253 | targets[i] = nfts[i].token; 254 | calldataLengthSum += calldataLengths[i]; 255 | } 256 | 257 | bytes memory _calldata = new bytes(calldataLengthSum); 258 | uint256 offset = 0; 259 | for (uint256 i = 0; i < calldatas.length; i++) { 260 | for (uint256 j = 0; j < calldataLengths[i]; j++) { 261 | _calldata[offset] = calldatas[i][j]; 262 | offset++; 263 | } 264 | } 265 | 266 | return 267 | abi.encodeWithSelector( 268 | IAtomicizer.atomicize.selector, 269 | targets, 270 | values, 271 | calldataLengths, 272 | _calldata 273 | ); 274 | } 275 | 276 | function buildERC721SellOrder( 277 | address maker, 278 | address taker, 279 | address paymentToken, 280 | uint256 price, 281 | TestItem721 memory nft, 282 | address feeRecipient, 283 | uint256 fee 284 | ) internal view returns (Order memory order, Sig memory signature) { 285 | bytes memory _calldata = encodeERC721TransferFrom( 286 | maker, 287 | NULL_ADDRESS, 288 | nft.identifier 289 | ); 290 | bytes memory replacementPattern = encodeERC721ReplacementPatternSell(); 291 | 292 | return 293 | buildOrder( 294 | maker, 295 | taker, 296 | paymentToken, 297 | price, 298 | nft.token, 299 | feeRecipient, 300 | fee, 301 | Side.Sell, 302 | HowToCall.Call, 303 | _calldata, 304 | replacementPattern 305 | ); 306 | } 307 | 308 | function buildERC721BuyOrder( 309 | address maker, 310 | address taker, 311 | address paymentToken, 312 | uint256 price, 313 | TestItem721 memory nft, 314 | address feeRecipient, 315 | uint256 fee 316 | ) internal view returns (Order memory order, Sig memory signature) { 317 | bytes memory _calldata = encodeERC721TransferFrom( 318 | NULL_ADDRESS, 319 | maker, 320 | nft.identifier 321 | ); 322 | bytes memory replacementPattern = encodeERC721ReplacementPatternBuy(); 323 | 324 | return 325 | buildOrder( 326 | maker, 327 | taker, 328 | paymentToken, 329 | price, 330 | nft.token, 331 | feeRecipient, 332 | fee, 333 | Side.Buy, 334 | HowToCall.Call, 335 | _calldata, 336 | replacementPattern 337 | ); 338 | } 339 | 340 | function buildERC721BatchSellOrder( 341 | address maker, 342 | address taker, 343 | address paymentToken, 344 | uint256 price, 345 | TestItem721[] memory nfts, 346 | address feeRecipient 347 | ) internal view returns (Order memory order, Sig memory signature) { 348 | bytes memory _calldata = encodeERC721BatchTransferFrom( 349 | maker, 350 | taker, 351 | nfts 352 | ); 353 | 354 | return 355 | buildOrder( 356 | maker, 357 | taker, 358 | paymentToken, 359 | price, 360 | ATOMICIZER, 361 | feeRecipient, 362 | 0, 363 | Side.Sell, 364 | HowToCall.DelegateCall, 365 | _calldata, 366 | "" 367 | ); 368 | } 369 | 370 | function buildERC721BatchBuyOrder( 371 | address maker, 372 | address taker, 373 | address paymentToken, 374 | uint256 price, 375 | TestItem721[] memory nfts, 376 | address feeRecipient 377 | ) internal view returns (Order memory order, Sig memory signature) { 378 | bytes memory _calldata = encodeERC721BatchTransferFrom( 379 | taker, 380 | maker, 381 | nfts 382 | ); 383 | 384 | return 385 | buildOrder( 386 | maker, 387 | taker, 388 | paymentToken, 389 | price, 390 | ATOMICIZER, 391 | feeRecipient, 392 | 0, 393 | Side.Buy, 394 | HowToCall.DelegateCall, 395 | _calldata, 396 | "" 397 | ); 398 | } 399 | 400 | /*////////////////////////////////////////////////////////////// 401 | ERC1155 Helpers 402 | //////////////////////////////////////////////////////////////*/ 403 | 404 | function encodeERC1155SafeTransferFrom( 405 | address maker, 406 | address taker, 407 | uint256 identifier, 408 | uint256 amount 409 | ) internal pure returns (bytes memory) { 410 | return 411 | abi.encodeWithSelector( 412 | IERC1155.safeTransferFrom.selector, 413 | maker, 414 | taker, 415 | identifier, 416 | amount, 417 | EMPTY_BYTES 418 | ); 419 | } 420 | 421 | function encodeERC1155ReplacementPatternSell() 422 | internal 423 | pure 424 | returns (bytes memory) 425 | { 426 | bytes memory rawBytes = abi.encodeWithSelector( 427 | IERC1155.safeTransferFrom.selector, 428 | NULL_ADDRESS, 429 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 430 | 0, 431 | 0, 432 | EMPTY_BYTES 433 | ); 434 | rawBytes[0] = 0; 435 | rawBytes[1] = 0; 436 | rawBytes[2] = 0; 437 | rawBytes[3] = 0; 438 | rawBytes[163] = 0; 439 | 440 | return rawBytes; 441 | } 442 | 443 | function encodeERC1155ReplacementPatternBuy() 444 | internal 445 | pure 446 | returns (bytes memory) 447 | { 448 | bytes memory rawBytes = abi.encodeWithSelector( 449 | IERC1155.safeTransferFrom.selector, 450 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 451 | NULL_ADDRESS, 452 | 0, 453 | 0, 454 | EMPTY_BYTES 455 | ); 456 | rawBytes[0] = 0; 457 | rawBytes[1] = 0; 458 | rawBytes[2] = 0; 459 | rawBytes[3] = 0; 460 | rawBytes[163] = 0; 461 | 462 | return rawBytes; 463 | } 464 | 465 | function buildERC1155SellOrder( 466 | address maker, 467 | address taker, 468 | address paymentToken, 469 | uint256 price, 470 | TestItem1155 memory nft, 471 | address feeRecipient, 472 | uint256 fee 473 | ) internal view returns (Order memory order, Sig memory signature) { 474 | bytes memory _calldata = encodeERC1155SafeTransferFrom( 475 | maker, 476 | taker, 477 | nft.identifier, 478 | nft.amount 479 | ); 480 | bytes memory replacementPattern = encodeERC1155ReplacementPatternSell(); 481 | 482 | return 483 | buildOrder( 484 | maker, 485 | taker, 486 | paymentToken, 487 | price, 488 | nft.token, 489 | feeRecipient, 490 | fee, 491 | Side.Sell, 492 | HowToCall.Call, 493 | _calldata, 494 | replacementPattern 495 | ); 496 | } 497 | 498 | function buildERC1155BuyOrder( 499 | address maker, 500 | address taker, 501 | address paymentToken, 502 | uint256 price, 503 | TestItem1155 memory nft, 504 | address feeRecipient, 505 | uint256 fee 506 | ) internal view returns (Order memory order, Sig memory signature) { 507 | bytes memory _calldata = encodeERC1155SafeTransferFrom( 508 | taker, 509 | maker, 510 | nft.identifier, 511 | nft.amount 512 | ); 513 | bytes memory replacementPattern = encodeERC1155ReplacementPatternBuy(); 514 | 515 | return 516 | buildOrder( 517 | maker, 518 | taker, 519 | paymentToken, 520 | price, 521 | nft.token, 522 | feeRecipient, 523 | fee, 524 | Side.Buy, 525 | HowToCall.Call, 526 | _calldata, 527 | replacementPattern 528 | ); 529 | } 530 | 531 | /*////////////////////////////////////////////////////////////// 532 | Setup 533 | //////////////////////////////////////////////////////////////*/ 534 | 535 | function beforeAllPrepareMarketplaceCall( 536 | address seller, 537 | address buyer, 538 | address[] calldata, 539 | address[] calldata 540 | ) external pure override returns (SetupCall[] memory) { 541 | SetupCall[] memory setupCalls = new SetupCall[](2); 542 | setupCalls[0] = SetupCall( 543 | seller, 544 | address(proxyRegistry), 545 | abi.encodeWithSelector(proxyRegistry.registerProxy.selector) 546 | ); 547 | setupCalls[1] = SetupCall( 548 | buyer, 549 | address(proxyRegistry), 550 | abi.encodeWithSelector(proxyRegistry.registerProxy.selector) 551 | ); 552 | return setupCalls; 553 | } 554 | 555 | function beforeAllPrepareMarketplace(address seller, address buyer) 556 | external 557 | override 558 | { 559 | // Create Wyvern Proxy 560 | buyerErc20ApprovalTarget = sellerErc20ApprovalTarget = 0xE5c783EE536cf5E63E792988335c4255169be4E1; 561 | buyerNftApprovalTarget = proxyRegistry.proxies(buyer); 562 | sellerNftApprovalTarget = proxyRegistry.proxies(seller); 563 | } 564 | 565 | /*////////////////////////////////////////////////////////////// 566 | Test Payload Calls 567 | //////////////////////////////////////////////////////////////*/ 568 | 569 | function getPayload_BuyOfferedERC721WithEther( 570 | TestOrderContext calldata context, 571 | TestItem721 memory nft, 572 | uint256 ethAmount 573 | ) external view override returns (TestOrderPayload memory execution) { 574 | ( 575 | Order memory sellOrder, 576 | Sig memory sellSignature 577 | ) = buildERC721SellOrder( 578 | context.offerer, 579 | NULL_ADDRESS, 580 | 0x0000000000000000000000000000000000000000, 581 | ethAmount, 582 | nft, 583 | DEFAULT_FEE_RECIPIENT, 584 | 0 585 | ); 586 | (Order memory buyOrder, ) = buildERC721BuyOrder( 587 | context.fulfiller, 588 | NULL_ADDRESS, 589 | 0x0000000000000000000000000000000000000000, 590 | ethAmount, 591 | nft, 592 | NULL_ADDRESS, 593 | 0 594 | ); 595 | if (context.listOnChain) { 596 | execution.submitOrder = TestCallParameters( 597 | address(wyvern), 598 | 0, 599 | encodeApproveOrder(sellOrder) 600 | ); 601 | sellSignature.v = 0; 602 | sellSignature.r = 0; 603 | sellSignature.s = 0; 604 | } 605 | execution.executeOrder = TestCallParameters( 606 | address(wyvern), 607 | ethAmount, 608 | encodeAtomicMatch(buyOrder, sellOrder, EMPTY_SIG, sellSignature) 609 | ); 610 | } 611 | 612 | function getPayload_BuyOfferedERC1155WithEther( 613 | TestOrderContext calldata context, 614 | TestItem1155 memory nft, 615 | uint256 ethAmount 616 | ) external view override returns (TestOrderPayload memory execution) { 617 | ( 618 | Order memory sellOrder, 619 | Sig memory sellSignature 620 | ) = buildERC1155SellOrder( 621 | context.offerer, 622 | NULL_ADDRESS, 623 | 0x0000000000000000000000000000000000000000, 624 | ethAmount, 625 | nft, 626 | DEFAULT_FEE_RECIPIENT, 627 | 0 628 | ); 629 | (Order memory buyOrder, ) = buildERC1155BuyOrder( 630 | context.fulfiller, 631 | NULL_ADDRESS, 632 | 0x0000000000000000000000000000000000000000, 633 | ethAmount, 634 | nft, 635 | NULL_ADDRESS, 636 | 0 637 | ); 638 | 639 | if (context.listOnChain) { 640 | execution.submitOrder = TestCallParameters( 641 | address(wyvern), 642 | 0, 643 | encodeApproveOrder(sellOrder) 644 | ); 645 | } 646 | execution.executeOrder = TestCallParameters( 647 | address(wyvern), 648 | ethAmount, 649 | encodeAtomicMatch(buyOrder, sellOrder, EMPTY_SIG, sellSignature) 650 | ); 651 | } 652 | 653 | function getPayload_BuyOfferedERC721WithERC20( 654 | TestOrderContext calldata context, 655 | TestItem721 memory nft, 656 | TestItem20 memory erc20 657 | ) external view override returns (TestOrderPayload memory execution) { 658 | ( 659 | Order memory sellOrder, 660 | Sig memory sellSignature 661 | ) = buildERC721SellOrder( 662 | context.offerer, 663 | NULL_ADDRESS, 664 | erc20.token, 665 | erc20.amount, 666 | nft, 667 | DEFAULT_FEE_RECIPIENT, 668 | 0 669 | ); 670 | (Order memory buyOrder, ) = buildERC721BuyOrder( 671 | context.fulfiller, 672 | NULL_ADDRESS, 673 | erc20.token, 674 | erc20.amount, 675 | nft, 676 | NULL_ADDRESS, 677 | 0 678 | ); 679 | if (context.listOnChain) { 680 | execution.submitOrder = TestCallParameters( 681 | address(wyvern), 682 | 0, 683 | encodeApproveOrder(sellOrder) 684 | ); 685 | sellSignature = EMPTY_SIG; 686 | } 687 | execution.executeOrder = TestCallParameters( 688 | address(wyvern), 689 | 0, 690 | encodeAtomicMatch(buyOrder, sellOrder, EMPTY_SIG, sellSignature) 691 | ); 692 | } 693 | 694 | function getPayload_BuyOfferedERC1155WithERC20( 695 | TestOrderContext calldata context, 696 | TestItem1155 calldata nft, 697 | TestItem20 memory erc20 698 | ) external view override returns (TestOrderPayload memory execution) { 699 | ( 700 | Order memory sellOrder, 701 | Sig memory sellSignature 702 | ) = buildERC1155SellOrder( 703 | context.offerer, 704 | NULL_ADDRESS, 705 | erc20.token, 706 | erc20.amount, 707 | nft, 708 | DEFAULT_FEE_RECIPIENT, 709 | 0 710 | ); 711 | (Order memory buyOrder, ) = buildERC1155BuyOrder( 712 | context.fulfiller, 713 | NULL_ADDRESS, 714 | erc20.token, 715 | erc20.amount, 716 | nft, 717 | NULL_ADDRESS, 718 | 0 719 | ); 720 | 721 | if (context.listOnChain) { 722 | execution.submitOrder = TestCallParameters( 723 | address(wyvern), 724 | 0, 725 | encodeApproveOrder(sellOrder) 726 | ); 727 | sellSignature = EMPTY_SIG; 728 | } 729 | execution.executeOrder = TestCallParameters( 730 | address(wyvern), 731 | 0, 732 | encodeAtomicMatch(buyOrder, sellOrder, EMPTY_SIG, sellSignature) 733 | ); 734 | } 735 | 736 | function getPayload_BuyOfferedERC20WithERC721( 737 | TestOrderContext calldata context, 738 | TestItem20 calldata erc20, 739 | TestItem721 calldata nft 740 | ) external view override returns (TestOrderPayload memory execution) { 741 | (Order memory buyOrder, Sig memory buySignature) = buildERC721BuyOrder( 742 | context.offerer, 743 | NULL_ADDRESS, 744 | erc20.token, 745 | erc20.amount, 746 | nft, 747 | DEFAULT_FEE_RECIPIENT, 748 | 0 749 | ); 750 | (Order memory sellOrder, ) = buildERC721SellOrder( 751 | context.fulfiller, 752 | NULL_ADDRESS, 753 | erc20.token, 754 | erc20.amount, 755 | nft, 756 | NULL_ADDRESS, 757 | 0 758 | ); 759 | 760 | if (context.listOnChain) { 761 | execution.submitOrder = TestCallParameters( 762 | address(wyvern), 763 | 0, 764 | encodeApproveOrder(buyOrder) 765 | ); 766 | buySignature = EMPTY_SIG; 767 | } 768 | 769 | execution.executeOrder = TestCallParameters( 770 | address(wyvern), 771 | 0, 772 | encodeAtomicMatch(buyOrder, sellOrder, buySignature, EMPTY_SIG) 773 | ); 774 | } 775 | 776 | function getPayload_BuyOfferedERC20WithERC1155( 777 | TestOrderContext calldata context, 778 | TestItem20 calldata erc20, 779 | TestItem1155 calldata nft 780 | ) external view override returns (TestOrderPayload memory execution) { 781 | (Order memory buyOrder, Sig memory buySignature) = buildERC1155BuyOrder( 782 | context.offerer, 783 | NULL_ADDRESS, 784 | erc20.token, 785 | erc20.amount, 786 | nft, 787 | DEFAULT_FEE_RECIPIENT, 788 | 0 789 | ); 790 | (Order memory sellOrder, ) = buildERC1155SellOrder( 791 | context.fulfiller, 792 | NULL_ADDRESS, 793 | erc20.token, 794 | erc20.amount, 795 | nft, 796 | NULL_ADDRESS, 797 | 0 798 | ); 799 | 800 | if (context.listOnChain) { 801 | execution.submitOrder = TestCallParameters( 802 | address(wyvern), 803 | 0, 804 | encodeApproveOrder(buyOrder) 805 | ); 806 | buySignature = EMPTY_SIG; 807 | } 808 | execution.executeOrder = TestCallParameters( 809 | address(wyvern), 810 | 0, 811 | encodeAtomicMatch(buyOrder, sellOrder, buySignature, EMPTY_SIG) 812 | ); 813 | } 814 | 815 | function getPayload_BuyOfferedERC721WithEtherOneFeeRecipient( 816 | TestOrderContext calldata context, 817 | TestItem721 memory nft, 818 | uint256 priceEthAmount, 819 | address feeRecipient, 820 | uint256 feeEthAmount 821 | ) external view override returns (TestOrderPayload memory execution) { 822 | (Order memory sellOrder, Sig memory sellSignature) = buildERC721SellOrder( 823 | context.offerer, 824 | NULL_ADDRESS, 825 | 0x0000000000000000000000000000000000000000, 826 | priceEthAmount + feeEthAmount, 827 | nft, 828 | feeRecipient, 829 | (feeEthAmount * 10000) / (priceEthAmount + feeEthAmount) + 1 // Calculate fee percentage 830 | ); 831 | (Order memory buyOrder, ) = buildERC721BuyOrder( 832 | context.fulfiller, 833 | NULL_ADDRESS, 834 | 0x0000000000000000000000000000000000000000, 835 | priceEthAmount + feeEthAmount, 836 | nft, 837 | NULL_ADDRESS, 838 | (feeEthAmount * 10000) / (priceEthAmount + feeEthAmount) + 1 // Calculate fee percentage 839 | ); 840 | 841 | if (context.listOnChain) { 842 | execution.submitOrder = TestCallParameters( 843 | address(wyvern), 844 | 0, 845 | encodeApproveOrder(sellOrder) 846 | ); 847 | sellSignature = EMPTY_SIG; 848 | } 849 | execution.executeOrder = TestCallParameters( 850 | address(wyvern), 851 | priceEthAmount + feeEthAmount, 852 | encodeAtomicMatch(buyOrder, sellOrder, EMPTY_SIG, sellSignature) 853 | ); 854 | } 855 | 856 | function getPayload_BuyOfferedManyERC721WithEther( 857 | TestOrderContext calldata context, 858 | TestItem721[] calldata nfts, 859 | uint256 ethAmount 860 | ) external view override returns (TestOrderPayload memory execution) { 861 | ( 862 | Order memory sellOrder, 863 | Sig memory sellSignature 864 | ) = buildERC721BatchSellOrder( 865 | context.offerer, 866 | context.fulfiller, 867 | 0x0000000000000000000000000000000000000000, 868 | ethAmount, 869 | nfts, 870 | DEFAULT_FEE_RECIPIENT 871 | ); 872 | (Order memory buyOrder, ) = buildERC721BatchBuyOrder( 873 | context.fulfiller, 874 | context.offerer, 875 | 0x0000000000000000000000000000000000000000, 876 | ethAmount, 877 | nfts, 878 | NULL_ADDRESS 879 | ); 880 | 881 | if (context.listOnChain) { 882 | execution.submitOrder = TestCallParameters( 883 | address(wyvern), 884 | 0, 885 | encodeApproveOrder(sellOrder) 886 | ); 887 | sellSignature = EMPTY_SIG; 888 | } 889 | execution.executeOrder = TestCallParameters( 890 | address(wyvern), 891 | ethAmount, 892 | encodeAtomicMatch(buyOrder, sellOrder, EMPTY_SIG, sellSignature) 893 | ); 894 | } 895 | } 896 | --------------------------------------------------------------------------------