Code Generation
Generate typed TypeScript clients from Anchor IDLs or Solidity ABIs.
Code Generation
Polyq generates TypeScript clients from your contract schemas. Supports both Anchor IDLs (Solana) and Solidity ABIs (EVM).
Quick Start
# Auto-detect chain and generate from all schemas
npx polyq codegen
# Specify a single schema
npx polyq codegen --idl target/idl/my_program.json
# Custom output directory
npx polyq codegen --out src/generated
# Watch mode — auto-build and regenerate on source changes
npx polyq codegen --watch SVM Output (Anchor IDL)
For each Anchor program, Polyq generates:
generated/my-program/
types.ts # TypeScript interfaces from IDL type definitions
pda.ts # PDA derivation helpers (findProgramAddressSync)
instructions.ts # Instruction builders with Borsh serialization
accounts.ts # Account fetchers with Borsh deserialization
errors.ts # Error enum and lookup function
index.ts # Barrel export Types
// generated/my-program/types.ts
export interface PoolState {
authority: PublicKey
tokenMint: PublicKey
totalDeposits: bigint
isActive: boolean
} PDA Helpers
// generated/my-program/pda.ts
export function derivePoolState(
tokenMint: PublicKey,
programId: PublicKey = PROGRAM_ID,
): [PublicKey, number] {
return PublicKey.findProgramAddressSync(
[Buffer.from('pool'), tokenMint.toBuffer()],
programId,
)
} Instructions (with Borsh Serialization)
// generated/my-program/instructions.ts
export function createSwapInstruction(
accounts: SwapAccounts,
args: SwapArgs,
): TransactionInstruction {
const keys = [/* typed account metas */]
const argsLayout = borsh.struct([
borsh.u64('amountIn'),
borsh.u64('minimumAmountOut'),
])
const argsBuffer = Buffer.alloc(10240)
const argsLen = argsLayout.encode(args, argsBuffer)
const data = Buffer.concat([discriminator, argsBuffer.subarray(0, argsLen)])
return new TransactionInstruction({ keys, programId: PROGRAM_ID, data })
} Account Fetchers (with Borsh Deserialization)
// generated/my-program/accounts.ts
export async function fetchPoolState(
connection: Connection,
address: PublicKey,
): Promise<PoolState | null> {
const accountInfo = await connection.getAccountInfo(address)
if (!accountInfo) return null
// Discriminator check + Borsh decode
return poolStateAccountLayout.decode(accountInfo.data.subarray(8))
} EVM Output (Solidity ABI)
For each contract, Polyq generates:
generated/my-contract/
contract.ts # ABI const + typed address map
types.ts # Function input args + return types
events.ts # Event types
errors.ts # Custom error types
index.ts # Barrel export Contract ABI Export
// generated/my-contract/contract.ts
export const MY_CONTRACT_ABI = [/* full ABI */] as const
export type MyContractAbi = typeof MY_CONTRACT_ABI
export const MY_CONTRACT_ADDRESS: Record<string, `0x${string}`> = {
// localhost: '0x...',
// sepolia: '0x...',
} Typed Function Args + Returns
// generated/my-contract/types.ts
export interface SwapArgs {
to: `0x${string}`
amountIn: bigint
amountOut: bigint
route: `0x${string}`
}
export interface BalanceOfReturn {
value: bigint
} Event Types
// generated/my-contract/events.ts
export interface TransferEvent {
from: `0x${string}`
to: `0x${string}`
amount: bigint
} Watch Mode
polyq codegen --watch watches both source files and artifact directories:
Source files (.rs for Solana, .sol for EVM):
- Detects change in
programs/**/*.rsorsrc/**/*.sol - Runs
anchor buildorforge buildautomatically - Regenerates TypeScript after build completes
Artifact directory (target/idl/ or out/):
- Detects direct changes to IDL/ABI JSON files
- Regenerates TypeScript immediately (no rebuild)
Programmatic API
import { generateFromSchema } from 'polyq/codegen'
// Auto-detect chain
generateFromSchema('target/idl/my_program.json', 'generated/')
// Explicit chain
generateFromSchema('out/MyContract.sol/MyContract.json', 'generated/', undefined, 'evm') // SVM-specific (backwards compatible)
import { generateFromIdl } from 'polyq/codegen'
generateFromIdl('target/idl/my_program.json', 'generated/')