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 package

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.

Function

Held

func Held() int64
Ask AI Claude ChatGPT

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

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/reimbursements.Held()"

Result