Skip to content

@thru/programs

View as Markdown

@thru/programs contains program-specific bindings for built-in Thru programs. It is published as one package with subpath exports for each program surface.

Terminal window
npm install @thru/programs @thru/sdk
ImportWhat it provides
@thru/programs/tokenToken program instruction builders, account parsers, address derivation, ABI builders, and formatting helpers.
@thru/programs/passkey-managerPasskey-manager instruction encoders, challenge helpers, account-context builders, derivation helpers, account parsers, and P-256/WebAuthn encoding utilities.
@thru/programs/multicallMulticall instruction encoding for batching calls to multiple programs in one instruction payload.
@thru/programs/ammAMM pool derivation, instruction builders, pool metadata parsing, swap quoting, constants, and generated ABI views.
@thru/programs/oracleRead-only Oracle helpers: feed address derivation, price and boolean feed parsing, update-event decoding, and program-error mapping.
@thru/programs/managerManager-program constants, managed-program address derivation, meta parsing, program-image validation, error decoding, and instruction builders.
@thru/programs/abi-managerABI-manager constants, official and external ABI address derivation, ABI account parsing, error decoding, and instruction builders.
@thru/programs/uploaderUploader-program constants, upload address derivation, upload meta parsing, and chunked upload instruction builders.
@thru/programs/deployProgram and ABI deploy, upgrade, and inspection workflows built on the manager, ABI-manager, and uploader programs.

There is no root runtime import. Import from the program subpath you need.

Use @thru/programs/token when you are creating token mints, creating token accounts, transferring or minting tokens, parsing token account state, or formatting raw token amounts.

import {
createTransferInstruction,
deriveTokenAccountAddress,
formatRawAmount,
parseTokenAccountData,
} from "@thru/programs/token";
const destination = deriveTokenAccountAddress(
ownerAddress,
mintAddress,
tokenProgramAddress
);
const instructionData = createTransferInstruction({
sourceAccountBytes,
destinationAccountBytes: destination.bytes,
amount: 1_000_000n,
});
const parsed = parseTokenAccountData(account);
const displayAmount = formatRawAmount(parsed.amount, 6);

Important token exports include:

  • createInitializeMintInstruction
  • createInitializeAccountInstruction
  • createMintToInstruction
  • createTransferInstruction
  • deriveMintAddress
  • deriveTokenAccountAddress
  • deriveWalletSeed
  • parseMintAccountData
  • parseTokenAccountData
  • formatRawAmount

Use @thru/programs/passkey-manager when you need to build passkey-managed wallet instructions, create validate challenges, derive wallet or credential lookup addresses, parse wallet state, or compose passkey-manager instruction bytes.

import {
buildAccountContext,
concatenateInstructions,
createValidateChallenge,
encodeTransferInstruction,
encodeValidateInstruction,
} from "@thru/programs/passkey-manager";
const accountContext = buildAccountContext({
feePayerAddress,
walletAddress,
readWriteAccounts: [destinationAddress],
});
const transfer = encodeTransferInstruction({
accountContext,
toAddress: destinationAddress,
amount: 1_000_000n,
});
const challenge = createValidateChallenge({
nonce,
accountAddresses: accountContext.accountAddresses,
instructionData: transfer,
});
const validate = encodeValidateInstruction({
accountContext,
authorityIndex,
challenge,
signature,
authenticatorData,
clientDataJSON,
});
const instructionData = concatenateInstructions(validate, transfer);

Important passkey-manager exports include:

  • encodeCreateInstruction
  • encodeValidateInstruction
  • encodeTransferInstruction
  • encodeInvokeInstruction
  • encodeAddAuthorityInstruction
  • encodeRemoveAuthorityInstruction
  • encodeRegisterCredentialInstruction
  • createValidateChallenge
  • deriveWalletAddress
  • deriveCredentialLookupAddress
  • buildAccountContext
  • parseWalletNonce
  • fetchWalletNonce
  • parseWalletAuthorities
  • P-256 and byte/base64 helpers used by WebAuthn flows

Use @thru/programs/multicall when you need to encode a single multicall payload that dispatches multiple inner program instructions.

import {
MULTICALL_PROGRAM_ADDRESS,
buildMulticallInstruction,
} from "@thru/programs/multicall";
const instructionData = buildMulticallInstruction([
{
programIdx: 2,
instructionData: tokenTransferInstruction,
},
{
programIdx: 5,
instructionData: anotherInstruction,
},
]);

Each call uses the target program’s account index in the transaction account context plus the already-encoded instruction bytes for that program.

Important multicall exports include:

  • MULTICALL_PROGRAM_ADDRESS
  • MULTICALL_PROGRAM_PUBKEY
  • buildMulticallInstruction
  • MulticallCall
  • Generated InstructionData, InstructionDataBuilder, MulticallArgs, MulticallArgsBuilder, and MulticallError ABI types

Use @thru/programs/amm when you need to derive AMM pool addresses, create pool/liquidity/swap instructions, parse AMM pool metadata, or quote exact-input swaps.

import {
AMM_PROGRAM_ADDRESS,
createSwapInstruction,
deriveAmmPoolAddresses,
parseAmmPoolMetadata,
quoteAmmSwapExactIn,
} from "@thru/programs/amm";
const pool = deriveAmmPoolAddresses(thru, {
ammProgramAddress: AMM_PROGRAM_ADDRESS,
mintAAddress,
mintBAddress,
});
const quote = quoteAmmSwapExactIn({
amountIn: 1_000_000n,
reserveIn,
reserveOut,
swapFeeBps: pool.swapFeeBps,
});
const swapInstruction = createSwapInstruction({
poolAccountBytes: pool.poolBytes,
userTransferAuthorityBytes,
userInputAccountBytes,
userOutputAccountBytes,
vaultInputAccountBytes,
vaultOutputAccountBytes,
lpMintAccountBytes,
tokenProgramAccountBytes,
amountIn: quote.amountIn,
});
const metadata = parseAmmPoolMetadata(poolAccount);

The AMM instruction builders return an async InstructionData function. Pass swapInstruction the same account lookup context used to build the transaction so account indexes are resolved against the final account list.

Important AMM exports include:

  • AMM_PROGRAM_ADDRESS
  • AMM_DEFAULT_SWAP_FEE_BPS
  • AMM_MAX_SWAP_FEE_BPS
  • AMM_MINIMUM_LIQUIDITY
  • AMM_POOL_METADATA_SIZE
  • sortAmmMints
  • deriveAmmPoolAddresses
  • deriveAmmLpMintSeed
  • createInitPoolInstruction
  • createAddLiquidityInstruction
  • createWithdrawLiquidityInstruction
  • createSwapInstruction
  • parseAmmPoolMetadata
  • quoteAmmSwapExactIn
  • Generated AMM instruction, event, metadata, and error ABI types

Use @thru/programs/oracle when your application reads Oracle feeds. The TypeScript surface is read-only: it derives feed addresses and decodes feed accounts, update events, and program errors.

import { createThruClient } from "@thru/sdk/client";
import {
deriveOracleFeedAddress,
parseOracleFeedAccount,
} from "@thru/programs/oracle";
const thru = createThruClient({ baseUrl: "https://rpc.alphanet.thru.org" });
const { address: feedAddress } = deriveOracleFeedAddress(
thru,
oracleProgramAddress,
"btc-usd:ticker@coinbase"
);
const feed = parseOracleFeedAccount(await thru.accounts.get(feedAddress));
if (feed.kind === "price") {
console.log(feed.common.feedName, feed.price, feed.exponent);
}

Prices and nanosecond timestamps are bigint. A price’s decimal value is price * 10^exponent, so keep integer arithmetic until you format for display. String seeds are UTF-8 encoded, truncated to 32 bytes, and zero-padded when shorter.

Important Oracle exports include:

  • deriveOracleFeedAddress
  • normalizeOracleFeedSeed
  • parseOracleFeedAccount
  • parseOracleEvent
  • oracleProgramErrorFromCode
  • OracleProgramError
  • Oracle feed-type and event-type constants, plus feed and event types

Use @thru/programs/manager, @thru/programs/abi-manager, and @thru/programs/uploader when you need individual instructions for the deployment system programs. Each entry point exports the canonical program address, address derivation, account parsers, error decoding, generated ABI types, raw instruction builders, and account-aware instruction callbacks that can be passed directly as instructionData when building a transaction with @thru/sdk.

import {
createUpgradeProgramInstruction,
deriveManagedProgramAddresses,
} from "@thru/programs/manager";
const addresses = deriveManagedProgramAddresses("nft");
const instructionData = createUpgradeProgramInstruction({
metaAccount: addresses.programMetaAccountBytes,
programAccount: addresses.programAccountBytes,
sourceBufferAccount: uploadBufferAddressBytes,
sourceSize: programBytes.length,
});
  • @thru/programs/manager manages seed-derived program accounts: MANAGER_PROGRAM_ADDRESS, deriveManagedProgramAddresses, parseManagerProgramMeta, validateManagerProgramImage, decodeManagerError, and create, upgrade, finalize, authority, pause, and destroy instruction builders.
  • @thru/programs/abi-manager manages official and external ABI accounts: ABI_MANAGER_PROGRAM_ADDRESS, deriveOfficialABIAddresses, deriveExternalABIAddresses, deriveProgramABIAddresses, parseABIAccount, parseABIMetaAccount, decodeABIManagerError, and the matching create, upgrade, finalize, and close instruction builders.
  • @thru/programs/uploader manages temporary bulk-upload accounts: UPLOADER_PROGRAM_ADDRESS, chunk-size constants, deriveUploadAddresses, parseUploaderProgramMeta, and buffer, write, finalize, and destroy instruction builders.

These modules encode single program calls. Multi-transaction upload, signing, retry, atomic deployment, and verification belong to the deployment workflow below.

Use @thru/programs/deploy to upload and manage program images and official ABIs through @thru/sdk. A combined program and ABI deploy or upgrade commits both artifacts in one multicall transaction.

import { readFile } from "node:fs/promises";
import { deploy, type DeployProgramResult } from "@thru/programs/deploy";
const result: DeployProgramResult = await deploy.deployProgram({
seed: "nft",
signer: {
address: signerAddress,
privateKey: process.env.THRU_PRIVATE_KEY!,
},
program: await readFile("./nft.bin"),
abi: await readFile("./nft.abi.yaml"),
onProgress: (event) => console.log(event.phase, event.status),
});
console.log(result.programAccountAddress, result.transactionSignature);

Create operations fail before uploading when a final account already exists; upgrade operations require open accounts controlled by the signer. inspectProgramDeployment runs the same ownership, authority, and relationship checks without submitting a transaction and reports program and ABI account pairs as missing, partial, or present.

Failures throw DeployError with a stable code of INVALID_INPUT, SIGNER_MISMATCH, TARGET_EXISTS, TARGET_NOT_FOUND, TARGET_FINALIZED, UPLOAD_CONFLICT, TRANSACTION_FAILED, VERIFICATION_FAILED, OUTCOME_UNKNOWN, or RPC_ERROR.

Program images must use managed image version 1 and end in an eight-byte zero trailer. ABI input must be valid, self-contained UTF-8 YAML, so prepare or flatten local path imports before publishing. The default upload chunk size is 30,720 bytes and may be set from 1,024 through 31,000 bytes.

Important deploy exports include:

  • deploy namespace plus deployProgram, deployProgramABI, upgradeProgram, and upgradeProgramABI
  • inspectProgramDeployment
  • DeployError and DeployErrorCode
  • Deployment request, result, progress-event, and upload-result types