add pinocchio pda-mint-authority example#612
Conversation
Greptile SummaryAdds a Pinocchio port of the
Confidence Score: 4/5The three-instruction flow is functionally correct and all tests pass; the two findings are edge-case correctness and a developer UX side-effect. The tokens/pda-mint-authority/pinocchio/prepare.mjs (cluster config side-effect) and tokens/pda-mint-authority/pinocchio/program/src/instructions/init.rs (non-canonical bump acceptance) Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant Program as pinocchio program
participant System as System Program
participant Token as SPL Token
participant ATA as Associated Token
participant Meta as Token Metadata
Client->>Program: "Init (discriminator=0, bump)"
Program->>System: CreateAccount (PDA, signed via seeds)
Program-->>Client: PDA stores bump
Client->>Program: "Create (discriminator=1, name/symbol/uri)"
Program->>System: CreateAccount (mint)
Program->>Token: "InitializeMint2 (authority=PDA)"
Program->>Meta: CreateMetadataAccountV3 (invoke_signed w/ PDA seeds)
Program-->>Client: Mint + Metadata accounts created
Client->>Program: "Mint (discriminator=2)"
Program->>ATA: CreateIdempotent (payer ATA)
Program->>Token: MintTo 1 token (invoke_signed w/ PDA seeds)
Program->>Meta: CreateMasterEditionV3 (invoke_signed w/ PDA seeds)
Program-->>Client: ATA holds 1 token, master edition locked
%%{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 pinocchio program
participant System as System Program
participant Token as SPL Token
participant ATA as Associated Token
participant Meta as Token Metadata
Client->>Program: "Init (discriminator=0, bump)"
Program->>System: CreateAccount (PDA, signed via seeds)
Program-->>Client: PDA stores bump
Client->>Program: "Create (discriminator=1, name/symbol/uri)"
Program->>System: CreateAccount (mint)
Program->>Token: "InitializeMint2 (authority=PDA)"
Program->>Meta: CreateMetadataAccountV3 (invoke_signed w/ PDA seeds)
Program-->>Client: Mint + Metadata accounts created
Client->>Program: "Mint (discriminator=2)"
Program->>ATA: CreateIdempotent (payer ATA)
Program->>Token: MintTo 1 token (invoke_signed w/ PDA seeds)
Program->>Meta: CreateMasterEditionV3 (invoke_signed w/ PDA seeds)
Program-->>Client: ATA holds 1 token, master edition locked
Reviews (1): Last reviewed commit: "add pinocchio pda-mint-authority example" | 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.
solana config set -um is never reverted
pnpm install runs this script as a postinstall hook, permanently redirecting the developer's Solana CLI cluster to mainnet. A developer who normally works against devnet or localnet will silently end up with a wrong cluster config after installing. Saving the current cluster with solana config get json_rpc_url, pointing to mainnet only for the dump, then restoring it afterward would avoid the side-effect.
| let bump = *data.first().ok_or(ProgramError::InvalidInstructionData)?; | ||
|
|
||
| // Verify the supplied account is the canonical PDA for this bump. | ||
| let pda = derive_address( | ||
| &[MintAuthorityPda::SEED_PREFIX], | ||
| Some(bump), | ||
| program_id.as_array(), | ||
| ); |
There was a problem hiding this comment.
Non-canonical bump is accepted without validation
The bump is taken directly from instruction data and passed to derive_address with Some(bump), which computes a PDA for whatever bump value was supplied rather than requiring the canonical one. A caller who deliberately (or accidentally) provides a non-canonical bump will create the mint-authority account at a different address than findProgramAddressSync would derive client-side. Downstream clients that recompute the PDA without knowing which bump was stored will then resolve a different address and be unable to interact with the mints this program created. Since pinocchio_pubkey::derive_address with bump: None finds the canonical bump on-chain, using it here would prevent this class of error.
Adds a Pinocchio port of the
tokens/pda-mint-authorityexample, alongside the existinganchorandnativeversions.What it does
A program-derived address — not a wallet — is the mint and freeze authority for every NFT this program creates. Three instructions, dispatched by a leading discriminator byte (matching the native
MyInstructionenum):0) — creates the mint-authority PDA ([b"mint_authority"]), signed by its own seeds, and persists the canonical bump in the account.1) — creates a 0-decimal SPL mint whose authority is the PDA, then attaches a Metaplex metadata account via a hand-rolledCreateMetadataAccountV3CPI. The metadata CPI is authorized with the PDA's seeds viainvoke_signed.2) — creates the payer's associated token account (idempotent), mints the single token, then creates the master edition via a hand-rolledCreateMasterEditionV3CPI (max_supply = Some(1)). Both theMintToand master-edition CPIs are signed by the PDA.The new building block here versus the other token examples is PDA-as-signer:
Init, the metadata CPI, theMintTo, and the master-edition CPI all sign as the PDA usingpinocchio::cpi::{Seed, Signer}andinvoke_signed, rather than relying on a wallet signature. The bump recorded byInitis read back from the PDA account to rebuild the signer seeds without re-deriving the address on-chain.Tests
tests/test.tsruns undersolana-bankrun, loading the program plus the Token Metadata program (dumped from mainnet intotests/fixturesbyprepare.mjs). Three cases:CreateMasterEditionV3CPI succeeded).