2

I am following the tutorial to send a token to a specific recipient (https://spl.solana.com/token#example-transferring-tokens-to-an-explicit-recipient-token-account). Below is the code that works where I can send the transactions 1 by one, but it would take forever for large amounts of wallets.

const walletsTo = [wallet1, 'wallet2, 'wallet3'...]


  walletsTo.forEach(async function (addressTo) {

    const toWallet = new PublicKey(addressTo);

    const toTokenAccount = await getOrCreateAssociatedTokenAccount(
      connection,
      fromWallet,
      new PublicKey(mintToken),
      toWallet
    );

    let signature = await transfer(
      connection,
      fromWallet,
      fromTokenAccount.address,
      toTokenAccount.address,
      fromWallet.publicKey,
      1
    );

    console.log("Signature tx: ", signature);
  });

Is there a way to do the same but send the token to dozens or hundreds of recipients by batching the transactions?

EDIT: Based on Rudra Sarkar help, I was able to edit the code and have it work on Devnet as below, but still getting some Server responded with 429 Too Many Requests. Retrying after 500ms delay...

import {
  getOrCreateAssociatedTokenAccount,
  createTransferInstruction,
  Account,
} from "@solana/spl-token";
import {
  Connection,
  Keypair,
  PublicKey,
  clusterApiUrl,
  Transaction,
  TransactionInstruction,
} from "@solana/web3.js";

// Your passphrase in Uint8 format
const fromWalletSecretPassphrase = [];

//List of wallets
const walletsTo = [
  "wallet1",
  "wallet2",
];

const mintToken = "yourMintTokenAddress";
const transfer_amount = 30;
const numberDecimals = 6;
const batchSize = 1; // adjust your batch size according to your need

const fromWallet = Keypair.fromSecretKey(
  new Uint8Array(fromWalletSecretPassphrase)
);

const connection = new Connection(clusterApiUrl("devnet"), "confirmed");

const getBatchInstructions = async (
  batch: string[],
  fromTokenAccount: Account
) => {
  const instructions = await Promise.all(
    batch.map(async (addressTo: string) => {
      const toWallet = new PublicKey(addressTo);
      const toTokenAccount = await getOrCreateAssociatedTokenAccount(
        connection,
        fromWallet,
        new PublicKey(mintToken),
        toWallet
      );

      return createTransferInstruction(
        fromTokenAccount.address,
        toTokenAccount.address,
        fromWallet.publicKey,
        transfer_amount * Math.pow(10, numberDecimals)
      );
    })
  );

  return instructions;
};

const sendBatches = async () => {
  const batches: TransactionInstruction[][] = [];
  // let batches = [];

  const fromTokenAccount = await getOrCreateAssociatedTokenAccount(
    connection,
    fromWallet,
    new PublicKey(mintToken),
    fromWallet.publicKey
  );

  console.log(
    `Sending ${transfer_amount} ${mintToken} from ${fromWallet.publicKey.toString()}`
  );

  for (let i = 0; i < walletsTo.length; i += batchSize) {
    const batch = walletsTo.slice(i, i + batchSize);
    const instructions = await getBatchInstructions(batch, fromTokenAccount);
    batches.push(instructions);
  }

  for (const batch of batches) {
    const transaction = new Transaction().add(...batch);
    transaction.feePayer = fromWallet.publicKey;
    transaction.recentBlockhash = (
      await connection.getLatestBlockhash("confirmed")
    ).blockhash;

    transaction.sign({
      publicKey: fromWallet.publicKey,
      secretKey: new Uint8Array(fromWalletSecretPassphrase),
    });

    const txid = await connection.sendRawTransaction(transaction.serialize());
    console.log("Batch transaction:", txid);
  }
};

sendBatches();

2 Answers 2

1

The code is inefficient for sending tokens to a large number of recipients because it processes each transfer separately because it sending the code in a synchronous way. To fix this problem what you can do is send multiple transfers to the Solana network as a single transaction using batches instead transfer it separately.

Here's a code snippet that may help you:

const web3 = require('@solana/web3.js');
const splToken = require('@solana/spl-token');

// Initialize connection, wallet, and token accounts for your application here

const walletsTo = ['wallet1', 'wallet2', 'wallet3'];

const getBatchInstructions = async (batch) => {
  const instructions = await Promise.all(batch.map(async addressTo => {
    const toWallet = new web3.PublicKey(addressTo);
    const toTokenAccount = await getOrCreateAssociatedTokenAccount(
      connection,
      fromWallet,
      new PublicKey(mintToken),
      toWallet
    );

    return splToken.createTransferInstruction(
      fromTokenAccount.address,
      toTokenAccount.address,
      fromWallet.publicKey,
      1
    );
  }));

  return instructions;
};

const batches = [];
const batchSize = 10; // adjust your batch size according to your need

for (let i = 0; i < walletsTo.length; i += batchSize) {
  const batch = walletsTo.slice(i, i + batchSize);
  const instructions = await getBatchInstructions(batch);
  batches.push(instructions);
}

for (const batch of batches) {
  const transaction = new web3.Transaction().add(...batch);
  transaction.feePayer = fromWallet.publicKey;
  transaction.recentBlockhash = (await connection.getRecentBlockhash()).blockhash;

  const signedTransaction = await fromWallet.signTransaction(transaction);
  const txid = await connection.sendRawTransaction(signedTransaction.serialize());
  console.log('Batch transaction:', txid);
}

let me know if it is working for you, If it works mark this answer as accepted.

4
  • thank you. I was able to tweak the code a bit and have it work on Devnet. However, on Mainnet, I am getting "Server responded with 429 Too Many Requests. Retrying after 500ms delay..." so seems like it's still sending it separately? I'll post my complete code below.
    – bigbulb
    Commented Mar 31 at 0:47
  • How many batch are you sending? 429 meaning rate limiting request they have limitation on the request. Commented Mar 31 at 5:13
  • Got it resolved, I needed a paid RPC API to be able to send more. Thanks a lot for the help.
    – bigbulb
    Commented Mar 31 at 17:34
  • Awesome, I forget to mention about the paid RPC API anyway you figured it out :) Commented Apr 1 at 9:57
0

You can Multisender to send tokens to multiple wallets, It supports spl, spl22 as well as SOL. It works up to 10k address, and sends in few seconds to few mins.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.