Tokenomics

Ootle / Concepts / Tokenomics

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:

crates/template_lib_types/src/constants.rs
/// 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;
PropertyValue
SymbolTARI (tTARI on testnet, $TARI on mainnet)
Resource constantTARI_TOKEN (alias of STEALTH_TARI_RESOURCE_ADDRESS)
TypeStealth — amounts and owners hidden
Divisibility6
Base unitmicrotari: 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 TARIL2text{burn } text{XTM}_{L1} ;longrightarrow; text{mint } text{TARI}_{L2}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)?;
  1. Verify the burn proof.
  2. 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.
  3. 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:

crates/consensus/src/consensus_constants.rs
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:

crates/engine/src/runtime/working_state.rs
/// 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=⌊fee×50010,000⌋text{burn} = leftlfloor frac{text{fee} times 500}{10{,}000} rightrfloorburn=⌊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_XTR are deprecated aliases.
  • Divisibility is 6; 1 TARI = 1_000_000 microtari.
  • 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.