feat(compression/cutils): add pinocchio example - #723
Conversation
Greptile SummaryThe PR adds a Pinocchio implementation of the compressed NFT utilities example, including hand-built Bubblegum and account-compression CPIs.
Confidence Score: 4/5The PR does not yet appear safe to merge because its test dependency requires Node 24 while the repository remains configured for Node 22. The unresolved runtime contract mismatch remains: the lockfile selects Files Needing Attention: compression/cutils/pinocchio/package.json, compression/cutils/pinocchio/pnpm-lock.yaml Important Files Changed
Reviews (2): Last reviewed commit: "feat(compression/cutils): add pinocchio ..." | Re-trigger Greptile |
|
Real inconsistency, but not one this PR introduces and not one the suggested fix resolves. Every It is already the status quo on The root Leaving the dependency as-is to match the merged examples. Bumping the root |
|
Split the runtime bump out to #724 as offered — CONTRIBUTING.md asks for runtime bumps to be deliberate rather than incidental, so it stays out of this PR. |
Ports both instructions. `mint` CPIs into mpl-bubblegum's `MintToCollectionV1` and `verify` rebuilds the leaf hash and CPIs into SPL Account Compression's `verify_leaf`. Neither program has a pinocchio crate, so the instruction data is built by hand. `MetadataArgs` is borsh-encoded straight into a stack buffer sized for the longest URI Token Metadata accepts, which keeps the mint allocation-free. The leaf hash uses the keccak syscall directly. The anchor version cannot run in CI — it needs devnet and a DAS indexer, which is why it sits in .ghaignore, and its verify test is commented out. This one runs on LiteSVM against the four mainnet programs prepare.mjs dumps: it builds a real Metaplex collection, mints into it through the program, and asserts the tree's own root matches the leaf hash recomputed in TypeScript. Both verify failure modes are covered. The anchor example's `Data` account is not carried over: nothing constructs or reads it.
d33bf16 to
e0e0f6c
Compare
|
@amilz could you take a look at this one when you get a chance? No open review threads left on it, so it is ready for maintainer review. It is one of 23 open Pinocchio ports I have up — they are independent and self-contained, so they can be reviewed and merged in any order: https://github.com/solana-developers/program-examples/pulls/MarkFeder |
What
Adds a Pinocchio implementation of the cutils example, alongside the existing
anchorversion. This is the last un-ported example undercompression/.Both instructions are carried over:
mint— CPIs into mpl-bubblegum'sMintToCollectionV1to mint a compressed NFT into a Metaplex collection. The metadata is fixed (BURGER/BURG, one creator at 100%) apart from the URI, which is the instruction data.verify— recomputes the asset ID and theLeafSchema::V1hash, then CPIs into SPL Account Compression'sverify_leafto prove the leaf is in the tree. Nothing is written; it either succeeds or the CPI fails.How it works
Neither bubblegum nor account-compression has a Pinocchio crate, so both instructions are built by hand:
MetadataArgsis borsh-encoded straight into a stack buffer sized for the longest URI Token Metadata accepts (200 bytes), so the mint needs no allocator. A smallWritercursor handles the length-prefixed strings.sol_keccak256), matchingLeafSchema::V1::hash()byte for byte — version tag, asset ID, owner, delegate, nonce, data hash, creator hash.verifyusesinvoke_with_boundswith the account list capped at 30 — the protocol maximummax_depth, so no valid proof can exceed it.Test
The anchor version can't run in CI — it needs devnet and a DAS indexer, which is why
compression/cutils/anchorsits in.ghaignoreand itsVerifytest is commented out. The Pinocchio version runs entirely on LiteSVM against the four mainnet programsprepare.mjsdumps (bubblegum, account-compression, noop, token-metadata).The test builds a real Metaplex collection — mint, ATA,
CreateMetadataAccountV3with collection details,CreateMasterEditionV3— creates a merkle tree, mints through the program, then asserts the tree's own root equals the root recomputed from a leaf hash built independently in TypeScript. That check is what pins down the metadata serialization, both hashes and the leaf schema.Verified locally:
cargo build-sbf,cargo fmt --check, Clippy (-D warnings),cargo test,tsc --noEmit, Prettier, andpnpm install --frozen-lockfileall clean.Note
The anchor example's
Datastate account is not carried over — nothing in either action constructs or reads it, so it is dead code there. Happy to port it if you'd rather keep the two in lockstep.prepare.mjsusessolana program dump -umrather thansolana config set -um, per #720.AI use: I chose the approach (hand-built CPI wire formats, stack-allocated borsh encoding, the LiteSVM collection setup) and verified the discriminators and hashing against the mpl-bubblegum and spl-account-compression sources; implementation and tests were written with Claude Code and reviewed by me.