Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions artifacts/stablecoin-idl.json
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,71 @@
}
]
},
{
"name": "generate_debt",
"accounts": [
{
"name": "owner",
"writable": false,
"signer": true,
"init": false
},
{
"name": "position",
"writable": true,
"signer": false,
"init": false
},
{
"name": "stablecoin_definition",
"writable": true,
"signer": false,
"init": false
},
{
"name": "user_stablecoin_holding",
"writable": true,
"signer": false,
"init": false
},
{
"name": "stability_fee_accumulator",
"writable": false,
"signer": false,
"init": false
},
{
"name": "redemption_price_state",
"writable": false,
"signer": false,
"init": false
},
{
"name": "market_price_oracle",
"writable": false,
"signer": false,
"init": false
},
{
"name": "protocol_parameters",
"writable": false,
"signer": false,
"init": false
},
{
"name": "clock",
"writable": false,
"signer": false,
"init": false
}
],
"args": [
{
"name": "amount",
"type": "u128"
}
]
},
{
"name": "withdraw_collateral",
"accounts": [
Expand Down
24 changes: 24 additions & 0 deletions programs/stablecoin/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,30 @@ pub enum Instruction {
/// Collateral tokens to move from the user's holding into the vault.
amount: u128,
},
/// Mint stablecoins against an existing position, increasing its debt.
///
/// Blocked while frozen. The §6.2 collateralization invariant is checked
/// after the mint, and the normalized-debt delta is rounded **up** (§6.3).
///
/// Required accounts (9), in order:
/// 1. `owner` — authorized.
/// 2. `position` — initialized, writable, owned by `self_program_id`; at its `(owner,
/// position_nonce)` PDA.
/// 3. `stablecoin_definition` — initialized, writable via the chained `Token::Mint`; must
/// equal `protocol_parameters.stablecoin_definition_id`.
/// 4. `user_stablecoin_holding` — initialized mint destination; NOT required to be authorized.
/// Same Token Program and definition as `stablecoin_definition`.
/// 5. `stability_fee_accumulator` — initialized, read-only; at its canonical PDA.
/// 6. `redemption_price_state` — initialized, read-only; at its canonical PDA.
/// 7. `market_price_oracle` — initialized, read-only; must equal
/// `protocol_parameters.market_price_oracle_id`. Liveness gate only — its price is not
/// consumed.
/// 8. `protocol_parameters` — initialized, read-only; at its canonical PDA.
/// 9. `clock` — the system `CLOCK_01` account; read-only.
GenerateDebt {
/// Stablecoin atomic units to mint to `user_stablecoin_holding`.
amount: u128,
},
/// Withdraw `amount` collateral tokens from a position back to a user-controlled holding.
///
/// Blocked while the protocol is frozen. The §6.2 collateralization
Expand Down
50 changes: 50 additions & 0 deletions programs/stablecoin/methods/guest/src/bin/stablecoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,56 @@ mod stablecoin {
))
}

/// Mint stablecoins against an existing position (spec §10.7; host fn
/// `stablecoin_program::generate_debt`).
///
/// Blocked while frozen. The oracle is a liveness gate only. Wall-clock time
/// comes from the system `CLOCK_01` account passed as the 9th input.
///
/// # Errors
/// Returns the host program's panic-converted error if any precondition
/// fails — see the host fn for the full list.
#[instruction]
#[allow(
clippy::too_many_arguments,
reason = "the nine account inputs mirror the spec §10.7 ABI"
)]
pub fn generate_debt(
ctx: ProgramContext,
#[account(signer)]
owner: AccountWithMetadata,
#[account(mut)]
position: AccountWithMetadata,
#[account(mut)]
stablecoin_definition: AccountWithMetadata,
#[account(mut)]
user_stablecoin_holding: AccountWithMetadata,
stability_fee_accumulator: AccountWithMetadata,
redemption_price_state: AccountWithMetadata,
market_price_oracle: AccountWithMetadata,
protocol_parameters: AccountWithMetadata,
clock: AccountWithMetadata,
amount: u128,
) -> SpelResult {
let (post_states, chained_calls) = stablecoin_program::generate_debt::generate_debt(
owner,
position,
stablecoin_definition,
user_stablecoin_holding,
stability_fee_accumulator,
redemption_price_state,
market_price_oracle,
protocol_parameters,
clock,
ctx.self_program_id,
amount,
);
Ok(spel_framework::SpelOutput::execute(
post_states,
chained_calls,
))
}

/// Withdraw `amount` collateral tokens from an existing position back to a
/// user-controlled holding.
///
Expand Down
28 changes: 28 additions & 0 deletions programs/stablecoin/src/checks.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
//! Shared validation helpers reused across the position-lifecycle instructions.

use alloy_primitives::U256;
use lee_core::{
account::{Account, AccountId, AccountWithMetadata, Data},
program::ProgramId,
};
use stablecoin_core::{math::FIXED_POINT_ONE, Position};

/// Assert that `position` satisfies the collateralization invariant from spec §6.2:
Expand Down Expand Up @@ -62,6 +66,30 @@ pub fn assert_position_is_collateralized(
);
}

/// Validate a read-only global: initialized, program-owned, and at its canonical
/// PDA. Returns its `Data` for the caller to decode.
pub(crate) fn decode_global(
account: &AccountWithMetadata,
expected_id: AccountId,
stablecoin_program_id: ProgramId,
label: &str,
) -> Data {
assert_ne!(
account.account,
Account::default(),
"{label} account must be initialized"
);
assert_eq!(
account.account.program_owner, stablecoin_program_id,
"{label} account must be owned by the stablecoin program"
);
assert_eq!(
account.account_id, expected_id,
"{label} account ID does not match expected PDA derivation"
);
account.account.data.clone()
}

#[cfg(test)]
#[allow(
clippy::arithmetic_side_effects,
Expand Down
Loading
Loading