@solana/spl-token
Version:
SPL Token Program JS API
57 lines • 3.5 kB
JavaScript
import { sendAndConfirmTransaction, Transaction } from '@solana/web3.js';
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } from '../constants.js';
import { TokenAccountNotFoundError, TokenInvalidAccountOwnerError, TokenInvalidMintError, TokenInvalidOwnerError, } from '../errors.js';
import { createAssociatedTokenAccountInstruction } from '../instructions/associatedTokenAccount.js';
import { getAccount } from '../state/account.js';
import { getAssociatedTokenAddressSync } from '../state/mint.js';
/**
* Retrieve the associated token account, or create it if it doesn't exist
*
* @param connection Connection to use
* @param payer Payer of the transaction and initialization fees
* @param mint Mint associated with the account to set or verify
* @param owner Owner of the account to set or verify
* @param allowOwnerOffCurve Allow the owner account to be a PDA (Program Derived Address)
* @param commitment Desired level of commitment for querying the state
* @param confirmOptions Options for confirming the transaction
* @param programId SPL Token program account
* @param associatedTokenProgramId SPL Associated Token program account
*
* @return Address of the new associated token account
*/
export async function getOrCreateAssociatedTokenAccount(connection, payer, mint, owner, allowOwnerOffCurve = false, commitment, confirmOptions, programId = TOKEN_PROGRAM_ID, associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID) {
const associatedToken = getAssociatedTokenAddressSync(mint, owner, allowOwnerOffCurve, programId, associatedTokenProgramId);
// This is the optimal logic, considering TX fee, client-side computation, RPC roundtrips and guaranteed idempotent.
// Sadly we can't do this atomically.
let account;
try {
account = await getAccount(connection, associatedToken, commitment, programId);
}
catch (error) {
// TokenAccountNotFoundError can be possible if the associated address has already received some lamports,
// becoming a system account. Assuming program derived addressing is safe, this is the only case for the
// TokenInvalidAccountOwnerError in this code path.
if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
// As this isn't atomic, it's possible others can create associated accounts meanwhile.
try {
const transaction = new Transaction().add(createAssociatedTokenAccountInstruction(payer.publicKey, associatedToken, owner, mint, programId, associatedTokenProgramId));
await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions);
}
catch (error) {
// Ignore all errors; for now there is no API-compatible way to selectively ignore the expected
// instruction error if the associated account exists already.
}
// Now this should always succeed
account = await getAccount(connection, associatedToken, commitment, programId);
}
else {
throw error;
}
}
if (!account.mint.equals(mint))
throw new TokenInvalidMintError();
if (!account.owner.equals(owner))
throw new TokenInvalidOwnerError();
return account;
}
//# sourceMappingURL=getOrCreateAssociatedTokenAccount.js.map