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.
| Contract | Address · codeHash |
|---|---|
| PairFactoryCreates pools; every pool is a copy of one template | zANiQU3bdSeRcoWfHdPRosqfm2LUD4udxhsxg6TsGHwDcodeHash 44e41eb2efc3577148ab728434db38901d419074f4283388d68e35921ee2083e |
| FeeExchangeHolds the fee basket at a posted wRATR price | xoeufYzS2jEmnQRwyUrTB9gsipfNL8GnxMUUEwXkiqsMcodeHash 26f45fe0866f4ae32435ed770c92e8b5d267072c6ac9cc7845fa254db0604226 |
| FeeSplitterDivides wRATR between lockers and the Sink | 25RHugjfH5uiFHdmYEg7d7kB75JwjdbuAfX5QTKmMxxZmcodeHash cf35213c7884cf634ff41ce6d597fb8c3992ede67f2477b6683528d94a76ac2f |
| SinkThe one-way door | 22RouFo2WazkebbaCFtcmrKhsAqBC1HH28eDGhN7bs1YBcodeHash 15976c02bb4451eb47a342d35efaf27ea692043d1f08a979ce96eb0f4d15bfa0 |
| VeLockLock positions, power and lockers’ fees | 29PFEoZdTBeoMccnpioeegbv1n88fnx6Ava6MGMq8MtbHcodeHash 400a904394400d25248ebea856a9da91ddc7bfab1984f6204d91109beec39ee0 |
| VoterGauge registry and the vote ledger | u6hwrPBg9nFqib9z3e4Ph7UikpTUFVfUwMAA7PxdxsrfcodeHash c3c56abcbfa8dcaec99dcc8d707f3a332824697b511a3b6dbe6c6ed064dedac3 |
| MinterThe emission pot and its weekly booking | 21kwbuLwXVwKFjpV7nVmqLJVGVwphed7PoVeymUs1WQ23codeHash e78f7dfc17bfc9e4ff88d00081c7a7057985285555317b78f5f13f547ddfc575 |
| VoteIncentiveVaultVote incentives, split by vote weight | 22netg82X5F4wAGd8ETFdGUsY3atPPhfS5LjnYPyusXSFcodeHash 44c71a75de48686a4a31787b0a435c603e71cb444686f47354dde6d73f479629 |
| ProtocolConfigThe authority, the guardian and the pause | 25YV8Z9chs4Sk7TRq7WWUnMcXZfKHkWMFKmytsvpJgV6fcodeHash 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.
| Pool | Address · gauge |
|---|---|
| ALPH / wRATRgauged | 27Ary2mu4yuHtrpdoMDkLf7nWbcJn9ubULWNfiLEFSzbZcodeHash 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 gauge | 27R9bf8RYMPT1A4kUEazb6AimYZ7Tewh27LDcC6AmQz2BcodeHash 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:
- The LPs' share stays in the pool's reserves. Which share applies follows one switch: whether the pool has a gauge.
- 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.
- 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.
- 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 state | LPs · lockers · sink, of every fee |
|---|---|
| GaugedALPH / wRATR | 50% · 35% · 15% |
| No gaugeUSDT.bsc / wRATR, ALPH / USDT.bsc, xALPH / wRATR, USDC.eth / wRATR | 95% · 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.
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.
| Value | Kind · read from |
|---|---|
| Lockers’ share7,000 bps of the protocol’s cut (70%) | Settable FeeSplitter.getLockBps() |
| Swap fee30 bps (0.3%) on every pool | Settable Pair.fee field, per pool |
| Ungauged LP share9,500 bps (95%) on every pool | Settable Pair.lpShare field, per pool |
| Gauged LP share, ALPH / wRATR5,000 bps (50%) | Settable Pair.currentLpShareBps() |
| Gauged pools1 of 5: ALPH / wRATR | Settable Voter.getGaugeCount(), Pair.isGauged() |
| Fee-basket price0.05 wRATR for the whole basket | Settable FeeExchange.getThreshold() |
| Fee-basket tokensALPH, USDT (USDT.bsc, bridged from BNB Chain), USDC (USDC.eth, bridged from Ethereum), xALPH | Settable 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 bps | Compiled Pair.MIN_GAUGED_LP_SHARE_BPS |
| Lockers' share bounds3,300–8,000 bps of the cut | Compiled FeeSplitter.MIN_LOCK_BPS, MAX_LOCK_BPS |
| Permanently locked LP1,000 base units per pool | Compiled Pair.MIN_LIQUIDITY |
| Lock term1 to 26 epochs | Compiled VeLock.MIN_TERM_EPOCHS, MAX_LOCK_EPOCHS |
| Roll per call32 epochs | Compiled VeLock.MAX_ROLL |
| Fee-basket size8 tokens at most | Compiled FeeExchange.MAX_FEE_TOKENS |
| Emission rate step1% of the rate, once per epoch | Compiled Minter.NUDGE_BPS |
| Incentive residue grace5 epochs | Compiled 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)andVeLock.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.