A permissive conversion at the boundary
The local recorder converts public program-log data into research events. Its trade parser used int(bool(byte)) for a Borsh boolean. Python treats every nonzero integer as true; the wire format permits only 0 or 1. Synthetic bytes 2 and 255 therefore became valid-looking buy records. The Borsh specification defines the canonical representations.
A second boundary needed separate treatment. A transaction can mention Pump while another program emits a data line. A matching event discriminator establishes a byte layout, not who emitted it. Solana documents the subscription filter at transaction scope. Our recorder had been decoding matching data lines without tracking the active invocation.
Parsing the bytes and attributing the event are two different checks.
Small inputs that expose the failure
- Open the boolean fixture. Keep the synthetic trade prefix intact and select byte
2, then255. Compare coercion with canonical validation. - Use
0and1as positive controls. A stricter decoder must still accept legitimate sell and buy values. - Open the attribution fixture. Put identical synthetic event bytes inside an
OTHERframe nested underPUMP. The transaction mentions Pump; the active emitter is still Other.
| Input | Old coercion | Canonical decoder |
|---|---|---|
| 0 | SELL / 0 | Accept / 0 |
| 1 | BUY / 1 | Accept / 1 |
| 2 or 255 | BUY / 1 | Reject invalid bool |
Program PUMP invoke [1]
Program OTHER invoke [2]
Program data: <synthetic event payload>
Program OTHER success
Program PUMP success
# Active emitter at the data line: OTHER
# Expected admitted Pump events: 0The public lab uses symbolic program IDs and a reduced model. Its downloadable script reproduces the boundary checks without a network connection; it is not the complete production decoder.
Validate, attribute, then admit
- buy = int(bool(wire[56]))
+ is_buy = wire[56]
+ if is_buy not in (0, 1):
+ raise ValueError("noncanonical Borsh bool")
+ buy = is_buyValidation also checks the discriminator and the full consumed prefix before indexing. The trade path consumes 113 bytes; its former 105-byte precheck was too short. Length-prefixed strings now require complete byte ranges and valid UTF-8. Invalid payloads increment diagnostic counters instead of becoming events.
The transaction-level repair tracks anchored invocation, success and failure records as a stack. Data is eligible only when Pump is the active frame. Candidate events remain buffered until the complete transaction has been checked. A caught failing child discards its subtree; valid sibling events can remain. Non-null transaction errors, malformed control records and incomplete stacks discard the batch. The stream also rejects missing transaction status.
Prefix compatibility remains explicit. Each decoded event records consumed bytes, remaining bytes, scope: prefix_only and full_schema_validated: false. Appended fields do not silently become validated fields. The pinned public IDL is a schema reference, not verification of a deployed binary.
Test the rejection paths
The project's decoder regressions load reviewed pure functions through Python's AST, avoiding the recorder's websocket and writer initialization. Public examples contain only synthetic inputs and reduced logic.
- Canonical values, invalid booleans, wrong discriminators, and every truncation of the consumed trade/create prefixes.
- Multibyte strings, impossible lengths, malformed UTF-8, strict base64, and explicit unparsed extensions.
- Direct and nested emitter traces, foreign frames, truncated stacks, failed traces and transaction errors.
The repository verification passed 33 decoder test methods on 10 September 2026, including an in-memory stream/writer boundary. This is a dated repository result, separate from the smaller public script. A valid direct event still passes; malformed or wrongly attributed data cannot quietly become a trusted research row.
What this work establishes
The work concerns our recorder defects, demonstrated with synthetic data. It does not establish a Pump contract vulnerability, malicious transaction, exploit, or historical corruption rate. No account connection, signing or transaction submission was needed.
REMAINING LIMITS
Invocation attribution depends on the integrity and completeness of the supplied RPC logs. Processed events are not finalized-chain proof. Prefix parsing leaves current-IDL extensions, quote units and fees unvalidated. Missing creates, reconnect gaps and receipt-time heuristics still limit the research tape. A sell or shared launch slot does not establish common ownership or rug-pull intent.
For backend and security engineering, this is the transferable work: define the trust boundary, preserve compatibility deliberately, reject ambiguous input, expose diagnostic state and separate a verified property from a broader claim.
PRIMARY REFERENCES
- Borsh specification — canonical booleans, field order and string encoding.
- Solana logsSubscribe — transaction filtering, notification shape and commitment.
- Pump's published IDL, pinned reference — reviewed event field layout.
- Anchor event emission — log transport and truncation limits.