Summary
examples/stability/stability_vault.ark has the same recovery-asymmetry class as #67, in its most severe form: the seeker (counterparty) holds an unconstrained unilateral CSV exit that can sweep the entire totalCollateral — including the provider's collateral — while the provider has no operator-independent recovery path at all.
Severity: High (the seeker can take more than their entitlement, at the provider's expense, and the provider cannot self-recover).
Details
The vault's totalCollateral is funded partly by the taker's userBTC and partly by the provider's offer collateral (stability_offer.ark: totalCollateral = userBTC * (100 + collateralRatioPct) / 100). So the provider is the disadvantaged depositor.
- The seeker's only raw leaf is
function unilateral(signature seekerSig) tapscript { require(older(exit)); require(checkSig(seekerSig, ...)); } — unconstrained, so after older(exit) the seeker can send the whole vault anywhere.
- The seeker's true entitlement is
seekerPayout = clamp(newTargetUSD·1e8 / P, 0, totalCollateral), normally less than totalCollateral. The unilateral leaf lets them take all of it.
- The provider's recovery paths —
providerExit(...), removeCapital(...), settleAndUpdateFunding(...) — are all covenants (server + tweaked emulator, plus an oracle signature). There is no operator-free provider exit. Under operator outage the provider cannot recover collateral, but the seeker's CSV exit still resolves.
Root cause
Same as #67: covenant paths are mandatorily co-signed (src/compiler/tapscript.rs:521-557), and a tapscript leaf cannot pin outputs (src/parser/grammar.pest:41-42, src/parser/tapscript.rs:146-148). The compiler is correct; the contract hands the unconstrained unilateral exit to the wrong party and gives the depositor (provider) no operator-free path.
Fix direction
Two things are needed (they're independent and ideally both):
- Give the provider an operator-independent exit — a CSV +
checkSig(providerSig, providerPk) tapscript leaf, so the provider can always recover their collateral without the operator.
- Constrain the seeker's exit so it cannot exceed
seekerPayout. Since a tapscript leaf cannot introspect outputs, this can't be a raw unilateral leaf — the bounded seeker payout must remain a covenant path, and the seeker's unconstrained unilateral leaf should be removed (or its CSV delay set strictly after the provider's exit so the provider always recovers first).
Because a bounded, output-pinning exit is only expressible as a covenant (operator-dependent), the honest resolution here is: the provider — whose principal is at stake — gets the operator-free unilateral exit, and the seeker's over-claiming raw exit is removed.
Companion: the general payment-family instance is #67; the authoring-guidance gap + optional validator lint is tracked separately (linked once filed).
Summary
examples/stability/stability_vault.arkhas the same recovery-asymmetry class as #67, in its most severe form: the seeker (counterparty) holds an unconstrained unilateral CSV exit that can sweep the entiretotalCollateral— including the provider's collateral — while the provider has no operator-independent recovery path at all.Severity: High (the seeker can take more than their entitlement, at the provider's expense, and the provider cannot self-recover).
Details
The vault's
totalCollateralis funded partly by the taker'suserBTCand partly by the provider's offer collateral (stability_offer.ark:totalCollateral = userBTC * (100 + collateralRatioPct) / 100). So the provider is the disadvantaged depositor.function unilateral(signature seekerSig) tapscript { require(older(exit)); require(checkSig(seekerSig, ...)); }— unconstrained, so afterolder(exit)the seeker can send the whole vault anywhere.seekerPayout = clamp(newTargetUSD·1e8 / P, 0, totalCollateral), normally less thantotalCollateral. The unilateral leaf lets them take all of it.providerExit(...),removeCapital(...),settleAndUpdateFunding(...)— are all covenants (server + tweaked emulator, plus an oracle signature). There is no operator-free provider exit. Under operator outage the provider cannot recover collateral, but the seeker's CSV exit still resolves.Root cause
Same as #67: covenant paths are mandatorily co-signed (
src/compiler/tapscript.rs:521-557), and atapscriptleaf cannot pin outputs (src/parser/grammar.pest:41-42,src/parser/tapscript.rs:146-148). The compiler is correct; the contract hands the unconstrained unilateral exit to the wrong party and gives the depositor (provider) no operator-free path.Fix direction
Two things are needed (they're independent and ideally both):
checkSig(providerSig, providerPk)tapscript leaf, so the provider can always recover their collateral without the operator.seekerPayout. Since atapscriptleaf cannot introspect outputs, this can't be a raw unilateral leaf — the bounded seeker payout must remain a covenant path, and the seeker's unconstrainedunilateralleaf should be removed (or its CSV delay set strictly after the provider's exit so the provider always recovers first).Because a bounded, output-pinning exit is only expressible as a covenant (operator-dependent), the honest resolution here is: the provider — whose principal is at stake — gets the operator-free unilateral exit, and the seeker's over-claiming raw exit is removed.
Companion: the general payment-family instance is #67; the authoring-guidance gap + optional validator lint is tracked separately (linked once filed).