add pinocchio spl-token-minter example#600
Conversation
2819844 to
383287d
Compare
Greptile SummaryThis PR adds a Pinocchio implementation of the
Confidence Score: 5/5Safe to merge — new self-contained example with no changes to shared program logic. The change adds a new Pinocchio example that is fully isolated from existing programs. The Metaplex CPI is hand-rolled correctly against the V3 instruction layout, parsing helpers are bounds-checked, and the bankrun test exercises both instructions end-to-end. All prior review concerns have been addressed in earlier commits. No files require special attention. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant Program as spl-token-minter<br/>(Pinocchio)
participant System as System Program
participant Token as SPL Token Program
participant ATA as Associated Token<br/>Account Program
participant Metaplex as Token Metadata<br/>Program
Note over Client,Metaplex: Instruction 0 — Create
Client->>Program: Create(name, symbol, uri)
Program->>System: "CreateAccount(mint_account, space=82)"
System-->>Program: ok
Program->>Token: "InitializeMint2(decimals=9, mint_authority=payer)"
Token-->>Program: ok
Program->>Metaplex: "CreateMetadataAccountV3(DataV2, is_mutable=false)"
Metaplex-->>Program: ok
Program-->>Client: ok
Note over Client,Metaplex: Instruction 1 — Mint
Client->>Program: Mint(quantity)
Program->>ATA: CreateIdempotent(payer, mint)
ATA-->>Program: ok (created or already exists)
Program->>Token: "MintTo(mint, ata, authority=payer, amount=quantity)"
Token-->>Program: ok
Program-->>Client: ok
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client
participant Program as spl-token-minter<br/>(Pinocchio)
participant System as System Program
participant Token as SPL Token Program
participant ATA as Associated Token<br/>Account Program
participant Metaplex as Token Metadata<br/>Program
Note over Client,Metaplex: Instruction 0 — Create
Client->>Program: Create(name, symbol, uri)
Program->>System: "CreateAccount(mint_account, space=82)"
System-->>Program: ok
Program->>Token: "InitializeMint2(decimals=9, mint_authority=payer)"
Token-->>Program: ok
Program->>Metaplex: "CreateMetadataAccountV3(DataV2, is_mutable=false)"
Metaplex-->>Program: ok
Program-->>Client: ok
Note over Client,Metaplex: Instruction 1 — Mint
Client->>Program: Mint(quantity)
Program->>ATA: CreateIdempotent(payer, mint)
ATA-->>Program: ok (created or already exists)
Program->>Token: "MintTo(mint, ata, authority=payer, amount=quantity)"
Token-->>Program: ok
Program-->>Client: ok
Reviews (3): Last reviewed commit: "spl-token-minter pinocchio: dump fixture..." | Re-trigger Greptile |
| try { | ||
| mkdirSync(outputDir, { recursive: true }); | ||
| // Point the Solana CLI at mainnet, where the canonical program lives. | ||
| execSync("solana config set -um", { stdio: "inherit" }); |
There was a problem hiding this comment.
postinstall mutates the global Solana CLI config
solana config set -um permanently sets the user's active cluster to mainnet-beta in ~/.config/solana/cli/config.yml. Any developer who runs pnpm install in this directory while working against devnet or localnet will silently have their Solana CLI endpoint changed. solana program dump accepts a --url flag (--url mainnet-beta or --url m), which would fetch from mainnet without touching the global config.
There was a problem hiding this comment.
Fixed in dd291b9. Switched to a per-command solana program dump -um <id> and dropped the global solana config set -um, so postinstall no longer mutates the developer's ~/.config/solana/cli/config.yml. This mirrors the pattern already used in the sibling pda-mint-authority example.
| MintTo { | ||
| mint: mint_account, | ||
| account: associated_token_account, | ||
| mint_authority, | ||
| amount: quantity, | ||
| } | ||
| .invoke()?; |
There was a problem hiding this comment.
Implicit
mint_authority == payer invariant is not asserted
MintTo requires its mint_authority to be a signer. Here mint_authority (account index 1) is declared as a non-signer ([]) in the instruction — the CPI works only because the payer's address is passed for both slots and the payer signs the transaction. If a caller provides a genuinely different mint_authority key, the SPL Token program rejects the CPI with an opaque "missing required signature" error instead of a clear program-level message. The same applies to create.rs. For an educational example a quick equality check would make the design intent explicit and produce a cleaner error for learners who experiment with different account layouts.
There was a problem hiding this comment.
Intentional for this example. It models the canonical "payer is the mint authority" flow: the same key fills both the payer and mint_authority slots, and since the payer already signs the transaction the MintTo/InitializeMint CPIs satisfy the signer requirement — which is why mint_authority is marked non-signer ([]) here. The SPL Token program is the authority on that invariant and enforces it, so an added equality check would be redundant and would diverge from the anchor example this port mirrors. Happy to add an explicit assert if you'd prefer it for teaching clarity.
| function readTokenAmount(data: Uint8Array): number { | ||
| const buffer = Buffer.from(data); | ||
| return buffer.readUInt32LE(64) + buffer.readUInt32LE(68) * 4294967296; // 2^32 | ||
| } |
There was a problem hiding this comment.
readTokenAmount silently loses precision for amounts above 2^53
The high-word multiplication buffer.readUInt32LE(68) * 4294967296 uses JavaScript's number type, which only holds integers exactly up to 2^53. Token amounts are stored as raw u64 units: 150 tokens at 9 decimals = 1.5 × 10^11, well under 2^53 and safe here. But if a future test mints a much larger amount, the assertion could silently pass with a wrong value. The u64le helper already acknowledges this ceiling; readTokenAmount should carry a similar comment, or the check should use BigInt arithmetic.
There was a problem hiding this comment.
Fixed in dd291b9 via your suggested alternative — added a comment mirroring the u64le encoder, documenting the deliberate es6/no-BigInt design and the exact-below-2^53 guarantee (the 150-token mint asserted here is well within range). Kept number for symmetry with the encoder rather than introducing BigInt on only the decode side.
…t readTokenAmount es6 range
dd291b9 to
c6690d5
Compare
|
@Perelyn-sama @dev-jodee — rebased onto latest main (picks up the ASM sbpf/Solana pin from #625), CI is now fully green. Ready for review whenever you have a chance 🙏 |
Adds a Pinocchio implementation of
tokens/spl-token-minter, continuing the Pinocchio token-examples lane aftertransfer-tokens(#596),escrow(#598), andcreate-token(#599).What it does
Two instructions, matching the
anchorandnativeoptions:Notes
0= Create,1= Mint), matching the Borsh enum variant index used by thenativeexample.CreateMetadataAccountV3CPI is built by hand (rawInstructionView/cpi::invoke) — the same approach as add pinocchio create-token example #599.token_metadata.sofixture via apostinstallscript, like theanchorexample.nativeexample.Files
tokens/spl-token-minter/pinocchio/(program + bankrun test)Cargo.toml(workspace member),README.md(pinocchio link),Cargo.lock