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
14 changes: 9 additions & 5 deletions clients/js/src/mintToATA.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { InstructionPlan, sequentialInstructionPlan, Address, TransactionSigner } from '@solana/kit';
import {
getAddressFromResolvedInstructionAccount,
type InstructionAccountInput,
} from '@solana/kit/program-client-core';

import {
findAssociatedTokenPda,
Expand All @@ -16,11 +20,11 @@ export type MintToATAInstructionPlanInput = {
* Note: Use {@link getMintToATAInstructionPlanAsync} instead to derive this automatically.
* Note: Use {@link findAssociatedTokenPda} to derive the associated token account address.
*/
ata: Address;
ata: InstructionAccountInput;
/** Wallet address for the associated token account. */
owner: Address;
owner: InstructionAccountInput;
/** The token mint for the associated token account. */
mint: Address;
mint: InstructionAccountInput;
/** The mint's minting authority or its multisignature account. */
mintAuthority: Address | TransactionSigner;
/** The amount of new tokens to mint. */
Expand Down Expand Up @@ -81,9 +85,9 @@ export async function getMintToATAInstructionPlanAsync(
let ata = input.ata;
if (!ata) {
[ata] = await findAssociatedTokenPda({
owner: input.owner,
owner: getAddressFromResolvedInstructionAccount('owner', input.owner),
tokenProgram,
mint: input.mint,
mint: getAddressFromResolvedInstructionAccount('mint', input.mint),
});
}
return getMintToATAInstructionPlan(
Expand Down
24 changes: 14 additions & 10 deletions clients/js/src/transferToATA.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { InstructionPlan, sequentialInstructionPlan, Address, TransactionSigner } from '@solana/kit';
import {
getAddressFromResolvedInstructionAccount,
type InstructionAccountInput,
} from '@solana/kit/program-client-core';

import {
findAssociatedTokenPda,
Expand All @@ -12,19 +16,19 @@ export type TransferToATAInstructionPlanInput = {
/** Funding account (must be a system account). */
payer: TransactionSigner;
/** The token mint to transfer. */
mint: Address;
mint: InstructionAccountInput;
/** The source account for the transfer. */
source: Address;
source: InstructionAccountInput;
/** The source account's owner/delegate or its multisignature account. */
authority: Address | TransactionSigner;
/** Associated token account address to transfer to.
* Will be created if it does not already exist.
* Note: Use {@link getTransferToATAInstructionPlanAsync} instead to derive this automatically.
* Note: Use {@link findAssociatedTokenPda} to derive the associated token account address.
*/
destination: Address;
destination: InstructionAccountInput;
/** Wallet address for the destination. */
recipient: Address;
recipient: InstructionAccountInput;
/** The amount of tokens to transfer. */
amount: number | bigint;
/** Expected number of base 10 digits to the right of the decimal place. */
Expand Down Expand Up @@ -84,23 +88,23 @@ export async function getTransferToATAInstructionPlanAsync(
): Promise<InstructionPlan> {
const tokenProgram = config?.tokenProgram ?? TOKEN_PROGRAM_ADDRESS;

const mint = getAddressFromResolvedInstructionAccount('mint', input.mint);

let destination = input.destination;
if (!destination) {
[destination] = await findAssociatedTokenPda({
owner: input.recipient,
owner: getAddressFromResolvedInstructionAccount('recipient', input.recipient),
tokenProgram,
mint: input.mint,
mint,
});
}

let source = input.source;
if (!source) {
const authorityAddress: Address =
typeof input.authority === 'string' ? input.authority : input.authority.address;
const [sourceAta] = await findAssociatedTokenPda({
owner: authorityAddress,
owner: getAddressFromResolvedInstructionAccount('authority', input.authority),
tokenProgram,
mint: input.mint,
mint,
});
source = sourceAta;
}
Expand Down
33 changes: 33 additions & 0 deletions clients/js/test/mintToATA.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,36 @@ it('also mints to an existing associated token account', async () => {
},
});
});

it('accepts address wrappers for the mint and owner', async () => {
// Given a mint account, its mint authority and a token owner.
const client = await createTestClient();
const [mintAuthority, owner, mint] = await Promise.all([
generateKeyPairSigner(),
generateKeyPairSigner(),
generateKeyPairSigner(),
]);
const decimals = 2;
await client.token.instructions
.createMint({ newMint: mint, decimals, mintAuthority: mintAuthority.address })
.sendTransaction();

// When we mint to the owner using HasAddress wrappers.
await client.token.instructions
.mintToATA({
mint: { address: mint.address },
owner: { address: owner.address },
mintAuthority,
amount: 1_000n,
decimals,
})
.sendTransaction();

// Then we expect the derived ATA to hold the minted tokens.
const [ata] = await findAssociatedTokenPda({
mint: mint.address,
owner: owner.address,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
});
expect(await fetchToken(client.rpc, ata)).toMatchObject({ data: { amount: 1000n } });
});
40 changes: 40 additions & 0 deletions clients/js/test/transferToATA.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,43 @@ it('derives both source and destination ATAs and transfers tokens', async () =>
expect(tokenDataA).toMatchObject(<Token>{ amount: 50n });
expect(tokenDataB).toMatchObject(<Token>{ amount: 50n });
});

it('accepts address wrappers for the mint and recipient', async () => {
// Given a mint account and ownerA's ATA with 100 tokens.
const client = await createTestClient();
const [mintAuthority, ownerA, ownerB, mint] = await Promise.all([
generateKeyPairSigner(),
generateKeyPairSigner(),
generateKeyPairSigner(),
generateKeyPairSigner(),
]);
const decimals = 2;
await client.token.instructions
.createMint({ newMint: mint, decimals, mintAuthority: mintAuthority.address })
.sendTransaction();
const tokenA = await createTokenPdaWithAmount(client, mintAuthority, mint.address, ownerA.address, 100n, decimals);

// When owner A transfers 50 tokens to owner B using HasAddress wrappers.
await client.token.instructions
.transferToATA({
mint: { address: mint.address },
authority: ownerA,
recipient: { address: ownerB.address },
amount: 50n,
decimals,
})
.sendTransaction();

// Then we expect both ATAs to have the correct balances.
const [tokenB] = await findAssociatedTokenPda({
owner: ownerB.address,
mint: mint.address,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
});
const [{ data: tokenDataA }, { data: tokenDataB }] = await Promise.all([
fetchToken(client.rpc, tokenA),
fetchToken(client.rpc, tokenB),
]);
expect(tokenDataA).toMatchObject(<Token>{ amount: 50n });
expect(tokenDataB).toMatchObject(<Token>{ amount: 50n });
});
Loading