RealmPath
untyped stringRealmPath is this realm's own path, used to build Render links.
Value
"/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/invoices"
Denom
untyped stringDenom is the only asset this realm holds.
Value
"ugnot"
MaxFeeBps
int64MaxFeeBps is the hard ceiling on any fee this realm could ever charge, enforced by the feeledger the realm is constructed with — a feeBps above it is refused by the primitive's own validation, not by a check in this file. 500 bps = 5%.
Value
500
SettlementFeeBps
int64SettlementFeeBps is charged on each payment, on the gross, floor-rounded in the ISSUER's favour: 100 bps = 1%. It matches the most conservative live fee in this portfolio (fee\_split, crowdfund, budgets, payroll). The ISSUER bears it, not the payer: a payment of N reduces the outstanding balance by exactly N, and the issuer is credited N minus the fee. A payer's debt must be discharged by what the payer sent, or the arithmetic of "paid in full" would depend on this realm's fee schedule. It is a compile-time constant with no setter, so an invoice's economics cannot be changed after it is issued. The fee is floored PER PAYMENT, so splitting a payment can lower the total fee. Each payment boundary forfeits at most 1 ugnot, and MinPayment bounds how many boundaries an invoice admits. MEASURED on onyx-1, not reasoned: 10,990 paid at once charges 109, while ten payments of 1,099 charge 100. The beneficiary is the ISSUER, who is credited the difference and does not control the payment schedule; the payer gains nothing, because the outstanding balance falls by the gross either way. Gas for one extra payment costs more than the 1 ugnot it saves, so this is priced out rather than prevented, and a colluding issuer/payer pair would spend more to exploit it than they recovered. A final settlement below the fee denominator pays zero fee for that same reason and is NOT a separate case: floor(99/100) is 0 whether or not MinPayment's settle-exactly clause admitted it, so that clause forfeits nothing on its own.
Value
100
FeeCollector
.uverse.addressFeeCollector receives withdrawn fees and swept surplus. Compile-time constant: there is no setter and no transfer path. It is the deploying namespace's address — stated plainly: the operator of this realm. NOTE FOR ANY RE-NAMESPACED PORT: this is the address-as-data hazard that a namespace substitution rewrites silently while every byte-comparison still reports the tree faithful, because the rewrite IS the intended transformation. Re-state it deliberately per chain; never let it be derived.
Value
<gnolang.StringValue>
MinInvoiceAmount
int64MinInvoiceAmount is the smallest invoice that may be issued. Below the fee denominator a 1% fee floors to zero, so a smaller invoice would be fee-free by accident rather than by design.
Value
1000
MinPayment
int64MinPayment keeps dust settlements out. It is NOT a floor on the final payment: a payment is accepted when it is at least MinPayment OR when it settles the invoice exactly. Without that second clause an invoice with 300 ugnot outstanding could never be closed, and "unpayable remainder" is a worse failure than dust.
Value
1000
MaxRecordsPerIssuer
untyped bigintMaxRecordsPerIssuer caps the invoice records one issuer may have STORED at once — open, settled and voided alike. It is what makes the issuer-scoped getters gas-bounded; see the package doc for why the cap counts stored records and not open ones. Issue is permissionless, so this is an AVAILABILITY BOUND and it is documented rather than hidden: an issuer can exhaust their OWN capacity and no one else's, because the bound is per-issuer. The cost of filling it is gas plus a storage deposit on every record. Hitting it is self-service to clear: Release is permissionless and the chain refunds the freed deposit to whoever calls it, so an issuer at the cap can always release their own finished records, and a stranger has a standing incentive to do it for them.
Value
(64 <untyped> bigint)
MaxMemoLen
untyped bigintMaxMemoLen bounds the free-text memo, in bytes. The memo is rendered, so it is sanitized at the render boundary, never at the write boundary — storing what the issuer actually wrote keeps the record honest.
Value
(256 <untyped> bigint)
StatusOpen
untyped stringInvoice statuses.
Value
"open"
StatusSettled
untyped stringInvoice statuses.
Value
"settled"
StatusVoided
untyped stringInvoice statuses.
Value
"voided"
invoice
typeValue
invoices.invoice
issuerRec
typeValue
invoices.issuerRec
self
.uverse.addressValue
<gnolang.StringValue>
invoices
*v0.Tree// "issuer|%019d" -> \*invoice
- OID
- 0a544c…2232:5
invoices details
issuers
*v0.Tree// address string -> \*issuerRec
- OID
- 0a544c…2232:8
issuers details
ledger
*feeledger.Ledger- OID
- 0a544c…2232:11
ledger details
nextID
int64Value
3
init.19
func()- OID
- 0a544c…2232:16
init.19 details
rejectStraySend
func()rejectStraySend aborts when coins are attached to a non-payable call. This realm holds funds, so a stray send would become sweepable surplus, silently converting a user's coins into operator funds.
- OID
- 0a544c…2232:18
rejectStraySend details
Issue
func(payer .uverse.address, amount int64, dueHeight int64, memo string) int64Issue records an obligation of \`amount\` owed by \`payer\` to the caller, to be settled by \`dueHeight\`. It moves no coins and reserves nothing. Returns the invoice id. dueHeight must not be in the past. A backdated due height would create an invoice that is overdue in the transaction that issued it, which is a claim about a deadline that was never offered.
- OID
- 0a544c…2232:19
Issue details
Pay
func(issuer .uverse.address, id int64) int64Pay settles part or all of an invoice with the attached coins. The named payer only. Direct EOA calls with -send only (the receipt-guaranteed shape); exactly one positive ugnot coin. The attached amount must not exceed the outstanding balance. Accepting an overpayment would turn a payment into a donation the payer did not choose to make and this realm has no path to return, so it is refused and the payer keeps their coins. Returns the outstanding balance remaining after this payment.
- OID
- 0a544c…2232:20
Pay details
Void
func(id int64)Void cancels the unpaid remainder of an invoice. The issuer only — this is an issuer forgiving a debt, and nobody else may forgive it for them. It does NOT reverse payments already made. Coins the payer sent are already credited to the issuer's ledger balance and may already have been claimed; there is no mechanism on chain to take them back, and pretending otherwise in the status would be worse than saying so. A voided invoice therefore keeps its \`paid\` figure, which is what makes the record readable afterwards. Voiding does not remove the record. Release does that.
- OID
- 0a544c…2232:21
Void details
Release
func(issuer .uverse.address, id int64)Release removes a finished invoice and frees its storage. Permissionless: the chain refunds the freed storage deposit to the caller, which is the standing incentive to tidy up a record that is not your own. It refuses an open invoice, which is what makes it safe to let a stranger call: a Release can never destroy an outstanding obligation. An issuer who wants a record gone must Void it first, and that leaves an event. Releasing frees the issuer's slot against MaxRecordsPerIssuer. That is the only way a slot is ever freed — settling and voiding do not free one, because the record is still stored and still walked by every issuer-scoped getter.
- OID
- 0a544c…2232:22
Release details
Claim
func() int64Claim pays the caller their whole settled balance. The issuer only — there is no "claim on behalf of", because that would let a third party choose the moment someone else's coins move.
- OID
- 0a544c…2232:23
Claim details
WithdrawFees
func() int64WithdrawFees moves the accrued fee pot to FeeCollector. Anyone may call it; the destination is a compile-time constant, so the caller cannot influence where the coins go.
- OID
- 0a544c…2232:24
WithdrawFees details
SweepSurplus
func() int64SweepSurplus sends coins held beyond every obligation to FeeCollector. The reserve passed to coinio is the realm's full liability, so a sweep can never reach a coin an issuer is owed.
- OID
- 0a544c…2232:25
SweepSurplus details
isOverdue
func(inv *invoices.invoice, now int64) boolisOverdue is a pure function of an invoice and the clock, which is what makes it unit-testable without a chain. Overdue means: still open, still owing, and the due height has PASSED. Strictly passed — at exactly dueHeight the invoice is due but not yet late, which is what lets dueHeight == issue height mean "due on receipt" without being born late.
- OID
- 0a544c…2232:26
isOverdue details
IsOverdue
func(issuer .uverse.address, id int64) boolIsOverdue reports whether one invoice is overdue right now.
- OID
- 0a544c…2232:27
IsOverdue details
OutstandingFrom
func(issuer .uverse.address, payer .uverse.address) int64OutstandingFrom is the total a payer still owes THIS issuer across the issuer's open invoices. The iteration is bounded by MaxRecordsPerIssuer, NOT by the number of open invoices: it walks the issuer's whole stored prefix and skips the records that are not open. That is why the cap counts stored records — see the package doc. This is the issuer-scoped shape the package doc argues for: a consumer realm gating its own service on its own unpaid bills is trusting only itself. There is deliberately no global per-payer total — see the package doc, "Anyone may issue an invoice naming anyone".
- OID
- 0a544c…2232:28
OutstandingFrom details
OverdueFrom
func(issuer .uverse.address, payer .uverse.address) int64OverdueFrom is OutstandingFrom restricted to invoices whose due height has passed. A consumer realm that wants to withhold service from a late payer reads this.
- OID
- 0a544c…2232:29
OverdueFrom details
HasOverdueFrom
func(issuer .uverse.address, payer .uverse.address) boolHasOverdueFrom is OverdueFrom as a predicate, for a consumer that only needs the boolean. It stops at the first hit rather than summing.
- OID
- 0a544c…2232:30
HasOverdueFrom details
InvoiceInfo
func(issuer .uverse.address, id int64) (payer .uverse.address, amount int64, paid int64, dueHeight int64, issuedAt int64, status string)- OID
- 0a544c…2232:31
InvoiceInfo details
Outstanding
func(issuer .uverse.address, id int64) int64Outstanding is what is still owed on one invoice. Zero for a settled or voided one — a voided invoice owes nothing by definition, which is the point of voiding it.
- OID
- 0a544c…2232:32
Outstanding details
Memo
func(issuer .uverse.address, id int64) stringMemo is an invoice's free-text memo, exactly as the issuer wrote it. It is UNSANITIZED and caller-supplied: a consumer that renders it owes it the same treatment Render gives it here.
- OID
- 0a544c…2232:33
Memo details
StatusOf
func(issuer .uverse.address, id int64) stringStatusOf is an invoice's status.
- OID
- 0a544c…2232:34
StatusOf details
HasInvoice
func(issuer .uverse.address, id int64) boolHasInvoice reports whether a record exists, without panicking on absence — every other invoice accessor panics, which makes them unusable as an existence probe.
- OID
- 0a544c…2232:35
HasInvoice details
OpenOf
func(issuer .uverse.address) intOpenOf is the number of open invoices an issuer holds.
- OID
- 0a544c…2232:36
OpenOf details
RecordsOf
func(issuer .uverse.address) intRecordsOf is the number of invoice records an issuer currently has stored, of any status. This — not OpenOf — is the figure capped by MaxRecordsPerIssuer, so it is the one an issuer near the cap needs.
- OID
- 0a544c…2232:37
RecordsOf details
IssuedOf
func(issuer .uverse.address) (count int64, invoiced int64)IssuedOf is how many invoices an address has ever issued, and their total face value. Note the tense: both are lifetime figures and neither falls when an invoice is settled, voided or released.
- OID
- 0a544c…2232:38
IssuedOf details
BalanceOf
func(issuer .uverse.address) int64BalanceOf is an issuer's settled, unclaimed balance.
- OID
- 0a544c…2232:39
BalanceOf details
Records
func() intRecords is the number of invoice records currently stored, open or not. It falls on Release, so it is a storage figure rather than a count of invoices that have existed.
- OID
- 0a544c…2232:40
Records details
Issuers
func() intIssuers is the number of addresses that have ever issued an invoice.
- OID
- 0a544c…2232:41
Issuers details
NextID
func() int64NextID is the id the next Issue will allocate. Exposed so a caller can tell a never-issued id from a released one.
- OID
- 0a544c…2232:42
NextID details
SettledTotal
func() int64SettledTotal is the sum of every issuer's unclaimed balance.
- OID
- 0a544c…2232:43
SettledTotal details
FeesAccrued
func() int64FeesAccrued is the fee pot awaiting WithdrawFees.
- OID
- 0a544c…2232:44
FeesAccrued details
Liabilities
func() int64Liabilities is every coin this realm owes to anyone: the ledger's own total, which is issuer balances plus the fee pot. There is no pool term, and its ABSENCE is the unfunded property this realm exists to model — see the package doc. Do not "fix" this to match payroll's shape.
- OID
- 0a544c…2232:45
Liabilities details
Held
func() int64Held is the realm's actual ugnot balance, read from the chain.
- OID
- 0a544c…2232:46
Held details
Surplus
func() int64Surplus is coins held beyond every obligation — what SweepSurplus would move. It is zero when every coin arrived through an entrypoint.
- OID
- 0a544c…2232:47
Surplus details
Address
func() .uverse.address- OID
- 0a544c…2232:48
Address details
Height
func() int64Height is the current chain height, exposed so a caller can compute due heights against the same clock this realm uses.
- OID
- 0a544c…2232:49
Height details
invKey
func(issuer .uverse.address, id int64) stringinvKey is issuer|paddedID. The id is left-padded to 19 digits — the width of MaxInt64 — so that lexicographic order over an issuer's prefix is numeric order over their invoice ids. An unpadded decimal would sort "10" before "9" and make a prefix scan return them out of order.
- OID
- 0a544c…2232:50
invKey details
padID
func(id int64) string- OID
- 0a544c…2232:51
padID details
eachOf
func(issuer .uverse.address, fn func(...))eachOf iterates one issuer's invoice records in id order. The callback returns true to stop, matching avl's own convention.
- OID
- 0a544c…2232:52
eachOf details
getIssuer
func(key string) *invoices.issuerRec- OID
- 0a544c…2232:53
getIssuer details
mustInvoice
func(issuer .uverse.address, id int64) *invoices.invoice- OID
- 0a544c…2232:54
mustInvoice details
mustDecOpen
func(issuer .uverse.address)mustDecOpen decrements an issuer's open count by one and REFUSES rather than masking if it cannot. Both call sites — settlement in Pay and forgiveness in Void — have already proved the invoice exists and was StatusOpen, so neither branch below can fire. The decrement is reachable only through a status transition out of StatusOpen, each invoice makes that transition at most once (both guards require StatusOpen and both set a terminal status), and Issue increments \`open\` for every record it stores. So an open invoice contributed exactly one to this counter and has not yet taken it back. The earlier spelling was \`if r := getIssuer(...); r != nil && r.open > 0\`, which reads as prudence and is the opposite. Those clauses cannot prevent the inconsistency — the status has already been written by the time we get here — they can only decline to notice it, and then COMMIT it. A panic aborts the whole transaction and reverts the status write too, which leaves the realm consistent; masking leaves it inconsistent and silent, and the counter it corrupts is the one MaxRecordsPerIssuer reads. So the guard exists for a future edit, not for today: a path that retires an invoice without coming through here fails loudly on the next one instead of quietly leaking a slot. A check that can never fire and a check that always passes are the same artifact from a reader's seat; the difference is what each does when it is wrong.
- OID
- 0a544c…2232:55
mustDecOpen details
checkedAdd
func(a int64, b int64) (int64, bool)- OID
- 0a544c…2232:56
checkedAdd details
itoa
func(n int64) string- OID
- 0a544c…2232:57
itoa details
Render
func(path string) string- OID
- 0a544c…2232:58
Render details
cell
func(s string) stringcell prepares caller-supplied text for a GFM table cell, truncating it for display first. TableCell rather than InlineText, and the slot is the reason: these bytes land between two \`|\` delimiters, and an unescaped \`|\` in a memo would open a new column and shift every column to its right. InlineText does not escape the pipe. The payer cell above uses the same helper for the same slot even though a bech32 address cannot contain one — matching the helper to the slot is the rule that survives a later edit, whereas "this particular value happens to be safe" is a fact about today's charset. TRUNCATE BEFORE ESCAPING, and on a RUNE boundary. Escaping first and cutting afterwards can slice a \`\\|\` in half and leave the cell ending in a bare backslash; cutting on a byte boundary can split a multi-byte rune and emit invalid UTF-8. Neither is something the sanitizer is asked to repair, because by the time it sees the string the damage is upstream.
- OID
- 0a544c…2232:59