feat: Program Metadata Program — decode instructions, accounts & events; use PMP IDLs everywhere#1116
Conversation
…pector + account parsing - Decode the Program Metadata Program's own instructions (Initialize / Allocate / SetData / SetAuthority / SetImmutable / Trim / Close / Extend / Write) using the @solana-program/program-metadata Codama decoders, in both the transaction page and the inspector. Previously these rendered as raw hex / "Unknown Instruction". - Inspector: parse instructions of programs that publish an IDL via the Program Metadata Program (the transaction page already did this; the inspector did not). - Account data: decode account state using an Anchor-format IDL published through the Program Metadata Program when no legacy on-chain Anchor IDL exists, and show the "Anchor Data" tab for such PMP-only programs. Tests: ProgramMetadataDetailsCard decoding (Allocate/SetAuthority).
|
@ChewingGlass is attempting to deploy a commit to the Solana Foundation Team on Vercel. A member of the Team first needs to authorize it. |
Resolving a large IDL (e.g. a 500KB+ PMP IDL) through the server runtime intermittently aborts the response body with ERR_STREAM_PREMATURE_CLOSE. The route previously treated that as a non-transient error: it paged Sentry and returned a non-retryable 502, so consumers saw a hard "no IDL". Classify these connection-level fetch errors as retryable, attempt resolution a few times with a fresh client, and only warn (not page) when they persist.
…via BorshInstructionCoder The Program Metadata IDL instruction card decoded only through Codama's parseInstruction (incl. rootNodeFromAnchor). Some real Anchor IDLs can't be converted — e.g. an instruction with an unnamed arg makes rootNodeFromAnchor throw "Argument name [id] is missing from the instruction definition" — so those instructions rendered as "Unknown Instruction" even with the IDL present. Fall back to building an Anchor Program from the IDL and decoding with Anchor's (more lenient) BorshInstructionCoder via AnchorDetailsCard before giving up.
…es events AnchorDetailsCard decodes Anchor events from the transaction logs via useTransactionDetails(signature). The Program Metadata IDL card's Anchor fallback passed an empty signature, so a PMP-IDL program's events never decoded on the tx page. Forward the signature (present on the tx page; absent in the inspector, which has no logs anyway).
…tch past precompiles Anchor event decoding only handled `Program data:` (sol_log_data) lines and matched events to instructions by counting `invoke` log lines. Two consequences: - Programs that emit events as base64 via `Program log:` (e.g. Drift/Velocity's msg!-style logging) had their events ignored entirely. - Instructions that emit no `invoke` log — the ed25519/secp256k1 precompiles — shifted the running index, so events attached to a later instruction were matched to the wrong one (or dropped). extractEventsFromLogs now also collects base64-shaped `Program log:` payloads (plain-text logs fail the base64 check; the event decoder rejects any stragglers), and, given the ordered top-level program ids, matches invocations to their instruction by program id so precompiles don't offset the index. AnchorDetailsCard passes those program ids.
Remove the Program-log event-extraction test that hard-coded a Velocity/Drift OrderActionRecord payload and instruction names, and use a generic program id in the PMP IDL card test. The extractEventsFromLogs change is unchanged; only the protocol-specific fixture is gone.
Greptile SummaryThis PR teaches the explorer to use Program Metadata Program IDLs across more views. The main changes are:
Confidence Score: 4/5This is close, but the event raw-data pairing should be fixed before merging.
app/utils/program-logs.ts Important Files Changed
Reviews (2): Last reviewed commit: "feat(program-metadata): label PMP instru..." | Re-trigger Greptile |
| events.push(log.slice('Program data: '.length).trim()); | ||
| } else if (log.startsWith('Program log:')) { | ||
| const payload = log.slice('Program log: '.length).trim(); | ||
| if (isLikelyBase64Event(payload)) events.push(payload); |
There was a problem hiding this comment.
Raw Event Payloads Desynchronize
When an instruction logs a base64-looking Program log: message before a real Anchor event, this path adds both strings to eventDataList. ProgramEventsCard later filters failed decodes out of decodedEvents but still reads raw bytes by eventDataList[eventIndex], so the decoded event can show unrelated raw data in the Raw view.
The PMP instruction card decoded the data fields but rendered accounts as a bare numbered list. Label them by role (Metadata/Authority/Program/Program Data/…) using the per-instruction account ordering from @solana-program/program-metadata, with Writable/Signer badges; accounts beyond the known list fall back to "Account #N".
| } else if (currentIxIndex === instructionIndex) { | ||
| if (log.startsWith('Program data:')) { | ||
| events.push(log.slice('Program data: '.length).trim()); | ||
| } else if (log.startsWith('Program log:')) { |
There was a problem hiding this comment.
This still appends every base64-shaped Program log: payload before event decoding proves it is an event. When an instruction logs a base64-looking message before a real Anchor event, the decoder can drop the first payload but the raw view can still read from the original eventDataList by decoded-event index. The decoded event can then show the earlier non-event bytes as its raw payload. The raw bytes need to stay paired with the payload that decoded successfully, or the raw list needs to be filtered at the same point as decoding.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary
Makes the explorer understand the Program Metadata Program (PMP,
ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S) and actually use PMP‑published IDLs everywhere it parses program data — instructions, accounts, and events — not just on the transaction page.1. Decode the PMP's own instructions
Instructions to the Program Metadata Program (
Initialize/Allocate/SetData/SetAuthority/SetImmutable/Trim/Close/Extend/Write) rendered as raw hex / "Unknown Instruction". A newProgramMetadataDetailsCarddecodes them with the@solana-program/program-metadataCodama decoders (deterministic, no IDL fetch), wired into both the transaction page and the inspector.2. Use PMP‑published IDLs in the inspector
The transaction page already preferred a program's PMP IDL when decoding instructions; the inspector was Anchor‑only. The inspector now resolves the PMP IDL and renders the
ProgramMetadataIdlInstructionDetailsCard, so a program that publishes its interface via the PMP decodes there too.3. Decode account data via PMP‑published IDLs
AnchorAccountCardonly used the legacy on‑chain Anchor IDL, so accounts owned by programs that publish only a PMP IDL showed nothing and the "Anchor Data" tab was hidden. It now falls back to an Anchor‑format IDL fetched from the PMP (reusing the existing Borsh decode + row rendering), and the tab is shown for such programs.4. Decode Anchor‑format PMP IDLs that Codama can't convert
The PMP IDL instruction card decoded only through Codama (
parseInstruction/rootNodeFromAnchor). Real Anchor IDLs can fail that conversion — e.g. an instruction with an unnamed arg makesrootNodeFromAnchorthrowArgument name [id] is missing…— leaving the instruction "Unknown" even with the IDL present. It now falls back to building an AnchorProgramand decoding with Anchor's (more lenient)BorshInstructionCoderbefore giving up.5. Decode Anchor events emitted via
Program log:(and fix index matching)Two pre‑existing bugs in
extractEventsFromLogs:Program data:(sol_log_data) lines, so programs that emit events as base64 viaProgram log:(msg!) had their events ignored. It now also collects base64‑shapedProgram log:payloads (plain‑text logs fail the base64 check; the event decoder rejects any straggler).invokelog lines, but instructions that emit none — the ed25519/secp256k1 precompiles — shifted the index, mis‑attributing or dropping events. Given the ordered top‑level program ids, invocations are now matched to their instruction by program id.AnchorDetailsCardpasses those ids, and the PMP Anchor fallback forwards the signature so events can be read from the tx logs.6. Make IDL resolution resilient to transient fetch errors
Resolving a large IDL through the server runtime occasionally aborts the response body with
ERR_STREAM_PREMATURE_CLOSE. The/api/idl-latestroute treated that as fatal (paged Sentry, non‑retryable 502). It now classifies these connection‑level errors as retryable and retries with a fresh client.Motivation
Programs increasingly publish their IDL through the Program Metadata Program rather than a legacy Anchor IDL account. Without these changes the explorer shows raw hex for PMP instructions, can't decode the instructions/accounts/events of PMP‑only programs, and (for IDLs Codama can't convert or events logged via
Program log:) shows "Unknown" even when the IDL is present.Testing
pnpm test:ci,pnpm typecheck, andpnpm lintall green. New tests cover: PMP instruction decoding (ProgramMetadataDetailsCard), the Codama→Anchor instruction‑decode fallback and event‑signature forwarding (ProgramMetadataIdlInstructionDetailsCard), and the IDL route's retry/transient‑error handling.Screenshots