Chapter XI · Alephium

Reference

The harbour's contracts, in their own words. Addresses, the bounds no key can widen, where every fee goes, and how to read the live values yourself.

The wRATR mark: the messenger with the scroll

The other dispatches tell you what to do. This one is what Ratatoskr copied off the carving itself: the rules cut into the harbour's contracts, which no hand can recut once they are set.

Most of it is reasoning, not numbers. The contracts carry their reasons beside their code, and where they do, this chapter repeats them. Where a number does appear, it says when it was read and where it was read from, so a stale one shows its age.

How to read this chapter #

Every value here is one of three kinds, and each kind is marked:

  • Compiled A constant in the contract's code. It cannot change for as long as that contract exists. No key reaches it.
  • Fixed at deploy Set once when the contract was created and immutable after. A different value would mean a different contract.
  • Settable A field the protocol's authority key can change, inside compiled bounds. What is printed here is a snapshot with its date. The contract is the live answer.

The snapshots on this page were read from Alephium mainnet at 2026-10-03 15:46 UTC, epoch 2961.

The contracts #

Each address opens on the Alephium explorer. The codeHash is the fingerprint of the code at that address. On 2026-10-03 each one was read from the chain and compared with the codeHash compiled from the protocol's source: every one matched.

ContractAddress · codeHash
PairFactoryCreates pools; every pool is a copy of one templatezANiQU3bdSeRcoWfHdPRosqfm2LUD4udxhsxg6TsGHwDcodeHash 44e41eb2efc3577148ab728434db38901d419074f4283388d68e35921ee2083e
FeeExchangeHolds the fee basket at a posted wRATR pricexoeufYzS2jEmnQRwyUrTB9gsipfNL8GnxMUUEwXkiqsMcodeHash 26f45fe0866f4ae32435ed770c92e8b5d267072c6ac9cc7845fa254db0604226
FeeSplitterDivides wRATR between lockers and the Sink25RHugjfH5uiFHdmYEg7d7kB75JwjdbuAfX5QTKmMxxZmcodeHash cf35213c7884cf634ff41ce6d597fb8c3992ede67f2477b6683528d94a76ac2f
SinkThe one-way door22RouFo2WazkebbaCFtcmrKhsAqBC1HH28eDGhN7bs1YBcodeHash 15976c02bb4451eb47a342d35efaf27ea692043d1f08a979ce96eb0f4d15bfa0
VeLockLock positions, power and lockers’ fees29PFEoZdTBeoMccnpioeegbv1n88fnx6Ava6MGMq8MtbHcodeHash 400a904394400d25248ebea856a9da91ddc7bfab1984f6204d91109beec39ee0
VoterGauge registry and the vote ledgeru6hwrPBg9nFqib9z3e4Ph7UikpTUFVfUwMAA7PxdxsrfcodeHash c3c56abcbfa8dcaec99dcc8d707f3a332824697b511a3b6dbe6c6ed064dedac3
MinterThe emission pot and its weekly booking21kwbuLwXVwKFjpV7nVmqLJVGVwphed7PoVeymUs1WQ23codeHash e78f7dfc17bfc9e4ff88d00081c7a7057985285555317b78f5f13f547ddfc575
VoteIncentiveVaultVote incentives, split by vote weight22netg82X5F4wAGd8ETFdGUsY3atPPhfS5LjnYPyusXSFcodeHash 44c71a75de48686a4a31787b0a435c603e71cb444686f47354dde6d73f479629
ProtocolConfigThe authority, the guardian and the pause25YV8Z9chs4Sk7TRq7WWUnMcXZfKHkWMFKmytsvpJgV6fcodeHash b4a6d547eb26287947f1d0cc1c81a4085a9b9cdd47ae59cc6361a2a6b4a5dddf

Pools and gauges #

Pools are created by the PairFactory, which records each one in a PairCreated event. This list was built from those events, not typed in. A token's symbol is the one its contract reports; for the two dollars, the harbour's name and the bridge each came over are given beside it. Gauges are listed from the Voter's GaugeSet events.

PoolAddress · gauge
ALPH / wRATRgauged27Ary2mu4yuHtrpdoMDkLf7nWbcJn9ubULWNfiLEFSzbZcodeHash c9c66eaaa5c72ce96c0cd3b06aafb7519a7e8c4ae58bf3e38d3012ec940a7c0bGauge 2B3xEpDbiTW7QWwh9QCqdXtqaBUwkZis8qt8rbGreWSKqcodeHash 0c09d1f512318e411114590d72e19c6474bfd40e750bcb8959dd86070a706625
USDT.bsc / wRATRno gaugeUSDT (USDT.bsc, bridged from BNB Chain)2AHZFVyb58tH1LuwHuyzn6Xu6oXpM8UpEbk6YwaawzfiwcodeHash c9c66eaaa5c72ce96c0cd3b06aafb7519a7e8c4ae58bf3e38d3012ec940a7c0b
ALPH / USDT.bscno gaugeUSDT (USDT.bsc, bridged from BNB Chain)29HhMggyuQAaXPKVE8XF3hDJXv5SBS1xMtpPrNh3aT1hHcodeHash c9c66eaaa5c72ce96c0cd3b06aafb7519a7e8c4ae58bf3e38d3012ec940a7c0b
xALPH / wRATRno gauge27R9bf8RYMPT1A4kUEazb6AimYZ7Tewh27LDcC6AmQz2BcodeHash c9c66eaaa5c72ce96c0cd3b06aafb7519a7e8c4ae58bf3e38d3012ec940a7c0b
USDC.eth / wRATRno gaugeUSDC (USDC.eth, bridged from Ethereum)xUYoveeQ8RDsBPcR2Pc4poSinezKfxCpQ2Hki9f438UfcodeHash c9c66eaaa5c72ce96c0cd3b06aafb7519a7e8c4ae58bf3e38d3012ec940a7c0b

Every pool shares one codeHash, and so does every gauge. A pool or gauge whose codeHash differs is not one of these.

Where every fee goes #

A swap pays the pool's fee on what goes in. The fee rounds up, so rounding never favours the trader. It splits at once, inside the swap:

  1. The LPs' share stays in the pool's reserves. Which share applies follows one switch: whether the pool has a gauge.
  2. The rest is the protocol's cut. It is taken out of the reserves and held in the pool until anyone sweeps it to the FeeExchange.
  3. At the FeeExchange, fees that are not wRATR wait in the basket until someone buys the whole basket for its posted price in wRATR. wRATR fees go straight on.
  4. Every unit of that wRATR reaches the FeeSplitter, which divides it at receipt: the lockers' share to the VeLock, the remainder to the Sink.

So the lockers and the Sink share the wRATR the protocol's cut becomes, not the raw tokens it was paid in. The LP share floors and the protocol takes the remainder, and the splitter floors the lockers' share and gives the Sink the remainder. No base unit is created or lost by either division.

As read at 2026-10-03, the settings give:

Pool stateLPs · lockers · sink, of every fee
GaugedALPH / wRATR50% · 35% · 15%
No gaugeUSDT.bsc / wRATR, ALPH / USDT.bsc, xALPH / wRATR, USDC.eth / wRATR95% · 3.5% · 1.5%

All three settings are settable within compiled bounds (see values), so the rule above is permanent and the percentages are not. An ungauged pool's LPs can never fall below 80% of the fee, and the lockers' share of the cut can never leave 33% to 80%.

Pair: the pool #

The pool contract is the one the source calls "the money file", because when money is stolen from a DEX it comes out of the pool, not governance. It is written to be the slowest file to change, and it is constant-product only: no stable curve, no iterative solver anywhere near the reserves.

Not Uniswap v2's fee accounting #

The curve is the familiar one. The output for an input of x after fee is

out = reserveOut × x_net ÷ (reserveIn + x_net), rounded down

What differs is where the fee goes. Uniswap v2 leaves the whole fee in the reserves and collects the protocol's part later, by minting new LP tokens when liquidity changes. Here the protocol's cut is split out in the swap itself: it leaves the reserves immediately and is held apart until swept. The LPs' share is the only part that stays in the pool. After the swap, the product of the reserves is checked again and must never fall.

Two arithmetic rules are checked on every review: multiply before dividing, because wRATR has 8 decimals and an early division truncates small amounts to zero; and round in the pool's favour, always.

What the authority can set #

  • The fee, bounded 1 to 100 basis points (0.01% to 1.00%). A captured key cannot set a 100% fee.
  • The ungauged LP share, bounded 80% to 100%.
  • The gauged LP share, bounded 0% to 100%. Zero is legitimate here: a gauged pool's LPs can be paid in emissions instead.
  • The gauge and where swept fees go. Both must be live contracts, so a wallet cannot be named as either.

Each is set in one act with no timelock, and each change emits an event carrying the old and new value, so every change is visible in the log. Setting a gauge switches the pool to its gauged share in one act. Clearing it returns the pool to the ungauged share and its 80% floor, and that direction is never the slow one.

The exit is never paused #

The protocol has an incident stop. It halts swaps and new deposits. It does not reach removeLiquidity: pausing new exposure is a safety stop, and pausing the exit would be confiscation. The first deposit into a pool permanently locks 1,000 base units of LP supply, so no pool can be emptied to a state that misprices the next depositor.

FeeExchange: the protocol never trades #

Fees paid in tokens other than wRATR are not sold by the protocol. The contract posts one fixed price, in wRATR, for the whole basket, and waits. Anyone may pay that price and take every allowlisted token it holds. There is no oracle, no TWAP, no slippage setting, no keeper, and so no trade of the protocol's for anyone to sandwich.

If the price is wrong, the basket simply sits until it is re-priced. The source calls that a loud, local, harmless failure, and a better one than a keeper trading at a manipulated price. An empty basket cannot be bought: paying for nothing reverts.

  • The allowlist holds at most eight tokens. On Alephium a contract's assets live in one output, so a long tail of junk tokens would make every call heavier. The cap is a security control, not tidiness.
  • wRATR can never be allowlisted. It is what the taker pays, so allowing it would let the price be paid and taken straight back.
  • Its revenue counter answers zero for any epoch it has no record of, never a nearby figure. A missing measurement must push spending down, not up.

FeeSplitter: lockers and sink #

The splitter divides wRATR at the moment it arrives and books the two shares separately. A later change to the lockers' share never re-splits money already received. Moving each share on is a separate, permissionless call, so a fault on one side can never hold up the other.

The lockers' share, lockBps, is bounded to 3,300–8,000 basis points of the protocol's cut, and the bounds are compiled in. The floor is not zero on purpose: at zero the authority could switch lockers' rewards off, turning the lock's yield from a property into a promise. The ceiling keeps at least 20% of the cut for the Sink.

There is no timelock. A setting change lands in one act. That was ruled across the whole stack: the bounds stay, because they are what protects anyone; the delay went, because a delay is a clumsy fix for a wrong value. The control now sits before signing: the change script reads the destination back from the chain and waits for an explicit yes.

The lockers' leg alone can be paused. While it is paused its share keeps accumulating in the splitter rather than being lost, and the Sink's leg is never paused.

Sink: the one-way door #

One receiving function and a counter. No withdrawal, no owner, no rescue, no migrate, no self-destruct. Each absence is deliberate: an owner field set to zero can be set again later, but one that was never declared cannot; self-destruct pays the remaining assets to an address, so it is a withdrawal under another name; and "recover mistakenly sent tokens" is how a one-way door grows a hinge.

It is never replaced. A future protocol points at a new sink, and this one keeps its record and its holdings for good. Nothing in it calls a burn, so wRATR's total supply is untouched and the bridge's backing arithmetic stays honest. Its received counter is its own claim; its wRATR balance on chain is the independent check. Both are public.

VeLock: power and fees #

A position's power is its amount times the epochs it has left, over the maximum term:

power = amount × (unlockEpoch − currentEpoch) ÷ 26

Power steps down once per epoch, at the turn. Twenty-six epochs is the longest term and one the shortest, both compiled; the maximum is a constant because power is measured against it, and changing it would silently reprice every position.

Every fee delivery is shared across all live power at that moment. The contract does this with two running accumulators rather than a list of holders, so a claim costs the same whether there are ten lockers or ten thousand, and there is no check-in step to miss. Its power function takes no time argument, ever: it reads now, and only now, so the class of bugs that comes from reading power at a past instant cannot be reached.

  • Withdraw returns principal only, and is never paused. Fee accounting is kept away from the principal on purpose: a fault on the fee side must never be able to trap anyone's principal. Fees owed stay claimable after withdrawal.
  • Fees never expire. A claim pays everything settled so far and can be made at any time.
  • No authority surface. No admin, upgrade, rescue, sweep or destroy function exists in the lock. The pause stops new positions, top-ups and extensions; it never stops a withdrawal or a claim.
  • Fees refuse a stale book. Expiries are processed as the books roll forward, at most 32 epochs per call (compiled, measured against the chain's gas ceiling). Fees are only booked once the books are current, so they are never shared against power that has already expired.

Voter: weight over time #

A vote is cast by a lock position, not a wallet, and a position's power divides across pools rather than copying. A vote accrues weight multiplied by the time it is in place, credited to the end of the epoch:

vote at t: + weight × (epochEnd − t)  ·  withdrawn at t′: − weight × (epochEnd − t′)

So a vote counts for exactly the time it stood. A vote cast in the final block counts for almost nothing, and the source makes the point that this is not an anti-snipe rule. There is no rule. Sniping is simply arithmetically worthless.

Withdrawing a vote debits the weight recorded when it was cast, never the position's power now. If it read live power, a voter could vote small, top up, and withdraw large, subtracting more than was ever credited: the shape behind several drains in the ve(3,3) record. Storing the weight makes that unrepresentable.

The Voter holds no tokens at all. A vote ledger that holds money is one somebody can be paid to corrupt; this one has nothing to take. It is also not governance: nothing is voted on except gauge weight. Adding, removing or replacing a gauge is an authority act, in one step, with no timelock.

Minter: the contract that says no #

The name is inherited and the source says plainly that it is wrong. In every fork of this lineage the Minter mints. This one cannot. wRATR is issued by the bridge, and no contract in the protocol contains a minting call of any kind. The Minter holds a finite pot of wRATR, funded from outside, and decides once per epoch how much of it may leave.

It has no drain #

No mint, no self-destruct, no rescue, no migrate, no owner withdrawal. The only way wRATR leaves is to a gauge the Voter lists. Funding is permissionless and can only add. The source states the cost and keeps it: a mis-funded pot cannot be recovered, because a drain that recovers a mistake is the same function that empties the pot.

startEpoch must never fail #

The weekly booking, startEpoch, carries no asset annotation, and the source calls that the most important line in the file. It touches no assets, creates no map entry and approves nothing, so it has nothing that can be short. It is also why the pot is a tracked counter rather than a balance read: reading a balance needs an asset frame, and this function is forbidden one. If anything ever added tokens around the counter, the counter would understate the pot, and an understated pot books less, never more.

It says no without reverting. Before the start epoch, after the end epoch, over the lifetime cap or with an empty pot, it books zero and records that it did. Called twice in an epoch it does nothing; called after missed epochs it jumps to the current one and books one epoch's budget, never a catch-up burst.

The split uses last epoch's frozen weights #

Each gauge's share of an epoch's budget is set by the votes of the epoch before. That epoch is closed, so its weights can no longer move. Splitting by the current epoch would divide by a total still growing as people vote, and whoever was paid last would be short. Because the weights are frozen this way, no voting blackout is needed while the budget is paid out. Shares are rounded down, and what rounds away stays in the pot.

The schedule #

Four values shape the stream, all fixed when this Minter was deployed: the taper applied each epoch, the lifetime cap on everything that may ever leave, the first epoch that can book, and the epoch after which nothing books. The fifth, the rate, is the only one that can move in place, and only by walking: at most 1% per epoch, once per epoch, compiled. The source's reason is that a lever with only an outer bound gets pulled all the way at once, while one that can only be walked is seen while it moves. Whatever the rate says, each epoch's booking is clamped to the lifetime cap and to what the pot actually holds.

This chapter does not print the schedule. It is the emissions budget and it can change. Read it from the Minter itself: getRatePerEpoch(), getEpochBudget(), pot(), lifetimeRemaining(), and the fields taperBps, lifetimeCap, startGateEpoch and endEpoch. A schedule beyond what those allow means a new Minter, and the Voter's gauges would then be paid from that one.

Gauge: the stream #

One gauge per gauged pool. It holds staked LP tokens and streams its share of each epoch's emission to stakers per second, in proportion to stake, over one epoch from the moment it arrives. Whatever an earlier stream had not yet paid folds into the new one, so nothing is stranded by timing.

When nobody is staked, the stream pauses. Nothing accrues to no one; the schedule shifts forward and resumes for the first staker back. Only the Minter can pay a gauge, and staking, unstaking and claiming touch only the caller's own stake. Unstaking is never paused.

The gauge has no self-destruct, rescue, drain, migrate or owner withdrawal. It holds other people's LP tokens, and the source names self-destruct as the one route by which an operator could ever reach a staker's principal. It is absent.

VoteIncentiveVault: paying for votes #

The first contract in the protocol that holds other people's money: tokens deposited by anyone to draw votes to a gauge. Deposits are permissionless and not refundable. A depositor who could pull an incentive back after seeing the votes it drew would be paying nothing for a real effect.

  • Split by the Voter's own numbers. Once an epoch closes, its deposits divide among the positions that voted for that gauge, by the weight-over-time each earned. The vault keeps no copy of the votes, so there is no second ledger to drift.
  • Nothing strands. If nobody voted for a gauge, its incentives roll forward to the same gauge's current epoch. Rounding dust rolls forward too, but only after a grace of five epochs (compiled), leaving four closed epochs in which to claim.
  • Claim before pruning. Pruning an old vote reclaims its deposit and deletes the record the vault pays from. The harbour claims first, then prunes, in one transaction.
  • The freeze can only stop. A slot can be frozen to halt payouts during an incident. Freezing can never move or redirect value. A guardian key may freeze; only the authority may unfreeze.

The authority key #

There is no governance body, no token vote on parameters and no proposal mechanism. One authority key, held by the operator, sets the settable values in this chapter, each in one act, each inside its compiled bounds. A guardian key exists to stop things quickly. It can pause, but never restart, move or release anything.

The pause is an incident stop for new exposure. Across the harbour:

Stops while pausedNever paused
Swap, add liquidityRemove liquidity
Open, add to or extend a lockWithdraw a matured lock, claim lock fees
VoteWithdraw a vote
Stake in a gaugeUnstake, claim emissions
Deposit a vote incentive, buy the fee basket 

What the key cannot do is the point of the bounds: take an ungauged pool's LPs below 80%, set a fee above 1%, take the lockers' share below 33% or above 80%, move a locker's principal, a staker's LP, the Minter's pot or anything in the Sink.

Values #

Settable values below were read from Alephium mainnet at 2026-10-03 15:46 UTC. The method column names the contract field or call that gives the live value.

ValueKind · read from
Lockers’ share7,000 bps of the protocol’s cut (70%)Settable FeeSplitter.getLockBps()
Swap fee30 bps (0.3%) on every poolSettable Pair.fee field, per pool
Ungauged LP share9,500 bps (95%) on every poolSettable Pair.lpShare field, per pool
Gauged LP share, ALPH / wRATR5,000 bps (50%)Settable Pair.currentLpShareBps()
Gauged pools1 of 5: ALPH / wRATRSettable Voter.getGaugeCount(), Pair.isGauged()
Fee-basket price0.05 wRATR for the whole basketSettable FeeExchange.getThreshold()
Fee-basket tokensALPH, USDT (USDT.bsc, bridged from BNB Chain), USDC (USDC.eth, bridged from Ethereum), xALPHSettable FeeExchange.getAllowlist()
Epoch length604,800,000 ms (7 days)Fixed at deploy VeLock.epochMs
Swap fee bounds1–100 bps (0.01%–1.00%)Compiled Pair.MIN_FEE_BPS, MAX_FEE_BPS
Ungauged LP share floor8,000 bps (80%)Compiled Pair.MIN_LP_SHARE_BPS
Gauged LP share floor0 bpsCompiled Pair.MIN_GAUGED_LP_SHARE_BPS
Lockers' share bounds3,300–8,000 bps of the cutCompiled FeeSplitter.MIN_LOCK_BPS, MAX_LOCK_BPS
Permanently locked LP1,000 base units per poolCompiled Pair.MIN_LIQUIDITY
Lock term1 to 26 epochsCompiled VeLock.MIN_TERM_EPOCHS, MAX_LOCK_EPOCHS
Roll per call32 epochsCompiled VeLock.MAX_ROLL
Fee-basket size8 tokens at mostCompiled FeeExchange.MAX_FEE_TOKENS
Emission rate step1% of the rate, once per epochCompiled Minter.NUDGE_BPS
Incentive residue grace5 epochsCompiled VoteIncentiveVault.RESIDUE_GRACE_EPOCHS

Reading state yourself #

Nothing in this chapter needs to be taken on trust. Every value is public, and any Alephium node or the explorer will show it.

  • Fields. A contract's state lists its fields in declaration order, immutable ones first. The names in this chapter are the names in the source.
  • Views. Calls such as FeeSplitter.getLockBps(), Pair.currentLpShareBps(), Pair.isGauged(), FeeExchange.getThreshold(), Voter.totalWeight(e) and VeLock.totalPower() cost nothing to read.
  • Events. Every setting change emits an event with the old and new value. A contract's event log is its full change history.
  • Epochs. An epoch is the block time in milliseconds divided by 604,800,000 and rounded down: weeks counted from 1 January 1970, which was a Thursday. That is why epochs turn at Thursday 00:00 UTC. Epoch 2961 began Thu, 01 Oct 2026 00:00 UTC.
  • Base units. wRATR has 8 decimals: 100,000,000 base units are one wRATR. Basis points are hundredths of a percent: 10,000 bps is 100%.

Carried down 2026-10-03. Rules here are as the contracts state them. Settable values are snapshots with their date; the contract is the live answer.