PoTU — Proof of Token Used
Every block burns a small amount of OMBRA proportional to the total AI tokens consumed in that block. This deflationary pressure offsets task-reward issuance and ensures supply doesn't run away as inference volume grows.
Burn formula
For each block with blockTotalTokens (sum of outputTokens + thinkingTokens across all TASK_RESPONSE txs included):
burnAmount = max(MIN_BURN_PER_BLOCK, blockTotalTokens / BURN_TOKEN_DIVISOR)
| Constant | Value | Meaning |
|---|---|---|
MIN_BURN_PER_BLOCK | 10 micro | Floor: ensures every block burns at least 0.00001 OMBRA |
BURN_TOKEN_DIVISOR | 10_000_000 | 1 OMBRA burn per 10M tokens consumed |
Mechanism
The proposer proposes a BURN tx at the head of the block, signed by their own wallet. The tx subtracts burnAmount from state.totalSupply (no recipient). If the proposer omits or under-declares, validators reject the block.
Pseudo-code: build BURN tx
function buildBlockBurnTx(block, proposerWallet):
totalTokens = sum(
tx.outputTokens + tx.thinkingTokens
for tx in block.transactions
if tx.type == "TASK_RESPONSE"
)
amount = max(MIN_BURN_PER_BLOCK, totalTokens / BURN_TOKEN_DIVISOR)
return buildBurnTx(
from = proposerWallet.address,
amount = amount,
blockIndex = block.index,
tokensConsumed = totalTokens,
nonce = state.accounts[proposerWallet.address].nonce,
privateKey = proposerWallet.privateKey,
)
Pseudo-code: validate BURN tx
function validateBlockBurn(block, state):
burnTxs = [tx for tx in block.transactions if tx.type == "BURN"]
assert len(burnTxs) == 1, "exactly one BURN per block"
burn = burnTxs[0]
assert burn.from == block.proposer
assert burn.blockIndex == block.index
totalTokens = sum(
tx.outputTokens + tx.thinkingTokens
for tx in block.transactions
if tx.type == "TASK_RESPONSE"
)
expected = max(MIN_BURN_PER_BLOCK, totalTokens / BURN_TOKEN_DIVISOR)
assert burn.amount == expected
assert burn.tokensConsumed == totalTokens
Example
Block contains 3 task responses with token counts: 1_200, 4_800, 2_000 (total = 8_000).
amount = max(10, 8_000 / 10_000_000)
= max(10, 0.0008)
= 10 // floor wins (block too cheap to exceed floor)
A busier block with 15_000_000 total tokens:
amount = max(10, 15_000_000 / 10_000_000)
= max(10, 1.5)
= 1 // wait, BigInt truncation
In BigInt arithmetic, 15_000_000 / 10_000_000 = 1 (integer division). So:
amount = max(10, 1) = 10
The divisor effectively means: burn 1 micro per 10M tokens, floored at MIN_BURN_PER_BLOCK = 10. For blocks below 100M tokens, the floor dominates. Above 100M, real PoTU kicks in.
Why a floor
Without MIN_BURN_PER_BLOCK, an empty block (only system txs) would burn 0 OMBRA — supply would grow purely from task rewards without any sink. The floor guarantees deflationary pressure even at zero load.
Next: Validator Attestation →