Skip to content

add pinocchio create-token example#599

Open
MarkFeder wants to merge 1 commit into
solana-foundation:mainfrom
MarkFeder:tokens-create-token-pinocchio
Open

add pinocchio create-token example#599
MarkFeder wants to merge 1 commit into
solana-foundation:mainfrom
MarkFeder:tokens-create-token-pinocchio

Conversation

@MarkFeder

Copy link
Copy Markdown
Contributor

Adds a Pinocchio implementation of tokens/create-token, continuing the Pinocchio token-examples lane after transfer-tokens (#596) and escrow (#598).

What it does

Creates an SPL Token mint and attaches an on-chain Metaplex metadata account (name, symbol, URI) — the same flow as the existing anchor and native options.

Notes

  • The Metaplex Token Metadata program has no typed Pinocchio crate, so the CreateMetadataAccountV3 CPI is built by hand (raw InstructionView / cpi::invoke), matching the on-chain wire format.
  • Single instruction, no discriminator byte — the instruction data is Borsh [name, symbol, uri, decimals], identical to the native example's wire format.
  • The bankrun test loads the Metaplex program from a mainnet-dumped token_metadata.so fixture via a postinstall script, the same approach as the anchor example.
  • The mint authority is passed as a non-signer and aliased to the payer (which signs), mirroring the native example.

Files

  • New: tokens/create-token/pinocchio/ (program + bankrun test)
  • Edited: root Cargo.toml (workspace member), README.md (pinocchio link), Cargo.lock

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a complete Pinocchio implementation of the create-token example, building an SPL Token mint and attaching a Metaplex metadata account via a hand-rolled CreateMetadataAccountV3 CPI (no typed Pinocchio crate exists for Metaplex). The implementation is well-structured, correctly follows the V3 account layout, and uses the same bankrun test harness as the existing transfer-tokens pinocchio example.

  • Program (create_token.rs, mod.rs): custom zero-copy Borsh parser, three sequential CPIs (system/token/Metaplex), and a hand-built metadata instruction that matches the V3 wire format — discriminator, DataV2, is_mutable, collection_details.
  • Tests (test.ts, prepare.mjs): bankrun test loading a mainnet-dumped Metaplex fixture via postinstall, exercising both the SPL-token and NFT (0-decimal) paths with account-ownership assertions.

Confidence Score: 5/5

The change is safe to merge; it adds a self-contained new example directory and two minor root-level edits.

The V3 CPI is correctly constructed with the right account layout and wire format. The Borsh parser handles all bounds-check failures safely. Both test paths are exercised against the mainnet-dumped Metaplex fixture.

No files require special attention.

Important Files Changed

Filename Overview
tokens/create-token/pinocchio/program/src/instructions/create_token.rs Core implementation: three sequential CPIs (system, token, Metaplex V3), hand-built metadata instruction with correct wire layout (discriminator 33, DataV2, is_mutable:false, collection_details:None). Account list matches V3 spec.
tokens/create-token/pinocchio/program/src/instructions/mod.rs Zero-copy Borsh parser for CreateTokenArgs; correctly reads three length-prefixed strings then a u8, with proper bounds checking at each step.
tokens/create-token/pinocchio/tests/test.ts bankrun test exercising SPL-token (9-decimal) and NFT (0-decimal) paths with ownership assertions; follows the established async-describe pattern.
tokens/create-token/pinocchio/prepare.mjs postinstall script that dumps the mainnet Metaplex program into tests/fixtures; same approach as the anchor example.
tokens/create-token/pinocchio/program/src/lib.rs Standard no_std entrypoint with bump allocator; panic handler correctly installed.
tokens/create-token/pinocchio/program/src/processor.rs Single-instruction dispatcher; no discriminator byte, correct for a single-instruction program.
tokens/create-token/pinocchio/package.json Standard package configuration with postinstall hook; consistent with sibling pinocchio examples.
tokens/create-token/pinocchio/program/Cargo.toml Workspace-pinned pinocchio dependencies; cdylib + lib crate types; no issues.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Client as Test Client
    participant Prog as Pinocchio Program
    participant Sys as System Program
    participant Tok as Token Program
    participant Mpl as Metaplex Metadata

    Client->>Prog: CreateToken(name, symbol, uri, decimals)
    Note over Prog: parse Borsh args (CreateTokenArgs)
    Prog->>Sys: CreateAccount(payer to mint, 82 bytes, rent-exempt)
    Sys-->>Prog: ok
    Prog->>Tok: InitializeMint2(decimals, mint_authority, freeze_authority)
    Tok-->>Prog: ok
    Note over Prog: build_metadata_data() builds discriminator + DataV2 + is_mutable + collection_details
    Prog->>Mpl: CreateMetadataAccountV3(metadata, mint, mint_authority, payer, update_authority, system_program)
    Mpl-->>Prog: ok
    Prog-->>Client: Ok(())
Loading
%%{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 as Test Client
    participant Prog as Pinocchio Program
    participant Sys as System Program
    participant Tok as Token Program
    participant Mpl as Metaplex Metadata

    Client->>Prog: CreateToken(name, symbol, uri, decimals)
    Note over Prog: parse Borsh args (CreateTokenArgs)
    Prog->>Sys: CreateAccount(payer to mint, 82 bytes, rent-exempt)
    Sys-->>Prog: ok
    Prog->>Tok: InitializeMint2(decimals, mint_authority, freeze_authority)
    Tok-->>Prog: ok
    Note over Prog: build_metadata_data() builds discriminator + DataV2 + is_mutable + collection_details
    Prog->>Mpl: CreateMetadataAccountV3(metadata, mint, mint_authority, payer, update_authority, system_program)
    Mpl-->>Prog: ok
    Prog-->>Client: Ok(())
Loading

Reviews (2): Last reviewed commit: "add pinocchio create-token example" | Re-trigger Greptile

Comment thread tokens/create-token/pinocchio/tests/test.ts
@MarkFeder MarkFeder force-pushed the tokens-create-token-pinocchio branch from e995793 to ac589b7 Compare July 9, 2026 07:38
@MarkFeder

Copy link
Copy Markdown
Contributor Author

@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 🙏

@dev-jodee dev-jodee left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the work, couple of comments !

try {
mkdirSync(outputDir, { recursive: true });
// Point the Solana CLI at mainnet, where the canonical program lives.
execSync("solana config set -um", { stdio: "inherit" });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't think we should change the config of the user's cli, program dump can take the url as a parameter, better do it then


/// Reads a Borsh `string` (a 4-byte little-endian length prefix followed by that
/// many UTF-8 bytes) starting at `*offset`, advancing `offset` past it.
fn read_borsh_string<'a>(data: &'a [u8], offset: &mut usize) -> Result<&'a [u8], ProgramError> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this shoudl probably be in a util file instead of the mod.rs, would be cleaner

}

impl<'a> CreateTokenArgs<'a> {
/// Parses the instruction data: three Borsh strings followed by a `u8`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think this should be in create_token.rs file as well since its related to that specific ix

pub use create_token::*;

/// Size (in bytes) of an SPL Token mint account.
pub const MINT_SIZE: usize = 82;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does pinocchio have that as a const already? might be able to reuse

)[0];
}

describe("Create Token (Pinocchio)", async () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this runs 0 test, since describe is async, it doesn't wait for anything ? All the setup (async) should be in before()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants