Skip to content

Commit a170e81

Browse files
committed
Document the instruction display API in the README
This PR documents the clear-signing instruction display API in the @codama/dynamic-instructions README. It adds an Instruction display section covering getInstructionDisplay and getInstructionDisplayFromParsedInstruction, the InstructionDisplay result shape (intent, interpolatedIntent, fields), and the fetchAccountData option for resolving account-sourced values, noting that address presentation is left to the renderer. No changeset is included, consistent with the rest of the 1.7.0 stack.
1 parent 15e0425 commit a170e81

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

packages/dynamic-instructions/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
This package provides a runtime Solana instruction builder that dynamically constructs `Instruction` (`@solana/instructions`). It provides instruction arguments encoding and validation, accounts resolution. Powers [`@codama/dynamic-client`](../dynamic-client/README.md) with `InstructionsBuilder`.
1111

12+
It also provides a **clear-signing display** layer that turns a concrete instruction into human-readable text — see [Instruction display](#instruction-display-clear-signing).
13+
1214
## Installation
1315

1416
```sh
@@ -112,3 +114,62 @@ import type { TransferArgs } from './generated/<idl-name>-instruction-types';
112114

113115
const data = encodeInstructionArguments<TransferArgs>(root, ixNode, { amount: 1_000_000_000n });
114116
```
117+
118+
## Instruction display (clear signing)
119+
120+
Given an IDL enriched with `display` metadata (per [sRFC 39](https://github.com/solana-foundation/SRFCs/discussions/4)), this package resolves a concrete instruction into human-readable text for user verification. The result carries both presentation modes and lets the renderer choose:
121+
122+
```ts
123+
type InstructionDisplay = {
124+
// A short imperative label, e.g. "Transfer" (derived from the instruction name when absent).
125+
intent: string;
126+
// The interpolated sentence, e.g. "Transfer 1.5 USDC to toly.sol", or `null` when a
127+
// placeholder cannot be resolved (the renderer then falls back to `fields`).
128+
interpolatedIntent: string | null;
129+
// The structured fallback list of labelled fields, e.g. [{ label: 'Amount', value: '1.5 USDC' }].
130+
fields: { label: string; value: string }[];
131+
};
132+
```
133+
134+
### `getInstructionDisplay(root, instruction, options?)`
135+
136+
Parses a raw `Instruction` (`@solana/instructions`) against the root and resolves its display. Returns `null` when the instruction cannot be identified or decoded (e.g. an instruction from an unknown program).
137+
138+
```ts
139+
import { getInstructionDisplay } from '@codama/dynamic-instructions';
140+
141+
const display = await getInstructionDisplay(root, instruction);
142+
// => { intent: 'Transfer', interpolatedIntent: 'Transfer 1500000 to 3Wnd5…5PxJX', fields: [...] } | null
143+
```
144+
145+
### `getInstructionDisplayFromParsedInstruction(root, parsedInstruction, options?)`
146+
147+
The same, starting from an already-parsed instruction (`ParsedInstruction` from [`@codama/dynamic-parsers`](../dynamic-parsers/README.md)). Useful when you have already called `parseInstruction`.
148+
149+
```ts
150+
import { parseInstruction } from '@codama/dynamic-parsers';
151+
import { getInstructionDisplayFromParsedInstruction } from '@codama/dynamic-instructions';
152+
153+
const parsed = parseInstruction(root, instruction);
154+
if (parsed) {
155+
const display = await getInstructionDisplayFromParsedInstruction(root, parsed);
156+
}
157+
```
158+
159+
### Options
160+
161+
Some display values live in on-chain account state (e.g. a token's `decimals`/`symbol` injected into an amount, or interpolation paths that read an account field). Supply `fetchAccount` to resolve them; without it, such values degrade gracefully (amounts stay raw, `whenInjected` fields remain visible).
162+
163+
`fetchAccount` returns Kit's `MaybeEncodedAccount` — an `exists` flag plus, when the account exists, its raw bytes. No decoding is required on your side: the display layer decodes the bytes itself using the referenced account's `accountLink` from the IDL, which already describes the layout. This makes `fetchEncodedAccount` a drop-in.
164+
165+
```ts
166+
import type { Address } from '@solana/addresses';
167+
import { fetchEncodedAccount } from '@solana/accounts';
168+
169+
const display = await getInstructionDisplay(root, instruction, {
170+
// Forward Kit's MaybeEncodedAccount for an address.
171+
fetchAccount: (address: Address) => fetchEncodedAccount(rpc, address),
172+
});
173+
```
174+
175+
Address presentation (`.sol` names, address-book aliases, truncation) is intentionally left to the renderer: `fields` and `interpolatedIntent` contain raw base58 addresses that the consuming wallet/UI formats as it sees fit.

0 commit comments

Comments
 (0)