From 84c53ef59a2a20a9a15df2bbf66f181573507225 Mon Sep 17 00:00:00 2001 From: Steven Luscher Date: Sat, 1 Mar 2025 00:25:34 +0000 Subject: [PATCH] Document `@solana/instructions` with TypeDoc --- packages/instructions/README.md | 6 +-- packages/instructions/src/accounts.ts | 57 ++++++++++++++++++++++++ packages/instructions/src/index.ts | 4 ++ packages/instructions/src/instruction.ts | 43 ++++++++++++++++++ packages/instructions/src/roles.ts | 45 ++++++++++++++++++- 5 files changed, 150 insertions(+), 5 deletions(-) diff --git a/packages/instructions/README.md b/packages/instructions/README.md index 1cdad0080..45ae27ba3 100644 --- a/packages/instructions/README.md +++ b/packages/instructions/README.md @@ -75,7 +75,7 @@ type StakeProgramInstruction = IInstruction<'StakeConfig111111111111111111111111 ### `IInstructionWithAccounts` -Use this type to specify an instruction that contains certain accounts. +Use this type to specify an instruction that loads certain accounts. ```ts type InstructionWithTwoAccounts = IInstructionWithAccounts< @@ -88,7 +88,7 @@ type InstructionWithTwoAccounts = IInstructionWithAccounts< ### `IInstructionWithData` -Use this type to specify an instruction whose data conforms to a certain type. This is most useful when you have a branded `Uint8Array` that represents a particular instruction. +Use this type to specify an instruction whose data conforms to a certain type. This is most useful when you have a branded `Uint8Array` that represents a particular instruction's data. For example, here is how the `AdvanceNonce` instruction is typed. @@ -134,7 +134,7 @@ Returns an `AccountRole` representing the non-signer variant of the supplied rol ### `downgradeRoleToReadonly(role: AccountRole)` -Returns an `AccountRole` representing the non-writable variant of the supplied role. +Returns an `AccountRole` representing the read-only variant of the supplied role. ### `upgradeRoleToSigner(role: AccountRole)` diff --git a/packages/instructions/src/accounts.ts b/packages/instructions/src/accounts.ts index e99c11b00..9ec363401 100644 --- a/packages/instructions/src/accounts.ts +++ b/packages/instructions/src/accounts.ts @@ -2,22 +2,73 @@ import { Address } from '@solana/addresses'; import { AccountRole } from './roles'; +/** + * Represents an account's address and metadata about its mutability and whether it must be a signer + * of the transaction. + * + * Typically, you will use one of its subtypes. + * + * | | `role` | `isSigner` | `isWritable` | + * | --------------------------------- | ----------------------------- | ---------- | ------------ | + * | `ReadonlyAccount` | `AccountRole.READONLY` | No | No | + * | `WritableAccount` | `AccountRole.WRITABLE` | No | Yes | + * | `ReadonlySignerAccount` | `AccountRole.READONLY_SIGNER` | Yes | No | + * | `WritableSignerAccount` | `AccountRole.WRITABLE_SIGNER` | Yes | Yes | + * + * @example A type for the Rent sysvar account + * ```ts + * type RentSysvar = ReadonlyAccount<'SysvarRent111111111111111111111111111111111'>; + * ``` + */ export interface IAccountMeta { readonly address: Address; readonly role: AccountRole; } +/** + * @see {@link IAccountMeta} + */ export type ReadonlyAccount = IAccountMeta & { readonly role: AccountRole.READONLY; }; +/** + * @see {@link IAccountMeta} + */ export type WritableAccount = IAccountMeta & { role: AccountRole.WRITABLE }; +/** + * @see {@link IAccountMeta} + */ export type ReadonlySignerAccount = IAccountMeta & { role: AccountRole.READONLY_SIGNER; }; +/** + * @see {@link IAccountMeta} + */ export type WritableSignerAccount = IAccountMeta & { role: AccountRole.WRITABLE_SIGNER; }; +/** + * Represents a lookup of the account's address in an address lookup table. It specifies which + * lookup table account in which to perform the lookup, the index of the desired account address in + * that table, and metadata about its mutability. Notably, account addresses obtained via lookups + * may not act as signers. + * + * Typically, you will use one of its subtypes. + * + * | | `role` | `isSigner` | `isWritable` | + * | ------------------------------------------------------ | ---------------------- | ---------- | ------------ | + * | `ReadonlyLookupAccount` | `AccountRole.READONLY` | No | No | + * | `WritableLookupAccount` | `AccountRole.WRITABLE` | No | Yes | + * + * @example A type for the Rent sysvar account that you looked up in a lookup table + * ```ts + * type RentSysvar = ReadonlyLookupAccount< + * 'SysvarRent111111111111111111111111111111111', + * 'MyLookupTable111111111111111111111111111111' + * >; + * ``` + */ export interface IAccountLookupMeta { readonly address: Address; readonly addressIndex: number; @@ -25,10 +76,16 @@ export interface IAccountLookupMeta = IAccountLookupMeta & { readonly role: AccountRole.READONLY }; +/** + * @see {@link IAccountLookupMeta} + */ export type WritableAccountLookup< TAddress extends string = string, TLookupTableAddress extends string = string, diff --git a/packages/instructions/src/index.ts b/packages/instructions/src/index.ts index 90c377cb4..0e884549f 100644 --- a/packages/instructions/src/index.ts +++ b/packages/instructions/src/index.ts @@ -1,3 +1,7 @@ +/** + * This package contains types for creating transaction instructions. It can be used standalone, but it is also exported as part of Kit [`@solana/kit`](https://github.com/anza-xyz/kit/tree/main/packages/kit). + * @packageDocumentation + */ export * from './accounts'; export * from './instruction'; export * from './roles'; diff --git a/packages/instructions/src/instruction.ts b/packages/instructions/src/instruction.ts index a7c676bdf..a77f32842 100644 --- a/packages/instructions/src/instruction.ts +++ b/packages/instructions/src/instruction.ts @@ -9,6 +9,14 @@ import { import { IAccountLookupMeta, IAccountMeta } from './accounts'; +/** + * An instruction destined for a given program. + * + * @example + * ```ts + * type StakeProgramInstruction = IInstruction<'StakeConfig11111111111111111111111111111111'>; + * ``` + */ export interface IInstruction< TProgramAddress extends string = string, TAccounts extends readonly (IAccountLookupMeta | IAccountMeta)[] = readonly (IAccountLookupMeta | IAccountMeta)[], @@ -18,6 +26,19 @@ export interface IInstruction< readonly programAddress: Address; } +/** + * An instruction that loads certain accounts. + * + * @example + * ```ts + * type InstructionWithTwoAccounts = IInstructionWithAccounts< + * [ + * WritableAccount, // First account + * RentSysvar, // Second account + * ] + * >; + * ``` + */ export interface IInstructionWithAccounts extends IInstruction { readonly accounts: TAccounts; @@ -61,6 +82,28 @@ export function assertIsInstructionWithAccounts< } } +/** + * An instruction whose data conforms to a certain type. + * + * This is most useful when you have a branded `Uint8Array` that represents a particular + * instruction's data. + * + * @example A type for the \`AdvanceNonce\` instruction of the System program + * ```ts + * type AdvanceNonceAccountInstruction< + * TNonceAccountAddress extends string = string, + * TNonceAuthorityAddress extends string = string, + * > = IInstruction<'11111111111111111111111111111111'> & + * IInstructionWithAccounts< + * [ + * WritableAccount, + * ReadonlyAccount<'SysvarRecentB1ockHashes11111111111111111111'>, + * ReadonlySignerAccount, + * ] + * > & + * IInstructionWithData; + * ``` + */ export interface IInstructionWithData extends IInstruction { readonly data: TData; } diff --git a/packages/instructions/src/roles.ts b/packages/instructions/src/roles.ts index bed913e3f..a13c28278 100644 --- a/packages/instructions/src/roles.ts +++ b/packages/instructions/src/roles.ts @@ -1,7 +1,17 @@ /** - * Quick primer on bitwise operations: https://stackoverflow.com/a/1436448/802047 + * Describes the purpose for which an account participates in a transaction. + * + * Every account that participates in a transaction can be read from, but only ones that you mark as + * writable may be written to, and only ones that you indicate must sign the transaction will gain + * the privileges associated with signers at runtime. + * + * | | `isSigner` | `isWritable` | + * | ----------------------------- | ---------- | ------------ | + * | `AccountRole.READONLY` | ❌ | ❌ | + * | `AccountRole.WRITABLE` | ❌ | ✅ | + * | `AccountRole.READONLY_SIGNER` | ✅ | ❌ | + * | `AccountRole.WRITABLE_SIGNER` | ✅ | ✅ | */ - export enum AccountRole { // Bitflag guide: is signer ⌄⌄ is writable WRITABLE_SIGNER = /* 3 */ 0b11, // prettier-ignore @@ -10,9 +20,13 @@ export enum AccountRole { READONLY = /* 0 */ 0b00, // prettier-ignore } +// Quick primer on bitwise operations: https://stackoverflow.com/a/1436448/802047 const IS_SIGNER_BITMASK = 0b10; const IS_WRITABLE_BITMASK = 0b01; +/** + * @returns An {@link AccountRole} representing the non-signer variant of the supplied role. + */ export function downgradeRoleToNonSigner(role: AccountRole.READONLY_SIGNER): AccountRole.READONLY; export function downgradeRoleToNonSigner(role: AccountRole.WRITABLE_SIGNER): AccountRole.WRITABLE; export function downgradeRoleToNonSigner(role: AccountRole): AccountRole; @@ -20,6 +34,9 @@ export function downgradeRoleToNonSigner(role: AccountRole): AccountRole { return role & ~IS_SIGNER_BITMASK; } +/** + * @returns An {@link AccountRole} representing the read-only variant of the supplied role. + */ export function downgradeRoleToReadonly(role: AccountRole.WRITABLE): AccountRole.READONLY; export function downgradeRoleToReadonly(role: AccountRole.WRITABLE_SIGNER): AccountRole.READONLY_SIGNER; export function downgradeRoleToReadonly(role: AccountRole): AccountRole; @@ -27,14 +44,32 @@ export function downgradeRoleToReadonly(role: AccountRole): AccountRole { return role & ~IS_WRITABLE_BITMASK; } +/** + * Returns `true` if the {@link AccountRole} given represents that of a signer. Also refines the + * TypeScript type of the supplied role. + */ export function isSignerRole(role: AccountRole): role is AccountRole.READONLY_SIGNER | AccountRole.WRITABLE_SIGNER { return role >= AccountRole.READONLY_SIGNER; } +/** + * Returns `true` if the {@link AccountRole} given represents that of a writable account. Also + * refines the TypeScript type of the supplied role. + */ export function isWritableRole(role: AccountRole): role is AccountRole.WRITABLE | AccountRole.WRITABLE_SIGNER { return (role & IS_WRITABLE_BITMASK) !== 0; } +/** + * Given two {@link AccountRole | AccountRoles}, will return the {@link AccountRole} that grants the + * highest privileges of both. + * + * @example + * ```ts + * // Returns `AccountRole.WRITABLE_SIGNER` + * mergeRoles(AccountRole.READONLY_SIGNER, AccountRole.WRITABLE); + * ``` + */ export function mergeRoles(roleA: AccountRole.WRITABLE, roleB: AccountRole.READONLY_SIGNER): AccountRole.WRITABLE_SIGNER; // prettier-ignore export function mergeRoles(roleA: AccountRole.READONLY_SIGNER, roleB: AccountRole.WRITABLE): AccountRole.WRITABLE_SIGNER; // prettier-ignore export function mergeRoles(roleA: AccountRole, roleB: AccountRole.WRITABLE_SIGNER): AccountRole.WRITABLE_SIGNER; // prettier-ignore @@ -49,6 +84,9 @@ export function mergeRoles(roleA: AccountRole, roleB: AccountRole): AccountRole return roleA | roleB; } +/** + * @returns An {@link AccountRole} representing the signer variant of the supplied role. + */ export function upgradeRoleToSigner(role: AccountRole.READONLY): AccountRole.READONLY_SIGNER; export function upgradeRoleToSigner(role: AccountRole.WRITABLE): AccountRole.WRITABLE_SIGNER; export function upgradeRoleToSigner(role: AccountRole): AccountRole; @@ -56,6 +94,9 @@ export function upgradeRoleToSigner(role: AccountRole): AccountRole { return role | IS_SIGNER_BITMASK; } +/** + * @returns An {@link AccountRole} representing the writable variant of the supplied role. + */ export function upgradeRoleToWritable(role: AccountRole.READONLY): AccountRole.WRITABLE; export function upgradeRoleToWritable(role: AccountRole.READONLY_SIGNER): AccountRole.WRITABLE_SIGNER; export function upgradeRoleToWritable(role: AccountRole): AccountRole;