Skip to content

Commit

Permalink
Define IDL instructions for token group extension (#28)
Browse files Browse the repository at this point in the history
* Add initializeTokenGroup instruction

* Add test

* Fetch Token22 program from mainnet

This is because the Token22 program baked in the `solana-test-validator` is not up-to-date enough to support the token group interface.

* Fix token group extension layout

For some reason, the example account was using u32 numbers for size and maxSize whereas the token group interface clearly uses u64 numbers.

* Add updateTokenGroupMaxSize

* Add failing test

* Add updateTokenGroupUpdateAuthority

* Add initializeTokenGroupMember

* Add token group post instruction to helpers

* Add tests for updateTokenGroupUpdateAuthority

* Add failing test

* Fix token member extension test
  • Loading branch information
lorisleiva authored Oct 14, 2024
1 parent b1f2c8a commit 271880c
Show file tree
Hide file tree
Showing 19 changed files with 1,727 additions and 21 deletions.
4 changes: 4 additions & 0 deletions clients/js/src/generated/instructions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export * from './initializeMint2';
export * from './initializeMintCloseAuthority';
export * from './initializeMultisig';
export * from './initializeMultisig2';
export * from './initializeTokenGroup';
export * from './initializeTokenGroupMember';
export * from './initializeTokenMetadata';
export * from './initializeTransferFeeConfig';
export * from './mintTo';
Expand All @@ -67,6 +69,8 @@ export * from './updateDefaultAccountState';
export * from './updateGroupMemberPointer';
export * from './updateGroupPointer';
export * from './updateMetadataPointer';
export * from './updateTokenGroupMaxSize';
export * from './updateTokenGroupUpdateAuthority';
export * from './updateTokenMetadataField';
export * from './updateTokenMetadataUpdateAuthority';
export * from './withdrawWithheldTokensFromAccounts';
Expand Down
243 changes: 243 additions & 0 deletions clients/js/src/generated/instructions/initializeTokenGroup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
/**
* This code was AUTOGENERATED using the codama library.
* Please DO NOT EDIT THIS FILE, instead use visitors
* to add features, then rerun codama to update it.
*
* @see https://github.com/codama-idl/codama
*/

import {
combineCodec,
getAddressDecoder,
getAddressEncoder,
getBytesDecoder,
getBytesEncoder,
getOptionDecoder,
getOptionEncoder,
getStructDecoder,
getStructEncoder,
getU64Decoder,
getU64Encoder,
transformEncoder,
type Address,
type Codec,
type Decoder,
type Encoder,
type IAccountMeta,
type IAccountSignerMeta,
type IInstruction,
type IInstructionWithAccounts,
type IInstructionWithData,
type Option,
type OptionOrNullable,
type ReadonlyAccount,
type ReadonlySignerAccount,
type ReadonlyUint8Array,
type TransactionSigner,
type WritableAccount,
} from '@solana/web3.js';
import { TOKEN_2022_PROGRAM_ADDRESS } from '../programs';
import { getAccountMetaFactory, type ResolvedAccount } from '../shared';

export const INITIALIZE_TOKEN_GROUP_DISCRIMINATOR = new Uint8Array([
121, 113, 108, 39, 54, 51, 0, 4,
]);

export function getInitializeTokenGroupDiscriminatorBytes() {
return getBytesEncoder().encode(INITIALIZE_TOKEN_GROUP_DISCRIMINATOR);
}

export type InitializeTokenGroupInstruction<
TProgram extends string = typeof TOKEN_2022_PROGRAM_ADDRESS,
TAccountGroup extends string | IAccountMeta<string> = string,
TAccountMint extends string | IAccountMeta<string> = string,
TAccountMintAuthority extends string | IAccountMeta<string> = string,
TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
> = IInstruction<TProgram> &
IInstructionWithData<Uint8Array> &
IInstructionWithAccounts<
[
TAccountGroup extends string
? WritableAccount<TAccountGroup>
: TAccountGroup,
TAccountMint extends string
? ReadonlyAccount<TAccountMint>
: TAccountMint,
TAccountMintAuthority extends string
? ReadonlySignerAccount<TAccountMintAuthority> &
IAccountSignerMeta<TAccountMintAuthority>
: TAccountMintAuthority,
...TRemainingAccounts,
]
>;

export type InitializeTokenGroupInstructionData = {
discriminator: ReadonlyUint8Array;
/** Update authority for the group */
updateAuthority: Option<Address>;
/** The maximum number of group members */
maxSize: bigint;
};

export type InitializeTokenGroupInstructionDataArgs = {
/** Update authority for the group */
updateAuthority: OptionOrNullable<Address>;
/** The maximum number of group members */
maxSize: number | bigint;
};

export function getInitializeTokenGroupInstructionDataEncoder(): Encoder<InitializeTokenGroupInstructionDataArgs> {
return transformEncoder(
getStructEncoder([
['discriminator', getBytesEncoder()],
[
'updateAuthority',
getOptionEncoder(getAddressEncoder(), {
prefix: null,
noneValue: 'zeroes',
}),
],
['maxSize', getU64Encoder()],
]),
(value) => ({
...value,
discriminator: INITIALIZE_TOKEN_GROUP_DISCRIMINATOR,
})
);
}

export function getInitializeTokenGroupInstructionDataDecoder(): Decoder<InitializeTokenGroupInstructionData> {
return getStructDecoder([
['discriminator', getBytesDecoder()],
[
'updateAuthority',
getOptionDecoder(getAddressDecoder(), {
prefix: null,
noneValue: 'zeroes',
}),
],
['maxSize', getU64Decoder()],
]);
}

export function getInitializeTokenGroupInstructionDataCodec(): Codec<
InitializeTokenGroupInstructionDataArgs,
InitializeTokenGroupInstructionData
> {
return combineCodec(
getInitializeTokenGroupInstructionDataEncoder(),
getInitializeTokenGroupInstructionDataDecoder()
);
}

export type InitializeTokenGroupInput<
TAccountGroup extends string = string,
TAccountMint extends string = string,
TAccountMintAuthority extends string = string,
> = {
group: Address<TAccountGroup>;
mint: Address<TAccountMint>;
mintAuthority: TransactionSigner<TAccountMintAuthority>;
updateAuthority: InitializeTokenGroupInstructionDataArgs['updateAuthority'];
maxSize: InitializeTokenGroupInstructionDataArgs['maxSize'];
};

export function getInitializeTokenGroupInstruction<
TAccountGroup extends string,
TAccountMint extends string,
TAccountMintAuthority extends string,
TProgramAddress extends Address = typeof TOKEN_2022_PROGRAM_ADDRESS,
>(
input: InitializeTokenGroupInput<
TAccountGroup,
TAccountMint,
TAccountMintAuthority
>,
config?: { programAddress?: TProgramAddress }
): InitializeTokenGroupInstruction<
TProgramAddress,
TAccountGroup,
TAccountMint,
TAccountMintAuthority
> {
// Program address.
const programAddress = config?.programAddress ?? TOKEN_2022_PROGRAM_ADDRESS;

// Original accounts.
const originalAccounts = {
group: { value: input.group ?? null, isWritable: true },
mint: { value: input.mint ?? null, isWritable: false },
mintAuthority: { value: input.mintAuthority ?? null, isWritable: false },
};
const accounts = originalAccounts as Record<
keyof typeof originalAccounts,
ResolvedAccount
>;

// Original args.
const args = { ...input };

const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
const instruction = {
accounts: [
getAccountMeta(accounts.group),
getAccountMeta(accounts.mint),
getAccountMeta(accounts.mintAuthority),
],
programAddress,
data: getInitializeTokenGroupInstructionDataEncoder().encode(
args as InitializeTokenGroupInstructionDataArgs
),
} as InitializeTokenGroupInstruction<
TProgramAddress,
TAccountGroup,
TAccountMint,
TAccountMintAuthority
>;

return instruction;
}

export type ParsedInitializeTokenGroupInstruction<
TProgram extends string = typeof TOKEN_2022_PROGRAM_ADDRESS,
TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
> = {
programAddress: Address<TProgram>;
accounts: {
group: TAccountMetas[0];
mint: TAccountMetas[1];
mintAuthority: TAccountMetas[2];
};
data: InitializeTokenGroupInstructionData;
};

export function parseInitializeTokenGroupInstruction<
TProgram extends string,
TAccountMetas extends readonly IAccountMeta[],
>(
instruction: IInstruction<TProgram> &
IInstructionWithAccounts<TAccountMetas> &
IInstructionWithData<Uint8Array>
): ParsedInitializeTokenGroupInstruction<TProgram, TAccountMetas> {
if (instruction.accounts.length < 3) {
// TODO: Coded error.
throw new Error('Not enough accounts');
}
let accountIndex = 0;
const getNextAccount = () => {
const accountMeta = instruction.accounts![accountIndex]!;
accountIndex += 1;
return accountMeta;
};
return {
programAddress: instruction.programAddress,
accounts: {
group: getNextAccount(),
mint: getNextAccount(),
mintAuthority: getNextAccount(),
},
data: getInitializeTokenGroupInstructionDataDecoder().decode(
instruction.data
),
};
}
Loading

0 comments on commit 271880c

Please sign in to comment.