1. const fetch = require('node-fetch');
  2. const { ethers } = require('ethers');
  3.  
  4. // .env must contain:
  5. // - OWNER_ADDRESS
  6. // - OWNER_PRIVATE_KEY
  7. // - POLYGON_ALCHEMY_KEY
  8. // - OPENSEA_API_KEY
  9. require('dotenv').config();
  10.  
  11. const OPENSEA_CONDUIT_KEY = '0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000';
  12. const CROSS_CHAIN_SEAPORT_ADDRESS = '0x00000000006c3852cbef3e08e8df289169ede581';
  13. const SEAPORT_CONTRACT_NAME = 'Seaport';
  14. const SEAPORT_CONTRACT_VERSION = '1.1';
  15.  
  16. const EIP_712_ORDER_TYPE = {
  17.     OrderComponents: [
  18.         { name: 'offerer', type: 'address' },
  19.         { name: 'zone', type: 'address' },
  20.         { name: 'offer', type: 'OfferItem[]' },
  21.         { name: 'consideration', type: 'ConsiderationItem[]' },
  22.         { name: 'orderType', type: 'uint8' },
  23.         { name: 'startTime', type: 'uint256' },
  24.         { name: 'endTime', type: 'uint256' },
  25.         { name: 'zoneHash', type: 'bytes32' },
  26.         { name: 'salt', type: 'uint256' },
  27.         { name: 'conduitKey', type: 'bytes32' },
  28.         { name: 'counter', type: 'uint256' },
  29.     ],
  30.     OfferItem: [
  31.         { name: 'itemType', type: 'uint8' },
  32.         { name: 'token', type: 'address' },
  33.         { name: 'identifierOrCriteria', type: 'uint256' },
  34.         { name: 'startAmount', type: 'uint256' },
  35.         { name: 'endAmount', type: 'uint256' },
  36.     ],
  37.     ConsiderationItem: [
  38.         { name: 'itemType', type: 'uint8' },
  39.         { name: 'token', type: 'address' },
  40.         { name: 'identifierOrCriteria', type: 'uint256' },
  41.         { name: 'startAmount', type: 'uint256' },
  42.         { name: 'endAmount', type: 'uint256' },
  43.         { name: 'recipient', type: 'address' },
  44.     ],
  45. };
  46.  
  47. const ITEM_TYPE = {
  48.     NATIVE: 0,
  49.     ERC20: 1,
  50.     ERC721: 2,
  51.     ERC1155: 3,
  52.     ERC721_WITH_CRITERIA: 4,
  53.     ERC1155_WITH_CRITERIA: 5,
  54. }
  55.  
  56. const POLYGON_ALCHEMY_URL = 'https://polygon-mainnet.g.alchemy.com/v2/' + process.env.POLYGON_ALCHEMY_KEY;
  57. const POLYGON_CHAIN_ID = 137
  58. const OPENSEA_API_URL = 'https://api.opensea.io/v2/orders/matic/seaport/listings';
  59.  
  60. //struct OrderParameters {
  61. //  address offerer;
  62. //  address zone;
  63. //  struct OfferItem[] offer;
  64. //  struct ConsiderationItem[] consideration;
  65. //  enum OrderType orderType;
  66. //  uint256 startTime;
  67. //  uint256 endTime;
  68. //  bytes32 zoneHash;
  69. //  uint256 salt;
  70. //  bytes32 conduitKey;
  71. //  uint256 totalOriginalConsiderationItems;
  72. //}
  73.  
  74. /**
  75.  * Submits a request to your provider to sign the order.
  76.  * @param {Object} signer
  77.  * @param {Object} orderParameters - standard OrderParameters struct (see above)
  78.  * @param {number} counter - counter of the offerer
  79.  * @returns {string} the EIP-2098 order signature
  80.  */
  81. async function signOrder(signer, orderParameters, counter)
  82. {
  83.     const domainData = {
  84.         name: SEAPORT_CONTRACT_NAME,
  85.         version: SEAPORT_CONTRACT_VERSION,
  86.         chainId: POLYGON_CHAIN_ID,
  87.         verifyingContract: CROSS_CHAIN_SEAPORT_ADDRESS,
  88.     };
  89.     const orderComponents = {
  90.         ...orderParameters,
  91.         counter,
  92.     };
  93.     const signature = await signer._signTypedData(
  94.         domainData,
  95.         EIP_712_ORDER_TYPE,
  96.         orderComponents
  97.     );
  98.     // Use EIP-2098 compact signatures to save gas. https://eips.ethereum.org/EIPS/eip-2098
  99.     return ethers.utils.splitSignature(signature).compact;
  100. }
  101.  
  102. /**
  103.  * Retrieves listings on OpenSea
  104.  * See https://docs.opensea.io/v2.0/reference/retrieve-listings
  105.  * @param {string} nftContractAddress
  106.  * @param {string} nftTokenId
  107.  * @returns {Object} API result (Promise)
  108.  */
  109. function getPolygonNFTListings(nftContractAddress, nftTokenId)
  110. {
  111.     return fetch(`${OPENSEA_API_URL}?asset_contract_address=${nftContractAddress}&token_ids=${nftTokenId}`, {
  112.         method: 'GET',
  113.         headers: {'X-API-KEY': process.env.OPENSEA_API_KEY},
  114.     })
  115.     .then((data) => data.json());
  116. }
  117.  
  118. /**
  119.  * Creates new listing on OpenSea
  120.  * See https://docs.opensea.io/v2.0/reference/create-an-order
  121.  * @param {Object} signer
  122.  * @param {string} nftContractAddress
  123.  * @param {string} nftTokenId
  124.  * @param {number} nftItemType - 2 for ERC721, 3 for ERC1155
  125.  * @param {string} nftPriceEth
  126.  * @param {number} startTime - Unix timestamp, in seconds
  127.  * @param {number} endTime - Unix timestamp, in seconds
  128.  * @returns {Object} API result (Promise)
  129.  */
  130. async function listPolygonNFT(signer, nftContractAddress, nftTokenId, nftItemType, nftPriceEth, startTime, endTime)
  131. {
  132.     // 2.5 % fee
  133.     const priceNet = (ethers.utils.parseEther(nftPriceEth) * .975).toString();
  134.     const priceFee = (ethers.utils.parseEther(nftPriceEth) * .025).toString();
  135.  
  136.     const orderParameters = {
  137.         offerer: process.env.OWNER_ADDRESS,
  138.         zone: '0x0000000000000000000000000000000000000000',
  139.         offer: [
  140.             {
  141.                 itemType: nftItemType,
  142.                 token: nftContractAddress,
  143.                 identifierOrCriteria: nftTokenId,
  144.                 startAmount: '1',
  145.                 endAmount: '1',
  146.             },
  147.         ],
  148.         consideration: [
  149.             {
  150.                 itemType: 1,
  151.                 token: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
  152.                 identifierOrCriteria: '0',
  153.                 startAmount: priceNet,
  154.                 endAmount: priceNet,
  155.                 recipient: process.env.OWNER_ADDRESS,
  156.             },
  157.             {
  158.                 itemType: 1,
  159.                 token: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
  160.                 identifierOrCriteria: '0',
  161.                 startAmount: priceFee,
  162.                 endAmount: priceFee,
  163.                 recipient: '0x0000a26b00c1F0DF003000390027140000fAa719',
  164.             },
  165.         ],
  166.         orderType: 1,
  167.         startTime,
  168.         endTime,
  169.         zoneHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
  170.         salt: 0,
  171.         conduitKey: OPENSEA_CONDUIT_KEY,
  172.         totalOriginalConsiderationItems: 2,
  173.     };
  174.  
  175.     const counter = 0;
  176.  
  177.     const signature = await signOrder(
  178.         signer,
  179.         orderParameters,
  180.         counter,
  181.     );
  182.  
  183.     const order = {
  184.         parameters: { ...orderParameters, counter },
  185.         signature,
  186.     };
  187.  
  188.     return fetch(OPENSEA_API_URL, {
  189.         method: 'POST',
  190.         headers: {
  191.             'X-API-KEY': process.env.OPENSEA_API_KEY,
  192.             'Accept': 'application/json',
  193.             'Content-Type': 'application/json',
  194.         },
  195.         body: JSON.stringify(order),
  196.     })
  197.     .then((data) => data.json());
  198. }
  199.  
  200. //######################################
  201. // Example:
  202. // list some Polygon ERC721 NFT for 0.005 ETH on OpenSea, starting right now, and ending in 7 days
  203. //######################################
  204.  
  205. const provider = new ethers.providers.JsonRpcProvider(POLYGON_ALCHEMY_URL);
  206. const signer = new ethers.Wallet(process.env.OWNER_PRIVATE_KEY, provider);
  207.  
  208. const nftContractAddress = '0x........................................';
  209. const nftTokenId = '1';
  210. const nftItemType = ITEM_TYPE.ERC721;
  211. const nftPriceEth = '0.005';
  212. const startTime = Math.floor(new Date().getTime() / 1000);
  213. const endTime = startTime + 7 * 24 * 3600;
  214.  
  215. listPolygonNFT(signer, nftContractAddress, nftTokenId, nftItemType, nftPriceEth, startTime, endTime)
  216. .then(result => {
  217.     console.log(result);
  218. })
  219. .catch((error) => {
  220.     console.error(error);
  221. });
  222.  
  223. //######################################
  224. // Example:
  225. // get existing listings for some Polygon NFT on OpenSea
  226. //######################################
  227.  
  228. //getPolygonNFTListings(nftContractAddress, nftTokenId)
  229. //.then(result => {
  230. //    console.log(result);
  231. //})
  232. //.catch((error) => {
  233. //    console.error(error);
  234. //});
  235.  
[raw code]