Realm vesting escrows native GNOT for a named beneficiary and releases it in DISCRETE, INDIVIDUALLY DATED TRANCHES — each payable exactly once, at or after its own due height.
DISCRETE, NOT LINEAR — read this first
Everywhere else in this ecosystem "vesting" means CONTINUOUS LINEAR ACCRUAL: a formula of the form amount * elapsed / duration, evaluated whenever someone asks. This realm does no such arithmetic. A funder states an explicit list of (delay, amount) pairs at funding time, and nothing accrues between them: a tranche is worth zero until its due height and its full amount from that height on.
That is a deliberate choice, not a simplification:
- It is the shape real schedules actually have — cliffs, quarterly tranches, milestone payments, a lump sum on a date.
- It has no `amount * elapsed` term, so the int64 overflow class that has already shipped a NEGATIVE vested amount in two independent ecosystem packages cannot occur here: that MULTIPLICATIVE class is designed out rather than guarded against. The additive sums that do exist are guarded, not designed out — Fund checks the tranche total and the escrow total with checkedAdd, each with its own panic, before any state changes. Both halves are true and neither replaces the other.
- It maps exactly onto duebook, whose exactly-once Claim is what makes a double release structurally impossible rather than merely checked.
There is no default curve to be surprised by: every tranche is stated explicitly at creation, or the schedule does not exist.
COMPOSITION
This realm owns only the schedule state machine. Everything else is delegated to primitives already audited and deployed:
Example
1duebook — the entire schedule: due heights, and exactly-once release
2coinio — coin custody: escrow-in, payout, surplus sweep
3feeledger — claimable balances and the fee pot
4sanitize — funder-supplied free text, before it reaches markdown
It is duebook's FIRST PRODUCTIVE CONSUMER. duebook_demo is the falsification experiment and holds no coins by design; this realm is the first to put value behind the exactly-once claim.
LIFECYCLE
Example
1Fund (EOA + -send) : one transaction creates the schedule, escrows
2 the exact total, and registers every tranche
3 in duebook. There is no draft state and no
4 half-funded schedule.
5Release (ANYONE) : once a tranche is due, anyone may release it.
6 The value is credited to the RECORDED
7 BENEFICIARY, never to the caller.
8Revoke (funder only) : returns a NOT-YET-DUE tranche to the funder,
9 and only if the schedule was created
10 revocable. A due tranche can never be revoked.
11Withdraw / WithdrawAll : pays out the caller's own claimable balance.
WHY NOTHING CAN BE TRAPPED, AND WHY THERE IS NO EXPIRY
Tranches are scheduled with duebook's ttl == 0, so they NEVER expire. Vested value cannot be lost by a beneficiary who is slow, offline, or gone. The sibling realms in this portfolio use a permissionless expiry valve to guarantee escrow is never trapped; here the valve is different and stronger: Release is PERMISSIONLESS, so every tranche becomes claimable-by-anyone the moment it is due, and the schedule drains itself without depending on any particular actor staying alive. Combined with "a due tranche can never be revoked", every ugnot in escrow has exactly one reachable destination and a height after which nobody can block it.
VESTED MEANS IRREVOCABLE, WITH NO RACE
Revoke refuses any tranche whose due height has arrived, even though duebook's Cancel would happily consume it. THAT CHECK IS THIS REALM'S, not duebook's. Without it a funder could watch for the due height and front-run the beneficiary's Release to claw back value already earned. Likewise, `revocable` is fixed at funding and has no setter: an irrevocable schedule cannot be made revocable later, which is the only thing that makes the promise worth anything.
AUTHORIZATION
Every identity is derived from the crossing entrypoint's cur.Previous().Address(). No function takes a caller identity as a parameter, so acting as another account is impossible by construction. Release is permissionless *because* it pays the recorded beneficiary: the caller's identity is not used to route value, so there is nothing for a stranger to gain.
HOW IT WIRES DUEBOOK CORRECTLY (duebook's five-point consumer contract)
- THE CLOCK IS runtime.ChainHeight(), never a call argument. A realm that lets a caller choose `now` has no delay at all.
- HEIGHTS ARE THE ONLY UNIT. No timestamp appears anywhere.
- THE *Book IS NEVER EXPORTED. It is an unexported package var and is never returned across a realm boundary; handing it out would hand out the right to schedule, cancel and claim.
- THE ACTOR IS AUTHORIZED from cur.Previous().Address(); the duebook owner recorded at Schedule is the funder, and duebook checks Cancel against that recorded value, never against an argument.
- THE EFFECT HAPPENS AFTER A SUCCESSFUL Claim, IN THE SAME TRANSACTION.
The deferral PAYLOAD is a human-readable label for events and rendering. THIS REALM NEVER PARSES IT. Every authoritative fact about a tranche — its amount, its schedule, its beneficiary — comes from this realm's own `tranches` tree, keyed by the deferral ID that duebook guarantees is never reused.
MONETARY INVARIANT (conservation)
Let H be ugnot held at this realm's address, E = escrowTotal (the sum of amounts over tranches still pending), U the ledger's claimable balances, F the fee pot, and S >= 0 out-of-band surplus:
Example
1H == E + U + F + S
Fund raises H and E together (coinio.Receive is the receipt-guaranteed shape). Release moves one tranche amount from E into U+F, with feeledger guaranteeing credited + fee == amount. Revoke moves a tranche amount from E into U, fee-free. Withdrawals debit the ledger BEFORE coinio.Payout moves the identical amount out (checks-effects-interactions). Any panic aborts the whole transaction. This realm never issues or removes coins. Surplus is recoverable only via SweepDenom, which reserves Liabilities().
ONLY GNOT
Fund rejects any envelope that is not exactly one positive ugnot coin. Foreign denominations sit in surplus and are sweepable.