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.

deposits source realm

Package deposits is a refundable security deposit with condition-gated, partially-forfeitable settlement.

Overview

Package deposits is a refundable security deposit with condition-gated, partially-forfeitable settlement.

The shape

A HOLDER (landlord, service provider, equipment lessor) opens an agreement naming a DEPOSITOR, an exact amount, a term, and a FORFEIT CEILING — the most the holder may ever claim out of the deposit. The depositor reads all of it, including the named arbiter, and consents by FUNDING. At the end of the term the holder may file a claim up to the ceiling; the depositor may accept it, dispute it to the arbiter, or let it stand. Whatever is not awarded to the holder is refunded to the depositor.

That makes this the first realm in the portfolio where ONE POT SPLITS PARTIALLY BETWEEN TWO RIVAL CLAIMANTS by a conditional rule, and the first where the depositor's own money is at risk of partial loss. `crowdfund` is all-or-nothing, `auction` is winner-takes, `loans` is principal-out / larger-back, `reimbursements` is approver-gated to a single claimant. See DISCOVERY.md for the §7 gate and the five candidates it killed.

Lifecycle

Example
1OpenAgreement  -> open       (nothing held; depositor has not consented)
2Fund           -> funded     (depositor attaches the exact amount)
3FileClaim      -> claimed    (holder, after term end, within the claim window)
4Dispute        -> disputed   (depositor, within the dispute window)
5ResolveDispute -> settled    (arbiter awards 0..claim to the holder)

Five routes reach `settled`, and THREE OF THEM ARE PERMISSIONLESS, so no terminal state depends on a party acting:

Example
1AcceptClaim       depositor agrees           award = claim
2SettleUndisputed  ANYONE, dispute window up  award = claim
3ReleaseUnclaimed  ANYONE, claim window up    award = 0  (holder never claimed)
4LapseDispute      ANYONE, arbiter silent     award = 0  (claim unsubstantiated)
5Release           holder waives early        award = 0

Custody

Coins are held by the realm from Fund until settlement, then become PULL-CLAIMABLE through `p/feeledger`. Nothing is ever pushed: a settlement credits balances and the parties `Claim` them, which removes the grief and reentrancy surface a push settlement would have.

The conservation invariant carries one bucket more than the sibling realms:

Example
1Held == locked + contested + feeledger.Liabilities()

`locked` and `contested` are DISJOINT — a disputed deposit moves out of one and into the other in the same transaction. The split exists because a contested deposit is the one balance whose destination is not yet decided by this realm's own rules; keeping it separate makes "what is the arbiter deciding over" a readable number rather than an inference.

The fee

The protocol fee applies ONLY to the portion AWARDED to the holder. A refund to the depositor is returned in full at fee 0, matching `auction`'s rule that a refund is not a fee event. A fully-refunded deposit therefore costs the depositor nothing.

What the depositor is trusting, stated plainly

  • The FORFEIT CEILING is the depositor's hard protection, and they consent to it by funding. Nothing can take more than the ceiling.
  • If NO ARBITER is named, an unaccepted claim STANDS once the dispute window passes. That is a legitimate configuration — a deposit with a low ceiling and no arbiter — and the depositor sees `Arbiter() == ""` before funding.
  • If an arbiter IS named, `LapseDispute` awards the contested amount to the DEPOSITOR. A disappeared arbiter cannot strand funds, and the holder — who chose the arbiter — bears the cost of that choice failing.
  • Residual, not fixed here: a hostile depositor can dispute a legitimate claim to delay settlement by one dispute window. The delay is bounded and costs the depositor the same wait; it is disclosed rather than mitigated.
  • The realm settles MONEY. Whether the damage was real is off-chain; this realm binds the ceiling, the windows, and the arbiter's authority.

Scope

Single-denom (ugnot), single depositor per agreement, flat ceiling. Graduated forfeiture over damage severity (`p/tiers`) is a deliberate v2 candidate, not an omission — see DISCOVERY.md §5.

Constants 4

const DepositOpen, DepositFunded, DepositClaimed, DepositDisputed, DepositSettled, DepositCancelled

1const (
2	DepositOpen      = "open"      // created; the depositor has not funded
3	DepositFunded    = "funded"    // funded; the term is running or the claim window is open
4	DepositClaimed   = "claimed"   // the holder filed a claim; the depositor may accept or dispute
5	DepositDisputed  = "disputed"  // the depositor disputed; the arbiter decides
6	DepositSettled   = "settled"   // split credited; both parties pull
7	DepositCancelled = "cancelled" // never funded; nothing moved
8)
source

Agreement status values. Settled and Cancelled are terminal.

const MinDeposit, MaxDeposit, MinClaim, MinTerm, MaxTerm, MinWindow, MaxWindow, FundWindow, MaxMemoLen, MaxReasonLen, MaxAgreementsPerHolder, RenderLimit

 1const (
 2	// MinDeposit is the dust floor; MaxDeposit keeps the split, the ceiling and
 3	// the fee far from int64 overflow. The depositor must ATTACH the deposit, so
 4	// the real ceiling is their balance.
 5	MinDeposit = int64(10_000)
 6	MaxDeposit = int64(1_000_000_000_000) // 1,000,000 GNOT
 7
 8	// MinClaim is the dust floor for a forfeit ceiling and for any claim filed
 9	// against it. A ceiling below it would make FileClaim unsatisfiable.
10	MinClaim = int64(1_000)
11
12	// Term and window bounds, in blocks. The window floors keep a counterparty
13	// from being given a deadline they cannot physically meet; the ceilings keep
14	// height arithmetic far from overflow.
15	MinTerm   = int64(10)
16	MaxTerm   = int64(7_500_000)
17	MinWindow = int64(10)
18	MaxWindow = int64(7_500_000)
19
20	// FundWindow is how long an unfunded agreement stays fundable. After it,
21	// Fund refuses and CancelUnfunded is permissionless, so an abandoned offer
22	// cannot occupy a holder's slot forever.
23	FundWindow = int64(1_000_000)
24
25	// MaxMemoLen and MaxReasonLen bound the stored free text.
26	MaxMemoLen   = 200
27	MaxReasonLen = 200
28
29	// MaxAgreementsPerHolder bounds one holder's stored agreements. A terminal
30	// agreement does NOT free the slot: a settled deposit is a permanent record
31	// both parties and an auditor may need to read.
32	MaxAgreementsPerHolder = 50
33
34	// RenderLimit bounds every rendered list — an unbounded Render was YELLOW in
35	// several prior audits in this portfolio.
36	RenderLimit = 20
37)
source

Input bounds.

const Denom

1const Denom = "ugnot"
source

Denom is the only asset this realm accepts.

const MaxFeeBps

1const MaxFeeBps = int64(1000)
source

MaxFeeBps is the hard protocol-fee cap: 1000 bps = 10%. The fee applies only to the portion AWARDED to the holder, never to a refund.

Functions 54

func AcceptClaim

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

AcceptClaim settles at the claimed amount. Depositor only, while a claim stands. Accepting early is allowed — the dispute window is a deadline for disputing, not a waiting period.

func CancelUnfunded

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

CancelUnfunded abandons an agreement the depositor never funded. Either party may cancel at any time — refusing to fund is the depositor's right and withdrawing the offer is the holder's — and ANYONE may cancel once the fund deadline has passed, so a dead offer cannot hold a slot forever. No coins have ever been held, so nothing moves.

func Claim

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

Claim sends amount ugnot of the caller's claimable balance — refunds and awards from settled agreements — to the caller.

func ClaimAll

crossing Action
1func ClaimAll(cur realm)
source

ClaimAll sends the caller's entire claimable balance to the caller.

func Dispute

crossing Action
1func Dispute(cur realm, id int64, reason string)
source

Dispute sends a standing claim to the arbiter. Depositor only, before the dispute deadline, and only if an arbiter was named — with no arbiter there is nobody to decide, and the depositor saw that before funding. The deposit moves from `locked` into `contested`; no coins move.

func DisputeDeadlineOf

Action
1func DisputeDeadlineOf(id int64) int64
source

DisputeDeadlineOf is the last height at which the depositor may Dispute a standing claim; ArbiterDeadlineOf is the last height at which the arbiter's ruling pre-empts LapseDispute. Both are 0 until the relevant stage is live.

func FileClaim

crossing Action
1func FileClaim(cur realm, id, amount int64, reason string)
source

FileClaim claims `amount` of the deposit against the ceiling. Holder only, only once, only after the term has ended and before the claim window closes. `amount` must be in [MinClaim, forfeitCeiling]. The depositor may then accept it, dispute it to the arbiter, or let it stand.

func Fund

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

Fund attaches the exact deposit and starts the term. The named depositor only; the attached amount must equal the agreement's amount exactly, and the fund deadline must not have passed. Funding IS the depositor's consent to the ceiling, the windows and the arbiter, all of which were readable beforehand.

func IsClaimable

Action
1func IsClaimable(id int64) bool
source

IsClaimable reports whether FileClaim would be accepted at this height.

func IsDisputable

Action
1func IsDisputable(id int64) bool
source

IsDisputable reports whether Dispute would be accepted at this height.

func LapseDispute

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

LapseDispute refunds the whole deposit because the arbiter did not rule before their deadline. Anyone may call it. The award is 0, not the claim: an unsubstantiated claim fails. The holder chose the arbiter and bears the cost of that choice failing — which is also why a disappeared arbiter can never strand a deposit.

func MaxClaimable

Action
1func MaxClaimable(id int64) int64
source

MaxClaimable is the most the holder could still take out of this agreement. It is the ceiling while the deposit is funded, the filed claim once one stands, and 0 once the agreement is terminal.

func OpenAgreement

crossing Action
1func OpenAgreement(cur realm, depositor address, amount, forfeitCeiling, termBlocks, claimWindow, disputeWindow int64, arbiter address, memo string) int64
source

OpenAgreement offers a deposit agreement. The caller becomes the holder and names the `depositor`, the exact `amount` they must attach, the `termBlocks` the deposit is held for, the `claimWindow` the holder has to file a claim after the term ends, the `disputeWindow` the depositor has to dispute one (and the arbiter then has to rule), and the `forfeitCeiling` — the most the holder may ever claim, in [MinClaim, amount]. `arbiter` may be the zero address, in which case a claim cannot be disputed; it may not be either party. Nothing is held until the depositor Funds, which is how they consent to all of it. The current protocol fee is snapshotted in. Returns the id.

func PendingValve

Action
1func PendingValve(id int64) string
source

PendingValve names the permissionless call this agreement is ready for, or "" when none is. It is the one view a keeper needs.

func Refund

Action
1func Refund(id int64) int64
source

Refund is the amount the depositor gets back, known only once settled.

func Release

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

Release returns the whole deposit to the depositor early. Holder only, while the agreement is funded and unclaimed. It is the holder waiving the claim they have not yet filed — the one route to settlement that needs no clock.

func ReleaseUnclaimed

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

ReleaseUnclaimed refunds the whole deposit because the holder never filed a claim before the window closed. Anyone may call it.

func ResolveDispute

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

ResolveDispute rules on a disputed claim. Arbiter only. `awardToHolder` is in [0, the claimed amount] — the arbiter may reduce a claim or deny it outright but never award more than was claimed, which is itself capped by the ceiling the depositor consented to. No deadline is enforced here: a late ruling is better than a stranded deposit, and `LapseDispute` is the race that bounds it.

func SetFeeRecipient

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

SetFeeRecipient names the address that may withdraw the accrued fee pot.

It deliberately does NOT enforce disjointness from agreement parties, in contrast to OpenAgreement, which refuses an arbiter equal to either party. The two rules are not comparable, and the asymmetry is the point:

  • The arbiter rule is per-agreement and checked at the moment the agreement is created, so it is an invariant of that agreement for life.
  • The fee recipient is one global over an unbounded, growing set of agreements. A check here could only test a snapshot, and the very next OpenAgreement could name the sitting recipient as holder or depositor. A guard that any later transaction can walk through is worse than no guard: it reads as an invariant and is not one.

Nothing is at stake in the overlap, which is why the honest answer is a documented omission rather than a decorative check. The fee is derived only from the holder's award inside ledger.MustDeposit, never from the refund, so a recipient who is also a holder collects the same coins by two routes instead of more coins. Solvency is unaffected in either direction: feeledger accounts user credit and the fee pot as separate buckets, both are inside ledger.Liabilities(), and SweepDenom's reserve is locked + contested + ledger.Liabilities() — so an overlapping recipient cannot make the swept surplus overstate what the realm is free to release.

func SettleUndisputed

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

SettleUndisputed settles at the claimed amount because the depositor neither accepted nor disputed before the dispute window closed. Anyone may call it.

func SweepDenom

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

SweepDenom recovers out-of-band coins to the fee recipient. For the ledger denom the reserve is locked + contested + Liabilities() — held deposits, contested deposits, claimable refunds, awards and the fee pot are all structurally unreachable.

Dependencies

Source Files 2