const LoanActive, LoanRepaid, LoanDefaulted
Loan status values. Repaid and Defaulted are terminal.
Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.
Realm loans is an unsecured term loan with a fixed installment schedule and a flat finance charge, holding real ugnot...
Realm loans is an unsecured term loan with a fixed installment schedule and a flat finance charge, holding real ugnot. A LENDER opens a loan to a named BORROWER by attaching the principal, which the realm disburses to the borrower in the same transaction; the borrower REPAYS over a fixed number of installments; each repayment credits the lender's pull-claimable balance, the protocol fee splitting off at the ledger. A loan whose installments go unpaid past a grace window is permissionlessly DEFAULT-able — a recorded state, not a seizure, because v1 is unsecured.
It is the one financial primitive this portfolio never had: CREDIT. The accounting realms RECORD obligations (r/invoices, r/receivables — p/aging's own doc lists "invoices, loans, dues") and the disbursement realms pay a KNOWN TOTAL out on a schedule (r/vesting, r/payroll, r/budgets). A loan is the asymmetric opposite: principal OUT once, a LARGER total back over time, plus the finance charge and the default state a disbursement realm has no use for. See onyx/r/loans/DISCOVERY.md.
This is the first custodial realm the factory has selected since the publishers (r/latefees, r/cashflow), so the monetary invariant governs it exactly as it does r/subscriptions and r/meters:
1Held == Σ lender-claimable + fee pot (== feeledger.Liabilities())
The principal is NEVER held across a transaction: Open receives it and pays it out to the borrower in the same call, so the only coins this realm ever holds are repayments awaiting the lender's Claim. A stuck or abandoned loan therefore strands no principal — it was disbursed — and the only value at risk is the lender's expected return, which is the real nature of unsecured credit and is disclosed, not engineered away.
The total repayable is `principal + floor(principal × rateBps / 10000)` — a flat charge over the whole term, NOT compounding and NOT accruing per block. Per-installment is `ceil(total / installments)` with the rounding remainder on the FINAL installment, so the installments sum to exactly the total (the cross-foot). Compounding interest and a tiered-by-duration rate (which p/tiers could price) are a v2: pricing them in v1 would add an accrual surface whose bugs are the ones lending audits most often find.
A borrower past the grace window who has not repaid in full is Default-able by anyone (like the sibling realms' valves). It freezes the loan and records the shortfall; no coins move, because there is nothing to seize. Secured lending is a v2.
REALM-CALLER CAVEAT, inherited from the siblings: coinio's receipt guard admits only EOA payers, so the LENDER (who funds Open) and any REPAYER (who funds Repay) are EOAs. The BORROWER is a parameter — coinio.Payout delivers the principal to any valid address, including a realm — but a realm borrower could never Repay (Repay is payable, EOA-only), so naming one is the lender's own footgun (an un-repayable gift), not a realm or third-party hazard. assertNoSend reads the ORIGIN envelope, so every non-payable function refuses any transaction that attached coins anywhere.
Loan status values. Repaid and Defaulted are terminal.
1const (
2 // MinPrincipal keeps a loan from being dust; MaxPrincipal keeps
3 // principal × rateBps and the total far from int64 overflow (the
4 // checked arithmetic still guards it). The lender must ATTACH the
5 // principal, so the real ceiling is their balance.
6 MinPrincipal = int64(1_000)
7 MaxPrincipal = int64(1_000_000_000_000) // 1,000,000 GNOT
8
9 // MaxRateBps caps the flat finance charge at 100% of principal over the
10 // whole term. A 0 rate is a legitimate interest-free loan.
11 MaxRateBps = int64(10_000)
12
13 // Installment-count and period bounds. The period bounds match the
14 // sibling realms: the floor keeps repayment off a per-block treadmill,
15 // the ceiling keeps schedule arithmetic far from overflow.
16 MinInstallments = int64(1)
17 MaxInstallments = int64(60)
18 MinPeriodBlocks = int64(10)
19 MaxPeriodBlocks = int64(7_500_000)
20
21 // MaxLoansPerLender bounds one lender's stored loans; terminal loans do
22 // NOT free the slot, because a Repaid/Defaulted loan is a permanent
23 // record a borrower and auditor may need to read.
24 MaxLoansPerLender = 50
25
26 // RenderLimit bounds every rendered list — an unbounded Render was
27 // YELLOW in three prior audits.
28 RenderLimit = 20
29)Input bounds.
Denom is the only asset this realm accepts.
MaxFeeBps is the hard protocol-fee cap: 1000 bps = 10%.
AmountDueNow is how far behind schedule the borrower is right now: ScheduledDue − repaid, floored at zero (a borrower who has prepaid is not behind).
Claim sends amount ugnot of the caller's claimable balance (repayments collected on their loans, net of the protocol fee) back to the caller.
ClaimAll sends the caller's entire claimable balance back to the caller.
Default marks an active loan defaulted once it is past its grace window — the final installment's due height plus one more period — and not repaid in full. Permissionless by design: no loan's terminal state may depend on either party acting. No coins move; the shortfall is recorded.
DefaultableAt is the height at or after which an unpaid loan may be Defaulted: the final installment's due height plus one grace period.
InstallmentAmount is the per-installment amount: ceil(total/installments), with the rounding remainder carried on the FINAL installment so the installments sum to exactly the total.
IsDefaultable reports whether the loan may be Defaulted right now.
Open funds a loan: the caller (the lender) attaches the principal, which is disbursed to borrower in this same transaction, and the loan is recorded with a flat finance charge of rateBps of the principal, repayable over `installments` installments of `periodBlocks` blocks each. The current protocol fee is snapshotted in. Returns the loan id.
Outstanding is total − repaid.
Repay pays toward the caller's own loan. The caller must be the loan's borrower; the attached amount must be positive and must not exceed the outstanding balance. The payment credits the lender's claimable balance (the protocol fee splitting off at the ledger); when the cumulative repaid equals the total, the loan is Repaid. Repaying ahead of schedule is allowed.
ScheduledDue is the cumulative amount that should have been repaid by the current height: the sum of every installment whose due height has passed, capped at the total. Installment k (1-based) is due at start + k·period.
SweepDenom recovers out-of-band coins to the fee recipient. For the ledger denom the reserve is Liabilities() — lender balances and the fee pot are structurally unreachable.