Skip to main content
When someone pledges vault shares to a second lender, that lender has an MPTokenIssuanceID and nothing else. The share token carries no price field. The MPTokenIssuance records who issued it and how many units are outstanding, but says nothing about what a unit is worth. Orma solves this by writing a pointer into the share token’s own MPTokenMetadata at vault creation — not a value, a URL template. Whoever holds the token reads its metadata off the ledger, substitutes the issuance ID they already have into the template, follows it, and receives a valuation computed from public ledger state. Nothing in that chain requires the lender to know Orma exists.

Two constraints that shape the whole design

The metadata is write-once, permanently

MPTokenMetadata can only be set on VaultCreate. Three transactions on Devnet confirm there is no correction path: The third result is the important one. The issuer of a vault share is the vault’s pseudo-account, which carries lsfDisableMaster and has no regular key. No key in existence can sign an update. The field is frozen for the life of the vault.
A number written into share metadata is a lie within one ledger close. There is no correction path, no admin override, and no migration. Never put a NAV value, an audit date, or a grade in MPTokenMetadata.

The pointer cannot name the thing it describes

MPTokenMetadata must be composed at the moment of VaultCreate submission — before the vault exists. At that moment the VaultID and the share MPTokenIssuanceID are both unknown (assigned by the transactor), and the field can never be corrected once either becomes known. A self-referential URL is therefore impossible. Instead, Orma writes a template with a placeholder:
The reader supplies the missing half. Anyone reading this metadata already holds the issuance ID (that’s how they found the metadata), so the pointer only has to name the service and the substitution rule.

The pointer structure

The full metadata written to a vault share — XLS-89 conformance fields first, then one namespaced orma block:
rippled volunteers a warning when metadata is not XLS-89 shaped: it reports the token “might not be discoverable by Explorers and Indexers” and lists the five fields it expects — ticker, name, icon, asset_class, issuer_name. A vault share created without metadata is invisible to every indexer that reads the standard. Five fields is a cheap price for being listed.asset_class: "rwa" is the closest true statement for a private credit facility (a real-world asset). The exact instrument type lives in orma.instrument as vault-share. The xls89Report() API response carries a taxonomyNote field explaining this so consumers are not misled by the coarser field.
The nav_url_param field exists so a consumer who has never read this page can still identify which substring to replace: substitute nav_url_param’s value with the issuance ID you hold.

How a second lender resolves the pointer

A lender holds an MPT issuance ID and nothing else — no relationship with the facility, no access to its reporting, no reason to trust any figure someone hands them.
Every step before the last is public ledger state. The last is a URL the ledger named. Nothing in this loop requires Orma to be known to the lender in advance — that is the difference between a dashboard (which you have to already know about) and a primitive (reachable from the asset itself).

Using the API

To run the resolution loop via Orma’s API, call GET /api/mpt/:mptId/resolve:
The issuer on the first step — r3hE8HanpccSZdgmeHCfYFEkwxFjdDmmvt — is the vault pseudo-account, the same account whose lsfDisableMaster flag makes the metadata permanent.
The /resolve and /nav routes are kept separate deliberately. /nav is what the template resolves to and never follows a pointer itself (a valuation reachable only by walking a pointer to itself is a loop). /resolve is the diagnostic that walks every step. Both send open CORS headers — the entire point is that a party with no relationship to Orma can call them.

Valuing a pledge

To value a specific number of units — for example, 1,000,000 units pledged as collateral — call GET /api/mpt/:mptId/nav?units=1000000: 19.61% of the stated collateral value was not there. A haircut does not fix this: a haircut applies to the held value and absorbs volatility in a number that is correct. It does not absorb a number that is wrong. The valuation response carries a provenance block with the three raw ledger integers and a recompute formula, so you can verify the figure yourself without trusting Orma:
Read AssetsTotal and LossUnrealized from the Vault object on any Devnet node and apply the one-line formula in recompute. The numbers should match to the drop.

When resolution fails

Every failure mode is a normal outcome, not an error. The resolver reports a failed step and continues so the lender receives the most useful answer possible.
If the endpoint answers but carries no unitValue, the pledge value response returns an explicit unpriced string rather than a number or an exception — a lender’s tool has to be able to say “I could not value this”.

Embedding the pointer at vault creation

Build and encode the metadata before submitting VaultCreate:
Verify the encoded blob does not exceed the ledger’s 1,024-byte ceiling before submitting. Just under the ceiling is a permanent record you cannot trim later — validate the length and abort rather than submit an oversized blob.
BASE_URL must be a hostname you are willing to keep serving for the full life of the vault. The pointer is permanent. Writing a host you might move off is the one mistake this design cannot recover from.
VaultKind: 1 with SubscriptionDate and RedemptionDate is required. A vault that is not closed-ended cannot have a LoanBroker, so every vault whose shares are worth valuing this way is a fixed-term fund.

API routes for second lenders

GET /api/mpt/:mptId/nav

Returns the current unit value (held and reported), divergence in basis points, and the raw provenance inputs. Add ?units=N to value a specific pledge.

GET /api/mpt/:mptId/resolve

Traces every step of the resolution loop: reads the issuance, decodes the metadata, checks XLS-89 conformance, finds the pointer, and follows it. Add ?resolve=false to stop at the pointer without following it.
Both routes are read-only views of public ledger state and send open CORS headers.