├── .commitlintrc.json ├── .eslintignore ├── .eslintrc ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── change_request.md │ └── technical_question.md └── workflows │ ├── build.yml │ ├── format.yml │ ├── lint.yml │ └── test.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .nycrc.json ├── .prettierignore ├── .prettierrc ├── .release-it.js ├── .solhint.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── doc ├── README.md ├── classes │ └── LooksRare.LooksRare.md ├── enums │ ├── types.ChainId.md │ ├── types.CollectionType.md │ ├── types.MerkleTreeNodePosition.md │ ├── types.OrderValidatorCode.md │ ├── types.QuoteType.md │ └── types.StrategyType.md ├── interfaces │ ├── types.Addresses.md │ ├── types.BatchTransferItem.md │ ├── types.ChainInfo.md │ ├── types.ContractMethods.md │ ├── types.CreateMakerAskOutput.md │ ├── types.CreateMakerBidOutput.md │ ├── types.CreateMakerInput.md │ ├── types.Maker.md │ ├── types.MerkleTree.md │ ├── types.MerkleTreeProof.md │ ├── types.Referrer.md │ ├── types.SignMerkleTreeOrdersOutput.md │ ├── types.StrategyInfo.md │ └── types.Taker.md └── modules │ ├── LooksRare.md │ └── types.md ├── guides ├── cancelOrders.md ├── collectionOrder.md ├── createMakerAsk.md ├── createMakerBid.md ├── executeTrade.md ├── orderValidity.md ├── readme.md └── signMultipleMakerOrders.md ├── hardhat.config.ts ├── package.json ├── rollup.config.js ├── src ├── LooksRare.ts ├── __tests__ │ ├── eip712.test.ts │ ├── encodeOrderParams.test.ts │ ├── helpers │ │ ├── eip712.ts │ │ ├── setup.ts │ │ └── tokens.ts │ ├── looksrare │ │ ├── constructor.test.ts │ │ ├── createMakerAsk.test.ts │ │ ├── createMakerBid.test.ts │ │ ├── createTaker.test.ts │ │ ├── executeCollectionOrder.test.ts │ │ ├── executeCollectionOrderWithProof.test.ts │ │ ├── executeMultipleTakerBids.test.ts │ │ ├── executeTakerAsk.test.ts │ │ ├── executeTakerBid.test.ts │ │ ├── nonces.test.ts │ │ ├── orderValidator.test.ts │ │ ├── signMakerOrders.test.ts │ │ ├── strategies.test.ts │ │ └── transferManager.test.ts │ ├── tokens.test.ts │ └── tsconfig.json ├── abis │ ├── IERC1155.json │ ├── IERC20.json │ ├── IERC721.json │ ├── ILooksRareProtocol.json │ ├── ITransferManager.json │ ├── IWETH.json │ ├── LooksRareProtocol.json │ ├── OrderValidatorV2A.json │ ├── ProtocolHelpers.json │ ├── TransferManager.json │ ├── WETH.json │ └── index.ts ├── constants │ ├── addresses.ts │ ├── chains.ts │ ├── eip712.ts │ └── index.ts ├── contracts │ ├── LooksRareProtocol.sol │ ├── OrderValidatorV2A.sol │ ├── TransferManager.sol │ └── tests │ │ ├── CreatorFeeManagerWithRoyalties.sol │ │ ├── MockERC1155.sol │ │ ├── MockERC721.sol │ │ ├── MockRoyaltyFeeRegistry.sol │ │ ├── StrategyCollectionOffer.sol │ │ ├── StrategyManager.sol │ │ ├── Verifier.sol │ │ └── WETH.sol ├── errors.ts ├── index.ts ├── types.ts └── utils │ ├── Eip712MakerMerkleTree.ts │ ├── Eip712MerkleTree.ts │ ├── calls │ ├── exchange.ts │ ├── nonces.ts │ ├── orderValidator.ts │ ├── strategies.ts │ ├── tokens.ts │ └── transferManager.ts │ ├── eip712.ts │ ├── encodeOrderParams.ts │ └── signMakerOrders.ts ├── tsconfig.build.json ├── tsconfig.json ├── typedoc.json └── yarn.lock /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"], 3 | "rules": { 4 | "subject-case": [2, "always", "sentence-case"], 5 | "type-enum": [ 6 | 2, 7 | "always", 8 | ["build", "ci", "chore", "docs", "feat", "fix", "perf", "refactor", "revert", "security", "test"] 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "es6": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | "parser": "@typescript-eslint/parser", 9 | "extends": ["plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"], 10 | "plugins": ["@typescript-eslint"], 11 | "rules": { 12 | "curly": "warn", 13 | "no-await-in-loop": "warn", 14 | "no-console": ["warn", { "allow": ["info", "warn", "error"] }], 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | // Typescript overrides 18 | "no-unused-vars": "off", 19 | "@typescript-eslint/no-unused-vars": "warn", 20 | "no-use-before-define": "off", 21 | "@typescript-eslint/no-use-before-define": ["warn"], 22 | "no-shadow": "off", 23 | "@typescript-eslint/no-shadow": ["error"] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 2 | 3 | @niratodev @CloudLooks @BrooceLR 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/change_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Change request 3 | about: Suggest a change 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Please describe your problem** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/technical_question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Technical question 3 | about: Technical question about the SDK, how does it work, how to implement, etc... 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Before you start** 10 | 11 | Make sure you read: 12 | 13 | - [The implementation guides](https://github.com/LooksRare/sdk-v2/tree/master/guides) 14 | - [The FAQ](https://github.com/LooksRare/sdk-v2#faq) 15 | 16 | **If you couldn't find your answer in the doc** 17 | 18 | - Explain your problem as clearly a possible 19 | - Explain what you tried to do to solve it 20 | - Share your code 21 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup Node 18 | uses: actions/setup-node@v3 19 | 20 | - name: Install dependencies 21 | run: yarn install --frozen-lockfile 22 | 23 | - name: Build 24 | run: yarn build 25 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Format 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | code-format: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup Node 18 | uses: actions/setup-node@v3 19 | 20 | - name: Install dependencies 21 | run: yarn install --frozen-lockfile 22 | 23 | - name: Run Prettier 24 | run: yarn format:check 25 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | eslint: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup Node 18 | uses: actions/setup-node@v3 19 | 20 | - name: Install dependencies 21 | run: yarn install --frozen-lockfile 22 | 23 | - name: Run ESLint 24 | run: yarn lint 25 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup Node 18 | uses: actions/setup-node@v3 19 | 20 | - name: Install dependencies 21 | run: yarn install --frozen-lockfile 22 | 23 | - name: Run tests 24 | run: yarn test 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # testing 5 | coverage 6 | coverage.json 7 | .nyc_output 8 | 9 | # build 10 | lib 11 | dist 12 | cache 13 | typechain 14 | artifacts 15 | 16 | # misc 17 | .DS_Store 18 | .env 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | tsconfig.tsbuildinfo 25 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@istanbuljs/nyc-config-typescript", 3 | "all": true, 4 | "include": ["src/**/*.ts", "src/**/*.js"], 5 | "exclude": ["src/__tests__/**", "src/constants/**", "src/index.ts", "src/typechain/**"], 6 | "reporter": ["html", "text"] 7 | } 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | abis 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /.release-it.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | 3 | // Doc: https://github.com/release-it/release-it 4 | module.exports = { 5 | git: { 6 | commitMessage: "build: Release v${version}", 7 | requireUpstream: false, 8 | pushRepo: "upstream", // Push tags and commit to the remote `upstream` (fails if doesn't exist) 9 | requireBranch: "master", // Push commit to the branch `master` (fail if on other branch) 10 | requireCommits: true, // Require new commits since latest tag 11 | }, 12 | github: { 13 | release: true, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "compiler-version": ["error", "^0.8.0"], 5 | "func-visibility": [{ "ignoreConstructors": true }], 6 | "func-name-mixedcase": "off", 7 | "reason-string": "off", 8 | "var-name-mixedcase": "off" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Dev 2 | 3 | ### Setup 4 | 5 | Install dependencies with `yarn` 6 | 7 | ### Commands 8 | 9 | - **Dev**: `yarn dev` 10 | - **Build**: `yarn build` 11 | - **Test**: `yarn test` 12 | - **Lint**: `yarn lint` 13 | - **Format**: `yarn format:check`, `yarn format:write` 14 | - **Documentation**: `yarn doc` 15 | 16 | ### Submit a PR 17 | 18 | Before you submit a PR, make sure that: 19 | 20 | - All the tests pass ✅ and your code is covered 21 | - Your code is properly formatted with Prettier 22 | - You code doesn't raise any ESlint warning 23 | - Your changes are explained in your PR 24 | 25 | When in doubt, [Create a new issue](https://github.com/LooksRare/sdk-v2/issues/new) to discuss your proposal first. 26 | 27 | ### Release 28 | 29 | - Create a [personal access token](https://github.com/settings/tokens/new?scopes=repo&description=release-it) (Don't change the default scope) 30 | - Create an `.env` (copy `.env.template`) and set you github personal access token. 31 | - `yarn release` will run all the checks, build, and publish the package, and publish the github release note. 32 | - `yarn release --dry-run` simulates a release process. 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Looksrare 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @looksrare/sdk-v2 2 | 3 | ![GitHub package.json version](https://img.shields.io/github/package-json/v/LooksRare/sdk-v2) ![GitHub](https://img.shields.io/github/license/LooksRare/sdk-v2) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/LooksRare/sdk-v2/build.yml) ![npm](https://img.shields.io/npm/dt/@looksrare/sdk-v2) 4 | 5 | A collection of typescript tools to interact with LooksRare protocol V2 smart contracts 👀💎 6 | 7 | ## Install 8 | 9 | This package has a peer dependency on [etherjs V5](https://docs.ethers.io/v5/). 10 | 11 | ```bash 12 | yarn add @looksrare/sdk-v2 ethers 13 | ``` 14 | 15 | ```bash 16 | npm install @looksrare/sdk-v2 ethers --save 17 | ``` 18 | 19 | ## Getting Started 20 | 21 | The SDK expose a main class used to perform all the onchain operations: 22 | 23 | ```ts 24 | import { ChainId } from "@looksrare/sdk-v2"; 25 | const looksrare = new LooksRare(ChainId.MAINNET, provider, signer); 26 | ``` 27 | 28 | The signer is optional if you need access to read only data (:warning: Calls to function that need a signer will throw a `Signer is undefined` exception): 29 | 30 | ```ts 31 | import { ChainId } from "@looksrare/sdk-v2"; 32 | const looksrare = new LooksRare(ChainId.MAINNET, provider); 33 | ``` 34 | 35 | If you work on a hardhat setup, you can override the addresses as follow: 36 | 37 | ```ts 38 | import { Addresses } from "@looksrare/sdk-v2"; 39 | const addresses: Addresses = {...}; 40 | const looksrare = new LooksRare(ChainId.HARDHAT, provider, signer, addresses); 41 | ``` 42 | 43 | :information_source: Use [`JsonRpcProvider`](https://docs.ethers.org/v6/api/providers/jsonrpc/#JsonRpcApiProviderOptions) from `ethers v6` if you want to make batched RPC calls. 44 | 45 | ```ts 46 | import { JsonRpcProvider, Network } from "ethers"; 47 | 48 | // Create a HTTP/HTTPS batch call provider 49 | const provider = new JsonRpcProvider(RPC_URL, CHAIN_ID, { staticNetwork: Network.from(CHAIN_ID) }); 50 | 51 | // Create LooksRare instance using this provider 52 | const looksrare = new LooksRare(ChainId.HARDHAT, provider, signer, addresses); 53 | ``` 54 | 55 | Prior to [`@looksrare/sdk-v2@0.9.2`](https://www.npmjs.com/package/@looksrare/sdk-v2/v/0.9.2?activeTab=readme), LooksRareSDK was making batched calls using `0xsequence/multicall`. But this is not natively supported since `@looksrare/sdk-v2@1.0.0`. 56 | 57 | ## Documentation 58 | 59 | Read the [guides](./guides) if you need help with the implementation. 60 | 61 | You can also read the detailed [api documentation](./doc). 62 | 63 | ## FAQ 64 | 65 | ### ❓ How to use ABIs 66 | 67 | The SDK exposes most of the LooksRare contract [ABIs](https://github.com/LooksRare/sdk-v2/tree/master/src/abis). For convenience, some commonly used ABIs are also exported. 68 | 69 | ```js 70 | import LooksRareProtocolABI from "@looksrare/sdk-v2/dist/abis/LooksRareProtocol.json"; 71 | ``` 72 | 73 | ### ❓ How to retrieve order nonce ? 74 | 75 | Call the public api endpoint [/orders/nonce](https://looksrare.dev/reference/getordernonce), and use this nonce directly. 76 | 77 | ### ❓ What to do when the order is created and signed ? 78 | 79 | Use the public api endpoint [/orders](https://looksrare.dev/reference/createorder) to push the order to the database. After that, the order will be visible by everyone using the API (looksrare.org, aggregators, etc..). 80 | 81 | ### ❓ When should I use merkle tree orders ? 82 | 83 | Merkle tree orders are used to create several orders with a single signature. You shouldn't use them when using a bot. Their main purpose is to facilitate orders creation using a user interface. 84 | 85 | ### ❓ Why do I need to call grantTransferManagerApproval ? 86 | 87 | When you approve a collection to be traded on LooksRare, you approve the TransferManager instead of the exchange. Calling `grantTransferManagerApproval` gives the exchange contract the right to call the transfer function on the TransferManager. You need to call this function only once, the first time you use the V2. 88 | 89 | ### ❓ What are subset nonces and how to use them ? 90 | 91 | tl;dr subset nonces allow you to cancel all the orders sharing the same subset nonce. 92 | Subset nonces allow you to group some orders together according to arbitrary rules (for example all your orders for a specific collection, all your orders above a certain threshold, etc). You can then cancel them all with a single call to `cancelSubsetOrders`. 93 | :information_source: Unlike order nonces, executing an order with a specific subset nonce doesn't invalidate other orders sharing the same subset nonce. 94 | 95 | ## Resources 96 | 97 | 🔗 [Developer documentation](https://docs.looksrare.org/developers/welcome) 98 | 99 | 🔗 [Public API documentation](https://looksrare.dev/reference/important-information) 100 | 101 | 🔗 [Developer discord](https://discord.gg/jJA4qM5dXz) 102 | 103 | ## Ethers Version 5 & 6 104 | 105 | The SDK maintains a version for Ethers V5 and V6. To use the SDK with Ethers V5 install versions `< 1`, e.g. `0.9.0`. Version 6 of Ethers is on versions `> 1.0.0` 106 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # @looksrare/sdk-v2 2 | 3 | ## Modules 4 | 5 | - [LooksRare](modules/LooksRare.md) 6 | - [types](modules/types.md) 7 | -------------------------------------------------------------------------------- /doc/enums/types.ChainId.md: -------------------------------------------------------------------------------- 1 | # Enumeration: ChainId 2 | 3 | [types](../modules/types.md).ChainId 4 | 5 | List of supported chains 6 | 7 | ## Enumeration Members 8 | 9 | ### GOERLI 10 | 11 | • **GOERLI** = ``5`` 12 | 13 | ___ 14 | 15 | ### HARDHAT 16 | 17 | • **HARDHAT** = ``31337`` 18 | 19 | ___ 20 | 21 | ### MAINNET 22 | 23 | • **MAINNET** = ``1`` 24 | 25 | ___ 26 | 27 | ### SEPOLIA 28 | 29 | • **SEPOLIA** = ``11155111`` 30 | -------------------------------------------------------------------------------- /doc/enums/types.CollectionType.md: -------------------------------------------------------------------------------- 1 | # Enumeration: CollectionType 2 | 3 | [types](../modules/types.md).CollectionType 4 | 5 | List of collection types supported by the protocol 6 | 7 | ## Enumeration Members 8 | 9 | ### ERC1155 10 | 11 | • **ERC1155** = ``1`` 12 | 13 | ___ 14 | 15 | ### ERC721 16 | 17 | • **ERC721** = ``0`` 18 | -------------------------------------------------------------------------------- /doc/enums/types.MerkleTreeNodePosition.md: -------------------------------------------------------------------------------- 1 | # Enumeration: MerkleTreeNodePosition 2 | 3 | [types](../modules/types.md).MerkleTreeNodePosition 4 | 5 | Merkle tree node position (needed by the SC) 6 | 7 | ## Enumeration Members 8 | 9 | ### Left 10 | 11 | • **Left** = ``0`` 12 | 13 | ___ 14 | 15 | ### Right 16 | 17 | • **Right** = ``1`` 18 | -------------------------------------------------------------------------------- /doc/enums/types.OrderValidatorCode.md: -------------------------------------------------------------------------------- 1 | # Enumeration: OrderValidatorCode 2 | 3 | [types](../modules/types.md).OrderValidatorCode 4 | 5 | Error errors returned by the order validator contract 6 | 7 | ## Enumeration Members 8 | 9 | ### BUNDLE\_ERC2981\_NOT\_SUPPORTED 10 | 11 | • **BUNDLE\_ERC2981\_NOT\_SUPPORTED** = ``901`` 12 | 13 | ___ 14 | 15 | ### CREATOR\_FEE\_TOO\_HIGH 16 | 17 | • **CREATOR\_FEE\_TOO\_HIGH** = ``902`` 18 | 19 | ___ 20 | 21 | ### CURRENCY\_NOT\_ALLOWED 22 | 23 | • **CURRENCY\_NOT\_ALLOWED** = ``101`` 24 | 25 | ___ 26 | 27 | ### ERC1155\_BALANCE\_OF\_DOES\_NOT\_EXIST 28 | 29 | • **ERC1155\_BALANCE\_OF\_DOES\_NOT\_EXIST** = ``631`` 30 | 31 | ___ 32 | 33 | ### ERC1155\_BALANCE\_OF\_ITEM\_ID\_INFERIOR\_TO\_AMOUNT 34 | 35 | • **ERC1155\_BALANCE\_OF\_ITEM\_ID\_INFERIOR\_TO\_AMOUNT** = ``632`` 36 | 37 | ___ 38 | 39 | ### ERC1155\_IS\_APPROVED\_FOR\_ALL\_DOES\_NOT\_EXIST 40 | 41 | • **ERC1155\_IS\_APPROVED\_FOR\_ALL\_DOES\_NOT\_EXIST** = ``633`` 42 | 43 | ___ 44 | 45 | ### ERC1155\_NO\_APPROVAL\_FOR\_ALL 46 | 47 | • **ERC1155\_NO\_APPROVAL\_FOR\_ALL** = ``634`` 48 | 49 | ___ 50 | 51 | ### ERC20\_APPROVAL\_INFERIOR\_TO\_PRICE 52 | 53 | • **ERC20\_APPROVAL\_INFERIOR\_TO\_PRICE** = ``612`` 54 | 55 | ___ 56 | 57 | ### ERC20\_BALANCE\_INFERIOR\_TO\_PRICE 58 | 59 | • **ERC20\_BALANCE\_INFERIOR\_TO\_PRICE** = ``611`` 60 | 61 | ___ 62 | 63 | ### ERC721\_ITEM\_ID\_DOES\_NOT\_EXIST 64 | 65 | • **ERC721\_ITEM\_ID\_DOES\_NOT\_EXIST** = ``621`` 66 | 67 | ___ 68 | 69 | ### ERC721\_ITEM\_ID\_NOT\_IN\_BALANCE 70 | 71 | • **ERC721\_ITEM\_ID\_NOT\_IN\_BALANCE** = ``622`` 72 | 73 | ___ 74 | 75 | ### ERC721\_NO\_APPROVAL\_FOR\_ALL\_OR\_ITEM\_ID 76 | 77 | • **ERC721\_NO\_APPROVAL\_FOR\_ALL\_OR\_ITEM\_ID** = ``623`` 78 | 79 | ___ 80 | 81 | ### INVALID\_SIGNATURE\_LENGTH 82 | 83 | • **INVALID\_SIGNATURE\_LENGTH** = ``411`` 84 | 85 | ___ 86 | 87 | ### INVALID\_SIGNER\_EOA 88 | 89 | • **INVALID\_SIGNER\_EOA** = ``415`` 90 | 91 | ___ 92 | 93 | ### INVALID\_S\_PARAMETER\_EOA 94 | 95 | • **INVALID\_S\_PARAMETER\_EOA** = ``412`` 96 | 97 | ___ 98 | 99 | ### INVALID\_USER\_GLOBAL\_ASK\_NONCE 100 | 101 | • **INVALID\_USER\_GLOBAL\_ASK\_NONCE** = ``322`` 102 | 103 | ___ 104 | 105 | ### INVALID\_USER\_GLOBAL\_BID\_NONCE 106 | 107 | • **INVALID\_USER\_GLOBAL\_BID\_NONCE** = ``321`` 108 | 109 | ___ 110 | 111 | ### INVALID\_V\_PARAMETER\_EOA 112 | 113 | • **INVALID\_V\_PARAMETER\_EOA** = ``413`` 114 | 115 | ___ 116 | 117 | ### MAKER\_ORDER\_INVALID\_CURRENCY\_NON\_STANDARD\_SALE 118 | 119 | • **MAKER\_ORDER\_INVALID\_CURRENCY\_NON\_STANDARD\_SALE** = ``212`` 120 | 121 | ___ 122 | 123 | ### MAKER\_ORDER\_INVALID\_STANDARD\_SALE 124 | 125 | • **MAKER\_ORDER\_INVALID\_STANDARD\_SALE** = ``201`` 126 | 127 | ___ 128 | 129 | ### MAKER\_ORDER\_PERMANENTLY\_INVALID\_NON\_STANDARD\_SALE 130 | 131 | • **MAKER\_ORDER\_PERMANENTLY\_INVALID\_NON\_STANDARD\_SALE** = ``211`` 132 | 133 | ___ 134 | 135 | ### MAKER\_ORDER\_TEMPORARILY\_INVALID\_NON\_STANDARD\_SALE 136 | 137 | • **MAKER\_ORDER\_TEMPORARILY\_INVALID\_NON\_STANDARD\_SALE** = ``213`` 138 | 139 | ___ 140 | 141 | ### MERKLE\_PROOF\_PROOF\_TOO\_LARGE 142 | 143 | • **MERKLE\_PROOF\_PROOF\_TOO\_LARGE** = ``402`` 144 | 145 | ___ 146 | 147 | ### MISSING\_IS\_VALID\_SIGNATURE\_FUNCTION\_EIP1271 148 | 149 | • **MISSING\_IS\_VALID\_SIGNATURE\_FUNCTION\_EIP1271** = ``421`` 150 | 151 | ___ 152 | 153 | ### NO\_TRANSFER\_MANAGER\_APPROVAL\_BY\_USER\_FOR\_EXCHANGE 154 | 155 | • **NO\_TRANSFER\_MANAGER\_APPROVAL\_BY\_USER\_FOR\_EXCHANGE** = ``801`` 156 | 157 | ___ 158 | 159 | ### NULL\_SIGNER\_EOA 160 | 161 | • **NULL\_SIGNER\_EOA** = ``414`` 162 | 163 | ___ 164 | 165 | ### ORDER\_EXPECTED\_TO\_BE\_VALID 166 | 167 | • **ORDER\_EXPECTED\_TO\_BE\_VALID** = ``0`` 168 | 169 | ___ 170 | 171 | ### ORDER\_HASH\_PROOF\_NOT\_IN\_MERKLE\_TREE 172 | 173 | • **ORDER\_HASH\_PROOF\_NOT\_IN\_MERKLE\_TREE** = ``401`` 174 | 175 | ___ 176 | 177 | ### POTENTIAL\_INVALID\_COLLECTION\_TYPE\_SHOULD\_BE\_ERC1155 178 | 179 | • **POTENTIAL\_INVALID\_COLLECTION\_TYPE\_SHOULD\_BE\_ERC1155** = ``702`` 180 | 181 | ___ 182 | 183 | ### POTENTIAL\_INVALID\_COLLECTION\_TYPE\_SHOULD\_BE\_ERC721 184 | 185 | • **POTENTIAL\_INVALID\_COLLECTION\_TYPE\_SHOULD\_BE\_ERC721** = ``701`` 186 | 187 | ___ 188 | 189 | ### SAME\_ITEM\_ID\_IN\_BUNDLE 190 | 191 | • **SAME\_ITEM\_ID\_IN\_BUNDLE** = ``601`` 192 | 193 | ___ 194 | 195 | ### SIGNATURE\_INVALID\_EIP1271 196 | 197 | • **SIGNATURE\_INVALID\_EIP1271** = ``422`` 198 | 199 | ___ 200 | 201 | ### START\_TIME\_GREATER\_THAN\_END\_TIME 202 | 203 | • **START\_TIME\_GREATER\_THAN\_END\_TIME** = ``501`` 204 | 205 | ___ 206 | 207 | ### STRATEGY\_INVALID\_QUOTE\_TYPE 208 | 209 | • **STRATEGY\_INVALID\_QUOTE\_TYPE** = ``112`` 210 | 211 | ___ 212 | 213 | ### STRATEGY\_NOT\_ACTIVE 214 | 215 | • **STRATEGY\_NOT\_ACTIVE** = ``113`` 216 | 217 | ___ 218 | 219 | ### STRATEGY\_NOT\_IMPLEMENTED 220 | 221 | • **STRATEGY\_NOT\_IMPLEMENTED** = ``111`` 222 | 223 | ___ 224 | 225 | ### TOO\_EARLY\_TO\_EXECUTE\_ORDER 226 | 227 | • **TOO\_EARLY\_TO\_EXECUTE\_ORDER** = ``503`` 228 | 229 | ___ 230 | 231 | ### TOO\_LATE\_TO\_EXECUTE\_ORDER 232 | 233 | • **TOO\_LATE\_TO\_EXECUTE\_ORDER** = ``502`` 234 | 235 | ___ 236 | 237 | ### TRANSFER\_MANAGER\_APPROVAL\_REVOKED\_BY\_OWNER\_FOR\_EXCHANGE 238 | 239 | • **TRANSFER\_MANAGER\_APPROVAL\_REVOKED\_BY\_OWNER\_FOR\_EXCHANGE** = ``802`` 240 | 241 | ___ 242 | 243 | ### USER\_ORDER\_NONCE\_EXECUTED\_OR\_CANCELLED 244 | 245 | • **USER\_ORDER\_NONCE\_EXECUTED\_OR\_CANCELLED** = ``311`` 246 | 247 | ___ 248 | 249 | ### USER\_ORDER\_NONCE\_IN\_EXECUTION\_WITH\_OTHER\_HASH 250 | 251 | • **USER\_ORDER\_NONCE\_IN\_EXECUTION\_WITH\_OTHER\_HASH** = ``312`` 252 | 253 | ___ 254 | 255 | ### USER\_SUBSET\_NONCE\_CANCELLED 256 | 257 | • **USER\_SUBSET\_NONCE\_CANCELLED** = ``301`` 258 | -------------------------------------------------------------------------------- /doc/enums/types.QuoteType.md: -------------------------------------------------------------------------------- 1 | # Enumeration: QuoteType 2 | 3 | [types](../modules/types.md).QuoteType 4 | 5 | Type for maker order 6 | 7 | ## Enumeration Members 8 | 9 | ### Ask 10 | 11 | • **Ask** = ``1`` 12 | 13 | ___ 14 | 15 | ### Bid 16 | 17 | • **Bid** = ``0`` 18 | -------------------------------------------------------------------------------- /doc/enums/types.StrategyType.md: -------------------------------------------------------------------------------- 1 | # Enumeration: StrategyType 2 | 3 | [types](../modules/types.md).StrategyType 4 | 5 | List of trading strategies 6 | 7 | ## Enumeration Members 8 | 9 | ### collection 10 | 11 | • **collection** = ``1`` 12 | 13 | ___ 14 | 15 | ### collectionWithMerkleTree 16 | 17 | • **collectionWithMerkleTree** = ``2`` 18 | 19 | ___ 20 | 21 | ### standard 22 | 23 | • **standard** = ``0`` 24 | -------------------------------------------------------------------------------- /doc/interfaces/types.Addresses.md: -------------------------------------------------------------------------------- 1 | # Interface: Addresses 2 | 3 | [types](../modules/types.md).Addresses 4 | 5 | Addresses used to create a LooksRare instance 6 | 7 | ## Properties 8 | 9 | ### AGGREGATOR\_UNISWAP\_V3 10 | 11 | • **AGGREGATOR\_UNISWAP\_V3**: `string` 12 | 13 | ___ 14 | 15 | ### EXCHANGE\_V2 16 | 17 | • **EXCHANGE\_V2**: `string` 18 | 19 | ___ 20 | 21 | ### LOOKS 22 | 23 | • **LOOKS**: `string` 24 | 25 | ___ 26 | 27 | ### LOOKS\_LP\_V3 28 | 29 | • **LOOKS\_LP\_V3**: `string` 30 | 31 | ___ 32 | 33 | ### ORDER\_VALIDATOR\_V2 34 | 35 | • **ORDER\_VALIDATOR\_V2**: `string` 36 | 37 | ___ 38 | 39 | ### REVERSE\_RECORDS 40 | 41 | • **REVERSE\_RECORDS**: `string` 42 | 43 | ___ 44 | 45 | ### STAKING\_POOL\_FOR\_LOOKS\_LP 46 | 47 | • **STAKING\_POOL\_FOR\_LOOKS\_LP**: `string` 48 | 49 | ___ 50 | 51 | ### TRANSFER\_MANAGER\_V2 52 | 53 | • **TRANSFER\_MANAGER\_V2**: `string` 54 | 55 | ___ 56 | 57 | ### WETH 58 | 59 | • **WETH**: `string` 60 | -------------------------------------------------------------------------------- /doc/interfaces/types.BatchTransferItem.md: -------------------------------------------------------------------------------- 1 | # Interface: BatchTransferItem 2 | 3 | [types](../modules/types.md).BatchTransferItem 4 | 5 | Item structure used for batch transfers 6 | 7 | **`See`** 8 | 9 | [TransferManager interface](https://github.com/LooksRare/contracts-exchange-v2/blob/8de425de2571a57112e9e67cf0c925439a83c9e3/contracts/interfaces/ITransferManager.sol#L16) 10 | 11 | ## Properties 12 | 13 | ### amounts 14 | 15 | • **amounts**: `BigNumberish`[] 16 | 17 | ___ 18 | 19 | ### collection 20 | 21 | • **collection**: `string` 22 | 23 | ___ 24 | 25 | ### collectionType 26 | 27 | • **collectionType**: [`CollectionType`](../enums/types.CollectionType.md) 28 | 29 | ___ 30 | 31 | ### itemIds 32 | 33 | • **itemIds**: `BigNumberish`[] 34 | -------------------------------------------------------------------------------- /doc/interfaces/types.ChainInfo.md: -------------------------------------------------------------------------------- 1 | # Interface: ChainInfo 2 | 3 | [types](../modules/types.md).ChainInfo 4 | 5 | ChainInfo data used to interact with LooksRare ecosystem 6 | 7 | ## Properties 8 | 9 | ### appUrl 10 | 11 | • **appUrl**: `string` 12 | 13 | ___ 14 | 15 | ### baseApiUrl 16 | 17 | • **baseApiUrl**: `string` 18 | 19 | ___ 20 | 21 | ### cdnUrl 22 | 23 | • **cdnUrl**: `string` 24 | 25 | ___ 26 | 27 | ### cloudinaryUrl 28 | 29 | • **cloudinaryUrl**: `string` 30 | 31 | ___ 32 | 33 | ### explorer 34 | 35 | • **explorer**: `string` 36 | 37 | ___ 38 | 39 | ### label 40 | 41 | • **label**: `string` 42 | 43 | ___ 44 | 45 | ### osApiUrl 46 | 47 | • **osApiUrl**: `string` 48 | 49 | ___ 50 | 51 | ### rewardsSubgraphUrl 52 | 53 | • **rewardsSubgraphUrl**: `string` 54 | 55 | ___ 56 | 57 | ### rpcUrl 58 | 59 | • **rpcUrl**: `string` 60 | 61 | ___ 62 | 63 | ### wsUrl 64 | 65 | • **wsUrl**: `string` 66 | -------------------------------------------------------------------------------- /doc/interfaces/types.ContractMethods.md: -------------------------------------------------------------------------------- 1 | # Interface: ContractMethods 2 | 3 | [types](../modules/types.md).ContractMethods 4 | 5 | Return type for any on chain call 6 | 7 | ## Properties 8 | 9 | ### call 10 | 11 | • **call**: (`additionalOverrides?`: `Overrides`) => `Promise`<`ContractTransactionResponse`\> 12 | 13 | #### Type declaration 14 | 15 | ▸ (`additionalOverrides?`): `Promise`<`ContractTransactionResponse`\> 16 | 17 | ##### Parameters 18 | 19 | | Name | Type | 20 | | :------ | :------ | 21 | | `additionalOverrides?` | `Overrides` | 22 | 23 | ##### Returns 24 | 25 | `Promise`<`ContractTransactionResponse`\> 26 | 27 | ___ 28 | 29 | ### callStatic 30 | 31 | • **callStatic**: (`additionalOverrides?`: `Overrides`) => `Promise`<`any`\> 32 | 33 | #### Type declaration 34 | 35 | ▸ (`additionalOverrides?`): `Promise`<`any`\> 36 | 37 | ##### Parameters 38 | 39 | | Name | Type | 40 | | :------ | :------ | 41 | | `additionalOverrides?` | `Overrides` | 42 | 43 | ##### Returns 44 | 45 | `Promise`<`any`\> 46 | 47 | ___ 48 | 49 | ### estimateGas 50 | 51 | • **estimateGas**: (`additionalOverrides?`: `Overrides`) => `Promise`<`bigint`\> 52 | 53 | #### Type declaration 54 | 55 | ▸ (`additionalOverrides?`): `Promise`<`bigint`\> 56 | 57 | ##### Parameters 58 | 59 | | Name | Type | 60 | | :------ | :------ | 61 | | `additionalOverrides?` | `Overrides` | 62 | 63 | ##### Returns 64 | 65 | `Promise`<`bigint`\> 66 | -------------------------------------------------------------------------------- /doc/interfaces/types.CreateMakerAskOutput.md: -------------------------------------------------------------------------------- 1 | # Interface: CreateMakerAskOutput 2 | 3 | [types](../modules/types.md).CreateMakerAskOutput 4 | 5 | Return object of createMakerAsk 6 | 7 | ## Properties 8 | 9 | ### isCollectionApproved 10 | 11 | • **isCollectionApproved**: `boolean` 12 | 13 | ___ 14 | 15 | ### isTransferManagerApproved 16 | 17 | • **isTransferManagerApproved**: `boolean` 18 | 19 | ___ 20 | 21 | ### maker 22 | 23 | • **maker**: [`Maker`](types.Maker.md) 24 | -------------------------------------------------------------------------------- /doc/interfaces/types.CreateMakerBidOutput.md: -------------------------------------------------------------------------------- 1 | # Interface: CreateMakerBidOutput 2 | 3 | [types](../modules/types.md).CreateMakerBidOutput 4 | 5 | Return object of createMakerBid 6 | 7 | ## Properties 8 | 9 | ### isBalanceSufficient 10 | 11 | • **isBalanceSufficient**: `boolean` 12 | 13 | ___ 14 | 15 | ### isCurrencyApproved 16 | 17 | • **isCurrencyApproved**: `boolean` 18 | 19 | ___ 20 | 21 | ### maker 22 | 23 | • **maker**: [`Maker`](types.Maker.md) 24 | -------------------------------------------------------------------------------- /doc/interfaces/types.CreateMakerInput.md: -------------------------------------------------------------------------------- 1 | # Interface: CreateMakerInput 2 | 3 | [types](../modules/types.md).CreateMakerInput 4 | 5 | Input of the createMakerAsk function 6 | 7 | ## Properties 8 | 9 | ### additionalParameters 10 | 11 | • `Optional` **additionalParameters**: `any`[] 12 | 13 | Additional parameters for complex orders 14 | 15 | **`Default Value`** 16 | 17 | [] 18 | 19 | ___ 20 | 21 | ### amounts 22 | 23 | • `Optional` **amounts**: `BigNumberish`[] 24 | 25 | Amount for each item ids (needs to have the same length as itemIds array) 26 | 27 | ___ 28 | 29 | ### collection 30 | 31 | • **collection**: `string` 32 | 33 | Collection address 34 | 35 | ___ 36 | 37 | ### collectionType 38 | 39 | • **collectionType**: [`CollectionType`](../enums/types.CollectionType.md) 40 | 41 | Asset type, 0: ERC-721, 1:ERC-1155, etc 42 | 43 | ___ 44 | 45 | ### currency 46 | 47 | • `Optional` **currency**: `string` 48 | 49 | Currency address 50 | 51 | **`Default Value`** 52 | 53 | ETH 54 | 55 | ___ 56 | 57 | ### endTime 58 | 59 | • **endTime**: `BigNumberish` 60 | 61 | Timestamp in seconds when the order becomes invalid 62 | 63 | ___ 64 | 65 | ### itemIds 66 | 67 | • **itemIds**: `BigNumberish`[] 68 | 69 | List of items ids to be sold 70 | 71 | **`Default Value`** 72 | 73 | [1] 74 | 75 | ___ 76 | 77 | ### orderNonce 78 | 79 | • **orderNonce**: `BigNumberish` 80 | 81 | Order nonce, get it from the LooksRare api 82 | 83 | ___ 84 | 85 | ### price 86 | 87 | • **price**: `BigNumberish` 88 | 89 | Asset price in wei 90 | 91 | ___ 92 | 93 | ### startTime 94 | 95 | • `Optional` **startTime**: `BigNumberish` 96 | 97 | Order validity start time 98 | 99 | **`Default Value`** 100 | 101 | now 102 | 103 | ___ 104 | 105 | ### strategyId 106 | 107 | • **strategyId**: [`StrategyType`](../enums/types.StrategyType.md) 108 | 109 | Strategy ID, 0: Standard, 1: Collection, etc 110 | 111 | ___ 112 | 113 | ### subsetNonce 114 | 115 | • **subsetNonce**: `BigNumberish` 116 | 117 | Subset nonce used to group an arbitrary number of orders under the same nonce 118 | -------------------------------------------------------------------------------- /doc/interfaces/types.Maker.md: -------------------------------------------------------------------------------- 1 | # Interface: Maker 2 | 3 | [types](../modules/types.md).Maker 4 | 5 | Maker object to be used in execute functions 6 | 7 | ## Properties 8 | 9 | ### additionalParameters 10 | 11 | • **additionalParameters**: `BytesLike` 12 | 13 | Additional parameters for complex orders 14 | 15 | ___ 16 | 17 | ### amounts 18 | 19 | • **amounts**: `BigNumberish`[] 20 | 21 | List of amount for each item ID (1 for ERC721) 22 | 23 | ___ 24 | 25 | ### collection 26 | 27 | • **collection**: `string` 28 | 29 | Collection address 30 | 31 | ___ 32 | 33 | ### collectionType 34 | 35 | • **collectionType**: [`CollectionType`](../enums/types.CollectionType.md) 36 | 37 | Asset type, 0: ERC-721, 1:ERC-1155, etc 38 | 39 | ___ 40 | 41 | ### currency 42 | 43 | • **currency**: `string` 44 | 45 | Currency address (zero address for ETH) 46 | 47 | ___ 48 | 49 | ### endTime 50 | 51 | • **endTime**: `BigNumberish` 52 | 53 | Timestamp in second of the time when the order becomes invalid 54 | 55 | ___ 56 | 57 | ### globalNonce 58 | 59 | • **globalNonce**: `BigNumberish` 60 | 61 | User's current bid / ask nonce 62 | 63 | ___ 64 | 65 | ### itemIds 66 | 67 | • **itemIds**: `BigNumberish`[] 68 | 69 | List of item IDS 70 | 71 | ___ 72 | 73 | ### orderNonce 74 | 75 | • **orderNonce**: `BigNumberish` 76 | 77 | Nonce for this specific order 78 | 79 | ___ 80 | 81 | ### price 82 | 83 | • **price**: `BigNumberish` 84 | 85 | Minimum price to be received after the trade 86 | 87 | ___ 88 | 89 | ### quoteType 90 | 91 | • **quoteType**: [`QuoteType`](../enums/types.QuoteType.md) 92 | 93 | Bid (0) or Ask (1) 94 | 95 | ___ 96 | 97 | ### signer 98 | 99 | • **signer**: `string` 100 | 101 | Signer address 102 | 103 | ___ 104 | 105 | ### startTime 106 | 107 | • **startTime**: `BigNumberish` 108 | 109 | Timestamp in second of the time when the order starts to be valid 110 | 111 | ___ 112 | 113 | ### strategyId 114 | 115 | • **strategyId**: [`StrategyType`](../enums/types.StrategyType.md) 116 | 117 | Strategy ID, 0: Standard, 1: Collection, etc 118 | 119 | ___ 120 | 121 | ### subsetNonce 122 | 123 | • **subsetNonce**: `BigNumberish` 124 | 125 | Subset nonce used to group an arbitrary number of orders under the same nonce 126 | -------------------------------------------------------------------------------- /doc/interfaces/types.MerkleTree.md: -------------------------------------------------------------------------------- 1 | # Interface: MerkleTree 2 | 3 | [types](../modules/types.md).MerkleTree 4 | 5 | Merkle root object to be used in the execute function for a multi listing 6 | 7 | ## Properties 8 | 9 | ### proof 10 | 11 | • **proof**: [`MerkleTreeProof`](types.MerkleTreeProof.md)[] 12 | 13 | ___ 14 | 15 | ### root 16 | 17 | • **root**: `string` 18 | -------------------------------------------------------------------------------- /doc/interfaces/types.MerkleTreeProof.md: -------------------------------------------------------------------------------- 1 | # Interface: MerkleTreeProof 2 | 3 | [types](../modules/types.md).MerkleTreeProof 4 | 5 | Merkle tree proof to be used within a merkle tree 6 | 7 | ## Properties 8 | 9 | ### position 10 | 11 | • **position**: [`MerkleTreeNodePosition`](../enums/types.MerkleTreeNodePosition.md) 12 | 13 | ___ 14 | 15 | ### value 16 | 17 | • **value**: `string` 18 | -------------------------------------------------------------------------------- /doc/interfaces/types.Referrer.md: -------------------------------------------------------------------------------- 1 | # Interface: Referrer 2 | 3 | [types](../modules/types.md).Referrer 4 | 5 | Referrer object to be used in maker order API 6 | 7 | ## Properties 8 | 9 | ### address 10 | 11 | • **address**: `string` 12 | 13 | ___ 14 | 15 | ### rate 16 | 17 | • **rate**: `BigNumberish` 18 | -------------------------------------------------------------------------------- /doc/interfaces/types.SignMerkleTreeOrdersOutput.md: -------------------------------------------------------------------------------- 1 | # Interface: SignMerkleTreeOrdersOutput 2 | 3 | [types](../modules/types.md).SignMerkleTreeOrdersOutput 4 | 5 | Return type of the sign multiple orders function 6 | 7 | ## Properties 8 | 9 | ### merkleTreeProofs 10 | 11 | • **merkleTreeProofs**: [`MerkleTree`](types.MerkleTree.md)[] 12 | 13 | ___ 14 | 15 | ### signature 16 | 17 | • **signature**: `string` 18 | 19 | ___ 20 | 21 | ### tree 22 | 23 | • **tree**: `Eip712MakerMerkleTree` 24 | -------------------------------------------------------------------------------- /doc/interfaces/types.StrategyInfo.md: -------------------------------------------------------------------------------- 1 | # Interface: StrategyInfo 2 | 3 | [types](../modules/types.md).StrategyInfo 4 | 5 | Return type for strategyInfo 6 | 7 | ## Properties 8 | 9 | ### isActive 10 | 11 | • **isActive**: `boolean` 12 | 13 | ___ 14 | 15 | ### maxProtocolFeeBp 16 | 17 | • **maxProtocolFeeBp**: `number` 18 | 19 | ___ 20 | 21 | ### minTotalFeeBp 22 | 23 | • **minTotalFeeBp**: `number` 24 | 25 | ___ 26 | 27 | ### standardProtocolFeeBp 28 | 29 | • **standardProtocolFeeBp**: `number` 30 | -------------------------------------------------------------------------------- /doc/interfaces/types.Taker.md: -------------------------------------------------------------------------------- 1 | # Interface: Taker 2 | 3 | [types](../modules/types.md).Taker 4 | 5 | Taker order 6 | 7 | ## Properties 8 | 9 | ### additionalParameters 10 | 11 | • **additionalParameters**: `BytesLike` 12 | 13 | Additional parameters for complex orders 14 | 15 | ___ 16 | 17 | ### recipient 18 | 19 | • **recipient**: `string` 20 | 21 | Recipient of the transaction 22 | -------------------------------------------------------------------------------- /doc/modules/LooksRare.md: -------------------------------------------------------------------------------- 1 | # Module: LooksRare 2 | 3 | ## Classes 4 | 5 | - [LooksRare](../classes/LooksRare.LooksRare.md) 6 | -------------------------------------------------------------------------------- /doc/modules/types.md: -------------------------------------------------------------------------------- 1 | # Module: types 2 | 3 | ## Enumerations 4 | 5 | - [ChainId](../enums/types.ChainId.md) 6 | - [CollectionType](../enums/types.CollectionType.md) 7 | - [MerkleTreeNodePosition](../enums/types.MerkleTreeNodePosition.md) 8 | - [OrderValidatorCode](../enums/types.OrderValidatorCode.md) 9 | - [QuoteType](../enums/types.QuoteType.md) 10 | - [StrategyType](../enums/types.StrategyType.md) 11 | 12 | ## Interfaces 13 | 14 | - [Addresses](../interfaces/types.Addresses.md) 15 | - [BatchTransferItem](../interfaces/types.BatchTransferItem.md) 16 | - [ChainInfo](../interfaces/types.ChainInfo.md) 17 | - [ContractMethods](../interfaces/types.ContractMethods.md) 18 | - [CreateMakerAskOutput](../interfaces/types.CreateMakerAskOutput.md) 19 | - [CreateMakerBidOutput](../interfaces/types.CreateMakerBidOutput.md) 20 | - [CreateMakerInput](../interfaces/types.CreateMakerInput.md) 21 | - [Maker](../interfaces/types.Maker.md) 22 | - [MerkleTree](../interfaces/types.MerkleTree.md) 23 | - [MerkleTreeProof](../interfaces/types.MerkleTreeProof.md) 24 | - [Referrer](../interfaces/types.Referrer.md) 25 | - [SignMerkleTreeOrdersOutput](../interfaces/types.SignMerkleTreeOrdersOutput.md) 26 | - [StrategyInfo](../interfaces/types.StrategyInfo.md) 27 | - [Taker](../interfaces/types.Taker.md) 28 | 29 | ## Type Aliases 30 | 31 | ### CreateMakerCollectionOfferInput 32 | 33 | Ƭ **CreateMakerCollectionOfferInput**: `Omit`<[`CreateMakerInput`](../interfaces/types.CreateMakerInput.md), ``"strategyId"`` \| ``"itemIds"``\> 34 | 35 | ___ 36 | 37 | ### CreateMakerCollectionOfferWithProofInput 38 | 39 | Ƭ **CreateMakerCollectionOfferWithProofInput**: `Omit`<[`CreateMakerInput`](../interfaces/types.CreateMakerInput.md), ``"strategyId"``\> 40 | 41 | ___ 42 | 43 | ### EIP712TypedData 44 | 45 | Ƭ **EIP712TypedData**: `Record`<`string`, `TypedDataField`[]\> 46 | 47 | EIP712 type data 48 | 49 | ___ 50 | 51 | ### SolidityType 52 | 53 | Ƭ **SolidityType**: ``"bool"`` \| ``"address"`` \| ``"uint8"`` \| ``"uint16"`` \| ``"uint112"`` \| ``"uint256"`` \| ``"uint256[]"`` \| ``"bytes"`` \| ``"bytes32"`` \| ``"bytes32[]"`` \| ``"string"`` 54 | 55 | Solidity types (used for EIP-712 types) 56 | -------------------------------------------------------------------------------- /guides/cancelOrders.md: -------------------------------------------------------------------------------- 1 | :warning: These code snippets are just examples and the data should never be used as is :warning: 2 | 3 | # How to cancel orders 4 | 5 | ## Cancel orders using order nonces 6 | 7 | This method is used to invalidate the `orderNonce`. If multiple maker orders share the same nonce, they will all be cancelled and non-executable. An `orderNonce` is also invalidated once an order with that nonce is executed. 8 | 9 | ```ts 10 | import { LooksRare, ChainId } from "@looksrare/sdk-v2"; 11 | 12 | const lr = new LooksRare(ChainId.MAINNET, provider, signer); 13 | 14 | // Cancel order nonce 0 15 | const tx = await lr.cancelOrders([0]).call(); 16 | const receipt = await tx.wait(); 17 | 18 | // Cancel order nonce 0 and 12 19 | const tx = await lr.cancelOrders([0, 12]).call(); 20 | const receipt = await tx.wait(); 21 | ``` 22 | 23 | ## Cancel orders using subset nonces 24 | 25 | The `subsetNonce` can only be invalidated/cancelled manually. The purpose of the `subsetNonce` is to allow the user to arbitrarily group different orders under the same `subsetNonce`, for example, all the orders from the same collection could have the same `subsetNonce`, allowing for a more targeted cancel functionality. 26 | 27 | This is pending implementation within our frontend, it should only be used by users who have a thorough understanding of its functionality. 28 | 29 | ### Important to note 30 | 31 | If you cancel the `subsetNonce` with a value of 0, that address will be unable to create orders via our [website](https://looksrare.org) as this feature is currently not implemented and orders are only created with the `subsetNonce` set to 0. If you plan to make use of the `subsetNonce` functionalities, consider using a value greater than 0. 32 | 33 | ```ts 34 | import { LooksRare, ChainId } from "@looksrare/sdk-v2"; 35 | 36 | const lr = new LooksRare(ChainId.MAINNET, provider, signer); 37 | 38 | // Cancel order with the subset nonce 1 39 | const tx = await lr.cancelSubsetOrders([1]).call(); 40 | const receipt = await tx.wait(); 41 | 42 | // Cancel order with the subset 1 and 12 43 | const tx = await lr.cancelSubsetOrders([1, 12]).call(); 44 | const receipt = await tx.wait(); 45 | ``` 46 | 47 | ## Cancel all your bids and/or all your asks 48 | 49 | This function can be used to cancel all the sender's bids or all the sender's asks, or both in a single call. The following example showcases all the possible combinations. 50 | 51 | ```ts 52 | import { LooksRare, ChainId } from "@looksrare/sdk-v2"; 53 | 54 | const lr = new LooksRare(ChainId.MAINNET, provider, signer); 55 | 56 | // Cancel all bids 57 | const tx = await lr.cancelAllOrders(true, false).call(); 58 | const receipt = await tx.wait(); 59 | 60 | // Cancel all asks 61 | const tx = await lr.cancelAllOrders(false, true).call(); 62 | const receipt = await tx.wait(); 63 | 64 | // Cancel all bids and asks 65 | const tx = await lr.cancelAllOrders(true, true).call(); 66 | const receipt = await tx.wait(); 67 | ``` 68 | 69 | For more details on our triple-nonce system, see the related documentation: [Triple-nonce system](https://docs.looksrare.org/developers/protocol/triple-nonce-system-v2) 70 | 71 | ## Need help? 72 | 73 | You can reach out to the LooksRare team via our Developers Discord: [https://discord.gg/LooksRareDevelopers](https://discord.gg/LooksRareDevelopers) 74 | -------------------------------------------------------------------------------- /guides/collectionOrder.md: -------------------------------------------------------------------------------- 1 | :warning: These code snippets are just examples and the data should never be used as is :warning: 2 | 3 | # How to create a collection offer (maker bid with collection strategy) 4 | 5 | The code snippet below is an example of how to create a maker bid using the `@looksrare/sdk-v2` library. 6 | 7 | > A collection order is just a maker bid order using the `StrategyType.collection`. 8 | 9 | The main steps are: 10 | 11 | 1. Initialize a LooksRare class instance by providing the chain id, [RPC provider](https://docs.ethers.io/v5/api/providers/) and a [signer](https://docs.ethers.io/v5/api/signer/). 12 | 2. Use the `createMakerCollectionOffer` method to create a maker bid with the parameters of your order. 13 | 3. Check and grant necessary approvals for transferring assets. 14 | 4. Sign the maker bid order with `signMakerOrder` method. 15 | 16 | > The `orderNonce` has to be retrieved via our Public API, see [get order nonce](https://looksrare.dev/v2/reference/getordernonce). 17 | 18 | Here is an example: 19 | 20 | ```ts 21 | import { parseEther } from "ethers"; 22 | import { LooksRare, ChainId, CollectionType, StrategyType } from "@looksrare/sdk-v2"; 23 | 24 | const lr = new LooksRare(ChainId.MAINNET, provider, signer); 25 | 26 | const { maker, isCurrencyApproved, isBalanceSufficient } = await lr.createMakerCollectionOffer({ 27 | collection: "0x0000000000000000000000000000000000000000", // Collection address 28 | collectionType: CollectionType.ERC721, 29 | subsetNonce: 0, // keep 0 if you don't know what it is used for 30 | orderNonce: 0, // You need to retrieve this value from the API 31 | endTime: Math.floor(Date.now() / 1000) + 86400, // If you use a timestamp in ms, the function will revert 32 | price: parseEther("1"), // Be careful to use a price in wei, this example is for 1 ETH 33 | amounts: [1], // Use it for listing multiple ERC-1155 (Optional, Default to [1]) 34 | startTime: Math.floor(Date.now() / 1000), // Use it to create an order that will be valid in the future (Optional, Default to now) 35 | }); 36 | 37 | // Approve spending of the currency used for bidding 38 | if (!isCurrencyApproved) { 39 | const tx = await lr.approveErc20(lr.addresses.WETH); 40 | await tx.wait(); 41 | } 42 | 43 | // Checks if the WETH balance is enough to cover the bid 44 | if (!isBalanceSufficient) { 45 | throw new Error(`WETH balance too low.`); 46 | } 47 | 48 | // Sign your maker order 49 | const signature = await lr.signMakerOrder(maker); 50 | ``` 51 | 52 | > Once, the maker bid for your collection offer has been created, the approvals sorted and the order signed, you will have to send it along with the signature to the `POST /api/v2/orders` endpoint. For more details and examples, see [create order](https://looksrare.dev/v2/reference/createorder)). 53 | 54 | # How to execute a collection offer 55 | 56 | `createTakerForCollectionOrder` is just a convenient wrapper around `createTaker`. 57 | 58 | ```ts 59 | import { LooksRare, ChainId } from "@looksrare/sdk-v2"; 60 | 61 | const lr = new LooksRare(ChainId.MAINNET, provider, signer); 62 | 63 | // To be done only once the first a user is interacting with the V2. 64 | // It will grant the Exchange contract with the right to use your collections approvals done on the transfer manager. 65 | await lr.grantTransferManagerApproval().call(); 66 | await setApprovalForAll(signer, maker.collection, lr.addresses.TRANSFER_MANAGER_V2); 67 | 68 | const taker = lr.createTakerForCollectionOrder(maker, TOKEN_ID); // Change the token id 69 | 70 | const { call } = lr.executeOrder(maker, taker, signature); 71 | const tx = await call(); 72 | const receipt = await tx.wait(); 73 | ``` 74 | 75 | ## Need help? 76 | 77 | You can reach out to the LooksRare team via our Developers Discord: [https://discord.gg/LooksRareDevelopers](https://discord.gg/LooksRareDevelopers) 78 | -------------------------------------------------------------------------------- /guides/createMakerAsk.md: -------------------------------------------------------------------------------- 1 | :warning: These code snippets are just examples and the data should never be used as is :warning: 2 | 3 | # How to create a maker ask order 4 | 5 | The code snippet below is an example of how to create a maker ask using the `@looksrare/sdk-v2` library. 6 | 7 | The main steps are: 8 | 9 | 1. Initialize a LooksRare class instance by providing the chain id, [RPC provider](https://docs.ethers.io/v5/api/providers/) and a [signer](https://docs.ethers.io/v5/api/signer/). 10 | 2. Use the `createMakerAsk` method to create a maker ask with the parameters of your order. 11 | 3. Check and grant necessary approvals for transferring assets. 12 | 4. Sign the maker ask order with `signMakerOrder` method. 13 | 14 | > The `orderNonce` has to be retrieved via our Public API, see [get order nonce](https://looksrare.dev/v2/reference/getordernonce). 15 | 16 | Here is an example: 17 | 18 | ```ts 19 | import { parseEther } from "ethers"; 20 | import { LooksRare, ChainId, CollectionType, StrategyType } from "@looksrare/sdk-v2"; 21 | 22 | const lr = new LooksRare(ChainId.MAINNET, provider, signer); 23 | 24 | const { maker, isCollectionApproved, isTransferManagerApproved } = await lr.createMakerAsk({ 25 | collection: "0x0000000000000000000000000000000000000000", // Collection address 26 | collectionType: CollectionType.ERC721, 27 | strategyId: StrategyType.standard, 28 | subsetNonce: 0, // keep 0 if you don't know what it is used for 29 | orderNonce: 0, // You need to retrieve this value from the API 30 | endTime: Math.floor(Date.now() / 1000) + 86400, // If you use a timestamp in ms, the function will revert 31 | price: parseEther("1"), // Be careful to use a price in wei, this example is for 1 ETH 32 | itemIds: [0], // Token id of the NFT(s) you want to sell, add several ids to create a bundle 33 | amounts: [1], // Use it for listing multiple ERC-1155 (Optional, Default to [1]) 34 | startTime: Math.floor(Date.now() / 1000), // Use it to create an order that will be valid in the future (Optional, Default to now) 35 | }); 36 | 37 | // Grant the TransferManager the right the transfer assets on behalf od the LooksRareProtocol 38 | if (!isTransferManagerApproved) { 39 | const tx = await lr.grantTransferManagerApproval().call(); 40 | await tx.wait(); 41 | } 42 | 43 | // Approve the collection items to be transferred by the TransferManager 44 | if (!isCollectionApproved) { 45 | const tx = await lr.approveAllCollectionItems(maker.collection); 46 | await tx.wait(); 47 | } 48 | 49 | // Sign your maker order 50 | const signature = await lr.signMakerOrder(maker); 51 | ``` 52 | 53 | > Once, the maker ask for your collection offer has been created, the approvals sorted and the order signed, you will have to send it along with the signature to the `POST /api/v2/orders` endpoint. For more details and examples, see [create order](https://looksrare.dev/v2/reference/createorder)). 54 | 55 | ## Need help? 56 | 57 | You can reach out to the LooksRare team via our Developers Discord: [https://discord.gg/LooksRareDevelopers](https://discord.gg/LooksRareDevelopers) 58 | -------------------------------------------------------------------------------- /guides/createMakerBid.md: -------------------------------------------------------------------------------- 1 | :warning: These code snippets are just examples and the data should never be used as is :warning: 2 | 3 | # How to create a maker bid order 4 | 5 | The code snippet below is an example of how to create a maker bid using the `@looksrare/sdk-v2` library. 6 | 7 | The main steps are: 8 | 9 | 1. Initialize a LooksRare class instance by providing the chain id, [JSON-RPC provider](https://docs.ethers.org/v6/api/providers/jsonrpc/) and a [signer](https://docs.ethers.org/v6/api/providers/#Signer). 10 | 2. Use the `createMakerBid` method to create a maker bid with the parameters of your order. 11 | 3. Check and grant necessary approvals for transferring assets. 12 | 4. Sign the maker bid order with `signMakerOrder` method. 13 | 14 | > The `orderNonce` has to be retrieved via our Public API, see [get order nonce](https://looksrare.dev/v2/reference/getordernonce). 15 | 16 | Here is an example: 17 | 18 | ```ts 19 | import { ethers } from "ethers"; 20 | import { LooksRare, ChainId, CollectionType, StrategyType } from "@looksrare/sdk-v2"; 21 | 22 | const lr = new LooksRare(ChainId.MAINNET, provider, signer); 23 | 24 | const { maker, isCurrencyApproved, isBalanceSufficient } = await lr.createMakerBid({ 25 | collection: "0x0000000000000000000000000000000000000000", // Collection address 26 | collectionType: CollectionType.ERC721, 27 | strategyId: StrategyType.standard, 28 | subsetNonce: 0, // keep 0 if you don't know what it is used for 29 | orderNonce: 0, // You need to retrieve this value from the API 30 | endTime: Math.floor(Date.now() / 1000) + 86400, // If you use a timestamp in ms, the function will revert 31 | price: parseEther("1"), // Be careful to use a price in wei, this example is for 1 ETH 32 | itemIds: [0], // Token id of the NFT you want to buy 33 | amounts: [1], // Use it for listing several ERC-1155 (Optional, Default to [1]) 34 | startTime: Math.floor(Date.now() / 1000), // Use it to create an order that will be valid in the future (Optional, Default to now) 35 | }); 36 | 37 | // Approve spending of the currency used for bidding 38 | if (!isCurrencyApproved) { 39 | const tx = await lr.approveErc20(lr.addresses.WETH); 40 | await tx.wait(); 41 | } 42 | 43 | // Checks if the WETH balance is enough to cover the bid 44 | if (!isBalanceSufficient) { 45 | throw new Error(`WETH balance too low.`); 46 | } 47 | 48 | // Sign your maker order 49 | const signature = await lr.signMakerOrder(maker); 50 | ``` 51 | 52 | > Once, the maker bid for your collection offer has been created, the approvals sorted and the order signed, you will have to send it along with the signature to the `POST /api/v2/orders` endpoint. For more details and examples, see [create order](https://looksrare.dev/v2/reference/createorder)). 53 | 54 | ## Need help? 55 | 56 | You can reach out to the LooksRare team via our Developers Discord: [https://discord.gg/LooksRareDevelopers](https://discord.gg/LooksRareDevelopers) 57 | -------------------------------------------------------------------------------- /guides/executeTrade.md: -------------------------------------------------------------------------------- 1 | :warning: These code snippets are just examples and the data should never be used as is :warning: 2 | 3 | # How to create a Taker order and execute a trade 4 | 5 | Trades are executed on-chain by matching a `Maker` order with a `Taker` order. The maker order can be retrieved from the API, see [get v2 orders](https://looksrare.dev/v2/reference/getorders) for more details. While the taker order can be obtained by calling the `createTaker` method as shown here: 6 | 7 | ```ts 8 | import { LooksRare, ChainId } from "@looksrare/sdk-v2"; 9 | 10 | const lr = new LooksRare(ChainId.MAINNET, provider, signer); 11 | 12 | // The recipient address is optional, if you don't provide it will use your signer address 13 | const takerOrder = lr.createTaker(makerOrder, recipientAddress); 14 | ``` 15 | 16 | From the API response, you will also get the `signature`, which is necessary to execute the trade on-chain. To execute the trade, you can call the `executeOrder` method passing the `Maker`, `Taker` and the `signature`. The method will return a contract call. Here is an example: 17 | 18 | ```ts 19 | const { call } = lr.executeOrder(makerOrder, takerOrder, signature); 20 | const tx = await call(); 21 | const receipt = await tx.wait(); 22 | ``` 23 | 24 | ## Need help? 25 | 26 | You can reach out to the LooksRare team via our Developers Discord: [https://discord.gg/LooksRareDevelopers](https://discord.gg/LooksRareDevelopers) 27 | -------------------------------------------------------------------------------- /guides/orderValidity.md: -------------------------------------------------------------------------------- 1 | :warning: These code snippets are just examples and the data should never be used as is :warning: 2 | 3 | # Verify order validity 4 | 5 | The LooksRare SDK also provides a method to validate your order. It can be used as follows: 6 | 7 | ```ts 8 | import { LooksRare, ChainId } from "@looksrare/sdk-v2"; 9 | 10 | const lr = new LooksRare(ChainId.MAINNET, provider, signer); 11 | 12 | const validatorCodes = await lr.verifyMakerOrders([makerOrder], [signature]); 13 | ``` 14 | 15 | To see all the possible validation codes, see the `OrderValidatorCode` enum located in [src/types.ts](../src/types.ts#L217). 16 | 17 | ## Need help? 18 | 19 | You can reach out to the LooksRare team via our Developers Discord: [https://discord.gg/LooksRareDevelopers](https://discord.gg/LooksRareDevelopers) 20 | -------------------------------------------------------------------------------- /guides/readme.md: -------------------------------------------------------------------------------- 1 | # Guides 2 | 3 | - [How to create a maker ask order](./createMakerAsk.md) 4 | - [How to create a maker bid order](./createMakerBid.md) 5 | - [How to create a Taker order and execute a trade](./executeTrade.md) 6 | - [How to create and execute collection orders](./collectionOrder.md) 7 | - [How to cancel orders](./cancelOrders.md) 8 | - [Verify order validity](./orderValidity.md) 9 | -------------------------------------------------------------------------------- /guides/signMultipleMakerOrders.md: -------------------------------------------------------------------------------- 1 | :warning: These code snippets are just examples and the data should never be used as is :warning: 2 | 3 | # Sign multiple maker orders with a single signature 4 | 5 | > **This functionality is for UI implementations only. If you are using a bot, don't use the `signMultipleMakerOrders` function, just loop over the `createMakerBid` and `createMakerAsk` functions.** 6 | 7 | The code snippet below is an example of how to sign multiple orders with one signature using Merkle trees via the `@looksrare/sdk-v2` library. 8 | 9 | **NOTE**: In this example we only used maker asks, for maker bids the approvals logic needs to be adjusted. See the documentation on [creating maker bids](./createMakerBid.md) for more details. 10 | 11 | ```ts 12 | import { ethers } from "ethers"; 13 | import { LooksRare, ChainId, CollectionType, StrategyType } from "@looksrare/sdk-v2"; 14 | 15 | const lr = new LooksRare(ChainId.MAINNET, provider, signer); 16 | 17 | const orders = []; 18 | 19 | orders.push(await lr.createMakerAsk(...)); 20 | orders.push(await lr.createMakerAsk(...)); 21 | 22 | // Grant the TransferManager the right the transfer assets on behalf od the LooksRareProtocol. Only needs to be done once per signer. 23 | if (!orders[0].isTransferManagerApproved) { 24 | const tx = await lr.grantTransferManagerApproval().call(); 25 | await tx.wait(); 26 | } 27 | 28 | for (const order of orders) { 29 | // Approve the collection items to be transferred by the TransferManager 30 | if (!order.isCollectionApproved) { 31 | const tx = await lr.approveAllCollectionItems(order.maker.collection); 32 | await tx.wait(); 33 | } 34 | } 35 | 36 | const { signature, merkleTreeProofs } = await lr.signMultipleMakerOrders(orders); 37 | ``` 38 | 39 | > The maker orders, merkleTreeProofs and signature will have be sent to the `POST /api/v2/orders/tree` endpoint. For more details and examples, see [create Merkle tree order](https://looksrare.dev/v2/reference/createmerkletree). 40 | 41 | For more information on how to create your orders, see the [createMakerAsk](./createMakerAsk.md) and [createMakerBid](./createMakerBid.md) documentation. 42 | 43 | ## Need help? 44 | 45 | You can reach out to the LooksRare team via our Developers Discord: [https://discord.gg/LooksRareDevelopers](https://discord.gg/LooksRareDevelopers) 46 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-ethers"; 2 | import "@typechain/hardhat"; 3 | import "hardhat-abi-exporter"; 4 | 5 | const config = { 6 | defaultNetwork: "hardhat", 7 | networks: { 8 | hardhat: { 9 | allowUnlimitedContractSize: true, 10 | }, 11 | }, 12 | solidity: { 13 | compilers: [ 14 | { 15 | version: "0.8.17", 16 | settings: { 17 | optimizer: { 18 | enabled: true, 19 | runs: 200, 20 | }, 21 | }, 22 | }, 23 | { 24 | version: "0.4.18", 25 | settings: { 26 | optimizer: { 27 | enabled: true, 28 | runs: 200, 29 | }, 30 | }, 31 | }, 32 | ], 33 | }, 34 | abiExporter: { 35 | path: "./src/abis", 36 | runOnCompile: true, 37 | clear: true, 38 | flat: true, 39 | pretty: false, 40 | only: [ 41 | "LooksRareProtocol", 42 | "TransferManager", 43 | "OrderValidatorV2A", 44 | "IERC721", 45 | "IERC1155", 46 | "IERC20", 47 | "WETH", 48 | "ProtocolHelpers", 49 | ], 50 | }, 51 | typechain: { 52 | outDir: "src/typechain", 53 | target: "ethers-v6", 54 | }, 55 | paths: { 56 | tests: "src/__tests__", 57 | artifacts: "src/artifacts", 58 | sources: "src/contracts", 59 | }, 60 | }; 61 | 62 | export default config; 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@looksrare/sdk-v2", 3 | "version": "1.3.3", 4 | "license": "MIT", 5 | "main": "dist/index.cjs.js", 6 | "module": "dist/index.esm.js", 7 | "types": "dist/index.d.ts", 8 | "files": [ 9 | "dist" 10 | ], 11 | "publishConfig": { 12 | "access": "public", 13 | "registry": "https://registry.npmjs.org" 14 | }, 15 | "keywords": [ 16 | "looksrare" 17 | ], 18 | "homepage": "https://looksrare.org/", 19 | "bugs": "https://github.com/LooksRare/sdk-v2/issues", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/LooksRare/sdk-v2.git" 23 | }, 24 | "scripts": { 25 | "prebuild": "rm -rf ./src/typechain ./src/artifacts cache dist", 26 | "prepublishOnly": "yarn build", 27 | "dev": "rollup -c --bundleConfigAsCjs -w", 28 | "build:ts": "rollup -c --bundleConfigAsCjs", 29 | "build:sc": "hardhat compile", 30 | "build": "yarn build:sc && yarn build:ts", 31 | "test": "nyc hardhat test", 32 | "doc": "typedoc --plugin typedoc-plugin-markdown --tsconfig tsconfig.build.json", 33 | "lint": "eslint --max-warnings 0 'src/**/*.{js,ts}'", 34 | "format:check": "prettier --check 'src/**/*.{js,ts,json,yaml,yml,md}'", 35 | "format:write": "prettier --write 'src/**/*.{js,ts,json,yaml,yml,md}'", 36 | "release": "release-it --only-version --set-upstream" 37 | }, 38 | "lint-staged": { 39 | "*.{js,jsx,ts,tsx,json,yaml,yml}": "yarn format:write" 40 | }, 41 | "peerDependencies": { 42 | "ethers": "^6.6.2" 43 | }, 44 | "devDependencies": { 45 | "@commitlint/cli": "^17.0.2", 46 | "@commitlint/config-conventional": "^17.0.2", 47 | "@istanbuljs/nyc-config-typescript": "^1.0.2", 48 | "@looksrare/contracts-exchange-v1": "^1.2.0", 49 | "@looksrare/contracts-exchange-v2": "^0.1.2", 50 | "@nomicfoundation/hardhat-ethers": "^3.0.4", 51 | "@rollup/plugin-json": "^6.0.0", 52 | "@rollup/plugin-typescript": "^11.0.0", 53 | "@typechain/ethers-v6": "^0.4.0", 54 | "@typechain/hardhat": "^8.0.0", 55 | "@types/chai": "^4.3.1", 56 | "@types/chai-as-promised": "^7.1.5", 57 | "@types/mocha": "^10.0.1", 58 | "@types/node": "^18.14.6", 59 | "@typescript-eslint/eslint-plugin": "^5.54.1", 60 | "@typescript-eslint/parser": "^5.54.1", 61 | "chai": "^4.3.6", 62 | "chai-as-promised": "^7.1.1", 63 | "dotenv": "^16.0.3", 64 | "eslint": "^8.17.0", 65 | "eslint-config-prettier": "^8.7.0", 66 | "eslint-plugin-prettier": "^4.2.1", 67 | "ethers": "^6.6.2", 68 | "hardhat": "^2.16.1", 69 | "hardhat-abi-exporter": "^2.9.0", 70 | "husky": "^8.0.1", 71 | "lint-staged": "^13.0.0", 72 | "nyc": "^15.1.0", 73 | "prettier": "^2.6.2", 74 | "prettier-plugin-solidity": "^1.1.3", 75 | "release-it": "^15.7.0", 76 | "rollup": "^3.18.0", 77 | "rollup-plugin-bundle-size": "^1.0.3", 78 | "rollup-plugin-copy": "^3.4.0", 79 | "solhint": "^3.4.1", 80 | "solmate": "^6.6.1", 81 | "source-map-support": "^0.5.21", 82 | "ts-node": "^10.9.1", 83 | "typechain": "^8.2.0", 84 | "typedoc": "^0.24.7", 85 | "typedoc-plugin-markdown": "^3.15.3", 86 | "typescript": "^4.7.3" 87 | }, 88 | "dependencies": { 89 | "merkletreejs": "^0.3.9" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from "@rollup/plugin-typescript"; 2 | import json from "@rollup/plugin-json"; 3 | import copy from "rollup-plugin-copy"; 4 | import bundleSize from "rollup-plugin-bundle-size"; 5 | import pkg from "./package.json"; 6 | 7 | /** @type {import('rollup').RollupOptions} */ 8 | export default { 9 | input: "src/index.ts", 10 | output: [ 11 | { file: pkg.main, format: "cjs" }, 12 | { file: pkg.module, format: "es" }, 13 | ], 14 | plugins: [ 15 | copy({ 16 | targets: [{ src: "src/abis/**/*", dest: "dist/abis" }], 17 | }), 18 | json(), 19 | typescript({ tsconfig: "./tsconfig.build.json" }), 20 | bundleSize(), 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /src/__tests__/eip712.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { AbiCoder, parseEther } from "ethers"; 3 | import { TypedDataDomain } from "@ethersproject/abstract-signer"; 4 | import { setUpContracts, SetupMocks, getSigners, Signers } from "./helpers/setup"; 5 | import { computeDigestMaker, getDomainSeparator } from "./helpers/eip712"; 6 | import { contractName, version } from "../constants/eip712"; 7 | import { getMakerHash } from "../utils/eip712"; 8 | import { ChainId, Maker, CollectionType, QuoteType } from "../types"; 9 | 10 | describe("EIP-712", () => { 11 | let mocks: SetupMocks; 12 | let signers: Signers; 13 | let domain: TypedDataDomain; 14 | let makerAskOrder: Maker; 15 | 16 | beforeEach(async () => { 17 | mocks = await setUpContracts(); 18 | signers = await getSigners(); 19 | 20 | domain = { 21 | name: contractName, 22 | version: version.toString(), 23 | chainId: ChainId.HARDHAT, 24 | verifyingContract: mocks.addresses.EXCHANGE_V2, 25 | }; 26 | 27 | makerAskOrder = { 28 | quoteType: QuoteType.Ask, 29 | globalNonce: 1, 30 | subsetNonce: 1, 31 | strategyId: 1, 32 | collectionType: CollectionType.ERC721, 33 | orderNonce: 1, 34 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 35 | currency: mocks.addresses.WETH, 36 | signer: signers.user1.address, 37 | startTime: Math.floor(Date.now() / 1000), 38 | endTime: Math.floor(Date.now() / 1000 + 3600), 39 | price: parseEther("1").toString(), 40 | itemIds: [1], 41 | amounts: [1], 42 | additionalParameters: AbiCoder.defaultAbiCoder().encode([], []), 43 | }; 44 | }); 45 | it("validate domain data", async () => { 46 | const { verifier } = mocks.contracts; 47 | const domainSc = await verifier.getDomainSeparator(); 48 | const domainJs = getDomainSeparator(domain); 49 | expect(domainSc === domainJs); 50 | }); 51 | it("validate maker order digest", async () => { 52 | const { verifier } = mocks.contracts; 53 | const digestSc = await verifier.computeMakerDigest(makerAskOrder); 54 | const digestJs = computeDigestMaker(domain, makerAskOrder); 55 | expect(digestSc === digestJs); 56 | }); 57 | it("validate maker ask order hash", async () => { 58 | const { verifier } = mocks.contracts; 59 | const orderHashSc = await verifier.getMakerHash(makerAskOrder); 60 | const orderHashJs = getMakerHash(makerAskOrder); 61 | expect(orderHashSc).to.equal(orderHashJs); 62 | }); 63 | it("validate maker bid order hash", async () => { 64 | const makerBid: Maker = { 65 | ...makerAskOrder, 66 | quoteType: QuoteType.Bid, 67 | }; 68 | 69 | const { verifier } = mocks.contracts; 70 | const orderHashSc = await verifier.getMakerHash(makerBid); 71 | const orderHashJs = getMakerHash(makerBid); 72 | expect(orderHashSc).to.equal(orderHashJs); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /src/__tests__/encodeOrderParams.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { getMakerParamsTypes, getTakerParamsTypes, encodeParams } from "../utils/encodeOrderParams"; 3 | import { StrategyType } from "../types"; 4 | 5 | describe("getMakerParamsTypes", () => { 6 | it("standard", () => { 7 | const paramsTypes = getMakerParamsTypes(StrategyType.standard); 8 | expect(paramsTypes).to.eql([]); 9 | }); 10 | it("collection", () => { 11 | const paramsTypes = getMakerParamsTypes(StrategyType.collection); 12 | expect(paramsTypes).to.eql([]); 13 | }); 14 | it("invalid param", () => { 15 | // Enforce a check with no params just for code coverage 16 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 17 | //@ts-ignore 18 | const paramsTypes = getMakerParamsTypes(); 19 | expect(paramsTypes).to.eql([]); 20 | }); 21 | }); 22 | 23 | describe("getTakerParamsTypes", () => { 24 | it("standard", () => { 25 | const paramsTypes = getTakerParamsTypes(StrategyType.standard); 26 | expect(paramsTypes).to.eql([]); 27 | }); 28 | it("collection", () => { 29 | const paramsTypes = getTakerParamsTypes(StrategyType.collection); 30 | expect(paramsTypes).to.eql(["uint256"]); 31 | }); 32 | it("invalid param", () => { 33 | // Enforce a check with no params just for code coverage 34 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 35 | //@ts-ignore 36 | const paramsTypes = getTakerParamsTypes(); 37 | expect(paramsTypes).to.eql([]); 38 | }); 39 | }); 40 | 41 | describe("encodeOrderParams", () => { 42 | it("standard", () => { 43 | const encodedParams = encodeParams([], getMakerParamsTypes(StrategyType.standard)); 44 | expect(encodedParams).to.equal("0x"); 45 | }); 46 | it("collection", () => { 47 | const encodedParams = encodeParams([], getMakerParamsTypes(StrategyType.collection)); 48 | expect(encodedParams).to.equal("0x"); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /src/__tests__/helpers/eip712.ts: -------------------------------------------------------------------------------- 1 | import { AbiCoder, keccak256, solidityPackedKeccak256, toUtf8Bytes } from "ethers"; 2 | import { TypedDataDomain } from "@ethersproject/abstract-signer"; 3 | import { getMakerHash } from "../../utils/eip712"; 4 | import { Maker, SolidityType } from "../../types"; 5 | 6 | // Emulate contract cryptographic functions using JS. Used for testing purpose. 7 | 8 | /** 9 | * Emulate the EIP-712 domain separator computation 10 | * @external LooksRareProtocol constructor 11 | * @param domain 12 | * @returns string (bytes32) 13 | */ 14 | export const getDomainSeparator = (domain: TypedDataDomain): string => { 15 | const types: SolidityType[] = ["bytes32", "bytes32", "bytes32", "uint256", "address"]; 16 | const values = [ 17 | keccak256(toUtf8Bytes("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")), 18 | keccak256(toUtf8Bytes(domain.name!)), 19 | keccak256(toUtf8Bytes(domain.version!)), 20 | domain.chainId!, 21 | domain.verifyingContract!, 22 | ]; 23 | return keccak256(AbiCoder.defaultAbiCoder().encode(types, values)); 24 | }; 25 | 26 | /** 27 | * Emulate digest computation 28 | * @external LooksRareProtocol _computeDigestAndVerify function 29 | * @param domain 30 | * @param makerOrder 31 | * @returns string (bytes32) 32 | */ 33 | export const computeDigestMaker = (domain: TypedDataDomain, makerOrder: Maker): string => { 34 | const domainSeparator = getDomainSeparator(domain); 35 | const hash = getMakerHash(makerOrder); 36 | const types: SolidityType[] = ["string", "bytes32", "bytes32"]; 37 | return solidityPackedKeccak256(types, ["\x19\x01", domainSeparator, hash]); 38 | }; 39 | -------------------------------------------------------------------------------- /src/__tests__/helpers/setup.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-await-in-loop */ 2 | import { ContractTransactionResponse, ZeroAddress, parseEther } from "ethers"; 3 | import chai from "chai"; 4 | import chaiAsPromised from "chai-as-promised"; 5 | import { ethers } from "hardhat"; 6 | import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; 7 | import { Addresses } from "../../types"; 8 | import type { LooksRareProtocol } from "../../typechain/@looksrare/contracts-exchange-v2/contracts/LooksRareProtocol"; 9 | import type { TransferManager } from "../../typechain/@looksrare/contracts-exchange-v2/contracts/TransferManager"; 10 | import type { StrategyCollectionOffer } from "../../typechain/@looksrare/contracts-exchange-v2/contracts/executionStrategies/StrategyCollectionOffer"; 11 | import type { CreatorFeeManagerWithRoyalties } from "../../typechain/@looksrare/contracts-exchange-v2/contracts/CreatorFeeManagerWithRoyalties"; 12 | import type { OrderValidatorV2A } from "../../typechain/@looksrare/contracts-exchange-v2/contracts/helpers/OrderValidatorV2A"; 13 | import type { MockERC721 } from "../../typechain/src/contracts/tests/MockERC721"; 14 | import type { MockERC1155 } from "../../typechain/src/contracts/tests/MockERC1155"; 15 | import type { WETH } from "../../typechain/solmate/src/tokens/WETH"; 16 | import type { Verifier } from "../../typechain/src/contracts/tests/Verifier"; 17 | 18 | chai.use(chaiAsPromised); 19 | 20 | export interface SetupMocks { 21 | contracts: { 22 | looksRareProtocol: LooksRareProtocol; 23 | transferManager: TransferManager; 24 | collectionERC721: MockERC721; 25 | collectionERC1155: MockERC1155; 26 | weth: WETH; 27 | verifier: Verifier; 28 | orderValidator: OrderValidatorV2A; 29 | }; 30 | addresses: Addresses & { 31 | MOCK_COLLECTION_ERC721: string; 32 | MOCK_COLLECTION_ERC1155: string; 33 | }; 34 | } 35 | 36 | export interface Signers { 37 | owner: SignerWithAddress; 38 | operator: SignerWithAddress; 39 | protocolFeeRecipient: SignerWithAddress; 40 | user1: SignerWithAddress; 41 | user2: SignerWithAddress; 42 | user3: SignerWithAddress; 43 | } 44 | 45 | export const NB_NFT_PER_USER = 3; 46 | 47 | export const getSigners = async (): Promise => { 48 | const signers = await ethers.getSigners(); 49 | return { 50 | owner: signers[0], 51 | operator: signers[1], 52 | protocolFeeRecipient: signers[2], 53 | user1: signers[3], 54 | user2: signers[4], 55 | user3: signers[5], 56 | }; 57 | }; 58 | 59 | const deploy = async (name: string, ...args: any[]) => { 60 | const factory = await ethers.getContractFactory(name); 61 | const contract = await factory.deploy(...args); 62 | await contract.waitForDeployment(); 63 | return contract; 64 | }; 65 | 66 | export const setUpContracts = async (): Promise => { 67 | const signers = await getSigners(); 68 | let tx: ContractTransactionResponse; 69 | 70 | // Deploy contracts 71 | const transferManager = (await deploy("TransferManager", signers.owner.address)) as TransferManager; 72 | const royaltyFeeRegistry = await deploy("MockRoyaltyFeeRegistry", signers.owner.address, 9500); 73 | const feeManager = (await deploy( 74 | "CreatorFeeManagerWithRoyalties", 75 | await royaltyFeeRegistry.getAddress() 76 | )) as CreatorFeeManagerWithRoyalties; 77 | const weth = (await deploy("WETH")) as WETH; 78 | const looksRareProtocol = (await deploy( 79 | "LooksRareProtocol", 80 | signers.owner.address, 81 | signers.protocolFeeRecipient.address, 82 | await transferManager.getAddress(), 83 | await weth.getAddress() 84 | )) as LooksRareProtocol; 85 | const strategyCollectionOffer = (await deploy("StrategyCollectionOffer")) as StrategyCollectionOffer; 86 | 87 | const addresses = { 88 | weth: await weth.getAddress(), 89 | looksRareProtocol: await looksRareProtocol.getAddress(), 90 | strategyCollectionOffer: await strategyCollectionOffer.getAddress(), 91 | transferManager: await transferManager.getAddress(), 92 | feeManager: await feeManager.getAddress(), 93 | }; 94 | 95 | tx = await looksRareProtocol.updateCreatorFeeManager.send(addresses.feeManager); 96 | await tx.wait(); 97 | tx = await looksRareProtocol.updateCurrencyStatus(ZeroAddress, true); 98 | await tx.wait(); 99 | tx = await looksRareProtocol.updateCurrencyStatus(addresses.weth, true); 100 | await tx.wait(); 101 | tx = await transferManager.allowOperator(addresses.looksRareProtocol); 102 | await tx.wait(); 103 | tx = await looksRareProtocol.addStrategy( 104 | 250, 105 | 250, 106 | 300, 107 | strategyCollectionOffer.interface.getFunction("executeCollectionStrategyWithTakerAsk").selector, 108 | true, 109 | addresses.strategyCollectionOffer 110 | ); 111 | tx = await looksRareProtocol.addStrategy( 112 | 250, 113 | 250, 114 | 300, 115 | strategyCollectionOffer.interface.getFunction("executeCollectionStrategyWithTakerAskWithProof").selector, 116 | true, 117 | addresses.strategyCollectionOffer 118 | ); 119 | await tx.wait(); 120 | 121 | const orderValidator = (await deploy("OrderValidatorV2A", addresses.looksRareProtocol)) as OrderValidatorV2A; 122 | const collectionERC721 = (await deploy("MockERC721", "collectionERC721", "COL1")) as MockERC721; 123 | const collectionERC1155 = (await deploy("MockERC1155")) as MockERC1155; 124 | const verifier = (await deploy("Verifier", addresses.looksRareProtocol)) as Verifier; 125 | 126 | // Setup balances 127 | const wethUser1 = new ethers.Contract(addresses.weth, weth.interface, signers.user1); 128 | tx = await wethUser1.deposit({ value: parseEther("10") }); 129 | await tx.wait(); 130 | 131 | const wethUser2 = new ethers.Contract(addresses.weth, weth.interface, signers.user2); 132 | tx = await wethUser2.deposit({ value: parseEther("10") }); 133 | await tx.wait(); 134 | 135 | for (let i = 0; i < NB_NFT_PER_USER; i++) { 136 | tx = await collectionERC721.mint(signers.user1.address); 137 | await tx.wait(); 138 | tx = await collectionERC1155.mint(signers.user2.address, i, 10); 139 | await tx.wait(); 140 | } 141 | tx = await collectionERC1155.mint(signers.user1.address, 0, 10); 142 | await tx.wait(); 143 | 144 | return { 145 | contracts: { 146 | looksRareProtocol, 147 | transferManager, 148 | collectionERC721, 149 | collectionERC1155, 150 | weth, 151 | verifier, 152 | orderValidator, 153 | }, 154 | addresses: { 155 | EXCHANGE_V2: addresses.looksRareProtocol, 156 | LOOKS: ZeroAddress, 157 | TRANSFER_MANAGER_V2: addresses.transferManager, 158 | WETH: addresses.weth, 159 | ORDER_VALIDATOR_V2: await orderValidator.getAddress(), 160 | REVERSE_RECORDS: ZeroAddress, 161 | LOOKS_LP_V3: ZeroAddress, 162 | MOCK_COLLECTION_ERC721: await collectionERC721.getAddress(), 163 | MOCK_COLLECTION_ERC1155: await collectionERC1155.getAddress(), 164 | STAKING_POOL_FOR_LOOKS_LP: ZeroAddress, 165 | AGGREGATOR_UNISWAP_V3: ZeroAddress, 166 | }, 167 | }; 168 | }; 169 | -------------------------------------------------------------------------------- /src/__tests__/helpers/tokens.ts: -------------------------------------------------------------------------------- 1 | import { BigNumberish, Contract, Overrides } from "ethers"; 2 | import { ERC721 } from "../../typechain/solmate/src/tokens/ERC721.sol/ERC721"; 3 | import abiIERC721 from "../../abis/IERC721.json"; 4 | import { Signer } from "../../types"; 5 | 6 | export const balanceOf = async (signer: Signer, contractAddress: string, owner?: string, overrides?: Overrides) => { 7 | const contract = new Contract(contractAddress, abiIERC721).connect(signer) as ERC721; 8 | return contract.balanceOf(owner ?? (await signer.getAddress()), { ...overrides }); 9 | }; 10 | 11 | export const ownerOf = async (signer: Signer, collection: string, id: BigNumberish, overrides?: Overrides) => { 12 | const contract = new Contract(collection, abiIERC721).connect(signer) as ERC721; 13 | return contract.ownerOf(id, { ...overrides }); 14 | }; 15 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/constructor.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers } from "hardhat"; 3 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 4 | import { LooksRare } from "../../LooksRare"; 5 | import { ChainId } from "../../types"; 6 | import { ErrorSigner } from "../../errors"; 7 | 8 | describe("LooksRare class", () => { 9 | let mocks: SetupMocks; 10 | let signers: Signers; 11 | beforeEach(async () => { 12 | mocks = await setUpContracts(); 13 | signers = await getSigners(); 14 | }); 15 | it("instanciate LooksRare object with a signer", () => { 16 | expect(new LooksRare(1, ethers.provider, signers.user1).chainId).to.equal(1); 17 | expect(new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1).chainId).to.equal(ChainId.HARDHAT); 18 | }); 19 | it("instanciate LooksRare object with a signer and override addresses", () => { 20 | const { addresses } = mocks; 21 | const lr = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, addresses); 22 | expect(lr.addresses).to.be.eql(addresses); 23 | }); 24 | it("instanciate LooksRare object without a signer and reject a contract call", async () => { 25 | const lr = new LooksRare(ChainId.HARDHAT, ethers.provider); 26 | expect(lr.getTypedDataDomain().chainId).to.be.eql(ChainId.HARDHAT); 27 | expect(() => lr.cancelAllOrders(true, true)).to.throw(ErrorSigner); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/createMakerAsk.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ZeroAddress, parseEther } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 5 | import { LooksRare } from "../../LooksRare"; 6 | import { isApprovedForAll } from "../../utils/calls/tokens"; 7 | import { ErrorTimestamp } from "../../errors"; 8 | import { ChainId, CollectionType, StrategyType, QuoteType, CreateMakerInput, Maker } from "../../types"; 9 | 10 | describe("Create maker ask", () => { 11 | let mocks: SetupMocks; 12 | let signers: Signers; 13 | let lrUser1: LooksRare; 14 | let baseMakerAskInput: CreateMakerInput; 15 | 16 | beforeEach(async () => { 17 | mocks = await setUpContracts(); 18 | signers = await getSigners(); 19 | 20 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 21 | 22 | baseMakerAskInput = { 23 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 24 | collectionType: CollectionType.ERC721, 25 | strategyId: StrategyType.standard, 26 | subsetNonce: 0, 27 | orderNonce: 0, 28 | endTime: Math.floor(Date.now() / 1000) + 3600, 29 | price: parseEther("1"), 30 | itemIds: [1], 31 | }; 32 | }); 33 | 34 | it("throws an error when creating maker ask with wrong time format", async () => { 35 | await expect(lrUser1.createMakerAsk({ ...baseMakerAskInput, startTime: Date.now() })).to.eventually.be.rejectedWith( 36 | ErrorTimestamp 37 | ); 38 | await expect(lrUser1.createMakerAsk({ ...baseMakerAskInput, endTime: Date.now() })).to.eventually.be.rejectedWith( 39 | ErrorTimestamp 40 | ); 41 | }); 42 | 43 | it("approvals checks are false if no approval was made", async () => { 44 | const { isCollectionApproved, isTransferManagerApproved } = await lrUser1.createMakerAsk(baseMakerAskInput); 45 | expect(isCollectionApproved).to.be.false; 46 | expect(isTransferManagerApproved).to.be.false; 47 | 48 | const tx = await lrUser1.approveAllCollectionItems(baseMakerAskInput.collection); 49 | await tx.wait(); 50 | const isApproved = await isApprovedForAll( 51 | ethers.provider, 52 | baseMakerAskInput.collection, 53 | signers.user1.address, 54 | mocks.addresses.TRANSFER_MANAGER_V2 55 | ); 56 | expect(isApproved).to.be.true; 57 | }); 58 | 59 | it("approval checks are true if approval were made", async () => { 60 | let tx = await lrUser1.approveAllCollectionItems(baseMakerAskInput.collection); 61 | await tx.wait(); 62 | tx = await lrUser1.grantTransferManagerApproval().call(); 63 | await tx.wait(); 64 | 65 | const { isCollectionApproved, isTransferManagerApproved } = await lrUser1.createMakerAsk(baseMakerAskInput); 66 | expect(isCollectionApproved).to.be.true; 67 | expect(isTransferManagerApproved).to.be.true; 68 | }); 69 | 70 | it("create a simple maker ask with default values", async () => { 71 | const output = await lrUser1.createMakerAsk(baseMakerAskInput); 72 | const makerOrder: Maker = { 73 | quoteType: QuoteType.Ask, 74 | globalNonce: 0n, 75 | subsetNonce: baseMakerAskInput.subsetNonce, 76 | strategyId: baseMakerAskInput.strategyId, 77 | collectionType: baseMakerAskInput.collectionType, 78 | orderNonce: baseMakerAskInput.orderNonce, 79 | collection: baseMakerAskInput.collection, 80 | currency: ZeroAddress, 81 | signer: signers.user1.address, 82 | startTime: output.maker.startTime, // Can't really test the Date.now( executed inside the function) 83 | endTime: baseMakerAskInput.endTime, 84 | price: baseMakerAskInput.price, 85 | itemIds: baseMakerAskInput.itemIds, 86 | amounts: [1], 87 | additionalParameters: "0x", 88 | }; 89 | expect(output.maker).to.eql(makerOrder); 90 | }); 91 | 92 | it("create a simple maker ask with non default values", async () => { 93 | const input = { 94 | ...baseMakerAskInput, 95 | amounts: [1], 96 | currency: mocks.addresses.WETH, 97 | startTime: Math.floor(Date.now() / 1000), 98 | recipient: signers.user2.address, 99 | additionalParameters: [], 100 | }; 101 | const output = await lrUser1.createMakerAsk(input); 102 | const makerOrder: Maker = { 103 | quoteType: QuoteType.Ask, 104 | globalNonce: 0n, 105 | subsetNonce: input.subsetNonce, 106 | strategyId: input.strategyId, 107 | collectionType: input.collectionType, 108 | orderNonce: input.orderNonce, 109 | collection: input.collection, 110 | currency: input.currency!, 111 | signer: signers.user1.address, 112 | startTime: input.startTime!, 113 | endTime: input.endTime, 114 | price: input.price, 115 | itemIds: input.itemIds, 116 | amounts: input.amounts!, 117 | additionalParameters: "0x", 118 | }; 119 | expect(output.maker).to.eql(makerOrder); 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/createMakerBid.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { MaxUint256, parseEther } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 5 | import { LooksRare } from "../../LooksRare"; 6 | import { allowance } from "../../utils/calls/tokens"; 7 | import { ErrorTimestamp } from "../../errors"; 8 | import { ChainId, CollectionType, StrategyType, QuoteType, CreateMakerInput, Maker } from "../../types"; 9 | 10 | describe("Create maker bid", () => { 11 | let mocks: SetupMocks; 12 | let signers: Signers; 13 | let lrUser1: LooksRare; 14 | let baseMakerInput: CreateMakerInput; 15 | 16 | beforeEach(async () => { 17 | mocks = await setUpContracts(); 18 | signers = await getSigners(); 19 | 20 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 21 | 22 | baseMakerInput = { 23 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 24 | collectionType: CollectionType.ERC721, 25 | strategyId: StrategyType.standard, 26 | subsetNonce: 0, 27 | orderNonce: 0, 28 | endTime: Math.floor(Date.now() / 1000) + 3600, 29 | price: parseEther("1"), 30 | itemIds: [1], 31 | }; 32 | }); 33 | 34 | it("throws an error when creating maker bid with wrong time format", async () => { 35 | await expect(lrUser1.createMakerBid({ ...baseMakerInput, startTime: Date.now() })).to.eventually.be.rejectedWith( 36 | ErrorTimestamp 37 | ); 38 | await expect(lrUser1.createMakerBid({ ...baseMakerInput, endTime: Date.now() })).to.eventually.be.rejectedWith( 39 | ErrorTimestamp 40 | ); 41 | }); 42 | 43 | it("approvals checks are false if no approval was made", async () => { 44 | const { isCurrencyApproved } = await lrUser1.createMakerBid(baseMakerInput); 45 | expect(isCurrencyApproved).to.be.false; 46 | 47 | await lrUser1.approveErc20(mocks.addresses.WETH); 48 | const valueApproved = await allowance( 49 | ethers.provider, 50 | mocks.addresses.WETH, 51 | signers.user1.address, 52 | mocks.addresses.EXCHANGE_V2 53 | ); 54 | expect(valueApproved).to.be.eq(MaxUint256); 55 | }); 56 | 57 | it("approval checks are true if approval were made", async () => { 58 | const tx = await lrUser1.approveErc20(mocks.addresses.WETH); 59 | await tx.wait(); 60 | const { isCurrencyApproved } = await lrUser1.createMakerBid(baseMakerInput); 61 | expect(isCurrencyApproved).to.be.true; 62 | }); 63 | 64 | it("balance checks are false if balance is not sufficient", async () => { 65 | const { isBalanceSufficient } = await lrUser1.createMakerBid({ 66 | ...baseMakerInput, 67 | price: parseEther("100000"), 68 | }); 69 | expect(isBalanceSufficient).to.be.false; 70 | }); 71 | 72 | it("balance checks are true if balance is sufficient", async () => { 73 | const { isBalanceSufficient } = await lrUser1.createMakerBid(baseMakerInput); 74 | expect(isBalanceSufficient).to.be.true; 75 | }); 76 | 77 | it("create a simple maker bid with default values", async () => { 78 | const output = await lrUser1.createMakerBid(baseMakerInput); 79 | const makerOrder: Maker = { 80 | quoteType: QuoteType.Bid, 81 | globalNonce: 0n, 82 | subsetNonce: baseMakerInput.subsetNonce, 83 | strategyId: baseMakerInput.strategyId, 84 | collectionType: baseMakerInput.collectionType, 85 | orderNonce: baseMakerInput.orderNonce, 86 | collection: baseMakerInput.collection, 87 | currency: mocks.addresses.WETH, 88 | signer: signers.user1.address, 89 | startTime: output.maker.startTime, // Can't really test the Date.now (executed inside the function) 90 | endTime: baseMakerInput.endTime, 91 | price: baseMakerInput.price, 92 | itemIds: baseMakerInput.itemIds, 93 | amounts: [1], 94 | additionalParameters: "0x", 95 | }; 96 | expect(output.maker).to.eql(makerOrder); 97 | }); 98 | 99 | it("create a simple maker bid with non default values", async () => { 100 | const input = { 101 | ...baseMakerInput, 102 | amounts: [1], 103 | currency: mocks.addresses.WETH, 104 | startTime: Math.floor(Date.now() / 1000), 105 | recipient: signers.user2.address, 106 | additionalParameters: [], 107 | }; 108 | const output = await lrUser1.createMakerBid(input); 109 | const makerOrder: Maker = { 110 | quoteType: QuoteType.Bid, 111 | globalNonce: 0n, 112 | subsetNonce: input.subsetNonce, 113 | strategyId: input.strategyId, 114 | collectionType: input.collectionType, 115 | orderNonce: input.orderNonce, 116 | collection: input.collection, 117 | currency: input.currency!, 118 | signer: signers.user1.address, 119 | startTime: input.startTime!, 120 | endTime: input.endTime, 121 | price: input.price, 122 | itemIds: input.itemIds, 123 | amounts: input.amounts!, 124 | additionalParameters: "0x", 125 | }; 126 | expect(output.maker).to.eql(makerOrder); 127 | }); 128 | }); 129 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/createTaker.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { AbiCoder, parseEther } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | import { getTakerParamsTypes } from "../../utils/encodeOrderParams"; 5 | import { LooksRare } from "../../LooksRare"; 6 | import { ChainId, CollectionType, StrategyType, CreateMakerInput, QuoteType } from "../../types"; 7 | import { ErrorStrategyType, ErrorQuoteType, ErrorItemId } from "../../errors"; 8 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 9 | 10 | describe("Create takers", () => { 11 | let mocks: SetupMocks; 12 | let signers: Signers; 13 | let lrUser1: LooksRare; 14 | let baseMakerInput: CreateMakerInput; 15 | 16 | beforeEach(async () => { 17 | mocks = await setUpContracts(); 18 | signers = await getSigners(); 19 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 20 | 21 | baseMakerInput = { 22 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 23 | collectionType: CollectionType.ERC721, 24 | strategyId: StrategyType.standard, 25 | subsetNonce: 0, 26 | orderNonce: 0, 27 | startTime: Math.floor(Date.now() / 1000), 28 | endTime: Math.floor(Date.now() / 1000) + 3600, 29 | price: parseEther("1"), 30 | itemIds: [1], 31 | }; 32 | }); 33 | 34 | describe("createTaker", async () => { 35 | it("create taker with recipient", async () => { 36 | const { maker } = await lrUser1.createMakerAsk(baseMakerInput); 37 | const taker = lrUser1.createTaker(maker, signers.user2.address); 38 | 39 | expect(taker.recipient).to.be.equal(signers.user2.address); 40 | expect(taker.additionalParameters).to.be.equal("0x"); 41 | }); 42 | 43 | it("create taker without recipient", async () => { 44 | const { maker } = await lrUser1.createMakerAsk(baseMakerInput); 45 | const taker = lrUser1.createTaker(maker); 46 | 47 | expect(taker.recipient).to.be.equal(ethers.ZeroAddress); 48 | expect(taker.additionalParameters).to.be.equal("0x"); 49 | }); 50 | }); 51 | 52 | describe("createTakerCollectionOffer", async () => { 53 | it("create taker for collection order", async () => { 54 | const { maker } = await lrUser1.createMakerBid({ 55 | ...baseMakerInput, 56 | strategyId: StrategyType.collection, 57 | }); 58 | const taker = lrUser1.createTakerCollectionOffer(maker, 1, signers.user2.address); 59 | const [itemId] = AbiCoder.defaultAbiCoder().decode( 60 | getTakerParamsTypes(maker.strategyId), 61 | taker.additionalParameters 62 | ); 63 | 64 | expect(taker.recipient).to.be.equal(signers.user2.address); 65 | expect(BigInt(itemId)).to.be.equal(1n); 66 | }); 67 | 68 | it("throw when quote type is wrong", async () => { 69 | const { maker } = await lrUser1.createMakerAsk({ 70 | ...baseMakerInput, 71 | strategyId: StrategyType.collection, 72 | }); 73 | 74 | expect(() => lrUser1.createTakerCollectionOffer(maker, 1, signers.user2.address)).to.throw(ErrorQuoteType); 75 | }); 76 | 77 | it("throw when strategy type is wrong", async () => { 78 | const { maker } = await lrUser1.createMakerBid(baseMakerInput); 79 | expect(() => lrUser1.createTakerCollectionOffer(maker, 1, signers.user2.address)).to.throw(ErrorStrategyType); 80 | }); 81 | }); 82 | 83 | describe("createTakerCollectionOfferWithMerkleTree", async () => { 84 | const itemIds = [0, 1, 2]; 85 | 86 | it("create taker for collection order", async () => { 87 | const { maker } = await lrUser1.createMakerCollectionOfferWithProof({ 88 | ...baseMakerInput, 89 | itemIds, 90 | }); 91 | const taker = lrUser1.createTakerCollectionOfferWithProof(maker, 1, itemIds, signers.user2.address); 92 | 93 | const [itemId] = AbiCoder.defaultAbiCoder().decode( 94 | getTakerParamsTypes(maker.strategyId), 95 | taker.additionalParameters 96 | ); 97 | expect(taker.recipient).to.be.equal(signers.user2.address); 98 | expect(BigInt(itemId)).to.be.equal(1n); 99 | }); 100 | 101 | it("throw when quote type is wrong", async () => { 102 | const { maker } = await lrUser1.createMakerCollectionOfferWithProof({ 103 | ...baseMakerInput, 104 | itemIds, 105 | }); 106 | 107 | const callback = () => 108 | lrUser1.createTakerCollectionOfferWithProof( 109 | { ...maker, quoteType: QuoteType.Ask }, 110 | 1, 111 | itemIds, 112 | signers.user2.address 113 | ); 114 | expect(callback).to.throw(ErrorQuoteType); 115 | }); 116 | 117 | it("throw when strategy type is wrong", async () => { 118 | const { maker } = await lrUser1.createMakerCollectionOfferWithProof({ 119 | ...baseMakerInput, 120 | itemIds, 121 | }); 122 | 123 | const callback = () => 124 | lrUser1.createTakerCollectionOfferWithProof( 125 | { ...maker, strategyId: StrategyType.collection }, 126 | 1, 127 | itemIds, 128 | signers.user2.address 129 | ); 130 | expect(callback).to.throw(ErrorStrategyType); 131 | }); 132 | 133 | it("throw when item cannot be found", async () => { 134 | const { maker } = await lrUser1.createMakerCollectionOfferWithProof({ 135 | ...baseMakerInput, 136 | itemIds, 137 | }); 138 | 139 | const callback = () => lrUser1.createTakerCollectionOfferWithProof(maker, 4, itemIds, signers.user2.address); 140 | expect(callback).to.throw(ErrorItemId); 141 | }); 142 | }); 143 | }); 144 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/executeCollectionOrder.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers } from "hardhat"; 3 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 4 | import { ownerOf, balanceOf } from "../helpers/tokens"; 5 | import { LooksRare } from "../../LooksRare"; 6 | import { ChainId, CollectionType, CreateMakerCollectionOfferInput } from "../../types"; 7 | import { parseEther } from "ethers"; 8 | 9 | describe("execute collection order", () => { 10 | let mocks: SetupMocks; 11 | let signers: Signers; 12 | let lrUser1: LooksRare; 13 | let lrUser2: LooksRare; 14 | let collectionOfferInput: CreateMakerCollectionOfferInput; 15 | 16 | beforeEach(async () => { 17 | mocks = await setUpContracts(); 18 | signers = await getSigners(); 19 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 20 | lrUser2 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user2, mocks.addresses); 21 | 22 | collectionOfferInput = { 23 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 24 | collectionType: CollectionType.ERC721, 25 | subsetNonce: 0, 26 | orderNonce: 0, 27 | startTime: Math.floor(Date.now() / 1000), 28 | endTime: Math.floor(Date.now() / 1000) + 3600, 29 | price: parseEther("1"), 30 | }; 31 | 32 | let tx = await lrUser1.grantTransferManagerApproval().call(); 33 | await tx.wait(); 34 | 35 | tx = await lrUser2.approveErc20(mocks.addresses.WETH); 36 | await tx.wait(); 37 | 38 | tx = await lrUser1.approveAllCollectionItems(mocks.addresses.MOCK_COLLECTION_ERC721); 39 | await tx.wait(); 40 | }); 41 | 42 | it("execute estimatedGas and callStatic", async () => { 43 | const { maker } = await lrUser2.createMakerCollectionOffer(collectionOfferInput); 44 | const signature = await lrUser2.signMakerOrder(maker); 45 | const taker = lrUser1.createTakerCollectionOffer(maker, 0); 46 | const contractMethods = lrUser1.executeOrder(maker, taker, signature); 47 | 48 | const estimatedGas = await contractMethods.estimateGas(); 49 | expect(Number(estimatedGas)).to.be.greaterThan(0); 50 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 51 | }); 52 | 53 | it("execute collection order", async () => { 54 | const itemId = 0; 55 | const { maker } = await lrUser2.createMakerCollectionOffer(collectionOfferInput); 56 | const signature = await lrUser2.signMakerOrder(maker); 57 | const taker = lrUser1.createTakerCollectionOffer(maker, itemId); 58 | 59 | const user1InitialBalance = await balanceOf(signers.user1, mocks.addresses.WETH); 60 | const contractMethods = lrUser1.executeOrder(maker, taker, signature); 61 | 62 | const receipt = await (await contractMethods.call()).wait(); 63 | expect(receipt?.status).to.be.equal(1); 64 | 65 | const owner = await ownerOf(signers.user2, collectionOfferInput.collection, itemId); 66 | expect(owner).to.be.equal(signers.user2.address); 67 | 68 | const user1UpdatedBalance = await balanceOf(signers.user1, mocks.addresses.WETH); 69 | expect(user1UpdatedBalance > user1InitialBalance).to.be.true; 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/executeCollectionOrderWithProof.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers } from "hardhat"; 3 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 4 | import { ownerOf, balanceOf } from "../helpers/tokens"; 5 | import { LooksRare } from "../../LooksRare"; 6 | import { ChainId, CollectionType, CreateMakerCollectionOfferInput } from "../../types"; 7 | import { parseEther } from "ethers"; 8 | 9 | describe("execute collection order with proof", () => { 10 | let mocks: SetupMocks; 11 | let lrUser1: LooksRare; 12 | let signers: Signers; 13 | let lrUser2: LooksRare; 14 | let collectionOfferInput: CreateMakerCollectionOfferInput; 15 | const itemIds = [0, 1, 2]; 16 | 17 | beforeEach(async () => { 18 | mocks = await setUpContracts(); 19 | signers = await getSigners(); 20 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 21 | lrUser2 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user2, mocks.addresses); 22 | 23 | collectionOfferInput = { 24 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 25 | collectionType: CollectionType.ERC721, 26 | subsetNonce: 0, 27 | orderNonce: 0, 28 | startTime: Math.floor(Date.now() / 1000), 29 | endTime: Math.floor(Date.now() / 1000) + 3600, 30 | price: parseEther("1"), 31 | }; 32 | 33 | let tx = await lrUser1.grantTransferManagerApproval().call(); 34 | await tx.wait(); 35 | 36 | tx = await lrUser2.approveErc20(mocks.addresses.WETH); 37 | await tx.wait(); 38 | 39 | tx = await lrUser1.approveAllCollectionItems(mocks.addresses.MOCK_COLLECTION_ERC721); 40 | await tx.wait(); 41 | }); 42 | 43 | it("execute estimatedGas and callStatic", async () => { 44 | const { maker } = await lrUser2.createMakerCollectionOfferWithProof({ 45 | ...collectionOfferInput, 46 | itemIds, 47 | }); 48 | const signature = await lrUser2.signMakerOrder(maker); 49 | 50 | const taker = lrUser1.createTakerCollectionOfferWithProof(maker, 1, itemIds, signers.user1.address); 51 | const contractMethods = lrUser1.executeOrder(maker, taker, signature); 52 | 53 | const estimatedGas = await contractMethods.estimateGas(); 54 | expect(Number(estimatedGas)).to.be.greaterThan(0); 55 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 56 | }); 57 | 58 | it("execute collection order", async () => { 59 | const itemId = 0; 60 | const { maker } = await lrUser2.createMakerCollectionOfferWithProof({ 61 | ...collectionOfferInput, 62 | itemIds, 63 | }); 64 | const signature = await lrUser2.signMakerOrder(maker); 65 | 66 | const taker = lrUser1.createTakerCollectionOfferWithProof(maker, itemId, itemIds, signers.user1.address); 67 | 68 | const user1InitialBalance = await balanceOf(signers.user1, mocks.addresses.WETH); 69 | const { call } = lrUser1.executeOrder(maker, taker, signature); 70 | 71 | const receipt = await (await call()).wait(); 72 | expect(receipt?.status).to.be.equal(1); 73 | 74 | const owner = await ownerOf(signers.user2, collectionOfferInput.collection, itemId); 75 | expect(owner).to.be.equal(signers.user2.address); 76 | 77 | const user1UpdatedBalance = await balanceOf(signers.user1, mocks.addresses.WETH); 78 | expect(user1UpdatedBalance > user1InitialBalance).to.be.true; 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/executeMultipleTakerBids.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { parseEther } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 5 | import { ownerOf } from "../helpers/tokens"; 6 | import { ErrorQuoteType } from "../../errors"; 7 | import { LooksRare } from "../../LooksRare"; 8 | import { ChainId, CollectionType, StrategyType, QuoteType, CreateMakerInput, Maker } from "../../types"; 9 | 10 | describe("execute multiple taker bids", () => { 11 | let mocks: SetupMocks; 12 | let signers: Signers; 13 | let lrUser1: LooksRare; 14 | let lrUser2: LooksRare; 15 | let makers: Maker[] = []; 16 | 17 | beforeEach(async () => { 18 | mocks = await setUpContracts(); 19 | signers = await getSigners(); 20 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 21 | lrUser2 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user2, mocks.addresses); 22 | 23 | const baseMakerAskInput: CreateMakerInput = { 24 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 25 | collectionType: CollectionType.ERC721, 26 | strategyId: StrategyType.standard, 27 | subsetNonce: 0, 28 | orderNonce: 0, 29 | startTime: Math.floor(Date.now() / 1000), 30 | endTime: Math.floor(Date.now() / 1000) + 3600, 31 | price: parseEther("1"), 32 | itemIds: [0], 33 | }; 34 | 35 | let tx = await lrUser1.grantTransferManagerApproval().call(); 36 | await tx.wait(); 37 | 38 | tx = await lrUser1.approveAllCollectionItems(baseMakerAskInput.collection); 39 | await tx.wait(); 40 | 41 | tx = await lrUser2.approveErc20(mocks.addresses.WETH); 42 | await tx.wait(); 43 | 44 | const { maker: maker1 } = await lrUser1.createMakerAsk(baseMakerAskInput); 45 | const { maker: maker2 } = await lrUser1.createMakerAsk({ ...baseMakerAskInput, itemIds: [1], orderNonce: 1 }); 46 | makers = [maker1, maker2]; 47 | }); 48 | 49 | it("execute estimatedGas and callStatic", async () => { 50 | const taker1 = lrUser2.createTaker(makers[0], signers.user2.address); 51 | const taker2 = lrUser2.createTaker(makers[1], signers.user2.address); 52 | const signature1 = await lrUser1.signMakerOrder(makers[0]); 53 | const signature2 = await lrUser1.signMakerOrder(makers[1]); 54 | 55 | const orders = [ 56 | { maker: makers[0], taker: taker1, signature: signature1 }, 57 | { maker: makers[1], taker: taker2, signature: signature2 }, 58 | ]; 59 | const contractMethods = lrUser2.executeMultipleOrders(orders, true); 60 | 61 | const estimatedGas = await contractMethods.estimateGas(); 62 | expect(Number(estimatedGas)).to.be.greaterThan(0); 63 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 64 | }); 65 | 66 | it("execute multiple taker bid atomically", async () => { 67 | const taker1 = lrUser2.createTaker(makers[0], signers.user2.address); 68 | const taker2 = lrUser2.createTaker(makers[1], signers.user2.address); 69 | const signature1 = await lrUser1.signMakerOrder(makers[0]); 70 | const signature2 = await lrUser1.signMakerOrder(makers[1]); 71 | 72 | const orders = [ 73 | { maker: makers[0], taker: taker1, signature: signature1 }, 74 | { maker: makers[1], taker: taker2, signature: signature2 }, 75 | ]; 76 | const user1InitialBalance = await ethers.provider.getBalance(signers.user1.address); 77 | const contractMethods = lrUser2.executeMultipleOrders(orders, true); 78 | 79 | const receipt = await (await contractMethods.call()).wait(); 80 | expect(receipt?.status).to.be.equal(1); 81 | 82 | const owner = await ownerOf(signers.user2, makers[0].collection, makers[0].itemIds[0]); 83 | expect(owner).to.be.equal(signers.user2.address); 84 | 85 | const user1UpdatedBalance = await ethers.provider.getBalance(signers.user1.address); 86 | expect(user1UpdatedBalance > user1InitialBalance).to.be.true; 87 | }); 88 | 89 | it("execute multiple taker bid non atomically", async () => { 90 | const taker1 = lrUser2.createTaker(makers[0], signers.user2.address); 91 | const taker2 = lrUser2.createTaker(makers[1], signers.user2.address); 92 | const signature1 = await lrUser1.signMakerOrder(makers[0]); 93 | const signature2 = await lrUser1.signMakerOrder(makers[1]); 94 | 95 | const orders = [ 96 | { maker: makers[0], taker: taker1, signature: signature1 }, 97 | { maker: makers[1], taker: taker2, signature: signature2 }, 98 | ]; 99 | const user1InitialBalance = await ethers.provider.getBalance(signers.user1.address); 100 | const contractMethods = lrUser2.executeMultipleOrders(orders, false); 101 | 102 | const receipt = await (await contractMethods.call()).wait(); 103 | expect(receipt.status).to.be.equal(1); 104 | 105 | const owner = await ownerOf(signers.user2, makers[0].collection, makers[0].itemIds[0]); 106 | expect(owner).to.be.equal(signers.user2.address); 107 | 108 | const user1UpdatedBalance = await ethers.provider.getBalance(signers.user1.address); 109 | expect(user1UpdatedBalance > user1InitialBalance).to.be.true; 110 | }); 111 | 112 | it("execute multiple taker bid with a merkle tree", async () => { 113 | const taker1 = lrUser2.createTaker(makers[0], signers.user2.address); 114 | const taker2 = lrUser2.createTaker(makers[1], signers.user2.address); 115 | const { signature, merkleTreeProofs } = await lrUser1.signMultipleMakerOrders(makers); 116 | 117 | const orders = [ 118 | { maker: makers[0], taker: taker1, signature: signature, merkleTree: merkleTreeProofs[0] }, 119 | { maker: makers[1], taker: taker2, signature: signature, merkleTree: merkleTreeProofs[1] }, 120 | ]; 121 | const user1InitialBalance = await ethers.provider.getBalance(signers.user1.address); 122 | const contractMethods = lrUser2.executeMultipleOrders(orders, true); 123 | 124 | const receipt = await (await contractMethods.call()).wait(); 125 | expect(receipt.status).to.be.equal(1); 126 | 127 | const owner = await ownerOf(signers.user2, makers[0].collection, makers[0].itemIds[0]); 128 | expect(owner).to.be.equal(signers.user2.address); 129 | 130 | const user1UpdatedBalance = await ethers.provider.getBalance(signers.user1.address); 131 | expect(user1UpdatedBalance > user1InitialBalance).to.be.true; 132 | }); 133 | 134 | it("throw when the quote type is incorrect", async () => { 135 | const taker1 = lrUser2.createTaker(makers[0], signers.user2.address); 136 | const taker2 = lrUser2.createTaker(makers[1], signers.user2.address); 137 | const signature1 = await lrUser1.signMakerOrder(makers[0]); 138 | const signature2 = await lrUser1.signMakerOrder(makers[1]); 139 | 140 | const orders = [ 141 | { maker: makers[0], taker: taker1, signature: signature1 }, 142 | { maker: { ...makers[1], quoteType: QuoteType.Bid }, taker: taker2, signature: signature2 }, 143 | ]; 144 | 145 | const callback = () => lrUser2.executeMultipleOrders(orders, true); 146 | expect(callback).to.throw(ErrorQuoteType); 147 | }); 148 | }); 149 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/executeTakerAsk.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { parseEther } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 5 | import { LooksRare } from "../../LooksRare"; 6 | import { ChainId, CollectionType, StrategyType, CreateMakerInput } from "../../types"; 7 | 8 | describe("execute taker ask", () => { 9 | let mocks: SetupMocks; 10 | let signers: Signers; 11 | let lrUser1: LooksRare; 12 | let lrUser2: LooksRare; 13 | let baseMakerAskInput: CreateMakerInput; 14 | 15 | beforeEach(async () => { 16 | mocks = await setUpContracts(); 17 | signers = await getSigners(); 18 | 19 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 20 | lrUser2 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user2, mocks.addresses); 21 | 22 | baseMakerAskInput = { 23 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 24 | collectionType: CollectionType.ERC721, 25 | strategyId: StrategyType.standard, 26 | subsetNonce: 0, 27 | orderNonce: 0, 28 | startTime: Math.floor(Date.now() / 1000), 29 | endTime: Math.floor(Date.now() / 1000) + 3600, 30 | price: parseEther("1"), 31 | itemIds: [1], 32 | }; 33 | 34 | let tx = await lrUser1.grantTransferManagerApproval().call(); 35 | await tx.wait(); 36 | 37 | tx = await lrUser1.approveAllCollectionItems(baseMakerAskInput.collection); 38 | await tx.wait(); 39 | 40 | tx = await lrUser2.approveErc20(mocks.addresses.WETH); 41 | await tx.wait(); 42 | }); 43 | 44 | it("execute estimatedGas and callStatic", async () => { 45 | const { maker } = await lrUser2.createMakerBid(baseMakerAskInput); 46 | const signature = await lrUser2.signMakerOrder(maker); 47 | const taker = lrUser1.createTaker(maker, signers.user2.address); 48 | 49 | const contractMethods = lrUser1.executeOrder(maker, taker, signature); 50 | 51 | const estimatedGas = await contractMethods.estimateGas(); 52 | expect(Number(estimatedGas)).to.be.greaterThan(0); 53 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 54 | }); 55 | 56 | it("execute maker bid and taker ask", async () => { 57 | const { maker } = await lrUser2.createMakerBid(baseMakerAskInput); 58 | const signature = await lrUser2.signMakerOrder(maker); 59 | const taker = lrUser1.createTaker(maker, signers.user2.address); 60 | 61 | const contractMethods = lrUser1.executeOrder(maker, taker, signature); 62 | 63 | const tx = await contractMethods.call(); 64 | const receipt = await tx.wait(); 65 | expect(receipt?.status).to.be.equal(1); 66 | }); 67 | 68 | it("execute maker bid from a merkle tree signature, and taker ask", async () => { 69 | const order1 = await lrUser2.createMakerBid(baseMakerAskInput); 70 | const order2 = await lrUser2.createMakerBid(baseMakerAskInput); 71 | const { signature, merkleTreeProofs } = await lrUser2.signMultipleMakerOrders([order1.maker, order2.maker]); 72 | const taker = lrUser1.createTaker(order1.maker, signers.user2.address); 73 | 74 | const contractMethods = lrUser1.executeOrder(order1.maker, taker, signature, merkleTreeProofs[0]); 75 | 76 | const tx = await contractMethods.call(); 77 | const receipt = await tx.wait(); 78 | expect(receipt?.status).to.be.equal(1); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/executeTakerBid.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { parseEther } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 5 | import { LooksRare } from "../../LooksRare"; 6 | import { ChainId, CollectionType, StrategyType, CreateMakerInput } from "../../types"; 7 | 8 | describe("execute taker bid", () => { 9 | let mocks: SetupMocks; 10 | let signers: Signers; 11 | let lrUser1: LooksRare; 12 | let lrUser2: LooksRare; 13 | let baseMakerAskInput: CreateMakerInput; 14 | 15 | beforeEach(async () => { 16 | mocks = await setUpContracts(); 17 | signers = await getSigners(); 18 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 19 | lrUser2 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user2, mocks.addresses); 20 | 21 | baseMakerAskInput = { 22 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 23 | collectionType: CollectionType.ERC721, 24 | strategyId: StrategyType.standard, 25 | subsetNonce: 0, 26 | orderNonce: 0, 27 | startTime: Math.floor(Date.now() / 1000), 28 | endTime: Math.floor(Date.now() / 1000) + 3600, 29 | price: parseEther("1"), 30 | itemIds: [1], 31 | }; 32 | 33 | let tx = await lrUser1.grantTransferManagerApproval().call(); 34 | await tx.wait(); 35 | 36 | tx = await lrUser1.approveAllCollectionItems(baseMakerAskInput.collection); 37 | await tx.wait(); 38 | 39 | tx = await lrUser2.approveErc20(mocks.addresses.WETH); 40 | await tx.wait(); 41 | }); 42 | 43 | it("execute estimatedGas and callStatic", async () => { 44 | const { maker } = await lrUser1.createMakerAsk(baseMakerAskInput); 45 | const signature = await lrUser1.signMakerOrder(maker); 46 | const taker = lrUser2.createTaker(maker, signers.user2.address); 47 | const contractMethods = lrUser2.executeOrder(maker, taker, signature); 48 | 49 | const estimatedGas = await contractMethods.estimateGas(); 50 | expect(Number(estimatedGas)).to.be.greaterThan(0); 51 | 52 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 53 | }); 54 | 55 | it("execute maker ask and taker bid with ETH", async () => { 56 | const { maker } = await lrUser1.createMakerAsk(baseMakerAskInput); 57 | const signature = await lrUser1.signMakerOrder(maker); 58 | const taker = lrUser2.createTaker(maker, signers.user2.address); 59 | const contractMethods = lrUser2.executeOrder(maker, taker, signature); 60 | 61 | const tx = await contractMethods.call(); 62 | 63 | const receipt = await tx.wait(); 64 | expect(receipt?.status).to.be.equal(1); 65 | }); 66 | 67 | it("execute maker ask and taker bid with WETH", async () => { 68 | const { maker } = await lrUser1.createMakerAsk({ ...baseMakerAskInput, currency: mocks.addresses.WETH }); 69 | const signature = await lrUser1.signMakerOrder(maker); 70 | const taker = lrUser2.createTaker(maker, signers.user2.address); 71 | const contractMethods = lrUser2.executeOrder(maker, taker, signature); 72 | 73 | const tx = await contractMethods.call(); 74 | 75 | const receipt = await tx.wait(); 76 | expect(receipt?.status).to.be.equal(1); 77 | }); 78 | 79 | it("execute maker ask from a merkle tree signature and taker bid", async () => { 80 | const order1 = await lrUser1.createMakerAsk(baseMakerAskInput); 81 | const order2 = await lrUser1.createMakerAsk(baseMakerAskInput); 82 | const { signature, merkleTreeProofs } = await lrUser1.signMultipleMakerOrders([order1.maker, order2.maker]); 83 | const taker = lrUser2.createTaker(order1.maker, signers.user2.address); 84 | const contractMethods = lrUser2.executeOrder(order1.maker, taker, signature, merkleTreeProofs[0]); 85 | 86 | const tx = await contractMethods.call(); 87 | 88 | const receipt = await tx.wait(); 89 | expect(receipt?.status).to.be.equal(1); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/nonces.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers } from "hardhat"; 3 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 4 | import { viewUserBidAskNonces } from "../../utils/calls/nonces"; 5 | import { LooksRare } from "../../LooksRare"; 6 | import { ChainId } from "../../types"; 7 | 8 | describe("Nonces and order cancellation", () => { 9 | let mocks: SetupMocks; 10 | let signers: Signers; 11 | let lrUser1: LooksRare; 12 | 13 | beforeEach(async () => { 14 | mocks = await setUpContracts(); 15 | signers = await getSigners(); 16 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 17 | }); 18 | 19 | describe("cancelOrders", () => { 20 | it("cancel a nonce", async () => { 21 | const tx = await lrUser1.cancelOrders([0]).call(); 22 | const receipt = await tx.wait(); 23 | expect(receipt?.status).to.equal(1); 24 | }); 25 | 26 | it("cancel several nonces", async () => { 27 | const tx = await lrUser1.cancelOrders([0, 1]).call(); 28 | const receipt = await tx.wait(); 29 | expect(receipt?.status).to.equal(1); 30 | }); 31 | 32 | it("method analysis", async () => { 33 | const contractMethods = lrUser1.cancelOrders([0, 1]); 34 | const estimatedGas = await contractMethods.estimateGas(); 35 | expect(Number(estimatedGas)).to.be.greaterThan(0); 36 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 37 | }); 38 | }); 39 | 40 | describe("cancelSubsetOrders", () => { 41 | it("cancel a subset nonce", async () => { 42 | const tx = await lrUser1.cancelSubsetOrders([0]).call(); 43 | const receipt = await tx.wait(); 44 | expect(receipt?.status).to.equal(1); 45 | }); 46 | 47 | it("cancel several subset nonces", async () => { 48 | const tx = await lrUser1.cancelSubsetOrders([0, 1]).call(); 49 | const receipt = await tx.wait(); 50 | expect(receipt?.status).to.equal(1); 51 | }); 52 | 53 | it("method analysis", async () => { 54 | const contractMethods = lrUser1.cancelSubsetOrders([0, 1]); 55 | const estimatedGas = await contractMethods.estimateGas(); 56 | expect(Number(estimatedGas)).to.be.greaterThan(0); 57 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 58 | }); 59 | }); 60 | 61 | describe("cancelAllOrders", () => { 62 | it("increment bid nonce", async () => { 63 | const tx = await lrUser1.cancelAllOrders(true, false).call(); 64 | const receipt = await tx.wait(); 65 | expect(receipt?.status).to.equal(1); 66 | 67 | const userNonces = await viewUserBidAskNonces(signers.user1, mocks.addresses.EXCHANGE_V2, signers.user1.address); 68 | expect(userNonces.bidNonce > 0n).to.be.true; 69 | expect(userNonces.askNonce).to.be.eq(0n); 70 | }); 71 | 72 | it("increment ask nonce", async () => { 73 | const tx = await lrUser1.cancelAllOrders(false, true).call(); 74 | const receipt = await tx.wait(); 75 | expect(receipt?.status).to.equal(1); 76 | 77 | const userNonces = await viewUserBidAskNonces(signers.user1, mocks.addresses.EXCHANGE_V2, signers.user1.address); 78 | expect(userNonces.bidNonce).to.be.eq(0n); 79 | expect(userNonces.askNonce > 0n).to.be.true; 80 | }); 81 | 82 | it("increment bid/ask nonces", async () => { 83 | const tx = await lrUser1.cancelAllOrders(true, true).call(); 84 | const receipt = await tx.wait(); 85 | expect(receipt?.status).to.equal(1); 86 | 87 | const userNonces = await viewUserBidAskNonces(signers.user1, mocks.addresses.EXCHANGE_V2, signers.user1.address); 88 | expect(userNonces.bidNonce > 0n).to.be.true; 89 | expect(userNonces.askNonce > 0n).to.be.true; 90 | }); 91 | 92 | it("method analysis", async () => { 93 | const contractMethods = lrUser1.cancelAllOrders(true, true); 94 | const estimatedGas = await lrUser1.cancelAllOrders(true, true).estimateGas(); 95 | expect(Number(estimatedGas)).to.be.greaterThan(0); 96 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 97 | }); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/orderValidator.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers } from "hardhat"; 3 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 4 | import { LooksRare } from "../../LooksRare"; 5 | import { ChainId, CollectionType, StrategyType, CreateMakerInput, OrderValidatorCode } from "../../types"; 6 | import { parseEther } from "ethers"; 7 | 8 | describe("Order validation", () => { 9 | let mocks: SetupMocks; 10 | let signers: Signers; 11 | let lrUser1: LooksRare; 12 | let baseMakerAskInput: CreateMakerInput; 13 | let baseMakerBidInput: CreateMakerInput; 14 | 15 | beforeEach(async () => { 16 | mocks = await setUpContracts(); 17 | signers = await getSigners(); 18 | 19 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 20 | 21 | baseMakerAskInput = { 22 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 23 | collectionType: CollectionType.ERC721, 24 | strategyId: StrategyType.standard, 25 | subsetNonce: 0, 26 | orderNonce: 0, 27 | startTime: Math.floor(Date.now() / 1000), 28 | endTime: Math.floor(Date.now() / 1000) + 3600, 29 | price: parseEther("1"), 30 | itemIds: [1], 31 | }; 32 | 33 | baseMakerBidInput = { 34 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 35 | collectionType: CollectionType.ERC721, 36 | strategyId: StrategyType.standard, 37 | subsetNonce: 0, 38 | orderNonce: 0, 39 | endTime: Math.floor(Date.now() / 1000) + 3600, 40 | price: parseEther("1"), 41 | itemIds: [1], 42 | }; 43 | 44 | const tx = await mocks.contracts.transferManager 45 | .connect(signers.user1) 46 | .grantApprovals([mocks.addresses.EXCHANGE_V2]); 47 | await tx.wait(); 48 | }); 49 | 50 | it("verify maker ask orders", async () => { 51 | const { maker } = await lrUser1.createMakerAsk(baseMakerAskInput); 52 | const signature = await lrUser1.signMakerOrder(maker); 53 | 54 | let orders = await lrUser1.verifyMakerOrders([maker], [signature]); 55 | expect(orders[0].some((code) => code === OrderValidatorCode.ERC721_NO_APPROVAL_FOR_ALL_OR_ITEM_ID)).to.be.true; 56 | 57 | const tx = await lrUser1.approveAllCollectionItems(baseMakerAskInput.collection); 58 | await tx.wait(); 59 | 60 | orders = await lrUser1.verifyMakerOrders([maker], [signature]); 61 | expect(orders[0].every((code) => code === OrderValidatorCode.ORDER_EXPECTED_TO_BE_VALID)).to.be.true; 62 | }); 63 | 64 | it("verify maker bid orders", async () => { 65 | const { maker } = await lrUser1.createMakerBid(baseMakerBidInput); 66 | const signature = await lrUser1.signMakerOrder(maker); 67 | 68 | let orders = await lrUser1.verifyMakerOrders([maker], [signature]); 69 | 70 | orders = await lrUser1.verifyMakerOrders([maker], [signature]); 71 | expect(orders[0].some((code) => code === OrderValidatorCode.ERC20_APPROVAL_INFERIOR_TO_PRICE)).to.be.true; 72 | 73 | const tx = await lrUser1.approveErc20(mocks.addresses.WETH); 74 | await tx.wait(); 75 | 76 | orders = await lrUser1.verifyMakerOrders([maker], [signature]); 77 | expect(orders[0].every((code) => code === OrderValidatorCode.ORDER_EXPECTED_TO_BE_VALID)).to.be.true; 78 | }); 79 | 80 | it("verify multiple maker orders with a Merkle tree", async () => { 81 | const { maker: makerBid } = await lrUser1.createMakerBid(baseMakerBidInput); 82 | const { maker: makerAsk } = await lrUser1.createMakerAsk(baseMakerAskInput); 83 | const { signature, merkleTreeProofs } = await lrUser1.signMultipleMakerOrders([makerBid, makerAsk]); 84 | 85 | let tx = await lrUser1.approveAllCollectionItems(baseMakerAskInput.collection); 86 | await tx.wait(); 87 | tx = await lrUser1.approveErc20(mocks.addresses.WETH); 88 | await tx.wait(); 89 | 90 | const orders = await lrUser1.verifyMakerOrders( 91 | [makerBid, makerAsk], 92 | [signature, signature], 93 | [merkleTreeProofs[0], merkleTreeProofs[1]] 94 | ); 95 | 96 | expect(orders[0].every((code) => code === OrderValidatorCode.ORDER_EXPECTED_TO_BE_VALID)).to.be.true; 97 | expect(orders[1].every((code) => code === OrderValidatorCode.ORDER_EXPECTED_TO_BE_VALID)).to.be.true; 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/signMakerOrders.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { AbiCoder, parseEther, verifyTypedData, TypedDataDomain } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | import { LooksRare } from "../../LooksRare"; 5 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 6 | import { contractName, version } from "../../constants/eip712"; 7 | import { MAX_ORDERS_PER_TREE } from "../../constants"; 8 | import { encodeParams, getMakerParamsTypes, getTakerParamsTypes } from "../../utils/encodeOrderParams"; 9 | import { makerTypes } from "../../utils/eip712"; 10 | import { ChainId, Maker, CollectionType, StrategyType, QuoteType } from "../../types"; 11 | import { ErrorMerkleTreeDepth } from "../../errors"; 12 | 13 | const faultySignature = 14 | "0xcafe829116da9a4b31a958aa790682228b85e5d03b1ae7bb15f8ce4c8432a20813934991833da8e913894c9f35f1f018948c58d68fb61bbca0e07bd43c4492fa2b"; 15 | 16 | describe("Sign maker orders", () => { 17 | let mocks: SetupMocks; 18 | let signers: Signers; 19 | let lrUser1: LooksRare; 20 | let domain: TypedDataDomain; 21 | 22 | beforeEach(async () => { 23 | mocks = await setUpContracts(); 24 | signers = await getSigners(); 25 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 26 | 27 | domain = { 28 | name: contractName, 29 | version: version.toString(), 30 | chainId: ChainId.HARDHAT, 31 | verifyingContract: mocks.addresses.EXCHANGE_V2, 32 | }; 33 | }); 34 | 35 | describe("Sign single maker orders", () => { 36 | it("sign maker ask order", async () => { 37 | const { collectionERC721, verifier } = mocks.contracts; 38 | const makerOrder: Maker = { 39 | quoteType: QuoteType.Ask, 40 | globalNonce: 1, 41 | subsetNonce: 1, 42 | strategyId: 1, 43 | collectionType: CollectionType.ERC721, 44 | orderNonce: 1, 45 | collection: await collectionERC721.getAddress(), 46 | currency: mocks.addresses.WETH, 47 | signer: signers.user1.address, 48 | startTime: Math.floor(Date.now() / 1000), 49 | endTime: Math.floor(Date.now() / 1000 + 3600), 50 | price: parseEther("1").toString(), 51 | itemIds: [1], 52 | amounts: [1], 53 | additionalParameters: encodeParams([], getMakerParamsTypes(StrategyType.standard)), 54 | }; 55 | 56 | const signature = await lrUser1.signMakerOrder(makerOrder); 57 | 58 | expect(verifyTypedData(domain, makerTypes, makerOrder, signature)).to.equal(signers.user1.address); 59 | await expect(verifier.verifySignature(makerOrder, signature)).to.eventually.be.fulfilled; 60 | await expect(verifier.verifySignature(makerOrder, faultySignature)).to.eventually.be.rejectedWith( 61 | /reverted with/ 62 | ); 63 | }); 64 | 65 | it("sign maker bid order", async () => { 66 | const { collectionERC721, verifier } = mocks.contracts; 67 | const makerOrder: Maker = { 68 | quoteType: QuoteType.Bid, 69 | globalNonce: 1, 70 | subsetNonce: 1, 71 | strategyId: 1, 72 | collectionType: CollectionType.ERC721, 73 | orderNonce: 1, 74 | collection: await collectionERC721.getAddress(), 75 | currency: mocks.addresses.WETH, 76 | signer: signers.user1.address, 77 | startTime: Math.floor(Date.now() / 1000), 78 | endTime: Math.floor(Date.now() / 1000 + 3600), 79 | price: parseEther("1").toString(), 80 | itemIds: [1], 81 | amounts: [1], 82 | additionalParameters: encodeParams([], getTakerParamsTypes(StrategyType.standard)), 83 | }; 84 | 85 | const signature = await lrUser1.signMakerOrder(makerOrder); 86 | 87 | expect(verifyTypedData(domain, makerTypes, makerOrder, signature)).to.equal(signers.user1.address); 88 | await expect(verifier.verifySignature(makerOrder, signature)).to.eventually.be.fulfilled; 89 | await expect(verifier.verifySignature(makerOrder, faultySignature)).to.eventually.be.rejectedWith( 90 | /reverted with/ 91 | ); 92 | }); 93 | }); 94 | 95 | describe("Sign multiple maker orders", () => { 96 | it("sign multiple maker bid order (merkle tree)", async () => { 97 | const { verifier } = mocks.contracts; 98 | const makerOrders: Maker[] = [ 99 | { 100 | quoteType: QuoteType.Bid, 101 | globalNonce: 1, 102 | subsetNonce: 1, 103 | strategyId: 1, 104 | collectionType: CollectionType.ERC721, 105 | orderNonce: 1, 106 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 107 | currency: mocks.addresses.WETH, 108 | signer: signers.user1.address, 109 | startTime: Math.floor(Date.now() / 1000), 110 | endTime: Math.floor(Date.now() / 1000 + 3600), 111 | price: parseEther("1").toString(), 112 | itemIds: [1], 113 | amounts: [1], 114 | additionalParameters: AbiCoder.defaultAbiCoder().encode([], []), 115 | }, 116 | { 117 | quoteType: QuoteType.Bid, 118 | globalNonce: 1, 119 | subsetNonce: 1, 120 | strategyId: 1, 121 | collectionType: CollectionType.ERC721, 122 | orderNonce: 1, 123 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 124 | currency: mocks.addresses.WETH, 125 | signer: signers.user1.address, 126 | startTime: Math.floor(Date.now() / 1000), 127 | endTime: Math.floor(Date.now() / 1000 + 3600), 128 | price: parseEther("1").toString(), 129 | itemIds: [1], 130 | amounts: [1], 131 | additionalParameters: AbiCoder.defaultAbiCoder().encode([], []), 132 | }, 133 | { 134 | quoteType: QuoteType.Bid, 135 | globalNonce: 1, 136 | subsetNonce: 1, 137 | strategyId: 1, 138 | collectionType: CollectionType.ERC721, 139 | orderNonce: 1, 140 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 141 | currency: mocks.addresses.WETH, 142 | signer: signers.user1.address, 143 | startTime: Math.floor(Date.now() / 1000), 144 | endTime: Math.floor(Date.now() / 1000 + 3600), 145 | price: parseEther("1").toString(), 146 | itemIds: [1], 147 | amounts: [1], 148 | additionalParameters: AbiCoder.defaultAbiCoder().encode([], []), 149 | }, 150 | ]; 151 | 152 | const { signature, merkleTreeProofs, tree } = await lrUser1.signMultipleMakerOrders(makerOrders); 153 | const signerAddress = signers.user1.address; 154 | 155 | expect(verifyTypedData(domain, tree.types, tree.getDataToSign(), signature)).to.equal(signerAddress); 156 | 157 | merkleTreeProofs.forEach(async (merkleTreeProof) => { 158 | await expect(verifier.verifyMerkleTree(merkleTreeProof, signature, signerAddress)).to.eventually.be.fulfilled; 159 | await expect( 160 | verifier.verifyMerkleTree(merkleTreeProof, faultySignature, signerAddress) 161 | ).to.eventually.be.rejectedWith(/reverted with/); 162 | }); 163 | }); 164 | 165 | it("sign orders when number of orders = MAX_ORDERS_PER_TREE", async () => { 166 | const makerOrders: Maker[] = [...Array(MAX_ORDERS_PER_TREE)].map(() => ({ 167 | quoteType: QuoteType.Bid, 168 | globalNonce: 1, 169 | subsetNonce: 1, 170 | strategyId: 1, 171 | collectionType: CollectionType.ERC721, 172 | orderNonce: 1, 173 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 174 | currency: mocks.addresses.WETH, 175 | signer: signers.user1.address, 176 | startTime: Math.floor(Date.now() / 1000), 177 | endTime: Math.floor(Date.now() / 1000 + 3600), 178 | price: parseEther("1").toString(), 179 | itemIds: [1], 180 | amounts: [1], 181 | additionalParameters: AbiCoder.defaultAbiCoder().encode([], []), 182 | })); 183 | 184 | await expect(lrUser1.signMultipleMakerOrders(makerOrders)).to.eventually.be.fulfilled; 185 | }); 186 | 187 | it("revert if number of orders > MAX_ORDERS_PER_TREE", async () => { 188 | const makerOrders: Maker[] = [...Array(MAX_ORDERS_PER_TREE + 1)].map(() => ({ 189 | quoteType: QuoteType.Bid, 190 | globalNonce: 1, 191 | subsetNonce: 1, 192 | strategyId: 1, 193 | collectionType: CollectionType.ERC721, 194 | orderNonce: 1, 195 | collection: mocks.addresses.MOCK_COLLECTION_ERC721, 196 | currency: mocks.addresses.WETH, 197 | signer: signers.user1.address, 198 | startTime: Math.floor(Date.now() / 1000), 199 | endTime: Math.floor(Date.now() / 1000 + 3600), 200 | price: parseEther("1").toString(), 201 | itemIds: [1], 202 | amounts: [1], 203 | additionalParameters: AbiCoder.defaultAbiCoder().encode([], []), 204 | })); 205 | 206 | await expect(lrUser1.signMultipleMakerOrders(makerOrders)).to.eventually.be.rejectedWith(ErrorMerkleTreeDepth); 207 | }); 208 | }); 209 | }); 210 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/strategies.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers } from "hardhat"; 3 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 4 | import { LooksRare } from "../../LooksRare"; 5 | import { ChainId, StrategyType } from "../../types"; 6 | 7 | describe("Strategies", () => { 8 | let mocks: SetupMocks; 9 | let signers: Signers; 10 | 11 | beforeEach(async () => { 12 | mocks = await setUpContracts(); 13 | signers = await getSigners(); 14 | }); 15 | 16 | it("fetch strategies info", async () => { 17 | const lr = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 18 | const standardStrategy = await lr.strategyInfo(StrategyType.standard); 19 | expect(standardStrategy).to.not.be.undefined; 20 | expect(standardStrategy.isActive).to.be.true; 21 | 22 | const collectionStrategy = await lr.strategyInfo(StrategyType.collection); 23 | expect(collectionStrategy).to.not.be.undefined; 24 | expect(collectionStrategy.isActive).to.be.true; 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/__tests__/looksrare/transferManager.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { Contract } from "ethers"; 3 | import { ethers } from "hardhat"; 4 | import { ERC721 } from "../../typechain/solmate/src/tokens/ERC721.sol/ERC721"; 5 | import { ERC1155 } from "../../typechain/solmate/src/tokens/ERC1155.sol/ERC1155"; 6 | import abiIERC721 from "../../abis/IERC721.json"; 7 | import abiIERC1155 from "../../abis/IERC1155.json"; 8 | import { setUpContracts, SetupMocks, getSigners, Signers } from "../helpers/setup"; 9 | import { LooksRare } from "../../LooksRare"; 10 | import { ChainId, CollectionType, BatchTransferItem } from "../../types"; 11 | 12 | describe("Transfer manager", () => { 13 | let mocks: SetupMocks; 14 | let signers: Signers; 15 | let lrUser1: LooksRare; 16 | 17 | beforeEach(async () => { 18 | mocks = await setUpContracts(); 19 | signers = await getSigners(); 20 | lrUser1 = new LooksRare(ChainId.HARDHAT, ethers.provider, signers.user1, mocks.addresses); 21 | }); 22 | 23 | it("has user approved", async () => { 24 | expect(await lrUser1.isTransferManagerApproved()).to.be.false; 25 | 26 | const methods = lrUser1.grantTransferManagerApproval(); 27 | const tx = await methods.call(); 28 | await tx.wait(); 29 | 30 | expect(await lrUser1.isTransferManagerApproved()).to.be.true; 31 | }); 32 | 33 | it("grant operator approvals", async () => { 34 | const contractMethods = lrUser1.grantTransferManagerApproval(); 35 | 36 | const estimatedGas = await contractMethods.estimateGas(); 37 | expect(Number(estimatedGas)).to.be.greaterThan(0); 38 | 39 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 40 | 41 | const tx = await contractMethods.call(); 42 | const receipt = await tx.wait(); 43 | expect(receipt?.status).to.equal(1); 44 | }); 45 | 46 | it("revoke operator approvals", async () => { 47 | await (await lrUser1.grantTransferManagerApproval().call()).wait(); 48 | const contractMethods = lrUser1.revokeTransferManagerApproval(); 49 | 50 | const estimatedGas = await contractMethods.estimateGas(); 51 | expect(Number(estimatedGas)).to.be.greaterThan(0); 52 | 53 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 54 | 55 | const tx = await contractMethods.call(); 56 | const receipt = await tx.wait(); 57 | expect(receipt?.status).to.equal(1); 58 | }); 59 | 60 | it("transfer items from a single collection", async () => { 61 | const { addresses } = mocks; 62 | await (await lrUser1.approveAllCollectionItems(addresses.MOCK_COLLECTION_ERC721)).wait(); 63 | await (await lrUser1.grantTransferManagerApproval().call()).wait(); 64 | const collectionERC721 = new Contract(addresses.MOCK_COLLECTION_ERC721, abiIERC721).connect( 65 | ethers.provider 66 | ) as ERC721; 67 | 68 | const receipient = signers.user3.address; 69 | const tokenId = 0; 70 | 71 | // Check the initial owner 72 | const initialOwner = await collectionERC721.ownerOf(tokenId); 73 | expect(initialOwner).to.be.equal(signers.user1.address); 74 | 75 | const items: BatchTransferItem[] = [ 76 | { 77 | collection: addresses.MOCK_COLLECTION_ERC721, 78 | collectionType: CollectionType.ERC721, 79 | itemIds: [tokenId], 80 | amounts: [1], 81 | }, 82 | ]; 83 | const contractMethods = await lrUser1.transferItemsAcrossCollection(receipient, items); 84 | 85 | const estimatedGas = await contractMethods.estimateGas(); 86 | expect(Number(estimatedGas)).to.be.greaterThan(0); 87 | 88 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 89 | 90 | const tx = await contractMethods.call(); 91 | const receipt = await tx.wait(); 92 | expect(receipt?.status).to.equal(1); 93 | 94 | // Check the new owner 95 | const newOwner = await collectionERC721.ownerOf(tokenId); 96 | expect(newOwner).to.be.equal(receipient); 97 | }); 98 | 99 | it("transfer items from multiple collections", async () => { 100 | const { addresses } = mocks; 101 | await (await lrUser1.approveAllCollectionItems(addresses.MOCK_COLLECTION_ERC721)).wait(); 102 | await (await lrUser1.approveAllCollectionItems(addresses.MOCK_COLLECTION_ERC1155)).wait(); 103 | (await lrUser1.grantTransferManagerApproval().call()).wait(); 104 | const collectionERC721 = new Contract(addresses.MOCK_COLLECTION_ERC721, abiIERC721).connect( 105 | ethers.provider 106 | ) as ERC721; 107 | const collectionERC1155 = new Contract(addresses.MOCK_COLLECTION_ERC1155, abiIERC1155).connect( 108 | ethers.provider 109 | ) as ERC1155; 110 | 111 | const receipient = signers.user3.address; 112 | 113 | // Execute the transfer 114 | const items: BatchTransferItem[] = [ 115 | { 116 | collection: addresses.MOCK_COLLECTION_ERC721, 117 | collectionType: CollectionType.ERC721, 118 | itemIds: [0], 119 | amounts: [1], 120 | }, 121 | { 122 | collection: addresses.MOCK_COLLECTION_ERC1155, 123 | collectionType: CollectionType.ERC1155, 124 | itemIds: [0], 125 | amounts: [10], 126 | }, 127 | ]; 128 | const contractMethods = await lrUser1.transferItemsAcrossCollection(receipient, items); 129 | 130 | const estimatedGas = await contractMethods.estimateGas(); 131 | expect(Number(estimatedGas)).to.be.greaterThan(0); 132 | 133 | await expect(contractMethods.callStatic()).to.eventually.be.fulfilled; 134 | 135 | const tx = await contractMethods.call(); 136 | const receipt = await tx.wait(); 137 | expect(receipt?.status).to.equal(1); 138 | 139 | // Check the new owner 140 | const newOwner = await collectionERC721.ownerOf(0); 141 | expect(newOwner).to.be.equal(receipient); 142 | const newBalance = await collectionERC1155.balanceOf(receipient, 0); 143 | expect(newBalance).to.be.equal(10n); 144 | }); 145 | }); 146 | -------------------------------------------------------------------------------- /src/__tests__/tokens.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers } from "hardhat"; 3 | import { setUpContracts, SetupMocks, getSigners, Signers } from "./helpers/setup"; 4 | import { isApprovedForAll, setApprovalForAll } from "../utils/calls/tokens"; 5 | 6 | describe("Tokens", () => { 7 | let mocks: SetupMocks; 8 | let signers: Signers; 9 | beforeEach(async () => { 10 | mocks = await setUpContracts(); 11 | signers = await getSigners(); 12 | }); 13 | it("approve ERC721", async () => { 14 | const provider = ethers.provider; 15 | const { collectionERC721 } = mocks.contracts; 16 | const collectionERC721Address = await collectionERC721.getAddress(); 17 | 18 | let isApproved = await isApprovedForAll( 19 | provider, 20 | collectionERC721Address, 21 | signers.user1.address, 22 | signers.operator.address 23 | ); 24 | expect(isApproved).to.be.false; 25 | 26 | // Approve 27 | let transaction = await setApprovalForAll(signers.user1, collectionERC721Address, signers.operator.address, true); 28 | let receipt = await transaction.wait(); 29 | expect(receipt?.status).to.equal(1); 30 | 31 | isApproved = await isApprovedForAll( 32 | provider, 33 | collectionERC721Address, 34 | signers.user1.address, 35 | signers.operator.address 36 | ); 37 | expect(isApproved).to.be.true; 38 | 39 | // Cancel approval 40 | transaction = await setApprovalForAll(signers.user1, collectionERC721Address, signers.operator.address, false); 41 | receipt = await transaction.wait(); 42 | expect(receipt?.status).to.equal(1); 43 | 44 | isApproved = await isApprovedForAll( 45 | provider, 46 | collectionERC721Address, 47 | signers.user1.address, 48 | signers.operator.address 49 | ); 50 | expect(isApproved).to.be.false; 51 | }); 52 | it("approve ERC1155", async () => { 53 | const provider = ethers.provider; 54 | const { collectionERC1155 } = mocks.contracts; 55 | const collectionERC1155Address = await collectionERC1155.getAddress(); 56 | 57 | let isApproved = await isApprovedForAll( 58 | provider, 59 | collectionERC1155Address, 60 | signers.user2.address, 61 | signers.operator.address 62 | ); 63 | expect(isApproved).to.be.false; 64 | 65 | const transaction = await setApprovalForAll( 66 | signers.user2, 67 | collectionERC1155Address, 68 | signers.operator.address, 69 | true 70 | ); 71 | const receipt = await transaction.wait(); 72 | expect(receipt?.status).to.equal(1); 73 | 74 | isApproved = await isApprovedForAll( 75 | provider, 76 | collectionERC1155Address, 77 | signers.user2.address, 78 | signers.operator.address 79 | ); 80 | expect(isApproved).to.be.true; 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /src/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "files": ["../../hardhat.config.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /src/abis/IERC1155.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "account", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "operator", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "bool", 20 | "name": "approved", 21 | "type": "bool" 22 | } 23 | ], 24 | "name": "ApprovalForAll", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "operator", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "from", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": true, 44 | "internalType": "address", 45 | "name": "to", 46 | "type": "address" 47 | }, 48 | { 49 | "indexed": false, 50 | "internalType": "uint256[]", 51 | "name": "ids", 52 | "type": "uint256[]" 53 | }, 54 | { 55 | "indexed": false, 56 | "internalType": "uint256[]", 57 | "name": "values", 58 | "type": "uint256[]" 59 | } 60 | ], 61 | "name": "TransferBatch", 62 | "type": "event" 63 | }, 64 | { 65 | "anonymous": false, 66 | "inputs": [ 67 | { 68 | "indexed": true, 69 | "internalType": "address", 70 | "name": "operator", 71 | "type": "address" 72 | }, 73 | { 74 | "indexed": true, 75 | "internalType": "address", 76 | "name": "from", 77 | "type": "address" 78 | }, 79 | { 80 | "indexed": true, 81 | "internalType": "address", 82 | "name": "to", 83 | "type": "address" 84 | }, 85 | { 86 | "indexed": false, 87 | "internalType": "uint256", 88 | "name": "id", 89 | "type": "uint256" 90 | }, 91 | { 92 | "indexed": false, 93 | "internalType": "uint256", 94 | "name": "value", 95 | "type": "uint256" 96 | } 97 | ], 98 | "name": "TransferSingle", 99 | "type": "event" 100 | }, 101 | { 102 | "anonymous": false, 103 | "inputs": [ 104 | { 105 | "indexed": false, 106 | "internalType": "string", 107 | "name": "value", 108 | "type": "string" 109 | }, 110 | { 111 | "indexed": true, 112 | "internalType": "uint256", 113 | "name": "id", 114 | "type": "uint256" 115 | } 116 | ], 117 | "name": "URI", 118 | "type": "event" 119 | }, 120 | { 121 | "inputs": [ 122 | { 123 | "internalType": "address", 124 | "name": "account", 125 | "type": "address" 126 | }, 127 | { 128 | "internalType": "uint256", 129 | "name": "id", 130 | "type": "uint256" 131 | } 132 | ], 133 | "name": "balanceOf", 134 | "outputs": [ 135 | { 136 | "internalType": "uint256", 137 | "name": "", 138 | "type": "uint256" 139 | } 140 | ], 141 | "stateMutability": "view", 142 | "type": "function" 143 | }, 144 | { 145 | "inputs": [ 146 | { 147 | "internalType": "address[]", 148 | "name": "accounts", 149 | "type": "address[]" 150 | }, 151 | { 152 | "internalType": "uint256[]", 153 | "name": "ids", 154 | "type": "uint256[]" 155 | } 156 | ], 157 | "name": "balanceOfBatch", 158 | "outputs": [ 159 | { 160 | "internalType": "uint256[]", 161 | "name": "", 162 | "type": "uint256[]" 163 | } 164 | ], 165 | "stateMutability": "view", 166 | "type": "function" 167 | }, 168 | { 169 | "inputs": [ 170 | { 171 | "internalType": "address", 172 | "name": "account", 173 | "type": "address" 174 | }, 175 | { 176 | "internalType": "address", 177 | "name": "operator", 178 | "type": "address" 179 | } 180 | ], 181 | "name": "isApprovedForAll", 182 | "outputs": [ 183 | { 184 | "internalType": "bool", 185 | "name": "", 186 | "type": "bool" 187 | } 188 | ], 189 | "stateMutability": "view", 190 | "type": "function" 191 | }, 192 | { 193 | "inputs": [ 194 | { 195 | "internalType": "address", 196 | "name": "from", 197 | "type": "address" 198 | }, 199 | { 200 | "internalType": "address", 201 | "name": "to", 202 | "type": "address" 203 | }, 204 | { 205 | "internalType": "uint256[]", 206 | "name": "ids", 207 | "type": "uint256[]" 208 | }, 209 | { 210 | "internalType": "uint256[]", 211 | "name": "amounts", 212 | "type": "uint256[]" 213 | }, 214 | { 215 | "internalType": "bytes", 216 | "name": "data", 217 | "type": "bytes" 218 | } 219 | ], 220 | "name": "safeBatchTransferFrom", 221 | "outputs": [], 222 | "stateMutability": "nonpayable", 223 | "type": "function" 224 | }, 225 | { 226 | "inputs": [ 227 | { 228 | "internalType": "address", 229 | "name": "from", 230 | "type": "address" 231 | }, 232 | { 233 | "internalType": "address", 234 | "name": "to", 235 | "type": "address" 236 | }, 237 | { 238 | "internalType": "uint256", 239 | "name": "id", 240 | "type": "uint256" 241 | }, 242 | { 243 | "internalType": "uint256", 244 | "name": "amount", 245 | "type": "uint256" 246 | }, 247 | { 248 | "internalType": "bytes", 249 | "name": "data", 250 | "type": "bytes" 251 | } 252 | ], 253 | "name": "safeTransferFrom", 254 | "outputs": [], 255 | "stateMutability": "nonpayable", 256 | "type": "function" 257 | }, 258 | { 259 | "inputs": [ 260 | { 261 | "internalType": "address", 262 | "name": "operator", 263 | "type": "address" 264 | }, 265 | { 266 | "internalType": "bool", 267 | "name": "approved", 268 | "type": "bool" 269 | } 270 | ], 271 | "name": "setApprovalForAll", 272 | "outputs": [], 273 | "stateMutability": "nonpayable", 274 | "type": "function" 275 | } 276 | ] 277 | -------------------------------------------------------------------------------- /src/abis/IERC20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "owner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "spender", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "value", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Approval", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "from", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "to", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "value", 46 | "type": "uint256" 47 | } 48 | ], 49 | "name": "Transfer", 50 | "type": "event" 51 | }, 52 | { 53 | "inputs": [ 54 | { 55 | "internalType": "address", 56 | "name": "owner", 57 | "type": "address" 58 | }, 59 | { 60 | "internalType": "address", 61 | "name": "spender", 62 | "type": "address" 63 | } 64 | ], 65 | "name": "allowance", 66 | "outputs": [ 67 | { 68 | "internalType": "uint256", 69 | "name": "", 70 | "type": "uint256" 71 | } 72 | ], 73 | "stateMutability": "view", 74 | "type": "function" 75 | }, 76 | { 77 | "inputs": [ 78 | { 79 | "internalType": "address", 80 | "name": "spender", 81 | "type": "address" 82 | }, 83 | { 84 | "internalType": "uint256", 85 | "name": "amount", 86 | "type": "uint256" 87 | } 88 | ], 89 | "name": "approve", 90 | "outputs": [ 91 | { 92 | "internalType": "bool", 93 | "name": "", 94 | "type": "bool" 95 | } 96 | ], 97 | "stateMutability": "nonpayable", 98 | "type": "function" 99 | }, 100 | { 101 | "inputs": [ 102 | { 103 | "internalType": "address", 104 | "name": "account", 105 | "type": "address" 106 | } 107 | ], 108 | "name": "balanceOf", 109 | "outputs": [ 110 | { 111 | "internalType": "uint256", 112 | "name": "", 113 | "type": "uint256" 114 | } 115 | ], 116 | "stateMutability": "view", 117 | "type": "function" 118 | }, 119 | { 120 | "inputs": [], 121 | "name": "totalSupply", 122 | "outputs": [ 123 | { 124 | "internalType": "uint256", 125 | "name": "", 126 | "type": "uint256" 127 | } 128 | ], 129 | "stateMutability": "view", 130 | "type": "function" 131 | }, 132 | { 133 | "inputs": [ 134 | { 135 | "internalType": "address", 136 | "name": "to", 137 | "type": "address" 138 | }, 139 | { 140 | "internalType": "uint256", 141 | "name": "amount", 142 | "type": "uint256" 143 | } 144 | ], 145 | "name": "transfer", 146 | "outputs": [ 147 | { 148 | "internalType": "bool", 149 | "name": "", 150 | "type": "bool" 151 | } 152 | ], 153 | "stateMutability": "nonpayable", 154 | "type": "function" 155 | }, 156 | { 157 | "inputs": [ 158 | { 159 | "internalType": "address", 160 | "name": "from", 161 | "type": "address" 162 | }, 163 | { 164 | "internalType": "address", 165 | "name": "to", 166 | "type": "address" 167 | }, 168 | { 169 | "internalType": "uint256", 170 | "name": "amount", 171 | "type": "uint256" 172 | } 173 | ], 174 | "name": "transferFrom", 175 | "outputs": [ 176 | { 177 | "internalType": "bool", 178 | "name": "", 179 | "type": "bool" 180 | } 181 | ], 182 | "stateMutability": "nonpayable", 183 | "type": "function" 184 | } 185 | ] 186 | -------------------------------------------------------------------------------- /src/abis/IERC721.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "owner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "approved", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": true, 19 | "internalType": "uint256", 20 | "name": "tokenId", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Approval", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "owner", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "operator", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "bool", 45 | "name": "approved", 46 | "type": "bool" 47 | } 48 | ], 49 | "name": "ApprovalForAll", 50 | "type": "event" 51 | }, 52 | { 53 | "anonymous": false, 54 | "inputs": [ 55 | { 56 | "indexed": true, 57 | "internalType": "address", 58 | "name": "from", 59 | "type": "address" 60 | }, 61 | { 62 | "indexed": true, 63 | "internalType": "address", 64 | "name": "to", 65 | "type": "address" 66 | }, 67 | { 68 | "indexed": true, 69 | "internalType": "uint256", 70 | "name": "tokenId", 71 | "type": "uint256" 72 | } 73 | ], 74 | "name": "Transfer", 75 | "type": "event" 76 | }, 77 | { 78 | "inputs": [ 79 | { 80 | "internalType": "address", 81 | "name": "to", 82 | "type": "address" 83 | }, 84 | { 85 | "internalType": "uint256", 86 | "name": "tokenId", 87 | "type": "uint256" 88 | } 89 | ], 90 | "name": "approve", 91 | "outputs": [], 92 | "stateMutability": "nonpayable", 93 | "type": "function" 94 | }, 95 | { 96 | "inputs": [ 97 | { 98 | "internalType": "address", 99 | "name": "owner", 100 | "type": "address" 101 | } 102 | ], 103 | "name": "balanceOf", 104 | "outputs": [ 105 | { 106 | "internalType": "uint256", 107 | "name": "balance", 108 | "type": "uint256" 109 | } 110 | ], 111 | "stateMutability": "view", 112 | "type": "function" 113 | }, 114 | { 115 | "inputs": [ 116 | { 117 | "internalType": "uint256", 118 | "name": "tokenId", 119 | "type": "uint256" 120 | } 121 | ], 122 | "name": "getApproved", 123 | "outputs": [ 124 | { 125 | "internalType": "address", 126 | "name": "operator", 127 | "type": "address" 128 | } 129 | ], 130 | "stateMutability": "view", 131 | "type": "function" 132 | }, 133 | { 134 | "inputs": [ 135 | { 136 | "internalType": "address", 137 | "name": "owner", 138 | "type": "address" 139 | }, 140 | { 141 | "internalType": "address", 142 | "name": "operator", 143 | "type": "address" 144 | } 145 | ], 146 | "name": "isApprovedForAll", 147 | "outputs": [ 148 | { 149 | "internalType": "bool", 150 | "name": "", 151 | "type": "bool" 152 | } 153 | ], 154 | "stateMutability": "view", 155 | "type": "function" 156 | }, 157 | { 158 | "inputs": [ 159 | { 160 | "internalType": "uint256", 161 | "name": "tokenId", 162 | "type": "uint256" 163 | } 164 | ], 165 | "name": "ownerOf", 166 | "outputs": [ 167 | { 168 | "internalType": "address", 169 | "name": "owner", 170 | "type": "address" 171 | } 172 | ], 173 | "stateMutability": "view", 174 | "type": "function" 175 | }, 176 | { 177 | "inputs": [ 178 | { 179 | "internalType": "address", 180 | "name": "from", 181 | "type": "address" 182 | }, 183 | { 184 | "internalType": "address", 185 | "name": "to", 186 | "type": "address" 187 | }, 188 | { 189 | "internalType": "uint256", 190 | "name": "tokenId", 191 | "type": "uint256" 192 | } 193 | ], 194 | "name": "safeTransferFrom", 195 | "outputs": [], 196 | "stateMutability": "nonpayable", 197 | "type": "function" 198 | }, 199 | { 200 | "inputs": [ 201 | { 202 | "internalType": "address", 203 | "name": "from", 204 | "type": "address" 205 | }, 206 | { 207 | "internalType": "address", 208 | "name": "to", 209 | "type": "address" 210 | }, 211 | { 212 | "internalType": "uint256", 213 | "name": "tokenId", 214 | "type": "uint256" 215 | }, 216 | { 217 | "internalType": "bytes", 218 | "name": "data", 219 | "type": "bytes" 220 | } 221 | ], 222 | "name": "safeTransferFrom", 223 | "outputs": [], 224 | "stateMutability": "nonpayable", 225 | "type": "function" 226 | }, 227 | { 228 | "inputs": [ 229 | { 230 | "internalType": "address", 231 | "name": "operator", 232 | "type": "address" 233 | }, 234 | { 235 | "internalType": "bool", 236 | "name": "_approved", 237 | "type": "bool" 238 | } 239 | ], 240 | "name": "setApprovalForAll", 241 | "outputs": [], 242 | "stateMutability": "nonpayable", 243 | "type": "function" 244 | }, 245 | { 246 | "inputs": [ 247 | { 248 | "internalType": "address", 249 | "name": "from", 250 | "type": "address" 251 | }, 252 | { 253 | "internalType": "address", 254 | "name": "to", 255 | "type": "address" 256 | }, 257 | { 258 | "internalType": "uint256", 259 | "name": "tokenId", 260 | "type": "uint256" 261 | } 262 | ], 263 | "name": "transferFrom", 264 | "outputs": [], 265 | "stateMutability": "nonpayable", 266 | "type": "function" 267 | } 268 | ] 269 | -------------------------------------------------------------------------------- /src/abis/ITransferManager.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "OperatorAlreadyAllowed", 5 | "type": "error" 6 | }, 7 | { 8 | "inputs": [], 9 | "name": "OperatorAlreadyApprovedByUser", 10 | "type": "error" 11 | }, 12 | { 13 | "inputs": [], 14 | "name": "OperatorNotAllowed", 15 | "type": "error" 16 | }, 17 | { 18 | "inputs": [], 19 | "name": "OperatorNotApprovedByUser", 20 | "type": "error" 21 | }, 22 | { 23 | "inputs": [], 24 | "name": "TransferCallerInvalid", 25 | "type": "error" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": false, 32 | "internalType": "address", 33 | "name": "user", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": false, 38 | "internalType": "address[]", 39 | "name": "operators", 40 | "type": "address[]" 41 | } 42 | ], 43 | "name": "ApprovalsGranted", 44 | "type": "event" 45 | }, 46 | { 47 | "anonymous": false, 48 | "inputs": [ 49 | { 50 | "indexed": false, 51 | "internalType": "address", 52 | "name": "user", 53 | "type": "address" 54 | }, 55 | { 56 | "indexed": false, 57 | "internalType": "address[]", 58 | "name": "operators", 59 | "type": "address[]" 60 | } 61 | ], 62 | "name": "ApprovalsRemoved", 63 | "type": "event" 64 | }, 65 | { 66 | "anonymous": false, 67 | "inputs": [ 68 | { 69 | "indexed": false, 70 | "internalType": "address", 71 | "name": "operator", 72 | "type": "address" 73 | } 74 | ], 75 | "name": "OperatorAllowed", 76 | "type": "event" 77 | }, 78 | { 79 | "anonymous": false, 80 | "inputs": [ 81 | { 82 | "indexed": false, 83 | "internalType": "address", 84 | "name": "operator", 85 | "type": "address" 86 | } 87 | ], 88 | "name": "OperatorRemoved", 89 | "type": "event" 90 | } 91 | ] 92 | -------------------------------------------------------------------------------- /src/abis/IWETH.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "deposit", 5 | "outputs": [], 6 | "stateMutability": "payable", 7 | "type": "function" 8 | }, 9 | { 10 | "inputs": [ 11 | { 12 | "internalType": "address", 13 | "name": "dst", 14 | "type": "address" 15 | }, 16 | { 17 | "internalType": "uint256", 18 | "name": "wad", 19 | "type": "uint256" 20 | } 21 | ], 22 | "name": "transfer", 23 | "outputs": [ 24 | { 25 | "internalType": "bool", 26 | "name": "", 27 | "type": "bool" 28 | } 29 | ], 30 | "stateMutability": "nonpayable", 31 | "type": "function" 32 | }, 33 | { 34 | "inputs": [ 35 | { 36 | "internalType": "uint256", 37 | "name": "wad", 38 | "type": "uint256" 39 | } 40 | ], 41 | "name": "withdraw", 42 | "outputs": [], 43 | "stateMutability": "nonpayable", 44 | "type": "function" 45 | } 46 | ] 47 | -------------------------------------------------------------------------------- /src/abis/WETH.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "owner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "spender", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "amount", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Approval", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "from", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": false, 38 | "internalType": "uint256", 39 | "name": "amount", 40 | "type": "uint256" 41 | } 42 | ], 43 | "name": "Deposit", 44 | "type": "event" 45 | }, 46 | { 47 | "anonymous": false, 48 | "inputs": [ 49 | { 50 | "indexed": true, 51 | "internalType": "address", 52 | "name": "from", 53 | "type": "address" 54 | }, 55 | { 56 | "indexed": true, 57 | "internalType": "address", 58 | "name": "to", 59 | "type": "address" 60 | }, 61 | { 62 | "indexed": false, 63 | "internalType": "uint256", 64 | "name": "amount", 65 | "type": "uint256" 66 | } 67 | ], 68 | "name": "Transfer", 69 | "type": "event" 70 | }, 71 | { 72 | "anonymous": false, 73 | "inputs": [ 74 | { 75 | "indexed": true, 76 | "internalType": "address", 77 | "name": "to", 78 | "type": "address" 79 | }, 80 | { 81 | "indexed": false, 82 | "internalType": "uint256", 83 | "name": "amount", 84 | "type": "uint256" 85 | } 86 | ], 87 | "name": "Withdrawal", 88 | "type": "event" 89 | }, 90 | { 91 | "inputs": [], 92 | "name": "DOMAIN_SEPARATOR", 93 | "outputs": [ 94 | { 95 | "internalType": "bytes32", 96 | "name": "", 97 | "type": "bytes32" 98 | } 99 | ], 100 | "stateMutability": "view", 101 | "type": "function" 102 | }, 103 | { 104 | "inputs": [ 105 | { 106 | "internalType": "address", 107 | "name": "", 108 | "type": "address" 109 | }, 110 | { 111 | "internalType": "address", 112 | "name": "", 113 | "type": "address" 114 | } 115 | ], 116 | "name": "allowance", 117 | "outputs": [ 118 | { 119 | "internalType": "uint256", 120 | "name": "", 121 | "type": "uint256" 122 | } 123 | ], 124 | "stateMutability": "view", 125 | "type": "function" 126 | }, 127 | { 128 | "inputs": [ 129 | { 130 | "internalType": "address", 131 | "name": "spender", 132 | "type": "address" 133 | }, 134 | { 135 | "internalType": "uint256", 136 | "name": "amount", 137 | "type": "uint256" 138 | } 139 | ], 140 | "name": "approve", 141 | "outputs": [ 142 | { 143 | "internalType": "bool", 144 | "name": "", 145 | "type": "bool" 146 | } 147 | ], 148 | "stateMutability": "nonpayable", 149 | "type": "function" 150 | }, 151 | { 152 | "inputs": [ 153 | { 154 | "internalType": "address", 155 | "name": "", 156 | "type": "address" 157 | } 158 | ], 159 | "name": "balanceOf", 160 | "outputs": [ 161 | { 162 | "internalType": "uint256", 163 | "name": "", 164 | "type": "uint256" 165 | } 166 | ], 167 | "stateMutability": "view", 168 | "type": "function" 169 | }, 170 | { 171 | "inputs": [], 172 | "name": "decimals", 173 | "outputs": [ 174 | { 175 | "internalType": "uint8", 176 | "name": "", 177 | "type": "uint8" 178 | } 179 | ], 180 | "stateMutability": "view", 181 | "type": "function" 182 | }, 183 | { 184 | "inputs": [], 185 | "name": "deposit", 186 | "outputs": [], 187 | "stateMutability": "payable", 188 | "type": "function" 189 | }, 190 | { 191 | "inputs": [], 192 | "name": "name", 193 | "outputs": [ 194 | { 195 | "internalType": "string", 196 | "name": "", 197 | "type": "string" 198 | } 199 | ], 200 | "stateMutability": "view", 201 | "type": "function" 202 | }, 203 | { 204 | "inputs": [ 205 | { 206 | "internalType": "address", 207 | "name": "", 208 | "type": "address" 209 | } 210 | ], 211 | "name": "nonces", 212 | "outputs": [ 213 | { 214 | "internalType": "uint256", 215 | "name": "", 216 | "type": "uint256" 217 | } 218 | ], 219 | "stateMutability": "view", 220 | "type": "function" 221 | }, 222 | { 223 | "inputs": [ 224 | { 225 | "internalType": "address", 226 | "name": "owner", 227 | "type": "address" 228 | }, 229 | { 230 | "internalType": "address", 231 | "name": "spender", 232 | "type": "address" 233 | }, 234 | { 235 | "internalType": "uint256", 236 | "name": "value", 237 | "type": "uint256" 238 | }, 239 | { 240 | "internalType": "uint256", 241 | "name": "deadline", 242 | "type": "uint256" 243 | }, 244 | { 245 | "internalType": "uint8", 246 | "name": "v", 247 | "type": "uint8" 248 | }, 249 | { 250 | "internalType": "bytes32", 251 | "name": "r", 252 | "type": "bytes32" 253 | }, 254 | { 255 | "internalType": "bytes32", 256 | "name": "s", 257 | "type": "bytes32" 258 | } 259 | ], 260 | "name": "permit", 261 | "outputs": [], 262 | "stateMutability": "nonpayable", 263 | "type": "function" 264 | }, 265 | { 266 | "inputs": [], 267 | "name": "symbol", 268 | "outputs": [ 269 | { 270 | "internalType": "string", 271 | "name": "", 272 | "type": "string" 273 | } 274 | ], 275 | "stateMutability": "view", 276 | "type": "function" 277 | }, 278 | { 279 | "inputs": [], 280 | "name": "totalSupply", 281 | "outputs": [ 282 | { 283 | "internalType": "uint256", 284 | "name": "", 285 | "type": "uint256" 286 | } 287 | ], 288 | "stateMutability": "view", 289 | "type": "function" 290 | }, 291 | { 292 | "inputs": [ 293 | { 294 | "internalType": "address", 295 | "name": "to", 296 | "type": "address" 297 | }, 298 | { 299 | "internalType": "uint256", 300 | "name": "amount", 301 | "type": "uint256" 302 | } 303 | ], 304 | "name": "transfer", 305 | "outputs": [ 306 | { 307 | "internalType": "bool", 308 | "name": "", 309 | "type": "bool" 310 | } 311 | ], 312 | "stateMutability": "nonpayable", 313 | "type": "function" 314 | }, 315 | { 316 | "inputs": [ 317 | { 318 | "internalType": "address", 319 | "name": "from", 320 | "type": "address" 321 | }, 322 | { 323 | "internalType": "address", 324 | "name": "to", 325 | "type": "address" 326 | }, 327 | { 328 | "internalType": "uint256", 329 | "name": "amount", 330 | "type": "uint256" 331 | } 332 | ], 333 | "name": "transferFrom", 334 | "outputs": [ 335 | { 336 | "internalType": "bool", 337 | "name": "", 338 | "type": "bool" 339 | } 340 | ], 341 | "stateMutability": "nonpayable", 342 | "type": "function" 343 | }, 344 | { 345 | "inputs": [ 346 | { 347 | "internalType": "uint256", 348 | "name": "amount", 349 | "type": "uint256" 350 | } 351 | ], 352 | "name": "withdraw", 353 | "outputs": [], 354 | "stateMutability": "nonpayable", 355 | "type": "function" 356 | }, 357 | { 358 | "stateMutability": "payable", 359 | "type": "receive" 360 | } 361 | ] 362 | -------------------------------------------------------------------------------- /src/abis/index.ts: -------------------------------------------------------------------------------- 1 | export { default as IERC1155Abi } from "./IERC1155.json"; 2 | export { default as IERC20Abi } from "./IERC20.json"; 3 | export { default as IERC721Abi } from "./IERC721.json"; 4 | export { default as LooksRareProtocolAbi } from "./LooksRareProtocol.json"; 5 | export { default as TransferManagerAbi } from "./TransferManager.json"; 6 | export { default as OrderValidatorV2AAbi } from "./OrderValidatorV2A.json"; 7 | export { default as WETHAbi } from "./WETH.json"; 8 | -------------------------------------------------------------------------------- /src/constants/addresses.ts: -------------------------------------------------------------------------------- 1 | import { Addresses, ChainId } from "../types"; 2 | 3 | const mainnetAddresses: Addresses = { 4 | LOOKS: "0xf4d2888d29D722226FafA5d9B24F9164c092421E", 5 | EXCHANGE_V2: "0x0000000000E655fAe4d56241588680F86E3b2377", 6 | TRANSFER_MANAGER_V2: "0x000000000060C4Ca14CfC4325359062ace33Fe3D", 7 | WETH: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", 8 | ORDER_VALIDATOR_V2: "0x2a784a5b5C8AE0bd738FBc67E4C069dB4F4961B7", 9 | REVERSE_RECORDS: "0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C", 10 | LOOKS_LP_V3: "0x4b5Ab61593A2401B1075b90c04cBCDD3F87CE011", 11 | STAKING_POOL_FOR_LOOKS_LP: "0x2A70e7F51f6cd40C3E9956aa964137668cBfAdC5", 12 | AGGREGATOR_UNISWAP_V3: "0x3ab16Af1315dc6C95F83Cbf522fecF98D00fd9ba", 13 | }; 14 | 15 | const goerliAddresses: Addresses = { 16 | LOOKS: "0x20A5A36ded0E4101C3688CBC405bBAAE58fE9eeC", 17 | EXCHANGE_V2: "0x35C2215F2FFe8917B06454eEEaba189877F200cf", 18 | TRANSFER_MANAGER_V2: "0xC20E0CeAD98abBBEb626B77efb8Dc1E5D781f90c", 19 | WETH: "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6", 20 | ORDER_VALIDATOR_V2: "0x7454Cc9AEB024bcE6A2CDC49ad4733B4D8215fb8", 21 | REVERSE_RECORDS: "0x333Fc8f550043f239a2CF79aEd5e9cF4A20Eb41e", 22 | LOOKS_LP_V3: "0x87C81267796Cd65347130e789CADdCdAf7bD2231", 23 | STAKING_POOL_FOR_LOOKS_LP: "", 24 | AGGREGATOR_UNISWAP_V3: "0x63c38B3BE3eF075a00a5edaeC36F499088c7334C", 25 | }; 26 | 27 | const sepoliaAddresses: Addresses = { 28 | LOOKS: "0xa68c2CaA3D45fa6EBB95aA706c70f49D3356824E", // @note - not "LOOKS", but a test ERC20 29 | EXCHANGE_V2: "0x34098cc15a8a48Da9d3f31CC0F63F01f9aa3D9F3", 30 | TRANSFER_MANAGER_V2: "0xb46f116ecBa8451E661189F4b2B63aC60a618092", 31 | WETH: "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14", 32 | ORDER_VALIDATOR_V2: "0x0bc129E4c1f8D7b5583eAbAeb1F7468935B6ec0C", 33 | REVERSE_RECORDS: "", 34 | LOOKS_LP_V3: "", 35 | STAKING_POOL_FOR_LOOKS_LP: "", 36 | AGGREGATOR_UNISWAP_V3: "", 37 | }; 38 | 39 | const arbitrumSepoliaAddresses: Addresses = { 40 | LOOKS: "", 41 | EXCHANGE_V2: "", 42 | TRANSFER_MANAGER_V2: "", 43 | WETH: "", 44 | ORDER_VALIDATOR_V2: "", 45 | REVERSE_RECORDS: "", 46 | LOOKS_LP_V3: "", 47 | STAKING_POOL_FOR_LOOKS_LP: "", 48 | AGGREGATOR_UNISWAP_V3: "", 49 | }; 50 | 51 | const arbitrumMainnetAddresses: Addresses = { 52 | LOOKS: "", 53 | EXCHANGE_V2: "", 54 | TRANSFER_MANAGER_V2: "", 55 | WETH: "", 56 | ORDER_VALIDATOR_V2: "", 57 | REVERSE_RECORDS: "", 58 | LOOKS_LP_V3: "", 59 | STAKING_POOL_FOR_LOOKS_LP: "", 60 | AGGREGATOR_UNISWAP_V3: "", 61 | }; 62 | 63 | const baseMainnetAddresses: Addresses = { 64 | LOOKS: "", 65 | EXCHANGE_V2: "", 66 | TRANSFER_MANAGER_V2: "", 67 | WETH: "", 68 | ORDER_VALIDATOR_V2: "", 69 | REVERSE_RECORDS: "", 70 | LOOKS_LP_V3: "", 71 | STAKING_POOL_FOR_LOOKS_LP: "", 72 | AGGREGATOR_UNISWAP_V3: "", 73 | }; 74 | 75 | const baseSepoliaAddresses: Addresses = { 76 | LOOKS: "", 77 | EXCHANGE_V2: "", 78 | TRANSFER_MANAGER_V2: "", 79 | WETH: "", 80 | ORDER_VALIDATOR_V2: "", 81 | REVERSE_RECORDS: "", 82 | LOOKS_LP_V3: "", 83 | STAKING_POOL_FOR_LOOKS_LP: "", 84 | AGGREGATOR_UNISWAP_V3: "", 85 | }; 86 | 87 | const blastSepoliaAddresses: Addresses = { 88 | LOOKS: "", 89 | EXCHANGE_V2: "", 90 | TRANSFER_MANAGER_V2: "", 91 | WETH: "", 92 | ORDER_VALIDATOR_V2: "", 93 | REVERSE_RECORDS: "", 94 | LOOKS_LP_V3: "", 95 | STAKING_POOL_FOR_LOOKS_LP: "", 96 | AGGREGATOR_UNISWAP_V3: "", 97 | }; 98 | 99 | /** 100 | * List of useful contract addresses 101 | */ 102 | export const addressesByNetwork: { [chainId in ChainId]: Addresses } = { 103 | [ChainId.MAINNET]: mainnetAddresses, 104 | [ChainId.GOERLI]: goerliAddresses, 105 | [ChainId.HARDHAT]: goerliAddresses, 106 | [ChainId.SEPOLIA]: sepoliaAddresses, 107 | [ChainId.ARB_SEPOLIA]: arbitrumSepoliaAddresses, 108 | [ChainId.ARB_MAINNET]: arbitrumMainnetAddresses, 109 | [ChainId.BASE_MAINNET]: baseMainnetAddresses, 110 | [ChainId.BASE_SEPOLIA]: baseSepoliaAddresses, 111 | [ChainId.BLAST_SEPOLIA]: blastSepoliaAddresses, 112 | }; 113 | -------------------------------------------------------------------------------- /src/constants/chains.ts: -------------------------------------------------------------------------------- 1 | import { ChainId, ChainInfo } from "../types"; 2 | 3 | export const chainInfo: { [chainId in ChainId]: ChainInfo } = { 4 | [ChainId.MAINNET]: { 5 | label: "Ethereum", 6 | appUrl: "https://looksrare.org", 7 | explorer: "https://etherscan.io", 8 | rpcUrl: "https://eth-mainnet.g.alchemy.com/v2", 9 | baseApiUrl: "https://graphql.looksrare.org", 10 | osApiUrl: "https://api.opensea.io", 11 | cdnUrl: "https://static.looksnice.org", 12 | rewardsSubgraphUrl: "https://api.thegraph.com/subgraphs/name/looksrare/looks-distribution", 13 | cloudinaryUrl: "https://looksrare.mo.cloudinary.net", 14 | wsUrl: "wss://ws.looksrare.org/ws", 15 | }, 16 | [ChainId.GOERLI]: { 17 | label: "Goerli", 18 | appUrl: "https://goerli.looksrare.org", 19 | explorer: "https://goerli.etherscan.io", 20 | rpcUrl: "https://eth-goerli.g.alchemy.com/v2", 21 | baseApiUrl: "https://graphql-goerli.looksrare.org", 22 | osApiUrl: "https://testnets-api.opensea.io", 23 | cdnUrl: "https://static-goerli.looksnice.org", 24 | rewardsSubgraphUrl: "https://api.thegraph.com/subgraphs/name/0xjurassicpunk/looks-distribution", 25 | cloudinaryUrl: "https://looksrare.mo.cloudinary.net/goerli", 26 | wsUrl: "wss://ws-goerli.looksrare.org/ws", 27 | }, 28 | [ChainId.SEPOLIA]: { 29 | label: "Sepolia", 30 | appUrl: "https://sepolia.looksrare.org", 31 | explorer: "https://sepolia.etherscan.io", 32 | rpcUrl: "https://eth-sepolia.g.alchemy.com/v2", 33 | baseApiUrl: "https://graphql-sepolia.looksrare.org", 34 | osApiUrl: "https://testnets-api.opensea.io", 35 | cdnUrl: "https://static-sepolia.looksnice.org", 36 | rewardsSubgraphUrl: "https://api.thegraph.com/subgraphs/name/0xjurassicpunk/looks-distribution", 37 | cloudinaryUrl: "https://looksrare.mo.cloudinary.net/sepolia", 38 | wsUrl: "wss://ws-sepolia.looksrare.org/ws", 39 | }, 40 | [ChainId.ARB_MAINNET]: { 41 | label: "Arbitrum", 42 | appUrl: "https://looksrare.org", 43 | explorer: "https://arbiscan.io", 44 | rpcUrl: "https://arb-mainnet.g.alchemy.com/v2", 45 | baseApiUrl: "https://graphql.looksrare.org", 46 | osApiUrl: "https://api.opensea.io", 47 | cdnUrl: "https://static.looksnice.org", 48 | rewardsSubgraphUrl: "https://api.thegraph.com/subgraphs/name/looksrare/looks-distribution", 49 | cloudinaryUrl: "https://looksrare.mo.cloudinary.net", 50 | wsUrl: "wss://ws.looksrare.org/ws", 51 | }, 52 | [ChainId.ARB_SEPOLIA]: { 53 | label: "Arbitrum Sepolia", 54 | appUrl: "https://sepolia.looksrare.org", 55 | explorer: "https://sepolia.arbiscan.io", 56 | rpcUrl: "https://arb-sepolia.g.alchemy.com/v2", 57 | baseApiUrl: "https://graphql-sepolia.looksrare.org", 58 | osApiUrl: "https://testnets-api.opensea.io", 59 | cdnUrl: "https://static-sepolia.looksnice.org", 60 | rewardsSubgraphUrl: "https://api.thegraph.com/subgraphs/name/0xjurassicpunk/looks-distribution", 61 | cloudinaryUrl: "https://looksrare.mo.cloudinary.net/sepolia", 62 | wsUrl: "wss://ws-sepolia.looksrare.org/ws", 63 | }, 64 | [ChainId.BASE_MAINNET]: { 65 | label: "Base Mainnet", 66 | appUrl: "https://looksrare.org", 67 | explorer: "https://basescan.org", 68 | rpcUrl: "https://base-mainnet.g.alchemy.com/v2", 69 | baseApiUrl: "https://graphql.looksrare.org", 70 | osApiUrl: "https://api.opensea.io", 71 | cdnUrl: "https://static.looksnice.org", 72 | rewardsSubgraphUrl: "https://api.thegraph.com/subgraphs/name/looksrare/looks-distribution", 73 | cloudinaryUrl: "https://looksrare.mo.cloudinary.net", 74 | wsUrl: "wss://ws.looksrare.org/ws", 75 | }, 76 | [ChainId.BASE_SEPOLIA]: { 77 | label: "Base Sepolia", 78 | appUrl: "https://sepolia.looksrare.org", 79 | explorer: "https://sepolia-explorer.base.org", 80 | rpcUrl: "https://base-sepolia.g.alchemy.com/v2", 81 | baseApiUrl: "https://graphql-sepolia.looksrare.org", 82 | osApiUrl: "https://testnets-api.opensea.io", 83 | cdnUrl: "https://static-sepolia.looksnice.org", 84 | rewardsSubgraphUrl: "https://api.thegraph.com/subgraphs/name/0xjurassicpunk/looks-distribution", 85 | cloudinaryUrl: "https://looksrare.mo.cloudinary.net/sepolia", 86 | wsUrl: "wss://ws-sepolia.looksrare.org/ws", 87 | }, 88 | [ChainId.HARDHAT]: { 89 | label: "Hardhat", 90 | appUrl: "http://localhost:3000", 91 | explorer: "https://etherscan.io", 92 | rpcUrl: "http://127.0.0.1:8545", 93 | baseApiUrl: "http://localhost:4000", 94 | osApiUrl: "https://testnets-api.opensea.io", 95 | cdnUrl: "https://via.placeholder.com", 96 | rewardsSubgraphUrl: "https://api.thegraph.com/subgraphs/name/0xjurassicpunk/looks-distribution", 97 | cloudinaryUrl: "", 98 | wsUrl: "ws://localhost:5001/ws", 99 | }, 100 | [ChainId.BLAST_SEPOLIA]: { 101 | label: "Blast Sepolia", 102 | appUrl: "https://sepolia.looksrare.org", 103 | explorer: "https://testnet.blastscan.io/", 104 | rpcUrl: "https://sepolia.blast.io", 105 | baseApiUrl: "https://graphql-sepolia.looksrare.org", 106 | osApiUrl: "https://testnets-api.opensea.io", 107 | cdnUrl: "https://static-sepolia.looksnice.org", 108 | rewardsSubgraphUrl: "https://api.thegraph.com/subgraphs/name/0xjurassicpunk/looks-distribution", 109 | cloudinaryUrl: "https://looksrare.mo.cloudinary.net/sepolia", 110 | wsUrl: "wss://ws-sepolia.looksrare.org/ws", 111 | }, 112 | [ChainId.BLAST_MAINNET]: { 113 | appUrl: "https://yologames.io", 114 | baseApiUrl: "https://graphql.yologames.io", 115 | // For potential backwards compatibility of asset urls 116 | // use looksnice. Important to note this should not actually be called. 117 | cdnUrl: "https://static.looksnice.org", 118 | cloudinaryUrl: "https://looksrare.mo.cloudinary.net", 119 | explorer: "https://blastscan.io", 120 | label: "Blast", 121 | osApiUrl: "", // N/A 122 | rewardsSubgraphUrl: "", // N/A 123 | rpcUrl: "https://rpc.blast.io", 124 | wsUrl: "wss://ws.yologames.io/ws", 125 | }, 126 | }; 127 | -------------------------------------------------------------------------------- /src/constants/eip712.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Name of the LooksRare contract. Used for EIP712 domain separator. 3 | * @see {@link https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator EIP712 doc} 4 | */ 5 | export const contractName = "LooksRareProtocol"; 6 | 7 | /** 8 | * Protocol version. Used for EIP712 domain separator. 9 | * @see {@link https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator EIP712 doc} 10 | */ 11 | export const version = 2; 12 | -------------------------------------------------------------------------------- /src/constants/index.ts: -------------------------------------------------------------------------------- 1 | import { ZeroHash } from "ethers"; 2 | 3 | /** Maximum amount of orders in a merkle tree 4 | * This limit is enforced in the contract with the value 2^10 (=1024) 5 | * @see {@link https://github.com/LooksRare/contracts-exchange-v2/blob/master/contracts/constants/NumericConstants.sol#L7} 6 | */ 7 | export const MAX_ORDERS_PER_TREE = 1024; 8 | 9 | /** 10 | * Default merkle tree value used when the merkle tree can be omitted. 11 | */ 12 | export const defaultMerkleTree = { root: ZeroHash, proof: [] }; 13 | 14 | export { addressesByNetwork } from "./addresses"; 15 | export { chainInfo } from "./chains"; 16 | -------------------------------------------------------------------------------- /src/contracts/LooksRareProtocol.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "@looksrare/contracts-exchange-v2/contracts/LooksRareProtocol.sol"; 5 | -------------------------------------------------------------------------------- /src/contracts/OrderValidatorV2A.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "@looksrare/contracts-exchange-v2/contracts/helpers/OrderValidatorV2A.sol"; 5 | -------------------------------------------------------------------------------- /src/contracts/TransferManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "@looksrare/contracts-exchange-v2/contracts/TransferManager.sol"; 5 | -------------------------------------------------------------------------------- /src/contracts/tests/CreatorFeeManagerWithRoyalties.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "@looksrare/contracts-exchange-v2/contracts/CreatorFeeManagerWithRoyalties.sol"; 5 | -------------------------------------------------------------------------------- /src/contracts/tests/MockERC1155.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "solmate/src/tokens/ERC1155.sol"; 5 | 6 | contract MockERC1155 is ERC1155 { 7 | function mint( 8 | address to, 9 | uint256 tokenId, 10 | uint256 amount 11 | ) public { 12 | _mint(to, tokenId, amount, ""); 13 | } 14 | 15 | function uri(uint256) public pure override returns (string memory) { 16 | return "tokenURI"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/contracts/tests/MockERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "solmate/src/tokens/ERC721.sol"; 5 | 6 | contract MockERC721 is ERC721 { 7 | uint256 public currentTokenId; 8 | 9 | constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) { 10 | currentTokenId = 0; 11 | } 12 | 13 | function mint(address to) public { 14 | _mint(to, currentTokenId++); 15 | } 16 | 17 | function tokenURI(uint256) public pure override returns (string memory) { 18 | return "tokenURI"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/contracts/tests/MockRoyaltyFeeRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "@looksrare/contracts-exchange-v2/test/mock/MockRoyaltyFeeRegistry.sol"; 5 | -------------------------------------------------------------------------------- /src/contracts/tests/StrategyCollectionOffer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "@looksrare/contracts-exchange-v2/contracts/executionStrategies/StrategyCollectionOffer.sol"; 5 | -------------------------------------------------------------------------------- /src/contracts/tests/StrategyManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "@looksrare/contracts-exchange-v2/contracts/StrategyManager.sol"; 5 | -------------------------------------------------------------------------------- /src/contracts/tests/Verifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "@looksrare/contracts-exchange-v2/contracts/LooksRareProtocol.sol"; 5 | import "@looksrare/contracts-exchange-v2/contracts/helpers/ProtocolHelpers.sol"; 6 | 7 | contract Verifier is ProtocolHelpers { 8 | using OrderStructs for OrderStructs.Maker; 9 | using OrderStructs for OrderStructs.MerkleTree; 10 | using OrderStructs for bytes32; 11 | 12 | constructor(address _looksRareProtocol) ProtocolHelpers(_looksRareProtocol) {} 13 | 14 | function getDomainSeparator() public view returns (bytes32) { 15 | bytes32 domainSeparator = looksRareProtocol.domainSeparator(); 16 | return domainSeparator; 17 | } 18 | 19 | function getMakerHash(OrderStructs.Maker memory maker) public pure returns (bytes32 orderHash) { 20 | return maker.hash(); 21 | } 22 | 23 | function verifySignature(OrderStructs.Maker calldata maker, bytes calldata signature) external view { 24 | verifyMakerSignature(maker, signature, maker.signer); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/contracts/tests/WETH.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "solmate/src/tokens/WETH.sol"; 5 | -------------------------------------------------------------------------------- /src/errors.ts: -------------------------------------------------------------------------------- 1 | import { MAX_ORDERS_PER_TREE } from "./constants"; 2 | 3 | /** Invalid timestamp format */ 4 | export class ErrorTimestamp extends Error { 5 | public readonly name = "ErrorTimestamp"; 6 | constructor() { 7 | super("Timestamps should be in seconds"); 8 | } 9 | } 10 | 11 | /** Undefined signer */ 12 | export class ErrorSigner extends Error { 13 | public readonly name = "ErrorSigner"; 14 | constructor() { 15 | super("Signer is undefined"); 16 | } 17 | } 18 | 19 | /** Too many orders in one merkle tree */ 20 | export class ErrorMerkleTreeDepth extends Error { 21 | public readonly name = "ErrorMerkleTreeDepth"; 22 | constructor() { 23 | super(`Too many orders (limit: ${MAX_ORDERS_PER_TREE})`); 24 | } 25 | } 26 | 27 | /** Wrong quote type */ 28 | export class ErrorQuoteType extends Error { 29 | public readonly name = "ErrorQuoteType"; 30 | constructor() { 31 | super("Wrong quote type"); 32 | } 33 | } 34 | 35 | /** Wrong strategy type */ 36 | export class ErrorStrategyType extends Error { 37 | public readonly name = "ErrorStrategyType"; 38 | constructor() { 39 | super("Wrong strategy type"); 40 | } 41 | } 42 | 43 | /** Invalid item ID for orders with proof */ 44 | export class ErrorItemId extends Error { 45 | public readonly name = "ErrorItemId"; 46 | constructor() { 47 | super("Item id is not in the list"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as tokens from "./utils/calls/tokens"; 2 | import * as exchange from "./utils/calls/exchange"; 3 | import * as nonces from "./utils/calls/nonces"; 4 | import * as transferManager from "./utils/calls/transferManager"; 5 | import * as orderValidator from "./utils/calls/orderValidator"; 6 | import * as strategies from "./utils/calls/strategies"; 7 | import * as encode from "./utils/encodeOrderParams"; 8 | import * as signMakerOrders from "./utils/signMakerOrders"; 9 | import * as eip712 from "./utils/eip712"; 10 | const utils = { 11 | ...tokens, 12 | ...exchange, 13 | ...nonces, 14 | ...transferManager, 15 | ...orderValidator, 16 | ...strategies, 17 | ...encode, 18 | ...signMakerOrders, 19 | ...eip712, 20 | }; 21 | export { utils }; 22 | 23 | export * from "./constants"; 24 | export * from "./errors"; 25 | export * from "./types"; 26 | export * from "./abis"; 27 | 28 | export { Eip712MakerMerkleTree } from "./utils/Eip712MakerMerkleTree"; 29 | export { Eip712MerkleTree } from "./utils/Eip712MerkleTree"; 30 | 31 | export { LooksRare } from "./LooksRare"; 32 | -------------------------------------------------------------------------------- /src/utils/Eip712MakerMerkleTree.ts: -------------------------------------------------------------------------------- 1 | import { ZeroAddress } from "ethers"; 2 | import { Eip712MerkleTree } from "./Eip712MerkleTree"; 3 | import { getBatchOrderTypes } from "./eip712"; 4 | import { Maker, QuoteType, StrategyType, CollectionType } from "../types"; 5 | 6 | const defaultMaker: Maker = { 7 | quoteType: QuoteType.Bid, 8 | globalNonce: 0, 9 | subsetNonce: 0, 10 | orderNonce: 0, 11 | strategyId: StrategyType.standard, 12 | collectionType: CollectionType.ERC721, 13 | collection: ZeroAddress, 14 | currency: ZeroAddress, 15 | signer: ZeroAddress, 16 | startTime: 0, 17 | endTime: 0, 18 | price: 0, 19 | itemIds: [0], 20 | amounts: [0], 21 | additionalParameters: "0x", 22 | }; 23 | 24 | /** 25 | * Specific implementation of Eip712MerkleTree for Maker type 26 | * @extends Eip712MerkleTree 27 | */ 28 | export class Eip712MakerMerkleTree extends Eip712MerkleTree { 29 | constructor(public makerOrders: Maker[]) { 30 | const height = Eip712MerkleTree.getTreeHeight(makerOrders.length); 31 | const types = getBatchOrderTypes(height); 32 | 33 | super(types, "Maker", defaultMaker, makerOrders, height); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/utils/Eip712MerkleTree.ts: -------------------------------------------------------------------------------- 1 | import { TypedDataEncoder } from "ethers"; 2 | import { keccak256, BytesLike } from "ethers"; 3 | import { MerkleTree } from "merkletreejs"; 4 | import { EIP712TypedData } from "../types"; 5 | 6 | type BatchOrderElements = [T, T] | [BatchOrderElements, BatchOrderElements]; 7 | 8 | const hexToBuffer = (value: string) => Buffer.from(value.slice(2), "hex"); 9 | 10 | const chunk = (array: T[], size: number): T[][] => 11 | Array(Math.ceil(array.length / size)) 12 | .fill(0) 13 | .map((_, i) => array.slice(i * size, (i + 1) * size)); 14 | 15 | const fillArray = (arr: T[], length: number, value: T): T[] => { 16 | const arrCopy = [...arr]; 17 | if (length > arr.length) { 18 | arrCopy.push(...Array(length - arr.length).fill(value)); 19 | } 20 | return arrCopy; 21 | }; 22 | 23 | /** 24 | * Generic wrapper around MerkleTreeJS to support EIP signing 25 | */ 26 | export class Eip712MerkleTree = any> { 27 | public tree: MerkleTree; 28 | private completeLeaves: string[]; 29 | private completeElements: BaseType[]; 30 | 31 | /** 32 | * Compute the tree height 33 | * @param length length of the array of elements 34 | * @returns height as a number 35 | */ 36 | static getTreeHeight = (length: number): number => Math.max(Math.ceil(Math.log2(length)), 1); 37 | 38 | /** 39 | * 40 | * @param types EIP712 Typed data 41 | * @param leafType Leaf type used in the Typed data 42 | * @param defaultNode Dummy node 43 | * @param elements Array of data blocks 44 | * @param depth tree depth 45 | */ 46 | constructor( 47 | public types: EIP712TypedData, 48 | public leafType: string, 49 | defaultNode: BaseType, 50 | public elements: BaseType[], 51 | public depth: number 52 | ) { 53 | const encoder = TypedDataEncoder.from(types); 54 | const leafHasher = (leaf: BaseType) => encoder.hashStruct(leafType, leaf); 55 | 56 | const leaves = this.elements.map(leafHasher); 57 | const defaultLeaf = leafHasher(defaultNode); 58 | 59 | this.completeLeaves = fillArray(leaves, this.completedSize, defaultLeaf); 60 | this.completeElements = fillArray(elements, this.completedSize, defaultNode); 61 | 62 | this.tree = new MerkleTree( 63 | this.completeLeaves.map(hexToBuffer), 64 | (value: BytesLike) => hexToBuffer(keccak256(value)), 65 | { 66 | sort: false, 67 | hashLeaves: false, 68 | fillDefaultHash: hexToBuffer(defaultLeaf), 69 | } 70 | ); 71 | } 72 | 73 | get completedSize() { 74 | return Math.pow(2, this.depth); 75 | } 76 | 77 | get hexRoot() { 78 | return this.tree.getHexRoot(); 79 | } 80 | 81 | public getPositionalProof(i: number) { 82 | const leaf = this.completeLeaves[i]; 83 | const proof = this.tree.getPositionalHexProof(leaf, i); 84 | return { leaf, proof }; 85 | } 86 | 87 | public getDataToSign(): { tree: BatchOrderElements } { 88 | let layer = this.completeElements as any; 89 | while (layer.length > 2) { 90 | layer = chunk(layer, 2); 91 | } 92 | return { tree: layer }; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/utils/calls/exchange.ts: -------------------------------------------------------------------------------- 1 | import { Contract, ZeroAddress, Signer } from "ethers"; 2 | import { LooksRareProtocol } from "../../typechain/@looksrare/contracts-exchange-v2/contracts/LooksRareProtocol"; 3 | import abiLooksRareProtocol from "../../abis/LooksRareProtocol.json"; 4 | import { Maker, MerkleTree, Taker, ContractMethods } from "../../types"; 5 | import { PayableOverrides } from "../../typechain/common"; 6 | 7 | export const executeTakerBid = ( 8 | signer: Signer, 9 | address: string, 10 | taker: Taker, 11 | maker: Maker, 12 | makerSignature: string, 13 | merkleTree: MerkleTree, 14 | affiliate: string, 15 | overrides?: PayableOverrides 16 | ): ContractMethods => { 17 | const overridesWithValue: PayableOverrides = { 18 | ...overrides, 19 | ...(maker.currency === ZeroAddress && { value: maker.price }), 20 | }; 21 | const contract = new Contract(address, abiLooksRareProtocol).connect(signer) as LooksRareProtocol; 22 | return { 23 | call: (additionalOverrides?: PayableOverrides) => 24 | contract.executeTakerBid.send(taker, maker, makerSignature, merkleTree, affiliate, { 25 | ...overridesWithValue, 26 | ...additionalOverrides, 27 | }), 28 | estimateGas: (additionalOverrides?: PayableOverrides) => 29 | contract.executeTakerBid.estimateGas(taker, maker, makerSignature, merkleTree, affiliate, { 30 | ...overridesWithValue, 31 | ...additionalOverrides, 32 | }), 33 | callStatic: (additionalOverrides?: PayableOverrides) => 34 | contract.executeTakerBid.staticCall(taker, maker, makerSignature, merkleTree, affiliate, { 35 | ...overridesWithValue, 36 | ...additionalOverrides, 37 | }), 38 | }; 39 | }; 40 | 41 | export const executeTakerAsk = ( 42 | signer: Signer, 43 | address: string, 44 | taker: Taker, 45 | maker: Maker, 46 | makerSignature: string, 47 | merkleTree: MerkleTree, 48 | affiliate: string, 49 | overrides?: PayableOverrides 50 | ): ContractMethods => { 51 | const contract = new Contract(address, abiLooksRareProtocol).connect(signer) as LooksRareProtocol; 52 | return { 53 | call: (additionalOverrides?: PayableOverrides) => 54 | contract.executeTakerAsk.send(taker, maker, makerSignature, merkleTree, affiliate, { 55 | ...overrides, 56 | ...additionalOverrides, 57 | }), 58 | estimateGas: (additionalOverrides?: PayableOverrides) => 59 | contract.executeTakerAsk.estimateGas(taker, maker, makerSignature, merkleTree, affiliate, { 60 | ...overrides, 61 | ...additionalOverrides, 62 | }), 63 | callStatic: (additionalOverrides?: PayableOverrides) => 64 | contract.executeTakerAsk.staticCall(taker, maker, makerSignature, merkleTree, affiliate, { 65 | ...overrides, 66 | ...additionalOverrides, 67 | }), 68 | }; 69 | }; 70 | 71 | export const executeMultipleTakerBids = ( 72 | signer: Signer, 73 | address: string, 74 | taker: Taker[], 75 | maker: Maker[], 76 | makerSignature: string[], 77 | isAtomic: boolean, 78 | merkleTree: MerkleTree[], 79 | affiliate: string, 80 | overrides?: PayableOverrides 81 | ) => { 82 | const value = maker.reduce((acc, order) => (order.currency === ZeroAddress ? acc + BigInt(order.price) : acc), 0n); 83 | const overridesWithValue: PayableOverrides = { 84 | ...overrides, 85 | ...(value > 0 && { value }), 86 | }; 87 | const contract = new Contract(address, abiLooksRareProtocol).connect(signer) as LooksRareProtocol; 88 | return { 89 | call: (additionalOverrides?: PayableOverrides) => 90 | contract.executeMultipleTakerBids.send(taker, maker, makerSignature, merkleTree, affiliate, isAtomic, { 91 | ...overridesWithValue, 92 | ...additionalOverrides, 93 | }), 94 | estimateGas: (additionalOverrides?: PayableOverrides) => 95 | contract.executeMultipleTakerBids.estimateGas(taker, maker, makerSignature, merkleTree, affiliate, isAtomic, { 96 | ...overridesWithValue, 97 | ...additionalOverrides, 98 | }), 99 | callStatic: (additionalOverrides?: PayableOverrides) => 100 | contract.executeMultipleTakerBids.staticCall(taker, maker, makerSignature, merkleTree, affiliate, isAtomic, { 101 | ...overridesWithValue, 102 | ...additionalOverrides, 103 | }), 104 | }; 105 | }; 106 | -------------------------------------------------------------------------------- /src/utils/calls/nonces.ts: -------------------------------------------------------------------------------- 1 | import { Contract, Overrides, BigNumberish, Provider, Signer } from "ethers"; 2 | import { LooksRareProtocol } from "../../typechain/@looksrare/contracts-exchange-v2/contracts/LooksRareProtocol"; 3 | import abi from "../../abis/LooksRareProtocol.json"; 4 | import { ContractMethods } from "../../types"; 5 | 6 | export const viewUserBidAskNonces = async ( 7 | signerOrProvider: Provider | Signer, 8 | address: string, 9 | account: string, 10 | overrides?: Overrides 11 | ): Promise<{ bidNonce: bigint; askNonce: bigint }> => { 12 | const contract = new Contract(address, abi).connect(signerOrProvider) as LooksRareProtocol; 13 | const nonces = await contract.userBidAskNonces(account, { ...overrides }); 14 | return { bidNonce: nonces[0], askNonce: nonces[1] }; 15 | }; 16 | 17 | export const cancelOrderNonces = ( 18 | signer: Signer, 19 | address: string, 20 | nonces: BigNumberish[], 21 | overrides?: Overrides 22 | ): ContractMethods => { 23 | const contract = new Contract(address, abi).connect(signer) as LooksRareProtocol; 24 | return { 25 | call: (additionalOverrides?: Overrides) => 26 | contract.cancelOrderNonces.send(nonces, { ...overrides, ...additionalOverrides }), 27 | estimateGas: (additionalOverrides?: Overrides) => 28 | contract.cancelOrderNonces.estimateGas(nonces, { ...overrides, ...additionalOverrides }), 29 | callStatic: (additionalOverrides?: Overrides) => 30 | contract.cancelOrderNonces.staticCall(nonces, { ...overrides, ...additionalOverrides }), 31 | }; 32 | }; 33 | 34 | export const cancelSubsetNonces = ( 35 | signer: Signer, 36 | address: string, 37 | nonces: BigNumberish[], 38 | overrides?: Overrides 39 | ): ContractMethods => { 40 | const contract = new Contract(address, abi).connect(signer) as LooksRareProtocol; 41 | return { 42 | call: (additionalOverrides?: Overrides) => 43 | contract.cancelSubsetNonces.send(nonces, { ...overrides, ...additionalOverrides }), 44 | estimateGas: (additionalOverrides?: Overrides) => 45 | contract.cancelSubsetNonces.estimateGas(nonces, { ...overrides, ...additionalOverrides }), 46 | callStatic: (additionalOverrides?: Overrides) => 47 | contract.cancelSubsetNonces.staticCall(nonces, { ...overrides, ...additionalOverrides }), 48 | }; 49 | }; 50 | 51 | export const incrementBidAskNonces = ( 52 | signer: Signer, 53 | address: string, 54 | bid: boolean, 55 | ask: boolean, 56 | overrides?: Overrides 57 | ): ContractMethods => { 58 | const contract = new Contract(address, abi).connect(signer) as LooksRareProtocol; 59 | return { 60 | call: (additionalOverrides?: Overrides) => 61 | contract.incrementBidAskNonces.send(bid, ask, { ...overrides, ...additionalOverrides }), 62 | estimateGas: (additionalOverrides?: Overrides) => 63 | contract.incrementBidAskNonces.estimateGas(bid, ask, { ...overrides, ...additionalOverrides }), 64 | callStatic: (additionalOverrides?: Overrides) => 65 | contract.incrementBidAskNonces.staticCall(bid, ask, { ...overrides, ...additionalOverrides }), 66 | }; 67 | }; 68 | -------------------------------------------------------------------------------- /src/utils/calls/orderValidator.ts: -------------------------------------------------------------------------------- 1 | import { Contract, Provider, Overrides, Signer } from "ethers"; 2 | import { OrderValidatorV2A } from "../../typechain/@looksrare/contracts-exchange-v2/contracts/helpers/OrderValidatorV2A"; 3 | import abi from "../../abis/OrderValidatorV2A.json"; 4 | import { Maker, MerkleTree, OrderValidatorCode } from "../../types"; 5 | 6 | export const verifyMakerOrders = async ( 7 | signerOrProvider: Provider | Signer, 8 | address: string, 9 | makerOrders: Maker[], 10 | signatures: string[], 11 | merkleTrees: MerkleTree[], 12 | overrides?: Overrides 13 | ): Promise => { 14 | const contract = new Contract(address, abi).connect(signerOrProvider) as OrderValidatorV2A; 15 | const orders = await contract.checkMultipleMakerOrderValidities(makerOrders, signatures, merkleTrees, { 16 | ...overrides, 17 | }); 18 | return orders.map((order) => order.map((code) => Number(code) as OrderValidatorCode)); 19 | }; 20 | -------------------------------------------------------------------------------- /src/utils/calls/strategies.ts: -------------------------------------------------------------------------------- 1 | import { Contract, Overrides, Provider, Signer } from "ethers"; 2 | import { LooksRareProtocol } from "../../typechain/@looksrare/contracts-exchange-v2/contracts/LooksRareProtocol"; 3 | import abi from "../../abis/LooksRareProtocol.json"; 4 | import { StrategyType, StrategyInfo } from "../../types"; 5 | 6 | export const strategyInfo = async ( 7 | signerOrProvider: Provider | Signer, 8 | address: string, 9 | strategyId: StrategyType, 10 | overrides?: Overrides 11 | ): Promise => { 12 | const contract = new Contract(address, abi).connect(signerOrProvider) as LooksRareProtocol; 13 | const strategy = await contract.strategyInfo(strategyId, { ...overrides }); 14 | return { 15 | isActive: strategy.isActive, 16 | standardProtocolFeeBp: Number(strategy.standardProtocolFeeBp), 17 | minTotalFeeBp: Number(strategy.minTotalFeeBp), 18 | maxProtocolFeeBp: Number(strategy.maxProtocolFeeBp), 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /src/utils/calls/tokens.ts: -------------------------------------------------------------------------------- 1 | import { Contract, Provider, Overrides, ContractTransactionResponse, Signer } from "ethers"; 2 | import { ERC721 } from "../../typechain/solmate/src/tokens/ERC721.sol/ERC721"; 3 | import { ERC20 } from "../../typechain/solmate/src/tokens/ERC20"; 4 | import abiIERC721 from "../../abis/IERC721.json"; 5 | import abiIERC20 from "../../abis/IERC20.json"; 6 | 7 | // ER721 and ERC1155 8 | 9 | export const setApprovalForAll = ( 10 | signer: Signer, 11 | collection: string, 12 | operator: string, 13 | approved: boolean, 14 | overrides?: Overrides 15 | ): Promise => { 16 | const contract = new Contract(collection, abiIERC721).connect(signer) as ERC721; 17 | return contract.setApprovalForAll(operator, approved, { ...overrides }); 18 | }; 19 | 20 | export const isApprovedForAll = ( 21 | signerOrProvider: Provider | Signer, 22 | collection: string, 23 | account: string, 24 | operator: string, 25 | overrides?: Overrides 26 | ): Promise => { 27 | const contract = new Contract(collection, abiIERC721).connect(signerOrProvider) as ERC721; 28 | return contract.isApprovedForAll(account, operator, { ...overrides }); 29 | }; 30 | 31 | // ERC20 32 | 33 | export const allowance = ( 34 | signerOrProvider: Provider | Signer, 35 | currency: string, 36 | account: string, 37 | operator: string, 38 | overrides?: Overrides 39 | ): Promise => { 40 | const contract = new Contract(currency, abiIERC20).connect(signerOrProvider) as ERC20; 41 | return contract.allowance(account, operator, { ...overrides }); 42 | }; 43 | 44 | export const approve = ( 45 | signer: Signer, 46 | currency: string, 47 | operator: string, 48 | amount: bigint, 49 | overrides?: Overrides 50 | ): Promise => { 51 | const contract = new Contract(currency, abiIERC20).connect(signer) as ERC20; 52 | return contract.approve(operator, amount, { ...overrides }); 53 | }; 54 | 55 | export const balanceOf = ( 56 | signerOrProvider: Provider | Signer, 57 | currency: string, 58 | account: string, 59 | overrides?: Overrides 60 | ): Promise => { 61 | const contract = new Contract(currency, abiIERC20).connect(signerOrProvider) as ERC20; 62 | return contract.balanceOf(account, { ...overrides }); 63 | }; 64 | -------------------------------------------------------------------------------- /src/utils/calls/transferManager.ts: -------------------------------------------------------------------------------- 1 | import { Contract, Overrides, Provider, Signer } from "ethers"; 2 | import { TransferManager } from "../../typechain/@looksrare/contracts-exchange-v2/contracts/TransferManager"; 3 | import abi from "../../abis/TransferManager.json"; 4 | import { ContractMethods, BatchTransferItem } from "../../types"; 5 | 6 | export const hasUserApprovedOperator = async ( 7 | signerOrProvider: Provider | Signer, 8 | address: string, 9 | user: string, 10 | operator: string, 11 | overrides?: Overrides 12 | ): Promise => { 13 | const contract = new Contract(address, abi).connect(signerOrProvider) as TransferManager; 14 | const hasApproved = await contract.hasUserApprovedOperator(user, operator, { ...overrides }); 15 | return hasApproved; 16 | }; 17 | 18 | export const grantApprovals = ( 19 | signer: Signer, 20 | address: string, 21 | operators: string[], 22 | overrides?: Overrides 23 | ): ContractMethods => { 24 | const contract = new Contract(address, abi).connect(signer) as TransferManager; 25 | return { 26 | call: (additionalOverrides?: Overrides) => 27 | contract.grantApprovals.send(operators, { ...overrides, ...additionalOverrides }), 28 | estimateGas: (additionalOverrides?: Overrides) => 29 | contract.grantApprovals.estimateGas(operators, { ...overrides, ...additionalOverrides }), 30 | callStatic: (additionalOverrides?: Overrides) => 31 | contract.grantApprovals.staticCall(operators, { ...overrides, ...additionalOverrides }), 32 | }; 33 | }; 34 | 35 | export const revokeApprovals = ( 36 | signer: Signer, 37 | address: string, 38 | operators: string[], 39 | overrides?: Overrides 40 | ): ContractMethods => { 41 | const contract = new Contract(address, abi).connect(signer) as TransferManager; 42 | return { 43 | call: (additionalOverrides?: Overrides) => 44 | contract.revokeApprovals.send(operators, { ...overrides, ...additionalOverrides }), 45 | estimateGas: (additionalOverrides?: Overrides) => 46 | contract.revokeApprovals.estimateGas(operators, { ...overrides, ...additionalOverrides }), 47 | callStatic: (additionalOverrides?: Overrides) => 48 | contract.revokeApprovals.staticCall(operators, { ...overrides, ...additionalOverrides }), 49 | }; 50 | }; 51 | 52 | export const transferBatchItemsAcrossCollections = ( 53 | signer: Signer, 54 | address: string, 55 | items: BatchTransferItem[], 56 | from: string, 57 | to: string, 58 | overrides?: Overrides 59 | ): ContractMethods => { 60 | const contract = new Contract(address, abi).connect(signer) as TransferManager; 61 | return { 62 | call: (additionalOverrides?: Overrides) => 63 | contract.transferBatchItemsAcrossCollections.send(items, from, to, { 64 | ...overrides, 65 | ...additionalOverrides, 66 | }), 67 | estimateGas: (additionalOverrides?: Overrides) => 68 | contract.transferBatchItemsAcrossCollections.estimateGas(items, from, to, { 69 | ...overrides, 70 | ...additionalOverrides, 71 | }), 72 | callStatic: (additionalOverrides?: Overrides) => 73 | contract.transferBatchItemsAcrossCollections.staticCall(items, from, to, { 74 | ...overrides, 75 | ...additionalOverrides, 76 | }), 77 | }; 78 | }; 79 | -------------------------------------------------------------------------------- /src/utils/eip712.ts: -------------------------------------------------------------------------------- 1 | import { AbiCoder, keccak256, solidityPackedKeccak256 } from "ethers"; 2 | import { EIP712TypedData, Maker } from "../types"; 3 | 4 | // EIP 712 (Typed structured data hashing and signing) related data 5 | // https://eips.ethereum.org/EIPS/eip-712 6 | 7 | export const MAKER_HASH = "0x003c1bce41a2de73dfe64d6eeb2b3d7f15f1c0c382d9d963c2c6daeb75f0e539"; 8 | 9 | export const hashingMakerTypes: string[] = [ 10 | "bytes32", 11 | "uint8", 12 | "uint256", 13 | "uint256", 14 | "uint256", 15 | "uint256", 16 | "uint8", 17 | "address", 18 | "address", 19 | "address", 20 | "uint256", 21 | "uint256", 22 | "uint256", 23 | "bytes32", 24 | "bytes32", 25 | "bytes32", 26 | ]; 27 | 28 | export const makerTypes: EIP712TypedData = { 29 | Maker: [ 30 | { name: "quoteType", type: "uint8" }, 31 | { name: "globalNonce", type: "uint256" }, 32 | { name: "subsetNonce", type: "uint256" }, 33 | { name: "orderNonce", type: "uint256" }, 34 | { name: "strategyId", type: "uint256" }, 35 | { name: "collectionType", type: "uint8" }, 36 | { name: "collection", type: "address" }, 37 | { name: "currency", type: "address" }, 38 | { name: "signer", type: "address" }, 39 | { name: "startTime", type: "uint256" }, 40 | { name: "endTime", type: "uint256" }, 41 | { name: "price", type: "uint256" }, 42 | { name: "itemIds", type: "uint256[]" }, 43 | { name: "amounts", type: "uint256[]" }, 44 | { name: "additionalParameters", type: "bytes" }, 45 | ], 46 | }; 47 | 48 | export const getBatchOrderTypes = (height: number): EIP712TypedData => { 49 | return { 50 | BatchOrder: [{ name: "tree", type: `Maker${`[2]`.repeat(height)}` }], 51 | Maker: [ 52 | { name: "quoteType", type: "uint8" }, 53 | { name: "globalNonce", type: "uint256" }, 54 | { name: "subsetNonce", type: "uint256" }, 55 | { name: "orderNonce", type: "uint256" }, 56 | { name: "strategyId", type: "uint256" }, 57 | { name: "collectionType", type: "uint8" }, 58 | { name: "collection", type: "address" }, 59 | { name: "currency", type: "address" }, 60 | { name: "signer", type: "address" }, 61 | { name: "startTime", type: "uint256" }, 62 | { name: "endTime", type: "uint256" }, 63 | { name: "price", type: "uint256" }, 64 | { name: "itemIds", type: "uint256[]" }, 65 | { name: "amounts", type: "uint256[]" }, 66 | { name: "additionalParameters", type: "bytes" }, 67 | ], 68 | }; 69 | }; 70 | 71 | /** 72 | * Hash maker ask order. 73 | * This function is used for testing purpose in the SDK, but is exposed because it's used by the BE. 74 | * @param maker Maker 75 | * @returns string (bytes32) 76 | */ 77 | export const getMakerHash = (maker: Maker): string => { 78 | const values = [ 79 | MAKER_HASH, 80 | maker.quoteType, 81 | maker.globalNonce, 82 | maker.subsetNonce, 83 | maker.orderNonce, 84 | maker.strategyId, 85 | maker.collectionType, 86 | maker.collection, 87 | maker.currency, 88 | maker.signer, 89 | maker.startTime, 90 | maker.endTime, 91 | maker.price, 92 | solidityPackedKeccak256(["uint256[]"], [maker.itemIds]), 93 | solidityPackedKeccak256(["uint256[]"], [maker.amounts]), 94 | keccak256(maker.additionalParameters), 95 | ]; 96 | return keccak256(AbiCoder.defaultAbiCoder().encode(hashingMakerTypes, values)); 97 | }; 98 | -------------------------------------------------------------------------------- /src/utils/encodeOrderParams.ts: -------------------------------------------------------------------------------- 1 | import { AbiCoder, BytesLike } from "ethers"; 2 | import { SolidityType, StrategyType } from "../types"; 3 | 4 | /** 5 | * Get additional params types for a maker order based on the strategy used 6 | * @param strategy Maker strategy 7 | * @returns Array of solidity types for encoding 8 | */ 9 | export const getMakerParamsTypes = (strategy: StrategyType): SolidityType[] => { 10 | if (strategy === StrategyType.standard || strategy === StrategyType.collection) { 11 | return []; 12 | } 13 | if (strategy === StrategyType.collectionWithMerkleTree) { 14 | return ["bytes32"]; // Merkle tree root 15 | } 16 | return []; 17 | }; 18 | 19 | /** 20 | * Get additional params types for a maker order based on the strategy used 21 | * @param strategy Maker strategy 22 | * @returns Array of solidity types for encoding 23 | */ 24 | export const getTakerParamsTypes = (strategy: StrategyType): SolidityType[] => { 25 | if (strategy === StrategyType.standard) { 26 | return []; 27 | } 28 | if (strategy === StrategyType.collection) { 29 | return ["uint256"]; // Item id 30 | } 31 | if (strategy === StrategyType.collectionWithMerkleTree) { 32 | return ["uint256", "bytes32[]"]; // Item id, merkle proof 33 | } 34 | return []; 35 | }; 36 | 37 | /** 38 | * Given an array of params, returns the encoded params. 39 | * To be used for orders signature and orders execution 40 | * @param params Array of params 41 | * @param types Array of solidity types 42 | * @returns encoded params 43 | */ 44 | export const encodeParams = (params: any[], types: SolidityType[]): BytesLike => { 45 | return AbiCoder.defaultAbiCoder().encode(types, params); 46 | }; 47 | -------------------------------------------------------------------------------- /src/utils/signMakerOrders.ts: -------------------------------------------------------------------------------- 1 | import { ethers, TypedDataDomain, Signer } from "ethers"; 2 | import { Eip712MakerMerkleTree } from "./Eip712MakerMerkleTree"; 3 | import { makerTypes } from "./eip712"; 4 | import { Maker, MerkleTree, SignMerkleTreeOrdersOutput } from "../types"; 5 | 6 | /** 7 | * Sign a maker order 8 | * @param signer Ethers typed data signer 9 | * @param domain Typed data domain 10 | * @param makerOrder Maker order 11 | * @returns Signature 12 | */ 13 | export const signMakerOrder = async (signer: Signer, domain: TypedDataDomain, makerOrder: Maker): Promise => { 14 | const signature = await signer.signTypedData(domain, makerTypes, makerOrder); 15 | return ethers.Signature.from(signature).serialized; 16 | }; 17 | 18 | /** 19 | * Sign a list of maker orders with a merkle tree 20 | * @param signer Ethers typed data signer 21 | * @param domain Typed data domain 22 | * @param makerOrder Maker order 23 | * @returns Signature, array of proofs, and tree 24 | */ 25 | export const signMerkleTreeOrders = async ( 26 | signer: Signer, 27 | domain: TypedDataDomain, 28 | makerOrders: Maker[] 29 | ): Promise => { 30 | const tree = new Eip712MakerMerkleTree(makerOrders); 31 | 32 | const hexRoot = tree.hexRoot; 33 | const merkleTreeProofs: MerkleTree[] = makerOrders.map((_, index) => { 34 | const { proof } = tree.getPositionalProof(index); 35 | return { 36 | root: hexRoot, 37 | proof: proof.map((node) => { 38 | return { 39 | position: node[0] as number, 40 | value: node[1] as string, 41 | }; 42 | }), 43 | }; 44 | }); 45 | 46 | const signature = await signer.signTypedData(domain, tree.types, tree.getDataToSign()); 47 | return { signature: ethers.Signature.from(signature).serialized, merkleTreeProofs, tree }; 48 | }; 49 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ESNext" 5 | }, 6 | "exclude": ["src/__tests__/**/*", "src/contracts/**/*", "src/artifacts/**/*"] 7 | } 8 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src/LooksRare.ts", "./src/types.ts"], 3 | "out": "./doc", 4 | "githubPages": false, 5 | "readme": "none", 6 | "gitRemote": "https://github.com/LooksRare/sdk-v2", 7 | "disableSources": true, 8 | 9 | // typedoc-plugin-markdown 10 | "hideBreadcrumbs": true, 11 | "hideInPageTOC": true 12 | } 13 | --------------------------------------------------------------------------------