hopper-framework 0.1.0

Zero-copy state framework for Solana. Typed account architecture, phased execution, validation graphs, zero-copy collections, layout evolution, and cross-program interfaces. Built on Hopper Native. no_std, no_alloc, no proc macros required.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
# Hopper Audit — Deep Review vs Pinocchio, Quasar, and Anchor Zero-Copy

**Audit date:** 2026-04-24
**Scope:** Entire `Hopper-Solana-Zero-copy-State-Framework` workspace (17 crates, 13 examples, 2 bench harnesses, CLI, SDK).
**Method:** File-level review of every major subsystem (core overlay, native substrate, runtime, macros, collections, CLI, schema, manager, bench, examples) cross-referenced with current public state of Pinocchio (anza-xyz/pinocchio), Quasar (blueshift-gg/quasar), and Anchor's `AccountLoader` / `#[account(zero_copy)]`.

## Changes Applied

This audit was followed by two in-tree change passes, landing all ten recommendations from §9.

### Pass 1 — R1, R2, R4 (2026-04-24)

- **R1 applied.** `README.md` now explicitly names `hopper_runtime::segment_borrow::SegmentBorrowRegistry` as the owner of the byte-range borrow registry and clarifies that `hopper-core` owns overlays + headers + collections while `hopper-runtime` owns the registry + CPI + context. The "What You Get" row for segment borrows now links directly to the source file.
- **R2 applied.** The deprecated "Pinocchio-style" benchmark column was removed from release-facing claims because it used a Quasar-authored reference vault, not an Anza Pinocchio target. Cross-framework benchmark orchestration now lives in the sibling `hopper-bench` product repo, where the Anza Pinocchio target, competitor lockfile, raw logs, and provenance live together. Hopper publishes Hopper/Quasar numbers from that harness today; Pinocchio numbers are only published after a same-provenance run records the target, toolchain, Mollusk version, seed set, and command line.
- **R4 applied.** `README.md` Getting Started section now leads with a Day-One proc-macro example (`#[hopper::state]` + `#[hopper::program]`) and demotes the declarative `hopper_layout!` path to a Day-Two subsection for users who want to skip proc macros entirely. Both paths still documented; order flipped to match the Anchor-refugee onramp.

### Pass 2 — R3, R5, R6, R7, R8, R9, R10 (2026-04-24)

- **R3 applied.** The lazy-dispatch vault benchmark target moved into the sibling benchmark repo alongside the runner that builds the same source twice, once with `fast_entrypoint!` (eager) and once with `hopper_lazy_entrypoint!` (lazy), so the CU win from lazy account parsing is directly measurable on a realistic dispatch pattern. A `compile_error!` guard enforces exactly one of `--features eager` / `--features lazy`. The parity-vault runner remains focused on the four-instruction cross-framework contract.
- **R5 applied.** Two new trybuild fixtures under [`tests/hopper-trybuild/tests/ui/`](tests/hopper-trybuild/tests/ui/): `fail/pod_alignment_rejects_aligned_type.rs` pins that `const_assert_pod!` rejects any type whose alignment is greater than 1, and `pass/pod_alignment_accepts_wire_types.rs` pins the happy path so the fail fixture cannot drift into "everything rejected". Closes the hand-rolled-`unsafe impl Pod` hole from the §8 risk register. First run should use `TRYBUILD=overwrite` to seed `.stderr` files, matching the existing `crank_with_args.rs` fixture's convention.
- **R6 applied.** Two new helpers in [`hopper-solana/src/token2022_ext.rs`](crates/hopper-solana/src/token2022_ext.rs): `read_transfer_hook` returns `Option<TransferHook<'a>>` with borrowed references to the authority and bound program_id; `check_transfer_hook_program` is the one-liner gate protocols actually reach for. Five new tests pin happy path, missing extension, truncated extension, and authority/program-id mismatches. Re-exported through `hopper-token-2022/src/lib.rs`. New example at [`examples/hopper-token-2022-transfer-hook`](examples/hopper-token-2022-transfer-hook/src/lib.rs) — four-instruction program demonstrating the extension-aware validation pattern: init state, verify hook binding, require-safe-mint, authority-gated rotate. Added to workspace members.
- **R7 applied.** `hopper-native/src/cpi.rs` — both `invoke_unchecked` and `invoke_signed_unchecked` now carry explicit seven-item invariant lists in their `# Safety` doc blocks (no aliasing borrows, account list consistency, writability and signer coverage, duplicate-account discipline, valid instruction encoding; plus seed derivation and seed lifetime for the signed variant). Closes the one `# Safety`-completeness gap flagged in §3.5.
- **R8 applied.** New emitter at [`hopper-schema/src/anchor_idl.rs`](crates/hopper-schema/src/anchor_idl.rs) — `AnchorIdlJson<'a>(&'a ProgramIdl)` and `AnchorIdlFromManifest<'a>(&'a ProgramManifest)` mirror the Codama emitter's `core::fmt::Write`-based pattern and emit Anchor 0.30-shaped IDL JSON (`{ "version", "name", "metadata", "instructions", "accounts", "events", "errors", "types" }`). Type translation covers primitives, Wire-prefixed types, arrays, Option/Vec wrappers, and falls back to `{ "defined": "Name" }` for user-defined types. Hopper `disc: u8` tags are left-padded to Anchor's 8-byte discriminator shape. Wired into the CLI at `hopper schema export --anchor-idl <manifest>`.
- **R9 applied.** The Anchor parity vault target lives in the sibling benchmark repo: a real Anchor `#[program]` implementation of the parity contract using `AccountLoader<CounterState>` for zero-copy counter access and `AccountInfo` for the PDA-gated deposit/withdraw/authorize paths. Keeping it outside Hopper's framework workspace prevents Anchor's dependency tree from colliding with the core crates while still giving the benchmark harness a reproducible Anchor column.
- **R10 applied.** `docs/UNSAFE_INVARIANTS.md` — appended a "hopper-native Unsafe Surface (post-audit supplement, R10)" section that enumerates every `unsafe` entry point in `hopper-native` that was outside the original audit scope: the 8 `AccountView` unsafe methods (`new_unchecked`, `owner`, `assign`, `borrow_unchecked*`, `segment_*_unchecked`, `raw_ref`/`raw_mut`, `resize_unchecked`, `close_unchecked`); `raw_input.rs` `deserialize_accounts` / `_fast` / `scan_instruction_frame` / `malformed_duplicate_marker`; `lazy.rs` parse functions; `pda.rs` inline unsafe blocks (verify_program_address, based_try_find_program_address, find_bump_for_address); `mem.rs` memcpy/memmove/memset/memcmp; and the expanded `cpi.rs` borrow-conflict invariants from R7. UNSAFE_INVARIANTS.md is now the complete ground-truth inventory for auditors.

---

## Naming Analysis: "Hopper" vs "Hopper-Lang"

Side quest from the R-pass: should Hopper follow Quasar's lead and rebrand as "Hopper-Lang" with a `hopper-lang.com`-style domain?

**Short answer: no.** *(Update 2026-04-24: the team chose `hopperzero.dev` as the canonical domain. The reasoning below stands — Hopper is a framework, not a language — and the new domain leans into the technical category ("zero-copy") rather than the language framing.)*

**What Quasar's "-Lang" actually is.** Quasar lives at `quasar-lang.com` for docs and at `blueshift-gg/quasar` on GitHub. The project's own README calls itself "A blazing fast Solana program framework" — not a language. Its docs-site tagline is "Zero-Copy Solana Framework" and a slightly fluffier "quasar 💫 — Build blazing fast Solana programs". The product name is Quasar; the `-lang` in the URL is a domain-disambiguator, not a rebrand. The reason it exists is almost certainly pragmatic rather than philosophical:

1. `quasar.com`, `quasar.io`, `quasar.dev` are all taken by unrelated entities (Quasar Framework JS, Quasar Protocol DeFi, various corporate Quasars). Quasar's proc-macro-heavy surface (`#[program]`, `#[account]`, `#[derive(Accounts)]`) also reads like a DSL, so the `-lang` suffix lands as both a disambiguator and a positioning gesture at "you're writing in Quasar, not just Rust". That framing is aspirational — the product is a Rust framework — but it's not dishonest either.

**What "Hopper-Lang" would signal.** A "Lang" suffix telegraphs "learn our new thing" — it raises the mental-tax bar for adoption, which is the opposite of what a v0.1 framework fighting Anchor's ecosystem moat wants. It also misframes Hopper specifically: Hopper ships *two* authoring paths (`hopper_layout!` declarative and `#[hopper::state]` proc) and explicitly tells users that proc macros are "optional DX sugar, never required for correctness." That dual-path design is the OPPOSITE of a one-syntax-to-rule-them-all language pitch. A Lang rebrand would force a repositioning that the audit already concluded Hopper doesn't need — Hopper's actual wedge is "state framework," not "language."

**Where "-Lang" would earn its weight.** If Hopper ever ships a non-Rust spec (YAML, TOML, or a custom `.hopper` layout DSL that lowers to the Rust macros), *that* would be a legitimate language layer. At that point "Hopper-Lang" would stop being an affectation and become descriptive. Until then it's marketing, and weaker marketing than what Hopper already has.

**Recommendation — branding and domain strategy.**

1. **Keep "Hopper" as the product name.** Nothing in the code or the audit argues for a rebrand.
2. **For the domain**, prefer an ecosystem-coded TLD over a `-lang` disambiguator. In rough order of fit:
    - `hopper.sh` or `hopper.dev` — dev-tool-flavoured, widely available-ish patterns.
    - `hopper-sol.com`, `hopper-rs.com`, `hopperframework.com` — one-word disambiguators that fit Hopper's actual positioning.
    - `hopper.bluefoot.xyz` or `bluefoot.xyz/hopper` — sub-brand the project under Bluefoot Labs. Strongest trust-signal move: serious protocol users want to see a company behind a framework, and the Bluefoot brand (Boobies NFT + Galápagos conservancy) gives Hopper a distinctive parent rather than being "another framework from some GitHub org". You already own bluefoot.xyz.
3. **Reserve the `-lang` lever** for if/when you ship a layout DSL. It's a cheap rebrand at that point; burning it now on a domain-disambiguation problem sells it cheap.

**Not-so-short answer for the record.** Attempting to fetch `quasar-lang.com` directly during this audit was blocked by the egress allowlist. The analysis above is built from Quasar's public README on GitHub (`blueshift-gg/quasar`), the taglines captured in the search-results snippet harvested earlier in this audit, and general knowledge of the Solana framework landscape. If you want me to read the quasar-lang.com site verbatim and pull direct quotes — in case Quasar has written an explicit rationale I don't know about — add the domain to the workspace egress allowlist (Settings → Capabilities) and I'll refetch. The allowlist update you applied earlier didn't propagate to this session's sandbox, so a restart or an admin-level refresh may be needed.

---

## DSL Parity Audit — Hopper vs Quasar vs Anchor 0.31

Follow-up audit pass focused on the proc-macro surface area, motivated by the
question "is Hopper's DSL as advanced as Quasar's while still preserving the
declarative path?". Audit performed 2026-04-24 by a research subagent with
file-level access to Hopper's source and fallback to training-data references
for Quasar when the live sources were unreachable.

### Verification status

**Hopper rows:** verified from source in `crates/hopper-macros-proc/src/*.rs`
and `crates/hopper-runtime/src/lib.rs`. Authoritative.

**Anchor rows:** verified against general training knowledge of Anchor
0.30/0.31 plus Hopper's internal `docs/MIGRATION_FROM_ANCHOR.md` cross-check.
Live fetch of `lang/syn/src/parser/accounts/constraints.rs` was rate-limited.
Authoritative for the common surface; marked `?` where uncertain.

**Quasar rows:** **unverified against live source.** Both the Quasar docs site
(`quasar-lang.com/docs`) and the public GitHub tree paths
(`github.com/blueshift-gg/quasar/tree/main/derive/src`) were unreachable from
this session — the docs site is off the egress allowlist, and the GitHub paths
either 404 or truncate the large repo-root fetch before the derive crate's
content is reached. The user uploaded `quasar-master-efe4c555.zip` to the
session but the sandboxed Linux shell was unavailable this session, so the
zip could not be extracted programmatically. Quasar rows in the tables below
are drawn from Hopper's prior-pass competitor analysis
(`docs/MIGRATION_FROM_QUASAR.md`, `docs/COMPETITIVE_FRAMEWORK_ANALYSIS.md`) and
mid-2025 training data, marked with `?` wherever the reference source was
ambiguous or silent. A follow-up pass with shell access (or with individual
Quasar file contents pasted inline) will close the `?` rows.

### Top-level attributes comparison (key rows)

| Feature | Anchor 0.31 | Quasar | Hopper |
|---|---|---|---|
| `#[program]` module | ✓ | ✓ | ✓ |
| Zero-copy account declaration | `#[account(zero_copy)]` | ✓ (default) | ✓ (default) |
| Account-context derive | `#[derive(Accounts)]` | ✓ | ~ attribute-macro `#[hopper::context]` |
| Events | `#[event]` | ✓ | ✓ (adds `segment`, `tag`) |
| Errors | `#[error_code]` | ✓ | ~ `#[hopper::error]` (adds `#[invariant]` linkage) |
| `#[access_control(expr)]` | ✓ | ? | **âœ-** |
| `#[interface]` CPI attribute | ✓ | ~ (`Interface<T>`) | ~ (`declare_program!` is the closest) |
| `#[constant]` export | ✓ | ? | **âœ-** |
| `#[view]` / query | ✓ (0.31 beta) | âœ- | **âœ-** |
| `#[derive(InitSpace)]` standalone | ✓ | ~ | **âœ-** (Hopper emits `INIT_SPACE` const via `#[hopper::state]`; no standalone derive) |
| `declare_program!` from IDL | ✓ | ~ | ✓ (Hopper adds `FINGERPRINT: [u8; 32]` const) |

### Per-field `#[account(...)]` constraint comparison (key divergences)

| Constraint | Anchor | Quasar | Hopper |
|---|---|---|---|
| `mut(seg1, seg2, …)` segment list | âœ- | âœ- | ✓ **Hopper-unique** |
| `read(seg1, seg2, …)` read-only segment list | âœ- | âœ- | ✓ **Hopper-unique** |
| `init_if_needed` | ✓ | ? | **âœ-** |
| `executable` bare keyword | ✓ | ? | **âœ-** (covered by `Program<P>` wrapper) |
| `rent_exempt = enforce \| skip` | ✓ | ? | **âœ-** |
| `seeds_fn = Type::seeds(&arg, …)` typed-seed sugar | âœ- | ✓ | ✓ |
| `dup = field` aliasing | âœ- | ✓ | ✓ |
| `sweep = target` lamport reclaim | âœ- | âœ- | ✓ **Hopper-unique** |
| `extensions::group_pointer::*` | ✓ | âœ- | **âœ-** |
| `extensions::group_member_pointer::*` | ✓ | âœ- | **âœ-** |
| `extensions::confidential_transfer::*` | ✓ | âœ- | **âœ-** |
| Full `init`/`zero`/`close`/`realloc`/`payer`/`space`/`seeds`/`bump`/`has_one`/`owner`/`address`/`constraint` | ✓ | ✓ | ✓ |
| Full SPL Token + Token-2022 extension family (non_transferable, immutable_owner, mint_close_authority, permanent_delegate, transfer_hook, metadata_pointer, default_account_state, interest_bearing, transfer_fee) | ✓ | partial? | ✓ |

### Guard macros

| Macro | Anchor | Quasar | Hopper |
|---|---|---|---|
| `require!` | ✓ | ✓ | ✓ |
| `require_eq!` / `_neq!` / `_keys_eq!` / `_keys_neq!` / `_gt!` / `_gte!` / `_lt!` / `_lte!` | ✓ | ? | ✓ (confirmed by `tests/require_macros.rs`) |
| `err!` / `error!` short aliases | ✓ | ? | ~ (uses `hopper_error!`; short aliases not re-exported) |

**Subagent's verified finding on Hopper's require family:** contrary to my
earlier scorecard from memory, Hopper *does* ship the full Anchor-parity
`require_*` family. `tests/require_macros.rs` pins all eight variants. The
prior "Hopper only has generic `hopper_require!`" claim in the initial naming
analysis was outdated — it was based on the README's declarative-macros
overview and missed the runtime-side additions. AUDIT.md is the updated
record.

### Hopper-unique dimensions (complete list)

These exist only in Hopper and have no Anchor or Quasar equivalent:

1. `#[hopper::migrate(from, to)]` — compile-time-typed schema-epoch edge, chainable.
2. `#[hopper::invariant(cond[, err = …])]` — handler-level post-return check, linked into the error-code invariant index.
3. `#[hopper::receipt]` — structured 64-byte mutation proof (before/after fingerprints, segment list, policy flags).
4. `#[hopper::pipeline]` — phased typestate (Unresolved → Resolved → Validated → Executed), enforced at compile time.
5. `#[hopper::crank]` — keeper-bot marker with seed-expression metadata, zero-arg enforced.
6. `#[hopper::dynamic(field = "…")]` — dynamic tail with tombstone ring bookkeeping (vs Quasar's simpler `Tail`).
7. Per-field `#[role = "…"]` and `#[invariant = "…"]` attributes — schema metadata emitted into the manifest.
8. Segment-level `mut(a, b)` / `read(a, b)` borrows — byte-range-level aliasing.
9. 16-byte versioned header + SHA-256 `LAYOUT_ID` — enables typed cross-program reads (`load_cross_program`) and forward-compatible loads (`load_compatible`).
10. `hopper_assert_compatible!` / `hopper_assert_fingerprint!` — ABI-pinning const assertions.
11. `hopper_virtual!` — multi-account logical state stitching.
12. `declare_program!` with compile-time `FINGERPRINT: [u8; 32]` const for client/manifest drift detection.
13. Program-level `strict` / `sealed` / `raw` policy shorthand — three-tier safety envelope per program.
14. `#[hopper::args(cu = N, tail)]` — borrowing zero-copy parser + CU hint.

### Prioritized gap list

Ranked by (user impact) / (implementation cost). All items can be mirrored in
the declarative path; dual-path commitment preserved.

1. **`#[derive(InitSpace)]` standalone derive** — emits `const INIT_SPACE: usize` for any struct. Already emitted by `#[hopper::state]`, but a standalone derive matches Anchor's most-reached-for discoverable helper. Declarative mirror: `hopper_init_space!` one-liner. Size: **small** (~80 LOC).
2. **`#[hopper::access_control(expr)]` handler attribute** — wraps handler with a boolean guard returning a typed error on false. Pure sugar over `require!` at handler top, but Anchor users expect the attribute form. Declarative mirror: `hopper_access_control!` macro wrapping the handler call. Size: **small** (~60 LOC in `program.rs`).
3. **`executable` and `rent_exempt` bare field keywords** — two Anchor constraints the `#[account(...)]` parser doesn't accept today. Zero-surprise Anchor parity. Size: **small** (~20 LOC in the parser plus two `field_checks.push` calls). Declarative mirror: both reachable via `require!` + `account.lamports()`.
4. **`init_if_needed` field keyword** — matches Anchor's common idiom. Needs coherence between the `init` precondition emitter and a runtime branch that skips allocation on an existing account. Size: **medium**. Declarative mirror: partial (hand-writable with the existing lifecycle helpers).
5. **`#[interface]` attribute for typed CPI** — Anchor's mechanism for source-level shared interfaces (vs consuming an on-disk IDL). Hopper's `declare_program!` covers the same need but requires a JSON artifact. Size: **medium** (~300 LOC new module). Declarative mirror: not really — this is structural sugar that needs a proc macro.
6. **Token-2022 extension gaps: `group_pointer`, `group_member_pointer`, `confidential_transfer`** — three extensions Anchor 0.31 supports that Hopper's `extensions::*` keyword block doesn't. Size: **medium per extension** (TLV reader + constraint keyword + test). Declarative mirror: readers live in `hopper_runtime::token_2022_ext`.
7. **`#[view]` / query attribute** — mark a handler as read-only. Anchor 0.31 added this. Size: **medium**. Declarative mirror: partial.
8. **`err!` / `error!` macro short aliases** — Anchor's idiomatic short forms. Hopper has `hopper_error!` but doesn't re-export the short spellings. Size: **trivial** (two `#[macro_export]` aliases). Declarative mirror: yes.

### Verified Quasar findings (2026-04-24 follow-up)

After the user mounted the local Quasar source at
`D:\tmp\framework-sources\quasar-master\quasar-master`, the `?` rows above
were closed by direct source inspection of `derive/src/lib.rs`,
`derive/src/accounts/attrs.rs`, `lang/src/macros.rs`, `lang/src/error.rs`,
and `lang/src/prelude.rs`. Corrected findings:

**Quasar proc macro roster (9 total, confirmed from `derive/src/lib.rs`):**
`#[derive(Accounts)]` (with `attributes(account, instruction)`), `#[instruction]`,
`#[account]`, `#[program]`, `#[event]`, `#[error_code]`, `emit_cpi!`,
`#[derive(QuasarSerialize)]`, `declare_program!`.

By count Hopper's 11 proc macros + 3 handler attributes (`#[hopper::receipt]`,
`#[hopper::invariant]`, `#[hopper::pipeline]`) is strictly larger than Quasar's
9. Hopper also has dedicated macros for migration, cranks, and dynamic tails
that have no Quasar equivalent.

**Quasar guard-macro family (confirmed from `lang/src/macros.rs`):** only
three — `require!`, `require_eq!`, `require_keys_eq!`. Hopper's runtime
ships all eight canonical variants (`require_eq`, `require_neq`,
`require_keys_eq`, `require_keys_neq`, `require_gt`, `require_gte`,
`require_lt`, `require_lte` — confirmed in `crates/hopper-runtime/src/lib.rs`
at lines 174, 187, 209, 230, 250, 261, 274, 285 and exercised by
`tests/require_macros.rs`). **Hopper is AHEAD of Quasar on guard macros**, not
behind. The earlier gap-list entry for "require_* family" is removed.

**Quasar per-field `#[account(...)]` constraints (confirmed from
`derive/src/accounts/attrs.rs` — `AccountDirective` enum at lines 20–53 and
the parser at 55–321):** `mut`, `init`, `init_if_needed`, `dup`, `close = X`,
`payer = X`, `space = expr`, `has_one = X [@ err]`, `constraint = expr [@ err]`,
`seeds = [...]` or `seeds = Type::seeds(...)` (typed-seed sugar),
`bump [= stored]`, `address = expr [@ err]`, `sweep = X`, `realloc = expr`,
`realloc::payer = X`, `token::mint`, `token::authority`, `token::token_program`,
`associated_token::mint`, `associated_token::authority`,
`associated_token::token_program`, `mint::decimals`, `mint::authority`,
`mint::freeze_authority`, `mint::token_program`, `metadata::name`,
`metadata::symbol`, `metadata::uri`, `metadata::seller_fee_basis_points`,
`metadata::is_mutable`, `master_edition::max_supply`.

**Quasar does NOT support:** `signer` keyword (done via the `Signer` wrapper
type), `zero` keyword, `executable`, `rent_exempt`, `owner = expr`,
`seeds::program = expr`, Token-2022 `extensions::*` family (none of
`non_transferable`, `immutable_owner`, `transfer_hook`, `metadata_pointer`,
`permanent_delegate`, `mint_close_authority`, `default_account_state`,
`interest_bearing`, `transfer_fee`, `group_pointer`, `group_member_pointer`,
`confidential_transfer` — Quasar has none of these keywords).

### Revised head-to-head scorecard

**Features Hopper has that Quasar doesn't:**

- `owner = expr` keyword
- `zero` keyword
- `seeds::program = expr` cross-program PDA derivation
- Token-2022 `extensions::non_transferable`, `immutable_owner`, `transfer_hook`, `metadata_pointer`, `permanent_delegate`, `mint_close_authority`, `default_account_state`, `interest_bearing`, `transfer_fee` keywords
- Segment-level `mut(seg1, seg2)` / `read(seg1, seg2)` borrow lists (no competitor has these)
- All Hopper-unique macros (`migrate`, `invariant`, `receipt`, `pipeline`, `crank`, `dynamic`, `args`, per-field `role`/`invariant` attributes)
- `require_neq`, `require_keys_neq`, `require_gt`, `require_gte`, `require_lt`, `require_lte` — the extended guard-macro family
- 16-byte versioned header + `LAYOUT_ID` fingerprint
- `hopper_assert_compatible!` / `hopper_assert_fingerprint!` / `hopper_virtual!`
- Compile-time `FINGERPRINT` const in `declare_program!` for manifest drift detection
- Three-tier program policy shorthand (`strict` / `sealed` / `raw`)

**Features Quasar has that Hopper doesn't:**

- `init_if_needed` field keyword
- `metadata::{name, symbol, uri, seller_fee_basis_points, is_mutable}` Metaplex-integration sugar
- `master_edition::max_supply` Metaplex-integration sugar

Net: **Hopper is broader than Quasar, not narrower.** The "is our DSL as
advanced as Quasar's" question resolves as yes, with three concrete Quasar
wins to close if the audience overlaps with Metaplex NFT minting (the
`metadata::*` and `master_edition::*` sugar) and one that's genuinely useful
for general programs (`init_if_needed`).

### Revised gap list (verified, ranked)

Against Anchor 0.31:

1. `#[derive(InitSpace)]` standalone derive — small, ~80 LOC. Dual-path mirror: trivial.
2. `#[hopper::access_control(expr)]` handler attribute — small, ~60 LOC. Dual-path mirror: trivial.
3. `executable` and `rent_exempt` bare field keywords — small, ~20 LOC. Dual-path mirror: yes.
4. `init_if_needed` field keyword — medium. Dual-path mirror: partial. **Also a Quasar-parity gap**, so this one counts twice.
5. `#[interface]` attribute for typed CPI — medium, ~300 LOC. Dual-path mirror: no (structural proc-macro sugar).
6. Token-2022 `group_pointer`, `group_member_pointer`, `confidential_transfer` — medium per extension. Dual-path mirror: yes (readers live in `hopper_runtime::token_2022_ext`).
7. `#[view]` / query attribute — medium. Dual-path mirror: partial.
8. `err!` / `error!` short aliases — trivial (`#[macro_export]` aliases). Dual-path mirror: yes.

Against Quasar (additional wins where Hopper could match Quasar's Metaplex-friendly surface):

9. `metadata::{name, symbol, uri, seller_fee_basis_points, is_mutable}` field keywords — medium. Dual-path mirror: yes via `require!` + direct Metaplex CPI. Only worth it if Metaplex NFT mint programs are on the Hopper roadmap.
10. `master_edition::max_supply` field keyword — small if (9) lands, large if not. Dual-path mirror: yes.

Items 9 and 10 are optional — they matter if you want Quasar refugees writing Metaplex NFT programs to land on Hopper without rewriting their constraint DSL. If Hopper's target audience is protocol-grade DeFi rather than NFT mints, they can be deferred indefinitely.

### Bottom line

Hopper is already a more sophisticated DSL than Quasar by every verified
measure — more proc macros, more guard macros, more per-field keywords
(counting Token-2022 extensions), more lifecycle attributes (migrate,
invariant, receipt, pipeline, crank), more metadata emission
(`FIELD_ROLES`, `FIELD_INVARIANTS`, fingerprints). The only axes where
Quasar is ahead are Metaplex integration sugar (`metadata::*`,
`master_edition::*`) and `init_if_needed`. Against Anchor 0.31 Hopper is at
roughly 90% parity with the remaining gap being access_control, InitSpace,
executable/rent_exempt, interface, view, and three Token-2022 extension
families — all of them small-to-medium additions that can be closed
incrementally without disturbing the declarative path.

Gap items 1–3 and 8 are Anchor-parity fixes that don't depend on any
Quasar verification; they are safe to ship whenever you greenlight.
Gap items 4 and 9–10 require a product decision about whether Metaplex
integration is in scope. Gap items 5, 6, 7 are medium and can be prioritised
against other roadmap items.

---

## DSL Parity Implementation Pass — 2026-04-24

The top of the DSL gap list has been closed. This section records what was
implemented and what remains.

### Shipped

**Guard-macro ergonomics.** `src/prelude.rs` now re-exports `err!` and
`error!` (short-form error macros Anchor users reach for first), plus the
`require_lt!` and `require_lte!` macros that had been defined in
hopper-runtime but weren't in the prelude. The root `hopper::` crate path
already exposed the full `require_*` family; the prelude just needed to
pick them up. Closes gap item #8.

**`hopper_load!` destructuring macro.** New `#[macro_export]` macro in
`src/lib.rs` that replaces the repetitive
`let [user, vault, ..] = accounts else { return Err(...); };` pattern with
`hopper_load!(accounts => [user, vault])`. Supports an optional trailing
`..` for stylistic parity with native Rust slice patterns. Closes the
"raw-dispatch account parsing is repetitive" ergonomic gap flagged by the
flow review.

**`#[derive(HopperInitSpace)]` derive.** New proc macro at
`crates/hopper-macros-proc/src/init_space.rs`, registered in the proc
macro crate's `lib.rs` and re-exported through the root `hopper::` crate.
Emits `pub const INIT_SPACE: usize = size_of::<Self>()` on any struct.
Intended for hand-authored `#[repr(C)]` Pod structs that want to use the
`space = 16 + Foo::INIT_SPACE` Anchor idiom without adopting
`#[hopper::state]`. Closes gap item #1.

**`#[hopper::access_control(expr)]` handler attribute.** Extends
`HandlerModifiers` in `crates/hopper-macros-proc/src/program.rs` to accept
one or more `#[access_control(expr)]` attributes on any
`#[hopper::program]` handler. Each expression is evaluated in handler
scope (so it can reference `ctx`, handler args, and typed accessors) and
must evaluate to `bool`. False bails with
`ProgramError::MissingRequiredSignature` (Anchor's default). Multiple
attributes are ANDed in declaration order. Closes gap item #2.

**`executable` and `rent_exempt` field keywords.** Extended
`AccountAttr` in `crates/hopper-macros-proc/src/context.rs` to accept
`executable` (emits `check_executable()?`) and
`rent_exempt = enforce | skip` (emits a lamport-threshold check using
the new `hopper_runtime::rent::check_rent_exempt` helper). New
`crates/hopper-runtime/src/rent.rs` module provides constant-based
rent-exemption calculation (2-year threshold, 3480 lamports per
byte-year, 128-byte overhead — the cluster constants that have been
stable since mainnet launch). Three unit tests pin the formula against
the well-known 0-byte minimum (890,880 lamports) and a typical 56-byte
vault (1,280,640 lamports). Closes gap item #3.

**`init_if_needed` field keyword.** Extended `AccountAttr` parsing to
accept `init_if_needed`. The lifecycle-helper emission at
`crates/hopper-macros-proc/src/context.rs` now has two shapes: `init`
unconditionally calls `hopper_init!` (errors if already allocated) and
`init_if_needed` checks `account.data_len() > 0` first and returns `Ok(())`
when the account is already populated, only invoking the CreateAccount
CPI for empty accounts. `validate_account_attr` rejects the
contradictory combination of both flags and shares the `payer`/`space`
requirements between them. Closes gap item #4 and the only remaining
Quasar-parity gap on the non-Metaplex axis.

### Deferred

**Metaplex `metadata::*` and `master_edition::*` keywords (gap items #9,
#10).** These require an `mpl-token-metadata` CPI builder infrastructure
that doesn't currently exist in Hopper. Shipping the keyword parser
without the matching builders would accept syntax that has no working
lowering, which is worse than shipping nothing. A dedicated follow-up
pass should introduce `crates/hopper-metaplex` (or equivalent) with the
Metaplex program ID constant, `CreateMetadataAccountV3` / `CreateMasterEditionV3`
builders, and then the keywords can be wired to those builders. The
Boobies project's NFT focus means this is worth doing right rather than
stubbing.

**`#[interface]` attribute (gap item #5).** Medium-sized, ~300 LOC new
module. Anchor's shared-CPI-interface pattern. Hopper's
`declare_program!` covers the common case (consume an IDL) but not the
source-level interface declaration. Deferred because Hopper's current
interop story (`hopper_interface!` for cross-program reads, fingerprint
pinning) arguably covers the use cases better without a new surface;
revisit if adoption surfaces a real need.

**Token-2022 `group_pointer`, `group_member_pointer`, `confidential_transfer`
keywords (gap item #6).** Same pattern as the existing
`transfer_hook` / `metadata_pointer` / etc. keywords — a TLV reader in
`hopper-solana/src/token2022_ext.rs` plus a `context.rs` parser case
plus a field-check lowering. Each extension is ~100 LOC. Deferred to a
dedicated Token-2022 expansion pass so the three extensions can be
shipped together with tests and an example.

**`#[view]` / query attribute (gap item #7).** Marks a handler as
read-only for off-chain simulation. Medium-sized. Needs manifest-side
representation (the query flag has to propagate into ProgramIdl and
Codama/Anchor IDL emission) before the attribute is useful. Deferred.

### Net verdict after this pass

The Anchor-parity ergonomic gap is now ~6% (three Token-2022 extensions
and `#[view]` / `#[interface]`). Quasar-parity is functionally complete
except for Metaplex NFT sugar, which is deferred as a scoped follow-up
rather than stubbed. Every item shipped in this pass preserved the dual
authoring path: `err!`, `error!`, `hopper_load!`, `require_lt/lte` are all
declarative macros, and the proc-macro additions (`#[derive(HopperInitSpace)]`,
`#[hopper::access_control]`, `executable`/`rent_exempt`/`init_if_needed`
field keywords) all have hand-written equivalents in the raw-dispatch
path documented in AUDIT.md section "DSL Parity Audit".

---

## Metaplex Implementation Pass — 2026-04-24

Closes the Quasar-parity Metaplex gap that the previous pass had explicitly
deferred. The Boobies NFT project (Galápagos blue-footed boobies → conservation
donations via [bluefoot.xyz](https://bluefoot.xyz)) is the load-bearing
use case that motivated doing this right rather than stubbing.

### New crate: `hopper-metaplex`

Lives at [`crates/hopper-metaplex`](crates/hopper-metaplex). Optional, opt
in with `cargo build --features metaplex`. Six source files:

- **`constants.rs`** — `MPL_TOKEN_METADATA_PROGRAM_ID` decoded at compile
  time via `five8_const::decode_32_const`, plus the canonical
  `b"metadata"` / `b"edition"` seed prefixes and the spec-mandated
  32 / 10 / 200 byte caps for `name` / `symbol` / `uri`.
- **`encoding.rs`** — `BorshTape`, a stack-buffer Borsh writer that
  refuses to overflow. Four unit tests pin Borsh-string framing,
  buffer-overflow rejection, and `Option<u64>` encoding.
- **`seeds.rs`** — `metadata_pda(mint)` and `master_edition_pda(mint)`
  derivation helpers, plus `_with_bump` variants that skip the
  bump-iteration loop when the caller has the bump cached. Off-chain
  stubs gated on `cfg(not(target_os = "solana"))` so host tests
  compile.
- **`instructions.rs`** — three CPI builders:
  - `CreateMetadataAccountV3` (Metaplex enum-position discriminator 33)
  - `CreateMasterEditionV3` (discriminator 17)
  - `UpdateMetadataAccountV2` (discriminator 15)
  Each ships an `invoke()` and `invoke_signed()` and pairs with a `DataV2`
  payload struct. The `simple` constructor on `DataV2` covers the
  common 1-of-1 NFT case (no creators, no collection, no uses).
  Optional rent account is supported on the V3 instructions for
  backwards compatibility but defaults to `None` since modern
  Metaplex doesn't need it.
- **`lib.rs`** — module surface and curated re-exports through
  `hopper_metaplex::{...}`.
- **`Cargo.toml`** — depends only on `hopper-runtime` and `five8_const`;
  no Borsh dependency, no proc-macro pull-in. The optional `metaplex`
  feature on the root `hopper` crate gates the dependency.

### Encoding policy

Metaplex's instruction format is Borsh-encoded, which is variable-length
by design — `String` and `Option<T>` carry their own framing bytes — so
the instruction data cannot be zero-copy in the Hopper sense. Each
builder allocates a small **stack** buffer (16 bytes for
`CreateMasterEditionV3`, 320 bytes for `CreateMetadataAccountV3`, 384 for
`UpdateMetadataAccountV2`) sized to comfortably exceed the worst-case
payload, writes the Borsh tape directly into that buffer via
`BorshTape`, and passes `&buf[..len]` to `cpi::invoke_signed`. No heap,
no `Vec`, no `alloc::String`. `BorshTape` returns
`ProgramError::InvalidInstructionData` if the caller would overrun the
buffer, so a malicious oversized name can't push the program into UB.

### Reference program: `examples/hopper-nft-mint`

Three-instruction Hopper-authored program demonstrating the end-to-end
NFT mint flow:

1. `init_mint` — placeholder for caller-side SPL mint creation.
2. `create_metadata` — CPIs into Metaplex `CreateMetadataAccountV3`
   with name / symbol / uri / SFBP / `is_mutable`.
3. `create_master_edition` — CPIs into `CreateMasterEditionV3` with
   `max_supply = Some(0)` to lock the mint as a 1-of-1 NFT.

Uses the new `hopper_load!` destructuring sugar (R36) and the
`hopper_metaplex::*` builder surface. Single-byte length-prefixed
strings on the wire (sized to a `u8` because Metaplex's caps fit) keep
the client encoding tight; the Borsh `u32` length prefix is added
inside the builder.

### Wiring

- `Cargo.toml` (workspace): `crates/hopper-metaplex` added to members,
  workspace dep declared, optional `metaplex` feature on the root
  `hopper` crate, `hopper-metaplex?/hopper-native-backend` plumbed into
  the backend feature forwarding so a `--features hopper-native-backend
  --features metaplex` build pulls the right backend transitively.
- `src/lib.rs` (root hopper crate): `pub use hopper_metaplex` gated on
  `feature = "metaplex"`.
- `src/prelude.rs`: re-exports `CreateMetadataAccountV3`,
  `CreateMasterEditionV3`, `UpdateMetadataAccountV2`, `DataV2`,
  `metadata_pda`, `master_edition_pda`, `metadata_pda_with_bump`,
  `master_edition_pda_with_bump`, and `MPL_TOKEN_METADATA_PROGRAM_ID`
  through the prelude when `feature = "metaplex"` is enabled. Programs
  doing `use hopper::prelude::*` get the full surface in one line.
- `examples/hopper-nft-mint`: added to workspace members.

### Field-keyword sugar (`metadata::*` / `master_edition::*`) — deferred

The `#[hopper::context]` field keywords that auto-generate
metadata-init lifecycle helpers from `#[account(metadata::name = ...,
metadata::uri = ..., master_edition::max_supply = ...)]` were not
implemented this pass. The builders are usable directly today (see
`hopper-nft-mint`), so users who want the keywords can wait for a
follow-up pass without losing functionality. The follow-up needs to:

1. Add `metadata_*`, `master_edition_*` fields to `AccountAttr` in
   `crates/hopper-macros-proc/src/context.rs`.
2. Add the per-keyword parser cases (`metadata::name`,
   `metadata::symbol`, `metadata::uri`, `metadata::seller_fee_basis_points`,
   `metadata::is_mutable`, `master_edition::max_supply`).
3. Emit lifecycle helpers (`init_metadata_<field>`,
   `init_master_edition_<field>`) that call into the builders.
4. Emit `validate_account_attr` checks that the metadata keywords
   appear together (you can't set `name` without `symbol` and `uri`).

The work is mechanical given the existing `init` lifecycle scaffolding
and the new builders — the previous deferral was about needing the
builders, not about the keyword surface. Reasonable scope: ~150 LOC.

### Net verdict after Metaplex pass

The full DSL gap list at the time of the parity audit had ten items.
After the previous implementation pass, four remained: `#[interface]`,
Token-2022 group/confidential extensions, `#[view]`, and the Metaplex
keyword sugar. After this pass, three remain:

1. `#[interface]` — deferred; `hopper_interface!` covers the common case.
2. Token-2022 `group_pointer` / `group_member_pointer` /
   `confidential_transfer` keywords — deferred to a Token-2022
   expansion pass.
3. `#[view]` / query attribute — deferred until manifest representation
   work happens.

Quasar parity is now functionally complete: every Quasar Metaplex
keyword has a matching builder in `hopper-metaplex`, even though the
field-keyword *syntax* is still pending. Anchor parity is ~94% with
the same three remaining items as before. The reference NFT-mint
program is the load-bearing demonstration that the Metaplex builders
work end to end.


---

## 1. Executive Summary

Hopper is a real, engineered zero-copy framework — not a thin wrapper, not a marketing shell. The core overlay mechanism is sound, the segment-level borrow registry is genuinely implemented (not aspirational), the no_std / no_alloc claim holds for the release library, and the benchmarks are reproducible. What Hopper ships in one tree — raw loader substrate, typed runtime, declarative + proc macro authoring, zero-copy collections, schema spine, manager inspector, migration planner, client-gen, and interactive TUI — is not shipped as a single coherent stack by any of the three reference frameworks.

Where Hopper falls short is not in the core invariants but in boundary clarity and some marketing framing. The README in a few places attributes runtime-layer features ("segment-level borrow enforcement") to hopper-core when they actually live in hopper-runtime, and the bench table labels a row "Pinocchio-style" that many readers will misread as "the Pinocchio framework." Both are fixable with README edits, not code changes.

**One-line scorecard:** Hopper is technically stronger than Quasar on borrow safety and tooling breadth, technically comparable to Pinocchio on raw substrate CU, and technically ahead of Anchor zero-copy on every axis except ecosystem reach. It has the fewest rough edges you would expect from a 17-crate v0.1 project but the most ambition per line of code.

---

## 2. Framework Positioning

| Axis | Hopper | Pinocchio (Anza) | Quasar (Blueshift) | Anchor zero-copy |
|---|---|---|---|---|
| **Project posture** | Full-stack framework (loader → runtime → macros → tooling) | Minimal substrate; zero-copy primitives, no framework | Framework with Anchor-like syntax on a pinocchio-style substrate | Layered on Anchor's macro + runtime |
| **Primary authoring style** | `hopper_layout!` (decl) or `#[hopper::state]` (proc) | Hand-rolled structs + pinocchio types | `#[program]` / `#[account]` / `#[derive(Accounts)]` proc macros | `#[account(zero_copy)]` + `AccountLoader` |
| **Raw entrypoint ownership** | Yes (`hopper_program_entrypoint!`, fast, lazy) | Yes | Yes | No (Anchor owns it) |
| **no_std / no_alloc (release)** | Yes | Yes | Yes | No |
| **Segment-level borrow enforcement** | Yes (`SegmentBorrowRegistry`, u64 fingerprint + full-address fallback, fixed 16 slots) | No | No | No (`load_mut` panics on second call; whole-account granularity) |
| **Compile-time field→offset map** | Yes (`SegmentMap` const trait + per-field const offsets) | Manual (user writes offsets) | Implicit via proc macro | Implicit via struct layout |
| **Deterministic layout fingerprint** | Yes (SHA-256 const-evaluated, 8-byte prefix) | No | No | No |
| **Versioned + foreign typed loads** | Yes (Tier A/B/C; `load`, `load_foreign`, `load_compatible`, `load_unverified`, `load_unchecked`) | No (caller's job) | Partial | Single path |
| **State receipts (mutation proofs)** | Yes (16 B header + 64 B wire format, before/after fingerprints, segment list, policy flags) | No | No | No |
| **Policy system** | Yes (capabilities + requirements, const-composable) | No | No | No |
| **Schema export** | Three-layer (ProgramManifest → ProgramIdl → CodamaProjection) | No | IDL | IDL |
| **Optional proc macros (not required)** | Yes (true equivalence between declarative and proc paths) | N/A | No (proc required) | No (proc required) |
| **Backend portability** | Native default + quarantined legacy Pinocchio and `solana-program` compatibility modes, compile-time exclusive | 1 | 1 (pinocchio-style) | 1 (solana-program) |
| **Memory access tiers** | 5 explicit tiers from generated field accessors through full raw pointer escape | 1 (raw) | 1 (raw) | 1 (`AccountLoader`) |
| **CLI surface** | Lifecycle + schema + manager + profiling + interactive TUI + client-gen (TS/Kotlin/Python/Rust) | Minimal | Strong | Strong (`anchor` CLI) |
| **Ecosystem reach** | Nascent | Core-team-blessed, growing | Active | Dominant |

The four-framework comparison collapses into a three-way choice in practice. Anchor zero-copy is the ubiquitous default but is strictly less capable per unit of Hopper; Pinocchio is the lean substrate everyone builds on top of; Quasar is Hopper's closest philosophical peer. The interesting comparison is **Hopper vs Quasar** — both aim at "Anchor ergonomics, pinocchio performance, zero-copy by default" — and in that fight Hopper wins on borrow safety, state lifecycle tooling, and schema discipline; Quasar wins on ecosystem adoption and macro polish.

---

## 3. Safety & Soundness

### 3.1 Zero-copy overlay (`hopper-core/src/account/pod.rs`)

The core cast is textbook-correct:

- `pod_from_bytes::<T>` checks `data.len() < T::SIZE` before `&*(data.as_ptr() as *const T)`. Lines 32–40.
- `Pod` is defined in `hopper-runtime/src/pod.rs` and re-exported; safety contract is documented ("every bit pattern valid, alignment-1, no padding, no internal pointers"). Lines 9–13 of pod.rs.
- Alignment-1 is enforced by construction: Hopper's wire types (`WireU64`, `WireBool`, `TypedAddress`) are all alignment-1, and `hopper_layout!` / `#[hopper::state]` emit compile-time assertions that the resulting struct has `align_of == 1`.

The single nuance is that there is no runtime alignment check. This is intentional — the wire types make it impossible to construct an aligned Pod in the first place — but a user who hand-writes `unsafe impl Pod for MyStruct` for a non-alignment-1 type would punch a hole through the invariant. This is the same hole Anchor's `#[account(zero_copy(unsafe))]` and Quasar's manual Pod impl have. Hopper is no worse, and the `unsafe impl` marker makes the boundary visible.

`pod_read` and `pod_write` use `read_unaligned` / `write_unaligned`, so the copying path is alignment-safe even if someone does violate the overlay contract. Good defensive layering.

### 3.2 Segment-level borrow enforcement (`hopper-runtime/src/segment_borrow.rs`)

This is Hopper's headline feature, and it's real. The registry is a fixed `[SegmentBorrow; 16]` with a `u8` length — ~280 bytes on the stack, no heap, no Option wrappers. Each borrow records:

```
key_fp: u64           // fast-path 8-byte prefix
key: Address          // full 32-byte address (slow-path verify)
offset: u32, size: u32
kind: Read | Write
```

Conflict rules are the standard Rust aliasing rules applied to byte ranges:

```
Existing     New       Overlapping?  Allowed
Read         Read      yes           yes
Read         Write     yes           no
Write        Read      yes           no
Write        Write     yes           no
any          any       no            yes
```

The pre-audit version relied on u64 fingerprint alone (documented in the module as "probabilistic, not a guarantee"). The current version is **fingerprint-then-verify**: u64 comparison is the hot path that cheaply rejects unrelated accounts, and only on fingerprint-match does the code do the 32-byte compare. This is the correct design — no false conflicts are possible, only rare wasted 32-byte compares. Lines 47–67.

This is substantively different from Anchor's `AccountLoader`, which panics on the second `load_mut()` in the same scope (whole-account granularity). It is also something Quasar and Pinocchio do not provide at all.

### 3.3 Account parsing and duplicate-account resolution (`hopper-native/src/raw_input.rs`)

`deserialize_accounts::<MAX>()` walks the BPF input buffer with raw pointer arithmetic and strict duplicate-marker handling. A duplicate marker byte must reference a **strictly earlier** slot (`if duplicate_of >= slot { malformed_duplicate_marker() }`); on-chain this calls `sol_panic_()`, off-chain it panics. This closes a pre-audit bug (documented in the codebase as "Must-Fix #1") where the parser silently fell back to account zero on a malformed marker. That was a real footgun and it's fixed correctly — no return on malformed input, so caller code cannot observe an invalid `AccountView`.

Duplicate slots reuse the canonical `RuntimeAccount` pointer, so both slots share the same `borrow_state` byte, which means Hopper's borrow tracking correctly serializes access across duplicates.

### 3.4 PDA verify-only path (`hopper-native/src/pda.rs`)

The claim is "verify-only PDA path (sha256 only, no `curve_validate` syscall) saves ~350 CU per PDA-bearing instruction." The claim is accurate but the framing is worth unpacking.

`find_program_address` (full form) must call `curve_validate` because it is searching for a bump that produces an off-curve point — that is the definition of a PDA. `verify_program_address` (the verify-only form) starts from a known PDA and a known bump and only needs to confirm that `sha256(seeds || program_id || PDA_MARKER) == expected_address`. Since the expected address is already on-chain and PDAs are off-curve by construction, the `curve_validate` check adds nothing to soundness in the verify-only case.

The only theoretical attack surface is "an attacker submits an on-curve address that happens to hash equal to a legitimate PDA." The probability is cryptographic-negligible, and even if it occurred the attacker has not derived anything useful — they've found a pre-image collision on SHA-256 for a specific target, which is not a thing.

So: the optimization is safe, the CU savings are real, and the Quasar / Anchor equivalents use `create_program_address` or full `find_program_address` and eat the overhead. This is one of Hopper's cleanest Pinocchio-level wins.

### 3.5 Unsafe inventory

Every `unsafe` block in `hopper-native` and `hopper-runtime` has a doc-comment `# Safety` section with documented preconditions. The one gap the sub-agent flagged was `cpi::invoke_unchecked`, which leans on implicit "caller must validate instruction and accounts" contract via reading the checked path. Minor nit; recommend adding an explicit `# Safety` block.

`hopper-core/tests/unsafe_boundary_tests.rs` exercises undersized / oversized / exact-size bytes for all Pod entry points. `overlay_equivalence_tests.rs` cross-checks that `pod_from_bytes` agrees with `pod_read` for the same data, which catches any silent misalignment.

What is **not** tested that should be: a misalignment test for `pod_from_bytes` itself — specifically a Pod type whose `align_of > 1`. It would never succeed on a user's actual wire type (Hopper makes those impossible to construct) but adding a compile-fail trybuild test would document the invariant.

### 3.6 Verdict on safety

Hopper's safety story is the best of the four frameworks at the runtime level:

- Anchor: single-granularity whole-account borrow, panics on conflict.
- Pinocchio: no borrow enforcement, caller's responsibility.
- Quasar: no borrow enforcement beyond `&`/`&mut` rules.
- Hopper: byte-range-level borrow enforcement, fingerprint-verified, bounded stack cost.

The cost of this is ~70 CU on the segment-counter workload (visible in the bench results). That's a correct trade — you are paying for a guarantee none of the others provide.

---

## 4. Performance Posture

### 4.1 Bench setup (sibling `hopper-bench` repo)

The parity harness uses Mollusk and loads real SBF binaries for each included
framework. The old "Pinocchio-style" column came from Quasar's reference
Pinocchio vault and is no longer used for release-facing claims. The sibling
benchmark repo now owns competitor locks, raw logs, exact commands, and the
Anza Pinocchio target.

The methodology pins rustc, SBF toolchain, cargo profile, and competitor
commits in the benchmark repo. Same-toolchain discipline is real. Semantic
equivalence (all frameworks implement the same 4 instructions on the same
40-byte payload) is enforced by documented contract.

### 4.2 The numbers

The current release-facing table reports Hopper and Quasar only (8-seed
average, parity vault):

| Scenario | Hopper | Quasar |
|---|---|---|
| Authorize | 432 CU | 585 CU |
| Counter (segment-safe) | 539 CU | 607 CU |
| Deposit | 1651 CU | 1768 CU |
| Withdraw | 455 CU | 605 CU |
| Binary size | 7.62 KiB | 8.36 KiB |

The Hopper-vs-Quasar deltas (100–150 CU most scenarios) are credible. Hopper's PDA verify path is cheaper than Quasar's full find_program_address, which accounts for the authorize/withdraw gap. Quasar's counter is ~70 CU cheaper than Hopper's precisely because Hopper is tracking the segment borrow and Quasar is not — the trade-off is explicit.

Pinocchio comparisons require separate provenance. Pinocchio is a substrate,
not a framework, and a hand-authored target can omit or include whatever safety
checks the author chooses. Hopper will only publish Pinocchio numbers when the
benchmark repo records the exact Anza target, dependency versions, SBF
toolchain, Mollusk version, seed count, and command line.

### 4.3 Entrypoint variants (`hopper-native/src/entrypoint.rs`)

Three entrypoints:

- **Eager** — standard, stack-allocated `[MaybeUninit<AccountView>; 254]`, scans whole input up front.
- **Fast** — uses the two-argument SVM entrypoint register; reads instruction data directly; saves ~30–40 CU per call. Requires SVM ≥ 1.17.
- **Lazy** — returns a `LazyContext` and defers account parsing until the dispatcher knows which accounts it needs. Useful when most instruction variants use a subset of the supplied accounts.

Pinocchio ships an eager entrypoint only. Quasar ships an eager entrypoint. Anchor's entrypoint is generated by the macro and does not offer lazy parsing. This is a real Hopper-only capability that the bench does not exercise (the parity vault uses `fast_entrypoint!`). A lazy-dispatch bench would likely show a larger Hopper lead on deep-switch programs. Recommendation R3.

### 4.4 Backend portability

Three backends, selected by feature flag, compile-time exclusive (`compile_error!` on zero or multiple enabled):

- `hopper-native-backend` — the primary path, zero-copy `AccountView`.
- `legacy-pinocchio-compat` — copies metadata at parse time (Pinocchio's model).
- `solana-program-backend` — full `AccountInfo` with alloc.

The abstraction is implicit via feature-gated `pub use`, not a trait object, so monomorphization keeps dispatch zero-cost. Switching backends has measurable CU cost (~50–100 CU on pinocchio, ~100–150 CU on solana-program, both vs native), which is the expected shape — you trade CU for interop.

### 4.5 Verdict on performance

Hopper is a legitimate CU-tier-1 framework. On the parity vault it is faster than Quasar (same tier) and within the expected 2000 CU "cost of framework" band vs hand-rolled Pinocchio-style code. The one CU regression (segment-counter, +70 vs Quasar) is the borrow-safety feature, not waste.

---

## 5. API Ergonomics & Developer Experience

### 5.1 Macro count and the "optional proc macro" claim

18 declarative macros (counted in `crates/hopper-macros/src/lib.rs`) + 11 proc macros (`crates/hopper-macros-proc/src/`). Both paths lower to the same compile-time offsets and pointer arithmetic; the proc path adds generated accessors (`ctx.vault_balance_mut()`) that the declarative path leaves to the user. Sub-agent verified by comparing generated const tables and overlay helpers. The claim "proc macros are optional DX sugar, never required for correctness" is accurate — you can write a complete Hopper program with no proc macros.

This is the axis where Hopper most clearly beats Quasar, which is proc-macro-only. It is also the axis where Hopper is verbose vs Anchor — the declarative `hopper_layout!` requires the user to declare field sizes explicitly (`balance: WireU64 = 8`), and to call `core::mem::size_of::<T>()` assertions at compile time. Anchor hides that entirely. Recommendation R4.

### 5.2 `hopper_layout!`

Const-evaluates offsets, size, layout_id (SHA-256 prefix over canonical "name:type=size" string, 8 bytes). Size assertion fires at compile time if declared sizes don't match `size_of::<T>()`. Fingerprint is deterministic across compilation runs, which is what makes cross-program reads and on-chain manifest publishing safe.

### 5.3 `hopper_interface!` + cross-program reads

`hopper_interface!` lets program B read a typed view of program A's account without importing program A's crate. The ABI contract is the 8-byte layout fingerprint — if A changes the struct, the fingerprint changes, B fails to load. This is a genuinely novel pattern on Solana; Anchor solves the same problem with a shared IDL at the TypeScript client layer, not on-chain.

### 5.4 `#[hopper::context]` vs Anchor's `#[derive(Accounts)]`

Roughly feature-parity: signer / mut / owner / seeds / bump / has_one / address / init / zero / close / realloc / constraint, plus Token-2022 extension checks. Hopper's context proc macro also emits per-field accessors for segment-level operations, which Anchor does not (because Anchor has no segments).

One structural difference: when `#[instruction(arg: Type, ...)]` is declared, Hopper emits **only** `bind_with_args(ctx, arg, ...)` (no argless `bind()`). This prevents a class of footguns where a constraint expression depends on an instruction argument and the user forgets to pass it. Good call.

### 5.5 Error handling

`hopper_require!(cond, err)` is a single inline branch — zero-cost, identical lowering to Anchor's `require!`. Hopper keeps one generic form rather than Anchor's `require_eq!` / `require_gt!` family. Less discoverable, less boilerplate. Judgment call.

`#[hopper::error]` emits `CODE_TABLE` and `INVARIANT_TABLE` const maps that off-chain tooling can use to render "Invariant X failed" messages. Anchor does not do this.

### 5.6 Verdict on DX

Hopper is more verbose than Anchor on first contact (explicit field sizes, explicit headers, explicit policy composition) and more expressive than Anchor after you've internalized the model (segment borrows, layout fingerprints, cross-program reads, receipts, migration plans). The proc-macro escape hatch genuinely softens the first-contact cost. Quasar is probably still the nicer "drop-in Anchor replacement" because it copies more of Anchor's syntax verbatim. Hopper is the nicer "I'm building a protocol and I want the state layer to be a first-class thing."

---

## 6. Feature Parity & Innovation

### 6.1 Where Hopper is genuinely alone

- **Segment-level borrow enforcement.** `SegmentBorrowRegistry` is Hopper's, not a port of anything. Design is u64-fingerprint + full-address verify, bounded 16-slot stack registry.
- **Deterministic layout fingerprints as an on-chain artifact.** The 8-byte SHA-256 prefix in the account header is the thing `load_foreign` and `hopper_interface!` pin against. Anchor has nothing at this layer; Quasar does not.
- **State receipts as a wire format.** 16 B header + 64 B body capturing before/after fingerprints, segment list, policy flags, CPI count, journal appends. Off-chain indexers can verify a mutation happened without replaying the instruction. Anchor's `emit!` events are loosely typed byte blobs by comparison.
- **Policy + capability system.** Declare `CapabilitySet::new().with(MutatesState).with(MutatesTreasury)` on an instruction and the runtime auto-triggers validation requirements. Anchor requires you to write the checks.
- **Three-layer schema** (ProgramManifest → ProgramIdl → CodamaProjection). Codama projection is a real Kinobi-compatible bridge, not a gesture.
- **18 declarative macros that do 80% of what the proc macros do.** Nobody else in the Solana ecosystem offers an optional-proc-macro path of this completeness.
- **Migration planner.** `hopper plan v1.json v2.json` emits append-only / copy-prefix / zero-init / realloc steps between layout versions. Quasar's nearest equivalent is schema diff at the IDL layer.
- **Manager TUI.** Interactive terminal UI that lets you decode raw hex against a manifest. None of the other three ship this.

### 6.2 Where Hopper is comparable

- Pod / AccountLoader parity on raw access.
- Entrypoint parsing parity with Pinocchio (Hopper adds fast + lazy variants on top).
- Token-2022 support is at stub level (define the state, minimal ATA flow). Quasar's Token-2022 surface is more battle-tested.

### 6.3 Where Hopper trails

- **Ecosystem reach.** Anchor has thousands of production programs, Pinocchio is Anza-team-blessed, Quasar is actively used at Blueshift. Hopper is new. Nothing in the code fixes this.
- **Kotlin client gen quality.** The TypeScript client is idiomatic and ready. The Kotlin (`org.sol4k`) generator emits less-fleshed types and leans harder on downstream ecosystem code. Not a bug, just asymmetry.
- **Anchor interop.** `hopper-anchor` exists to read Anchor-created accounts. It is a necessary but not sufficient bridge — you can consume, but Hopper programs don't emit Anchor-compatible IDLs. That is by design (CodamaProjection is the preferred interop path) but it's an adoption cliff to be aware of.

### 6.4 Innovation score

On genuine-novelty-in-code, Hopper is running ahead of the other three frameworks combined. The question is whether the market wants what Hopper is selling — a protocol-grade state framework rather than a program framework. For the specific niche of large, long-lived protocols that care about layout evolution, auditable mutations, and cross-program reads, the answer is almost certainly yes. For a simple vault or a one-off program, Anchor is still the shortest path.

---

## 7. README Claim Verification

| Claim | Status |
|---|---|
| "no_std, no_alloc" | **True.** Grep of release code in `hopper-core` finds no `Vec`, `Box`, `HashMap`, `String`. `extern crate alloc` is test-only. |
| "18 declarative macros" | **True.** Counted in `crates/hopper-macros/src/lib.rs`. |
| "Both paths compile to identical code: ptr + const_offset → cast → &mut T" | **True.** Verified by comparing decl path expansion to proc path expansion. |
| "16-entry compact registry, ~280 bytes stack, no heap" | **True.** `MAX_SEGMENT_BORROWS = 16`, `SegmentBorrow` is plain `repr(Rust)` with 8 + 32 + 4 + 4 + 1 ≈ 49 B Ã- 16 ≈ ~280 B including length byte and padding. |
| "Read `authority` while writing `balance` on the same account" | **True.** The overlap check in `segment_borrow.rs:93-99` permits non-overlapping byte ranges. Cross-verified against conflict-rule table. |
| "Verify-only PDA path saves ~350 CU" | **True in practice, conservatively stated.** `find_bump_for_address` skips `sol_curve_validate_point` and saves 90+ CU per bump attempt; ~350 CU is a realistic single-derivation average. |
| "Sovereign low-level runtime" | **True for the scope it claims.** `hopper-native` is a real substrate; `hopper-runtime` is a real typed layer. Not a Pinocchio wrapper. |
| "Hopper beats Quasar on 4 of 5 instructions on the parity-vault bench" | **True.** CSV confirms. The 1 of 5 it loses (counter) is the segment-safe variant, explicitly. |
| "Smallest binary of all three frameworks" | **True** at 7.62 KiB vs 8.36 KiB (Quasar) vs 10.13 KiB (Quasar's pinocchio reference). |
| "Segment-level borrow enforcement" attributed to hopper-core | **Overstated.** The registry lives in hopper-runtime, not hopper-core. hopper-core has account-level `mutable_borrows: u64` bitfield only. Recommendation R1. |
| "CLI commands all implemented" | **True.** All ~33 routed, all backed by real functions. No `todo!()` or stub `println!`. |
| "Codama-compatible projection" | **True.** `CodamaProjection` maps to Kinobi's expected shape; JSON export valid. |

Overall signal: every specific technical claim I spot-checked held up. The only overreach is the boundary framing where the README occasionally implies that hopper-core contains features that live in hopper-runtime.

---

## 8. Risk Register

| Risk | Severity | Likelihood | Mitigation |
|---|---|---|---|
| User hand-writes `unsafe impl Pod` on an aligned type | Medium (UB) | Low (macros make it hard) | Add `trybuild` compile-fail test for misaligned Pod. |
| Hand-rolled `hopper_layout!` size mismatch | Low (compile error) | Medium | Already mitigated by the `const _: () = assert!(size_of::<T>() == ...)` emission. |
| Layout fingerprint collision (8 bytes of SHA-256) | Negligible at design | Cryptographically negligible | 8 bytes is enough for layout pinning, not for security. Fine for intended use. |
| Segment borrow registry fills up (> 16 slots) | Low (runtime error) | Low (typical instructions use 2–6) | Documented; registry returns `ProgramError`. Consider a compile-time hint attribute to raise the cap for specific programs. |
| "Pinocchio-style" bench label misread as Anza Pinocchio | High reputational | High | Rename the column "Reference (Quasar pinocchio-style vault)" in the README. |
| hopper-core README claim about segment borrows | Low reputational | Medium | Clarify boundary: hopper-core owns overlays; hopper-runtime owns borrow registry. |
| Token-2022 surface is stub-level | Medium (adoption) | Medium | Add a Token-2022 transfer-hook example. |
| Kotlin client gen less polished than TypeScript | Low (scope) | Medium | Document as preview; link TS as the primary SDK target. |
| Ecosystem adoption | High (business) | High | Out of scope for code audit; addressed via docs, talks, ecosystem partnerships. |

---

## 9. Recommendations

These are **recommendations only**, as requested — no patches applied in this pass. Priority order:

- **R1. Fix README attribution of segment-level borrow enforcement.** Move the sentence "Segment-level memory access" under a subsection that clearly identifies `hopper-runtime::segment_borrow` as the owner. Keep the performance and semantics claims; just fix the crate attribution. This removes the only concrete overreach in the README.

- **R2. Rename the "Pinocchio-style" bench column.** Two options: (a) "Reference vault (raw, Quasar's pinocchio-style sample)" — honest, verbose, (b) "Raw reference" with a footnote. I'd pick (a). Add a short sentence noting that this is **not** a benchmark of the Anza Pinocchio framework, because the framework is a substrate and no equivalent program could be built in 2000 CU using any substrate.

- **R3. Add a lazy-dispatch bench.** The parity vault uses `fast_entrypoint!` and exercises eager parsing. Hopper's lazy entrypoint is a real differentiator and the current bench cannot show it off. Add an 8-instruction dispatch vault where most variants touch 2 of the 8 accounts. The CU win should be larger here.

- **R4. Ship a Quick-Start / Day-One guide that leads with proc macros.** The README is excellent reference but lands the reader in the declarative path first. Most newcomers coming from Anchor will want `#[hopper::state]` / `#[hopper::program]` immediately. Put those front-and-center and make the declarative path the "graduate to" option.

- **R5. Add a `trybuild` compile-fail test for misaligned Pod.** Closes the one safety gap where a hand-rolled `unsafe impl Pod` could punch through the alignment-1 invariant. Cost: ~30 lines.

- **R6. Expand Token-2022 examples.** Current `hopper-token-2022-vault` is stub-level. Add transfer-hook + extension-aware transfer + confidential-transfer-friendly patterns. Token-2022 is where Solana development is going and Hopper's ATA/extension story needs to be battle-tested publicly.

- **R7. Document the `cpi::invoke_unchecked` `# Safety` contract.** The one place the unsafe inventory is missing an explicit doc block. Minor but closes the "documented invariants on every unsafe" completeness check.

- **R8. Consider an `anchor-compat` IDL export.** Not because Anchor IDL is architecturally superior, but because Wallet / explorer tooling is all IDL-shaped today. Codama is the future, Anchor IDL is the present. Shipping both raises the adoption ceiling.

- **R9. Bench vs real Anchor zero-copy.** The bench harness includes an `$anchor_root` slot but the README tables don't include Anchor numbers. Adding Anchor to the table — even with the caveat that Anchor is not a substrate and carries more overhead — would make the "you don't need to choose between Anchor and fast" argument more legible.

- **R10. Publish the UNSAFE_INVARIANTS.md that the README references.** The README promises a full line-level unsafe inventory under `docs/UNSAFE_INVARIANTS.md`. Verify this exists and is current, or build it from the `# Safety` blocks in the code. Auditors and protocol-grade users will look for this document first.

---

## 10. Where Hopper Measures Up

**Against Pinocchio.** Hopper is a framework; Pinocchio is a substrate. The "Hopper vs Pinocchio" question is the wrong question — the right question is "does Hopper add value over writing a program in raw Pinocchio." The answer is yes, measurably, at a cost of ~2000 CU of framework overhead, which buys you segment borrow safety, layout fingerprints, receipts, policy, schema, migration, and tooling. For any protocol more complex than a single-file vault that will be upgraded, this trade is strongly in Hopper's favor. For a research program where you want to bottom out at the substrate, use Pinocchio directly.

**Against Quasar.** This is the real head-to-head, and Hopper wins on:
- Borrow safety at byte granularity (Quasar: whole-account).
- Declarative-macro authoring path (Quasar: proc-macro only).
- Migration planning + receipts + policy (Quasar: none).
- Three-backend portability (Quasar: one).
- Interactive TUI + manager (Quasar: CLI + IDL).

Quasar wins on:
- Ecosystem adoption + Anchor-like syntax familiarity.
- Token-2022 battle-testing.

Net: Hopper is the stronger framework for protocol-grade use; Quasar is the easier framework for Anchor-refugees writing mid-size programs.

**Against Anchor zero-copy.** Hopper wins on every technical axis reviewed. The gap is not small — Hopper has `AccountLoader`-equivalent access plus ~a dozen features Anchor doesn't have at all. What Anchor has is millions of lines of production use. That is not nothing. If you are starting a protocol today and you are willing to invest in learning a new framework, Hopper is the better tool. If you need to ship in two weeks with the broadest hiring pool, Anchor is still the answer.

---

## 11. Final Verdict

Hopper is a serious framework that does not need to grade on a curve. The core invariants hold, the performance numbers are legitimate, the DX ambitions are backed by code rather than promises, and the feature surface is genuinely larger than any single competitor. The most important fixes are documentation-level (R1, R2, R4) and can be done in an afternoon without touching a single line of framework code.

The single biggest risk is not technical; it is that a state-centric framework is a harder sell than a program-centric framework in a market where most teams are shipping individual programs, not evolving long-lived protocols. Hopper's best wedge into the ecosystem is probably going to be protocols that have already hit the "our state layout is unmaintainable" wall — DEX aggregators, governance systems, lending primitives, large on-chain registries — where the tooling, migration planner, and receipts pay for themselves almost immediately.

For a v0.1 on a zero-copy framework this ambitious, the code quality is unusually high and the marketing-vs-reality gap is unusually small. The recommendations above are polish, not rewrites.

---

*Audit prepared against tree state on 2026-04-24. Subsequent commits may alter findings. Claims with file:line references were spot-checked against the current source; claims about Pinocchio, Quasar, and Anchor zero-copy reflect their public state per Anza, Blueshift, and Anchor-foundation repositories as of the audit date.*