feat(allow-block-list-token): migrate frontend to @solana/kit - #689
feat(allow-block-list-token): migrate frontend to @solana/kit#689Harsh-H-Shah wants to merge 3 commits into
Conversation
Moves the webapp off @solana/web3.js + @solana/wallet-adapter-react onto @solana/kit + @solana/connector, matching the sibling kit examples (nft-meta-data-pointer, world-cup). Wallet connection goes through @solana/connector/react, RPC calls use kit's typed createSolanaRpc, and program interaction goes through a Codama-generated Kit-native client built from the Anchor IDL (scripts/generate-client.ts) instead of the @anchor-lang/core Program wrapper. Rebased the migration onto origin/main's already-merged abl-token fix (solana-foundation#672) rather than the stale program this branch forked from, since that fix changes tx_hook's client-facing account layout: transfers now need both the sender's and receiver's ab_wallet PDA (source first, then destination), not just the receiver's. useSendTokens resolves and appends both, in the order the program's get_extra_account_metas() expects. Verified: pnpm typecheck/build/lint/format:check all pass, anchor test passes (9 unit + 5 litesvm + 1 mocha, including the source-blocked regression test), no web3.js/wallet-adapter imports remain in src/, and anchor/ has zero diff from origin/main. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Greptile SummaryThe PR migrates the allow/block-list Token-2022 frontend from legacy Web3.js and wallet-adapter transaction APIs to Solana Kit, Connector, and a Codama-generated client.
Confidence Score: 5/5The PR appears safe to merge because no blocking failure remains in the eligible follow-up review scope. No blocking failure remains. Important Files Changed
Reviews (3): Last reviewed commit: "fix(allow-block-list-token): address rev..." | Re-trigger Greptile |
The root `pnpm run check` script runs prettier from the repo root, which only reads the root .prettierignore, not the app-level one - so the app-level ignore added for idl/abl_token.json (a raw copy of the anchor build output, regenerated on every `pnpm run generate-client`) had no effect on CI's root-level check. Mirrors the existing games/gacha/pinocchio/idl/ entry. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
amilz
left a comment
There was a problem hiding this comment.
@Harsh-H-Shah thank you! This is looking great. Drop some comments throughout. LMK if you have any questions!
- Any interest in adding some basic tests using the new TS Client?
tokens/token-2022/transfer-hook/allow-block-list-token/anchor/tests/basic.test.ts anchor/…/instructions/remove_wallet.rs- can you addwalletseeds?seeds = [AB_WALLET_SEED, wallet.key().as_ref()], bumpand regenerate idl/clients so thatgetRemoveWalletInstructionAsync({ authority, wallet })resolves both.
| "peerDependencies": { | ||
| "@solana/kit": "^6.10.0" | ||
| }, | ||
| "dependencies": { | ||
| "@solana/program-client-core": "^6.10.0" |
There was a problem hiding this comment.
can we use 7.1? I think there are some new hooks in the latest version.
| /** | ||
| * Derives kit's RPC + RPC-subscriptions clients from the active cluster's endpoint. Every | ||
| * part of the app that talks to a cluster should read from this hook so that switching | ||
| * clusters (via `setCluster`) moves them all together. | ||
| */ | ||
| export function useClusterRpc() { | ||
| const { cluster } = useCluster(); | ||
| return useMemo( | ||
| () => ({ | ||
| rpc: createSolanaRpc(cluster.endpoint), | ||
| rpcSubscriptions: createSolanaRpcSubscriptions(deriveWebsocketUrl(cluster.endpoint)), | ||
| }), | ||
| [cluster.endpoint], | ||
| ); | ||
| } |
There was a problem hiding this comment.
i think this duplicates useSolanaClient from connector
There was a problem hiding this comment.
Though this looks fine, i think you already have some helpers to clean this up. Something like
const { prepare } = useTransactionPreparer();
const { client } = useSolanaClient();
const prepared = await prepare(txMessage);
const signed = await signTransactionMessageWithSigners(prepared);
await client.sendAndConfirmTransaction(signed);There was a problem hiding this comment.
I think Connector has a lot of these hooks out of the box. Double check please (useGetBalance / useGetTokenAccounts / useGetSignatures vs connector's useBalance / useTokens / useTransactions), which share one cache (1 RPC round trip vs your 3).
I think can swap them and use invalidateSharedQuery(getBalanceQueryKey(…)) instead of the hardcoded key strings.
Also, check out https://github.com/anza-xyz/kit/tree/main/packages/react/src/query
There was a problem hiding this comment.
legacy bug, but should this be transferHookAuthority?
| transferHookAuthority: args.transferHookAuthority, |
| mutationKey: ['transfer-sol', { endpoint: connection.rpcEndpoint, address }], | ||
| mutationFn: async (input: { destination: PublicKey; amount: number }) => { | ||
| let signature: TransactionSignature = ''; | ||
| mutationKey: ['transfer-sol', { endpoint: cluster.endpoint, address }], |
There was a problem hiding this comment.
key is address but changed destination from address to signer. i think need to key off of signer.address or do some check that address === signer.address
| .requestAirdrop(address, lamports(BigInt(Math.round(amount * LAMPORTS_PER_SOL))), { | ||
| commitment: 'confirmed', | ||
| }) | ||
| .send(); |
There was a problem hiding this comment.
do we need confirmation logic?
| @@ -77,7 +77,9 @@ export function ClusterProvider({ children }: { children: ReactNode }) { | |||
| clusters: clusters.sort((a, b) => (a.name > b.name ? 1 : -1)), | |||
| addCluster: (cluster: SolanaCluster) => { | |||
There was a problem hiding this comment.
probably worth doing url validation somewhere (e.g., new URL(endpoint))
|
|
||
| // The local validator serves its websocket subscriptions on port 8900, not the HTTP | ||
| // RPC port (8899); every other cluster serves subscriptions on the same host as HTTP. | ||
| function deriveWebsocketUrl(endpoint: string): string { |
There was a problem hiding this comment.
i think you'll get this from connector via useSolanaClient
| mintInfo.decimals, | ||
| undefined, | ||
| TOKEN_2022_PROGRAM_ID, | ||
| const createAtaIx = await getCreateAssociatedTokenIdempotentInstructionAsync({ |
There was a problem hiding this comment.
token client has getMintToATAInstructionPlanAsync and getTransferToATAInstructionPlanAsync helpers so you don't need to manaully assemble each ix
- remove_wallet.rs: declare ab_wallet's PDA seeds (seeds = [AB_WALLET_SEED,
wallet.key()]) instead of requiring the caller to pre-derive and pass the
PDA directly, so getRemoveWalletInstructionAsync({ authority, wallet })
resolves it the same way getInitWalletInstructionAsync already does.
Also aligns config's seeds with the CONFIG_SEED constant, matching
init_wallet.rs. Regenerated the IDL/client and simplified the two
frontend callers (removeWallet, processBatchWallets) accordingly.
- Bump @solana/kit and @solana/program-client-core to ^7.1.0 (both the
app's own deps and the generated client's peerDependencies, via a new
dependencyVersions option on the codama renderVisitor call) and
@solana-program/token-2022 to ^0.15.0.
- cluster-data-access.tsx: drop useClusterRpc/deriveWebsocketUrl in favor
of @solana/connector's useSolanaClient across every consumer, and fix
addCluster's endpoint validation, which silently accepted any string -
createSolanaRpc doesn't parse its endpoint eagerly despite a comment
claiming otherwise. new URL(endpoint) is the actual check.
- use-send-instruction.ts: adopt @solana/connector's useTransactionPreparer
for blockhash + simulation-derived compute unit limit, sourcing
rpc/rpcSubscriptions for the send-and-confirm step from useSolanaClient
instead of the removed custom hook. (client.sendAndConfirmTransaction,
suggested in review, doesn't actually exist in the installed - and
latest published - @solana/connector@0.2.6, despite one JSDoc example;
kept sendAndConfirmTransactionFactory from kit for that step.)
- account-data-access.tsx: useSendTokens now resolves the transfer-hook's
extra accounts via @solana-program/token-2022's
getTransferCheckedWithTransferHookInstructionAsync (reads the mint's
on-chain extra-account-metas list) instead of hardcoding this program's
ab_wallet PDA convention client-side. useRequestAirdrop now uses kit's
airdropFactory, which confirms the airdrop instead of returning
immediately after requesting it. useTransferSol now checks
signer.address against the viewed account instead of silently signing
with a possibly-different connected wallet than the page's address.
(useGetBalance/useGetTokenAccounts/useGetSignatures stay on a
cluster-scoped RPC call, not connector's useBalance/useTokens/
useTransactions - those hooks are scoped to the connected wallet only
and don't take an address, so they can't back the generic
/account/[address] page, which needs to read arbitrary addresses.)
- abl-token-data-access.tsx: fixed transferHookAuthority being set to
mintAuthority instead of the form's own transferHookAuthority field (a
legacy bug predating this migration). mintTo now uses
getMintToATAInstructionPlanAsync + flattenInstructionPlan instead of
manually assembling the create-ATA and mint-to instructions.
- Added anchor/tests/basic.test.ts: LiteSVM-backed tests exercising the
generated Kit client directly (init_config, init_wallet, the new
seeds-based remove_wallet, and an authority-mismatch rejection case).
This project's `anchor test` has no local-validator step to test
against - its Anchor.toml [scripts] test command fully replaces
Anchor's normal build+validator+deploy flow - so a real RPC connection
isn't available; LiteSVM gives the TS client something real to run
against without one. The old placeholder test never actually exercised
anything.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Thanks so much for the thorough review — really appreciate you taking the time to go through this in detail. I've pushed a commit addressing everything. |
Summary
@solana/web3.js+@solana/wallet-adapter-reactonto@solana/kit+@solana/connector, matching the sibling kit examples (nft-meta-data-pointer, world-cup).@solana/connector/react; program interaction via a Codama-generated Kit-native client built from the Anchor IDL (scripts/generate-client.ts) instead of the@anchor-lang/coreProgramwrapper.main's already-merged#672fix rather than the stale program my branch originally forked from — that fix changestx_hook's client-facing account layout (both sender's and receiver'sab_walletPDA now required, not just the receiver's).useSendTokensnow resolves and appends both, in the order the program expects.Test plan
pnpm run typecheck— cleanpnpm run build— cleanpnpm run lint— clean (one pre-existing warning, unrelated to this PR)pnpm run format:check— clean (addedidl/andsrc/generated/to.prettierignore)pnpm run anchor-test— 9 unit + 5 litesvm + 1 mocha tests passing, including thesource_blocked_is_always_rejectedregression test@solana/web3.js/wallet-adapterimports remain insrc/anchor/has zero diff frommain— program untouched