You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 210ca10
Browse filesBrowse the repository at this point in the historyBrowse files
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:
114
+
Given an IDL enriched with `display` metadata, this package presents a concrete instruction to humans, e.g. to clear sign it. The result carries both presentation modes and lets the renderer choose:
115
115
116
116
```ts
117
117
typeInstructionDisplay= {
118
-
// A short imperative label, e.g. "Transfer" (derived from the instruction name when absent).
118
+
// A short imperative label, e.g. "Transfer", derived from the instruction identifier when absent.
119
119
intent:string;
120
-
// The interpolated sentence, e.g. "Transfer 1.5 USDC to toly.sol", or `null` when a
121
-
//placeholder cannot be resolved (the renderer then falls back to `fields`).
120
+
// The interpolated sentence, e.g. "Transfer 1.5 USDC to Alice", or `null` when it cannot be
121
+
//presented with confidence, in which case the renderer falls back to `fields`.
122
122
interpolatedIntent:string|null;
123
-
// The structured fallback list of labelled fields, e.g. [{ label: 'Amount', value: '1.5 USDC' }].
123
+
// The rows of the fallback list, e.g. [{ label: 'Amount', value: '1.5 USDC' }].
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).
The same, starting from an already-parsed instruction (`ParsedInstruction` from [`@codama/dynamic-parsers`](../dynamic-parsers/README.md)). Useful when you have already called `parseInstruction`.
130
+
Presents an instruction parsed with `parseInstruction` from [`@codama/dynamic-parsers`](../dynamic-parsers/README.md). Values are formatted with the formatters of [`@codama/dynamic-codecs`](../dynamic-codecs/README.md#formatting), whose options it accepts, e.g. `formatAddress` to name addresses or `numberFormat` for a locale.
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 and visibly: an amount whose scale cannot be resolved renders marked as raw in the field list (e.g. `1500000 (raw)`) so it cannot be mistaken for a scaled amount, any sentence referencing it is suppressed (`interpolatedIntent` becomes `null`, falling back to the fields), and `whenInjected` members remain visible.
156
-
157
-
`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.
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.
170
-
171
-
## Offline display dictionary
172
-
173
-
An offline renderer — typically a hardware wallet — cannot reach an RPC to resolve the values above, nor a name service to present addresses. The **display dictionary** is a serialisable bundle of exactly that external data, assembled by an online companion and handed to the device so it can resolve a display with no network access.
174
-
175
-
```ts
176
-
typeDisplayDictionary= {
177
-
// Fetched on-chain account state, keyed by address (the offline counterpart of `fetchAccount`).
178
-
accounts:ReadonlyMap<Address, EncodedAccount>;
179
-
// Human-readable names, keyed by address — a `.sol` domain, token symbol, program label, alias…
180
-
names:ReadonlyMap<Address, string>;
181
-
};
182
-
```
183
-
184
-
Only accounts that exist are stored: a missing key means "no data for this address", which is all the renderer can act on. It cannot, nor does it need to, distinguish an account that was never fetched from one that does not exist on-chain — both degrade the display the same way.
185
-
186
-
The `names` map is deliberately generic: it names an address, whatever the source. This is how an offline renderer recovers the presentation the online layer would delegate to it.
187
-
188
-
### Building the dictionary (online)
189
-
190
-
`getRequiredAccountsForDisplay(root, parsedInstruction)` returns the addresses whose account state a display would read — computed statically from the IDL and the instruction, with no network access. `getDisplayAccountMap` uses it to batch-fetch those accounts into the `accounts` map:
> A filler for the `names` map is **not** provided: its data comes from sources Codama has no opinion on (name services, token registries, curated label lists). Populate it yourself from whichever sources you trust.
206
-
207
-
### Consuming the dictionary (offline)
145
+
-**Rows:** one per data field, then one per account and remaining account. Optional accounts that are omitted, by leaving them out or by passing the program address, are left out, and remaining accounts are shown even when the IDL declares no groups for them. Labels come from display nodes, or from identifiers in title case. Members skipped `always` are left out, flattened structs are replaced by their prefixed fields, and lists of addresses show one numbered row per address. Other structs, lists and maps show as a single row, e.g. `Config: Fee: 25, Owner: Alice`, and empty values, e.g. an empty list or string, show as `Empty`.
146
+
-**Value labels:** a field matching one of its value labels shows the label rather than its formatted value, in its row and in the sentence.
147
+
-**Addresses:** every address, in public key fields, account rows, placeholders and address units, goes through the `formatAddress` option.
148
+
-**Injected values:** injected values of display nodes, e.g. the `decimals` of an amount, resolve from the `provides` of the instruction and its parents: literal values, accounts, the program, or values of the instruction data.
149
+
-**Degraded amounts:** an amount whose decimals cannot be resolved shows its raw value followed by `(raw)`, so it cannot be mistaken for a scaled amount, and makes the sentence `null`. So do placeholders referring to omitted accounts or empty values.
208
150
209
-
`fetchAccounts` (a `FetchMaybeAccountsFn`, since missing accounts are dropped) is the batch counterpart of the display layer's `fetchAccount`; wire it to Kit's `fetchEncodedAccounts` for a single `getMultipleAccounts` round-trip. The bundle is encoded with byte codecs — `getDisplayDictionaryCodec` (and per-map `getDisplayAccountMapCodec` / `getDisplayNamedMapCodec`, each also available as split `…Encoder` / `…Decoder`). The offline renderer decodes it and resolves the display from the maps: account bytes are decoded through the IDL's `accountLink` exactly as online, and addresses are named from `names`.
151
+
Account data is not read yet, so values read from accounts, e.g. `${accounts.mint.data.decimals}`, cannot be presented, and members skipped `whenInjected`are always shown.
0 commit comments