Typescript SDK
Developers can use the published @myth-finance/bench-sdk package to read and interact with Bench contracts programmatically. It wraps typed clients generated from the ABI specs, and takes care of transaction group composition, asset and box references, minimum balance payments and state decoding.
The SDK has complete support for all operations that the contracts support: reading the directory and bench states, buying, creating and managing benches, as well as fee admin and registry admin operations.
npm install @myth-finance/bench-sdk @algorandfoundation/algokit-utils algosdk p-map
Peer dependencies: @algorandfoundation/algokit-utils ^9, algosdk ≥ 3.4, p-map. Both an ESM and a CommonJS build are published.
Clients
import { AlgorandClient } from "@algorandfoundation/algokit-utils";
import { BenchRegistryClient, BenchInstanceClient } from "@myth-finance/bench-sdk";
const algorand = AlgorandClient.fromConfig({
algodConfig: { server: "https://mainnet-api.4160.nodely.dev", port: 443, token: "" },
});
const registryAppId = 3194114544n; // mainnet, see Deployments
const registry = new BenchRegistryClient({ registryAppId, algorand, overrides: true });
const bench = new BenchInstanceClient({ instanceAppId: 3277191702n, registryAppId, algorand, overrides: true });
Constructor options, common to both clients:
| Option | Description |
|---|---|
algorand | An AlgorandClient. Note that the SDK sets its default signer to an empty signer and configures its suggested params cache. |
readerAccount | Address used as the sender of read-only simulate calls. Defaults to a well-known account. |
overrides | Include historical mainnet benches (IDs 0–4, created before the current registry) from built-in fixtures, so that listings are complete. Default false. |
paramsCacheTimeout | Suggested params cache duration, in ms. |
The generated low-level clients are available as registry.client and bench.client for anything not covered by the SDK.
Reading
// registry settings: fees, eligibility labels, id counter
const settings = await registry.getRegistryState();
// all benches, keyed by bench app ID
const benches = await registry.getBenchSmallStates();
for (const [appId, b] of benches) {
console.log(b.benchId, `${b.sellingUnitName}/${b.purchaseUnitName}`, b.price, b.remainingTokens, b.timeEnd);
}
// one bench, including its metadata and the caller's remaining purchase allowance
const state = await bench.getFullState(/* optional caller address */);
getRegistryKeys()enumerates the registry's directory boxes and decodes them intoRegistryKeys.getBenchSmallStates(keys?)fetches bench states in batches of 42 per simulate call, using the registry'slog_bench_statesmethod, and returns aMap<appId, BenchStateSmall>.getFullState(sender?)callsget_state_with_metaon the bench and returns aBenchStateFull, which addsmeta,caller,userMaxSpend(the caller's remaining spend allowance, in purchase asset base units) anduserNeedMBR(whether the caller's next purchase requires the box minimum balance payment) to the listing fields.
Amount fields: price, total and remainingTokens are in whole units (decimals applied); sellingAmount, purchaseLimit, feesAccrued, userMaxSpend and purchasePricePer_100m are in base units.
Signing
Methods that send transactions take a sender (an algosdk Address) and a signer (an algosdk TransactionSigner), for example an account's signer from AlgoKit Utils, or the transaction signer provided by a wallet library:
import { Address } from "algosdk";
const sender = Address.fromString("YOUR-ADDRESS");
const signer = /* TransactionSigner */;
Every action also has a make…Txns counterpart (makeBuyTxns, makeCreateTxns, makeWithdrawTxns, ...) that returns the transaction group composer without sending it, which can be used to build, simulate or sign transactions in your own flow:
const group = await bench.makeBuyTxns({ amount, sender, signer });
const { transactions } = await (await group.composer()).buildTransactions();
Buying
await bench.buy({ amount: 10_000_000n, sender, signer }); // spend 10 USDC (base units)
amount is the spend, in base units of the purchase asset. The SDK adds an opt-in to the asset for sale and the box minimum balance payment to the group when needed. See Buying.
Creating a bench
const { instanceAppId } = await registry.create({
owner: sender, // beneficiary, see Creating a Bench
sellingAsset: 2726252423n, // 0n for ALGO
sellingAmount: 1_000_000_000_000n, // base units
purchaseAsset: 31566704n, // 0n for ALGO
purchasePricePer_100m: 940_500n, // see Pricing
timeStart: 0n, // unix seconds, 0n = open immediately
timeEnd: 0n, // unix seconds, 0n = no end time
cancellable: true,
purchaseLimit: 0n, // purchase asset base units, 0n = unlimited
meta: { tc: "https://example.com/terms", bc: ["US"] }, // optional, see Metadata
sender,
signer,
});
The SDK reads the registry settings, computes the required ALGO payment (setup fee, escrow seed, metadata storage) and composes the full creation group.
Managing a bench
await bench.withdraw({ amount, sender, signer });
await bench.end({ sender, signer });
const boxNames = await bench.getPurchaseBoxNames();
if (boxNames.length) await bench.deleteBoxes({ boxNames, sender, signer });
await bench.close({ sender, signer });
// update or remove metadata (meta: null removes it)
await registry.setMeta({ registryKey: await bench.getRegistryKey(), meta: { tc: "https://example.com/terms" }, sender, signer });
Fee admin: bench.withdrawFees. Registry admin: registry.changeFee, changeSetupFee, changeFeeAddr, changeAbelAppId, changeAllowlistLabels, changeBanlistLabels, setIdCounter, changeAdmin, cleanup.
Types
The package exports the BenchStateSmall, BenchStateFull, BenchMeta, RegistryState and WithSigner types, and the validateMeta helper. BenchRegistryClient.getRegistryKey(state) and bench.getRegistryKey() provide the registry key that addresses a bench in the registry directory (see Storage).