mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-08-25 22:21:16 +00:00
feat(redemption-controller): implement initialize and update functions for redemption rate feedback controller
- Added `initialize_redemption_controller` and `update_redemption_controller` functions to manage redemption rate feedback. - Introduced `RedemptionController` struct to maintain state for redemption rates. - Updated `Cargo.toml` and `Cargo.lock` to include `twap_oracle_core` dependency. - Modified integration tests to cover new redemption controller functionality.
This commit is contained in:
@@ -3,8 +3,12 @@ use nssa::{
|
||||
public_transaction, PrivateKey, PublicKey, PublicTransaction, V03State,
|
||||
};
|
||||
use nssa_core::account::{Account, AccountId, Data, Nonce};
|
||||
use stablecoin_core::{compute_position_pda, compute_position_vault_pda, Position};
|
||||
use stablecoin_core::{
|
||||
compute_position_pda, compute_position_vault_pda, compute_redemption_controller_pda, Position,
|
||||
RedemptionController, CONTROLLER_GAIN_SCALE,
|
||||
};
|
||||
use token_core::{TokenDefinition, TokenHolding};
|
||||
use twap_oracle_core::OraclePriceAccount;
|
||||
|
||||
struct Keys;
|
||||
struct Ids;
|
||||
@@ -50,6 +54,26 @@ impl Ids {
|
||||
AccountId::new([6; 32])
|
||||
}
|
||||
|
||||
fn reference_asset() -> AccountId {
|
||||
AccountId::new([7; 32])
|
||||
}
|
||||
|
||||
fn price_feed() -> AccountId {
|
||||
AccountId::new([8; 32])
|
||||
}
|
||||
|
||||
fn redemption_controller() -> AccountId {
|
||||
compute_redemption_controller_pda(
|
||||
Self::stablecoin_program(),
|
||||
Self::stablecoin_definition(),
|
||||
Self::price_feed(),
|
||||
)
|
||||
}
|
||||
|
||||
fn oracle_program() -> nssa_core::program::ProgramId {
|
||||
[9u32; 8]
|
||||
}
|
||||
|
||||
fn user_stablecoin_holding() -> AccountId {
|
||||
AccountId::from(&PublicKey::new_from_private_key(
|
||||
&Keys::user_stablecoin_holding(),
|
||||
@@ -86,6 +110,14 @@ impl Balances {
|
||||
1_000
|
||||
}
|
||||
|
||||
fn redemption_price() -> u128 {
|
||||
1_000
|
||||
}
|
||||
|
||||
fn market_price_below_redemption() -> u128 {
|
||||
900
|
||||
}
|
||||
|
||||
fn user_stablecoin_holding_init() -> u128 {
|
||||
1_000
|
||||
}
|
||||
@@ -152,6 +184,22 @@ impl Accounts {
|
||||
}
|
||||
}
|
||||
|
||||
fn price_feed_init(price: u128, timestamp: u64) -> Account {
|
||||
Account {
|
||||
program_owner: Ids::oracle_program(),
|
||||
balance: 0_u128,
|
||||
data: Data::from(&OraclePriceAccount {
|
||||
base_asset: Ids::stablecoin_definition(),
|
||||
quote_asset: Ids::reference_asset(),
|
||||
price,
|
||||
timestamp,
|
||||
source_id: String::from("twap"),
|
||||
confidence_interval: 0,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn position_with_debt_init() -> Account {
|
||||
Account {
|
||||
program_owner: stablecoin_methods::STABLECOIN_ID,
|
||||
@@ -219,6 +267,20 @@ fn state_for_stablecoin_repay_tests() -> V03State {
|
||||
state
|
||||
}
|
||||
|
||||
fn state_for_stablecoin_redemption_controller_tests(price: u128, timestamp: u64) -> V03State {
|
||||
let mut state = V03State::new();
|
||||
deploy_programs(&mut state);
|
||||
state.force_insert_account(
|
||||
Ids::stablecoin_definition(),
|
||||
Accounts::stablecoin_definition_init(),
|
||||
);
|
||||
state.force_insert_account(
|
||||
Ids::price_feed(),
|
||||
Accounts::price_feed_init(price, timestamp),
|
||||
);
|
||||
state
|
||||
}
|
||||
|
||||
fn assert_position(state: &V03State, expected_collateral: u128) {
|
||||
let position =
|
||||
Position::try_from(&state.get_account_by_id(Ids::position()).data).expect("valid Position");
|
||||
@@ -398,3 +460,68 @@ fn stablecoin_repay_debt_burns_stablecoins_and_decreases_debt() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stablecoin_redemption_controller_initializes_and_updates_from_price_feed() {
|
||||
let current_timestamp = 100_u64;
|
||||
let mut state = state_for_stablecoin_redemption_controller_tests(
|
||||
Balances::market_price_below_redemption(),
|
||||
current_timestamp,
|
||||
);
|
||||
|
||||
let initialize = stablecoin_core::Instruction::InitializeRedemptionController {
|
||||
reference_asset_id: Ids::reference_asset(),
|
||||
initial_redemption_price: Balances::redemption_price(),
|
||||
proportional_gain: CONTROLLER_GAIN_SCALE,
|
||||
integral_gain: 0,
|
||||
max_integral_error: 1_000,
|
||||
max_redemption_rate: 500,
|
||||
max_price_feed_age: 10,
|
||||
current_timestamp,
|
||||
};
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::stablecoin_program(),
|
||||
vec![
|
||||
Ids::redemption_controller(),
|
||||
Ids::stablecoin_definition(),
|
||||
Ids::price_feed(),
|
||||
],
|
||||
vec![],
|
||||
initialize,
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state
|
||||
.transition_from_public_transaction(&tx, 0, current_timestamp)
|
||||
.expect("initialize_redemption_controller must succeed");
|
||||
|
||||
let controller =
|
||||
RedemptionController::try_from(&state.get_account_by_id(Ids::redemption_controller()).data)
|
||||
.expect("valid RedemptionController");
|
||||
assert_eq!(controller.redemption_price, Balances::redemption_price());
|
||||
assert_eq!(controller.redemption_rate, 0);
|
||||
assert_eq!(controller.oracle_program_id, Ids::oracle_program());
|
||||
|
||||
let update = stablecoin_core::Instruction::UpdateRedemptionController { current_timestamp };
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::stablecoin_program(),
|
||||
vec![Ids::redemption_controller(), Ids::price_feed()],
|
||||
vec![],
|
||||
update,
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state
|
||||
.transition_from_public_transaction(&tx, 0, current_timestamp)
|
||||
.expect("update_redemption_controller must succeed");
|
||||
|
||||
let controller =
|
||||
RedemptionController::try_from(&state.get_account_by_id(Ids::redemption_controller()).data)
|
||||
.expect("valid RedemptionController");
|
||||
assert_eq!(controller.redemption_price, Balances::redemption_price());
|
||||
assert_eq!(controller.redemption_rate, 100);
|
||||
assert_eq!(controller.accumulated_error, 0);
|
||||
assert_eq!(controller.last_update_timestamp, current_timestamp);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user