CLI tool to fetch ABIs from deployed Stacks blockchain contracts and output TypeScript-ready definitions (as const).
# Global install
npm install -g @satoshai/abi-cli
# Or run directly with npx
npx @satoshai/abi-cli fetch <contract>By default, the CLI writes a TypeScript file named after the contract to the current directory:
abi-cli fetch SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01
# → writes amm-pool-v2-01.tsThe generated file looks like:
import type { ClarityAbi } from '@stacks/transactions';
// ABI for SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01
// Generated by @satoshai/abi-cli
export const abi = {
"functions": [...],
"variables": [...],
...
} as const satisfies ClarityAbi;
export type Abi = typeof abi;Use --stdout to print the output instead of writing a file:
abi-cli fetch SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 --stdoutabi-cli fetch SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 -o ./abis/amm-pool.ts# Write to file (amm-pool-v2-01.json)
abi-cli fetch SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 --format json
# Print to stdout
abi-cli fetch SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 --format json --stdoutDefaults to mainnet. Use -n / --network to switch:
# Testnet
abi-cli fetch ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.my-contract -n testnet
# Devnet (localhost:3999)
abi-cli fetch ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.my-contract -n devnet
# Custom Stacks API URL
abi-cli fetch SP...contract -n https://my-node.example.comComma-separate contract IDs to fetch several at once:
abi-cli fetch SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01,SP2C2YFP12AJZB1KD5HQ4XFRYGEK02H70HVK8GQH.arkadiko-swap-v2-1
# → writes amm-pool-v2-01.ts and arkadiko-swap-v2-1.tsFor projects with multiple contracts, create a config file to keep ABIs in sync declaratively.
Create abi.config.json in your project root:
{
"outDir": "./src/abis",
"format": "ts",
"network": "mainnet",
"contracts": [
{ "id": "SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01", "name": "amm-pool" },
{ "id": "SP2C2YFP12AJZB1KD5HQ4XFRYGEK02H70HVK8GQH.arkadiko-swap-v2-1", "name": "arkadiko-swap" },
{ "id": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.my-contract", "network": "testnet" }
]
}The name field controls the output filename. This decouples your imports from the on-chain contract ID — when you upgrade to a new contract version, change the id but keep the name. Your imports stay the same.
Or use abi.config.ts for type-safe config with autocomplete:
import type { AbiConfig } from '@satoshai/abi-cli';
export default {
outDir: './src/abis',
format: 'ts',
network: 'mainnet',
contracts: [
{ id: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01', name: 'amm-pool' },
{ id: 'SP2C2YFP12AJZB1KD5HQ4XFRYGEK02H70HVK8GQH.arkadiko-swap-v2-1', name: 'arkadiko-swap' },
],
} satisfies AbiConfig;Then run:
abi-cli sync
# → reads abi.config.json (or .ts), writes all ABIs to outDirThis generates:
src/abis/
├── amm-pool.ts
├── arkadiko-swap.ts
└── index.ts # barrel file with re-exports
The barrel file re-exports all ABIs with camelCase names:
export { abi as ammPoolAbi } from './amm-pool.js';
export { abi as arkadikoSwapAbi } from './arkadiko-swap.js';Use --config / -c to point to a custom config path:
abi-cli sync --config ./configs/my-abis.json| Field | Type | Required | Default | Description |
|---|---|---|---|---|
outDir |
string |
yes | — | Output directory for generated files |
format |
"ts" | "json" |
no | "ts" |
Output format |
network |
string |
no | "mainnet" |
Default network for all contracts |
contracts |
ContractEntry[] |
yes | — | List of contracts to sync |
contracts[].id |
string |
yes | — | Contract ID in address.name format |
contracts[].name |
string |
no | contract name from ID | Alias for output filename and barrel export |
contracts[].network |
string |
no | top-level network |
Per-contract network override |
Use --check to verify local files are in sync with on-chain ABIs without writing anything. Exits with code 1 if any files are stale or missing.
# Single contract
abi-cli fetch SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 --check
# All contracts from config
abi-cli sync --checkAdd it to your CI pipeline to catch stale types:
- name: Check ABIs are up-to-date
run: npx @satoshai/abi-cli sync --check| Flag | Alias | Default | Description |
|---|---|---|---|
--network |
-n |
mainnet |
Network: mainnet, testnet, devnet, or a custom URL |
--output |
-o |
<contract-name>.<format> |
Output file path (single contract only) |
--format |
-f |
ts |
Output format: ts or json |
--stdout |
false |
Print to stdout instead of writing a file | |
--check |
false |
Check if local files match on-chain ABI (exit 1 if stale) | |
--help |
Show help |
| Flag | Alias | Default | Description |
|---|---|---|---|
--config |
-c |
auto-discover | Path to config file |
--check |
false |
Check if local files match on-chain ABIs (exit 1 if stale) | |
--help |
Show help |
import { fetchContractAbi, generateTypescript, generateJson, parseContractId } from '@satoshai/abi-cli';
// Fetch an ABI
const abi = await fetchContractAbi('mainnet', 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM', 'amm-pool-v2-01');
// Generate TypeScript (as const)
const tsCode = generateTypescript('SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01', abi);
// Generate JSON
const json = generateJson(abi);
// Parse a contract ID string
const { address, name } = parseContractId('SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01');import { loadConfig, validateConfig } from '@satoshai/abi-cli';
import type { AbiConfig, ContractEntry } from '@satoshai/abi-cli';
// Load and validate from file (auto-discovers abi.config.json/.ts)
const config = await loadConfig();
// Or from a specific path
const config2 = await loadConfig('./my-config.json');
// Validate a raw object
const validated = validateConfig({ outDir: './abis', contracts: [{ id: 'SP1.token' }] });Types are re-exported from @stacks/transactions:
import type { ClarityAbi, ClarityAbiFunction, ClarityAbiType } from '@satoshai/abi-cli';MIT