├── .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 │ ├── blur │ │ ├── interfaces │ │ │ └── IBlurExchange.sol │ │ ├── lib │ │ │ ├── OrderStructs.sol │ │ │ └── BlurTypeHashes.sol │ │ └── BlurConfig.sol │ ├── foundation │ │ ├── interfaces │ │ │ └── IFoundation.sol │ │ └── FoundationConfig.sol │ ├── zeroEx │ │ ├── lib │ │ │ ├── LibSignature.sol │ │ │ └── LibNFTOrder.sol │ │ └── interfaces │ │ │ └── IZeroEx.sol │ ├── X2Y2 │ │ ├── interfaces │ │ │ ├── IX2Y2Marketplace.sol │ │ │ └── MarketConstants.sol │ │ ├── lib │ │ │ ├── X2Y2TypeHashes.sol │ │ │ ├── Strings.sol │ │ │ └── ECDSA.sol │ │ └── X2Y2Config.sol │ ├── sudoswap │ │ ├── interfaces │ │ │ ├── IPair.sol │ │ │ ├── IRouter.sol │ │ │ └── IPairFactory.sol │ │ └── SudoswapConfig.sol │ ├── looksRare │ │ ├── interfaces │ │ │ ├── ICurrencyManager.sol │ │ │ └── ILooksRareExchange.sol │ │ ├── lib │ │ │ ├── LooksRareTypeHashes.sol │ │ │ └── OrderTypes.sol │ │ └── LooksRareConfig.sol │ ├── seaport-1.1 │ │ └── lib │ │ │ ├── ConsiderationEnums.sol │ │ │ ├── ConsiderationStructs.sol │ │ │ └── ConsiderationTypeHashes.sol │ └── seaport-1.4 │ │ └── lib │ │ ├── ConsiderationEnums.sol │ │ ├── ConsiderationStructs.sol │ │ └── ConsiderationTypeHashes.sol └── Types.sol ├── .vscode └── settings.json ├── test ├── tokens │ ├── TestERC20.sol │ ├── TestERC721.sol │ ├── TestERC1155.sol │ └── WETH.sol └── utils │ └── BaseOrderTest.sol ├── 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 | [profile.default] 2 | solc = "0.8.14" 3 | bytecode_hash = "none" 4 | optimizer_runs = 1000000 5 | ignored_error_codes = [5667, 9302] 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/marketplaces/blur/interfaces/IBlurExchange.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import { Input, Execution } from "../lib/OrderStructs.sol"; 5 | 6 | interface IBlurExchange { 7 | function open() external; 8 | 9 | function execute(Input calldata sell, Input calldata buy) external payable; 10 | 11 | function bulkExecute(Execution[] calldata executions) external payable; 12 | } 13 | -------------------------------------------------------------------------------- /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/zeroEx/lib/LibSignature.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | library LibSignature { 4 | enum SignatureType { 5 | ILLEGAL, 6 | INVALID, 7 | EIP712, 8 | ETHSIGN, 9 | PRESIGNED 10 | } 11 | 12 | struct Signature { 13 | // How to validate the signature. 14 | SignatureType signatureType; 15 | // EC Signature data. 16 | uint8 v; 17 | // EC Signature data. 18 | bytes32 r; 19 | // EC Signature data. 20 | bytes32 s; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/marketplaces/X2Y2/interfaces/IX2Y2Marketplace.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import { Market } from "./MarketConstants.sol"; 5 | 6 | interface IX2Y2Marketplace { 7 | function run1( 8 | Market.Order memory order, 9 | Market.SettleShared memory shared, 10 | Market.SettleDetail memory detail 11 | ) external returns (uint256); 12 | 13 | function run(Market.RunInput memory input) external payable; 14 | 15 | function updateSigners(address[] memory toAdd, address[] memory toRemove) 16 | external; 17 | } 18 | -------------------------------------------------------------------------------- /src/marketplaces/sudoswap/interfaces/IPair.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | interface IPair { 4 | function swapNFTsForToken( 5 | uint256[] calldata nftIds, 6 | uint256 minExpectedTokenOutput, 7 | address payable tokenRecipient, 8 | bool isRouter, 9 | address routerCaller 10 | ) external returns (uint256 outputAmount); 11 | 12 | function swapTokenForSpecificNFTs( 13 | uint256[] calldata nftIds, 14 | uint256 maxExpectedTokenInput, 15 | address nftRecipient, 16 | bool isRouter, 17 | address routerCaller 18 | ) external payable returns (uint256 inputAmount); 19 | } 20 | -------------------------------------------------------------------------------- /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/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 | struct TestItem721 { 5 | address token; 6 | uint256 identifier; 7 | } 8 | 9 | struct TestItem1155 { 10 | address token; 11 | uint256 identifier; 12 | uint256 amount; 13 | } 14 | 15 | struct TestItem20 { 16 | address token; 17 | uint256 amount; 18 | } 19 | 20 | struct TestCallParameters { 21 | address target; 22 | uint256 value; 23 | bytes data; 24 | } 25 | 26 | struct SetupCall { 27 | address sender; 28 | address target; 29 | bytes data; 30 | } 31 | 32 | struct TestOrderContext { 33 | bool listOnChain; 34 | address offerer; 35 | address fulfiller; 36 | } 37 | 38 | struct TestOrderPayload { 39 | // Call needed to submit order on-chain without signature 40 | TestCallParameters submitOrder; 41 | // Call needed to actually execute the order 42 | TestCallParameters executeOrder; 43 | } 44 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/marketplaces/blur/lib/OrderStructs.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | enum Side { 5 | Buy, 6 | Sell 7 | } 8 | enum SignatureVersion { 9 | Single, 10 | Bulk 11 | } 12 | enum AssetType { 13 | ERC721, 14 | ERC1155 15 | } 16 | 17 | struct Fee { 18 | uint16 rate; 19 | address payable recipient; 20 | } 21 | 22 | struct Order { 23 | address trader; 24 | Side side; 25 | address matchingPolicy; 26 | address collection; 27 | uint256 tokenId; 28 | uint256 amount; 29 | address paymentToken; 30 | uint256 price; 31 | uint256 listingTime; 32 | /* Order expiration timestamp - 0 for oracle cancellations. */ 33 | uint256 expirationTime; 34 | Fee[] fees; 35 | uint256 salt; 36 | bytes extraParams; 37 | } 38 | 39 | struct Input { 40 | Order order; 41 | uint8 v; 42 | bytes32 r; 43 | bytes32 s; 44 | bytes extraSignature; 45 | SignatureVersion signatureVersion; 46 | uint256 blockNumber; 47 | } 48 | 49 | struct Execution { 50 | Input sell; 51 | Input buy; 52 | } 53 | -------------------------------------------------------------------------------- /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/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) external; 46 | } 47 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/tokens/WETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.14; 3 | 4 | import { TestERC20 } from "./TestERC20.sol"; 5 | 6 | contract WETH is TestERC20 { 7 | event Deposit(address indexed dst, uint256 wad); 8 | event Withdrawal(address indexed src, uint256 wad); 9 | 10 | function deposit() public payable { 11 | balanceOf[msg.sender] += msg.value; 12 | emit Deposit(msg.sender, msg.value); 13 | } 14 | 15 | function withdraw(uint256 wad) public { 16 | require(balanceOf[msg.sender] >= wad); 17 | balanceOf[msg.sender] -= wad; 18 | address payable sender = payable(msg.sender); 19 | sender.transfer(wad); 20 | emit Withdrawal(msg.sender, wad); 21 | } 22 | 23 | function approve(address guy, uint256 wad) public override returns (bool) { 24 | allowance[msg.sender][guy] = wad; 25 | emit Approval(msg.sender, guy, wad); 26 | return true; 27 | } 28 | 29 | function transfer(address dst, uint256 wad) public override returns (bool) { 30 | return transferFrom(msg.sender, dst, wad); 31 | } 32 | 33 | function transferFrom( 34 | address src, 35 | address dst, 36 | uint256 wad 37 | ) public override returns (bool) { 38 | require(balanceOf[src] >= wad); 39 | 40 | if ( 41 | src != msg.sender && allowance[src][msg.sender] != type(uint256).max 42 | ) { 43 | require(allowance[src][msg.sender] >= wad); 44 | allowance[src][msg.sender] -= wad; 45 | } 46 | 47 | balanceOf[src] -= wad; 48 | balanceOf[dst] += wad; 49 | 50 | emit Transfer(src, dst, wad); 51 | 52 | return true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/zeroEx/lib/LibNFTOrder.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | /// @dev A library for common NFT order operations. 4 | library LibNFTOrder { 5 | enum OrderStatus { 6 | INVALID, 7 | FILLABLE, 8 | UNFILLABLE, 9 | EXPIRED 10 | } 11 | 12 | enum TradeDirection { 13 | SELL_NFT, 14 | BUY_NFT 15 | } 16 | 17 | struct Property { 18 | address propertyValidator; 19 | bytes propertyData; 20 | } 21 | 22 | struct Fee { 23 | address recipient; 24 | uint256 amount; 25 | bytes feeData; 26 | } 27 | 28 | // "Base struct" for ERC721Order and ERC1155, used 29 | // by the abstract contract `NFTOrders`. 30 | struct NFTOrder { 31 | TradeDirection direction; 32 | address maker; 33 | address taker; 34 | uint256 expiry; 35 | uint256 nonce; 36 | address erc20Token; 37 | uint256 erc20TokenAmount; 38 | Fee[] fees; 39 | address nft; 40 | uint256 nftId; 41 | Property[] nftProperties; 42 | } 43 | 44 | // All fields align with those of NFTOrder 45 | struct ERC721Order { 46 | TradeDirection direction; 47 | address maker; 48 | address taker; 49 | uint256 expiry; 50 | uint256 nonce; 51 | address erc20Token; 52 | uint256 erc20TokenAmount; 53 | Fee[] fees; 54 | address erc721Token; 55 | uint256 erc721TokenId; 56 | Property[] erc721TokenProperties; 57 | } 58 | 59 | // All fields except `erc1155TokenAmount` align 60 | // with those of NFTOrder 61 | struct ERC1155Order { 62 | TradeDirection direction; 63 | address maker; 64 | address taker; 65 | uint256 expiry; 66 | uint256 nonce; 67 | address erc20Token; 68 | uint256 erc20TokenAmount; 69 | Fee[] fees; 70 | address erc1155Token; 71 | uint256 erc1155TokenId; 72 | Property[] erc1155TokenProperties; 73 | // End of fields shared with NFTOrder 74 | uint128 erc1155TokenAmount; 75 | } 76 | 77 | struct OrderInfo { 78 | bytes32 orderHash; 79 | OrderStatus status; 80 | // `orderAmount` is 1 for all ERC721Orders, and 81 | // `erc1155TokenAmount` for ERC1155Orders. 82 | uint128 orderAmount; 83 | // The remaining amount of the ERC721/ERC1155 asset 84 | // that can be filled for the order. 85 | uint128 remainingAmount; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /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 was 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/blur/lib/BlurTypeHashes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./OrderStructs.sol"; 5 | 6 | contract BlurTypeHashes { 7 | bytes32 public constant DOMAIN_SEPARATOR = 8 | keccak256( 9 | abi.encode( 10 | keccak256( 11 | "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" 12 | ), 13 | keccak256("Blur Exchange"), 14 | keccak256("1.0"), // for versionId = 1 15 | 1, 16 | 0x000000000000Ad05Ccc4F10045630fb830B95127 // mainnet Blur exchange address 17 | ) 18 | ); 19 | 20 | // function _hashToSign(bytes32 orderHash) 21 | // internal 22 | // view 23 | // returns (bytes32 hash) 24 | // { 25 | // return keccak256(abi.encodePacked( 26 | // "\x19\x01", 27 | // DOMAIN_SEPARATOR, 28 | // orderHash 29 | // )); 30 | // } 31 | 32 | bytes32 public constant ORDER_TYPEHASH = 33 | keccak256( 34 | "Order(address trader,uint8 side,address matchingPolicy,address collection,uint256 tokenId,uint256 amount,address paymentToken,uint256 price,uint256 listingTime,uint256 expirationTime,Fee[] fees,uint256 salt,bytes extraParams,uint256 nonce)Fee(uint16 rate,address recipient)" 35 | ); 36 | 37 | bytes32 public constant FEE_TYPEHASH = 38 | keccak256("Fee(uint16 rate,address recipient)"); 39 | 40 | function _hashOrder(Order memory order, uint256 nonce) 41 | internal 42 | pure 43 | returns (bytes32) 44 | { 45 | return 46 | keccak256( 47 | bytes.concat( 48 | abi.encode( 49 | ORDER_TYPEHASH, 50 | order.trader, 51 | order.side, 52 | order.matchingPolicy, 53 | order.collection, 54 | order.tokenId, 55 | order.amount, 56 | order.paymentToken, 57 | order.price, 58 | order.listingTime, 59 | order.expirationTime, 60 | _packFees(order.fees), 61 | order.salt, 62 | keccak256(order.extraParams) 63 | ), 64 | abi.encode(nonce) 65 | ) 66 | ); 67 | } 68 | 69 | function _packFees(Fee[] memory fees) internal pure returns (bytes32) { 70 | bytes32[] memory feeHashes = new bytes32[](fees.length); 71 | for (uint256 i = 0; i < fees.length; i++) { 72 | feeHashes[i] = _hashFee(fees[i]); 73 | } 74 | return keccak256(abi.encodePacked(feeHashes)); 75 | } 76 | 77 | function _hashFee(Fee memory fee) internal pure returns (bytes32) { 78 | return keccak256(abi.encode(FEE_TYPEHASH, fee.rate, fee.recipient)); 79 | } 80 | 81 | // function _deriveOrderDigest( 82 | // Order memory order 83 | // ) internal pure returns (bytes32) { 84 | // bytes32 orderHash = keccak256( 85 | // abi.encode( 86 | // order.trader, 87 | // order.side, 88 | // order.matchingPolicy, 89 | // order.collection, 90 | // order.tokenId, 91 | // order.amount, 92 | // order.paymentToken, 93 | // order.price, 94 | // order.listingTime, 95 | // order.expirationTime, 96 | // order.fees.length, 97 | // order.fees, 98 | // order.salt, 99 | // order.extraParams 100 | // ) 101 | // ); 102 | // return 103 | // keccak256( 104 | // abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, orderHash) 105 | // ); 106 | // } 107 | } 108 | -------------------------------------------------------------------------------- /src/marketplaces/seaport-1.1/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/seaport-1.4/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 | 27 | // Create dir if needed 28 | if (!fs.existsSync("./results")) { 29 | fs.mkdirSync("./results"); 30 | } 31 | 32 | fs.writeFileSync("./results/results.tex", eoaLatex); 33 | 34 | /// {market:{testName:{actionName:{gas,direct}}}} 35 | let directTests = {}; 36 | directTests.results = {}; 37 | parseOutput(directTests, stdout, true); 38 | const directLatex = generateLatex( 39 | directTests.results, 40 | "Benchmark Tests (Direct)" 41 | ); 42 | fs.writeFileSync("./results/results-direct.tex", directLatex); 43 | } 44 | ); 45 | 46 | /** 47 | * Parses the entire `stdout` from forge. Sets the values in `tests` 48 | * @param {*} tests The variable which holds test results 49 | * @param {*} stdout The output from running forge on the market-benchmark tests 50 | * @param {*} showDirect Show direct contract interactions (as opposed to EOA) 51 | */ 52 | function parseOutput(tests, stdout, showDirect = false) { 53 | const outputLines = stdout.split("\n"); 54 | let doNextLine = false; 55 | for (let outputLine of outputLines) { 56 | outputLine = outputLine.trim(); 57 | 58 | if (outputLine == "") { 59 | doNextLine = false; 60 | } else if (doNextLine) { 61 | parseTestLine(tests, outputLine, showDirect); 62 | } else if (outputLine.includes("Logs:")) { 63 | doNextLine = true; 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * Parses a line of text from the forge output. Sets corresponding keys in `tests` 70 | */ 71 | function parseTestLine(tests, testLine, showDirect) { 72 | const marketName = testLine.split("]")[0].substring(1); 73 | const testName = testLine.split(")")[0].split("(")[1]; 74 | const actionName = testLine 75 | .split(")")[1] 76 | .split("(")[0] 77 | .split("--")[0] 78 | .trim(); 79 | const direct = testLine.includes("(direct)"); 80 | const gasUsage = 81 | testLine.split("gas:").length > 1 82 | ? testLine.split("gas:")[1].trim() 83 | : 0; 84 | if ( 85 | (showDirect && !direct && gasUsage != 0) || 86 | (!showDirect && direct && gasUsage != 0) 87 | ) 88 | return; // Skip unwanted results 89 | addTestResults(tests, marketName, testName, actionName, gasUsage); 90 | } 91 | 92 | function addTestResults(tests, market, testName, actionName, gasUsage) { 93 | if (!tests.results.hasOwnProperty(market)) { 94 | tests.results[market] = {}; 95 | } 96 | if (!tests.results[market].hasOwnProperty(testName)) { 97 | tests.results[market][testName] = {}; 98 | } 99 | if (actionName == "") return; 100 | tests.results[market][testName][actionName] = { gasUsage }; 101 | } 102 | 103 | /** 104 | * Generates the latex from the global dictionary `tests` 105 | * @returns String containing latex 106 | */ 107 | function generateLatex(tests, title) { 108 | let latex = ""; 109 | const markets = Object.keys(tests); 110 | const testNames = Object.keys(tests[markets[0]]); 111 | 112 | latex += 113 | "\\documentclass[border = 4pt]{standalone}\n\\usepackage{emoji}\n\\usepackage{xcolor}\n\\usepackage{multirow}\n\\begin{document}" + 114 | `\n\\setemojifont{TwemojiMozilla}\n\\begin{tabular}{ |c|c|${"c|".repeat( 115 | markets.length 116 | )} } \n\\hline\n\\multicolumn{${ 117 | 2 + markets.length 118 | }}{|c|}{${title}} \\\\ \n` + 119 | "\\hline \n Test Name & Action Name "; 120 | 121 | for (const market of markets) { 122 | latex += `& ${market} `; 123 | } 124 | 125 | latex += "\\\\ \n\\hline\\hline\n"; 126 | 127 | for (const testName of testNames) { 128 | let actionNames = []; 129 | for (const market of markets) { 130 | const tempActionNames = Object.keys(tests[market][testName]); 131 | if (actionNames.length < tempActionNames.length) 132 | actionNames = tempActionNames; 133 | } 134 | if (actionNames.length == 0) { 135 | actionNames = [""]; 136 | } 137 | 138 | latex += `\\multirow{${actionNames.length}}{18em}{${testName}}`; 139 | for (const actionName of actionNames) { 140 | latex += `& ${actionName} `; 141 | 142 | let gasValues = []; 143 | for (const market of markets) { 144 | if (tests[market][testName][actionName] === undefined) { 145 | gasValues.push(0); 146 | } else { 147 | gasValues.push( 148 | parseInt(tests[market][testName][actionName].gasUsage) 149 | ); 150 | } 151 | } 152 | const maxGas = Math.max(...gasValues); 153 | const minGas = Math.min.apply(null, gasValues.filter(Boolean)); 154 | 155 | for (const gasValue of gasValues) { 156 | const color = getColor(minGas, maxGas, gasValue); 157 | if (gasValue == 0) { 158 | latex += `& \\emoji{cross-mark} `; 159 | } else { 160 | const percentChange = Math.round( 161 | ((gasValue - minGas) * 100.0) / minGas 162 | ); 163 | latex += 164 | `& \\color[RGB]{${color.values[0]},${color.values[1]},${color.values[2]}} ${gasValue}` + 165 | (percentChange != 0 ? `(+${percentChange}\\%)` : ``); // Only show percent change if not 0 166 | } 167 | } 168 | 169 | latex += "\\\\\n"; 170 | latex += `\\cline{2-${2 + markets.length}}`; 171 | } 172 | latex += "\\cline{0-1} \n"; 173 | } 174 | 175 | latex += "\\end{tabular}\n\\end{document}"; 176 | 177 | return latex; 178 | } 179 | 180 | /** 181 | * Generate interpolated color between green and red (green lowest gas, and red highest) 182 | * @param {*} minGas The minimum gas used for the test 183 | * @param {*} maxGas The maximum gas used for the test 184 | * @param {*} gas The gas used by a market for this test 185 | * @returns The color to display market results as for the test 186 | */ 187 | function getColor(minGas, maxGas, gas) { 188 | let color; 189 | if (minGas == maxGas) { 190 | color = colormap(0); 191 | } else if (!Number.isFinite(minGas)) { 192 | color = colormap(0); 193 | } else { 194 | color = colormap(((gas - minGas) * 1.0) / (maxGas - minGas)); 195 | } 196 | 197 | const parsed = parse(color); 198 | return parsed; 199 | } 200 | -------------------------------------------------------------------------------- /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 { WETH } from "../tokens/WETH.sol"; 8 | import { TestERC721 } from "../tokens/TestERC721.sol"; 9 | 10 | contract BaseOrderTest is DSTestPlus { 11 | using stdStorage for StdStorage; 12 | StdStorage stdstore; 13 | 14 | uint256 constant MAX_INT = ~uint256(0); 15 | 16 | uint256 internal alicePk = 0xa11ce; 17 | uint256 internal bobPk = 0xb0b; 18 | uint256 internal calPk = 0xca1; 19 | uint256 internal feeReciever1Pk = 0xfee1; 20 | uint256 internal feeReciever2Pk = 0xfee2; 21 | address payable internal alice = payable(hevm.addr(alicePk)); 22 | address payable internal bob = payable(hevm.addr(bobPk)); 23 | address payable internal cal = payable(hevm.addr(calPk)); 24 | address payable internal feeReciever1 = payable(hevm.addr(feeReciever1Pk)); 25 | address payable internal feeReciever2 = payable(hevm.addr(feeReciever2Pk)); 26 | 27 | TestERC20 internal token1; 28 | TestERC20 internal token2; 29 | TestERC20 internal token3; 30 | // TestERC20 internal weth; 31 | WETH internal constant weth = 32 | WETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 33 | 34 | TestERC721 internal test721_1; 35 | TestERC721 internal test721_2; 36 | TestERC721 internal test721_3; 37 | 38 | TestERC1155 internal test1155_1; 39 | TestERC1155 internal test1155_2; 40 | TestERC1155 internal test1155_3; 41 | 42 | address[] allTokens; 43 | TestERC20[] erc20s; 44 | address[] erc20Addresses; 45 | TestERC721[] erc721s; 46 | address[] erc721Addresses; 47 | TestERC1155[] erc1155s; 48 | address[] accounts; 49 | mapping(address => uint256) internal privateKeys; 50 | 51 | mapping(bytes32 => bool) originalMarketWriteSlots; 52 | 53 | event Transfer(address indexed from, address indexed to, uint256 value); 54 | 55 | event TransferSingle( 56 | address indexed operator, 57 | address indexed from, 58 | address indexed to, 59 | uint256 id, 60 | uint256 value 61 | ); 62 | 63 | struct RestoreERC20Balance { 64 | address token; 65 | address who; 66 | } 67 | 68 | function setUp() public virtual { 69 | hevm.label(alice, "alice"); 70 | hevm.label(bob, "bob"); 71 | hevm.label(cal, "cal"); 72 | hevm.label(address(this), "testContract"); 73 | 74 | privateKeys[alice] = alicePk; 75 | privateKeys[bob] = bobPk; 76 | privateKeys[cal] = calPk; 77 | 78 | _deployTestTokenContracts(); 79 | accounts = [alice, bob, cal, address(this)]; 80 | erc20s = [token1, token2, token3]; 81 | erc20Addresses = [ 82 | address(token1), 83 | address(token2), 84 | address(token3), 85 | address(weth) 86 | ]; 87 | erc721s = [test721_1, test721_2, test721_3]; 88 | erc721Addresses = [ 89 | address(test721_1), 90 | address(test721_2), 91 | address(test721_3) 92 | ]; 93 | erc1155s = [test1155_1, test1155_2, test1155_3]; 94 | allTokens = [ 95 | address(token1), 96 | address(token2), 97 | address(token3), 98 | address(weth), 99 | address(test721_1), 100 | address(test721_2), 101 | address(test721_3), 102 | address(test1155_1), 103 | address(test1155_2), 104 | address(test1155_3) 105 | ]; 106 | } 107 | 108 | /** 109 | * @dev deploy test token contracts 110 | */ 111 | function _deployTestTokenContracts() internal { 112 | token1 = new TestERC20(); 113 | token2 = new TestERC20(); 114 | token3 = new TestERC20(); 115 | test721_1 = new TestERC721(); 116 | test721_2 = new TestERC721(); 117 | test721_3 = new TestERC721(); 118 | test1155_1 = new TestERC1155(); 119 | test1155_2 = new TestERC1155(); 120 | test1155_3 = new TestERC1155(); 121 | hevm.label(address(token1), "token1"); 122 | hevm.label(address(weth), "weth"); 123 | hevm.label(address(test721_1), "test721_1"); 124 | hevm.label(address(test1155_1), "test1155_1"); 125 | hevm.label(address(feeReciever1), "feeReciever1"); 126 | hevm.label(address(feeReciever2), "feeReciever2"); 127 | } 128 | 129 | function _setApprovals( 130 | address _owner, 131 | address _erc20Target, 132 | address _erc721Target, 133 | address _erc1155Target 134 | ) internal { 135 | hevm.startPrank(_owner); 136 | for (uint256 i = 0; i < erc20s.length; i++) { 137 | erc20s[i].approve(_erc20Target, MAX_INT); 138 | } 139 | weth.approve(_erc20Target, MAX_INT); 140 | for (uint256 i = 0; i < erc721s.length; i++) { 141 | erc721s[i].setApprovalForAll(_erc721Target, true); 142 | } 143 | for (uint256 i = 0; i < erc1155s.length; i++) { 144 | erc1155s[i].setApprovalForAll( 145 | _erc1155Target != address(0) ? _erc1155Target : _erc721Target, 146 | true 147 | ); 148 | } 149 | 150 | hevm.stopPrank(); 151 | } 152 | 153 | /** 154 | * @dev reset written token storage slots to 0 and reinitialize 155 | * uint128(MAX_INT) erc20 balances for 3 test accounts and this. 156 | */ 157 | function _resetStorageAndEth(address market) internal { 158 | _resetTokensStorage(); 159 | _restoreEthBalances(); 160 | _resetMarketStorage(market); 161 | hevm.record(); 162 | } 163 | 164 | function _restoreEthBalances() internal { 165 | for (uint256 i = 0; i < accounts.length; i++) { 166 | hevm.deal(accounts[i], uint128(MAX_INT)); 167 | } 168 | hevm.deal(feeReciever1, 0); 169 | hevm.deal(feeReciever2, 0); 170 | } 171 | 172 | /** 173 | * @dev Reset market storage between runs to allow for duplicate orders 174 | */ 175 | function _resetMarketStorage(address market) internal { 176 | if (!originalMarketWriteSlots[0]) { 177 | (, bytes32[] memory writeSlots1) = hevm.accesses(market); 178 | for (uint256 i = 0; i < writeSlots1.length; i++) { 179 | originalMarketWriteSlots[writeSlots1[i]] = true; 180 | } 181 | originalMarketWriteSlots[0] = true; 182 | } 183 | (, bytes32[] memory writeSlots) = hevm.accesses(market); 184 | for (uint256 i = 0; i < writeSlots.length; i++) { 185 | if (originalMarketWriteSlots[writeSlots[i]]) continue; 186 | hevm.store(market, writeSlots[i], bytes32(0)); 187 | } 188 | } 189 | 190 | function _resetTokensStorage() internal { 191 | for (uint256 i = 0; i < allTokens.length; i++) { 192 | _resetStorage(allTokens[i]); 193 | } 194 | } 195 | 196 | /** 197 | * @dev reset all storage written at an address thus far to 0; will 198 | * overwrite totalSupply()for ERC20s but that should be fine with the 199 | * goal of resetting the balances and owners of tokens - but note: 200 | * should be careful about approvals, etc 201 | * 202 | * note: must be called in conjunction with vm.record() 203 | */ 204 | function _resetStorage(address _addr) internal { 205 | (, bytes32[] memory writeSlots) = hevm.accesses(_addr); 206 | for (uint256 i = 0; i < writeSlots.length; i++) { 207 | hevm.store(_addr, writeSlots[i], bytes32(0)); 208 | } 209 | } 210 | 211 | receive() external payable virtual {} 212 | } 213 | -------------------------------------------------------------------------------- /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-1.1/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-1.4/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-1.1/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/seaport-1.4/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(0x00000000000001ad428e4906aE43D8F9852d0dD6); 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.4")); 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 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import { LooksRareTypeHashes } from "./lib/LooksRareTypeHashes.sol"; 5 | import { OrderTypes } from "./lib/OrderTypes.sol"; 6 | import { ILooksRareExchange } from "./interfaces/ILooksRareExchange.sol"; 7 | import { ICurrencyManager } from "./interfaces/ICurrencyManager.sol"; 8 | import { BaseMarketConfig } from "../../BaseMarketConfig.sol"; 9 | import { TestCallParameters, TestOrderContext, TestOrderPayload, TestItem721, TestItem1155, TestItem20, SetupCall } from "../../Types.sol"; 10 | 11 | contract LooksRareConfig is BaseMarketConfig, LooksRareTypeHashes { 12 | function name() external pure override returns (string memory) { 13 | return "LooksRare"; 14 | } 15 | 16 | function market() public pure override returns (address) { 17 | return address(looksRare); 18 | } 19 | 20 | address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 21 | 22 | ILooksRareExchange internal constant looksRare = 23 | ILooksRareExchange(0x59728544B08AB483533076417FbBB2fD0B17CE3a); 24 | address internal constant looksRareOwner = 25 | 0xBfb6669Ef4C4c71ae6E722526B1B8d7d9ff9a019; 26 | 27 | address internal constant fixedPriceStrategy = 28 | 0x56244Bb70CbD3EA9Dc8007399F61dFC065190031; 29 | 30 | ICurrencyManager internal constant currencyManager = 31 | ICurrencyManager(0xC881ADdf409eE2C4b6bBc8B607c2C5CAFaB93d25); 32 | address internal constant currencyManagerOwner = 33 | 0xB624e4148ef395458D361547C9Fdb59B62a84bd2; 34 | 35 | /*////////////////////////////////////////////////////////////// 36 | Generic Helpers 37 | //////////////////////////////////////////////////////////////*/ 38 | 39 | function buildMakerOrder( 40 | bool isOrderAsk, 41 | address maker, 42 | address fungibleToken, 43 | uint256 fungibleAmount, 44 | address nftToken, 45 | uint256 nftAmount, 46 | uint256 nftTokenId 47 | ) internal view returns (OrderTypes.MakerOrder memory makerOrder) { 48 | makerOrder = OrderTypes.MakerOrder( 49 | isOrderAsk, 50 | maker, 51 | nftToken, 52 | fungibleAmount, 53 | nftTokenId, 54 | nftAmount, 55 | fixedPriceStrategy, 56 | fungibleToken, 57 | 0, 58 | block.timestamp, 59 | block.timestamp + 1, 60 | 0, 61 | "", 62 | 0, 63 | 0, 64 | 0 65 | ); 66 | (uint8 v, bytes32 r, bytes32 s) = _sign( 67 | maker, 68 | _deriveOrderDigest(makerOrder) 69 | ); 70 | makerOrder.v = v; 71 | makerOrder.r = r; 72 | makerOrder.s = s; 73 | } 74 | 75 | function buildTakerOrder( 76 | address taker, 77 | OrderTypes.MakerOrder memory makerOrder 78 | ) internal pure returns (OrderTypes.TakerOrder memory) { 79 | return 80 | OrderTypes.TakerOrder( 81 | !makerOrder.isOrderAsk, 82 | taker, 83 | makerOrder.price, 84 | makerOrder.tokenId, 85 | 0, 86 | "" 87 | ); 88 | } 89 | 90 | /*////////////////////////////////////////////////////////////// 91 | Setup 92 | //////////////////////////////////////////////////////////////*/ 93 | 94 | function beforeAllPrepareMarketplace(address, address) external override { 95 | buyerNftApprovalTarget = sellerNftApprovalTarget = 0xf42aa99F011A1fA7CDA90E5E98b277E306BcA83e; // ERC721 transfer manager 96 | buyerErc1155ApprovalTarget = sellerErc1155ApprovalTarget = 0xFED24eC7E22f573c2e08AEF55aA6797Ca2b3A051; // ERC1155 transfer manager 97 | buyerErc20ApprovalTarget = sellerErc20ApprovalTarget = address( 98 | looksRare 99 | ); 100 | } 101 | 102 | function beforeAllPrepareMarketplaceCall( 103 | address, 104 | address, 105 | address[] calldata erc20Tokens, 106 | address[] calldata 107 | ) external pure override returns (SetupCall[] memory) { 108 | SetupCall[] memory setupCalls = new SetupCall[](erc20Tokens.length + 1); 109 | for (uint256 i = 0; i < erc20Tokens.length; i++) { 110 | // Whitelist necessary ERC-20 tokens 111 | setupCalls[i] = SetupCall( 112 | currencyManagerOwner, 113 | address(currencyManager), 114 | abi.encodeWithSelector( 115 | ICurrencyManager.addCurrency.selector, 116 | erc20Tokens[i] 117 | ) 118 | ); 119 | } 120 | 121 | // Remove protocol fee 122 | setupCalls[erc20Tokens.length] = SetupCall( 123 | looksRareOwner, 124 | address(looksRare), 125 | abi.encodeWithSelector( 126 | ILooksRareExchange.updateProtocolFeeRecipient.selector, 127 | address(0) 128 | ) 129 | ); 130 | 131 | return setupCalls; 132 | } 133 | 134 | /*////////////////////////////////////////////////////////////// 135 | Test Payload Calls 136 | //////////////////////////////////////////////////////////////*/ 137 | 138 | function getPayload_BuyOfferedERC721WithEther( 139 | TestOrderContext calldata context, 140 | TestItem721 memory nft, 141 | uint256 ethAmount 142 | ) external view override returns (TestOrderPayload memory execution) { 143 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 144 | true, 145 | context.offerer, 146 | WETH, 147 | ethAmount, 148 | nft.token, 149 | 1, 150 | nft.identifier 151 | ); 152 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 153 | context.fulfiller, 154 | makerOrder 155 | ); 156 | 157 | if (context.listOnChain) { 158 | _notImplemented(); 159 | } 160 | execution.executeOrder = TestCallParameters( 161 | address(looksRare), 162 | ethAmount, 163 | abi.encodeWithSelector( 164 | ILooksRareExchange.matchAskWithTakerBidUsingETHAndWETH.selector, 165 | takerOrder, 166 | makerOrder 167 | ) 168 | ); 169 | } 170 | 171 | function getPayload_BuyOfferedERC1155WithEther( 172 | TestOrderContext calldata context, 173 | TestItem1155 calldata nft, 174 | uint256 ethAmount 175 | ) external view override returns (TestOrderPayload memory execution) { 176 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 177 | true, 178 | context.offerer, 179 | WETH, 180 | ethAmount, 181 | nft.token, 182 | nft.amount, 183 | nft.identifier 184 | ); 185 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 186 | context.fulfiller, 187 | makerOrder 188 | ); 189 | 190 | if (context.listOnChain) { 191 | _notImplemented(); 192 | } 193 | execution.executeOrder = TestCallParameters( 194 | address(looksRare), 195 | ethAmount, 196 | abi.encodeWithSelector( 197 | ILooksRareExchange.matchAskWithTakerBidUsingETHAndWETH.selector, 198 | takerOrder, 199 | makerOrder 200 | ) 201 | ); 202 | } 203 | 204 | function getPayload_BuyOfferedERC721WithERC20( 205 | TestOrderContext calldata context, 206 | TestItem721 calldata nft, 207 | TestItem20 calldata erc20 208 | ) external view override returns (TestOrderPayload memory execution) { 209 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 210 | true, 211 | context.offerer, 212 | erc20.token, 213 | erc20.amount, 214 | nft.token, 215 | 1, 216 | nft.identifier 217 | ); 218 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 219 | context.fulfiller, 220 | makerOrder 221 | ); 222 | 223 | if (context.listOnChain) { 224 | _notImplemented(); 225 | } 226 | 227 | execution.executeOrder = TestCallParameters( 228 | address(looksRare), 229 | 0, 230 | abi.encodeWithSelector( 231 | ILooksRareExchange.matchAskWithTakerBid.selector, 232 | takerOrder, 233 | makerOrder 234 | ) 235 | ); 236 | } 237 | 238 | function getPayload_BuyOfferedERC721WithWETH( 239 | TestOrderContext calldata context, 240 | TestItem721 calldata nft, 241 | TestItem20 calldata erc20 242 | ) external view override returns (TestOrderPayload memory execution) { 243 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 244 | true, 245 | context.offerer, 246 | erc20.token, 247 | erc20.amount, 248 | nft.token, 249 | 1, 250 | nft.identifier 251 | ); 252 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 253 | context.fulfiller, 254 | makerOrder 255 | ); 256 | 257 | if (context.listOnChain) { 258 | _notImplemented(); 259 | } 260 | 261 | execution.executeOrder = TestCallParameters( 262 | address(looksRare), 263 | 0, 264 | abi.encodeWithSelector( 265 | ILooksRareExchange.matchAskWithTakerBid.selector, 266 | takerOrder, 267 | makerOrder 268 | ) 269 | ); 270 | } 271 | 272 | function getPayload_BuyOfferedERC1155WithERC20( 273 | TestOrderContext calldata context, 274 | TestItem1155 calldata nft, 275 | TestItem20 calldata erc20 276 | ) external view override returns (TestOrderPayload memory execution) { 277 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 278 | true, 279 | context.offerer, 280 | erc20.token, 281 | erc20.amount, 282 | nft.token, 283 | nft.amount, 284 | nft.identifier 285 | ); 286 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 287 | context.fulfiller, 288 | makerOrder 289 | ); 290 | 291 | if (context.listOnChain) { 292 | _notImplemented(); 293 | } 294 | 295 | execution.executeOrder = TestCallParameters( 296 | address(looksRare), 297 | 0, 298 | abi.encodeWithSelector( 299 | ILooksRareExchange.matchAskWithTakerBid.selector, 300 | takerOrder, 301 | makerOrder 302 | ) 303 | ); 304 | } 305 | 306 | function getPayload_BuyOfferedERC20WithERC721( 307 | TestOrderContext calldata context, 308 | TestItem20 calldata erc20, 309 | TestItem721 calldata nft 310 | ) external view override returns (TestOrderPayload memory execution) { 311 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 312 | false, 313 | context.offerer, 314 | erc20.token, 315 | erc20.amount, 316 | nft.token, 317 | 1, 318 | nft.identifier 319 | ); 320 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 321 | context.fulfiller, 322 | makerOrder 323 | ); 324 | 325 | if (context.listOnChain) { 326 | _notImplemented(); 327 | } 328 | 329 | execution.executeOrder = TestCallParameters( 330 | address(looksRare), 331 | 0, 332 | abi.encodeWithSelector( 333 | ILooksRareExchange.matchBidWithTakerAsk.selector, 334 | takerOrder, 335 | makerOrder 336 | ) 337 | ); 338 | } 339 | 340 | function getPayload_BuyOfferedWETHWithERC721( 341 | TestOrderContext calldata context, 342 | TestItem20 calldata erc20, 343 | TestItem721 calldata nft 344 | ) external view override returns (TestOrderPayload memory execution) { 345 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 346 | false, 347 | context.offerer, 348 | erc20.token, 349 | erc20.amount, 350 | nft.token, 351 | 1, 352 | nft.identifier 353 | ); 354 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 355 | context.fulfiller, 356 | makerOrder 357 | ); 358 | 359 | if (context.listOnChain) { 360 | _notImplemented(); 361 | } 362 | 363 | execution.executeOrder = TestCallParameters( 364 | address(looksRare), 365 | 0, 366 | abi.encodeWithSelector( 367 | ILooksRareExchange.matchBidWithTakerAsk.selector, 368 | takerOrder, 369 | makerOrder 370 | ) 371 | ); 372 | } 373 | 374 | function getPayload_BuyOfferedERC20WithERC1155( 375 | TestOrderContext calldata context, 376 | TestItem20 calldata erc20, 377 | TestItem1155 calldata nft 378 | ) external view override returns (TestOrderPayload memory execution) { 379 | OrderTypes.MakerOrder memory makerOrder = buildMakerOrder( 380 | false, 381 | context.offerer, 382 | erc20.token, 383 | erc20.amount, 384 | nft.token, 385 | nft.amount, 386 | nft.identifier 387 | ); 388 | OrderTypes.TakerOrder memory takerOrder = buildTakerOrder( 389 | context.fulfiller, 390 | makerOrder 391 | ); 392 | 393 | if (context.listOnChain) { 394 | _notImplemented(); 395 | } 396 | 397 | execution.executeOrder = TestCallParameters( 398 | address(looksRare), 399 | 0, 400 | abi.encodeWithSelector( 401 | ILooksRareExchange.matchBidWithTakerAsk.selector, 402 | takerOrder, 403 | makerOrder 404 | ) 405 | ); 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /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 | 211 | IRouter.PairSwapSpecific[] 212 | memory swapList = new IRouter.PairSwapSpecific[](1); 213 | swapList[0] = IRouter.PairSwapSpecific({ 214 | pair: address(erc20NftPool), 215 | nftIds: nftIds 216 | }); 217 | execution.executeOrder = TestCallParameters({ 218 | target: address(ROUTER), 219 | value: 0, 220 | data: abi.encodeWithSelector( 221 | IRouter.swapERC20ForSpecificNFTs.selector, 222 | swapList, 223 | erc20.amount, 224 | context.fulfiller, 225 | type(uint256).max 226 | ) 227 | }); 228 | } 229 | 230 | function getPayload_BuyOfferedERC20WithERC721( 231 | TestOrderContext calldata context, 232 | TestItem20 calldata erc20, 233 | TestItem721 calldata nft 234 | ) external override returns (TestOrderPayload memory execution) { 235 | if ( 236 | !context.listOnChain || 237 | nft.token != erc721Address || 238 | erc20.token != erc20Address || 239 | erc20.amount != NFT_PRICE 240 | ) _notImplemented(); 241 | 242 | // update market address so tests know where the ERC20 will be escrowed 243 | currentMarket = address(erc20TokenPool); 244 | 245 | // construct submitOrder payload 246 | // offerer transfers ERC20 to erc20TokenPool 247 | execution.submitOrder = TestCallParameters({ 248 | target: erc20.token, 249 | value: 0, 250 | data: abi.encodeWithSelector( 251 | ERC20.transfer.selector, 252 | address(erc20TokenPool), 253 | erc20.amount 254 | ) 255 | }); 256 | 257 | // construct executeOrder payload 258 | // fulfiller calls router 259 | uint256[] memory nftIds = new uint256[](1); 260 | nftIds[0] = nft.identifier; 261 | 262 | IRouter.PairSwapSpecific[] 263 | memory swapList = new IRouter.PairSwapSpecific[](1); 264 | swapList[0] = IRouter.PairSwapSpecific({ 265 | pair: address(erc20TokenPool), 266 | nftIds: nftIds 267 | }); 268 | execution.executeOrder = TestCallParameters({ 269 | target: address(ROUTER), 270 | value: 0, 271 | data: abi.encodeWithSelector( 272 | IRouter.swapNFTsForToken.selector, 273 | swapList, 274 | 0, 275 | context.fulfiller, 276 | type(uint256).max 277 | ) 278 | }); 279 | } 280 | 281 | function getPayload_BuyOfferedManyERC721WithEther( 282 | TestOrderContext calldata context, 283 | TestItem721[] calldata nfts, 284 | uint256 ethAmount 285 | ) external override returns (TestOrderPayload memory execution) { 286 | if (!context.listOnChain) _notImplemented(); 287 | 288 | // update market address so tests know where the ERC721 will be escrowed 289 | currentMarket = address(ethNftPool); 290 | 291 | address nftAddress = nfts[0].token; 292 | 293 | uint256[] memory ids = new uint256[](nfts.length); 294 | for (uint256 i = 0; i < nfts.length; i++) { 295 | ids[i] = nfts[i].identifier; 296 | } 297 | 298 | // construct submitOrder payload 299 | // offerer transfers 10 ERC721s to ethNftPool 300 | execution.submitOrder = TestCallParameters({ 301 | target: address(ROUTER), 302 | value: 0, 303 | data: abi.encodeWithSignature( 304 | "depositNFTs(address,uint256[],address)", 305 | nftAddress, 306 | ids, 307 | address(ethNftPool) 308 | ) 309 | }); 310 | 311 | // construct executeOrder payload 312 | // fulfiller calls pair directly to swap 313 | execution.executeOrder = TestCallParameters({ 314 | target: address(ethNftPool), 315 | value: ethAmount, 316 | data: abi.encodeWithSelector( 317 | IPair.swapTokenForSpecificNFTs.selector, 318 | ids, 319 | type(uint256).max, 320 | context.fulfiller, 321 | false, 322 | address(0) 323 | ) 324 | }); 325 | } 326 | 327 | function getPayload_BuyOfferedManyERC721WithEtherDistinctOrders( 328 | TestOrderContext[] calldata contexts, 329 | TestItem721[] calldata nfts, 330 | uint256[] calldata ethAmounts 331 | ) external view override returns (TestOrderPayload memory execution) { 332 | if (!contexts[0].listOnChain) _notImplemented(); 333 | 334 | address[] memory pools = new address[](nfts.length); 335 | uint256[] memory ids = new uint256[](nfts.length); 336 | 337 | for (uint256 i = 0; i < nfts.length; i++) { 338 | pools[i] = address(ethNftPoolsForDistinct[i]); 339 | ids[i] = nfts[i].identifier; 340 | } 341 | 342 | execution.submitOrder = TestCallParameters({ 343 | target: address(ROUTER), 344 | value: 0, 345 | data: abi.encodeWithSignature( 346 | "depositNFTs(address,uint256[],address[])", 347 | nfts[0].token, 348 | ids, 349 | pools 350 | ) 351 | }); 352 | 353 | // construct executeOrder payload 354 | // fulfiller calls router 355 | 356 | IRouter.PairSwapSpecific[] 357 | memory swapList = new IRouter.PairSwapSpecific[](nfts.length); 358 | 359 | for (uint256 i = 0; i < nfts.length; i++) { 360 | uint256[] memory singleId = new uint256[](1); 361 | singleId[0] = nfts[i].identifier; 362 | swapList[i] = IRouter.PairSwapSpecific({ 363 | pair: address(ethNftPoolsForDistinct[i]), 364 | nftIds: singleId 365 | }); 366 | } 367 | 368 | uint256 totalEthAmount = 0; 369 | for (uint256 i = 0; i < ethAmounts.length; i++) { 370 | totalEthAmount += ethAmounts[i]; 371 | } 372 | 373 | execution.executeOrder = TestCallParameters({ 374 | target: address(ROUTER), 375 | value: totalEthAmount, 376 | data: abi.encodeWithSelector( 377 | IRouter.swapETHForSpecificNFTs.selector, 378 | swapList, 379 | contexts[0].offerer, 380 | contexts[0].fulfiller, 381 | type(uint256).max 382 | ) 383 | }); 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /src/marketplaces/zeroEx/interfaces/IZeroEx.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "../lib/LibNFTOrder.sol"; 5 | import "../lib/LibSignature.sol"; 6 | 7 | interface IZeroEx { 8 | /// IERC721OrdersFeature 9 | 10 | /// @dev Sells an ERC721 asset to fill the given order. 11 | /// @param buyOrder The ERC721 buy order. 12 | /// @param signature The order signature from the maker. 13 | /// @param erc721TokenId The ID of the ERC721 asset being 14 | /// sold. If the given order specifies properties, 15 | /// the asset must satisfy those properties. Otherwise, 16 | /// it must equal the tokenId in the order. 17 | /// @param unwrapNativeToken If this parameter is true and the 18 | /// ERC20 token of the order is e.g. WETH, unwraps the 19 | /// token before transferring it to the taker. 20 | /// @param callbackData If this parameter is non-zero, invokes 21 | /// `zeroExERC721OrderCallback` on `msg.sender` after 22 | /// the ERC20 tokens have been transferred to `msg.sender` 23 | /// but before transferring the ERC721 asset to the buyer. 24 | function sellERC721( 25 | LibNFTOrder.ERC721Order calldata buyOrder, 26 | LibSignature.Signature calldata signature, 27 | uint256 erc721TokenId, 28 | bool unwrapNativeToken, 29 | bytes calldata callbackData 30 | ) external; 31 | 32 | /// @dev Buys an ERC721 asset by filling the given order. 33 | /// @param sellOrder The ERC721 sell order. 34 | /// @param signature The order signature. 35 | /// @param callbackData If this parameter is non-zero, invokes 36 | /// `zeroExERC721OrderCallback` on `msg.sender` after 37 | /// the ERC721 asset has been transferred to `msg.sender` 38 | /// but before transferring the ERC20 tokens to the seller. 39 | /// Native tokens acquired during the callback can be used 40 | /// to fill the order. 41 | function buyERC721( 42 | LibNFTOrder.ERC721Order calldata sellOrder, 43 | LibSignature.Signature calldata signature, 44 | bytes calldata callbackData 45 | ) external payable; 46 | 47 | /// @dev Cancel a single ERC721 order by its nonce. The caller 48 | /// should be the maker of the order. Silently succeeds if 49 | /// an order with the same nonce has already been filled or 50 | /// cancelled. 51 | /// @param orderNonce The order nonce. 52 | function cancelERC721Order(uint256 orderNonce) external; 53 | 54 | /// @dev Cancel multiple ERC721 orders by their nonces. The caller 55 | /// should be the maker of the orders. Silently succeeds if 56 | /// an order with the same nonce has already been filled or 57 | /// cancelled. 58 | /// @param orderNonces The order nonces. 59 | function batchCancelERC721Orders(uint256[] calldata orderNonces) external; 60 | 61 | /// @dev Buys multiple ERC721 assets by filling the 62 | /// given orders. 63 | /// @param sellOrders The ERC721 sell orders. 64 | /// @param signatures The order signatures. 65 | /// @param callbackData The data (if any) to pass to the taker 66 | /// callback for each order. Refer to the `callbackData` 67 | /// parameter to for `buyERC721`. 68 | /// @param revertIfIncomplete If true, reverts if this 69 | /// function fails to fill any individual order. 70 | /// @return successes An array of booleans corresponding to whether 71 | /// each order in `orders` was successfully filled. 72 | function batchBuyERC721s( 73 | LibNFTOrder.ERC721Order[] calldata sellOrders, 74 | LibSignature.Signature[] calldata signatures, 75 | bytes[] calldata callbackData, 76 | bool revertIfIncomplete 77 | ) external payable returns (bool[] memory successes); 78 | 79 | /// @dev Matches a pair of complementary orders that have 80 | /// a non-negative spread. Each order is filled at 81 | /// their respective price, and the matcher receives 82 | /// a profit denominated in the ERC20 token. 83 | /// @param sellOrder Order selling an ERC721 asset. 84 | /// @param buyOrder Order buying an ERC721 asset. 85 | /// @param sellOrderSignature Signature for the sell order. 86 | /// @param buyOrderSignature Signature for the buy order. 87 | /// @return profit The amount of profit earned by the caller 88 | /// of this function (denominated in the ERC20 token 89 | /// of the matched orders). 90 | function matchERC721Orders( 91 | LibNFTOrder.ERC721Order calldata sellOrder, 92 | LibNFTOrder.ERC721Order calldata buyOrder, 93 | LibSignature.Signature calldata sellOrderSignature, 94 | LibSignature.Signature calldata buyOrderSignature 95 | ) external returns (uint256 profit); 96 | 97 | /// @dev Matches pairs of complementary orders that have 98 | /// non-negative spreads. Each order is filled at 99 | /// their respective price, and the matcher receives 100 | /// a profit denominated in the ERC20 token. 101 | /// @param sellOrders Orders selling ERC721 assets. 102 | /// @param buyOrders Orders buying ERC721 assets. 103 | /// @param sellOrderSignatures Signatures for the sell orders. 104 | /// @param buyOrderSignatures Signatures for the buy orders. 105 | /// @return profits The amount of profit earned by the caller 106 | /// of this function for each pair of matched orders 107 | /// (denominated in the ERC20 token of the order pair). 108 | /// @return successes An array of booleans corresponding to 109 | /// whether each pair of orders was successfully matched. 110 | function batchMatchERC721Orders( 111 | LibNFTOrder.ERC721Order[] calldata sellOrders, 112 | LibNFTOrder.ERC721Order[] calldata buyOrders, 113 | LibSignature.Signature[] calldata sellOrderSignatures, 114 | LibSignature.Signature[] calldata buyOrderSignatures 115 | ) external returns (uint256[] memory profits, bool[] memory successes); 116 | 117 | /// @dev Callback for the ERC721 `safeTransferFrom` function. 118 | /// This callback can be used to sell an ERC721 asset if 119 | /// a valid ERC721 order, signature and `unwrapNativeToken` 120 | /// are encoded in `data`. This allows takers to sell their 121 | /// ERC721 asset without first calling `setApprovalForAll`. 122 | /// @param operator The address which called `safeTransferFrom`. 123 | /// @param from The address which previously owned the token. 124 | /// @param tokenId The ID of the asset being transferred. 125 | /// @param data Additional data with no specified format. If a 126 | /// valid ERC721 order, signature and `unwrapNativeToken` 127 | /// are encoded in `data`, this function will try to fill 128 | /// the order using the received asset. 129 | /// @return success The selector of this function (0x150b7a02), 130 | /// indicating that the callback succeeded. 131 | function onERC721Received( 132 | address operator, 133 | address from, 134 | uint256 tokenId, 135 | bytes calldata data 136 | ) external returns (bytes4 success); 137 | 138 | /// @dev Approves an ERC721 order on-chain. After pre-signing 139 | /// the order, the `PRESIGNED` signature type will become 140 | /// valid for that order and signer. 141 | /// @param order An ERC721 order. 142 | function preSignERC721Order(LibNFTOrder.ERC721Order calldata order) 143 | external; 144 | 145 | /// @dev Checks whether the given signature is valid for the 146 | /// the given ERC721 order. Reverts if not. 147 | /// @param order The ERC721 order. 148 | /// @param signature The signature to validate. 149 | function validateERC721OrderSignature( 150 | LibNFTOrder.ERC721Order calldata order, 151 | LibSignature.Signature calldata signature 152 | ) external view; 153 | 154 | /// @dev If the given order is buying an ERC721 asset, checks 155 | /// whether or not the given token ID satisfies the required 156 | /// properties specified in the order. If the order does not 157 | /// specify any properties, this function instead checks 158 | /// whether the given token ID matches the ID in the order. 159 | /// Reverts if any checks fail, or if the order is selling 160 | /// an ERC721 asset. 161 | /// @param order The ERC721 order. 162 | /// @param erc721TokenId The ID of the ERC721 asset. 163 | function validateERC721OrderProperties( 164 | LibNFTOrder.ERC721Order calldata order, 165 | uint256 erc721TokenId 166 | ) external view; 167 | 168 | /// @dev Get the current status of an ERC721 order. 169 | /// @param order The ERC721 order. 170 | /// @return status The status of the order. 171 | function getERC721OrderStatus(LibNFTOrder.ERC721Order calldata order) 172 | external 173 | view 174 | returns (LibNFTOrder.OrderStatus status); 175 | 176 | /// @dev Get the EIP-712 hash of an ERC721 order. 177 | /// @param order The ERC721 order. 178 | /// @return orderHash The order hash. 179 | function getERC721OrderHash(LibNFTOrder.ERC721Order calldata order) 180 | external 181 | view 182 | returns (bytes32 orderHash); 183 | 184 | /// @dev Get the order status bit vector for the given 185 | /// maker address and nonce range. 186 | /// @param maker The maker of the order. 187 | /// @param nonceRange Order status bit vectors are indexed 188 | /// by maker address and the upper 248 bits of the 189 | /// order nonce. We define `nonceRange` to be these 190 | /// 248 bits. 191 | /// @return bitVector The order status bit vector for the 192 | /// given maker and nonce range. 193 | function getERC721OrderStatusBitVector(address maker, uint248 nonceRange) 194 | external 195 | view 196 | returns (uint256 bitVector); 197 | 198 | /// IERC1155OrdersFeature 199 | 200 | /// @dev Sells an ERC1155 asset to fill the given order. 201 | /// @param buyOrder The ERC1155 buy order. 202 | /// @param signature The order signature from the maker. 203 | /// @param erc1155TokenId The ID of the ERC1155 asset being 204 | /// sold. If the given order specifies properties, 205 | /// the asset must satisfy those properties. Otherwise, 206 | /// it must equal the tokenId in the order. 207 | /// @param erc1155SellAmount The amount of the ERC1155 asset 208 | /// to sell. 209 | /// @param unwrapNativeToken If this parameter is true and the 210 | /// ERC20 token of the order is e.g. WETH, unwraps the 211 | /// token before transferring it to the taker. 212 | /// @param callbackData If this parameter is non-zero, invokes 213 | /// `zeroExERC1155OrderCallback` on `msg.sender` after 214 | /// the ERC20 tokens have been transferred to `msg.sender` 215 | /// but before transferring the ERC1155 asset to the buyer. 216 | function sellERC1155( 217 | LibNFTOrder.ERC1155Order calldata buyOrder, 218 | LibSignature.Signature calldata signature, 219 | uint256 erc1155TokenId, 220 | uint128 erc1155SellAmount, 221 | bool unwrapNativeToken, 222 | bytes calldata callbackData 223 | ) external; 224 | 225 | /// @dev Buys an ERC1155 asset by filling the given order. 226 | /// @param sellOrder The ERC1155 sell order. 227 | /// @param signature The order signature. 228 | /// @param erc1155BuyAmount The amount of the ERC1155 asset 229 | /// to buy. 230 | /// @param callbackData If this parameter is non-zero, invokes 231 | /// `zeroExERC1155OrderCallback` on `msg.sender` after 232 | /// the ERC1155 asset has been transferred to `msg.sender` 233 | /// but before transferring the ERC20 tokens to the seller. 234 | /// Native tokens acquired during the callback can be used 235 | /// to fill the order. 236 | function buyERC1155( 237 | LibNFTOrder.ERC1155Order calldata sellOrder, 238 | LibSignature.Signature calldata signature, 239 | uint128 erc1155BuyAmount, 240 | bytes calldata callbackData 241 | ) external payable; 242 | 243 | /// @dev Cancel a single ERC1155 order by its nonce. The caller 244 | /// should be the maker of the order. Silently succeeds if 245 | /// an order with the same nonce has already been filled or 246 | /// cancelled. 247 | /// @param orderNonce The order nonce. 248 | function cancelERC1155Order(uint256 orderNonce) external; 249 | 250 | /// @dev Cancel multiple ERC1155 orders by their nonces. The caller 251 | /// should be the maker of the orders. Silently succeeds if 252 | /// an order with the same nonce has already been filled or 253 | /// cancelled. 254 | /// @param orderNonces The order nonces. 255 | function batchCancelERC1155Orders(uint256[] calldata orderNonces) external; 256 | 257 | /// @dev Buys multiple ERC1155 assets by filling the 258 | /// given orders. 259 | /// @param sellOrders The ERC1155 sell orders. 260 | /// @param signatures The order signatures. 261 | /// @param erc1155TokenAmounts The amounts of the ERC1155 assets 262 | /// to buy for each order. 263 | /// @param callbackData The data (if any) to pass to the taker 264 | /// callback for each order. Refer to the `callbackData` 265 | /// parameter to for `buyERC1155`. 266 | /// @param revertIfIncomplete If true, reverts if this 267 | /// function fails to fill any individual order. 268 | /// @return successes An array of booleans corresponding to whether 269 | /// each order in `orders` was successfully filled. 270 | function batchBuyERC1155s( 271 | LibNFTOrder.ERC1155Order[] calldata sellOrders, 272 | LibSignature.Signature[] calldata signatures, 273 | uint128[] calldata erc1155TokenAmounts, 274 | bytes[] calldata callbackData, 275 | bool revertIfIncomplete 276 | ) external payable returns (bool[] memory successes); 277 | 278 | /// @dev Callback for the ERC1155 `safeTransferFrom` function. 279 | /// This callback can be used to sell an ERC1155 asset if 280 | /// a valid ERC1155 order, signature and `unwrapNativeToken` 281 | /// are encoded in `data`. This allows takers to sell their 282 | /// ERC1155 asset without first calling `setApprovalForAll`. 283 | /// @param operator The address which called `safeTransferFrom`. 284 | /// @param from The address which previously owned the token. 285 | /// @param tokenId The ID of the asset being transferred. 286 | /// @param value The amount being transferred. 287 | /// @param data Additional data with no specified format. If a 288 | /// valid ERC1155 order, signature and `unwrapNativeToken` 289 | /// are encoded in `data`, this function will try to fill 290 | /// the order using the received asset. 291 | /// @return success The selector of this function (0xf23a6e61), 292 | /// indicating that the callback succeeded. 293 | function onERC1155Received( 294 | address operator, 295 | address from, 296 | uint256 tokenId, 297 | uint256 value, 298 | bytes calldata data 299 | ) external returns (bytes4 success); 300 | 301 | /// @dev Approves an ERC1155 order on-chain. After pre-signing 302 | /// the order, the `PRESIGNED` signature type will become 303 | /// valid for that order and signer. 304 | /// @param order An ERC1155 order. 305 | function preSignERC1155Order(LibNFTOrder.ERC1155Order calldata order) 306 | external; 307 | 308 | /// @dev Checks whether the given signature is valid for the 309 | /// the given ERC1155 order. Reverts if not. 310 | /// @param order The ERC1155 order. 311 | /// @param signature The signature to validate. 312 | function validateERC1155OrderSignature( 313 | LibNFTOrder.ERC1155Order calldata order, 314 | LibSignature.Signature calldata signature 315 | ) external view; 316 | 317 | /// @dev If the given order is buying an ERC1155 asset, checks 318 | /// whether or not the given token ID satisfies the required 319 | /// properties specified in the order. If the order does not 320 | /// specify any properties, this function instead checks 321 | /// whether the given token ID matches the ID in the order. 322 | /// Reverts if any checks fail, or if the order is selling 323 | /// an ERC1155 asset. 324 | /// @param order The ERC1155 order. 325 | /// @param erc1155TokenId The ID of the ERC1155 asset. 326 | function validateERC1155OrderProperties( 327 | LibNFTOrder.ERC1155Order calldata order, 328 | uint256 erc1155TokenId 329 | ) external view; 330 | 331 | /// @dev Get the order info for an ERC1155 order. 332 | /// @param order The ERC1155 order. 333 | /// @return orderInfo Infor about the order. 334 | function getERC1155OrderInfo(LibNFTOrder.ERC1155Order calldata order) 335 | external 336 | view 337 | returns (LibNFTOrder.OrderInfo memory orderInfo); 338 | 339 | /// @dev Get the EIP-712 hash of an ERC1155 order. 340 | /// @param order The ERC1155 order. 341 | /// @return orderHash The order hash. 342 | function getERC1155OrderHash(LibNFTOrder.ERC1155Order calldata order) 343 | external 344 | view 345 | returns (bytes32 orderHash); 346 | } 347 | -------------------------------------------------------------------------------- /src/marketplaces/X2Y2/X2Y2Config.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import { IX2Y2Marketplace } from "./interfaces/IX2Y2Marketplace.sol"; 5 | import { BaseMarketConfig } from "../../BaseMarketConfig.sol"; 6 | import { Market } from "./interfaces/MarketConstants.sol"; 7 | import { X2Y2TypeHashes } from "./lib/X2Y2TypeHashes.sol"; 8 | import { SetupCall, TestCallParameters, TestOrderContext, TestOrderPayload, TestItem721, TestItem1155, TestItem20 } from "../../Types.sol"; 9 | 10 | contract X2Y2Config is BaseMarketConfig, X2Y2TypeHashes { 11 | IX2Y2Marketplace internal constant X2Y2 = 12 | IX2Y2Marketplace(0x74312363e45DCaBA76c59ec49a7Aa8A65a67EeD3); 13 | 14 | address internal constant X2Y2Owner = 15 | 0x5D7CcA9Fb832BBD99C8bD720EbdA39B028648301; 16 | 17 | address internal constant erc721Delegate = 18 | 0xF849de01B080aDC3A814FaBE1E2087475cF2E354; 19 | 20 | address internal X2Y2Signer; 21 | 22 | function name() external pure override returns (string memory) { 23 | return "X2Y2"; 24 | } 25 | 26 | function market() public pure override returns (address) { 27 | return address(X2Y2); 28 | } 29 | 30 | function beforeAllPrepareMarketplace(address, address) external override { 31 | buyerNftApprovalTarget = sellerNftApprovalTarget = erc721Delegate; 32 | buyerErc20ApprovalTarget = sellerErc20ApprovalTarget = address(X2Y2); 33 | } 34 | 35 | function beforeAllPrepareMarketplaceCall( 36 | address seller, 37 | address, 38 | address[] calldata, 39 | address[] calldata 40 | ) external override returns (SetupCall[] memory) { 41 | SetupCall[] memory setupCalls = new SetupCall[](1); 42 | 43 | address[] memory removeSigners = new address[](0); 44 | address[] memory addSigners = new address[](1); 45 | addSigners[0] = seller; 46 | 47 | // Set seller as a signer for X2Y2 48 | setupCalls[0] = SetupCall( 49 | X2Y2Owner, 50 | address(X2Y2), 51 | abi.encodeWithSelector( 52 | IX2Y2Marketplace.updateSigners.selector, 53 | addSigners, 54 | removeSigners 55 | ) 56 | ); 57 | 58 | X2Y2Signer = seller; 59 | 60 | return setupCalls; 61 | } 62 | 63 | function encodeFillOrderDistinctOrders( 64 | TestOrderContext[] calldata contexts, 65 | TestItem721[] memory nfts, 66 | uint256[] memory prices, 67 | address currency, 68 | uint256 intent 69 | ) internal view returns (bytes memory payload, uint256 ethSum) { 70 | Market.RunInput memory input; 71 | 72 | input.shared.user = contexts[0].fulfiller; 73 | input.shared.deadline = block.timestamp + 1; 74 | 75 | Market.Order[] memory orders = new Market.Order[](nfts.length); 76 | Market.SettleDetail[] memory details = new Market.SettleDetail[]( 77 | nfts.length 78 | ); 79 | 80 | for (uint256 i = 0; i < nfts.length; i++) { 81 | { 82 | ethSum += prices[i]; 83 | } 84 | { 85 | orders[i].user = contexts[i].offerer; 86 | orders[i].network = 1; 87 | orders[i].intent = intent; 88 | orders[i].delegateType = 1; 89 | orders[i].deadline = block.timestamp + 1; 90 | orders[i].currency = currency; 91 | 92 | Market.OrderItem[] memory items = new Market.OrderItem[](1); 93 | 94 | Market.Pair[] memory itemPairs = new Market.Pair[](1); 95 | itemPairs[0] = Market.Pair(nfts[i].token, nfts[i].identifier); 96 | 97 | items[0] = Market.OrderItem(prices[i], abi.encode(itemPairs)); 98 | 99 | orders[i].items = items; 100 | 101 | (orders[i].v, orders[i].r, orders[i].s) = _sign( 102 | contexts[i].offerer, 103 | _deriveOrderDigest(orders[i]) 104 | ); 105 | orders[i].signVersion = Market.SIGN_V1; 106 | } 107 | { 108 | details[i].op = intent == Market.INTENT_SELL 109 | ? Market.Op.COMPLETE_SELL_OFFER 110 | : Market.Op.COMPLETE_BUY_OFFER; 111 | details[i].orderIdx = i; 112 | details[i].itemIdx = 0; 113 | details[i].price = prices[i]; 114 | details[i].itemHash = _hashItem(orders[i], orders[i].items[0]); 115 | details[i].executionDelegate = erc721Delegate; 116 | } 117 | } 118 | 119 | input.orders = orders; 120 | input.details = details; 121 | 122 | (input.v, input.r, input.s) = _sign( 123 | X2Y2Signer, 124 | _deriveInputDigest(input) 125 | ); 126 | 127 | payload = abi.encodeWithSelector(IX2Y2Marketplace.run.selector, input); 128 | } 129 | 130 | function encodeFillOrder( 131 | address offerer, 132 | address fulfiller, 133 | TestItem721[] memory nfts, 134 | uint256 price, 135 | address currency, 136 | uint256 intent, 137 | Market.Fee[] memory fees 138 | ) internal view returns (bytes memory) { 139 | Market.RunInput memory input; 140 | 141 | input.shared.user = fulfiller; 142 | input.shared.deadline = block.timestamp + 1; 143 | 144 | Market.Order[] memory orders = new Market.Order[](1); 145 | orders[0].user = offerer; 146 | orders[0].network = 1; 147 | orders[0].intent = intent; 148 | orders[0].delegateType = 1; 149 | orders[0].deadline = block.timestamp + 1; 150 | orders[0].currency = currency; 151 | 152 | Market.OrderItem[] memory items = new Market.OrderItem[](1); 153 | 154 | Market.Pair[] memory itemPairs = new Market.Pair[](nfts.length); 155 | 156 | for (uint256 i = 0; i < nfts.length; i++) { 157 | itemPairs[i] = Market.Pair(nfts[i].token, nfts[i].identifier); 158 | } 159 | 160 | items[0] = Market.OrderItem(price, abi.encode(itemPairs)); 161 | 162 | orders[0].items = items; 163 | 164 | (orders[0].v, orders[0].r, orders[0].s) = _sign( 165 | offerer, 166 | _deriveOrderDigest(orders[0]) 167 | ); 168 | orders[0].signVersion = Market.SIGN_V1; 169 | 170 | input.orders = orders; 171 | 172 | Market.SettleDetail[] memory details = new Market.SettleDetail[](1); 173 | details[0].op = intent == Market.INTENT_SELL 174 | ? Market.Op.COMPLETE_SELL_OFFER 175 | : Market.Op.COMPLETE_BUY_OFFER; 176 | details[0].orderIdx = 0; 177 | details[0].itemIdx = 0; 178 | details[0].price = price; 179 | details[0].fees = fees; 180 | details[0].itemHash = _hashItem(orders[0], orders[0].items[0]); 181 | details[0].executionDelegate = erc721Delegate; 182 | input.details = details; 183 | 184 | (input.v, input.r, input.s) = _sign( 185 | X2Y2Signer, 186 | _deriveInputDigest(input) 187 | ); 188 | 189 | return abi.encodeWithSelector(IX2Y2Marketplace.run.selector, input); 190 | } 191 | 192 | function getPayload_BuyOfferedERC721WithEther( 193 | TestOrderContext calldata context, 194 | TestItem721 calldata nft, 195 | uint256 ethAmount 196 | ) external view override returns (TestOrderPayload memory execution) { 197 | if (context.listOnChain) { 198 | _notImplemented(); 199 | } 200 | 201 | TestItem721[] memory nfts = new TestItem721[](1); 202 | nfts[0] = nft; 203 | 204 | Market.Fee[] memory fees = new Market.Fee[](0); 205 | 206 | bytes memory payload = encodeFillOrder( 207 | context.offerer, 208 | context.fulfiller, 209 | nfts, 210 | ethAmount, 211 | address(0), 212 | Market.INTENT_SELL, 213 | fees 214 | ); 215 | 216 | execution.executeOrder = TestCallParameters( 217 | address(X2Y2), 218 | ethAmount, 219 | payload 220 | ); 221 | } 222 | 223 | function getPayload_BuyOfferedERC721WithERC20( 224 | TestOrderContext calldata context, 225 | TestItem721 calldata nft, 226 | TestItem20 calldata erc20 227 | ) external view override returns (TestOrderPayload memory execution) { 228 | if (context.listOnChain) { 229 | _notImplemented(); 230 | } 231 | 232 | TestItem721[] memory nfts = new TestItem721[](1); 233 | nfts[0] = nft; 234 | 235 | Market.Fee[] memory fees = new Market.Fee[](0); 236 | 237 | bytes memory payload = encodeFillOrder( 238 | context.offerer, 239 | context.fulfiller, 240 | nfts, 241 | erc20.amount, 242 | erc20.token, 243 | Market.INTENT_SELL, 244 | fees 245 | ); 246 | 247 | execution.executeOrder = TestCallParameters(address(X2Y2), 0, payload); 248 | } 249 | 250 | function getPayload_BuyOfferedERC721WithWETH( 251 | TestOrderContext calldata context, 252 | TestItem721 calldata nft, 253 | TestItem20 calldata erc20 254 | ) external view override returns (TestOrderPayload memory execution) { 255 | if (context.listOnChain) { 256 | _notImplemented(); 257 | } 258 | 259 | TestItem721[] memory nfts = new TestItem721[](1); 260 | nfts[0] = nft; 261 | 262 | Market.Fee[] memory fees = new Market.Fee[](0); 263 | 264 | bytes memory payload = encodeFillOrder( 265 | context.offerer, 266 | context.fulfiller, 267 | nfts, 268 | erc20.amount, 269 | erc20.token, 270 | Market.INTENT_SELL, 271 | fees 272 | ); 273 | 274 | execution.executeOrder = TestCallParameters(address(X2Y2), 0, payload); 275 | } 276 | 277 | function getPayload_BuyOfferedERC20WithERC721( 278 | TestOrderContext calldata context, 279 | TestItem20 calldata erc20, 280 | TestItem721 calldata nft 281 | ) external view override returns (TestOrderPayload memory execution) { 282 | if (context.listOnChain) { 283 | _notImplemented(); 284 | } 285 | 286 | TestItem721[] memory nfts = new TestItem721[](1); 287 | nfts[0] = nft; 288 | 289 | Market.Fee[] memory fees = new Market.Fee[](0); 290 | 291 | bytes memory payload = encodeFillOrder( 292 | context.offerer, 293 | context.fulfiller, 294 | nfts, 295 | erc20.amount, 296 | erc20.token, 297 | Market.INTENT_BUY, 298 | fees 299 | ); 300 | 301 | execution.executeOrder = TestCallParameters(address(X2Y2), 0, payload); 302 | } 303 | 304 | function getPayload_BuyOfferedWETHWithERC721( 305 | TestOrderContext calldata context, 306 | TestItem20 calldata erc20, 307 | TestItem721 calldata nft 308 | ) external view override returns (TestOrderPayload memory execution) { 309 | if (context.listOnChain) { 310 | _notImplemented(); 311 | } 312 | 313 | TestItem721[] memory nfts = new TestItem721[](1); 314 | nfts[0] = nft; 315 | 316 | Market.Fee[] memory fees = new Market.Fee[](0); 317 | 318 | bytes memory payload = encodeFillOrder( 319 | context.offerer, 320 | context.fulfiller, 321 | nfts, 322 | erc20.amount, 323 | erc20.token, 324 | Market.INTENT_BUY, 325 | fees 326 | ); 327 | 328 | execution.executeOrder = TestCallParameters(address(X2Y2), 0, payload); 329 | } 330 | 331 | function getPayload_BuyOfferedERC721WithEtherOneFeeRecipient( 332 | TestOrderContext calldata context, 333 | TestItem721 memory nft, 334 | uint256 priceEthAmount, 335 | address feeRecipient, 336 | uint256 feeEthAmount 337 | ) external view override returns (TestOrderPayload memory execution) { 338 | if (context.listOnChain) { 339 | _notImplemented(); 340 | } 341 | 342 | TestItem721[] memory nfts = new TestItem721[](1); 343 | nfts[0] = nft; 344 | 345 | Market.Fee[] memory fees = new Market.Fee[](1); 346 | fees[0] = Market.Fee( 347 | (feeEthAmount * 1000000) / (priceEthAmount + feeEthAmount) + 1, 348 | feeRecipient 349 | ); 350 | 351 | bytes memory payload = encodeFillOrder( 352 | context.offerer, 353 | context.fulfiller, 354 | nfts, 355 | priceEthAmount + feeEthAmount, 356 | address(0), 357 | Market.INTENT_SELL, 358 | fees 359 | ); 360 | 361 | execution.executeOrder = TestCallParameters( 362 | address(X2Y2), 363 | priceEthAmount + feeEthAmount, 364 | payload 365 | ); 366 | } 367 | 368 | function getPayload_BuyOfferedERC721WithEtherTwoFeeRecipient( 369 | TestOrderContext calldata context, 370 | TestItem721 memory nft, 371 | uint256 priceEthAmount, 372 | address feeRecipient1, 373 | uint256 feeEthAmount1, 374 | address feeRecipient2, 375 | uint256 feeEthAmount2 376 | ) external view override returns (TestOrderPayload memory execution) { 377 | if (context.listOnChain) { 378 | _notImplemented(); 379 | } 380 | 381 | TestItem721[] memory nfts = new TestItem721[](1); 382 | nfts[0] = nft; 383 | 384 | Market.Fee[] memory fees = new Market.Fee[](2); 385 | fees[0] = Market.Fee( 386 | (feeEthAmount1 * 1000000) / 387 | (priceEthAmount + feeEthAmount1 + feeEthAmount2) + 388 | 1, 389 | feeRecipient1 390 | ); 391 | fees[1] = Market.Fee( 392 | (feeEthAmount2 * 1000000) / 393 | (priceEthAmount + feeEthAmount1 + feeEthAmount2) + 394 | 1, 395 | feeRecipient2 396 | ); 397 | 398 | bytes memory payload = encodeFillOrder( 399 | context.offerer, 400 | context.fulfiller, 401 | nfts, 402 | priceEthAmount + feeEthAmount1 + feeEthAmount2, 403 | address(0), 404 | Market.INTENT_SELL, 405 | fees 406 | ); 407 | 408 | execution.executeOrder = TestCallParameters( 409 | address(X2Y2), 410 | priceEthAmount + feeEthAmount1 + feeEthAmount2, 411 | payload 412 | ); 413 | } 414 | 415 | function getPayload_BuyOfferedManyERC721WithEther( 416 | TestOrderContext calldata context, 417 | TestItem721[] calldata nfts, 418 | uint256 ethAmount 419 | ) external view override returns (TestOrderPayload memory execution) { 420 | if (context.listOnChain) { 421 | _notImplemented(); 422 | } 423 | 424 | Market.Fee[] memory fees = new Market.Fee[](0); 425 | 426 | bytes memory payload = encodeFillOrder( 427 | context.offerer, 428 | context.fulfiller, 429 | nfts, 430 | ethAmount, 431 | address(0), 432 | Market.INTENT_SELL, 433 | fees 434 | ); 435 | 436 | execution.executeOrder = TestCallParameters( 437 | address(X2Y2), 438 | ethAmount, 439 | payload 440 | ); 441 | } 442 | 443 | function getPayload_BuyOfferedManyERC721WithEtherDistinctOrders( 444 | TestOrderContext[] calldata contexts, 445 | TestItem721[] calldata nfts, 446 | uint256[] calldata ethAmounts 447 | ) external view override returns (TestOrderPayload memory execution) { 448 | if (contexts[0].listOnChain) { 449 | _notImplemented(); 450 | } 451 | 452 | (bytes memory payload, uint256 ethSum) = encodeFillOrderDistinctOrders( 453 | contexts, 454 | nfts, 455 | ethAmounts, 456 | address(0), 457 | Market.INTENT_SELL 458 | ); 459 | 460 | execution.executeOrder = TestCallParameters( 461 | address(X2Y2), 462 | ethSum, 463 | payload 464 | ); 465 | } 466 | 467 | function getPayload_BuyOfferedManyERC721WithErc20DistinctOrders( 468 | TestOrderContext[] calldata contexts, 469 | address erc20Address, 470 | TestItem721[] calldata nfts, 471 | uint256[] calldata erc20Amounts 472 | ) external view override returns (TestOrderPayload memory execution) { 473 | if (contexts[0].listOnChain) { 474 | _notImplemented(); 475 | } 476 | 477 | (bytes memory payload, ) = encodeFillOrderDistinctOrders( 478 | contexts, 479 | nfts, 480 | erc20Amounts, 481 | erc20Address, 482 | Market.INTENT_SELL 483 | ); 484 | 485 | execution.executeOrder = TestCallParameters(address(X2Y2), 0, payload); 486 | } 487 | 488 | function getPayload_BuyOfferedManyERC721WithWETHDistinctOrders( 489 | TestOrderContext[] calldata contexts, 490 | address erc20Address, 491 | TestItem721[] calldata nfts, 492 | uint256[] calldata erc20Amounts 493 | ) external view override returns (TestOrderPayload memory execution) { 494 | if (contexts[0].listOnChain) { 495 | _notImplemented(); 496 | } 497 | 498 | (bytes memory payload, ) = encodeFillOrderDistinctOrders( 499 | contexts, 500 | nfts, 501 | erc20Amounts, 502 | erc20Address, 503 | Market.INTENT_SELL 504 | ); 505 | 506 | execution.executeOrder = TestCallParameters(address(X2Y2), 0, payload); 507 | } 508 | } 509 | -------------------------------------------------------------------------------- /src/marketplaces/blur/BlurConfig.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.7; 3 | 4 | import { BaseMarketConfig } from "../../BaseMarketConfig.sol"; 5 | import { TestCallParameters, TestOrderContext, TestOrderPayload, TestItem721, TestItem1155, TestItem20, SetupCall } from "../../Types.sol"; 6 | import "./lib/OrderStructs.sol"; 7 | import "./lib/BlurTypeHashes.sol"; 8 | import { IBlurExchange } from "./interfaces/IBlurExchange.sol"; 9 | import "forge-std/console2.sol"; 10 | import { TestERC20 } from "../../../test/tokens/TestERC20.sol"; 11 | 12 | contract BlurConfig is BaseMarketConfig, BlurTypeHashes { 13 | function name() external pure override returns (string memory) { 14 | return "Blur"; 15 | } 16 | 17 | function market() public pure override returns (address) { 18 | return address(blur); 19 | } 20 | 21 | IBlurExchange internal constant blur = 22 | IBlurExchange(0x000000000000Ad05Ccc4F10045630fb830B95127); 23 | 24 | // The "execution delegate" — functions similarly to a conduit. 25 | address internal constant approvalTarget = 26 | 0x00000000000111AbE46ff893f3B2fdF1F759a8A8; 27 | 28 | // see "policy manager" at 0x3a35A3102b5c6bD1e4d3237248Be071EF53C8331 29 | address internal constant matchingPolicy = 30 | 0x00000000006411739DA1c40B106F8511de5D1FAC; 31 | 32 | address internal constant BlurOwner = 33 | 0x0000000000000000000000000000000000000000; 34 | 35 | // address internal constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 36 | TestERC20 internal constant weth = 37 | TestERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 38 | 39 | function beforeAllPrepareMarketplace(address, address) external override { 40 | buyerNftApprovalTarget = sellerNftApprovalTarget = buyerErc20ApprovalTarget = sellerErc20ApprovalTarget = address( 41 | approvalTarget 42 | ); 43 | } 44 | 45 | function beforeAllPrepareMarketplaceCall( 46 | address, 47 | address, 48 | address[] calldata, 49 | address[] calldata 50 | ) external pure override returns (SetupCall[] memory) { 51 | SetupCall[] memory setupCalls = new SetupCall[](1); 52 | 53 | setupCalls[0] = SetupCall( 54 | BlurOwner, 55 | address(blur), 56 | abi.encodeWithSelector(IBlurExchange.open.selector) 57 | ); 58 | 59 | return setupCalls; 60 | } 61 | 62 | function buildOrder( 63 | address creator, 64 | Side side, 65 | address nftContractAddress, 66 | uint256 nftTokenId, 67 | address paymentToken, 68 | uint256 paymentTokenAmount, 69 | Fee[] memory fee 70 | ) 71 | internal 72 | view 73 | returns ( 74 | Order memory _order, 75 | uint8 _v, 76 | bytes32 _r, 77 | bytes32 _s 78 | ) 79 | { 80 | Order memory order; 81 | 82 | order.trader = creator; 83 | order.side = side; 84 | 85 | order.matchingPolicy = matchingPolicy; 86 | 87 | order.collection = nftContractAddress; 88 | order.tokenId = nftTokenId; 89 | order.amount = 1; // TODO: Add suppport for amounts other than 1. 90 | order.paymentToken = paymentToken; 91 | order.price = paymentTokenAmount; 92 | order.listingTime = 0; 93 | order.expirationTime = block.timestamp + 1; 94 | order.fees = fee; 95 | order.salt = 0; 96 | order.extraParams = new bytes(0); 97 | 98 | (uint8 v, bytes32 r, bytes32 s) = _sign( 99 | creator, 100 | keccak256( 101 | abi.encodePacked( 102 | "\x19\x01", 103 | DOMAIN_SEPARATOR, 104 | _hashOrder(order, 0) // Nonce management might be a pain. 105 | ) 106 | ) 107 | ); 108 | 109 | return (order, v, r, s); 110 | } 111 | 112 | function buildInput( 113 | Order memory order, 114 | uint8 v, 115 | bytes32 r, 116 | bytes32 s, 117 | SignatureVersion signatureVersion 118 | ) internal view returns (Input memory _input) { 119 | Input memory input; 120 | 121 | input.order = order; 122 | input.v = v; 123 | input.r = r; 124 | input.s = s; 125 | input.extraSignature = new bytes(0); 126 | input.signatureVersion = signatureVersion; 127 | input.blockNumber = block.number; 128 | 129 | return input; 130 | } 131 | 132 | function buildInputPair( 133 | address maker, 134 | address taker, 135 | address nftContractAddress, 136 | uint256 nftTokenId, 137 | address paymentToken, 138 | uint256 paymentTokenAmount, 139 | Fee[] memory fee, 140 | bool isOffer 141 | ) internal view returns (Input memory makerInput, Input memory takerInput) { 142 | Order memory makerOrder; 143 | Order memory takerOrder; 144 | uint8 v; 145 | bytes32 r; 146 | bytes32 s; 147 | 148 | // If it's an offer of ERC20 for ERC721, then the maker is the buyer and 149 | // the taker is the seller. 150 | (makerOrder, v, r, s) = buildOrder( 151 | maker, 152 | isOffer ? Side.Buy : Side.Sell, 153 | nftContractAddress, 154 | nftTokenId, 155 | paymentToken, 156 | paymentTokenAmount, 157 | fee 158 | ); 159 | 160 | makerInput = buildInput(makerOrder, v, r, s, SignatureVersion.Single); 161 | 162 | (takerOrder, v, r, s) = buildOrder( 163 | taker, 164 | isOffer ? Side.Sell : Side.Buy, 165 | nftContractAddress, 166 | nftTokenId, 167 | paymentToken, 168 | paymentTokenAmount, 169 | fee 170 | ); 171 | 172 | takerInput = buildInput(takerOrder, v, r, s, SignatureVersion.Single); 173 | 174 | return (makerInput, takerInput); 175 | } 176 | 177 | function buildExecution( 178 | address maker, 179 | address taker, 180 | address nftContractAddress, 181 | uint256 nftTokenId, 182 | address paymentToken, 183 | uint256 paymentTokenAmount, 184 | Fee[] memory fee, 185 | bool isOffer 186 | ) internal view returns (Execution memory _execution) { 187 | Execution memory execution; 188 | Input memory makerInput; 189 | Input memory takerInput; 190 | 191 | (makerInput, takerInput) = buildInputPair( 192 | maker, 193 | taker, 194 | nftContractAddress, 195 | nftTokenId, 196 | paymentToken, 197 | paymentTokenAmount, 198 | fee, 199 | isOffer 200 | ); 201 | 202 | execution.sell = makerInput; 203 | execution.buy = takerInput; 204 | 205 | return execution; 206 | } 207 | 208 | function getPayload_BuyOfferedERC721WithEther( 209 | TestOrderContext calldata context, 210 | TestItem721 memory nft, 211 | uint256 ethAmount 212 | ) external view override returns (TestOrderPayload memory execution) { 213 | (Input memory makerInput, Input memory takerInput) = buildInputPair( 214 | context.offerer, 215 | context.fulfiller, 216 | nft.token, 217 | nft.identifier, 218 | address(0), 219 | ethAmount, 220 | new Fee[](0), 221 | false 222 | ); 223 | 224 | if (context.listOnChain) { 225 | _notImplemented(); 226 | } 227 | 228 | execution.executeOrder = TestCallParameters( 229 | address(blur), 230 | ethAmount, 231 | abi.encodeWithSelector( 232 | IBlurExchange.execute.selector, 233 | makerInput, 234 | takerInput 235 | ) 236 | ); 237 | } 238 | 239 | // The current matching policy at 0x0000...1FAC does not allow for 1155s to 240 | // be sold. This pattern should be close to viable when they update the 241 | // policy. 242 | // 243 | // See https://etherscan.io/address/0xb38827497daf7f28261910e33e22219de087c8f5#code#F1#L521, 244 | // https://etherscan.io/address/0x00000000006411739DA1c40B106F8511de5D1FAC#code#F1#L36. 245 | // function getPayload_BuyOfferedERC1155WithEther( 246 | // TestOrderContext calldata context, 247 | // TestItem1155 memory nft, 248 | // uint256 ethAmount 249 | // ) external view override returns (TestOrderPayload memory execution) { 250 | // (Input memory makerInput, Input memory takerInput) = buildInputPair( 251 | // context.offerer, 252 | // context.fulfiller, 253 | // nft.token, 254 | // nft.identifier, 255 | // address(0), 256 | // ethAmount 257 | // ); 258 | 259 | // if (context.listOnChain) { 260 | // _notImplemented(); 261 | // } 262 | 263 | // execution.executeOrder = TestCallParameters( 264 | // address(blur), 265 | // ethAmount, 266 | // abi.encodeWithSelector( 267 | // IBlurExchange.execute.selector, 268 | // makerInput, 269 | // takerInput 270 | // ) 271 | // ); 272 | // } 273 | 274 | // It's not possible to purchase NFTs with tokens other than ETH, WETH, or 275 | // Blur's proprietary version of WETH. 276 | // See https://etherscan.io/address/0xb38827497daf7f28261910e33e22219de087c8f5#code#F1#L594. 277 | function getPayload_BuyOfferedERC721WithWETH( 278 | TestOrderContext calldata context, 279 | TestItem721 memory nft, 280 | TestItem20 memory erc20 281 | ) external view override returns (TestOrderPayload memory execution) { 282 | (Input memory makerInput, Input memory takerInput) = buildInputPair( 283 | context.offerer, 284 | context.fulfiller, 285 | nft.token, 286 | nft.identifier, 287 | erc20.token, 288 | erc20.amount, 289 | new Fee[](0), 290 | false 291 | ); 292 | 293 | if (context.listOnChain) { 294 | _notImplemented(); 295 | } 296 | 297 | execution.executeOrder = TestCallParameters( 298 | address(blur), 299 | 0, 300 | abi.encodeWithSelector( 301 | IBlurExchange.execute.selector, 302 | makerInput, 303 | takerInput 304 | ) 305 | ); 306 | } 307 | 308 | function getPayload_BuyOfferedWETHWithERC721( 309 | TestOrderContext calldata context, 310 | TestItem20 memory erc20, 311 | TestItem721 memory nft 312 | ) external view override returns (TestOrderPayload memory execution) { 313 | (Input memory makerInput, Input memory takerInput) = buildInputPair( 314 | context.offerer, 315 | context.fulfiller, 316 | nft.token, 317 | nft.identifier, 318 | erc20.token, 319 | erc20.amount, 320 | new Fee[](0), 321 | true 322 | ); 323 | 324 | if (context.listOnChain) { 325 | _notImplemented(); 326 | } 327 | 328 | execution.executeOrder = TestCallParameters( 329 | address(blur), 330 | 0, 331 | abi.encodeWithSelector( 332 | IBlurExchange.execute.selector, 333 | takerInput, 334 | makerInput 335 | ) 336 | ); 337 | } 338 | 339 | function convert(uint256 val) internal pure returns (uint16) { 340 | return uint16(val); 341 | } 342 | 343 | function getPayload_BuyOfferedERC721WithEtherOneFeeRecipient( 344 | TestOrderContext calldata context, 345 | TestItem721 memory nft, 346 | uint256 priceEthAmount, 347 | address feeRecipient, 348 | uint256 feeEthAmount 349 | ) external view override returns (TestOrderPayload memory execution) { 350 | // TODO: figure out why this isn't working 351 | _notImplemented(); 352 | 353 | Fee[] memory fees = new Fee[](1); 354 | uint256 rate; 355 | rate = (feeEthAmount * 10000) / (priceEthAmount) + 1; 356 | uint16 convertedRate; 357 | convertedRate = convert(rate); 358 | fees[0] = Fee({ 359 | recipient: payable(feeRecipient), 360 | rate: convertedRate 361 | }); 362 | (Input memory makerInput, Input memory takerInput) = buildInputPair( 363 | context.offerer, 364 | context.fulfiller, 365 | nft.token, 366 | nft.identifier, 367 | address(0), 368 | priceEthAmount, 369 | fees, 370 | false 371 | ); 372 | 373 | if (context.listOnChain) { 374 | _notImplemented(); 375 | } 376 | 377 | execution.executeOrder = TestCallParameters( 378 | address(blur), 379 | priceEthAmount + feeEthAmount, 380 | abi.encodeWithSelector( 381 | IBlurExchange.execute.selector, 382 | makerInput, 383 | takerInput 384 | ) 385 | ); 386 | } 387 | 388 | function getPayload_BuyOfferedERC721WithEtherTwoFeeRecipient( 389 | TestOrderContext calldata context, 390 | TestItem721 memory nft, 391 | uint256 priceEthAmount, 392 | address feeRecipient1, 393 | uint256 feeEthAmount1, 394 | address feeRecipient2, 395 | uint256 feeEthAmount2 396 | ) external view override returns (TestOrderPayload memory execution) { 397 | // TODO: figure out why this isn't working 398 | _notImplemented(); 399 | 400 | Fee[] memory fees = new Fee[](2); 401 | uint256 rate; 402 | rate = (feeEthAmount1 * 10000) / (priceEthAmount) + 1; 403 | fees[0] = Fee({ 404 | recipient: payable(feeRecipient1), 405 | rate: convert(rate) 406 | }); 407 | rate = (feeEthAmount2 * 10000) / (priceEthAmount) + 1; 408 | fees[1] = Fee({ 409 | recipient: payable(feeRecipient2), 410 | rate: convert(rate) 411 | }); 412 | (Input memory makerInput, Input memory takerInput) = buildInputPair( 413 | context.offerer, 414 | context.fulfiller, 415 | nft.token, 416 | nft.identifier, 417 | address(0), 418 | priceEthAmount, 419 | fees, 420 | false 421 | ); 422 | 423 | if (context.listOnChain) { 424 | _notImplemented(); 425 | } 426 | 427 | execution.executeOrder = TestCallParameters( 428 | address(blur), 429 | priceEthAmount + feeEthAmount1 + feeEthAmount2, 430 | abi.encodeWithSelector( 431 | IBlurExchange.execute.selector, 432 | makerInput, 433 | takerInput 434 | ) 435 | ); 436 | } 437 | 438 | function getPayload_BuyOfferedManyERC721WithEtherDistinctOrders( 439 | TestOrderContext[] calldata contexts, 440 | TestItem721[] calldata nfts, 441 | uint256[] calldata ethAmounts 442 | ) external view override returns (TestOrderPayload memory execution) { 443 | require( 444 | contexts.length == nfts.length && nfts.length == ethAmounts.length, 445 | "BlurConfig::getPayload_BuyOfferedManyERC721WithEtherDistinctOrders: invalid input" 446 | ); 447 | 448 | uint256 sumEthAmount; 449 | 450 | Execution[] memory executions = new Execution[](nfts.length); 451 | Execution memory _execution; 452 | for (uint256 i = 0; i < nfts.length; i++) { 453 | if (contexts[i].listOnChain) { 454 | _notImplemented(); 455 | } 456 | 457 | _execution = buildExecution( 458 | contexts[i].offerer, 459 | contexts[i].fulfiller, 460 | nfts[i].token, 461 | nfts[i].identifier, 462 | address(0), 463 | ethAmounts[i], 464 | new Fee[](0), 465 | false 466 | ); 467 | 468 | executions[i] = _execution; 469 | 470 | sumEthAmount += ethAmounts[i]; 471 | } 472 | 473 | execution.executeOrder = TestCallParameters( 474 | address(blur), 475 | sumEthAmount, 476 | abi.encodeWithSelector( 477 | IBlurExchange.bulkExecute.selector, 478 | executions 479 | ) 480 | ); 481 | } 482 | 483 | function getPayload_BuyOfferedManyERC721WithWETHDistinctOrders( 484 | TestOrderContext[] calldata contexts, 485 | address erc20Address, 486 | TestItem721[] calldata nfts, 487 | uint256[] calldata erc20Amounts 488 | ) external view override returns (TestOrderPayload memory execution) { 489 | require( 490 | contexts.length == nfts.length && 491 | nfts.length == erc20Amounts.length, 492 | "BlurConfig::getPayload_BuyOfferedManyERC721WithEtherDistinctOrders: invalid input" 493 | ); 494 | 495 | Execution[] memory executions = new Execution[](nfts.length); 496 | Execution memory _execution; 497 | 498 | for (uint256 i = 0; i < nfts.length; i++) { 499 | if (contexts[i].listOnChain) { 500 | _notImplemented(); 501 | } 502 | 503 | _execution = buildExecution( 504 | contexts[i].offerer, 505 | contexts[i].fulfiller, 506 | nfts[i].token, 507 | nfts[i].identifier, 508 | erc20Address, 509 | erc20Amounts[i], 510 | new Fee[](0), 511 | false 512 | ); 513 | 514 | executions[i] = _execution; 515 | } 516 | 517 | execution.executeOrder = TestCallParameters( 518 | address(blur), 519 | 0, 520 | abi.encodeWithSelector( 521 | IBlurExchange.bulkExecute.selector, 522 | executions 523 | ) 524 | ); 525 | } 526 | } 527 | --------------------------------------------------------------------------------