Technical Protocol Overview
Pre-audit specification
The interfaces on this page describe the intended design and may change before audit and deployment. Contract source links will be added when repositories are public.
Architecture
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#eef0ff','primaryBorderColor':'#4d4dff','primaryTextColor':'#1e293b','lineColor':'#94a3b8','secondaryColor':'#e9f5f2','tertiaryColor':'#f8fafc','clusterBkg':'#f6f9fc','clusterBorder':'#dbe3ec','edgeLabelBackground':'#ffffff','actorBkg':'#eef0ff','actorBorder':'#4d4dff','signalColor':'#475569','signalTextColor':'#1e293b','labelBoxBkgColor':'#eef0ff','labelBoxBorderColor':'#4d4dff','noteBkgColor':'#fff7e6','noteBorderColor':'#c2a06a'}}}%%
flowchart LR
subgraph Reserve
USDC[(USDC)] --- BASE[BasePositionManager<br/>reserve custodian]
TB[(T-bill fund token)] --- BASE
end
USDest[USDest<br/>token + mint/burn accounting] -->|stake| V[sUSDest vault<br/>ERC-4626 / ERC-7540]
BASE -. protocolMint / releaseUSDC .- USDest
V --- BPM[BondPositionManager<br/>ONCHAINID holder]
V --- BASE
BPM --- B1[(ERC-3643 bond A)]
BPM --- B2[(ERC-3643 bond B)]
G[BondAllowlist &<br/>RiskParameters] -. checks .- BPM
TL[TimelockController] -. governs .- GUSDest
USDest is a USDC-backed synthetic dollar. It is mainly the entry and exit point for Staked USDest (sUSDest). The reserve behind it — USDC plus approved tokenized T-bill fund tokens — is custodied by the BasePositionManager. The USDest contract itself holds no reserve assets: it is token and mint/burn accounting, and it pulls USDC from the BasePositionManager to settle institutional redemptions.
Minting
KYC'd institutions on the mint/redeem allowlist can mint USDest by depositing USDC. The USDC goes straight into reserve custody at the BasePositionManager.
/**
* @notice Deposit USDC and mint USDest
* @param depositAmount USDC amount
* @param usdestAmountMinimum Minimum USDest to receive (after fees)
* @return USDest amount minted
*/
function deposit(
uint256 depositAmount,
uint256 usdestAmountMinimum
) external returns (uint256);
/**
* @notice Whether an account is on the institutional mint/redeem allowlist
* @param account Wallet to check
*/
function isMintAllowlisted(address account) external view returns (bool);
The App and integrators use isMintAllowlisted to check an institution's status. Changes to this allowlist are compliance actions and take effect immediately; the 7-day ALLOWLIST_DELAY applies to the bond allowlist only.
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#eef0ff','primaryBorderColor':'#4d4dff','primaryTextColor':'#1e293b','lineColor':'#94a3b8','secondaryColor':'#e9f5f2','tertiaryColor':'#f8fafc','clusterBkg':'#f6f9fc','clusterBorder':'#dbe3ec','edgeLabelBackground':'#ffffff','actorBkg':'#eef0ff','actorBorder':'#4d4dff','signalColor':'#475569','signalTextColor':'#1e293b','labelBoxBkgColor':'#eef0ff','labelBoxBorderColor':'#4d4dff','noteBkgColor':'#fff7e6','noteBorderColor':'#c2a06a'}}}%%
sequenceDiagram
actor Institution
participant USDest
participant BASE as BasePositionManager
Institution->>USDest: Deposit USDC
USDest->>BASE: USDC into reserve custody
USDest->>Institution: Mint USDestBurning
KYC'd institutions on the mint/redeem allowlist can burn USDest and withdraw USDC, up to the USDC buffer the BasePositionManager holds (BasePositionManager.availableUsdcBuffer()). USDest holds no USDC itself: it pulls the USDC from the BasePositionManager (releaseUSDC) and pays it to the recipient.
/**
* @notice Burn USDest and withdraw USDC
* @param usdestAmount USDest amount to burn
* @param recipient USDC recipient
* @return USDC amount withdrawn (after fees)
*/
function withdraw(
uint256 usdestAmount,
address recipient
) external returns (uint256);
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#eef0ff','primaryBorderColor':'#4d4dff','primaryTextColor':'#1e293b','lineColor':'#94a3b8','secondaryColor':'#e9f5f2','tertiaryColor':'#f8fafc','clusterBkg':'#f6f9fc','clusterBorder':'#dbe3ec','edgeLabelBackground':'#ffffff','actorBkg':'#eef0ff','actorBorder':'#4d4dff','signalColor':'#475569','signalTextColor':'#1e293b','labelBoxBkgColor':'#eef0ff','labelBoxBorderColor':'#4d4dff','noteBkgColor':'#fff7e6','noteBorderColor':'#c2a06a'}}}%%
sequenceDiagram
actor Institution
participant USDest
participant BASE as BasePositionManager
Institution->>USDest: Burn USDest
USDest->>BASE: releaseUSDC()
BASE->>Institution: USDCIf a redemption exceeds the USDC buffer, it is escrowed and filled once T-bill fund units have been redeemed:
/**
* @notice Request a USDest redemption larger than the USDC buffer
* @param usdestAmount USDest amount to escrow
* @param recipient USDC recipient
* @return requestId Withdrawal request ID
*/
function requestWithdraw(uint256 usdestAmount, address recipient) external returns (uint256 requestId);
/**
* @notice Fulfill an escrowed withdrawal once USDC is available (STRATEGY_ADMIN_ROLE)
* @param requestId Withdrawal request ID
*/
function fulfillWithdraw(uint256 requestId) external;
Staked USDest
Staked USDest (sUSDest) is a yield-bearing ERC-4626 vault token with ERC-7540 asynchronous redemptions. It earns yield from USDest reserve (T-bill) yield and from real-estate bond positions held by the BondPositionManager. USDest can be staked for sUSDest and later redeemed back for USDest. Unlike USDest, sUSDest is not a stablecoin. It floats freely and represents shares in a portfolio of permissioned bond positions plus unallocated USDest.
Staking
Anyone can stake USDest to receive sUSDest at the current deposit share price. Staking is a synchronous ERC-4626 deposit.
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
function mint(uint256 shares, address receiver) external returns (uint256 assets);
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#eef0ff','primaryBorderColor':'#4d4dff','primaryTextColor':'#1e293b','lineColor':'#94a3b8','secondaryColor':'#e9f5f2','tertiaryColor':'#f8fafc','clusterBkg':'#f6f9fc','clusterBorder':'#dbe3ec','edgeLabelBackground':'#ffffff','actorBkg':'#eef0ff','actorBorder':'#4d4dff','signalColor':'#475569','signalTextColor':'#1e293b','labelBoxBkgColor':'#eef0ff','labelBoxBorderColor':'#4d4dff','noteBkgColor':'#fff7e6','noteBorderColor':'#c2a06a'}}}%%
sequenceDiagram
actor User
User->>+sUSDest: Stake USDest (deposit()/mint())
Note right of sUSDest: USDest transferred to sUSDest
sUSDest->>+User: Mint sUSDest tokensdeposit() and mint() have overloads with slippage protection for EOAs.
Unstaking
Holders unstake sUSDest to receive USDest at the redemption share price in force when their request is serviced. Unstaking is an asynchronous ERC-7540 redeem. Requests are serviced at the close of each epoch (EPOCH_LENGTH). A request must be at least MIN_REDEEM_SHARES (1 sUSDest); a wallet may have any number of requests open, each its own FIFO entry. Once serviced, a request is claimed either per request with claimRequest() (what the App uses) or through the ERC-7540 redeem() / withdraw() path, which claims serviced requests oldest-first.
function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256 requestId);
function claimRequest(uint256 requestId, address receiver) external returns (uint256 assets);
function redeem(uint256 shares, address receiver, address controller) external returns (uint256 assets);
function withdraw(uint256 assets, address receiver, address controller) external returns (uint256 shares);
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#eef0ff','primaryBorderColor':'#4d4dff','primaryTextColor':'#1e293b','lineColor':'#94a3b8','secondaryColor':'#e9f5f2','tertiaryColor':'#f8fafc','clusterBkg':'#f6f9fc','clusterBorder':'#dbe3ec','edgeLabelBackground':'#ffffff','actorBkg':'#eef0ff','actorBorder':'#4d4dff','signalColor':'#475569','signalTextColor':'#1e293b','labelBoxBkgColor':'#eef0ff','labelBoxBorderColor':'#4d4dff','noteBkgColor':'#fff7e6','noteBorderColor':'#c2a06a'}}}%%
sequenceDiagram
actor User
User->>+sUSDest: Unstake sUSDest (requestRedeem())
Note right of sUSDest: Shares escrowed in queue
sUSDest->>+User: Redemption ID
Note over sUSDest: Epoch close: serviceRedemptions()
User->>+sUSDest: claimRequest() / redeem() / withdraw()
sUSDest->>+User: USDestQueued shares stay in total supply until they are serviced. They keep sharing in NAV changes, gains and impairments alike, until then.
Position Managers
The sUSDest vault's underlying asset is USDest. The vault earns base yield on it and deploys it into bond positions through position managers.
Interacting with position managers requires STRATEGY_ADMIN_ROLE, held by the strategy multisig. Its allocation decisions are discretionary, but every call is checked against the guardrails in RiskParameters and BondAllowlist.
The BasePositionManager custodies the USDC and T-bill reserve behind USDest and harvests base yield on it. Base yield is the amount by which reserve value exceeds USDest supply. harvestBaseYield() takes BASE_YIELD_ADMIN_FEE_BPS to the treasury and calls USDest.protocolMint(vault, netSurplus, "BASE_YIELD"), which mints the net surplus as USDest into the sUSDest vault, then credits the vault's accounted reserve. protocolMint is callable only by contracts holding RESERVE_MANAGER_ROLE (the position managers).
/**
* @notice Harvest base yield (reserve value above USDest supply)
* @return Harvested USDest amount
* @return Admin fee
*/
function harvestBaseYield() external returns (uint256, uint256);
/**
* @notice Move reserve USDC into an approved T-bill fund, respecting USDC_BUFFER_BPS
* @param fund Approved T-bill fund token
* @param usdcAmount USDC amount
*/
function allocateReserve(address fund, uint256 usdcAmount) external;
/**
* @notice Redeem T-bill fund units back to USDC
* @param fund Approved T-bill fund token
* @param units Fund units to redeem
*/
function deallocateReserve(address fund, uint256 units) external;
On the USDest side, two protocol paths mint or burn without an institutional deposit. Both are gated by RESERVE_MANAGER_ROLE, held only by the position managers:
/**
* @notice Mint USDest against USDC already held in reserve custody (RESERVE_MANAGER_ROLE)
* Reasons: BASE_YIELD, COUPON, PRINCIPAL, RECOVERY
* @param to Recipient of the minted USDest (the sUSDest vault)
* @param usdcIn USDC amount already in reserve custody
* @param reason Mint reason
* @return minted USDest amount minted
*/
function protocolMint(address to, uint256 usdcIn, bytes32 reason) external returns (uint256 minted);
/**
* @notice Burn USDest and release the matching USDC from reserve custody (RESERVE_MANAGER_ROLE)
* @param from Holder whose USDest is burned
* @param usdestAmount USDest amount to burn
* @param usdcTo USDC recipient
* @return usdcOut USDC amount released
*/
function protocolBurn(address from, uint256 usdestAmount, address usdcTo) external returns (uint256 usdcOut);
Settlement of a bond subscription burns the committed USDest via protocolBurn, and the BasePositionManager releases the USDC to the issuer.
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#eef0ff','primaryBorderColor':'#4d4dff','primaryTextColor':'#1e293b','lineColor':'#94a3b8','secondaryColor':'#e9f5f2','tertiaryColor':'#f8fafc','clusterBkg':'#f6f9fc','clusterBorder':'#dbe3ec','edgeLabelBackground':'#ffffff','actorBkg':'#eef0ff','actorBorder':'#4d4dff','signalColor':'#475569','signalTextColor':'#1e293b','labelBoxBkgColor':'#eef0ff','labelBoxBorderColor':'#4d4dff','noteBkgColor':'#fff7e6','noteBorderColor':'#c2a06a'}}}%%
sequenceDiagram
actor Strategy
participant BASE as BasePositionManager
participant USDest
participant sUSDest
Strategy->>BASE: harvestBaseYield()
BASE->>BASE: reserve value − USDest supply
BASE->>USDest: protocolMint(vault, netSurplus, "BASE_YIELD")
USDest->>sUSDest: mint USDest (net of admin fee)
BASE->>sUSDest: creditReserve(netSurplus)The BondPositionManager deploys USDest into bond subscriptions and deposits coupons, principal and recoveries. Its address is registered as an ONCHAINID identity, with claims issued for the Holding Subsidiary, in the identity registry of each allowlisted ERC-3643 bond.
Subscriptions are funded from the Subscription Timelock. USDest is committed against a hash of the agreed subscription terms and released only at settlement.
/**
* @notice Commit USDest to a bond subscription
* @param bondToken Allowlisted ERC-3643 bond token
* @param subscriptionTermsHash Hash of (bondToken, units, price, issuerPayout, settlementDate)
* @param usdestAmount USDest amount committed
* @param expiration Expiration timestamp (≤ MAX_SUBSCRIPTION_EXPIRY)
*/
function depositSubscriptionTimelock(
address bondToken,
bytes32 subscriptionTermsHash,
uint256 usdestAmount,
uint64 expiration
) external;
The commitment reverts unless:
\text{bondToken} \in \text{Allowlist} \;\wedge\; t \geq t^{allow}_{bond}\frac{E_{g} + \text{usdestAmount}}{\mathrm{NAV}^{redeem}} \leq \text{ISSUER\_CAP}\frac{U - \text{usdestAmount} - Q}{\mathrm{NAV}^{redeem}} \geq \text{RESERVE\_FLOOR}where E_g is current exposure to the bond's issuer group (positions plus pending commitments), U is USDest held by the vault and Q is USDest-equivalent redemptions queued for the current epoch.
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#eef0ff','primaryBorderColor':'#4d4dff','primaryTextColor':'#1e293b','lineColor':'#94a3b8','secondaryColor':'#e9f5f2','tertiaryColor':'#f8fafc','clusterBkg':'#f6f9fc','clusterBorder':'#dbe3ec','edgeLabelBackground':'#ffffff','actorBkg':'#eef0ff','actorBorder':'#4d4dff','signalColor':'#475569','signalTextColor':'#1e293b','labelBoxBkgColor':'#eef0ff','labelBoxBorderColor':'#4d4dff','noteBkgColor':'#fff7e6','noteBorderColor':'#c2a06a'}}}%%
sequenceDiagram
actor Strategy
Strategy->>+sUSDest: Deposit Subscription Timelock
sUSDest->>+Subscription Timelock: USDest tokensSettlement is delivery-versus-payment. Once the issuer's token agent has delivered the bond tokens to the BondPositionManager, anyone can call settleSubscription() with the agreed terms, at any time up to the commitment's expiry; the contract recomputes the terms hash and requires it to match the commitment. The settlementDate in the terms is informational for offchain DvP coordination, so an issuer that settles a day early does not cause a revert; expiry (MAX_SUBSCRIPTION_EXPIRY) is the only hard bound.
/**
* @notice Settle a subscription once bond tokens are delivered (DvP)
* @param terms Agreed subscription terms (bondToken, units, price, issuerPayout, settlementDate);
* re-hashed onchain and required to match the commitment
*/
function settleSubscription(SubscriptionTerms calldata terms) external;
If terms change or the commitment expires, the USDest is returned to the vault with cancelSubscriptionTimelock():
/**
* @notice Cancel subscription timelock
* @param subscriptionTermsHash Subscription terms hash
*/
function cancelSubscriptionTimelock(bytes32 subscriptionTermsHash) external;
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#eef0ff','primaryBorderColor':'#4d4dff','primaryTextColor':'#1e293b','lineColor':'#94a3b8','secondaryColor':'#e9f5f2','tertiaryColor':'#f8fafc','clusterBkg':'#f6f9fc','clusterBorder':'#dbe3ec','edgeLabelBackground':'#ffffff','actorBkg':'#eef0ff','actorBorder':'#4d4dff','signalColor':'#475569','signalTextColor':'#1e293b','labelBoxBkgColor':'#eef0ff','labelBoxBorderColor':'#4d4dff','noteBkgColor':'#fff7e6','noteBorderColor':'#c2a06a'}}}%%
sequenceDiagram
participant Agent as Issuer Token Agent
participant BPM as BondPositionManager
participant ST as Subscription Timelock
participant USDest
participant BASE as BasePositionManager
participant Issuer
Agent->>BPM: Deliver ERC-3643 bond tokens
BPM->>ST: settleSubscription(terms)
ST->>ST: Verify units delivered, terms hash matches
BPM->>USDest: protocolBurn(committed USDest)
USDest->>BASE: release USDC
BASE->>Issuer: USDC (issuer payout)Coupons and principal are paid in USDC to the BondPositionManager. Eligibility requires USDC coupons, so a receipt in another currency is exceptional; when one must be swapped, it goes through a timelock-managed allowlisted router with an onchain minOut bounded by MAX_SWAP_SLIPPAGE_BPS. The BondPositionManager then converts the USDC through USDest.protocolMint(vault, amount, "COUPON") (or "PRINCIPAL") and calls sUSDest.creditReserve(amount, reason); the redemption share price steps up on that credit.
/**
* @notice Deposit a coupon payment
* @param bondToken Bond token the coupon relates to
* @param currencyToken Currency received
* @param depositAmount Amount received
* @param usdestAmountMinimum Minimum USDest after swap and performance fee
* @param data Swap data (if currencyToken is not USDC)
*/
function depositCoupon(
address bondToken,
address currencyToken,
uint256 depositAmount,
uint256 usdestAmountMinimum,
bytes calldata data
) external;
/**
* @notice Deposit a principal repayment (maturity, amortisation, issuer redemption)
* @param bondToken Bond token the principal relates to
* @param currencyToken Currency received
* @param depositAmount Amount received
* @param unitsRetired Bond units redeemed or cancelled
* @param usdestAmountMinimum Minimum USDest after swap
* @param data Swap data (if currencyToken is not USDC)
*/
function depositPrincipal(
address bondToken,
address currencyToken,
uint256 depositAmount,
uint256 unitsRetired,
uint256 usdestAmountMinimum,
bytes calldata data
) external;
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#eef0ff','primaryBorderColor':'#4d4dff','primaryTextColor':'#1e293b','lineColor':'#94a3b8','secondaryColor':'#e9f5f2','tertiaryColor':'#f8fafc','clusterBkg':'#f6f9fc','clusterBorder':'#dbe3ec','edgeLabelBackground':'#ffffff','actorBkg':'#eef0ff','actorBorder':'#4d4dff','signalColor':'#475569','signalTextColor':'#1e293b','labelBoxBkgColor':'#eef0ff','labelBoxBorderColor':'#4d4dff','noteBkgColor':'#fff7e6','noteBorderColor':'#c2a06a'}}}%%
sequenceDiagram
actor Strategy
participant BPM as BondPositionManager
participant Router as Allowlisted router
participant USDest
participant sUSDest
Strategy->>BPM: depositCoupon() / depositPrincipal()
BPM->>Router: Swap to USDC (exceptional, only if not USDC)
Router->>BPM: USDC
BPM->>USDest: protocolMint(vault, amount, "COUPON" / "PRINCIPAL")
USDest->>sUSDest: mint USDest (coupons net of performance fee)
BPM->>sUSDest: creditReserve(amount, reason)Credit events are recorded by the Holding Subsidiary under CREDIT_AGENT_ROLE, following the published rule:
/**
* @notice Record a credit event; stops coupon accrual for the position
* @param bondToken Bond token
* @param evidenceHash Hash of the supporting notice or record
*/
function recordCreditEvent(address bondToken, bytes32 evidenceHash) external;
/**
* @notice Flag a formal insolvency or enforcement notice, opening the early impairment path
* @param bondToken Bond token
* @param evidenceHash Hash of the notice
*/
function flagInsolvency(address bondToken, bytes32 evidenceHash) external;
/**
* @notice Write the position down to its impairment value
* @param bondToken Bond token
* @param impairedValue Carrying value after impairment (USDest terms)
* @param evidenceHash Hash of the impairment computation inputs
*/
function recordImpairment(address bondToken, uint256 impairedValue, bytes32 evidenceHash) external;
/**
* @notice Deposit recoveries and close the position
* @param bondToken Bond token
* @param currencyToken Currency received
* @param depositAmount Amount recovered
* @param usdestAmountMinimum Minimum USDest after swap
* @param data Swap data
*/
function recordRecovery(
address bondToken,
address currencyToken,
uint256 depositAmount,
uint256 usdestAmountMinimum,
bytes calldata data
) external;
recordImpairment is only callable once IMPAIRMENT_DELAY has elapsed since the credit event, or once flagInsolvency has been called for that position. The delayed path stays the norm; the early path is explicit and evidenced. Impairment lowers the position's carrying value inside the BondPositionManager; the vault reads that value into both share prices.
Bond Allowlist & Risk Parameters
BondAllowlist stores each eligible bond's token address, ISIN, issuer group, day-count convention, coupon schedule and the timestamp from which it may receive allocation. Additions, removals and parameter changes can be made only by the TimelockController:
/**
* @notice Add a bond to the allowlist (callable only by TimelockController)
* @param bondToken ERC-3643 bond token
* @param params Bond parameters (ISIN, issuer group, coupon rate, frequency, day count, maturity)
*/
function addBond(address bondToken, BondParams calldata params) external;
/**
* @notice Remove a bond from the allowlist; existing positions are unaffected
* @param bondToken ERC-3643 bond token
*/
function removeBond(address bondToken) external;
RiskParameters exposes reserveFloorBps(), issuerCapBps(), bondCapBps(), allowlistDelay(), maxSubscriptionExpiry(), epochLength(), epochCutoff(), minRedeemShares(), usdcBufferBps(), maxSwapSlippageBps(), impairmentDelay(), impairmentHaircutBps() and the fee rates. Every setter can be called only by the TimelockController. Setters enforce range bounds (bps ≤ 10 000, and every fee rate capped in code at 3000 bps) but no cross-parameter checks, so an emergency change can never be blocked by an unrelated value.
Share Pricing - Net Asset Value (NAV)
The net asset value of sUSDest is the value of unallocated USDest plus its bond positions. Bond positions are valued conservatively at carrying value, or optimistically at carrying value plus coupon accrued since the last coupon date.
The deposit share price uses the optimistic NAV; the redemption share price uses the conservative NAV. The deposit share price is always greater than or equal to the redemption share price. The two are equal when no performing bond has accrued unpaid coupon and all base yield has been harvested.
The rest of this section describes the optimistic NAV used for deposit pricing.
In general, the optimistic NAV is defined as:
\begin{aligned}
S_t &: \text{USDest total supply} \\
r_b &: \text{reserve (T-bill) yield rate, net of admin fee} \\
F_{i,u} &: \text{face value of bond } i \text{ held at time } u \\
c_i &: \text{coupon rate of bond } i \text{, net of performance fee} \\
n &: \text{number of bond positions}
\end{aligned}\mathrm{NAV}_t = \mathrm{NAV}_0 + \underbrace{\int_0^t \sum_{i=1}^{n} c_i F_{i,u} \, du}_{\text{continuous coupon accrual}} + \underbrace{\int_0^t r_b S_u \, du}_{\text{continuous reserve yield}}In practice, coupon accrual follows each bond's day-count convention and resets at every coupon date, when the accrued amount is replaced by cash received. After a credit event, the position stops accruing. NAV is therefore defined more precisely with a switch:
\mathbf{1}_{i \text{ live at } u} = \begin{cases} 1 & \text{bond } i \text{ performing} \\ 0 & \text{credit event recorded for bond } i \end{cases}\mathrm{NAV}_t = \mathrm{NAV}_0 + \underbrace{\int_0^t \sum_{i=1}^{n} c_i F_{i,u} \, \mathbf{1}_{i \text{ live at } u} \, du}_{\text{continuous coupon accrual}} + \underbrace{\int_0^t r_b S_u \, du}_{\text{continuous reserve yield}}To show how impairment and recovery adjust NAV, define:
\begin{aligned}
\tau &&&: \text{time the credit event is recorded} \\
T_1 &&&: \text{time an impairment is recorded (if any)} \\
T_2 &&&: \text{time recoveries are returned to the protocol} \\
P_j &&&: \text{carrying value of position } j \text{ before impairment} \\
V_j &= F_{j} + A_j(\tau) &&: \text{noteholder claim: face value plus accrued unpaid coupon} \\
K_j &&&: \text{impairment value under the published rule } (K_j \leq P_j) \\
R_j &&&: \text{net enforcement proceeds attributable to position } j \\
\Pi_j &&&: \text{proceeds returned to the protocol}
\end{aligned}The vault is a creditor, not an equity holder. It cannot recover more than its claim:
\Pi_j = \min\left( R_j, \; V_j \right)
Real-estate recoveries depend on collateral value, enforcement costs, ranking, timing and the issuer's documentation. There is no insurance payout term. The impairment value is an estimate under the published rule, and final recoveries may be above or below it.
With impairment and recovery, NAV has at most two discrete adjustments in a credit-event lifecycle:
\mathrm{NAV}_{T_1} = \mathrm{NAV}_{T_1^-} - \left( P_j - K_j \right)\mathrm{NAV}_{T_2} = \mathrm{NAV}_{T_2^-} + \Pi_j - K_jIf no impairment was recorded, K_j = P_j and only the recovery adjustment applies.
For how credit events are handled, see Onchain / Offchain Structure. No service provider has discretion to increase NAV or allocate additional yield outside the disclosed methodology.
Redemption Queue
sUSDest redemptions use a FIFO queue. Requests are collected throughout each epoch and processed at its close.
The STRATEGY_ADMIN_ROLE services redemptions at epoch close by calling serviceRedemptions(). It must release all available USDest (vault USDest not committed to a subscription timelock) to the queue in order:
/**
* @notice Service pending redemption requests in FIFO order, in batches
* @param maxRequests Maximum number of requests to process in this call; the next call resumes from the cursor
* @return Number of requests serviced
*/
function serviceRedemptions(
uint256 maxRequests
) external returns (uint256);
serviceRedemptions is batched and cursor-based, so a long queue can be worked through in several transactions. The epoch cannot advance until every eligible request has been filled or available USDest is exhausted, so the strategy role cannot hold liquidity back from the queue. Epoch closes are scheduled: the next close is always the previous close plus EPOCH_LENGTH, never the timestamp of the servicing call.
Bond Position Record
Each bond position is an ERC-3643 security token held by the BondPositionManager on behalf of the sUSDest vault. It is the onchain evidence of a position whose legal rights are set by the bond documents and held by the Holding Subsidiary. The bond token is not separately marketed to users. It is not the underlying real estate, it does not replace the bond documents, and depositors never hold it directly.
Roles
Vault operations execution
Every privileged call is submitted from a Safe multisig, so no single key can move protocol value.
The strategy role does have discretion over allocation: how much capital to keep as USDest, and which allowlisted bonds to subscribe to and when. That discretion is limited by onchain checks (reserve floor, per-issuer cap, per-bond cap, allowlist timelock) and by the published eligibility criteria.
No role has discretion to allocate to non-allowlisted bonds, change the redemption order, hold available liquidity back from the queue, change NAV methodology, redirect ordinary-course payments, or determine yield distribution outside the published rules. Any offchain scheduling process is only a secure way to submit transactions within those rules.
Note: each core contract has a pause function controlled by a dedicated pause-admin role. It is a safety measure. While paused, a contract stops value-moving protocol paths, but all balances stay put and every read function keeps working. In practice, USDest mint and burn, base-yield harvest, subscription commit and settle, and redemption request, servicing and claim are all put on hold. Pausing never freezes ERC-20 transfers of USDest or sUSDest: holders can always send their tokens, and DeFi integrations that only transfer the tokens keep working.
The pause guardian may use this role only in a few urgent cases: a suspected bug or active exploit; abnormal behaviour in a system the protocol relies on, such as USDC, a T-bill fund, a bond token or an oracle; or a brief window for an emergency upgrade.
For security review details, see Audits. For deployed references, see Contract Addresses.