feat(stablecoin): implement withdraw_collateral (#92)#112
Conversation
3a8e5c3 to
b7257ad
Compare
There was a problem hiding this comment.
Pull request overview
This PR implements the withdraw_collateral instruction for the stablecoin program (closes #92). The implementation mirrors the conventions established in open_position: validate inputs, decode the Position, verify position/vault PDAs, and emit a chained Token::Transfer from the vault to the destination under the vault's PDA authority. As called out in issue #92, full functionality requires stability fee accrual and a collateralization-ratio check (issues #95/#96/#97); until those land, the instruction hard-asserts Position.debt_amount == 0.
Changes:
- Adds
withdraw_collateralcore logic that validates accounts, decrementsPosition.collateral_amount, and emits a chainedToken::Transfer. - Wires the instruction into the guest entrypoint and the
Instructionenum, regenerating the stablecoin IDL. - Adds 12 new unit tests covering the happy path, edge cases (zero/full drain), and each panic precondition.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| stablecoin/src/withdraw_collateral.rs | New module implementing the instruction's logic, account validation, and chained transfer emission. |
| stablecoin/src/lib.rs | Exposes the new withdraw_collateral module. |
| stablecoin/src/tests.rs | New helper constructors and 12 unit tests for the happy path and all panic preconditions. |
| stablecoin/core/src/lib.rs | Adds the WithdrawCollateral { amount } variant to the Instruction enum with account-list documentation. |
| stablecoin/methods/guest/src/bin/stablecoin.rs | Guest-side #[instruction] wrapper that delegates to withdraw_collateral. |
| artifacts/stablecoin-idl.json | Regenerated IDL entry for the new instruction. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3esmit
left a comment
There was a problem hiding this comment.
Add a test that gives the vault a balance matching Position.collateral_amount and either executes the emitted transfer through token_program::transfer::transfer or, better, adds an integration test that opens a stablecoin position and then withdraws from it through LEZ chained-call execution
| let (post_states, chained_calls) = crate::withdraw_collateral::withdraw_collateral( | ||
| owner_account(), | ||
| init_position_account(initial_collateral, 0), | ||
| init_vault_account(), |
There was a problem hiding this comment.
Vault account is initialized with balance 0
| #[test] | ||
| fn withdraw_collateral_updates_position_and_emits_transfer() { | ||
| let initial_collateral: u128 = 500; | ||
| let amount: u128 = 200; |
| }, | ||
| ) | ||
| .with_pda_seeds(vec![compute_position_vault_pda_seed(position_id())]); | ||
| assert_eq!(chained_calls[0], expected_transfer); |
There was a problem hiding this comment.
Only checks if Transfer is emmited, but the downstream token program would reject the transfer because it does not have balance.
closes #92
Note: fee accrual and post-withdrawal collateralization checks are defered to be implemented together with generate_debt.