Skip to content

Add Token-2022 cpi-guard Pinocchio example - #701

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

Add Token-2022 cpi-guard Pinocchio example#701
MarkFeder wants to merge 1 commit into
solana-foundation:mainfrom
MarkFeder:tokens-token-2022-cpi-guard-pinocchio

Conversation

@MarkFeder

Copy link
Copy Markdown
Contributor

What

Adds a Pinocchio implementation of the Token-2022 cpi-guard example. The example previously had only an anchor version (no native), so this is a fresh port, following the same kit + litesvm template as the other Token-2022 pinocchio examples.

How it works — and why it's different

The CpiGuard extension prevents privileged token operations (transfer, burn, approve, close) from being performed through a CPI when they're authorized by the account owner — a protection against malicious programs redirecting funds.

Crucially, CpiGuard cannot be enabled or disabled through a CPI (Token-2022 requires those to be transaction-level). So — unlike every other extension example — the program cannot initialize this extension. Instead:

  • The program exposes a single cpi_transfer instruction that performs a Token-2022 TransferChecked CPI (variant 12) from a source account to a destination, signed by the source's owner. It reads the mint's decimals directly from the mint account data (offset 44) so the checked transfer matches the mint.
  • The example demonstrates the guard's effect: enabling/disabling CpiGuard happens client-side in the test.

Test

litesvm + @solana/kit. The test:

  1. Creates a mint and a source account with CpiGuard enabled (client-side, via getEnableCpiGuardInstruction), funded with tokens, plus a plain destination account.
  2. Invokes the program's cpi_transfer while the guard is enabled → asserts it is rejected (FailedTransactionMetadata).
  3. Disables CpiGuard, then invokes cpi_transfer again → asserts it succeeds, and that the destination balance is 1.
Token-2022 CPI Guard (Pinocchio)
  ✔ Blocks a CPI transfer while CpiGuard is enabled and allows it once disabled
1 passing

The success-after-disable (with the balance moving) proves the guard is the cause of the rejection, not a mechanical transfer error.

Verified locally: cargo build-sbf, the litesvm test, tsc --noEmit, Prettier, cargo fmt --check, Clippy, and pnpm install --frozen-lockfile all clean.

Ports the cpi-guard Token-2022 example to Pinocchio (the anchor example has
no native sibling). Unlike the other extension examples, CpiGuard cannot be
enabled or disabled through a CPI, so the program cannot initialize it;
instead the program exposes a cpi_transfer instruction (a Token-2022
TransferChecked CPI, reading the mint's decimals from account data) and the
example demonstrates the guard's effect.

The litesvm test creates a source account with CpiGuard enabled (client-side)
and funded, then shows the program's CPI transfer is rejected while the guard
is on and succeeds once it is disabled, asserting the destination balance.
Matches the kit + litesvm template of the other token-2022 pinocchio examples.
@MarkFeder
MarkFeder requested a review from dev-jodee as a code owner August 26, 2026 20:44
@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a Pinocchio implementation and LiteSVM test for demonstrating Token-2022 CPI Guard behavior.

  • Adds a hand-built TransferChecked CPI that reads the mint’s decimals.
  • Tests rejection while CPI Guard is enabled and successful transfer after it is disabled.
  • Adds workspace, package, lockfile, build, and deployment configuration.

Confidence Score: 4/5

The program and test behavior appear sound, but the broken artifact paths in both deployment commands should be corrected before merging.

The build emits token_2022_cpi_guard_pinocchio_program.so, while both newly added deployment paths attempt to deploy program.so, causing the documented deployment workflow to fail.

Files Needing Attention: tokens/token-2022/cpi-guard/pinocchio/cicd.sh; tokens/token-2022/cpi-guard/pinocchio/package.json

Important Files Changed

Filename Overview
tokens/token-2022/cpi-guard/pinocchio/program/src/instructions/cpi_transfer.rs Builds the Token-2022 TransferChecked instruction and forwards the expected source, mint, destination, and authority accounts.
tokens/token-2022/cpi-guard/pinocchio/tests/test.ts Exercises both guarded rejection and unguarded success, including verification of the destination balance.
tokens/token-2022/cpi-guard/pinocchio/cicd.sh Builds the program but deploys a nonexistent generic program.so artifact.
tokens/token-2022/cpi-guard/pinocchio/package.json Defines the test and build workflow, but its deployment script repeats the incorrect artifact filename.
tokens/token-2022/cpi-guard/pinocchio/program/src/processor.rs Dispatches the example’s sole data-free instruction directly to the CPI transfer handler.

Reviews (1): Last reviewed commit: "token-2022 cpi-guard: add pinocchio exam..." | Re-trigger Greptile

# Run this bad boy with "bash cicd.sh" or "./cicd.sh"

cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so
solana program deploy ./program/target/so/program.so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Deployment artifact path mismatch

When either deployment command runs after the build, it attempts to deploy program.so instead of the crate-derived token_2022_cpi_guard_pinocchio_program.so, causing deployment to fail because the referenced artifact does not exist.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This cicd.sh (and the matching deploy npm script) is a verbatim copy of the template used by every merged pinocchio Token-2022 example — immutable-owner, permanent-delegate, interest-bearing, and transfer-fee all ship the identical solana program deploy ./program/target/so/program.so line. It's a reference/convenience script and is not exercised by CI or the tests: the build-and-test flow builds with --sbf-out-dir=./tests/fixtures, which produces and loads the crate-named token_2022_cpi_guard_pinocchio_program.so, so all checks pass.

You're right that the standalone deploy path wouldn't resolve the crate-derived filename as-is. Since it's identical across the whole merged series, I've kept it consistent here rather than diverging in a single example — correcting it is better done as a repo-wide follow-up so every example stays in sync. Happy to open that separately if the maintainers want it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That context makes sense. Since this is a non-CI reference script intentionally kept consistent with the other merged Pinocchio Token-2022 examples, I agree this should be handled as a repo-wide follow-up rather than changed in isolation. I’m treating the artifact-path mismatch as non-blocking for this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Opened the repo-wide fix as #702 — it switches the deploy path to a *.so glob across all affected native and pinocchio examples so every example stays consistent.

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.

1 participant