Skip to content

Using the CLI ​

clarity-abitype ships a small CLI that keeps ABI declarations in your repository in sync with deployed contracts. It reads a config file listing the contracts you care about, fetches their ABIs from the Stacks API, and writes a TypeScript file containing the ABIs with as const assertions so TypeScript can infer types from them.

Installation ​

The CLI is included in the clarity-abitype package:

bash
npm install clarity-abitype
bash
pnpm add clarity-abitype
bash
yarn add clarity-abitype

Initialize ​

Create a clarity.config.ts file at the root of your project:

bash
npx clarity-abitype init

This scaffolds a config with an empty contract list:

ts
import { defineConfig } from "clarity-abitype/config";

export default defineConfig({
  out: "src/generated.ts",
  contracts: [],
});
OptionDescription
-c, --config <path>Path to config file
-r, --root <path>Directory to create the config in

Configure contracts ​

Add every contract you want an ABI for. The contract field is the full contract identifier (<address>.<contract-name>):

ts
import { defineConfig } from "clarity-abitype/config";

export default defineConfig({
  out: "src/abis/generated.ts",
  contracts: [
    {
      name: "sbtcToken",
      contract: "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token",
    },
    {
      name: "myToken",
      contract: "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.my-token",
    },
  ],
});

Contract options ​

OptionRequiredDescription
nameYesName used to derive the generated export. sbtcToken generates sbtcTokenAbi.
contractYesContract identifier in the form <address>.<contract-name>.
abiNoPre-resolved ABI. When provided, the ABI is not fetched from the API. Useful for offline generation.

Config options ​

OptionDefaultDescription
outsrc/generated.tsOutput file path, relative to the current working directory.
contracts[]Contracts to generate ABIs for.
apiUrlhttps://api.mainnet.hiro.soStacks API base URL. Use https://api.testnet.hiro.so for testnet contracts.

The config can also export a function, which is useful for computing contracts dynamically:

ts
import { defineConfig } from "clarity-abitype/config";

export default defineConfig(async () => ({
  out: "src/abis/generated.ts",
  contracts: await getContracts(),
}));

Generate ​

Fetch the ABIs and write the output file:

bash
npx clarity-abitype generate
bash
Using config clarity.config.ts
Fetched SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
Generated 1 ABI at src/abis/generated.ts
OptionDescription
-c, --config <path>Path to config file
-r, --root <path>Directory to resolve the config from

Contracts are sorted by name, so the generated file is stable between runs. A header and a banner per contract are added to make it clear the file is generated:

ts
// Generated by clarity-abitype. Do not edit.

////////////////////////////////////////////////////////////////////////////////////////////////////
// sbtcToken
// SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
////////////////////////////////////////////////////////////////////////////////////////////////////

export const sbtcTokenAbi = {
  functions: [
    // ...
  ],
  variables: [
    // ...
  ],
  maps: [],
  fungible_tokens: [{ name: "sbtc-token" }],
  non_fungible_tokens: [],
  epoch: "Epoch30",
  clarity_version: "Clarity3",
} as const;

Then import the ABI and use it as usual:

ts
import type { ExtractAbiFunctionNames } from "clarity-abitype";
import { sbtcTokenAbi } from "./abis/generated";

type ReadOnlyFunctions = ExtractAbiFunctionNames<
  typeof sbtcTokenAbi,
  "read_only"
>;
//   ^? "get-name" | "get-symbol" | ...

Config resolution ​

The CLI looks for clarity.config.ts, clarity.config.mts, clarity.config.js, or clarity.config.mjs, searching upwards from the current working directory. Use --config to point to a different file or --root to search from a different directory.

TypeScript configs are loaded natively and require Node.js 22.18 or later. On older Node.js versions, use a .js or .mjs config.

Local contracts ​

The CLI fetches ABIs from a Stacks API, so it targets deployed contracts. For local Clarinet contracts, see the Clarinet SDK guide for how to read interfaces from the simnet.