Skip to content

Commit

Permalink
Define IDL for create native mint instruction with test cases (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
adpthegreat authored Nov 14, 2024
1 parent 0c8b0b8 commit 40eec02
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 0 deletions.
203 changes: 203 additions & 0 deletions clients/js/src/generated/instructions/createNativeMint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/**
* 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,
getStructDecoder,
getStructEncoder,
getU8Decoder,
getU8Encoder,
transformEncoder,
type Address,
type Codec,
type Decoder,
type Encoder,
type IAccountMeta,
type IAccountSignerMeta,
type IInstruction,
type IInstructionWithAccounts,
type IInstructionWithData,
type ReadonlyAccount,
type TransactionSigner,
type WritableAccount,
type WritableSignerAccount,
} from '@solana/web3.js';
import { TOKEN_2022_PROGRAM_ADDRESS } from '../programs';
import { getAccountMetaFactory, type ResolvedAccount } from '../shared';

export const CREATE_NATIVE_MINT_DISCRIMINATOR = 31;

export function getCreateNativeMintDiscriminatorBytes() {
return getU8Encoder().encode(CREATE_NATIVE_MINT_DISCRIMINATOR);
}

export type CreateNativeMintInstruction<
TProgram extends string = typeof TOKEN_2022_PROGRAM_ADDRESS,
TAccountPayer extends string | IAccountMeta<string> = string,
TAccountNativeMint extends string | IAccountMeta<string> = string,
TAccountSystemProgram extends
| string
| IAccountMeta<string> = '11111111111111111111111111111111',
TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
> = IInstruction<TProgram> &
IInstructionWithData<Uint8Array> &
IInstructionWithAccounts<
[
TAccountPayer extends string
? WritableSignerAccount<TAccountPayer> &
IAccountSignerMeta<TAccountPayer>
: TAccountPayer,
TAccountNativeMint extends string
? WritableAccount<TAccountNativeMint>
: TAccountNativeMint,
TAccountSystemProgram extends string
? ReadonlyAccount<TAccountSystemProgram>
: TAccountSystemProgram,
...TRemainingAccounts,
]
>;

export type CreateNativeMintInstructionData = { discriminator: number };

export type CreateNativeMintInstructionDataArgs = {};

export function getCreateNativeMintInstructionDataEncoder(): Encoder<CreateNativeMintInstructionDataArgs> {
return transformEncoder(
getStructEncoder([['discriminator', getU8Encoder()]]),
(value) => ({ ...value, discriminator: CREATE_NATIVE_MINT_DISCRIMINATOR })
);
}

export function getCreateNativeMintInstructionDataDecoder(): Decoder<CreateNativeMintInstructionData> {
return getStructDecoder([['discriminator', getU8Decoder()]]);
}

export function getCreateNativeMintInstructionDataCodec(): Codec<
CreateNativeMintInstructionDataArgs,
CreateNativeMintInstructionData
> {
return combineCodec(
getCreateNativeMintInstructionDataEncoder(),
getCreateNativeMintInstructionDataDecoder()
);
}

export type CreateNativeMintInput<
TAccountPayer extends string = string,
TAccountNativeMint extends string = string,
TAccountSystemProgram extends string = string,
> = {
/** Funding account (must be a system account) */
payer: TransactionSigner<TAccountPayer>;
/** The native mint address */
nativeMint: Address<TAccountNativeMint>;
/** System program for mint account funding */
systemProgram?: Address<TAccountSystemProgram>;
};

export function getCreateNativeMintInstruction<
TAccountPayer extends string,
TAccountNativeMint extends string,
TAccountSystemProgram extends string,
TProgramAddress extends Address = typeof TOKEN_2022_PROGRAM_ADDRESS,
>(
input: CreateNativeMintInput<
TAccountPayer,
TAccountNativeMint,
TAccountSystemProgram
>,
config?: { programAddress?: TProgramAddress }
): CreateNativeMintInstruction<
TProgramAddress,
TAccountPayer,
TAccountNativeMint,
TAccountSystemProgram
> {
// Program address.
const programAddress = config?.programAddress ?? TOKEN_2022_PROGRAM_ADDRESS;

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

// Resolve default values.
if (!accounts.systemProgram.value) {
accounts.systemProgram.value =
'11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
}

const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
const instruction = {
accounts: [
getAccountMeta(accounts.payer),
getAccountMeta(accounts.nativeMint),
getAccountMeta(accounts.systemProgram),
],
programAddress,
data: getCreateNativeMintInstructionDataEncoder().encode({}),
} as CreateNativeMintInstruction<
TProgramAddress,
TAccountPayer,
TAccountNativeMint,
TAccountSystemProgram
>;

return instruction;
}

export type ParsedCreateNativeMintInstruction<
TProgram extends string = typeof TOKEN_2022_PROGRAM_ADDRESS,
TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
> = {
programAddress: Address<TProgram>;
accounts: {
/** Funding account (must be a system account) */
payer: TAccountMetas[0];
/** The native mint address */
nativeMint: TAccountMetas[1];
/** System program for mint account funding */
systemProgram: TAccountMetas[2];
};
data: CreateNativeMintInstructionData;
};

export function parseCreateNativeMintInstruction<
TProgram extends string,
TAccountMetas extends readonly IAccountMeta[],
>(
instruction: IInstruction<TProgram> &
IInstructionWithAccounts<TAccountMetas> &
IInstructionWithData<Uint8Array>
): ParsedCreateNativeMintInstruction<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: {
payer: getNextAccount(),
nativeMint: getNextAccount(),
systemProgram: getNextAccount(),
},
data: getCreateNativeMintInstructionDataDecoder().decode(instruction.data),
};
}
1 change: 1 addition & 0 deletions clients/js/src/generated/instructions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export * from './confidentialWithdraw';
export * from './configureConfidentialTransferAccount';
export * from './createAssociatedToken';
export * from './createAssociatedTokenIdempotent';
export * from './createNativeMint';
export * from './disableConfidentialCredits';
export * from './disableCpiGuard';
export * from './disableMemoTransfers';
Expand Down
8 changes: 8 additions & 0 deletions clients/js/src/generated/programs/token2022.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
type ParsedConfidentialTransferWithFeeInstruction,
type ParsedConfidentialWithdrawInstruction,
type ParsedConfigureConfidentialTransferAccountInstruction,
type ParsedCreateNativeMintInstruction,
type ParsedDisableConfidentialCreditsInstruction,
type ParsedDisableCpiGuardInstruction,
type ParsedDisableMemoTransfersInstruction,
Expand Down Expand Up @@ -166,6 +167,7 @@ export enum Token2022Instruction {
Reallocate,
EnableMemoTransfers,
DisableMemoTransfers,
CreateNativeMint,
InitializeNonTransferableMint,
EnableCpiGuard,
DisableCpiGuard,
Expand Down Expand Up @@ -418,6 +420,9 @@ export function identifyToken2022Instruction(
) {
return Token2022Instruction.DisableMemoTransfers;
}
if (containsBytes(data, getU8Encoder().encode(31), 0)) {
return Token2022Instruction.CreateNativeMint;
}
if (containsBytes(data, getU8Encoder().encode(32), 0)) {
return Token2022Instruction.InitializeNonTransferableMint;
}
Expand Down Expand Up @@ -718,6 +723,9 @@ export type ParsedToken2022Instruction<
| ({
instructionType: Token2022Instruction.DisableMemoTransfers;
} & ParsedDisableMemoTransfersInstruction<TProgram>)
| ({
instructionType: Token2022Instruction.CreateNativeMint;
} & ParsedCreateNativeMintInstruction<TProgram>)
| ({
instructionType: Token2022Instruction.InitializeNonTransferableMint;
} & ParsedInitializeNonTransferableMintInstruction<TProgram>)
Expand Down
39 changes: 39 additions & 0 deletions clients/js/test/createNativeMint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Account, address, none } from '@solana/web3.js';
import test from 'ava';
import { Mint, fetchMint, getCreateNativeMintInstruction } from '../src';
import {
createDefaultSolanaClient,
generateKeyPairSignerWithSol,
sendAndConfirmInstructions,
} from './_setup';

//Mint for native SOL Token accounts
const NATIVE_MINT = address('9pan9bMn5HatX4EJdBwg9VgCa7Uz5HL8N1m5D3NdXejP');

test('it creates a native mint account', async (t) => {
// Given a payer account.
const client = createDefaultSolanaClient();
const payer = await generateKeyPairSignerWithSol(client);

// When we create a native mint account.
await sendAndConfirmInstructions(client, payer, [
getCreateNativeMintInstruction({
payer: payer,
nativeMint: NATIVE_MINT,
}),
]);

// Then we expect the native mint account to exist with the following data.
const nativeMintAccount = await fetchMint(client.rpc, NATIVE_MINT);
t.like(nativeMintAccount, <Account<Mint>>{
address: NATIVE_MINT,
data: {
mintAuthority: none(), // Native mint has no mint authority
supply: 0n,
decimals: 9, // Native SOL has 9 decimals
isInitialized: true,
freezeAuthority: none(),
extensions: none(), // Native mint doesn't have extensions
},
});
});
67 changes: 67 additions & 0 deletions program/idl.json
Original file line number Diff line number Diff line change
Expand Up @@ -5345,6 +5345,73 @@
}
]
},
{
"kind": "instructionNode",
"name": "createNativeMint",
"docs": [
"Creates the native mint.",
"",
"This instruction only needs to be invoked once after deployment and is",
"permissionless. Wrapped SOL (`native_mint::id()`) will not be",
"available until this instruction is successfully executed."
],
"optionalAccountStrategy": "programId",
"accounts": [
{
"kind": "instructionAccountNode",
"name": "payer",
"isWritable": true,
"isSigner": true,
"isOptional": false,
"docs": ["Funding account (must be a system account)"]
},
{
"kind": "instructionAccountNode",
"name": "nativeMint",
"isWritable": true,
"isSigner": false,
"isOptional": false,
"docs": ["The native mint address"]
},
{
"kind": "instructionAccountNode",
"name": "systemProgram",
"isWritable": false,
"isSigner": false,
"isOptional": false,
"docs": ["System program for mint account funding"],
"defaultValue": {
"kind": "publicKeyValueNode",
"publicKey": "11111111111111111111111111111111"
}
}
],
"arguments": [
{
"kind": "instructionArgumentNode",
"name": "discriminator",
"defaultValueStrategy": "omitted",
"docs": [],
"type": {
"kind": "numberTypeNode",
"format": "u8",
"endian": "le"
},
"defaultValue": {
"kind": "numberValueNode",
"number": 31
}
}
],
"remainingAccounts": [],
"discriminators": [
{
"kind": "fieldDiscriminatorNode",
"name": "discriminator",
"offset": 0
}
]
},
{
"kind": "instructionNode",
"name": "initializeNonTransferableMint",
Expand Down

0 comments on commit 40eec02

Please sign in to comment.