Skip to content

add pinocchio token-2022 mint-close-authority example#624

Merged
dev-jodee merged 6 commits into
solana-foundation:mainfrom
MarkFeder:tokens-token-2022-mint-close-authority-pinocchio
Jul 23, 2026
Merged

add pinocchio token-2022 mint-close-authority example#624
dev-jodee merged 6 commits into
solana-foundation:mainfrom
MarkFeder:tokens-token-2022-mint-close-authority-pinocchio

Conversation

@MarkFeder

Copy link
Copy Markdown
Contributor

Adds a Pinocchio port of the tokens/token-2022/mint-close-authority example, alongside the existing anchor and native versions.

What it does

A single instruction creates an SPL Token-2022 mint carrying the MintCloseAuthority extension, so a designated authority can later close the mint and reclaim its rent.

Notes

Token-2022 has no Pinocchio wrapper crate (pinocchio-token targets the legacy SPL Token program), so the Token-2022 instructions are built by hand and CPI'd:

  1. CreateAccount for the mint, sized to 202 bytes (base Account length 165 + account-type byte + one MintCloseAuthority TLV entry) and owned by the Token-2022 program.
  2. InitializeMintCloseAuthority (variant 25) — must run before the mint is initialized.
  3. InitializeMint (variant 0).

The bankrun test asserts the resulting mint is owned by Token-2022, is exactly 202 bytes, has the correct decimals, and that the extension header (account type Mint, TLV type MintCloseAuthority) and stored close authority were written correctly.

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a Pinocchio port of the tokens/token-2022/mint-close-authority example, sitting alongside the existing anchor and native variants. Because no pinocchio-token-2022 crate exists, the three Token-2022 instructions (CreateAccount, InitializeMintCloseAuthority, InitializeMint) are hand-serialized and CPI'd using hardcoded program IDs.

  • create_mint.rs: Implements the single instruction by issuing three sequential CPIs — CreateAccount (202 bytes, owned by Token-2022), InitializeMintCloseAuthority (variant 25, 34-byte data with 1-byte COption tag), and InitializeMint (variant 0, 67-byte data). All instruction data layouts match Token-2022's unpack_pubkey_option expectations.
  • mod.rs: Defines MINT_SIZE = 202 (base 165 + account-type 1 + TLV 36) and TOKEN_2022_PROGRAM_ID constant; both are consistent with ExtensionType::try_calculate_account_len::<Mint>(&[MintCloseAuthority]).
  • test.ts: Uses a distinct closeAuthority keypair (separate from payer/mint authority) and validates the result via the official getMintDecoder() codec, which implicitly verifies TLV structure, decimals, and the stored close authority address.

Confidence Score: 5/5

Safe to merge; all three hand-rolled Token-2022 CPIs are correctly encoded and the test validates mint ownership, size, extension type, and the stored close authority using the official codec.

The instruction data layouts (34-byte InitializeMintCloseAuthority, 67-byte InitializeMint) are consistent with Token-2022's unpack_pubkey_option (1-byte COption tag + 32-byte key). MINT_SIZE=202 matches ExtensionType::try_calculate_account_len. The test now uses a distinct closeAuthority keypair, making the stored-authority assertion genuinely meaningful. No functional gaps were found.

No files require special attention.

Important Files Changed

Filename Overview
tokens/token-2022/mint-close-authority/pinocchio/program/src/instructions/create_mint.rs Core instruction implementation; correctly serializes InitializeMintCloseAuthority (34 bytes, 1-byte COption tag) and InitializeMint (67 bytes) matching Token-2022's unpack_pubkey_option format; CPI account slices are minimal and correct.
tokens/token-2022/mint-close-authority/pinocchio/program/src/instructions/mod.rs Defines MINT_SIZE=202 (165+1+36) and TOKEN_2022_PROGRAM_ID; both values match the SPL Token-2022 extension layout; CreateTokenArgs parsing is minimal and correct.
tokens/token-2022/mint-close-authority/pinocchio/tests/test.ts Comprehensive bankrun test using a distinct closeAuthority keypair and the official getMintDecoder() codec; validates owner, size, decimals, extension kind, and stored close authority address.
tokens/token-2022/mint-close-authority/pinocchio/program/src/lib.rs Standard no_std pinocchio entrypoint boilerplate with global allocator (needed for Vec-based instruction data building).
tokens/token-2022/mint-close-authority/pinocchio/program/src/processor.rs Single-instruction dispatch; no discriminator byte consumed (matching the native wire format as documented).
tokens/token-2022/mint-close-authority/pinocchio/program/Cargo.toml Correct workspace dependencies (pinocchio, pinocchio-log, pinocchio-system); no pinocchio-token-2022 needed since Token-2022 instructions are built by hand.
tokens/token-2022/mint-close-authority/pinocchio/package.json Standard test harness setup matching other pinocchio examples in the repo; uses litesvm for bankrun-style testing.

Sequence Diagram

sequenceDiagram
    participant Client as Test Client
    participant Program as Pinocchio Program
    participant SysProg as System Program
    participant T22 as Token-2022 Program

    Client->>Program: CreateMint (decimals, 7 accounts)
    activate Program

    Program->>Program: Rent::get() → compute lamports for 202 bytes

    Program->>SysProg: "CreateAccount(payer→mint, 202 bytes, owner=Token-2022)"
    SysProg-->>Program: ✓ mint account created

    Program->>T22: "InitializeMintCloseAuthority(variant=25) [1-byte tag + 32-byte close authority]"
    T22-->>Program: ✓ MintCloseAuthority TLV written

    Program->>T22: "InitializeMint(variant=0) [decimals + mint_authority + freeze_authority]"
    T22-->>Program: ✓ base mint initialized

    Program-->>Client: Ok(())
    deactivate Program

    Client->>Client: getMintDecoder().decode(mintData) assert decimals, MintCloseAuthority extension, close authority address
Loading

Reviews (10): Last reviewed commit: "token-2022 mint-close-authority pinocchi..." | Re-trigger Greptile

Comment thread tokens/token-2022/mint-close-authority/pinocchio/tests/test.ts
Comment thread tokens/token-2022/mint-close-authority/pinocchio/tests/test.ts Outdated
@MarkFeder

Copy link
Copy Markdown
Contributor Author

Hi @Perelyn-sama — thanks again for merging my previous pinocchio examples! This one adds the token-2022/mint-close-authority pinocchio port (the first Token-2022 example in the pinocchio series). All checks are green except a single red X on the ASM build-and-test-group-0 — that's the pre-existing repo-wide ASM failure, unrelated to this diff, which #625 fixes. Greptile has it at 5/5 "safe to merge." Would appreciate a review whenever you get a chance. Thanks!

@MarkFeder
MarkFeder force-pushed the tokens-token-2022-mint-close-authority-pinocchio branch 2 times, most recently from 6a49f60 to b40292f Compare July 9, 2026 07:32
@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 🙏

@MarkFeder
MarkFeder requested a review from dev-jodee as a code owner July 15, 2026 21:47
@MarkFeder
MarkFeder force-pushed the tokens-token-2022-mint-close-authority-pinocchio branch from 3f941a9 to 87b819a Compare July 15, 2026 21:52
Comment thread tokens/token-2022/mint-close-authority/pinocchio/tests/test.ts Outdated

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

need signed commits please

MarkFeder and others added 5 commits July 22, 2026 00:12
Use a distinct close-authority keypair so the stored-authority check
verifies sourcing from account index 2, and assert the TLV length field
(32) explicitly.
… run

Move the async bankrun setup out of the `describe` callback and into a
`before` hook so Mocha collects the `it` block (an async `describe` body
registers tests after the suite is already collected, so nothing ran).

With the test now executing, replace `Rent::try_minimum_balance` with the
integer rent formula: its floating-point exemption-threshold path emits an
opcode the bankrun VM rejects ("unsupported BPF instruction"). Matches the
create-token example.
Build the transaction with @solana/kit (addresses, instruction, message,
signing) instead of @solana/web3.js. A thin web3.js shim remains only where
solana-bankrun@0.3.1's BanksClient still requires it (start()/getAccount()
PublicKey, processTransaction() VersionedTransaction). Bump tsconfig
target/lib to es2020 for kit's BigInt usage.
Move the test runtime from solana-bankrun to litesvm, which is kit-native, so
web3.js is dropped entirely. The kit transaction returned by
signTransactionMessageWithSigners is handed straight to LiteSVM.sendTransaction
(no VersionedTransaction shim), and getAccount/airdrop take kit Addresses.

- deps: drop @solana/web3.js + solana-bankrun, add litesvm; pin @solana/kit to
  ^6.10.0 (litesvm's kit major) so there is a single kit in the tree
- tsconfig: bump typescript to ^5 and lib to es2022+dom (kit's types), add
  @types/node; the suite is now type-clean under tsc --noEmit
- LiteSVM()'s standard runtime bundles Token-2022, so no fixture load is needed
  beyond the program .so
@MarkFeder
MarkFeder force-pushed the tokens-token-2022-mint-close-authority-pinocchio branch from cd5cf69 to 3a8f9e7 Compare July 21, 2026 22:12
@MarkFeder
MarkFeder requested a review from dev-jodee July 21, 2026 22:18

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

couple more things :) we're getting close ! (also apply them to all the other prs too when it makes sense)

Comment thread tokens/token-2022/mint-close-authority/pinocchio/tests/test.ts Outdated
…al Rent

Address @dev-jodee review feedback on solana-foundation#624:

- program: now that tests run on litesvm (not bankrun), compute rent with
  Rent::get()?.try_minimum_balance() instead of the integer-math workaround
  that avoided the f64 opcode the old bankrun VM rejected.
- test: source program ids and the rent sysvar from the official packages
  (@solana-program/token-2022, @solana-program/system, @solana/sysvars) and
  decode the mint + its MintCloseAuthority extension with getMintDecoder()
  instead of hand-rolled address constants and raw byte offsets.
- tsconfig: switch to moduleResolution 'bundler' so the packages' subpath
  exports type-check.
MarkFeder added a commit to MarkFeder/program-examples that referenced this pull request Jul 22, 2026
Mirror the solana-foundation#624 review refinements here:

- program: compute rent with Rent::get()?.try_minimum_balance() now that the
  tests run on litesvm instead of bankrun.
- test: source the program ids and rent sysvar from the official packages
  (@solana-program/token-2022, @solana-program/system, @solana/sysvars) and
  decode the mint + its NonTransferable extension with getMintDecoder()
  instead of hand-rolled address constants and raw byte offsets.
- tsconfig: moduleResolution 'bundler' so the packages' subpath exports
  type-check.
@MarkFeder
MarkFeder requested a review from dev-jodee July 22, 2026 22:10
@dev-jodee
dev-jodee merged commit fded009 into solana-foundation:main Jul 23, 2026
25 checks passed
@dev-jodee

Copy link
Copy Markdown
Collaborator

thanks for the work @MarkFeder, can you do the pr to fix the previous one we already merged with the feedback from this one please :)

dev-jodee pushed a commit that referenced this pull request Jul 23, 2026
* add pinocchio token-2022 non-transferable example

* token-2022 non-transferable pinocchio: make bankrun test actually run

Move the async bankrun setup out of the `describe` callback and into a
`before` hook so Mocha collects the `it` block (an async `describe` body
registers tests after the suite is already collected, so nothing ran).

With the test now executing, replace `Rent::try_minimum_balance` with the
integer rent formula: its floating-point exemption-threshold path emits an
opcode the bankrun VM rejects ("unsupported BPF instruction"). Matches the
create-token example (#599).

* token-2022 non-transferable pinocchio: migrate test to kit + litesvm

Apply the kit + litesvm template from the mint-close-authority example:
build and sign the transaction with @solana/kit and run it on litesvm,
dropping @solana/web3.js and solana-bankrun entirely.

- deps: drop @solana/web3.js + solana-bankrun, add litesvm; pin @solana/kit
  to ^6.10.0 (litesvm's kit major) so there is a single kit in the tree
- tsconfig: bump typescript to ^5 and lib to es2022+dom (kit's types), add
  @types/node; the suite is type-clean under tsc --noEmit
- LiteSVM's standard runtime bundles Token-2022, so no fixture load is needed
  beyond the program .so

* token-2022 non-transferable pinocchio: use official packages + real Rent

Mirror the #624 review refinements here:

- program: compute rent with Rent::get()?.try_minimum_balance() now that the
  tests run on litesvm instead of bankrun.
- test: source the program ids and rent sysvar from the official packages
  (@solana-program/token-2022, @solana-program/system, @solana/sysvars) and
  decode the mint + its NonTransferable extension with getMintDecoder()
  instead of hand-rolled address constants and raw byte offsets.
- tsconfig: moduleResolution 'bundler' so the packages' subpath exports
  type-check.

---------

Co-authored-by: MarkFeder <5670736+MarkFeder@users.noreply.github.com>
@MarkFeder

Copy link
Copy Markdown
Contributor Author

Thanks for merging @dev-jodee! 🙏 Opened the follow-up you asked for — #641 brings the already-merged create-token example up to this same kit + litesvm + official-package template (real Rent::get(), @solana-program/token/@solana-program/system, getProgramDerivedAddress, tsconfig moduleResolution: bundler). Verified locally: cargo build-sbf + ts-mocha → 2 passing, tsc/biome/fmt/clippy clean, frozen-lockfile OK, commit signed.

MarkFeder added a commit to MarkFeder/program-examples that referenced this pull request Jul 23, 2026
Applies dev-jodee's solana-foundation#624 review refinements on top of the kit + litesvm test:

- program: compute rent with Rent::get()?.try_minimum_balance(MINT_SIZE)?
  instead of the integer-math workaround (only needed to dodge the f64 opcode
  the old bankrun VM rejected; litesvm runs the real syscall).
- test: source the token, associated-token and system program ids from the
  official @solana-program/token and @solana-program/system packages, and read
  the minted amount with getTokenDecoder().decode(...).amount instead of a raw
  byte offset. Token Metadata has no official @solana-program client, so its id
  stays hand-rolled.
- tsconfig: moduleResolution bundler for the packages' subpath exports.

Verified locally: cargo build-sbf + ts-mocha -> 2 passing, tsc --noEmit and
biome clean, frozen-lockfile OK.
MarkFeder added a commit to MarkFeder/program-examples that referenced this pull request Jul 23, 2026
Applies dev-jodee's solana-foundation#624 review refinements on top of the kit + litesvm test:

- program: compute rent with Rent::get()?.try_minimum_balance(MINT_SIZE)?
  instead of the integer-math workaround (only needed to dodge the f64 opcode
  the old bankrun VM rejected; litesvm runs the real syscall).
- test: source the token, associated-token and system program ids from the
  official @solana-program/token and @solana-program/system packages, and read
  the minted amount with getTokenDecoder().decode(...).amount instead of a raw
  byte offset. Token Metadata has no official @solana-program client, so its id
  stays hand-rolled.
- tsconfig: moduleResolution bundler for the packages' subpath exports.

Verified locally: cargo build-sbf + ts-mocha -> 2 passing, tsc --noEmit and
biome clean, frozen-lockfile OK.
MarkFeder added a commit to MarkFeder/program-examples that referenced this pull request Jul 23, 2026
Applies dev-jodee's solana-foundation#624 review refinements on top of the kit + litesvm test:

- program: compute rent with Rent::get()?.try_minimum_balance(..)? in both the
  Init (PDA account) and Create (mint) instructions, instead of the integer-math
  workaround (only needed to dodge the f64 opcode the old bankrun VM rejected;
  litesvm runs the real syscall).
- test: source the token, associated-token and system program ids from the
  official @solana-program/token and @solana-program/system packages, and read
  the minted amount with getTokenDecoder().decode(...).amount instead of a raw
  byte offset. Token Metadata has no official @solana-program client, so its id
  stays hand-rolled.
- tsconfig: moduleResolution bundler for the packages' subpath exports.

Verified locally: cargo build-sbf + ts-mocha -> 3 passing, tsc --noEmit and
biome clean, frozen-lockfile OK.
dev-jodee pushed a commit that referenced this pull request Jul 23, 2026
…ackages + real Rent (#641)

Applies the kit + litesvm + official-package treatment from the token-2022
mint-close-authority example (#624) to the already-merged create-token example:

- program: compute rent with Rent::get()?.try_minimum_balance(Mint::LEN)?
  instead of the integer-math workaround (that was only needed to dodge the
  f64 opcode the old bankrun VM rejected; litesvm runs the real syscall).
- test: drop @solana/web3.js + solana-bankrun; build and sign transactions
  with @solana/kit and run them on litesvm. Program ids come from the official
  @solana-program/token and @solana-program/system packages, and the metadata
  PDA is derived with getProgramDerivedAddress. Token Metadata has no official
  @solana-program client, so its id stays hand-rolled.
- tsconfig: es2022 + dom, moduleResolution bundler for the packages' subpath
  exports; pin @solana-program/*@^0.12/^0.14 + @solana/kit@^6.10 to stay on the
  single kit that litesvm@1.3 requires.

Verified locally: cargo build-sbf + ts-mocha -> 2 passing, tsc --noEmit and
biome clean, frozen-lockfile OK.

Co-authored-by: MarkFeder <5670736+MarkFeder@users.noreply.github.com>
MarkFeder added a commit to MarkFeder/program-examples that referenced this pull request Jul 23, 2026
Applies dev-jodee's solana-foundation#624 review refinements on top of the kit + litesvm test:

- program: compute rent with Rent::get()?.try_minimum_balance(MINT_SIZE)? in
  both the create-collection and mint-nft instructions, instead of the
  integer-math workaround (only needed to dodge the f64 opcode the old bankrun
  VM rejected; litesvm runs the real syscall).
- test: source the token, associated-token and system program ids and the
  instructions sysvar from the official @solana-program/token,
  @solana-program/system and @solana/sysvars packages, and read the minted
  amount with getTokenDecoder().decode(...).amount instead of a raw byte offset.
  Token Metadata has no official @solana-program client, so its id stays
  hand-rolled.
- tsconfig: moduleResolution bundler for the packages' subpath exports.

Verified locally: cargo build-sbf + ts-mocha -> 3 passing, tsc --noEmit and
biome clean, frozen-lockfile OK.
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