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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

reimbursements source realm

Realm reimbursements is a claimant-initiated, approver-gated expense reimbursement desk in GNOT: an owner funds a poo...

Overview

Realm reimbursements is a claimant-initiated, approver-gated expense reimbursement desk in GNOT: an owner funds a pool and appoints an approver with a bounded, rate-limited, revocable authority to approve against it; anyone may then SUBMIT a request for a specific amount with a memo; and the approver either approves it — which settles the money into the claimant's credit immediately — or rejects it.

WHY THIS EXISTS (see DISCOVERY.md): request-then-approve is NOT new here, and an earlier draft of this comment wrongly claimed it was. Three realms in this portfolio already let a requester initiate and a second party gate before value moves — grants (applicants apply, the creator selects), bounties (a funder designates a winner) and bounty_panel (contributors submit, an M-of-N panel votes). The §7 sweep that found them is recorded in DISCOVERY.md. The claim this realm actually rests on is narrower, and all three parts are load-bearing:

  • THE REQUESTER PRICES IT. A grant or bounty amount is the funder's offer, fixed before anyone asks. A reimbursement amount is a cost the claimant already incurred, so the claimant names it and the approver's only lever is yes or no on that figure.
  • SETTLEMENT IS NON-EXCLUSIVE. Those three are competitive awards: escrow is reserved per item and exactly one requester wins. Here an unbounded number of requests settle against one SHARED, UN-ESCROWED pool, which is why a pending request is not a reservation (see below) — the divergence this realm is built on.
  • THE APPROVER IS RATE-LIMITED. In all three the selecting party may award whenever it likes. Here the approver is not the owner and holds a bounded, windowed, revocable spendbook authority, so an approver key cannot drain the pool.

The four other disbursement shapes here are further away still: treasury_board needs the PROPOSER to be a signer, so an outside claimant cannot ask; budgets grants authority up front with no per-item gate; invoices is a receivable nobody approves; payroll and vesting are employer-funded accrual on a schedule.

It is also, deliberately, the SECOND CONSUMER of spendbook, and the first with an INVERTED temporal order. budgets consumes authority at spend time by the spender. Here the spendbook "spender" is the APPROVER, and the authority is consumed at APPROVE time against a request a third party submitted. A primitive with one consumer is barely proven as a primitive; a second consumer that uses it the same way proves little more. This one exercises the opposite direction.

COMPOSITION: approval authority is delegated entirely to spendbook (anchored windows, lifetime caps, expiry, one-sided amendments, revocation), coin movement to coinio, per-claimant credit and the fee pot to feeledger, ordered storage to p/nt/avl/v0, and free-text render output to p/nt/markdown/sanitize/v0. This realm owns the pool accounting, the desk and request records, the conflict-of-interest rule, and the solvency rule.

A desk, and what an approval IS

A desk is one (owner, approver) pair with a bounded authority over the owner's pool. A request is one claimant's ask against one desk.

The DELIBERATE DIVERGENCE from budgets is at approval:

Example
1An approved reimbursement is a SETTLED OBLIGATION, not authority.

Approve moves the gross out of the owner's pool and into the claimant's ledger credit in the same call. The owner cannot then withdraw it out from under an approved claimant, and no second step can fail. budgets' rule — "a budget is authority, not a reservation" — is explicitly NOT inherited, because the two realms are answering different questions. A budget is an ongoing permission whose point is not pre-committing capital. An approved expense claim is a decision about a cost already incurred, and a decision that can evaporate is not a decision.

The realm therefore never holds a partially-committed state: a request is pending (nothing has moved) or settled (everything has moved).

A PENDING REQUEST IS NOT A LIABILITY

Stated plainly because the opposite is the natural assumption. Submitting reserves nothing, locks nothing, and is not a claim on the pool. Ten pending requests of 100 against a pool of 100 are all ten submittable; the approver can approve one of them. Liabilities() counts pooled funds and settled credit, and a pending request appears in neither. What a claimant can rely on is the moment of approval, which is atomic, and the Approvable view, which reports the honest figure before they are told yes.

THE APPROVER IS THE SPENDBOOK SPENDER

The desk's allowance is consumed by the GROSS approved amount, so it bounds the owner's total OUTFLOW per window rather than the claimant's receipt — the figure an owner actually cares about capping, and the same treatment budgets gives its fee. An approver with a PerPeriod of 1 GNOT can approve 1 GNOT of the owner's money per window no matter how many claimants ask, and no single compromised approver key can do more than its window allows before the owner revokes the desk.

SELF-APPROVAL IS PROHIBITED

Approve refuses when the request's claimant is the approving address. This is a real conflict-of-interest control and not a formality: an approver with a desk could otherwise reimburse themselves at will up to the window cap. The rule is per-APPROVAL, not per-desk, so an address cannot route its own claim through a second desk it also approves. An approver may still submit requests — to be approved by somebody else.

LIFECYCLE

Example
 1Fund (owner, +send)      : adds real coins to the caller's own pool.
 2                           Anyone may fund; funding makes you an
 3                           owner. Minimum MinFunding.
 4WithdrawPool (owner)     : pulls UNSPENT pool back out, any amount,
 5                           any time. Approved credit is already out
 6                           of the pool and unreachable.
 7WithdrawPoolAll (owner)  : the whole unspent pool.
 8OpenDesk (owner)         : appoints one approver with bounded
 9                           authority and returns the desk id. The
10                           approval fee is snapshotted here.
11AmendDesk (owner)        : changes the desk's terms. Tighter-or-equal
12                           applies now; looser waits for the next
13                           window boundary. periodLength is NOT a
14                           parameter — spendbook makes it immutable.
15CloseDesk (owner)        : immediate; the id is never reused.
16ReapDesk (ANYONE)        : removes an EXPIRED desk, freeing its slot.
17Submit (ANYONE)          : files a request against a desk for a
18                           specific amount with a memo, and returns
19                           the request id. Expires after RequestTTL
20                           blocks.
21Approve (APPROVER)       : settles the request — debits the owner's
22                           pool, credits the claimant net of the fee,
23                           consumes the desk's authority, removes the
24                           request.
25Reject (APPROVER)        : closes the request with a reason. Nothing
26                           moves. Allowed even after expiry.
27Retract (CLAIMANT)       : the claimant withdraws their own request.
28                           Nothing moves. Named Retract, not
29                           Withdraw, because every Withdraw* in this
30                           portfolio moves money and this moves none.
31ExpireRequest (ANYONE)   : removes a request past its expiry.
32Claim (CLAIMANT)         : pays the claimant's whole settled credit
33                           out. The portfolio's standard pull.
34WithdrawFees (ANYONE)    : pays the fee pot to the compile-time
35                           FeeCollector. Fixed destination.
36SweepSurplus (ANYONE)    : out-of-band coins to the FeeCollector,
37                           never touching tracked liabilities.

A CLOSED DESK LEAVES ITS REQUESTS UN-APPROVABLE

CloseDesk and ReapDesk remove the desk and its allowance. Requests filed against it remain as records and can never be approved — Approve panics on the missing desk. They can be Retracted by their claimant or, once past expiry, ExpireRequested by anyone.

There is deliberately NO CASCADE DELETE. Finding a closed desk's requests means iterating the request tree, which is unbounded work in a transaction whose gas the owner pays, and an owner who cannot close a desk because it has too many pending requests is a worse realm than one where closing leaves records to be swept. The condition is documented, visible in RequestInfo and on the request's render page, and self-clearing within RequestTTL.

AVAILABILITY BOUND, stated honestly

Submit is permissionless, so the request tree is fillable. The per-claimant and per-desk caps bound ONE address and ONE desk, and are defeated by many addresses — that is the correct way to read a per-identity cap on a permissionless chain, and budgets' own corrected comment (finding Y1) is this portfolio's record of getting it wrong the other way.

What genuinely helps here, and does not help in budgets, is that a request has a MANDATORY expiry: RequestTTL is a compile-time constant, every request carries submittedAt + RequestTTL, and ExpireRequest is permissionless with the freed storage deposit refunded to the sweeping transaction. So the fill is bounded in DURATION as well as priced in gas and storage, and it drains itself. budgets' grants with expiresAt == 0 can never be reaped by anyone, which is strictly worse. The residual is real: for RequestTTL blocks a filled tree blocks NEW requests. Every desk, pool, approval, credit and claim keeps working, and no value is at risk.

THERE IS NO ADMIN

The fee rate, the fee collector, the TTL, every bound and every cap are compile-time constants. No address can change terms, pause the realm, touch a pool it did not fund, approve at a desk it was not appointed to, or move a request it did not file. The deployer has no privilege of any kind after deployment.

MONETARY INVARIANT (conservation)

Let H be the ugnot held at this realm's address, P the sum of all owner pools (tracked O(1) as totalPooled), L the ledger's liabilities (settled claimant credit plus the fee pot), and S >= 0 the out-of-band surplus:

Example
1H == P + L + S

Every transition moves value between exactly two terms inside one transaction. Fund raises H and P together (coinio.Receive is the receipt-guaranteed shape). WithdrawPool debits P before the identical amount leaves H. Approve moves the gross out of P and into L, split into the claimant's credit and the fee — H does not move at all, which is why Approve has no payout to fail. Claim and WithdrawFees debit L before their payout. Submit, Reject, Retract and ExpireRequest move nothing in any term. Any panic aborts the whole transaction; this realm never issues or removes coins.

ONLY GNOT IS ACCEPTED

Fund rejects any envelope that is not exactly one positive ugnot coin (coinio.Receive). Every other entrypoint rejects attached coins outright rather than converting them into sweepable surplus.

AUTHORIZATION

Every identity is derived from the crossing entrypoint's cur.Previous().Address(). No function takes a caller identity as a parameter, so funding another owner's pool, approving at another address's desk, retracting another claimant's request, or claiming another claimant's credit is impossible by construction. This is spendbook consumer contract item 2, closed structurally.

The clock is runtime.ChainHeight(), never an argument — spendbook consumer contract item 1. A caller who could choose `now` could roll the approval window at will, and could also expire a request out from under its claimant or keep an expired one alive.

STORAGE

A pool entry is paid for by the owner's own funding transaction; a desk record and its allowance by the owner's OpenDesk; a request record by the claimant's Submit. CloseDesk, ReapDesk, Approve, Reject, Retract and ExpireRequest each remove a record, and the chain refunds the freed storage deposit to the transaction that frees it — which is the standing incentive to call ExpireRequest or ReapDesk on somebody else's stale record.

Constants 18

const ApprovalFeeBps

1const ApprovalFeeBps = int64(100)
source

ApprovalFeeBps is the fee actually snapshotted into every desk: 100 bps = 1%, charged once per approval on the gross amount, floor-rounded in the claimant's favor. The most conservative live fee in this portfolio (fee_split, crowdfund, budgets) is also 1%.

const Denom

1const Denom = "ugnot"
source

Denom is the only asset this realm holds.

const FeeCollector

1const FeeCollector = address("g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3")
source

FeeCollector 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.

const MaxDesksPerOwner

1const MaxDesksPerOwner = int64(16)
source

MaxDesksPerOwner bounds how much of the global desk cap one owner can occupy. spendbook's package documentation names this as the gap a consuming realm must close: "maxLive is a single global figure, so any owner permitted to Open can exhaust it and lock out the rest … a Book shared across mutually untrusting owners needs its own per-owner cap on top of this one." This is that cap.

const MaxFeeBps

1const MaxFeeBps = int64(500)
source

MaxFeeBps is the hard ceiling on any approval 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%.

const MaxLabelLen

1const MaxLabelLen = 64
source

MaxLabelLen bounds a desk's free-text label.

const MaxLiveDesks

1const MaxLiveDesks = 4096
source

MaxLiveDesks caps simultaneously live desks, via spendbook's own construction bound. OpenDesk requires a funded pool, which is NOT a capital bound — see MaxOpenPerClaimant for why that mistake is not repeated here. spendbook's own MaxLiveLimit is 10000.

const MaxLiveRequests

1const MaxLiveRequests = 4096
source

MaxLiveRequests caps simultaneously live requests across the realm. Submit is permissionless, so this cap is exhaustible, and the package doc states that bound rather than claiming the per-identity caps below close it.

const MaxMemoLen

1const MaxMemoLen = 160
source

MaxMemoLen bounds a request's free-text memo and a rejection's reason. Larger than MaxLabelLen because a memo is the expense description — the thing an approver reads to decide — while a label only names a desk.

const MaxOpenPerClaimant

1const MaxOpenPerClaimant = int64(16)
source

MaxOpenPerClaimant bounds one claimant's simultaneously-pending requests. It is a GRIEF-COST multiplier and not a defence: one address is bounded, N addresses are not, and an attacker controls N. The real mitigation is RequestTTL plus permissionless ExpireRequest — see the package doc's availability section.

const MaxOpenPerDesk

1const MaxOpenPerDesk = int64(64)
source

MaxOpenPerDesk bounds the pending requests at one desk, so a single desk's approver is not handed an unreviewable queue by one burst. It is per-desk, so filling it denies that desk only; other desks are unaffected.

const MaxRenderRows

1const MaxRenderRows = 20
source

MaxRenderRows bounds the lists on the index page.

const MinFunding

1const MinFunding = int64(1_000_000)
source

MinFunding keeps dust pools out: 1 GNOT per Fund call.

const MinPerPeriod

1const MinPerPeriod = int64(1_000)
source

MinPerPeriod is the smallest per-window approval cap a desk may carry. It exists so a desk cannot be opened that no approval could ever satisfy, and it is paired with MinRequestAmount below rather than standing alone.

const MinPeriodBlocks

1const MinPeriodBlocks = int64(100)
source

MinPeriodBlocks is the shortest window a desk may use. A window of a handful of blocks is a rate, not a budget, and it makes PerPeriod meaningless as a bound on worst-case loss. A periodLength of 0 is still accepted and means ONE LIFETIME WINDOW that never refills (spendbook's own semantics).

const MinRequestAmount

1const MinRequestAmount = int64(1_000)
source

MinRequestAmount is the smallest request that may be filed, and it is deliberately EQUAL TO MinPerPeriod rather than smaller. The two together are what make a desk's authority expressible: a desk whose per-window cap is at the floor can still approve a request at the floor. Setting this below MinPerPeriod would admit dust requests that cost the approver a transaction each; setting it above would create desks no request could use.

const RealmPath

1const RealmPath = "/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/reimbursements"
source

RealmPath is this realm's own path, used to build Render links.

const RequestTTL

1const RequestTTL = int64(86_400)
source

RequestTTL is how long a request stays approvable, IN BLOCKS. No wall-clock equivalent is stated because onyx-1's block time has never been measured by this project, and converting blocks to hours with an assumed rate is how a document comes to carry a number nobody checked. It is sized to be long enough that an approver need not be online continuously and short enough that the request tree drains.

Functions 41

func AmendDesk

crossing Action
1func AmendDesk(cur realm, id int64, perPeriod, total, expiresAt int64) bool
source

AmendDesk changes a desk's terms. Owner only. The returned bool reports whether the new terms are in force NOW (a tighter-or-equal amendment) or queued for the next window boundary (a looser one) — spendbook's one-sided rule, and the distinction worth knowing.

periodLength is NOT a parameter. spendbook makes it immutable because moving it would move every future boundary, which is an enlargement vector wearing a different hat; this realm reads the desk's own periodLength and passes it unchanged, so changing it is not merely refused, it is unexpressible.

Amending does not touch pending requests. Their fee was snapshotted at Submit and their amount is fixed; what changes is whether the approver still has the authority to settle them, which Approvable reports.

A QUEUED EXPIRY EXTENSION CAN BE LOST, AND THE DESK BECOMES REAPABLE IN THE MEANTIME. A widening amendment is queued rather than applied, and spendbook surfaces a queued term only through its internal at() accessor — which IsExpired does NOT use: it reads the live Terms.ExpiresAt directly. So between queueing and the next approval, IsDeskExpired and ReapDesk still read the OLD expiry. If the old expiry passes with no Approve and no restated AmendDesk in between, ANYONE may ReapDesk the desk and the queued extension dies with it. Restating the same amendment while the old expiry is still in the future makes it permanent, because spendbook writes Pending back to Terms on a spend or on a later amendment; restating it after the old expiry is refused with "allowance has expired".

NO FUNDS ARE AT RISK and that is structural, not incidental: dropDesk moves no coins, and the claimant pools are keyed by CLAIMANT ADDRESS rather than by desk, so a reap strands open requests and leaves every accrued balance claimable. The root cause is in spendbook, which is already live and immutable, so the repair belongs to a future /p/ version and not to this realm. See AUDIT.md Y1 and DEPLOY_READINESS.md.

func Approvable

Action
1func Approvable(id int64) int64
source

Approvable is the HONEST figure for a desk, and the one both an approver and a prospective claimant should read: the lesser of what the desk authorizes and what the owner's pool holds. This distinction is the realm's own — spendbook bounds permission and says so, never solvency.

func ApprovalBlocker

Action
1func ApprovalBlocker(id int64) string
source

ApprovalBlocker returns the empty string when the request could be approved right now by its desk's approver, and otherwise the single reason it could not. It is the view a UI wants before showing an approve button, and an approver wants before paying gas for a call that will abort.

The reasons are reported in the same order Approve checks them, so this view and that entrypoint can never disagree about WHICH reason applies. It deliberately does NOT report "you are not the approver": the caller of a view is not the caller of the approval, so this answers about the request and the desk, not about whoever is asking.

func Approve

crossing Action
1func Approve(cur realm, reqID int64) int64
source

Approve settles a request. APPROVER ONLY: the approver is the calling address, never an argument. The claimant is credited net of the fee and claims separately, which is this portfolio's standard pull shape.

The checks fail in this order, and the order matters:

  1. the desk must still exist (a closed or reaped desk strands its requests, by design);
  2. the caller must be that desk's approver;
  3. the caller must not be the request's own claimant (no self-approval);
  4. the request must not have expired;
  5. the owner's pool must cover the GROSS amount;
  6. the desk's spendbook authority must permit the gross amount.

Solvency (5) is checked BEFORE the allowance is consumed (6), so an approval refused for insolvency leaves the desk's window untouched rather than burning capacity on an approval that did not happen. This is budgets' ordering, kept for the same reason.

func Claim

crossing Action
1func Claim(cur realm) int64
source

Claim pays the caller's entire settled credit out. CLAIMANT ONLY: the account is the calling address. Credit from a desk that has since been closed, expired or reaped is still claimable — it was settled under a live authority and this realm does not revisit that.

func CloseDesk

crossing Action
1func CloseDesk(cur realm, id int64)
source

CloseDesk ends an approver's authority immediately. Owner only. The id is never reused, so a closed desk cannot be resurrected.

Closing does NOT claw back credit already approved — that is the claimant's and stays claimable. It does NOT remove requests filed at this desk either: see the package doc, "a closed desk leaves its requests un-approvable". The count of those is reported in the event so the owner learns what they just stranded.

func CreditOf

Action
1func CreditOf(addr address) int64
source

CreditOf returns a claimant's settled-but-unclaimed credit.

func DeskLabel

Action
1func DeskLabel(id int64) string
source

DeskLabel returns a desk's raw label (unsanitized: callers rendering it into markdown must sanitize, as this realm's Render does).

func DeskUsage

Action
1func DeskUsage(id int64) (approvedWindow, approvedTotal, windowIndex,
2	approvals, rejections, pending int64)
source

DeskUsage returns a desk's consumption AT THE CURRENT HEIGHT: what the window the chain is in right now has approved, the lifetime gross, that window's index, how many approvals there have been, how many rejections, and how many requests are pending at the desk.

The two window figures are computed rather than read straight off the record, and that is the whole point of windowAt below: a window that has rolled with no approval since leaves Allowance.WindowIndex naming a window the chain has already left, and Allowance.SpentWindow holding the previous window's spend.

func DesksOf

Action
1func DesksOf(addr address) int64
source

DesksOf returns how many live desks an owner currently holds.

func ExpireRequest

crossing Action
1func ExpireRequest(cur realm, reqID int64)
source

ExpireRequest removes a request past its expiry, freeing its slot and its storage. ANYONE may call it, and the reasoning matches ReapDesk's: an expired request can never be approved, so removing it takes nothing from the claimant and grants nothing to the caller. The chain refunds the freed storage deposit to the expiring transaction, which is the standing incentive and the mechanism that drains the queue.

func Fund

crossing Action
1func Fund(cur realm) int64
source

Fund adds the attached coins to the caller's own pool. Direct EOA calls with -send only (the receipt-guaranteed shape); exactly one positive ugnot coin, at least MinFunding. Anyone may fund; funding is what makes an address an owner.

func Height

Action
1func Height() int64
source

Height returns the current chain height: the clock every window, expiry and TTL is measured against, and the one no caller can choose.

func Held

Action
1func Held() int64
source

Held returns H: the ugnot actually at this realm's address.

func IsDeskExpired

Action
1func IsDeskExpired(id int64) bool
source

IsDeskExpired reports whether a desk's authority has lapsed, which is also the condition under which anyone may ReapDesk it.

It reads the LIVE expiry only. A queued expiry extension reported by PendingTerms is NOT considered here, so this can read true while an amendment widening the window is still queued — see AmendDesk for the full disclosure and why no funds are at risk.

func IsRequestExpired

Action
1func IsRequestExpired(id int64) bool
source

IsRequestExpired reports whether a request is past its expiry, which is both the condition under which Approve refuses and the condition under which anyone may ExpireRequest it.

func Liabilities

Action
1func Liabilities() int64
source

Liabilities returns P + L — everything this realm owes. A pending request is not part of it.

func LiveDesks

Action
1func LiveDesks() int64
source

LiveDesks returns how many desks are currently live, from the primitive's own count.

func LiveRequests

Action
1func LiveRequests() int64
source

LiveRequests returns how many requests are currently pending. A pending request may still be unapprovable — see ApprovalBlocker.

func NumDesks

Action
1func NumDesks() int64
source

NumDesks returns how many desks have ever been opened. Ids are never reused, so this only grows.

func NumRequests

Action
1func NumRequests() int64
source

NumRequests returns how many requests have ever been filed. Ids are never reused, so this only grows.

func OpenDesk

crossing Action
1func OpenDesk(cur realm, approver address, label string,
2	perPeriod, periodLength, total, expiresAt int64) int64
source

OpenDesk appoints an approver with a bounded authority over the caller's pool and returns the desk id. The caller becomes the desk's owner and must already have a funded pool — not because the desk reserves anything (it does not), but because a desk over an unfunded pool is an invitation to claimants to file requests nothing can settle, and this realm declines to record one.

Terms, all in the chain's own units:

Example
1perPeriod    the gross the approver may approve per window, at least
2             MinPerPeriod. Required.
3periodLength blocks per window. 0 means ONE LIFETIME WINDOW that never
4             refills; otherwise at least MinPeriodBlocks.
5total        lifetime cap across every window. 0 means unlimited.
6expiresAt    absolute block height at which the approver's authority
7             ends. 0 means no expiry.

Every bound above except the two realm minimums is validated and enforced by spendbook, and an invalid combination surfaces as that package's own sentinel error.

func OpenRequestsOf

Action
1func OpenRequestsOf(addr address) int64
source

OpenRequestsOf returns how many pending requests a claimant has.

func PendingTerms

Action
1func PendingTerms(id int64) (perPeriod, total, expiresAt int64, pending bool)
source

PendingTerms returns a queued loosening amendment on a desk and whether there is one. An amendment is queued only when it would widen the live window, and it takes effect at the next window boundary.

func PoolOf

Action
1func PoolOf(addr address) int64
source

PoolOf returns an owner's unspent pool.

func ReapDesk

crossing Action
1func ReapDesk(cur realm, id int64)
source

ReapDesk removes a desk whose authority has EXPIRED, freeing its slot and its storage. Anyone may call it, and the reasoning is spendbook's own: an expired allowance can never authorize another approval, so removing it takes nothing from the owner and grants nothing to the caller. The chain refunds the freed storage deposit to the reaping transaction, which is the standing incentive.

A desk with no expiry can never be reaped; only its owner can close it.

func Reject

crossing Action
1func Reject(cur realm, reqID int64, reason string)
source

Reject closes a request without settling it. APPROVER ONLY. Nothing moves in any term of the invariant, and no authority is consumed — a rejection costs the desk's window nothing, which is deliberate: an approver must be able to say no as often as necessary.

Rejecting an EXPIRED request is allowed. The alternative would force an approver who wants the record to carry a reason to race the expiry, and the reason is the useful artifact: ExpireRequest emits no explanation.

func Remaining

Action
1func Remaining(id int64) int64
source

Remaining is the AUTHORITY figure for a desk: what its terms permit the approver to approve right now, ignoring whether the owner's pool can cover it.

func Render

1func Render(path string) string
source

Render shows the realm at "", a desk page at "desk/<id>" and a request page at "request/<id>". Labels and memos are free text and pass through the ecosystem sanitizer before hitting markdown.

func RequestMemo

Action
1func RequestMemo(id int64) string
source

RequestMemo returns a request's raw memo (unsanitized: callers rendering it into markdown must sanitize, as this realm's Render does).

func Retract

crossing Action
1func Retract(cur realm, reqID int64)
source

Retract removes the caller's own pending request. CLAIMANT ONLY. Nothing moves.

Named Retract rather than Withdraw on purpose: every Withdraw* in this portfolio moves money, and this moves none. A claimant reading a function list should not have to open the source to learn which.

func Submit

crossing Action
1func Submit(cur realm, deskID int64, amount int64, memo string) int64
source

Submit files a reimbursement request against a desk and returns the request id. ANYONE may submit: that is the point of the realm — the party who spent the money asks, and does not need to be a signer, a delegate, or known to the desk in advance.

Nothing is reserved and nothing moves. The amount and the fee are fixed here (the fee is snapshotted from the desk, so a later AmendDesk cannot change the terms of a request already filed), and the request expires at submittedAt + RequestTTL.

Submitting against a desk whose approver is the submitter is permitted and useless: Approve refuses self-approval, so such a request can only be rejected, retracted or expired. It is not refused here because an approver may legitimately submit at a desk they do not approve, and a check at Submit would have to guess which desk the claimant meant.

func Surplus

Action
1func Surplus() int64
source

Surplus returns H - (P + L) — out-of-band coins, sweepable.

func SweepSurplus

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

SweepSurplus sends out-of-band coins to the FeeCollector: for the pool denom, everything above tracked liabilities; for any foreign denom, the full balance. Owner pools, claimant credit and the fee pot are untouchable by construction (coinio.Sweep refuses to dip below the reserve). Anyone may call it.

With nothing out of band this ABORTS ("coinio: no surplus to sweep for <denom>") rather than returning 0. That is the primitive's choice and the right one: a sweep is a transfer, and a transfer of nothing that reports success is how a caller comes to believe a balance was moved.

func TotalPooled

Action
1func TotalPooled() int64
source

TotalPooled returns the P term: unspent owner funds. Pending requests are NOT included; they are not liabilities.

func UnclaimedCredit

Action
1func UnclaimedCredit() int64
source

UnclaimedCredit returns the claimants' share of the L term: approved and not yet claimed.

func WithdrawFees

crossing Action
1func WithdrawFees(cur realm) int64
source

WithdrawFees pays the entire accrued fee pot to the compile-time FeeCollector. Anyone may call it: the destination is fixed, so there is nothing for a caller to steer.

func WithdrawPool

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

WithdrawPool returns part of the caller's UNSPENT pool and reports the pool REMAINING afterwards.

Unlike budgets, this is not a hazard to a counterparty, and the reason is the divergence in the package doc: an approved reimbursement has already left the pool and sits in the ledger as the claimant's. What an owner can still do is empty the pool under a live desk, so that the approver's next Approve fails on solvency and pending requests cannot be settled. That is the owner declining to spend their own money, which is theirs to decline; CloseDesk is the clean way to end the relationship.

func WithdrawPoolAll

crossing Action
1func WithdrawPoolAll(cur realm) int64
source

WithdrawPoolAll returns the caller's entire unspent pool and reports the amount WITHDRAWN.

The return values of the two withdraw entrypoints are deliberately asymmetric, which is worth naming rather than letting a caller discover: each returns the figure its caller does not already have. WithdrawPool was told the amount and wants to know what is left; WithdrawPoolAll knows what is left (nothing) and wants to know what it got. Returning the remainder here would hand every caller a constant 0.

Dependencies

Source Files 2