mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-07-03 21:49:28 +00:00
fix(stablecoin): align redemption controller feedback
This commit is contained in:
parent
d2f3e1d8fb
commit
61c3f6299f
@ -137,7 +137,7 @@
|
|||||||
],
|
],
|
||||||
"args": [
|
"args": [
|
||||||
{
|
{
|
||||||
"name": "reference_asset_id",
|
"name": "collateral_definition_id",
|
||||||
"type": "account_id"
|
"type": "account_id"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -229,17 +229,13 @@
|
|||||||
"type": "account_id"
|
"type": "account_id"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "reference_asset_id",
|
"name": "collateral_definition_id",
|
||||||
"type": "account_id"
|
"type": "account_id"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "price_feed_id",
|
"name": "price_feed_id",
|
||||||
"type": "account_id"
|
"type": "account_id"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "oracle_program_id",
|
|
||||||
"type": "program_id"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "redemption_price",
|
"name": "redemption_price",
|
||||||
"type": "u128"
|
"type": "u128"
|
||||||
|
|||||||
@ -54,10 +54,6 @@ impl Ids {
|
|||||||
AccountId::new([6; 32])
|
AccountId::new([6; 32])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reference_asset() -> AccountId {
|
|
||||||
AccountId::new([7; 32])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn price_feed() -> AccountId {
|
fn price_feed() -> AccountId {
|
||||||
AccountId::new([8; 32])
|
AccountId::new([8; 32])
|
||||||
}
|
}
|
||||||
@ -190,7 +186,7 @@ impl Accounts {
|
|||||||
balance: 0_u128,
|
balance: 0_u128,
|
||||||
data: Data::from(&OraclePriceAccount {
|
data: Data::from(&OraclePriceAccount {
|
||||||
base_asset: Ids::stablecoin_definition(),
|
base_asset: Ids::stablecoin_definition(),
|
||||||
quote_asset: Ids::reference_asset(),
|
quote_asset: Ids::collateral_definition(),
|
||||||
price,
|
price,
|
||||||
timestamp,
|
timestamp,
|
||||||
source_id: String::from("twap"),
|
source_id: String::from("twap"),
|
||||||
@ -470,7 +466,7 @@ fn stablecoin_redemption_controller_initializes_and_updates_from_price_feed() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let initialize = stablecoin_core::Instruction::InitializeRedemptionController {
|
let initialize = stablecoin_core::Instruction::InitializeRedemptionController {
|
||||||
reference_asset_id: Ids::reference_asset(),
|
collateral_definition_id: Ids::collateral_definition(),
|
||||||
initial_redemption_price: Balances::redemption_price(),
|
initial_redemption_price: Balances::redemption_price(),
|
||||||
proportional_gain: CONTROLLER_GAIN_SCALE,
|
proportional_gain: CONTROLLER_GAIN_SCALE,
|
||||||
integral_gain: 0,
|
integral_gain: 0,
|
||||||
@ -501,7 +497,6 @@ fn stablecoin_redemption_controller_initializes_and_updates_from_price_feed() {
|
|||||||
.expect("valid RedemptionController");
|
.expect("valid RedemptionController");
|
||||||
assert_eq!(controller.redemption_price, Balances::redemption_price());
|
assert_eq!(controller.redemption_price, Balances::redemption_price());
|
||||||
assert_eq!(controller.redemption_rate, 0);
|
assert_eq!(controller.redemption_rate, 0);
|
||||||
assert_eq!(controller.oracle_program_id, Ids::oracle_program());
|
|
||||||
|
|
||||||
let update = stablecoin_core::Instruction::UpdateRedemptionController { current_timestamp };
|
let update = stablecoin_core::Instruction::UpdateRedemptionController { current_timestamp };
|
||||||
let message = public_transaction::Message::try_new(
|
let message = public_transaction::Message::try_new(
|
||||||
@ -521,7 +516,7 @@ fn stablecoin_redemption_controller_initializes_and_updates_from_price_feed() {
|
|||||||
RedemptionController::try_from(&state.get_account_by_id(Ids::redemption_controller()).data)
|
RedemptionController::try_from(&state.get_account_by_id(Ids::redemption_controller()).data)
|
||||||
.expect("valid RedemptionController");
|
.expect("valid RedemptionController");
|
||||||
assert_eq!(controller.redemption_price, Balances::redemption_price());
|
assert_eq!(controller.redemption_price, Balances::redemption_price());
|
||||||
assert_eq!(controller.redemption_rate, 100);
|
assert_eq!(controller.redemption_rate, -100);
|
||||||
assert_eq!(controller.accumulated_error, 0);
|
assert_eq!(controller.accumulated_error, 0);
|
||||||
assert_eq!(controller.last_update_timestamp, current_timestamp);
|
assert_eq!(controller.last_update_timestamp, current_timestamp);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,14 +92,15 @@ pub enum Instruction {
|
|||||||
/// - Redemption controller account (uninitialized, address must match
|
/// - Redemption controller account (uninitialized, address must match
|
||||||
/// `compute_redemption_controller_pda(self_program_id, stablecoin_definition, price_feed)`)
|
/// `compute_redemption_controller_pda(self_program_id, stablecoin_definition, price_feed)`)
|
||||||
/// - Stablecoin token definition account (initialized fungible token)
|
/// - Stablecoin token definition account (initialized fungible token)
|
||||||
/// - Oracle price feed account (initialized; its `program_owner` becomes the configured oracle
|
/// - Oracle price feed account (initialized; must decode as the configured
|
||||||
/// program)
|
/// stablecoin/collateral market price)
|
||||||
///
|
///
|
||||||
/// `proportional_gain` and `integral_gain` use [`CONTROLLER_GAIN_SCALE`] fixed-point
|
/// `proportional_gain` and `integral_gain` use [`CONTROLLER_GAIN_SCALE`] fixed-point
|
||||||
/// precision. For example, `CONTROLLER_GAIN_SCALE / 10` represents `0.1`.
|
/// precision. For example, `CONTROLLER_GAIN_SCALE / 10` represents `0.1`.
|
||||||
InitializeRedemptionController {
|
InitializeRedemptionController {
|
||||||
/// Asset that denominates both the oracle market price and redemption price.
|
/// Collateral token definition that denominates the oracle market price and redemption
|
||||||
reference_asset_id: AccountId,
|
/// price.
|
||||||
|
collateral_definition_id: AccountId,
|
||||||
/// Initial redemption price, in the same units and precision as the oracle price.
|
/// Initial redemption price, in the same units and precision as the oracle price.
|
||||||
initial_redemption_price: u128,
|
initial_redemption_price: u128,
|
||||||
/// Proportional controller gain, scaled by [`CONTROLLER_GAIN_SCALE`].
|
/// Proportional controller gain, scaled by [`CONTROLLER_GAIN_SCALE`].
|
||||||
@ -146,7 +147,7 @@ pub struct Position {
|
|||||||
pub debt_amount: u128,
|
pub debt_amount: u128,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Global redemption feedback controller state for a stablecoin/feed pair.
|
/// Redemption feedback controller state for a stablecoin/feed pair.
|
||||||
///
|
///
|
||||||
/// `redemption_rate` is signed price drift per timestamp unit. Positive rates raise the
|
/// `redemption_rate` is signed price drift per timestamp unit. Positive rates raise the
|
||||||
/// redemption price; negative rates lower it. `accumulated_error` stores the integral term
|
/// redemption price; negative rates lower it. `accumulated_error` stores the integral term
|
||||||
@ -156,12 +157,10 @@ pub struct Position {
|
|||||||
pub struct RedemptionController {
|
pub struct RedemptionController {
|
||||||
/// Stablecoin token definition priced by the oracle feed.
|
/// Stablecoin token definition priced by the oracle feed.
|
||||||
pub stablecoin_definition_id: AccountId,
|
pub stablecoin_definition_id: AccountId,
|
||||||
/// Asset that denominates both oracle prices and redemption price.
|
/// Collateral token definition that denominates oracle prices and redemption price.
|
||||||
pub reference_asset_id: AccountId,
|
pub collateral_definition_id: AccountId,
|
||||||
/// Configured oracle price feed account.
|
/// Configured oracle price feed account.
|
||||||
pub price_feed_id: AccountId,
|
pub price_feed_id: AccountId,
|
||||||
/// Program expected to own `price_feed_id`.
|
|
||||||
pub oracle_program_id: ProgramId,
|
|
||||||
/// Current redemption price in oracle price units.
|
/// Current redemption price in oracle price units.
|
||||||
pub redemption_price: u128,
|
pub redemption_price: u128,
|
||||||
/// Current redemption rate in price units per timestamp unit.
|
/// Current redemption rate in price units per timestamp unit.
|
||||||
|
|||||||
@ -131,7 +131,7 @@ mod stablecoin {
|
|||||||
controller: AccountWithMetadata,
|
controller: AccountWithMetadata,
|
||||||
stablecoin_definition: AccountWithMetadata,
|
stablecoin_definition: AccountWithMetadata,
|
||||||
price_feed: AccountWithMetadata,
|
price_feed: AccountWithMetadata,
|
||||||
reference_asset_id: AccountId,
|
collateral_definition_id: AccountId,
|
||||||
initial_redemption_price: u128,
|
initial_redemption_price: u128,
|
||||||
proportional_gain: u128,
|
proportional_gain: u128,
|
||||||
integral_gain: u128,
|
integral_gain: u128,
|
||||||
@ -146,7 +146,7 @@ mod stablecoin {
|
|||||||
stablecoin_definition,
|
stablecoin_definition,
|
||||||
price_feed,
|
price_feed,
|
||||||
ctx.self_program_id,
|
ctx.self_program_id,
|
||||||
reference_asset_id,
|
collateral_definition_id,
|
||||||
initial_redemption_price,
|
initial_redemption_price,
|
||||||
proportional_gain,
|
proportional_gain,
|
||||||
integral_gain,
|
integral_gain,
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const CONTROLLER_GAIN_SCALE_I128: i128 = 1_000_000_000;
|
|||||||
/// - `controller` is already initialized.
|
/// - `controller` is already initialized.
|
||||||
/// - `controller.account_id` does not match the stablecoin/feed PDA.
|
/// - `controller.account_id` does not match the stablecoin/feed PDA.
|
||||||
/// - `stablecoin_definition` is uninitialized or not a fungible token definition.
|
/// - `stablecoin_definition` is uninitialized or not a fungible token definition.
|
||||||
/// - `price_feed` is uninitialized.
|
/// - `price_feed` is uninitialized, malformed, stale, or not the stablecoin/collateral feed.
|
||||||
/// - Initial price, gains, or clamp limits do not fit controller math bounds.
|
/// - Initial price, gains, or clamp limits do not fit controller math bounds.
|
||||||
#[expect(
|
#[expect(
|
||||||
clippy::too_many_arguments,
|
clippy::too_many_arguments,
|
||||||
@ -25,7 +25,7 @@ pub fn initialize_redemption_controller(
|
|||||||
stablecoin_definition: AccountWithMetadata,
|
stablecoin_definition: AccountWithMetadata,
|
||||||
price_feed: AccountWithMetadata,
|
price_feed: AccountWithMetadata,
|
||||||
stablecoin_program_id: ProgramId,
|
stablecoin_program_id: ProgramId,
|
||||||
reference_asset_id: AccountId,
|
collateral_definition_id: AccountId,
|
||||||
initial_redemption_price: u128,
|
initial_redemption_price: u128,
|
||||||
proportional_gain: u128,
|
proportional_gain: u128,
|
||||||
integral_gain: u128,
|
integral_gain: u128,
|
||||||
@ -69,6 +69,13 @@ pub fn initialize_redemption_controller(
|
|||||||
matches!(token_definition, TokenDefinition::Fungible { .. }),
|
matches!(token_definition, TokenDefinition::Fungible { .. }),
|
||||||
"Stablecoin definition must be fungible"
|
"Stablecoin definition must be fungible"
|
||||||
);
|
);
|
||||||
|
assert_live_price_feed(
|
||||||
|
&price_feed,
|
||||||
|
stablecoin_definition.account_id,
|
||||||
|
collateral_definition_id,
|
||||||
|
current_timestamp,
|
||||||
|
max_price_feed_age,
|
||||||
|
);
|
||||||
|
|
||||||
let controller_seed = verify_redemption_controller_and_get_seed(
|
let controller_seed = verify_redemption_controller_and_get_seed(
|
||||||
&controller,
|
&controller,
|
||||||
@ -78,9 +85,8 @@ pub fn initialize_redemption_controller(
|
|||||||
);
|
);
|
||||||
let controller_state = RedemptionController {
|
let controller_state = RedemptionController {
|
||||||
stablecoin_definition_id: stablecoin_definition.account_id,
|
stablecoin_definition_id: stablecoin_definition.account_id,
|
||||||
reference_asset_id,
|
collateral_definition_id,
|
||||||
price_feed_id: price_feed.account_id,
|
price_feed_id: price_feed.account_id,
|
||||||
oracle_program_id: price_feed.account.program_owner,
|
|
||||||
redemption_price: initial_redemption_price,
|
redemption_price: initial_redemption_price,
|
||||||
redemption_rate: 0,
|
redemption_rate: 0,
|
||||||
accumulated_error: 0,
|
accumulated_error: 0,
|
||||||
@ -165,16 +171,14 @@ fn live_market_price(
|
|||||||
price_feed: &AccountWithMetadata,
|
price_feed: &AccountWithMetadata,
|
||||||
current_timestamp: u64,
|
current_timestamp: u64,
|
||||||
) -> Option<u128> {
|
) -> Option<u128> {
|
||||||
if price_feed.account_id != controller.price_feed_id
|
if price_feed.account_id != controller.price_feed_id || price_feed.account == Account::default()
|
||||||
|| price_feed.account == Account::default()
|
|
||||||
|| price_feed.account.program_owner != controller.oracle_program_id
|
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let price_account = OraclePriceAccount::try_from(&price_feed.account.data).ok()?;
|
let price_account = OraclePriceAccount::try_from(&price_feed.account.data).ok()?;
|
||||||
if price_account.base_asset != controller.stablecoin_definition_id
|
if price_account.base_asset != controller.stablecoin_definition_id
|
||||||
|| price_account.quote_asset != controller.reference_asset_id
|
|| price_account.quote_asset != controller.collateral_definition_id
|
||||||
|| price_account.price == 0
|
|| price_account.price == 0
|
||||||
|| price_account.price > i128_max_as_u128()
|
|| price_account.price > i128_max_as_u128()
|
||||||
|| price_account.timestamp > current_timestamp
|
|| price_account.timestamp > current_timestamp
|
||||||
@ -190,6 +194,38 @@ fn live_market_price(
|
|||||||
Some(price_account.price)
|
Some(price_account.price)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_live_price_feed(
|
||||||
|
price_feed: &AccountWithMetadata,
|
||||||
|
stablecoin_definition_id: AccountId,
|
||||||
|
collateral_definition_id: AccountId,
|
||||||
|
current_timestamp: u64,
|
||||||
|
max_price_feed_age: u64,
|
||||||
|
) {
|
||||||
|
let price_account = OraclePriceAccount::try_from(&price_feed.account.data)
|
||||||
|
.expect("Price feed account must hold a valid OraclePriceAccount");
|
||||||
|
assert_eq!(
|
||||||
|
price_account.base_asset, stablecoin_definition_id,
|
||||||
|
"Price feed base asset must be the stablecoin definition"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
price_account.quote_asset, collateral_definition_id,
|
||||||
|
"Price feed quote asset must be the collateral definition"
|
||||||
|
);
|
||||||
|
assert_ne!(price_account.price, 0, "Price feed price must be nonzero");
|
||||||
|
assert_price_value_fits_i128(price_account.price, "Price feed price");
|
||||||
|
assert!(
|
||||||
|
price_account.timestamp <= current_timestamp,
|
||||||
|
"Price feed timestamp cannot be in the future"
|
||||||
|
);
|
||||||
|
let age = current_timestamp
|
||||||
|
.checked_sub(price_account.timestamp)
|
||||||
|
.expect("Price feed timestamp was checked to be current or older");
|
||||||
|
assert!(
|
||||||
|
age <= max_price_feed_age,
|
||||||
|
"Price feed age exceeds maximum allowed age"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn compute_next_controller_state(
|
fn compute_next_controller_state(
|
||||||
controller: &RedemptionController,
|
controller: &RedemptionController,
|
||||||
market_price: u128,
|
market_price: u128,
|
||||||
@ -212,16 +248,15 @@ fn compute_next_controller_state(
|
|||||||
);
|
);
|
||||||
let proportional_term = scaled_term(error, controller.proportional_gain);
|
let proportional_term = scaled_term(error, controller.proportional_gain);
|
||||||
let integral_term = scaled_term(accumulated_error, controller.integral_gain);
|
let integral_term = scaled_term(accumulated_error, controller.integral_gain);
|
||||||
let redemption_rate = clamp_signed(
|
let rate_adjustment = proportional_term
|
||||||
proportional_term.saturating_add(integral_term),
|
.saturating_add(integral_term)
|
||||||
controller.max_redemption_rate,
|
.saturating_neg();
|
||||||
);
|
let redemption_rate = clamp_signed(rate_adjustment, controller.max_redemption_rate);
|
||||||
|
|
||||||
RedemptionController {
|
RedemptionController {
|
||||||
stablecoin_definition_id: controller.stablecoin_definition_id,
|
stablecoin_definition_id: controller.stablecoin_definition_id,
|
||||||
reference_asset_id: controller.reference_asset_id,
|
collateral_definition_id: controller.collateral_definition_id,
|
||||||
price_feed_id: controller.price_feed_id,
|
price_feed_id: controller.price_feed_id,
|
||||||
oracle_program_id: controller.oracle_program_id,
|
|
||||||
redemption_price,
|
redemption_price,
|
||||||
redemption_rate,
|
redemption_rate,
|
||||||
accumulated_error,
|
accumulated_error,
|
||||||
@ -302,7 +337,7 @@ mod tests {
|
|||||||
AccountId::new([1; 32])
|
AccountId::new([1; 32])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reference_asset_id() -> AccountId {
|
fn collateral_definition_id() -> AccountId {
|
||||||
AccountId::new([2; 32])
|
AccountId::new([2; 32])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,13 +376,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn price_feed_account(price: u128, timestamp: u64) -> AccountWithMetadata {
|
fn price_feed_account(price: u128, timestamp: u64) -> AccountWithMetadata {
|
||||||
|
price_feed_account_with_owner(price, timestamp, ORACLE_PROGRAM_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn price_feed_account_with_owner(
|
||||||
|
price: u128,
|
||||||
|
timestamp: u64,
|
||||||
|
program_owner: ProgramId,
|
||||||
|
) -> AccountWithMetadata {
|
||||||
AccountWithMetadata {
|
AccountWithMetadata {
|
||||||
account: Account {
|
account: Account {
|
||||||
program_owner: ORACLE_PROGRAM_ID,
|
program_owner,
|
||||||
balance: 0,
|
balance: 0,
|
||||||
data: Data::from(&OraclePriceAccount {
|
data: Data::from(&OraclePriceAccount {
|
||||||
base_asset: stablecoin_definition_id(),
|
base_asset: stablecoin_definition_id(),
|
||||||
quote_asset: reference_asset_id(),
|
quote_asset: collateral_definition_id(),
|
||||||
price,
|
price,
|
||||||
timestamp,
|
timestamp,
|
||||||
source_id: "twap".to_owned(),
|
source_id: "twap".to_owned(),
|
||||||
@ -384,9 +427,8 @@ mod tests {
|
|||||||
fn controller_state() -> RedemptionController {
|
fn controller_state() -> RedemptionController {
|
||||||
RedemptionController {
|
RedemptionController {
|
||||||
stablecoin_definition_id: stablecoin_definition_id(),
|
stablecoin_definition_id: stablecoin_definition_id(),
|
||||||
reference_asset_id: reference_asset_id(),
|
collateral_definition_id: collateral_definition_id(),
|
||||||
price_feed_id: price_feed_id(),
|
price_feed_id: price_feed_id(),
|
||||||
oracle_program_id: ORACLE_PROGRAM_ID,
|
|
||||||
redemption_price: 1_000,
|
redemption_price: 1_000,
|
||||||
redemption_rate: 0,
|
redemption_rate: 0,
|
||||||
accumulated_error: 0,
|
accumulated_error: 0,
|
||||||
@ -406,7 +448,7 @@ mod tests {
|
|||||||
stablecoin_definition_account(),
|
stablecoin_definition_account(),
|
||||||
price_feed_account(1_000, 100),
|
price_feed_account(1_000, 100),
|
||||||
STABLECOIN_PROGRAM_ID,
|
STABLECOIN_PROGRAM_ID,
|
||||||
reference_asset_id(),
|
collateral_definition_id(),
|
||||||
1_000,
|
1_000,
|
||||||
CONTROLLER_GAIN_SCALE,
|
CONTROLLER_GAIN_SCALE,
|
||||||
0,
|
0,
|
||||||
@ -427,9 +469,11 @@ mod tests {
|
|||||||
controller.stablecoin_definition_id,
|
controller.stablecoin_definition_id,
|
||||||
stablecoin_definition_id()
|
stablecoin_definition_id()
|
||||||
);
|
);
|
||||||
assert_eq!(controller.reference_asset_id, reference_asset_id());
|
assert_eq!(
|
||||||
|
controller.collateral_definition_id,
|
||||||
|
collateral_definition_id()
|
||||||
|
);
|
||||||
assert_eq!(controller.price_feed_id, price_feed_id());
|
assert_eq!(controller.price_feed_id, price_feed_id());
|
||||||
assert_eq!(controller.oracle_program_id, ORACLE_PROGRAM_ID);
|
|
||||||
assert_eq!(controller.redemption_price, 1_000);
|
assert_eq!(controller.redemption_price, 1_000);
|
||||||
assert_eq!(controller.redemption_rate, 0);
|
assert_eq!(controller.redemption_rate, 0);
|
||||||
assert_eq!(controller.last_update_timestamp, 100);
|
assert_eq!(controller.last_update_timestamp, 100);
|
||||||
@ -447,10 +491,24 @@ mod tests {
|
|||||||
assert_eq!(post_states.len(), 2);
|
assert_eq!(post_states.len(), 2);
|
||||||
let controller =
|
let controller =
|
||||||
RedemptionController::try_from(&post_states[0].account().data).expect("valid state");
|
RedemptionController::try_from(&post_states[0].account().data).expect("valid state");
|
||||||
assert_eq!(controller.redemption_rate, 100);
|
assert_eq!(controller.redemption_rate, -100);
|
||||||
assert_eq!(controller.last_update_timestamp, 100);
|
assert_eq!(controller.last_update_timestamp, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn update_redemption_controller_accepts_matching_feed_from_any_program_owner() {
|
||||||
|
let post_states = update_redemption_controller(
|
||||||
|
controller_account(&controller_state()),
|
||||||
|
price_feed_account_with_owner(900, 100, [7u32; 8]),
|
||||||
|
STABLECOIN_PROGRAM_ID,
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
|
||||||
|
let controller =
|
||||||
|
RedemptionController::try_from(&post_states[0].account().data).expect("valid state");
|
||||||
|
assert_eq!(controller.redemption_rate, -100);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn update_redemption_controller_pauses_when_price_feed_is_stale() {
|
fn update_redemption_controller_pauses_when_price_feed_is_stale() {
|
||||||
let controller = controller_state();
|
let controller = controller_state();
|
||||||
@ -487,19 +545,19 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn controller_sets_positive_rate_when_market_price_is_below_redemption_price() {
|
fn controller_sets_negative_rate_when_market_price_is_below_redemption_price() {
|
||||||
let updated = compute_next_controller_state(&controller_state(), 900, 100);
|
let updated = compute_next_controller_state(&controller_state(), 900, 100);
|
||||||
|
|
||||||
assert_eq!(updated.redemption_rate, 100);
|
assert_eq!(updated.redemption_rate, -100);
|
||||||
assert_eq!(updated.accumulated_error, 0);
|
assert_eq!(updated.accumulated_error, 0);
|
||||||
assert_eq!(updated.redemption_price, 1_000);
|
assert_eq!(updated.redemption_price, 1_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn controller_sets_negative_rate_when_market_price_is_above_redemption_price() {
|
fn controller_sets_positive_rate_when_market_price_is_above_redemption_price() {
|
||||||
let updated = compute_next_controller_state(&controller_state(), 1_100, 100);
|
let updated = compute_next_controller_state(&controller_state(), 1_100, 100);
|
||||||
|
|
||||||
assert_eq!(updated.redemption_rate, -100);
|
assert_eq!(updated.redemption_rate, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -527,6 +585,6 @@ mod tests {
|
|||||||
let updated = compute_next_controller_state(&controller, 900, 101);
|
let updated = compute_next_controller_state(&controller, 900, 101);
|
||||||
|
|
||||||
assert_eq!(updated.accumulated_error, 100);
|
assert_eq!(updated.accumulated_error, 100);
|
||||||
assert_eq!(updated.redemption_rate, 80);
|
assert_eq!(updated.redemption_rate, -80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user