Skip to content

feat(stablecoin): rebuild withdraw_collateral with the collateralization check - #348

Open
gravityblast wants to merge 1 commit into
feat/stablecoin-deposit-collateralfrom
feat/stablecoin-withdraw-collateral-rebuild
Open

feat(stablecoin): rebuild withdraw_collateral with the collateralization check#348
gravityblast wants to merge 1 commit into
feat/stablecoin-deposit-collateralfrom
feat/stablecoin-withdraw-collateral-rebuild

Conversation

@gravityblast

Copy link
Copy Markdown
Collaborator

Rebuilds withdraw_collateral to spec §10.6, replacing the Plan 1 scaffold's
normalized_debt_amount == 0 placeholder with the real §6.2 collateralization
check.

Accounts go 4 → 8, adding stability_fee_accumulator, redemption_price_state,
protocol_parameters and clock, all read-only and echoed unchanged. The check
runs after the decrement, against the accumulator and redemption price both
projected forward to the clock timestamp (§5.3), using the helper from #336.
Also adds the frozen check, which the scaffold never had.

All three globals are validated at their canonical PDAs via a small shared
decode_global helper — initialized, program-owned, right address — so the same
substitution gap Copilot flagged on #341 can't apply here.

8 new tests: the four read-only globals echoed, withdrawal with debt when
collateralization holds, the exact ratio boundary, one unit below it, frozen, and
an uninitialized variant of each global. The scaffold's
rejects_withdrawal_with_outstanding_debt test is removed — it pinned the
placeholder this issue deletes.

The open/withdraw e2e seeds the two new globals and passes all four accounts.

Fourth of eight issues in Plan 3 (#173). Stacked on #345.

closes #177

@gravityblast
gravityblast force-pushed the feat/stablecoin-deposit-collateral branch from 3a92d45 to b13e825 Compare September 2, 2026 12:08
@gravityblast
gravityblast force-pushed the feat/stablecoin-withdraw-collateral-rebuild branch from d6108e0 to 17d1634 Compare September 2, 2026 12:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are still doc/test inconsistencies in the modified areas (outdated panic/docs and a test that claims to assert four echoed globals but only checks one), which should be corrected before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR rebuilds the stablecoin program’s withdraw_collateral instruction to follow the spec’s fee-aware, post-decrement collateralization invariant, expanding the ABI to include additional read-only global/state accounts and updating the guest entrypoint, IDL, unit tests, and the zkVM integration test fixtures accordingly.

Changes:

  • Replaces the scaffolded “no debt allowed” guard with a spec-aligned collateralization check using projected accumulator/redemption-price values and adds the protocol frozen gate.
  • Expands WithdrawCollateral account list from 4 → 8 (adds accumulator, redemption price state, protocol parameters, and clock) and regenerates IDL/guest wiring.
  • Updates stablecoin unit tests and the integration test harness to seed/pass the new global accounts.
File summaries
File Description
programs/stablecoin/src/withdraw_collateral.rs Implements freeze check + post-decrement collateralization enforcement; adds global PDA validation helper; updates destination naming.
programs/stablecoin/src/tests.rs Adds new withdraw-collateral tests and updates existing ones for the 8-account ABI.
programs/stablecoin/methods/guest/src/bin/stablecoin.rs Updates guest instruction signature to accept the four new accounts.
programs/stablecoin/core/src/lib.rs Updates Instruction::WithdrawCollateral docs to reflect the new account list and semantics.
programs/integration_tests/tests/stablecoin.rs Seeds and passes the new global accounts in the e2e test state and call site.
artifacts/stablecoin-idl.json Regenerates IDL to match the new withdraw-collateral ABI (account names/order).
Review details

Suppressed comments (3)

programs/stablecoin/src/withdraw_collateral.rs:37

  • The panic list still claims non-zero debt is rejected, but debt is now allowed as long as the post-withdrawal position remains collateralized. Update this bullet to reflect the actual failure mode.
/// - `Position.normalized_debt_amount` is non-zero.

programs/stablecoin/src/tests.rs:1310

  • Test name still uses the old destination terminology, but the account is now user_collateral_holding. Renaming the test improves clarity and keeps terminology consistent.
        STABLECOIN_PROGRAM_ID,
        100,
    );
}

programs/stablecoin/src/tests.rs:1333

  • Test name still uses the old destination terminology, but the account is now user_collateral_holding. Renaming the test improves clarity and keeps terminology consistent.
        protocol_parameters_account(false),
        clock_account(NOW),
        STABLECOIN_PROGRAM_ID,
        100,
    );
}
  • Files reviewed: 6/6 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +975 to +981
assert_eq!(post_states.len(), 8);
assert_eq!(chained_calls.len(), 1);
assert_eq!(
*post_states[7].account(),
clock_account(NOW).account,
"clock must be echoed unchanged"
);
/// `Position.normalized_debt_amount == 0` instead of accruing fees and
/// checking the collateralization ratio.
WithdrawCollateral {
/// Amount of collateral tokens to move from the vault back to `destination`.
expected = "Destination token definition does not match the position's collateral definition"
expected = "User collateral holding definition does not match the position's collateral definition"
)]
fn withdraw_collateral_rejects_destination_for_other_definition() {
Comment on lines +14 to 18
/// Withdraw `amount` collateral tokens from `position`'s vault back to `user_collateral_holding`.
///
/// Decreases `Position.collateral_amount` by `amount` and emits a single chained
/// `Token::Transfer` from the vault to `destination`, authorized by the vault
/// `Token::Transfer` from the vault to `user_collateral_holding`, authorized by the vault
/// PDA seed. The position post-state uses plain [`AccountPostState::new`] —
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants