Skip to content

Add Token-2022 memo-transfer Pinocchio example - #700

Open
MarkFeder wants to merge 2 commits into
solana-foundation:mainfrom
MarkFeder:tokens-token-2022-memo-transfer-pinocchio
Open

Add Token-2022 memo-transfer Pinocchio example#700
MarkFeder wants to merge 2 commits into
solana-foundation:mainfrom
MarkFeder:tokens-token-2022-memo-transfer-pinocchio

Conversation

@MarkFeder

Copy link
Copy Markdown
Contributor

What

Adds a Pinocchio implementation of the Token-2022 memo-transfer example. The example previously had only an anchor version (no native), so this is a fresh port. It follows the same kit + litesvm + official-@solana-program-packages template as the merged Token-2022 pinocchio examples, and builds directly on the account-extension shape from immutable-owner (#696).

How it works

The single instruction creates a Token-2022 token account with the MemoTransfer extension enabled. Once enabled, every transfer into the account must be preceded by a memo instruction, or the transfer fails.

The program hand-rolls three CPIs:

  1. CreateAccount — with space for the extension.
  2. InitializeAccount3 (variant 18): [18] + owner(32).
  3. EnableRequiredMemoTransfers — wrapper MemoTransferExtension (variant 30), sub Enable (0), empty data → [30, 0].

Unlike ImmutableOwner (which must be initialized before InitializeAccount), this extension is enabled after the account is initialized, and the enable must be signed by the account owner (here, the payer). The owner is the payer, so a single signature covers funding and the enable.

The extended token account is 171 bytes (base 165 + account-type byte 1 + a 5-byte MemoTransfer TLV entry whose value is a single bool).

Test

litesvm + @solana/kit. The test creates a plain Token-2022 mint client-side, invokes the program to create the token account, then decodes it with the official @solana-program/token-2022 codec and asserts:

  • the account is owned by Token-2022 and sized to 171 bytes,
  • the decoded mint and owner, and
  • the MemoTransfer extension's requireIncomingTransferMemos is true.
Token-2022 Memo Transfer (Pinocchio)
  ✔ Creates a Token-2022 token account with required memo transfers enabled
1 passing

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

Ports the memo-transfer Token-2022 example to Pinocchio (the anchor example
has no native sibling), matching the token-2022 pinocchio account-extension
template (kit + litesvm, official @solana-program packages, real Rent::get()).

The program hand-rolls three Token-2022 CPIs to create a token account with
the MemoTransfer extension enabled: it creates the account, runs
InitializeAccount3, then (unlike ImmutableOwner, which is pre-init) enables
required memo transfers via EnableRequiredMemoTransfers after initialization,
which the account owner signs. The owner is the payer. Account size is 171
bytes (a 1-byte MemoTransfer TLV value). The litesvm test decodes the account
with the official Token-2022 codec and asserts the requirement is enabled.
@MarkFeder
MarkFeder requested a review from dev-jodee as a code owner August 26, 2026 20:13
@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds a Pinocchio Token-2022 memo-transfer example that creates an extended token account and enables required incoming-transfer memos.

  • Adds the Pinocchio program, workspace configuration, package metadata, and build/deployment script.
  • Adds LiteSVM tests for account initialization, rejection without a memo, and successful transfer with a preceding memo.

Confidence Score: 5/5

The PR appears safe to merge.

The previously reported memo-enforcement coverage gap is fixed by explicit rejection and successful-transfer cases, and no blocking failure remains.

Important Files Changed

Filename Overview
tokens/token-2022/memo-transfer/pinocchio/program/src/instructions/create_token_account.rs Creates, initializes, and enables the MemoTransfer extension on a correctly sized Token-2022 account using the payer as owner and signer.
tokens/token-2022/memo-transfer/pinocchio/program/src/processor.rs Dispatches the example’s single data-free instruction to the token-account creation handler.
tokens/token-2022/memo-transfer/pinocchio/tests/test.ts Verifies the extended account state and now covers both memo-less transfer rejection and memo-prefixed transfer success.
tokens/token-2022/memo-transfer/pinocchio/package.json Defines the example’s pinned-lockfile-compatible build and LiteSVM test environment.
Cargo.toml Registers the new Pinocchio program as a workspace member.

Reviews (2): Last reviewed commit: "token-2022 memo-transfer pinocchio: test..." | Re-trigger Greptile

}

// The extension was enabled by the post-init CPI.
assert.equal(memoTransfer.requireIncomingTransferMemos, true);

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.

P2 Memo enforcement remains untested

The test only checks that requireIncomingTransferMemos is stored as true; it never verifies that an incoming transfer without a memo fails and one preceded by a memo succeeds. A regression in the example's advertised transfer behavior would therefore lack direct test coverage, so add both negative and positive transfer cases as in the Anchor example.

Knowledge Base Used: Token-2022 extension patterns

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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.

Good call — addressed in 80a66dac. Added a second test that funds a source account and verifies a TransferChecked into the memo-required account is rejected without a preceding memo and succeeds when preceded by one (SPL Memo program, which LiteSVM bundles), then asserts the destination balance. Both tests pass.

Adds a behavior test alongside the extension-state assertion: it funds a
source account, then verifies a TransferChecked into the memo-required
account is rejected without a preceding memo and succeeds when preceded by
one (SPL Memo program, bundled by LiteSVM). Addresses review feedback that
the example's advertised transfer behavior was untested.
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