1

As part of the onboarding process for my project, I create wallets for users.

These wallets are fully onboarded to my game and are prefunded with the games native token. Users literally click 1 button and are fully onboarded.

However, when the Solana network is under load, getOrCreateAssociatedTokenAccount fails. In fact, getOrCreateAssociatedTokenAccount fails very often even when solana is not under load.

I believe this is due to getOrCreateAssociatedTokenAccount not having a priority fee. I cannot attach a priority fee or set the CU's on the function.

This high level function call which is supposed to create or get a users token account is highly unreliable, as it fails any time Solana is under load.

Is it possible for me to create my own Transaction object in which I can attach a priority fee myself so that Solana doesnt drop the transaction?

I use a Helius paid RPC, and whenever I can attach a priority fee to any transaction, those transactions go through almost every time.

https://solana-labs.github.io/solana-program-library/token/js/functions/getOrCreateAssociatedTokenAccount.html

Here is a code snippet of my code

const getConnection = () => {

  const endpoint = process.env.NODE_ENV == 'production'
    ? `https://mainnet.helius-rpc.com/?api-key=${process.env.HELIUS_API_KEY}`
    : 'https://api.devnet.solana.com';

  let connection = new Connection(endpoint, 'confirmed');

  return connection
};


async function getOrCreateRNGTokenAccount(user_pub_key, keyPair, connection) {
  try {
    logWithFileAndLine('info', `ATTEMPTING TO GET OR CREATE RNG TOKEN ACCOUNT FOR USER`);
    
    const userTokenAccount = await getOrCreateAssociatedTokenAccount(
      connection,
      keyPair,
      RNG_TOKEN_MINT,
      user_pub_key
    );

    return userTokenAccount.address.toString();
  } catch (error) {
    logWithFileAndLine('error', `Error in getOrCreateAssociatedTokenAccount: ${error.message}`);
    return null;
  }
}

1 Answer 1

1

If I understand your question correctly, you are trying to get an instruction for creating an associated token account if needed, so that you can append a compute unit budget to it. I'd recommend using createAssociatedTokenAccountIdempotentInstruction to generate an instruction for creating the account. You can follow a similar logic to the native code for getOrCreateAssociatedTokenAccount which you can find GitHub.

I think this (or something similar) should achieve your desired results.

import { 
    ASSOCIATED_TOKEN_PROGRAM_ID, 
    createAssociatedTokenAccountIdempotentInstruction, 
    getAssociatedTokenAddressSync,
    TOKEN_PROGRAM_ID, 
    TokenAccountNotFoundError, 
    TokenInvalidAccountOwnerError 
} from "@solana/spl-token";
import { 
    Commitment, 
    ComputeBudgetProgram, 
    ConfirmOptions, 
    Connection, 
    PublicKey, 
    sendAndConfirmTransaction, 
    Signer, 
    Transaction 
} from "@solana/web3.js";

export async function getOrCreateAtaWithComputeUnits(
    connection: Connection,
    payer: Signer,
    mint: PublicKey,
    owner: PublicKey,
    allowOwnerOffCurve = false,
    commitment?: Commitment,
    confirmOptions?: ConfirmOptions,
    programId = TOKEN_PROGRAM_ID,
    associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID,
    computeUnits = 250000,
): Promise<PublicKey> {
    const associatedToken = getAssociatedTokenAddressSync(
        mint,
        owner,
        allowOwnerOffCurve,
        programId,
        associatedTokenProgramId,
    );

    try {
        await connection.getAccountInfo(associatedToken, commitment);
        console.log("Associated token account exists: ", associatedToken.toBase58());
    } catch (error: unknown) {
        if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
            try {
                const transaction = new Transaction();
                const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
                    units: computeUnits,
                });
                const createIx = createAssociatedTokenAccountIdempotentInstruction(
                    payer.publicKey,
                    associatedToken,
                    owner,
                    mint,
                    programId,
                    associatedTokenProgramId,
                );
                transaction.add(computeIx, createIx);
                let { blockhash } = await connection.getLatestBlockhash();
                transaction.recentBlockhash = blockhash;
                transaction.feePayer = payer.publicKey;

                await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions);
            } catch (error: unknown) {
                console.error(error);
            }
        } else {
            throw error;
        }
    }

    return associatedToken;
}

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.