Stablecoin Plan 4 — Admin parameter updates
Goal: Land the seven admin-only set_* instructions described in spec §10.10–§10.16, with a shared admin-auth helper, exhaustive unit tests, end-to-end integration coverage, and a regenerated IDL. After this plan ships, an authorized admin can tune every mutable knob in ProtocolParameters — the stability fee (with auto-accrue at the OLD rate first), the minimum collateralization ratio, the PI controller gains, the market price oracle, the two timing parameters, the admin handle itself, and the freeze-authority handle.
Architecture: All seven setters share a common skeleton (admin auth check + writable ProtocolParameters), so they live alongside one another in a new programs/stablecoin/src/admin.rs module. A tiny stablecoin_program::checks::assert_admin_authorized helper centralizes the two-part authorization check (admin.is_authorized AND admin.account_id == params.admin_account_id) used by all seven. The bound constants from spec §8 are factored into stablecoin_program::bounds (re-using the same numbers initialize_program already enforces) so init and set_* agree on the band by construction. set_stability_fee_per_millisecond is the lone special case: it touches a third account (StabilityFeeAccumulator) and auto-advances it at the OLD rate before writing the new rate — reusing the same accumulator-advance routine the permissionless accrue_stability_fee poke uses (landed in Plan 2).
Tech stack: Rust 2021, RISC Zero zkVM, nssa_core (LEZ runtime types), Borsh + serde, spel_framework_macros::account_type, existing twap_oracle_core::OraclePriceAccount for the set_market_price_oracle validation path.
Spec reference: docs/superpowers/specs/2026-06-03-stablecoin-rfp013-design.md. Sections directly implemented by this plan: §9.4 (instruction-set summary), §10.10–§10.16 (per-instruction details, shared skeleton), §8 (numerical bounds — same bands initialize_program already enforces), §7 cross-instruction invariants (the accumulator-monotonicity guarantee that justifies the auto-accrue path in set_stability_fee_per_millisecond).
Dependency graph
Plan 1 (ProtocolParameters, StabilityFeeAccumulator,
Instruction::InitializeProgram)
|
v
[01 — admin-auth helper]
/ \
v v
[02 — set_stability_fee_ [03 — other six setters]
per_millisecond] |
| ^ |
| | |
| Plan 2 |
| (accumulator |
| advance routine |
| from accrue_ |
| stability_fee) |
v v
[04 — guest wiring + integration tests + IDL regen]
What is NOT in this plan
- The bootstrap path (
initialize_program) — Plan 1.
- Permissionless pokes (
accrue_stability_fee, update_redemption_rate) — Plan 2.
- Position lifecycle instructions — Plan 3.
- Emergency
freeze / unfreeze — Plan 5. (These are the freeze-authority's job, not the admin's; the data model overlaps with this plan because both write ProtocolParameters, but the auth check is different — is_authorized + freeze_authority_account_id match, not the admin match. Plan 5 will use a parallel assert_freeze_authority_authorized helper.)
- Liquidation / surplus auctions — out of scope per RFP-013 (§12).
- Two-step
pending_admin / accept_admin rotation — explicitly deferred per spec §14 ("Two-step admin / freeze rotation"). This plan ships one-step rotation; the comment block in issue 03 records the trade-off.
Plan ordering vs Plan 2
Plan 4's issue 02 (set_stability_fee_per_millisecond) auto-accrues the global accumulator at the OLD rate before writing the new rate. The cleanest implementation reuses the accumulator-advance routine that Plan 2's accrue_stability_fee host function exposes — same math, same monotonicity guarantee, same overflow guards (spec §7.2). Recommendation: land Plan 2 before Plan 4 issue 02 so the helper exists. If Plan 4 needs to land first, issue 02 can inline the accumulator-advance math directly (it's small — a compound_rate(old_rate, dt) call plus a mul_div against the anchor) and Plan 2 can refactor it into a shared helper afterward.
The rest of Plan 4 (issues 01, 03, 04) depends only on Plan 1 and can land before Plan 2 with no friction.
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 04
Suggested GitHub labels
When creating issues, optional labels for triage:
area/stablecoin — all issues in this plan
plan-4 — this plan, to keep them grouped
kind/foundation — issue 01 (the shared helper)
kind/instruction — issues 02, 03
kind/integration — issue 04
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):, refactor(stablecoin):, test(stablecoin):).
Stablecoin Plan 4 — Admin parameter updates
Goal: Land the seven admin-only
set_*instructions described in spec §10.10–§10.16, with a shared admin-auth helper, exhaustive unit tests, end-to-end integration coverage, and a regenerated IDL. After this plan ships, an authorized admin can tune every mutable knob inProtocolParameters— the stability fee (with auto-accrue at the OLD rate first), the minimum collateralization ratio, the PI controller gains, the market price oracle, the two timing parameters, the admin handle itself, and the freeze-authority handle.Architecture: All seven setters share a common skeleton (admin auth check + writable
ProtocolParameters), so they live alongside one another in a newprograms/stablecoin/src/admin.rsmodule. A tinystablecoin_program::checks::assert_admin_authorizedhelper centralizes the two-part authorization check (admin.is_authorizedANDadmin.account_id == params.admin_account_id) used by all seven. The bound constants from spec §8 are factored intostablecoin_program::bounds(re-using the same numbersinitialize_programalready enforces) so init andset_*agree on the band by construction.set_stability_fee_per_millisecondis the lone special case: it touches a third account (StabilityFeeAccumulator) and auto-advances it at the OLD rate before writing the new rate — reusing the same accumulator-advance routine the permissionlessaccrue_stability_feepoke uses (landed in Plan 2).Tech stack: Rust 2021, RISC Zero zkVM,
nssa_core(LEZ runtime types), Borsh + serde,spel_framework_macros::account_type, existingtwap_oracle_core::OraclePriceAccountfor theset_market_price_oraclevalidation path.Spec reference:
docs/superpowers/specs/2026-06-03-stablecoin-rfp013-design.md. Sections directly implemented by this plan: §9.4 (instruction-set summary), §10.10–§10.16 (per-instruction details, shared skeleton), §8 (numerical bounds — same bandsinitialize_programalready enforces), §7 cross-instruction invariants (the accumulator-monotonicity guarantee that justifies the auto-accrue path inset_stability_fee_per_millisecond).Dependency graph
What is NOT in this plan
initialize_program) — Plan 1.accrue_stability_fee,update_redemption_rate) — Plan 2.freeze/unfreeze— Plan 5. (These are the freeze-authority's job, not the admin's; the data model overlaps with this plan because both writeProtocolParameters, but the auth check is different —is_authorized+freeze_authority_account_idmatch, not the admin match. Plan 5 will use a parallelassert_freeze_authority_authorizedhelper.)pending_admin/accept_adminrotation — explicitly deferred per spec §14 ("Two-step admin / freeze rotation"). This plan ships one-step rotation; the comment block in issue 03 records the trade-off.Plan ordering vs Plan 2
Plan 4's issue 02 (
set_stability_fee_per_millisecond) auto-accrues the global accumulator at the OLD rate before writing the new rate. The cleanest implementation reuses the accumulator-advance routine that Plan 2'saccrue_stability_feehost function exposes — same math, same monotonicity guarantee, same overflow guards (spec §7.2). Recommendation: land Plan 2 before Plan 4 issue 02 so the helper exists. If Plan 4 needs to land first, issue 02 can inline the accumulator-advance math directly (it's small — acompound_rate(old_rate, dt)call plus amul_divagainst the anchor) and Plan 2 can refactor it into a shared helper afterward.The rest of Plan 4 (issues 01, 03, 04) depends only on Plan 1 and can land before Plan 2 with no friction.
Working in this plan
Suggested GitHub labels
When creating issues, optional labels for triage:
area/stablecoin— all issues in this planplan-4— this plan, to keep them groupedkind/foundation— issue 01 (the shared helper)kind/instruction— issues 02, 03kind/integration— issue 04Commits / 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):,refactor(stablecoin):,test(stablecoin):).