Before your token can exist on Spark, it needs to be announced on Bitcoin L1 — publicly and immutably.

To do this, you broadcast a transaction embedding your token’s identifier and metadata in an OP_RETURN output, a special field in Bitcoin transactions that lets you attach arbitrary data. Learn more about OP_RETURN.

This is a one-time commitment. Once the transaction is mined, your LRC-20 token is locked in — name, supply, decimals, and any additional settings become part of Bitcoin’s canonical history. It’s like etching the blueprint into stone — once it’s set, there’s no going back.

What you keed to know

  • Each Issuer Wallet can only issue one token
  • Bitcoin network fees are required for the announcement
  • Confirmation typically takes 1–2 blocks (~10–20 minutes)
  • Token metadata is permanent after announcement
  • Save the announcement transaction ID so you can refer to it later

Prerequisites

Step 1: Get Your L1 Wallet Address

Once your wallet is initialized, retrieve its Bitcoin L1 address. This is where you’ll send funds to cover the announcement transaction fee.

const l1Address = wallet.getTokenL1Address();
console.log(l1Address); // example: bc1....

Step 2: Fund Your L1 Wallet

Send a small amount of BTC to the address above - around $3-5 worth is plenty. This will be used to pay the network fee for your token announcement and should take about >10 minutes to confirm.

For REGTEST, you can deposit test funds to your REGTEST Spark wallet using our faucet.

Step 3: Broadcast the L1 Announcement Transaction

You’re now ready to announce your token on Bitcoin L1.

Potential loss of L1 funds
Announcing multiple times on L1 from the same wallet will cause a loss of L1 funds.
Only the first announcement by a wallet confirmed on chain will be recognized as valid.

Check the OP_RETURN for the LRC20 prefix in the returned L1 txid to verify announcement on chain.
reference transaction: link

const announcementTx = await wallet.announceTokenL1(
  tokenName: "Test Token",
  tokenTicker: "TEST",
  decimals: 8,
  maxSupply: 10000000n,
  isFreezable: true,
);

console.log("Announcement TX:", announcementTx);

Parameters

The announceTokenL1() function accepts the following metadata:

ParameterTypeDescription
tokenNamestringHuman-readable token name (e.g., “USD Coin”)
tokenTickerstringToken symbol (e.g., “USDC”)
decimalsnumberNumber of decimal places (e.g., 6 for USDC, 8 for BTC)
maxSupplybigintMaximum token supply in base units. Set to 0n for unlimited supply
isFreezeablebooleanWhether the token can be frozen after issuance

Returns

Promise<string>; // Bitcoin transaction ID of the announcement

Examples

Creating an Unlimited Supply Token

const txId = await wallet.announceTokenL1(
  tokenName: "USD Coin",
  tokenTicker: "USDC",
  maxSupply: 0n, // Unlimited supply
  decimals: 6, // 1 USDC = 1_000_000 base units
  isFreezeable: true,
);

console.log("Announcement TX:", txId);

Creating a Fixed Supply Token

// Create a token with max supply of 1 million
const txId = await wallet.announceTokenL1(
  tokenName: "My Fixed Token",
  tokenTicker: "MFT",
  maxSupply: 1_000_000_000_000n, // 1,000,000.000000 tokens
  decimals: 6,
  feeRateSatsPerVb: 2,
  isFreezeable: false,
);

console.log("Announcement TX:", txId);
// Create a token with max 21 million supply and minimum denomination of 0.00000001
const txId = await wallet.announceTokenL1(
  tokenName: "Example Bitcoin",
  tokenTicker: "ExBTC",
  maxSupply: 2_100_000_000_000_000n,
  decimals: 8,
  feeRateSatsPerVb: 2,
  isFreezeable: false,
);

Next Steps

Once your token is created, you can mint some tokens.