Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

vesting source realm

Realm vesting escrows native GNOT for a named beneficiary and releases it in DISCRETE, INDIVIDUALLY DATED TRANCHES — ...

Overview

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)

  1. THE CLOCK IS runtime.ChainHeight(), never a call argument. A realm that lets a caller choose `now` has no delay at all.
  2. HEIGHTS ARE THE ONLY UNIT. No timestamp appears anywhere.
  3. 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.
  4. 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.
  5. 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.

Constants 4

const StatusPending, StatusReleased, StatusRevoked

1const (
2	StatusPending  = "pending"
3	StatusReleased = "released"
4	StatusRevoked  = "revoked"
5)
source

Tranche status values. A tranche is created pending and reaches exactly one terminal state; both terminals are frozen.

const MinDelayBlocks, MaxDelayBlocks, MaxTranches, MaxOpenTranches, MaxOpenPerFunder, MaxTitleLen, MaxMemoLen, MaxSpecLen, MaxListed

 1const (
 2	// MinDelayBlocks is the floor on how far ahead the FIRST tranche may
 3	// fall. One block is enough to prove the delay is enforced while
 4	// keeping the realm exercisable on a live chain.
 5	MinDelayBlocks = int64(1)
 6
 7	// MaxDelayBlocks caps the furthest tranche at roughly 10.8 years at
 8	// pearl-1's ~3.42s blocks. It also bounds now+delay far inside int64.
 9	MaxDelayBlocks = int64(100_000_000)
10
11	// MaxTranches bounds one schedule. It bounds the Fund transaction's
12	// work, the RevokeAll loop, and the schedule detail render.
13	MaxTranches = 64
14
15	// MaxOpenTranches caps pending tranches across EVERY schedule at once
16	// — it is a whole-realm limit, not a per-schedule one, because it is
17	// duebook's open-deferral cap. Fund fails when a new schedule would
18	// cross it. Must be <= duebook.MaxOpenLimit.
19	MaxOpenTranches = 5000
20
21	// MaxOpenPerFunder bounds how many tranches ONE funder may hold
22	// pending at once, so that MaxOpenTranches cannot be consumed by a
23	// single account.
24	//
25	// Without this bound the whole-realm cap is a denial-of-service
26	// surface that costs nothing to use: 79 Fund calls of 64 one-ugnot
27	// tranches fill the realm, Fund then aborts for everybody, and the
28	// attacker recovers 100% of the escrow fee-free with RevokeAll. Two
29	// deliberate choices elsewhere remove every recovery path — ttl is
30	// always 0, so duebook's permissionless Expire valve never opens, and
31	// MaxOpenTranches is a compile-time constant with no admin setter. The
32	// denial would therefore be total and permanent, which is out of all
33	// proportion to its cost.
34	//
35	// 256 leaves one funder four full MaxTranches schedules open at once —
36	// generous for the intended use — while exhausting the realm now takes
37	// at least ceil(5000/256) = 20 separately funded accounts.
38	//
39	// That is a SYBIL BOUND and nothing more, so the residual is quantified
40	// here rather than left to be inferred from the 20: the cap counts
41	// TRANCHES, never value. parseSpec accepts amount == 1, so 256 pending
42	// tranches cost 256ugnot and all 20 accounts together escrow about
43	// 5000ugnot — every bit of it recoverable fee-free, since revokeOne
44	// refunds at 0 bps. The real cost of the realm-wide denial is therefore
45	// ~80 Fund transactions' gas and 20 funded addresses, NOT capital.
46	// Anyone sizing the residual risk from the "20 accounts" figure alone
47	// will size it wrong by orders of magnitude.
48	//
49	// Bounding the attack by VALUE instead would take a per-tranche minimum
50	// amount. That is a change to what the application is — it would price
51	// small legitimate tranches out — so it is a reviewer's decision, not a
52	// routine fix, and it is deliberately not made here.
53	MaxOpenPerFunder = 256
54
55	// MaxTitleLen bounds a schedule title. Titles are charset-restricted
56	// so they are list-safe in Render without escaping.
57	MaxTitleLen = 80
58
59	// MaxMemoLen bounds the free-text memo. It is a BYTE length, not a
60	// rune count, because its job is to bound storage.
61	MaxMemoLen = 1000
62
63	// MaxSpecLen bounds the tranche specification string, so parsing cost
64	// is bounded before any parsing begins.
65	MaxSpecLen = 1400
66
67	// MaxListed caps how many entries a single view or render returns.
68	MaxListed = 50
69)
source

Bounds. All are compile-time constants: none is admin-mutable, so a schedule's shape cannot be changed under its funder or beneficiary.

const Denom

1const Denom = "ugnot"
source

Denom is the only asset this realm escrows.

const MaxFeeBps

1const MaxFeeBps = int64(1000)
source

MaxFeeBps is the hard protocol-fee cap: 1000 bps = 10%.

Functions 32

func AcceptAdmin

crossing Action
1func AcceptAdmin(cur realm)
source

AcceptAdmin completes a handover. Only the proposed account may call it.

func BalanceOf

Action
1func BalanceOf(addr address) int64
source

BalanceOf returns addr's claimable balance (released tranches for a beneficiary, revocations for a funder).

func EscrowTotal

Action
1func EscrowTotal() int64
source

EscrowTotal returns the escrow held by pending tranches (the E term).

func FeeBps

Action
1func FeeBps() int64
source

FeeBps returns the fee that will be snapshotted into newly funded schedules. Existing schedules keep their own snapshot.

func FeeOn

Action
1func FeeOn(amount int64) (fee, credited int64)
source

FeeOn previews the fee and net credit for a tranche of amount at the CURRENT FeeBps.

func Fund

crossing Action
1func Fund(cur realm, beneficiary address, title, memo, spec string, revocable bool, maxFeeBps int64) int64
source

Fund creates a vesting schedule in a single transaction: it escrows the attached GNOT, registers every tranche with duebook, and returns the new schedule id. Only direct EOA calls with -send are accepted.

spec lists the tranches as "delay:amount" pairs separated by commas, for example "100:500,200:500,300:1000". Delays are in BLOCKS FROM NOW and must be STRICTLY INCREASING — two payments at the same height are one tranche with the amounts added. Every amount must be positive, and the amounts must sum to EXACTLY the attached send: this realm will not silently absorb a surplus or create an underfunded schedule.

revocable decides, permanently, whether the funder may reclaim not-yet-due tranches. There is no setter; an irrevocable schedule stays irrevocable.

maxFeeBps is the ceiling the caller signs for. Funding is rejected if the live fee exceeds it, which closes the race where an admin sequences a fee raise ahead of this transaction. Pass MaxFeeBps to accept any legal fee.

func FundersWithOpenTranches

Action
1func FundersWithOpenTranches() int
source

FundersWithOpenTranches returns how many distinct funders hold at least one pending tranche. It is the width of the per-funder cap: exhausting MaxOpenTranches requires this to reach ceil(MaxOpenTranches/MaxOpenPerFunder).

func Height

Action
1func Height() int64
source

Height returns the current chain height. Delays in Fund's spec are relative to the height at which Fund executes, so this is an aid for composing a spec, not an input to one.

func Held

Action
1func Held() int64
source

Held returns the ugnot actually held at this realm's address (H).

func Liabilities

Action
1func Liabilities() int64
source

Liabilities returns everything this realm owes: EscrowTotal + UsersTotal + FeesAccrued.

func Memo

Action
1func Memo(id int64) string
source

Memo returns a schedule's raw memo text.

func OpenTrancheCount

Action
1func OpenTrancheCount() int
source

OpenTrancheCount returns how many tranches are pending across the whole realm, against the MaxOpenTranches cap.

func OpenTranchesOfFunder

Action
1func OpenTranchesOfFunder(funder address) int64
source

OpenTranchesOfFunder returns how many tranches funder currently holds pending, against the MaxOpenPerFunder cap. A funder can read this before funding to see the headroom they have left.

func ProposeAdmin

crossing Action
1func ProposeAdmin(cur realm, next address)
source

ProposeAdmin nominates a successor admin. The handover takes effect only when that account calls AcceptAdmin, so a typo cannot strand the role. Admin only; the zero address clears a pending proposal.

func ReleasableNow

Action
1func ReleasableNow() string
source

ReleasableNow lists tranche ids that are due and still pending, up to MaxListed, newline-separated. Anyone may Release each of them.

func Release

crossing Action
1func Release(cur realm, trancheID int64) string
source

Release pays out a due tranche. ANYONE may call it: the value is credited to the schedule's RECORDED BENEFICIARY, never to the caller, so a stranger calling this gains nothing and the beneficiary needs no liveness of their own. The protocol fee snapshotted at funding is charged here.

Aborts if the tranche does not exist, is not due yet, or has already been released or revoked — the last of those is duebook's guarantee, not a check written here.

func Render

1func Render(path string) string
source

Render shows the realm at "" and a schedule detail at "<id>". Free text (titles are charset-restricted; memos are not) passes through the ecosystem sanitizer before it reaches markdown.

func Revoke

crossing Action
1func Revoke(cur realm, trancheID int64) string
source

Revoke returns a NOT-YET-DUE tranche to the funder's claimable balance, fee-free. Only the funder may call it, and only on a schedule created revocable.

A tranche whose due height has arrived can never be revoked, even though duebook's Cancel would consume it: that refusal is this realm's, and it is what stops a funder front-running the beneficiary's Release to claw back value already vested.

func RevokeAll

crossing Action
1func RevokeAll(cur realm, scheduleID int64) string
source

RevokeAll returns every not-yet-due tranche of a schedule to the funder's claimable balance in one transaction, and returns how many were revoked. Already-due and already-settled tranches are skipped, not an error. Funder only, revocable schedules only. Aborts if nothing was revocable, so a no-op cannot be mistaken for success.

func SetFeeBps

crossing Action
1func SetFeeBps(cur realm, bps int64)
source

SetFeeBps sets the protocol fee snapshotted into FUTURE schedules at funding. Existing schedules keep the fee they were funded under, so an in-flight schedule's economics cannot be changed. Admin only.

func SetFeeRecipient

crossing Action
1func SetFeeRecipient(cur realm, next address)
source

SetFeeRecipient re-points the fee/surplus role. The pot accrued under the OUTGOING recipient is settled to that recipient's own claimable balance first, so the successor inherits the role and not the earnings. Admin only; the zero address is rejected.

func SweepDenom

crossing Action
1func SweepDenom(cur realm, denom string) string
source

SweepDenom sends the surplus of a single denomination to the fee recipient. For ugnot only the excess over Liabilities() moves — escrowed tranches and claimable balances are untouchable by construction. Other denominations move wholly. Only the fee recipient may call it.

func TrancheInfo

Action
1func TrancheInfo(trancheID int64) (scheduleID, seq, amount, dueAt int64, status string, settledAt int64)
source

TrancheInfo returns one tranche's fields by value. It answers for settled tranches too, after duebook has consumed the deferral.

func TranchesOf

Action
1func TranchesOf(id int64) string
source

TranchesOf lists a schedule's tranches, newline-separated, one per line as "id seq amount dueAt status".

func UsersTotal

Action
1func UsersTotal() int64
source

UsersTotal returns the sum of all claimable balances (the U term).

func VestedOf

Action
1func VestedOf(id int64) (vested, unvested int64)
source

VestedOf returns, for a schedule, the amount whose due height has arrived and which is therefore no longer revocable — whether or not it has been released yet — alongside the amount still revocable by the funder.

func Withdraw

crossing Action
1func Withdraw(cur realm, amount int64) string
source

Withdraw sends amount ugnot of the caller's claimable balance (released tranches for a beneficiary, revocations for a funder) to the caller.

func WithdrawAll

crossing Action
1func WithdrawAll(cur realm) string
source

WithdrawAll sends the caller's entire claimable balance to the caller. Aborts if there is nothing to withdraw.

func WithdrawFees

crossing Action
1func WithdrawFees(cur realm) string
source

WithdrawFees sends the accrued fee pot to the fee recipient. Only the fee recipient may call it.

Imports 10

Source Files 3