Skip to content

feat: add transfer hook helpers to the JS client#1232

Open
dev-jodee wants to merge 2 commits into
solana-program:mainfrom
dev-jodee:feat/js-transfer-hook-helpers
Open

feat: add transfer hook helpers to the JS client#1232
dev-jodee wants to merge 2 commits into
solana-program:mainfrom
dev-jodee:feat/js-transfer-hook-helpers

Conversation

@dev-jodee

Copy link
Copy Markdown

Summary

Adds transfer hook support to the kit-based JS client as hand-written helpers in clients/js/src, porting the extra-account-meta resolution that previously only existed in clients/js-legacy. Closes #736.

  • Codecs: ExtraAccountMeta and ExtraAccountMetaAccountData (with an Args type for encoding), plus EXECUTE_DISCRIMINATOR.
  • Account and PDA helpers, following the generated client conventions: decodeExtraAccountMetaList, fetchExtraAccountMetaList, fetchMaybeExtraAccountMetaList and findExtraAccountMetasPda.
  • Resolution: resolveExtraAccountMeta covering every address config — literal key, hook-program PDA, pubkey data (from instruction or account data) and external-program PDA — and every seed type (literal, instruction data, account key, account data); getExtraAccountMetasForExecute and addExtraAccountMetasForExecute, including the legacy client's privilege de-escalation.
  • Instruction builders: getExecuteInstruction, getTransferCheckedWithTransferHookInstructionAsync and getTransferCheckedWithFeeAndTransferHookInstructionAsync, which fetch the mint, resolve the hook's extra accounts and append them (plus hook program and validation account) to the generated instruction. getTransferHookProgramId exposes the mint's active hook.

Behavior:

  • Mints without a transfer hook, or with an inert one (programId unset), build the plain transfer instruction with no extra RPC calls.
  • An active hook whose validation account does not exist throws SOLANA_ERROR__ACCOUNTS__ACCOUNT_NOT_FOUND, matching the Rust offchain helper. (The legacy JS client silently returns the instruction unchanged, which fails on-chain later.)
  • Reserved discriminators 3..127 and truncated seed configs are rejected with explicit errors. (The legacy JS client crashes on the former and mis-resolves the latter.)

Tests

  • test/extensions/transferHook/resolveExtraAccountMetas.test.ts — mock-RPC unit tests: validation account fetch/decode round-trip, every address-config discriminator including a chained external PDA, the with-fee builder, missing required account, missing validation account, invalid discriminator and truncated seed config.
  • test/extensions/transferHook/transferCheckedWithTransferHook.test.ts — e2e against the test validator's spl-transfer-hook-example fixture: the builder resolves a fixed extra account and a seed-derived PDA, the transfer succeeds with the hook executing, and a transfer without the forwarded hook accounts fails with SOLANA_ERROR__INSTRUCTION_ERROR__MISSING_ACCOUNT.

pnpm test (tsc + ava, 140 tests) passes against the local test validator; eslint and prettier clean.

Port the legacy client's extra-account-meta resolution to @solana/kit:
ExtraAccountMeta codecs, getExtraAccountMetaAddress, resolveExtraAccountMeta,
addExtraAccountMetasForExecute, and async transferChecked /
transferCheckedWithFee builders that resolve and append the accounts
required by a mint's transfer hook.

Closes solana-program#736
@dev-jodee dev-jodee marked this pull request as draft June 10, 2026 18:36

@amilz amilz left a comment

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.

Looking good. I'm just wondering if some of this should get generated from the idl (IDK...just has me thinking)

Comment thread clients/js/src/transferHook.ts
Comment thread clients/js/src/transferHook.ts Outdated
Comment thread clients/js/src/transferHook.ts Outdated
Comment thread clients/js/src/transferHook.ts Outdated
Comment thread clients/js/src/transferHook.ts Outdated
@dev-jodee dev-jodee marked this pull request as ready for review June 11, 2026 12:06
@dev-jodee

Copy link
Copy Markdown
Author

Looking good. I'm just wondering if some of this should get generated from the idl (IDK...just has me thinking)

Yeah, I'll post as a comment as well on the pr itself, but token-2022 is not the program that has the types ExtraAccountMetaList and Execute for Transfer Hook, it's https://github.com/solana-program/transfer-hook, so the idl isn't part of this repo. Choice was either to create a new package specific for transfer hook or to add it to the token-2022. I found the latter to make more sense and make it easier for devs, especially because transfer hook is part of 2022, but if that's not the preferred way, all good. We could also just add the ExtraAccountMetaList and Execute, to the IDL in this repo, but I'm not sure if it has precedence / if it's the style we want.

Use kit role helpers in roleFromFlags and deEscalateAccountMeta, thread
an optional FetchAccountConfig through all RPC-taking functions, expose
Execute instruction data codecs and getExecuteDiscriminatorBytes, let
the transfer builders' return types be inferred, and add a privilege
de-escalation test.
@dev-jodee dev-jodee requested a review from amilz June 11, 2026 13:05
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.

Improved Transfer Hook Support in t22 js library

2 participants