3

As a newbie dev, I tried to write an airdrop script using AI but I'm failing to get it to work.

My goal is to be able to send an equal amount of Solana to multiple wallet addresses listed in a .txt file. Can anyone help troubleshoot on this? I keep getting TypeErrors.

const solanaWeb3 = require('@solana/web3.js')

// Replace with your own mnemonic and wallet address
const MNEMONIC = 'password password password password password password password password password password password password'
const WALLET_ADDRESS = 'walletaddresswouldgohere69696969'

// Replace with the path to the wallets file
const WALLETS_FILE_PATH = '/Users/Documents/Airdrops/wallets.txt'

// Replace with the amount of Solana to airdrop
const AIRDROP_AMOUNT = 0.0001

async function airdrop() {
  // Initialize the wallet
  const wallet = await solanaWeb3.wallet.fromMnemonic(MNEMONIC)

  // Set the wallet address
  wallet.setAddress(WALLET_ADDRESS)

  // Initialize the result logs
  const logs = {
    cancelled: [],
    failed: [],
    successful: [],
    unconfirmed: [],
  }

  // Read the wallets file
  const wallets = fs.readFileSync(WALLETS_FILE_PATH, 'utf-8')

  // Split the file content into an array of addresses
  const recipientAddresses = wallets.split('\n')

  // Iterate over the recipient addresses
  for (const recipientAddress of recipientAddresses) {
    // Airdrop the Solana
    const result = await wallet.send(recipientAddress, AIRDROP_AMOUNT)

    // Check the transaction status and add it to the appropriate log
    if (result.isCancelled()) {
      logs.cancelled.push(result)
    } else if (result.isFailed()) {
      logs.failed.push(result)
    } else if (result.isSuccessful()) {
      logs.successful.push(result)
    } else {
      logs.unconfirmed.push(result)
    }
  }

  // Write the logs to separate .txt files
  fs.writeFileSync('./cancelled.txt', logs.cancelled.join('\n'))
  fs.writeFileSync('./failed.txt', logs.failed.join('\n'))
  fs.writeFileSync('./successful.txt', logs.successful.join('\n'))
  fs.writeFileSync('./unconfirmed.txt', logs.unconfirmed.join('\n'))
}

// Run the airdrop function
airdrop()

2 Answers 2

2

I feel like ur script mixes some stuff from Ethers.js, which is not relevant for @solana/web3.js.

  1. As far as I know, there is no Wallet in @solana/web3.js, which you are trying to create from mnemonic. For such script u need a keypair.
  2. Also, assuming ur .txt file has public keys as bs58 encoded strings, you need to construct PublicKey objects inside ur script from these.
  3. Since there is no wallet, there is also no wallet.send() methods. I believe these are the reasons u're getting typeErrors.

So here is an example, how u may do it.

  1. Make a keypair instance: const wallet = solanaWeb3.Keypair.fromSecretKey(secretKey). U may export secret key of ur wallet from ur wallet extenstion, like phantom. Once u exported it, u will have bs58 encoded secret key, which u will need to convert to u8 arr. So final constructor with secret key from browser wallet extension will look like that:
const wallet: Keypair = Keypair.fromSecretKey(
    Uint8Array.from(bs58.decode('THING_U_COPIED_FROM_WALLET_EXTENSION'))
)
  1. Read and map your .txt file:
const wallets = fs.readFileSync(WALLETS_FILE_PATH, 'utf-8')
const recipientAddresses: PublicKey[] = wallets.split('\n').map(a => new web3.PublicKey(a))
  1. You will also need to establish connection to JSON RPC endpoint:
const connection: Connection = new Connection('https://api.mainnet-beta.solana.com')

If u have custom rpc, use it instead.

  1. Iterate over ur recipients, construct and send "send sol" transactions for them:
for (const recipientAddress of recipientAddresses) {

    const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash()

    const airdropInstruction = web3.SystemProgram.transfer(
      {
        fromPubkey: wallet.publicKey,
        toPubkey: recipientAddress,
        lamports: AIRDROP_AMOUNT * LAMPORTS_PER_SOL
      }
    )

    const airdropMessage = new web3.TransactionMessage({
      payerKey: wallet.publicKey,
      recentBlockhash: blockhash,
      instructions: [airdropInstruction]
    }).compileToV0Message()

    const tx = new web3.VersionedTransaction(airdropMessage)

    tx.sign([wallet])

    try {
      const txid = await connection.sendTransaction(tx)
      await connection.confirmTransaction({ blockhash, lastValidBlockHeight, signature: txid }, 'confirmed')
      logs.successful.push(recipientAddress.toString())
    } catch {
      logs.failed.push(recipientAddress.toString())
    }
  }

It is an example with versioned transactions, which might be a bit confusing, so, alternatively, u may send legacy txs like that:

  const tx = new web3.Transaction().add(
    web3.SystemProgram.transfer(
      {
        fromPubkey: wallet.publicKey,
        toPubkey: recipientAddress,
        lamports: AIRDROP_AMOUNT * LAMPORTS_PER_SOL
      }
    )
  )
  tx.recentBlockhash = blockhash
  tx.lastValidBlockHeight = lastValidBlockHeight
  tx.sign(wallet)

  try {
    const txid = await connection.sendRawTransaction(tx.serialize())
    await connection.confirmTransaction({ blockhash, lastValidBlockHeight, signature: txid }, 'confirmed')
    logs.successful.push(recipientAddress.toString())
  } catch {
    logs.failed.push(recipientAddress.toString())
  }
  1. In case you have many wallets to airdrop to, you should consider adding multiple instructions into same transaction to save time and sol:
    for (let i = 0; i < 10; i++) {
      const airdropInstruction = web3.SystemProgram.transfer(
        {
          fromPubkey: wallet.publicKey,
          toPubkey: recipientAddresses[i],
          lamports: AIRDROP_AMOUNT * LAMPORTS_PER_SOL
        }
      )
      instructions.push(airdropInstruction)
    }

    const airdropMessage = new web3.TransactionMessage({
      payerKey: wallet.publicKey,
      recentBlockhash: blockhash,
      instructions: instructions
    }).compileToV0Message()

Note that u need to control how many instructions u add into same transaction, since there is a limit of 1232 bytes length of transaction (accounts size, instructions data size). tx.serialize().byteLength will return the size of ur transaction.

2

As an alternative, you can make use of the solana-tokens tool which simply takes an input csv file and automatically does this work for you.

For example, given a csv file recipients.csv:

recipient,amount,lockup_date
3ihfUy1n9gaqihM5bJCiTAGLgWc5zo3DqVUS6T736NLM,42.0,
CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT,43.0,

You can run:

solana-tokens distribute-tokens --from <KEYPAIR> --input-csv recipients.csv --fee-payer <KEYPAIR>

You can find more information at https://lib.rs/crates/solana-tokens

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.