2

Is there a Solana Tool made that allows you to send the same amount of an spl token to multiple addresses with an added custom memoID?

I've seen a handful of tools that allow you to send to multiple wallets but none that actually let you add a custom memoID to each transaction, as I need.

UPDATE:

I have managed to create a script that sends single spl token transactions with a memo id. I am struggling to follow either answer to upgrade this script to send multiple transactions.

import { getOrCreateAssociatedTokenAccount, createTransferInstruction } from "@solana/spl-token";
import { Connection, Keypair, ParsedAccountData, PublicKey, sendAndConfirmTransaction, SystemProgram, Transaction, TransactionInstruction } from "@solana/web3.js";
import secret from './guideSecret.json';
import importedRecipientAddress from './recipients.csv'

const RPC = 'https://solana-api.projectserum.com';
const SOLANA_CONNECTION = new Connection(RPC);

const FROM_KEYPAIR = Keypair.fromSecretKey(new Uint8Array(secret));
const DESTINATION_WALLET = 'importedRecipientAddress'; 
const MINT_ADDRESS = 'kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6';
const TRANSFER_AMOUNT = 1;

async function getNumberDecimals(MINT_ADDRESS: string):Promise<number> {
    const info = await SOLANA_CONNECTION.getParsedAccountInfo(new PublicKey(MINT_ADDRESS));
    const result = (info.value?.data as ParsedAccountData).parsed.info.decimals as number;
    return result;
}

async function sendTokens(message: string) {
    console.log(`Sending ${TRANSFER_AMOUNT} ${(MINT_ADDRESS)} from ${(FROM_KEYPAIR.publicKey.toString())} to ${(DESTINATION_WALLET)}.`)
    //Step 1
    console.log(`1 - Getting Source Token Account`);
    let sourceAccount = await getOrCreateAssociatedTokenAccount(
        SOLANA_CONNECTION, 
        FROM_KEYPAIR,
        new PublicKey(MINT_ADDRESS),
        FROM_KEYPAIR.publicKey
    );
    console.log(`    Source Account: ${sourceAccount.address.toString()}`);

    //Step 2
    console.log(`2 - Getting Destination Token Account`);
    let destinationAccount = await getOrCreateAssociatedTokenAccount(
        SOLANA_CONNECTION, 
        FROM_KEYPAIR,
        new PublicKey(MINT_ADDRESS),
        new PublicKey(DESTINATION_WALLET)
    );
    console.log(`    Destination Account: ${destinationAccount.address.toString()}`);

    //Step 3
    console.log(`3 - Fetching Number of Decimals for Mint: ${MINT_ADDRESS}`);
    const numberDecimals = await getNumberDecimals(MINT_ADDRESS);
    console.log(`    Number of Decimals: ${numberDecimals}`);

    //Step 4
    console.log(`4 - Creating Transaction`);
    const tx = new Transaction();
    tx.add(createTransferInstruction(
        sourceAccount.address,
        destinationAccount.address,
        FROM_KEYPAIR.publicKey,
        TRANSFER_AMOUNT * Math.pow(10, numberDecimals)
    ))

    // Step 5
    console.log(`5 - Adding Memo ID to Transaction`);
    tx.add(new TransactionInstruction({
        keys: [{ pubkey: FROM_KEYPAIR.publicKey, isSigner: true, isWritable: true }],
        data: Buffer.from(message, "utf-8"),
        programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
    }))

    console.log(`6 - Sending Transaction`);
    const latestBlockHash = await SOLANA_CONNECTION.getLatestBlockhash('confirmed');
    tx.recentBlockhash = await latestBlockHash.blockhash;    
    const signature = await sendAndConfirmTransaction(SOLANA_CONNECTION,tx,[FROM_KEYPAIR]);
    console.log(
        '\x1b[32m', //Green Text
        `   Transaction Success!🎉`,
        `\n    https://explorer.solana.com/tx/${signature}?cluster=mainnet-beta`
    );
}

sendTokens("KRE AppIndex: 394");

2 Answers 2

1

Not Aware of any that does this but creating a custom script that does this shouldn't be complex.My approach would be to create a CSV File with all the Wallet Address to which the Token needs to be sent then import these address's in the script and loop through these address's in async way and create and send the transaction. If I would like to put it more programmatically. I will add the instructions for this transaction.

const { blockhash } =
  await program.provider.connection.getLatestBlockhash();
const transaction = new anchor.web3.Transaction({
  recentBlockhash: blockhash,
  feePayer:wallet.publicKey,
});


ix1 transfer's one Token from the Owner to the CSV imported address
const ix1 = createTransferCheckedInstruction(
 ownerATA, // from (should be a token account)
 splTokenMint, // mint
 importedAddress, // to (should be a token account)
 ownerPubKey, // from's owner
 1, // amount, if your decimals is 8, send 10^8 for 1 token
 6 // decimals
)

ix2 add's a memo instruction to the transaction
const ix2 = new TransactionInstruction({
keys: [{ pubkey: ownerPubKey, isSigner: true, isWritable: true 
}],
data: Buffer.from("Data to send in transaction", "utf-8"),
programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
})

transaction.add(ix1)
transaction.add(ix2)

After creating the Transaction it needs to be signed by the owner and sent to the network.

1
  • Hello. I have taken information from your answer and added it to my script but I still can't get it to function properly. I appreciate your assistance.
    – ET.Sol
    Nov 5, 2022 at 5:02
0

The solana tokens tool is meant to do this, but you'll have to extend it to add a memo field in the CSV file. This allows you to create a CSV file like:

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

and then you can run:

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

Check out the repo at https://github.com/solana-labs/solana/tree/master/tokens

2
  • Hello. I have attempted to follow your solution but when I try to submit any transaction, it doesn't recognize the information as factual; doesn't see the csv file even though it's there, doesn't see the keypair even though it's there
    – ET.Sol
    Nov 5, 2022 at 5:03
  • Ah that's too bad, there could be some configuration error somewhere
    – Jon C
    Nov 9, 2022 at 18:02

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.