-
Notifications
You must be signed in to change notification settings - Fork 92
Add an instruction plan to mint to an ATA #110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| export * from './generated'; | ||
| export * from './createMint'; | ||
| export * from './mintToATA'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import { | ||
| InstructionPlan, | ||
| sequentialInstructionPlan, | ||
| Address, | ||
| TransactionSigner, | ||
| } from '@solana/kit'; | ||
| import { | ||
| findAssociatedTokenPda, | ||
| getCreateAssociatedTokenIdempotentInstruction, | ||
| getMintToCheckedInstruction, | ||
| TOKEN_PROGRAM_ADDRESS, | ||
| } from './generated'; | ||
|
|
||
| type MintToATAInstructionPlanInput = { | ||
| /** Funding account (must be a system account). */ | ||
| payer: TransactionSigner; | ||
| /** Associated token account address to mint to. | ||
| * Will be created if it does not already exist. | ||
| * Note: Use {@link mintToATAInstructionPlanAsync} instead to derive this automatically. | ||
| * Note: Use {@link findAssociatedTokenPda} to derive the associated token account address. | ||
| */ | ||
| ata: Address; | ||
| /** Wallet address for the associated token account. */ | ||
| owner: Address; | ||
| /** The token mint for the associated token account. */ | ||
| mint: Address; | ||
| /** The mint's minting authority or its multisignature account. */ | ||
| mintAuthority: Address | TransactionSigner; | ||
| /** The amount of new tokens to mint. */ | ||
| amount: number | bigint; | ||
| /** Expected number of base 10 digits to the right of the decimal place. */ | ||
| decimals: number; | ||
| multiSigners?: Array<TransactionSigner>; | ||
| }; | ||
|
|
||
| type MintToATAInstructionPlanConfig = { | ||
| systemProgram?: Address; | ||
| tokenProgram?: Address; | ||
| associatedTokenProgram?: Address; | ||
| }; | ||
|
|
||
| export function mintToATAInstructionPlan( | ||
| input: MintToATAInstructionPlanInput, | ||
| config?: MintToATAInstructionPlanConfig | ||
| ): InstructionPlan { | ||
| return sequentialInstructionPlan([ | ||
| getCreateAssociatedTokenIdempotentInstruction( | ||
| { | ||
| payer: input.payer, | ||
| ata: input.ata, | ||
| owner: input.owner, | ||
| mint: input.mint, | ||
| systemProgram: config?.systemProgram, | ||
| tokenProgram: config?.tokenProgram, | ||
| }, | ||
| { | ||
| programAddress: config?.associatedTokenProgram, | ||
| } | ||
| ), | ||
| // mint to this token account | ||
| getMintToCheckedInstruction( | ||
| { | ||
| mint: input.mint, | ||
| token: input.ata, | ||
| mintAuthority: input.mintAuthority, | ||
| amount: input.amount, | ||
| decimals: input.decimals, | ||
| multiSigners: input.multiSigners, | ||
| }, | ||
| { | ||
| programAddress: config?.tokenProgram, | ||
| } | ||
| ), | ||
| ]); | ||
| } | ||
|
|
||
| type MintToATAInstructionPlanAsyncInput = Omit< | ||
| MintToATAInstructionPlanInput, | ||
| 'ata' | ||
| >; | ||
|
|
||
| export async function mintToATAInstructionPlanAsync( | ||
| input: MintToATAInstructionPlanAsyncInput, | ||
| config?: MintToATAInstructionPlanConfig | ||
| ): Promise<InstructionPlan> { | ||
| const [ataAddress] = await findAssociatedTokenPda({ | ||
| owner: input.owner, | ||
| tokenProgram: config?.tokenProgram ?? TOKEN_PROGRAM_ADDRESS, | ||
| mint: input.mint, | ||
| }); | ||
| return mintToATAInstructionPlan( | ||
| { | ||
| ...input, | ||
| ata: ataAddress, | ||
| }, | ||
| config | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| import { Account, generateKeyPairSigner, none } from '@solana/kit'; | ||
| import test from 'ava'; | ||
| import { | ||
| AccountState, | ||
| TOKEN_PROGRAM_ADDRESS, | ||
| Token, | ||
| mintToATAInstructionPlan, | ||
| mintToATAInstructionPlanAsync, | ||
| fetchToken, | ||
| findAssociatedTokenPda, | ||
| } from '../src'; | ||
| import { | ||
| createDefaultSolanaClient, | ||
| createDefaultTransactionPlanExecutor, | ||
| createDefaultTransactionPlanner, | ||
| createMint, | ||
| generateKeyPairSignerWithSol, | ||
| } from './_setup'; | ||
|
|
||
| test('it creates a new associated token account with an initial balance', async (t) => { | ||
| // Given a mint account, its mint authority, a token owner and the ATA. | ||
| const client = createDefaultSolanaClient(); | ||
| const [payer, mintAuthority, owner] = await Promise.all([ | ||
| generateKeyPairSignerWithSol(client), | ||
| generateKeyPairSigner(), | ||
| generateKeyPairSigner(), | ||
| ]); | ||
| const decimals = 2; | ||
| const mint = await createMint(client, payer, mintAuthority.address, decimals); | ||
| const [ata] = await findAssociatedTokenPda({ | ||
| mint, | ||
| owner: owner.address, | ||
| tokenProgram: TOKEN_PROGRAM_ADDRESS, | ||
| }); | ||
|
|
||
| // When we mint to a token account at this address. | ||
| const instructionPlan = mintToATAInstructionPlan({ | ||
| payer, | ||
| ata, | ||
| mint, | ||
| owner: owner.address, | ||
| mintAuthority, | ||
| amount: 1_000n, | ||
| decimals, | ||
| }); | ||
|
|
||
| const transactionPlanner = createDefaultTransactionPlanner(client, payer); | ||
| const transactionPlan = await transactionPlanner(instructionPlan); | ||
| const transactionPlanExecutor = createDefaultTransactionPlanExecutor(client); | ||
| await transactionPlanExecutor(transactionPlan); | ||
|
|
||
| // Then we expect the token account to exist and have the following data. | ||
| t.like(await fetchToken(client.rpc, ata), <Account<Token>>{ | ||
| address: ata, | ||
| data: { | ||
| mint, | ||
| owner: owner.address, | ||
| amount: 1000n, | ||
| delegate: none(), | ||
| state: AccountState.Initialized, | ||
| isNative: none(), | ||
| delegatedAmount: 0n, | ||
| closeAuthority: none(), | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| test('it derives a new associated token account with an initial balance', async (t) => { | ||
| // Given a mint account, its mint authority, a token owner and the ATA. | ||
| const client = createDefaultSolanaClient(); | ||
| const [payer, mintAuthority, owner] = await Promise.all([ | ||
| generateKeyPairSignerWithSol(client), | ||
| generateKeyPairSigner(), | ||
| generateKeyPairSigner(), | ||
| ]); | ||
| const decimals = 2; | ||
| const mint = await createMint(client, payer, mintAuthority.address, decimals); | ||
|
|
||
| // When we mint to a token account for the mint. | ||
| const instructionPlan = await mintToATAInstructionPlanAsync({ | ||
| payer, | ||
| mint, | ||
| owner: owner.address, | ||
| mintAuthority, | ||
| amount: 1_000n, | ||
| decimals, | ||
| }); | ||
|
|
||
| const transactionPlanner = createDefaultTransactionPlanner(client, payer); | ||
| const transactionPlan = await transactionPlanner(instructionPlan); | ||
| const transactionPlanExecutor = createDefaultTransactionPlanExecutor(client); | ||
| await transactionPlanExecutor(transactionPlan); | ||
|
|
||
| // Then we expect the token account to exist and have the following data. | ||
| const [ata] = await findAssociatedTokenPda({ | ||
| mint, | ||
| owner: owner.address, | ||
| tokenProgram: TOKEN_PROGRAM_ADDRESS, | ||
| }); | ||
|
|
||
| t.like(await fetchToken(client.rpc, ata), <Account<Token>>{ | ||
| address: ata, | ||
| data: { | ||
| mint, | ||
| owner: owner.address, | ||
| amount: 1000n, | ||
| delegate: none(), | ||
| state: AccountState.Initialized, | ||
| isNative: none(), | ||
| delegatedAmount: 0n, | ||
| closeAuthority: none(), | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| test('it also mints to an existing associated token account', async (t) => { | ||
| // Given a mint account, its mint authority, a token owner and the ATA. | ||
| const client = createDefaultSolanaClient(); | ||
| const [payer, mintAuthority, owner] = await Promise.all([ | ||
| generateKeyPairSignerWithSol(client), | ||
| generateKeyPairSigner(), | ||
| generateKeyPairSigner(), | ||
| ]); | ||
| const decimals = 2; | ||
| const mint = await createMint(client, payer, mintAuthority.address, decimals); | ||
| const [ata] = await findAssociatedTokenPda({ | ||
| mint, | ||
| owner: owner.address, | ||
| tokenProgram: TOKEN_PROGRAM_ADDRESS, | ||
| }); | ||
|
|
||
| // When we create and initialize a token account at this address. | ||
| const instructionPlan = mintToATAInstructionPlan({ | ||
| payer, | ||
| ata, | ||
| mint, | ||
| owner: owner.address, | ||
| mintAuthority, | ||
| amount: 1_000n, | ||
| decimals, | ||
| }); | ||
|
|
||
| const transactionPlanner = createDefaultTransactionPlanner(client, payer); | ||
| const transactionPlan = await transactionPlanner(instructionPlan); | ||
| const transactionPlanExecutor = createDefaultTransactionPlanExecutor(client); | ||
| await transactionPlanExecutor(transactionPlan); | ||
|
|
||
| // And then we mint additional tokens to the same account. | ||
| const instructionPlan2 = mintToATAInstructionPlan({ | ||
| payer, | ||
| ata, | ||
| mint, | ||
| owner: owner.address, | ||
| mintAuthority, | ||
| amount: 1_000n, | ||
| decimals, | ||
| }); | ||
|
|
||
| const transactionPlan2 = await transactionPlanner(instructionPlan2); | ||
| await transactionPlanExecutor(transactionPlan2); | ||
|
|
||
| // Then we expect the token account to exist and have the following data. | ||
| t.like(await fetchToken(client.rpc, ata), <Account<Token>>{ | ||
| address: ata, | ||
| data: { | ||
| mint, | ||
| owner: owner.address, | ||
| amount: 2000n, | ||
| delegate: none(), | ||
| state: AccountState.Initialized, | ||
| isNative: none(), | ||
| delegatedAmount: 0n, | ||
| closeAuthority: none(), | ||
| }, | ||
| }); | ||
| }); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.