Skip to content
LogoLogo

Issue an asset

Create a new asset, such as a stablecoin or a tokenised deposit, in one transaction.

You need: a funded regtest key (Quick start). Issuing costs the network's issuance fee in ARRK (1 ARRK on regtest and testnet, 10 ARRK on mainnet), on top of the normal fee.

Decide the asset's rules first

They are fixed at issuance and public forever:

ChoiceEffect
Fixed supplyamount == max_supply; nobody can ever mint more
MINTABLEan authority token is created; spending it allows minting up to max_supply
FREEZABLEa control token is created; spending it freezes or unfreezes a holder's lock
CLAWBACKthe control token can also recover holders' coins without their signature
metadata_hashcommits to a ARRK-AMS-1 document; required to publish a controls_policy if the asset has controls

Keep the authority and control tokens behind multisig or a policy lock; whoever can spend them holds the issuer's powers. Burning the control token removes the controls permanently.

What the example does

  1. Builds a transaction with an issuances entry (ticker, name, decimals, amount, max supply, flags, metadata hash), funded from the issuer's ARRK coins.
  2. Pays the new supply and any authority/control tokens to the issuer's addresses.
  3. Signs and submits it, then reads the asset back with getAsset({ idOrTicker }).

The asset id is derived from the first input's outpoint, so it is known before the transaction confirms.

/**
 * Issue an asset: a mintable, freezable token with ARRK-AMS-1 metadata; pay, mint, freeze.
 *
 *   ARREKTA_URL=http://127.0.0.1:28332 ARREKTA_ADMIN_TOKEN=... npx tsx examples/issue-asset.ts
 */
import {
  createClient,
  formatUnits,
  freezeAsset,
  fromHex,
  generateSeed,
  getAsset,
  getChain,
  getCoins,
  getFrozen,
  issueAsset,
  keychainFromSeed,
  mine,
  mintAsset,
  sendPayment,
  unfreezeAsset,
} from 'arrekta-sdk';
 
const URL = process.env.ARREKTA_URL ?? 'http://127.0.0.1:28332';
const ADMIN = process.env.ARREKTA_ADMIN_TOKEN;
const SEED = fromHex(process.env.ARREKTA_SEED ?? 'a1'.repeat(32));
 
// 1. Connect; the issuer's wallet and a holder's.
const client = createClient({ url: URL, adminToken: ADMIN });
const { network } = await getChain(client);
const issuer = keychainFromSeed(SEED, { network });
const holder = keychainFromSeed(generateSeed(), { network });
if ((await getCoins(client, { wallet: issuer })).length === 0) await mine(client, issuer.address(0), 101);
 
// 2. Issue 1,000.00 units (max 5,000.00), mintable and freezable, with metadata.
const letters = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
const ticker = 'GLD' + Array.from({ length: 4 }, () => letters[Math.floor(Math.random() * letters.length)]).join('');
const metadata = {
  schema: 'arrk-ams-1',
  ticker,
  name: 'Demo Gold',
  issuer: { name: 'Demo Issuer' },
  controls_policy: 'Freezes only on court order.',
};
const issued = await issueAsset(client, {
  wallet: issuer,
  ticker,
  name: 'Demo Gold',
  amount: '1000',
  decimals: 2,
  maxSupply: '5000',
  mintable: true,
  freezable: true,
  metadata,
});
await mine(client, issuer.address(0), 1);
console.log(`issued ${ticker} = ${issued.asset}`);
 
// 3. Pay the holder 12.50 units.
await sendPayment(client, { wallet: issuer, to: holder.address(0), amount: '12.5', asset: ticker });
await mine(client, issuer.address(0), 1);
 
// 4. Mint 100.00 more (spends and re-creates the authority token).
await mintAsset(client, { wallet: issuer, asset: ticker, amount: '100' });
await mine(client, issuer.address(0), 1);
const asset = await getAsset(client, { idOrTicker: ticker });
console.log(`supply ${formatUnits(asset.issued, asset.decimals)} ${ticker}`);
 
// 5. Freeze the holder, list frozen holders, then unfreeze.
await freezeAsset(client, { wallet: issuer, asset: ticker, address: holder.address(0) });
await mine(client, issuer.address(0), 1);
console.log('frozen:', (await getFrozen(client, { asset: ticker })).frozen.map((f) => f.address));
await unfreezeAsset(client, { wallet: issuer, asset: ticker, address: holder.address(0) });
await mine(client, issuer.address(0), 1);
console.log('frozen after unfreeze:', (await getFrozen(client, { asset: ticker })).frozen);

Source: sdk/typescript/examples/issue-asset.ts (updated 2026-11-02) · sdk/python/examples/issue_asset.py (updated 2026-11-02). Runnable against a regtest node; CI runs every example.

After issuance

  • Mint: spend the authority token with a mints entry (§7.2).
  • Freeze/unfreeze: spend the control token with a freezes entry; check with getFrozen({ asset }).
  • Vesting: pay holders to a vesting policy (AND(KEY(holder), AFTER(unlock_height))).
  • Regulated workflows (holder registry, restricted transfers, distributions, dividends): the issuer service.

Common errors: issuance-ticker-taken and an insufficient fee for the issuance fee.