Stablecoin Plan 5 — Emergency Freeze
Goal: Land the emergency kill switch — freeze and unfreeze instructions that flip ProtocolParameters.is_frozen, plus the propagation of the frozen check into the three Plan 3 instructions that must be blocked while the protocol is frozen (open_position, withdraw_collateral, generate_debt). After this plan ships, the freeze authority can halt new risk-increasing operations end-to-end via the zkVM and resume normal operation later. Operations that REDUCE risk (deposit_collateral, repay_debt, close_position), permissionless keeper pokes, and admin parameter updates remain available throughout a freeze.
Architecture: Two new instructions follow the existing set_* shape from Plan 4: a 2-account input list (freeze_authority, protocol_parameters), an authority-check that mirrors the admin auth helper, and a single-field write that toggles ProtocolParameters.is_frozen. Both instructions are idempotent — calling freeze when already frozen is a no-op success, same for unfreeze. The frozen-check propagation is a single new assert! line in each of the three target Plan 3 host functions, placed immediately after the ProtocolParameters decode so every other precondition is still evaluated when the protocol is NOT frozen.
Tech stack: Rust 2021, RISC Zero zkVM, nssa_core (LEZ runtime types), Borsh + serde, spel_framework_macros::account_type, existing stablecoin_core types (ProtocolParameters, compute_protocol_parameters_pda).
Spec reference: docs/superpowers/specs/2026-06-03-stablecoin-rfp013-design.md. Sections directly implemented by this plan: §10.17 (freeze), §10.18 (unfreeze), and the protocol_parameters.is_frozen = true panic conditions called out in §10.4 (open_position), §10.6 (withdraw_collateral), §10.7 (generate_debt). The "Emergency" walkthrough in §16.2 doubles as the end-to-end integration test sequence.
Dependencies
- Plan 1 (Foundation + Bootstrap) — REQUIRED. We rely on the
ProtocolParameters struct (with is_frozen: bool), the compute_protocol_parameters_pda / compute_protocol_parameters_pda_seed helpers, and the freeze_authority_account_id field. Without Plan 1 there is no ProtocolParameters account to flip.
- Plan 3 (Position lifecycle) — REQUIRED. We patch
open_position, withdraw_collateral, generate_debt host functions to add the frozen-check assert!. These host functions must already exist in their full Plan-3 form (reading protocol_parameters, decoding it as ProtocolParameters). If Plan 3 hasn't landed, the issue 01 step that adds the assert! has nothing to attach to.
- Plan 4 (Admin parameter updates) — LIGHT dependency. Plan 4 issue 01 introduces the admin authorization helper pattern (
verify_admin_and_get_seed or equivalent name — confirm the actual final name when Plan 4 lands). Plan 5 mirrors the structural shape of that helper for the freeze_authority instead of admin. Plan 5 is parallelizable with Plan 4 in scheduling but the freeze-authority helper should reuse the structural shape Plan 4 settled on (separate helper, separate field on ProtocolParameters). If Plan 4 hasn't landed yet, Plan 5 issue 02 should land the helper inline; a small follow-up after Plan 4 can refactor to share patterns if desirable.
Dependency graph
Plan 1 ──────────────────► 01 ──► 02 ──► 03
Plan 3 ─────────────────► 01 ▲
Plan 4 (light, structural) ────────────┘
What is NOT in this plan
- The
ProtocolParameters struct itself, including the is_frozen field — Plan 1 issue 02.
- The three Plan 3 instructions (
open_position, withdraw_collateral, generate_debt) themselves; this plan only adds ONE line per host function (the frozen-check assert!) plus tests covering the new behaviour.
deposit_collateral, repay_debt, close_position — these stay ALLOWED when frozen by deliberate design (see §10.5, §10.8, §10.9, §16.2). Do NOT add a frozen check to them.
- Permissionless pokes (
accrue_stability_fee, update_redemption_rate, refresh_globals) — also stay allowed when frozen (Plan 2 owns them; nothing to change here).
- Admin
set_* instructions — also stay allowed when frozen (Plan 4 owns them; the admin can rotate the oracle while frozen, see §16.2). Nothing to change here.
Working in this plan
# Spec ergonomics
cat docs/superpowers/specs/2026-06-03-stablecoin-rfp013-design.md # full design
docs/superpowers/scripts/render-pdf.sh # regen PDF
# Local build / test loop
make clippy # all crates
RISC0_DEV_MODE=1 cargo test -p stablecoin_core # type-level tests
RISC0_DEV_MODE=1 cargo test -p stablecoin_program # host-function tests
RISC0_DEV_MODE=1 cargo test -p integration_tests --test stablecoin # end-to-end (zkVM)
cargo +nightly fmt --all # nightly required (rustfmt.toml uses unstable opts)
make idl # regenerate artifacts/stablecoin-idl.json after the guest changes in issue 03
Suggested GitHub labels
When creating issues, optional labels for triage:
area/stablecoin — all issues in this plan
plan-5 — this plan, to keep them grouped
kind/emergency — issues 02, 03 (the freeze kill-switch)
kind/guardrail — issue 01 (the frozen-check propagation)
Commits / PRs
One PR per issue, branched off main. Each PR should land green CI (clippy + tests + IDL check). Commit subjects in the body of each issue follow the existing repo style (feat(stablecoin):, test(stablecoin):).
Stablecoin Plan 5 — Emergency Freeze
Goal: Land the emergency kill switch —
freezeandunfreezeinstructions that flipProtocolParameters.is_frozen, plus the propagation of the frozen check into the three Plan 3 instructions that must be blocked while the protocol is frozen (open_position,withdraw_collateral,generate_debt). After this plan ships, the freeze authority can halt new risk-increasing operations end-to-end via the zkVM and resume normal operation later. Operations that REDUCE risk (deposit_collateral,repay_debt,close_position), permissionless keeper pokes, and admin parameter updates remain available throughout a freeze.Architecture: Two new instructions follow the existing
set_*shape from Plan 4: a 2-account input list (freeze_authority,protocol_parameters), an authority-check that mirrors the admin auth helper, and a single-field write that togglesProtocolParameters.is_frozen. Both instructions are idempotent — callingfreezewhen already frozen is a no-op success, same forunfreeze. The frozen-check propagation is a single newassert!line in each of the three target Plan 3 host functions, placed immediately after theProtocolParametersdecode so every other precondition is still evaluated when the protocol is NOT frozen.Tech stack: Rust 2021, RISC Zero zkVM,
nssa_core(LEZ runtime types), Borsh + serde,spel_framework_macros::account_type, existingstablecoin_coretypes (ProtocolParameters,compute_protocol_parameters_pda).Spec reference:
docs/superpowers/specs/2026-06-03-stablecoin-rfp013-design.md. Sections directly implemented by this plan: §10.17 (freeze), §10.18 (unfreeze), and theprotocol_parameters.is_frozen = truepanic conditions called out in §10.4 (open_position), §10.6 (withdraw_collateral), §10.7 (generate_debt). The "Emergency" walkthrough in §16.2 doubles as the end-to-end integration test sequence.Dependencies
ProtocolParametersstruct (withis_frozen: bool), thecompute_protocol_parameters_pda/compute_protocol_parameters_pda_seedhelpers, and thefreeze_authority_account_idfield. Without Plan 1 there is noProtocolParametersaccount to flip.open_position,withdraw_collateral,generate_debthost functions to add the frozen-checkassert!. These host functions must already exist in their full Plan-3 form (readingprotocol_parameters, decoding it asProtocolParameters). If Plan 3 hasn't landed, the issue 01 step that adds theassert!has nothing to attach to.verify_admin_and_get_seedor equivalent name — confirm the actual final name when Plan 4 lands). Plan 5 mirrors the structural shape of that helper for thefreeze_authorityinstead ofadmin. Plan 5 is parallelizable with Plan 4 in scheduling but the freeze-authority helper should reuse the structural shape Plan 4 settled on (separate helper, separate field onProtocolParameters). If Plan 4 hasn't landed yet, Plan 5 issue 02 should land the helper inline; a small follow-up after Plan 4 can refactor to share patterns if desirable.Dependency graph
What is NOT in this plan
ProtocolParametersstruct itself, including theis_frozenfield — Plan 1 issue 02.open_position,withdraw_collateral,generate_debt) themselves; this plan only adds ONE line per host function (the frozen-checkassert!) plus tests covering the new behaviour.deposit_collateral,repay_debt,close_position— these stay ALLOWED when frozen by deliberate design (see §10.5, §10.8, §10.9, §16.2). Do NOT add a frozen check to them.accrue_stability_fee,update_redemption_rate,refresh_globals) — also stay allowed when frozen (Plan 2 owns them; nothing to change here).set_*instructions — also stay allowed when frozen (Plan 4 owns them; the admin can rotate the oracle while frozen, see §16.2). Nothing to change here.Working in this plan
Suggested GitHub labels
When creating issues, optional labels for triage:
area/stablecoin— all issues in this planplan-5— this plan, to keep them groupedkind/emergency— issues 02, 03 (the freeze kill-switch)kind/guardrail— issue 01 (the frozen-check propagation)Commits / PRs
One PR per issue, branched off
main. Each PR should land green CI (clippy + tests + IDL check). Commit subjects in the body of each issue follow the existing repo style (feat(stablecoin):,test(stablecoin):).