Skip to content

feat: Program Metadata Program — decode instructions, accounts & events; use PMP IDLs everywhere#1116

Open
ChewingGlass wants to merge 7 commits into
solana-foundation:masterfrom
ChewingGlass:feat/program-metadata-instruction-parsing
Open

feat: Program Metadata Program — decode instructions, accounts & events; use PMP IDLs everywhere#1116
ChewingGlass wants to merge 7 commits into
solana-foundation:masterfrom
ChewingGlass:feat/program-metadata-instruction-parsing

Conversation

@ChewingGlass

@ChewingGlass ChewingGlass commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

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 new ProgramMetadataDetailsCard decodes them with the @solana-program/program-metadata Codama 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

AnchorAccountCard only 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 makes rootNodeFromAnchor throw Argument name [id] is missing… — leaving the instruction "Unknown" even with the IDL present. It now falls back to building an Anchor Program and decoding with Anchor's (more lenient) BorshInstructionCoder before giving up.

5. Decode Anchor events emitted via Program log: (and fix index matching)

Two pre‑existing bugs in extractEventsFromLogs:

  • It only read Program data: (sol_log_data) lines, so programs that emit events as base64 via Program log: (msg!) had their events ignored. It now also collects base64‑shaped Program log: payloads (plain‑text logs fail the base64 check; the event decoder rejects any straggler).
  • It matched events to instructions by counting invoke log 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. AnchorDetailsCard passes 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-latest route 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, and pnpm lint all 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.

PMP IDL resolution is gated behind the existing NEXT_PUBLIC_PMP_IDL_ENABLED flag (same as the transaction page's current PMP path), so items 2–5 require it enabled.

Screenshots

To be added from a local instance before requesting review: a decoded Program Metadata Program instruction; a PMP‑only program's instruction decoded in the inspector; a PMP‑only program account's fields under the Anchor Data tab; and a decoded Anchor event.

…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).
@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

@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.
@ChewingGlass ChewingGlass changed the title feat: decode Program Metadata Program instructions & use PMP IDLs for inspector + account parsing feat: Program Metadata Program — decode instructions, accounts & events; use PMP IDLs everywhere Jun 26, 2026
@ChewingGlass ChewingGlass marked this pull request as ready for review June 26, 2026 21:48
@greptile-apps

greptile-apps Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR teaches the explorer to use Program Metadata Program IDLs across more views. The main changes are:

  • Decode the Program Metadata Program's own instructions.
  • Use PMP-published IDLs in the inspector and account data tab.
  • Add an Anchor fallback when Codama cannot parse a PMP IDL.
  • Read Anchor events from Program log: payloads and improve instruction matching.
  • Retry transient IDL fetch failures in the API route.

Confidence Score: 4/5

This is close, but the event raw-data pairing should be fixed before merging.

  • Base64-shaped non-event logs can still be kept beside real events.
  • The decoded event list and raw payload list can become misaligned.
  • The Raw view can show bytes from the wrong log entry.

app/utils/program-logs.ts

Important Files Changed

Filename Overview
app/utils/program-logs.ts Adds Program log: event extraction and program-id matching, but raw event payloads can still become mispaired after decode filtering.
app/components/instruction/AnchorDetailsCard.tsx Passes ordered top-level program IDs into event extraction for more accurate instruction matching.
app/components/instruction/program-metadata-idl/ProgramMetadataIdlInstructionDetailsCard.tsx Adds an Anchor decoder fallback for PMP IDLs that Codama cannot convert.
app/components/account/AnchorAccountCard.tsx Adds a PMP IDL fallback for account-data decoding.
app/api/idl-latest/route.ts Retries transient RPC and fetch-body failures during IDL resolution.

Reviews (2): Last reviewed commit: "feat(program-metadata): label PMP instru..." | Re-trigger Greptile

Comment thread app/utils/program-logs.ts
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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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".
Comment thread app/utils/program-logs.ts
} else if (currentIxIndex === instructionIndex) {
if (log.startsWith('Program data:')) {
events.push(log.slice('Program data: '.length).trim());
} else if (log.startsWith('Program log:')) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Raw payloads still drift

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.

@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
explorer Ready Ready Preview, Comment Jun 29, 2026 1:34pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants