Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit f61af23

Browse files
authored
[token-js] : JS binding for CreateIdempotent instruction (#3558)
* JS binding for CreateIdempotent instruction * revert * newline * Removed Idempotent flag and extracted Idempotent logic to its own set of functions * Conventions
1 parent c84f65c commit f61af23

File tree

4 files changed

+113
-2
lines changed

4 files changed

+113
-2
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { ConfirmOptions, Connection, PublicKey, Signer } from '@solana/web3.js';
2+
import { sendAndConfirmTransaction, Transaction } from '@solana/web3.js';
3+
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } from '../constants.js';
4+
import { createAssociatedTokenAccountIdempotentInstruction } from '../instructions/associatedTokenAccount.js';
5+
import { getAssociatedTokenAddress } from '../state/mint.js';
6+
7+
/**
8+
* Create and initialize a new associated token account
9+
* The instruction will succeed even if the associated token account already exists
10+
*
11+
* @param connection Connection to use
12+
* @param payer Payer of the transaction and initialization fees
13+
* @param mint Mint for the account
14+
* @param owner Owner of the new account
15+
* @param confirmOptions Options for confirming the transaction
16+
* @param programId SPL Token program account
17+
* @param associatedTokenProgramId SPL Associated Token program account
18+
*
19+
* @return Address of the new or existing associated token account
20+
*/
21+
export async function createAssociatedTokenAccountIdempotent(
22+
connection: Connection,
23+
payer: Signer,
24+
mint: PublicKey,
25+
owner: PublicKey,
26+
confirmOptions?: ConfirmOptions,
27+
programId = TOKEN_PROGRAM_ID,
28+
associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID
29+
): Promise<PublicKey> {
30+
const associatedToken = await getAssociatedTokenAddress(mint, owner, false, programId, associatedTokenProgramId);
31+
32+
const transaction = new Transaction().add(
33+
createAssociatedTokenAccountIdempotentInstruction(
34+
payer.publicKey,
35+
associatedToken,
36+
owner,
37+
mint,
38+
programId,
39+
associatedTokenProgramId
40+
)
41+
);
42+
43+
await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions);
44+
45+
return associatedToken;
46+
}

token/js/src/actions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './burnChecked.js';
66
export * from './closeAccount.js';
77
export * from './createAccount.js';
88
export * from './createAssociatedTokenAccount.js';
9+
export * from './createAssociatedTokenAccountIdempotent.js';
910
export * from './createMint.js';
1011
export * from './createMultisig.js';
1112
export * from './createNativeMint.js';

token/js/src/instructions/associatedTokenAccount.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { SystemProgram, TransactionInstruction } from '@solana/web3.js';
33
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } from '../constants.js';
44

55
/**
6-
* Construct an AssociatedTokenAccount instruction
6+
* Construct a CreateAssociatedTokenAccount instruction
77
*
88
* @param payer Payer of the initialization fees
99
* @param associatedToken New associated token account
@@ -21,6 +21,57 @@ export function createAssociatedTokenAccountInstruction(
2121
mint: PublicKey,
2222
programId = TOKEN_PROGRAM_ID,
2323
associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID
24+
): TransactionInstruction {
25+
return buildAssociatedTokenAccountInstruction(
26+
payer,
27+
associatedToken,
28+
owner,
29+
mint,
30+
Buffer.alloc(0),
31+
programId,
32+
associatedTokenProgramId
33+
);
34+
}
35+
36+
/**
37+
* Construct a CreateAssociatedTokenAccountIdempotent instruction
38+
*
39+
* @param payer Payer of the initialization fees
40+
* @param associatedToken New associated token account
41+
* @param owner Owner of the new account
42+
* @param mint Token mint account
43+
* @param programId SPL Token program account
44+
* @param associatedTokenProgramId SPL Associated Token program account
45+
*
46+
* @return Instruction to add to a transaction
47+
*/
48+
export function createAssociatedTokenAccountIdempotentInstruction(
49+
payer: PublicKey,
50+
associatedToken: PublicKey,
51+
owner: PublicKey,
52+
mint: PublicKey,
53+
programId = TOKEN_PROGRAM_ID,
54+
associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID
55+
): TransactionInstruction {
56+
return buildAssociatedTokenAccountInstruction(
57+
payer,
58+
associatedToken,
59+
owner,
60+
mint,
61+
Buffer.from([1]),
62+
programId,
63+
associatedTokenProgramId
64+
);
65+
}
66+
67+
function buildAssociatedTokenAccountInstruction(
68+
payer: PublicKey,
69+
associatedToken: PublicKey,
70+
owner: PublicKey,
71+
mint: PublicKey,
72+
instructionData: Buffer,
73+
programId = TOKEN_PROGRAM_ID,
74+
associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID
2475
): TransactionInstruction {
2576
const keys = [
2677
{ pubkey: payer, isSigner: true, isWritable: true },
@@ -34,6 +85,6 @@ export function createAssociatedTokenAccountInstruction(
3485
return new TransactionInstruction({
3586
keys,
3687
programId: associatedTokenProgramId,
37-
data: Buffer.alloc(0),
88+
data: instructionData,
3889
});
3990
}

token/js/test/e2e/create.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
createMint,
1111
getMint,
1212
createAccount,
13+
createAssociatedTokenAccountIdempotent,
1314
getAccount,
1415
getAssociatedTokenAddress,
1516
} from '../../src';
@@ -142,5 +143,17 @@ describe('createAccount', () => {
142143
TEST_PROGRAM_ID
143144
)
144145
).to.be.rejected;
146+
147+
// when creating again but with idempotent mode, TX should not throw error
148+
return expect(
149+
createAssociatedTokenAccountIdempotent(
150+
connection,
151+
payer,
152+
mint,
153+
owner.publicKey,
154+
undefined,
155+
TEST_PROGRAM_ID
156+
)
157+
).to.be.fulfilled;
145158
});
146159
});

0 commit comments

Comments
 (0)