rippled 3.4.0-rc5 at wss://s.devnet.rippletest.net:51233 (network id 2).
The vault is a ledger object, not a contract
A vault is a native object on the XRP Ledger. There is no contract code to read or audit. Depositors sendVaultDeposit, receive units of a share token, and the object carries the fund’s balance sheet in a handful of fields:
Two of those deserve immediate attention.
AssetsTotal does not net LossUnrealized, and units outstanding live on a separate ledger object — the share token’s MPTokenIssuance. A tool that reads AssetsTotal and divides by units is reading the fund before provisions.
Open-ended and closed-ended vaults
An open-ended vault has no calendar. Deposits and withdrawals are legal at any time, subject to liquidity. It cannot be used for lending — attaching aLoanBroker returns tecNO_PERMISSION.
A closed-ended vault is created with VaultKind: 1 plus a SubscriptionDate and a RedemptionDate. It is a fixed-term fund: capital is raised during Subscription, deployed during Investment, then returned during Redemption.
VaultKind is immutable. A vault created open-ended can never become a lending vault. If you plan to lend, you must decide that at VaultCreate — you cannot change it afterwards.The three phases
Phase is derived, not stored. It is a comparison between the validated ledger’sclose_time and the two dates on the vault object. The clock is the validated ledger close time, not the wall clock — Devnet close time can lag wall time by up to ten seconds, and a countdown computed against the wrong clock goes negative on stage.
Subscription runs until SubscriptionDate. Capital comes in and may leave freely. No lending is possible yet — LoanSet returns tecTOO_SOON.
Investment runs from SubscriptionDate to RedemptionDate. The book is locked. Loans are originated, impaired, and defaulted here. Investors cannot deposit and cannot withdraw for the entire phase — VaultDeposit returns tecEXPIRED and VaultWithdraw returns tecTOO_SOON. This is a phase predicate, not a liquidity check: Orma measured it with 80 XRP of AssetsAvailable sitting idle and the exit was still closed.
Redemption begins at RedemptionDate. Withdrawals reopen, deposits stay closed, and no new loan can be written. Under the default WithdrawalPolicy: 1, redemption is first-come first-served, so the order investors queue matters when liquidity is short.
Phase rejection matrix
The table below is measured, not inferred. Each row was verified against a live Devnet facility with a passing control alongside it (a control that succeeds where the action is legal proves the rejection is about phase, not about a malformed transaction).
* The Investment
LoanSet returned tecNO_PERMISSION in this particular matrix run because the loan’s scheduled maturity overran the vault’s RedemptionDate. The ledger rule is StartDate + PaymentInterval × PaymentTotal + 60 ≤ RedemptionDate. Three of Orma’s four live facilities have loans written successfully in Investment — this was a window-sizing issue in the matrix script, not a phase rule.
Because the exit is closed for the entire Investment phase, a credit signal published during Investment cannot be acted on by selling. That is why the redemption cliff anchors the composite grade rather than sitting as one factor among five, and why Orma publishes the signal where a third party can act on it — not just display it. See Mathematics — The Five Measured Factors.
The LoanBroker and first-loss capital
Loans are not made directly by the vault. They flow through a LoanBroker, a separate ledger object that points at the vault and holds its own fields:
Both rates are expressed in units of . A stated
10000 means — ten percent — divided by . The cover consumed on a single default is:
where is the broker’s total drawn debt, is the principal of the defaulting loan, and is CoverAvailable. At the effective coefficient is — one percent of the book, not ten.
Note what the base is. The formula uses the broker’s total book , not the principal of the loan that failed. This means the same loan defaulting inside a larger book liquidates more cover, and a loan that represents most of the book liquidates cover keyed to that entire book and leaves little behind it.
Orma reports strandedCoverFraction to surface this. On Calder Structured Credit III — 10 XRP of debt, 5 XRP of cover posted — 98 percent of the posted cover cannot be reached by any single default at the current book size.
The broker owner is the vault owner
This is a protocol fact, not a configuration choice.LoanBrokerSet requires the submitting account to be the vault’s Owner. So the party who posts first-loss capital and the party who manages the fund are necessarily the same account.
Only that owner may impair, unimpair, or default a loan. There is no independent servicer, no trustee, and no rating committee. Credit events are declared by the party whose own capital those declarations consume.
Cash-basis accounting and LossUnrealized
Under LEVersion: 1 the vault keeps cash-basis books. The practical consequences:
- Origination moves
DebtTotal, notAssetsTotal. Lending out cash is not a gain or a loss, so the vault’s asset total does not change when a loan is written. - Impairment moves only
LossUnrealized. Nothing else on the Vault object changes. - Default moves assets. The loss is realised: the asset is written off, the provision is released, cover is liquidated, and the book reads clean afterwards.
Impairment vs. default
Test loan flags by bit, never by equality. A loan can carry more than one flag, and
Flags === 131072 silently stops matching the moment a second flag is set. Use a bitwise check: (flags & 0x20000) !== 0 for impaired, (flags & 0x10000) !== 0 for defaulted.lossUnrealized of "0" and a NAV divergence of zero basis points. The naive and correct unit values agree at 0.210000 — and only the 79% gap between units outstanding and assets held tells you what happened.
Lever one: when to recognise a loss
The manager chooses the momentLossUnrealized moves. Until they act, AssetsTotal carries the troubled loan at book and a naive reader sees a fund at par:
On Meridian Trade Finance I: drops, , . Naive reads 1.000000 and held reads 0.803922 — a divergence of 1961 basis points on the same object at the same instant.
The delay is not merely invisible to careless tools. It is invisible to careful ones that watch transaction metadata. On the first impairment of a healthy vault, LossUnrealized moves from zero and under cash-basis accounting nothing else changes, so PreviousFields is emitted empty. An indexer that diffs metadata sees no change at all on the most important credit event the protocol has.
Delay also flatters the manager directly: CoverRateMinimum is keyed to DebtTotal, so the cover they must hold, and the cover liquidated when they eventually act, both shrink as the book ages.
Lever two: in what order to realise losses
Because is keyed toDebtTotal at the instant of each default, and DebtTotal is decremented as each default lands, declaring losses in a different order changes the total cover consumed. With , the book at the first default, and defaults declared in order :
Only the sum depends on , and its coefficient decreases in . By the rearrangement inequality the sum is maximised — and minimised — when principals are declared in decreasing order.
Total first-loss capital consumed is minimised by declaring the largest exposure first, and maximised by declaring the smallest first.Orma places the observed sequence between the two extremes as a sequence score: is the sequence worst for investors; is the best. Kestrel Bridge Financing II scored — the manager chose the investor-worst order on live Devnet. See Mathematics for the full derivation and three independent cross-checks.
One asymmetry, two levers, then enforcement
The vault owner and the broker owner are the same account — that is the single asymmetry. It gives one party two levers over investor money: when a loss is recognised, and in what order losses are realised. Both are discretionary, both move money, and neither leaves a signal a metadata-diffing tool would catch. Orma’s response is three steps, each on its own page:- Measure both levers from public ledger state, with no privileged access. See Mathematics.
- Publish the result where it can be acted on: a native XLS-47 Oracle object and a pointer inside the share token’s own metadata. See Mathematics — Publication and NAV Pointer.
- Enforce with XLS-70 credentials and a permissioned domain, so an independent rater decides who may subscribe. See Gate.