2026-07-02 17:25:56 -03:00

193 lines
6.5 KiB
Rust

#![cfg_attr(not(test), no_main)]
use nssa_core::account::{AccountId, AccountWithMetadata};
use spel_framework::{context::ProgramContext, prelude::*};
#[cfg(not(test))]
risc0_zkvm::guest::entry!(main);
#[lez_program(instruction = "stablecoin_core::Instruction")]
mod stablecoin {
#[allow(unused_imports)]
use super::*;
/// Open a new collateral-only position for the calling owner.
///
/// # Errors
/// Returns the host program's panic-converted error if any precondition fails (see
/// [`stablecoin_program::open_position::open_position`] for the full list).
#[instruction]
pub fn open_position(
ctx: ProgramContext,
#[account(signer)]
owner: AccountWithMetadata,
#[account(init)]
position: AccountWithMetadata,
#[account(init)]
vault: AccountWithMetadata,
#[account(mut, signer)]
user_holding: AccountWithMetadata,
token_definition: AccountWithMetadata,
collateral_amount: u128,
) -> SpelResult {
let (post_states, chained_calls) = stablecoin_program::open_position::open_position(
owner,
position,
vault,
user_holding,
token_definition,
ctx.self_program_id,
collateral_amount,
);
Ok(spel_framework::SpelOutput::execute(
post_states,
chained_calls,
))
}
/// Withdraw `amount` collateral tokens from an existing position back to a
/// user-controlled holding.
///
/// # Errors
/// Returns the host program's panic-converted error if any precondition
/// fails (see
/// [`stablecoin_program::withdraw_collateral::withdraw_collateral`] for the
/// full list).
#[instruction]
pub fn withdraw_collateral(
ctx: ProgramContext,
#[account(signer)]
owner: AccountWithMetadata,
#[account(mut)]
position: AccountWithMetadata,
#[account(mut)]
vault: AccountWithMetadata,
#[account(mut)]
destination: AccountWithMetadata,
amount: u128,
) -> SpelResult {
let (post_states, chained_calls) =
stablecoin_program::withdraw_collateral::withdraw_collateral(
owner,
position,
vault,
destination,
ctx.self_program_id,
amount,
);
Ok(spel_framework::SpelOutput::execute(
post_states,
chained_calls,
))
}
/// Repay `amount` of outstanding stablecoin debt against an existing position.
///
/// # Errors
/// Returns the host program's panic-converted error if any precondition
/// fails (see [`stablecoin_program::repay_debt::repay_debt`] for the
/// full list).
#[instruction]
pub fn repay_debt(
ctx: ProgramContext,
#[account(signer)]
owner: AccountWithMetadata,
#[account(mut)]
position: AccountWithMetadata,
#[account(mut)]
stablecoin_definition: AccountWithMetadata,
#[account(mut, signer)]
user_stablecoin_holding: AccountWithMetadata,
amount: u128,
) -> SpelResult {
let (post_states, chained_calls) = stablecoin_program::repay_debt::repay_debt(
owner,
position,
stablecoin_definition,
user_stablecoin_holding,
ctx.self_program_id,
amount,
);
Ok(spel_framework::SpelOutput::execute(
post_states,
chained_calls,
))
}
/// Initialize redemption-rate feedback controller state for one stablecoin/feed pair.
///
/// # Errors
/// Returns the host program's panic-converted error if any precondition
/// fails (see
/// [`stablecoin_program::redemption_controller::initialize_redemption_controller`]
/// for the full list).
#[expect(
clippy::too_many_arguments,
reason = "instruction interface exposes controller configuration explicitly"
)]
#[instruction]
pub fn initialize_redemption_controller(
ctx: ProgramContext,
controller: AccountWithMetadata,
stablecoin_definition: AccountWithMetadata,
price_feed: AccountWithMetadata,
collateral_definition_id: AccountId,
initial_redemption_price: u128,
proportional_gain: u128,
integral_gain: u128,
max_integral_error: u128,
max_redemption_rate: u128,
max_price_feed_age: u64,
current_timestamp: u64,
) -> SpelResult {
let post_states =
stablecoin_program::redemption_controller::initialize_redemption_controller(
controller,
stablecoin_definition,
price_feed,
ctx.self_program_id,
collateral_definition_id,
initial_redemption_price,
proportional_gain,
integral_gain,
max_integral_error,
max_redemption_rate,
max_price_feed_age,
current_timestamp,
);
let validity_end = current_timestamp
.checked_add(1)
.expect("current_timestamp must allow an exact validity window");
Ok(spel_framework::SpelOutput::execute(post_states, vec![])
.try_with_timestamp_validity_window(current_timestamp..validity_end)
.expect("exact timestamp validity window must be non-empty"))
}
/// Update redemption price and redemption rate from the configured price feed.
///
/// # Errors
/// Returns the host program's panic-converted error if controller state
/// validation fails. Stale or unavailable price feeds pause updates by
/// emitting the controller state unchanged.
#[instruction]
pub fn update_redemption_controller(
ctx: ProgramContext,
controller: AccountWithMetadata,
price_feed: AccountWithMetadata,
current_timestamp: u64,
) -> SpelResult {
let post_states = stablecoin_program::redemption_controller::update_redemption_controller(
controller,
price_feed,
ctx.self_program_id,
current_timestamp,
);
let validity_end = current_timestamp
.checked_add(1)
.expect("current_timestamp must allow an exact validity window");
Ok(spel_framework::SpelOutput::execute(post_states, vec![])
.try_with_timestamp_validity_window(current_timestamp..validity_end)
.expect("exact timestamp validity window must be non-empty"))
}
}