add pinocchio create-token example#599
Conversation
6e08f7a to
e995793
Compare
Greptile SummaryThis PR adds a Pinocchio implementation of the
Confidence Score: 5/5Safe to merge — entirely additive change in a new subdirectory with no modifications to existing programs. The hand-built Metaplex CPI wire format is verified byte-for-byte against the native example, and the fixed-size stack buffer exactly accommodates the Metaplex field maxima with overflow caught by slice.get() bounds checks. Both test scenarios exercise the full on-chain flow against the real mainnet Metaplex binary. No files require special attention. Important Files Changed
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 Program
Client->>Prog: CreateToken(name, symbol, uri, decimals)
Note over Prog: Parse Borsh args
Prog->>Sys: "CreateAccount(mint, space=Mint::LEN, owner=TokenProgram)"
Sys-->>Prog: OK
Prog->>Tok: InitializeMint2(mint, decimals, mint_authority, freeze_authority)
Tok-->>Prog: OK
Note over Prog: build_metadata_data() on stack buffer
Prog->>Mpl: CreateMetadataAccountV3(metadata_pda, mint, mint_authority, payer, update_authority, system_program)
Mpl->>Sys: CreateAccount(metadata_pda)
Sys-->>Mpl: OK
Mpl-->>Prog: OK
Prog-->>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 as Test Client
participant Prog as Pinocchio Program
participant Sys as System Program
participant Tok as Token Program
participant Mpl as Metaplex Program
Client->>Prog: CreateToken(name, symbol, uri, decimals)
Note over Prog: Parse Borsh args
Prog->>Sys: "CreateAccount(mint, space=Mint::LEN, owner=TokenProgram)"
Sys-->>Prog: OK
Prog->>Tok: InitializeMint2(mint, decimals, mint_authority, freeze_authority)
Tok-->>Prog: OK
Note over Prog: build_metadata_data() on stack buffer
Prog->>Mpl: CreateMetadataAccountV3(metadata_pda, mint, mint_authority, payer, update_authority, system_program)
Mpl->>Sys: CreateAccount(metadata_pda)
Sys-->>Mpl: OK
Mpl-->>Prog: OK
Prog-->>Client: OK
Reviews (4): Last reviewed commit: "create-token pinocchio: rustfmt write_by..." | Re-trigger Greptile |
e995793 to
ac589b7
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 🙏 |
dev-jodee
left a comment
There was a problem hiding this comment.
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" }); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Good call — dropped the solana config set -um and now pass the cluster straight to the dump: solana program dump -um <id> <file>. No more touching the user's CLI config.
|
|
||
| /// 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> { |
There was a problem hiding this comment.
this shoudl probably be in a util file instead of the mod.rs, would be cleaner
There was a problem hiding this comment.
Done — the borsh string helpers (read_borsh_string/push_borsh_string) now live in a new util.rs, so mod.rs is just module wiring.
| } | ||
|
|
||
| impl<'a> CreateTokenArgs<'a> { | ||
| /// Parses the instruction data: three Borsh strings followed by a `u8`. |
There was a problem hiding this comment.
think this should be in create_token.rs file as well since its related to that specific ix
There was a problem hiding this comment.
Agreed — moved CreateTokenArgs (struct + parse) into create_token.rs since it's specific to that instruction. The Metaplex program-id const went with it.
| pub use create_token::*; | ||
|
|
||
| /// Size (in bytes) of an SPL Token mint account. | ||
| pub const MINT_SIZE: usize = 82; |
There was a problem hiding this comment.
Does pinocchio have that as a const already? might be able to reuse
There was a problem hiding this comment.
Yes it does — switched to pinocchio_token::state::Mint::LEN and removed the hand-rolled MINT_SIZE const.
| )[0]; | ||
| } | ||
|
|
||
| describe("Create Token (Pinocchio)", async () => { |
There was a problem hiding this comment.
I think this runs 0 test, since describe is async, it doesn't wait for anything ? All the setup (async) should be in before()
There was a problem hiding this comment.
Nice catch — you're right, the async describe callback registered the it blocks too late. Moved the async bankrun setup into a before() hook and kept describe synchronous.
- prepare.mjs: dump program via 'solana program dump -um' instead of mutating the user's Solana CLI config - reuse pinocchio_token Mint::LEN instead of a hand-rolled MINT_SIZE const - move CreateTokenArgs into create_token.rs (ix-specific) and the borsh string helpers into a util module; slim mod.rs to module wiring - tests: run async bankrun setup in a before() hook so the it() blocks actually register (a describe callback runs synchronously)
|
@dev-jodee thanks for the review! Pushed a commit addressing all five points:
Ready for another look when you have a moment. |
…g tests The tests were previously registered inside an async describe callback, so mocha ran 0 of them and CI passed without ever executing the program. With the before() hook they actually run — and surfaced a real failure: Program log: Instruction: CreateToken failed: unsupported BPF instruction Root cause: Rent::try_minimum_balance() takes a floating-point path for the exemption threshold (an f64), which the current platform-tools lower to a float instruction the solana-bankrun VM rejects. Compute the rent-exempt minimum with integer math using pinocchio's default rent constants instead (DEFAULT_LAMPORTS_PER_BYTE already folds in the 2-year threshold). While here, make the program alloc-free: build the Metaplex instruction data in a fixed stack buffer and switch to program_entrypoint! + no_allocator!, matching the other pinocchio examples. Verified with cargo build-sbf (Solana 4.1.1 / platform-tools v1.54) + the bankrun test: both cases pass.
Adds a Pinocchio implementation of
tokens/create-token, continuing the Pinocchio token-examples lane aftertransfer-tokens(#596) andescrow(#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
anchorandnativeoptions.Notes
CreateMetadataAccountV3CPI is built by hand (rawInstructionView/cpi::invoke), matching the on-chain wire format.[name, symbol, uri, decimals], identical to thenativeexample's wire format.token_metadata.sofixture via apostinstallscript, the same approach as theanchorexample.nativeexample.Files
tokens/create-token/pinocchio/(program + bankrun test)Cargo.toml(workspace member),README.md(pinocchio link),Cargo.lock