Tokenomics
This page covers the economics of the Ootle layer specifically:
- The L2 native token: what resource it is and the constants that name it.
- The burn-to-mint bridge from the base layer, and why it is one-way.
- The claim mechanism that mints L2 value from a base-layer burn.
- How transaction fees are split between the burn and the validator.
Note
This page is about the Ootle L2 only. The supply, emission curve, and monetary policy of the base-layer coin belong to the upstream Minotari project and are out of scope here. What matters for Ootle is the bridge: how value crosses from one layer to the other.
The native token
Ootle’s native token is a stealth resource. Its identity is a fixed resource address constant:
/// The Tari network native resource address. ... fungible resource with a divisibility of 6.pub const STEALTH_TARI_RESOURCE_ADDRESS: ResourceAddress = ResourceAddress::new(ObjectKey::from_array([1u8; ObjectKey::LENGTH]));
/// Shorthand version of the `STEALTH_TARI_RESOURCE_ADDRESS` constantpub const TARI_TOKEN: ResourceAddress = STEALTH_TARI_RESOURCE_ADDRESS;| Property | Value |
|---|---|
| Symbol | TARI (tTARI on testnet, $TARI on mainnet) |
| Resource constant | TARI_TOKEN (alias of STEALTH_TARI_RESOURCE_ADDRESS) |
| Type | Stealth — amounts and owners hidden |
| Divisibility | 6 |
| Base unit | microtari: 1 TARI = 1_000_000 microtari (const TARI: u64 = 1_000_000) |
Being a stealth resource, TARI does not live in vaults — it exists as UTXOs with hidden amounts and one-time addresses, which is why network fees are paid via a revealed output (see Privacy).
Caution
XTR and ONE_XTR are deprecated aliases (#[deprecated(since = "0.24.5", note = "Use TARI_TOKEN instead")]). They still resolve, but new code should use TARI_TOKEN for the resource
and TARI for the unit, and refer to the token as TARI. The faucet constants
(XTR_FAUCET_*) and the built-in template name XtrFaucet are not deprecated — they retain their
historical spelling.
The burn-to-mint bridge
Value enters Ootle along a single, one-way path: burn on the base layer, claim on Ootle.
burn XTML1⟶mint TARIL2
There is no peg-out. You cannot burn TARI on Ootle to recreate base-layer coins. The asymmetry is deliberate:
- Base-layer supply stays under base-layer control. Ootle can never inflate the base-layer coin, because it only ever consumes it.
- Ootle’s supply is anchored, not invented. Every unit of TARI on the L2 corresponds to a burned L1 output; the bridge can only move value inward.
- Demand for TARI is tied to the fee market. Because the only route in is a burn, holding TARI is a claim on Ootle’s execution, not on the base layer.
Note
This is not a rollup bridge. There are no state roots posted to the base layer and no fraud-or-validity proof over Ootle execution. The bridge moves value, one direction, and nothing else. See Overview.
The claim
Moving value across the bridge is a claim: the holder proves that a base-layer output was burned
to them, and the engine mints the matching L2 value. The claim carries a MinotariBurnClaimProof
(crates/engine_types/src/confidential/claim.rs) — a commitment, an ownership proof, the burn
output, and a Merkle proof of that output against the base layer block it was mined in.
The engine executes a claim in three steps, atomically
(crates/engine/src/runtime/impl.rs):
// 1. verify the claim proof against the base-layer burn// 2. Create a tombstonelet address = ClaimedOutputTombstoneAddress::from_commitment(claim.commitment);state_mut.new_substate(address, ClaimedOutputTombstone { value: claim.value })?;// 3. Create the stealth UTXO.let address = UtxoAddress::new(TARI_TOKEN, claim.commitment.into());state_mut.new_substate(address, utxo)?;- Verify the burn proof.
- Tombstone the commitment. The tombstone’s address is derived from the commitment itself
(
ClaimedOutputTombstoneAddress::from_commitment), so a second claim of the same commitment would collide with the existing tombstone and be rejected. This is what makes the claim double-spend-proof. - Mint the stealth UTXO at
UtxoAddress::new(TARI_TOKEN, commitment).
Because the claim is Output → Utxo and writes both the tombstone and the UTXO in one substate
diff, it either completes entirely or not at all.
Note
The claim proof is checked through a pluggable ClaimProofVerifier trait. This repository ships a
test mock, AlwaysPassesProofVerifier (crates/template_test_tooling/src/mocks.rs); production
deployments supply a verifier that validates the base-layer output Merkle proof. The verification
step is part of the engine’s claim path; the verifier is injected.
Why minting here does not break supply
The claim mints a stealth UTXO with no matching increase_total_supply. That is sound only because
the genesis TARI resource disables total supply tracking and denies Burn — so no other path can
increase or decrease the tracked supply, and the mint cannot create a discrepancy. A resource that
enabled supply tracking or allowed burns would need a matching supply update here. See
Privacy in Applications for how supply
tracking interacts with mint and burn for ordinary stealth resources.
Fees
Every transaction pays a fee for the metered work it performs (see State and Execution). The fee is then split between a burn and the validator.
The split is governed by exhaust_burn_rate, a per-network constant expressed in basis points:
pub exhaust_burn_rate: ExhaustBurnRate, // 500 bps on every network here// ...exhaust_burn_rate: ExhaustBurnRate::new(500), // 5%The burn is a floor share of everything collected — overcharge included — and the validator receives the remainder:
/// The share of `paid` that is burned at `rate`: `⌊paid × rate / 10_000⌋`.fn exhaust_burn_share(paid: u64, rate: ExhaustBurnRate) -> u64 { (u128::from(paid) * u128::from(rate.as_bps()) / 10_000) as u64}With the network rate of 500 bps:
burn=⌊10,000fee×500⌋
The configured rate is 5%, but the result is rounded down to a whole base unit. The validator that
produced the block receives fee - burn, which can therefore be slightly more than 95%; for fees
smaller than 20 base units the burn rounds to zero. The validator’s share is held in a
ValidatorFeePool substate and withdrawn later via ClaimValidatorFees.
Tip
The burn is a deflationary sink on L2 fees: it removes a slice of TARI from circulation on every transaction. Because the only way to replace it is to burn more base-layer coins, the fee burn is the mechanism that couples Ootle’s fee market back to the base layer’s supply.
What is not in the fee model
There is no priority or throttle layer. Fees are a deterministic function of metered execution — a transaction pays for the work it performs, and there is no market for ordering beyond the protocol’s own sequencing. Searching the codebase for a fee-priority or throttle concept returns nothing; the only rate limiting present is an unrelated wallet API-key limit.
Summary
- The L2 native token is TARI, a stealth resource addressed by
TARI_TOKEN/STEALTH_TARI_RESOURCE_ADDRESS;XTR/ONE_XTRare deprecated aliases. - Divisibility is 6;
1 TARI = 1_000_000microtari. - Value enters by burning a base-layer coin and claiming an L2 UTXO — a strictly one-way bridge with no peg-out.
- A claim verifies a burn proof, writes a commitment-derived tombstone (double-claim proof), and mints a stealth UTXO — atomically.
- Fees use a 500 bps burn rate rounded down to a whole base unit; the remainder accrues to the validator.
- There is no fee priority/throttle concept; fees are deterministic metering.
Next: the A–Z of terms used across this section — Glossary.