Skip to content

Plan 4 — Admin parameter updates #182

Description

@gravityblast

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):).

Metadata

Metadata

Assignees

No one assigned

    Type

    Fields

    No fields configured for Task.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions