Eight chapters, in the order the protocol runs them: how a TRUFA score sets the risk tier, how the auction opens, who the Liquidity Nodes are, what happens when nobody bids, and which of the four Soroban contracts holds each step.
The industry solved issuance and declared victory. But an investor who cannot get out of a position in a solar plant or a fleet of machines has not bought a liquid instrument — they have bought a private placement with a nicer interface.
A holder who needs cash before maturity has one option: find a private buyer themselves, negotiate blind, and hope. Most simply hold something they no longer want.
Where a price exists it is usually the issuer's own valuation. Nobody has committed capital at that number, so it tells you what the seller hopes, not what the asset is worth.
A constant-product curve prices a token against inventory. It has no view on whether the machine still runs, whether the operator still reports, or whether the cashflow arrived.
One asset, one score, one tier, one exit. Everything below is the whole mechanism in the order it actually happens — from how an asset gets priced to what occurs on the day nobody wants to buy.
DobDex is not a standalone exchange. It is the last stage of a pipeline that already runs on Stellar mainnet: an asset gets validated, tokenized, and starts distributing yield. DobDex is what lets the holder of that position leave before maturity.
Validation agents review the operator's documents, contracts and reported cashflow, and produce a TRUFA score from 0 to 100.
The validated asset is tokenized into a pool with participation shares. This is where an investor buys in.
Revenue from the asset is paid out to shareholders in USDC through the distribution contracts, on a schedule.
The holder opens an exit. Liquidity Nodes price it, the settlement router pays USDC, and an anchor turns that into local currency.
A secondary exit venue. Holders sell positions they already own, for USDC, to Liquidity Nodes who choose to buy them. The protocol's job is to make that trade fair, bounded and observable — not to be the counterparty.
Not a redemption window, not a pegged swap, and not a promise of instant liquidity. The oracle prices the asset; the exit price is whatever a Liquidity Node will actually pay for it today.
Assets do not price themselves. The TRUFA score is produced by DobValidator and travels with the asset. Each Liquidity Node reads it, prices off-chain, and answers with a number it is willing to be held to.
Validation agents review the operator's documentation, contracts and reported cash flows and produce a TRUFA score from 0 to 100. The score answers one question: how much should a stranger trust what this asset says about itself?
The certificate is attached to the asset itself, and its history is public. Anyone pricing that asset — a buyer on the primary market, a Liquidity Node quoting an exit — is reading the same record, before any exit is opened.
How much a score is worth in discount is a judgement, and it belongs to whoever is risking the capital. One Node weights cashflow history, another weights the operator, another simply refuses the asset. The protocol does not arbitrate between them.
The contracts never re-derive a price. They verify the Node's USDC is really in the vault, that the bid clears the floor the Node itself declared, and that it stays under the exposure ceiling the Node itself set. A wrong model costs the Node, not the seller.
Tiers are the shared vocabulary Liquidity Nodes price in, not a rule the chain imposes. They say what a given score band has historically had to pay to find a buyer, and how quickly. Select a tier to see how exits in it tend to clear.
The tier describes where bids on that score band usually land. Where a given exit actually lands depends on how much capital is competing for it, how large the position is, and what each Node already holds of that asset. Two identical scores can clear at different prices on different days — that is the market working, not a bug.
Tiers can move; your exit cannot
be moved underneath you. The reference you declared and the floor you set are written into
the exit at open_exit and never change. Nodes re-price freely — that
changes what they offer, never what you agreed to accept.
Every exit ends one of three ways, and the seller decides which one in two of them. Buying into an asset in the first place happens on Token Studio; DobDex only handles the way out.
The ordinary path. The seller asked what the position is worth, saw a number they liked,
and said yes — without waiting for the window to run out. accept_bid is a
separate signature, so a bid is never binding on the seller.
Five minutes by default, and the seller can set anything from one minute to a day. At
expiry the highest bid settles if it met the floor. close is permissionless
— leaving capital committed to a finished auction helps nobody.
Nothing met the floor. The exit takes a numbered FIFO position visible on-chain and keeps taking bids from there — while an asset has a queue, only the exit at its head can settle. The seller can walk away at any time.
The seller signs with Freighter (or any Soroban-compatible wallet). The tokens move into router escrow in the same transaction, so every bid that follows is against tokens that are demonstrably there. The seller declares two numbers: what they think the size is worth, and the least they will take for it.
Each Node prices off-chain — the asset's TRUFA score, its own model, whatever the primary market is doing — and names a number. Absolute, not a discount: whatever the seller declared as a reference, a Node can only ever be held to the figure it named itself. Each bid must beat the standing one.
The auction checks nothing about the Node's model and everything about its money. Every
bid commits real USDC in lp_vault at the moment it is placed — free
balance and the Node's own exposure ceiling for that asset both have to allow it. The
previous best bid is released in the same call.
The seller can accept the standing bid at any point, or let the window close and take
the highest. Either way it is measured against their min_accept_usdc. If
nothing clears it, the exit is not force-filled at a bad price: it moves to
fifo_queue with a public position and keeps taking bids from there. The
seller keeps their tokens and their option to cancel.
settlement_router.settle transfers the participation tokens to the winning
Liquidity Node, the USDC to the seller net of the discount and protocol fee, and emits
exit.settled with the full breakdown. Soroban's atomicity means there is no
state where one leg happened and the other did not. A seller who wants fiat continues into
an Anchor Platform withdrawal from the same USDC balance.
A Liquidity Node is anyone willing to buy someone else's RWA position at a discount they set themselves. Permissionless to join, explicit about risk appetite per asset, and never filled on something they did not name.
Send USDC to lp_vault and become a Node. It stays yours and stays free until you
put it behind a bid.
Per asset: the minimum discount you require and your maximum exposure. No appetite set means no fills — you are never matched into something you did not name.
Name a number on any open exit. Win and you receive the participation tokens at your own price — a yield-bearing position bought below what the seller called it worth.
Keep collecting the asset's distributions, or open your own exit later and let the next Node price it. Nodes are not locked into what they buy.
deposited
− committed − pending_withdrawal. Real USDC the contract holds. It is the
solvency invariant, and nothing bypasses it.Not everyone wants to manage tier appetites and bid on individual exits. The Pooled Node is a shared vault position that does it on behalf of its depositors.
The Pooled Node registers as a single Liquidity Node in lp_vault. Depositors
contribute USDC and receive shares; the operator sets the tier appetite and bidding strategy
for the whole position. Depositors get exposure to exit spreads without running a bidding
operation.
Positions acquired through winning bids, and the distributions they generate, accrue to shareholders in proportion to their shares. Redemption is against the pool's share of vault capital plus the assets it currently holds.
Deposit USDC, receive shares. No bidding, no per-asset parameters to maintain.
The operator adjusts which tiers to serve and at what discount as conditions change.
Nothing stops several Pooled Nodes existing side by side with different risk mandates.
Most RWA venues have no answer for this case, which is why their liquidity claims break the first time an asset gets uncomfortable. DobDex makes the unfilled case an explicit, public protocol state.
A queued position is still yours and still earns whatever the underlying asset distributes while it waits.
Queue depth and your place in it are readable on-chain by anyone. No private list, no discretionary reordering.
Withdraw from the queue at any time and re-open later at a different threshold, or hold the asset to maturity.
The exit layer is deliberately small and separable. Each contract owns one decision and emits typed events. Their error codes are disjoint — 1–99, 100–199, 200–299, 300–399 — so a refusal that surfaces through a cross-contract call still says which contract said no, instead of decoding as the caller's own error with the same number. Source on GitHub.
exit_auction owns the
lifecycle and holds no funds: it cannot transfer a token, cannot pay a seller, and has no
function that reorders the queue. Solvency is answered by lp_vault before a bid
is accepted, and both legs of a fill are executed by settlement_router in one
Soroban transaction — so there is no state where the tokens moved and the USDC did not.
The disjoint error ranges mean a refusal surfacing through a cross‑contract call still
names the contract that said no.
Where Liquidity Nodes deposit USDC and declare, per asset, the minimum discount they require and the most of it they will hold. Free balance is the solvency invariant and nothing bypasses it. 17 tests · errors 1–99
The only contract in the layer that moves value. Escrowed tokens have exactly two exits: to a Node that paid for them, or back to the seller. There is no admin path to them, at any fee. 12 tests · errors 100–199
When demand for the door exceeds the capital standing behind it, exits wait instead of racing. Position is public, leaving is always allowed, and there is no function anywhere that reorders the line — not for the admin either. 7 tests · errors 200–299
The lifecycle, and the only caller the other three accept. It holds no funds itself: the vault knows whether a Node's capital is really there, the router performs the transfer or reverts, and the queue records who was waiting first. 15 tests · errors 300–399
Every state change emits a typed Soroban event. An indexer subscribed to Soroban RPC reconstructs the entire history of exits, bids and vault movements without trusting our API.
exit.openedA position is on the block and the tokens are escrowed. Carries the asset, the size, the seller's declared reference and their floor.exit.bidA Node put capital behind a price. Carries the absolute USDC and the discount it implies — the discount is descriptive, the USDC is what binds.exit.settledOne transaction moved everything: tokens to the Node, USDC to the seller net of discount and fee, fee to the treasury.exit.queuedNothing met the floor. Carries the FIFO position assigned and the queue depth at that moment.exit.cancelledThe seller withdrew. Bids are conditional at fill time, so nobody was owed anything.vault.deposit / vault.withdrawA Node added USDC, or capital left after the timelock and the free-balance check.vault.committed / vault.releasedCapital locked behind a live bid, then freed because the bid was outbid, cancelled or expired.vault.paid_outCapital actually left the vault to pay a seller — the only event in the pair that is not reversible.The failure modes that matter for an exit venue are a quote nobody can honour, a seller filled at a price they never took, a queue somebody pays to skip, and an admin who can reach the money. Each has a specific rail, and none of them is a promise — they are all checks.
Every exit carries a min_accept_usdc. A bid below it cannot settle — the
exit queues instead. And a bid is only ever an offer: accepting it is a separate signature,
so sellers are never force-filled at a price they did not take.
deposited − committed − pending_withdrawal. Real USDC the contract
holds, checked before any bid is accepted. Nothing in the layer bypasses it, which is why a
Node cannot show a quote it cannot honour.
Five minutes by default, floored at one and capped at a day. The seller sets it when they
open; nobody else can move it afterwards, and close is permissionless so a
finished auction cannot be held open to strand a Node's committed capital.
Tokens locked by the router can only leave towards a Node that paid for them, or back to the seller. There is no admin path to them at any fee, and settlement is one atomic Soroban transaction — there is no state where one leg happened and the other did not.
While an asset has a queue, only the exit at its head can settle. Bidding on the others stays open, but nobody buys their way past a seller who has been waiting — and no function anywhere reorders the line, not for the admin either.
Node withdrawals are requested, not taken. The amount stops counting as free balance the moment it is requested, so liquidity cannot be pulled out from under a bid that is already standing.
A Node's per-asset ceiling does not reset when it pays out — it is then holding the asset. Only the Node can mark a position divested, so the number it is trusting is its own. Lying here risks its capital and can never make the vault insolvent.
Error codes are disjoint across the four contracts. A bid rejected by the Node's own vault terms and a bid rejected by the auction are different failures, and they never decode to the same thing.
The admin sets the wiring — which auction the vault answers, the treasury address,
the protocol fee within a hard MAX_PROTOCOL_FEE_BPS of 500. It cannot price an
exit, reorder the queue, or reach escrowed tokens.
The four contracts ship with 51
tests that link the real peers and run the layer end to end, and a written threat model
covering vault insolvency, escrow reachability, queue manipulation and admin-key compromise.
Monitoring alerts on abnormal queue growth, exits closing into the queue at unusual rates, and
any config change. The contracts are open source with Cargo.lock committed for
reproducible SEP-55 builds, and the event stream is public — so the monitoring we run is
monitoring anyone can reproduce. Nothing is deployed yet.
Move the score and the ticket size to see roughly what a Liquidity Node would pay for the position today. Illustrative — every Node prices off-chain, so the real number is whatever a Node actually bids.
Illustrative model, not a quote. Real prices are posted by Liquidity Nodes and can differ in either direction; DobDex does not guarantee a price or a fill, and never buys the position itself.
DobDex is deliberately not a self-contained island. Each piece of the exit path that Stellar already solves well is delegated to the ecosystem primitive that solves it.
All four contracts are Rust on Soroban. Atomic multi-step settlement, typed events and predictable fees are the reason the exit path can be a single transaction instead of a choreography.
Liquidity Node capital sitting in the vault waiting for exits should not sit dead. DeFindex strategies put idle reserve capital to work, so the opportunity cost of standing ready is lower — which is what makes deeper reserves economically rational.
A Node that wants to bid on an exit without unwinding another position needs credit. Blend lets Nodes source short-term liquidity against collateral instead of keeping every dollar of bidding power idle in the vault.
An exit that ends in USDC is only half an exit for an investor who needs local currency. The settlement router hands off to Anchor withdrawal flows so the last mile is fiat in a bank account, not another token to figure out.
The single settlement asset for the entire protocol. Bids, fills, fees and queue positions are all denominated in it, so there is no second exchange-rate risk hidden inside the exit.
An earlier version of this design was built and tested against EVM. It de-risks the mechanism — the auction, tiering and queue logic have run before — but it is not the production target and is not where DobDex ships. Stellar is.
DobDex is being built on top of a protocol that already runs on Stellar mainnet. These are production database figures, not projections.
Three milestones from a working MVP to mainnet, each ending in something verifiable on-chain rather than a status update.
Step through the mechanism in the simulator, watch the product walkthrough, or explore the live Dobprotocol pools that DobDex is being built to serve.