Skip to content

Commit

Permalink
Add more helpers to get init instructions for extensions (#15)
Browse files Browse the repository at this point in the history
* Add more helpers to get init instructions for extensions

* Add memo transfer to post init helper

* Refactor tests
  • Loading branch information
lorisleiva authored Oct 10, 2024
1 parent 1f85414 commit 872081d
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 80 deletions.
92 changes: 92 additions & 0 deletions clients/js/src/getInitializeInstructionsForExtensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Address, IInstruction, TransactionSigner } from '@solana/web3.js';
import {
ExtensionArgs,
getDisableMemoTransfersInstruction,
getEnableMemoTransfersInstruction,
getInitializeConfidentialTransferMintInstruction,
getInitializeDefaultAccountStateInstruction,
getInitializeTransferFeeConfigInstruction,
} from './generated';

/**
* Given a mint address and a list of mint extensions, returns a list of
* instructions that MUST be run _before_ the `initializeMint` instruction
* to properly initialize the given extensions on the mint account.
*/
export function getPreInitializeInstructionsForMintExtensions(
mint: Address,
extensions: ExtensionArgs[]
): IInstruction[] {
return extensions.flatMap((extension) => {
switch (extension.__kind) {
case 'ConfidentialTransferMint':
return [
getInitializeConfidentialTransferMintInstruction({
mint,
...extension,
}),
];
case 'DefaultAccountState':
return [
getInitializeDefaultAccountStateInstruction({
mint,
state: extension.state,
}),
];
case 'TransferFeeConfig':
return [
getInitializeTransferFeeConfigInstruction({
mint,
transferFeeConfigAuthority: extension.transferFeeConfigAuthority,
withdrawWithheldAuthority: extension.withdrawWithheldAuthority,
transferFeeBasisPoints:
extension.newerTransferFee.transferFeeBasisPoints,
maximumFee: extension.newerTransferFee.maximumFee,
}),
];
default:
return [];
}
});
}

/**
* Given a mint address and a list of mint extensions, returns a list of
* instructions that MUST be run _after_ the `initializeMint` instruction
* to properly initialize the given extensions on the mint account.
*/
export function getPostInitializeInstructionsForMintExtensions(
_mint: Address,
extensions: ExtensionArgs[]
): IInstruction[] {
return extensions.flatMap((extension) => {
switch (extension.__kind) {
default:
return [];
}
});
}

/**
* Given a token address, its owner and a list of token extensions, returns a list
* of instructions that MUST be run _after_ the `initializeAccount` instruction
* to properly initialize the given extensions on the token account.
*/
export function getPostInitializeInstructionsForTokenExtensions(
token: Address,
owner: TransactionSigner,
extensions: ExtensionArgs[]
): IInstruction[] {
return extensions.flatMap((extension) => {
switch (extension.__kind) {
case 'MemoTransfer':
return [
extension.requireIncomingTransferMemos
? getEnableMemoTransfersInstruction({ owner, token })
: getDisableMemoTransfersInstruction({ owner, token }),
];
default:
return [];
}
});
}
44 changes: 0 additions & 44 deletions clients/js/src/getInitializeInstructionsForMintExtensions.ts

This file was deleted.

2 changes: 1 addition & 1 deletion clients/js/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export * from './generated';

export * from './getInitializeInstructionsForMintExtensions';
export * from './getInitializeInstructionsForExtensions';
export * from './getTokenSize';
export * from './getMintSize';
51 changes: 43 additions & 8 deletions clients/js/test/_setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ import {
ExtensionArgs,
TOKEN_2022_PROGRAM_ADDRESS,
getInitializeAccountInstruction,
getInitializeInstructionsForMintExtensions,
getPreInitializeInstructionsForMintExtensions,
getInitializeMintInstruction,
getMintSize,
getMintToInstruction,
getTokenSize,
getPostInitializeInstructionsForMintExtensions,
getPostInitializeInstructionsForTokenExtensions,
} from '../src';

type Client = {
Expand Down Expand Up @@ -170,34 +172,67 @@ export const createMint = async (
});
await sendAndConfirmInstructions(input.client, input.payer, [
createAccount,
...getInitializeInstructionsForMintExtensions(
...getPreInitializeInstructionsForMintExtensions(
mint.address,
input.extensions ?? []
),
initMint,
...getPostInitializeInstructionsForMintExtensions(
mint.address,
input.extensions ?? []
),
]);
return mint.address;
};

export const createToken = async (
input: Omit<Parameters<typeof getCreateTokenInstructions>[0], 'token'>
input: Omit<
Parameters<typeof getCreateTokenInstructions>[0],
'token' | 'owner'
> & { owner: TransactionSigner }
): Promise<Address> => {
const token = await generateKeyPairSigner();
const instructions = await getCreateTokenInstructions({ ...input, token });
await sendAndConfirmInstructions(input.client, input.payer, instructions);
const [createAccount, initToken] = await getCreateTokenInstructions({
...input,
owner: input.owner.address,
token,
});
await sendAndConfirmInstructions(input.client, input.payer, [
createAccount,
initToken,
...getPostInitializeInstructionsForTokenExtensions(
token.address,
input.owner,
input.extensions ?? []
),
]);
return token.address;
};

export const createTokenWithAmount = async (
input: Omit<Parameters<typeof getCreateTokenInstructions>[0], 'token'> & {
input: Omit<
Parameters<typeof getCreateTokenInstructions>[0],
'token' | 'owner'
> & {
amount: number | bigint;
mintAuthority: TransactionSigner;
owner: TransactionSigner;
}
): Promise<Address> => {
const token = await generateKeyPairSigner();
const instructions = await getCreateTokenInstructions({ ...input, token });
const [createAccount, initToken] = await getCreateTokenInstructions({
...input,
owner: input.owner.address,
token,
});
await sendAndConfirmInstructions(input.client, input.payer, [
...instructions,
createAccount,
initToken,
...getPostInitializeInstructionsForTokenExtensions(
token.address,
input.owner,
input.extensions ?? []
),
getMintToInstruction({
mint: input.mint,
token: token.address,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Account, address, generateKeyPairSigner, some } from '@solana/web3.js';
import { Account, generateKeyPairSigner, some } from '@solana/web3.js';
import test from 'ava';
import {
AccountState,
Expand Down Expand Up @@ -65,9 +65,10 @@ test('it initializes a mint account with a default account state extension', asy
test('it initializes a token account with the default state defined on the mint account', async (t) => {
// Given some signer accounts.
const client = createDefaultSolanaClient();
const [authority, freezeAuthority] = await Promise.all([
const [authority, freezeAuthority, owner] = await Promise.all([
generateKeyPairSignerWithSol(client),
generateKeyPairSigner(),
generateKeyPairSigner(),
]);

// And a mint account initialized with a default account state extension.
Expand All @@ -82,12 +83,7 @@ test('it initializes a token account with the default state defined on the mint
});

// When we create a new token account for the mint.
const token = await createToken({
client,
mint,
owner: address('HHS1XymmkBpYAkg3XTbZLxgHa5n11PAWUCWdiVtRmzzS'),
payer: authority,
});
const token = await createToken({ client, mint, owner, payer: authority });

// Then we expect the token account to have the default state defined on the mint account.
const tokenAccount = await fetchToken(client.rpc, token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
extension,
fetchToken,
getDisableMemoTransfersInstruction,
getEnableMemoTransfersInstruction,
} from '../../../src';
import {
createDefaultSolanaClient,
Expand Down Expand Up @@ -81,12 +80,9 @@ test('it disables an active memo transfers extension', async (t) => {
extension('MemoTransfer', { requireIncomingTransferMemos: true }),
],
mint,
owner: owner.address,
owner,
payer: authority,
});
await sendAndConfirmInstructions(client, authority, [
getEnableMemoTransfersInstruction({ token, owner }),
]);

// When we disable the memo transfers extension.
await sendAndConfirmInstructions(client, authority, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
Token,
extension,
fetchToken,
getDisableMemoTransfersInstruction,
getEnableMemoTransfersInstruction,
} from '../../../src';
import {
Expand Down Expand Up @@ -81,12 +80,9 @@ test('it enables an disabled memo transfers extension', async (t) => {
extension('MemoTransfer', { requireIncomingTransferMemos: false }),
],
mint,
owner: owner.address,
owner,
payer: authority,
});
await sendAndConfirmInstructions(client, authority, [
getDisableMemoTransfersInstruction({ token, owner }),
]);

// When we enable the memo transfers extension.
await sendAndConfirmInstructions(client, authority, [
Expand Down
2 changes: 1 addition & 1 deletion clients/js/test/extensions/reallocate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ test('it reallocates token accounts to fit the provided extensions', async (t) =
const token = await createToken({
client,
mint,
owner: owner.address,
owner,
payer: authority,
});
t.is(await getAccountLength(client, token), 165);
Expand Down
7 changes: 1 addition & 6 deletions clients/js/test/mintTo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,7 @@ test('it mints tokens to a token account', async (t) => {
payer,
authority: mintAuthority.address,
});
const token = await createToken({
client,
payer,
mint,
owner: owner.address,
});
const token = await createToken({ client, payer, mint, owner });

// When the mint authority mints tokens to the token account.
const mintTo = getMintToInstruction({
Expand Down
4 changes: 2 additions & 2 deletions clients/js/test/transfer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ test('it transfers tokens from one account to another', async (t) => {
payer,
mintAuthority,
mint,
owner: ownerA.address,
owner: ownerA,
amount: 100n,
}),
createToken({ client, payer, mint, owner: ownerB.address }),
createToken({ client, payer, mint, owner: ownerB }),
]);

// When owner A transfers 50 tokens to owner B.
Expand Down

0 comments on commit 872081d

Please sign in to comment.