Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ account tracks:
1. **Mint**: The specific token type it holds
2. **Owner**: The authority who can transfer tokens from this account

There are two approaches to creating token accounts:

1. Create the account using the public key of a keypair as the address
2. Create an associated token account (ATA) with a deterministic address

### Example: USD Coin (USDC)

- Mint address: `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`
Expand Down Expand Up @@ -130,7 +135,7 @@ The resulting token accounts are owned by the Token Program and use the standard

</Callout>

## How to Create a Token Account
## How to Create a Token Account with a Keypair

Creating a token account requires the
[`InitializeAccount`](https://github.com/solana-program/token/blob/a7c488ca39ed4cd71a87950ed854929816e9099f/program/src/instruction.rs#L63)
Expand Down
102 changes: 39 additions & 63 deletions apps/web/content/docs/en/tokens/extensions/cpi-guard.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import {
AccountState,
extension,
getEnableCpiGuardInstruction,
getInitializeAccountInstruction,
getInitializeMintInstruction,
findAssociatedTokenPda,
getCreateAssociatedTokenInstructionAsync,
getMintSize,
getTokenSize,
TOKEN_2022_PROGRAM_ADDRESS
TOKEN_2022_PROGRAM_ADDRESS,
ASSOCIATED_TOKEN_PROGRAM_ADDRESS
} from "@solana-program/token-2022";
import {
airdropFactory,
Expand Down Expand Up @@ -85,37 +87,25 @@ const cpiGuardExtension = extension("CpiGuard", {
lockCpi: true
});

// Generate keypair to use as address of token account
const tokenAccount = await generateKeyPairSigner();

// get token account size with extension enabled
const tokenAccountLen = BigInt(getTokenSize([cpiGuardExtension]));

// Get minimum balance for rent exemption
const tokenAccountRent = await rpc
.getMinimumBalanceForRentExemption(tokenAccountLen)
.send();

// Instruction to create new account for the token account
// Invokes the system program
const createTokenAccountInstruction = getCreateAccountInstruction({
payer: authority,
newAccount: tokenAccount,
lamports: tokenAccountRent,
space: tokenAccountLen,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
// Use findAssociatedTokenPda to derive the ATA address
const [associatedTokenAddress] = await findAssociatedTokenPda({
mint: mint.address,
owner: authority.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});

// Instruction to initialize the created token account
const initializeTokenAccountInstruction = getInitializeAccountInstruction({
account: tokenAccount.address,
// Create instruction to create the associated token account
const createAtaInstruction = await getCreateAssociatedTokenInstructionAsync({
payer: authority,
ata: associatedTokenAddress,
mint: mint.address,
owner: authority.address
owner: authority.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});

// Instruction to enable the cpi guard on initialized token account
let initializeCpiGuardExtension = getEnableCpiGuardInstruction({
token: tokenAccount.address,
token: associatedTokenAddress,
owner: authority.address
});

Expand All @@ -131,8 +121,7 @@ const initializeMintInstruction = getInitializeMintInstruction({
const instructions = [
createMintAccountInstruction,
initializeMintInstruction,
createTokenAccountInstruction,
initializeTokenAccountInstruction,
createAtaInstruction,
initializeCpiGuardExtension
];

Expand Down Expand Up @@ -160,7 +149,7 @@ const transactionSignature = getSignatureFromTransaction(signedTransaction);
console.log("Mint Address:", mint.address.toString());
console.log(
"Token account with CPI guard enabled:",
tokenAccount.address.toString()
associatedTokenAddress.toString()
);
console.log("Transaction Signature:", transactionSignature);
```
Expand All @@ -183,7 +172,8 @@ import {
TOKEN_2022_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID,
getAccountLen,
createInitializeAccountInstruction,
getAssociatedTokenAddressSync,
createAssociatedTokenAccountInstruction,
AccountState,
createEnableCpiGuardInstruction
} from "@solana/spl-token";
Expand Down Expand Up @@ -236,41 +226,31 @@ const initializeMintInstruction = createInitializeMintInstruction(
TOKEN_2022_PROGRAM_ID
);

// Generate keypair to use as token account
const tokenAccount = new Keypair();

// Get token account size with extension enabled
const tokenAccountLen = getAccountLen(extensions);

// Get minimum balance for rent exemption
const tokenAccountRent =
await connection.getMinimumBalanceForRentExemption(tokenAccountLen);
const ata = getAssociatedTokenAddressSync(
mint.publicKey,
feePayer.publicKey,
true,
TOKEN_2022_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID
);

// Instruction to create new account for the token account
const createTokenAccountInstruction = SystemProgram.createAccount({
fromPubkey: feePayer.publicKey,
newAccountPubkey: tokenAccount.publicKey,
space: tokenAccountLen,
lamports: tokenAccountRent,
programId: TOKEN_2022_PROGRAM_ID
});
const createAtaInstruction = createAssociatedTokenAccountInstruction(
feePayer.publicKey,
ata,
feePayer.publicKey,
mint.publicKey,
TOKEN_2022_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID
);

// 'enable CPI Guard' instruction
const enableCpiGuardInstruction = createEnableCpiGuardInstruction(
tokenAccount.publicKey,
ata,
feePayer.publicKey,
[],
TOKEN_2022_PROGRAM_ID
);

// Initialize token account
const initializeTokenAccountInstruction = createInitializeAccountInstruction(
tokenAccount.publicKey,
mint.publicKey,
feePayer.publicKey,
TOKEN_2022_PROGRAM_ID
);

// Construct transaction to create mint, ATA, mint tokens, create token account,
// and initialize with default account state extension
const transaction = new Transaction({
Expand All @@ -280,23 +260,19 @@ const transaction = new Transaction({
}).add(
createMintAccountInstruction,
initializeMintInstruction,
createTokenAccountInstruction,
initializeTokenAccountInstruction,
createAtaInstruction,
enableCpiGuardInstruction
);

// Sign transaction
const transactionSignature = await sendAndConfirmTransaction(
connection,
transaction,
[feePayer, mint, tokenAccount]
[feePayer, mint]
);

console.log("Mint Address:", mint.publicKey.toBase58());
console.log(
"Token account with CPI guard enabled:",
tokenAccount.publicKey.toBase58()
);
console.log("Token account with CPI guard enabled:", ata.toBase58());
console.log("Transaction Signature:", transactionSignature);
```

Expand Down
95 changes: 37 additions & 58 deletions apps/web/content/docs/en/tokens/extensions/default-state.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import { getCreateAccountInstruction } from "@solana-program/system";
import {
AccountState,
extension,
getInitializeAccountInstruction,
findAssociatedTokenPda,
getCreateAssociatedTokenInstructionAsync,
getInitializeDefaultAccountStateInstruction,
getInitializeMintInstruction,
getMintSize,
getTokenSize,
TOKEN_2022_PROGRAM_ADDRESS
TOKEN_2022_PROGRAM_ADDRESS,
ASSOCIATED_TOKEN_PROGRAM_ADDRESS
} from "@solana-program/token-2022";
import {
airdropFactory,
Expand Down Expand Up @@ -86,32 +88,20 @@ const createMintAccountInstruction = getCreateAccountInstruction({
programAddress: TOKEN_2022_PROGRAM_ADDRESS
});

// Generate keypair to use as address of token account
const tokenAccount = await generateKeyPairSigner();

// get token account size with extension enabled
const tokenAccountLen = BigInt(getTokenSize([defaultAccountStateExtension]));

// Get minimum balance for rent exemption
const tokenAccountRent = await rpc
.getMinimumBalanceForRentExemption(tokenAccountLen)
.send();

// Instruction to create new account for the token account
// Invokes the system program
const createTokenAccountInstruction = getCreateAccountInstruction({
payer: authority,
newAccount: tokenAccount,
lamports: tokenAccountRent,
space: tokenAccountLen,
programAddress: TOKEN_2022_PROGRAM_ADDRESS
// Use findAssociatedTokenPda to derive the ATA address
const [associatedTokenAddress] = await findAssociatedTokenPda({
mint: mint.address,
owner: authority.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});

// Instruction to initialize the created token account
const initializeTokenAccountInstruction = getInitializeAccountInstruction({
account: tokenAccount.address,
// Create instruction to create the associated token account
const createAtaInstruction = await getCreateAssociatedTokenInstructionAsync({
payer: authority,
ata: associatedTokenAddress,
mint: mint.address,
owner: authority.address
owner: authority.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});

// Instruction to initialize account with default account state (frozen)
Expand All @@ -135,8 +125,7 @@ const instructions = [
createMintAccountInstruction,
initializeDefaultAccountState,
initializeMintInstruction,
createTokenAccountInstruction,
initializeTokenAccountInstruction
createAtaInstruction
];

// Create transaction message
Expand All @@ -161,7 +150,7 @@ await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
const transactionSignature = getSignatureFromTransaction(signedTransaction);

console.log("Mint Address:", mint.address.toString());
console.log("Frozen token account:", tokenAccount.address.toString());
console.log("Frozen token account:", associatedTokenAddress.toString());
console.log("Transaction Signature:", transactionSignature);
```

Expand All @@ -183,8 +172,9 @@ import {
TOKEN_2022_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID,
getAccountLen,
createInitializeAccountInstruction,
createInitializeDefaultAccountStateInstruction,
getAssociatedTokenAddressSync,
createAssociatedTokenAccountInstruction,
AccountState
} from "@solana/spl-token";

Expand Down Expand Up @@ -236,24 +226,22 @@ const initializeMintInstruction = createInitializeMintInstruction(
TOKEN_2022_PROGRAM_ID
);

// Generate keypair to use as token account
const tokenAccount = new Keypair();

// Get token account size with extension enabled
const tokenAccountLen = getAccountLen(extensions);

// Get minimum balance for rent exemption
const tokenAccountRent =
await connection.getMinimumBalanceForRentExemption(tokenAccountLen);
const ata = getAssociatedTokenAddressSync(
mint.publicKey,
feePayer.publicKey,
true,
TOKEN_2022_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID
);

// Instruction to create new account for the token account
const createTokenAccountInstruction = SystemProgram.createAccount({
fromPubkey: feePayer.publicKey,
newAccountPubkey: tokenAccount.publicKey,
space: tokenAccountLen,
lamports: tokenAccountRent,
programId: TOKEN_2022_PROGRAM_ID
});
const createAtaInstruction = createAssociatedTokenAccountInstruction(
feePayer.publicKey,
ata,
feePayer.publicKey,
mint.publicKey,
TOKEN_2022_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID
);

// Initialize the default account state extension
const initializeDefaultStateInstruction =
Expand All @@ -263,14 +251,6 @@ const initializeDefaultStateInstruction =
TOKEN_2022_PROGRAM_ID
);

// Initialize token account
const initializeTokenAccountInstruction = createInitializeAccountInstruction(
tokenAccount.publicKey,
mint.publicKey,
feePayer.publicKey,
TOKEN_2022_PROGRAM_ID
);

// Construct transaction to create mint, ATA, mint tokens, create token account,
// and initialize with default account state extension
const transaction = new Transaction({
Expand All @@ -281,19 +261,18 @@ const transaction = new Transaction({
createMintAccountInstruction,
initializeDefaultStateInstruction,
initializeMintInstruction,
createTokenAccountInstruction,
initializeTokenAccountInstruction
createAtaInstruction
);

// Sign transaction
const transactionSignature = await sendAndConfirmTransaction(
connection,
transaction,
[feePayer, mint, tokenAccount]
[feePayer, mint]
);

console.log("Mint Address:", mint.publicKey.toBase58());
console.log("Token Account (frozen state):", tokenAccount.publicKey.toBase58());
console.log("Token Account (frozen state):", ata.toBase58());
console.log("Transaction Signature:", transactionSignature);
```

Expand Down