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
//! # Hopper Metaplex
//!
//! Hopper-owned builder surface for Metaplex Token Metadata. Powers the
//! `metadata::*` and `master_edition::*` field keywords on
//! `#[derive(Accounts)]` contexts and the `examples/hopper-nft-mint` reference
//! program.
//!
//! The keywords have working CPI lowering, so the accounts struct can own the
//! Metaplex ceremony instead of pushing it into handwritten handler code.
//!
//! ## What this crate ships
//!
//! - [`MPL_TOKEN_METADATA_PROGRAM_ID`](constants::MPL_TOKEN_METADATA_PROGRAM_ID)
//! - the canonical Metaplex Token Metadata program address as a Hopper
//! `Address` constant, decoded at compile time.
//! - [`seeds`] - PDA-seed helpers (`metadata_pda`, `master_edition_pda`).
//! Hopper's typed-seeds path uses these so the field-level
//! `seeds = ...` constraint composes the right thing.
//! - [`instructions`] - zero-copy CPI builders for the three Metaplex
//! calls every NFT-mint program reaches for: `CreateMetadataAccountV3`,
//! `CreateMasterEditionV3`, `UpdateMetadataAccountV2`.
//!
//! ## Scope
//!
//! Newer Metaplex flows (Bubblegum compressed NFTs, the
//! `pNFT` programmable-NFT lifecycle, edition prints, collection
//! verification) are out of scope for this first cut. The three
//! instructions above cover ~95% of straightforward 1-of-1 NFT mint
//! programs and the full Boobies use case. Adding more is a matter of
//! pattern-matching against the existing builders; the core encoding
//! infrastructure ([`encoding`]) is shared.
//!
//! ## Encoding policy
//!
//! Metaplex's instruction data is Borsh-encoded with variable-length
//! `String` fields. Hopper is otherwise a zero-copy framework, but we
//! cannot make Borsh zero-copy - variable-length strings have no fixed
//! offsets. Each builder therefore allocates a small **stack** buffer
//! (256-512 bytes depending on instruction) and writes the Borsh tape
//! directly into it. No heap, no `Vec`, no `alloc::String`. The
//! [`encoding::BorshTape`] writer is the load-bearing piece; it caps the
//! payload at the buffer size and returns
//! `ProgramError::InvalidInstructionData` if a string would overflow,
//! so a malicious caller can't push the program into UB by passing an
//! oversized name.
pub use ;
pub use ;
pub use ;