Baklava

The swap

A pool of two assets, a constant product, and providers who own a share of it — and one way to give that share up for ever, so a launch can prove its liquidity cannot be pulled. The last module, and the only one that asked a rule which had already shipped to change.

Using one

make, fill, trade, empty
./baklava pool     -a BKLV -b <token id>
./baklava deposit  -pool <id> -a 1000000 -max-b 4000000 --min-shares 1900000
./baklava trade    -pool <id> -in BKLV -amount 10000 -min-out 9800
./baklava withdraw -pool <id> -shares 500000

Or /pool, /deposit, /trade and /withdraw from the console. A pool is created empty; the first deposit is what sets the price. There is a fifth transition, /burn, which gives a share up for ever — it is below.

Swap is the same thing without a terminal: every pool on the chain, what is in it, and a quote that composes the command above with -min-out already filled in. It signs nothing, and the reserves it shows are two ordinary balances the node proves.

A liquidity share is not a token, and never becomes one.

A provider's share has to exist when they deposit and stop existing when they withdraw. That is a mint and a burn, and the token module deliberately has neither — a holder never has to wonder what the creator might do next, because there is nothing they can do.

So a share is stored in the leaf a token balance already uses, under a key derived from the pool and the holder, and there is no token record anywhere near it. A token transfer refuses a token with no record, so a share cannot be moved by the token module: non-transferability is not a check, it is an absence. The token module reads exactly as it did.

One pool per pairThe two assets are put in canonical order — BKLV is twenty zeroes, so it always sorts first — and the pool's id is derived from the ordered pair rather than from a creator and a nonce. There is no second pool to split the liquidity, and anybody can compute where a pair's pool is without asking.
The reserves are balancesNot numbers in a record. They sit at an address derived from the pool, for which no private key exists — the same trick the stake pool and the market escrow use. So nothing is minted or burned by a deposit, a withdrawal or a trade, and the supply invariant needs no help. A reserve is proved by the account and token balance answers that already exist.
The fee stays in the poolA trade puts the whole amount into the reserve and prices only the part after 30 basis points, so the product grows on every trade and the share count does not. A provider's return is the pool's growth; there is no separate fee ledger and nothing to claim.
Every rounding favours the poolDeposits round the required second amount up, withdrawals round both returns down, and a trade rounds its output down. A rounding that can be nudged the other way is not an error, it is a leak of one Krumb per trade, for ever, to whoever noticed it first.

A deposit that would buy nothing is refused

That one rule closes the whole family of share-inflation attacks without locking a minimum liquidity away for ever.

The attack is: be the first provider and take one share, then donate a large amount straight to the reserve address so that the next provider's deposit rounds to nothing and is swallowed. Here the second provider's transaction fails and they keep their money. The attacker has spent real value to make a pool useless for small deposits, which is vandalism rather than theft.

Anybody can pay into a pool's reserve, because it is an ordinary address and gate 1 cannot recognise one. Doing so is a gift to the existing providers.

Minimum out, and the attack it does not close

A leader chooses the order of the transactions in its block, so it can put its own trade in front of somebody else's and take the difference. Sortition changes who the leader is at every height and the seed is a VRF nobody can steer — so this is not a standing position anybody holds, but it is a position somebody holds at every height.

Every trade therefore carries a minimum out, and deposits and withdrawals carry their equivalents. It does not stop the attack; it bounds what the attack can take to whatever slippage you agreed to in advance. Use it.

A commit–reveal would close it properly and is not being taken: it costs two blocks per trade, and on a chain whose rounds turn only when there is something to put in a block, doubling the round count of every trade is a large price for an attack whose profit is bounded already.

Burning a share, so a launch can prove it is locked

A provider's only exit is a withdrawal, which returns the reserves. So a token's creator could always take the liquidity back, and had no way to prove otherwise even if they meant not to — which is the one guarantee a launch actually sells. /burn makes it expressible.

give up a claim for ever
./baklava burn -pool <id> -all
/burn <id> all                # or from the console, which asks first

The share does not disappear. It moves to H("baklava/swap/burn/v1") — twenty bytes that are the hash of a string, so no private key for them exists — and the pool's total share count is left exactly alone.

Leaving the total alone is the whole mechanism.

A withdrawal returns shares × reserve / total. Subtract the burned shares from the total and every remaining holder's fraction rises to cover the same reserves — so a burn would be a gift to the other providers rather than a lock, and a creator holding shares at two addresses could burn from one and collect at the other. The guarantee would be forgeable by the one person it exists to bind.

It also means “no shares, but reserves” is a state that is never created. A deposit into that state has no ratio to mint against, so it would take the empty-pool path and hand a one-Krumb depositor a claim on liquidity somebody else locked.

Anybody can check itTwo answers that already exist, both proved against a state root rather than taken on trust: the pool record says how many shares there are, and the share query — asked about the burn address — says how many are burned. Equal means the reserves can never leave. Deleting the leaf instead would have made the claim the absence of every share leaf, and this chain proves single leaves; there is no absence proof for a list.
Nothing is destroyedNot in the supply sense. The reserves stay exactly where they are, in an account with no key; what is destroyed is a claim on them. The sum of every balance is what it was.
A burned pool still worksTrades run against it as before, and new providers may still deposit — they take a proportional claim on what they themselves put in, and the locked part stays locked.
It costs nothingA burned share is a share: the same eight-byte leaf, the same table, the same query. No new leaf kind, no new width, no change to what state_root folds.

What is not here

No share transferFor this version. Withdrawing and letting somebody else deposit is the same trade in two steps; if a transfer is ever wanted it is a transition of its own, in this module, where a reader already knows what a share is. Burning one is the single exception, and it has one fixed destination rather than a named one — so there is no counterparty and nothing to sell.
No routingA trade names one pool. Getting from A to C through B is two transactions, and a router that did it in one would be choosing a path — a policy, and policies belong outside a chain with no virtual machine.
No flash loansAnd there cannot be. Every transition is settled when it returns; there is no point at which a caller holds value it has not paid for.
No oracleA pool's price is whatever its reserves say at the height somebody reads them, and it can be moved by anybody with enough money for exactly as long as they hold the position. Nothing in this chain reads it, and nothing should.

Rule version 4 — the first one that is not a batch.

Slashing went into version 2 beside unstaking, and NFTs and the market into version 3 beside tokens, each because no chain had activated the version yet. Version 3 has been activated, so that door is shut: the swap is a version of its own, and it arrives at a height the chain agrees on in advance. See Upgrades and readiness.

The reasoning in full, including the two ways of minting a share that were rejected and why the rounding has a direction, is under The swap in the protocol document.