From 45453030db74080e236ab41ad916760e71273c27 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Wed, 16 Sep 2026 17:29:25 -0400 Subject: [PATCH] fix(clients): default Token-2022-only instructions to the right program Codama defaults any account named `tokenProgram` to the legacy SPL Token program. Three bridge instructions declare theirs as `Program` onchain, so Anchor rejects that default and the instruction fails for any caller who omits the account. Pins the default through a generator visitor rather than editing the generated files, so it survives regeneration. The `bridge_spl` instructions keep the legacy default: they take `Interface` and accept either program. Co-authored-by: Cursor --- .../instructions/bridgeWrappedToken.ts | 4 ++-- .../bridgeWrappedTokenWithBufferedCall.ts | 4 ++-- .../generated/instructions/wrapToken.ts | 4 ++-- .../commands/sol/generate-client.handler.ts | 22 +++++++++++++++++++ 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/clients/ts/src/bridge/generated/instructions/bridgeWrappedToken.ts b/clients/ts/src/bridge/generated/instructions/bridgeWrappedToken.ts index ae0ac541..35e4b791 100644 --- a/clients/ts/src/bridge/generated/instructions/bridgeWrappedToken.ts +++ b/clients/ts/src/bridge/generated/instructions/bridgeWrappedToken.ts @@ -67,7 +67,7 @@ export type BridgeWrappedTokenInstruction< TAccountOutgoingMessage extends string | AccountMeta = string, TAccountTokenProgram extends | string - | AccountMeta = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', + | AccountMeta = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb', TAccountSystemProgram extends | string | AccountMeta = '11111111111111111111111111111111', @@ -285,7 +285,7 @@ export function getBridgeWrappedTokenInstruction< // Resolve default values. if (!accounts.tokenProgram.value) { accounts.tokenProgram.value = - 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' as Address<'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'>; + 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb' as Address<'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'>; } if (!accounts.systemProgram.value) { accounts.systemProgram.value = diff --git a/clients/ts/src/bridge/generated/instructions/bridgeWrappedTokenWithBufferedCall.ts b/clients/ts/src/bridge/generated/instructions/bridgeWrappedTokenWithBufferedCall.ts index 0443c65b..52898ed0 100644 --- a/clients/ts/src/bridge/generated/instructions/bridgeWrappedTokenWithBufferedCall.ts +++ b/clients/ts/src/bridge/generated/instructions/bridgeWrappedTokenWithBufferedCall.ts @@ -58,7 +58,7 @@ export type BridgeWrappedTokenWithBufferedCallInstruction< TAccountOutgoingMessage extends string | AccountMeta = string, TAccountTokenProgram extends | string - | AccountMeta = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', + | AccountMeta = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb', TAccountSystemProgram extends | string | AccountMeta = '11111111111111111111111111111111', @@ -294,7 +294,7 @@ export function getBridgeWrappedTokenWithBufferedCallInstruction< // Resolve default values. if (!accounts.tokenProgram.value) { accounts.tokenProgram.value = - 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' as Address<'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'>; + 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb' as Address<'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'>; } if (!accounts.systemProgram.value) { accounts.systemProgram.value = diff --git a/clients/ts/src/bridge/generated/instructions/wrapToken.ts b/clients/ts/src/bridge/generated/instructions/wrapToken.ts index bb55c8cd..38fd5f27 100644 --- a/clients/ts/src/bridge/generated/instructions/wrapToken.ts +++ b/clients/ts/src/bridge/generated/instructions/wrapToken.ts @@ -58,7 +58,7 @@ export type WrapTokenInstruction< TAccountOutgoingMessage extends string | AccountMeta = string, TAccountTokenProgram extends | string - | AccountMeta = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', + | AccountMeta = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb', TAccountSystemProgram extends | string | AccountMeta = '11111111111111111111111111111111', @@ -284,7 +284,7 @@ export function getWrapTokenInstruction< // Resolve default values. if (!accounts.tokenProgram.value) { accounts.tokenProgram.value = - 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' as Address<'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'>; + 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb' as Address<'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'>; } if (!accounts.systemProgram.value) { accounts.systemProgram.value = diff --git a/scripts/src/commands/sol/generate-client.handler.ts b/scripts/src/commands/sol/generate-client.handler.ts index d2149045..15eff959 100644 --- a/scripts/src/commands/sol/generate-client.handler.ts +++ b/scripts/src/commands/sol/generate-client.handler.ts @@ -8,6 +8,18 @@ import { z } from "zod"; import { logger } from "@internal/logger"; import { findGitRoot } from "@internal/utils"; +const TOKEN_2022_PROGRAM_ADDRESS = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"; + +// Bridge instructions whose `token_program` is declared `Program` onchain. Codama +// defaults any account named `tokenProgram` to the legacy SPL Token program, which Anchor rejects +// for these, so the default has to be pinned. The `bridge_spl` instructions are intentionally +// absent: they take `Interface` and accept either program. +const TOKEN_2022_ONLY_INSTRUCTIONS = [ + "wrapToken", + "bridgeWrappedToken", + "bridgeWrappedTokenWithBufferedCall", +]; + export const argsSchema = z.object({ program: z .enum(["bridge", "base-relayer"], { @@ -43,6 +55,16 @@ export async function handleGenerateClient( const idl = rootNodeFromAnchor(require(idlPath)); const codama = c.createFromRoot(idl); + codama.update( + c.setInstructionAccountDefaultValuesVisitor( + TOKEN_2022_ONLY_INSTRUCTIONS.map((instruction) => ({ + instruction, + account: "tokenProgram", + defaultValue: c.publicKeyValueNode(TOKEN_2022_PROGRAM_ADDRESS), + })) + ) + ); + logger.info("Rendering TypeScript client..."); codama.accept(renderJavaScriptVisitor(clientOutputPath));