feat(token-2022/transfer-hook/counter): add pinocchio example - #710
feat(token-2022/transfer-hook/counter): add pinocchio example#710MarkFeder wants to merge 4 commits into
Conversation
Greptile SummaryThe PR adds a self-contained Pinocchio implementation of the Token-2022 counter transfer hook.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains; the current implementation resolves both previously reported counter-setup failures. Important Files Changed
Reviews (4): Last reviewed commit: "token-2022 transfer-hook counter: create..." | Re-trigger Greptile |
…figuring another mint
|
Audit follow-up from #714: every PDA this example creates has a publicly derivable address, and Fixed here too. PDA creation now goes through a Covered by pre-funding each derivable address with one lamport in the setup test before the creating instruction runs. |
|
@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 |
Adds a Pinocchio implementation of the Token-2022
countertransfer hook, alongside the existing Anchor one.What it does
Four instructions:
Initialize— creates a Token-2022 mint carrying theTransferHookextension pointed at this program. (The Anchor version creates the mint client-side; doing it in-program keeps the example self-contained and matches the siblinghello-worldPinocchio example.)InitializeExtraAccountMetaList— writes theExtraAccountMetaListPDA and creates the counter PDA.Execute— the transfer-hook interface entrypoint. Verifies the transfer, then increments and persists the count.There is no Pinocchio crate for Token-2022, so its instructions and TLV extension layout are built and parsed by hand (
token2022.rsis a small bounds-checked TLV reader, shared with thehello-worldexample).The ExtraAccountMetaList encoding
This is the first of my ports with a non-empty extra-account list. Rather than depend on the TLV encoder, the 51-byte layout is a documented constant:
This is validated end-to-end rather than by inspection: Token-2022 reads the list on-chain during the transfer, derives
[b"counter"]itself, and passes the resulting account toExecute. A wrong encoding fails the transfer.One deliberate difference from the Anchor version
The Anchor program computes the incremented count but never assigns it back (
counter_accountis notmut), so its counter reports1on every transfer and never actually advances. This port persists the new count, which is the behaviour the example is named for — covered by a test that transfers twice and asserts the counter reaches 2.Security checks on
ExecuteExecuteis a public entrypoint, so it does not trust the accounts it is handed:TransferHookextension must name this program (a mint hooked to a different program is mid-transfer too, and that program could otherwise CPI in),transferringflag must be set.The equivalent guarantees come from Anchor's
InterfaceAccount/seedsconstraints in the reference.I did not port Anchor's
token::authority = ownerconstraint: Token-2022 passes the transfer's authority, which may be a delegate, so that constraint would reject legitimate delegated transfers.Tests
8 LiteSVM tests, including the two negative cases above and a forged-source-account case. Verified locally:
tsc --noEmit,pnpm test,prettier --check,cargo fmt --check,cargo clippy -D warnings.