Issuance
Asset issuance on Liquid allows you to create new digital assets. When you issue an asset, you create a certain amount of that asset and optionally you also create another asset, called reissuance token, that allows you to create more of the asset later.
Understanding Asset Issuance
When issuing an asset, you need to specify:
- Asset amount: The number of units (in satoshis) of the asset to create
- Asset receiver: The address that will receive the newly issued asset (optional, defaults to a wallet address)
- Token amount: The number of reissuance tokens to create (optional, can be 0)
- Token receiver: The address that will receive the reissuance tokens (optional, defaults to a wallet address)
- Contract: Metadata about the asset (optional, necessary for asset registry)
The asset receiver address and token receiver address can belong to different wallets with different spending policies, allowing for a more secure and customizable setup according to the issuer's needs.
The asset ID is deterministically derived from the transaction input and contract metadata (if provided). This means that if you use the same contract and transaction input, you'll get the same asset ID.
Creating a Contract
A contract defines metadata about your asset, such as its name, ticker, precision, and issuer information. While contracts are optional, they are highly recommended as they allow your asset to be registered in the Liquid Asset Registry and displayed with proper metadata in wallets.
A contract contains:
- domain: The domain of the asset issuer (e.g., "example.com")
- issuer_pubkey: The public key of the issuer (33 bytes, hex-encoded)
- name: The name of the asset (1-255 ASCII characters)
- precision: Decimal precision (0-8, where 8 is like Bitcoin)
- ticker: The ticker symbol (3-24 characters, letters, numbers, dots, and hyphens)
- version: Contract version (currently only 0 is supported)
Amounts expressed in satoshi are always whole numbers without any decimal places. If you want to represent decimal values, you should use the "precision" variable in the contract. This variable determines the number of decimal places, i.e., how many digits appear after the decimal point. The following table shows different precision values with the issuance of 1 million satoshi.
| Satoshi | Precision | Asset units |
|---|---|---|
| 1.000.000 | 0 | 1.000.000 |
| 1.000.000 | 1 | 100.000,0 |
| 1.000.000 | 2 | 10.000,00 |
| 1.000.000 | 3 | 1.000,000 |
| 1.000.000 | 4 | 100,000 |
| 1.000.000 | 5 | 10,00000 |
| 1.000.000 | 6 | 1,000000 |
| 1.000.000 | 8 | 0.1000000 |
| 1.000.000 | 8 | 0.01000000 |
let contract_str = "{\"entity\":{\"domain\":\"ciao.it\"},\"issuer_pubkey\":\"0337cceec0beea0232ebe14cba0197a9fbd45fcf2ec946749de920e71434c2b904\",\"name\":\"name\",\"precision\":8,\"ticker\":\"TTT\",\"version\":0}";
let contract = Contract::from_str(contract_str)?;
contract = Contract(
domain = "ciao.it", \
issuer_pubkey = "0337cceec0beea0232ebe14cba0197a9fbd45fcf2ec946749de920e71434c2b904", \
name = "name", \
precision = 8,
ticker = "TTT",
version = 0)
Issuing an Asset
To issue an asset, use TxBuilder::issue_asset() before calling finish(). You need to have some LBTC in your wallet to pay for the transaction fees.
// Issue asset
let issued_asset = 10_000;
let reissuance_tokens = 1;
// Create a transaction builder and the issuance transaction
let builder = wollet.tx_builder();
// isue asset
let mut pset = builder
.issue_asset(
issued_asset,
None, // None -> a wallet from the address is used
reissuance_tokens,
None, // None -> a wallet from the address is used
Some(contract.clone()),
)?
.finish()?;
// Sign the transaction and finalize it
let signatures_added = signer.sign(&mut pset).expect("signing failed");
let _ = wollet.finalize(&mut pset)?;
let tx = pset.extract_tx()?;
// Broadcast the transaction
let txid = client.broadcast(&tx)?;
issued_asset = 10000
reissuance_tokens = 1
# Create an issuance transaction
builder = network.tx_builder()
builder.issue_asset(issued_asset, wollet_adddress, reissuance_tokens, wollet_adddress, contract)
unsigned_pset = builder.finish(wollet)
Getting Asset and Token IDs
After creating the issuance PSET, you can extract the asset ID and reissuance token ID from the transaction input:
let asset_id = pset.inputs()[0].issuance_ids().0;
let token_id = pset.inputs()[0].issuance_ids().1;
asset_id = signed_pset.inputs()[0].issuance_asset()
token_id = signed_pset.inputs()[0].issuance_token()
Complete Example
Here's a complete example that issues an asset, signs it, and broadcasts it:
let mut client = test_client_electrum(&env.electrum_url());
// Create wallet
let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
let signer = SwSigner::new(mnemonic, false)?;
let desc = signer.wpkh_slip77_descriptor()?;
let mut wollet = Wollet::without_persist(network, WolletDescriptor::from_str(&desc)?)?;
let wollet_address = wollet.address(None)?;
let wallet_address_str = wollet_address.address().to_string();
let txid =
let contract_str = "{\"entity\":{\"domain\":\"ciao.it\"},\"issuer_pubkey\":\"0337cceec0beea0232ebe14cba0197a9fbd45fcf2ec946749de920e71434c2b904\",\"name\":\"name\",\"precision\":8,\"ticker\":\"TTT\",\"version\":0}";
let contract = Contract::from_str(contract_str)?;
// Issue asset
let issued_asset = 10_000;
let reissuance_tokens = 1;
// Create a transaction builder and the issuance transaction
let builder = wollet.tx_builder();
// isue asset
let mut pset = builder
.issue_asset(
issued_asset,
None, // None -> a wallet from the address is used
reissuance_tokens,
None, // None -> a wallet from the address is used
Some(contract.clone()),
)?
.finish()?;
// Sign the transaction and finalize it
let signatures_added = signer.sign(&mut pset).expect("signing failed");
let _ = wollet.finalize(&mut pset)?;
let tx = pset.extract_tx()?;
// Broadcast the transaction
let txid = client.broadcast(&tx)?;
let asset_id = pset.inputs()[0].issuance_ids().0;
let token_id = pset.inputs()[0].issuance_ids().1;
network = Network.regtest_default()
policy_asset = network.policy_asset()
client = ElectrumClient.from_url(node.electrum_url())
# Create wallet
mnemonic = Mnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about")
signer = Signer(mnemonic, network)
desc = signer.wpkh_slip77_descriptor()
wollet = Wollet(network, desc, datadir=None)
wollet_address_result = wollet.address(0)
wollet_adddress = wollet_address_result.address()
contract = Contract(
domain = "ciao.it", \
issuer_pubkey = "0337cceec0beea0232ebe14cba0197a9fbd45fcf2ec946749de920e71434c2b904", \
name = "name", \
precision = 8,
ticker = "TTT",
version = 0)
issued_asset = 10000
reissuance_tokens = 1
# Create an issuance transaction
builder = network.tx_builder()
builder.issue_asset(issued_asset, wollet_adddress, reissuance_tokens, wollet_adddress, contract)
unsigned_pset = builder.finish(wollet)
# Sign the transaction and finalize it
signed_pset = signer.sign(unsigned_pset)
finalized_pset = wollet.finalize(signed_pset)
tx = finalized_pset.extract_tx()
# Broadcast the transaction
txid = client.broadcast(tx)
asset_id = signed_pset.inputs()[0].issuance_asset()
token_id = signed_pset.inputs()[0].issuance_token()
Important Notes
- Asset amount limit: The maximum asset amount is 21,000,000 BTC (2,100,000,000,000,000 satoshis)
- At least one amount required: Either
asset_satsortoken_satsmust be greater than 0 - Reissuance tokens: If you want to be able to create more of the asset later, you must issue at least 1 reissuance token. The holder of the reissuance token can use it to reissue more of the asset
- Contract commitment: If a contract is provided, its metadata is committed in the asset ID. This means the asset ID will be the same if you use the same contract and transaction input
- Confidential issuance: The issuance amounts can be confidential (blinded) or explicit. LWK handles this automatically
Next Steps
After issuing an asset, you can:
- Reissue more of the asset if you have reissuance tokens
- Burn some of the asset to reduce the supply
- Send the asset to other addresses using regular transaction creation
Next: Reissuance