NodeJS对接OpenSea NFT测试如何获取0-500区间不重复tokenId
OpenSea批量上架tokenId去重实现方案
核心实现逻辑
维护全局可用tokenId池,每次取用后直接从池中移除,从根源避免重复生成,同时处理池耗尽后的停止逻辑,避免无效请求。
修改后完整代码
const opensea = require("opensea-js"); const OpenSeaPort = opensea.OpenSeaPort; const Network = opensea.Network; const MnemonicWalletSubprovider = require("@0x/subproviders") .MnemonicWalletSubprovider; const RPCSubprovider = require("web3-provider-engine/subproviders/rpc"); const Web3ProviderEngine = require("web3-provider-engine"); const MNEMONIC = 'SECRET'; const NODE_API_KEY = 'MyKEY'; const isInfura = true; const FACTORY_CONTRACT_ADDRESS = '0x745e6b0CAd1eDc72647B9fFec5C69e4608f73ab2'; const NFT_CONTRACT_ADDRESS = '0x745e6b0CAd1eDc72647B9fFec5C69e4608f73ab2'; const OWNER_ADDRESS = '0xaEBB892210eB23C47b1e710561c7BC4CFA63A62e'; const NETWORK = 'rinkeby'; const API_KEY = ""; // 高频率请求建议填写API key // 新增:全局可用tokenId池,初始填充0-499所有整数 const availableTokenIds = Array.from({length: 500}, (_, i) => i); // 新增:定时器实例,用于控制任务启停 let publishTimer = null; if (!MNEMONIC || !NODE_API_KEY || !NETWORK || !OWNER_ADDRESS) { console.error( "请配置助记词、Alchemy/Infura密钥、所有者地址、网络、API key、NFT合约地址和工厂合约地址" ); process.exit(); } if (!FACTORY_CONTRACT_ADDRESS && !NFT_CONTRACT_ADDRESS) { console.error("请配置工厂合约地址或NFT合约地址"); process.exit(); } const BASE_DERIVATION_PATH = `44'/60'/0'/0`; const mnemonicWalletSubprovider = new MnemonicWalletSubprovider({ mnemonic: MNEMONIC, baseDerivationPath: BASE_DERIVATION_PATH, }); const network = NETWORK === "mainnet" || NETWORK === "live" ? "mainnet" : "rinkeby"; const infuraRpcSubprovider = new RPCSubprovider({ rpcUrl: isInfura ? "https://" + network + ".infura.io/v3/" + NODE_API_KEY : "https://eth-" + network + ".alchemyapi.io/v2/" + NODE_API_KEY, }); const providerEngine = new Web3ProviderEngine(); providerEngine.addProvider(mnemonicWalletSubprovider); providerEngine.addProvider(infuraRpcSubprovider); providerEngine.start(); const seaport = new OpenSeaPort( providerEngine, { networkName: NETWORK === "mainnet" || NETWORK === "live" ? Network.Main : Network.Rinkeby, apiKey: API_KEY, }, (arg) => console.log(arg) ); // 新增:从未使用的tokenId池中随机取一个id,取完即移除 function getUnusedTokenId() { if (availableTokenIds.length === 0) return null; const randomIndex = Math.floor(Math.random() * availableTokenIds.length); return availableTokenIds.splice(randomIndex, 1)[0]; } async function sellTheItems() { // 取出未使用的tokenId const tokenId = getUnusedTokenId(); // 没有可用id时停止任务 if (tokenId === null) { clearInterval(publishTimer); console.log("0-499范围内所有tokenId均已上架,任务结束"); process.exit(); } console.log("正在创建固定价格售卖订单..."); const fixedPriceSellOrder = await seaport.createSellOrder({ asset: { tokenId: tokenId.toString(), tokenAddress: NFT_CONTRACT_ADDRESS, }, startAmount: 0.36, expirationTime: 0, accountAddress: OWNER_ADDRESS, }); console.log( `固定价格售卖订单创建成功! ${fixedPriceSellOrder.asset.openseaLink}\n` ); } async function doItAgain() { // 新增await避免异步并发导致的重复取值问题 await sellTheItems(); } // 把定时器赋值给全局变量方便控制 publishTimer = setInterval(doItAgain, 5000);
关键改动说明
- 移除原随机生成tokenId的逻辑,改为从预先生成的可用id池中抽取,抽取后立即删除该id,保证不会重复
- 给异步上架逻辑加了
await控制执行顺序,避免并发请求导致的id重复问题 - 新增id池耗尽后的自动停止逻辑,避免无效请求
- 如果你需要程序重启后仍然不重复使用之前用过的id,可以把已使用的id写入本地json文件存储,启动时从可用池中过滤掉已使用的id即可。
内容的提问来源于stack exchange,提问作者Leandro Machado
相关产品推荐
相关产品推荐

