require("dotenv").config(); const fetch = require("cross-fetch"); const readline = require('readline'); const { AccountId, Client, PrivateKey, TransferTransaction, TokenId, } = require("@hashgraph/sdk"); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); // This program will scan the given NFT token IDs in your wallet and ask you how many you'd like to send when you run it. // Your keys will stay local, they're needed to interact with the hashgraph, but no part of this program sends that information anywhere. // Direct input for program behaviors const operatorId = "0.0.XXXX"; // Replace with your source wallet ID const operatorKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // Replace with your source wallet private key const retry_milliseconds = 500; //don't worry about this const chunkSize = 5; //how many NFTs to send in a chunk var account = "0.0.XXXXXX"; // Replace with the wallet ID of the target wallet var token_ids = ["0.0.XXXXXX", "0.0.XXXXXX"]; // Each entry in this array is the token ID of an NFT collection to send, just add commas and more entries for multiples. let client = Client.forName({ "35.237.200.180:50211": "0.0.3" }).setOperator( AccountId.fromString(operatorId), PrivateKey.fromString(operatorKey), ); async function fetchJSON(url) { for (var retry = 0; retry < 10; retry++) { try { let res = await fetch(url); if (res.status != 200) { console.log("Status !=200 in Fetch()", url); throw new Error("Status !=200"); } const raw = await res.json(); return raw; } catch (err) { console.log(err); } await new Promise(resolve => setTimeout(resolve, retry_milliseconds)); console.log("Retrying to fetch JSON from Hedera mirror..."); } } async function transferTokenOnly(tokenId, address, memo = "HDH Freebies", serials = []) { try { console.log(`Initiating transfer of ${tokenId} to ${address}`); let tokenTransferTx = new TransferTransaction().setTransactionMemo(memo); for (let serial of serials) { tokenTransferTx.addNftTransfer(tokenId, serial, client.operatorAccountId, address); } tokenTransferTx = await tokenTransferTx.freezeWith(client).sign(PrivateKey.fromString(process.env.OPERATOR_KEY)); let tokenTransferSubmit = await tokenTransferTx.execute(client); let tokenTransferRx = await tokenTransferSubmit.getReceipt(client); console.log(`NFT transferred: ${tokenTransferRx.status}`); return tokenTransferRx.status.toString(); } catch (e) { console.log(e); return e.toString(); } } async function machineGun(token, nftCount) { let url_serials = `https://mainnet-public.mirrornode.hedera.com/api/v1/tokens/${token}/nfts?limit=5000&account.id=${client.operatorAccountId}&order=asc`; let available_serials_json = await fetchJSON(url_serials); let available_serials = available_serials_json["nfts"].map(nft => nft.serial_number); let chunks = []; for (let i = 0; i < available_serials.length; i += chunkSize) { chunks.push(available_serials.slice(i, Math.min(i + chunkSize, available_serials.length))); } let transferPromises = chunks.map(chunk => transferTokenOnly(token, account, "HDH Freebies", chunk)); await Promise.all(transferPromises); console.log('All transfer transactions initiated.'); } async function main() { let nftChoices = []; for (const tokenId of token_ids) { let url_serials = `https://mainnet-public.mirrornode.hedera.com/api/v1/tokens/${tokenId}/nfts?limit=5000&account.id=${client.operatorAccountId}&order=asc`; let available_serials_json = await fetchJSON(url_serials); let available_serials_count = available_serials_json["nfts"].length; console.log(`Token ID: ${tokenId}`); console.log(`Available NFTs to send: ${available_serials_count}`); let nftsend = await new Promise(resolve => { rl.question('How many NFTs do you want to send? ', resolve); }); nftChoices.push({ tokenId, nftsend: parseInt(nftsend) }); } for (const choice of nftChoices) { if (choice.nftsend > 0) { await machineGun(choice.tokenId, choice.nftsend); } } rl.close(); console.log("All operations completed."); process.exit(); } main();