diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 93d600cad..72312647d 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -22,7 +22,7 @@ jobs: run: npm install - name: Run linters - run: npm run lint:check + run: npm run lint test: runs-on: ubuntu-latest @@ -38,29 +38,29 @@ jobs: - name: Run tests env: - API_KEY: ${{ secrets.API_KEY }} + OPENSEA_API_KEY: ${{ secrets.OPENSEA_API_KEY }} run: npm run test - # integration-test: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v3 - # - uses: actions/setup-node@v3 - # with: - # node-version-file: .nvmrc - # cache: npm + test-integration: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version-file: .nvmrc + cache: npm - # - name: Install dependencies - # run: npm install + - name: Install dependencies + run: npm install - # - name: Run integration tests - # env: - # API_KEY: ${{ secrets.API_KEY }} - # SELL_ORDER_CONTRACT_ADDRESS: ${{ secrets.SELL_ORDER_CONTRACT_ADDRESS }} - # SELL_ORDER_TOKEN_ID: ${{ secrets.SELL_ORDER_TOKEN_ID }} - # LISTING_AMOUNT: ${{ secrets.LISTING_AMOUNT }} - # ETH_TO_WRAP: ${{ secrets.ETH_TO_WRAP }} - # run: npm run test:integration + - name: Run integration tests + env: + OPENSEA_API_KEY: ${{ secrets.OPENSEA_API_KEY }} + ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }} + WALLET_PRIV_KEY: ${{ secrets.WALLET_PRIV_KEY }} + SELL_ORDER_CONTRACT_ADDRESS: ${{ secrets.SELL_ORDER_CONTRACT_ADDRESS }} + SELL_ORDER_TOKEN_ID: ${{ secrets.SELL_ORDER_TOKEN_ID }} + run: npm run test:integration test-earliest-node-engine-support: runs-on: ubuntu-latest @@ -76,5 +76,5 @@ jobs: - name: Run tests env: - API_KEY: ${{ secrets.API_KEY }} + OPENSEA_API_KEY: ${{ secrets.OPENSEA_API_KEY }} run: npm run test diff --git a/README.md b/README.md index e078c5b8d..8b775660f 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,7 @@ [![Docs][docs-badge]][docs-link] [![Discussions][discussions-badge]][discussions-link] -A JavaScript library for crypto-native ecommerce: buying, selling, and bidding on any cryptogood. With OpenSea.js, you can easily build your own native marketplace for your non-fungible tokens, or NFTs. These can be ERC-721 or ERC-1155 (semi-fungible) items. You don't have to deploy your own smart contracts or backend orderbooks. - -Published on [GitHub](https://github.com/ProjectOpenSea/opensea-js) and [npm](https://www.npmjs.com/package/opensea-js) +A JavaScript library for crypto-native e-commerce: buying, selling, and bidding on NFTs (non-fungible tokens). With OpenSea.js, you can easily build your own native marketplace. These can be ERC-721 or ERC-1155 (semi-fungible) items. You don't have to deploy your own smart contracts or manage backend orderbooks. - [Synopsis](#synopsis) - [Installation](#installation) @@ -37,7 +35,7 @@ Published on [GitHub](https://github.com/ProjectOpenSea/opensea-js) and [npm](ht - [Private Auctions](#private-auctions) - [Listening to Events](#listening-to-events) - [Learning More](#learning-more) -- [Migrating to version 1.0](#migrating-to-version-10) +- [Changelog](#changelog) - [Development Information](#development-information) - [Diagnosing Common Issues](#diagnosing-common-issues) - [Testing your branch locally](#testing-your-branch-locally) @@ -60,6 +58,8 @@ Then, in your project, run: ```bash npm install --save opensea-js +# or +yarn add opensea-js ``` ## Getting Started @@ -70,7 +70,7 @@ Then, create a new OpenSeaJS client, called an OpenSeaSDK 🚢, using your web3 ```typescript import { ethers } from "ethers"; -import { OpenSeaSDK, Network } from "opensea-js"; +import { OpenSeaSDK, Chain } from "opensea-js"; // This example provider won't let you make transactions, only read-only calls: const provider = new ethers.providers.JsonRpcProvider( @@ -78,14 +78,14 @@ const provider = new ethers.providers.JsonRpcProvider( ); const openseaSDK = new OpenSeaSDK(provider, { - networkName: Network.Main, + chain: Chain.Mainnet, apiKey: YOUR_API_KEY, }); ``` -**NOTE:** for testnet, please use `Network.Goerli` as the `networkName` - Rinkeby was deprecated in 2022. +**NOTE:** For testnet, please use `Chain.Goerli` as the `chain`. Rinkeby was deprecated in 2022. -**NOTE:** Using the sample Infura provider above won't let you authorize transactions, which are needed when approving and trading assets and currency. To make transactions, you need a provider with a private key or mnemonic set. +**NOTE:** Using the sample provider above won't let you authorize transactions, which are needed when approving and trading assets and currency. To make transactions, you need a provider with a private key or mnemonic set. In a browser with web3 or an extension like [MetaMask](https://metamask.io/) or [Coinbase Wallet](https://www.coinbase.com/wallet), you can use `window.ethereum` to access the native provider. @@ -120,7 +120,7 @@ You can fetch an asset using the `OpenSeaAPI`, which will return an `OpenSeaAsse ```TypeScript const asset: OpenSeaAsset = await openseaSDK.api.getAsset({ tokenAddress, // string - tokenId, // string | number | null + tokenId, // string | number | BigNumber | null }) ``` @@ -375,7 +375,7 @@ Example for transferring 2 DAI ($2) to another address: ```typescript const paymentToken = (await openseaSDK.api.getPaymentTokens({ symbol: "DAI" })) .tokens[0]; -const quantity = BigNumber.from(Math.pow(10, paymentToken.decimals)).times(2); +const quantity = ethers.utils.parseUnits("2", paymentToken.decimals); const transactionHash = await openseaSDK.transfer({ asset: { tokenId: null, @@ -487,9 +487,8 @@ Events are fired whenever transactions or orders are being created, and when tra Our recommendation is that you "forward" OpenSea events to your own store or state management system. Here's an example of doing that with a Redux action: ```typescript -import { EventType } from 'opensea-js' +import { openSeaSDK, EventType } from 'opensea-js' import * as ActionTypes from './index' -import { openSeaSDK } from '../globalSingletons' // ... @@ -564,7 +563,7 @@ To remove all listeners and start over, just call `openseaSDK.removeAllListeners Auto-generated documentation for each export is available [here](https://projectopensea.github.io/opensea-js/). -## Migrating to version 1.0 +## Changelog See the [Changelog](CHANGELOG.md). @@ -598,7 +597,7 @@ Or run the tests: npm test ``` -Note that the tests require access to both Infura and the OpenSea API. The timeout is adjustable via the `test` script in `package.json`. +Note that the tests require access to Alchemy and the OpenSea API. The timeout is adjustable via the `test` script in `package.json`. **Generate Documentation** diff --git a/package-lock.json b/package-lock.json index b831f1f2a..c67013a4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@opensea/seaport-js": "^1.3.0", + "@opensea/seaport-js": "^2.0.0", "ethers": "^5.7.2", "isomorphic-unfetch": "^4.0.2" }, @@ -50,41 +50,177 @@ } }, "node_modules/@0xsequence/abi": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/@0xsequence/abi/-/abi-0.39.6.tgz", - "integrity": "sha512-ROHfAyQvN1N2G6DeVyWR4wxi0dd0HjJZQ+2UkxvYJrdywQZONmrUPD6MfT4Fd+JrQwdIxkcNu2/QfgkyZDerFw==" + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/abi/-/abi-0.43.29.tgz", + "integrity": "sha512-tx5rwgkIrNGmvfFPEjm9s0/eX6hyWo90FMCEf1yxLZqjxUIhKS8AJvAvGH4sjmQ5eeAx5ujkE8xNxWEFs6gZPQ==" + }, + "node_modules/@0xsequence/api": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/api/-/api-0.43.29.tgz", + "integrity": "sha512-o/BW/KKX5SG+Rk58J1wv78MmKdW3OEqzcTZJ6isnUOSeG+4NYVrY+RV4r1zwu9BInI54jin2w0UGk9hjp5buqw==" + }, + "node_modules/@0xsequence/auth": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/auth/-/auth-0.43.29.tgz", + "integrity": "sha512-yX5i5b2tx5a/eF3g8NxkqXYTXa9LSAhDgJQCwFNKa1Trb10Ev3ADj+SlCpkxSpLk8ikUyPkg2ELwf9Usro4v4Q==", + "dependencies": { + "@0xsequence/abi": "^0.43.29", + "@0xsequence/api": "^0.43.29", + "@0xsequence/config": "^0.43.29", + "@0xsequence/ethauth": "^0.8.0", + "@0xsequence/indexer": "^0.43.29", + "@0xsequence/metadata": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/provider": "^0.43.29", + "@0xsequence/utils": "^0.43.29", + "@0xsequence/wallet": "^0.43.29" + }, + "peerDependencies": { + "ethers": ">=5.5 < 6" + } + }, + "node_modules/@0xsequence/config": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/config/-/config-0.43.29.tgz", + "integrity": "sha512-HT63jMmEtM8+fj76oSuCOxiDEUe4pXrFB3FRiA7K+/SHu5JWyRoK3rv9RoEB/MPUpHPcqIBkFtTXaYLjGRe4yg==", + "dependencies": { + "@0xsequence/abi": "^0.43.29", + "@0xsequence/multicall": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/utils": "^0.43.29" + }, + "peerDependencies": { + "ethers": ">=5.5 < 6" + } + }, + "node_modules/@0xsequence/ethauth": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@0xsequence/ethauth/-/ethauth-0.8.1.tgz", + "integrity": "sha512-P21cxRSS+2mDAqFVAJt0lwQFtbObX+Ewlj8DMyDELp81+QbfHFh6LCyu8dTXNdBx6UbmRFOCSBno5Txd50cJPQ==", + "dependencies": { + "js-base64": "^3.7.2" + }, + "peerDependencies": { + "ethers": ">=5.5" + } + }, + "node_modules/@0xsequence/guard": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/guard/-/guard-0.43.29.tgz", + "integrity": "sha512-IZ40BoChB/ws09NbSzytOi/8xojsR3g8sgwzZ/eth6YWeMP1MIuZc2BjRDAc59oStkspZxguWnE4HgVibTBHug==" + }, + "node_modules/@0xsequence/indexer": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/indexer/-/indexer-0.43.29.tgz", + "integrity": "sha512-Myx/2D+vcoY/cPYKIqoB1VyALo4e4P3KQsOWxkb6o2JADOntY0mcFq9YIMPl+Zj1xPkGHTCNIk2p1udKMhbKzQ==" + }, + "node_modules/@0xsequence/metadata": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/metadata/-/metadata-0.43.29.tgz", + "integrity": "sha512-wk15QFMaz0h0kNP2W9Ms1rVykG6N/tPzRdhqvBviq2vROZFHmBwq7TbWae4t/gIeoo+1FZI5507771edJ8KJaw==" }, "node_modules/@0xsequence/multicall": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/@0xsequence/multicall/-/multicall-0.39.6.tgz", - "integrity": "sha512-oHgxAuYoanYXKArrzPF+a8f9CAzDX5xYp+U+bcCd3qOagIn1xeQCEW4zbnGDRD+ZguhlcP2I7wiW1eImmoOi1g==", + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/multicall/-/multicall-0.43.29.tgz", + "integrity": "sha512-7kV3koB0aaLHjJjjoxIYhk8R/40WMIFOzJNZSRnzmeSKf67hvQNw9rkZwr0qx3B11IPANfZnmhs2gOnybAbiBQ==", "dependencies": { - "@0xsequence/abi": "^0.39.6", - "@0xsequence/network": "^0.39.6", - "@0xsequence/utils": "^0.39.6", - "@ethersproject/providers": "^5.5.1", - "ethers": "^5.5.2" + "@0xsequence/abi": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/utils": "^0.43.29" + }, + "peerDependencies": { + "ethers": ">=5.5 < 6" } }, "node_modules/@0xsequence/network": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/@0xsequence/network/-/network-0.39.6.tgz", - "integrity": "sha512-esmdTCT98tjjbYoiYjaOmJIG0aSgadg9wR1FB6lzPdAVuq+xOGvxygtTiqhTqXF+YI2Alc4b5EP7tUWC9OA10Q==", + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/network/-/network-0.43.29.tgz", + "integrity": "sha512-0CmgIpmfOVOmqzIbaDg8hzRc7aR/z2Lx4wwrqEAsfsggp7+y6mwm8rSda4Z6ZOtlBeWSod9w+x3AqpL8iZenYg==", + "dependencies": { + "@0xsequence/indexer": "^0.43.29", + "@0xsequence/provider": "^0.43.29", + "@0xsequence/relayer": "^0.43.29", + "@0xsequence/utils": "^0.43.29" + }, + "peerDependencies": { + "ethers": ">=5.5 < 6" + } + }, + "node_modules/@0xsequence/provider": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/provider/-/provider-0.43.29.tgz", + "integrity": "sha512-ZfKMh8YgtRcJ9gt5PChHjGA11R226IRjA5UjYHBYOSywcQxTUjpyEMrCEfFQ5x9n8X3MVF+0RJaFbPFs6FyDxg==", + "dependencies": { + "@0xsequence/abi": "^0.43.29", + "@0xsequence/auth": "^0.43.29", + "@0xsequence/config": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/relayer": "^0.43.29", + "@0xsequence/transactions": "^0.43.29", + "@0xsequence/utils": "^0.43.29", + "@0xsequence/wallet": "^0.43.29", + "eventemitter2": "^6.4.5", + "webextension-polyfill": "^0.10.0" + }, + "peerDependencies": { + "ethers": ">=5.5 < 6" + } + }, + "node_modules/@0xsequence/relayer": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/relayer/-/relayer-0.43.29.tgz", + "integrity": "sha512-Yp1mrOvhzMLvrvLoxrH+qHo6kp6LEzTLzF0NYzM/j7wVKdY0FBOEHEO6sSCVNrSOv8YRDRjtnz/3K7Sh3eweXw==", "dependencies": { - "@0xsequence/utils": "^0.39.6", - "@ethersproject/providers": "^5.5.1", - "ethers": "^5.5.2" + "@0xsequence/abi": "^0.43.29", + "@0xsequence/config": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/transactions": "^0.43.29", + "@0xsequence/utils": "^0.43.29" + }, + "peerDependencies": { + "ethers": ">=5.5 < 6" + } + }, + "node_modules/@0xsequence/transactions": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/transactions/-/transactions-0.43.29.tgz", + "integrity": "sha512-htJ+vY372Yfe9ojufP2KzJRXaL9cnWGTP+dZD3KAp47AJbCfcf44/ggqL6rLzCFhQSqF3e6m2BoBNA2GvIsilQ==", + "dependencies": { + "@0xsequence/abi": "^0.43.29", + "@0xsequence/config": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/utils": "^0.43.29" + }, + "peerDependencies": { + "ethers": ">=5.5 < 6" } }, "node_modules/@0xsequence/utils": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/@0xsequence/utils/-/utils-0.39.6.tgz", - "integrity": "sha512-Zf5NTRSzrOrc48SXsmtRDs1uaJ0gaOjwQYkANoIco54Rq+IA1Dlz9HWRebgyjeCUrXLnGCckljBemAUoZbVgIg==", + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/utils/-/utils-0.43.29.tgz", + "integrity": "sha512-5GVIIoxlGESZ+GIPn/0IKu4oTcPTpteDbks4dKjfL+jF/jogZB7qWIWUpOF3O0wgZO5xbimZIur6GowcQq8aLw==", "dependencies": { - "@ethersproject/abstract-signer": "^5.5.0", - "@ethersproject/properties": "^5.5.0", - "ethers": "^5.5.2", "js-base64": "^3.7.2" + }, + "peerDependencies": { + "ethers": ">=5.5 < 6" + } + }, + "node_modules/@0xsequence/wallet": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/wallet/-/wallet-0.43.29.tgz", + "integrity": "sha512-TBx+tQK/hM7QgfKdC+PtrEGNGsTHhEKmvbt7kiVzikKRcP5lWmQRfb2wrHds6/RpRvEUBg9yoUEGMdqCW+JjiQ==", + "dependencies": { + "@0xsequence/abi": "^0.43.29", + "@0xsequence/config": "^0.43.29", + "@0xsequence/guard": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/relayer": "^0.43.29", + "@0xsequence/transactions": "^0.43.29", + "@0xsequence/utils": "^0.43.29" + }, + "peerDependencies": { + "ethers": ">=5.5 < 6" } }, "node_modules/@ampproject/remapping": { @@ -1514,16 +1650,16 @@ } }, "node_modules/@opensea/seaport-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@opensea/seaport-js/-/seaport-js-1.3.0.tgz", - "integrity": "sha512-yKtjP4k2lLGfF/eBsjsIj0FPquD+WL2M1BfihrAiFTQuGIEb+PMAxZmVLUr9UAWX79TcAwnZGJW/qMxF3VsR3A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opensea/seaport-js/-/seaport-js-2.0.0.tgz", + "integrity": "sha512-G6SiEfJAk085CY1d2WEl8fJcncGy2R1WDYDVsu3sE7DHrXg5yR8z9yMovxSff7Juq+dCOmQKyNGvixQubrlvxA==", "dependencies": { - "@0xsequence/multicall": "^0.39.0", - "ethers": "^5.6.7", - "merkletreejs": "^0.3.9" + "@0xsequence/multicall": "^0.43.29", + "ethers": "^5.7.2", + "merkletreejs": "^0.3.10" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@pkgr/utils": { @@ -4341,6 +4477,11 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" }, + "node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -9628,6 +9769,11 @@ "node": ">=8.0.0" } }, + "node_modules/webextension-polyfill": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz", + "integrity": "sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -9967,43 +10113,149 @@ }, "dependencies": { "@0xsequence/abi": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/@0xsequence/abi/-/abi-0.39.6.tgz", - "integrity": "sha512-ROHfAyQvN1N2G6DeVyWR4wxi0dd0HjJZQ+2UkxvYJrdywQZONmrUPD6MfT4Fd+JrQwdIxkcNu2/QfgkyZDerFw==" + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/abi/-/abi-0.43.29.tgz", + "integrity": "sha512-tx5rwgkIrNGmvfFPEjm9s0/eX6hyWo90FMCEf1yxLZqjxUIhKS8AJvAvGH4sjmQ5eeAx5ujkE8xNxWEFs6gZPQ==" + }, + "@0xsequence/api": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/api/-/api-0.43.29.tgz", + "integrity": "sha512-o/BW/KKX5SG+Rk58J1wv78MmKdW3OEqzcTZJ6isnUOSeG+4NYVrY+RV4r1zwu9BInI54jin2w0UGk9hjp5buqw==" + }, + "@0xsequence/auth": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/auth/-/auth-0.43.29.tgz", + "integrity": "sha512-yX5i5b2tx5a/eF3g8NxkqXYTXa9LSAhDgJQCwFNKa1Trb10Ev3ADj+SlCpkxSpLk8ikUyPkg2ELwf9Usro4v4Q==", + "requires": { + "@0xsequence/abi": "^0.43.29", + "@0xsequence/api": "^0.43.29", + "@0xsequence/config": "^0.43.29", + "@0xsequence/ethauth": "^0.8.0", + "@0xsequence/indexer": "^0.43.29", + "@0xsequence/metadata": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/provider": "^0.43.29", + "@0xsequence/utils": "^0.43.29", + "@0xsequence/wallet": "^0.43.29" + } + }, + "@0xsequence/config": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/config/-/config-0.43.29.tgz", + "integrity": "sha512-HT63jMmEtM8+fj76oSuCOxiDEUe4pXrFB3FRiA7K+/SHu5JWyRoK3rv9RoEB/MPUpHPcqIBkFtTXaYLjGRe4yg==", + "requires": { + "@0xsequence/abi": "^0.43.29", + "@0xsequence/multicall": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/utils": "^0.43.29" + } + }, + "@0xsequence/ethauth": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@0xsequence/ethauth/-/ethauth-0.8.1.tgz", + "integrity": "sha512-P21cxRSS+2mDAqFVAJt0lwQFtbObX+Ewlj8DMyDELp81+QbfHFh6LCyu8dTXNdBx6UbmRFOCSBno5Txd50cJPQ==", + "requires": { + "js-base64": "^3.7.2" + } + }, + "@0xsequence/guard": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/guard/-/guard-0.43.29.tgz", + "integrity": "sha512-IZ40BoChB/ws09NbSzytOi/8xojsR3g8sgwzZ/eth6YWeMP1MIuZc2BjRDAc59oStkspZxguWnE4HgVibTBHug==" + }, + "@0xsequence/indexer": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/indexer/-/indexer-0.43.29.tgz", + "integrity": "sha512-Myx/2D+vcoY/cPYKIqoB1VyALo4e4P3KQsOWxkb6o2JADOntY0mcFq9YIMPl+Zj1xPkGHTCNIk2p1udKMhbKzQ==" + }, + "@0xsequence/metadata": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/metadata/-/metadata-0.43.29.tgz", + "integrity": "sha512-wk15QFMaz0h0kNP2W9Ms1rVykG6N/tPzRdhqvBviq2vROZFHmBwq7TbWae4t/gIeoo+1FZI5507771edJ8KJaw==" }, "@0xsequence/multicall": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/@0xsequence/multicall/-/multicall-0.39.6.tgz", - "integrity": "sha512-oHgxAuYoanYXKArrzPF+a8f9CAzDX5xYp+U+bcCd3qOagIn1xeQCEW4zbnGDRD+ZguhlcP2I7wiW1eImmoOi1g==", + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/multicall/-/multicall-0.43.29.tgz", + "integrity": "sha512-7kV3koB0aaLHjJjjoxIYhk8R/40WMIFOzJNZSRnzmeSKf67hvQNw9rkZwr0qx3B11IPANfZnmhs2gOnybAbiBQ==", "requires": { - "@0xsequence/abi": "^0.39.6", - "@0xsequence/network": "^0.39.6", - "@0xsequence/utils": "^0.39.6", - "@ethersproject/providers": "^5.5.1", - "ethers": "^5.5.2" + "@0xsequence/abi": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/utils": "^0.43.29" } }, "@0xsequence/network": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/@0xsequence/network/-/network-0.39.6.tgz", - "integrity": "sha512-esmdTCT98tjjbYoiYjaOmJIG0aSgadg9wR1FB6lzPdAVuq+xOGvxygtTiqhTqXF+YI2Alc4b5EP7tUWC9OA10Q==", + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/network/-/network-0.43.29.tgz", + "integrity": "sha512-0CmgIpmfOVOmqzIbaDg8hzRc7aR/z2Lx4wwrqEAsfsggp7+y6mwm8rSda4Z6ZOtlBeWSod9w+x3AqpL8iZenYg==", + "requires": { + "@0xsequence/indexer": "^0.43.29", + "@0xsequence/provider": "^0.43.29", + "@0xsequence/relayer": "^0.43.29", + "@0xsequence/utils": "^0.43.29" + } + }, + "@0xsequence/provider": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/provider/-/provider-0.43.29.tgz", + "integrity": "sha512-ZfKMh8YgtRcJ9gt5PChHjGA11R226IRjA5UjYHBYOSywcQxTUjpyEMrCEfFQ5x9n8X3MVF+0RJaFbPFs6FyDxg==", + "requires": { + "@0xsequence/abi": "^0.43.29", + "@0xsequence/auth": "^0.43.29", + "@0xsequence/config": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/relayer": "^0.43.29", + "@0xsequence/transactions": "^0.43.29", + "@0xsequence/utils": "^0.43.29", + "@0xsequence/wallet": "^0.43.29", + "eventemitter2": "^6.4.5", + "webextension-polyfill": "^0.10.0" + } + }, + "@0xsequence/relayer": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/relayer/-/relayer-0.43.29.tgz", + "integrity": "sha512-Yp1mrOvhzMLvrvLoxrH+qHo6kp6LEzTLzF0NYzM/j7wVKdY0FBOEHEO6sSCVNrSOv8YRDRjtnz/3K7Sh3eweXw==", + "requires": { + "@0xsequence/abi": "^0.43.29", + "@0xsequence/config": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/transactions": "^0.43.29", + "@0xsequence/utils": "^0.43.29" + } + }, + "@0xsequence/transactions": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/transactions/-/transactions-0.43.29.tgz", + "integrity": "sha512-htJ+vY372Yfe9ojufP2KzJRXaL9cnWGTP+dZD3KAp47AJbCfcf44/ggqL6rLzCFhQSqF3e6m2BoBNA2GvIsilQ==", "requires": { - "@0xsequence/utils": "^0.39.6", - "@ethersproject/providers": "^5.5.1", - "ethers": "^5.5.2" + "@0xsequence/abi": "^0.43.29", + "@0xsequence/config": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/utils": "^0.43.29" } }, "@0xsequence/utils": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/@0xsequence/utils/-/utils-0.39.6.tgz", - "integrity": "sha512-Zf5NTRSzrOrc48SXsmtRDs1uaJ0gaOjwQYkANoIco54Rq+IA1Dlz9HWRebgyjeCUrXLnGCckljBemAUoZbVgIg==", + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/utils/-/utils-0.43.29.tgz", + "integrity": "sha512-5GVIIoxlGESZ+GIPn/0IKu4oTcPTpteDbks4dKjfL+jF/jogZB7qWIWUpOF3O0wgZO5xbimZIur6GowcQq8aLw==", "requires": { - "@ethersproject/abstract-signer": "^5.5.0", - "@ethersproject/properties": "^5.5.0", - "ethers": "^5.5.2", "js-base64": "^3.7.2" } }, + "@0xsequence/wallet": { + "version": "0.43.29", + "resolved": "https://registry.npmjs.org/@0xsequence/wallet/-/wallet-0.43.29.tgz", + "integrity": "sha512-TBx+tQK/hM7QgfKdC+PtrEGNGsTHhEKmvbt7kiVzikKRcP5lWmQRfb2wrHds6/RpRvEUBg9yoUEGMdqCW+JjiQ==", + "requires": { + "@0xsequence/abi": "^0.43.29", + "@0xsequence/config": "^0.43.29", + "@0xsequence/guard": "^0.43.29", + "@0xsequence/network": "^0.43.29", + "@0xsequence/relayer": "^0.43.29", + "@0xsequence/transactions": "^0.43.29", + "@0xsequence/utils": "^0.43.29" + } + }, "@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -10954,13 +11206,13 @@ } }, "@opensea/seaport-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@opensea/seaport-js/-/seaport-js-1.3.0.tgz", - "integrity": "sha512-yKtjP4k2lLGfF/eBsjsIj0FPquD+WL2M1BfihrAiFTQuGIEb+PMAxZmVLUr9UAWX79TcAwnZGJW/qMxF3VsR3A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@opensea/seaport-js/-/seaport-js-2.0.0.tgz", + "integrity": "sha512-G6SiEfJAk085CY1d2WEl8fJcncGy2R1WDYDVsu3sE7DHrXg5yR8z9yMovxSff7Juq+dCOmQKyNGvixQubrlvxA==", "requires": { - "@0xsequence/multicall": "^0.39.0", - "ethers": "^5.6.7", - "merkletreejs": "^0.3.9" + "@0xsequence/multicall": "^0.43.29", + "ethers": "^5.7.2", + "merkletreejs": "^0.3.10" } }, "@pkgr/utils": { @@ -13152,6 +13404,11 @@ } } }, + "eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" + }, "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -17097,6 +17354,11 @@ "utf8": "3.0.0" } }, + "webextension-polyfill": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz", + "integrity": "sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==" + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 373295a13..3ab24338f 100644 --- a/package.json +++ b/package.json @@ -18,26 +18,26 @@ "src" ], "scripts": { - "postinstall": "npm run build", + "postinstall": "husky install", + "prepare": "npm run build", "build": "npm run abi-type-gen && tsc -p tsconfig.build.json && npm run bundle", "bundle": "browserify lib/index.js -o lib/bundle.js", "abi-type-gen": "typechain --target=ethers-v5 src/abi/*.json --out-dir=src/typechain/contracts", "check-types": "tsc --noEmit --project tsconfig.json", "coverage-report": "nyc report --reporter=text-lcov | coveralls", "docs-build": "typedoc --out docs src/index.ts", + "lint": "concurrently \"npm run check-types\" \"npm run prettier:check\" \"npm run eslint:check\"", "eslint:check": "eslint . --max-warnings 0 --ext .js,.ts", - "eslint:fix": "eslint . --fix --max-warnings 0 --ext .js,.ts", - "lint:check": "concurrently \"npm run check-types\" \"npm run prettier:check\" \"npm run eslint:check\"", - "prepare": "husky install && npm run build", + "eslint:fix": "npm run eslint:check -- --fix", "prettier:check": "prettier --check .", - "prettier:check:package.json": "npm run prettier-package-json --list-different", + "prettier:check:package.json": "prettier-package-json --list-different", "prettier:fix": "prettier --write .", "test": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' TS_NODE_TRANSPILE_ONLY=true nyc mocha -r ts-node/register test/**/*.spec.ts --exclude test/integration/**/*.ts --timeout 15000", "test:integration": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' TS_NODE_TRANSPILE_ONLY=true nyc mocha -r ts-node/register -r dotenv/config test/integration/**/*.spec.ts --timeout 15000" }, "types": "lib/index.d.ts", "dependencies": { - "@opensea/seaport-js": "^1.3.0", + "@opensea/seaport-js": "^2.0.0", "ethers": "^5.7.2", "isomorphic-unfetch": "^4.0.2" }, diff --git a/src/api.ts b/src/api.ts index c9fb2957c..e33163989 100644 --- a/src/api.ts +++ b/src/api.ts @@ -29,7 +29,7 @@ import { getPostCollectionOfferPayload, } from "./orders/utils"; import { - Network, + Chain, OpenSeaAPIConfig, OpenSeaAsset, OpenSeaAssetBundle, @@ -62,23 +62,23 @@ export class OpenSeaAPI { public logger: (arg: string) => void; private apiKey: string | undefined; - private networkName: Network; + private chain: Chain; private retryDelay = 3000; /** * Create an instance of the OpenSea API - * @param config OpenSeaAPIConfig for setting up the API, including an optional API key, network name, and base URL + * @param config OpenSeaAPIConfig for setting up the API, including an optional API key, Chain name, and base URL * @param logger Optional function for logging debug strings before and after requests are made */ constructor(config: OpenSeaAPIConfig, logger?: (arg: string) => void) { this.apiKey = config.apiKey; - this.networkName = config.networkName ?? Network.Main; + this.chain = config.chain ?? Chain.Mainnet; - switch (config.networkName) { - case Network.Goerli: + switch (config.chain) { + case Chain.Goerli: this.apiBaseUrl = config.apiBaseUrl ?? API_BASE_TESTNET; break; - case Network.Main: + case Chain.Mainnet: default: this.apiBaseUrl = config.apiBaseUrl ?? API_BASE_MAINNET; break; @@ -99,7 +99,7 @@ export class OpenSeaAPI { ...restOptions }: Omit): Promise { const { orders } = await this.get( - getOrdersAPIPath(this.networkName, protocol, side), + getOrdersAPIPath(this.chain, protocol, side), serializeOrdersQueryOptions({ limit: 1, orderBy, @@ -129,7 +129,7 @@ export class OpenSeaAPI { } > { const response = await this.get( - getOrdersAPIPath(this.networkName, protocol, side), + getOrdersAPIPath(this.chain, protocol, side), serializeOrdersQueryOptions({ limit: this.pageSize, orderBy, @@ -158,14 +158,14 @@ export class OpenSeaAPI { fulfillerAddress, orderHash, protocolAddress, - this.networkName + this.chain ); } else { payload = getFulfillOfferPayload( fulfillerAddress, orderHash, protocolAddress, - this.networkName + this.chain ); } const response = await this.post( @@ -188,7 +188,7 @@ export class OpenSeaAPI { const { protocol = "seaport", side, protocolAddress } = apiOptions; try { response = await this.post( - getOrdersAPIPath(this.networkName, protocol, side), + getOrdersAPIPath(this.chain, protocol, side), { ...order, protocol_address: protocolAddress } ); } catch (error) { diff --git a/src/constants.ts b/src/constants.ts index 6ec2cf696..954d3c800 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,5 @@ -import { Network } from "./types"; +import { Chain } from "./types"; -export const NULL_ADDRESS = "0x0000000000000000000000000000000000000000"; export const INVERSE_BASIS_POINT = 10_000; // 100 basis points per 1% export const MAX_EXPIRATION_MONTHS = 1; @@ -13,13 +12,13 @@ export const MERKLE_VALIDATOR_MAINNET = "0xbaf2127b49fc93cbca6269fade0f7f31df4c88a7"; export const WETH_ADDRESS_BY_NETWORK = { - [Network.Main]: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - [Network.Goerli]: "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", + [Chain.Mainnet]: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + [Chain.Goerli]: "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", } as const; export const DEFAULT_ZONE_BY_NETWORK = { - [Network.Main]: "0x0000000000000000000000000000000000000000", - [Network.Goerli]: "0x0000000000000000000000000000000000000000", + [Chain.Mainnet]: "0x0000000000000000000000000000000000000000", + [Chain.Goerli]: "0x0000000000000000000000000000000000000000", } as const; export const SHARED_STOREFRONT_ADDRESS_MAINNET = diff --git a/src/index.ts b/src/index.ts index 590d2a894..dd27be52e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,16 +1,16 @@ /* eslint-disable import/no-unused-modules */ import { OpenSeaAPI } from "./api"; import { OpenSeaSDK } from "./sdk"; -import { Network, EventData, EventType } from "./types"; +import { Chain, EventData, EventType } from "./types"; /** * Example setup: * * import { ethers } from 'ethers' - * import { OpenSeaPort, Network } from 'opensea-js' + * import { OpenSeaPort, Chain } from 'opensea-js' * const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io') * const client = new OpenSeaPort(provider, { - * networkName: Network.Main + * chain: Chain.Mainnet * }) */ @@ -24,7 +24,7 @@ export { // Useful for serializing and deserializing orders: // Types to help initialize SDK and listen to events. EventType, - Network, + Chain, }; export type { EventData }; diff --git a/src/orders/utils.ts b/src/orders/utils.ts index 0c9e231de..fa3e48d4f 100644 --- a/src/orders/utils.ts +++ b/src/orders/utils.ts @@ -8,25 +8,25 @@ import { SerializedOrderV2, ProtocolData, } from "./types"; -import { Network } from "../types"; +import { Chain } from "../types"; import { accountFromJSON, assetBundleFromJSON } from "../utils"; -const NETWORK_TO_CHAIN = { - [Network.Main]: "ethereum", - [Network.Goerli]: "goerli", +const CHAIN_TO_NAME = { + [Chain.Mainnet]: "ethereum", + [Chain.Goerli]: "goerli", }; export const DEFAULT_SEAPORT_CONTRACT_ADDRESS = CROSS_CHAIN_SEAPORT_V1_5_ADDRESS; export const getOrdersAPIPath = ( - network: Network, + chain: Chain, protocol: OrderProtocol, side: OrderSide ) => { - const chain = NETWORK_TO_CHAIN[network]; + const chainName = CHAIN_TO_NAME[chain]; const sidePath = side === "ask" ? "listings" : "offers"; - return `/v2/orders/${chain}/${protocol}/${sidePath}`; + return `/v2/orders/${chainName}/${protocol}/${sidePath}`; }; export const getCollectionPath = (slug: string) => { @@ -80,13 +80,13 @@ export const getFulfillListingPayload = ( fulfillerAddress: string, order_hash: string, protocolAddress: string, - network: Network + chain: Chain ) => { - const chain = NETWORK_TO_CHAIN[network]; + const chainName = CHAIN_TO_NAME[chain]; return { listing: { hash: order_hash, - chain, + chain: chainName, protocol_address: protocolAddress, }, fulfiller: { @@ -99,13 +99,13 @@ export const getFulfillOfferPayload = ( fulfillerAddress: string, order_hash: string, protocolAddress: string, - network: Network + chain: Chain ) => { - const chain = NETWORK_TO_CHAIN[network]; + const chainName = CHAIN_TO_NAME[chain]; return { offer: { hash: order_hash, - chain, + chain: chainName, protocol_address: protocolAddress, }, fulfiller: { diff --git a/src/sdk.ts b/src/sdk.ts index e37c78f60..73801e876 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -1,5 +1,6 @@ import EventEmitter = require("events"); import { Seaport } from "@opensea/seaport-js"; +import { OPENSEA_CONDUIT_KEY } from "@opensea/seaport-js/lib/constants"; import { ConsiderationInputItem, CreateInputItem, @@ -18,7 +19,6 @@ import { parseEther } from "ethers/lib/utils"; import { OpenSeaAPI } from "./api"; import { INVERSE_BASIS_POINT, - NULL_ADDRESS, DEFAULT_ZONE_BY_NETWORK, WETH_ADDRESS_BY_NETWORK, } from "./constants"; @@ -39,7 +39,7 @@ import { ComputedFees, EventData, EventType, - Network, + Chain, OpenSeaAPIConfig, OpenSeaAsset, OpenSeaCollection, @@ -58,25 +58,26 @@ import { } from "./utils/utils"; export class OpenSeaSDK { - // Provider + /** Provider */ public provider: providers.JsonRpcProvider; - // Seaport v1.5 client + /** Seaport v1.5 client */ public seaport_v1_5: Seaport; - // Logger function to use when debugging + /** Logger function to use when debugging */ public logger: (arg: string) => void; - // API instance on this seaport + /** API instance on this seaport */ public readonly api: OpenSeaAPI; + /** The configured chain */ + public readonly chain: Chain; - private _networkName: Network; private _emitter: EventEmitter; - private signerOrProvider: Wallet | providers.JsonRpcProvider; + private _signerOrProvider: Wallet | providers.JsonRpcProvider; /** * Your very own seaport. * Create a new instance of OpenSeaJS. * @param provider Provider to use for transactions. For example: * `const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io')` - * @param apiConfig configuration options, including `networkName` + * @param apiConfig configuration options, including `chain` * @param logger logger, optional, a function that will be called with debugging * @param wallet optional, if you'd like to use an ethers wallet for order posting * information @@ -88,14 +89,16 @@ export class OpenSeaSDK { wallet?: Wallet ) { // API config - apiConfig.networkName = apiConfig.networkName ?? Network.Main; + apiConfig.chain ??= Chain.Mainnet; + this.chain = apiConfig.chain; this.api = new OpenSeaAPI(apiConfig); - this._networkName = apiConfig.networkName; this.provider = provider; - this.signerOrProvider = wallet ?? this.provider; + this._signerOrProvider = wallet ?? this.provider; - this.seaport_v1_5 = new Seaport(this.signerOrProvider); + this.seaport_v1_5 = new Seaport(this._signerOrProvider, { + overrides: { defaultConduitKey: OPENSEA_CONDUIT_KEY }, + }); // Emit events this._emitter = new EventEmitter(); @@ -155,7 +158,7 @@ export class OpenSeaSDK { amountInEth: BigNumberish; accountAddress: string; }) { - const token = getCanonicalWrappedEther(this._networkName); + const token = getCanonicalWrappedEther(this.chain); const value = parseEther(FixedNumber.from(amountInEth).toString()); @@ -164,7 +167,7 @@ export class OpenSeaSDK { const wethContract = new Contract( token.address, ["function deposit() payable"], - this.signerOrProvider + this._signerOrProvider ); wethContract.connect(this.provider); @@ -194,7 +197,7 @@ export class OpenSeaSDK { amountInEth: BigNumberish; accountAddress: string; }) { - const token = getCanonicalWrappedEther(this._networkName); + const token = getCanonicalWrappedEther(this.chain); const amount = parseEther(FixedNumber.from(amountInEth).toString()); @@ -203,7 +206,7 @@ export class OpenSeaSDK { const wethContract = new Contract( token.address, ["function withdraw(uint wad) public"], - this.signerOrProvider + this._signerOrProvider ); wethContract.connect(this.provider); @@ -291,11 +294,11 @@ export class OpenSeaSDK { } private getAssetItems( - assets: Asset[], + assets: OpenSeaAsset[], quantities: BigNumber[] = [] ): CreateInputItem[] { return assets.map((asset, index) => ({ - itemType: getAssetItemType(asset.tokenStandard), + itemType: getAssetItemType(asset.assetContract.tokenStandard), token: getAddressAfterRemappingSharedStorefrontAddressToLazyMintAdapterAddress( asset.tokenAddress @@ -340,12 +343,12 @@ export class OpenSeaSDK { throw new Error("Asset must have a tokenId"); } paymentTokenAddress = - paymentTokenAddress ?? WETH_ADDRESS_BY_NETWORK[this._networkName]; + paymentTokenAddress ?? WETH_ADDRESS_BY_NETWORK[this.chain]; const openseaAsset = await this.api.getAsset(asset); const considerationAssetItems = this.getAssetItems( [openseaAsset], - [BigNumber.from(quantity)] + [BigNumber.from(quantity ?? 1)] ); const { basePrice } = await this._getPriceParameters( @@ -375,12 +378,13 @@ export class OpenSeaSDK { }, ], consideration: [...considerationAssetItems, ...considerationFeeItems], - endTime: expirationTime - ? BigNumber.from(expirationTime).toString() - : getMaxOrderExpirationTimestamp().toString(), - zone: DEFAULT_ZONE_BY_NETWORK[this._networkName], + endTime: + expirationTime !== undefined + ? BigNumber.from(expirationTime).toString() + : getMaxOrderExpirationTimestamp().toString(), + zone: DEFAULT_ZONE_BY_NETWORK[this.chain], domain, - salt: BigNumber.from(salt).toString(), + salt: BigNumber.from(salt ?? 0).toString(), restrictedByZone: false, allowPartialFills: true, }, @@ -420,7 +424,7 @@ export class OpenSeaSDK { salt, listingTime, expirationTime, - paymentTokenAddress = NULL_ADDRESS, + paymentTokenAddress = ethers.constants.AddressZero, buyerAddress, }: { asset: Asset; @@ -442,7 +446,7 @@ export class OpenSeaSDK { const openseaAsset = await this.api.getAsset(asset); const offerAssetItems = this.getAssetItems( [openseaAsset], - [BigNumber.from(quantity)] + [BigNumber.from(quantity ?? 1)] ); const { basePrice, endPrice } = await this._getPriceParameters( @@ -483,9 +487,9 @@ export class OpenSeaSDK { endTime: expirationTime?.toString() ?? getMaxOrderExpirationTimestamp().toString(), - zone: DEFAULT_ZONE_BY_NETWORK[this._networkName], + zone: DEFAULT_ZONE_BY_NETWORK[this.chain], domain, - salt: BigNumber.from(salt).toString(), + salt: BigNumber.from(salt ?? 0).toString(), restrictedByZone: false, allowPartialFills: true, }, @@ -568,9 +572,9 @@ export class OpenSeaSDK { endTime: expirationTime?.toString() ?? getMaxOrderExpirationTimestamp().toString(), - zone: DEFAULT_ZONE_BY_NETWORK[this._networkName], + zone: DEFAULT_ZONE_BY_NETWORK[this.chain], domain, - salt: BigNumber.from(salt).toString(), + salt: BigNumber.from(salt ?? 0).toString(), restrictedByZone: false, allowPartialFills: true, }; @@ -812,7 +816,7 @@ export class OpenSeaSDK { .call(); if (count !== undefined) { - return BigNumber.from(count); + return count; } } else if (asset.tokenStandard == TokenStandard.ERC721) { const contract = new ethers.Contract( @@ -929,7 +933,7 @@ export class OpenSeaSDK { waitingForBestCounterOrder = false, englishAuctionReservePrice?: BigNumberish ) { - const isEther = tokenAddress === NULL_ADDRESS; + const isEther = tokenAddress === ethers.constants.AddressZero; let paymentToken; if (!isEther) { const { tokens } = await this.api.getPaymentTokens({ @@ -989,10 +993,12 @@ export class OpenSeaSDK { "Expiration time must be set if order will change in price." ); } - if (!reservePrice?.isZero() && !waitingForBestCounterOrder) { + const reservePriceIsDefinedAndNonZero = + reservePrice && !reservePrice.isZero(); + if (reservePriceIsDefinedAndNonZero && !waitingForBestCounterOrder) { throw new Error("Reserve prices may only be set on English auctions."); } - if (!reservePrice?.isZero() && reservePrice?.lt(startAmountWei)) { + if (reservePriceIsDefinedAndNonZero && reservePrice?.lt(startAmountWei)) { throw new Error( "Reserve price must be greater than or equal to the start amount." ); diff --git a/src/types.ts b/src/types.ts index 06cfd715c..8dc1e5949 100644 --- a/src/types.ts +++ b/src/types.ts @@ -77,17 +77,17 @@ export interface EventData { /** * OpenSea API configuration object * @param apiKey Optional key to use for API - * @param networkName `Network` type to use. Defaults to `Network.Main` (mainnet) + * @param chain `Chain` type to use. Defaults to `Chain.Mainnet` (mainnet) * @param apiBaseUrl Optional base URL to use for the API */ export interface OpenSeaAPIConfig { - networkName?: Network; + chain?: Chain; apiKey?: string; apiBaseUrl?: string; } -export enum Network { - Main = "main", +export enum Chain { + Mainnet = "main", Goerli = "goerli", } diff --git a/src/utils/tokens/index.ts b/src/utils/tokens/index.ts index 3b358342d..8febd9fb1 100644 --- a/src/utils/tokens/index.ts +++ b/src/utils/tokens/index.ts @@ -1,6 +1,6 @@ import { tokens as goerliTokens } from "./goerli"; import { tokens as mainTokens } from "./main"; -import { Network } from "../../types"; +import { Chain } from "../../types"; interface Token { name: string; @@ -14,11 +14,11 @@ export interface NetworkTokens { otherTokens: Token[]; } -export const getCanonicalWrappedEther = function (network: Network): Token { - switch (network) { - case Network.Main: +export const getCanonicalWrappedEther = function (chain: Chain): Token { + switch (chain) { + case Chain.Mainnet: return tokens.main.canonicalWrappedEther; - case Network.Goerli: + case Chain.Goerli: return tokens.goerli.canonicalWrappedEther; } }; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 6a246ad08..dbb4ba4d8 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -12,7 +12,7 @@ import { } from "../constants"; import { AssetEvent, - Network, + Chain, OpenSeaAccount, OpenSeaAsset, OpenSeaAssetBundle, @@ -287,8 +287,8 @@ export async function getNonCompliantApprovalAddress( } export const merkleValidatorByNetwork = { - [Network.Main]: MERKLE_VALIDATOR_MAINNET, - [Network.Goerli]: null, + [Chain.Mainnet]: MERKLE_VALIDATOR_MAINNET, + [Chain.Goerli]: null, }; /** diff --git a/test/api/api.spec.ts b/test/api/api.spec.ts index fb71469c4..fd4863372 100644 --- a/test/api/api.spec.ts +++ b/test/api/api.spec.ts @@ -3,39 +3,39 @@ import { suite, test } from "mocha"; import { BAYC_CONTRACT_ADDRESS, BAYC_TOKEN_ID, - mainApi, + mainAPI, MAINNET_API_KEY, - testnetApi, + testnetAPI, } from "../utils/constants"; suite("API", () => { test("API has correct base url", () => { - assert.equal(mainApi.apiBaseUrl, "https://api.opensea.io"); - assert.equal(testnetApi.apiBaseUrl, "https://testnets-api.opensea.io"); + assert.equal(mainAPI.apiBaseUrl, "https://api.opensea.io"); + assert.equal(testnetAPI.apiBaseUrl, "https://testnets-api.opensea.io"); }); test("Includes API key in request", async () => { - const oldLogger = mainApi.logger; + const oldLogger = mainAPI.logger; const logPromise = new Promise((resolve, reject) => { - mainApi.logger = (log) => { + mainAPI.logger = (log) => { try { assert.include(log, `"X-API-KEY":"${MAINNET_API_KEY}"`); resolve(); } catch (e) { reject(e); } finally { - mainApi.logger = oldLogger; + mainAPI.logger = oldLogger; } }; - mainApi.getPaymentTokens({ symbol: "WETH" }); + mainAPI.getPaymentTokens({ symbol: "WETH" }); }); await logPromise; }); test("API fetches fees for an asset", async () => { - const asset = await mainApi.getAsset({ + const asset = await mainAPI.getAsset({ tokenAddress: BAYC_CONTRACT_ADDRESS, tokenId: BAYC_TOKEN_ID, }); @@ -44,7 +44,7 @@ suite("API", () => { }); test("API fetches assets", async () => { - const { assets } = await mainApi.getAssets({ + const { assets } = await mainAPI.getAssets({ asset_contract_address: BAYC_CONTRACT_ADDRESS, order_by: "sale_date", }); @@ -56,7 +56,7 @@ suite("API", () => { test("API handles errors", async () => { // 404 Not found for random token id try { - await mainApi.get(`/asset/${BAYC_CONTRACT_ADDRESS}/202020202020`); + await mainAPI.get(`/asset/${BAYC_CONTRACT_ADDRESS}/202020202020`); } catch (error) { assert.include((error as Error).message, "Not found"); } diff --git a/test/api/fulfillment.spec.ts b/test/api/fulfillment.spec.ts index e3fa92ffa..d46504c27 100644 --- a/test/api/fulfillment.spec.ts +++ b/test/api/fulfillment.spec.ts @@ -1,11 +1,11 @@ import "../utils/setup"; import { assert } from "chai"; import { suite, test } from "mocha"; -import { mainApi } from "../utils/constants"; +import { mainAPI } from "../utils/constants"; suite("Generating fulfillment data", () => { test(`Generate fulfillment data for listing`, async () => { - const order = await mainApi.getOrder({ + const order = await mainAPI.getOrder({ protocol: "seaport", side: "ask", }); @@ -14,7 +14,7 @@ suite("Generating fulfillment data", () => { return; } - const fulfillment = await mainApi.generateFulfillmentData( + const fulfillment = await mainAPI.generateFulfillmentData( "0x000000000000000000000000000000000000dEaD", order.orderHash, order.protocolAddress, @@ -25,7 +25,7 @@ suite("Generating fulfillment data", () => { }); test(`Generate fulfillment data for offer`, async () => { - const order = await mainApi.getOrder({ + const order = await mainAPI.getOrder({ protocol: "seaport", side: "bid", }); @@ -34,7 +34,7 @@ suite("Generating fulfillment data", () => { return; } - const fulfillment = await mainApi.generateFulfillmentData( + const fulfillment = await mainAPI.generateFulfillmentData( "0x000000000000000000000000000000000000dEaD", order.orderHash, order.protocolAddress, diff --git a/test/api/getOrders.spec.ts b/test/api/getOrders.spec.ts index b3357cb43..64ac0b0b6 100644 --- a/test/api/getOrders.spec.ts +++ b/test/api/getOrders.spec.ts @@ -5,14 +5,14 @@ import { OrderSide } from "src/orders/types"; import { BAYC_CONTRACT_ADDRESS, BAYC_TOKEN_IDS, - mainApi, + mainAPI, } from "../utils/constants"; import { expectValidOrder } from "../utils/utils"; suite("Getting orders", () => { ["ask", "bid"].forEach((side) => { test(`getOrder should return a single order > ${side}`, async () => { - const order = await mainApi.getOrder({ + const order = await mainAPI.getOrder({ protocol: "seaport", side, }); @@ -22,7 +22,7 @@ suite("Getting orders", () => { test(`getOrder should throw if no order found`, async () => { await expect( - mainApi.getOrder({ + mainAPI.getOrder({ protocol: "seaport", side: "ask", maker: "0x000000000000000000000000000000000000dEaD", @@ -34,7 +34,7 @@ suite("Getting orders", () => { ["ask", "bid"].forEach((side) => { test(`getOrders should return a list of orders > ${side}`, async () => { - const { orders, next, previous } = await mainApi.getOrders({ + const { orders, next, previous } = await mainAPI.getOrders({ protocol: "seaport", side, tokenIds: BAYC_TOKEN_IDS, diff --git a/test/integration/README.md b/test/integration/README.md index 70a0b1d40..108452cfc 100644 --- a/test/integration/README.md +++ b/test/integration/README.md @@ -7,20 +7,20 @@ These tests were built to test the order posting functionality of the SDK. Signi Environment variables for integration tests are set using `.env`. This file is not in the source code for the repository so you will need to create a file with the following fields: ```bash -API_KEY = "" # OpenSea API Key +OPENSEA_API_KEY = "" # OpenSea API Key ALCHEMY_API_KEY = "" # Alchemy API Key WALLET_PRIV_KEY = "0x" # Wallet private key # The following needs to be an NFT owned by the wallet address derived from WALLET_PRIV_KEY SELL_ORDER_CONTRACT_ADDRESS = "0x" -SELL_ORDER_TOKEN_ID = "" +SELL_ORDER_TOKEN_ID = "123" ``` Optional: ```bash -OFFER_AMOUNT = "" # Defaults to 0.004 -LISTING_AMOUNT = "" # Defaults to 40 +OFFER_AMOUNT = "0.004" # Defaults to 0.004 +LISTING_AMOUNT = "40" # Defaults to 40 ``` #### WETH Tests diff --git a/test/integration/postOrder.spec.ts b/test/integration/postOrder.spec.ts index c1c4391e1..848b21c7e 100644 --- a/test/integration/postOrder.spec.ts +++ b/test/integration/postOrder.spec.ts @@ -1,7 +1,5 @@ import { expect } from "chai"; import { suite, test } from "mocha"; -import { Network } from "src"; -import { WETH_ADDRESS_BY_NETWORK } from "src/constants"; import { LISTING_AMOUNT, TOKEN_ADDRESS, @@ -9,6 +7,7 @@ import { sdk, walletAddress, } from "./setup"; +import { WETH_ADDRESS_BY_NETWORK } from "../../src/constants"; import { OFFER_AMOUNT } from "../utils/constants"; import { expectValidOrder } from "../utils/utils"; @@ -16,7 +15,7 @@ suite("SDK: order posting", () => { test("Post Buy Order", async () => { const buyOrder = { accountAddress: walletAddress, - startAmount: OFFER_AMOUNT ?? "0.004", + startAmount: OFFER_AMOUNT, asset: { tokenAddress: "0x1a92f7381b9f03921564a437210bb9396471050c", tokenId: "2288", @@ -35,7 +34,7 @@ suite("SDK: order posting", () => { const sellOrder = { accountAddress: walletAddress, - startAmount: LISTING_AMOUNT ?? "40", + startAmount: LISTING_AMOUNT, asset: { tokenAddress: TOKEN_ADDRESS, tokenId: TOKEN_ID, @@ -49,16 +48,13 @@ suite("SDK: order posting", () => { test("Post collection offer", async () => { const collection = await sdk.api.getCollection("cool-cats-nft"); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const network: Network = (sdk as any)._networkName; - + const paymentTokenAddress = WETH_ADDRESS_BY_NETWORK[sdk.chain]; const postOrderRequest = { collectionSlug: collection.slug, accountAddress: walletAddress, - amount: OFFER_AMOUNT ?? "0.004", + amount: OFFER_AMOUNT, quantity: 1, - paymentTokenAddress: WETH_ADDRESS_BY_NETWORK[network], + paymentTokenAddress, }; const offerResponse = await sdk.createCollectionOffer(postOrderRequest); diff --git a/test/integration/setup.ts b/test/integration/setup.ts index a71538696..f38b040e3 100644 --- a/test/integration/setup.ts +++ b/test/integration/setup.ts @@ -1,6 +1,6 @@ import { ethers } from "ethers"; import { OpenSeaSDK } from "../../src/sdk"; -import { Network } from "../../src/types"; +import { Chain } from "../../src/types"; import { MAINNET_API_KEY, RPC_PROVIDER, @@ -15,7 +15,7 @@ for (const envVar of ["WALLET_PRIV_KEY"]) { export const TOKEN_ADDRESS = process.env.SELL_ORDER_CONTRACT_ADDRESS; export const TOKEN_ID = process.env.SELL_ORDER_TOKEN_ID; -export const LISTING_AMOUNT = process.env.LISTING_AMOUNT; +export const LISTING_AMOUNT = process.env.LISTING_AMOUNT ?? "40"; export const ETH_TO_WRAP = process.env.ETH_TO_WRAP; const wallet = new ethers.Wallet(WALLET_PRIV_KEY as string, RPC_PROVIDER); @@ -24,7 +24,7 @@ export const walletAddress = wallet.address; export const sdk = new OpenSeaSDK( RPC_PROVIDER, { - networkName: Network.Main, + chain: Chain.Mainnet, apiKey: MAINNET_API_KEY, }, (line) => console.info(`MAINNET: ${line}`), diff --git a/test/integration/wrapEth.spec.ts b/test/integration/wrapEth.spec.ts index b120f951a..0f9ef3aad 100644 --- a/test/integration/wrapEth.spec.ts +++ b/test/integration/wrapEth.spec.ts @@ -2,7 +2,7 @@ import { assert } from "chai"; import { parseEther } from "ethers/lib/utils"; import { describe, test } from "mocha"; import { ETH_TO_WRAP, sdk, walletAddress } from "./setup"; -import { Network, TokenStandard } from "../../src/types"; +import { Chain, TokenStandard } from "../../src/types"; import { getCanonicalWrappedEther } from "../../src/utils/tokens"; describe("SDK: WETH", () => { @@ -13,7 +13,7 @@ describe("SDK: WETH", () => { } const wethAsset = { - tokenAddress: getCanonicalWrappedEther(Network.Main).address, + tokenAddress: getCanonicalWrappedEther(Chain.Mainnet).address, tokenId: null, tokenStandard: TokenStandard.ERC20, }; diff --git a/test/sdk/fees.spec.ts b/test/sdk/fees.spec.ts index acf7d65df..67301a03b 100644 --- a/test/sdk/fees.spec.ts +++ b/test/sdk/fees.spec.ts @@ -1,7 +1,7 @@ import { assert } from "chai"; import { before, suite, test } from "mocha"; import { OpenSeaSDK } from "../../src/index"; -import { Network, OpenSeaAsset, OrderSide } from "../../src/types"; +import { Chain, OpenSeaAsset, OrderSide } from "../../src/types"; import { feesToBasisPoints } from "../../src/utils/utils"; import { BAYC_CONTRACT_ADDRESS, @@ -13,7 +13,7 @@ import { const client = new OpenSeaSDK( RPC_PROVIDER, { - networkName: Network.Main, + chain: Chain.Mainnet, apiKey: MAINNET_API_KEY, }, (line) => console.info(`MAINNET: ${line}`) diff --git a/test/sdk/misc.spec.ts b/test/sdk/misc.spec.ts index 4119076b1..7e0033045 100644 --- a/test/sdk/misc.spec.ts +++ b/test/sdk/misc.spec.ts @@ -6,7 +6,7 @@ import { SHARED_STOREFRONT_ADDRESS_GOERLI, } from "../../src/constants"; import { OpenSeaSDK } from "../../src/index"; -import { Network } from "../../src/types"; +import { Chain } from "../../src/types"; import { getAddressAfterRemappingSharedStorefrontAddressToLazyMintAdapterAddress } from "../../src/utils/utils"; import { DAPPER_ADDRESS, @@ -17,7 +17,7 @@ import { const client = new OpenSeaSDK( RPC_PROVIDER, { - networkName: Network.Main, + chain: Chain.Mainnet, apiKey: MAINNET_API_KEY, }, (line) => console.info(`MAINNET: ${line}`) diff --git a/test/sdk/orders.spec.ts b/test/sdk/orders.spec.ts index 60b21e1d5..121be3ddd 100644 --- a/test/sdk/orders.spec.ts +++ b/test/sdk/orders.spec.ts @@ -1,13 +1,13 @@ import { assert } from "chai"; import { suite, test } from "mocha"; import { OpenSeaSDK } from "../../src/index"; -import { Network } from "../../src/types"; +import { Chain } from "../../src/types"; import { MAINNET_API_KEY, RPC_PROVIDER } from "../utils/constants"; const client = new OpenSeaSDK( RPC_PROVIDER, { - networkName: Network.Main, + chain: Chain.Mainnet, apiKey: MAINNET_API_KEY, }, (line) => console.info(`MAINNET: ${line}`) diff --git a/test/utils/constants.ts b/test/utils/constants.ts index a7c8d5731..091c9a581 100644 --- a/test/utils/constants.ts +++ b/test/utils/constants.ts @@ -1,8 +1,8 @@ import { ethers } from "ethers"; import { OpenSeaAPI } from "../../src/api"; -import { Network } from "../../src/types"; +import { Chain } from "../../src/types"; -export const MAINNET_API_KEY = process.env.API_KEY; +export const MAINNET_API_KEY = process.env.OPENSEA_API_KEY; export const WALLET_PRIV_KEY = process.env.WALLET_PRIV_KEY; const ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY; @@ -10,24 +10,24 @@ export const RPC_PROVIDER = new ethers.providers.JsonRpcProvider( `https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}` ); -export const mainApi = new OpenSeaAPI( +export const OFFER_AMOUNT = process.env.OFFER_AMOUNT ?? "0.004"; +export const DAPPER_ADDRESS = "0x4819352bd7fadcCFAA8A2cDA4b2825a9ec51417c"; +export const BAYC_CONTRACT_ADDRESS = + "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"; +export const BAYC_TOKEN_ID = "1"; +export const BAYC_TOKEN_IDS = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]; + +export const mainAPI = new OpenSeaAPI( { apiKey: MAINNET_API_KEY, - networkName: Network.Main, + chain: Chain.Mainnet, }, process.env.DEBUG ? console.log : undefined ); -export const testnetApi = new OpenSeaAPI( +export const testnetAPI = new OpenSeaAPI( { - networkName: Network.Goerli, + chain: Chain.Goerli, }, process.env.DEBUG ? console.log : undefined ); - -export const OFFER_AMOUNT = process.env.OFFER_AMOUNT; -export const DAPPER_ADDRESS = "0x4819352bd7fadcCFAA8A2cDA4b2825a9ec51417c"; -export const BAYC_CONTRACT_ADDRESS = - "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"; -export const BAYC_TOKEN_ID = "1"; -export const BAYC_TOKEN_IDS = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];