the tab

documentation

Every fee opens a tab.
The tab is settled while you hold.

This is the whole mechanism, the whole reading path, and every number that can be changed. If something on the site is not explained here, that is a bug in this page.

The idea

A holder-rewards distribution usually reads everyone’s balance at one moment and splits a pot by what it saw. That asks people to be holding at an instant, which makes the optimal play buy-just-before and sell-just-after, and the reader cannot tell a month of holding from thirty seconds of it.

This measures something the reader already has and nobody uses: what you spent. Every trade pays a fee. That fee opens a tab in your name. The distribution pays your tab back down while you keep holding, and stops while you don’t.

The consequence worth stating plainly: a wallet can never draw out more than it has paid in. Not as a rule anyone enforces — as the shape of the thing. That single fact removes almost every attack on a distribution before it needs a rule.

This is a rebate, not a yield. It gives back part of a cost you already paid. If trading volume is zero, the pot is zero and nothing is owed to anyone. Nothing here promises a return.

The three numbers

paidEvery fee this wallet has spent on this token, in wei. Monotone — it only goes up. It is the ceiling on everything that can ever come back.
drawnHow much of that has already been returned, summed from every published epoch. Never exceeds paid; the code refuses to write a draw that would.
openpaid − drawn. The tab. When it reaches zero the tab is closed and takes nothing further until the wallet trades again.

And one ratio that decides how fast a tab is drawn down:

held = min(1, balance_now / tokens_ever_bought)

Hold everything you bought and you draw at full weight. Sell half, draw at half. Sell it all and you draw nothing — but the tab stays open, because you did pay those fees and they are still yours if you come back.

Tokens that arrive by plain transfer count toward balance but not toward bought, and held is capped at 1, so sending yourself tokens cannot push you past full and cannot open a tab. No fee, no tab.

Weight and ceiling

Two separate things, deliberately kept apart.

weight  = open × held          // how big your share of the pot is
ceiling = open × DRAW_RATE     // the most you can be handed this epoch

DRAW_RATE is 2500 basis points — a quarter. So a tab takes at least four epochs of continuous holding to settle in full, and stops the moment the holding does. Without that, a holder gets the whole fee back the week they buy and then has no reason left to be here: a rebate, but not a reason to hold.

The ceiling is deliberately not scaled by retention. That is what makes the waterfall do real work — a wallet holding everything weighs the most against its own ceiling, so it fills first, and what it cannot take runs sideways.

Weight is linear in both terms. Two wallets holding half a bag each weigh precisely what one wallet holding the whole bag weighs. Splitting across addresses gains nothing, so nothing here goes looking for people who have — and no innocent wallet gets caught by a heuristic that guesses wrong.

Not square root. Square-root weighting sounds fairer and is farmed by exactly the split above: two half-bags outweigh one whole one. Linear, and no clustering.

The waterfall

One round would be a division. The ceilings make it a cascade:

  1. Divide what is left in proportion to weight, across everyone still under their ceiling.
  2. Anyone whose share would reach their ceiling takes exactly their ceiling and drops out.
  3. If anyone dropped out, go again with what remains and whoever is left.
  4. When a round caps nobody, that division stands and the epoch is done.

It always terminates: every round that caps somebody removes at least one wallet. If the pot outlasts every ceiling, what is left rolls into the next epoch rather than being forced on anyone.

The animation on the front page is this routine, run in the browser at a hundred pot sizes over the live standing. It is not a drawing of the algorithm; it is the algorithm.

Remainders and dust

Everything is integer wei. Nothing in the path touches a float — at eighteen decimals a float64 stops being able to count individual units somewhere above 0.009 of a token, and being wrong in the ninth decimal place is how you end up in somebody’s screenshot.

Integer division leaves a remainder. It is handed out by largest remainder, with the address as a deterministic tiebreak, so two people running this get byte-identical files.

Some payments come out smaller than the gas needed to send them. Those are not paid and not kept: they are named in the ledger and roll into the next pot, with the tab left open, so a small holder accumulates instead of getting nothing forever.

paid + rollover === pot

Asserted before anything is published, and over four hundred randomly generated epochs in the test suite. An epoch that does not balance to the wei refuses to publish at all.

What it survives

wash tradingEvery round trip costs two full fees and returns a fraction of one, and it leaves the wallet holding nothing, which sets its weight to zero. The return rate is printed on the front page, computed from the epoch, and is always under one.
splitting across walletsWeight is linear. Two halves weigh what one whole weighs. Nothing gained, so nothing policed.
buying just before closeWorks, and is fine. You paid the fee; you get part of that fee back, bounded by what you spent. There is no snapshot to stand on because the reward is not a share of a prize — it is a discount on your own bill.
airdrops inNo fee was paid, so no tab opens. held is capped at 1, so topping a wallet up cannot push it past full.
shuffling between your own walletsKeeps a tab’s retention alive, and gains nothing: the cap is on fees paid, not on wallets, and the tokens had to be bought by somebody who paid a fee to get them.

What it reads, and what it can’t

Balances are rebuilt from Transfer logs, which are deltas, not balances. Miss one log and the number is wrong forever and nothing inside the reconstruction would ever notice.

So every wallet is checked against a live balanceOf before anyone is paid. A wallet whose reconstruction disagrees with the chain is held back — named in the ledger, paid nothing that epoch — rather than paid on a number nobody can stand behind.

A transfer with the pool on one side is a trade. A transfer between two ordinary wallets is not, and opens no tab.

What a Transfer log does not carry is the price. The cost of a trade is the other side of the same transaction — the fee asset moving in or out of the pool — so the reader groups those by transaction hash to get each trade’s ETH leg. The rate that turns a leg into a fee is FEE_BPS, a stated setting, printed on the page as a setting. Nothing pretends to have read it off the venue.

Native-coin pairs. If the pair trades against the chain’s own coin rather than a wrapped token, there are no Transfer logs for the ETH leg and per-trade cost cannot be read without a trace API. In that configuration trades carry a zero fee and the operator has to point the reader at a leg source. It says so rather than inventing one.

Where the pot comes from

The pot is whatever has actually arrived at the treasury address. Every claim counted is a transaction that was looked up, with its to checked against the treasury. A claim that does not land at that address is not counted, whoever says otherwise. Outgoing payments are never counted — only what arrives.

fee asset transfersFees paid in WETH or another token: Transfer logs into the treasury. Exact, cheap and complete. The good case.
native transfersA block scan for transactions paying the treasury. Value forwarded inside a contract call does not appear in a block’s transaction list, so this mode reports itself as incomplete rather than quietly under-counting.
claim listA curated list of claim hashes, each verified on chain: exists, did not revert, pays the treasury, moves value. The fallback that always works.

It is not money from new buyers. There is nothing to stake, nothing to lock, nothing to bond, and no contract that holds anyone’s tokens. A mechanism that pays existing holders out of incoming buyers’ money is a Ponzi regardless of how elegant the weighting is.

The ledger

State is not a database. It is the stack of closed epochs, and the running total of what each wallet has drawn is recomputed from them on every boot. An epoch is published once; a correction is a new epoch with a note, not a quiet edit to an old one.

The ledger goes up before the money moves, so anyone who saved a copy can hold it against the payment transactions afterwards. Every wallet that was considered is in it, including the ones paid nothing, and why:

tab settled in fulleverything that wallet paid has come back
reached this epoch ceilingtook its quarter; more is available next epoch
sold out, tab open but drawing nothingholds none of what it bought
under the send floor, rolled forwardsmaller than the gas to send it
held back: balance disagreed with chainreconstruction failed the check

Settings

RPC_URLJSON-RPC endpoint. Read from the environment at boot. No request can influence it — an endpoint that fetches a URL somebody handed it is an SSRF with better manners, and there is no flag to turn the guard off.
TOKENthe coin
POOLthe pair it trades against
TREASURYthe address creator fees are claimed to
FEE_ASSETthe asset fees are paid in, or the word native
FEE_BPSthe venue’s swap fee in basis points. Stated, not read.
FROM_BLOCKthe block the token was created in
CLAIM_TXSoptional comma-separated claim hashes, each verified on chain
LEDGER_DIRwhere closed epochs live. Default ./ledgers
EPOCH_DAYShow often an epoch closes. Default 7
MIN_PAYthe send floor in wei. Below it, a payment rolls instead
TRUST_PROXY1 only behind a proxy you control

And two constants in the source rather than the environment, because changing them changes what the mechanism is and that should be a commit, not a deploy flag: DRAW_RATE = 2500 and the linear weighting.

The API

GET only. A POST gets a 405 and nothing else. Amounts travel as exact decimal strings alongside their raw wei, never as JSON numbers.

/api/epochthe open epoch: pot, source, every claim transaction, the full standing, the waterfall’s round count, and whether it balances
/api/tab?addr=one wallet: fees paid, already back, open tab, ceiling, retention, what this epoch pays it, and its rank
/api/tokenmarket figures, labelled as read from a public venue; fees claimed, distributed and rolling
/api/ledger.csvthe ledger as a file
/healthzok

No lookup is written to disk. There is no route that takes free text and returns generated text, and none that can be made to fetch something on a caller’s behalf.

Running it

node test-tab.mjs      # the book and the waterfall
node test-http.mjs     # the routes, and the promises the source has to keep
node dev.js            # local preview, invented history, real engine

RPC_URL=… TOKEN=… POOL=… TREASURY=… FEE_ASSET=… FEE_BPS=… FROM_BLOCK=… \
  node live.js         # the deployment

node close.js 2026-09-05
  → ledgers/2026-09-05.json     replayed by the next epoch
  → out/…-ledger.csv            the human-readable record
  → out/…-pay.csv               address, amount — for your own wallet

Zero dependencies. Node’s standard library and nothing else. The page loads no font and no script from anybody else, because a page that says it is not tracking you and then calls a CDN has broken that promise before it finishes rendering.

What it holds

Nothing.

No keys. No signing. No connect-wallet button, no account, no record of who looked up what. There is no route in this codebase that can spend anything, and the test suite greps its own source for private keys, seed phrases and signing calls and fails the build if it finds one.

close.js writes a file. A person sends the payments from their own wallet, on their own machine. That is deliberately unglamorous: every custody disaster in this category started with something that did it for them.

Known edges

Rewards depend on fees earned. Nothing here is financial advice.