Skip to content

Verify Token-2022 settlement against the relevant test suite wholistically - #121

Open
kaze-cow wants to merge 1 commit into
mainfrom
kaze/sc-153-token-2022-tests
Open

Verify Token-2022 settlement against the relevant test suite wholistically#121
kaze-cow wants to merge 1 commit into
mainfrom
kaze/sc-153-token-2022-tests

Conversation

@kaze-cow

@kaze-cow kaze-cow commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Add a macro to run any token-moving tests against both SPL token and Token 2022.

Stacked on #120.

Approach

The Token-2022 paths in #120 are worth little if they only hold for the handful of cases someone thought to write twice. So rather than a parallel Token-2022 suite, this reruns the suite that already exists against the second program.

common::also_under_token_2022!(some_test) sits in front of a test and generates some_test_token_2022, which runs the same body with Token-2022 as the thread-local active program:

common::also_under_token_2022!(settles_a_single_order);
#[test]
fn settles_a_single_order() { .. }

The body of the function can be used as is. The token helpers in tests/common/token.rs build against the program that owns the account they are handed (token::program_of), and payer_signed_tx / signed_tx repoint the legacy program id in every instruction they assemble.

A regular macro with pastey was used instead of a proc_macro to reduce the weight and maintenance that comes with needing to write an entire program to execute a macro. The only major downside I see with this approach is that the test name needs to be repeated in the macro parameters.

The max_buffers* tests were also included in this. In the case of CreateBuffers, we hit the CPI token account limit, so the limit is actually lower there.

Compute cost

bench-report.json gains readings for every generated test. Token-2022 costs more per instruction, as expected from the longer accounts and the extension-aware transfer path — e.g. settle/settles_multiple_orders 23,257 → 27,350 CU, create_buffers/happy_path_creates_initialized_buffer_token_account 10,361 → 11,695 CU. Transaction bytes and account counts are identical between the two.

Verification

Verify the approach. Confirm new tests are generating benchmarks and behavior as expected.

There may be cases where other non-generated tests are needed specific to token2022. If so, please say!

🤖 Generated with Claude Code

@linear-code

linear-code Bot commented Aug 26, 2026

Copy link
Copy Markdown

SC-153

@kaze-cow
kaze-cow changed the base branch from kaze/sc-153-token-2022-program to kaze/sc-153-token-2022-settle August 28, 2026 07:22
@kaze-cow
kaze-cow force-pushed the kaze/sc-153-token-2022-tests branch from b667e36 to 9f512c4 Compare August 28, 2026 07:22
@kaze-cow
kaze-cow force-pushed the kaze/sc-153-token-2022-settle branch from 7af181d to 35bb3fe Compare August 28, 2026 08:29
@kaze-cow
kaze-cow force-pushed the kaze/sc-153-token-2022-tests branch from 9f512c4 to 223bd0d Compare August 28, 2026 08:30
@kaze-cow

kaze-cow commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

add an extension for metadata to ensure we have full token2022 situational coverage

also double check that test suggestions from base pr have been addressed.

@kaze-cow
kaze-cow force-pushed the kaze/sc-153-token-2022-tests branch from 223bd0d to 2e22761 Compare September 4, 2026 08:04
@kaze-cow
kaze-cow changed the base branch from kaze/sc-153-token-2022-settle to kaze/sc-153-token-2022-program September 4, 2026 08:04
@kaze-cow
kaze-cow force-pushed the kaze/sc-153-token-2022-tests branch from 3351e6d to c47c7f2 Compare September 4, 2026 14:11
@kaze-cow
kaze-cow marked this pull request as ready for review September 4, 2026 14:55
@kaze-cow
kaze-cow requested a review from a team as a code owner September 4, 2026 14:55
@kaze-cow kaze-cow changed the title Verify Token-2022 settlement against the whole existing suite Verify Token-2022 settlement against the relevant test suite wholistically Sep 4, 2026
kaze-cow added a commit that referenced this pull request Sep 7, 2026
Adds the most basic level of Token-2022 support which widens the
accepted token programs and focuses on dealing with the edge cases of
`CreateBuffers` and `ReclaimBuffers`.

## What changes

`CreateBuffer` and `ReclaimBuffer` each take a `token_program` account
and, until now, rejected anything that wasn't the legacy SPL Token
program. They now accept Token-2022 too and issue all of their CPIs —
`InitializeAccount3`, `CloseAccount` — against whichever of the two they
were handed.

Token2022 accounts may have dynamic length. Previously, a buffer could
only hold 165 bytes (the length of SPL token account), but now it is now
allocated at the length its mint actually needs. `GetAccountDataSize` is
used to verify the length of the token required before creating it.

For `CreateBuffers` creation of token accounts, We looked into the
possibility of making the account longer than it needs to be and then
shrinking it, but this doesn't work well because a extra CPI call is
necessary to shrink the account back down to the correct size. But this
requires a CPI call to `Reallocate` on the token program, and that is
more expensive than just getting hte length.

There is an early return on `token_account_len` which allows for
skipping the `GetTokenAccountLength` CPI call. We originally did the
early return if the Mint size was th ebase size (the logic being that a
base size mint is either a canonical SPL token *OR* a token2022 mint
with no required extensions), but since the vast majority of token2022
mints *DO* actually have extensions, there is very little benefit in
having this broader check. So we decided to only do the early return for
SPL tokens.

## Library Handling Changes

`spl-token-2022-interface = "3"`, a library [published by
anza-team](https://crates.io/crates/spl-token-2022-interface), and
`pinocchio-token-2022 = "0.4"`, another library [published by
anza-team](https://crates.io/crates/pinocchio-token-2022), are added for
hopefully obvious reasons.

~~`spl-token-interface` was removed from the settlement program because
it is no longer needed (later it will also be removed from test-cli,
eliminating it as a direct dependency from the repo).~~ ended up being
re-added after adding new tests because apparently a lot of mint
creation utilities need to be recreated without it.

`pinocchio-token` is bumped to `0.7` in order to gain access to the
`invoke_with_unverified_program(token_program)` instruction builder
function (prior to this release, there was no way to specify an
alternative program). Its also the version that `pinnochio-token-2022`
transitively depends on.

Most of the functions in both `pinnochio-token` and
`pinnochio-token-2022` are close to identical. For now most of the
interfaces continue to use `pinnochio-token` because we never actually
work with token 2022 tokens directly and the interfaces usually have
slightly less dependencies (ex. not specifying the extension
information).

## Out of Scope

`BeginSettle` / `FinalizeSettle` require a different methodology to
support simultaneous settlement from both token programs, so those
follow in #128.

`test-cli` is covered separately in #134 .

The integration tests for both this PR and #128 are in #121, as we want
to expand coverage with 2022 across as many tests as possible.

At this time, only one token program can be supplied to the buffer
functions. Two separate calls to `CreateBuffers` is required if it is
necessary to create buffers for tokens on two separate prgorams.

## Compute cost

`bench-report.json` is regenerated. The buffer instructions shift by
roughly +0.2% to +0.4% from the added dispatch
(`reclaim_buffer/max_buffers_in_one_instruction` 136,501 → 137,046 is
the largest); the one- and two-unit drift on the unrelated settle and
transfer-authority lines is codegen, not behaviour.

## Test Plan

Verify the methodology. In particular, it would be good to verify the
library dependency status as described above, because it is a bit
awckward.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Federico Giacon <58218759+fedgiac@users.noreply.github.com>
Base automatically changed from kaze/sc-153-token-2022-program to main September 7, 2026 13:42
The Token-2022 paths are worth little if they only hold for the handful of
cases someone thought to write twice, so instead of a parallel suite this
reruns the suite that already exists against the second program.

`common::also_under_token_2022!(some_test)` sits in front of a test and
generates `some_test_token_2022`, which runs the same body with Token-2022 as
the thread-local active program. Nothing in the body changes: the token helpers
build against the program that owns the account they are handed, and
`payer_signed_tx` / `signed_tx` repoint the legacy program id in every
instruction they assemble. 19 tests are covered this way, across buffer
creation and reclamation -- error paths as much as happy ones.

`CreateBuffer` and `ReclaimBuffer` name their token program as an account, so
repointing reaches them. `common::buffer::ensure_buffer_exists` reads the
program off the mint instead of taking it as a parameter, a buffer being a
token account of its mint and so bound to the mint's own program; that also
lets one test build buffers under both programs at once, which is what
`ensure_buffer_exists_for` used to be for.

Naming the test in front of it, rather than wrapping the body, keeps the
indentation and makes a stale name a compile error instead of a test that
quietly stopped being generated. A test that can only hold under one program
goes without and says why -- the one pinned to the legacy native mint.

Three tests are Token-2022-only, covering what has no legacy analogue -- a
buffer for a mint with a `TransferFeeConfig`, which needs a `TransferFeeAmount`
on every account holding it and so must be longer than the base layout:

- `creates_buffer_sized_for_a_mint_with_extensions`
- `recreating_an_extension_mint_buffer_is_idempotent`
- `reclaims_a_buffer_sized_for_an_extension_mint`

`bench-report.json` gains the CU, account, and transaction-byte readings for
every generated test that is benched.

The settlement pair is deliberately left out. `BeginSettle` and `FinalizeSettle`
name their token programs by value rather than by account, so rerunning their
tests needs the slot handling from #128 and belongs on that branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@kaze-cow
kaze-cow force-pushed the kaze/sc-153-token-2022-tests branch from f451dd5 to aa656ca Compare September 7, 2026 14:32
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