Bound-Mode Unified Settlement

settleUpnlUnified now skips Muon verification and the upnl-based solvency checks when a bindable solver settles its own book against partyAs bound to it, matching every other oracle-less bound flow.

Summary

Bind mode is Symmio's oracle-less trading path. A partyA binds to a single solver, and that solver can open positions, fill closes, and charge funding for the bound partyA without presenting Muon signatures. The deprecated settleUpnl already participated in this model. When the target partyA was bound to the caller, the Muon settlement signature was not verified.

Its replacement, settleUpnlUnified, did not: it required a valid Muon signature on every call, so a bound solver that had migrated off the deprecated entry point was forced back onto the oracle for routine settlement. v0.8.6 closes that gap. When the caller is settling its own book and every partyA in the payload is bound to it, both the Muon verification and the upnl-based solvency checks are skipped.

When verification is skipped

All three conditions must hold; failing any one of them routes the call through full Muon verification:

  • The caller is sig.partyB. The solver is settling its own book. A third-party partyB settling someone else's positions always needs a Muon signature, since it would otherwise control the upnl inputs to another solver's settlement.
  • The caller is bindable. isPartyBBindable is still set. Revoking bindability immediately revokes the oracle-less privilege protocol-wide, including here.
  • Every partyA in sig.partyAs is bound to the caller. The unified payload can settle several partyAs at once; one unbound partyA in the batch means the whole call is verified.

The binding comparison is the same one used by every other bound flow: bindState[partyA].partyB against the caller. A partyA in the PENDING_UNBIND cooldown still counts as bound until the unbind completes, which is the protocol-wide behavior. The cooldown prevents obligations from being dodged through an immediate unbind.

What is skipped and what still runs

Only signature-dependent checks are skipped. Every guard that reads on-chain state instead of payload values runs unconditionally.
Check Reads Bound path
Muon TSS + gateway signature, timestamp freshness payload skipped
PartyB solvency (upnlPartyB / upnlPartyBPerPartyA) payload skipped
PartyA solvency (upnlPartyAs) payload skipped
PartyA not in liquidation, partyB not liquidating against it, not cross-liquidating storage enforced
Symbol not frozen for corporate-action adjustment storage enforced
Quote ownership (quote.partyB == sig.partyB, partyA index mapping), open-position status storage enforced
Updated price away from the opened price and no farther than the current price (opened exclusive, current inclusive) both enforced
PartyB-actions pause, onlyPartyB, upnl nonce bumps for both parties storage enforced

Why the bound path skips solvency checks

Settlement realizes unrealized PnL: it rewrites a quote's openedPrice and transfers exactly the corresponding difference between allocated balances. It is equity-neutral by construction, so whatever prices the payload claims, each party's allocated balance moves by precisely the amount its unrealized PnL moves the other way. A bound solver feeding fabricated prices cannot change either party's equity, and the balance mutations are underflow-guarded so no settlement can push a balance negative.

Binding gives PartyA no control over the settled price. A bound solver already opens and closes that partyA's positions without an oracle, and realizing PnL on an open position applies the same authority applied to a position that already exists.

A regression test pins one second-order effect. partyBAvailableForQuote takes max(-upnl, mm) on the negative-uPNL branch rather than stacking the realized loss on top of the modelled one because the two represent the same risk. A consequence is that a solver settling away from the market can draw down its own margin buffer once: it converts up to its maintenance margin into withdrawable collateral and lands exactly on the liquidation boundary, not over it.

The draw is capped at min(settled amount, mm), one wei more is refused, it does not compound across further settlements, and the solver stays able to pay its bound partyA in full. So the solver can choose to run itself at minimum margin without a market move. It draws down its own buffer, and nothing about the counterparty's claim or the protocol's solvency changes.

Flows that keep full verification

  • Third-party settlement. A different partyB settles on behalf of a partyA it shares positions with. Full Muon verification plus the settlement cooldown, exactly as before.
  • settlePartyBUpnlForLiquidation. Liquidators call it during PartyA liquidation. Its implementation verifies the unified settlement signature unconditionally, regardless of caller or binding state.
  • Force-close settlement. The force-close workflow verifies its own MuonFunction.ForceClose signature unconditionally.
  • Deprecated settleUpnl. Its behavior is frozen for backward compatibility. It keeps its existing Muon skip for bound partyAs and still runs its (payload-based) solvency checks.

Integration notes

  • Bound solvers can settle through settleUpnlUnified without fetching a Muon signature, provided the payload covers only their own book and only bound partyAs. Populate the upnl fields with real values. They are ignored on-chain in this path, but honest values keep off-chain reconciliation meaningful.
  • A batch mixing bound and unbound partyAs is verified as a whole. Split payloads by binding status if you want the oracle-less path for the bound subset.
  • Both parties' upnl nonces are bumped per settled partyA in the bound path too, so any in-flight Muon signatures minted against the old nonces are invalidated exactly as in verified settlement.
  • For anyone linking the library directly: LibSettlement.settleUpnlUnified gained a fifth parameter, skipSolvencyChecks. The facet derives it from the binding state; external integrations should pass false unless they replicate the same gating.