Pawl
Docs

Two integers, subtracted.

Pawl is an accrual engine with a website attached. This page is the engine: the three rules it will not break, the split it performs, and the four endpoints that expose both.

01The ratchet

Nobody is paid by a loop.

5,000 desks cannot be iterated inside a Solana transaction, so no payment is ever pushed. The protocol keeps one cumulative per slot. Each desk keeps one stamp. What a desk is owed is the difference.

owed(desk, slot)
  = wheel.cumulative[slot] − desk.stamp[slot]

A round adds perDesk to one cumulative — a single addition, whatever the number of desks. Claiming sets the stamp equal to the cumulative. Nothing else moves.

Monotonic

The cumulative never decreases and no stamp ever passes it, so `owed` can never come out negative. A stamp ahead of the wheel raises an error instead of being clamped to zero — clamping would destroy the only evidence that something upstream is broken.

Conserving

taken + still-owed + carried = bought, exactly. Every base unit ever acquired is in somebody's hands, still owed to them, or explicitly waiting for the next round. Nothing evaporates and nothing is conjured.

No backdating

A desk minted after round five is owed nothing from rounds one to five, because its stamp starts at the current cumulative rather than at zero. Starting it at zero is the most natural way to write this and it silently pays every new desk for history it was not present for.

See rule three fail on purpose →
02The flat split

One desk, one share.

What a round buys is divided equally across the desks that are live at that moment. Not by weight — the largest holder's desk receives exactly what the smallest one does.

The remainder is carried. It is added back into the next round on the same slot and split again. It is never dropped, and it is never handed to a subset of desks — with identical fractional parts, choosing who gets the odd units means choosing by array order.

A claim below the dust floor of 2,000,000 base units is refused, because moving a token costs rent for the account receiving it. Refusing loses nothing: the entitlement is derived from those same two integers and keeps accruing until it is worth taking.

bought       10.000000
desks        7

  each       1.428571
  × desks    9.999999
  carried    6 base units
             ──────────────
  sum        10.000000   = bought
03The rotation

The order is fixed and published.

Fees collect in a pot. The moment it clears 0.100000000 SOL the whole pot is spent on whichever of the 11 slots the wheel has reached, and the wheel advances one tooth.

The pot is never held. No treasury balance accumulates, there is no decision about when to deploy it, and slot eleven — the protocol's own coin — comes up exactly as often as any other.

The whole rule
slot = round mod 11

That is the entire selection logic. There is no scoring, no weighting by price, and nothing to tune — which means there is nothing to lean on.

04The API

Four endpoints, no key, no account, nothing stored. Everything the pages show is read through these, so anything you can see you can also check from a terminal.

GET/api/state

What this deployment is. ca stays null until a token exists, and minted is a known zero rather than a missing field.

{
  "name": "Pawl",
  "ticker": "PAWL",
  "ca": null,
  "minted": 0,
  "rounds": 0,
  "supplyCap": 5000,
  "slots": 11,
  "thresholdLamports": "100000000",
  "amounts": { "unit": "base units", "floats": false }
}
GET/api/rotation

The rotation with live prices. priceNano: null means we could not read it and is never zero; the own coin's mint is null because it does not exist.

{
  "count": 11,
  "readable": 10,
  "priceable": 10,
  "slots": [
    { "slot": 0, "symbol": "SPYx",
      "mint": "Xso…F2W", "decimals": 8,
      "own": false,
      "priceNano": "664120000000",
      "priceLabel": "$664.12" },
    { "slot": 10, "symbol": "PAWL",
      "mint": null, "own": true,
      "priceNano": null, "priceLabel": "—" }
  ]
}
POST/api/award

The flat split on its own. The remainder comes back as its own field rather than being folded into somebody's share.

// body
{ "bought": "1000000000", "desks": 7 }

// response
{ "perDesk": "142857142",
  "carry": "6",
  "conserves": true }
POST/api/ledger

Run a sequence through the engine and get the ledger back. Nothing is stored, so the same input always produces the same output — the only reason any of this is checkable by somebody who does not trust us.

// body
{ "operations": [
    { "op": "mint" },
    { "op": "turn", "bought": "1000000000" },
    { "op": "claim", "desk": 1, "slot": 0 }
  ] }

// response
{ "desks": 1, "rounds": 1,
  "positions": [ { "id": 1, "owed": ["0", …] } ],
  "books": { "bought": "1000000000",
             "claimed": "1000000000",
             "unclaimed": "0", "carry": "0",
             "conserves": true } }
Two things about the responses

Every amount is a string. These are integer base units and they routinely exceed what a JSON number can hold without losing digits. A balance that survives the network as a float is a balance that cannot be trusted to the last unit — and the last unit is the entire subject here.

Every response reports the invariant. conserves is computed by adding the columns up, not asserted. An endpoint that cannot say whether its own books balance is asking to be trusted, which is the opposite of the point.

05What is not built yet
No program is deployed. Nothing can be minted, and there is no button pretending otherwise.
No desk exists. The counter is zero and zero is the honest number.
No token is live. When one is, its address appears in exactly two places — pawl.lat and the official X.
Nothing is stored. There is no database and no account. The ledger endpoint is a pure function over what you send it.
Run the engine →About $PAWL
Rotation
reading prices…