From a3b67aa328e2bbb3f56e724897b69f1c32972932 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Fri, 14 Jan 2022 13:31:30 -0500 Subject: [PATCH 01/59] capped funding: fix bug when total fee lb is above total_fee_minus_distributions for capped funding --- programs/clearing_house/src/math/funding.rs | 27 ++++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/programs/clearing_house/src/math/funding.rs b/programs/clearing_house/src/math/funding.rs index dd542dce..895ffad8 100644 --- a/programs/clearing_house/src/math/funding.rs +++ b/programs/clearing_house/src/math/funding.rs @@ -44,18 +44,21 @@ pub fn calculate_funding_rate_long_short( .total_fee_minus_distributions .checked_sub(capped_funding_pnl.unsigned_abs()) .ok_or_else(math_error!())?; + + // clearing house is paying part of funding imbalance + if capped_funding_pnl != 0 { + let total_fee_minus_distributions_lower_bound = market + .amm + .total_fee + .checked_mul(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR) + .ok_or_else(math_error!())? + .checked_div(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR) + .ok_or_else(math_error!())?; - let total_fee_minus_distributions_lower_bound = market - .amm - .total_fee - .checked_mul(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR) - .ok_or_else(math_error!())? - .checked_div(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR) - .ok_or_else(math_error!())?; - - // makes sure the clearing house doesn't pay more than the share of fees allocated to `distributions` - if new_total_fee_minus_distributions < total_fee_minus_distributions_lower_bound { - return Err(ErrorCode::InvalidFundingProfitability.into()); + // makes sure the clearing house doesn't pay more than the share of fees allocated to `distributions` + if new_total_fee_minus_distributions < total_fee_minus_distributions_lower_bound { + return Err(ErrorCode::InvalidFundingProfitability.into()); + } } market.amm.total_fee_minus_distributions = new_total_fee_minus_distributions; @@ -77,7 +80,7 @@ pub fn calculate_funding_rate_long_short( fn calculate_capped_funding_rate( market: &Market, - uncapped_funding_pnl: i128, + uncapped_funding_pnl: i128, // if negative, users would net recieve from clearinghouse funding_rate: i128, ) -> ClearingHouseResult<(i128, i128)> { // The funding_rate_pnl_limit is the amount of fees the clearing house can use before it hits it's lower bound From ddc21537b39d83792d3e179240585caffd273393 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Fri, 14 Jan 2022 14:37:09 -0500 Subject: [PATCH 02/59] repeg: total_fee_lb helper function --- .../clearing_house/src/controller/repeg.rs | 13 +----------- programs/clearing_house/src/math/funding.rs | 20 ++++--------------- programs/clearing_house/src/math/repeg.rs | 18 +++++++++++++++++ 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index 11a29148..c2e1c3c4 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -1,10 +1,6 @@ use crate::error::*; use crate::math::{amm, repeg}; -use crate::math::constants::{ - SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, - SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, -}; use crate::math_error; use crate::state::market::Market; @@ -103,14 +99,7 @@ pub fn repeg( // Only a portion of the protocol fees are allocated to repegging // This checks that the total_fee_minus_distributions does not decrease too much after repeg - if market.amm.total_fee_minus_distributions - < market - .amm - .total_fee - .checked_mul(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR) - .ok_or_else(math_error!())? - .checked_div(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR) - .ok_or_else(math_error!())? + if market.amm.total_fee_minus_distributions < repeg::total_fee_lower_bound(&market)? { return Err(ErrorCode::InvalidRepegProfitability.into()); } diff --git a/programs/clearing_house/src/math/funding.rs b/programs/clearing_house/src/math/funding.rs index 895ffad8..adae2153 100644 --- a/programs/clearing_house/src/math/funding.rs +++ b/programs/clearing_house/src/math/funding.rs @@ -3,9 +3,9 @@ use crate::math::bn; use crate::math::casting::cast_to_i128; use crate::math::constants::{ AMM_TO_QUOTE_PRECISION_RATIO, FUNDING_PAYMENT_PRECISION, MARK_PRICE_PRECISION, - QUOTE_TO_BASE_AMT_FUNDING_PRECISION, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, - SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, + QUOTE_TO_BASE_AMT_FUNDING_PRECISION, }; +use crate::math::repeg::total_fee_lower_bound; use crate::math_error; use crate::state::market::Market; use crate::state::user::MarketPosition; @@ -47,13 +47,7 @@ pub fn calculate_funding_rate_long_short( // clearing house is paying part of funding imbalance if capped_funding_pnl != 0 { - let total_fee_minus_distributions_lower_bound = market - .amm - .total_fee - .checked_mul(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR) - .ok_or_else(math_error!())? - .checked_div(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR) - .ok_or_else(math_error!())?; + let total_fee_minus_distributions_lower_bound = total_fee_lower_bound(&market)?; // makes sure the clearing house doesn't pay more than the share of fees allocated to `distributions` if new_total_fee_minus_distributions < total_fee_minus_distributions_lower_bound { @@ -84,13 +78,7 @@ fn calculate_capped_funding_rate( funding_rate: i128, ) -> ClearingHouseResult<(i128, i128)> { // The funding_rate_pnl_limit is the amount of fees the clearing house can use before it hits it's lower bound - let total_fee_minus_distributions_lower_bound = market - .amm - .total_fee - .checked_mul(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR) - .ok_or_else(math_error!())? - .checked_div(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR) - .ok_or_else(math_error!())?; + let total_fee_minus_distributions_lower_bound = total_fee_lower_bound(&market)?; let funding_rate_pnl_limit = if market.amm.total_fee_minus_distributions > total_fee_minus_distributions_lower_bound { -cast_to_i128( diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index f57c9f9b..01c37c83 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -1,6 +1,13 @@ use crate::error::*; +use crate::math_error; use crate::math::position::_calculate_base_asset_value_and_pnl; use crate::state::market::Market; +use crate::math::constants::{ + SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, + SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, +}; +use solana_program::msg; + pub fn adjust_peg_cost(market: &mut Market, new_peg: u128) -> ClearingHouseResult { // Find the net market value before adjusting peg @@ -17,3 +24,14 @@ pub fn adjust_peg_cost(market: &mut Market, new_peg: u128) -> ClearingHouseResul Ok(cost) } + +pub fn total_fee_lower_bound(market: & Market) -> ClearingHouseResult { + let total_fee_lb = market.amm + .total_fee + .checked_mul(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR) + .ok_or_else(math_error!())? + .checked_div(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR) + .ok_or_else(math_error!())?; + + Ok(total_fee_lb) +} From f38424fef8002981f49937d20f81e010f7764ca9 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Fri, 14 Jan 2022 15:35:47 -0500 Subject: [PATCH 03/59] repeg: add formulaic/non-error throwing controller function --- .../clearing_house/src/controller/repeg.rs | 140 ++++++++------- programs/clearing_house/src/error.rs | 2 + programs/clearing_house/src/math/funding.rs | 2 +- programs/clearing_house/src/math/repeg.rs | 161 ++++++++++++++++-- sdk/src/idl/clearing_house.json | 51 +++--- 5 files changed, 255 insertions(+), 101 deletions(-) diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index c2e1c3c4..62c4c425 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -1,12 +1,11 @@ use crate::error::*; -use crate::math::{amm, repeg}; +use crate::math::repeg; use crate::math_error; use crate::state::market::Market; use crate::state::state::OracleGuardRails; -use crate::math::casting::cast_to_u128; use anchor_lang::prelude::AccountInfo; use solana_program::msg; @@ -21,71 +20,35 @@ pub fn repeg( return Err(ErrorCode::InvalidRepegRedundant.into()); } - let terminal_price_before = amm::calculate_terminal_price(market)?; - let adjustment_cost = repeg::adjust_peg_cost(market, new_peg_candidate)?; - let (oracle_price, _oracle_twap, oracle_conf, _oracle_twac, _oracle_delay) = - market.amm.get_oracle_price(price_oracle, clock_slot)?; - - let oracle_is_valid = amm::is_oracle_valid( - &market.amm, + let ( + oracle_is_valid, + direction_valid, + profitability_valid, + price_impact_valid, + oracle_terminal_divergence, + ) = repeg::calculate_repeg_validity( + market, price_oracle, + new_peg_candidate, clock_slot, - &oracle_guard_rails.validity, + oracle_guard_rails, )?; - // if oracle is valid: check on size/direction of repeg - if oracle_is_valid { - let terminal_price_after = amm::calculate_terminal_price(market)?; - - let mark_price_after = amm::calculate_price( - market.amm.quote_asset_reserve, - market.amm.base_asset_reserve, - market.amm.peg_multiplier, - )?; - - let oracle_conf_band_top = cast_to_u128(oracle_price)? - .checked_add(oracle_conf) - .ok_or_else(math_error!())?; - - let oracle_conf_band_bottom = cast_to_u128(oracle_price)? - .checked_sub(oracle_conf) - .ok_or_else(math_error!())?; - - if cast_to_u128(oracle_price)? > terminal_price_after { - // only allow terminal up when oracle is higher - if terminal_price_after < terminal_price_before { - return Err(ErrorCode::InvalidRepegDirection.into()); - } - - // only push terminal up to top of oracle confidence band - if oracle_conf_band_bottom < terminal_price_after { - return Err(ErrorCode::InvalidRepegProfitability.into()); - } - - // only push mark up to top of oracle confidence band - if mark_price_after > oracle_conf_band_top { - return Err(ErrorCode::InvalidRepegProfitability.into()); - } - } - - if cast_to_u128(oracle_price)? < terminal_price_after { - // only allow terminal down when oracle is lower - if terminal_price_after > terminal_price_before { - return Err(ErrorCode::InvalidRepegDirection.into()); - } + // only push terminal in direction of oracle + if !direction_valid { + return Err(ErrorCode::InvalidRepegDirection.into()); + } - // only push terminal down to top of oracle confidence band - if oracle_conf_band_top > terminal_price_after { - return Err(ErrorCode::InvalidRepegProfitability.into()); - } + // only push terminal up to closer edge of oracle confidence band + if !profitability_valid { + return Err(ErrorCode::InvalidRepegProfitability.into()); + } - // only push mark down to bottom of oracle confidence band - if mark_price_after < oracle_conf_band_bottom { - return Err(ErrorCode::InvalidRepegProfitability.into()); - } - } + // only push mark up to further edge of oracle confidence band + if !price_impact_valid { + return Err(ErrorCode::InvalidRepegPriceImpact.into()); } // Reduce pnl to quote asset precision and take the absolute value @@ -99,8 +62,7 @@ pub fn repeg( // Only a portion of the protocol fees are allocated to repegging // This checks that the total_fee_minus_distributions does not decrease too much after repeg - if market.amm.total_fee_minus_distributions < repeg::total_fee_lower_bound(&market)? - { + if market.amm.total_fee_minus_distributions < repeg::total_fee_lower_bound(&market)? { return Err(ErrorCode::InvalidRepegProfitability.into()); } } else { @@ -111,5 +73,61 @@ pub fn repeg( .ok_or_else(math_error!())?; } + market.amm.peg_multiplier = new_peg_candidate; + + Ok(adjustment_cost) +} + +pub fn formulaic_repeg( + market: &mut Market, + price_oracle: &AccountInfo, + new_peg_candidate: u128, + clock_slot: u64, + oracle_guard_rails: &OracleGuardRails, +) -> ClearingHouseResult { + if new_peg_candidate == market.amm.peg_multiplier { + return Err(ErrorCode::InvalidRepegRedundant.into()); + } + + let adjustment_cost = repeg::adjust_peg_cost(market, new_peg_candidate)?; + + let ( + oracle_is_valid, + direction_valid, + profitability_valid, + price_impact_valid, + oracle_terminal_divergence, + ) = repeg::calculate_repeg_validity( + market, + price_oracle, + new_peg_candidate, + clock_slot, + oracle_guard_rails, + )?; + + if oracle_is_valid && direction_valid && profitability_valid && price_impact_valid { + if adjustment_cost > 0 { + let new_total_fee_minus_distributions = market + .amm + .total_fee_minus_distributions + .checked_sub(adjustment_cost.unsigned_abs()) + .or(Some(0)) + .ok_or_else(math_error!())?; + + if new_total_fee_minus_distributions >= repeg::total_fee_lower_bound(&market)? { + market.amm.total_fee_minus_distributions = new_total_fee_minus_distributions; + market.amm.peg_multiplier = new_peg_candidate; + } + } else { + market.amm.total_fee_minus_distributions = market + .amm + .total_fee_minus_distributions + .checked_add(adjustment_cost.unsigned_abs()) + .ok_or_else(math_error!())?; + + market.amm.peg_multiplier = new_peg_candidate; + } + } + Ok(adjustment_cost) } diff --git a/programs/clearing_house/src/error.rs b/programs/clearing_house/src/error.rs index a9df1156..ed658b3a 100644 --- a/programs/clearing_house/src/error.rs +++ b/programs/clearing_house/src/error.rs @@ -34,6 +34,8 @@ pub enum ErrorCode { InvalidRepegDirection, #[msg("AMM repeg out of bounds pnl")] InvalidRepegProfitability, + #[msg("AMM repeg mark price impact vs oracle too large")] + InvalidRepegPriceImpact, #[msg("Slippage Outside Limit Price")] SlippageOutsideLimit, #[msg("Trade Size Too Small")] diff --git a/programs/clearing_house/src/math/funding.rs b/programs/clearing_house/src/math/funding.rs index adae2153..e863aa90 100644 --- a/programs/clearing_house/src/math/funding.rs +++ b/programs/clearing_house/src/math/funding.rs @@ -44,7 +44,7 @@ pub fn calculate_funding_rate_long_short( .total_fee_minus_distributions .checked_sub(capped_funding_pnl.unsigned_abs()) .ok_or_else(math_error!())?; - + // clearing house is paying part of funding imbalance if capped_funding_pnl != 0 { let total_fee_minus_distributions_lower_bound = total_fee_lower_bound(&market)?; diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 01c37c83..15a447a0 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -1,37 +1,166 @@ use crate::error::*; -use crate::math_error; -use crate::math::position::_calculate_base_asset_value_and_pnl; -use crate::state::market::Market; +use crate::math::amm; +use crate::math::casting::cast_to_u128; use crate::math::constants::{ SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, }; +use crate::math::position::_calculate_base_asset_value_and_pnl; +use crate::math_error; +use crate::state::market::Market; +use crate::state::state::OracleGuardRails; +use anchor_lang::prelude::AccountInfo; use solana_program::msg; +pub fn calculate_repeg_validity( + market: &mut Market, + price_oracle: &AccountInfo, + new_peg_candidate: u128, + clock_slot: u64, + oracle_guard_rails: &OracleGuardRails, +) -> ClearingHouseResult<(bool, bool, bool, bool, u128)> { + let terminal_price_before = amm::calculate_terminal_price(market)?; + + let (oracle_price, _oracle_twap, oracle_conf, _oracle_twac, _oracle_delay) = + market.amm.get_oracle_price(price_oracle, clock_slot)?; + + let oracle_is_valid = amm::is_oracle_valid( + &market.amm, + price_oracle, + clock_slot, + &oracle_guard_rails.validity, + )?; + + let oracle_price_u128 = cast_to_u128(oracle_price)?; + + let mut direction_valid = true; + let mut price_impact_valid = true; + let mut profitability_valid = true; + + let oracle_terminal_divergence_pct: u128; // for formulaic repeg + + // if oracle is valid: check on size/direction of repeg + if oracle_is_valid { + let terminal_price_after = amm::calculate_terminal_price(market)?; + + let mark_price_after = amm::calculate_price( + market.amm.quote_asset_reserve, + market.amm.base_asset_reserve, + market.amm.peg_multiplier, + )?; + + let oracle_conf_band_top = oracle_price_u128 + .checked_add(oracle_conf) + .ok_or_else(math_error!())?; + + let oracle_conf_band_bottom = oracle_price_u128 + .checked_sub(oracle_conf) + .ok_or_else(math_error!())?; + + if oracle_price_u128 > terminal_price_after { + // only allow terminal up when oracle is higher + if terminal_price_after < terminal_price_before { + direction_valid = false; + return Err(ErrorCode::InvalidRepegDirection.into()); + } + + // only push terminal up to top of oracle confidence band + if oracle_conf_band_bottom < terminal_price_after { + profitability_valid = false; + return Err(ErrorCode::InvalidRepegProfitability.into()); + } + + // only push mark up to top of oracle confidence band + if mark_price_after > oracle_conf_band_top { + price_impact_valid = false; + return Err(ErrorCode::InvalidRepegPriceImpact.into()); + } + + let oracle_terminal_spread = oracle_price_u128 + .checked_sub(terminal_price_after) + .ok_or_else(math_error!())?; + + oracle_terminal_divergence_pct = oracle_terminal_spread + .checked_shl(10) + .ok_or_else(math_error!())? + .checked_div(oracle_price_u128) + .ok_or_else(math_error!())?; + } else if oracle_price_u128 < terminal_price_after { + // only allow terminal down when oracle is lower + if terminal_price_after > terminal_price_before { + direction_valid = false; + return Err(ErrorCode::InvalidRepegDirection.into()); + } + + // only push terminal down to top of oracle confidence band + if oracle_conf_band_top > terminal_price_after { + profitability_valid = false; + return Err(ErrorCode::InvalidRepegProfitability.into()); + } + + // only push mark down to bottom of oracle confidence band + if mark_price_after < oracle_conf_band_bottom { + price_impact_valid = false; + return Err(ErrorCode::InvalidRepegPriceImpact.into()); + } + + let oracle_terminal_spread = terminal_price_after + .checked_sub(oracle_price_u128) + .ok_or_else(math_error!())?; + + oracle_terminal_divergence_pct = oracle_terminal_spread + .checked_shl(10) + .ok_or_else(math_error!())? + .checked_div(oracle_price_u128) + .ok_or_else(math_error!())?; + } else { + oracle_terminal_divergence_pct = 0; + } + } else { + direction_valid = false; + price_impact_valid = false; + profitability_valid = false; + oracle_terminal_divergence_pct = 0; + } + + Ok(( + oracle_is_valid, + direction_valid, + profitability_valid, + price_impact_valid, + oracle_terminal_divergence_pct, + )) +} + +pub fn adjust_peg_cost(market: &mut Market, new_peg_candidate: u128) -> ClearingHouseResult { + let market_deep_copy = &mut market.clone(); -pub fn adjust_peg_cost(market: &mut Market, new_peg: u128) -> ClearingHouseResult { // Find the net market value before adjusting peg - let (current_net_market_value, _) = - _calculate_base_asset_value_and_pnl(market.base_asset_amount, 0, &market.amm)?; + let (current_net_market_value, _) = _calculate_base_asset_value_and_pnl( + market_deep_copy.base_asset_amount, + 0, + &market_deep_copy.amm, + )?; - market.amm.peg_multiplier = new_peg; + market_deep_copy.amm.peg_multiplier = new_peg_candidate; let (_new_net_market_value, cost) = _calculate_base_asset_value_and_pnl( - market.base_asset_amount, + market_deep_copy.base_asset_amount, current_net_market_value, - &market.amm, + &market_deep_copy.amm, )?; Ok(cost) } -pub fn total_fee_lower_bound(market: & Market) -> ClearingHouseResult { - let total_fee_lb = market.amm - .total_fee - .checked_mul(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR) - .ok_or_else(math_error!())? - .checked_div(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR) - .ok_or_else(math_error!())?; +pub fn total_fee_lower_bound(market: &Market) -> ClearingHouseResult { + let total_fee_lb = market + .amm + .total_fee + .checked_mul(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR) + .ok_or_else(math_error!())? + .checked_div(SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR) + .ok_or_else(math_error!())?; Ok(total_fee_lb) } diff --git a/sdk/src/idl/clearing_house.json b/sdk/src/idl/clearing_house.json index ba4cae15..6631816f 100644 --- a/sdk/src/idl/clearing_house.json +++ b/sdk/src/idl/clearing_house.json @@ -2701,121 +2701,126 @@ }, { "code": 6015, + "name": "InvalidRepegPriceImpact", + "msg": "AMM repeg mark price impact vs oracle too large" + }, + { + "code": 6016, "name": "SlippageOutsideLimit", "msg": "Slippage Outside Limit Price" }, { - "code": 6016, + "code": 6017, "name": "TradeSizeTooSmall", "msg": "Trade Size Too Small" }, { - "code": 6017, + "code": 6018, "name": "InvalidUpdateK", "msg": "Price change too large when updating K" }, { - "code": 6018, + "code": 6019, "name": "AdminWithdrawTooLarge", "msg": "Admin tried to withdraw amount larger than fees collected" }, { - "code": 6019, + "code": 6020, "name": "MathError", "msg": "Math Error" }, { - "code": 6020, + "code": 6021, "name": "BnConversionError", "msg": "Conversion to u128/u64 failed with an overflow or underflow" }, { - "code": 6021, + "code": 6022, "name": "ClockUnavailable", "msg": "Clock unavailable" }, { - "code": 6022, + "code": 6023, "name": "UnableToLoadOracle", "msg": "Unable To Load Oracles" }, { - "code": 6023, + "code": 6024, "name": "OracleMarkSpreadLimit", "msg": "Oracle/Mark Spread Too Large" }, { - "code": 6024, + "code": 6025, "name": "HistoryAlreadyInitialized", "msg": "Clearing House history already initialized" }, { - "code": 6025, + "code": 6026, "name": "ExchangePaused", "msg": "Exchange is paused" }, { - "code": 6026, + "code": 6027, "name": "InvalidWhitelistToken", "msg": "Invalid whitelist token" }, { - "code": 6027, + "code": 6028, "name": "WhitelistTokenNotFound", "msg": "Whitelist token not found" }, { - "code": 6028, + "code": 6029, "name": "InvalidDiscountToken", "msg": "Invalid discount token" }, { - "code": 6029, + "code": 6030, "name": "DiscountTokenNotFound", "msg": "Discount token not found" }, { - "code": 6030, + "code": 6031, "name": "InvalidReferrer", "msg": "Invalid referrer" }, { - "code": 6031, + "code": 6032, "name": "ReferrerNotFound", "msg": "Referrer not found" }, { - "code": 6032, + "code": 6033, "name": "InvalidOracle", "msg": "InvalidOracle" }, { - "code": 6033, + "code": 6034, "name": "OracleNotFound", "msg": "OracleNotFound" }, { - "code": 6034, + "code": 6035, "name": "LiquidationsBlockedByOracle", "msg": "Liquidations Blocked By Oracle" }, { - "code": 6035, + "code": 6036, "name": "UserMaxDeposit", "msg": "Can not deposit more than max deposit" }, { - "code": 6036, + "code": 6037, "name": "CantDeleteUserWithCollateral", "msg": "Can not delete user that still has collateral" }, { - "code": 6037, + "code": 6038, "name": "InvalidFundingProfitability", "msg": "AMM funding out of bounds pnl" }, { - "code": 6038, + "code": 6039, "name": "CastingFailure", "msg": "Casting Failure" } From fb8db122186a35c2d459320fcc8e27ade05b34e2 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Sun, 16 Jan 2022 11:02:58 -0500 Subject: [PATCH 04/59] formulaic repeg init --- .../clearing_house/src/controller/repeg.rs | 46 +- programs/clearing_house/src/math/repeg.rs | 135 +- sdk/package-lock.json | 1300 +---------------- sdk/src/math/amm.ts | 59 + 4 files changed, 185 insertions(+), 1355 deletions(-) diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index 62c4c425..a261497c 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -1,9 +1,10 @@ use crate::error::*; use crate::math::repeg; +use crate::math::casting::{cast_to_i128, cast_to_u128}; use crate::math_error; use crate::state::market::Market; - +use crate::math::amm; use crate::state::state::OracleGuardRails; use anchor_lang::prelude::AccountInfo; @@ -19,8 +20,9 @@ pub fn repeg( if new_peg_candidate == market.amm.peg_multiplier { return Err(ErrorCode::InvalidRepegRedundant.into()); } + let terminal_price_before = amm::calculate_terminal_price(market)?; - let adjustment_cost = repeg::adjust_peg_cost(market, new_peg_candidate)?; + let (repegged_market, adjustment_cost) = repeg::adjust_peg_cost(market, new_peg_candidate)?; let ( oracle_is_valid, @@ -28,10 +30,10 @@ pub fn repeg( profitability_valid, price_impact_valid, oracle_terminal_divergence, - ) = repeg::calculate_repeg_validity( - market, + ) = repeg::calculate_repeg_validity_full( + repegged_market, price_oracle, - new_peg_candidate, + terminal_price_before, clock_slot, oracle_guard_rails, )?; @@ -51,6 +53,8 @@ pub fn repeg( return Err(ErrorCode::InvalidRepegPriceImpact.into()); } + // modify market's total fee change and peg change + // Reduce pnl to quote asset precision and take the absolute value if adjustment_cost > 0 { market.amm.total_fee_minus_distributions = market @@ -80,29 +84,35 @@ pub fn repeg( pub fn formulaic_repeg( market: &mut Market, - price_oracle: &AccountInfo, - new_peg_candidate: u128, - clock_slot: u64, - oracle_guard_rails: &OracleGuardRails, + oracle_price: i128, + oracle_conf: u128, + oracle_is_valid: bool, ) -> ClearingHouseResult { - if new_peg_candidate == market.amm.peg_multiplier { - return Err(ErrorCode::InvalidRepegRedundant.into()); - } - let adjustment_cost = repeg::adjust_peg_cost(market, new_peg_candidate)?; + let terminal_price_before = amm::calculate_terminal_price(market)?; + let oracle_terminal_spread_before = oracle_price + .checked_sub(cast_to_i128(terminal_price_before)?) + .ok_or_else(math_error!())?; + let oracle_terminal_divergence_pct_before = oracle_terminal_spread_before + .checked_shl(10) + .ok_or_else(math_error!())? + .checked_div(oracle_price) + .ok_or_else(math_error!())?; + + let (new_peg_candidate, adjustment_cost) = repeg::calculate_optimal_peg_and_cost(market, oracle_terminal_divergence_pct_before)?; let ( oracle_is_valid, direction_valid, profitability_valid, price_impact_valid, - oracle_terminal_divergence, + oracle_terminal_divergence_pct_after, ) = repeg::calculate_repeg_validity( market, - price_oracle, - new_peg_candidate, - clock_slot, - oracle_guard_rails, + oracle_price, + oracle_conf, + oracle_is_valid, + terminal_price_before, )?; if oracle_is_valid && direction_valid && profitability_valid && price_impact_valid { diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 15a447a0..7e84d78f 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -1,6 +1,6 @@ use crate::error::*; use crate::math::amm; -use crate::math::casting::cast_to_u128; +use crate::math::casting::{cast_to_i128, cast_to_u128}; use crate::math::constants::{ SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, @@ -12,14 +12,13 @@ use crate::state::state::OracleGuardRails; use anchor_lang::prelude::AccountInfo; use solana_program::msg; -pub fn calculate_repeg_validity( +pub fn calculate_repeg_validity_full( market: &mut Market, price_oracle: &AccountInfo, - new_peg_candidate: u128, + terminal_price_before: u128, clock_slot: u64, oracle_guard_rails: &OracleGuardRails, -) -> ClearingHouseResult<(bool, bool, bool, bool, u128)> { - let terminal_price_before = amm::calculate_terminal_price(market)?; +) -> ClearingHouseResult<(bool, bool, bool, bool, i128)> { let (oracle_price, _oracle_twap, oracle_conf, _oracle_twac, _oracle_delay) = market.amm.get_oracle_price(price_oracle, clock_slot)?; @@ -31,18 +30,55 @@ pub fn calculate_repeg_validity( &oracle_guard_rails.validity, )?; + let ( + oracle_is_valid, + direction_valid, + profitability_valid, + price_impact_valid, + oracle_terminal_divergence_pct_after, + ) = calculate_repeg_validity( + market, + oracle_price, + oracle_conf, + oracle_is_valid, + terminal_price_before, + )?; + + Ok(( + oracle_is_valid, + direction_valid, + profitability_valid, + price_impact_valid, + oracle_terminal_divergence_pct_after, + )) +} + +pub fn calculate_repeg_validity( + market: &mut Market, + oracle_price: i128, + oracle_conf: u128, + oracle_is_valid: bool, + terminal_price_before: u128, +) -> ClearingHouseResult<(bool, bool, bool, bool, i128)> { + let oracle_price_u128 = cast_to_u128(oracle_price)?; + let terminal_price_after = amm::calculate_terminal_price(market)?; + let oracle_terminal_spread_after = oracle_price + .checked_sub(cast_to_i128(terminal_price_after)?) + .ok_or_else(math_error!())?; + let oracle_terminal_divergence_pct_after = oracle_terminal_spread_after + .checked_shl(10) + .ok_or_else(math_error!())? + .checked_div(oracle_price) + .ok_or_else(math_error!())?; + let mut direction_valid = true; let mut price_impact_valid = true; let mut profitability_valid = true; - let oracle_terminal_divergence_pct: u128; // for formulaic repeg - // if oracle is valid: check on size/direction of repeg if oracle_is_valid { - let terminal_price_after = amm::calculate_terminal_price(market)?; - let mark_price_after = amm::calculate_price( market.amm.quote_asset_reserve, market.amm.base_asset_reserve, @@ -61,66 +97,38 @@ pub fn calculate_repeg_validity( // only allow terminal up when oracle is higher if terminal_price_after < terminal_price_before { direction_valid = false; - return Err(ErrorCode::InvalidRepegDirection.into()); } // only push terminal up to top of oracle confidence band if oracle_conf_band_bottom < terminal_price_after { profitability_valid = false; - return Err(ErrorCode::InvalidRepegProfitability.into()); } // only push mark up to top of oracle confidence band if mark_price_after > oracle_conf_band_top { price_impact_valid = false; - return Err(ErrorCode::InvalidRepegPriceImpact.into()); } - let oracle_terminal_spread = oracle_price_u128 - .checked_sub(terminal_price_after) - .ok_or_else(math_error!())?; - - oracle_terminal_divergence_pct = oracle_terminal_spread - .checked_shl(10) - .ok_or_else(math_error!())? - .checked_div(oracle_price_u128) - .ok_or_else(math_error!())?; } else if oracle_price_u128 < terminal_price_after { // only allow terminal down when oracle is lower if terminal_price_after > terminal_price_before { direction_valid = false; - return Err(ErrorCode::InvalidRepegDirection.into()); } // only push terminal down to top of oracle confidence band if oracle_conf_band_top > terminal_price_after { profitability_valid = false; - return Err(ErrorCode::InvalidRepegProfitability.into()); } // only push mark down to bottom of oracle confidence band if mark_price_after < oracle_conf_band_bottom { price_impact_valid = false; - return Err(ErrorCode::InvalidRepegPriceImpact.into()); } - - let oracle_terminal_spread = terminal_price_after - .checked_sub(oracle_price_u128) - .ok_or_else(math_error!())?; - - oracle_terminal_divergence_pct = oracle_terminal_spread - .checked_shl(10) - .ok_or_else(math_error!())? - .checked_div(oracle_price_u128) - .ok_or_else(math_error!())?; - } else { - oracle_terminal_divergence_pct = 0; } } else { direction_valid = false; price_impact_valid = false; profitability_valid = false; - oracle_terminal_divergence_pct = 0; } Ok(( @@ -128,12 +136,47 @@ pub fn calculate_repeg_validity( direction_valid, profitability_valid, price_impact_valid, - oracle_terminal_divergence_pct, + oracle_terminal_divergence_pct_after, )) } -pub fn adjust_peg_cost(market: &mut Market, new_peg_candidate: u128) -> ClearingHouseResult { - let market_deep_copy = &mut market.clone(); + +pub fn calculate_optimal_peg_and_cost( + market: &mut Market, + oracle_terminal_price_divergence: i128, +) -> ClearingHouseResult<(u128, i128)> { + + // does minimum valid repeg allowable iff satisfies the budget + + // budget is half of fee pool + let budget = calculate_fee_pool(market)?.checked_div(2).ok_or_else(math_error!())?; + + let mut optimal_peg: u128; + if oracle_terminal_price_divergence > 0 { + optimal_peg = market.amm.peg_multiplier.checked_add(1).ok_or_else(math_error!())?; + } else if oracle_terminal_price_divergence < 0 { + optimal_peg = market.amm.peg_multiplier.checked_sub(1).ok_or_else(math_error!())?; + } else{ + optimal_peg = market.amm.peg_multiplier; + } + + let (_, optimal_adjustment_cost) = adjust_peg_cost(market, optimal_peg)?; + + let candidate_peg: u128; + let candidate_cost: i128; + if optimal_adjustment_cost > 0 && optimal_adjustment_cost.unsigned_abs() > budget { + candidate_peg = market.amm.peg_multiplier; + candidate_cost = 0; + } else{ + candidate_peg = optimal_peg; + candidate_cost = optimal_adjustment_cost; + } + + Ok((candidate_peg, candidate_cost)) +} + +pub fn adjust_peg_cost(market: &mut Market, new_peg_candidate: u128) -> ClearingHouseResult<(&mut Market, i128)> { + let market_deep_copy = market; // Find the net market value before adjusting peg let (current_net_market_value, _) = _calculate_base_asset_value_and_pnl( @@ -150,7 +193,17 @@ pub fn adjust_peg_cost(market: &mut Market, new_peg_candidate: u128) -> Clearing &market_deep_copy.amm, )?; - Ok(cost) + Ok((market_deep_copy, cost)) +} + +pub fn calculate_fee_pool(market: &Market) -> ClearingHouseResult { + let total_fee_minus_distributions_lower_bound = total_fee_lower_bound(&market)?; + + let fee_pool = market.amm.total_fee_minus_distributions + .checked_sub(total_fee_minus_distributions_lower_bound) + .ok_or_else(math_error!())?; + + Ok(fee_pool) } pub fn total_fee_lower_bound(market: &Market) -> ClearingHouseResult { diff --git a/sdk/package-lock.json b/sdk/package-lock.json index 5aaf3b9e..6eef8526 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,54 +1,9 @@ { "name": "@drift-labs/sdk", - "version": "0.1.12", + "version": "0.1.14", "lockfileVersion": 1, "requires": true, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", - "dev": true - }, - "@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.15.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } - } - }, "@babel/runtime": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", @@ -57,31 +12,6 @@ "regenerator-runtime": "^0.13.4" } }, - "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, "@ethersproject/bytes": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", @@ -105,53 +35,10 @@ "hash.js": "1.1.7" } }, - "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, "@project-serum/anchor": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.19.0.tgz", - "integrity": "sha512-cs0LBmJOrL9eJ8MRNqitnzbpCT5QEzVdJmiIjfNV5YaGn1K9vISR7DtISj3Bdl3KBdLqii4CTw1mpHdi8iXUCg==", + "version": "0.19.1-beta.1", + "resolved": "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.19.1-beta.1.tgz", + "integrity": "sha512-TzkgS5a4WUDJotmur9/pQhqeqa5W14h++ppF+oGZVoR0EkcLbW7SKpx/z80XhsHCIhWz9/1hBAkbKElXdKUUiw==", "requires": { "@project-serum/borsh": "^0.2.2", "@solana/web3.js": "^1.17.0", @@ -301,12 +188,6 @@ "@types/range-parser": "*" } }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, "@types/lodash": { "version": "4.14.176", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.176.tgz", @@ -335,89 +216,6 @@ "@types/node": "*" } }, - "@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/experimental-utils": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", - "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" - } - }, - "@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" - } - }, - "@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" - } - }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -427,66 +225,6 @@ "through": ">=2.2.7 <3" } }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, "assert": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", @@ -498,23 +236,11 @@ "util": "^0.12.0" } }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, "base-x": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", @@ -554,25 +280,6 @@ } } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -618,99 +325,21 @@ "get-intrinsic": "^1.0.2" } }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "circular-json": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==" }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, "cross-fetch": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", @@ -719,37 +348,11 @@ "node-fetch": "2.6.1" } }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, "crypto-hash": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz", "integrity": "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==" }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -763,24 +366,6 @@ "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==" }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, "dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -816,21 +401,6 @@ } } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "es-abstract": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", @@ -886,196 +456,6 @@ "es6-promise": "^4.0.3" } }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, - "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true - }, - "eslint-plugin-prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", - "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -1086,70 +466,6 @@ "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "find": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/find/-/find-0.3.0.tgz", @@ -1158,44 +474,16 @@ "traverse-chain": "~0.1.0" } }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", - "dev": true - }, "foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -1215,52 +503,6 @@ "get-intrinsic": "^1.1.1" } }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1274,12 +516,6 @@ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", @@ -1317,38 +553,6 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1403,18 +607,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", @@ -1423,15 +615,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, "is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -1446,12 +629,6 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, "is-number-object": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", @@ -1510,12 +687,6 @@ "call-bind": "^1.0.0" } }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, "isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", @@ -1560,34 +731,6 @@ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -1598,33 +741,11 @@ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, "lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -1633,31 +754,6 @@ "tslib": "^2.0.3" } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -1668,27 +764,6 @@ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -1743,144 +818,16 @@ "object-keys": "^1.1.1" } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, "pako": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz", "integrity": "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==" }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, "rpc-websockets": { "version": "7.4.16", "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.4.16.tgz", @@ -1902,15 +849,6 @@ } } }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1926,30 +864,6 @@ "node-gyp-build": "^4.2.0" } }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -1960,49 +874,6 @@ "object-inspect": "^1.9.0" } }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, "snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -2012,28 +883,11 @@ "tslib": "^2.0.3" } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, "strict-event-emitter-types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==" }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -2052,93 +906,21 @@ "define-properties": "^1.1.3" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, "superstruct": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz", "integrity": "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==" }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.5.tgz", - "integrity": "sha512-LFNeryOqiQHqCVKzhkymKwt6ozeRhlm8IL1mE8rNUurkir4heF6PzMyRgaTa4tlyPTGGgXuvVOF/OLWiH09Lqw==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, "text-encoding-utf-8": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, "toml": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", @@ -2154,43 +936,11 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, "tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, "unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -2202,15 +952,6 @@ "which-boxed-primitive": "^1.0.2" } }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, "utf-8-validate": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", @@ -2238,21 +979,6 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, "which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", @@ -2278,28 +1004,10 @@ "is-typed-array": "^1.1.7" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, "ws": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } } diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index b95d4669..a8dc25b2 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -2,6 +2,7 @@ import { BN } from '@project-serum/anchor'; import { AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO, MARK_PRICE_PRECISION, + AMM_RESERVE_PRECISION, PEG_PRECISION, ZERO, } from '../constants/numericConstants'; @@ -213,6 +214,64 @@ export function calculateRepegCost( return cost; } +/** + * Helper function calculating adjust pegMultiplier (repeg) cost + * + * @param market + * @param marketIndex + * @param newPeg + * @returns cost : Precision QUOTE_ASSET_PRECISION + */ + export function calculateReserveRebalanceCost( + market: Market, + marketIndex: BN, +): BN { + const netUserPosition = { + baseAssetAmount: market.baseAssetAmount, + lastCumulativeFundingRate: market.amm.cumulativeFundingRate, + marketIndex: new BN(marketIndex), + quoteAssetAmount: new BN(0), + }; + + const currentValue = calculateBaseAssetValue(market, netUserPosition); + netUserPosition.quoteAssetAmount = currentValue; + const prevMarketPrice = calculateMarkPrice(market); + const marketNewPeg = Object.assign({}, market); + marketNewPeg.amm = Object.assign({}, market.amm); + + // const marketNewPeg = JSON.parse(JSON.stringify(market)); + const newPeg = calculateTerminalPrice(market).mul(PEG_PRECISION).div(MARK_PRICE_PRECISION); + // const newPeg = prevMarketPrice.mul(PEG_PRECISION).div(MARK_PRICE_PRECISION); + + const newBaseReserve = market.amm.baseAssetReserve.add(market.baseAssetAmount); + const newQuoteReserve = market.amm.sqrtK.mul(market.amm.sqrtK).div(newBaseReserve); + console.log('current reserves on close, quote:', convertToNumber(newQuoteReserve, AMM_RESERVE_PRECISION), + 'base:', convertToNumber(newBaseReserve, AMM_RESERVE_PRECISION)); + + let newSqrtK; + if(newPeg.lt(market.amm.pegMultiplier)){ + newSqrtK = newBaseReserve; + } else{ + newSqrtK = newQuoteReserve; + } + + marketNewPeg.amm.baseAssetReserve = newSqrtK.sub(market.baseAssetAmount); // newSqrtK.sub(market.baseAssetAmount); + marketNewPeg.amm.quoteAssetReserve = newSqrtK.mul(newSqrtK).div(marketNewPeg.amm.baseAssetReserve); + marketNewPeg.amm.sqrtK = newSqrtK; + marketNewPeg.amm.pegMultiplier = newPeg; + + console.log( + 'Price moves from', + convertToNumber(prevMarketPrice), + 'to', + convertToNumber(calculateMarkPrice(marketNewPeg)) + ); + + const cost = calculatePositionPNL(marketNewPeg, netUserPosition); + + return cost; +} + /** * Helper function calculating terminal price of amm * From 78b9e454ecf2585b360581b9db0e72b8a2554a22 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Sun, 16 Jan 2022 14:25:44 -0500 Subject: [PATCH 05/59] formula repeg: cargo fmt --- .../clearing_house/src/controller/repeg.rs | 8 ++-- programs/clearing_house/src/math/repeg.rs | 46 +++++++++++-------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index a261497c..b0369bb6 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -1,10 +1,10 @@ use crate::error::*; -use crate::math::repeg; use crate::math::casting::{cast_to_i128, cast_to_u128}; +use crate::math::repeg; +use crate::math::amm; use crate::math_error; use crate::state::market::Market; -use crate::math::amm; use crate::state::state::OracleGuardRails; use anchor_lang::prelude::AccountInfo; @@ -88,7 +88,6 @@ pub fn formulaic_repeg( oracle_conf: u128, oracle_is_valid: bool, ) -> ClearingHouseResult { - let terminal_price_before = amm::calculate_terminal_price(market)?; let oracle_terminal_spread_before = oracle_price .checked_sub(cast_to_i128(terminal_price_before)?) @@ -99,7 +98,8 @@ pub fn formulaic_repeg( .checked_div(oracle_price) .ok_or_else(math_error!())?; - let (new_peg_candidate, adjustment_cost) = repeg::calculate_optimal_peg_and_cost(market, oracle_terminal_divergence_pct_before)?; + let (new_peg_candidate, adjustment_cost) = + repeg::calculate_optimal_peg_and_cost(market, oracle_terminal_divergence_pct_before)?; let ( oracle_is_valid, diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 7e84d78f..10ecaadb 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -19,7 +19,6 @@ pub fn calculate_repeg_validity_full( clock_slot: u64, oracle_guard_rails: &OracleGuardRails, ) -> ClearingHouseResult<(bool, bool, bool, bool, i128)> { - let (oracle_price, _oracle_twap, oracle_conf, _oracle_twac, _oracle_delay) = market.amm.get_oracle_price(price_oracle, clock_slot)?; @@ -60,13 +59,12 @@ pub fn calculate_repeg_validity( oracle_is_valid: bool, terminal_price_before: u128, ) -> ClearingHouseResult<(bool, bool, bool, bool, i128)> { - let oracle_price_u128 = cast_to_u128(oracle_price)?; let terminal_price_after = amm::calculate_terminal_price(market)?; let oracle_terminal_spread_after = oracle_price - .checked_sub(cast_to_i128(terminal_price_after)?) - .ok_or_else(math_error!())?; + .checked_sub(cast_to_i128(terminal_price_after)?) + .ok_or_else(math_error!())?; let oracle_terminal_divergence_pct_after = oracle_terminal_spread_after .checked_shl(10) .ok_or_else(math_error!())? @@ -108,7 +106,6 @@ pub fn calculate_repeg_validity( if mark_price_after > oracle_conf_band_top { price_impact_valid = false; } - } else if oracle_price_u128 < terminal_price_after { // only allow terminal down when oracle is lower if terminal_price_after > terminal_price_before { @@ -140,23 +137,31 @@ pub fn calculate_repeg_validity( )) } - pub fn calculate_optimal_peg_and_cost( - market: &mut Market, + market: &mut Market, oracle_terminal_price_divergence: i128, ) -> ClearingHouseResult<(u128, i128)> { - // does minimum valid repeg allowable iff satisfies the budget // budget is half of fee pool - let budget = calculate_fee_pool(market)?.checked_div(2).ok_or_else(math_error!())?; - + let budget = calculate_fee_pool(market)? + .checked_div(2) + .ok_or_else(math_error!())?; + let mut optimal_peg: u128; if oracle_terminal_price_divergence > 0 { - optimal_peg = market.amm.peg_multiplier.checked_add(1).ok_or_else(math_error!())?; + optimal_peg = market + .amm + .peg_multiplier + .checked_add(1) + .ok_or_else(math_error!())?; } else if oracle_terminal_price_divergence < 0 { - optimal_peg = market.amm.peg_multiplier.checked_sub(1).ok_or_else(math_error!())?; - } else{ + optimal_peg = market + .amm + .peg_multiplier + .checked_sub(1) + .ok_or_else(math_error!())?; + } else { optimal_peg = market.amm.peg_multiplier; } @@ -167,7 +172,7 @@ pub fn calculate_optimal_peg_and_cost( if optimal_adjustment_cost > 0 && optimal_adjustment_cost.unsigned_abs() > budget { candidate_peg = market.amm.peg_multiplier; candidate_cost = 0; - } else{ + } else { candidate_peg = optimal_peg; candidate_cost = optimal_adjustment_cost; } @@ -175,7 +180,10 @@ pub fn calculate_optimal_peg_and_cost( Ok((candidate_peg, candidate_cost)) } -pub fn adjust_peg_cost(market: &mut Market, new_peg_candidate: u128) -> ClearingHouseResult<(&mut Market, i128)> { +pub fn adjust_peg_cost( + market: &mut Market, + new_peg_candidate: u128, +) -> ClearingHouseResult<(&mut Market, i128)> { let market_deep_copy = market; // Find the net market value before adjusting peg @@ -199,9 +207,11 @@ pub fn adjust_peg_cost(market: &mut Market, new_peg_candidate: u128) -> Clearing pub fn calculate_fee_pool(market: &Market) -> ClearingHouseResult { let total_fee_minus_distributions_lower_bound = total_fee_lower_bound(&market)?; - let fee_pool = market.amm.total_fee_minus_distributions - .checked_sub(total_fee_minus_distributions_lower_bound) - .ok_or_else(math_error!())?; + let fee_pool = market + .amm + .total_fee_minus_distributions + .checked_sub(total_fee_minus_distributions_lower_bound) + .ok_or_else(math_error!())?; Ok(fee_pool) } From 7b94ff377422e09b9150d2621e204f397faf88fa Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Tue, 18 Jan 2022 12:27:31 -0500 Subject: [PATCH 06/59] form-repeg: use mark price precomputed --- .../clearing_house/src/controller/repeg.rs | 7 ++-- programs/clearing_house/src/math/repeg.rs | 39 ++++++++++++------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index b0369bb6..b0cca0d4 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -84,6 +84,7 @@ pub fn repeg( pub fn formulaic_repeg( market: &mut Market, + precomputed_mark_price: u128, oracle_price: i128, oracle_conf: u128, oracle_is_valid: bool, @@ -98,8 +99,8 @@ pub fn formulaic_repeg( .checked_div(oracle_price) .ok_or_else(math_error!())?; - let (new_peg_candidate, adjustment_cost) = - repeg::calculate_optimal_peg_and_cost(market, oracle_terminal_divergence_pct_before)?; + let (new_peg_candidate, adjustment_cost, repegged_market) = + repeg::calculate_optimal_peg_and_cost(market, precomputed_mark_price, oracle_terminal_divergence_pct_before)?; let ( oracle_is_valid, @@ -108,7 +109,7 @@ pub fn formulaic_repeg( price_impact_valid, oracle_terminal_divergence_pct_after, ) = repeg::calculate_repeg_validity( - market, + repegged_market, oracle_price, oracle_conf, oracle_is_valid, diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 10ecaadb..5dccd903 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -139,45 +139,58 @@ pub fn calculate_repeg_validity( pub fn calculate_optimal_peg_and_cost( market: &mut Market, + precomputed_mark_price: u128, oracle_terminal_price_divergence: i128, -) -> ClearingHouseResult<(u128, i128)> { +) -> ClearingHouseResult<(u128, i128, &mut Market)> { // does minimum valid repeg allowable iff satisfies the budget - // budget is half of fee pool + // max budget for single repeg is half of fee pool for repegs let budget = calculate_fee_pool(market)? .checked_div(2) .ok_or_else(math_error!())?; + let current_peg = market.amm.peg_multiplier; let mut optimal_peg: u128; + let mut repeg_event = false; + if oracle_terminal_price_divergence > 0 { + // oracle is above terminal price + repeg_event = true; optimal_peg = market .amm .peg_multiplier .checked_add(1) .ok_or_else(math_error!())?; } else if oracle_terminal_price_divergence < 0 { + // oracle is below terminal price + repeg_event = true; optimal_peg = market .amm .peg_multiplier .checked_sub(1) .ok_or_else(math_error!())?; } else { - optimal_peg = market.amm.peg_multiplier; + // oracle == terminal price + optimal_peg = current_peg; } - let (_, optimal_adjustment_cost) = adjust_peg_cost(market, optimal_peg)?; + if repeg_event { + let (repegged_market, optimal_adjustment_cost) = adjust_peg_cost(market, optimal_peg)?; + + let candidate_peg: u128; + let candidate_cost: i128; + if optimal_adjustment_cost > 0 && optimal_adjustment_cost.unsigned_abs() > budget { + candidate_peg = current_peg; + candidate_cost = 0; + } else { + candidate_peg = optimal_peg; + candidate_cost = optimal_adjustment_cost; + } - let candidate_peg: u128; - let candidate_cost: i128; - if optimal_adjustment_cost > 0 && optimal_adjustment_cost.unsigned_abs() > budget { - candidate_peg = market.amm.peg_multiplier; - candidate_cost = 0; + Ok((candidate_peg, candidate_cost, repegged_market)) } else { - candidate_peg = optimal_peg; - candidate_cost = optimal_adjustment_cost; + Ok((market.amm.peg_multiplier, 0, market)) } - - Ok((candidate_peg, candidate_cost)) } pub fn adjust_peg_cost( From a0b50937d289a61852d1c1451f8c2b377edf9866 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Wed, 19 Jan 2022 23:03:14 -0500 Subject: [PATCH 07/59] clearing_house: calculate_peg_from_target_price, do repeg when profitable --- .../clearing_house/src/controller/repeg.rs | 19 ++- programs/clearing_house/src/math/repeg.rs | 129 +++++++++++++----- 2 files changed, 106 insertions(+), 42 deletions(-) diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index b0cca0d4..7d9b11f2 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -93,21 +93,26 @@ pub fn formulaic_repeg( let oracle_terminal_spread_before = oracle_price .checked_sub(cast_to_i128(terminal_price_before)?) .ok_or_else(math_error!())?; - let oracle_terminal_divergence_pct_before = oracle_terminal_spread_before - .checked_shl(10) - .ok_or_else(math_error!())? - .checked_div(oracle_price) - .ok_or_else(math_error!())?; + // let oracle_terminal_divergence_pct_before = oracle_terminal_spread_before + // .checked_shl(10) + // .ok_or_else(math_error!())? + // .checked_div(oracle_price) + // .ok_or_else(math_error!())?; let (new_peg_candidate, adjustment_cost, repegged_market) = - repeg::calculate_optimal_peg_and_cost(market, precomputed_mark_price, oracle_terminal_divergence_pct_before)?; + repeg::calculate_optimal_peg_and_cost( + market, + oracle_price, + precomputed_mark_price, + terminal_price_before, + )?; let ( oracle_is_valid, direction_valid, profitability_valid, price_impact_valid, - oracle_terminal_divergence_pct_after, + _oracle_terminal_divergence_pct_after, ) = repeg::calculate_repeg_validity( repegged_market, oracle_price, diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 5dccd903..94e0bcfb 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -1,7 +1,9 @@ use crate::error::*; use crate::math::amm; +use crate::math::bn; use crate::math::casting::{cast_to_i128, cast_to_u128}; use crate::math::constants::{ + MARK_PRICE_PRECISION, PEG_PRECISION, PRICE_TO_PEG_PRECISION_RATIO, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, }; @@ -137,60 +139,117 @@ pub fn calculate_repeg_validity( )) } +pub fn calculate_peg_from_target_price( + quote_asset_reserve: u128, + base_asset_reserve: u128, + target_price: u128, +) -> ClearingHouseResult { + // m = y*C*PTPPR/x + // C = m*x/y*PTPPR + + return bn::U192::from(target_price) + .checked_mul(bn::U192::from(base_asset_reserve)) + .ok_or_else(math_error!())? + .checked_div(bn::U192::from(quote_asset_reserve)) + .ok_or_else(math_error!())? + .checked_mul(bn::U192::from(PRICE_TO_PEG_PRECISION_RATIO)) + .ok_or_else(math_error!())? + .try_to_u128(); +} + pub fn calculate_optimal_peg_and_cost( market: &mut Market, - precomputed_mark_price: u128, - oracle_terminal_price_divergence: i128, + oracle_price: i128, + mark_price: u128, + terminal_price: u128, ) -> ClearingHouseResult<(u128, i128, &mut Market)> { // does minimum valid repeg allowable iff satisfies the budget + let oracle_mark_spread = oracle_price + .checked_sub(cast_to_i128(mark_price)?) + .ok_or_else(math_error!())?; + let oracle_mark_spread_pct = oracle_mark_spread + .checked_shl(10) + .ok_or_else(math_error!())? + .checked_div(oracle_price) + .ok_or_else(math_error!())?; + + let oracle_terminal_spread = oracle_price + .checked_sub(cast_to_i128(terminal_price)?) + .ok_or_else(math_error!())?; + let oracle_terminal_spread_pct = oracle_terminal_spread + .checked_shl(10) + .ok_or_else(math_error!())? + .checked_div(oracle_price) + .ok_or_else(math_error!())?; + + let ten_pct = 1_u128 + .checked_shl(10) + .ok_or_else(math_error!())? + .checked_div(10) + .ok_or_else(math_error!())?; + + if (oracle_terminal_spread_pct.unsigned_abs() < ten_pct + || oracle_terminal_spread.unsigned_abs() < PEG_PRECISION) + && (oracle_mark_spread_pct.unsigned_abs() < ten_pct + || oracle_mark_spread.unsigned_abs() < PEG_PRECISION) + { + // terminate early, no repeg needed + return Ok((market.amm.peg_multiplier, 0, market)); + } + + // find optimal peg + let optimal_peg: u128; + let current_peg = market.amm.peg_multiplier; + // max budget for single repeg is half of fee pool for repegs let budget = calculate_fee_pool(market)? .checked_div(2) .ok_or_else(math_error!())?; - let current_peg = market.amm.peg_multiplier; - let mut optimal_peg: u128; - let mut repeg_event = false; - - if oracle_terminal_price_divergence > 0 { + // let max_peg_delta = cast_to_u128(oracle_mark_spread)? + // .checked_mul(PEG_PRECISION) + // .ok_or_else(math_error!())? + // .checked_div(MARK_PRICE_PRECISION) + // .ok_or_else(math_error!())? + // .checked_div(2) + // .ok_or_else(math_error!())?; + // let min_peg_delta = 1_u128; + + // repeg is profitable when: + // 1) oracle above both mark and terminal AND terminal at/above mark + // 2) oracle below both mark and terminal AND terminal at/below mark + if (oracle_mark_spread > 0 && oracle_terminal_spread > 0 && terminal_price >= mark_price) + || (oracle_mark_spread < 0 && oracle_terminal_spread < 0 && terminal_price >= mark_price) + { + optimal_peg = calculate_peg_from_target_price( + market.amm.quote_asset_reserve, + market.amm.base_asset_reserve, + cast_to_u128(oracle_price)?, + )?; + } else if oracle_terminal_spread > 0 && oracle_mark_spread > 0 { // oracle is above terminal price - repeg_event = true; - optimal_peg = market - .amm - .peg_multiplier - .checked_add(1) - .ok_or_else(math_error!())?; - } else if oracle_terminal_price_divergence < 0 { + optimal_peg = current_peg.checked_add(1).ok_or_else(math_error!())?; + } else if oracle_terminal_spread < 0 && oracle_mark_spread < 0 { // oracle is below terminal price - repeg_event = true; - optimal_peg = market - .amm - .peg_multiplier - .checked_sub(1) - .ok_or_else(math_error!())?; + optimal_peg = current_peg.checked_sub(1).ok_or_else(math_error!())?; } else { - // oracle == terminal price optimal_peg = current_peg; } - if repeg_event { - let (repegged_market, optimal_adjustment_cost) = adjust_peg_cost(market, optimal_peg)?; - - let candidate_peg: u128; - let candidate_cost: i128; - if optimal_adjustment_cost > 0 && optimal_adjustment_cost.unsigned_abs() > budget { - candidate_peg = current_peg; - candidate_cost = 0; - } else { - candidate_peg = optimal_peg; - candidate_cost = optimal_adjustment_cost; - } + let (repegged_market, marginal_adjustment_cost) = adjust_peg_cost(market, optimal_peg)?; - Ok((candidate_peg, candidate_cost, repegged_market)) + let candidate_peg: u128; + let candidate_cost: i128; + if marginal_adjustment_cost > 0 && marginal_adjustment_cost.unsigned_abs() > budget { + candidate_peg = current_peg; + candidate_cost = 0; } else { - Ok((market.amm.peg_multiplier, 0, market)) + candidate_peg = optimal_peg; + candidate_cost = marginal_adjustment_cost; } + + Ok((candidate_peg, candidate_cost, repegged_market)) } pub fn adjust_peg_cost( From c4521a3c075bf93cf8f16dddd854240719ed4779 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 20 Jan 2022 18:03:17 -0500 Subject: [PATCH 08/59] merge master --- programs/clearing_house/src/lib.rs | 66 +++++++++++--- programs/clearing_house/src/math/amm.rs | 19 ++++- programs/clearing_house/src/math/funding.rs | 2 +- sdk/src/constants/markets.ts | 7 ++ sdk/src/math/funding.ts | 2 +- tests/adminWithdraw.ts | 6 +- tests/updateK.ts | 95 ++++++++++++++++++++- 7 files changed, 175 insertions(+), 22 deletions(-) diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 8af8e236..d3ab609a 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -714,11 +714,25 @@ pub mod clearing_house { // Trade fails if the trade is risk increasing and it pushes to mark price too far // away from the oracle price - let is_oracle_mark_too_divergent = amm::is_oracle_mark_too_divergent( + let is_oracle_mark_too_divergent_before = amm::is_oracle_mark_too_divergent( + oracle_mark_spread_pct_before, + &ctx.accounts.state.oracle_guard_rails.price_divergence, + )?; + let is_oracle_mark_too_divergent_after = amm::is_oracle_mark_too_divergent( oracle_mark_spread_pct_after, &ctx.accounts.state.oracle_guard_rails.price_divergence, )?; - if is_oracle_mark_too_divergent + + // if oracle-mark divergence pushed outside limit, block trade + if is_oracle_mark_too_divergent_after + && !is_oracle_mark_too_divergent_before + && is_oracle_valid + { + return Err(ErrorCode::OracleMarkSpreadLimit.into()); + } + + // if oracle-mark divergence outside limit and risk-increasing, block trade + if is_oracle_mark_too_divergent_after && oracle_mark_spread_pct_after.unsigned_abs() >= oracle_mark_spread_pct_before.unsigned_abs() && is_oracle_valid @@ -842,6 +856,14 @@ pub mod clearing_house { // Collect data about market before trade is executed so that it can be stored in trade history let mark_price_before = market.amm.mark_price()?; + let (oracle_price, _, oracle_mark_spread_pct_before) = + amm::calculate_oracle_mark_spread_pct( + &market.amm, + &ctx.accounts.oracle, + 0, + clock_slot, + Some(mark_price_before), + )?; let direction_to_close = math::position::direction_to_close_position(market_position.base_asset_amount); let (quote_asset_amount, base_asset_amount) = @@ -906,13 +928,15 @@ pub mod clearing_house { // Collect data about market after trade is executed so that it can be stored in trade history let mark_price_after = market.amm.mark_price()?; let price_oracle = &ctx.accounts.oracle; - let (oracle_price_after, _oracle_mark_spread_after) = amm::calculate_oracle_mark_spread( - &market.amm, - price_oracle, - 0, - clock_slot, - Some(mark_price_after), - )?; + + let (oracle_price_after, _oracle_mark_spread_after, oracle_mark_spread_pct_after) = + amm::calculate_oracle_mark_spread_pct( + &market.amm, + &ctx.accounts.oracle, + 0, + clock_slot, + Some(mark_price_after), + )?; let is_oracle_valid = amm::is_oracle_valid( &market.amm, @@ -924,6 +948,24 @@ pub mod clearing_house { amm::update_oracle_price_twap(&mut market.amm, now, oracle_price_after)?; } + // Trade fails if the trade is risk increasing and it pushes to mark price too far + // away from the oracle price + let is_oracle_mark_too_divergent_before = amm::is_oracle_mark_too_divergent( + oracle_mark_spread_pct_after, + &ctx.accounts.state.oracle_guard_rails.price_divergence, + )?; + let is_oracle_mark_too_divergent_after = amm::is_oracle_mark_too_divergent( + oracle_mark_spread_pct_after, + &ctx.accounts.state.oracle_guard_rails.price_divergence, + )?; + + // if closing position pushes outside of oracle-mark divergence limit, block trade + if (is_oracle_mark_too_divergent_after && !is_oracle_mark_too_divergent_before) + && is_oracle_valid + { + return Err(ErrorCode::OracleMarkSpreadLimit.into()); + } + // Add to the trade history account let trade_history_account = &mut ctx.accounts.trade_history.load_mut()?; let record_id = trade_history_account.next_record_id(); @@ -1313,11 +1355,7 @@ pub mod clearing_house { // The admin can move fees from the insurance fund back to the protocol so that money in // the insurance fund can be used to make market more optimal - market.amm.total_fee = market - .amm - .total_fee - .checked_add(cast(amount)?) - .ok_or_else(math_error!())?; + // 100% goes to user fee pool (symmetric funding, repeg, and k adjustments) market.amm.total_fee_minus_distributions = market .amm .total_fee_minus_distributions diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 4249e561..08cb038a 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -321,24 +321,37 @@ pub fn adjust_k_cost(market: &mut Market, new_sqrt_k: bn::U256) -> ClearingHouse let (current_net_market_value, _) = _calculate_base_asset_value_and_pnl(market.base_asset_amount, 0, &market.amm)?; + let ratio_scalar = bn::U256::from(MARK_PRICE_PRECISION); + let sqrt_k_ratio = new_sqrt_k - .checked_mul(bn::U256::from(MARK_PRICE_PRECISION)) + .checked_mul(ratio_scalar) .ok_or_else(math_error!())? .checked_div(bn::U256::from(market.amm.sqrt_k)) .ok_or_else(math_error!())?; + // if decreasing k, max decrease ratio for single transaction is 2.5% + if sqrt_k_ratio + < ratio_scalar + .checked_mul(bn::U256::from(975)) + .ok_or_else(math_error!())? + .checked_div(bn::U256::from(1000)) + .ok_or_else(math_error!())? + { + return Err(ErrorCode::InvalidUpdateK.into()); + } + market.amm.sqrt_k = new_sqrt_k.try_to_u128().unwrap(); market.amm.base_asset_reserve = bn::U256::from(market.amm.base_asset_reserve) .checked_mul(sqrt_k_ratio) .ok_or_else(math_error!())? - .checked_div(bn::U256::from(MARK_PRICE_PRECISION)) + .checked_div(ratio_scalar) .ok_or_else(math_error!())? .try_to_u128() .unwrap(); market.amm.quote_asset_reserve = bn::U256::from(market.amm.quote_asset_reserve) .checked_mul(sqrt_k_ratio) .ok_or_else(math_error!())? - .checked_div(bn::U256::from(MARK_PRICE_PRECISION)) + .checked_div(ratio_scalar) .ok_or_else(math_error!())? .try_to_u128() .unwrap(); diff --git a/programs/clearing_house/src/math/funding.rs b/programs/clearing_house/src/math/funding.rs index e863aa90..cc19422b 100644 --- a/programs/clearing_house/src/math/funding.rs +++ b/programs/clearing_house/src/math/funding.rs @@ -44,7 +44,7 @@ pub fn calculate_funding_rate_long_short( .total_fee_minus_distributions .checked_sub(capped_funding_pnl.unsigned_abs()) .ok_or_else(math_error!())?; - + // clearing house is paying part of funding imbalance if capped_funding_pnl != 0 { let total_fee_minus_distributions_lower_bound = total_fee_lower_bound(&market)?; diff --git a/sdk/src/constants/markets.ts b/sdk/src/constants/markets.ts index 1a649187..d30aaf5b 100644 --- a/sdk/src/constants/markets.ts +++ b/sdk/src/constants/markets.ts @@ -65,4 +65,11 @@ export const Markets: Market[] = [ devnetPythOracle: '7YAze8qFUMkBnyLVdKT4TFUUFui99EwS5gfRArMcrvFk', mainnetPythOracle: 'CrCpTerNqtZvqLcKqz1k13oVeXV9WkMD2zA9hBKXrsbN', }, + { + symbol: 'DOT-PERP', + baseAssetSymbol: 'DOT', + marketIndex: new BN(8), + devnetPythOracle: '4dqq5VBpN4EwYb7wyywjjfknvMKu7m78j9mKZRXTj462', + mainnetPythOracle: 'EcV1X1gY2yb4KXxjVQtTHTbioum2gvmPnFk4zYAt7zne', + }, ]; diff --git a/sdk/src/math/funding.ts b/sdk/src/math/funding.ts index 89ee3e90..86864c70 100644 --- a/sdk/src/math/funding.ts +++ b/sdk/src/math/funding.ts @@ -289,6 +289,6 @@ export async function calculateLongShortFundingRateAndLiveTwaps( export function calculateFundingPool(market: Market): BN { // todo const totalFeeLB = market.amm.totalFee.div(new BN(2)); - const feePool = market.amm.totalFeeMinusDistributions.sub(totalFeeLB); + const feePool = BN.max(ZERO, market.amm.totalFeeMinusDistributions.sub(totalFeeLB)); return feePool; } diff --git a/tests/adminWithdraw.ts b/tests/adminWithdraw.ts index 9ce8a6e4..468003ec 100644 --- a/tests/adminWithdraw.ts +++ b/tests/adminWithdraw.ts @@ -150,7 +150,11 @@ describe('admin withdraw', () => { assert(collateralVaultTokenAccount.amount.eq(new BN(9987562))); market = clearingHouse.getMarketsAccount().markets[0]; + + // deposits go entirely to distributions for sym-funding/repeg/k-adjustments console.log(market.amm.totalFee.toString()); - console.assert(market.amm.totalFee.eq(new BN(62187))); + console.log(market.amm.totalFeeMinusDistributions.toString()); + assert(market.amm.totalFee.lt(market.amm.totalFeeMinusDistributions)); + assert(market.amm.totalFeeMinusDistributions.eq(new BN(62187))); }); }); diff --git a/tests/updateK.ts b/tests/updateK.ts index cf78a5f3..0baab816 100644 --- a/tests/updateK.ts +++ b/tests/updateK.ts @@ -173,7 +173,7 @@ describe('update k', () => { assert(amm.sqrtK.eq(newSqrtK)); }); - it('lower k position imbalance (AMM PROFIT)', async () => { + it('failure: lower k (more than 2.5%) position imbalance (AMM PROFIT)', async () => { const marketIndex = Markets[0].marketIndex; const targetPriceBack = new BN( @@ -207,6 +207,98 @@ describe('update k', () => { const newSqrtK = ammOld.sqrtK .mul(new BN(0.5 * MARK_PRICE_PRECISION.toNumber())) .div(MARK_PRICE_PRECISION); + + try{ + await clearingHouse.updateK(newSqrtK, marketIndex); + assert(false); + } catch{ + + await clearingHouse.fetchAccounts(); + const marketsKChange = await clearingHouse.getMarketsAccount(); + const ammKChange = marketsKChange.markets[0].amm; + + const newKPrice = calculateMarkPrice(clearingHouse.getMarket(marketIndex)); + + console.log('$1 position closing'); + + await clearingHouse.closePosition(marketIndex); + console.log('$1 position closed'); + + const markets = await clearingHouse.getMarketsAccount(); + + const amm = markets.markets[0].amm; + + const marginOfError = new BN(MARK_PRICE_PRECISION.div(new BN(1000))); // price change less than 3 decimal places + + console.log( + 'oldSqrtK', + convertToNumber(ammOld.sqrtK), + 'oldKPrice:', + convertToNumber(oldKPrice) + ); + console.log( + 'newSqrtK', + convertToNumber(newSqrtK), + 'newKPrice:', + convertToNumber(newKPrice) + ); + + assert(ammOld.sqrtK.eq(amm.sqrtK)); + assert(newKPrice.sub(oldKPrice).abs().lt(marginOfError)); + assert(!amm.sqrtK.eq(newSqrtK)); + + console.log( + 'realizedFeeOld', + convertToNumber(ammOld.totalFeeMinusDistributions, QUOTE_PRECISION), + 'realizedFeePostK', + convertToNumber(ammKChange.totalFeeMinusDistributions, QUOTE_PRECISION), + 'realizedFeePostClose', + convertToNumber(amm.totalFeeMinusDistributions, QUOTE_PRECISION) + ); + console.log( + 'USER getTotalCollateral', + convertToNumber(userAccount.getTotalCollateral(), QUOTE_PRECISION) + ); + + // assert(amm.totalFeeMinusDistributions.lt(ammOld.totalFeeMinusDistributions)); + } + + + }); + it('lower k (2%) position imbalance (AMM PROFIT)', async () => { + const marketIndex = Markets[0].marketIndex; + + const targetPriceBack = new BN( + initialSOLPrice * MARK_PRICE_PRECISION.toNumber() + ); + + // const [direction, tradeSize, _] = clearingHouse.calculateTargetPriceTrade( + // marketIndex, + // targetPriceUp + // ); + await clearingHouse.moveAmmToPrice(marketIndex, targetPriceBack); + + console.log('taking position'); + await clearingHouse.openPosition( + PositionDirection.LONG, + new BN(QUOTE_PRECISION), + marketIndex + ); + console.log('$1 position taken'); + await clearingHouse.fetchAccounts(); + const marketsOld = await clearingHouse.getMarketsAccount(); + assert(!marketsOld.markets[0].baseAssetAmount.eq(ZERO)); + + const oldKPrice = calculateMarkPrice(clearingHouse.getMarket(marketIndex)); + const ammOld = marketsOld.markets[0].amm; + console.log( + 'USER getTotalCollateral', + convertToNumber(userAccount.getTotalCollateral(), QUOTE_PRECISION) + ); + + const newSqrtK = ammOld.sqrtK + .mul(new BN(0.98 * MARK_PRICE_PRECISION.toNumber())) + .div(MARK_PRICE_PRECISION); await clearingHouse.updateK(newSqrtK, marketIndex); await clearingHouse.fetchAccounts(); @@ -258,7 +350,6 @@ describe('update k', () => { // assert(amm.totalFeeMinusDistributions.lt(ammOld.totalFeeMinusDistributions)); }); - it('increase k position imbalance (AMM LOSS)', async () => { const marketIndex = Markets[0].marketIndex; const targetPriceBack = new BN( From 7cd4801e55da2df4e9c8988fcdeb966e79f203d8 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Sun, 27 Feb 2022 10:56:30 -0600 Subject: [PATCH 09/59] retryTxSender: init --- sdk/src/index.ts | 1 + sdk/src/tx/retryTxSender.ts | 196 ++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 sdk/src/tx/retryTxSender.ts diff --git a/sdk/src/index.ts b/sdk/src/index.ts index c453f161..8ffe847c 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -31,6 +31,7 @@ export * from './types'; export * from './math/utils'; export * from './config'; export * from './constants/numericConstants'; +export * from './tx/retryTxSender'; export * from './util/computeUnits'; export * from './util/tps'; diff --git a/sdk/src/tx/retryTxSender.ts b/sdk/src/tx/retryTxSender.ts new file mode 100644 index 00000000..2627d7c0 --- /dev/null +++ b/sdk/src/tx/retryTxSender.ts @@ -0,0 +1,196 @@ +import { TxSender } from './types'; +import { + Commitment, + ConfirmOptions, + Context, + RpcResponseAndContext, + Signer, + SignatureResult, + Transaction, + TransactionSignature, +} from '@solana/web3.js'; +import { Provider } from '@project-serum/anchor'; +import assert from 'assert'; +import bs58 from 'bs58'; + +const DEFAULT_TIMEOUT = 35000; +const DEFAULT_RETRY = 8000; + +type ResolveReference = { + resolve?: () => void; +}; + +export class RetryTxSender implements TxSender { + provider: Provider; + timeout: number; + retrySleep: number; + + public constructor( + provider: Provider, + timeout?: number, + retrySleep?: number + ) { + this.provider = provider; + this.timeout = timeout ?? DEFAULT_TIMEOUT; + this.retrySleep = retrySleep ?? DEFAULT_RETRY; + } + + async send( + tx: Transaction, + additionalSigners?: Array, + opts?: ConfirmOptions + ): Promise { + if (additionalSigners === undefined) { + additionalSigners = []; + } + if (opts === undefined) { + opts = this.provider.opts; + } + + await this.prepareTx(tx, additionalSigners, opts); + + const rawTransaction = tx.serialize(); + const startTime = this.getTimestamp(); + + const txid: TransactionSignature = + await this.provider.connection.sendRawTransaction(rawTransaction, opts); + + let done = false; + const resolveReference: ResolveReference = { + resolve: undefined, + }; + const stopWaiting = () => { + done = true; + if (resolveReference.resolve) { + resolveReference.resolve(); + } + }; + + (async () => { + while (!done && this.getTimestamp() - startTime < this.timeout) { + await this.sleep(resolveReference); + if (!done) { + this.provider.connection + .sendRawTransaction(rawTransaction, opts) + .catch((e) => { + console.error(e); + stopWaiting(); + }); + } + } + })(); + + try { + await this.confirmTransaction(txid, opts.commitment); + } catch (e) { + console.error(e); + throw e; + } finally { + stopWaiting(); + } + + return txid; + } + + async prepareTx( + tx: Transaction, + additionalSigners: Array, + opts: ConfirmOptions + ): Promise { + tx.feePayer = this.provider.wallet.publicKey; + tx.recentBlockhash = ( + await this.provider.connection.getRecentBlockhash( + opts.preflightCommitment + ) + ).blockhash; + + await this.provider.wallet.signTransaction(tx); + additionalSigners + .filter((s): s is Signer => s !== undefined) + .forEach((kp) => { + tx.partialSign(kp); + }); + + return tx; + } + + async confirmTransaction( + signature: TransactionSignature, + commitment?: Commitment + ): Promise> { + let decodedSignature; + try { + decodedSignature = bs58.decode(signature); + } catch (err) { + throw new Error('signature must be base58 encoded: ' + signature); + } + + assert(decodedSignature.length === 64, 'signature has invalid length'); + + const start = Date.now(); + const subscriptionCommitment = commitment || this.provider.opts.commitment; + + let subscriptionId; + let response: RpcResponseAndContext | null = null; + const confirmPromise = new Promise((resolve, reject) => { + try { + subscriptionId = this.provider.connection.onSignature( + signature, + (result: SignatureResult, context: Context) => { + subscriptionId = undefined; + response = { + context, + value: result, + }; + resolve(null); + }, + subscriptionCommitment + ); + } catch (err) { + reject(err); + } + }); + + try { + await this.promiseTimeout(confirmPromise, this.timeout); + } finally { + if (subscriptionId) { + this.provider.connection.removeSignatureListener(subscriptionId); + } + } + + if (response === null) { + const duration = (Date.now() - start) / 1000; + throw new Error( + `Transaction was not confirmed in ${duration.toFixed( + 2 + )} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.` + ); + } + + return response; + } + + getTimestamp(): number { + return new Date().getTime(); + } + + async sleep(reference: ResolveReference): Promise { + return new Promise((resolve) => { + reference.resolve = resolve; + setTimeout(resolve, this.retrySleep); + }); + } + + promiseTimeout(promise: Promise, timeoutMs: number): Promise { + let timeoutId: ReturnType; + const timeoutPromise: Promise = new Promise((resolve) => { + timeoutId = setTimeout(() => resolve(null), timeoutMs); + }); + + return Promise.race([promise, timeoutPromise]).then((result: T | null) => { + clearTimeout(timeoutId); + return result; + }); + } +} From e1bab51bcb680dff98436edaff291bc4861c669d Mon Sep 17 00:00:00 2001 From: Luke Steyn Date: Mon, 28 Feb 2022 13:00:09 +1000 Subject: [PATCH 10/59] sdk: Added logic to update provider for tx sender when updating wallet in clearinghouse --- sdk/src/clearingHouse.ts | 5 +++-- sdk/src/tx/types.ts | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/src/clearingHouse.ts b/sdk/src/clearingHouse.ts index 9e8d9d92..890c3591 100644 --- a/sdk/src/clearingHouse.ts +++ b/sdk/src/clearingHouse.ts @@ -250,12 +250,13 @@ export class ClearingHouse { this.program.programId, newProvider ); - const newTxSender = new DefaultTxSender(newProvider); + + // Update provider for txSender with new wallet details + this.txSender.provider = newProvider; this.wallet = newWallet; this.provider = newProvider; this.program = newProgram; - this.txSender = newTxSender; this.userAccountPublicKey = undefined; this.userAccount = undefined; } diff --git a/sdk/src/tx/types.ts b/sdk/src/tx/types.ts index ca931351..b612dad0 100644 --- a/sdk/src/tx/types.ts +++ b/sdk/src/tx/types.ts @@ -1,3 +1,4 @@ +import { Provider } from '@project-serum/anchor'; import { ConfirmOptions, Signer, @@ -6,6 +7,8 @@ import { } from '@solana/web3.js'; export interface TxSender { + provider: Provider; + send( tx: Transaction, additionalSigners?: Array, From 61e362e3ab1d123e2f98246b327e9d1373895039 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 28 Feb 2022 17:11:21 -0600 Subject: [PATCH 11/59] clearing_house: update oracle guard rails --- .../clearing_house/src/controller/orders.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index c5c32cff..ef596847 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -418,13 +418,24 @@ pub fn fill_order( oracle_mark_spread_pct_after = _oracle_mark_spread_pct_after; } - // Order fails if the trade is risk increasing and it pushes to mark price too far - // away from the oracle price - let is_oracle_mark_too_divergent = amm::is_oracle_mark_too_divergent( + let is_oracle_mark_too_divergent_before = amm::is_oracle_mark_too_divergent( + oracle_mark_spread_pct_before, + &state.oracle_guard_rails.price_divergence, + )?; + + let is_oracle_mark_too_divergent_after = amm::is_oracle_mark_too_divergent( oracle_mark_spread_pct_after, &state.oracle_guard_rails.price_divergence, )?; - if is_oracle_mark_too_divergent + + // if oracle-mark divergence pushed outside limit, block order + if is_oracle_mark_too_divergent_after && !is_oracle_mark_too_divergent_before && is_oracle_valid + { + return Err(ErrorCode::OracleMarkSpreadLimit); + } + + // if oracle-mark divergence outside limit and risk-increasing, block order + if is_oracle_mark_too_divergent_after && oracle_mark_spread_pct_after.unsigned_abs() >= oracle_mark_spread_pct_before.unsigned_abs() && is_oracle_valid From fbfdd919e41b62f018b9a255b622d3c239297344 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 1 Mar 2022 17:29:06 -0600 Subject: [PATCH 12/59] sdk: add bulkUserSubscription to index.ts --- sdk/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 8ffe847c..8f0d43ca 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -7,6 +7,7 @@ export * from './types'; export * from './constants/markets'; export * from './accounts/webSocketClearingHouseAccountSubscriber'; export * from './accounts/bulkAccountLoader'; +export * from './accounts/bulkUserSubscription'; export * from './accounts/pollingClearingHouseAccountSubscriber'; export * from './accounts/pollingTokenAccountSubscriber'; export * from './accounts/types'; From 5fe7a5f57f1c32e3d80bca90014f5509adb1fae3 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 2 Mar 2022 09:54:36 -0600 Subject: [PATCH 13/59] clearing_house: handle trigger market orders filling more than order requested --- .../clearing_house/src/controller/orders.rs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index ef596847..40859f78 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -753,9 +753,17 @@ pub fn execute_non_market_order( return Ok((0, 0, false)); } + let base_asset_amount_left_to_fill_before = order + .base_asset_amount + .checked_sub(order.base_asset_amount_filled) + .ok_or_else(math_error!())?; + let mut base_asset_amount = min( - base_asset_amount_market_can_execute, - base_asset_amount_user_can_execute, + min( + base_asset_amount_market_can_execute, + base_asset_amount_user_can_execute, + ), + base_asset_amount_left_to_fill_before, ); if base_asset_amount < market.amm.minimum_base_asset_trade_size { @@ -764,7 +772,7 @@ pub fn execute_non_market_order( } let minimum_base_asset_trade_size = market.amm.minimum_base_asset_trade_size; - let base_asset_amount_left_to_fill = order + let base_asset_amount_left_to_fill_after = order .base_asset_amount .checked_sub( order @@ -774,11 +782,11 @@ pub fn execute_non_market_order( ) .ok_or_else(math_error!())?; - if base_asset_amount_left_to_fill > 0 - && base_asset_amount_left_to_fill < minimum_base_asset_trade_size + if base_asset_amount_left_to_fill_after > 0 + && base_asset_amount_left_to_fill_after < minimum_base_asset_trade_size { base_asset_amount = base_asset_amount - .checked_add(base_asset_amount_left_to_fill) + .checked_add(base_asset_amount_left_to_fill_after) .ok_or_else(math_error!())?; } From d0fe8feb8536ad0b72df0204f77e99bded0f46a5 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 2 Mar 2022 09:58:46 -0600 Subject: [PATCH 14/59] Revert "clearing_house: handle trigger market orders filling more than order requested" This reverts commit 201fe7293fd0f8bf3fd1139f390a5cd967d9e1bd. --- .../clearing_house/src/controller/orders.rs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index 40859f78..ef596847 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -753,17 +753,9 @@ pub fn execute_non_market_order( return Ok((0, 0, false)); } - let base_asset_amount_left_to_fill_before = order - .base_asset_amount - .checked_sub(order.base_asset_amount_filled) - .ok_or_else(math_error!())?; - let mut base_asset_amount = min( - min( - base_asset_amount_market_can_execute, - base_asset_amount_user_can_execute, - ), - base_asset_amount_left_to_fill_before, + base_asset_amount_market_can_execute, + base_asset_amount_user_can_execute, ); if base_asset_amount < market.amm.minimum_base_asset_trade_size { @@ -772,7 +764,7 @@ pub fn execute_non_market_order( } let minimum_base_asset_trade_size = market.amm.minimum_base_asset_trade_size; - let base_asset_amount_left_to_fill_after = order + let base_asset_amount_left_to_fill = order .base_asset_amount .checked_sub( order @@ -782,11 +774,11 @@ pub fn execute_non_market_order( ) .ok_or_else(math_error!())?; - if base_asset_amount_left_to_fill_after > 0 - && base_asset_amount_left_to_fill_after < minimum_base_asset_trade_size + if base_asset_amount_left_to_fill > 0 + && base_asset_amount_left_to_fill < minimum_base_asset_trade_size { base_asset_amount = base_asset_amount - .checked_add(base_asset_amount_left_to_fill_after) + .checked_add(base_asset_amount_left_to_fill) .ok_or_else(math_error!())?; } From 51c792e6d94dc15c173272f258bec2482bfe4f49 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 2 Mar 2022 10:00:15 -0600 Subject: [PATCH 15/59] clearing_house: calculate_base_asset_amount_to_trade_for_trigger_market only returns base asset left to fill --- programs/clearing_house/src/math/orders.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/programs/clearing_house/src/math/orders.rs b/programs/clearing_house/src/math/orders.rs index 3d464866..c5454212 100644 --- a/programs/clearing_house/src/math/orders.rs +++ b/programs/clearing_house/src/math/orders.rs @@ -118,7 +118,10 @@ fn calculate_base_asset_amount_to_trade_for_trigger_market( } } - Ok(order.base_asset_amount) + order + .base_asset_amount + .checked_sub(order.base_asset_amount_filled) + .ok_or_else(math_error!()) } fn calculate_base_asset_amount_to_trade_for_trigger_limit( From 93fd2ca384cf334de44c3f0a1d44fc3a9ce776d1 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 28 Feb 2022 16:50:59 -0600 Subject: [PATCH 16/59] clearing_house: make round in favor work with placeAndFill --- programs/clearing_house/src/math/amm.rs | 13 ++++++++++++- programs/clearing_house/src/math/orders.rs | 15 ++++++--------- programs/clearing_house/src/math/pnl.rs | 4 ---- sdk/src/math/position.ts | 5 +++-- sdk/src/math/trade.ts | 11 ++++++++--- tests/clearingHouse.ts | 12 ++++++------ tests/marketOrder.ts | 2 +- tests/marketOrderBaseAssetAmount.ts | 12 ++++++------ tests/order.ts | 2 +- tests/roundInFavorBaseAsset.ts | 2 +- tests/stopLimits.ts | 2 +- 11 files changed, 45 insertions(+), 35 deletions(-) diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 8f637e4a..125d4398 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -259,7 +259,18 @@ pub fn calculate_quote_asset_amount_swapped( .ok_or_else(math_error!())?, }; - reserve_to_asset_amount(quote_asset_reserve_change, peg_multiplier) + let mut quote_asset_amount = + reserve_to_asset_amount(quote_asset_reserve_change, peg_multiplier)?; + + // when a user goes long base asset, make the base asset slightly more expensive + // by adding one unit of quote asset + if swap_direction == SwapDirection::Remove { + quote_asset_amount = quote_asset_amount + .checked_add(1) + .ok_or_else(math_error!())?; + } + + Ok(quote_asset_amount) } pub fn calculate_oracle_mark_spread( diff --git a/programs/clearing_house/src/math/orders.rs b/programs/clearing_house/src/math/orders.rs index c5454212..f9570345 100644 --- a/programs/clearing_house/src/math/orders.rs +++ b/programs/clearing_house/src/math/orders.rs @@ -209,7 +209,12 @@ pub fn calculate_available_quote_asset_user_can_execute( let market_position = &user_positions.positions[position_index]; let max_leverage = MARGIN_PRECISION - .checked_div(margin_ratio_initial) + .checked_div( + // add one to initial margin ratio so we don't fill exactly to max leverage + margin_ratio_initial + .checked_add(1) + .ok_or_else(math_error!())?, + ) .ok_or_else(math_error!())?; let risk_increasing_in_same_direction = market_position.base_asset_amount == 0 @@ -220,11 +225,7 @@ pub fn calculate_available_quote_asset_user_can_execute( let (free_collateral, _) = calculate_free_collateral(user, user_positions, markets, max_leverage, None)?; - // When opening new position, user may realize -1 pnl from rounding - // Subtract 1 from free collateral to avoid going over initial margin requirements free_collateral - .checked_sub(if free_collateral == 0 { 0 } else { 1 }) - .ok_or_else(math_error!())? .checked_mul(max_leverage) .ok_or_else(math_error!())? } else { @@ -237,11 +238,7 @@ pub fn calculate_available_quote_asset_user_can_execute( Some(market_index), )?; - // When opening new position, user may realize -1 pnl from rounding - // Subtract 1 from free collateral to avoid going over initial margin requirements free_collateral - .checked_sub(if free_collateral == 0 { 0 } else { 1 }) - .ok_or_else(math_error!())? .checked_mul(max_leverage) .ok_or_else(math_error!())? .checked_add(closed_position_base_asset_value) diff --git a/programs/clearing_house/src/math/pnl.rs b/programs/clearing_house/src/math/pnl.rs index b6f6589d..06abc3e5 100644 --- a/programs/clearing_house/src/math/pnl.rs +++ b/programs/clearing_house/src/math/pnl.rs @@ -14,12 +14,8 @@ pub fn calculate_pnl( SwapDirection::Add => cast_to_i128(exit_value)? .checked_sub(cast(entry_value)?) .ok_or_else(math_error!())?, - // base asset value is round down due to integer math - // subtract one from pnl so that users who are short dont get an extra +1 pnl from integer division SwapDirection::Remove => cast_to_i128(entry_value)? .checked_sub(cast(exit_value)?) - .ok_or_else(math_error!())? - .checked_sub(1) .ok_or_else(math_error!())?, }) } diff --git a/sdk/src/math/position.ts b/sdk/src/math/position.ts index cb227393..e58ea4d8 100644 --- a/sdk/src/math/position.ts +++ b/sdk/src/math/position.ts @@ -47,7 +47,8 @@ export function calculateBaseAssetValue( return newQuoteAssetReserve .sub(market.amm.quoteAssetReserve) .mul(market.amm.pegMultiplier) - .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO); + .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO) + .add(ONE); } } @@ -74,7 +75,7 @@ export function calculatePositionPNL( if (marketPosition.baseAssetAmount.gt(ZERO)) { pnl = baseAssetValue.sub(marketPosition.quoteAssetAmount); } else { - pnl = marketPosition.quoteAssetAmount.sub(baseAssetValue).sub(ONE); + pnl = marketPosition.quoteAssetAmount.sub(baseAssetValue); } if (withFunding) { diff --git a/sdk/src/math/trade.ts b/sdk/src/math/trade.ts index 46c66d5c..40905488 100644 --- a/sdk/src/math/trade.ts +++ b/sdk/src/math/trade.ts @@ -1,4 +1,4 @@ -import { Market, PositionDirection } from '../types'; +import { isVariant, Market, PositionDirection } from '../types'; import { BN } from '@project-serum/anchor'; import { assert } from '../assert/assert'; import { @@ -6,6 +6,7 @@ import { PEG_PRECISION, AMM_TO_QUOTE_PRECISION_RATIO, ZERO, + ONE, } from '../constants/numericConstants'; import { calculateMarkPrice } from './market'; import { @@ -114,16 +115,20 @@ export function calculateTradeAcquiredAmounts( return [ZERO, ZERO]; } + const swapDirection = getSwapDirection(inputAssetType, direction); const [newQuoteAssetReserve, newBaseAssetReserve] = calculateAmmReservesAfterSwap( market.amm, inputAssetType, amount, - getSwapDirection(inputAssetType, direction) + swapDirection ); const acquiredBase = market.amm.baseAssetReserve.sub(newBaseAssetReserve); - const acquiredQuote = market.amm.quoteAssetReserve.sub(newQuoteAssetReserve); + let acquiredQuote = market.amm.quoteAssetReserve.sub(newQuoteAssetReserve); + if (inputAssetType === 'base' && isVariant(swapDirection, 'remove')) { + acquiredQuote = acquiredQuote.sub(ONE); + } return [acquiredBase, acquiredQuote]; } diff --git a/tests/clearingHouse.ts b/tests/clearingHouse.ts index 631f1221..3a7f9bc2 100644 --- a/tests/clearingHouse.ts +++ b/tests/clearingHouse.ts @@ -540,7 +540,7 @@ describe('clearing_house', () => { ); assert.ok(tradeHistoryAccount.tradeRecords[2].liquidation == false); assert.ok( - tradeHistoryAccount.tradeRecords[3].quoteAssetAmount.eq(new BN(24875000)) + tradeHistoryAccount.tradeRecords[3].quoteAssetAmount.eq(new BN(24875001)) ); assert.ok(tradeHistoryAccount.tradeRecords[3].marketIndex.eq(new BN(0))); }); @@ -697,12 +697,12 @@ describe('clearing_house', () => { ); assert.ok( tradeHistoryAccount.tradeRecords[5].baseAssetAmount.eq( - new BN(122540299515118) + new BN(122540308307591) ) ); assert.ok(tradeHistoryAccount.tradeRecords[5].liquidation); assert.ok( - tradeHistoryAccount.tradeRecords[5].quoteAssetAmount.eq(new BN(13936591)) + tradeHistoryAccount.tradeRecords[5].quoteAssetAmount.eq(new BN(13936592)) ); assert.ok(tradeHistoryAccount.tradeRecords[5].marketIndex.eq(new BN(0))); @@ -715,12 +715,12 @@ describe('clearing_house', () => { assert.ok(liquidationHistory.liquidationRecords[0].partial); assert.ok( liquidationHistory.liquidationRecords[0].baseAssetValue.eq( - new BN(55746367) + new BN(55746368) ) ); assert.ok( liquidationHistory.liquidationRecords[0].baseAssetValueClosed.eq( - new BN(13936591) + new BN(13936592) ) ); assert.ok( @@ -825,7 +825,7 @@ describe('clearing_house', () => { ); assert.ok( tradeHistoryAccount.tradeRecords[6].baseAssetAmount.eq( - new BN(367582499847535) + new BN(367582491055062) ) ); assert.ok(tradeHistoryAccount.tradeRecords[6].liquidation); diff --git a/tests/marketOrder.ts b/tests/marketOrder.ts index cd221403..8d7eeaf4 100644 --- a/tests/marketOrder.ts +++ b/tests/marketOrder.ts @@ -201,7 +201,7 @@ describe('market order', () => { const firstPosition = userPositionsAccount.positions[0]; assert(firstPosition.baseAssetAmount.eq(baseAssetAmount)); - const expectedQuoteAssetAmount = new BN(1000002); + const expectedQuoteAssetAmount = new BN(1000003); assert(firstPosition.quoteAssetAmount.eq(expectedQuoteAssetAmount)); const tradeHistoryAccount = clearingHouse.getTradeHistoryAccount(); diff --git a/tests/marketOrderBaseAssetAmount.ts b/tests/marketOrderBaseAssetAmount.ts index 7fd81479..cdc04732 100644 --- a/tests/marketOrderBaseAssetAmount.ts +++ b/tests/marketOrderBaseAssetAmount.ts @@ -104,7 +104,7 @@ describe('clearing_house', () => { await clearingHouse.program.account.userPositions.fetch(user.positions); assert.ok( - userPositionsAccount.positions[0].quoteAssetAmount.eq(new BN(49750000)) + userPositionsAccount.positions[0].quoteAssetAmount.eq(new BN(49750001)) ); console.log(userPositionsAccount.positions[0].baseAssetAmount); assert.ok( @@ -141,7 +141,7 @@ describe('clearing_house', () => { ); assert.ok(tradeHistoryAccount.tradeRecords[0].liquidation == false); assert.ok( - tradeHistoryAccount.tradeRecords[0].quoteAssetAmount.eq(new BN(49750000)) + tradeHistoryAccount.tradeRecords[0].quoteAssetAmount.eq(new BN(49750001)) ); assert.ok(tradeHistoryAccount.tradeRecords[0].marketIndex.eq(marketIndex)); }); @@ -212,7 +212,7 @@ describe('clearing_house', () => { ); console.log(user.collateral.toString()); console.log(user.totalFeePaid.toString()); - assert.ok(user.collateral.eq(new BN(9926612))); + assert.ok(user.collateral.eq(new BN(9926611))); assert(user.totalFeePaid.eq(new BN(74626))); assert(user.cumulativeDeposits.eq(usdcAmount)); @@ -268,7 +268,7 @@ describe('clearing_house', () => { const userPositionsAccount: any = await clearingHouse.program.account.userPositions.fetch(user.positions); - assert.ok(user.collateral.eq(new BN(9875628))); + assert.ok(user.collateral.eq(new BN(9875627))); assert(user.totalFeePaid.eq(new BN(124371))); assert.ok( userPositionsAccount.positions[0].quoteAssetAmount.eq(new BN(24871287)) @@ -331,7 +331,7 @@ describe('clearing_house', () => { await clearingHouse.program.account.userPositions.fetch(user.positions); assert.ok(userPositionsAccount.positions[0].quoteAssetAmount.eq(new BN(0))); assert.ok(userPositionsAccount.positions[0].baseAssetAmount.eq(new BN(0))); - assert.ok(user.collateral.eq(new BN(9850756))); + assert.ok(user.collateral.eq(new BN(9850755))); assert(user.totalFeePaid.eq(new BN(149242))); const marketsAccount = clearingHouse.getMarketsAccount(); @@ -358,7 +358,7 @@ describe('clearing_house', () => { ); assert.ok(tradeHistoryAccount.tradeRecords[2].liquidation == false); assert.ok( - tradeHistoryAccount.tradeRecords[3].quoteAssetAmount.eq(new BN(24871287)) + tradeHistoryAccount.tradeRecords[3].quoteAssetAmount.eq(new BN(24871288)) ); assert.ok(tradeHistoryAccount.tradeRecords[3].marketIndex.eq(new BN(0))); }); diff --git a/tests/order.ts b/tests/order.ts index fc923cc5..1dc69525 100644 --- a/tests/order.ts +++ b/tests/order.ts @@ -420,7 +420,7 @@ describe('orders', () => { const firstPosition = userPositionsAccount.positions[0]; assert(firstPosition.baseAssetAmount.eq(baseAssetAmount)); - const expectedQuoteAssetAmount = new BN(1000002); + const expectedQuoteAssetAmount = new BN(1000003); // console.log(convertToNumber(firstPosition.quoteAssetAmount, QUOTE_PRECISION), // '!=', // convertToNumber(expectedQuoteAssetAmount, QUOTE_PRECISION), diff --git a/tests/roundInFavorBaseAsset.ts b/tests/roundInFavorBaseAsset.ts index f3f48f98..059908a6 100644 --- a/tests/roundInFavorBaseAsset.ts +++ b/tests/roundInFavorBaseAsset.ts @@ -184,7 +184,7 @@ describe('round in favor', () => { user = await primaryClearingHouse.program.account.user.fetch( userAccountPublicKey ); - assert(user.collateral.eq(new BN(9999000))); + assert(user.collateral.eq(new BN(9998999))); await clearingHouse.unsubscribe(); }); }); diff --git a/tests/stopLimits.ts b/tests/stopLimits.ts index 54c9ba3b..cc108f85 100644 --- a/tests/stopLimits.ts +++ b/tests/stopLimits.ts @@ -338,7 +338,7 @@ describe('stop limit', () => { const tradeHistoryRecord = tradeHistoryAccount.tradeRecords[3]; assert.ok(tradeHistoryRecord.baseAssetAmount.eq(baseAssetAmount)); - const expectedTradeQuoteAssetAmount = new BN(999998); + const expectedTradeQuoteAssetAmount = new BN(999999); assert.ok( tradeHistoryRecord.quoteAssetAmount.eq(expectedTradeQuoteAssetAmount) ); From 5868cc57c4ef9d50b0a4db84c38defbf2248778a Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 10 Mar 2022 11:30:28 -0500 Subject: [PATCH 17/59] repeg: use new oracle data and write budget function --- .../clearing_house/src/controller/funding.rs | 1 + .../clearing_house/src/controller/repeg.rs | 16 ++- programs/clearing_house/src/lib.rs | 2 +- programs/clearing_house/src/math/repeg.rs | 104 +++++++++++++++--- programs/clearing_house/src/state/market.rs | 2 +- 5 files changed, 101 insertions(+), 24 deletions(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 105d223b..3b0333f4 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -191,6 +191,7 @@ pub fn update_funding_rate( market.amm.last_funding_rate = funding_rate; market.amm.last_funding_rate_ts = now; + market.amm.net_revenue_since_last_funding = 0; let record_id = funding_rate_history.next_record_id(); funding_rate_history.append(FundingRateRecord { diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index 7cedf913..91970923 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -4,7 +4,7 @@ use crate::math::repeg; use crate::math::amm; use crate::math_error; -use crate::state::market::Market; +use crate::state::market::{Market, OraclePriceData, AMM}; use crate::state::state::OracleGuardRails; use anchor_lang::prelude::AccountInfo; @@ -85,10 +85,17 @@ pub fn repeg( pub fn formulaic_repeg( market: &mut Market, precomputed_mark_price: u128, - oracle_price: i128, - oracle_conf: u128, + oracle_price_data: &OraclePriceData, oracle_is_valid: bool, ) -> ClearingHouseResult { + let OraclePriceData { + price: oracle_price, + twap: oracle_twap, + confidence: oracle_conf, + twap_confidence: oracle_twap_conf, + delay: oracle_delay, + } = *oracle_price_data; + let terminal_price_before = amm::calculate_terminal_price(market)?; let oracle_terminal_spread_before = oracle_price .checked_sub(cast_to_i128(terminal_price_before)?) @@ -115,8 +122,7 @@ pub fn formulaic_repeg( _oracle_terminal_divergence_pct_after, ) = repeg::calculate_repeg_validity( repegged_market, - oracle_price, - oracle_conf, + oracle_price_data, oracle_is_valid, terminal_price_before, )?; diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 45552697..465244c9 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -310,7 +310,7 @@ pub mod clearing_house { last_oracle_price_twap_ts: now, last_oracle_price: oracle_price, minimum_base_asset_trade_size: 10000000, - padding1: 0, + net_revenue_since_last_funding: 0, padding2: 0, padding3: 0, }, diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 94e0bcfb..7a8be4d0 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -1,35 +1,34 @@ +use crate::controller::amm::SwapDirection; use crate::error::*; use crate::math::amm; +use crate::math::amm::calculate_swap_output; use crate::math::bn; use crate::math::casting::{cast_to_i128, cast_to_u128}; use crate::math::constants::{ - MARK_PRICE_PRECISION, PEG_PRECISION, PRICE_TO_PEG_PRECISION_RATIO, + AMM_TO_QUOTE_PRECISION_RATIO, MARK_PRICE_PRECISION, PEG_PRECISION, + PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, }; use crate::math::position::_calculate_base_asset_value_and_pnl; use crate::math_error; -use crate::state::market::Market; +use crate::state::market::{Market, OraclePriceData, AMM}; + use crate::state::state::OracleGuardRails; use anchor_lang::prelude::AccountInfo; use solana_program::msg; pub fn calculate_repeg_validity_full( market: &mut Market, - price_oracle: &AccountInfo, + oracle_account_info: &AccountInfo, terminal_price_before: u128, clock_slot: u64, oracle_guard_rails: &OracleGuardRails, ) -> ClearingHouseResult<(bool, bool, bool, bool, i128)> { - let (oracle_price, _oracle_twap, oracle_conf, _oracle_twac, _oracle_delay) = - market.amm.get_oracle_price(price_oracle, clock_slot)?; - - let oracle_is_valid = amm::is_oracle_valid( - &market.amm, - price_oracle, - clock_slot, - &oracle_guard_rails.validity, - )?; + let oracle_price_data = market + .amm + .get_oracle_price(oracle_account_info, clock_slot)?; + let oracle_is_valid = amm::is_oracle_valid(&oracle_price_data, &oracle_guard_rails.validity)?; let ( oracle_is_valid, @@ -39,8 +38,7 @@ pub fn calculate_repeg_validity_full( oracle_terminal_divergence_pct_after, ) = calculate_repeg_validity( market, - oracle_price, - oracle_conf, + &oracle_price_data, oracle_is_valid, terminal_price_before, )?; @@ -56,11 +54,18 @@ pub fn calculate_repeg_validity_full( pub fn calculate_repeg_validity( market: &mut Market, - oracle_price: i128, - oracle_conf: u128, + oracle_price_data: &OraclePriceData, oracle_is_valid: bool, terminal_price_before: u128, ) -> ClearingHouseResult<(bool, bool, bool, bool, i128)> { + let OraclePriceData { + price: oracle_price, + twap: oracle_twap, + confidence: oracle_conf, + twap_confidence: oracle_twap_conf, + delay: oracle_delay, + } = *oracle_price_data; + let oracle_price_u128 = cast_to_u128(oracle_price)?; let terminal_price_after = amm::calculate_terminal_price(market)?; @@ -147,7 +152,7 @@ pub fn calculate_peg_from_target_price( // m = y*C*PTPPR/x // C = m*x/y*PTPPR - return bn::U192::from(target_price) + let new_peg = bn::U192::from(target_price) .checked_mul(bn::U192::from(base_asset_reserve)) .ok_or_else(math_error!())? .checked_div(bn::U192::from(quote_asset_reserve)) @@ -155,6 +160,7 @@ pub fn calculate_peg_from_target_price( .checked_mul(bn::U192::from(PRICE_TO_PEG_PRECISION_RATIO)) .ok_or_else(math_error!())? .try_to_u128(); + Ok(new_peg) } pub fn calculate_optimal_peg_and_cost( @@ -165,6 +171,8 @@ pub fn calculate_optimal_peg_and_cost( ) -> ClearingHouseResult<(u128, i128, &mut Market)> { // does minimum valid repeg allowable iff satisfies the budget + // let fspr = (oracle - mark) - (oracle_twap - mark_twap) + let oracle_mark_spread = oracle_price .checked_sub(cast_to_i128(mark_price)?) .ok_or_else(math_error!())?; @@ -252,6 +260,68 @@ pub fn calculate_optimal_peg_and_cost( Ok((candidate_peg, candidate_cost, repegged_market)) } +pub fn calculate_budgeted_peg( + market: &mut Market, + budget: u128, + current_price: u128, + target_price: u128, +) -> ClearingHouseResult { + let order_swap_direction = if market.base_asset_amount > 0 { + SwapDirection::Add + } else { + SwapDirection::Remove + }; + + let (new_quote_asset_amount, _new_base_asset_amount) = calculate_swap_output( + market.base_asset_amount.unsigned_abs(), + market.amm.base_asset_reserve, + order_swap_direction, + market.amm.sqrt_k, + )?; + + let new_peg: u128 = if new_quote_asset_amount != market.amm.quote_asset_reserve { + let delta_quote_asset_reserves = market + .amm + .quote_asset_reserve + .checked_sub(new_quote_asset_amount) + .ok_or_else(math_error!())?; + + let delta_peg_multiplier = budget + .checked_mul(MARK_PRICE_PRECISION) + .ok_or_else(math_error!())? + .checked_div( + delta_quote_asset_reserves + .checked_div(AMM_TO_QUOTE_PRECISION_RATIO) + .ok_or_else(math_error!())?, + ) + .ok_or_else(math_error!())? + .checked_mul(PEG_PRECISION) + .ok_or_else(math_error!())? + .checked_div(QUOTE_PRECISION) + .ok_or_else(math_error!())?; + + let delta_peg_precision = delta_peg_multiplier + .checked_mul(PEG_PRECISION) + .ok_or_else(math_error!())? + .checked_div(MARK_PRICE_PRECISION) + .ok_or_else(math_error!())?; + + market + .amm + .peg_multiplier + .checked_sub(delta_peg_precision) + .ok_or_else(math_error!())? + } else { + calculate_peg_from_target_price( + market.amm.quote_asset_reserve, + market.amm.base_asset_reserve, + target_price, + )? + }; + + Ok(new_peg) +} + pub fn adjust_peg_cost( market: &mut Market, new_peg_candidate: u128, diff --git a/programs/clearing_house/src/state/market.rs b/programs/clearing_house/src/state/market.rs index 8626abea..94a6460d 100644 --- a/programs/clearing_house/src/state/market.rs +++ b/programs/clearing_house/src/state/market.rs @@ -93,7 +93,7 @@ pub struct AMM { pub minimum_base_asset_trade_size: u128, // upgrade-ability - pub padding1: u64, + pub net_revenue_since_last_funding: i64, pub padding2: u128, pub padding3: u128, } From a50ceb6ae16c469da73cf81e418b6b1b5882f56f Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Wed, 16 Mar 2022 17:24:57 -0400 Subject: [PATCH 18/59] formulaic repeg after fill order, start budget k --- .../clearing_house/src/controller/funding.rs | 28 +++++++++++++++++ .../clearing_house/src/controller/orders.rs | 21 ++++++++++--- .../clearing_house/src/controller/repeg.rs | 30 +++++++----------- programs/clearing_house/src/lib.rs | 5 +++ programs/clearing_house/src/math/repeg.rs | 31 +++++++++++++------ 5 files changed, 84 insertions(+), 31 deletions(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 3b0333f4..8f2b7792 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -177,6 +177,34 @@ pub fn update_funding_rate( let (funding_rate_long, funding_rate_short) = calculate_funding_rate_long_short(market, funding_rate)?; + // dynamic k + let funding_imbalance_cost = funding_rate + .checked_mul(market.base_asset_amount) + .ok_or_else(math_error!())? + .checked_div( + AMM_TO_QUOTE_PRECISION_RATIO_I128 * cast_to_i128(FUNDING_PAYMENT_PRECISION)?, + ) + .ok_or_else(math_error!())?; + + let budget = if funding_imbalance_cost < 0 { + funding_imbalance_cost + .checked_div(2) + .ok_or_else(math_error!())? + } else if market.amm.net_revenue_since_last_funding < (funding_imbalance_cost as i64) { + max(0, market.amm.net_revenue_since_last_funding) + .checked_sub(funding_imbalance_cost as i64) + .ok_or_else(math_error!())? + .checked_div(2) + .ok_or_else(math_error!())? as i128 + } else { + 0 + }; + + if budget != 0 { + msg!("todo"); + // controller::amm::budget_k_adjustment() + } + market.amm.cumulative_funding_rate_long = market .amm .cumulative_funding_rate_long diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index 990e9e1c..ebdcfabc 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -11,6 +11,7 @@ use std::cmp::min; use crate::context::*; use crate::math::{amm, fees, margin::*, orders::*}; +use crate::state::market::OraclePriceData; use crate::state::{ history::order_history::{OrderHistory, OrderRecord}, history::trade::{TradeHistory, TradeRecord}, @@ -354,24 +355,25 @@ pub fn fill_order( let oracle_mark_spread_pct_before: i128; let is_oracle_valid: bool; let oracle_price: i128; + let oracle_price_data: OraclePriceData; { let markets = &mut markets .load_mut() .or(Err(ErrorCode::UnableToLoadAccountLoader))?; let market = markets.get_market_mut(market_index); mark_price_before = market.amm.mark_price()?; - let oracle_price_data = &market.amm.get_oracle_price(oracle, clock_slot)?; + oracle_price_data = market.amm.get_oracle_price(oracle, clock_slot)?; oracle_mark_spread_pct_before = amm::calculate_oracle_mark_spread_pct( &market.amm, - oracle_price_data, + &oracle_price_data, 0, Some(mark_price_before), )?; oracle_price = oracle_price_data.price; let normalised_price = - normalise_oracle_price(&market.amm, oracle_price_data, Some(mark_price_before))?; + normalise_oracle_price(&market.amm, &oracle_price_data, Some(mark_price_before))?; is_oracle_valid = - amm::is_oracle_valid(oracle_price_data, &state.oracle_guard_rails.validity)?; + amm::is_oracle_valid(&oracle_price_data, &state.oracle_guard_rails.validity)?; if is_oracle_valid { amm::update_oracle_price_twap(&mut market.amm, now, normalised_price)?; } @@ -608,6 +610,17 @@ pub fn fill_order( state.funding_paused, Some(mark_price_before), )?; + + if market_index == 12 { + // todo for soft launch + controller::repeg::formulaic_repeg( + market, + mark_price_after, + &oracle_price_data, + is_oracle_valid, + fee_to_market, + )?; + } } Ok(base_asset_amount) diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index 91970923..3ce40f16 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -86,7 +86,8 @@ pub fn formulaic_repeg( market: &mut Market, precomputed_mark_price: u128, oracle_price_data: &OraclePriceData, - oracle_is_valid: bool, + is_oracle_valid: bool, + budget: u128, ) -> ClearingHouseResult { let OraclePriceData { price: oracle_price, @@ -100,22 +101,16 @@ pub fn formulaic_repeg( let oracle_terminal_spread_before = oracle_price .checked_sub(cast_to_i128(terminal_price_before)?) .ok_or_else(math_error!())?; - // let oracle_terminal_divergence_pct_before = oracle_terminal_spread_before - // .checked_shl(10) - // .ok_or_else(math_error!())? - // .checked_div(oracle_price) - // .ok_or_else(math_error!())?; - - let (new_peg_candidate, adjustment_cost, repegged_market) = - repeg::calculate_optimal_peg_and_cost( - market, - oracle_price, - precomputed_mark_price, - terminal_price_before, - )?; + + let (new_peg_candidate, adjustment_cost, repegged_market) = repeg::calculate_budgeted_peg( + market, + budget, + precomputed_mark_price, + cast_to_u128(oracle_price)?, + )?; let ( - oracle_is_valid, + oracle_valid, direction_valid, profitability_valid, price_impact_valid, @@ -123,17 +118,16 @@ pub fn formulaic_repeg( ) = repeg::calculate_repeg_validity( repegged_market, oracle_price_data, - oracle_is_valid, + is_oracle_valid, terminal_price_before, )?; - if oracle_is_valid && direction_valid && profitability_valid && price_impact_valid { + if oracle_valid && direction_valid && profitability_valid && price_impact_valid { if adjustment_cost > 0 { let new_total_fee_minus_distributions = market .amm .total_fee_minus_distributions .checked_sub(adjustment_cost.unsigned_abs()) - .or(Some(0)) .ok_or_else(math_error!())?; if new_total_fee_minus_distributions >= repeg::total_fee_lower_bound(&market)? { diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index a84e4253..54121d6d 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -641,6 +641,11 @@ pub mod clearing_house { .total_fee_minus_distributions .checked_add(fee_to_market) .ok_or_else(math_error!())?; + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(fee_to_market as i64) + .ok_or_else(math_error!())?; } // Subtract the fee from user's collateral diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 7a8be4d0..7a9e9271 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -13,6 +13,7 @@ use crate::math::constants::{ use crate::math::position::_calculate_base_asset_value_and_pnl; use crate::math_error; use crate::state::market::{Market, OraclePriceData, AMM}; +use std::cmp::{max, min}; use crate::state::state::OracleGuardRails; use anchor_lang::prelude::AccountInfo; @@ -159,7 +160,7 @@ pub fn calculate_peg_from_target_price( .ok_or_else(math_error!())? .checked_mul(bn::U192::from(PRICE_TO_PEG_PRECISION_RATIO)) .ok_or_else(math_error!())? - .try_to_u128(); + .try_to_u128()?; Ok(new_peg) } @@ -265,7 +266,9 @@ pub fn calculate_budgeted_peg( budget: u128, current_price: u128, target_price: u128, -) -> ClearingHouseResult { +) -> ClearingHouseResult<(u128, i128, &mut Market)> { + // calculates peg_multiplier that changing to would cost no more than budget + let order_swap_direction = if market.base_asset_amount > 0 { SwapDirection::Add } else { @@ -279,7 +282,13 @@ pub fn calculate_budgeted_peg( market.amm.sqrt_k, )?; - let new_peg: u128 = if new_quote_asset_amount != market.amm.quote_asset_reserve { + let optimal_peg = calculate_peg_from_target_price( + market.amm.quote_asset_reserve, + market.amm.base_asset_reserve, + target_price, + )?; + + let full_budget_peg: u128 = if new_quote_asset_amount != market.amm.quote_asset_reserve { let delta_quote_asset_reserves = market .amm .quote_asset_reserve @@ -312,14 +321,18 @@ pub fn calculate_budgeted_peg( .checked_sub(delta_peg_precision) .ok_or_else(math_error!())? } else { - calculate_peg_from_target_price( - market.amm.quote_asset_reserve, - market.amm.base_asset_reserve, - target_price, - )? + optimal_peg }; - Ok(new_peg) + let candidate_peg: u128 = if current_price > target_price { + min(full_budget_peg, optimal_peg) + } else { + max(full_budget_peg, optimal_peg) + }; + + let (repegged_market, candidate_cost) = adjust_peg_cost(market, optimal_peg)?; + + Ok((candidate_peg, candidate_cost, repegged_market)) } pub fn adjust_peg_cost( From 84c19ca972e14a74139e27485bf54f27cefddb29 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Sun, 20 Mar 2022 16:41:41 -0400 Subject: [PATCH 19/59] merged switchboard, fmt --- .../clearing_house/src/controller/funding.rs | 2 +- .../clearing_house/src/controller/orders.rs | 8 ++-- .../clearing_house/src/controller/repeg.rs | 3 +- programs/clearing_house/src/math/amm.rs | 39 +++++++++++++++++++ programs/clearing_house/src/math/repeg.rs | 11 ++++-- 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 8f2b7792..c7766e44 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -202,7 +202,7 @@ pub fn update_funding_rate( if budget != 0 { msg!("todo"); - // controller::amm::budget_k_adjustment() + // let (p_numer, p_denom) = controller::amm::budget_k_adjustment(market, budget); } market.amm.cumulative_funding_rate_long = market diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index fb838418..81036f2d 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -366,14 +366,16 @@ pub fn fill_order( oracle_mark_spread_pct_before = amm::calculate_oracle_mark_spread_pct( &market.amm, &oracle_price_data, - 0, Some(mark_price_before), )?; oracle_price = oracle_price_data.price; let normalised_price = normalise_oracle_price(&market.amm, &oracle_price_data, Some(mark_price_before))?; - is_oracle_valid = - amm::is_oracle_valid(&oracle_price_data, &state.oracle_guard_rails.validity)?; + is_oracle_valid = amm::is_oracle_valid( + &market.amm, + &oracle_price_data, + &state.oracle_guard_rails.validity, + )?; if is_oracle_valid { amm::update_oracle_price_twap(&mut market.amm, now, normalised_price)?; } diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index 3ce40f16..6a62df80 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -91,10 +91,9 @@ pub fn formulaic_repeg( ) -> ClearingHouseResult { let OraclePriceData { price: oracle_price, - twap: oracle_twap, confidence: oracle_conf, - twap_confidence: oracle_twap_conf, delay: oracle_delay, + has_sufficient_number_of_data_points: has_sufficient_number_of_data_points, } = *oracle_price_data; let terminal_price_before = amm::calculate_terminal_price(market)?; diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 4069a736..bfdfd92b 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -446,6 +446,45 @@ pub fn is_oracle_valid( || is_conf_too_large)) } +// pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseResult<(i128, i128)> { +// let y = market.amm.quote_asset_reserve; +// let x = market.amm.base_asset_reserve; +// let C = budget; +// let Q = market.amm.peg_multiplier; +// let d = market.base_asset_amount; + +// let numer1 = y +// .mul(d) +// .mul(Q) +// .div(AMM_RESERVE_PRECISION) +// .div(PEG_PRECISION); +// let numer2 = C.mul(x.add(d)).div(QUOTE_PRECISION); +// let denom1 = C +// .mul(x) +// .mul(x.add(d)) +// .div(AMM_RESERVE_PRECISION) +// .div(QUOTE_PRECISION); +// let denom2 = y +// .mul(d) +// .mul(d) +// .mul(Q) +// .div(AMM_RESERVE_PRECISION) +// .div(AMM_RESERVE_PRECISION) +// .div(PEG_PRECISION); + +// let numerator = d +// .mul(numer1.add(numer2)) +// .div(AMM_RESERVE_PRECISION) +// .div(AMM_RESERVE_PRECISION) +// .div(AMM_TO_QUOTE_PRECISION_RATIO); +// let denominator = denom1 +// .add(denom2) +// .div(AMM_RESERVE_PRECISION) +// .div(AMM_TO_QUOTE_PRECISION_RATIO); + +// Ok((numerator, denominator)) +// } + /// To find the cost of adjusting k, compare the the net market value before and after adjusting k /// Increasing k costs the protocol money because it reduces slippage and improves the exit price for net market position /// Decreasing k costs the protocol money because it increases slippage and hurts the exit price for net market position diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 7a9e9271..cc8f0d3c 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -29,7 +29,11 @@ pub fn calculate_repeg_validity_full( let oracle_price_data = market .amm .get_oracle_price(oracle_account_info, clock_slot)?; - let oracle_is_valid = amm::is_oracle_valid(&oracle_price_data, &oracle_guard_rails.validity)?; + let oracle_is_valid = amm::is_oracle_valid( + &market.amm, + &oracle_price_data, + &oracle_guard_rails.validity, + )?; let ( oracle_is_valid, @@ -61,10 +65,9 @@ pub fn calculate_repeg_validity( ) -> ClearingHouseResult<(bool, bool, bool, bool, i128)> { let OraclePriceData { price: oracle_price, - twap: oracle_twap, confidence: oracle_conf, - twap_confidence: oracle_twap_conf, - delay: oracle_delay, + delay: _, + has_sufficient_number_of_data_points: _, } = *oracle_price_data; let oracle_price_u128 = cast_to_u128(oracle_price)?; From 5ad3c1ed22e75076c93713c4c16f7a22d2b6b9da Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Sun, 20 Mar 2022 22:53:04 -0400 Subject: [PATCH 20/59] add budget k --- .../clearing_house/src/controller/orders.rs | 5 + .../clearing_house/src/controller/repeg.rs | 18 ++- programs/clearing_house/src/lib.rs | 10 ++ programs/clearing_house/src/math/amm.rs | 106 +++++++++++------- programs/clearing_house/src/math/funding.rs | 10 ++ sdk/src/idl/clearing_house.json | 42 +++---- test-scripts/run-anchor-tests.sh | 1 + tests/formulaCurve.ts | 92 +++++++++++++++ 8 files changed, 222 insertions(+), 62 deletions(-) create mode 100644 tests/formulaCurve.ts diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index 81036f2d..8b975da1 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -489,6 +489,11 @@ pub fn fill_order( .total_fee_minus_distributions .checked_add(fee_to_market) .ok_or_else(math_error!())?; + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(fee_to_market as i64) + .ok_or_else(math_error!())?; } // Subtract the fee from user's collateral diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index 6a62df80..af756c8b 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -61,7 +61,6 @@ pub fn repeg( .amm .total_fee_minus_distributions .checked_sub(adjustment_cost.unsigned_abs()) - .or(Some(0)) .ok_or_else(math_error!())?; // Only a portion of the protocol fees are allocated to repegging @@ -77,6 +76,12 @@ pub fn repeg( .ok_or_else(math_error!())?; } + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(adjustment_cost as i64) + .ok_or_else(math_error!())?; + market.amm.peg_multiplier = new_peg_candidate; Ok(adjustment_cost) @@ -131,6 +136,11 @@ pub fn formulaic_repeg( if new_total_fee_minus_distributions >= repeg::total_fee_lower_bound(&market)? { market.amm.total_fee_minus_distributions = new_total_fee_minus_distributions; + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(adjustment_cost as i64) + .ok_or_else(math_error!())?; market.amm.peg_multiplier = new_peg_candidate; } } else { @@ -140,6 +150,12 @@ pub fn formulaic_repeg( .checked_add(adjustment_cost.unsigned_abs()) .ok_or_else(math_error!())?; + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(adjustment_cost as i64) + .ok_or_else(math_error!())?; + market.amm.peg_multiplier = new_peg_candidate; } } diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 64f1392d..2ac469ef 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -2051,6 +2051,11 @@ pub mod clearing_house { .total_fee_minus_distributions .checked_sub(adjustment_cost.unsigned_abs()) .ok_or_else(math_error!())?; + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(adjustment_cost as i64) + .ok_or_else(math_error!())?; } } else { market.amm.total_fee_minus_distributions = market @@ -2058,6 +2063,11 @@ pub mod clearing_house { .total_fee_minus_distributions .checked_add(adjustment_cost.unsigned_abs()) .ok_or_else(math_error!())?; + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(adjustment_cost as i64) + .ok_or_else(math_error!())?; } let amm = &market.amm; diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index bfdfd92b..8d2e2f7e 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -9,8 +9,9 @@ use crate::math::bn; use crate::math::bn::U192; use crate::math::casting::{cast, cast_to_i128, cast_to_u128}; use crate::math::constants::{ - MARK_PRICE_PRECISION, PEG_PRECISION, PRICE_SPREAD_PRECISION, PRICE_SPREAD_PRECISION_U128, - PRICE_TO_PEG_PRECISION_RATIO, + AMM_RESERVE_PRECISION, AMM_TO_QUOTE_PRECISION_RATIO, MARK_PRICE_PRECISION, PEG_PRECISION, + PRICE_SPREAD_PRECISION, PRICE_SPREAD_PRECISION_U128, PRICE_TO_PEG_PRECISION_RATIO, + QUOTE_PRECISION, }; use crate::math::position::_calculate_base_asset_value_and_pnl; use crate::math::quote_asset::{asset_to_reserve_amount, reserve_to_asset_amount}; @@ -446,44 +447,69 @@ pub fn is_oracle_valid( || is_conf_too_large)) } -// pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseResult<(i128, i128)> { -// let y = market.amm.quote_asset_reserve; -// let x = market.amm.base_asset_reserve; -// let C = budget; -// let Q = market.amm.peg_multiplier; -// let d = market.base_asset_amount; - -// let numer1 = y -// .mul(d) -// .mul(Q) -// .div(AMM_RESERVE_PRECISION) -// .div(PEG_PRECISION); -// let numer2 = C.mul(x.add(d)).div(QUOTE_PRECISION); -// let denom1 = C -// .mul(x) -// .mul(x.add(d)) -// .div(AMM_RESERVE_PRECISION) -// .div(QUOTE_PRECISION); -// let denom2 = y -// .mul(d) -// .mul(d) -// .mul(Q) -// .div(AMM_RESERVE_PRECISION) -// .div(AMM_RESERVE_PRECISION) -// .div(PEG_PRECISION); - -// let numerator = d -// .mul(numer1.add(numer2)) -// .div(AMM_RESERVE_PRECISION) -// .div(AMM_RESERVE_PRECISION) -// .div(AMM_TO_QUOTE_PRECISION_RATIO); -// let denominator = denom1 -// .add(denom2) -// .div(AMM_RESERVE_PRECISION) -// .div(AMM_TO_QUOTE_PRECISION_RATIO); - -// Ok((numerator, denominator)) -// } +pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseResult<(i128, i128)> { + let y = market.amm.quote_asset_reserve; + let x = market.amm.base_asset_reserve; + let c = budget; + let q = cast_to_i128(market.amm.peg_multiplier)?; + let d = market.base_asset_amount; + + let AMM_RESERVE_PRECISIONi128 = cast_to_i128(AMM_RESERVE_PRECISION)?; + + let x_d = cast_to_i128(x)?.checked_add(d).ok_or_else(math_error!())?; + + let numer1 = cast_to_i128(y)? + .checked_mul(d) + .ok_or_else(math_error!())? + .checked_mul(q) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128(AMM_RESERVE_PRECISION * PEG_PRECISION)?) + .ok_or_else(math_error!())?; + let numer2 = c + .checked_mul(x_d) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128(QUOTE_PRECISION)?) + .ok_or_else(math_error!())?; + let denom1 = c + .checked_mul(cast_to_i128(x)?) + .ok_or_else(math_error!())? + .checked_mul(x_d) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128(AMM_RESERVE_PRECISION * QUOTE_PRECISION)?) + .ok_or_else(math_error!())?; + let denom2 = cast_to_i128(y)? + .checked_mul(d) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())? + .checked_mul(d) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())? + .checked_mul(q) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128(PEG_PRECISION)?) + .ok_or_else(math_error!())?; + + let numerator = d + .checked_mul(numer1.checked_add(numer2).ok_or_else(math_error!())?) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128(AMM_TO_QUOTE_PRECISION_RATIO)?) + .ok_or_else(math_error!())?; + let denominator = denom1 + .checked_add(denom2) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())?; + + Ok((numerator, denominator)) +} /// To find the cost of adjusting k, compare the the net market value before and after adjusting k /// Increasing k costs the protocol money because it reduces slippage and improves the exit price for net market position diff --git a/programs/clearing_house/src/math/funding.rs b/programs/clearing_house/src/math/funding.rs index dbbcc324..b8f0976c 100644 --- a/programs/clearing_house/src/math/funding.rs +++ b/programs/clearing_house/src/math/funding.rs @@ -33,6 +33,11 @@ pub fn calculate_funding_rate_long_short( .total_fee_minus_distributions .checked_add(uncapped_funding_pnl.unsigned_abs()) .ok_or_else(math_error!())?; + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(uncapped_funding_pnl as i64) + .ok_or_else(math_error!())?; return Ok((funding_rate, funding_rate)); } @@ -56,6 +61,11 @@ pub fn calculate_funding_rate_long_short( } market.amm.total_fee_minus_distributions = new_total_fee_minus_distributions; + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(uncapped_funding_pnl as i64) + .ok_or_else(math_error!())?; let funding_rate_long = if funding_rate < 0 { capped_funding_rate diff --git a/sdk/src/idl/clearing_house.json b/sdk/src/idl/clearing_house.json index e57dd213..3d1e777f 100644 --- a/sdk/src/idl/clearing_house.json +++ b/sdk/src/idl/clearing_house.json @@ -3190,8 +3190,8 @@ "type": "u128" }, { - "name": "padding1", - "type": "u64" + "name": "netRevenueSinceLastFunding", + "type": "i64" }, { "name": "padding2", @@ -4088,97 +4088,97 @@ "msg": "Casting Failure" }, { - "code": 6039, + "code": 6040, "name": "InvalidOrder", "msg": "Invalid Order" }, { - "code": 6040, + "code": 6041, "name": "UserHasNoOrder", "msg": "User has no order" }, { - "code": 6041, + "code": 6042, "name": "OrderAmountTooSmall", "msg": "Order Amount Too Small" }, { - "code": 6042, + "code": 6043, "name": "MaxNumberOfOrders", "msg": "Max number of orders taken" }, { - "code": 6043, + "code": 6044, "name": "OrderDoesNotExist", "msg": "Order does not exist" }, { - "code": 6044, + "code": 6045, "name": "OrderNotOpen", "msg": "Order not open" }, { - "code": 6045, + "code": 6046, "name": "CouldNotFillOrder", "msg": "CouldNotFillOrder" }, { - "code": 6046, + "code": 6047, "name": "ReduceOnlyOrderIncreasedRisk", "msg": "Reduce only order increased risk" }, { - "code": 6047, + "code": 6048, "name": "OrderStateAlreadyInitialized", "msg": "Order state already initialized" }, { - "code": 6048, + "code": 6049, "name": "UnableToLoadAccountLoader", "msg": "Unable to load AccountLoader" }, { - "code": 6049, + "code": 6050, "name": "TradeSizeTooLarge", "msg": "Trade Size Too Large" }, { - "code": 6050, + "code": 6051, "name": "UnableToWriteToRemainingAccount", "msg": "Unable to write to remaining account" }, { - "code": 6051, + "code": 6052, "name": "UserCantReferThemselves", "msg": "User cant refer themselves" }, { - "code": 6052, + "code": 6053, "name": "DidNotReceiveExpectedReferrer", "msg": "Did not receive expected referrer" }, { - "code": 6053, + "code": 6054, "name": "CouldNotDeserializeReferrer", "msg": "Could not deserialize referrer" }, { - "code": 6054, + "code": 6055, "name": "MarketOrderMustBeInPlaceAndFill", "msg": "Market order must be in place and fill" }, { - "code": 6055, + "code": 6056, "name": "UserOrderIdAlreadyInUse", "msg": "User Order Id Already In Use" }, { - "code": 6056, + "code": 6057, "name": "NoPositionsLiquidatable", "msg": "No positions liquidatable" }, { - "code": 6057, + "code": 6058, "name": "InvalidMarginRatio", "msg": "Invalid Margin Ratio" } diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 5287d108..37545720 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -5,6 +5,7 @@ if [ "$1" != "--skip-build" ] fi test_files=(order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts deleteUser.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) +test_files=(formulaCurve.ts) for test_file in ${test_files[@]}; do export ANCHOR_TEST_FILE=${test_file} && anchor test --skip-build || exit 1; diff --git a/tests/formulaCurve.ts b/tests/formulaCurve.ts new file mode 100644 index 00000000..a315acbe --- /dev/null +++ b/tests/formulaCurve.ts @@ -0,0 +1,92 @@ +import * as anchor from '@project-serum/anchor'; +import { assert } from 'chai'; +import { BN } from '../sdk'; + +import { Keypair } from '@solana/web3.js'; +import { Program } from '@project-serum/anchor'; +import { + Admin, + MARK_PRICE_PRECISION, + calculateMarkPrice, + ClearingHouseUser, + PEG_PRECISION, + PositionDirection, + convertToNumber, +} from '../sdk/src'; + +import { Markets } from '../sdk/src/constants/markets'; + +import { + createPriceFeed, + mockUSDCMint, + mockUserUSDCAccount, +} from './testHelpers'; +import { QUOTE_PRECISION } from '../sdk/lib'; + +const ZERO = new BN(0); + +describe('update k', () => { + const provider = anchor.Provider.local(); + const connection = provider.connection; + anchor.setProvider(provider); + const chProgram = anchor.workspace.ClearingHouse as Program; + + let clearingHouse: Admin; + + let usdcMint: Keypair; + let userUSDCAccount: Keypair; + const initialSOLPrice = 150; + + // ammInvariant == k == x * y + const mantissaSqrtScale = new BN(Math.sqrt(MARK_PRICE_PRECISION.toNumber())); + const ammInitialQuoteAssetReserve = new anchor.BN(5 * 10 ** 13).mul( + mantissaSqrtScale + ); + const ammInitialBaseAssetReserve = new anchor.BN(5 * 10 ** 13).mul( + mantissaSqrtScale + ); + const usdcAmount = new BN(1e9 * 10 ** 6); + + let userAccount: ClearingHouseUser; + + before(async () => { + usdcMint = await mockUSDCMint(provider); + userUSDCAccount = await mockUserUSDCAccount(usdcMint, usdcAmount, provider); + + clearingHouse = Admin.from( + connection, + provider.wallet, + chProgram.programId + ); + await clearingHouse.initialize(usdcMint.publicKey, true); + await clearingHouse.subscribe(); + + const periodicity = new BN(60 * 60); // 1 HOUR + + const solUsdOracle = await createPriceFeed({ + oracleProgram: anchor.workspace.Pyth, + initPrice: initialSOLPrice, + }); + + await clearingHouse.initializeMarket( + Markets[0].marketIndex, + solUsdOracle, + ammInitialBaseAssetReserve, + ammInitialQuoteAssetReserve, + periodicity, + new BN(initialSOLPrice * PEG_PRECISION.toNumber()) + ); + + await clearingHouse.initializeUserAccount(); + userAccount = ClearingHouseUser.from( + clearingHouse, + provider.wallet.publicKey + ); + await userAccount.subscribe(); + }); + + after(async () => { + await clearingHouse.unsubscribe(); + await userAccount.unsubscribe(); + }); +}); From 05cdffe800c2f16d994f00f7f4ce23de318da0fe Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Mon, 21 Mar 2022 14:32:54 -0400 Subject: [PATCH 21/59] add formulaPeg and formualK tests --- programs/clearing_house/src/lib.rs | 6 +- programs/clearing_house/src/math/repeg.rs | 101 +++++-- test-scripts/run-anchor-tests.sh | 2 +- tests/formulaCurve.ts | 92 ------ tests/formulaK.ts | 226 ++++++++++++++ tests/formulaPeg.ts | 347 ++++++++++++++++++++++ 6 files changed, 653 insertions(+), 121 deletions(-) delete mode 100644 tests/formulaCurve.ts create mode 100644 tests/formulaK.ts create mode 100644 tests/formulaPeg.ts diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 2ac469ef..38fa3ed7 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -851,7 +851,11 @@ pub mod clearing_house { .total_fee_minus_distributions .checked_add(fee_to_market) .ok_or_else(math_error!())?; - + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(fee_to_market as i64) + .ok_or_else(math_error!())?; // Subtract the fee from user's collateral user.collateral = user.collateral.checked_sub(user_fee).or(Some(0)).unwrap(); diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index cc8f0d3c..2709708d 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -161,7 +161,7 @@ pub fn calculate_peg_from_target_price( .ok_or_else(math_error!())? .checked_div(bn::U192::from(quote_asset_reserve)) .ok_or_else(math_error!())? - .checked_mul(bn::U192::from(PRICE_TO_PEG_PRECISION_RATIO)) + .checked_div(bn::U192::from(PRICE_TO_PEG_PRECISION_RATIO)) .ok_or_else(math_error!())? .try_to_u128()?; Ok(new_peg) @@ -272,6 +272,8 @@ pub fn calculate_budgeted_peg( ) -> ClearingHouseResult<(u128, i128, &mut Market)> { // calculates peg_multiplier that changing to would cost no more than budget + let old_peg = market.amm.peg_multiplier; + let order_swap_direction = if market.base_asset_amount > 0 { SwapDirection::Add } else { @@ -292,11 +294,23 @@ pub fn calculate_budgeted_peg( )?; let full_budget_peg: u128 = if new_quote_asset_amount != market.amm.quote_asset_reserve { - let delta_quote_asset_reserves = market - .amm - .quote_asset_reserve - .checked_sub(new_quote_asset_amount) - .ok_or_else(math_error!())?; + let delta_peg_sign = if market.amm.quote_asset_reserve > new_quote_asset_amount { + 1 + } else { + -1 + }; + + let delta_quote_asset_reserves = if delta_peg_sign > 0 { + market + .amm + .quote_asset_reserve + .checked_sub(new_quote_asset_amount) + .ok_or_else(math_error!())? + } else { + new_quote_asset_amount + .checked_sub(market.amm.quote_asset_reserve) + .ok_or_else(math_error!())? + }; let delta_peg_multiplier = budget .checked_mul(MARK_PRICE_PRECISION) @@ -318,22 +332,50 @@ pub fn calculate_budgeted_peg( .checked_div(MARK_PRICE_PRECISION) .ok_or_else(math_error!())?; - market - .amm - .peg_multiplier - .checked_sub(delta_peg_precision) - .ok_or_else(math_error!())? + let new_budget_peg = if delta_peg_sign > 0 { + market + .amm + .peg_multiplier + .checked_sub(delta_peg_precision) + .ok_or_else(math_error!())? + } else { + market + .amm + .peg_multiplier + .checked_add(delta_peg_precision) + .ok_or_else(math_error!())? + }; + + // considers free pegs that act againist net market + let new_budget_peg_or_free = if (delta_peg_sign > 0 && optimal_peg < new_budget_peg) + || (delta_peg_sign < 0 && optimal_peg > new_budget_peg) + { + optimal_peg + } else { + new_budget_peg + }; + + new_budget_peg_or_free } else { optimal_peg }; - let candidate_peg: u128 = if current_price > target_price { - min(full_budget_peg, optimal_peg) + // msg!("PEG:: fullbudget: {:?}, optimal: {:?}",full_budget_peg, optimal_peg); + // assert_eq!(optimal_peg < 200000, true); + + // avoid overshooting budget past target + let candidate_peg: u128 = if current_price > target_price && full_budget_peg < optimal_peg { + optimal_peg + } else if current_price < target_price && full_budget_peg > optimal_peg { + optimal_peg } else { - max(full_budget_peg, optimal_peg) + full_budget_peg }; - let (repegged_market, candidate_cost) = adjust_peg_cost(market, optimal_peg)?; + let (repegged_market, candidate_cost) = adjust_peg_cost(market, candidate_peg)?; + // msg!("{:?} for {:?}", candidate_peg, candidate_cost); + // assert_eq!(candidate_cost <= 0 && candidate_peg != old_peg, true); + // assert_eq!(candidate_cost >= 0 && candidate_peg == old_peg, true); Ok((candidate_peg, candidate_cost, repegged_market)) } @@ -344,20 +386,25 @@ pub fn adjust_peg_cost( ) -> ClearingHouseResult<(&mut Market, i128)> { let market_deep_copy = market; - // Find the net market value before adjusting peg - let (current_net_market_value, _) = _calculate_base_asset_value_and_pnl( - market_deep_copy.base_asset_amount, - 0, - &market_deep_copy.amm, - )?; + let cost = if new_peg_candidate != market_deep_copy.amm.peg_multiplier { + // Find the net market value before adjusting peg + let (current_net_market_value, _) = _calculate_base_asset_value_and_pnl( + market_deep_copy.base_asset_amount, + 0, + &market_deep_copy.amm, + )?; - market_deep_copy.amm.peg_multiplier = new_peg_candidate; + market_deep_copy.amm.peg_multiplier = new_peg_candidate; - let (_new_net_market_value, cost) = _calculate_base_asset_value_and_pnl( - market_deep_copy.base_asset_amount, - current_net_market_value, - &market_deep_copy.amm, - )?; + let (_new_net_market_value, cost) = _calculate_base_asset_value_and_pnl( + market_deep_copy.base_asset_amount, + current_net_market_value, + &market_deep_copy.amm, + )?; + cost + } else { + 0_i128 + }; Ok((market_deep_copy, cost)) } diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 37545720..a2467919 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -5,7 +5,7 @@ if [ "$1" != "--skip-build" ] fi test_files=(order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts deleteUser.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) -test_files=(formulaCurve.ts) +test_files=(formulaPeg.ts) for test_file in ${test_files[@]}; do export ANCHOR_TEST_FILE=${test_file} && anchor test --skip-build || exit 1; diff --git a/tests/formulaCurve.ts b/tests/formulaCurve.ts deleted file mode 100644 index a315acbe..00000000 --- a/tests/formulaCurve.ts +++ /dev/null @@ -1,92 +0,0 @@ -import * as anchor from '@project-serum/anchor'; -import { assert } from 'chai'; -import { BN } from '../sdk'; - -import { Keypair } from '@solana/web3.js'; -import { Program } from '@project-serum/anchor'; -import { - Admin, - MARK_PRICE_PRECISION, - calculateMarkPrice, - ClearingHouseUser, - PEG_PRECISION, - PositionDirection, - convertToNumber, -} from '../sdk/src'; - -import { Markets } from '../sdk/src/constants/markets'; - -import { - createPriceFeed, - mockUSDCMint, - mockUserUSDCAccount, -} from './testHelpers'; -import { QUOTE_PRECISION } from '../sdk/lib'; - -const ZERO = new BN(0); - -describe('update k', () => { - const provider = anchor.Provider.local(); - const connection = provider.connection; - anchor.setProvider(provider); - const chProgram = anchor.workspace.ClearingHouse as Program; - - let clearingHouse: Admin; - - let usdcMint: Keypair; - let userUSDCAccount: Keypair; - const initialSOLPrice = 150; - - // ammInvariant == k == x * y - const mantissaSqrtScale = new BN(Math.sqrt(MARK_PRICE_PRECISION.toNumber())); - const ammInitialQuoteAssetReserve = new anchor.BN(5 * 10 ** 13).mul( - mantissaSqrtScale - ); - const ammInitialBaseAssetReserve = new anchor.BN(5 * 10 ** 13).mul( - mantissaSqrtScale - ); - const usdcAmount = new BN(1e9 * 10 ** 6); - - let userAccount: ClearingHouseUser; - - before(async () => { - usdcMint = await mockUSDCMint(provider); - userUSDCAccount = await mockUserUSDCAccount(usdcMint, usdcAmount, provider); - - clearingHouse = Admin.from( - connection, - provider.wallet, - chProgram.programId - ); - await clearingHouse.initialize(usdcMint.publicKey, true); - await clearingHouse.subscribe(); - - const periodicity = new BN(60 * 60); // 1 HOUR - - const solUsdOracle = await createPriceFeed({ - oracleProgram: anchor.workspace.Pyth, - initPrice: initialSOLPrice, - }); - - await clearingHouse.initializeMarket( - Markets[0].marketIndex, - solUsdOracle, - ammInitialBaseAssetReserve, - ammInitialQuoteAssetReserve, - periodicity, - new BN(initialSOLPrice * PEG_PRECISION.toNumber()) - ); - - await clearingHouse.initializeUserAccount(); - userAccount = ClearingHouseUser.from( - clearingHouse, - provider.wallet.publicKey - ); - await userAccount.subscribe(); - }); - - after(async () => { - await clearingHouse.unsubscribe(); - await userAccount.unsubscribe(); - }); -}); diff --git a/tests/formulaK.ts b/tests/formulaK.ts new file mode 100644 index 00000000..83535452 --- /dev/null +++ b/tests/formulaK.ts @@ -0,0 +1,226 @@ +import * as anchor from '@project-serum/anchor'; +import { assert } from 'chai'; +import { BN, FUNDING_PAYMENT_PRECISION } from '../sdk'; + +import { Keypair } from '@solana/web3.js'; +import { Program } from '@project-serum/anchor'; +import { + Admin, + MARK_PRICE_PRECISION, + calculateMarkPrice, + ClearingHouseUser, + PEG_PRECISION, + PositionDirection, + convertToNumber, +} from '../sdk/src'; + +import { Markets } from '../sdk/src/constants/markets'; + +import { + createPriceFeed, + mockUSDCMint, + mockUserUSDCAccount, + setFeedPrice, + getFeedData, +} from './testHelpers'; +import { QUOTE_PRECISION } from '../sdk/lib'; + +const ZERO = new BN(0); + +describe('formulaic curve (repeg / k)', () => { + const provider = anchor.Provider.local(); + const connection = provider.connection; + anchor.setProvider(provider); + const chProgram = anchor.workspace.ClearingHouse as Program; + + let clearingHouse: Admin; + + let usdcMint: Keypair; + let userUSDCAccount: Keypair; + const initialSOLPrice = 150; + + // ammInvariant == k == x * y + const mantissaSqrtScale = new BN(Math.sqrt(MARK_PRICE_PRECISION.toNumber())); + const ammInitialQuoteAssetReserve = new anchor.BN(5 * 10 ** 13).mul( + mantissaSqrtScale + ); + const ammInitialBaseAssetReserve = new anchor.BN(5 * 10 ** 13).mul( + mantissaSqrtScale + ); + const usdcAmount = new BN(1e9 * 10 ** 6); + + let userAccount: ClearingHouseUser; + let solUsdOracle; + + before(async () => { + usdcMint = await mockUSDCMint(provider); + userUSDCAccount = await mockUserUSDCAccount(usdcMint, usdcAmount, provider); + + clearingHouse = Admin.from( + connection, + provider.wallet, + chProgram.programId + ); + await clearingHouse.initialize(usdcMint.publicKey, true); + await clearingHouse.subscribe(); + + const periodicity = new BN(0); // 1 HOUR + + solUsdOracle = await createPriceFeed({ + oracleProgram: anchor.workspace.Pyth, + initPrice: initialSOLPrice, + }); + + await clearingHouse.initializeMarket( + Markets[0].marketIndex, + solUsdOracle, + ammInitialBaseAssetReserve, + ammInitialQuoteAssetReserve, + periodicity, + new BN(initialSOLPrice * PEG_PRECISION.toNumber()) + ); + + await clearingHouse.initializeUserAccount(); + userAccount = ClearingHouseUser.from( + clearingHouse, + provider.wallet.publicKey + ); + await userAccount.subscribe(); + }); + + after(async () => { + await clearingHouse.unsubscribe(); + await userAccount.unsubscribe(); + }); + + it('track netRevenueSinceLastFunding', async () => { + await clearingHouse.depositCollateral( + usdcAmount, + userUSDCAccount.publicKey + ); + const marketIndex = Markets[0].marketIndex; + + const targetPriceBack = new BN( + initialSOLPrice * MARK_PRICE_PRECISION.toNumber() + ); + + // const [direction, tradeSize, _] = clearingHouse.calculateTargetPriceTrade( + // marketIndex, + // targetPriceUp + // ); + await clearingHouse.moveAmmToPrice(marketIndex, targetPriceBack); + await clearingHouse.updateFundingPaused(true); + + console.log('taking position'); + await clearingHouse.openPosition( + PositionDirection.LONG, + new BN(1000).mul(QUOTE_PRECISION), + marketIndex + ); + console.log('$1000 position taken'); + await clearingHouse.fetchAccounts(); + const marketsOld = await clearingHouse.getMarketsAccount(); + assert(!marketsOld.markets[0].baseAssetAmount.eq(ZERO)); + + const oldKPrice = calculateMarkPrice(clearingHouse.getMarket(marketIndex)); + const ammOld = marketsOld.markets[0].amm; + console.log( + 'USER getTotalCollateral', + convertToNumber(userAccount.getTotalCollateral(), QUOTE_PRECISION) + ); + + await clearingHouse.fetchAccounts(); + const marketsKChange = await clearingHouse.getMarketsAccount(); + const ammKChange = marketsKChange.markets[0].amm; + + const newKPrice = calculateMarkPrice(clearingHouse.getMarket(marketIndex)); + + console.log('$1000 position closing'); + + await clearingHouse.closePosition(marketIndex); + console.log('$1000 position closed'); + + const markets = await clearingHouse.getMarketsAccount(); + + const amm = markets.markets[0].amm; + + const marginOfError = new BN(MARK_PRICE_PRECISION.div(new BN(1000))); // price change less than 3 decimal places + + // console.log( + // 'oldSqrtK', + // convertToNumber(ammOld.sqrtK), + // 'oldKPrice:', + // convertToNumber(oldKPrice) + // ); + // console.log( + // 'newSqrtK', + // convertToNumber(newSqrtK), + // 'newKPrice:', + // convertToNumber(newKPrice) + // ); + + // assert(ammOld.sqrtK.eq(amm.sqrtK)); + // assert(newKPrice.sub(oldKPrice).abs().lt(marginOfError)); + // assert(!amm.sqrtK.eq(newSqrtK)); + + console.log( + 'realizedFeeOld', + convertToNumber(ammOld.totalFeeMinusDistributions, QUOTE_PRECISION), + 'realizedFeePostK', + convertToNumber(ammKChange.totalFeeMinusDistributions, QUOTE_PRECISION), + 'realizedFeePostClose', + convertToNumber(amm.totalFeeMinusDistributions, QUOTE_PRECISION), + 'netRevenue', + convertToNumber(amm.netRevenueSinceLastFunding, QUOTE_PRECISION) + ); + + assert(amm.netRevenueSinceLastFunding.eq(amm.totalFeeMinusDistributions)); + console.log( + 'USER getTotalCollateral', + convertToNumber(userAccount.getTotalCollateral(), QUOTE_PRECISION) + ); + }); + it('update funding (netRevenueSinceLastFunding)', async () => { + const marketIndex = Markets[0].marketIndex; + await clearingHouse.updateFundingPaused(false); + await new Promise((r) => setTimeout(r, 1000)); // wait 1 second + + const _tx = await clearingHouse.updateFundingRate( + solUsdOracle, + marketIndex + ); + await new Promise((r) => setTimeout(r, 1000)); // wait 1 second + + const markets = await clearingHouse.getMarketsAccount(); + const market = markets.markets[0]; + const amm = market.amm; + + // await setFeedPrice(program, newPrice, priceFeedAddress); + const oraclePx = await getFeedData(anchor.workspace.Pyth, amm.oracle); + + console.log( + 'markPrice:', + convertToNumber(calculateMarkPrice(market)), + 'oraclePrice:', + oraclePx.p + ); + console.log( + 'USER getTotalCollateral', + convertToNumber(userAccount.getTotalCollateral(), QUOTE_PRECISION), + 'fundingPnL:', + convertToNumber(userAccount.getUnrealizedFundingPNL(), QUOTE_PRECISION) + ); + console.log( + 'fundingRate:', + convertToNumber(amm.lastFundingRate, MARK_PRICE_PRECISION) + ); + console.log( + 'realizedFeePostClose', + convertToNumber(amm.totalFeeMinusDistributions, QUOTE_PRECISION), + 'netRevenue', + convertToNumber(amm.netRevenueSinceLastFunding, QUOTE_PRECISION) + ); + + assert(amm.netRevenueSinceLastFunding.eq(ZERO)); + }); +}); diff --git a/tests/formulaPeg.ts b/tests/formulaPeg.ts new file mode 100644 index 00000000..704e4198 --- /dev/null +++ b/tests/formulaPeg.ts @@ -0,0 +1,347 @@ +import * as anchor from '@project-serum/anchor'; +import { assert } from 'chai'; +import { Keypair } from '@solana/web3.js'; +import { Program } from '@project-serum/anchor'; +import { + BN, + FUNDING_PAYMENT_PRECISION, + Admin, + MARK_PRICE_PRECISION, + calculateMarkPrice, + ClearingHouseUser, + PEG_PRECISION, + PositionDirection, + // OrderStatus, + // OrderDiscountTier, + // OrderRecord, + // OrderAction, + // OrderTriggerCondition, + // calculateTargetPriceTrade, + convertToNumber, + AMM_RESERVE_PRECISION, + // Wallet, + // calculateTradeSlippage, + getLimitOrderParams, + // getTriggerMarketOrderParams, + findComputeUnitConsumption, + QUOTE_PRECISION, +} from '../sdk/src'; + +import { Markets } from '../sdk/src/constants/markets'; + +import { + createPriceFeed, + mockUSDCMint, + mockUserUSDCAccount, + setFeedPrice, + getFeedData, +} from './testHelpers'; + +const ZERO = new BN(0); + +describe('formulaic curve (repeg / k)', () => { + const provider = anchor.Provider.local(); + const connection = provider.connection; + anchor.setProvider(provider); + const chProgram = anchor.workspace.ClearingHouse as Program; + + let clearingHouse: Admin; + + let usdcMint: Keypair; + let userUSDCAccount: Keypair; + const initialSOLPrice = 150; + + const marketIndex = new BN(12); // for soft launch + + // ammInvariant == k == x * y + const mantissaSqrtScale = new BN(Math.sqrt(MARK_PRICE_PRECISION.toNumber())); + const ammInitialQuoteAssetReserve = new anchor.BN(5 * 10 ** 13).mul( + mantissaSqrtScale + ); + const ammInitialBaseAssetReserve = new anchor.BN(5 * 10 ** 13).mul( + mantissaSqrtScale + ); + const usdcAmount = new BN(1e9 * 10 ** 6); + + let userAccount: ClearingHouseUser; + let solUsdOracle; + + before(async () => { + usdcMint = await mockUSDCMint(provider); + userUSDCAccount = await mockUserUSDCAccount(usdcMint, usdcAmount, provider); + + clearingHouse = Admin.from( + connection, + provider.wallet, + chProgram.programId + ); + await clearingHouse.initialize(usdcMint.publicKey, true); + await clearingHouse.subscribe(); + + const periodicity = new BN(0); // 1 HOUR + + solUsdOracle = await createPriceFeed({ + oracleProgram: anchor.workspace.Pyth, + initPrice: initialSOLPrice, + }); + + await clearingHouse.initializeMarket( + marketIndex, + solUsdOracle, + ammInitialBaseAssetReserve, + ammInitialQuoteAssetReserve, + periodicity, + new BN(initialSOLPrice * PEG_PRECISION.toNumber()) + ); + + await clearingHouse.initializeUserAccount(); + userAccount = ClearingHouseUser.from( + clearingHouse, + provider.wallet.publicKey + ); + await userAccount.subscribe(); + }); + + after(async () => { + await clearingHouse.unsubscribe(); + await userAccount.unsubscribe(); + }); + + it('track netRevenueSinceLastFunding', async () => { + await clearingHouse.depositCollateral( + usdcAmount, + userUSDCAccount.publicKey + ); + + const targetPriceBack = new BN( + initialSOLPrice * MARK_PRICE_PRECISION.toNumber() + ); + + // const [direction, tradeSize, _] = clearingHouse.calculateTargetPriceTrade( + // marketIndex, + // targetPriceUp + // ); + await clearingHouse.moveAmmToPrice(marketIndex, targetPriceBack); + await clearingHouse.updateFundingPaused(true); + + let count = 0; + while (count <= 2) { + await clearingHouse.openPosition( + PositionDirection.LONG, + new BN(100000).mul(QUOTE_PRECISION), + marketIndex + ); + await clearingHouse.closePosition(marketIndex); + count += 1; + } + + const markets = await clearingHouse.getMarketsAccount(); + + const amm = markets.markets[marketIndex.toNumber()].amm; + console.log( + 'realizedFeePostClose', + convertToNumber(amm.totalFeeMinusDistributions, QUOTE_PRECISION), + 'netRevenue', + convertToNumber(amm.netRevenueSinceLastFunding, QUOTE_PRECISION) + ); + + assert(amm.netRevenueSinceLastFunding.eq(amm.totalFeeMinusDistributions)); + console.log( + 'USER getTotalCollateral', + convertToNumber(userAccount.getTotalCollateral(), QUOTE_PRECISION) + ); + }); + it('update funding/price (netRevenueSinceLastFunding)', async () => { + await clearingHouse.updateFundingPaused(false); + await new Promise((r) => setTimeout(r, 1000)); // wait 1 second + + const _tx = await clearingHouse.updateFundingRate( + solUsdOracle, + marketIndex + ); + await new Promise((r) => setTimeout(r, 1000)); // wait 1 second + await clearingHouse.updateFundingPaused(true); + + const markets = await clearingHouse.getMarketsAccount(); + const market = markets.markets[marketIndex.toNumber()]; + const amm = market.amm; + + await setFeedPrice(anchor.workspace.Pyth, 155, amm.oracle); + + const oraclePx = await getFeedData(anchor.workspace.Pyth, amm.oracle); + + console.log( + 'markPrice:', + convertToNumber(calculateMarkPrice(market)), + 'oraclePrice:', + oraclePx.price + ); + console.log( + 'USER getTotalCollateral', + convertToNumber(userAccount.getTotalCollateral(), QUOTE_PRECISION), + 'fundingPnL:', + convertToNumber(userAccount.getUnrealizedFundingPNL(), QUOTE_PRECISION) + ); + console.log( + 'fundingRate:', + convertToNumber(amm.lastFundingRate, MARK_PRICE_PRECISION) + ); + console.log( + 'realizedFeePostClose', + convertToNumber(amm.totalFeeMinusDistributions, QUOTE_PRECISION), + 'netRevenue', + convertToNumber(amm.netRevenueSinceLastFunding, QUOTE_PRECISION) + ); + }); + + it('cause repeg?', async () => { + const markets = await clearingHouse.getMarketsAccount(); + const market = markets.markets[marketIndex.toNumber()]; + const amm = market.amm; + const oraclePx = await getFeedData(anchor.workspace.Pyth, amm.oracle); + + const direction = PositionDirection.LONG; + const baseAssetAmount = new BN(AMM_RESERVE_PRECISION); + const price = MARK_PRICE_PRECISION.mul(new BN(oraclePx.price + 1)); + + // const prePosition = userAccount.getUserPosition(marketIndex); + // console.log(prePosition); + // assert(prePosition == undefined); // no existing position + + // const fillerUserAccount0 = userAccount.getUserAccount(); + + const orderParams = getLimitOrderParams( + marketIndex, + direction, + baseAssetAmount, + price, + false, + false + ); + const txSig = await clearingHouse.placeAndFillOrder( + orderParams + // discountTokenAccount.address + ); + + await clearingHouse.fetchAccounts(); + await userAccount.fetchAccounts(); + + const postPosition = userAccount.getUserPosition(marketIndex); + console.log( + 'User position: ', + convertToNumber(new BN(0), AMM_RESERVE_PRECISION), + '->', + convertToNumber(postPosition.baseAssetAmount, AMM_RESERVE_PRECISION) + ); + assert(postPosition.baseAssetAmount.abs().gt(new BN(0))); + assert(postPosition.baseAssetAmount.eq(baseAssetAmount)); // 100% filled + + const marketsAfter = await clearingHouse.getMarketsAccount(); + const marketAfter = marketsAfter.markets[marketIndex.toNumber()]; + const ammAfter = marketAfter.amm; + + console.log( + 'Oracle:', + oraclePx.price, + 'Mark:', + convertToNumber(calculateMarkPrice(market)), + '->', + convertToNumber(calculateMarkPrice(marketAfter)) + ); + + console.log( + 'Peg:', + convertToNumber(amm.pegMultiplier, PEG_PRECISION), + '->', + convertToNumber(ammAfter.pegMultiplier, PEG_PRECISION), + '(net rev=', + convertToNumber( + ammAfter.totalFeeMinusDistributions.sub(amm.totalFeeMinusDistributions), + QUOTE_PRECISION + ), + ')' + ); + + const computeUnits = await findComputeUnitConsumption( + clearingHouse.program.programId, + connection, + txSig + ); + + console.log('placeAndFill compute units', computeUnits[0]); + }); + it('cause repeg? close', async () => { + const markets = await clearingHouse.getMarketsAccount(); + const market = markets.markets[marketIndex.toNumber()]; + const amm = market.amm; + const oraclePx = await getFeedData(anchor.workspace.Pyth, amm.oracle); + + const direction = PositionDirection.SHORT; + const baseAssetAmount = new BN(AMM_RESERVE_PRECISION); + const price = MARK_PRICE_PRECISION.mul(new BN(oraclePx.price - 10)); + + const prePosition = userAccount.getUserPosition(marketIndex); + // console.log(prePosition); + // assert(prePosition == undefined); // no existing position + + // const fillerUserAccount0 = userAccount.getUserAccount(); + + const orderParams = getLimitOrderParams( + marketIndex, + direction, + baseAssetAmount, + price, + false, + false + ); + const txSig = await clearingHouse.placeAndFillOrder( + orderParams + // discountTokenAccount.address + ); + + await clearingHouse.fetchAccounts(); + await userAccount.fetchAccounts(); + + const postPosition = userAccount.getUserPosition(marketIndex); + console.log( + 'User position: ', + convertToNumber(prePosition.baseAssetAmount, AMM_RESERVE_PRECISION), + '->', + convertToNumber(postPosition.baseAssetAmount, AMM_RESERVE_PRECISION) + ); + + const marketsAfter = await clearingHouse.getMarketsAccount(); + const marketAfter = marketsAfter.markets[marketIndex.toNumber()]; + const ammAfter = marketAfter.amm; + + console.log( + 'Oracle:', + oraclePx.price, + 'Mark:', + convertToNumber(calculateMarkPrice(market)), + '->', + convertToNumber(calculateMarkPrice(marketAfter)) + ); + + console.log( + 'Peg:', + convertToNumber(amm.pegMultiplier, PEG_PRECISION), + '->', + convertToNumber(ammAfter.pegMultiplier, PEG_PRECISION), + '(net rev=', + convertToNumber( + ammAfter.totalFeeMinusDistributions.sub(amm.totalFeeMinusDistributions), + QUOTE_PRECISION + ), + ')' + ); + + const computeUnits = await findComputeUnitConsumption( + clearingHouse.program.programId, + connection, + txSig + ); + + console.log('placeAndFill compute units', computeUnits[0]); + }); +}); From 18e479009028179494ba462353c6af0cfb7481ea Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Mon, 21 Mar 2022 20:57:25 -0400 Subject: [PATCH 22/59] repeg use fspr --- .../clearing_house/src/controller/repeg.rs | 19 +++++++- programs/clearing_house/src/math/repeg.rs | 45 ++++++++++++++++--- sdk/src/math/repeg.ts | 9 ++-- tests/formulaPeg.ts | 35 ++++++++++++--- 4 files changed, 89 insertions(+), 19 deletions(-) diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index af756c8b..02b34a32 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -6,7 +6,9 @@ use crate::math::amm; use crate::math_error; use crate::state::market::{Market, OraclePriceData, AMM}; use crate::state::state::OracleGuardRails; +use std::cmp::{max, min}; +use crate::math::constants::{AMM_RESERVE_PRECISION, MARK_PRICE_PRECISION, QUOTE_PRECISION}; use anchor_lang::prelude::AccountInfo; use solana_program::msg; @@ -106,9 +108,24 @@ pub fn formulaic_repeg( .checked_sub(cast_to_i128(terminal_price_before)?) .ok_or_else(math_error!())?; + // max budget for single repeg is half of fee pool for repegs + let fee_pool = repeg::calculate_fee_pool(market)?; + let expected_funding_excess = + repeg::calculate_expected_funding_excess(market, oracle_price, precomputed_mark_price)?; + + let max_budget = max( + budget, + min( + cast_to_u128(max(0, expected_funding_excess))? + .checked_div(2) + .ok_or_else(math_error!())?, + fee_pool.checked_div(2).ok_or_else(math_error!())?, + ), + ); + let (new_peg_candidate, adjustment_cost, repegged_market) = repeg::calculate_budgeted_peg( market, - budget, + max_budget, precomputed_mark_price, cast_to_u128(oracle_price)?, )?; diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 2709708d..1eba26df 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -5,7 +5,7 @@ use crate::math::amm::calculate_swap_output; use crate::math::bn; use crate::math::casting::{cast_to_i128, cast_to_u128}; use crate::math::constants::{ - AMM_TO_QUOTE_PRECISION_RATIO, MARK_PRICE_PRECISION, PEG_PRECISION, + AMM_RESERVE_PRECISION, AMM_TO_QUOTE_PRECISION_RATIO, MARK_PRICE_PRECISION, PEG_PRECISION, PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, @@ -320,11 +320,11 @@ pub fn calculate_budgeted_peg( .checked_div(AMM_TO_QUOTE_PRECISION_RATIO) .ok_or_else(math_error!())?, ) - .ok_or_else(math_error!())? - .checked_mul(PEG_PRECISION) - .ok_or_else(math_error!())? - .checked_div(QUOTE_PRECISION) .ok_or_else(math_error!())?; + // .checked_mul(PEG_PRECISION) + // .ok_or_else(math_error!())? + // .checked_div(QUOTE_PRECISION) + // .ok_or_else(math_error!())?; let delta_peg_precision = delta_peg_multiplier .checked_mul(PEG_PRECISION) @@ -336,13 +336,13 @@ pub fn calculate_budgeted_peg( market .amm .peg_multiplier - .checked_sub(delta_peg_precision) + .checked_add(delta_peg_precision) .ok_or_else(math_error!())? } else { market .amm .peg_multiplier - .checked_add(delta_peg_precision) + .checked_sub(delta_peg_precision) .ok_or_else(math_error!())? }; @@ -409,6 +409,37 @@ pub fn adjust_peg_cost( Ok((market_deep_copy, cost)) } +pub fn calculate_expected_funding_excess( + market: &Market, + oracle_price: i128, + precomputed_mark_price: u128, +) -> ClearingHouseResult { + let oracle_mark_spread = oracle_price + .checked_sub(cast_to_i128(precomputed_mark_price)?) + .ok_or_else(math_error!())?; + + let oracle_mark_twap_spread = market + .amm + .last_oracle_price_twap + .checked_sub(cast_to_i128(market.amm.last_mark_price_twap)?) + .ok_or_else(math_error!())?; + + let funding_ev = market + .base_asset_amount + .checked_mul( + oracle_mark_spread + .checked_sub(oracle_mark_twap_spread) + .ok_or_else(math_error!())?, + ) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128( + MARK_PRICE_PRECISION * AMM_RESERVE_PRECISION / QUOTE_PRECISION, + )?) + .ok_or_else(math_error!())?; + + Ok(funding_ev) +} + pub fn calculate_fee_pool(market: &Market) -> ClearingHouseResult { let total_fee_minus_distributions_lower_bound = total_fee_lower_bound(&market)?; diff --git a/sdk/src/math/repeg.ts b/sdk/src/math/repeg.ts index b3124632..7b541ace 100644 --- a/sdk/src/math/repeg.ts +++ b/sdk/src/math/repeg.ts @@ -237,10 +237,11 @@ export function calculateBudgetedPeg(market: Market, cost: BN): BN { const C = cost.mul(new BN(-1)); const deltaQuoteAssetReserves = y.sub(k.div(x.add(d))); - const deltaPegMultiplier = C.mul(MARK_PRICE_PRECISION) - .div(deltaQuoteAssetReserves.div(AMM_TO_QUOTE_PRECISION_RATIO)) - .mul(PEG_PRECISION) - .div(QUOTE_PRECISION); + const deltaPegMultiplier = C.mul(MARK_PRICE_PRECISION).div( + deltaQuoteAssetReserves.div(AMM_TO_QUOTE_PRECISION_RATIO) + ); + // .mul(PEG_PRECISION) + // .div(QUOTE_PRECISION); console.log( Q.toNumber(), 'change by', diff --git a/tests/formulaPeg.ts b/tests/formulaPeg.ts index 704e4198..7e5b6ff5 100644 --- a/tests/formulaPeg.ts +++ b/tests/formulaPeg.ts @@ -11,6 +11,8 @@ import { ClearingHouseUser, PEG_PRECISION, PositionDirection, + calculateBudgetedPeg, + calculateBudgetedK, // OrderStatus, // OrderDiscountTier, // OrderRecord, @@ -233,6 +235,7 @@ describe('formulaic curve (repeg / k)', () => { '->', convertToNumber(postPosition.baseAssetAmount, AMM_RESERVE_PRECISION) ); + assert(postPosition.baseAssetAmount.abs().gt(new BN(0))); assert(postPosition.baseAssetAmount.eq(baseAssetAmount)); // 100% filled @@ -240,6 +243,16 @@ describe('formulaic curve (repeg / k)', () => { const marketAfter = marketsAfter.markets[marketIndex.toNumber()]; const ammAfter = marketAfter.amm; + // const newPeg = calculateBudgetedPeg(marketAfter, new BN(15000000)); + console.log( + 'Expected Peg Change:', + market.amm.pegMultiplier.toNumber(), + '->', + marketAfter.amm.pegMultiplier.toNumber() + // ' vs ->', + // newPeg.toNumber() + ); + console.log( 'Oracle:', oraclePx.price, @@ -259,6 +272,11 @@ describe('formulaic curve (repeg / k)', () => { ammAfter.totalFeeMinusDistributions.sub(amm.totalFeeMinusDistributions), QUOTE_PRECISION ), + ' | ', + convertToNumber(amm.totalFeeMinusDistributions, QUOTE_PRECISION), + '->', + convertToNumber(ammAfter.totalFeeMinusDistributions, QUOTE_PRECISION), + ')' ); @@ -335,13 +353,16 @@ describe('formulaic curve (repeg / k)', () => { ), ')' ); + try { + const computeUnits = await findComputeUnitConsumption( + clearingHouse.program.programId, + connection, + txSig + ); - const computeUnits = await findComputeUnitConsumption( - clearingHouse.program.programId, - connection, - txSig - ); - - console.log('placeAndFill compute units', computeUnits[0]); + console.log('placeAndFill compute units', computeUnits[0]); + } catch (e) { + console.log('err calc in compute units'); + } }); }); From cac6ef42348b533832df25d4e69a4c45274b4f51 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Tue, 22 Mar 2022 11:24:45 -0400 Subject: [PATCH 23/59] more test examples --- .../clearing_house/src/controller/orders.rs | 2 +- .../clearing_house/src/controller/repeg.rs | 14 +- programs/clearing_house/src/math/repeg.rs | 49 ++- tests/formulaPeg.ts | 396 +++++++++++------- 4 files changed, 303 insertions(+), 158 deletions(-) diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index 8b975da1..d38d8d10 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -617,7 +617,7 @@ pub fn fill_order( Some(mark_price_before), )?; - if market_index == 12 { + if market_index >= 12 { // todo for soft launch controller::repeg::formulaic_repeg( market, diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index 02b34a32..87dbf8e3 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -40,6 +40,11 @@ pub fn repeg( oracle_guard_rails, )?; + // cannot repeg if oracle is invalid + if !oracle_is_valid { + return Err(ErrorCode::InvalidOracle.into()); + } + // only push terminal in direction of oracle if !direction_valid { return Err(ErrorCode::InvalidRepegDirection.into()); @@ -96,6 +101,10 @@ pub fn formulaic_repeg( is_oracle_valid: bool, budget: u128, ) -> ClearingHouseResult { + if !is_oracle_valid { + return Ok(0); + } + let OraclePriceData { price: oracle_price, confidence: oracle_conf, @@ -112,16 +121,17 @@ pub fn formulaic_repeg( let fee_pool = repeg::calculate_fee_pool(market)?; let expected_funding_excess = repeg::calculate_expected_funding_excess(market, oracle_price, precomputed_mark_price)?; - + // let max_budget = budget; let max_budget = max( budget, min( cast_to_u128(max(0, expected_funding_excess))? .checked_div(2) .ok_or_else(math_error!())?, - fee_pool.checked_div(2).ok_or_else(math_error!())?, + fee_pool.checked_div(100).ok_or_else(math_error!())?, ), ); + // msg!("{:?}, {:?}", expected_funding_excess, fee_pool); let (new_peg_candidate, adjustment_cost, repegged_market) = repeg::calculate_budgeted_peg( market, diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 1eba26df..9598a527 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -3,10 +3,10 @@ use crate::error::*; use crate::math::amm; use crate::math::amm::calculate_swap_output; use crate::math::bn; -use crate::math::casting::{cast_to_i128, cast_to_u128}; +use crate::math::casting::{cast_to_i128, cast_to_i64, cast_to_u128}; use crate::math::constants::{ - AMM_RESERVE_PRECISION, AMM_TO_QUOTE_PRECISION_RATIO, MARK_PRICE_PRECISION, PEG_PRECISION, - PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, + AMM_RESERVE_PRECISION, AMM_TO_QUOTE_PRECISION_RATIO, MARK_PRICE_PRECISION, ONE_HOUR, + PEG_PRECISION, PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, }; @@ -105,6 +105,12 @@ pub fn calculate_repeg_validity( if oracle_price_u128 > terminal_price_after { // only allow terminal up when oracle is higher if terminal_price_after < terminal_price_before { + msg!( + "oracle: {:?}, termb: {:?}, terma: {:?},", + oracle_price_u128, + terminal_price_before, + terminal_price_after + ); direction_valid = false; } @@ -120,6 +126,12 @@ pub fn calculate_repeg_validity( } else if oracle_price_u128 < terminal_price_after { // only allow terminal down when oracle is lower if terminal_price_after > terminal_price_before { + msg!( + "oracle: {:?}, termb: {:?}, terma: {:?},", + oracle_price_u128, + terminal_price_before, + terminal_price_after + ); direction_valid = false; } @@ -321,10 +333,10 @@ pub fn calculate_budgeted_peg( .ok_or_else(math_error!())?, ) .ok_or_else(math_error!())?; - // .checked_mul(PEG_PRECISION) - // .ok_or_else(math_error!())? - // .checked_div(QUOTE_PRECISION) - // .ok_or_else(math_error!())?; + // .checked_mul(PEG_PRECISION) + // .ok_or_else(math_error!())? + // .checked_div(QUOTE_PRECISION) + // .ok_or_else(math_error!())?; let delta_peg_precision = delta_peg_multiplier .checked_mul(PEG_PRECISION) @@ -414,17 +426,22 @@ pub fn calculate_expected_funding_excess( oracle_price: i128, precomputed_mark_price: u128, ) -> ClearingHouseResult { - let oracle_mark_spread = oracle_price - .checked_sub(cast_to_i128(precomputed_mark_price)?) + let oracle_mark_spread = cast_to_i128(precomputed_mark_price)? + .checked_sub(oracle_price) .ok_or_else(math_error!())?; - let oracle_mark_twap_spread = market - .amm - .last_oracle_price_twap - .checked_sub(cast_to_i128(market.amm.last_mark_price_twap)?) + let oracle_mark_twap_spread = cast_to_i128(market.amm.last_mark_price_twap)? + .checked_sub(market.amm.last_oracle_price_twap) + .ok_or_else(math_error!())?; + + let one_hour_i64 = cast_to_i64(ONE_HOUR)?; + let period_adjustment = (24_i64) + .checked_mul(one_hour_i64) + .ok_or_else(math_error!())? + .checked_div(max(one_hour_i64, market.amm.funding_period)) .ok_or_else(math_error!())?; - let funding_ev = market + let funding_excess_ev = market .base_asset_amount .checked_mul( oracle_mark_spread @@ -432,12 +449,14 @@ pub fn calculate_expected_funding_excess( .ok_or_else(math_error!())?, ) .ok_or_else(math_error!())? + .checked_div(cast_to_i128(period_adjustment)?) + .ok_or_else(math_error!())? .checked_div(cast_to_i128( MARK_PRICE_PRECISION * AMM_RESERVE_PRECISION / QUOTE_PRECISION, )?) .ok_or_else(math_error!())?; - Ok(funding_ev) + Ok(funding_excess_ev) } pub fn calculate_fee_pool(market: &Market) -> ClearingHouseResult { diff --git a/tests/formulaPeg.ts b/tests/formulaPeg.ts index 7e5b6ff5..d7cb4a87 100644 --- a/tests/formulaPeg.ts +++ b/tests/formulaPeg.ts @@ -1,6 +1,6 @@ import * as anchor from '@project-serum/anchor'; import { assert } from 'chai'; -import { Keypair } from '@solana/web3.js'; +import { Connection, Keypair } from '@solana/web3.js'; import { Program } from '@project-serum/anchor'; import { BN, @@ -41,6 +41,126 @@ import { const ZERO = new BN(0); +export const matchEnum = (enum1: any, enum2) => { + return JSON.stringify(enum1) === JSON.stringify(enum2); +}; +async function formRepegHelper( + connection: Connection, + clearingHouse: Admin, + userAccount: ClearingHouseUser, + marketIndex: BN, + oraclePrice: Number, + amt: Number, + direction: PositionDirection +) { + const markets = await clearingHouse.getMarketsAccount(); + const market = markets.markets[marketIndex.toNumber()]; + const amm = market.amm; + await setFeedPrice(anchor.workspace.Pyth, oraclePrice, amm.oracle); + const oraclePx = await getFeedData(anchor.workspace.Pyth, amm.oracle); + + // const direction = PositionDirection.LONG; + const baseAssetAmount = new BN(AMM_RESERVE_PRECISION.toNumber() * amt); + let price = new BN(calculateMarkPrice(market).toNumber() * 1.02); + if (matchEnum(direction, PositionDirection.SHORT)) { + price = new BN(calculateMarkPrice(market).toNumber() * 0.988); + } + + const prePosition = userAccount.getUserPosition(marketIndex); + // console.log(prePosition); + // assert(prePosition == undefined); // no existing position + + // const fillerUserAccount0 = userAccount.getUserAccount(); + + const orderParams = getLimitOrderParams( + marketIndex, + direction, + baseAssetAmount, + price, + false, + false + ); + const txSig = await clearingHouse.placeAndFillOrder( + orderParams + // discountTokenAccount.address + ); + + await clearingHouse.fetchAccounts(); + await userAccount.fetchAccounts(); + + const postPosition = userAccount.getUserPosition(marketIndex); + + console.log( + 'User position: ', + convertToNumber( + prePosition?.baseAssetAmount ?? ZERO, + AMM_RESERVE_PRECISION + ), + '->', + convertToNumber(postPosition.baseAssetAmount, AMM_RESERVE_PRECISION) + ); + + // assert(postPosition.baseAssetAmount.abs().gt(new BN(0))); + // assert(postPosition.baseAssetAmount.eq(baseAssetAmount)); // 100% filled + + const marketsAfter = await clearingHouse.getMarketsAccount(); + const marketAfter = marketsAfter.markets[marketIndex.toNumber()]; + const ammAfter = marketAfter.amm; + + // const newPeg = calculateBudgetedPeg(marketAfter, new BN(15000000)); + console.log( + 'Expected Peg Change:', + market.amm.pegMultiplier.toNumber(), + '->', + marketAfter.amm.pegMultiplier.toNumber() + // ' vs ->', + // newPeg.toNumber() + ); + + console.log( + 'Oracle:', + oraclePx.price, + 'Mark:', + convertToNumber(calculateMarkPrice(market)), + '->', + convertToNumber(calculateMarkPrice(marketAfter)) + ); + + const netRevenue = convertToNumber( + ammAfter.totalFeeMinusDistributions.sub(amm.totalFeeMinusDistributions), + QUOTE_PRECISION + ); + + console.log( + 'Peg:', + convertToNumber(amm.pegMultiplier, PEG_PRECISION), + '->', + convertToNumber(ammAfter.pegMultiplier, PEG_PRECISION), + '(net rev=', + netRevenue, + ' | ', + convertToNumber(amm.totalFeeMinusDistributions, QUOTE_PRECISION), + '->', + convertToNumber(ammAfter.totalFeeMinusDistributions, QUOTE_PRECISION), + + ')' + ); + + try { + const computeUnits = await findComputeUnitConsumption( + clearingHouse.program.programId, + connection, + txSig + ); + + console.log('placeAndFill compute units', computeUnits[0]); + } catch (e) { + console.log('err calc in compute units'); + } + + return netRevenue; +} + describe('formulaic curve (repeg / k)', () => { const provider = anchor.Provider.local(); const connection = provider.connection; @@ -67,6 +187,7 @@ describe('formulaic curve (repeg / k)', () => { let userAccount: ClearingHouseUser; let solUsdOracle; + let dogUsdOracle; before(async () => { usdcMint = await mockUSDCMint(provider); @@ -87,6 +208,11 @@ describe('formulaic curve (repeg / k)', () => { initPrice: initialSOLPrice, }); + dogUsdOracle = await createPriceFeed({ + oracleProgram: anchor.workspace.Pyth, + initPrice: 0.11, + }); + await clearingHouse.initializeMarket( marketIndex, solUsdOracle, @@ -96,6 +222,15 @@ describe('formulaic curve (repeg / k)', () => { new BN(initialSOLPrice * PEG_PRECISION.toNumber()) ); + await clearingHouse.initializeMarket( + marketIndex.add(new BN(1)), + dogUsdOracle, + ammInitialBaseAssetReserve, + ammInitialQuoteAssetReserve, + periodicity, + new BN(110) + ); + await clearingHouse.initializeUserAccount(); userAccount = ClearingHouseUser.from( clearingHouse, @@ -196,173 +331,154 @@ describe('formulaic curve (repeg / k)', () => { ); }); - it('cause repeg?', async () => { - const markets = await clearingHouse.getMarketsAccount(); - const market = markets.markets[marketIndex.toNumber()]; - const amm = market.amm; - const oraclePx = await getFeedData(anchor.workspace.Pyth, amm.oracle); - - const direction = PositionDirection.LONG; - const baseAssetAmount = new BN(AMM_RESERVE_PRECISION); - const price = MARK_PRICE_PRECISION.mul(new BN(oraclePx.price + 1)); - - // const prePosition = userAccount.getUserPosition(marketIndex); - // console.log(prePosition); - // assert(prePosition == undefined); // no existing position - - // const fillerUserAccount0 = userAccount.getUserAccount(); - - const orderParams = getLimitOrderParams( + it('cause repeg? oracle > mark', async () => { + await formRepegHelper( + connection, + clearingHouse, + userAccount, marketIndex, - direction, - baseAssetAmount, - price, - false, - false + 155, + 1, + PositionDirection.LONG ); - const txSig = await clearingHouse.placeAndFillOrder( - orderParams - // discountTokenAccount.address + }); + it('cause repeg? oracle > mark close', async () => { + await formRepegHelper( + connection, + clearingHouse, + userAccount, + marketIndex, + 155, + 1, + PositionDirection.SHORT ); - - await clearingHouse.fetchAccounts(); - await userAccount.fetchAccounts(); - - const postPosition = userAccount.getUserPosition(marketIndex); - console.log( - 'User position: ', - convertToNumber(new BN(0), AMM_RESERVE_PRECISION), - '->', - convertToNumber(postPosition.baseAssetAmount, AMM_RESERVE_PRECISION) + }); + it('cause repeg? oracle < mark open/close', async () => { + await formRepegHelper( + connection, + clearingHouse, + userAccount, + marketIndex, + 149, + 1, + PositionDirection.SHORT ); - - assert(postPosition.baseAssetAmount.abs().gt(new BN(0))); - assert(postPosition.baseAssetAmount.eq(baseAssetAmount)); // 100% filled - - const marketsAfter = await clearingHouse.getMarketsAccount(); - const marketAfter = marketsAfter.markets[marketIndex.toNumber()]; - const ammAfter = marketAfter.amm; - - // const newPeg = calculateBudgetedPeg(marketAfter, new BN(15000000)); - console.log( - 'Expected Peg Change:', - market.amm.pegMultiplier.toNumber(), - '->', - marketAfter.amm.pegMultiplier.toNumber() - // ' vs ->', - // newPeg.toNumber() + await formRepegHelper( + connection, + clearingHouse, + userAccount, + marketIndex, + 149, + 1, + PositionDirection.LONG ); + }); - console.log( - 'Oracle:', - oraclePx.price, - 'Mark:', - convertToNumber(calculateMarkPrice(market)), - '->', - convertToNumber(calculateMarkPrice(marketAfter)) + it('cause repeg? PROFIT. oracle < mark open/close', async () => { + await formRepegHelper( + connection, + clearingHouse, + userAccount, + marketIndex, + 149, + 2, + PositionDirection.SHORT ); - console.log( - 'Peg:', - convertToNumber(amm.pegMultiplier, PEG_PRECISION), - '->', - convertToNumber(ammAfter.pegMultiplier, PEG_PRECISION), - '(net rev=', - convertToNumber( - ammAfter.totalFeeMinusDistributions.sub(amm.totalFeeMinusDistributions), - QUOTE_PRECISION - ), - ' | ', - convertToNumber(amm.totalFeeMinusDistributions, QUOTE_PRECISION), - '->', - convertToNumber(ammAfter.totalFeeMinusDistributions, QUOTE_PRECISION), + const newOracle = 151; + const base = 1; - ')' + const profit = await formRepegHelper( + connection, + clearingHouse, + userAccount, + marketIndex, + newOracle, + base, + PositionDirection.LONG ); - const computeUnits = await findComputeUnitConsumption( - clearingHouse.program.programId, + // net revenue above fee collected + const feeCollected = newOracle * base * 0.001; + assert(profit > feeCollected); + + const netRev2 = await formRepegHelper( connection, - txSig + clearingHouse, + userAccount, + marketIndex, + newOracle, + base, + PositionDirection.LONG ); - console.log('placeAndFill compute units', computeUnits[0]); + assert(netRev2 > 0); }); - it('cause repeg? close', async () => { + + it('cause repeg? ignore invalid oracle', async () => { const markets = await clearingHouse.getMarketsAccount(); const market = markets.markets[marketIndex.toNumber()]; const amm = market.amm; - const oraclePx = await getFeedData(anchor.workspace.Pyth, amm.oracle); - - const direction = PositionDirection.SHORT; - const baseAssetAmount = new BN(AMM_RESERVE_PRECISION); - const price = MARK_PRICE_PRECISION.mul(new BN(oraclePx.price - 10)); - - const prePosition = userAccount.getUserPosition(marketIndex); - // console.log(prePosition); - // assert(prePosition == undefined); // no existing position - - // const fillerUserAccount0 = userAccount.getUserAccount(); + await setFeedPrice(anchor.workspace.Pyth, 0.14, amm.oracle); + try { + await clearingHouse.repegAmmCurve(new BN(200), marketIndex); + assert(false); + } catch (e) { + console.log('oracle invalid'); + } - const orderParams = getLimitOrderParams( + await formRepegHelper( + connection, + clearingHouse, + userAccount, marketIndex, - direction, - baseAssetAmount, - price, - false, - false - ); - const txSig = await clearingHouse.placeAndFillOrder( - orderParams - // discountTokenAccount.address + 0.14, + 2, + PositionDirection.SHORT ); - await clearingHouse.fetchAccounts(); - await userAccount.fetchAccounts(); + const newOracle = 0.16; + const base = 2; - const postPosition = userAccount.getUserPosition(marketIndex); - console.log( - 'User position: ', - convertToNumber(prePosition.baseAssetAmount, AMM_RESERVE_PRECISION), - '->', - convertToNumber(postPosition.baseAssetAmount, AMM_RESERVE_PRECISION) + const profit = await formRepegHelper( + connection, + clearingHouse, + userAccount, + marketIndex, + newOracle, + base, + PositionDirection.LONG ); + }); - const marketsAfter = await clearingHouse.getMarketsAccount(); - const marketAfter = marketsAfter.markets[marketIndex.toNumber()]; - const ammAfter = marketAfter.amm; + it('cause repeg? tiny prices', async () => { + const dogIndex = marketIndex.add(new BN(1)); + const markets = await clearingHouse.getMarketsAccount(); + const market = markets.markets[dogIndex.toNumber()]; + const amm = market.amm; + await setFeedPrice(anchor.workspace.Pyth, 0.14, amm.oracle); - console.log( - 'Oracle:', - oraclePx.price, - 'Mark:', - convertToNumber(calculateMarkPrice(market)), - '->', - convertToNumber(calculateMarkPrice(marketAfter)) + await formRepegHelper( + connection, + clearingHouse, + userAccount, + dogIndex, + 0.111, + 100, + PositionDirection.SHORT ); - console.log( - 'Peg:', - convertToNumber(amm.pegMultiplier, PEG_PRECISION), - '->', - convertToNumber(ammAfter.pegMultiplier, PEG_PRECISION), - '(net rev=', - convertToNumber( - ammAfter.totalFeeMinusDistributions.sub(amm.totalFeeMinusDistributions), - QUOTE_PRECISION - ), - ')' - ); - try { - const computeUnits = await findComputeUnitConsumption( - clearingHouse.program.programId, - connection, - txSig - ); + const newOracle = 0.1699; + const base = 100; - console.log('placeAndFill compute units', computeUnits[0]); - } catch (e) { - console.log('err calc in compute units'); - } + const profit = await formRepegHelper( + connection, + clearingHouse, + userAccount, + dogIndex, + newOracle, + base, + PositionDirection.LONG + ); }); }); From 58f489147b5a9d79d4ceabe19a870e0deb068141 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Wed, 23 Mar 2022 14:15:50 -0400 Subject: [PATCH 24/59] cleanup controller/math for repeg --- .../clearing_house/src/controller/repeg.rs | 142 ++++++---------- programs/clearing_house/src/math/repeg.rs | 159 +++++------------- 2 files changed, 96 insertions(+), 205 deletions(-) diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index 87dbf8e3..ad2ca2b7 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -19,6 +19,8 @@ pub fn repeg( clock_slot: u64, oracle_guard_rails: &OracleGuardRails, ) -> ClearingHouseResult { + // for adhoc admin only repeg + if new_peg_candidate == market.amm.peg_multiplier { return Err(ErrorCode::InvalidRepegRedundant); } @@ -31,8 +33,8 @@ pub fn repeg( direction_valid, profitability_valid, price_impact_valid, - oracle_terminal_divergence, - ) = repeg::calculate_repeg_validity_full( + _oracle_terminal_divergence, + ) = repeg::calculate_repeg_validity_from_oracle_account( repegged_market, price_oracle, terminal_price_before, @@ -61,36 +63,13 @@ pub fn repeg( } // modify market's total fee change and peg change - - // Reduce pnl to quote asset precision and take the absolute value - if adjustment_cost > 0 { - market.amm.total_fee_minus_distributions = market - .amm - .total_fee_minus_distributions - .checked_sub(adjustment_cost.unsigned_abs()) - .ok_or_else(math_error!())?; - - // Only a portion of the protocol fees are allocated to repegging - // This checks that the total_fee_minus_distributions does not decrease too much after repeg - if market.amm.total_fee_minus_distributions < repeg::total_fee_lower_bound(&market)? { - return Err(ErrorCode::InvalidRepegProfitability.into()); - } + let cost_applied = apply_cost_to_market(market, adjustment_cost)?; + if cost_applied { + market.amm.peg_multiplier = new_peg_candidate; } else { - market.amm.total_fee_minus_distributions = market - .amm - .total_fee_minus_distributions - .checked_add(adjustment_cost.unsigned_abs()) - .ok_or_else(math_error!())?; + return Err(ErrorCode::InvalidRepegProfitability.into()); } - market.amm.net_revenue_since_last_funding = market - .amm - .net_revenue_since_last_funding - .checked_add(adjustment_cost as i64) - .ok_or_else(math_error!())?; - - market.amm.peg_multiplier = new_peg_candidate; - Ok(adjustment_cost) } @@ -99,45 +78,29 @@ pub fn formulaic_repeg( precomputed_mark_price: u128, oracle_price_data: &OraclePriceData, is_oracle_valid: bool, - budget: u128, + fee_budget: u128, ) -> ClearingHouseResult { + // backrun market swaps to do automatic on-chain repeg + if !is_oracle_valid { return Ok(0); } - let OraclePriceData { - price: oracle_price, - confidence: oracle_conf, - delay: oracle_delay, - has_sufficient_number_of_data_points: has_sufficient_number_of_data_points, - } = *oracle_price_data; - let terminal_price_before = amm::calculate_terminal_price(market)?; - let oracle_terminal_spread_before = oracle_price - .checked_sub(cast_to_i128(terminal_price_before)?) - .ok_or_else(math_error!())?; + // let oracle_terminal_spread_before = oracle_price + // .checked_sub(cast_to_i128(terminal_price_before)?) + // .ok_or_else(math_error!())?; - // max budget for single repeg is half of fee pool for repegs - let fee_pool = repeg::calculate_fee_pool(market)?; - let expected_funding_excess = - repeg::calculate_expected_funding_excess(market, oracle_price, precomputed_mark_price)?; - // let max_budget = budget; - let max_budget = max( - budget, - min( - cast_to_u128(max(0, expected_funding_excess))? - .checked_div(2) - .ok_or_else(math_error!())?, - fee_pool.checked_div(100).ok_or_else(math_error!())?, - ), - ); - // msg!("{:?}, {:?}", expected_funding_excess, fee_pool); + // max budget for single repeg what larger of pool budget and user fee budget + let pool_budget = + repeg::calculate_pool_budget(market, precomputed_mark_price, oracle_price_data)?; + let budget = min(fee_budget, pool_budget); let (new_peg_candidate, adjustment_cost, repegged_market) = repeg::calculate_budgeted_peg( market, - max_budget, + budget, precomputed_mark_price, - cast_to_u128(oracle_price)?, + cast_to_u128(oracle_price_data.price)?, )?; let ( @@ -154,38 +117,45 @@ pub fn formulaic_repeg( )?; if oracle_valid && direction_valid && profitability_valid && price_impact_valid { - if adjustment_cost > 0 { - let new_total_fee_minus_distributions = market - .amm - .total_fee_minus_distributions - .checked_sub(adjustment_cost.unsigned_abs()) - .ok_or_else(math_error!())?; - - if new_total_fee_minus_distributions >= repeg::total_fee_lower_bound(&market)? { - market.amm.total_fee_minus_distributions = new_total_fee_minus_distributions; - market.amm.net_revenue_since_last_funding = market - .amm - .net_revenue_since_last_funding - .checked_add(adjustment_cost as i64) - .ok_or_else(math_error!())?; - market.amm.peg_multiplier = new_peg_candidate; - } - } else { - market.amm.total_fee_minus_distributions = market - .amm - .total_fee_minus_distributions - .checked_add(adjustment_cost.unsigned_abs()) - .ok_or_else(math_error!())?; - - market.amm.net_revenue_since_last_funding = market - .amm - .net_revenue_since_last_funding - .checked_add(adjustment_cost as i64) - .ok_or_else(math_error!())?; - + let cost_applied = apply_cost_to_market(market, adjustment_cost)?; + if cost_applied { market.amm.peg_multiplier = new_peg_candidate; } } Ok(adjustment_cost) } + +fn apply_cost_to_market(market: &mut Market, cost: i128) -> ClearingHouseResult { + // positive cost is expense, negative cost is revenue + // Reduce pnl to quote asset precision and take the absolute value + if cost > 0 { + let new_total_fee_minus_distributions = market + .amm + .total_fee_minus_distributions + .checked_sub(cost.unsigned_abs()) + .ok_or_else(math_error!())?; + + // Only a portion of the protocol fees are allocated to repegging + // This checks that the total_fee_minus_distributions does not decrease too much after repeg + if new_total_fee_minus_distributions > repeg::total_fee_lower_bound(&market)? { + market.amm.total_fee_minus_distributions = new_total_fee_minus_distributions; + } else { + return Ok(false); + } + } else { + market.amm.total_fee_minus_distributions = market + .amm + .total_fee_minus_distributions + .checked_add(cost.unsigned_abs()) + .ok_or_else(math_error!())?; + } + + market.amm.net_revenue_since_last_funding = market + .amm + .net_revenue_since_last_funding + .checked_add(cost as i64) + .ok_or_else(math_error!())?; + + Ok(true) +} diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 9598a527..4dcf7af9 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -19,7 +19,7 @@ use crate::state::state::OracleGuardRails; use anchor_lang::prelude::AccountInfo; use solana_program::msg; -pub fn calculate_repeg_validity_full( +pub fn calculate_repeg_validity_from_oracle_account( market: &mut Market, oracle_account_info: &AccountInfo, terminal_price_before: u128, @@ -165,9 +165,6 @@ pub fn calculate_peg_from_target_price( base_asset_reserve: u128, target_price: u128, ) -> ClearingHouseResult { - // m = y*C*PTPPR/x - // C = m*x/y*PTPPR - let new_peg = bn::U192::from(target_price) .checked_mul(bn::U192::from(base_asset_reserve)) .ok_or_else(math_error!())? @@ -179,103 +176,6 @@ pub fn calculate_peg_from_target_price( Ok(new_peg) } -pub fn calculate_optimal_peg_and_cost( - market: &mut Market, - oracle_price: i128, - mark_price: u128, - terminal_price: u128, -) -> ClearingHouseResult<(u128, i128, &mut Market)> { - // does minimum valid repeg allowable iff satisfies the budget - - // let fspr = (oracle - mark) - (oracle_twap - mark_twap) - - let oracle_mark_spread = oracle_price - .checked_sub(cast_to_i128(mark_price)?) - .ok_or_else(math_error!())?; - let oracle_mark_spread_pct = oracle_mark_spread - .checked_shl(10) - .ok_or_else(math_error!())? - .checked_div(oracle_price) - .ok_or_else(math_error!())?; - - let oracle_terminal_spread = oracle_price - .checked_sub(cast_to_i128(terminal_price)?) - .ok_or_else(math_error!())?; - let oracle_terminal_spread_pct = oracle_terminal_spread - .checked_shl(10) - .ok_or_else(math_error!())? - .checked_div(oracle_price) - .ok_or_else(math_error!())?; - - let ten_pct = 1_u128 - .checked_shl(10) - .ok_or_else(math_error!())? - .checked_div(10) - .ok_or_else(math_error!())?; - - if (oracle_terminal_spread_pct.unsigned_abs() < ten_pct - || oracle_terminal_spread.unsigned_abs() < PEG_PRECISION) - && (oracle_mark_spread_pct.unsigned_abs() < ten_pct - || oracle_mark_spread.unsigned_abs() < PEG_PRECISION) - { - // terminate early, no repeg needed - return Ok((market.amm.peg_multiplier, 0, market)); - } - - // find optimal peg - let optimal_peg: u128; - let current_peg = market.amm.peg_multiplier; - - // max budget for single repeg is half of fee pool for repegs - let budget = calculate_fee_pool(market)? - .checked_div(2) - .ok_or_else(math_error!())?; - - // let max_peg_delta = cast_to_u128(oracle_mark_spread)? - // .checked_mul(PEG_PRECISION) - // .ok_or_else(math_error!())? - // .checked_div(MARK_PRICE_PRECISION) - // .ok_or_else(math_error!())? - // .checked_div(2) - // .ok_or_else(math_error!())?; - // let min_peg_delta = 1_u128; - - // repeg is profitable when: - // 1) oracle above both mark and terminal AND terminal at/above mark - // 2) oracle below both mark and terminal AND terminal at/below mark - if (oracle_mark_spread > 0 && oracle_terminal_spread > 0 && terminal_price >= mark_price) - || (oracle_mark_spread < 0 && oracle_terminal_spread < 0 && terminal_price >= mark_price) - { - optimal_peg = calculate_peg_from_target_price( - market.amm.quote_asset_reserve, - market.amm.base_asset_reserve, - cast_to_u128(oracle_price)?, - )?; - } else if oracle_terminal_spread > 0 && oracle_mark_spread > 0 { - // oracle is above terminal price - optimal_peg = current_peg.checked_add(1).ok_or_else(math_error!())?; - } else if oracle_terminal_spread < 0 && oracle_mark_spread < 0 { - // oracle is below terminal price - optimal_peg = current_peg.checked_sub(1).ok_or_else(math_error!())?; - } else { - optimal_peg = current_peg; - } - - let (repegged_market, marginal_adjustment_cost) = adjust_peg_cost(market, optimal_peg)?; - - let candidate_peg: u128; - let candidate_cost: i128; - if marginal_adjustment_cost > 0 && marginal_adjustment_cost.unsigned_abs() > budget { - candidate_peg = current_peg; - candidate_cost = 0; - } else { - candidate_peg = optimal_peg; - candidate_cost = marginal_adjustment_cost; - } - - Ok((candidate_peg, candidate_cost, repegged_market)) -} - pub fn calculate_budgeted_peg( market: &mut Market, budget: u128, @@ -284,8 +184,6 @@ pub fn calculate_budgeted_peg( ) -> ClearingHouseResult<(u128, i128, &mut Market)> { // calculates peg_multiplier that changing to would cost no more than budget - let old_peg = market.amm.peg_multiplier; - let order_swap_direction = if market.base_asset_amount > 0 { SwapDirection::Add } else { @@ -333,10 +231,6 @@ pub fn calculate_budgeted_peg( .ok_or_else(math_error!())?, ) .ok_or_else(math_error!())?; - // .checked_mul(PEG_PRECISION) - // .ok_or_else(math_error!())? - // .checked_div(QUOTE_PRECISION) - // .ok_or_else(math_error!())?; let delta_peg_precision = delta_peg_multiplier .checked_mul(PEG_PRECISION) @@ -358,7 +252,7 @@ pub fn calculate_budgeted_peg( .ok_or_else(math_error!())? }; - // considers free pegs that act againist net market + // considers pegs that act againist net market let new_budget_peg_or_free = if (delta_peg_sign > 0 && optimal_peg < new_budget_peg) || (delta_peg_sign < 0 && optimal_peg > new_budget_peg) { @@ -372,9 +266,6 @@ pub fn calculate_budgeted_peg( optimal_peg }; - // msg!("PEG:: fullbudget: {:?}, optimal: {:?}",full_budget_peg, optimal_peg); - // assert_eq!(optimal_peg < 200000, true); - // avoid overshooting budget past target let candidate_peg: u128 = if current_price > target_price && full_budget_peg < optimal_peg { optimal_peg @@ -385,9 +276,6 @@ pub fn calculate_budgeted_peg( }; let (repegged_market, candidate_cost) = adjust_peg_cost(market, candidate_peg)?; - // msg!("{:?} for {:?}", candidate_peg, candidate_cost); - // assert_eq!(candidate_cost <= 0 && candidate_peg != old_peg, true); - // assert_eq!(candidate_cost >= 0 && candidate_peg == old_peg, true); Ok((candidate_peg, candidate_cost, repegged_market)) } @@ -421,6 +309,34 @@ pub fn adjust_peg_cost( Ok((market_deep_copy, cost)) } +pub fn calculate_pool_budget( + market: &Market, + precomputed_mark_price: u128, + oracle_price_data: &OraclePriceData, +) -> ClearingHouseResult { + let fee_pool = calculate_fee_pool(market)?; + let expected_funding_excess = + calculate_expected_funding_excess(market, oracle_price_data.price, precomputed_mark_price)?; + + // for a single repeg, utilize the lesser of: + // 1) 1 QUOTE (for soft launch) + // 2) 1/10th the excess instantaneous funding vs extropolated funding + // 3) 1/100th of the fee pool for funding/repeg + + let max_budget_quote = QUOTE_PRECISION; + let pool_budget = min( + max_budget_quote, + min( + cast_to_u128(max(0, expected_funding_excess))? + .checked_div(10) + .ok_or_else(math_error!())?, + fee_pool.checked_div(100).ok_or_else(math_error!())?, + ), + ); + + Ok(pool_budget) +} + pub fn calculate_expected_funding_excess( market: &Market, oracle_price: i128, @@ -462,11 +378,16 @@ pub fn calculate_expected_funding_excess( pub fn calculate_fee_pool(market: &Market) -> ClearingHouseResult { let total_fee_minus_distributions_lower_bound = total_fee_lower_bound(&market)?; - let fee_pool = market - .amm - .total_fee_minus_distributions - .checked_sub(total_fee_minus_distributions_lower_bound) - .ok_or_else(math_error!())?; + let fee_pool = + if market.amm.total_fee_minus_distributions > total_fee_minus_distributions_lower_bound { + market + .amm + .total_fee_minus_distributions + .checked_sub(total_fee_minus_distributions_lower_bound) + .ok_or_else(math_error!())? + } else { + 0 + }; Ok(fee_pool) } From 41830cc6c5a00e7bf49d3c3031193556e0031859 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Wed, 23 Mar 2022 14:31:15 -0400 Subject: [PATCH 25/59] add formulaK.ts --- programs/clearing_house/src/math/repeg.rs | 6 +++--- test-scripts/run-anchor-tests.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 4dcf7af9..447d0d5e 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -267,9 +267,9 @@ pub fn calculate_budgeted_peg( }; // avoid overshooting budget past target - let candidate_peg: u128 = if current_price > target_price && full_budget_peg < optimal_peg { - optimal_peg - } else if current_price < target_price && full_budget_peg > optimal_peg { + let candidate_peg: u128 = if (current_price > target_price && full_budget_peg < optimal_peg) + || (current_price < target_price && full_budget_peg > optimal_peg) + { optimal_peg } else { full_budget_peg diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index a2467919..0581f6a1 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -5,7 +5,7 @@ if [ "$1" != "--skip-build" ] fi test_files=(order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts deleteUser.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) -test_files=(formulaPeg.ts) +test_files=(formulaK.ts formulaPeg.ts) for test_file in ${test_files[@]}; do export ANCHOR_TEST_FILE=${test_file} && anchor test --skip-build || exit 1; From 7c3e11c7f6d63d546fdac5c6a11ba7e744d2b13d Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Wed, 23 Mar 2022 21:00:47 -0400 Subject: [PATCH 26/59] fix typo --- tests/formulaK.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/formulaK.ts b/tests/formulaK.ts index 83535452..df750cd0 100644 --- a/tests/formulaK.ts +++ b/tests/formulaK.ts @@ -202,7 +202,7 @@ describe('formulaic curve (repeg / k)', () => { 'markPrice:', convertToNumber(calculateMarkPrice(market)), 'oraclePrice:', - oraclePx.p + oraclePx.price ); console.log( 'USER getTotalCollateral', From fa8046970ef4225907a0ac6e1055946b868af2e0 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 24 Mar 2022 11:53:44 -0400 Subject: [PATCH 27/59] repeg only --- .../clearing_house/src/controller/funding.rs | 19 ------------------- tests/formulaK.ts | 2 +- tests/formulaPeg.ts | 2 +- 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 0642d9e3..5da18828 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -186,25 +186,6 @@ pub fn update_funding_rate( ) .ok_or_else(math_error!())?; - let budget = if funding_imbalance_cost < 0 { - funding_imbalance_cost - .checked_div(2) - .ok_or_else(math_error!())? - } else if market.amm.net_revenue_since_last_funding < (funding_imbalance_cost as i64) { - max(0, market.amm.net_revenue_since_last_funding) - .checked_sub(funding_imbalance_cost as i64) - .ok_or_else(math_error!())? - .checked_div(2) - .ok_or_else(math_error!())? as i128 - } else { - 0 - }; - - if budget != 0 { - let (p_numer, p_denom) = budget_k_adjustment(market, budget)?; - msg!("update k by {:?}/{:?}", p_numer, p_denom); - } - market.amm.cumulative_funding_rate_long = market .amm .cumulative_funding_rate_long diff --git a/tests/formulaK.ts b/tests/formulaK.ts index 83535452..b9c4193e 100644 --- a/tests/formulaK.ts +++ b/tests/formulaK.ts @@ -27,7 +27,7 @@ import { QUOTE_PRECISION } from '../sdk/lib'; const ZERO = new BN(0); -describe('formulaic curve (repeg / k)', () => { +describe('formulaic curve (k)', () => { const provider = anchor.Provider.local(); const connection = provider.connection; anchor.setProvider(provider); diff --git a/tests/formulaPeg.ts b/tests/formulaPeg.ts index d7cb4a87..7b7cdaab 100644 --- a/tests/formulaPeg.ts +++ b/tests/formulaPeg.ts @@ -161,7 +161,7 @@ async function formRepegHelper( return netRevenue; } -describe('formulaic curve (repeg / k)', () => { +describe('formulaic curve (repeg)', () => { const provider = anchor.Provider.local(); const connection = provider.connection; anchor.setProvider(provider); From 8c624d4b4e0fa7c50c8adaa5a50d00fcec4829e1 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 24 Mar 2022 11:55:29 -0400 Subject: [PATCH 28/59] remove all adj k --- .../clearing_house/src/controller/funding.rs | 2 +- programs/clearing_house/src/math/amm.rs | 64 ------------------- 2 files changed, 1 insertion(+), 65 deletions(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 5da18828..a6c08e6e 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -5,7 +5,7 @@ use anchor_lang::prelude::*; use crate::error::*; use crate::math::amm; -use crate::math::amm::{budget_k_adjustment, normalise_oracle_price}; +use crate::math::amm::{normalise_oracle_price}; use crate::math::casting::{cast, cast_to_i128, cast_to_i64}; use crate::math::collateral::calculate_updated_collateral; use crate::math::constants::{ diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 8d2e2f7e..1965c13b 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -447,70 +447,6 @@ pub fn is_oracle_valid( || is_conf_too_large)) } -pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseResult<(i128, i128)> { - let y = market.amm.quote_asset_reserve; - let x = market.amm.base_asset_reserve; - let c = budget; - let q = cast_to_i128(market.amm.peg_multiplier)?; - let d = market.base_asset_amount; - - let AMM_RESERVE_PRECISIONi128 = cast_to_i128(AMM_RESERVE_PRECISION)?; - - let x_d = cast_to_i128(x)?.checked_add(d).ok_or_else(math_error!())?; - - let numer1 = cast_to_i128(y)? - .checked_mul(d) - .ok_or_else(math_error!())? - .checked_mul(q) - .ok_or_else(math_error!())? - .checked_div(cast_to_i128(AMM_RESERVE_PRECISION * PEG_PRECISION)?) - .ok_or_else(math_error!())?; - let numer2 = c - .checked_mul(x_d) - .ok_or_else(math_error!())? - .checked_div(cast_to_i128(QUOTE_PRECISION)?) - .ok_or_else(math_error!())?; - let denom1 = c - .checked_mul(cast_to_i128(x)?) - .ok_or_else(math_error!())? - .checked_mul(x_d) - .ok_or_else(math_error!())? - .checked_div(cast_to_i128(AMM_RESERVE_PRECISION * QUOTE_PRECISION)?) - .ok_or_else(math_error!())?; - let denom2 = cast_to_i128(y)? - .checked_mul(d) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) - .ok_or_else(math_error!())? - .checked_mul(d) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) - .ok_or_else(math_error!())? - .checked_mul(q) - .ok_or_else(math_error!())? - .checked_div(cast_to_i128(PEG_PRECISION)?) - .ok_or_else(math_error!())?; - - let numerator = d - .checked_mul(numer1.checked_add(numer2).ok_or_else(math_error!())?) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) - .ok_or_else(math_error!())? - .checked_div(cast_to_i128(AMM_TO_QUOTE_PRECISION_RATIO)?) - .ok_or_else(math_error!())?; - let denominator = denom1 - .checked_add(denom2) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) - .ok_or_else(math_error!())?; - - Ok((numerator, denominator)) -} - /// To find the cost of adjusting k, compare the the net market value before and after adjusting k /// Increasing k costs the protocol money because it reduces slippage and improves the exit price for net market position /// Decreasing k costs the protocol money because it increases slippage and hurts the exit price for net market position From 80d195673b23d4ed940b0e51d44b1beee9030e7d Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 24 Mar 2022 12:00:02 -0400 Subject: [PATCH 29/59] cleanup for merge --- .../clearing_house/src/controller/funding.rs | 11 +-- programs/clearing_house/src/error.rs | 4 +- sdk/src/idl/clearing_house.json | 96 +++++++++---------- 3 files changed, 51 insertions(+), 60 deletions(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index a6c08e6e..3b0333f4 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -5,7 +5,7 @@ use anchor_lang::prelude::*; use crate::error::*; use crate::math::amm; -use crate::math::amm::{normalise_oracle_price}; +use crate::math::amm::normalise_oracle_price; use crate::math::casting::{cast, cast_to_i128, cast_to_i64}; use crate::math::collateral::calculate_updated_collateral; use crate::math::constants::{ @@ -177,15 +177,6 @@ pub fn update_funding_rate( let (funding_rate_long, funding_rate_short) = calculate_funding_rate_long_short(market, funding_rate)?; - // dynamic k - let funding_imbalance_cost = funding_rate - .checked_mul(market.base_asset_amount) - .ok_or_else(math_error!())? - .checked_div( - AMM_TO_QUOTE_PRECISION_RATIO_I128 * cast_to_i128(FUNDING_PAYMENT_PRECISION)?, - ) - .ok_or_else(math_error!())?; - market.amm.cumulative_funding_rate_long = market .amm .cumulative_funding_rate_long diff --git a/programs/clearing_house/src/error.rs b/programs/clearing_house/src/error.rs index 5e203941..9a6d49a7 100644 --- a/programs/clearing_house/src/error.rs +++ b/programs/clearing_house/src/error.rs @@ -34,8 +34,6 @@ pub enum ErrorCode { InvalidRepegDirection, #[msg("AMM repeg out of bounds pnl")] InvalidRepegProfitability, - #[msg("AMM repeg mark price impact vs oracle too large")] - InvalidRepegPriceImpact, #[msg("Slippage Outside Limit Price")] SlippageOutsideLimit, #[msg("Trade Size Too Small")] @@ -124,6 +122,8 @@ pub enum ErrorCode { InvalidMarginRatio, #[msg("Cant Cancel Post Only Order")] CantCancelPostOnlyOrder, + #[msg("AMM repeg mark price impact vs oracle too large")] + InvalidRepegPriceImpact, } #[macro_export] diff --git a/sdk/src/idl/clearing_house.json b/sdk/src/idl/clearing_house.json index b0c2e2e3..61fff488 100644 --- a/sdk/src/idl/clearing_house.json +++ b/sdk/src/idl/clearing_house.json @@ -3968,228 +3968,228 @@ }, { "code": 6015, - "name": "InvalidRepegPriceImpact", - "msg": "AMM repeg mark price impact vs oracle too large" - }, - { - "code": 6016, "name": "SlippageOutsideLimit", "msg": "Slippage Outside Limit Price" }, { - "code": 6017, + "code": 6016, "name": "TradeSizeTooSmall", "msg": "Trade Size Too Small" }, { - "code": 6018, + "code": 6017, "name": "InvalidUpdateK", "msg": "Price change too large when updating K" }, { - "code": 6019, + "code": 6018, "name": "AdminWithdrawTooLarge", "msg": "Admin tried to withdraw amount larger than fees collected" }, { - "code": 6020, + "code": 6019, "name": "MathError", "msg": "Math Error" }, { - "code": 6021, + "code": 6020, "name": "BnConversionError", "msg": "Conversion to u128/u64 failed with an overflow or underflow" }, { - "code": 6022, + "code": 6021, "name": "ClockUnavailable", "msg": "Clock unavailable" }, { - "code": 6023, + "code": 6022, "name": "UnableToLoadOracle", "msg": "Unable To Load Oracles" }, { - "code": 6024, + "code": 6023, "name": "OracleMarkSpreadLimit", "msg": "Oracle/Mark Spread Too Large" }, { - "code": 6025, + "code": 6024, "name": "HistoryAlreadyInitialized", "msg": "Clearing House history already initialized" }, { - "code": 6026, + "code": 6025, "name": "ExchangePaused", "msg": "Exchange is paused" }, { - "code": 6027, + "code": 6026, "name": "InvalidWhitelistToken", "msg": "Invalid whitelist token" }, { - "code": 6028, + "code": 6027, "name": "WhitelistTokenNotFound", "msg": "Whitelist token not found" }, { - "code": 6029, + "code": 6028, "name": "InvalidDiscountToken", "msg": "Invalid discount token" }, { - "code": 6030, + "code": 6029, "name": "DiscountTokenNotFound", "msg": "Discount token not found" }, { - "code": 6031, + "code": 6030, "name": "InvalidReferrer", "msg": "Invalid referrer" }, { - "code": 6032, + "code": 6031, "name": "ReferrerNotFound", "msg": "Referrer not found" }, { - "code": 6033, + "code": 6032, "name": "InvalidOracle", "msg": "InvalidOracle" }, { - "code": 6034, + "code": 6033, "name": "OracleNotFound", "msg": "OracleNotFound" }, { - "code": 6035, + "code": 6034, "name": "LiquidationsBlockedByOracle", "msg": "Liquidations Blocked By Oracle" }, { - "code": 6036, + "code": 6035, "name": "UserMaxDeposit", "msg": "Can not deposit more than max deposit" }, { - "code": 6037, + "code": 6036, "name": "CantDeleteUserWithCollateral", "msg": "Can not delete user that still has collateral" }, { - "code": 6038, + "code": 6037, "name": "InvalidFundingProfitability", "msg": "AMM funding out of bounds pnl" }, { - "code": 6039, + "code": 6038, "name": "CastingFailure", "msg": "Casting Failure" }, { - "code": 6040, + "code": 6039, "name": "InvalidOrder", "msg": "Invalid Order" }, { - "code": 6041, + "code": 6040, "name": "UserHasNoOrder", "msg": "User has no order" }, { - "code": 6042, + "code": 6041, "name": "OrderAmountTooSmall", "msg": "Order Amount Too Small" }, { - "code": 6043, + "code": 6042, "name": "MaxNumberOfOrders", "msg": "Max number of orders taken" }, { - "code": 6044, + "code": 6043, "name": "OrderDoesNotExist", "msg": "Order does not exist" }, { - "code": 6045, + "code": 6044, "name": "OrderNotOpen", "msg": "Order not open" }, { - "code": 6046, + "code": 6045, "name": "CouldNotFillOrder", "msg": "CouldNotFillOrder" }, { - "code": 6047, + "code": 6046, "name": "ReduceOnlyOrderIncreasedRisk", "msg": "Reduce only order increased risk" }, { - "code": 6048, + "code": 6047, "name": "OrderStateAlreadyInitialized", "msg": "Order state already initialized" }, { - "code": 6049, + "code": 6048, "name": "UnableToLoadAccountLoader", "msg": "Unable to load AccountLoader" }, { - "code": 6050, + "code": 6049, "name": "TradeSizeTooLarge", "msg": "Trade Size Too Large" }, { - "code": 6051, + "code": 6050, "name": "UnableToWriteToRemainingAccount", "msg": "Unable to write to remaining account" }, { - "code": 6052, + "code": 6051, "name": "UserCantReferThemselves", "msg": "User cant refer themselves" }, { - "code": 6053, + "code": 6052, "name": "DidNotReceiveExpectedReferrer", "msg": "Did not receive expected referrer" }, { - "code": 6054, + "code": 6053, "name": "CouldNotDeserializeReferrer", "msg": "Could not deserialize referrer" }, { - "code": 6055, + "code": 6054, "name": "MarketOrderMustBeInPlaceAndFill", "msg": "Market order must be in place and fill" }, { - "code": 6056, + "code": 6055, "name": "UserOrderIdAlreadyInUse", "msg": "User Order Id Already In Use" }, { - "code": 6057, + "code": 6056, "name": "NoPositionsLiquidatable", "msg": "No positions liquidatable" }, { - "code": 6058, + "code": 6057, "name": "InvalidMarginRatio", "msg": "Invalid Margin Ratio" }, { - "code": 6059, + "code": 6058, "name": "CantCancelPostOnlyOrder", "msg": "Cant Cancel Post Only Order" + }, + { + "code": 6059, + "name": "InvalidRepegPriceImpact", + "msg": "AMM repeg mark price impact vs oracle too large" } ] } \ No newline at end of file From ed2960463b97e02d94634d26a5d7ee762f051126 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 24 Mar 2022 12:48:08 -0400 Subject: [PATCH 30/59] address comments p1 --- .../clearing_house/src/controller/funding.rs | 11 ++--- .../clearing_house/src/controller/orders.rs | 10 +++++ .../clearing_house/src/controller/repeg.rs | 42 +++++++++++++++++++ programs/clearing_house/src/lib.rs | 2 + programs/clearing_house/src/math/constants.rs | 2 +- programs/clearing_house/src/math/repeg.rs | 21 +++++----- test-scripts/run-anchor-tests.sh | 4 +- 7 files changed, 71 insertions(+), 21 deletions(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 3b0333f4..6b0f403e 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -6,11 +6,9 @@ use anchor_lang::prelude::*; use crate::error::*; use crate::math::amm; use crate::math::amm::normalise_oracle_price; -use crate::math::casting::{cast, cast_to_i128, cast_to_i64}; +use crate::math::casting::{cast, cast_to_i128}; use crate::math::collateral::calculate_updated_collateral; -use crate::math::constants::{ - AMM_TO_QUOTE_PRECISION_RATIO_I128, FUNDING_PAYMENT_PRECISION, ONE_HOUR, -}; +use crate::math::constants::{AMM_TO_QUOTE_PRECISION_RATIO_I128, FUNDING_PAYMENT_PRECISION, ONE_HOUR}; use crate::math::funding::{calculate_funding_payment, calculate_funding_rate_long_short}; use crate::math::oracle; use crate::math_error; @@ -150,11 +148,10 @@ pub fn update_funding_rate( amm::update_oracle_price_twap(&mut market.amm, now, normalised_oracle_price)?; let mark_price_twap = amm::update_mark_twap(&mut market.amm, now, None)?; - let one_hour_i64 = cast_to_i64(ONE_HOUR)?; let period_adjustment = (24_i64) - .checked_mul(one_hour_i64) + .checked_mul(ONE_HOUR) .ok_or_else(math_error!())? - .checked_div(max(one_hour_i64, market.amm.funding_period)) + .checked_div(max(ONE_HOUR, market.amm.funding_period)) .ok_or_else(math_error!())?; // funding period = 1 hour, window = 1 day // low periodicity => quickly updating/settled funding rates => lower funding rate payment per interval diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index b3991e29..155aef99 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -13,6 +13,7 @@ use crate::context::*; use crate::math::{amm, fees, margin::*, orders::*}; use crate::state::market::OraclePriceData; use crate::state::{ + history::curve::{ExtendedCurveHistory, ExtendedCurveRecord}, history::order_history::{OrderHistory, OrderRecord}, history::trade::{TradeHistory, TradeRecord}, market::Markets, @@ -300,6 +301,7 @@ pub fn fill_order( trade_history: &AccountLoader, order_history: &AccountLoader, funding_rate_history: &AccountLoader, + extended_curve_history: &AccountLoader, referrer: Option>, clock: &Clock, ) -> ClearingHouseResult { @@ -641,12 +643,20 @@ pub fn fill_order( if market_index >= 12 { // todo for soft launch + let extended_curve_history = &mut extended_curve_history + .load_mut() + .or(Err(ErrorCode::UnableToLoadAccountLoader))?; + controller::repeg::formulaic_repeg( market, mark_price_after, &oracle_price_data, is_oracle_valid, fee_to_market, + extended_curve_history, + now, + market_index, + trade_record_id, )?; } } diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index ad2ca2b7..7a961e2a 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -1,5 +1,7 @@ use crate::error::*; use crate::math::casting::{cast_to_i128, cast_to_u128}; +use std::cell::{Ref, RefMut}; + use crate::math::repeg; use crate::math::amm; @@ -9,6 +11,7 @@ use crate::state::state::OracleGuardRails; use std::cmp::{max, min}; use crate::math::constants::{AMM_RESERVE_PRECISION, MARK_PRICE_PRECISION, QUOTE_PRECISION}; +use crate::state::history::curve::{ExtendedCurveHistory, ExtendedCurveRecord}; use anchor_lang::prelude::AccountInfo; use solana_program::msg; @@ -79,6 +82,10 @@ pub fn formulaic_repeg( oracle_price_data: &OraclePriceData, is_oracle_valid: bool, fee_budget: u128, + curve_history: &mut RefMut, + now: i64, + market_index: u64, + trade_record: u128, ) -> ClearingHouseResult { // backrun market swaps to do automatic on-chain repeg @@ -86,6 +93,11 @@ pub fn formulaic_repeg( return Ok(0); } + let peg_multiplier_before = market.amm.peg_multiplier; + let base_asset_reserve_before = market.amm.base_asset_reserve; + let quote_asset_reserve_before = market.amm.quote_asset_reserve; + let sqrt_k_before = market.amm.sqrt_k; + let terminal_price_before = amm::calculate_terminal_price(market)?; // let oracle_terminal_spread_before = oracle_price // .checked_sub(cast_to_i128(terminal_price_before)?) @@ -120,6 +132,36 @@ pub fn formulaic_repeg( let cost_applied = apply_cost_to_market(market, adjustment_cost)?; if cost_applied { market.amm.peg_multiplier = new_peg_candidate; + + let peg_multiplier_after = market.amm.peg_multiplier; + let base_asset_reserve_after = market.amm.base_asset_reserve; + let quote_asset_reserve_after = market.amm.quote_asset_reserve; + let sqrt_k_after = market.amm.sqrt_k; + + let record_id = curve_history.next_record_id(); + curve_history.append(ExtendedCurveRecord { + ts: now, + record_id, + market_index, + peg_multiplier_before, + base_asset_reserve_before, + quote_asset_reserve_before, + sqrt_k_before, + peg_multiplier_after, + base_asset_reserve_after, + quote_asset_reserve_after, + sqrt_k_after, + base_asset_amount_long: market.base_asset_amount_long.unsigned_abs(), + base_asset_amount_short: market.base_asset_amount_short.unsigned_abs(), + base_asset_amount: market.base_asset_amount, + open_interest: market.open_interest, + total_fee: market.amm.total_fee, + total_fee_minus_distributions: market.amm.total_fee_minus_distributions, + adjustment_cost, + oracle_price: oracle_price_data.price, + trade_record, + padding: [0; 5], + }); } } diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 78210890..d8744ab6 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -1060,6 +1060,7 @@ pub mod clearing_house { &ctx.accounts.trade_history, &ctx.accounts.order_history, &ctx.accounts.funding_rate_history, + &ctx.accounts.extended_curve_history, referrer, &Clock::get()?, )?; @@ -1131,6 +1132,7 @@ pub mod clearing_house { &ctx.accounts.trade_history, &ctx.accounts.order_history, &ctx.accounts.funding_rate_history, + &ctx.accounts.extended_curve_history, referrer, &Clock::get()?, )?; diff --git a/programs/clearing_house/src/math/constants.rs b/programs/clearing_house/src/math/constants.rs index 2cf99a49..0e0709d8 100644 --- a/programs/clearing_house/src/math/constants.rs +++ b/programs/clearing_house/src/math/constants.rs @@ -29,7 +29,7 @@ pub const SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR: u128 = 2; pub const UPDATE_K_ALLOWED_PRICE_CHANGE: u128 = MARK_PRICE_PRECISION / 10; // TIME PERIODS -pub const ONE_HOUR: i128 = 3600; +pub const ONE_HOUR: i64 = 3600; // FEES pub const DEFAULT_FEE_NUMERATOR: u128 = 10; diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 447d0d5e..df37e7f4 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -3,10 +3,10 @@ use crate::error::*; use crate::math::amm; use crate::math::amm::calculate_swap_output; use crate::math::bn; -use crate::math::casting::{cast_to_i128, cast_to_i64, cast_to_u128}; +use crate::math::casting::{cast_to_i128, cast_to_u128}; use crate::math::constants::{ AMM_RESERVE_PRECISION, AMM_TO_QUOTE_PRECISION_RATIO, MARK_PRICE_PRECISION, ONE_HOUR, - PEG_PRECISION, PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, + PEG_PRECISION, PRICE_SPREAD_PRECISION, PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, }; @@ -77,7 +77,7 @@ pub fn calculate_repeg_validity( .checked_sub(cast_to_i128(terminal_price_after)?) .ok_or_else(math_error!())?; let oracle_terminal_divergence_pct_after = oracle_terminal_spread_after - .checked_shl(10) + .checked_mul(PRICE_SPREAD_PRECISION) .ok_or_else(math_error!())? .checked_div(oracle_price) .ok_or_else(math_error!())?; @@ -252,7 +252,7 @@ pub fn calculate_budgeted_peg( .ok_or_else(math_error!())? }; - // considers pegs that act againist net market + // considers pegs that act against net market let new_budget_peg_or_free = if (delta_peg_sign > 0 && optimal_peg < new_budget_peg) || (delta_peg_sign < 0 && optimal_peg > new_budget_peg) { @@ -320,7 +320,7 @@ pub fn calculate_pool_budget( // for a single repeg, utilize the lesser of: // 1) 1 QUOTE (for soft launch) - // 2) 1/10th the excess instantaneous funding vs extropolated funding + // 2) 1/10th the excess instantaneous funding vs extrapolated funding // 3) 1/100th of the fee pool for funding/repeg let max_budget_quote = QUOTE_PRECISION; @@ -350,14 +350,13 @@ pub fn calculate_expected_funding_excess( .checked_sub(market.amm.last_oracle_price_twap) .ok_or_else(math_error!())?; - let one_hour_i64 = cast_to_i64(ONE_HOUR)?; let period_adjustment = (24_i64) - .checked_mul(one_hour_i64) + .checked_mul(ONE_HOUR) .ok_or_else(math_error!())? - .checked_div(max(one_hour_i64, market.amm.funding_period)) + .checked_div(max(ONE_HOUR, market.amm.funding_period)) .ok_or_else(math_error!())?; - let funding_excess_ev = market + let expected_funding_excess = market .base_asset_amount .checked_mul( oracle_mark_spread @@ -372,11 +371,11 @@ pub fn calculate_expected_funding_excess( )?) .ok_or_else(math_error!())?; - Ok(funding_excess_ev) + Ok(expected_funding_excess) } pub fn calculate_fee_pool(market: &Market) -> ClearingHouseResult { - let total_fee_minus_distributions_lower_bound = total_fee_lower_bound(&market)?; + let total_fee_minus_distributions_lower_bound = total_fee_lower_bound(market)?; let fee_pool = if market.amm.total_fee_minus_distributions > total_fee_minus_distributions_lower_bound { diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 8c5bfa8c..b5da5a4b 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -5,8 +5,8 @@ if [ "$1" != "--skip-build" ] fi test_files=(order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts deleteUser.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) -test_files=(formulaK.ts formulaPeg.ts) -test_files=(formulaK.ts) +# test_files=(formulaK.ts formulaPeg.ts) +# test_files=(formulaK.ts) for test_file in ${test_files[@]}; do export ANCHOR_TEST_FILE=${test_file} && anchor test --skip-build || exit 1; From 665f7e065285276473041bf3995a7e65fc538cfd Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 24 Mar 2022 13:06:58 -0400 Subject: [PATCH 31/59] comments p2 --- .../clearing_house/src/controller/funding.rs | 4 +- .../clearing_house/src/controller/repeg.rs | 14 +++-- programs/clearing_house/src/math/amm.rs | 10 ++- programs/clearing_house/src/math/constants.rs | 3 + programs/clearing_house/src/math/repeg.rs | 62 +++++++++---------- 5 files changed, 50 insertions(+), 43 deletions(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 6b0f403e..b6839e13 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -8,7 +8,9 @@ use crate::math::amm; use crate::math::amm::normalise_oracle_price; use crate::math::casting::{cast, cast_to_i128}; use crate::math::collateral::calculate_updated_collateral; -use crate::math::constants::{AMM_TO_QUOTE_PRECISION_RATIO_I128, FUNDING_PAYMENT_PRECISION, ONE_HOUR}; +use crate::math::constants::{ + AMM_TO_QUOTE_PRECISION_RATIO_I128, FUNDING_PAYMENT_PRECISION, ONE_HOUR, +}; use crate::math::funding::{calculate_funding_payment, calculate_funding_rate_long_short}; use crate::math::oracle; use crate::math_error; diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index 7a961e2a..479c99e4 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -27,7 +27,8 @@ pub fn repeg( if new_peg_candidate == market.amm.peg_multiplier { return Err(ErrorCode::InvalidRepegRedundant); } - let terminal_price_before = amm::calculate_terminal_price(market)?; + let (terminal_price_before, _terminal_quote_reserves, _terminal_base_reserves) = + amm::calculate_terminal_price_and_reserves(market)?; let (repegged_market, adjustment_cost) = repeg::adjust_peg_cost(market, new_peg_candidate)?; @@ -78,7 +79,7 @@ pub fn repeg( pub fn formulaic_repeg( market: &mut Market, - precomputed_mark_price: u128, + mark_price: u128, oracle_price_data: &OraclePriceData, is_oracle_valid: bool, fee_budget: u128, @@ -98,20 +99,21 @@ pub fn formulaic_repeg( let quote_asset_reserve_before = market.amm.quote_asset_reserve; let sqrt_k_before = market.amm.sqrt_k; - let terminal_price_before = amm::calculate_terminal_price(market)?; + let (terminal_price_before, terminal_quote_reserves, _terminal_base_reserves) = + amm::calculate_terminal_price_and_reserves(market)?; // let oracle_terminal_spread_before = oracle_price // .checked_sub(cast_to_i128(terminal_price_before)?) // .ok_or_else(math_error!())?; // max budget for single repeg what larger of pool budget and user fee budget - let pool_budget = - repeg::calculate_pool_budget(market, precomputed_mark_price, oracle_price_data)?; + let pool_budget = repeg::calculate_pool_budget(market, mark_price, oracle_price_data)?; let budget = min(fee_budget, pool_budget); let (new_peg_candidate, adjustment_cost, repegged_market) = repeg::calculate_budgeted_peg( market, + terminal_quote_reserves, budget, - precomputed_mark_price, + mark_price, cast_to_u128(oracle_price_data.price)?, )?; diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 1965c13b..67f3308b 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -36,7 +36,9 @@ pub fn calculate_price( .try_to_u128() } -pub fn calculate_terminal_price(market: &mut Market) -> ClearingHouseResult { +pub fn calculate_terminal_price_and_reserves( + market: &mut Market, +) -> ClearingHouseResult<(u128, u128, u128)> { let swap_direction = if market.base_asset_amount > 0 { SwapDirection::Add } else { @@ -55,7 +57,11 @@ pub fn calculate_terminal_price(market: &mut Market) -> ClearingHouseResult new_quote_asset_amount { + let full_budget_peg: u128 = if terminal_quote_reserves != market.amm.quote_asset_reserve { + let delta_peg_sign = if market.amm.quote_asset_reserve > terminal_quote_reserves { 1 } else { -1 @@ -214,10 +210,10 @@ pub fn calculate_budgeted_peg( market .amm .quote_asset_reserve - .checked_sub(new_quote_asset_amount) + .checked_sub(terminal_quote_reserves) .ok_or_else(math_error!())? } else { - new_quote_asset_amount + terminal_quote_reserves .checked_sub(market.amm.quote_asset_reserve) .ok_or_else(math_error!())? }; @@ -233,9 +229,7 @@ pub fn calculate_budgeted_peg( .ok_or_else(math_error!())?; let delta_peg_precision = delta_peg_multiplier - .checked_mul(PEG_PRECISION) - .ok_or_else(math_error!())? - .checked_div(MARK_PRICE_PRECISION) + .checked_div(MARK_PRICE_PRECISION / PEG_PRECISION) .ok_or_else(math_error!())?; let new_budget_peg = if delta_peg_sign > 0 { @@ -311,12 +305,12 @@ pub fn adjust_peg_cost( pub fn calculate_pool_budget( market: &Market, - precomputed_mark_price: u128, + mark_price: u128, oracle_price_data: &OraclePriceData, ) -> ClearingHouseResult { let fee_pool = calculate_fee_pool(market)?; let expected_funding_excess = - calculate_expected_funding_excess(market, oracle_price_data.price, precomputed_mark_price)?; + calculate_expected_funding_excess(market, oracle_price_data.price, mark_price)?; // for a single repeg, utilize the lesser of: // 1) 1 QUOTE (for soft launch) @@ -340,9 +334,9 @@ pub fn calculate_pool_budget( pub fn calculate_expected_funding_excess( market: &Market, oracle_price: i128, - precomputed_mark_price: u128, + mark_price: u128, ) -> ClearingHouseResult { - let oracle_mark_spread = cast_to_i128(precomputed_mark_price)? + let oracle_mark_spread = cast_to_i128(mark_price)? .checked_sub(oracle_price) .ok_or_else(math_error!())?; @@ -350,25 +344,25 @@ pub fn calculate_expected_funding_excess( .checked_sub(market.amm.last_oracle_price_twap) .ok_or_else(math_error!())?; - let period_adjustment = (24_i64) - .checked_mul(ONE_HOUR) - .ok_or_else(math_error!())? - .checked_div(max(ONE_HOUR, market.amm.funding_period)) + let current_twap_spread = oracle_mark_spread + .checked_sub(oracle_mark_twap_spread) .ok_or_else(math_error!())?; + let period_adjustment = cast_to_i128( + (24_i64) + .checked_mul(ONE_HOUR) + .ok_or_else(math_error!())? + .checked_div(max(ONE_HOUR, market.amm.funding_period)) + .ok_or_else(math_error!())?, + )?; + let expected_funding_excess = market .base_asset_amount - .checked_mul( - oracle_mark_spread - .checked_sub(oracle_mark_twap_spread) - .ok_or_else(math_error!())?, - ) + .checked_mul(current_twap_spread) .ok_or_else(math_error!())? - .checked_div(cast_to_i128(period_adjustment)?) + .checked_div(period_adjustment) .ok_or_else(math_error!())? - .checked_div(cast_to_i128( - MARK_PRICE_PRECISION * AMM_RESERVE_PRECISION / QUOTE_PRECISION, - )?) + .checked_div(FUNDING_EXCESS_TO_QUOTE_RATIO) .ok_or_else(math_error!())?; Ok(expected_funding_excess) From 36a288d4b118d3855624bff8b8c9615539d751eb Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 24 Mar 2022 13:08:07 -0400 Subject: [PATCH 32/59] fix bug --- programs/clearing_house/src/math/constants.rs | 2 +- test-scripts/run-anchor-tests.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/clearing_house/src/math/constants.rs b/programs/clearing_house/src/math/constants.rs index 8a7a6484..a3e698ac 100644 --- a/programs/clearing_house/src/math/constants.rs +++ b/programs/clearing_house/src/math/constants.rs @@ -24,7 +24,7 @@ pub const MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO: u128 = MARK_PRICE_PRECISION * AMM_TO_QUOTE_PRECISION_RATIO; // expo 17 pub const FUNDING_EXCESS_TO_QUOTE_RATIO: i128 = - MARK_PRICE_PRECISION * AMM_RESERVE_PRECISION / QUOTE_PRECISION; // expo 11 + (MARK_PRICE_PRECISION * AMM_RESERVE_PRECISION / QUOTE_PRECISION) as i128; // expo 11 // FEE REBATES pub const SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR: u128 = 1; diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index b5da5a4b..7d11790b 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -5,7 +5,7 @@ if [ "$1" != "--skip-build" ] fi test_files=(order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts deleteUser.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) -# test_files=(formulaK.ts formulaPeg.ts) +test_files=(formulaPeg.ts) # test_files=(formulaK.ts) for test_file in ${test_files[@]}; do From d1c5327c6f70b29a284233166c84ff5a2f0a48bb Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Fri, 25 Mar 2022 10:56:00 -0400 Subject: [PATCH 33/59] add back k budget --- .../clearing_house/src/controller/funding.rs | 31 ++++++++- programs/clearing_house/src/math/amm.rs | 64 +++++++++++++++++++ test-scripts/run-anchor-tests.sh | 2 +- 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index b6839e13..93dab46f 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -5,7 +5,7 @@ use anchor_lang::prelude::*; use crate::error::*; use crate::math::amm; -use crate::math::amm::normalise_oracle_price; +use crate::math::amm::{budget_k_adjustment, normalise_oracle_price}; use crate::math::casting::{cast, cast_to_i128}; use crate::math::collateral::calculate_updated_collateral; use crate::math::constants::{ @@ -176,6 +176,35 @@ pub fn update_funding_rate( let (funding_rate_long, funding_rate_short) = calculate_funding_rate_long_short(market, funding_rate)?; + // dynamic k + let funding_imbalance_cost = funding_rate + .checked_mul(market.base_asset_amount) + .ok_or_else(math_error!())? + .checked_div( + AMM_TO_QUOTE_PRECISION_RATIO_I128 * cast_to_i128(FUNDING_PAYMENT_PRECISION)?, + ) + .ok_or_else(math_error!())?; + + let budget = if funding_imbalance_cost < 0 { + // negative cost is period revenue, give back half in k increase + funding_imbalance_cost + .checked_div(2) + .ok_or_else(math_error!())? + } else if market.amm.net_revenue_since_last_funding < (funding_imbalance_cost as i64) { + // cost exceeded period revenue, take back half in k decrease + max(0, market.amm.net_revenue_since_last_funding) + .checked_sub(funding_imbalance_cost as i64) + .ok_or_else(math_error!())? + .checked_div(2) + .ok_or_else(math_error!())? as i128 + } else { + 0 + }; + if budget != 0 { + let (p_numer, p_denom) = budget_k_adjustment(market, budget)?; + msg!("update k by {:?}/{:?}", p_numer, p_denom); + } + market.amm.cumulative_funding_rate_long = market .amm .cumulative_funding_rate_long diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 67f3308b..ed9acd53 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -453,6 +453,70 @@ pub fn is_oracle_valid( || is_conf_too_large)) } +pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseResult<(i128, i128)> { + let y = market.amm.quote_asset_reserve; + let x = market.amm.base_asset_reserve; + let c = budget; + let q = cast_to_i128(market.amm.peg_multiplier)?; + let d = market.base_asset_amount; + + let AMM_RESERVE_PRECISIONi128 = cast_to_i128(AMM_RESERVE_PRECISION)?; + + let x_d = cast_to_i128(x)?.checked_add(d).ok_or_else(math_error!())?; + + let numer1 = cast_to_i128(y)? + .checked_mul(d) + .ok_or_else(math_error!())? + .checked_mul(q) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128(AMM_RESERVE_PRECISION * PEG_PRECISION)?) + .ok_or_else(math_error!())?; + let numer2 = c + .checked_mul(x_d) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128(QUOTE_PRECISION)?) + .ok_or_else(math_error!())?; + let denom1 = c + .checked_mul(cast_to_i128(x)?) + .ok_or_else(math_error!())? + .checked_mul(x_d) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128(AMM_RESERVE_PRECISION * QUOTE_PRECISION)?) + .ok_or_else(math_error!())?; + let denom2 = cast_to_i128(y)? + .checked_mul(d) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())? + .checked_mul(d) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())? + .checked_mul(q) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128(PEG_PRECISION)?) + .ok_or_else(math_error!())?; + + let numerator = d + .checked_mul(numer1.checked_add(numer2).ok_or_else(math_error!())?) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())? + .checked_div(cast_to_i128(AMM_TO_QUOTE_PRECISION_RATIO)?) + .ok_or_else(math_error!())?; + let denominator = denom1 + .checked_add(denom2) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISIONi128) + .ok_or_else(math_error!())?; + + Ok((numerator, denominator)) +} + /// To find the cost of adjusting k, compare the the net market value before and after adjusting k /// Increasing k costs the protocol money because it reduces slippage and improves the exit price for net market position /// Decreasing k costs the protocol money because it increases slippage and hurts the exit price for net market position diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 7d11790b..419beb63 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -6,7 +6,7 @@ fi test_files=(order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts deleteUser.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) test_files=(formulaPeg.ts) -# test_files=(formulaK.ts) +test_files=(formulaK.ts) for test_file in ${test_files[@]}; do export ANCHOR_TEST_FILE=${test_file} && anchor test --skip-build || exit 1; From 5333cd4e16582af8bcb3a0d46ade5991e831cb92 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Mon, 4 Apr 2022 15:01:35 -0400 Subject: [PATCH 34/59] resolve conflicts --- sdk/src/idl/clearing_house.json | 8606 ++++++++++++++++--------------- sdk/src/math/amm.ts | 13 - 2 files changed, 4309 insertions(+), 4310 deletions(-) diff --git a/sdk/src/idl/clearing_house.json b/sdk/src/idl/clearing_house.json index 597d8962..7bd288a1 100644 --- a/sdk/src/idl/clearing_house.json +++ b/sdk/src/idl/clearing_house.json @@ -1,4298 +1,4310 @@ { - "version": "1.0.0", - "name": "clearing_house", - "instructions": [ - { - "name": "initialize", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - }, - { - "name": "collateralMint", - "isMut": false, - "isSigner": false - }, - { - "name": "collateralVault", - "isMut": true, - "isSigner": false - }, - { - "name": "collateralVaultAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "insuranceVault", - "isMut": true, - "isSigner": false - }, - { - "name": "insuranceVaultAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "rent", - "isMut": false, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "clearingHouseNonce", - "type": "u8" - }, - { - "name": "collateralVaultNonce", - "type": "u8" - }, - { - "name": "insuranceVaultNonce", - "type": "u8" - }, - { - "name": "adminControlsPrices", - "type": "bool" - } - ] - }, - { - "name": "initializeHistory", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "tradeHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "liquidationHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "depositHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingRateHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "curveHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [] - }, - { - "name": "initializeOrderState", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - }, - { - "name": "orderState", - "isMut": true, - "isSigner": false - }, - { - "name": "orderHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "rent", - "isMut": false, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "orderHouseNonce", - "type": "u8" - } - ] - }, - { - "name": "initializeMarket", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "oracle", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "ammBaseAssetReserve", - "type": "u128" - }, - { - "name": "ammQuoteAssetReserve", - "type": "u128" - }, - { - "name": "ammPeriodicity", - "type": "i64" - }, - { - "name": "ammPegMultiplier", - "type": "u128" - }, - { - "name": "oracleSource", - "type": { - "defined": "OracleSource" - } - }, - { - "name": "marginRatioInitial", - "type": "u32" - }, - { - "name": "marginRatioPartial", - "type": "u32" - }, - { - "name": "marginRatioMaintenance", - "type": "u32" - } - ] - }, - { - "name": "depositCollateral", - "accounts": [ - { - "name": "state", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "collateralVault", - "isMut": true, - "isSigner": false - }, - { - "name": "userCollateralAccount", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": false, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "depositHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - } - ] - }, - { - "name": "withdrawCollateral", - "accounts": [ - { - "name": "state", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "collateralVault", - "isMut": true, - "isSigner": false - }, - { - "name": "collateralVaultAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "insuranceVault", - "isMut": true, - "isSigner": false - }, - { - "name": "insuranceVaultAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "userCollateralAccount", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": false, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "depositHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - } - ] - }, - { - "name": "openPosition", - "accounts": [ - { - "name": "state", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "tradeHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingRateHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "oracle", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "direction", - "type": { - "defined": "PositionDirection" - } - }, - { - "name": "quoteAssetAmount", - "type": "u128" - }, - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "limitPrice", - "type": "u128" - }, - { - "name": "optionalAccounts", - "type": { - "defined": "ManagePositionOptionalAccounts" - } - } - ] - }, - { - "name": "closePosition", - "accounts": [ - { - "name": "state", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "tradeHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingRateHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "oracle", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "optionalAccounts", - "type": { - "defined": "ManagePositionOptionalAccounts" - } - } - ] - }, - { - "name": "placeOrder", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "orderState", - "isMut": false, - "isSigner": false - }, - { - "name": "user", - "isMut": false, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "markets", - "isMut": false, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "userOrders", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "orderHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "params", - "type": { - "defined": "OrderParams" - } - } - ] - }, - { - "name": "cancelOrder", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "orderState", - "isMut": false, - "isSigner": false - }, - { - "name": "user", - "isMut": false, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "markets", - "isMut": false, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "userOrders", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "orderHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "orderId", - "type": "u128" - } - ] - }, - { - "name": "cancelOrderByUserId", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "orderState", - "isMut": false, - "isSigner": false - }, - { - "name": "user", - "isMut": false, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "markets", - "isMut": false, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "userOrders", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "orderHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "userOrderId", - "type": "u8" - } - ] - }, - { - "name": "cancelAllOrders", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "orderState", - "isMut": false, - "isSigner": false - }, - { - "name": "user", - "isMut": false, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "markets", - "isMut": false, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "userOrders", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "orderHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [] - }, - { - "name": "expireOrders", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "orderState", - "isMut": false, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "filler", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "userOrders", - "isMut": true, - "isSigner": false - }, - { - "name": "orderHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [] - }, - { - "name": "fillOrder", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "orderState", - "isMut": false, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "filler", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "userOrders", - "isMut": true, - "isSigner": false - }, - { - "name": "tradeHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingRateHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "orderHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "extendedCurveHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "oracle", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "orderId", - "type": "u128" - } - ] - }, - { - "name": "placeAndFillOrder", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "orderState", - "isMut": false, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "userOrders", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "tradeHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingRateHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "orderHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "extendedCurveHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "oracle", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "params", - "type": { - "defined": "OrderParams" - } - } - ] - }, - { - "name": "liquidate", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "liquidator", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "collateralVault", - "isMut": true, - "isSigner": false - }, - { - "name": "collateralVaultAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "insuranceVault", - "isMut": true, - "isSigner": false - }, - { - "name": "insuranceVaultAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "tradeHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "liquidationHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [] - }, - { - "name": "moveAmmPrice", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "baseAssetReserve", - "type": "u128" - }, - { - "name": "quoteAssetReserve", - "type": "u128" - }, - { - "name": "marketIndex", - "type": "u64" - } - ] - }, - { - "name": "withdrawFees", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "collateralVault", - "isMut": true, - "isSigner": false - }, - { - "name": "collateralVaultAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "recipient", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "amount", - "type": "u64" - } - ] - }, - { - "name": "withdrawFromInsuranceVault", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "insuranceVault", - "isMut": true, - "isSigner": false - }, - { - "name": "insuranceVaultAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "recipient", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - } - ] - }, - { - "name": "withdrawFromInsuranceVaultToMarket", - "accounts": [ - { - "name": "state", - "isMut": true, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "insuranceVault", - "isMut": true, - "isSigner": false - }, - { - "name": "insuranceVaultAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "collateralVault", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "amount", - "type": "u64" - } - ] - }, - { - "name": "repegAmmCurve", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "oracle", - "isMut": false, - "isSigner": false - }, - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "curveHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "newPegCandidate", - "type": "u128" - }, - { - "name": "marketIndex", - "type": "u64" - } - ] - }, - { - "name": "updateAmmOracleTwap", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "oracle", - "isMut": false, - "isSigner": false - }, - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "curveHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - } - ] - }, - { - "name": "resetAmmOracleTwap", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "oracle", - "isMut": false, - "isSigner": false - }, - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "curveHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - } - ] - }, - { - "name": "initializeUser", - "accounts": [ - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": true - }, - { - "name": "authority", - "isMut": true, - "isSigner": true - }, - { - "name": "rent", - "isMut": false, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "userNonce", - "type": "u8" - }, - { - "name": "optionalAccounts", - "type": { - "defined": "InitializeUserOptionalAccounts" - } - } - ] - }, - { - "name": "initializeUserWithExplicitPayer", - "accounts": [ - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": true - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "payer", - "isMut": true, - "isSigner": true - }, - { - "name": "rent", - "isMut": false, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "userNonce", - "type": "u8" - }, - { - "name": "optionalAccounts", - "type": { - "defined": "InitializeUserOptionalAccounts" - } - } - ] - }, - { - "name": "initializeUserOrders", - "accounts": [ - { - "name": "user", - "isMut": false, - "isSigner": false - }, - { - "name": "userOrders", - "isMut": true, - "isSigner": false - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "rent", - "isMut": false, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "userOrdersNonce", - "type": "u8" - } - ] - }, - { - "name": "initializeUserOrdersWithExplicitPayer", - "accounts": [ - { - "name": "user", - "isMut": false, - "isSigner": false - }, - { - "name": "userOrders", - "isMut": true, - "isSigner": false - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "payer", - "isMut": true, - "isSigner": true - }, - { - "name": "rent", - "isMut": false, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "userOrdersNonce", - "type": "u8" - } - ] - }, - { - "name": "deleteUser", - "accounts": [ - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "userOrders", - "isMut": true, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - } - ], - "args": [] - }, - { - "name": "settleFundingPayment", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "markets", - "isMut": false, - "isSigner": false - }, - { - "name": "userPositions", - "isMut": true, - "isSigner": false - }, - { - "name": "fundingPaymentHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [] - }, - { - "name": "updateFundingRate", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "oracle", - "isMut": false, - "isSigner": false - }, - { - "name": "fundingRateHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - } - ] - }, - { - "name": "updateK", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - }, - { - "name": "oracle", - "isMut": false, - "isSigner": false - }, - { - "name": "curveHistory", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "sqrtK", - "type": "u128" - }, - { - "name": "marketIndex", - "type": "u64" - } - ] - }, - { - "name": "updateCurveHistory", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - }, - { - "name": "extendedCurveHistory", - "isMut": true, - "isSigner": false - }, - { - "name": "curveHistory", - "isMut": false, - "isSigner": false - } - ], - "args": [] - }, - { - "name": "updateMarginRatio", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "marginRatioInitial", - "type": "u32" - }, - { - "name": "marginRatioPartial", - "type": "u32" - }, - { - "name": "marginRatioMaintenance", - "type": "u32" - } - ] - }, - { - "name": "updatePartialLiquidationClosePercentage", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "numerator", - "type": "u128" - }, - { - "name": "denominator", - "type": "u128" - } - ] - }, - { - "name": "updatePartialLiquidationPenaltyPercentage", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "numerator", - "type": "u128" - }, - { - "name": "denominator", - "type": "u128" - } - ] - }, - { - "name": "updateFullLiquidationPenaltyPercentage", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "numerator", - "type": "u128" - }, - { - "name": "denominator", - "type": "u128" - } - ] - }, - { - "name": "updatePartialLiquidationLiquidatorShareDenominator", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "denominator", - "type": "u64" - } - ] - }, - { - "name": "updateFullLiquidationLiquidatorShareDenominator", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "denominator", - "type": "u64" - } - ] - }, - { - "name": "updateFee", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "fees", - "type": { - "defined": "FeeStructure" - } - } - ] - }, - { - "name": "updateOrderFillerRewardStructure", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "orderState", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "orderFillerRewardStructure", - "type": { - "defined": "OrderFillerRewardStructure" - } - } - ] - }, - { - "name": "updateOracleGuardRails", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "oracleGuardRails", - "type": { - "defined": "OracleGuardRails" - } - } - ] - }, - { - "name": "updateMarketOracle", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "oracle", - "type": "publicKey" - }, - { - "name": "oracleSource", - "type": { - "defined": "OracleSource" - } - } - ] - }, - { - "name": "updateMarketMinimumQuoteAssetTradeSize", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "minimumTradeSize", - "type": "u128" - } - ] - }, - { - "name": "updateMarketMinimumBaseAssetTradeSize", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "markets", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "minimumTradeSize", - "type": "u128" - } - ] - }, - { - "name": "updateAdmin", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "admin", - "type": "publicKey" - } - ] - }, - { - "name": "updateWhitelistMint", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "whitelistMint", - "type": "publicKey" - } - ] - }, - { - "name": "updateDiscountMint", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "discountMint", - "type": "publicKey" - } - ] - }, - { - "name": "updateMaxDeposit", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "maxDeposit", - "type": "u128" - } - ] - }, - { - "name": "updateExchangePaused", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "exchangePaused", - "type": "bool" - } - ] - }, - { - "name": "disableAdminControlsPrices", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [] - }, - { - "name": "updateFundingPaused", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "fundingPaused", - "type": "bool" - } - ] - } - ], - "accounts": [ - { - "name": "CurveHistory", - "type": { - "kind": "struct", - "fields": [ - { - "name": "head", - "type": "u64" - }, - { - "name": "curveRecords", - "type": { - "array": [ - { - "defined": "CurveRecord" - }, - 32 - ] - } - } - ] - } - }, - { - "name": "ExtendedCurveHistory", - "type": { - "kind": "struct", - "fields": [ - { - "name": "head", - "type": "u64" - }, - { - "name": "curveRecords", - "type": { - "array": [ - { - "defined": "ExtendedCurveRecord" - }, - 1024 - ] - } - } - ] - } - }, - { - "name": "DepositHistory", - "type": { - "kind": "struct", - "fields": [ - { - "name": "head", - "type": "u64" - }, - { - "name": "depositRecords", - "type": { - "array": [ - { - "defined": "DepositRecord" - }, - 1024 - ] - } - } - ] - } - }, - { - "name": "FundingPaymentHistory", - "type": { - "kind": "struct", - "fields": [ - { - "name": "head", - "type": "u64" - }, - { - "name": "fundingPaymentRecords", - "type": { - "array": [ - { - "defined": "FundingPaymentRecord" - }, - 1024 - ] - } - } - ] - } - }, - { - "name": "FundingRateHistory", - "type": { - "kind": "struct", - "fields": [ - { - "name": "head", - "type": "u64" - }, - { - "name": "fundingRateRecords", - "type": { - "array": [ - { - "defined": "FundingRateRecord" - }, - 1024 - ] - } - } - ] - } - }, - { - "name": "LiquidationHistory", - "type": { - "kind": "struct", - "fields": [ - { - "name": "head", - "type": "u64" - }, - { - "name": "liquidationRecords", - "type": { - "array": [ - { - "defined": "LiquidationRecord" - }, - 1024 - ] - } - } - ] - } - }, - { - "name": "Markets", - "type": { - "kind": "struct", - "fields": [ - { - "name": "markets", - "type": { - "array": [ - { - "defined": "Market" - }, - 64 - ] - } - } - ] - } - }, - { - "name": "OrderHistory", - "type": { - "kind": "struct", - "fields": [ - { - "name": "head", - "type": "u64" - }, - { - "name": "lastOrderId", - "type": "u128" - }, - { - "name": "orderRecords", - "type": { - "array": [ - { - "defined": "OrderRecord" - }, - 1024 - ] - } - } - ] - } - }, - { - "name": "OrderState", - "type": { - "kind": "struct", - "fields": [ - { - "name": "orderHistory", - "type": "publicKey" - }, - { - "name": "orderFillerRewardStructure", - "type": { - "defined": "OrderFillerRewardStructure" - } - }, - { - "name": "minOrderQuoteAssetAmount", - "type": "u128" - }, - { - "name": "padding", - "type": { - "array": ["u128", 10] - } - } - ] - } - }, - { - "name": "State", - "type": { - "kind": "struct", - "fields": [ - { - "name": "admin", - "type": "publicKey" - }, - { - "name": "exchangePaused", - "type": "bool" - }, - { - "name": "fundingPaused", - "type": "bool" - }, - { - "name": "adminControlsPrices", - "type": "bool" - }, - { - "name": "collateralMint", - "type": "publicKey" - }, - { - "name": "collateralVault", - "type": "publicKey" - }, - { - "name": "collateralVaultAuthority", - "type": "publicKey" - }, - { - "name": "collateralVaultNonce", - "type": "u8" - }, - { - "name": "depositHistory", - "type": "publicKey" - }, - { - "name": "tradeHistory", - "type": "publicKey" - }, - { - "name": "fundingPaymentHistory", - "type": "publicKey" - }, - { - "name": "fundingRateHistory", - "type": "publicKey" - }, - { - "name": "liquidationHistory", - "type": "publicKey" - }, - { - "name": "curveHistory", - "type": "publicKey" - }, - { - "name": "insuranceVault", - "type": "publicKey" - }, - { - "name": "insuranceVaultAuthority", - "type": "publicKey" - }, - { - "name": "insuranceVaultNonce", - "type": "u8" - }, - { - "name": "markets", - "type": "publicKey" - }, - { - "name": "marginRatioInitial", - "type": "u128" - }, - { - "name": "marginRatioMaintenance", - "type": "u128" - }, - { - "name": "marginRatioPartial", - "type": "u128" - }, - { - "name": "partialLiquidationClosePercentageNumerator", - "type": "u128" - }, - { - "name": "partialLiquidationClosePercentageDenominator", - "type": "u128" - }, - { - "name": "partialLiquidationPenaltyPercentageNumerator", - "type": "u128" - }, - { - "name": "partialLiquidationPenaltyPercentageDenominator", - "type": "u128" - }, - { - "name": "fullLiquidationPenaltyPercentageNumerator", - "type": "u128" - }, - { - "name": "fullLiquidationPenaltyPercentageDenominator", - "type": "u128" - }, - { - "name": "partialLiquidationLiquidatorShareDenominator", - "type": "u64" - }, - { - "name": "fullLiquidationLiquidatorShareDenominator", - "type": "u64" - }, - { - "name": "feeStructure", - "type": { - "defined": "FeeStructure" - } - }, - { - "name": "whitelistMint", - "type": "publicKey" - }, - { - "name": "discountMint", - "type": "publicKey" - }, - { - "name": "oracleGuardRails", - "type": { - "defined": "OracleGuardRails" - } - }, - { - "name": "maxDeposit", - "type": "u128" - }, - { - "name": "extendedCurveHistory", - "type": "publicKey" - }, - { - "name": "orderState", - "type": "publicKey" - }, - { - "name": "padding0", - "type": "u128" - }, - { - "name": "padding1", - "type": "u128" - }, - { - "name": "padding2", - "type": "u128" - }, - { - "name": "padding3", - "type": "u128" - } - ] - } - }, - { - "name": "TradeHistory", - "type": { - "kind": "struct", - "fields": [ - { - "name": "head", - "type": "u64" - }, - { - "name": "tradeRecords", - "type": { - "array": [ - { - "defined": "TradeRecord" - }, - 1024 - ] - } - } - ] - } - }, - { - "name": "User", - "type": { - "kind": "struct", - "fields": [ - { - "name": "authority", - "type": "publicKey" - }, - { - "name": "collateral", - "type": "u128" - }, - { - "name": "cumulativeDeposits", - "type": "i128" - }, - { - "name": "totalFeePaid", - "type": "u128" - }, - { - "name": "totalTokenDiscount", - "type": "u128" - }, - { - "name": "totalReferralReward", - "type": "u128" - }, - { - "name": "totalRefereeDiscount", - "type": "u128" - }, - { - "name": "positions", - "type": "publicKey" - }, - { - "name": "padding0", - "type": "u128" - }, - { - "name": "padding1", - "type": "u128" - }, - { - "name": "padding2", - "type": "u128" - }, - { - "name": "padding3", - "type": "u128" - } - ] - } - }, - { - "name": "UserPositions", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "publicKey" - }, - { - "name": "positions", - "type": { - "array": [ - { - "defined": "MarketPosition" - }, - 5 - ] - } - } - ] - } - }, - { - "name": "UserOrders", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "publicKey" - }, - { - "name": "orders", - "type": { - "array": [ - { - "defined": "Order" - }, - 32 - ] - } - } - ] - } - } - ], - "types": [ - { - "name": "InitializeUserOptionalAccounts", - "type": { - "kind": "struct", - "fields": [ - { - "name": "whitelistToken", - "type": "bool" - } - ] - } - }, - { - "name": "ManagePositionOptionalAccounts", - "type": { - "kind": "struct", - "fields": [ - { - "name": "discountToken", - "type": "bool" - }, - { - "name": "referrer", - "type": "bool" - } - ] - } - }, - { - "name": "OrderParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "orderType", - "type": { - "defined": "OrderType" - } - }, - { - "name": "direction", - "type": { - "defined": "PositionDirection" - } - }, - { - "name": "userOrderId", - "type": "u8" - }, - { - "name": "quoteAssetAmount", - "type": "u128" - }, - { - "name": "baseAssetAmount", - "type": "u128" - }, - { - "name": "price", - "type": "u128" - }, - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "reduceOnly", - "type": "bool" - }, - { - "name": "postOnly", - "type": "bool" - }, - { - "name": "immediateOrCancel", - "type": "bool" - }, - { - "name": "triggerPrice", - "type": "u128" - }, - { - "name": "triggerCondition", - "type": { - "defined": "OrderTriggerCondition" - } - }, - { - "name": "optionalAccounts", - "type": { - "defined": "OrderParamsOptionalAccounts" - } - }, - { - "name": "positionLimit", - "type": "u128" - }, - { - "name": "oraclePriceOffset", - "type": "i128" - }, - { - "name": "padding0", - "type": "bool" - }, - { - "name": "padding1", - "type": "bool" - } - ] - } - }, - { - "name": "OrderParamsOptionalAccounts", - "type": { - "kind": "struct", - "fields": [ - { - "name": "discountToken", - "type": "bool" - }, - { - "name": "referrer", - "type": "bool" - } - ] - } - }, - { - "name": "CurveRecord", - "type": { - "kind": "struct", - "fields": [ - { - "name": "ts", - "type": "i64" - }, - { - "name": "recordId", - "type": "u128" - }, - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "pegMultiplierBefore", - "type": "u128" - }, - { - "name": "baseAssetReserveBefore", - "type": "u128" - }, - { - "name": "quoteAssetReserveBefore", - "type": "u128" - }, - { - "name": "sqrtKBefore", - "type": "u128" - }, - { - "name": "pegMultiplierAfter", - "type": "u128" - }, - { - "name": "baseAssetReserveAfter", - "type": "u128" - }, - { - "name": "quoteAssetReserveAfter", - "type": "u128" - }, - { - "name": "sqrtKAfter", - "type": "u128" - }, - { - "name": "baseAssetAmountLong", - "type": "u128" - }, - { - "name": "baseAssetAmountShort", - "type": "u128" - }, - { - "name": "baseAssetAmount", - "type": "i128" - }, - { - "name": "openInterest", - "type": "u128" - }, - { - "name": "totalFee", - "type": "u128" - }, - { - "name": "totalFeeMinusDistributions", - "type": "u128" - }, - { - "name": "adjustmentCost", - "type": "i128" - } - ] - } - }, - { - "name": "ExtendedCurveRecord", - "type": { - "kind": "struct", - "fields": [ - { - "name": "ts", - "type": "i64" - }, - { - "name": "recordId", - "type": "u128" - }, - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "pegMultiplierBefore", - "type": "u128" - }, - { - "name": "baseAssetReserveBefore", - "type": "u128" - }, - { - "name": "quoteAssetReserveBefore", - "type": "u128" - }, - { - "name": "sqrtKBefore", - "type": "u128" - }, - { - "name": "pegMultiplierAfter", - "type": "u128" - }, - { - "name": "baseAssetReserveAfter", - "type": "u128" - }, - { - "name": "quoteAssetReserveAfter", - "type": "u128" - }, - { - "name": "sqrtKAfter", - "type": "u128" - }, - { - "name": "baseAssetAmountLong", - "type": "u128" - }, - { - "name": "baseAssetAmountShort", - "type": "u128" - }, - { - "name": "baseAssetAmount", - "type": "i128" - }, - { - "name": "openInterest", - "type": "u128" - }, - { - "name": "totalFee", - "type": "u128" - }, - { - "name": "totalFeeMinusDistributions", - "type": "u128" - }, - { - "name": "adjustmentCost", - "type": "i128" - }, - { - "name": "oraclePrice", - "type": "i128" - }, - { - "name": "tradeRecord", - "type": "u128" - }, - { - "name": "padding", - "type": { - "array": ["u128", 5] - } - } - ] - } - }, - { - "name": "DepositRecord", - "type": { - "kind": "struct", - "fields": [ - { - "name": "ts", - "type": "i64" - }, - { - "name": "recordId", - "type": "u128" - }, - { - "name": "userAuthority", - "type": "publicKey" - }, - { - "name": "user", - "type": "publicKey" - }, - { - "name": "direction", - "type": { - "defined": "DepositDirection" - } - }, - { - "name": "collateralBefore", - "type": "u128" - }, - { - "name": "cumulativeDepositsBefore", - "type": "i128" - }, - { - "name": "amount", - "type": "u64" - } - ] - } - }, - { - "name": "FundingPaymentRecord", - "type": { - "kind": "struct", - "fields": [ - { - "name": "ts", - "type": "i64" - }, - { - "name": "recordId", - "type": "u128" - }, - { - "name": "userAuthority", - "type": "publicKey" - }, - { - "name": "user", - "type": "publicKey" - }, - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "fundingPayment", - "type": "i128" - }, - { - "name": "baseAssetAmount", - "type": "i128" - }, - { - "name": "userLastCumulativeFunding", - "type": "i128" - }, - { - "name": "userLastFundingRateTs", - "type": "i64" - }, - { - "name": "ammCumulativeFundingLong", - "type": "i128" - }, - { - "name": "ammCumulativeFundingShort", - "type": "i128" - } - ] - } - }, - { - "name": "FundingRateRecord", - "type": { - "kind": "struct", - "fields": [ - { - "name": "ts", - "type": "i64" - }, - { - "name": "recordId", - "type": "u128" - }, - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "fundingRate", - "type": "i128" - }, - { - "name": "cumulativeFundingRateLong", - "type": "i128" - }, - { - "name": "cumulativeFundingRateShort", - "type": "i128" - }, - { - "name": "oraclePriceTwap", - "type": "i128" - }, - { - "name": "markPriceTwap", - "type": "u128" - } - ] - } - }, - { - "name": "LiquidationRecord", - "type": { - "kind": "struct", - "fields": [ - { - "name": "ts", - "type": "i64" - }, - { - "name": "recordId", - "type": "u128" - }, - { - "name": "userAuthority", - "type": "publicKey" - }, - { - "name": "user", - "type": "publicKey" - }, - { - "name": "partial", - "type": "bool" - }, - { - "name": "baseAssetValue", - "type": "u128" - }, - { - "name": "baseAssetValueClosed", - "type": "u128" - }, - { - "name": "liquidationFee", - "type": "u128" - }, - { - "name": "feeToLiquidator", - "type": "u64" - }, - { - "name": "feeToInsuranceFund", - "type": "u64" - }, - { - "name": "liquidator", - "type": "publicKey" - }, - { - "name": "totalCollateral", - "type": "u128" - }, - { - "name": "collateral", - "type": "u128" - }, - { - "name": "unrealizedPnl", - "type": "i128" - }, - { - "name": "marginRatio", - "type": "u128" - } - ] - } - }, - { - "name": "Market", - "type": { - "kind": "struct", - "fields": [ - { - "name": "initialized", - "type": "bool" - }, - { - "name": "baseAssetAmountLong", - "type": "i128" - }, - { - "name": "baseAssetAmountShort", - "type": "i128" - }, - { - "name": "baseAssetAmount", - "type": "i128" - }, - { - "name": "openInterest", - "type": "u128" - }, - { - "name": "amm", - "type": { - "defined": "AMM" - } - }, - { - "name": "marginRatioInitial", - "type": "u32" - }, - { - "name": "marginRatioPartial", - "type": "u32" - }, - { - "name": "marginRatioMaintenance", - "type": "u32" - }, - { - "name": "padding0", - "type": "u32" - }, - { - "name": "padding1", - "type": "u128" - }, - { - "name": "padding2", - "type": "u128" - }, - { - "name": "padding3", - "type": "u128" - }, - { - "name": "padding4", - "type": "u128" - } - ] - } - }, - { - "name": "AMM", - "type": { - "kind": "struct", - "fields": [ - { - "name": "oracle", - "type": "publicKey" - }, - { - "name": "oracleSource", - "type": { - "defined": "OracleSource" - } - }, - { - "name": "baseAssetReserve", - "type": "u128" - }, - { - "name": "quoteAssetReserve", - "type": "u128" - }, - { - "name": "cumulativeRepegRebateLong", - "type": "u128" - }, - { - "name": "cumulativeRepegRebateShort", - "type": "u128" - }, - { - "name": "cumulativeFundingRateLong", - "type": "i128" - }, - { - "name": "cumulativeFundingRateShort", - "type": "i128" - }, - { - "name": "lastFundingRate", - "type": "i128" - }, - { - "name": "lastFundingRateTs", - "type": "i64" - }, - { - "name": "fundingPeriod", - "type": "i64" - }, - { - "name": "lastOraclePriceTwap", - "type": "i128" - }, - { - "name": "lastMarkPriceTwap", - "type": "u128" - }, - { - "name": "lastMarkPriceTwapTs", - "type": "i64" - }, - { - "name": "sqrtK", - "type": "u128" - }, - { - "name": "pegMultiplier", - "type": "u128" - }, - { - "name": "totalFee", - "type": "u128" - }, - { - "name": "totalFeeMinusDistributions", - "type": "u128" - }, - { - "name": "totalFeeWithdrawn", - "type": "u128" - }, - { - "name": "minimumQuoteAssetTradeSize", - "type": "u128" - }, - { - "name": "lastOraclePriceTwapTs", - "type": "i64" - }, - { - "name": "lastOraclePrice", - "type": "i128" - }, - { - "name": "minimumBaseAssetTradeSize", - "type": "u128" - }, - { - "name": "netRevenueSinceLastFunding", - "type": "i64" - }, - { - "name": "padding2", - "type": "u128" - }, - { - "name": "padding3", - "type": "u128" - } - ] - } - }, - { - "name": "OrderRecord", - "type": { - "kind": "struct", - "fields": [ - { - "name": "ts", - "type": "i64" - }, - { - "name": "recordId", - "type": "u128" - }, - { - "name": "user", - "type": "publicKey" - }, - { - "name": "authority", - "type": "publicKey" - }, - { - "name": "order", - "type": { - "defined": "Order" - } - }, - { - "name": "action", - "type": { - "defined": "OrderAction" - } - }, - { - "name": "filler", - "type": "publicKey" - }, - { - "name": "tradeRecordId", - "type": "u128" - }, - { - "name": "baseAssetAmountFilled", - "type": "u128" - }, - { - "name": "quoteAssetAmountFilled", - "type": "u128" - }, - { - "name": "fee", - "type": "u128" - }, - { - "name": "fillerReward", - "type": "u128" - }, - { - "name": "quoteAssetAmountSurplus", - "type": "u128" - }, - { - "name": "padding", - "type": { - "array": ["u64", 8] - } - } - ] - } - }, - { - "name": "OrderFillerRewardStructure", - "type": { - "kind": "struct", - "fields": [ - { - "name": "rewardNumerator", - "type": "u128" - }, - { - "name": "rewardDenominator", - "type": "u128" - }, - { - "name": "timeBasedRewardLowerBound", - "type": "u128" - } - ] - } - }, - { - "name": "OracleGuardRails", - "type": { - "kind": "struct", - "fields": [ - { - "name": "priceDivergence", - "type": { - "defined": "PriceDivergenceGuardRails" - } - }, - { - "name": "validity", - "type": { - "defined": "ValidityGuardRails" - } - }, - { - "name": "useForLiquidations", - "type": "bool" - } - ] - } - }, - { - "name": "PriceDivergenceGuardRails", - "type": { - "kind": "struct", - "fields": [ - { - "name": "markOracleDivergenceNumerator", - "type": "u128" - }, - { - "name": "markOracleDivergenceDenominator", - "type": "u128" - } - ] - } - }, - { - "name": "ValidityGuardRails", - "type": { - "kind": "struct", - "fields": [ - { - "name": "slotsBeforeStale", - "type": "i64" - }, - { - "name": "confidenceIntervalMaxSize", - "type": "u128" - }, - { - "name": "tooVolatileRatio", - "type": "i128" - } - ] - } - }, - { - "name": "FeeStructure", - "type": { - "kind": "struct", - "fields": [ - { - "name": "feeNumerator", - "type": "u128" - }, - { - "name": "feeDenominator", - "type": "u128" - }, - { - "name": "discountTokenTiers", - "type": { - "defined": "DiscountTokenTiers" - } - }, - { - "name": "referralDiscount", - "type": { - "defined": "ReferralDiscount" - } - } - ] - } - }, - { - "name": "DiscountTokenTiers", - "type": { - "kind": "struct", - "fields": [ - { - "name": "firstTier", - "type": { - "defined": "DiscountTokenTier" - } - }, - { - "name": "secondTier", - "type": { - "defined": "DiscountTokenTier" - } - }, - { - "name": "thirdTier", - "type": { - "defined": "DiscountTokenTier" - } - }, - { - "name": "fourthTier", - "type": { - "defined": "DiscountTokenTier" - } - } - ] - } - }, - { - "name": "DiscountTokenTier", - "type": { - "kind": "struct", - "fields": [ - { - "name": "minimumBalance", - "type": "u64" - }, - { - "name": "discountNumerator", - "type": "u128" - }, - { - "name": "discountDenominator", - "type": "u128" - } - ] - } - }, - { - "name": "ReferralDiscount", - "type": { - "kind": "struct", - "fields": [ - { - "name": "referrerRewardNumerator", - "type": "u128" - }, - { - "name": "referrerRewardDenominator", - "type": "u128" - }, - { - "name": "refereeDiscountNumerator", - "type": "u128" - }, - { - "name": "refereeDiscountDenominator", - "type": "u128" - } - ] - } - }, - { - "name": "TradeRecord", - "type": { - "kind": "struct", - "fields": [ - { - "name": "ts", - "type": "i64" - }, - { - "name": "recordId", - "type": "u128" - }, - { - "name": "userAuthority", - "type": "publicKey" - }, - { - "name": "user", - "type": "publicKey" - }, - { - "name": "direction", - "type": { - "defined": "PositionDirection" - } - }, - { - "name": "baseAssetAmount", - "type": "u128" - }, - { - "name": "quoteAssetAmount", - "type": "u128" - }, - { - "name": "markPriceBefore", - "type": "u128" - }, - { - "name": "markPriceAfter", - "type": "u128" - }, - { - "name": "fee", - "type": "u128" - }, - { - "name": "referrerReward", - "type": "u128" - }, - { - "name": "refereeDiscount", - "type": "u128" - }, - { - "name": "tokenDiscount", - "type": "u128" - }, - { - "name": "liquidation", - "type": "bool" - }, - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "oraclePrice", - "type": "i128" - } - ] - } - }, - { - "name": "MarketPosition", - "type": { - "kind": "struct", - "fields": [ - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "baseAssetAmount", - "type": "i128" - }, - { - "name": "quoteAssetAmount", - "type": "u128" - }, - { - "name": "lastCumulativeFundingRate", - "type": "i128" - }, - { - "name": "lastCumulativeRepegRebate", - "type": "u128" - }, - { - "name": "lastFundingRateTs", - "type": "i64" - }, - { - "name": "openOrders", - "type": "u128" - }, - { - "name": "padding0", - "type": "u128" - }, - { - "name": "padding1", - "type": "u128" - }, - { - "name": "padding2", - "type": "u128" - }, - { - "name": "padding3", - "type": "u128" - }, - { - "name": "padding4", - "type": "u128" - }, - { - "name": "padding5", - "type": "u128" - }, - { - "name": "padding6", - "type": "u128" - } - ] - } - }, - { - "name": "Order", - "type": { - "kind": "struct", - "fields": [ - { - "name": "status", - "type": { - "defined": "OrderStatus" - } - }, - { - "name": "orderType", - "type": { - "defined": "OrderType" - } - }, - { - "name": "ts", - "type": "i64" - }, - { - "name": "orderId", - "type": "u128" - }, - { - "name": "userOrderId", - "type": "u8" - }, - { - "name": "marketIndex", - "type": "u64" - }, - { - "name": "price", - "type": "u128" - }, - { - "name": "userBaseAssetAmount", - "type": "i128" - }, - { - "name": "quoteAssetAmount", - "type": "u128" - }, - { - "name": "baseAssetAmount", - "type": "u128" - }, - { - "name": "baseAssetAmountFilled", - "type": "u128" - }, - { - "name": "quoteAssetAmountFilled", - "type": "u128" - }, - { - "name": "fee", - "type": "u128" - }, - { - "name": "direction", - "type": { - "defined": "PositionDirection" - } - }, - { - "name": "reduceOnly", - "type": "bool" - }, - { - "name": "postOnly", - "type": "bool" - }, - { - "name": "immediateOrCancel", - "type": "bool" - }, - { - "name": "discountTier", - "type": { - "defined": "OrderDiscountTier" - } - }, - { - "name": "triggerPrice", - "type": "u128" - }, - { - "name": "triggerCondition", - "type": { - "defined": "OrderTriggerCondition" - } - }, - { - "name": "referrer", - "type": "publicKey" - }, - { - "name": "oraclePriceOffset", - "type": "i128" - }, - { - "name": "padding", - "type": { - "array": ["u16", 3] - } - } - ] - } - }, - { - "name": "SwapDirection", - "type": { - "kind": "enum", - "variants": [ - { - "name": "Add" - }, - { - "name": "Remove" - } - ] - } - }, - { - "name": "Type", - "type": { - "kind": "enum", - "variants": [ - { - "name": "Repeg" - }, - { - "name": "UpdateK" - } - ] - } - }, - { - "name": "DepositDirection", - "type": { - "kind": "enum", - "variants": [ - { - "name": "DEPOSIT" - }, - { - "name": "WITHDRAW" - } - ] - } - }, - { - "name": "LiquidationType", - "type": { - "kind": "enum", - "variants": [ - { - "name": "NONE" - }, - { - "name": "PARTIAL" - }, - { - "name": "FULL" - } - ] - } - }, - { - "name": "OracleSource", - "type": { - "kind": "enum", - "variants": [ - { - "name": "Pyth" - }, - { - "name": "Switchboard" - } - ] - } - }, - { - "name": "OrderAction", - "type": { - "kind": "enum", - "variants": [ - { - "name": "Place" - }, - { - "name": "Cancel" - }, - { - "name": "Fill" - }, - { - "name": "Expire" - } - ] - } - }, - { - "name": "PositionDirection", - "type": { - "kind": "enum", - "variants": [ - { - "name": "Long" - }, - { - "name": "Short" - } - ] - } - }, - { - "name": "OrderStatus", - "type": { - "kind": "enum", - "variants": [ - { - "name": "Init" - }, - { - "name": "Open" - } - ] - } - }, - { - "name": "OrderType", - "type": { - "kind": "enum", - "variants": [ - { - "name": "Market" - }, - { - "name": "Limit" - }, - { - "name": "TriggerMarket" - }, - { - "name": "TriggerLimit" - } - ] - } - }, - { - "name": "OrderDiscountTier", - "type": { - "kind": "enum", - "variants": [ - { - "name": "None" - }, - { - "name": "First" - }, - { - "name": "Second" - }, - { - "name": "Third" - }, - { - "name": "Fourth" - } - ] - } - }, - { - "name": "OrderTriggerCondition", - "type": { - "kind": "enum", - "variants": [ - { - "name": "Above" - }, - { - "name": "Below" - } - ] - } - } - ], - "errors": [ - { - "code": 6000, - "name": "InvalidCollateralAccountAuthority", - "msg": "Clearing house not collateral account owner" - }, - { - "code": 6001, - "name": "InvalidInsuranceAccountAuthority", - "msg": "Clearing house not insurance account owner" - }, - { - "code": 6002, - "name": "InsufficientDeposit", - "msg": "Insufficient deposit" - }, - { - "code": 6003, - "name": "InsufficientCollateral", - "msg": "Insufficient collateral" - }, - { - "code": 6004, - "name": "SufficientCollateral", - "msg": "Sufficient collateral" - }, - { - "code": 6005, - "name": "MaxNumberOfPositions", - "msg": "Max number of positions taken" - }, - { - "code": 6006, - "name": "AdminControlsPricesDisabled", - "msg": "Admin Controls Prices Disabled" - }, - { - "code": 6007, - "name": "MarketIndexNotInitialized", - "msg": "Market Index Not Initialized" - }, - { - "code": 6008, - "name": "MarketIndexAlreadyInitialized", - "msg": "Market Index Already Initialized" - }, - { - "code": 6009, - "name": "UserAccountAndUserPositionsAccountMismatch", - "msg": "User Account And User Positions Account Mismatch" - }, - { - "code": 6010, - "name": "UserHasNoPositionInMarket", - "msg": "User Has No Position In Market" - }, - { - "code": 6011, - "name": "InvalidInitialPeg", - "msg": "Invalid Initial Peg" - }, - { - "code": 6012, - "name": "InvalidRepegRedundant", - "msg": "AMM repeg already configured with amt given" - }, - { - "code": 6013, - "name": "InvalidRepegDirection", - "msg": "AMM repeg incorrect repeg direction" - }, - { - "code": 6014, - "name": "InvalidRepegProfitability", - "msg": "AMM repeg out of bounds pnl" - }, - { - "code": 6015, - "name": "SlippageOutsideLimit", - "msg": "Slippage Outside Limit Price" - }, - { - "code": 6016, - "name": "TradeSizeTooSmall", - "msg": "Trade Size Too Small" - }, - { - "code": 6017, - "name": "InvalidUpdateK", - "msg": "Price change too large when updating K" - }, - { - "code": 6018, - "name": "AdminWithdrawTooLarge", - "msg": "Admin tried to withdraw amount larger than fees collected" - }, - { - "code": 6019, - "name": "MathError", - "msg": "Math Error" - }, - { - "code": 6020, - "name": "BnConversionError", - "msg": "Conversion to u128/u64 failed with an overflow or underflow" - }, - { - "code": 6021, - "name": "ClockUnavailable", - "msg": "Clock unavailable" - }, - { - "code": 6022, - "name": "UnableToLoadOracle", - "msg": "Unable To Load Oracles" - }, - { - "code": 6023, - "name": "OracleMarkSpreadLimit", - "msg": "Oracle/Mark Spread Too Large" - }, - { - "code": 6024, - "name": "HistoryAlreadyInitialized", - "msg": "Clearing House history already initialized" - }, - { - "code": 6025, - "name": "ExchangePaused", - "msg": "Exchange is paused" - }, - { - "code": 6026, - "name": "InvalidWhitelistToken", - "msg": "Invalid whitelist token" - }, - { - "code": 6027, - "name": "WhitelistTokenNotFound", - "msg": "Whitelist token not found" - }, - { - "code": 6028, - "name": "InvalidDiscountToken", - "msg": "Invalid discount token" - }, - { - "code": 6029, - "name": "DiscountTokenNotFound", - "msg": "Discount token not found" - }, - { - "code": 6030, - "name": "InvalidReferrer", - "msg": "Invalid referrer" - }, - { - "code": 6031, - "name": "ReferrerNotFound", - "msg": "Referrer not found" - }, - { - "code": 6032, - "name": "InvalidOracle", - "msg": "InvalidOracle" - }, - { - "code": 6033, - "name": "OracleNotFound", - "msg": "OracleNotFound" - }, - { - "code": 6034, - "name": "LiquidationsBlockedByOracle", - "msg": "Liquidations Blocked By Oracle" - }, - { - "code": 6035, - "name": "UserMaxDeposit", - "msg": "Can not deposit more than max deposit" - }, - { - "code": 6036, - "name": "CantDeleteUserWithCollateral", - "msg": "Can not delete user that still has collateral" - }, - { - "code": 6037, - "name": "InvalidFundingProfitability", - "msg": "AMM funding out of bounds pnl" - }, - { - "code": 6038, - "name": "CastingFailure", - "msg": "Casting Failure" - }, - { - "code": 6039, - "name": "InvalidOrder", - "msg": "Invalid Order" - }, - { - "code": 6040, - "name": "UserHasNoOrder", - "msg": "User has no order" - }, - { - "code": 6041, - "name": "OrderAmountTooSmall", - "msg": "Order Amount Too Small" - }, - { - "code": 6042, - "name": "MaxNumberOfOrders", - "msg": "Max number of orders taken" - }, - { - "code": 6043, - "name": "OrderDoesNotExist", - "msg": "Order does not exist" - }, - { - "code": 6044, - "name": "OrderNotOpen", - "msg": "Order not open" - }, - { - "code": 6045, - "name": "CouldNotFillOrder", - "msg": "CouldNotFillOrder" - }, - { - "code": 6046, - "name": "ReduceOnlyOrderIncreasedRisk", - "msg": "Reduce only order increased risk" - }, - { - "code": 6047, - "name": "OrderStateAlreadyInitialized", - "msg": "Order state already initialized" - }, - { - "code": 6048, - "name": "UnableToLoadAccountLoader", - "msg": "Unable to load AccountLoader" - }, - { - "code": 6049, - "name": "TradeSizeTooLarge", - "msg": "Trade Size Too Large" - }, - { - "code": 6050, - "name": "UnableToWriteToRemainingAccount", - "msg": "Unable to write to remaining account" - }, - { - "code": 6051, - "name": "UserCantReferThemselves", - "msg": "User cant refer themselves" - }, - { - "code": 6052, - "name": "DidNotReceiveExpectedReferrer", - "msg": "Did not receive expected referrer" - }, - { - "code": 6053, - "name": "CouldNotDeserializeReferrer", - "msg": "Could not deserialize referrer" - }, - { - "code": 6054, - "name": "MarketOrderMustBeInPlaceAndFill", - "msg": "Market order must be in place and fill" - }, - { - "code": 6055, - "name": "UserOrderIdAlreadyInUse", - "msg": "User Order Id Already In Use" - }, - { - "code": 6056, - "name": "NoPositionsLiquidatable", - "msg": "No positions liquidatable" - }, - { - "code": 6057, - "name": "InvalidMarginRatio", - "msg": "Invalid Margin Ratio" - }, - { - "code": 6058, - "name": "CantCancelPostOnlyOrder", - "msg": "Cant Cancel Post Only Order" - }, - { - "code": 6059, - "name": "InvalidOracleOffset", - "msg": "InvalidOracleOffset" - }, - { - "code": 6060, - "name": "CantExpireOrders", - "msg": "CantExpireOrders" - }, - { - "code": 6061, - "name": "InvalidRepegPriceImpact", - "msg": "AMM repeg mark price impact vs oracle too large" - } - ] -} + "version": "1.0.0", + "name": "clearing_house", + "instructions": [ + { + "name": "initialize", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralMint", + "isMut": false, + "isSigner": false + }, + { + "name": "collateralVault", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralVaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "insuranceVault", + "isMut": true, + "isSigner": false + }, + { + "name": "insuranceVaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "clearingHouseNonce", + "type": "u8" + }, + { + "name": "collateralVaultNonce", + "type": "u8" + }, + { + "name": "insuranceVaultNonce", + "type": "u8" + }, + { + "name": "adminControlsPrices", + "type": "bool" + } + ] + }, + { + "name": "initializeHistory", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "tradeHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "liquidationHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "depositHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingRateHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "curveHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "initializeOrderState", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "orderState", + "isMut": true, + "isSigner": false + }, + { + "name": "orderHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "orderHouseNonce", + "type": "u8" + } + ] + }, + { + "name": "initializeMarket", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "ammBaseAssetReserve", + "type": "u128" + }, + { + "name": "ammQuoteAssetReserve", + "type": "u128" + }, + { + "name": "ammPeriodicity", + "type": "i64" + }, + { + "name": "ammPegMultiplier", + "type": "u128" + }, + { + "name": "oracleSource", + "type": { + "defined": "OracleSource" + } + }, + { + "name": "marginRatioInitial", + "type": "u32" + }, + { + "name": "marginRatioPartial", + "type": "u32" + }, + { + "name": "marginRatioMaintenance", + "type": "u32" + } + ] + }, + { + "name": "depositCollateral", + "accounts": [ + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "collateralVault", + "isMut": true, + "isSigner": false + }, + { + "name": "userCollateralAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": false, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "depositHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "withdrawCollateral", + "accounts": [ + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "collateralVault", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralVaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "insuranceVault", + "isMut": true, + "isSigner": false + }, + { + "name": "insuranceVaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "userCollateralAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": false, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "depositHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "openPosition", + "accounts": [ + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "tradeHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingRateHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "direction", + "type": { + "defined": "PositionDirection" + } + }, + { + "name": "quoteAssetAmount", + "type": "u128" + }, + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "limitPrice", + "type": "u128" + }, + { + "name": "optionalAccounts", + "type": { + "defined": "ManagePositionOptionalAccounts" + } + } + ] + }, + { + "name": "closePosition", + "accounts": [ + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "tradeHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingRateHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "optionalAccounts", + "type": { + "defined": "ManagePositionOptionalAccounts" + } + } + ] + }, + { + "name": "placeOrder", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "orderState", + "isMut": false, + "isSigner": false + }, + { + "name": "user", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "markets", + "isMut": false, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "userOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "orderHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "OrderParams" + } + } + ] + }, + { + "name": "cancelOrder", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "orderState", + "isMut": false, + "isSigner": false + }, + { + "name": "user", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "markets", + "isMut": false, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "userOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "orderHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "orderId", + "type": "u128" + } + ] + }, + { + "name": "cancelOrderByUserId", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "orderState", + "isMut": false, + "isSigner": false + }, + { + "name": "user", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "markets", + "isMut": false, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "userOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "orderHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "userOrderId", + "type": "u8" + } + ] + }, + { + "name": "cancelAllOrders", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "orderState", + "isMut": false, + "isSigner": false + }, + { + "name": "user", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "markets", + "isMut": false, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "userOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "orderHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "expireOrders", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "orderState", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "filler", + "isMut": true, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "userOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "orderHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "fillOrder", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "orderState", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "filler", + "isMut": true, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "userOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "tradeHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingRateHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "orderHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "extendedCurveHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "orderId", + "type": "u128" + } + ] + }, + { + "name": "placeAndFillOrder", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "orderState", + "isMut": false, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "userOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "tradeHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingRateHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "orderHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "extendedCurveHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "OrderParams" + } + } + ] + }, + { + "name": "liquidate", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "liquidator", + "isMut": true, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralVault", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralVaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "insuranceVault", + "isMut": true, + "isSigner": false + }, + { + "name": "insuranceVaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "tradeHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "liquidationHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "moveAmmPrice", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "baseAssetReserve", + "type": "u128" + }, + { + "name": "quoteAssetReserve", + "type": "u128" + }, + { + "name": "marketIndex", + "type": "u64" + } + ] + }, + { + "name": "withdrawFees", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "collateralVault", + "isMut": true, + "isSigner": false + }, + { + "name": "collateralVaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "recipient", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "withdrawFromInsuranceVault", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "insuranceVault", + "isMut": true, + "isSigner": false + }, + { + "name": "insuranceVaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "recipient", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "withdrawFromInsuranceVaultToMarket", + "accounts": [ + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "insuranceVault", + "isMut": true, + "isSigner": false + }, + { + "name": "insuranceVaultAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "collateralVault", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "repegAmmCurve", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "curveHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "newPegCandidate", + "type": "u128" + }, + { + "name": "marketIndex", + "type": "u64" + } + ] + }, + { + "name": "updateAmmOracleTwap", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "curveHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + } + ] + }, + { + "name": "resetAmmOracleTwap", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "curveHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + } + ] + }, + { + "name": "initializeUser", + "accounts": [ + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": true + }, + { + "name": "authority", + "isMut": true, + "isSigner": true + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "userNonce", + "type": "u8" + }, + { + "name": "optionalAccounts", + "type": { + "defined": "InitializeUserOptionalAccounts" + } + } + ] + }, + { + "name": "initializeUserWithExplicitPayer", + "accounts": [ + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": true + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "userNonce", + "type": "u8" + }, + { + "name": "optionalAccounts", + "type": { + "defined": "InitializeUserOptionalAccounts" + } + } + ] + }, + { + "name": "initializeUserOrders", + "accounts": [ + { + "name": "user", + "isMut": false, + "isSigner": false + }, + { + "name": "userOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "userOrdersNonce", + "type": "u8" + } + ] + }, + { + "name": "initializeUserOrdersWithExplicitPayer", + "accounts": [ + { + "name": "user", + "isMut": false, + "isSigner": false + }, + { + "name": "userOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "userOrdersNonce", + "type": "u8" + } + ] + }, + { + "name": "deleteUser", + "accounts": [ + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "userOrders", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "settleFundingPayment", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "markets", + "isMut": false, + "isSigner": false + }, + { + "name": "userPositions", + "isMut": true, + "isSigner": false + }, + { + "name": "fundingPaymentHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "updateFundingRate", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, + { + "name": "fundingRateHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + } + ] + }, + { + "name": "updateK", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, + { + "name": "curveHistory", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "sqrtK", + "type": "u128" + }, + { + "name": "marketIndex", + "type": "u64" + } + ] + }, + { + "name": "updateCurveHistory", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "extendedCurveHistory", + "isMut": true, + "isSigner": false + }, + { + "name": "curveHistory", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "updateMarginRatio", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "marginRatioInitial", + "type": "u32" + }, + { + "name": "marginRatioPartial", + "type": "u32" + }, + { + "name": "marginRatioMaintenance", + "type": "u32" + } + ] + }, + { + "name": "updatePartialLiquidationClosePercentage", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "numerator", + "type": "u128" + }, + { + "name": "denominator", + "type": "u128" + } + ] + }, + { + "name": "updatePartialLiquidationPenaltyPercentage", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "numerator", + "type": "u128" + }, + { + "name": "denominator", + "type": "u128" + } + ] + }, + { + "name": "updateFullLiquidationPenaltyPercentage", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "numerator", + "type": "u128" + }, + { + "name": "denominator", + "type": "u128" + } + ] + }, + { + "name": "updatePartialLiquidationLiquidatorShareDenominator", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "denominator", + "type": "u64" + } + ] + }, + { + "name": "updateFullLiquidationLiquidatorShareDenominator", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "denominator", + "type": "u64" + } + ] + }, + { + "name": "updateFee", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "fees", + "type": { + "defined": "FeeStructure" + } + } + ] + }, + { + "name": "updateOrderFillerRewardStructure", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "orderState", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "orderFillerRewardStructure", + "type": { + "defined": "OrderFillerRewardStructure" + } + } + ] + }, + { + "name": "updateOracleGuardRails", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "oracleGuardRails", + "type": { + "defined": "OracleGuardRails" + } + } + ] + }, + { + "name": "updateMarketOracle", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "oracle", + "type": "publicKey" + }, + { + "name": "oracleSource", + "type": { + "defined": "OracleSource" + } + } + ] + }, + { + "name": "updateMarketMinimumQuoteAssetTradeSize", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "minimumTradeSize", + "type": "u128" + } + ] + }, + { + "name": "updateMarketMinimumBaseAssetTradeSize", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "minimumTradeSize", + "type": "u128" + } + ] + }, + { + "name": "updateAdmin", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "admin", + "type": "publicKey" + } + ] + }, + { + "name": "updateWhitelistMint", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "whitelistMint", + "type": "publicKey" + } + ] + }, + { + "name": "updateDiscountMint", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "discountMint", + "type": "publicKey" + } + ] + }, + { + "name": "updateMaxDeposit", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "maxDeposit", + "type": "u128" + } + ] + }, + { + "name": "updateExchangePaused", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "exchangePaused", + "type": "bool" + } + ] + }, + { + "name": "disableAdminControlsPrices", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "updateFundingPaused", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "fundingPaused", + "type": "bool" + } + ] + } + ], + "accounts": [ + { + "name": "CurveHistory", + "type": { + "kind": "struct", + "fields": [ + { + "name": "head", + "type": "u64" + }, + { + "name": "curveRecords", + "type": { + "array": [ + { + "defined": "CurveRecord" + }, + 32 + ] + } + } + ] + } + }, + { + "name": "ExtendedCurveHistory", + "type": { + "kind": "struct", + "fields": [ + { + "name": "head", + "type": "u64" + }, + { + "name": "curveRecords", + "type": { + "array": [ + { + "defined": "ExtendedCurveRecord" + }, + 1024 + ] + } + } + ] + } + }, + { + "name": "DepositHistory", + "type": { + "kind": "struct", + "fields": [ + { + "name": "head", + "type": "u64" + }, + { + "name": "depositRecords", + "type": { + "array": [ + { + "defined": "DepositRecord" + }, + 1024 + ] + } + } + ] + } + }, + { + "name": "FundingPaymentHistory", + "type": { + "kind": "struct", + "fields": [ + { + "name": "head", + "type": "u64" + }, + { + "name": "fundingPaymentRecords", + "type": { + "array": [ + { + "defined": "FundingPaymentRecord" + }, + 1024 + ] + } + } + ] + } + }, + { + "name": "FundingRateHistory", + "type": { + "kind": "struct", + "fields": [ + { + "name": "head", + "type": "u64" + }, + { + "name": "fundingRateRecords", + "type": { + "array": [ + { + "defined": "FundingRateRecord" + }, + 1024 + ] + } + } + ] + } + }, + { + "name": "LiquidationHistory", + "type": { + "kind": "struct", + "fields": [ + { + "name": "head", + "type": "u64" + }, + { + "name": "liquidationRecords", + "type": { + "array": [ + { + "defined": "LiquidationRecord" + }, + 1024 + ] + } + } + ] + } + }, + { + "name": "Markets", + "type": { + "kind": "struct", + "fields": [ + { + "name": "markets", + "type": { + "array": [ + { + "defined": "Market" + }, + 64 + ] + } + } + ] + } + }, + { + "name": "OrderHistory", + "type": { + "kind": "struct", + "fields": [ + { + "name": "head", + "type": "u64" + }, + { + "name": "lastOrderId", + "type": "u128" + }, + { + "name": "orderRecords", + "type": { + "array": [ + { + "defined": "OrderRecord" + }, + 1024 + ] + } + } + ] + } + }, + { + "name": "OrderState", + "type": { + "kind": "struct", + "fields": [ + { + "name": "orderHistory", + "type": "publicKey" + }, + { + "name": "orderFillerRewardStructure", + "type": { + "defined": "OrderFillerRewardStructure" + } + }, + { + "name": "minOrderQuoteAssetAmount", + "type": "u128" + }, + { + "name": "padding", + "type": { + "array": [ + "u128", + 10 + ] + } + } + ] + } + }, + { + "name": "State", + "type": { + "kind": "struct", + "fields": [ + { + "name": "admin", + "type": "publicKey" + }, + { + "name": "exchangePaused", + "type": "bool" + }, + { + "name": "fundingPaused", + "type": "bool" + }, + { + "name": "adminControlsPrices", + "type": "bool" + }, + { + "name": "collateralMint", + "type": "publicKey" + }, + { + "name": "collateralVault", + "type": "publicKey" + }, + { + "name": "collateralVaultAuthority", + "type": "publicKey" + }, + { + "name": "collateralVaultNonce", + "type": "u8" + }, + { + "name": "depositHistory", + "type": "publicKey" + }, + { + "name": "tradeHistory", + "type": "publicKey" + }, + { + "name": "fundingPaymentHistory", + "type": "publicKey" + }, + { + "name": "fundingRateHistory", + "type": "publicKey" + }, + { + "name": "liquidationHistory", + "type": "publicKey" + }, + { + "name": "curveHistory", + "type": "publicKey" + }, + { + "name": "insuranceVault", + "type": "publicKey" + }, + { + "name": "insuranceVaultAuthority", + "type": "publicKey" + }, + { + "name": "insuranceVaultNonce", + "type": "u8" + }, + { + "name": "markets", + "type": "publicKey" + }, + { + "name": "marginRatioInitial", + "type": "u128" + }, + { + "name": "marginRatioMaintenance", + "type": "u128" + }, + { + "name": "marginRatioPartial", + "type": "u128" + }, + { + "name": "partialLiquidationClosePercentageNumerator", + "type": "u128" + }, + { + "name": "partialLiquidationClosePercentageDenominator", + "type": "u128" + }, + { + "name": "partialLiquidationPenaltyPercentageNumerator", + "type": "u128" + }, + { + "name": "partialLiquidationPenaltyPercentageDenominator", + "type": "u128" + }, + { + "name": "fullLiquidationPenaltyPercentageNumerator", + "type": "u128" + }, + { + "name": "fullLiquidationPenaltyPercentageDenominator", + "type": "u128" + }, + { + "name": "partialLiquidationLiquidatorShareDenominator", + "type": "u64" + }, + { + "name": "fullLiquidationLiquidatorShareDenominator", + "type": "u64" + }, + { + "name": "feeStructure", + "type": { + "defined": "FeeStructure" + } + }, + { + "name": "whitelistMint", + "type": "publicKey" + }, + { + "name": "discountMint", + "type": "publicKey" + }, + { + "name": "oracleGuardRails", + "type": { + "defined": "OracleGuardRails" + } + }, + { + "name": "maxDeposit", + "type": "u128" + }, + { + "name": "extendedCurveHistory", + "type": "publicKey" + }, + { + "name": "orderState", + "type": "publicKey" + }, + { + "name": "padding0", + "type": "u128" + }, + { + "name": "padding1", + "type": "u128" + }, + { + "name": "padding2", + "type": "u128" + }, + { + "name": "padding3", + "type": "u128" + } + ] + } + }, + { + "name": "TradeHistory", + "type": { + "kind": "struct", + "fields": [ + { + "name": "head", + "type": "u64" + }, + { + "name": "tradeRecords", + "type": { + "array": [ + { + "defined": "TradeRecord" + }, + 1024 + ] + } + } + ] + } + }, + { + "name": "User", + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "collateral", + "type": "u128" + }, + { + "name": "cumulativeDeposits", + "type": "i128" + }, + { + "name": "totalFeePaid", + "type": "u128" + }, + { + "name": "totalTokenDiscount", + "type": "u128" + }, + { + "name": "totalReferralReward", + "type": "u128" + }, + { + "name": "totalRefereeDiscount", + "type": "u128" + }, + { + "name": "positions", + "type": "publicKey" + }, + { + "name": "padding0", + "type": "u128" + }, + { + "name": "padding1", + "type": "u128" + }, + { + "name": "padding2", + "type": "u128" + }, + { + "name": "padding3", + "type": "u128" + } + ] + } + }, + { + "name": "UserPositions", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "publicKey" + }, + { + "name": "positions", + "type": { + "array": [ + { + "defined": "MarketPosition" + }, + 5 + ] + } + } + ] + } + }, + { + "name": "UserOrders", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "publicKey" + }, + { + "name": "orders", + "type": { + "array": [ + { + "defined": "Order" + }, + 32 + ] + } + } + ] + } + } + ], + "types": [ + { + "name": "InitializeUserOptionalAccounts", + "type": { + "kind": "struct", + "fields": [ + { + "name": "whitelistToken", + "type": "bool" + } + ] + } + }, + { + "name": "ManagePositionOptionalAccounts", + "type": { + "kind": "struct", + "fields": [ + { + "name": "discountToken", + "type": "bool" + }, + { + "name": "referrer", + "type": "bool" + } + ] + } + }, + { + "name": "OrderParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "orderType", + "type": { + "defined": "OrderType" + } + }, + { + "name": "direction", + "type": { + "defined": "PositionDirection" + } + }, + { + "name": "userOrderId", + "type": "u8" + }, + { + "name": "quoteAssetAmount", + "type": "u128" + }, + { + "name": "baseAssetAmount", + "type": "u128" + }, + { + "name": "price", + "type": "u128" + }, + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "reduceOnly", + "type": "bool" + }, + { + "name": "postOnly", + "type": "bool" + }, + { + "name": "immediateOrCancel", + "type": "bool" + }, + { + "name": "triggerPrice", + "type": "u128" + }, + { + "name": "triggerCondition", + "type": { + "defined": "OrderTriggerCondition" + } + }, + { + "name": "optionalAccounts", + "type": { + "defined": "OrderParamsOptionalAccounts" + } + }, + { + "name": "positionLimit", + "type": "u128" + }, + { + "name": "oraclePriceOffset", + "type": "i128" + }, + { + "name": "padding0", + "type": "bool" + }, + { + "name": "padding1", + "type": "bool" + } + ] + } + }, + { + "name": "OrderParamsOptionalAccounts", + "type": { + "kind": "struct", + "fields": [ + { + "name": "discountToken", + "type": "bool" + }, + { + "name": "referrer", + "type": "bool" + } + ] + } + }, + { + "name": "CurveRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ts", + "type": "i64" + }, + { + "name": "recordId", + "type": "u128" + }, + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "pegMultiplierBefore", + "type": "u128" + }, + { + "name": "baseAssetReserveBefore", + "type": "u128" + }, + { + "name": "quoteAssetReserveBefore", + "type": "u128" + }, + { + "name": "sqrtKBefore", + "type": "u128" + }, + { + "name": "pegMultiplierAfter", + "type": "u128" + }, + { + "name": "baseAssetReserveAfter", + "type": "u128" + }, + { + "name": "quoteAssetReserveAfter", + "type": "u128" + }, + { + "name": "sqrtKAfter", + "type": "u128" + }, + { + "name": "baseAssetAmountLong", + "type": "u128" + }, + { + "name": "baseAssetAmountShort", + "type": "u128" + }, + { + "name": "baseAssetAmount", + "type": "i128" + }, + { + "name": "openInterest", + "type": "u128" + }, + { + "name": "totalFee", + "type": "u128" + }, + { + "name": "totalFeeMinusDistributions", + "type": "u128" + }, + { + "name": "adjustmentCost", + "type": "i128" + } + ] + } + }, + { + "name": "ExtendedCurveRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ts", + "type": "i64" + }, + { + "name": "recordId", + "type": "u128" + }, + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "pegMultiplierBefore", + "type": "u128" + }, + { + "name": "baseAssetReserveBefore", + "type": "u128" + }, + { + "name": "quoteAssetReserveBefore", + "type": "u128" + }, + { + "name": "sqrtKBefore", + "type": "u128" + }, + { + "name": "pegMultiplierAfter", + "type": "u128" + }, + { + "name": "baseAssetReserveAfter", + "type": "u128" + }, + { + "name": "quoteAssetReserveAfter", + "type": "u128" + }, + { + "name": "sqrtKAfter", + "type": "u128" + }, + { + "name": "baseAssetAmountLong", + "type": "u128" + }, + { + "name": "baseAssetAmountShort", + "type": "u128" + }, + { + "name": "baseAssetAmount", + "type": "i128" + }, + { + "name": "openInterest", + "type": "u128" + }, + { + "name": "totalFee", + "type": "u128" + }, + { + "name": "totalFeeMinusDistributions", + "type": "u128" + }, + { + "name": "adjustmentCost", + "type": "i128" + }, + { + "name": "oraclePrice", + "type": "i128" + }, + { + "name": "tradeRecord", + "type": "u128" + }, + { + "name": "padding", + "type": { + "array": [ + "u128", + 5 + ] + } + } + ] + } + }, + { + "name": "DepositRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ts", + "type": "i64" + }, + { + "name": "recordId", + "type": "u128" + }, + { + "name": "userAuthority", + "type": "publicKey" + }, + { + "name": "user", + "type": "publicKey" + }, + { + "name": "direction", + "type": { + "defined": "DepositDirection" + } + }, + { + "name": "collateralBefore", + "type": "u128" + }, + { + "name": "cumulativeDepositsBefore", + "type": "i128" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + }, + { + "name": "FundingPaymentRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ts", + "type": "i64" + }, + { + "name": "recordId", + "type": "u128" + }, + { + "name": "userAuthority", + "type": "publicKey" + }, + { + "name": "user", + "type": "publicKey" + }, + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "fundingPayment", + "type": "i128" + }, + { + "name": "baseAssetAmount", + "type": "i128" + }, + { + "name": "userLastCumulativeFunding", + "type": "i128" + }, + { + "name": "userLastFundingRateTs", + "type": "i64" + }, + { + "name": "ammCumulativeFundingLong", + "type": "i128" + }, + { + "name": "ammCumulativeFundingShort", + "type": "i128" + } + ] + } + }, + { + "name": "FundingRateRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ts", + "type": "i64" + }, + { + "name": "recordId", + "type": "u128" + }, + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "fundingRate", + "type": "i128" + }, + { + "name": "cumulativeFundingRateLong", + "type": "i128" + }, + { + "name": "cumulativeFundingRateShort", + "type": "i128" + }, + { + "name": "oraclePriceTwap", + "type": "i128" + }, + { + "name": "markPriceTwap", + "type": "u128" + } + ] + } + }, + { + "name": "LiquidationRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ts", + "type": "i64" + }, + { + "name": "recordId", + "type": "u128" + }, + { + "name": "userAuthority", + "type": "publicKey" + }, + { + "name": "user", + "type": "publicKey" + }, + { + "name": "partial", + "type": "bool" + }, + { + "name": "baseAssetValue", + "type": "u128" + }, + { + "name": "baseAssetValueClosed", + "type": "u128" + }, + { + "name": "liquidationFee", + "type": "u128" + }, + { + "name": "feeToLiquidator", + "type": "u64" + }, + { + "name": "feeToInsuranceFund", + "type": "u64" + }, + { + "name": "liquidator", + "type": "publicKey" + }, + { + "name": "totalCollateral", + "type": "u128" + }, + { + "name": "collateral", + "type": "u128" + }, + { + "name": "unrealizedPnl", + "type": "i128" + }, + { + "name": "marginRatio", + "type": "u128" + } + ] + } + }, + { + "name": "Market", + "type": { + "kind": "struct", + "fields": [ + { + "name": "initialized", + "type": "bool" + }, + { + "name": "baseAssetAmountLong", + "type": "i128" + }, + { + "name": "baseAssetAmountShort", + "type": "i128" + }, + { + "name": "baseAssetAmount", + "type": "i128" + }, + { + "name": "openInterest", + "type": "u128" + }, + { + "name": "amm", + "type": { + "defined": "AMM" + } + }, + { + "name": "marginRatioInitial", + "type": "u32" + }, + { + "name": "marginRatioPartial", + "type": "u32" + }, + { + "name": "marginRatioMaintenance", + "type": "u32" + }, + { + "name": "padding0", + "type": "u32" + }, + { + "name": "padding1", + "type": "u128" + }, + { + "name": "padding2", + "type": "u128" + }, + { + "name": "padding3", + "type": "u128" + }, + { + "name": "padding4", + "type": "u128" + } + ] + } + }, + { + "name": "AMM", + "type": { + "kind": "struct", + "fields": [ + { + "name": "oracle", + "type": "publicKey" + }, + { + "name": "oracleSource", + "type": { + "defined": "OracleSource" + } + }, + { + "name": "baseAssetReserve", + "type": "u128" + }, + { + "name": "quoteAssetReserve", + "type": "u128" + }, + { + "name": "cumulativeRepegRebateLong", + "type": "u128" + }, + { + "name": "cumulativeRepegRebateShort", + "type": "u128" + }, + { + "name": "cumulativeFundingRateLong", + "type": "i128" + }, + { + "name": "cumulativeFundingRateShort", + "type": "i128" + }, + { + "name": "lastFundingRate", + "type": "i128" + }, + { + "name": "lastFundingRateTs", + "type": "i64" + }, + { + "name": "fundingPeriod", + "type": "i64" + }, + { + "name": "lastOraclePriceTwap", + "type": "i128" + }, + { + "name": "lastMarkPriceTwap", + "type": "u128" + }, + { + "name": "lastMarkPriceTwapTs", + "type": "i64" + }, + { + "name": "sqrtK", + "type": "u128" + }, + { + "name": "pegMultiplier", + "type": "u128" + }, + { + "name": "totalFee", + "type": "u128" + }, + { + "name": "totalFeeMinusDistributions", + "type": "u128" + }, + { + "name": "totalFeeWithdrawn", + "type": "u128" + }, + { + "name": "minimumQuoteAssetTradeSize", + "type": "u128" + }, + { + "name": "lastOraclePriceTwapTs", + "type": "i64" + }, + { + "name": "lastOraclePrice", + "type": "i128" + }, + { + "name": "minimumBaseAssetTradeSize", + "type": "u128" + }, + { + "name": "netRevenueSinceLastFunding", + "type": "i64" + }, + { + "name": "padding2", + "type": "u128" + }, + { + "name": "padding3", + "type": "u128" + } + ] + } + }, + { + "name": "OrderRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ts", + "type": "i64" + }, + { + "name": "recordId", + "type": "u128" + }, + { + "name": "user", + "type": "publicKey" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "order", + "type": { + "defined": "Order" + } + }, + { + "name": "action", + "type": { + "defined": "OrderAction" + } + }, + { + "name": "filler", + "type": "publicKey" + }, + { + "name": "tradeRecordId", + "type": "u128" + }, + { + "name": "baseAssetAmountFilled", + "type": "u128" + }, + { + "name": "quoteAssetAmountFilled", + "type": "u128" + }, + { + "name": "fee", + "type": "u128" + }, + { + "name": "fillerReward", + "type": "u128" + }, + { + "name": "quoteAssetAmountSurplus", + "type": "u128" + }, + { + "name": "padding", + "type": { + "array": [ + "u64", + 8 + ] + } + } + ] + } + }, + { + "name": "OrderFillerRewardStructure", + "type": { + "kind": "struct", + "fields": [ + { + "name": "rewardNumerator", + "type": "u128" + }, + { + "name": "rewardDenominator", + "type": "u128" + }, + { + "name": "timeBasedRewardLowerBound", + "type": "u128" + } + ] + } + }, + { + "name": "OracleGuardRails", + "type": { + "kind": "struct", + "fields": [ + { + "name": "priceDivergence", + "type": { + "defined": "PriceDivergenceGuardRails" + } + }, + { + "name": "validity", + "type": { + "defined": "ValidityGuardRails" + } + }, + { + "name": "useForLiquidations", + "type": "bool" + } + ] + } + }, + { + "name": "PriceDivergenceGuardRails", + "type": { + "kind": "struct", + "fields": [ + { + "name": "markOracleDivergenceNumerator", + "type": "u128" + }, + { + "name": "markOracleDivergenceDenominator", + "type": "u128" + } + ] + } + }, + { + "name": "ValidityGuardRails", + "type": { + "kind": "struct", + "fields": [ + { + "name": "slotsBeforeStale", + "type": "i64" + }, + { + "name": "confidenceIntervalMaxSize", + "type": "u128" + }, + { + "name": "tooVolatileRatio", + "type": "i128" + } + ] + } + }, + { + "name": "FeeStructure", + "type": { + "kind": "struct", + "fields": [ + { + "name": "feeNumerator", + "type": "u128" + }, + { + "name": "feeDenominator", + "type": "u128" + }, + { + "name": "discountTokenTiers", + "type": { + "defined": "DiscountTokenTiers" + } + }, + { + "name": "referralDiscount", + "type": { + "defined": "ReferralDiscount" + } + } + ] + } + }, + { + "name": "DiscountTokenTiers", + "type": { + "kind": "struct", + "fields": [ + { + "name": "firstTier", + "type": { + "defined": "DiscountTokenTier" + } + }, + { + "name": "secondTier", + "type": { + "defined": "DiscountTokenTier" + } + }, + { + "name": "thirdTier", + "type": { + "defined": "DiscountTokenTier" + } + }, + { + "name": "fourthTier", + "type": { + "defined": "DiscountTokenTier" + } + } + ] + } + }, + { + "name": "DiscountTokenTier", + "type": { + "kind": "struct", + "fields": [ + { + "name": "minimumBalance", + "type": "u64" + }, + { + "name": "discountNumerator", + "type": "u128" + }, + { + "name": "discountDenominator", + "type": "u128" + } + ] + } + }, + { + "name": "ReferralDiscount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "referrerRewardNumerator", + "type": "u128" + }, + { + "name": "referrerRewardDenominator", + "type": "u128" + }, + { + "name": "refereeDiscountNumerator", + "type": "u128" + }, + { + "name": "refereeDiscountDenominator", + "type": "u128" + } + ] + } + }, + { + "name": "TradeRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ts", + "type": "i64" + }, + { + "name": "recordId", + "type": "u128" + }, + { + "name": "userAuthority", + "type": "publicKey" + }, + { + "name": "user", + "type": "publicKey" + }, + { + "name": "direction", + "type": { + "defined": "PositionDirection" + } + }, + { + "name": "baseAssetAmount", + "type": "u128" + }, + { + "name": "quoteAssetAmount", + "type": "u128" + }, + { + "name": "markPriceBefore", + "type": "u128" + }, + { + "name": "markPriceAfter", + "type": "u128" + }, + { + "name": "fee", + "type": "u128" + }, + { + "name": "referrerReward", + "type": "u128" + }, + { + "name": "refereeDiscount", + "type": "u128" + }, + { + "name": "tokenDiscount", + "type": "u128" + }, + { + "name": "liquidation", + "type": "bool" + }, + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "oraclePrice", + "type": "i128" + } + ] + } + }, + { + "name": "MarketPosition", + "type": { + "kind": "struct", + "fields": [ + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "baseAssetAmount", + "type": "i128" + }, + { + "name": "quoteAssetAmount", + "type": "u128" + }, + { + "name": "lastCumulativeFundingRate", + "type": "i128" + }, + { + "name": "lastCumulativeRepegRebate", + "type": "u128" + }, + { + "name": "lastFundingRateTs", + "type": "i64" + }, + { + "name": "openOrders", + "type": "u128" + }, + { + "name": "padding0", + "type": "u128" + }, + { + "name": "padding1", + "type": "u128" + }, + { + "name": "padding2", + "type": "u128" + }, + { + "name": "padding3", + "type": "u128" + }, + { + "name": "padding4", + "type": "u128" + }, + { + "name": "padding5", + "type": "u128" + }, + { + "name": "padding6", + "type": "u128" + } + ] + } + }, + { + "name": "Order", + "type": { + "kind": "struct", + "fields": [ + { + "name": "status", + "type": { + "defined": "OrderStatus" + } + }, + { + "name": "orderType", + "type": { + "defined": "OrderType" + } + }, + { + "name": "ts", + "type": "i64" + }, + { + "name": "orderId", + "type": "u128" + }, + { + "name": "userOrderId", + "type": "u8" + }, + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "price", + "type": "u128" + }, + { + "name": "userBaseAssetAmount", + "type": "i128" + }, + { + "name": "quoteAssetAmount", + "type": "u128" + }, + { + "name": "baseAssetAmount", + "type": "u128" + }, + { + "name": "baseAssetAmountFilled", + "type": "u128" + }, + { + "name": "quoteAssetAmountFilled", + "type": "u128" + }, + { + "name": "fee", + "type": "u128" + }, + { + "name": "direction", + "type": { + "defined": "PositionDirection" + } + }, + { + "name": "reduceOnly", + "type": "bool" + }, + { + "name": "postOnly", + "type": "bool" + }, + { + "name": "immediateOrCancel", + "type": "bool" + }, + { + "name": "discountTier", + "type": { + "defined": "OrderDiscountTier" + } + }, + { + "name": "triggerPrice", + "type": "u128" + }, + { + "name": "triggerCondition", + "type": { + "defined": "OrderTriggerCondition" + } + }, + { + "name": "referrer", + "type": "publicKey" + }, + { + "name": "oraclePriceOffset", + "type": "i128" + }, + { + "name": "padding", + "type": { + "array": [ + "u16", + 3 + ] + } + } + ] + } + }, + { + "name": "SwapDirection", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Add" + }, + { + "name": "Remove" + } + ] + } + }, + { + "name": "Type", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Repeg" + }, + { + "name": "UpdateK" + } + ] + } + }, + { + "name": "DepositDirection", + "type": { + "kind": "enum", + "variants": [ + { + "name": "DEPOSIT" + }, + { + "name": "WITHDRAW" + } + ] + } + }, + { + "name": "LiquidationType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "NONE" + }, + { + "name": "PARTIAL" + }, + { + "name": "FULL" + } + ] + } + }, + { + "name": "OracleSource", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Pyth" + }, + { + "name": "Switchboard" + } + ] + } + }, + { + "name": "OrderAction", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Place" + }, + { + "name": "Cancel" + }, + { + "name": "Fill" + }, + { + "name": "Expire" + } + ] + } + }, + { + "name": "PositionDirection", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Long" + }, + { + "name": "Short" + } + ] + } + }, + { + "name": "OrderStatus", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Init" + }, + { + "name": "Open" + } + ] + } + }, + { + "name": "OrderType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Market" + }, + { + "name": "Limit" + }, + { + "name": "TriggerMarket" + }, + { + "name": "TriggerLimit" + } + ] + } + }, + { + "name": "OrderDiscountTier", + "type": { + "kind": "enum", + "variants": [ + { + "name": "None" + }, + { + "name": "First" + }, + { + "name": "Second" + }, + { + "name": "Third" + }, + { + "name": "Fourth" + } + ] + } + }, + { + "name": "OrderTriggerCondition", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Above" + }, + { + "name": "Below" + } + ] + } + } + ], + "errors": [ + { + "code": 6000, + "name": "InvalidCollateralAccountAuthority", + "msg": "Clearing house not collateral account owner" + }, + { + "code": 6001, + "name": "InvalidInsuranceAccountAuthority", + "msg": "Clearing house not insurance account owner" + }, + { + "code": 6002, + "name": "InsufficientDeposit", + "msg": "Insufficient deposit" + }, + { + "code": 6003, + "name": "InsufficientCollateral", + "msg": "Insufficient collateral" + }, + { + "code": 6004, + "name": "SufficientCollateral", + "msg": "Sufficient collateral" + }, + { + "code": 6005, + "name": "MaxNumberOfPositions", + "msg": "Max number of positions taken" + }, + { + "code": 6006, + "name": "AdminControlsPricesDisabled", + "msg": "Admin Controls Prices Disabled" + }, + { + "code": 6007, + "name": "MarketIndexNotInitialized", + "msg": "Market Index Not Initialized" + }, + { + "code": 6008, + "name": "MarketIndexAlreadyInitialized", + "msg": "Market Index Already Initialized" + }, + { + "code": 6009, + "name": "UserAccountAndUserPositionsAccountMismatch", + "msg": "User Account And User Positions Account Mismatch" + }, + { + "code": 6010, + "name": "UserHasNoPositionInMarket", + "msg": "User Has No Position In Market" + }, + { + "code": 6011, + "name": "InvalidInitialPeg", + "msg": "Invalid Initial Peg" + }, + { + "code": 6012, + "name": "InvalidRepegRedundant", + "msg": "AMM repeg already configured with amt given" + }, + { + "code": 6013, + "name": "InvalidRepegDirection", + "msg": "AMM repeg incorrect repeg direction" + }, + { + "code": 6014, + "name": "InvalidRepegProfitability", + "msg": "AMM repeg out of bounds pnl" + }, + { + "code": 6015, + "name": "SlippageOutsideLimit", + "msg": "Slippage Outside Limit Price" + }, + { + "code": 6016, + "name": "TradeSizeTooSmall", + "msg": "Trade Size Too Small" + }, + { + "code": 6017, + "name": "InvalidUpdateK", + "msg": "Price change too large when updating K" + }, + { + "code": 6018, + "name": "AdminWithdrawTooLarge", + "msg": "Admin tried to withdraw amount larger than fees collected" + }, + { + "code": 6019, + "name": "MathError", + "msg": "Math Error" + }, + { + "code": 6020, + "name": "BnConversionError", + "msg": "Conversion to u128/u64 failed with an overflow or underflow" + }, + { + "code": 6021, + "name": "ClockUnavailable", + "msg": "Clock unavailable" + }, + { + "code": 6022, + "name": "UnableToLoadOracle", + "msg": "Unable To Load Oracles" + }, + { + "code": 6023, + "name": "OracleMarkSpreadLimit", + "msg": "Oracle/Mark Spread Too Large" + }, + { + "code": 6024, + "name": "HistoryAlreadyInitialized", + "msg": "Clearing House history already initialized" + }, + { + "code": 6025, + "name": "ExchangePaused", + "msg": "Exchange is paused" + }, + { + "code": 6026, + "name": "InvalidWhitelistToken", + "msg": "Invalid whitelist token" + }, + { + "code": 6027, + "name": "WhitelistTokenNotFound", + "msg": "Whitelist token not found" + }, + { + "code": 6028, + "name": "InvalidDiscountToken", + "msg": "Invalid discount token" + }, + { + "code": 6029, + "name": "DiscountTokenNotFound", + "msg": "Discount token not found" + }, + { + "code": 6030, + "name": "InvalidReferrer", + "msg": "Invalid referrer" + }, + { + "code": 6031, + "name": "ReferrerNotFound", + "msg": "Referrer not found" + }, + { + "code": 6032, + "name": "InvalidOracle", + "msg": "InvalidOracle" + }, + { + "code": 6033, + "name": "OracleNotFound", + "msg": "OracleNotFound" + }, + { + "code": 6034, + "name": "LiquidationsBlockedByOracle", + "msg": "Liquidations Blocked By Oracle" + }, + { + "code": 6035, + "name": "UserMaxDeposit", + "msg": "Can not deposit more than max deposit" + }, + { + "code": 6036, + "name": "CantDeleteUserWithCollateral", + "msg": "Can not delete user that still has collateral" + }, + { + "code": 6037, + "name": "InvalidFundingProfitability", + "msg": "AMM funding out of bounds pnl" + }, + { + "code": 6038, + "name": "CastingFailure", + "msg": "Casting Failure" + }, + { + "code": 6039, + "name": "InvalidOrder", + "msg": "Invalid Order" + }, + { + "code": 6040, + "name": "UserHasNoOrder", + "msg": "User has no order" + }, + { + "code": 6041, + "name": "OrderAmountTooSmall", + "msg": "Order Amount Too Small" + }, + { + "code": 6042, + "name": "MaxNumberOfOrders", + "msg": "Max number of orders taken" + }, + { + "code": 6043, + "name": "OrderDoesNotExist", + "msg": "Order does not exist" + }, + { + "code": 6044, + "name": "OrderNotOpen", + "msg": "Order not open" + }, + { + "code": 6045, + "name": "CouldNotFillOrder", + "msg": "CouldNotFillOrder" + }, + { + "code": 6046, + "name": "ReduceOnlyOrderIncreasedRisk", + "msg": "Reduce only order increased risk" + }, + { + "code": 6047, + "name": "OrderStateAlreadyInitialized", + "msg": "Order state already initialized" + }, + { + "code": 6048, + "name": "UnableToLoadAccountLoader", + "msg": "Unable to load AccountLoader" + }, + { + "code": 6049, + "name": "TradeSizeTooLarge", + "msg": "Trade Size Too Large" + }, + { + "code": 6050, + "name": "UnableToWriteToRemainingAccount", + "msg": "Unable to write to remaining account" + }, + { + "code": 6051, + "name": "UserCantReferThemselves", + "msg": "User cant refer themselves" + }, + { + "code": 6052, + "name": "DidNotReceiveExpectedReferrer", + "msg": "Did not receive expected referrer" + }, + { + "code": 6053, + "name": "CouldNotDeserializeReferrer", + "msg": "Could not deserialize referrer" + }, + { + "code": 6054, + "name": "MarketOrderMustBeInPlaceAndFill", + "msg": "Market order must be in place and fill" + }, + { + "code": 6055, + "name": "UserOrderIdAlreadyInUse", + "msg": "User Order Id Already In Use" + }, + { + "code": 6056, + "name": "NoPositionsLiquidatable", + "msg": "No positions liquidatable" + }, + { + "code": 6057, + "name": "InvalidMarginRatio", + "msg": "Invalid Margin Ratio" + }, + { + "code": 6058, + "name": "CantCancelPostOnlyOrder", + "msg": "Cant Cancel Post Only Order" + }, + { + "code": 6059, + "name": "InvalidOracleOffset", + "msg": "InvalidOracleOffset" + }, + { + "code": 6060, + "name": "CantExpireOrders", + "msg": "CantExpireOrders" + }, + { + "code": 6061, + "name": "InvalidRepegPriceImpact", + "msg": "AMM repeg mark price impact vs oracle too large" + } + ] +} \ No newline at end of file diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index 4aeb5cb2..b972bab5 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -8,10 +8,6 @@ import { AMM_TO_QUOTE_PRECISION_RATIO, QUOTE_PRECISION, } from '../constants/numericConstants'; -<<<<<<< HEAD -// import { calculateBaseAssetValue } from './position'; -======= ->>>>>>> origin/master import { AMM, PositionDirection, @@ -20,16 +16,7 @@ import { isVariant, } from '../types'; import { assert } from '../assert/assert'; -<<<<<<< HEAD -import { - // calculatePositionPNL, - // calculateMarkPrice, - // convertToNumber, - squareRootBN, -} from '..'; -======= import { squareRootBN } from '..'; ->>>>>>> origin/master /** * Calculates a price given an arbitrary base and quote amount (they must have the same precision) From 0399928f25709c857ca7521557a5f95f3d7b5980 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Mon, 4 Apr 2022 17:06:32 -0400 Subject: [PATCH 35/59] init --- programs/clearing_house/src/context.rs | 1 + programs/clearing_house/src/controller/amm.rs | 99 ++++++++++++++++++- .../clearing_house/src/controller/funding.rs | 24 +---- .../clearing_house/src/controller/orders.rs | 7 +- programs/clearing_house/src/lib.rs | 9 ++ programs/clearing_house/src/math/amm.rs | 33 ++++++- 6 files changed, 145 insertions(+), 28 deletions(-) diff --git a/programs/clearing_house/src/context.rs b/programs/clearing_house/src/context.rs index 23b86782..2d5f492b 100644 --- a/programs/clearing_house/src/context.rs +++ b/programs/clearing_house/src/context.rs @@ -851,6 +851,7 @@ pub struct UpdateFundingRate<'info> { constraint = &state.funding_rate_history.eq(&funding_rate_history.key()) )] pub funding_rate_history: AccountLoader<'info, FundingRateHistory>, + pub extended_curve_history: AccountLoader<'info, ExtendedCurveHistory>, } #[derive(Accounts)] diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index d136c105..bd903f57 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -4,9 +4,14 @@ use crate::error::{ClearingHouseResult, ErrorCode}; use crate::math::amm::calculate_quote_asset_amount_swapped; use crate::math::casting::{cast, cast_to_i128}; use crate::math::constants::PRICE_TO_PEG_PRECISION_RATIO; -use crate::math::{amm, bn, quote_asset::*}; +use crate::math::{amm, bn, quote_asset::*, repeg}; +use crate::controller::repeg::apply_cost_to_market; use crate::math_error; -use crate::state::market::AMM; +use crate::state::history::curve::{ExtendedCurveHistory, ExtendedCurveRecord}; +use crate::state::market::{Market, AMM, OraclePriceData}; +use std::cell::RefMut; + +use std::cmp::{max, min}; #[derive(Clone, Copy, PartialEq)] pub enum SwapDirection { @@ -92,6 +97,96 @@ pub fn move_price( Ok(()) } +pub fn formulaic_k( + market: &mut Market, + mark_price: u128, + oracle_price_data: &OraclePriceData, + is_oracle_valid: bool, + funding_imbalance_cost: i128, + curve_history: Option<&mut RefMut>, + now: i64, + market_index: u64, + trade_record: Option, +) -> ClearingHouseResult { + let peg_multiplier_before = market.amm.peg_multiplier; + let base_asset_reserve_before = market.amm.base_asset_reserve; + let quote_asset_reserve_before = market.amm.quote_asset_reserve; + let sqrt_k_before = market.amm.sqrt_k; + + // calculate budget + let budget = if funding_imbalance_cost < 0 { + // negative cost is period revenue, give back half in k increase + funding_imbalance_cost + .checked_div(2) + .ok_or_else(math_error!())? + } else if market.amm.net_revenue_since_last_funding < (funding_imbalance_cost as i64) { + // cost exceeded period revenue, take back half in k decrease + max(0, market.amm.net_revenue_since_last_funding) + .checked_sub(funding_imbalance_cost as i64) + .ok_or_else(math_error!())? + .checked_div(2) + .ok_or_else(math_error!())? as i128 + } else { + 0 + }; + + if budget != 0 && curve_history.is_some() { + let (p_numer, p_denom) = amm::budget_k_adjustment(market, budget)?; + + if p_numer > p_denom { + msg!("increase sqrt_k (* {:?}/{:?})", p_numer, p_denom); + } else if p_numer < p_denom { + msg!("decrease sqrt_k (* {:?}/{:?})", p_numer, p_denom); + } + + let new_sqrt_k = market + .amm + .sqrt_k + .checked_mul(p_numer) + .ok_or_else(math_error!())? + .checked_div(p_denom) + .ok_or_else(math_error!())?; + + // let adjustment_cost = adjust_k_cost(market, bn::U256::from(new_sqrt_k))?; + + let adjustment_cost = amm::adjust_k_cost(market, bn::U256::from(new_sqrt_k))?; + let cost_applied = apply_cost_to_market(market, adjustment_cost)?; + if cost_applied { + // todo: do actual k adj here + + let peg_multiplier_after = market.amm.peg_multiplier; + let base_asset_reserve_after = market.amm.base_asset_reserve; + let quote_asset_reserve_after = market.amm.quote_asset_reserve; + let sqrt_k_after = market.amm.sqrt_k; + + let record_id = curve_history?.next_record_id(); + curve_history?.append(ExtendedCurveRecord { + ts: now, + record_id, + market_index, + peg_multiplier_before, + base_asset_reserve_before, + quote_asset_reserve_before, + sqrt_k_before, + peg_multiplier_after, + base_asset_reserve_after, + quote_asset_reserve_after, + sqrt_k_after, + base_asset_amount_long: market.base_asset_amount_long.unsigned_abs(), + base_asset_amount_short: market.base_asset_amount_short.unsigned_abs(), + base_asset_amount: market.base_asset_amount, + open_interest: market.open_interest, + total_fee: market.amm.total_fee, + total_fee_minus_distributions: market.amm.total_fee_minus_distributions, + adjustment_cost, + oracle_price: oracle_price_data.price, + trade_record: trade_record.unwrap_or(0), + padding: [0; 5], + }); + } + } +} + #[allow(dead_code)] pub fn move_to_price(amm: &mut AMM, target_price: u128) -> ClearingHouseResult { let sqrt_k = bn::U256::from(amm.sqrt_k); diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index c2fad11f..50056818 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -5,7 +5,7 @@ use anchor_lang::prelude::*; use crate::error::*; use crate::math::amm; -use crate::math::amm::{budget_k_adjustment, normalise_oracle_price}; +use crate::math::amm::{adjust_k_cost, budget_k_adjustment, normalise_oracle_price}; use crate::math::casting::{cast, cast_to_i128}; use crate::math::collateral::calculate_updated_collateral; use crate::math::constants::{ @@ -14,6 +14,7 @@ use crate::math::constants::{ use crate::math::funding::{calculate_funding_payment, calculate_funding_rate_long_short}; use crate::math::oracle; use crate::math_error; +use crate::state::history::curve::ExtendedCurveHistory; use crate::state::history::funding_payment::{FundingPaymentHistory, FundingPaymentRecord}; use crate::state::history::funding_rate::{FundingRateHistory, FundingRateRecord}; use crate::state::market::AMM; @@ -93,6 +94,7 @@ pub fn update_funding_rate( now: UnixTimestamp, clock_slot: u64, funding_rate_history: &mut RefMut, + curve_history: &mut Option>, guard_rails: &OracleGuardRails, funding_paused: bool, precomputed_mark_price: Option, @@ -183,26 +185,6 @@ pub fn update_funding_rate( ) .ok_or_else(math_error!())?; - let budget = if funding_imbalance_cost < 0 { - // negative cost is period revenue, give back half in k increase - funding_imbalance_cost - .checked_div(2) - .ok_or_else(math_error!())? - } else if market.amm.net_revenue_since_last_funding < (funding_imbalance_cost as i64) { - // cost exceeded period revenue, take back half in k decrease - max(0, market.amm.net_revenue_since_last_funding) - .checked_sub(funding_imbalance_cost as i64) - .ok_or_else(math_error!())? - .checked_div(2) - .ok_or_else(math_error!())? as i128 - } else { - 0 - }; - if budget != 0 { - let (p_numer, p_denom) = budget_k_adjustment(market, budget)?; - msg!("update k by {:?}/{:?}", p_numer, p_denom); - } - market.amm.cumulative_funding_rate_long = market .amm .cumulative_funding_rate_long diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index ce7ff9c2..c0765450 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -800,6 +800,9 @@ pub fn fill_order( let funding_rate_history = &mut funding_rate_history .load_mut() .or(Err(ErrorCode::UnableToLoadAccountLoader))?; + let extended_curve_history = &mut extended_curve_history + .load_mut() + .or(Err(ErrorCode::UnableToLoadAccountLoader))?; controller::funding::update_funding_rate( market_index, market, @@ -807,6 +810,7 @@ pub fn fill_order( now, clock_slot, funding_rate_history, + Some(extended_curve_history), &state.oracle_guard_rails, state.funding_paused, Some(mark_price_before), @@ -814,9 +818,6 @@ pub fn fill_order( // if market_index >= 12 { // todo for soft launch - let extended_curve_history = &mut extended_curve_history - .load_mut() - .or(Err(ErrorCode::UnableToLoadAccountLoader))?; controller::repeg::formulaic_repeg( market, diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index f7ca7356..35aa49ea 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -765,6 +765,7 @@ pub mod clearing_house { now, clock_slot, funding_rate_history, + &mut Some(None), &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, Some(mark_price_before), @@ -959,6 +960,7 @@ pub mod clearing_house { now, clock_slot, funding_rate_history, + Some(None), &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, Some(mark_price_before), @@ -2079,6 +2081,12 @@ pub mod clearing_house { let clock_slot = clock.slot; let funding_rate_history = &mut ctx.accounts.funding_rate_history.load_mut()?; + let extended_curve_history = &mut ctx + .accounts + .extended_curve_history + .load_mut() + .or(Err(ErrorCode::UnableToLoadAccountLoader))?; + controller::funding::update_funding_rate( market_index, market, @@ -2086,6 +2094,7 @@ pub mod clearing_house { now, clock_slot, funding_rate_history, + Some(extended_curve_history), &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, None, diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 9843f674..594d22f1 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -453,7 +453,7 @@ pub fn is_oracle_valid( || is_conf_too_large)) } -pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseResult<(i128, i128)> { +pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseResult<(u128, u128)> { let y = market.amm.quote_asset_reserve; let x = market.amm.base_asset_reserve; let c = budget; @@ -514,7 +514,36 @@ pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseRe .checked_div(AMM_RESERVE_PRECISIONi128) .ok_or_else(math_error!())?; - Ok((numerator, denominator)) + let K_PCT_SCALE = 10000; + let lower_bound = 9991; + let upper_bound = 10010; + + assert!(upper_bound > K_PCT_SCALE); + assert!(lower_bound < K_PCT_SCALE); + assert!(upper_bound < (K_PCT_SCALE * 101 / 100)); + assert!(lower_bound > (K_PCT_SCALE * 99 / 100)); + + let numerator_clipped = if numerator > denominator { + min( + numerator, + denominator + .checked_mul(upper_bound) + .ok_or_else(math_error!())? + .checked_div(K_PCT_SCALE) + .ok_or_else(math_error!())?, + ) + } else { + max( + numerator, + denominator + .checked_mul(lower_bound) + .ok_or_else(math_error!())? + .checked_div(K_PCT_SCALE) + .ok_or_else(math_error!())?, + ) + }; + + Ok((cast_to_u128(numerator_clipped)?, cast_to_u128(denominator)?)) } /// To find the cost of adjusting k, compare the the net market value before and after adjusting k From 5ead5171194f0429c1cafc6557d4aecaa5c84952 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Tue, 5 Apr 2022 12:13:57 -0400 Subject: [PATCH 36/59] form-k: fix complier errors --- programs/clearing_house/src/context.rs | 4 ++++ programs/clearing_house/src/controller/amm.rs | 6 ++++-- programs/clearing_house/src/controller/funding.rs | 2 +- programs/clearing_house/src/controller/repeg.rs | 2 +- programs/clearing_house/src/lib.rs | 4 ++-- sdk/src/clearingHouse.ts | 1 + sdk/src/idl/clearing_house.json | 5 +++++ sdk/src/math/repeg.ts | 8 -------- 8 files changed, 18 insertions(+), 14 deletions(-) diff --git a/programs/clearing_house/src/context.rs b/programs/clearing_house/src/context.rs index 2d5f492b..19976a77 100644 --- a/programs/clearing_house/src/context.rs +++ b/programs/clearing_house/src/context.rs @@ -851,6 +851,10 @@ pub struct UpdateFundingRate<'info> { constraint = &state.funding_rate_history.eq(&funding_rate_history.key()) )] pub funding_rate_history: AccountLoader<'info, FundingRateHistory>, + #[account( + mut, + constraint = &state.extended_curve_history.eq(&extended_curve_history.key()) + )] pub extended_curve_history: AccountLoader<'info, ExtendedCurveHistory>, } diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index bd903f57..21f41346 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -131,6 +131,7 @@ pub fn formulaic_k( }; if budget != 0 && curve_history.is_some() { + let curve_history = curve_history.unwrap(); let (p_numer, p_denom) = amm::budget_k_adjustment(market, budget)?; if p_numer > p_denom { @@ -159,8 +160,8 @@ pub fn formulaic_k( let quote_asset_reserve_after = market.amm.quote_asset_reserve; let sqrt_k_after = market.amm.sqrt_k; - let record_id = curve_history?.next_record_id(); - curve_history?.append(ExtendedCurveRecord { + let record_id = curve_history.next_record_id(); + curve_history.append(ExtendedCurveRecord { ts: now, record_id, market_index, @@ -185,6 +186,7 @@ pub fn formulaic_k( }); } } + Ok(()) } #[allow(dead_code)] diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 50056818..672fda0c 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -94,7 +94,7 @@ pub fn update_funding_rate( now: UnixTimestamp, clock_slot: u64, funding_rate_history: &mut RefMut, - curve_history: &mut Option>, + curve_history: Option<&mut RefMut>, guard_rails: &OracleGuardRails, funding_paused: bool, precomputed_mark_price: Option, diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index ad27add0..f5215b47 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -172,7 +172,7 @@ pub fn formulaic_repeg( Ok(adjustment_cost) } -fn apply_cost_to_market(market: &mut Market, cost: i128) -> ClearingHouseResult { +pub fn apply_cost_to_market(market: &mut Market, cost: i128) -> ClearingHouseResult { // positive cost is expense, negative cost is revenue // Reduce pnl to quote asset precision and take the absolute value if cost > 0 { diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 35aa49ea..008f004f 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -765,7 +765,7 @@ pub mod clearing_house { now, clock_slot, funding_rate_history, - &mut Some(None), + None, &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, Some(mark_price_before), @@ -960,7 +960,7 @@ pub mod clearing_house { now, clock_slot, funding_rate_history, - Some(None), + None, &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, Some(mark_price_before), diff --git a/sdk/src/clearingHouse.ts b/sdk/src/clearingHouse.ts index 0cb02c4e..f114b954 100644 --- a/sdk/src/clearingHouse.ts +++ b/sdk/src/clearingHouse.ts @@ -1311,6 +1311,7 @@ export class ClearingHouse { markets: state.markets, oracle: oracle, fundingRateHistory: state.fundingRateHistory, + extendedCurveHistory: state.extendedCurveHistory, }, }); } diff --git a/sdk/src/idl/clearing_house.json b/sdk/src/idl/clearing_house.json index 7bd288a1..60ef605e 100644 --- a/sdk/src/idl/clearing_house.json +++ b/sdk/src/idl/clearing_house.json @@ -1561,6 +1561,11 @@ "name": "fundingRateHistory", "isMut": true, "isSigner": false + }, + { + "name": "extendedCurveHistory", + "isMut": true, + "isSigner": false } ], "args": [ diff --git a/sdk/src/math/repeg.ts b/sdk/src/math/repeg.ts index f7da465d..104261de 100644 --- a/sdk/src/math/repeg.ts +++ b/sdk/src/math/repeg.ts @@ -1,9 +1,5 @@ import { BN } from '@project-serum/anchor'; import { -<<<<<<< HEAD - // AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO, -======= ->>>>>>> origin/master MARK_PRICE_PRECISION, AMM_RESERVE_PRECISION, PEG_PRECISION, @@ -15,10 +11,6 @@ import { calculateBaseAssetValue } from './position'; import { calculateTerminalPrice } from './amm'; import { Market } from '../types'; -<<<<<<< HEAD -// import { assert } from '../assert/assert'; -======= ->>>>>>> origin/master import { calculatePositionPNL, calculateMarkPrice, convertToNumber } from '..'; /** From b76d3e759132efe9a7788d6ff9b3f098d0b6a112 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 4 Apr 2022 16:52:34 -0400 Subject: [PATCH 37/59] sdk: add NEAR --- sdk/src/constants/markets.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sdk/src/constants/markets.ts b/sdk/src/constants/markets.ts index 56148e2a..dceef194 100644 --- a/sdk/src/constants/markets.ts +++ b/sdk/src/constants/markets.ts @@ -187,6 +187,17 @@ export const Markets: MarketConfig[] = [ launchTs: 1648607439000, oracleSource: OracleSource.PYTH, }, + { + fullName: 'Near', + category: ['L1', 'Infra'], + symbol: 'NEAR-PERP', + baseAssetSymbol: 'NEAR', + marketIndex: new BN(16), + devnetPublicKey: '3gnSbT7bhoTdGkFVZc1dW1PvjreWzpUNUD5ppXwv1N59', + mainnetPublicKey: 'ECSFWQ1bnnpqPVvoy9237t2wddZAaHisW88mYxuEHKWf', + launchTs: 1649105516000, + oracleSource: OracleSource.PYTH, + }, // { // symbol: 'mSOL-PERP', // baseAssetSymbol: 'mSOL', From 21eeff9e1febe415b22bcdf7c356a76942cf88b3 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Mon, 4 Apr 2022 17:08:58 -0400 Subject: [PATCH 38/59] export math/repeg.ts, remove redudant amm.ts --- sdk/src/index.ts | 1 + sdk/src/math/amm.ts | 83 --------------------------------------------- 2 files changed, 1 insertion(+), 83 deletions(-) diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 3aafc501..8a4058ed 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -29,6 +29,7 @@ export * from './math/position'; export * from './math/amm'; export * from './math/trade'; export * from './math/orders'; +export * from './math/repeg'; export * from './orders'; export * from './orderParams'; export * from './wallet'; diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index b972bab5..bd32df7d 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -190,86 +190,3 @@ export function calculateMaxBaseAssetAmountToTrade( return [new BN(0), PositionDirection.LONG]; } } - -export function calculateBudgetedK(market: Market, cost: BN): [BN, BN] { - // wolframalpha.com - // (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p - // p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q) - - // todo: assumes k = x * y - // otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p - - // const k = market.amm.sqrtK.mul(market.amm.sqrtK); - const x = market.amm.baseAssetReserve; - const y = market.amm.quoteAssetReserve; - - const d = market.baseAssetAmount; - const Q = market.amm.pegMultiplier; - - const C = cost.mul(new BN(-1)); - - const numer1 = y.mul(d).mul(Q).div(AMM_RESERVE_PRECISION).div(PEG_PRECISION); - const numer2 = C.mul(x.add(d)).div(QUOTE_PRECISION); - const denom1 = C.mul(x) - .mul(x.add(d)) - .div(AMM_RESERVE_PRECISION) - .div(QUOTE_PRECISION); - const denom2 = y - .mul(d) - .mul(d) - .mul(Q) - .div(AMM_RESERVE_PRECISION) - .div(AMM_RESERVE_PRECISION) - .div(PEG_PRECISION); - - const numerator = d - .mul(numer1.add(numer2)) - .div(AMM_RESERVE_PRECISION) - .div(AMM_RESERVE_PRECISION) - .div(AMM_TO_QUOTE_PRECISION_RATIO); - const denominator = denom1 - .add(denom2) - .div(AMM_RESERVE_PRECISION) - .div(AMM_TO_QUOTE_PRECISION_RATIO); - console.log(numerator, denominator); - // const p = (numerator).div(denominator); - - // const formulaCost = (numer21.sub(numer20).sub(numer1)).mul(market.amm.pegMultiplier).div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO) - // console.log(convertToNumber(formulaCost, QUOTE_PRECISION)) - - return [numerator, denominator]; -} - -export function calculateBudgetedPeg(market: Market, cost: BN): BN { - // wolframalpha.com - // (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p - // p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q) - - // todo: assumes k = x * y - // otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p - - const k = market.amm.sqrtK.mul(market.amm.sqrtK); - const x = market.amm.baseAssetReserve; - const y = market.amm.quoteAssetReserve; - - const d = market.baseAssetAmount; - const Q = market.amm.pegMultiplier; - - const C = cost.mul(new BN(-1)); - - const deltaQuoteAssetReserves = y.sub(k.div(x.add(d))); - const deltaPegMultiplier = C.mul(MARK_PRICE_PRECISION) - .div(deltaQuoteAssetReserves.div(AMM_TO_QUOTE_PRECISION_RATIO)) - .mul(PEG_PRECISION) - .div(QUOTE_PRECISION); - console.log( - Q.toNumber(), - 'change by', - deltaPegMultiplier.toNumber() / MARK_PRICE_PRECISION.toNumber() - ); - const newPeg = Q.sub( - deltaPegMultiplier.mul(PEG_PRECISION).div(MARK_PRICE_PRECISION) - ); - - return newPeg; -} From aae07e66997379d6c635da49d5a9baf8d4b0a356 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Mon, 4 Apr 2022 17:09:38 -0400 Subject: [PATCH 39/59] amm.ts: remove unused imports --- sdk/src/math/amm.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index bd32df7d..a3facd1e 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -2,11 +2,8 @@ import { BN } from '@project-serum/anchor'; import { AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO, MARK_PRICE_PRECISION, - AMM_RESERVE_PRECISION, PEG_PRECISION, ZERO, - AMM_TO_QUOTE_PRECISION_RATIO, - QUOTE_PRECISION, } from '../constants/numericConstants'; import { AMM, From 5ee1f66b412ca288a1123aad1181224281d1d07a Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 4 Apr 2022 17:52:41 -0400 Subject: [PATCH 40/59] sdk: use pyth aggregate price for oracle price --- sdk/src/oracles/pythClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/oracles/pythClient.ts b/sdk/src/oracles/pythClient.ts index 23ed7a1f..691fd98d 100644 --- a/sdk/src/oracles/pythClient.ts +++ b/sdk/src/oracles/pythClient.ts @@ -28,7 +28,7 @@ export class PythClient { ): Promise { const priceData = parsePriceData(buffer); return { - price: convertPythPrice(priceData.price, priceData.exponent), + price: convertPythPrice(priceData.aggregate.price, priceData.exponent), slot: new BN(priceData.lastSlot.toString()), confidence: convertPythPrice(priceData.confidence, priceData.exponent), twap: convertPythPrice(priceData.twap.value, priceData.exponent), From 7eaeab661e7197cd0ad581551846d5d83d9fcc5c Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 4 Apr 2022 18:12:11 -0400 Subject: [PATCH 41/59] sdk: release v0.1.29 --- sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/package.json b/sdk/package.json index 3416afc1..5f59f545 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "0.1.29-master.1", + "version": "0.1.29", "main": "lib/index.js", "types": "lib/index.d.ts", "author": "crispheaney", From eea4332ae0d8714ffd0d6558868dd073283a1d55 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Wed, 6 Apr 2022 11:07:08 -0400 Subject: [PATCH 42/59] segment adjust and update for k --- programs/clearing_house/src/controller/amm.rs | 12 +++--- .../clearing_house/src/controller/funding.rs | 2 +- programs/clearing_house/src/lib.rs | 7 +++- programs/clearing_house/src/math/amm.rs | 31 +++++++++++----- test-scripts/run-anchor-tests.sh | 2 +- tests/formulaPeg.ts | 37 ++----------------- 6 files changed, 39 insertions(+), 52 deletions(-) diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index 21f41346..160b00a4 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -1,14 +1,14 @@ use solana_program::msg; +use crate::controller::repeg::apply_cost_to_market; use crate::error::{ClearingHouseResult, ErrorCode}; use crate::math::amm::calculate_quote_asset_amount_swapped; use crate::math::casting::{cast, cast_to_i128}; use crate::math::constants::PRICE_TO_PEG_PRECISION_RATIO; use crate::math::{amm, bn, quote_asset::*, repeg}; -use crate::controller::repeg::apply_cost_to_market; use crate::math_error; use crate::state::history::curve::{ExtendedCurveHistory, ExtendedCurveRecord}; -use crate::state::market::{Market, AMM, OraclePriceData}; +use crate::state::market::{Market, OraclePriceData, AMM}; use std::cell::RefMut; use std::cmp::{max, min}; @@ -132,7 +132,7 @@ pub fn formulaic_k( if budget != 0 && curve_history.is_some() { let curve_history = curve_history.unwrap(); - let (p_numer, p_denom) = amm::budget_k_adjustment(market, budget)?; + let (p_numer, p_denom) = amm::calculate_budgeted_k_scale(market, budget)?; if p_numer > p_denom { msg!("increase sqrt_k (* {:?}/{:?})", p_numer, p_denom); @@ -148,12 +148,12 @@ pub fn formulaic_k( .checked_div(p_denom) .ok_or_else(math_error!())?; - // let adjustment_cost = adjust_k_cost(market, bn::U256::from(new_sqrt_k))?; - - let adjustment_cost = amm::adjust_k_cost(market, bn::U256::from(new_sqrt_k))?; + let (adjust_k_market, adjustment_cost) = + amm::adjust_k_cost(market, bn::U256::from(new_sqrt_k))?; let cost_applied = apply_cost_to_market(market, adjustment_cost)?; if cost_applied { // todo: do actual k adj here + amm::update_k(market, bn::U256::from(new_sqrt_k))?; let peg_multiplier_after = market.amm.peg_multiplier; let base_asset_reserve_after = market.amm.base_asset_reserve; diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 672fda0c..c22f2011 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -5,7 +5,7 @@ use anchor_lang::prelude::*; use crate::error::*; use crate::math::amm; -use crate::math::amm::{adjust_k_cost, budget_k_adjustment, normalise_oracle_price}; +use crate::math::amm::{adjust_k_cost, calculate_budgeted_k_scale, normalise_oracle_price}; use crate::math::casting::{cast, cast_to_i128}; use crate::math::collateral::calculate_updated_collateral; use crate::math::constants::{ diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 008f004f..733a6092 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -2132,7 +2132,12 @@ pub mod clearing_house { let quote_asset_reserve_before = market.amm.quote_asset_reserve; let sqrt_k_before = market.amm.sqrt_k; - let adjustment_cost = math::amm::adjust_k_cost(market, bn::U256::from(sqrt_k))?; + let new_sqrt_k_U256 = bn::U256::from(sqrt_k); + + let (mut adjust_k_market, adjustment_cost) = + math::amm::adjust_k_cost(market, new_sqrt_k_U256)?; + + math::amm::update_k(market, new_sqrt_k_U256); if adjustment_cost > 0 { let max_cost = market diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 594d22f1..6edc62e1 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -453,7 +453,10 @@ pub fn is_oracle_valid( || is_conf_too_large)) } -pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseResult<(u128, u128)> { +pub fn calculate_budgeted_k_scale( + market: &mut Market, + budget: i128, +) -> ClearingHouseResult<(u128, u128)> { let y = market.amm.quote_asset_reserve; let x = market.amm.base_asset_reserve; let c = budget; @@ -514,6 +517,7 @@ pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseRe .checked_div(AMM_RESERVE_PRECISIONi128) .ok_or_else(math_error!())?; + // hardcoded scale bounds for a single update (.1% increase and .09% decrease) let K_PCT_SCALE = 10000; let lower_bound = 9991; let upper_bound = 10010; @@ -549,11 +553,24 @@ pub fn budget_k_adjustment(market: &mut Market, budget: i128) -> ClearingHouseRe /// To find the cost of adjusting k, compare the the net market value before and after adjusting k /// Increasing k costs the protocol money because it reduces slippage and improves the exit price for net market position /// Decreasing k costs the protocol money because it increases slippage and hurts the exit price for net market position -pub fn adjust_k_cost(market: &mut Market, new_sqrt_k: bn::U256) -> ClearingHouseResult { +pub fn adjust_k_cost(market: &Market, new_sqrt_k: bn::U256) -> ClearingHouseResult<(Market, i128)> { + let mut market_clone = *market; + // Find the net market value before adjusting k let (current_net_market_value, _) = - _calculate_base_asset_value_and_pnl(market.base_asset_amount, 0, &market.amm)?; + _calculate_base_asset_value_and_pnl(market_clone.base_asset_amount, 0, &market_clone.amm)?; + + update_k(&mut market_clone, new_sqrt_k); + + let (_new_net_market_value, cost) = _calculate_base_asset_value_and_pnl( + market_clone.base_asset_amount, + current_net_market_value, + &market_clone.amm, + )?; + Ok((market_clone, cost)) +} +pub fn update_k(market: &mut Market, new_sqrt_k: bn::U256) -> ClearingHouseResult { let mark_price_precision = bn::U256::from(MARK_PRICE_PRECISION); let sqrt_k_ratio = new_sqrt_k @@ -593,13 +610,7 @@ pub fn adjust_k_cost(market: &mut Market, new_sqrt_k: bn::U256) -> ClearingHouse .try_to_u128() .unwrap(); - let (_new_net_market_value, cost) = _calculate_base_asset_value_and_pnl( - market.base_asset_amount, - current_net_market_value, - &market.amm, - )?; - - Ok(cost) + Ok(()) } pub fn calculate_max_base_asset_amount_to_trade( diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 82078002..cc220c52 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -5,7 +5,7 @@ if [ "$1" != "--skip-build" ] fi test_files=(formulaPeg.ts order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts makerOrder.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts expireOrders.ts oracleOffsetOrders.ts cancelAllOrders.ts roundReduceOnlyOrder.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts deleteUser.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) -test_files=(formulaK.ts) +# test_files=(formulaK.ts) for test_file in ${test_files[@]}; do export ANCHOR_TEST_FILE=${test_file} && anchor test --skip-build || exit 1; diff --git a/tests/formulaPeg.ts b/tests/formulaPeg.ts index 7d7dd7b6..84408ea4 100644 --- a/tests/formulaPeg.ts +++ b/tests/formulaPeg.ts @@ -4,27 +4,12 @@ import { Connection, Keypair } from '@solana/web3.js'; import { Program } from '@project-serum/anchor'; import { BN, -<<<<<<< HEAD - FUNDING_PAYMENT_PRECISION, -======= ->>>>>>> origin/master Admin, MARK_PRICE_PRECISION, calculateMarkPrice, ClearingHouseUser, PEG_PRECISION, PositionDirection, -<<<<<<< HEAD - calculateBudgetedPeg, - calculateBudgetedK, - // OrderStatus, - // OrderDiscountTier, - // OrderRecord, - // OrderAction, - // OrderTriggerCondition, - // calculateTargetPriceTrade, -======= ->>>>>>> origin/master convertToNumber, AMM_RESERVE_PRECISION, // Wallet, @@ -35,11 +20,6 @@ import { QUOTE_PRECISION, } from '../sdk/src'; -<<<<<<< HEAD -import { Markets } from '../sdk/src/constants/markets'; - -======= ->>>>>>> origin/master import { createPriceFeed, mockUSDCMint, @@ -58,13 +38,8 @@ async function formRepegHelper( clearingHouse: Admin, userAccount: ClearingHouseUser, marketIndex: BN, -<<<<<<< HEAD - oraclePrice: Number, - amt: Number, -======= oraclePrice: number, amt: number, ->>>>>>> origin/master direction: PositionDirection ) { const markets = await clearingHouse.getMarketsAccount(); @@ -414,6 +389,7 @@ describe('formulaic curve (repeg)', () => { // net revenue above fee collected const feeCollected = newOracle * base * 0.001; + console.log('feeCollected (', feeCollected, ') < profit (', profit, ')'); assert(profit > feeCollected); const netRev2 = await formRepegHelper( @@ -454,11 +430,7 @@ describe('formulaic curve (repeg)', () => { const newOracle = 0.16; const base = 2; -<<<<<<< HEAD const profit = await formRepegHelper( -======= - await formRepegHelper( ->>>>>>> origin/master connection, clearingHouse, userAccount, @@ -467,6 +439,8 @@ describe('formulaic curve (repeg)', () => { base, PositionDirection.LONG ); + + console.log('profit:', profit); }); it('cause repeg? tiny prices', async () => { @@ -489,11 +463,7 @@ describe('formulaic curve (repeg)', () => { const newOracle = 0.1699; const base = 100; -<<<<<<< HEAD const profit = await formRepegHelper( -======= - await formRepegHelper( ->>>>>>> origin/master connection, clearingHouse, userAccount, @@ -502,5 +472,6 @@ describe('formulaic curve (repeg)', () => { base, PositionDirection.LONG ); + console.log('profit:', profit); }); }); From e1effd3a8f4044bb77da8d2a84142a93f564e9b1 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 7 Apr 2022 09:24:03 -0400 Subject: [PATCH 43/59] repeg: calculate target price as a function of oracle delay and normalised oracle price --- .../clearing_house/src/controller/repeg.rs | 6 +- programs/clearing_house/src/math/repeg.rs | 56 ++++++++++++++++--- programs/pyth/src/lib.rs | 1 + 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index f5215b47..1265a58e 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -89,9 +89,9 @@ pub fn formulaic_repeg( ) -> ClearingHouseResult { // backrun market swaps to do automatic on-chain repeg - if !is_oracle_valid || oracle_price_data.delay > 5 { + if !is_oracle_valid { msg!( - "invalid oracle (oracle delay = {:?})", + "skipping formulaic_repeg: invalid oracle (oracle delay = {:?})", oracle_price_data.delay ); return Ok(0); @@ -115,7 +115,7 @@ pub fn formulaic_repeg( terminal_quote_reserves, repeg_budget, mark_price, - cast_to_u128(oracle_price_data.price)?, + oracle_price_data, )?; let ( diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index c4fddfb5..25e3eb80 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -11,7 +11,7 @@ use crate::math::constants::{ }; use crate::math::position::_calculate_base_asset_value_and_pnl; use crate::math_error; -use crate::state::market::{Market, OraclePriceData}; +use crate::state::market::{Market, OraclePriceData, AMM}; use std::cmp::{max, min}; use crate::state::state::OracleGuardRails; @@ -177,23 +177,61 @@ pub fn calculate_peg_from_target_price( Ok(new_peg) } +pub fn calculate_mm_target_price( + amm: &AMM, + current_price: u128, + oracle_price_data: &OraclePriceData, +) -> ClearingHouseResult { + // calculates peg_multiplier that changing to would cost no more than budget + let oracle_delay = oracle_price_data.delay; + + let oracle_price_normalised = cast_to_u128(amm::normalise_oracle_price( + amm, + oracle_price_data, + Some(current_price), + )?)?; + + let weight_denom = 100_u128; + + let oracle_price_weight: u128 = cast_to_u128(max( + 0, + 50_i64 + .checked_sub(oracle_price_data.delay) + .ok_or_else(math_error!())?, + ))?; + let current_price_weight: u128 = weight_denom + .checked_sub(oracle_price_weight) + .ok_or_else(math_error!())?; + + let target_price = oracle_price_normalised + .checked_mul(oracle_price_weight) + .ok_or_else(math_error!())? + .checked_div(weight_denom) + .ok_or_else(math_error!())? + .checked_add( + current_price + .checked_mul(current_price_weight) + .ok_or_else(math_error!())? + .checked_div(weight_denom) + .ok_or_else(math_error!())?, + ) + .ok_or_else(math_error!())?; + + Ok(target_price) +} + pub fn calculate_budgeted_peg( market: &mut Market, terminal_quote_reserves: u128, budget: u128, current_price: u128, - target_price: u128, + oracle_price_data: &OraclePriceData, ) -> ClearingHouseResult<(u128, i128, Market)> { - // calculates peg_multiplier that changing to would cost no more than budget - + let target_price = calculate_mm_target_price(&market.amm, current_price, oracle_price_data)?; let optimal_peg = calculate_peg_from_target_price( market.amm.quote_asset_reserve, market.amm.base_asset_reserve, - target_price - .checked_add(current_price) - .ok_or_else(math_error!())? - .checked_div(2) - .ok_or_else(math_error!())?, + target_price, )?; let delta_peg_sign = if market.amm.quote_asset_reserve > terminal_quote_reserves { diff --git a/programs/pyth/src/lib.rs b/programs/pyth/src/lib.rs index e0f7ac69..ef92a8f1 100644 --- a/programs/pyth/src/lib.rs +++ b/programs/pyth/src/lib.rs @@ -23,6 +23,7 @@ pub mod pyth { price_oracle.agg.price = price; price_oracle.agg.conf = 0; + price_oracle.valid_slot = 228506959; //todo just turned 1->2 for negative delay price_oracle.twap = price; price_oracle.expo = expo; From ff1b7163901aa63b026235cc2309b3756056f045 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 7 Apr 2022 11:19:07 -0400 Subject: [PATCH 44/59] fix merge conflict --- programs/clearing_house/src/controller/amm.rs | 2 ++ .../clearing_house/src/controller/funding.rs | 26 ++++++++++++-- programs/clearing_house/src/math/repeg.rs | 2 +- stress/sim.ts | 36 ------------------- stress/stress.ts | 22 ------------ tests/clearingHouse.ts | 9 ----- tests/formulaK.ts | 34 +++++++++--------- 7 files changed, 43 insertions(+), 88 deletions(-) diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index 160b00a4..524d98c7 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -132,6 +132,8 @@ pub fn formulaic_k( if budget != 0 && curve_history.is_some() { let curve_history = curve_history.unwrap(); + + // single k scale is capped by .1% increase and .09% decrease (regardless of budget) let (p_numer, p_denom) = amm::calculate_budgeted_k_scale(market, budget)?; if p_numer > p_denom { diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index c22f2011..049b1082 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -4,8 +4,11 @@ use std::cmp::{max, min}; use anchor_lang::prelude::*; use crate::error::*; + +use crate::controller::amm::formulaic_k; + use crate::math::amm; -use crate::math::amm::{adjust_k_cost, calculate_budgeted_k_scale, normalise_oracle_price}; +use crate::math::amm::normalise_oracle_price; use crate::math::casting::{cast, cast_to_i128}; use crate::math::collateral::calculate_updated_collateral; use crate::math::constants::{ @@ -103,16 +106,21 @@ pub fn update_funding_rate( .checked_sub(market.amm.last_funding_rate_ts) .ok_or_else(math_error!())?; + let mark_price = match precomputed_mark_price { + Some(mark_price) => mark_price, + None => market.amm.mark_price()?, + }; + // Pause funding if oracle is invalid or if mark/oracle spread is too divergent let (block_funding_rate_update, oracle_price_data) = oracle::block_operation( &market.amm, price_oracle, clock_slot, guard_rails, - precomputed_mark_price, + Some(mark_price), )?; let normalised_oracle_price = - normalise_oracle_price(&market.amm, &oracle_price_data, precomputed_mark_price)?; + normalise_oracle_price(&market.amm, &oracle_price_data, Some(mark_price))?; // round next update time to be available on the hour let mut next_update_wait = market.amm.funding_period; @@ -185,6 +193,18 @@ pub fn update_funding_rate( ) .ok_or_else(math_error!())?; + formulaic_k( + market, + mark_price, + &oracle_price_data, + true, // only way to have gotten here + funding_imbalance_cost, + curve_history, + now, + market_index, + None, + )?; + market.amm.cumulative_funding_rate_long = market .amm .cumulative_funding_rate_long diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 25e3eb80..80580fbd 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -196,7 +196,7 @@ pub fn calculate_mm_target_price( let oracle_price_weight: u128 = cast_to_u128(max( 0, 50_i64 - .checked_sub(oracle_price_data.delay) + .checked_sub(max(0, oracle_price_data.delay)) .ok_or_else(math_error!())?, ))?; let current_price_weight: u128 = weight_denom diff --git a/stress/sim.ts b/stress/sim.ts index ddca7579..5747072e 100644 --- a/stress/sim.ts +++ b/stress/sim.ts @@ -1,40 +1,8 @@ -<<<<<<< HEAD - -import * as anchor from '@project-serum/anchor'; -import { - QUOTE_PRECISION, - MARK_PRICE_PRECISION, - PEG_PRECISION, - convertToNumber, - Wallet, - ClearingHouse, - Admin, - BN, -} from '../sdk/src'; -import { Connection, Keypair, PublicKey } from '@solana/web3.js'; -import { - getFeedData, - initUserAccounts, - mockOracle, - mockUserUSDCAccount, - mockUSDCMint, - setFeedPrice, -} from './../tests/testHelpers'; -import { - stress_test -} from './stress'; -import { - readStressCSV, - simEvent, - writeStressCSV, -} from './stressUtils'; -======= import * as anchor from '@project-serum/anchor'; import { Admin } from '../sdk/src'; import { Keypair } from '@solana/web3.js'; import { mockUSDCMint } from './../tests/testHelpers'; import { stress_test } from './stress'; ->>>>>>> origin/master describe('stress-test', () => { const provider = anchor.Provider.local(); @@ -115,11 +83,7 @@ describe('stress-test', () => { provider, 1, 100, -<<<<<<< HEAD - 10 * 10 ** 6, -======= 10 * 10 ** 6 ->>>>>>> origin/master // 25 * 10 ** 13, // 'stress/configs/clearingHouse.spec.pegmult.csv' ); diff --git a/stress/stress.ts b/stress/stress.ts index 00510dd4..c784235a 100644 --- a/stress/stress.ts +++ b/stress/stress.ts @@ -6,11 +6,7 @@ import { PEG_PRECISION, convertToNumber, calculateMarkPrice, -<<<<<<< HEAD - calculateTargetPriceTrade -======= calculateTargetPriceTrade, ->>>>>>> origin/master } from '../sdk/src'; import { assert } from '../sdk/src/assert/assert'; @@ -53,19 +49,9 @@ export async function stress_test( // todo: should be equal at init, with xeq for scale as oracle px const periodicity = new BN(1); // 1 SECOND const PAIR_AMT = sqrtk; -<<<<<<< HEAD - console.log('sqrtK:', sqrtk) - const ammInitialQuoteAssetAmount = (new BN(PAIR_AMT)).mul( - MARK_PRICE_PRECISION - ); - const ammInitialBaseAssetAmount = (new BN(PAIR_AMT)).mul( - MARK_PRICE_PRECISION - ); -======= console.log('sqrtK:', sqrtk); const ammInitialQuoteAssetAmount = new BN(PAIR_AMT).mul(MARK_PRICE_PRECISION); const ammInitialBaseAssetAmount = new BN(PAIR_AMT).mul(MARK_PRICE_PRECISION); ->>>>>>> origin/master for (let i = 0; i < oracles.length; i++) { const amtScale = pegs[i].div(PEG_PRECISION); // same slippage pct for regardless of peg levels @@ -150,18 +136,10 @@ export async function stress_test( ).mul(MARK_PRICE_PRECISION.div(PEG_PRECISION)); const markPriceMantissa = calculateMarkPrice(marketData); -<<<<<<< HEAD - [randEType, rand_amt, _entry_px] = - calculateTargetPriceTrade( - marketData, - oraclePriceMantissa - ); -======= [randEType, rand_amt, _entry_px] = calculateTargetPriceTrade( marketData, oraclePriceMantissa ); ->>>>>>> origin/master rand_amt = BN.min( rand_amt.abs(), diff --git a/tests/clearingHouse.ts b/tests/clearingHouse.ts index 744f2507..b980c12c 100644 --- a/tests/clearingHouse.ts +++ b/tests/clearingHouse.ts @@ -947,14 +947,6 @@ describe('clearing_house', () => { state.insuranceVault ); -<<<<<<< HEAD - console.log('user collateral for IF based withdrawal', - convertToNumber(user.collateral, QUOTE_PRECISION), - 'vault balance:', - convertToNumber(chCollateralAccountToken0.amount, QUOTE_PRECISION) - ,'IF balance:', - convertToNumber(chInsuranceAccountToken0.amount, QUOTE_PRECISION) -======= console.log( 'user collateral for IF based withdrawal', convertToNumber(user.collateral, QUOTE_PRECISION), @@ -962,7 +954,6 @@ describe('clearing_house', () => { convertToNumber(chCollateralAccountToken0.amount, QUOTE_PRECISION), 'IF balance:', convertToNumber(chInsuranceAccountToken0.amount, QUOTE_PRECISION) ->>>>>>> origin/master ); await clearingHouse.withdrawCollateral( diff --git a/tests/formulaK.ts b/tests/formulaK.ts index 3c449db4..cd0fbf66 100644 --- a/tests/formulaK.ts +++ b/tests/formulaK.ts @@ -1,6 +1,6 @@ import * as anchor from '@project-serum/anchor'; import { assert } from 'chai'; -import { BN, FUNDING_PAYMENT_PRECISION } from '../sdk'; +import { BN } from '../sdk'; import { Keypair } from '@solana/web3.js'; import { Program } from '@project-serum/anchor'; @@ -20,7 +20,7 @@ import { createPriceFeed, mockUSDCMint, mockUserUSDCAccount, - setFeedPrice, + // setFeedPrice, getFeedData, } from './testHelpers'; import { QUOTE_PRECISION } from '../sdk/lib'; @@ -146,22 +146,22 @@ describe('formulaic curve (k)', () => { const marginOfError = new BN(MARK_PRICE_PRECISION.div(new BN(1000))); // price change less than 3 decimal places - // console.log( - // 'oldSqrtK', - // convertToNumber(ammOld.sqrtK), - // 'oldKPrice:', - // convertToNumber(oldKPrice) - // ); - // console.log( - // 'newSqrtK', - // convertToNumber(newSqrtK), - // 'newKPrice:', - // convertToNumber(newKPrice) - // ); + console.log( + 'oldSqrtK', + convertToNumber(ammOld.sqrtK), + 'oldKPrice:', + convertToNumber(oldKPrice) + ); + console.log( + 'newSqrtK', + convertToNumber(newSqrtK), + 'newKPrice:', + convertToNumber(newKPrice) + ); - // assert(ammOld.sqrtK.eq(amm.sqrtK)); - // assert(newKPrice.sub(oldKPrice).abs().lt(marginOfError)); - // assert(!amm.sqrtK.eq(newSqrtK)); + assert(ammOld.sqrtK.eq(amm.sqrtK)); + assert(newKPrice.sub(oldKPrice).abs().lt(marginOfError)); + assert(!amm.sqrtK.eq(newSqrtK)); console.log( 'realizedFeeOld', From de7ab649e521e1de933ab4b50435405698d116cb Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Tue, 12 Apr 2022 15:56:42 -0400 Subject: [PATCH 45/59] address github comments --- programs/clearing_house/src/controller/amm.rs | 34 +- .../clearing_house/src/controller/funding.rs | 19 +- programs/clearing_house/src/lib.rs | 6 +- programs/clearing_house/src/math/amm.rs | 60 +- programs/clearing_house/src/math/constants.rs | 10 + programs/clearing_house/src/math/repeg.rs | 6 +- sdk/package-lock.json | 2296 ----------------- test-scripts/run-anchor-tests.sh | 3 +- 8 files changed, 79 insertions(+), 2355 deletions(-) delete mode 100644 sdk/package-lock.json diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index 524d98c7..ce9ca87f 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -3,7 +3,7 @@ use solana_program::msg; use crate::controller::repeg::apply_cost_to_market; use crate::error::{ClearingHouseResult, ErrorCode}; use crate::math::amm::calculate_quote_asset_amount_swapped; -use crate::math::casting::{cast, cast_to_i128}; +use crate::math::casting::{cast, cast_to_i128, cast_to_i64}; use crate::math::constants::PRICE_TO_PEG_PRECISION_RATIO; use crate::math::{amm, bn, quote_asset::*, repeg}; use crate::math_error; @@ -97,7 +97,7 @@ pub fn move_price( Ok(()) } -pub fn formulaic_k( +pub fn formulaic_update_k( market: &mut Market, mark_price: u128, oracle_price_data: &OraclePriceData, @@ -113,19 +113,22 @@ pub fn formulaic_k( let quote_asset_reserve_before = market.amm.quote_asset_reserve; let sqrt_k_before = market.amm.sqrt_k; + let funding_imbalance_cost_i64 = cast_to_i64(funding_imbalance_cost)?; + msg!("funding_imbalance_cost: {:?}", funding_imbalance_cost); + // calculate budget - let budget = if funding_imbalance_cost < 0 { + let budget = if funding_imbalance_cost_i64 < 0 { // negative cost is period revenue, give back half in k increase - funding_imbalance_cost + funding_imbalance_cost_i64 .checked_div(2) .ok_or_else(math_error!())? - } else if market.amm.net_revenue_since_last_funding < (funding_imbalance_cost as i64) { + } else if market.amm.net_revenue_since_last_funding < funding_imbalance_cost_i64 { // cost exceeded period revenue, take back half in k decrease max(0, market.amm.net_revenue_since_last_funding) - .checked_sub(funding_imbalance_cost as i64) + .checked_sub(funding_imbalance_cost_i64) .ok_or_else(math_error!())? .checked_div(2) - .ok_or_else(math_error!())? as i128 + .ok_or_else(math_error!())? } else { 0 }; @@ -134,28 +137,25 @@ pub fn formulaic_k( let curve_history = curve_history.unwrap(); // single k scale is capped by .1% increase and .09% decrease (regardless of budget) - let (p_numer, p_denom) = amm::calculate_budgeted_k_scale(market, budget)?; + let (p_numer, p_denom) = amm::calculate_budgeted_k_scale(market, cast_to_i128(budget)?)?; if p_numer > p_denom { msg!("increase sqrt_k (* {:?}/{:?})", p_numer, p_denom); - } else if p_numer < p_denom { + } else if p_numer <= p_denom { msg!("decrease sqrt_k (* {:?}/{:?})", p_numer, p_denom); } - let new_sqrt_k = market - .amm - .sqrt_k - .checked_mul(p_numer) + let new_sqrt_k = bn::U256::from(market.amm.sqrt_k) + .checked_mul(bn::U256::from(p_numer)) .ok_or_else(math_error!())? - .checked_div(p_denom) + .checked_div(bn::U256::from(p_denom)) .ok_or_else(math_error!())?; - let (adjust_k_market, adjustment_cost) = - amm::adjust_k_cost(market, bn::U256::from(new_sqrt_k))?; + let (adjust_k_market, adjustment_cost) = amm::adjust_k_cost(market, new_sqrt_k)?; let cost_applied = apply_cost_to_market(market, adjustment_cost)?; if cost_applied { // todo: do actual k adj here - amm::update_k(market, bn::U256::from(new_sqrt_k))?; + amm::update_k(market, new_sqrt_k)?; let peg_multiplier_after = market.amm.peg_multiplier; let base_asset_reserve_after = market.amm.base_asset_reserve; diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 049b1082..5a5b62eb 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -5,14 +5,16 @@ use anchor_lang::prelude::*; use crate::error::*; -use crate::controller::amm::formulaic_k; +use crate::controller::amm::formulaic_update_k; use crate::math::amm; use crate::math::amm::normalise_oracle_price; use crate::math::casting::{cast, cast_to_i128}; use crate::math::collateral::calculate_updated_collateral; use crate::math::constants::{ - AMM_TO_QUOTE_PRECISION_RATIO_I128, FUNDING_PAYMENT_PRECISION, ONE_HOUR, TWENTYFOUR_HOUR, + AMM_TO_QUOTE_PRECISION_RATIO_I128, + FUNDING_PAYMENT_PRECISION, ONE_HOUR, TWENTYFOUR_HOUR, + QUOTE_TO_BASE_AMT_FUNDING_PRECISION, }; use crate::math::funding::{calculate_funding_payment, calculate_funding_rate_long_short}; use crate::math::oracle; @@ -169,6 +171,8 @@ pub fn update_funding_rate( .checked_sub(oracle_price_twap) .ok_or_else(math_error!())?; + msg!("ps: {:?} - {:?}", mark_price_twap, oracle_price_twap); + // clamp price divergence to 3% for funding rate calculation let max_price_spread = oracle_price_twap .checked_div(33) @@ -185,15 +189,18 @@ pub fn update_funding_rate( calculate_funding_rate_long_short(market, funding_rate)?; // dynamic k + msg!( + "fr: {:?}, bam: {:?}", + funding_rate, + market.base_asset_amount + ); let funding_imbalance_cost = funding_rate .checked_mul(market.base_asset_amount) .ok_or_else(math_error!())? - .checked_div( - AMM_TO_QUOTE_PRECISION_RATIO_I128 * cast_to_i128(FUNDING_PAYMENT_PRECISION)?, - ) + .checked_div(QUOTE_TO_BASE_AMT_FUNDING_PRECISION) .ok_or_else(math_error!())?; - formulaic_k( + formulaic_update_k( market, mark_price, &oracle_price_data, diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 733a6092..2e5c595f 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -2132,12 +2132,12 @@ pub mod clearing_house { let quote_asset_reserve_before = market.amm.quote_asset_reserve; let sqrt_k_before = market.amm.sqrt_k; - let new_sqrt_k_U256 = bn::U256::from(sqrt_k); + let new_sqrt_k_u256 = bn::U256::from(sqrt_k); let (mut adjust_k_market, adjustment_cost) = - math::amm::adjust_k_cost(market, new_sqrt_k_U256)?; + math::amm::adjust_k_cost(market, new_sqrt_k_u256)?; - math::amm::update_k(market, new_sqrt_k_U256); + math::amm::update_k(market, new_sqrt_k_u256); if adjustment_cost > 0 { let max_cost = market diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 6edc62e1..af3d394f 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -9,7 +9,8 @@ use crate::math::bn; use crate::math::bn::U192; use crate::math::casting::{cast, cast_to_i128, cast_to_u128}; use crate::math::constants::{ - AMM_RESERVE_PRECISION, AMM_TO_QUOTE_PRECISION_RATIO, MARK_PRICE_PRECISION, PEG_PRECISION, + AMM_RESERVE_PRECISION, AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, + K_PCT_LOWER_BOUND, K_PCT_SCALE, K_PCT_UPPER_BOUND, MARK_PRICE_PRECISION, PEG_PRECISION, PRICE_SPREAD_PRECISION, PRICE_SPREAD_PRECISION_U128, PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, }; @@ -463,10 +464,19 @@ pub fn calculate_budgeted_k_scale( let q = cast_to_i128(market.amm.peg_multiplier)?; let d = market.base_asset_amount; - let AMM_RESERVE_PRECISIONi128 = cast_to_i128(AMM_RESERVE_PRECISION)?; - let x_d = cast_to_i128(x)?.checked_add(d).ok_or_else(math_error!())?; + msg!("x: {:?}", x); + msg!("x_d: {:?}", x_d); + msg!("c: {:?}", c); + + let x_times_x_d = U192::from(x) + .checked_mul(U192::from(x_d)) + .ok_or_else(math_error!())? + .checked_div(U192::from(AMM_RESERVE_PRECISION)) + .ok_or_else(math_error!())? + .try_to_u128()?; + let numer1 = cast_to_i128(y)? .checked_mul(d) .ok_or_else(math_error!())? @@ -474,64 +484,60 @@ pub fn calculate_budgeted_k_scale( .ok_or_else(math_error!())? .checked_div(cast_to_i128(AMM_RESERVE_PRECISION * PEG_PRECISION)?) .ok_or_else(math_error!())?; + let numer2 = c .checked_mul(x_d) .ok_or_else(math_error!())? .checked_div(cast_to_i128(QUOTE_PRECISION)?) .ok_or_else(math_error!())?; + let denom1 = c - .checked_mul(cast_to_i128(x)?) + .checked_mul(cast_to_i128(x_times_x_d)?) .ok_or_else(math_error!())? - .checked_mul(x_d) - .ok_or_else(math_error!())? - .checked_div(cast_to_i128(AMM_RESERVE_PRECISION * QUOTE_PRECISION)?) + .checked_div(cast_to_i128(QUOTE_PRECISION)?) .ok_or_else(math_error!())?; + let denom2 = cast_to_i128(y)? .checked_mul(d) .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) + .checked_div(AMM_RESERVE_PRECISION_I128) .ok_or_else(math_error!())? .checked_mul(d) .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) + .checked_div(AMM_RESERVE_PRECISION_I128) .ok_or_else(math_error!())? .checked_mul(q) .ok_or_else(math_error!())? .checked_div(cast_to_i128(PEG_PRECISION)?) .ok_or_else(math_error!())?; + msg!("numers: {:?} {:?}", numer1, numer2); + msg!("denoms: {:?} {:?}", denom1, denom2); + let numerator = d .checked_mul(numer1.checked_add(numer2).ok_or_else(math_error!())?) .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) + .checked_div(AMM_RESERVE_PRECISION_I128) .ok_or_else(math_error!())? + // .checked_div(AMM_RESERVE_PRECISION_I128) + // .ok_or_else(math_error!())? .checked_div(cast_to_i128(AMM_TO_QUOTE_PRECISION_RATIO)?) .ok_or_else(math_error!())?; let denominator = denom1 .checked_add(denom2) .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISIONi128) + // .checked_div(AMM_RESERVE_PRECISION_I128) + // .ok_or_else(math_error!())? + .checked_div(AMM_RESERVE_PRECISION_I128) .ok_or_else(math_error!())?; - // hardcoded scale bounds for a single update (.1% increase and .09% decrease) - let K_PCT_SCALE = 10000; - let lower_bound = 9991; - let upper_bound = 10010; - - assert!(upper_bound > K_PCT_SCALE); - assert!(lower_bound < K_PCT_SCALE); - assert!(upper_bound < (K_PCT_SCALE * 101 / 100)); - assert!(lower_bound > (K_PCT_SCALE * 99 / 100)); + msg!("{:?}/{:?}", numerator, denominator); let numerator_clipped = if numerator > denominator { min( numerator, denominator - .checked_mul(upper_bound) + .checked_mul(K_PCT_UPPER_BOUND) .ok_or_else(math_error!())? .checked_div(K_PCT_SCALE) .ok_or_else(math_error!())?, @@ -540,7 +546,7 @@ pub fn calculate_budgeted_k_scale( max( numerator, denominator - .checked_mul(lower_bound) + .checked_mul(K_PCT_LOWER_BOUND) .ok_or_else(math_error!())? .checked_div(K_PCT_SCALE) .ok_or_else(math_error!())?, @@ -560,7 +566,7 @@ pub fn adjust_k_cost(market: &Market, new_sqrt_k: bn::U256) -> ClearingHouseResu let (current_net_market_value, _) = _calculate_base_asset_value_and_pnl(market_clone.base_asset_amount, 0, &market_clone.amm)?; - update_k(&mut market_clone, new_sqrt_k); + update_k(&mut market_clone, new_sqrt_k)?; let (_new_net_market_value, cost) = _calculate_base_asset_value_and_pnl( market_clone.base_asset_amount, diff --git a/programs/clearing_house/src/math/constants.rs b/programs/clearing_house/src/math/constants.rs index bc19f1a8..9b41b5d5 100644 --- a/programs/clearing_house/src/math/constants.rs +++ b/programs/clearing_house/src/math/constants.rs @@ -26,6 +26,9 @@ pub const MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO: u128 = pub const FUNDING_EXCESS_TO_QUOTE_RATIO: i128 = (MARK_PRICE_PRECISION * AMM_RESERVE_PRECISION / QUOTE_PRECISION) as i128; // expo 11 +pub const AMM_TIMES_PEG_PRECISION: i128 = (AMM_RESERVE_PRECISION * PEG_PRECISION) as i128; // expo 16 +pub const AMM_RESERVE_PRECISION_I128: i128 = (AMM_RESERVE_PRECISION) as i128; + // FEE REBATES pub const SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR: u128 = 1; pub const SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR: u128 = 2; @@ -61,3 +64,10 @@ pub const MAX_LIQUIDATION_SLIPPAGE_U128: u128 = 100; // expo = -2 pub const MAX_MARK_TWAP_DIVERGENCE: u128 = 5_000; // expo = -3 pub const MAXIMUM_MARGIN_RATIO: u32 = MARGIN_PRECISION as u32; pub const MINIMUM_MARGIN_RATIO: u32 = MARGIN_PRECISION as u32 / 50; + +// FORMULAIC REPEG / K +pub const K_PCT_SCALE: i128 = 10000; // expo = -4 + +// hardcoded scale bounds for a single update (.1% increase and .09% decrease) +pub const K_PCT_LOWER_BOUND: i128 = 9991; +pub const K_PCT_UPPER_BOUND: i128 = 10010; diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 80580fbd..e2f6c55a 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -177,14 +177,12 @@ pub fn calculate_peg_from_target_price( Ok(new_peg) } -pub fn calculate_mm_target_price( +pub fn calculate_amm_target_price( amm: &AMM, current_price: u128, oracle_price_data: &OraclePriceData, ) -> ClearingHouseResult { // calculates peg_multiplier that changing to would cost no more than budget - let oracle_delay = oracle_price_data.delay; - let oracle_price_normalised = cast_to_u128(amm::normalise_oracle_price( amm, oracle_price_data, @@ -227,7 +225,7 @@ pub fn calculate_budgeted_peg( current_price: u128, oracle_price_data: &OraclePriceData, ) -> ClearingHouseResult<(u128, i128, Market)> { - let target_price = calculate_mm_target_price(&market.amm, current_price, oracle_price_data)?; + let target_price = calculate_amm_target_price(&market.amm, current_price, oracle_price_data)?; let optimal_peg = calculate_peg_from_target_price( market.amm.quote_asset_reserve, market.amm.base_asset_reserve, diff --git a/sdk/package-lock.json b/sdk/package-lock.json deleted file mode 100644 index 0b65f028..00000000 --- a/sdk/package-lock.json +++ /dev/null @@ -1,2296 +0,0 @@ -{ - "name": "@drift-labs/sdk", - "version": "0.1.21-master.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } - } - }, - "@babel/runtime": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", - "integrity": "sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, - "@ethersproject/bytes": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", - "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", - "requires": { - "@ethersproject/logger": "^5.5.0" - } - }, - "@ethersproject/logger": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", - "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" - }, - "@ethersproject/sha2": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.5.0.tgz", - "integrity": "sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==", - "requires": { - "@ethersproject/bytes": "^5.5.0", - "@ethersproject/logger": "^5.5.0", - "hash.js": "1.1.7" - } - }, - "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@project-serum/anchor": { - "version": "0.19.1-beta.1", - "resolved": "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.19.1-beta.1.tgz", - "integrity": "sha512-TzkgS5a4WUDJotmur9/pQhqeqa5W14h++ppF+oGZVoR0EkcLbW7SKpx/z80XhsHCIhWz9/1hBAkbKElXdKUUiw==", - "requires": { - "@project-serum/borsh": "^0.2.2", - "@solana/web3.js": "^1.17.0", - "base64-js": "^1.5.1", - "bn.js": "^5.1.2", - "bs58": "^4.0.1", - "buffer-layout": "^1.2.2", - "camelcase": "^5.3.1", - "crypto-hash": "^1.3.0", - "eventemitter3": "^4.0.7", - "find": "^0.3.0", - "js-sha256": "^0.9.0", - "pako": "^2.0.3", - "snake-case": "^3.0.4", - "toml": "^3.0.0" - } - }, - "@project-serum/borsh": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@project-serum/borsh/-/borsh-0.2.3.tgz", - "integrity": "sha512-lH9zEYADZE3cxrgiFym8+jbUE3NM/LH+WOKYcUjs65CT10Q64Hv45bcAAa/phwYk4Tpz0uQ1x+ergFaAoGt67Q==", - "requires": { - "bn.js": "^5.1.2", - "buffer-layout": "^1.2.0" - } - }, - "@pythnetwork/client": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@pythnetwork/client/-/client-2.5.3.tgz", - "integrity": "sha512-NBLxPnA6A3tZb/DYUooD4SO63UJ70s9DzzFPGXcQNBR9itcycp7aaV+UA5oUPloD/4UHL9soo2fRuDVur0gmhA==", - "requires": { - "@solana/web3.js": "^1.30.2", - "assert": "^2.0.0", - "buffer": "^6.0.1" - }, - "dependencies": { - "@solana/web3.js": { - "version": "1.34.0", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.34.0.tgz", - "integrity": "sha512-6QvqN2DqEELvuV+5yUQM8P9fRiSG+6SzQ58HjumJqODu14r7eu5HXVWEymvKAvMLGME+0TmAdJHjw9xD5NgUWA==", - "requires": { - "@babel/runtime": "^7.12.5", - "@ethersproject/sha2": "^5.5.0", - "@solana/buffer-layout": "^3.0.0", - "bn.js": "^5.0.0", - "borsh": "^0.4.0", - "bs58": "^4.0.1", - "buffer": "6.0.1", - "cross-fetch": "^3.1.4", - "jayson": "^3.4.4", - "js-sha3": "^0.8.0", - "rpc-websockets": "^7.4.2", - "secp256k1": "^4.0.2", - "superstruct": "^0.14.2", - "tweetnacl": "^1.0.0" - } - } - } - }, - "@solana/buffer-layout": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz", - "integrity": "sha512-MVdgAKKL39tEs0l8je0hKaXLQFb7Rdfb0Xg2LjFZd8Lfdazkg6xiS98uAZrEKvaoF3i4M95ei9RydkGIDMeo3w==", - "requires": { - "buffer": "~6.0.3" - }, - "dependencies": { - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - } - } - }, - "@solana/spl-token": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.1.8.tgz", - "integrity": "sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ==", - "requires": { - "@babel/runtime": "^7.10.5", - "@solana/web3.js": "^1.21.0", - "bn.js": "^5.1.0", - "buffer": "6.0.3", - "buffer-layout": "^1.2.0", - "dotenv": "10.0.0" - }, - "dependencies": { - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - } - } - }, - "@solana/web3.js": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.29.2.tgz", - "integrity": "sha512-gtoHzimv7upsKF2DIO4/vNfIMKN+cxSImBHvsdiMyp9IPqb8sctsHVU/+80xXl0JKXVKeairDv5RvVnesJYrtw==", - "requires": { - "@babel/runtime": "^7.12.5", - "@solana/buffer-layout": "^3.0.0", - "bn.js": "^5.0.0", - "borsh": "^0.4.0", - "bs58": "^4.0.1", - "buffer": "6.0.1", - "cross-fetch": "^3.1.4", - "crypto-hash": "^1.2.2", - "jayson": "^3.4.4", - "js-sha3": "^0.8.0", - "rpc-websockets": "^7.4.2", - "secp256k1": "^4.0.2", - "superstruct": "^0.14.2", - "tweetnacl": "^1.0.0" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.24", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz", - "integrity": "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "@types/lodash": { - "version": "4.14.176", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.176.tgz", - "integrity": "sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ==" - }, - "@types/node": { - "version": "16.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", - "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==" - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "@types/ws": { - "version": "7.4.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", - "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", - "requires": { - "@types/node": "*" - } - }, - "@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/experimental-utils": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", - "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" - } - }, - "@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" - } - }, - "@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" - } - }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", - "requires": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bn.js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", - "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" - }, - "borsh": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.4.0.tgz", - "integrity": "sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g==", - "requires": { - "@types/bn.js": "^4.11.5", - "bn.js": "^5.0.0", - "bs58": "^4.0.0", - "text-encoding-utf-8": "^1.0.2" - }, - "dependencies": { - "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "requires": { - "@types/node": "*" - } - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", - "requires": { - "base-x": "^3.0.2" - } - }, - "buffer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.1.tgz", - "integrity": "sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "buffer-layout": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz", - "integrity": "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==" - }, - "bufferutil": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", - "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", - "optional": true, - "requires": { - "node-gyp-build": "^4.3.0" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "circular-json": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", - "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==" - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "cross-fetch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", - "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", - "requires": { - "node-fetch": "2.6.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-hash": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz", - "integrity": "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==" - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "delay": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", - "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==" - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" - }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - } - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, - "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true - }, - "eslint-plugin-prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", - "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/find/-/find-0.3.0.tgz", - "integrity": "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw==", - "requires": { - "traverse-chain": "~0.1.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "13.12.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", - "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - } - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", - "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0" - } - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "requires": { - "call-bind": "^1.0.2" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isomorphic-ws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", - "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==" - }, - "jayson": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.6.5.tgz", - "integrity": "sha512-wmOjX+eQcnCDyPF4KORomaIj9wj3h0B5VEbeD0+2VHfTfErB+h1zpR7oBkgCZp36AFjp3+a4CLz6U72BYpFHAw==", - "requires": { - "@types/connect": "^3.4.33", - "@types/express-serve-static-core": "^4.17.9", - "@types/lodash": "^4.14.159", - "@types/node": "^12.12.54", - "@types/ws": "^7.4.4", - "JSONStream": "^1.3.5", - "commander": "^2.20.3", - "delay": "^5.0.0", - "es6-promisify": "^5.0.0", - "eyes": "^0.1.8", - "isomorphic-ws": "^4.0.1", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.20", - "uuid": "^3.4.0", - "ws": "^7.4.5" - }, - "dependencies": { - "@types/node": { - "version": "12.20.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.36.tgz", - "integrity": "sha512-+5haRZ9uzI7rYqzDznXgkuacqb6LJhAti8mzZKWxIXn/WEtvB+GHVJ7AuMwcN1HMvXOSJcrvA6PPoYHYOYYebA==" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - } - } - }, - "js-sha256": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", - "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" - }, - "js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "requires": { - "tslib": "^2.0.3" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "requires": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, - "node-gyp-build": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", - "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" - }, - "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" - }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "pako": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz", - "integrity": "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==" - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rpc-websockets": { - "version": "7.4.16", - "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.4.16.tgz", - "integrity": "sha512-0b7OVhutzwRIaYAtJo5tqtaQTWKfwAsKnaThOSOy+VkhVdleNUgb8eZnWSdWITRZZEigV5uPEIDr5KZe4DBrdQ==", - "requires": { - "@babel/runtime": "^7.11.2", - "bufferutil": "^4.0.1", - "circular-json": "^0.5.9", - "eventemitter3": "^4.0.7", - "utf-8-validate": "^5.0.2", - "uuid": "^8.3.0", - "ws": "^7.4.5" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - } - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "secp256k1": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", - "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", - "requires": { - "elliptic": "^6.5.2", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "strict-event-emitter-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", - "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "superstruct": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz", - "integrity": "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", - "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "text-encoding-utf-8": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", - "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toml": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", - "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" - }, - "traverse-chain": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz", - "integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE=" - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "utf-8-validate": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", - "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", - "optional": true, - "requires": { - "node-gyp-build": "^4.3.0" - } - }, - "util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", - "requires": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" - } - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", - "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.7" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } -} diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index cc220c52..d98ee534 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -4,8 +4,7 @@ if [ "$1" != "--skip-build" ] cp target/idl/clearing_house.json sdk/src/idl/ fi -test_files=(formulaPeg.ts order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts makerOrder.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts expireOrders.ts oracleOffsetOrders.ts cancelAllOrders.ts roundReduceOnlyOrder.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts deleteUser.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) -# test_files=(formulaK.ts) +test_files=(formulaPeg.ts order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts makerOrder.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts expireOrders oracleOffsetOrders.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) for test_file in ${test_files[@]}; do export ANCHOR_TEST_FILE=${test_file} && anchor test --skip-build || exit 1; From 08da5f2c0efade194fb43c0b4b045c9757758af3 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Tue, 12 Apr 2022 21:49:44 -0400 Subject: [PATCH 46/59] working formulaic k test cases (todo: rm debugging statements) --- programs/clearing_house/src/controller/amm.rs | 3 +- .../clearing_house/src/controller/funding.rs | 9 +- programs/clearing_house/src/math/amm.rs | 37 +-- sdk/src/math/repeg.ts | 21 +- sdk/src/types.ts | 1 + test-scripts/run-anchor-tests.sh | 4 +- tests/curve.ts | 2 +- tests/formulaK.ts | 244 +++++++++++++++++- 8 files changed, 289 insertions(+), 32 deletions(-) diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index ce9ca87f..02f6ffd6 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -115,13 +115,14 @@ pub fn formulaic_update_k( let funding_imbalance_cost_i64 = cast_to_i64(funding_imbalance_cost)?; msg!("funding_imbalance_cost: {:?}", funding_imbalance_cost); - + // assert!(false); // calculate budget let budget = if funding_imbalance_cost_i64 < 0 { // negative cost is period revenue, give back half in k increase funding_imbalance_cost_i64 .checked_div(2) .ok_or_else(math_error!())? + .abs() } else if market.amm.net_revenue_since_last_funding < funding_imbalance_cost_i64 { // cost exceeded period revenue, take back half in k decrease max(0, market.amm.net_revenue_since_last_funding) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 5a5b62eb..b5f0ec05 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -12,9 +12,8 @@ use crate::math::amm::normalise_oracle_price; use crate::math::casting::{cast, cast_to_i128}; use crate::math::collateral::calculate_updated_collateral; use crate::math::constants::{ - AMM_TO_QUOTE_PRECISION_RATIO_I128, - FUNDING_PAYMENT_PRECISION, ONE_HOUR, TWENTYFOUR_HOUR, - QUOTE_TO_BASE_AMT_FUNDING_PRECISION, + AMM_TO_QUOTE_PRECISION_RATIO_I128, FUNDING_PAYMENT_PRECISION, ONE_HOUR, + QUOTE_TO_BASE_AMT_FUNDING_PRECISION, TWENTYFOUR_HOUR, }; use crate::math::funding::{calculate_funding_payment, calculate_funding_rate_long_short}; use crate::math::oracle; @@ -194,7 +193,9 @@ pub fn update_funding_rate( funding_rate, market.base_asset_amount ); - let funding_imbalance_cost = funding_rate + + // negative since surplus to net user is cost to amm + let funding_imbalance_cost = -funding_rate .checked_mul(market.base_asset_amount) .ok_or_else(math_error!())? .checked_div(QUOTE_TO_BASE_AMT_FUNDING_PRECISION) diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index af3d394f..370a4941 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -10,9 +10,9 @@ use crate::math::bn::U192; use crate::math::casting::{cast, cast_to_i128, cast_to_u128}; use crate::math::constants::{ AMM_RESERVE_PRECISION, AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, - K_PCT_LOWER_BOUND, K_PCT_SCALE, K_PCT_UPPER_BOUND, MARK_PRICE_PRECISION, PEG_PRECISION, - PRICE_SPREAD_PRECISION, PRICE_SPREAD_PRECISION_U128, PRICE_TO_PEG_PRECISION_RATIO, - QUOTE_PRECISION, + AMM_TO_QUOTE_PRECISION_RATIO_I128, K_PCT_LOWER_BOUND, K_PCT_SCALE, K_PCT_UPPER_BOUND, + MARK_PRICE_PRECISION, PEG_PRECISION, PRICE_SPREAD_PRECISION, PRICE_SPREAD_PRECISION_U128, + PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, }; use crate::math::position::_calculate_base_asset_value_and_pnl; use crate::math::quote_asset::{asset_to_reserve_amount, reserve_to_asset_amount}; @@ -460,7 +460,7 @@ pub fn calculate_budgeted_k_scale( ) -> ClearingHouseResult<(u128, u128)> { let y = market.amm.quote_asset_reserve; let x = market.amm.base_asset_reserve; - let c = budget; + let c = -budget; let q = cast_to_i128(market.amm.peg_multiplier)?; let d = market.base_asset_amount; @@ -477,12 +477,17 @@ pub fn calculate_budgeted_k_scale( .ok_or_else(math_error!())? .try_to_u128()?; - let numer1 = cast_to_i128(y)? - .checked_mul(d) + let pegged_quote_times_d = U192::from(y) + .checked_mul(U192::from(q)) .ok_or_else(math_error!())? - .checked_mul(q) + .checked_div(U192::from(PEG_PRECISION)) + .ok_or_else(math_error!())? + .try_to_u128()?; + + let numer1 = cast_to_i128(pegged_quote_times_d)? + .checked_mul(d) .ok_or_else(math_error!())? - .checked_div(cast_to_i128(AMM_RESERVE_PRECISION * PEG_PRECISION)?) + .checked_div(AMM_RESERVE_PRECISION_I128) .ok_or_else(math_error!())?; let numer2 = c @@ -515,24 +520,26 @@ pub fn calculate_budgeted_k_scale( msg!("denoms: {:?} {:?}", denom1, denom2); let numerator = d - .checked_mul(numer1.checked_add(numer2).ok_or_else(math_error!())?) + .checked_mul(numer1.checked_sub(numer2).ok_or_else(math_error!())?) .ok_or_else(math_error!())? .checked_div(AMM_RESERVE_PRECISION_I128) .ok_or_else(math_error!())? - // .checked_div(AMM_RESERVE_PRECISION_I128) - // .ok_or_else(math_error!())? - .checked_div(cast_to_i128(AMM_TO_QUOTE_PRECISION_RATIO)?) + .checked_div(AMM_TO_QUOTE_PRECISION_RATIO_I128) .ok_or_else(math_error!())?; let denominator = denom1 .checked_add(denom2) .ok_or_else(math_error!())? - // .checked_div(AMM_RESERVE_PRECISION_I128) - // .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISION_I128) + .checked_div(AMM_TO_QUOTE_PRECISION_RATIO_I128) .ok_or_else(math_error!())?; msg!("{:?}/{:?}", numerator, denominator); + assert!((numerator > 0 && denominator > 0)); + + if budget < 0 && numerator > denominator { + assert!(false); + } + let numerator_clipped = if numerator > denominator { min( numerator, diff --git a/sdk/src/math/repeg.ts b/sdk/src/math/repeg.ts index 104261de..9f3c6b6d 100644 --- a/sdk/src/math/repeg.ts +++ b/sdk/src/math/repeg.ts @@ -185,6 +185,14 @@ export function calculateBudgetedK(market: Market, cost: BN): [BN, BN] { const C = cost.mul(new BN(-1)); + console.log( + convertToNumber(x, AMM_RESERVE_PRECISION), + convertToNumber(y, AMM_RESERVE_PRECISION), + convertToNumber(d, AMM_RESERVE_PRECISION), + Q.toNumber() / 1e3, + C.toNumber() / 1e6 + ); + const numer1 = y.mul(d).mul(Q).div(AMM_RESERVE_PRECISION).div(PEG_PRECISION); const numer2 = C.mul(x.add(d)).div(QUOTE_PRECISION); const denom1 = C.mul(x) @@ -199,8 +207,11 @@ export function calculateBudgetedK(market: Market, cost: BN): [BN, BN] { .div(AMM_RESERVE_PRECISION) .div(PEG_PRECISION); + console.log('numers:', convertToNumber(numer1), convertToNumber(numer2)); + console.log('denoms:', convertToNumber(denom1), convertToNumber(denom2)); + const numerator = d - .mul(numer1.add(numer2)) + .mul(numer1.sub(numer2)) .div(AMM_RESERVE_PRECISION) .div(AMM_RESERVE_PRECISION) .div(AMM_TO_QUOTE_PRECISION_RATIO); @@ -211,8 +222,12 @@ export function calculateBudgetedK(market: Market, cost: BN): [BN, BN] { console.log(numerator, denominator); // const p = (numerator).div(denominator); - // const formulaCost = (numer21.sub(numer20).sub(numer1)).mul(market.amm.pegMultiplier).div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO) - // console.log(convertToNumber(formulaCost, QUOTE_PRECISION)) + // const formulaCost = numer21 + // .sub(numer20) + // .sub(numer1) + // .mul(market.amm.pegMultiplier) + // .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO); + // console.log(convertToNumber(formulaCost, QUOTE_PRECISION)); return [numerator, denominator]; } diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 6886e606..64e1a293 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -122,6 +122,7 @@ export type DepositRecord = { export type ExtendedCurveRecord = { ts: BN; + adjustmentCost: BN; recordId: BN; marketIndex: BN; pegMultiplierBefore: BN; diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index d98ee534..4777e3f0 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -4,8 +4,8 @@ if [ "$1" != "--skip-build" ] cp target/idl/clearing_house.json sdk/src/idl/ fi -test_files=(formulaPeg.ts order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts makerOrder.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts expireOrders oracleOffsetOrders.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) - +test_files=(formulaK.ts formulaPeg.ts order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts makerOrder.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts expireOrders oracleOffsetOrders.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) +test_files=(formulaK.ts) for test_file in ${test_files[@]}; do export ANCHOR_TEST_FILE=${test_file} && anchor test --skip-build || exit 1; done \ No newline at end of file diff --git a/tests/curve.ts b/tests/curve.ts index 34cc17a9..d536d688 100644 --- a/tests/curve.ts +++ b/tests/curve.ts @@ -61,7 +61,7 @@ describe('AMM Curve', () => { userUSDCAccount = await mockUserUSDCAccount(usdcMint, usdcAmount, provider); await clearingHouse.initialize(usdcMint.publicKey, true); - await clearingHouse.subscribe(); + await clearingHouse.subscribeToAll(); solUsdOracle = await createPriceFeed({ oracleProgram: anchor.workspace.Pyth, diff --git a/tests/formulaK.ts b/tests/formulaK.ts index cd0fbf66..9f937d41 100644 --- a/tests/formulaK.ts +++ b/tests/formulaK.ts @@ -1,6 +1,6 @@ import * as anchor from '@project-serum/anchor'; import { assert } from 'chai'; -import { BN } from '../sdk'; +import { BN, calculateBudgetedK } from '../sdk'; import { Keypair } from '@solana/web3.js'; import { Program } from '@project-serum/anchor'; @@ -12,6 +12,7 @@ import { PEG_PRECISION, PositionDirection, convertToNumber, + calculateAdjustKCost, } from '../sdk/src'; import { Markets } from '../sdk/src/constants/markets'; @@ -20,7 +21,7 @@ import { createPriceFeed, mockUSDCMint, mockUserUSDCAccount, - // setFeedPrice, + setFeedPrice, getFeedData, } from './testHelpers'; import { QUOTE_PRECISION } from '../sdk/lib'; @@ -62,7 +63,7 @@ describe('formulaic curve (k)', () => { chProgram.programId ); await clearingHouse.initialize(usdcMint.publicKey, true); - await clearingHouse.subscribe(); + await clearingHouse.subscribeToAll(); const periodicity = new BN(0); // 1 HOUR @@ -152,16 +153,17 @@ describe('formulaic curve (k)', () => { 'oldKPrice:', convertToNumber(oldKPrice) ); + console.log( 'newSqrtK', - convertToNumber(newSqrtK), + convertToNumber(amm.sqrtK), 'newKPrice:', convertToNumber(newKPrice) ); assert(ammOld.sqrtK.eq(amm.sqrtK)); assert(newKPrice.sub(oldKPrice).abs().lt(marginOfError)); - assert(!amm.sqrtK.eq(newSqrtK)); + // assert(!amm.sqrtK.eq(newSqrtK)); console.log( 'realizedFeeOld', @@ -180,8 +182,12 @@ describe('formulaic curve (k)', () => { convertToNumber(userAccount.getTotalCollateral(), QUOTE_PRECISION) ); }); - it('update funding (netRevenueSinceLastFunding)', async () => { + it('update funding (no k change)', async () => { const marketIndex = Markets[0].marketIndex; + const marketsOld = await clearingHouse.getMarketsAccount(); + const marketOld = marketsOld.markets[0]; + const ammOld = marketOld.amm; + await clearingHouse.updateFundingPaused(false); await new Promise((r) => setTimeout(r, 1000)); // wait 1 second @@ -195,6 +201,13 @@ describe('formulaic curve (k)', () => { const market = markets.markets[0]; const amm = market.amm; + console.log( + 'oldSqrtK', + convertToNumber(ammOld.sqrtK), + 'newSqrtK', + convertToNumber(amm.sqrtK) + ); + // await setFeedPrice(program, newPrice, priceFeedAddress); const oraclePx = await getFeedData(anchor.workspace.Pyth, amm.oracle); @@ -221,6 +234,225 @@ describe('formulaic curve (k)', () => { convertToNumber(amm.netRevenueSinceLastFunding, QUOTE_PRECISION) ); + assert(amm.netRevenueSinceLastFunding.eq(ZERO)); + assert(amm.sqrtK); + }); + + it('update funding (k increase by max .01%)', async () => { + const marketIndex = Markets[0].marketIndex; + const marketsOld = await clearingHouse.getMarketsAccount(); + const marketOld = marketsOld.markets[0]; + const ammOld = marketsOld.markets[0].amm; + assert(marketOld.baseAssetAmount.eq(ZERO)); + + console.log('taking position'); + await clearingHouse.openPosition( + PositionDirection.LONG, + new BN(10000).mul(QUOTE_PRECISION), + marketIndex + ); + console.log('$10000 position taken'); + const marketsAfterPos = await clearingHouse.getMarketsAccount(); + const marketAfterPos = marketsAfterPos.markets[0]; + // const ammAfterPos = marketAfterPos.amm; + const maxAdjCost = calculateAdjustKCost( + marketAfterPos, + marketIndex, + new BN(10010), + new BN(10000) + ); + const [pNumer, pDenom] = calculateBudgetedK(marketAfterPos, maxAdjCost); + + console.log( + 'max increase k cost:', + convertToNumber(maxAdjCost, QUOTE_PRECISION), + 'budget k back out scale: multiply by', + convertToNumber(pNumer) / convertToNumber(pDenom) + ); + + await clearingHouse.updateFundingPaused(false); + await new Promise((r) => setTimeout(r, 1000)); // wait 1 second + + const _tx = await clearingHouse.updateFundingRate( + solUsdOracle, + marketIndex + ); + await new Promise((r) => setTimeout(r, 1000)); // wait 1 second + await clearingHouse.updateFundingPaused(true); + + const markets = await clearingHouse.getMarketsAccount(); + const market = markets.markets[0]; + const amm = market.amm; + + // await setFeedPrice(program, newPrice, priceFeedAddress); + const oraclePx = await getFeedData(anchor.workspace.Pyth, amm.oracle); + + console.log( + 'markPrice:', + convertToNumber(calculateMarkPrice(market)), + 'oraclePrice:', + oraclePx.price + ); + + console.log( + 'USER getTotalCollateral', + convertToNumber(userAccount.getTotalCollateral(), QUOTE_PRECISION), + 'fundingPnL:', + convertToNumber(userAccount.getUnrealizedFundingPNL(), QUOTE_PRECISION) + ); + console.log( + 'fundingRate:', + convertToNumber(amm.lastFundingRate, MARK_PRICE_PRECISION) + ); + console.log( + 'realizedFeePostClose', + convertToNumber(amm.totalFeeMinusDistributions, QUOTE_PRECISION), + 'netRevenue', + convertToNumber(amm.netRevenueSinceLastFunding, QUOTE_PRECISION) + ); + + console.log( + 'oldSqrtK', + convertToNumber(ammOld.sqrtK), + 'newSqrtK', + convertToNumber(amm.sqrtK) + ); + + // traders over traded => increase of k + assert(amm.sqrtK.gt(ammOld.sqrtK)); + + const curveHistoryAccount = clearingHouse.getCurveHistoryAccount(); + const curveHistoryHead = curveHistoryAccount.head.toNumber(); + assert.ok(curveHistoryHead === 1); + const cRecord = curveHistoryAccount.curveRecords[curveHistoryHead - 1]; + + console.log( + 'curve cost:', + convertToNumber(cRecord.adjustmentCost, QUOTE_PRECISION) + ); + + assert(amm.netRevenueSinceLastFunding.eq(ZERO)); + }); + it('update funding (k decrease by max .009%)', async () => { + const marketIndex = Markets[0].marketIndex; + const marketsOld = await clearingHouse.getMarketsAccount(); + const marketOld = marketsOld.markets[marketIndex.toNumber()]; + const ammOld = marketOld.amm; + await setFeedPrice( + anchor.workspace.Pyth, + initialSOLPrice * 1.05, + solUsdOracle + ); + + // await setFeedPrice(program, newPrice, priceFeedAddress); + const oraclePxOld = await getFeedData(anchor.workspace.Pyth, ammOld.oracle); + + console.log( + 'markPrice:', + convertToNumber(calculateMarkPrice(marketOld)), + 'oraclePrice:', + oraclePxOld.price + ); + + const maxAdjCost = calculateAdjustKCost( + marketsOld.markets[marketIndex.toNumber()], + marketIndex, + new BN(9991), + new BN(10000) + ); + + const maxAdjCostShrink100x = calculateAdjustKCost( + marketsOld.markets[marketIndex.toNumber()], + marketIndex, + new BN(1), + new BN(100) + ); + + const [pNumer, pDenom] = calculateBudgetedK(marketOld, maxAdjCost); + + const [pNumer2, pDenom2] = calculateBudgetedK(marketOld, new BN(-112934)); // ~$.11 + + console.log( + 'max decrease k cost:', + convertToNumber(maxAdjCost, QUOTE_PRECISION), + 'budget k back out scale: multiply by', + convertToNumber(pNumer) / convertToNumber(pDenom), + '\n', + '1/100th k cost:', + convertToNumber(maxAdjCostShrink100x, QUOTE_PRECISION), + 'budget k $-13:', + convertToNumber(pNumer2) / convertToNumber(pDenom2) + ); + + // console.log('taking position'); + // await clearingHouse.openPosition( + // PositionDirection.LONG, + // new BN(10000).mul(QUOTE_PRECISION), + // marketIndex + // ); + // console.log('$10000 position taken'); + + await clearingHouse.updateFundingPaused(false); + await new Promise((r) => setTimeout(r, 1000)); // wait 1 secon + + const _tx = await clearingHouse.updateFundingRate( + solUsdOracle, + marketIndex + ); + await new Promise((r) => setTimeout(r, 1000)); // wait 1 second + await clearingHouse.updateFundingPaused(true); + + const markets = await clearingHouse.getMarketsAccount(); + const market = markets.markets[0]; + const amm = market.amm; + + // await setFeedPrice(program, newPrice, priceFeedAddress); + const oraclePx = await getFeedData(anchor.workspace.Pyth, amm.oracle); + + console.log( + 'markPrice:', + convertToNumber(calculateMarkPrice(market)), + 'oraclePrice:', + oraclePx.price + ); + + console.log( + 'USER getTotalCollateral', + convertToNumber(userAccount.getTotalCollateral(), QUOTE_PRECISION), + 'fundingPnL:', + convertToNumber(userAccount.getUnrealizedFundingPNL(), QUOTE_PRECISION) + ); + console.log( + 'fundingRate:', + convertToNumber(amm.lastFundingRate, MARK_PRICE_PRECISION) + ); + console.log( + 'realizedFeePostClose', + convertToNumber(amm.totalFeeMinusDistributions, QUOTE_PRECISION), + 'netRevenue', + convertToNumber(amm.netRevenueSinceLastFunding, QUOTE_PRECISION) + ); + + console.log( + 'oldSqrtK', + convertToNumber(ammOld.sqrtK), + 'newSqrtK', + convertToNumber(amm.sqrtK) + ); + + // traders over traded => increase of k + assert(amm.sqrtK.lt(ammOld.sqrtK)); + + const curveHistoryAccount = clearingHouse.getCurveHistoryAccount(); + const curveHistoryHead = curveHistoryAccount.head.toNumber(); + assert.ok(curveHistoryHead === 2); + const cRecord = curveHistoryAccount.curveRecords[curveHistoryHead - 1]; + + console.log( + 'curve cost:', + convertToNumber(cRecord.adjustmentCost, QUOTE_PRECISION) + ); + assert(amm.netRevenueSinceLastFunding.eq(ZERO)); }); }); From d52e6c6627e93df2728596b8062b9c658dc5c217 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 18 Apr 2022 11:18:57 -0500 Subject: [PATCH 47/59] optimize formulaic repeg --- .../clearing_house/src/controller/orders.rs | 1 - .../clearing_house/src/controller/repeg.rs | 40 ++++++-------- programs/clearing_house/src/math/constants.rs | 3 +- programs/clearing_house/src/math/repeg.rs | 53 ++++++++----------- test-scripts/run-anchor-tests.sh | 2 +- tests/formulaPeg.ts | 18 ++++++- 6 files changed, 58 insertions(+), 59 deletions(-) diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index c0765450..213ea2bf 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -818,7 +818,6 @@ pub fn fill_order( // if market_index >= 12 { // todo for soft launch - controller::repeg::formulaic_repeg( market, mark_price_after, diff --git a/programs/clearing_house/src/controller/repeg.rs b/programs/clearing_house/src/controller/repeg.rs index 1265a58e..d87a446b 100644 --- a/programs/clearing_house/src/controller/repeg.rs +++ b/programs/clearing_house/src/controller/repeg.rs @@ -31,19 +31,14 @@ pub fn repeg( let (repegged_market, adjustment_cost) = repeg::adjust_peg_cost(market, new_peg_candidate)?; - let ( - oracle_is_valid, - direction_valid, - profitability_valid, - price_impact_valid, - _oracle_terminal_divergence, - ) = repeg::calculate_repeg_validity_from_oracle_account( - &repegged_market, - price_oracle, - terminal_price_before, - clock_slot, - oracle_guard_rails, - )?; + let (oracle_is_valid, direction_valid, profitability_valid, price_impact_valid) = + repeg::calculate_repeg_validity_from_oracle_account( + &repegged_market, + price_oracle, + terminal_price_before, + clock_slot, + oracle_guard_rails, + )?; // cannot repeg if oracle is invalid if !oracle_is_valid { @@ -118,18 +113,13 @@ pub fn formulaic_repeg( oracle_price_data, )?; - let ( - oracle_valid, - _direction_valid, - profitability_valid, - price_impact_valid, - _oracle_terminal_divergence_pct_after, - ) = repeg::calculate_repeg_validity( - &repegged_market, - oracle_price_data, - is_oracle_valid, - terminal_price_before, - )?; + let (oracle_valid, _direction_valid, profitability_valid, price_impact_valid) = + repeg::calculate_repeg_validity( + &repegged_market, + oracle_price_data, + is_oracle_valid, + terminal_price_before, + )?; // any budgeted direction valid for formulaic if oracle_valid && profitability_valid && price_impact_valid { diff --git a/programs/clearing_house/src/math/constants.rs b/programs/clearing_house/src/math/constants.rs index 9b41b5d5..52cd08ab 100644 --- a/programs/clearing_house/src/math/constants.rs +++ b/programs/clearing_house/src/math/constants.rs @@ -1,6 +1,7 @@ // PRECISIONS pub const AMM_RESERVE_PRECISION: u128 = 10_000_000_000_000; //expo = -13; pub const MARK_PRICE_PRECISION: u128 = 10_000_000_000; //expo = -10 +pub const MARK_PRICE_PRECISION_I128: i128 = 10_000_000_000; //expo = -10 pub const QUOTE_PRECISION: u128 = 1_000_000; // expo = -6 pub const FUNDING_PAYMENT_PRECISION: u128 = 10_000; // expo = -4 pub const MARGIN_PRECISION: u128 = 10_000; // expo = -4 @@ -24,7 +25,7 @@ pub const MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO: u128 = MARK_PRICE_PRECISION * AMM_TO_QUOTE_PRECISION_RATIO; // expo 17 pub const FUNDING_EXCESS_TO_QUOTE_RATIO: i128 = - (MARK_PRICE_PRECISION * AMM_RESERVE_PRECISION / QUOTE_PRECISION) as i128; // expo 11 + (MARK_PRICE_PRECISION * AMM_RESERVE_PRECISION / QUOTE_PRECISION) as i128; // expo 17 pub const AMM_TIMES_PEG_PRECISION: i128 = (AMM_RESERVE_PRECISION * PEG_PRECISION) as i128; // expo 16 pub const AMM_RESERVE_PRECISION_I128: i128 = (AMM_RESERVE_PRECISION) as i128; diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index e2f6c55a..f727e3a1 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -4,8 +4,10 @@ use crate::math::amm; use crate::math::bn; use crate::math::casting::{cast_to_i128, cast_to_u128}; use crate::math::constants::{ - AMM_TO_QUOTE_PRECISION_RATIO, FUNDING_EXCESS_TO_QUOTE_RATIO, MARK_PRICE_PRECISION, ONE_HOUR, - PEG_PRECISION, PRICE_SPREAD_PRECISION, PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, + AMM_RESERVE_PRECISION, AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, + AMM_TO_QUOTE_PRECISION_RATIO_I128, FUNDING_EXCESS_TO_QUOTE_RATIO, MARK_PRICE_PRECISION, + MARK_PRICE_PRECISION_I128, ONE_HOUR, PEG_PRECISION, PRICE_SPREAD_PRECISION, + PRICE_SPREAD_PRECISION_U128, PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, TWENTYFOUR_HOUR, }; @@ -24,7 +26,7 @@ pub fn calculate_repeg_validity_from_oracle_account( terminal_price_before: u128, clock_slot: u64, oracle_guard_rails: &OracleGuardRails, -) -> ClearingHouseResult<(bool, bool, bool, bool, i128)> { +) -> ClearingHouseResult<(bool, bool, bool, bool)> { let oracle_price_data = market .amm .get_oracle_price(oracle_account_info, clock_slot)?; @@ -34,25 +36,19 @@ pub fn calculate_repeg_validity_from_oracle_account( &oracle_guard_rails.validity, )?; - let ( - oracle_is_valid, - direction_valid, - profitability_valid, - price_impact_valid, - oracle_terminal_divergence_pct_after, - ) = calculate_repeg_validity( - market, - &oracle_price_data, - oracle_is_valid, - terminal_price_before, - )?; + let (oracle_is_valid, direction_valid, profitability_valid, price_impact_valid) = + calculate_repeg_validity( + market, + &oracle_price_data, + oracle_is_valid, + terminal_price_before, + )?; Ok(( oracle_is_valid, direction_valid, profitability_valid, price_impact_valid, - oracle_terminal_divergence_pct_after, )) } @@ -61,7 +57,7 @@ pub fn calculate_repeg_validity( oracle_price_data: &OraclePriceData, oracle_is_valid: bool, terminal_price_before: u128, -) -> ClearingHouseResult<(bool, bool, bool, bool, i128)> { +) -> ClearingHouseResult<(bool, bool, bool, bool)> { let OraclePriceData { price: oracle_price, confidence: oracle_conf, @@ -73,14 +69,6 @@ pub fn calculate_repeg_validity( let (terminal_price_after, _terminal_quote_reserves, _terminal_base_reserves) = amm::calculate_terminal_price_and_reserves(market)?; - let oracle_terminal_spread_after = oracle_price - .checked_sub(cast_to_i128(terminal_price_after)?) - .ok_or_else(math_error!())?; - let oracle_terminal_divergence_pct_after = oracle_terminal_spread_after - .checked_mul(PRICE_SPREAD_PRECISION) - .ok_or_else(math_error!())? - .checked_div(oracle_price) - .ok_or_else(math_error!())?; let mut direction_valid = true; let mut price_impact_valid = true; @@ -157,7 +145,6 @@ pub fn calculate_repeg_validity( direction_valid, profitability_valid, price_impact_valid, - oracle_terminal_divergence_pct_after, )) } @@ -387,13 +374,19 @@ pub fn calculate_expected_excess_funding_payment( .ok_or_else(math_error!())?, )?; - let expected_excess_funding_payment = market + let base_asset_amount = market .base_asset_amount - .checked_mul(expected_excess_funding) - .ok_or_else(math_error!())? + .checked_div(AMM_TO_QUOTE_PRECISION_RATIO_I128) + .ok_or_else(math_error!())?; + + let adjusted_excess_funding = expected_excess_funding .checked_div(period_adjustment) .ok_or_else(math_error!())? - .checked_div(FUNDING_EXCESS_TO_QUOTE_RATIO) + .checked_div(MARK_PRICE_PRECISION_I128) + .ok_or_else(math_error!())?; + + let expected_excess_funding_payment = base_asset_amount + .checked_mul(adjusted_excess_funding) .ok_or_else(math_error!())?; Ok(expected_excess_funding_payment) diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 4777e3f0..5247788d 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -5,7 +5,7 @@ if [ "$1" != "--skip-build" ] fi test_files=(formulaK.ts formulaPeg.ts order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts makerOrder.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts expireOrders oracleOffsetOrders.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) -test_files=(formulaK.ts) + for test_file in ${test_files[@]}; do export ANCHOR_TEST_FILE=${test_file} && anchor test --skip-build || exit 1; done \ No newline at end of file diff --git a/tests/formulaPeg.ts b/tests/formulaPeg.ts index 84408ea4..c5326fea 100644 --- a/tests/formulaPeg.ts +++ b/tests/formulaPeg.ts @@ -73,6 +73,18 @@ async function formRepegHelper( orderParams // discountTokenAccount.address ); + const computeUnits = await findComputeUnitConsumption( + clearingHouse.program.programId, + connection, + txSig, + 'confirmed' + ); + console.log('compute units', computeUnits); + console.log( + 'tx logs', + (await connection.getTransaction(txSig, { commitment: 'confirmed' })).meta + .logMessages + ); await clearingHouse.fetchAccounts(); await userAccount.fetchAccounts(); @@ -185,7 +197,11 @@ describe('formulaic curve (repeg)', () => { clearingHouse = Admin.from( connection, provider.wallet, - chProgram.programId + chProgram.programId, + { + commitment: 'confirmed', + preflightCommitment: 'confirmed', + } ); await clearingHouse.initialize(usdcMint.publicKey, true); await clearingHouse.subscribe(); From f208be3bc317d3916abeea84edc032964e399639 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 18 Apr 2022 17:46:16 -0500 Subject: [PATCH 48/59] some optimizations for formulaic k, avoid calculating funding_imbalance_cost twice --- programs/clearing_house/src/controller/amm.rs | 2 -- .../clearing_house/src/controller/funding.rs | 24 +++------------ programs/clearing_house/src/math/amm.rs | 30 ++++++++----------- programs/clearing_house/src/math/constants.rs | 5 ++-- programs/clearing_house/src/math/funding.rs | 14 +++++++-- tests/formulaK.ts | 22 ++++++++++++-- 6 files changed, 51 insertions(+), 46 deletions(-) diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index 02f6ffd6..ee73bd32 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -99,9 +99,7 @@ pub fn move_price( pub fn formulaic_update_k( market: &mut Market, - mark_price: u128, oracle_price_data: &OraclePriceData, - is_oracle_valid: bool, funding_imbalance_cost: i128, curve_history: Option<&mut RefMut>, now: i64, diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index b5f0ec05..7e9e6666 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -12,7 +12,8 @@ use crate::math::amm::normalise_oracle_price; use crate::math::casting::{cast, cast_to_i128}; use crate::math::collateral::calculate_updated_collateral; use crate::math::constants::{ - AMM_TO_QUOTE_PRECISION_RATIO_I128, FUNDING_PAYMENT_PRECISION, ONE_HOUR, + AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, AMM_TO_QUOTE_PRECISION_RATIO_I128, + FUNDING_PAYMENT_PRECISION, FUNDING_RATE_PRECISION_I128, ONE_HOUR, QUOTE_TO_BASE_AMT_FUNDING_PRECISION, TWENTYFOUR_HOUR, }; use crate::math::funding::{calculate_funding_payment, calculate_funding_rate_long_short}; @@ -26,6 +27,7 @@ use crate::state::market::{Market, Markets}; use crate::state::state::OracleGuardRails; use crate::state::user::{User, UserPositions}; use solana_program::clock::UnixTimestamp; +use solana_program::log::sol_log_compute_units; use solana_program::msg; /// Funding payments are settled lazily. The amm tracks its cumulative funding rate (for longs and shorts) @@ -170,8 +172,6 @@ pub fn update_funding_rate( .checked_sub(oracle_price_twap) .ok_or_else(math_error!())?; - msg!("ps: {:?} - {:?}", mark_price_twap, oracle_price_twap); - // clamp price divergence to 3% for funding rate calculation let max_price_spread = oracle_price_twap .checked_div(33) @@ -184,28 +184,12 @@ pub fn update_funding_rate( .checked_div(cast(period_adjustment)?) .ok_or_else(math_error!())?; - let (funding_rate_long, funding_rate_short) = + let (funding_rate_long, funding_rate_short, funding_imbalance_cost) = calculate_funding_rate_long_short(market, funding_rate)?; - // dynamic k - msg!( - "fr: {:?}, bam: {:?}", - funding_rate, - market.base_asset_amount - ); - - // negative since surplus to net user is cost to amm - let funding_imbalance_cost = -funding_rate - .checked_mul(market.base_asset_amount) - .ok_or_else(math_error!())? - .checked_div(QUOTE_TO_BASE_AMT_FUNDING_PRECISION) - .ok_or_else(math_error!())?; - formulaic_update_k( market, - mark_price, &oracle_price_data, - true, // only way to have gotten here funding_imbalance_cost, curve_history, now, diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 370a4941..5c9e90b1 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -458,51 +458,47 @@ pub fn calculate_budgeted_k_scale( market: &mut Market, budget: i128, ) -> ClearingHouseResult<(u128, u128)> { - let y = market.amm.quote_asset_reserve; - let x = market.amm.base_asset_reserve; - let c = -budget; - let q = cast_to_i128(market.amm.peg_multiplier)?; - let d = market.base_asset_amount; + let y = market.amm.quote_asset_reserve; // 1e6 + let x = market.amm.base_asset_reserve; //1e6 + let c = -budget; // 1e6 + let q = cast_to_i128(market.amm.peg_multiplier)?; // 1e3 + let d = market.base_asset_amount; // 1e6 let x_d = cast_to_i128(x)?.checked_add(d).ok_or_else(math_error!())?; - msg!("x: {:?}", x); - msg!("x_d: {:?}", x_d); - msg!("c: {:?}", c); - - let x_times_x_d = U192::from(x) + let x_times_x_d = U192::from(x) // 1e13 .checked_mul(U192::from(x_d)) .ok_or_else(math_error!())? .checked_div(U192::from(AMM_RESERVE_PRECISION)) .ok_or_else(math_error!())? .try_to_u128()?; - let pegged_quote_times_d = U192::from(y) + let pegged_quote_times_d = U192::from(y) // 1e13 .checked_mul(U192::from(q)) .ok_or_else(math_error!())? .checked_div(U192::from(PEG_PRECISION)) .ok_or_else(math_error!())? .try_to_u128()?; - let numer1 = cast_to_i128(pegged_quote_times_d)? + let numer1 = cast_to_i128(pegged_quote_times_d)? // 1e13 .checked_mul(d) .ok_or_else(math_error!())? .checked_div(AMM_RESERVE_PRECISION_I128) .ok_or_else(math_error!())?; - let numer2 = c + let numer2 = c // 1e13 .checked_mul(x_d) .ok_or_else(math_error!())? .checked_div(cast_to_i128(QUOTE_PRECISION)?) .ok_or_else(math_error!())?; - let denom1 = c + let denom1 = c // 1e13 .checked_mul(cast_to_i128(x_times_x_d)?) .ok_or_else(math_error!())? .checked_div(cast_to_i128(QUOTE_PRECISION)?) .ok_or_else(math_error!())?; - let denom2 = cast_to_i128(y)? + let denom2 = cast_to_i128(y)? //1e13 .checked_mul(d) .ok_or_else(math_error!())? .checked_div(AMM_RESERVE_PRECISION_I128) @@ -519,14 +515,14 @@ pub fn calculate_budgeted_k_scale( msg!("numers: {:?} {:?}", numer1, numer2); msg!("denoms: {:?} {:?}", denom1, denom2); - let numerator = d + let numerator = d // 1e0 .checked_mul(numer1.checked_sub(numer2).ok_or_else(math_error!())?) .ok_or_else(math_error!())? .checked_div(AMM_RESERVE_PRECISION_I128) .ok_or_else(math_error!())? .checked_div(AMM_TO_QUOTE_PRECISION_RATIO_I128) .ok_or_else(math_error!())?; - let denominator = denom1 + let denominator = denom1 // 1e0 .checked_add(denom2) .ok_or_else(math_error!())? .checked_div(AMM_TO_QUOTE_PRECISION_RATIO_I128) diff --git a/programs/clearing_house/src/math/constants.rs b/programs/clearing_house/src/math/constants.rs index 52cd08ab..32cbc094 100644 --- a/programs/clearing_house/src/math/constants.rs +++ b/programs/clearing_house/src/math/constants.rs @@ -17,9 +17,10 @@ pub const AMM_TO_QUOTE_PRECISION_RATIO_I128: i128 = (AMM_RESERVE_PRECISION / QUOTE_PRECISION) as i128; // expo: 7 pub const AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO: u128 = AMM_RESERVE_PRECISION * PEG_PRECISION / QUOTE_PRECISION; // expo: 10 +pub const FUNDING_RATE_PRECISION: u128 = MARK_PRICE_PRECISION * FUNDING_PAYMENT_PRECISION; // export: 14 +pub const FUNDING_RATE_PRECISION_I128: i128 = FUNDING_RATE_PRECISION as i128; // export: 14 pub const QUOTE_TO_BASE_AMT_FUNDING_PRECISION: i128 = - (AMM_RESERVE_PRECISION * MARK_PRICE_PRECISION * FUNDING_PAYMENT_PRECISION / QUOTE_PRECISION) - as i128; // expo: 21 + (AMM_RESERVE_PRECISION * FUNDING_RATE_PRECISION / QUOTE_PRECISION) as i128; // expo: 21 pub const PRICE_TO_QUOTE_PRECISION_RATIO: u128 = MARK_PRICE_PRECISION / QUOTE_PRECISION; // expo: 4 pub const MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO: u128 = MARK_PRICE_PRECISION * AMM_TO_QUOTE_PRECISION_RATIO; // expo 17 diff --git a/programs/clearing_house/src/math/funding.rs b/programs/clearing_house/src/math/funding.rs index ea09e533..3cc4e654 100644 --- a/programs/clearing_house/src/math/funding.rs +++ b/programs/clearing_house/src/math/funding.rs @@ -18,7 +18,7 @@ use std::cmp::max; pub fn calculate_funding_rate_long_short( market: &mut Market, funding_rate: i128, -) -> ClearingHouseResult<(i128, i128)> { +) -> ClearingHouseResult<(i128, i128, i128)> { // Calculate the funding payment owed by the net_market_position if funding is not capped // If the net market position owes funding payment, the clearing house receives payment let net_market_position = market.base_asset_amount; @@ -38,7 +38,11 @@ pub fn calculate_funding_rate_long_short( .net_revenue_since_last_funding .checked_add(uncapped_funding_pnl as i64) .ok_or_else(math_error!())?; - return Ok((funding_rate, funding_rate)); + return Ok(( + funding_rate, + funding_rate, + net_market_position_funding_payment, + )); } let (capped_funding_rate, capped_funding_pnl) = @@ -79,7 +83,11 @@ pub fn calculate_funding_rate_long_short( funding_rate }; - Ok((funding_rate_long, funding_rate_short)) + Ok(( + funding_rate_long, + funding_rate_short, + net_market_position_funding_payment, + )) } fn calculate_capped_funding_rate( diff --git a/tests/formulaK.ts b/tests/formulaK.ts index 9f937d41..8cd8b4a4 100644 --- a/tests/formulaK.ts +++ b/tests/formulaK.ts @@ -13,6 +13,7 @@ import { PositionDirection, convertToNumber, calculateAdjustKCost, + findComputeUnitConsumption, } from '../sdk/src'; import { Markets } from '../sdk/src/constants/markets'; @@ -60,7 +61,11 @@ describe('formulaic curve (k)', () => { clearingHouse = Admin.from( connection, provider.wallet, - chProgram.programId + chProgram.programId, + { + commitment: 'confirmed', + preflightCommitment: 'confirmed', + } ); await clearingHouse.initialize(usdcMint.publicKey, true); await clearingHouse.subscribeToAll(); @@ -273,10 +278,23 @@ describe('formulaic curve (k)', () => { await clearingHouse.updateFundingPaused(false); await new Promise((r) => setTimeout(r, 1000)); // wait 1 second - const _tx = await clearingHouse.updateFundingRate( + const txSig = await clearingHouse.updateFundingRate( solUsdOracle, marketIndex ); + const computeUnits = await findComputeUnitConsumption( + clearingHouse.program.programId, + connection, + txSig, + 'confirmed' + ); + console.log('compute units', computeUnits); + console.log( + 'tx logs', + (await connection.getTransaction(txSig, { commitment: 'confirmed' })).meta + .logMessages + ); + await new Promise((r) => setTimeout(r, 1000)); // wait 1 second await clearingHouse.updateFundingPaused(true); From cac091a77342226cb29d7b058ff7c28f160c6d2d Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 19 Apr 2022 20:06:10 -0500 Subject: [PATCH 49/59] optimize calculate_budgeted_k_scale --- programs/clearing_house/src/controller/amm.rs | 15 +- .../clearing_house/src/controller/funding.rs | 1 + programs/clearing_house/src/math/amm.rs | 149 ++++++++---------- programs/clearing_house/src/math/constants.rs | 2 + 4 files changed, 77 insertions(+), 90 deletions(-) diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index ee73bd32..80659381 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -11,6 +11,7 @@ use crate::state::history::curve::{ExtendedCurveHistory, ExtendedCurveRecord}; use crate::state::market::{Market, OraclePriceData, AMM}; use std::cell::RefMut; +use solana_program::log::sol_log_compute_units; use std::cmp::{max, min}; #[derive(Clone, Copy, PartialEq)] @@ -105,6 +106,7 @@ pub fn formulaic_update_k( now: i64, market_index: u64, trade_record: Option, + mark_price: u128, ) -> ClearingHouseResult { let peg_multiplier_before = market.amm.peg_multiplier; let base_asset_reserve_before = market.amm.base_asset_reserve; @@ -112,8 +114,7 @@ pub fn formulaic_update_k( let sqrt_k_before = market.amm.sqrt_k; let funding_imbalance_cost_i64 = cast_to_i64(funding_imbalance_cost)?; - msg!("funding_imbalance_cost: {:?}", funding_imbalance_cost); - // assert!(false); + // calculate budget let budget = if funding_imbalance_cost_i64 < 0 { // negative cost is period revenue, give back half in k increase @@ -136,13 +137,8 @@ pub fn formulaic_update_k( let curve_history = curve_history.unwrap(); // single k scale is capped by .1% increase and .09% decrease (regardless of budget) - let (p_numer, p_denom) = amm::calculate_budgeted_k_scale(market, cast_to_i128(budget)?)?; - - if p_numer > p_denom { - msg!("increase sqrt_k (* {:?}/{:?})", p_numer, p_denom); - } else if p_numer <= p_denom { - msg!("decrease sqrt_k (* {:?}/{:?})", p_numer, p_denom); - } + let (p_numer, p_denom) = + amm::calculate_budgeted_k_scale(market, cast_to_i128(budget)?, mark_price)?; let new_sqrt_k = bn::U256::from(market.amm.sqrt_k) .checked_mul(bn::U256::from(p_numer)) @@ -151,6 +147,7 @@ pub fn formulaic_update_k( .ok_or_else(math_error!())?; let (adjust_k_market, adjustment_cost) = amm::adjust_k_cost(market, new_sqrt_k)?; + sol_log_compute_units(); let cost_applied = apply_cost_to_market(market, adjustment_cost)?; if cost_applied { // todo: do actual k adj here diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 7e9e6666..df77a829 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -195,6 +195,7 @@ pub fn update_funding_rate( now, market_index, None, + mark_price, )?; market.amm.cumulative_funding_rate_long = market diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 5c9e90b1..dc75806d 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -11,14 +11,16 @@ use crate::math::casting::{cast, cast_to_i128, cast_to_u128}; use crate::math::constants::{ AMM_RESERVE_PRECISION, AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, AMM_TO_QUOTE_PRECISION_RATIO_I128, K_PCT_LOWER_BOUND, K_PCT_SCALE, K_PCT_UPPER_BOUND, - MARK_PRICE_PRECISION, PEG_PRECISION, PRICE_SPREAD_PRECISION, PRICE_SPREAD_PRECISION_U128, - PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, + MARK_PRICE_PRECISION, MARK_PRICE_PRECISION_I128, + MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO_I128, PEG_PRECISION, PRICE_SPREAD_PRECISION, + PRICE_SPREAD_PRECISION_U128, PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, }; use crate::math::position::_calculate_base_asset_value_and_pnl; use crate::math::quote_asset::{asset_to_reserve_amount, reserve_to_asset_amount}; use crate::math_error; use crate::state::market::{Market, OraclePriceData, AMM}; use crate::state::state::{PriceDivergenceGuardRails, ValidityGuardRails}; +use solana_program::log::sol_log_compute_units; pub fn calculate_price( quote_asset_reserve: u128, @@ -457,78 +459,45 @@ pub fn is_oracle_valid( pub fn calculate_budgeted_k_scale( market: &mut Market, budget: i128, + mark_price: u128, ) -> ClearingHouseResult<(u128, u128)> { - let y = market.amm.quote_asset_reserve; // 1e6 - let x = market.amm.base_asset_reserve; //1e6 - let c = -budget; // 1e6 - let q = cast_to_i128(market.amm.peg_multiplier)?; // 1e3 - let d = market.base_asset_amount; // 1e6 - - let x_d = cast_to_i128(x)?.checked_add(d).ok_or_else(math_error!())?; - - let x_times_x_d = U192::from(x) // 1e13 - .checked_mul(U192::from(x_d)) - .ok_or_else(math_error!())? - .checked_div(U192::from(AMM_RESERVE_PRECISION)) - .ok_or_else(math_error!())? - .try_to_u128()?; - - let pegged_quote_times_d = U192::from(y) // 1e13 - .checked_mul(U192::from(q)) - .ok_or_else(math_error!())? - .checked_div(U192::from(PEG_PRECISION)) - .ok_or_else(math_error!())? - .try_to_u128()?; - - let numer1 = cast_to_i128(pegged_quote_times_d)? // 1e13 - .checked_mul(d) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISION_I128) - .ok_or_else(math_error!())?; - - let numer2 = c // 1e13 - .checked_mul(x_d) - .ok_or_else(math_error!())? - .checked_div(cast_to_i128(QUOTE_PRECISION)?) + let mark_div_budget = cast_to_i128(mark_price)? + .checked_div(budget) .ok_or_else(math_error!())?; - let denom1 = c // 1e13 - .checked_mul(cast_to_i128(x_times_x_d)?) - .ok_or_else(math_error!())? - .checked_div(cast_to_i128(QUOTE_PRECISION)?) + let net_position = market.base_asset_amount; + let one_div_net_position = MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO_I128 + .checked_div(net_position) .ok_or_else(math_error!())?; + let base_asset_reserve = cast_to_i128(market.amm.base_asset_reserve)?; - let denom2 = cast_to_i128(y)? //1e13 - .checked_mul(d) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISION_I128) - .ok_or_else(math_error!())? - .checked_mul(d) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISION_I128) - .ok_or_else(math_error!())? - .checked_mul(q) + let mut numerator = mark_div_budget + .checked_add(one_div_net_position) .ok_or_else(math_error!())? - .checked_div(cast_to_i128(PEG_PRECISION)?) + .checked_add( + MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO_I128 + .checked_div(base_asset_reserve) + .ok_or_else(math_error!())?, + ) .ok_or_else(math_error!())?; - msg!("numers: {:?} {:?}", numer1, numer2); - msg!("denoms: {:?} {:?}", denom1, denom2); - - let numerator = d // 1e0 - .checked_mul(numer1.checked_sub(numer2).ok_or_else(math_error!())?) - .ok_or_else(math_error!())? - .checked_div(AMM_RESERVE_PRECISION_I128) - .ok_or_else(math_error!())? - .checked_div(AMM_TO_QUOTE_PRECISION_RATIO_I128) - .ok_or_else(math_error!())?; - let denominator = denom1 // 1e0 - .checked_add(denom2) + sol_log_compute_units(); + let mut denominator = mark_div_budget + .checked_sub(one_div_net_position) .ok_or_else(math_error!())? - .checked_div(AMM_TO_QUOTE_PRECISION_RATIO_I128) + .checked_sub( + base_asset_reserve + .checked_mul(one_div_net_position) + .ok_or_else(math_error!())? + .checked_div(net_position) + .ok_or_else(math_error!())?, + ) .ok_or_else(math_error!())?; - msg!("{:?}/{:?}", numerator, denominator); + if numerator < 0 && denominator < 0 { + numerator = numerator.abs(); + denominator = denominator.abs(); + } assert!((numerator > 0 && denominator > 0)); @@ -536,27 +505,45 @@ pub fn calculate_budgeted_k_scale( assert!(false); } - let numerator_clipped = if numerator > denominator { - min( - numerator, - denominator - .checked_mul(K_PCT_UPPER_BOUND) - .ok_or_else(math_error!())? - .checked_div(K_PCT_SCALE) - .ok_or_else(math_error!())?, - ) + let (numerator, denominator) = if numerator > denominator { + let current_pct_change = numerator + .checked_mul(100) + .ok_or_else(math_error!())? + .checked_div(denominator) + .ok_or_else(math_error!())?; + + let maximum_pct_change = K_PCT_UPPER_BOUND + .checked_mul(100) + .ok_or_else(math_error!())? + .checked_div(K_PCT_SCALE) + .ok_or_else(math_error!())?; + + if current_pct_change > maximum_pct_change { + (K_PCT_UPPER_BOUND, K_PCT_SCALE) + } else { + (numerator, denominator) + } } else { - max( - numerator, - denominator - .checked_mul(K_PCT_LOWER_BOUND) - .ok_or_else(math_error!())? - .checked_div(K_PCT_SCALE) - .ok_or_else(math_error!())?, - ) + let current_pct_change = numerator + .checked_mul(100) + .ok_or_else(math_error!())? + .checked_div(denominator) + .ok_or_else(math_error!())?; + + let maximum_pct_change = K_PCT_LOWER_BOUND + .checked_mul(100) + .ok_or_else(math_error!())? + .checked_div(K_PCT_SCALE) + .ok_or_else(math_error!())?; + + if current_pct_change < maximum_pct_change { + (K_PCT_LOWER_BOUND, K_PCT_SCALE) + } else { + (numerator, denominator) + } }; - Ok((cast_to_u128(numerator_clipped)?, cast_to_u128(denominator)?)) + Ok((cast_to_u128(numerator)?, cast_to_u128(denominator)?)) } /// To find the cost of adjusting k, compare the the net market value before and after adjusting k diff --git a/programs/clearing_house/src/math/constants.rs b/programs/clearing_house/src/math/constants.rs index 32cbc094..b396bcb5 100644 --- a/programs/clearing_house/src/math/constants.rs +++ b/programs/clearing_house/src/math/constants.rs @@ -24,6 +24,8 @@ pub const QUOTE_TO_BASE_AMT_FUNDING_PRECISION: i128 = pub const PRICE_TO_QUOTE_PRECISION_RATIO: u128 = MARK_PRICE_PRECISION / QUOTE_PRECISION; // expo: 4 pub const MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO: u128 = MARK_PRICE_PRECISION * AMM_TO_QUOTE_PRECISION_RATIO; // expo 17 +pub const MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO_I128: i128 = + MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO as i128; // expo 17 pub const FUNDING_EXCESS_TO_QUOTE_RATIO: i128 = (MARK_PRICE_PRECISION * AMM_RESERVE_PRECISION / QUOTE_PRECISION) as i128; // expo 17 From acd64bd5b23e47113339300961ad5a774592b22c Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 19 Apr 2022 20:08:04 -0500 Subject: [PATCH 50/59] remove extra sol_log_compute_units --- programs/clearing_house/src/controller/amm.rs | 1 - programs/clearing_house/src/math/amm.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index 80659381..cd059f1c 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -147,7 +147,6 @@ pub fn formulaic_update_k( .ok_or_else(math_error!())?; let (adjust_k_market, adjustment_cost) = amm::adjust_k_cost(market, new_sqrt_k)?; - sol_log_compute_units(); let cost_applied = apply_cost_to_market(market, adjustment_cost)?; if cost_applied { // todo: do actual k adj here diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index dc75806d..471867d6 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -481,7 +481,6 @@ pub fn calculate_budgeted_k_scale( ) .ok_or_else(math_error!())?; - sol_log_compute_units(); let mut denominator = mark_div_budget .checked_sub(one_div_net_position) .ok_or_else(math_error!())? From 478f276b1c9f80fdd0e143958a252a58f9ba3c32 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 19 Apr 2022 22:45:49 -0500 Subject: [PATCH 51/59] use U128 for cheaper multiplications --- programs/clearing_house/src/controller/amm.rs | 4 +- programs/clearing_house/src/lib.rs | 3 +- programs/clearing_house/src/math/amm.rs | 20 ++++----- programs/clearing_house/src/math/bn.rs | 45 +++++++++++++++++++ .../clearing_house/src/math/bn_operations.rs | 21 +++++++++ programs/clearing_house/src/math/mod.rs | 1 + 6 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 programs/clearing_house/src/math/bn_operations.rs diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index cd059f1c..679832d9 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -146,8 +146,10 @@ pub fn formulaic_update_k( .checked_div(bn::U256::from(p_denom)) .ok_or_else(math_error!())?; - let (adjust_k_market, adjustment_cost) = amm::adjust_k_cost(market, new_sqrt_k)?; + let adjustment_cost = amm::adjust_k_cost(market, new_sqrt_k)?; + let cost_applied = apply_cost_to_market(market, adjustment_cost)?; + if cost_applied { // todo: do actual k adj here amm::update_k(market, new_sqrt_k)?; diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 2e5c595f..0b5c0d61 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -2134,8 +2134,7 @@ pub mod clearing_house { let new_sqrt_k_u256 = bn::U256::from(sqrt_k); - let (mut adjust_k_market, adjustment_cost) = - math::amm::adjust_k_cost(market, new_sqrt_k_u256)?; + let adjustment_cost = math::amm::adjust_k_cost(market, new_sqrt_k_u256)?; math::amm::update_k(market, new_sqrt_k_u256); diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 471867d6..eca07dd0 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -7,6 +7,7 @@ use crate::controller::position::PositionDirection; use crate::error::*; use crate::math::bn; use crate::math::bn::U192; +use crate::math::bn_operations::{multiply_i128, multiply_u128}; use crate::math::casting::{cast, cast_to_i128, cast_to_u128}; use crate::math::constants::{ AMM_RESERVE_PRECISION, AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, @@ -485,8 +486,7 @@ pub fn calculate_budgeted_k_scale( .checked_sub(one_div_net_position) .ok_or_else(math_error!())? .checked_sub( - base_asset_reserve - .checked_mul(one_div_net_position) + multiply_i128(base_asset_reserve, one_div_net_position) .ok_or_else(math_error!())? .checked_div(net_position) .ok_or_else(math_error!())?, @@ -505,14 +505,12 @@ pub fn calculate_budgeted_k_scale( } let (numerator, denominator) = if numerator > denominator { - let current_pct_change = numerator - .checked_mul(100) + let current_pct_change = multiply_i128(numerator, 1000) .ok_or_else(math_error!())? .checked_div(denominator) .ok_or_else(math_error!())?; - let maximum_pct_change = K_PCT_UPPER_BOUND - .checked_mul(100) + let maximum_pct_change = multiply_i128(K_PCT_UPPER_BOUND, 1000) .ok_or_else(math_error!())? .checked_div(K_PCT_SCALE) .ok_or_else(math_error!())?; @@ -523,14 +521,12 @@ pub fn calculate_budgeted_k_scale( (numerator, denominator) } } else { - let current_pct_change = numerator - .checked_mul(100) + let current_pct_change = multiply_i128(numerator, 1000) .ok_or_else(math_error!())? .checked_div(denominator) .ok_or_else(math_error!())?; - let maximum_pct_change = K_PCT_LOWER_BOUND - .checked_mul(100) + let maximum_pct_change = multiply_i128(K_PCT_LOWER_BOUND, 1000) .ok_or_else(math_error!())? .checked_div(K_PCT_SCALE) .ok_or_else(math_error!())?; @@ -548,7 +544,7 @@ pub fn calculate_budgeted_k_scale( /// To find the cost of adjusting k, compare the the net market value before and after adjusting k /// Increasing k costs the protocol money because it reduces slippage and improves the exit price for net market position /// Decreasing k costs the protocol money because it increases slippage and hurts the exit price for net market position -pub fn adjust_k_cost(market: &Market, new_sqrt_k: bn::U256) -> ClearingHouseResult<(Market, i128)> { +pub fn adjust_k_cost(market: &Market, new_sqrt_k: bn::U256) -> ClearingHouseResult { let mut market_clone = *market; // Find the net market value before adjusting k @@ -562,7 +558,7 @@ pub fn adjust_k_cost(market: &Market, new_sqrt_k: bn::U256) -> ClearingHouseResu current_net_market_value, &market_clone.amm, )?; - Ok((market_clone, cost)) + Ok(cost) } pub fn update_k(market: &mut Market, new_sqrt_k: bn::U256) -> ClearingHouseResult { diff --git a/programs/clearing_house/src/math/bn.rs b/programs/clearing_house/src/math/bn.rs index 14e44366..64417418 100644 --- a/programs/clearing_house/src/math/bn.rs +++ b/programs/clearing_house/src/math/bn.rs @@ -134,3 +134,48 @@ impl U192 { impl_borsh_deserialize_for_bn!(U192); impl_borsh_serialize_for_bn!(U192); + +construct_uint! { + /// 128-bit unsigned integer. + pub struct U128(2); +} + +impl U128 { + /// Convert u192 to u64 + pub fn to_u64(self) -> Option { + self.try_to_u64().map_or_else(|_| None, Some) + } + + /// Convert u192 to u64 + pub fn try_to_u64(self) -> ClearingHouseResult { + self.try_into().map_err(|_| BnConversionError) + } + + /// Convert u192 to u128 + pub fn to_u128(self) -> Option { + self.try_to_u128().map_or_else(|_| None, Some) + } + + /// Convert u192 to u128 + pub fn try_to_u128(self) -> ClearingHouseResult { + self.try_into().map_err(|_| BnConversionError) + } + + /// Convert from little endian bytes + pub fn from_le_bytes(bytes: [u8; 24]) -> Self { + U128::from_little_endian(&bytes) + } + + /// Convert to little endian bytes + pub fn to_le_bytes(self) -> [u8; 24] { + let mut buf: Vec = Vec::with_capacity(size_of::()); + self.to_little_endian(buf.borrow_mut()); + + let mut bytes: [u8; 24] = [0u8; 24]; + bytes.copy_from_slice(buf.as_slice()); + bytes + } +} + +impl_borsh_deserialize_for_bn!(U128); +impl_borsh_serialize_for_bn!(U128); diff --git a/programs/clearing_house/src/math/bn_operations.rs b/programs/clearing_house/src/math/bn_operations.rs new file mode 100644 index 00000000..4d56569b --- /dev/null +++ b/programs/clearing_house/src/math/bn_operations.rs @@ -0,0 +1,21 @@ +use crate::error::ClearingHouseResult; +use crate::math::bn::U128; + +use crate::error::*; +use crate::math::casting::cast_to_i128; +use crate::math_error; +use num_traits::ToPrimitive; +use solana_program::msg; + +pub fn multiply_u128(a: u128, b: u128) -> Option { + U128::from(a).checked_mul(U128::from(b))?.try_to_u128().ok() +} + +pub fn multiply_i128(a: i128, b: i128) -> Option { + U128::from(a.unsigned_abs()) + .checked_mul(U128::from(b.unsigned_abs()))? + .try_to_u128() + .ok()? + .to_i128() + .map(|c| c * a.signum() * b.signum()) +} diff --git a/programs/clearing_house/src/math/mod.rs b/programs/clearing_house/src/math/mod.rs index bd7da2c0..88026d7a 100644 --- a/programs/clearing_house/src/math/mod.rs +++ b/programs/clearing_house/src/math/mod.rs @@ -1,5 +1,6 @@ pub mod amm; pub mod bn; +pub mod bn_operations; pub mod casting; pub mod collateral; pub mod constants; From 466dd7f1d1fb798e49f467ac3d62a1aa2803b2a6 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 20 Apr 2022 12:25:59 -0500 Subject: [PATCH 52/59] only calculate update k result once --- programs/clearing_house/src/controller/amm.rs | 8 ++-- programs/clearing_house/src/lib.rs | 9 ++-- programs/clearing_house/src/math/amm.rs | 45 ++++++++++++++----- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index 679832d9..80b4fac4 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -2,7 +2,7 @@ use solana_program::msg; use crate::controller::repeg::apply_cost_to_market; use crate::error::{ClearingHouseResult, ErrorCode}; -use crate::math::amm::calculate_quote_asset_amount_swapped; +use crate::math::amm::{calculate_quote_asset_amount_swapped, get_update_k_result}; use crate::math::casting::{cast, cast_to_i128, cast_to_i64}; use crate::math::constants::PRICE_TO_PEG_PRECISION_RATIO; use crate::math::{amm, bn, quote_asset::*, repeg}; @@ -146,13 +146,15 @@ pub fn formulaic_update_k( .checked_div(bn::U256::from(p_denom)) .ok_or_else(math_error!())?; - let adjustment_cost = amm::adjust_k_cost(market, new_sqrt_k)?; + let update_k_result = get_update_k_result(market, new_sqrt_k)?; + + let adjustment_cost = amm::adjust_k_cost(market, &update_k_result)?; let cost_applied = apply_cost_to_market(market, adjustment_cost)?; if cost_applied { // todo: do actual k adj here - amm::update_k(market, new_sqrt_k)?; + amm::update_k(market, &update_k_result)?; let peg_multiplier_after = market.amm.peg_multiplier; let base_asset_reserve_after = market.amm.base_asset_reserve; diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 0b5c0d61..6d1ead41 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -46,7 +46,8 @@ pub mod clearing_house { use super::*; use crate::margin_validation::validate_margin; use crate::math::amm::{ - calculate_mark_twap_spread_pct, is_oracle_mark_too_divergent, normalise_oracle_price, + calculate_mark_twap_spread_pct, get_update_k_result, is_oracle_mark_too_divergent, + normalise_oracle_price, }; use crate::math::casting::{cast, cast_to_i128, cast_to_u128}; use crate::math::slippage::{calculate_slippage, calculate_slippage_pct}; @@ -2134,9 +2135,11 @@ pub mod clearing_house { let new_sqrt_k_u256 = bn::U256::from(sqrt_k); - let adjustment_cost = math::amm::adjust_k_cost(market, new_sqrt_k_u256)?; + let update_k_result = get_update_k_result(market, new_sqrt_k_u256)?; - math::amm::update_k(market, new_sqrt_k_u256); + let adjustment_cost = math::amm::adjust_k_cost(market, &update_k_result)?; + + math::amm::update_k(market, &update_k_result); if adjustment_cost > 0 { let max_cost = market diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index eca07dd0..81f1c667 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -544,14 +544,17 @@ pub fn calculate_budgeted_k_scale( /// To find the cost of adjusting k, compare the the net market value before and after adjusting k /// Increasing k costs the protocol money because it reduces slippage and improves the exit price for net market position /// Decreasing k costs the protocol money because it increases slippage and hurts the exit price for net market position -pub fn adjust_k_cost(market: &Market, new_sqrt_k: bn::U256) -> ClearingHouseResult { +pub fn adjust_k_cost( + market: &Market, + update_k_result: &UpdateKResult, +) -> ClearingHouseResult { let mut market_clone = *market; // Find the net market value before adjusting k let (current_net_market_value, _) = _calculate_base_asset_value_and_pnl(market_clone.base_asset_amount, 0, &market_clone.amm)?; - update_k(&mut market_clone, new_sqrt_k)?; + update_k(&mut market_clone, update_k_result)?; let (_new_net_market_value, cost) = _calculate_base_asset_value_and_pnl( market_clone.base_asset_amount, @@ -561,7 +564,16 @@ pub fn adjust_k_cost(market: &Market, new_sqrt_k: bn::U256) -> ClearingHouseResu Ok(cost) } -pub fn update_k(market: &mut Market, new_sqrt_k: bn::U256) -> ClearingHouseResult { +pub struct UpdateKResult { + pub sqrt_k: u128, + pub base_asset_reserve: u128, + pub quote_asset_reserve: u128, +} + +pub fn get_update_k_result( + market: &Market, + new_sqrt_k: bn::U256, +) -> ClearingHouseResult { let mark_price_precision = bn::U256::from(MARK_PRICE_PRECISION); let sqrt_k_ratio = new_sqrt_k @@ -581,26 +593,35 @@ pub fn update_k(market: &mut Market, new_sqrt_k: bn::U256) -> ClearingHouseResul return Err(ErrorCode::InvalidUpdateK); } - market.amm.sqrt_k = new_sqrt_k.try_to_u128().unwrap(); - market.amm.base_asset_reserve = bn::U256::from(market.amm.base_asset_reserve) + let sqrt_k = new_sqrt_k.try_to_u128().unwrap(); + let base_asset_reserve = bn::U256::from(market.amm.base_asset_reserve) .checked_mul(sqrt_k_ratio) .ok_or_else(math_error!())? .checked_div(mark_price_precision) .ok_or_else(math_error!())? - .try_to_u128() - .unwrap(); + .try_to_u128()?; - let invariant_sqrt_u192 = U192::from(market.amm.sqrt_k); + let invariant_sqrt_u192 = U192::from(sqrt_k); let invariant = invariant_sqrt_u192 .checked_mul(invariant_sqrt_u192) .ok_or_else(math_error!())?; - market.amm.quote_asset_reserve = invariant - .checked_div(U192::from(market.amm.base_asset_reserve)) + let quote_asset_reserve = invariant + .checked_div(U192::from(base_asset_reserve)) .ok_or_else(math_error!())? - .try_to_u128() - .unwrap(); + .try_to_u128()?; + + Ok(UpdateKResult { + sqrt_k, + base_asset_reserve, + quote_asset_reserve, + }) +} +pub fn update_k(market: &mut Market, update_k_result: &UpdateKResult) -> ClearingHouseResult { + market.amm.sqrt_k = update_k_result.sqrt_k; + market.amm.base_asset_reserve = update_k_result.base_asset_reserve; + market.amm.quote_asset_reserve = update_k_result.quote_asset_reserve; Ok(()) } From 7b6fd9bc2a7430fad44b256bd162d01d97c84b08 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 20 Apr 2022 12:51:55 -0500 Subject: [PATCH 53/59] use 192 instead of 256 for k maths --- programs/clearing_house/src/controller/amm.rs | 8 +++---- programs/clearing_house/src/lib.rs | 4 ++-- programs/clearing_house/src/math/amm.rs | 21 +++++++------------ 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index 80b4fac4..36d186b1 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -137,13 +137,13 @@ pub fn formulaic_update_k( let curve_history = curve_history.unwrap(); // single k scale is capped by .1% increase and .09% decrease (regardless of budget) - let (p_numer, p_denom) = + let (k_scale_numerator, k_scale_denominator) = amm::calculate_budgeted_k_scale(market, cast_to_i128(budget)?, mark_price)?; - let new_sqrt_k = bn::U256::from(market.amm.sqrt_k) - .checked_mul(bn::U256::from(p_numer)) + let new_sqrt_k = bn::U192::from(market.amm.sqrt_k) + .checked_mul(bn::U192::from(k_scale_numerator)) .ok_or_else(math_error!())? - .checked_div(bn::U256::from(p_denom)) + .checked_div(bn::U192::from(k_scale_denominator)) .ok_or_else(math_error!())?; let update_k_result = get_update_k_result(market, new_sqrt_k)?; diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 6d1ead41..742dda0e 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -2133,9 +2133,9 @@ pub mod clearing_house { let quote_asset_reserve_before = market.amm.quote_asset_reserve; let sqrt_k_before = market.amm.sqrt_k; - let new_sqrt_k_u256 = bn::U256::from(sqrt_k); + let new_sqrt_k_u192 = bn::U192::from(sqrt_k); - let update_k_result = get_update_k_result(market, new_sqrt_k_u256)?; + let update_k_result = get_update_k_result(market, new_sqrt_k_u192)?; let adjustment_cost = math::amm::adjust_k_cost(market, &update_k_result)?; diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 81f1c667..37005f2d 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -572,32 +572,27 @@ pub struct UpdateKResult { pub fn get_update_k_result( market: &Market, - new_sqrt_k: bn::U256, + new_sqrt_k: bn::U192, ) -> ClearingHouseResult { - let mark_price_precision = bn::U256::from(MARK_PRICE_PRECISION); + let sqrt_k_ratio_precision = bn::U192::from(10_000); + let old_sqrt_k = bn::U192::from(market.amm.sqrt_k); let sqrt_k_ratio = new_sqrt_k - .checked_mul(mark_price_precision) + .checked_mul(sqrt_k_ratio_precision) .ok_or_else(math_error!())? - .checked_div(bn::U256::from(market.amm.sqrt_k)) + .checked_div(old_sqrt_k) .ok_or_else(math_error!())?; // if decreasing k, max decrease ratio for single transaction is 2.5% - if sqrt_k_ratio - < mark_price_precision - .checked_mul(bn::U256::from(975)) - .ok_or_else(math_error!())? - .checked_div(bn::U256::from(1000)) - .ok_or_else(math_error!())? - { + if sqrt_k_ratio < U192::from(9750) { return Err(ErrorCode::InvalidUpdateK); } let sqrt_k = new_sqrt_k.try_to_u128().unwrap(); - let base_asset_reserve = bn::U256::from(market.amm.base_asset_reserve) + let base_asset_reserve = bn::U192::from(market.amm.base_asset_reserve) .checked_mul(sqrt_k_ratio) .ok_or_else(math_error!())? - .checked_div(mark_price_precision) + .checked_div(sqrt_k_ratio_precision) .ok_or_else(math_error!())? .try_to_u128()?; From 30831d6f67023441c195672dfd932a43ef57a9b7 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 20 Apr 2022 13:26:20 -0500 Subject: [PATCH 54/59] pass around trade record --- programs/clearing_house/src/controller/funding.rs | 3 ++- programs/clearing_house/src/controller/orders.rs | 1 + programs/clearing_house/src/lib.rs | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index df77a829..5e76e5b4 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -104,6 +104,7 @@ pub fn update_funding_rate( guard_rails: &OracleGuardRails, funding_paused: bool, precomputed_mark_price: Option, + trade_record_id: Option, ) -> ClearingHouseResult { let time_since_last_update = now .checked_sub(market.amm.last_funding_rate_ts) @@ -194,7 +195,7 @@ pub fn update_funding_rate( curve_history, now, market_index, - None, + trade_record_id, mark_price, )?; diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index 213ea2bf..d91dff9a 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -814,6 +814,7 @@ pub fn fill_order( &state.oracle_guard_rails, state.funding_paused, Some(mark_price_before), + Some(trade_record_id), )?; // if market_index >= 12 { diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 742dda0e..d97f35a9 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -770,6 +770,7 @@ pub mod clearing_house { &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, Some(mark_price_before), + Some(record_id), )?; } @@ -965,6 +966,7 @@ pub mod clearing_house { &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, Some(mark_price_before), + Some(record_id), )?; Ok(()) @@ -2099,6 +2101,7 @@ pub mod clearing_house { &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, None, + None, )?; Ok(()) From be98bee78c4048b82dff824d191a4b40a223b78f Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 20 Apr 2022 14:11:06 -0500 Subject: [PATCH 55/59] optimize calculate_oracle_mark_spread_pct --- programs/clearing_house/src/math/amm.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 37005f2d..73ab3db3 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -365,8 +365,7 @@ pub fn calculate_oracle_mark_spread_pct( let (oracle_price, price_spread) = calculate_oracle_mark_spread(amm, oracle_price_data, precomputed_mark_price)?; - price_spread - .checked_mul(PRICE_SPREAD_PRECISION) + multiply_i128(price_spread, PRICE_SPREAD_PRECISION) .ok_or_else(math_error!())? .checked_div(oracle_price) .ok_or_else(math_error!()) From e9e15e0405ca91a16c66c6dbd56c468d1f0d18c0 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 20 Apr 2022 15:00:12 -0500 Subject: [PATCH 56/59] pass mark_price_after to update_funding_rate --- programs/clearing_house/src/controller/funding.rs | 14 +++++++++++--- programs/clearing_house/src/controller/orders.rs | 2 +- programs/clearing_house/src/lib.rs | 4 ++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 5e76e5b4..62c4b8aa 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -160,9 +160,17 @@ pub fn update_funding_rate( } if !funding_paused && !block_funding_rate_update && time_since_last_update >= next_update_wait { - let oracle_price_twap = - amm::update_oracle_price_twap(&mut market.amm, now, normalised_oracle_price)?; - let mark_price_twap = amm::update_mark_twap(&mut market.amm, now, None)?; + let oracle_price_twap = if market.amm.last_oracle_price_twap_ts != now { + amm::update_oracle_price_twap(&mut market.amm, now, normalised_oracle_price)? + } else { + market.amm.last_oracle_price_twap + }; + + let mark_price_twap = if market.amm.last_mark_price_twap_ts != now { + amm::update_mark_twap(&mut market.amm, now, Some(mark_price))? + } else { + market.amm.last_mark_price_twap + }; let period_adjustment = TWENTYFOUR_HOUR .checked_div(max(ONE_HOUR, market.amm.funding_period)) diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index d91dff9a..e18600af 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -813,7 +813,7 @@ pub fn fill_order( Some(extended_curve_history), &state.oracle_guard_rails, state.funding_paused, - Some(mark_price_before), + Some(mark_price_after), Some(trade_record_id), )?; diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index d97f35a9..ddb45086 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -769,7 +769,7 @@ pub mod clearing_house { None, &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, - Some(mark_price_before), + Some(mark_price_after), Some(record_id), )?; } @@ -965,7 +965,7 @@ pub mod clearing_house { None, &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, - Some(mark_price_before), + Some(mark_price_after), Some(record_id), )?; From 8a1d9d919739ea4ac14a7345b80710ddf9794225 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Thu, 21 Apr 2022 16:42:40 -0400 Subject: [PATCH 57/59] add update_intensity, add repeg bounds --- programs/clearing_house/src/lib.rs | 18 ++++ programs/clearing_house/src/math/amm.rs | 25 +++-- programs/clearing_house/src/math/constants.rs | 12 ++- programs/clearing_house/src/math/repeg.rs | 99 +++++++++++++++---- programs/clearing_house/src/state/market.rs | 9 +- sdk/src/admin.ts | 17 ++++ sdk/src/idl/clearing_house.json | 48 ++++++++- tests/formulaK.ts | 5 + tests/formulaPeg.ts | 6 ++ 9 files changed, 204 insertions(+), 35 deletions(-) diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index ddb45086..7e75d056 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -341,8 +341,12 @@ pub mod clearing_house { last_oracle_price: oracle_price, minimum_base_asset_trade_size: 10000000, net_revenue_since_last_funding: 0, + update_intensity: 0, padding2: 0, padding3: 0, + padding4: 0, + padding5: 0, + padding6: 0, }, }; @@ -2290,6 +2294,20 @@ pub mod clearing_house { Ok(()) } + #[access_control( + market_initialized(&ctx.accounts.markets, market_index) + )] + pub fn update_formulaic_update_intensity( + ctx: Context, + market_index: u64, + update_intensity: u8, + ) -> ProgramResult { + let market = + &mut ctx.accounts.markets.load_mut()?.markets[Markets::index_from_u64(market_index)]; + market.amm.update_intensity = update_intensity; + Ok(()) + } + #[access_control( market_initialized(&ctx.accounts.markets, market_index) )] diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 73ab3db3..33e8c498 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -11,7 +11,7 @@ use crate::math::bn_operations::{multiply_i128, multiply_u128}; use crate::math::casting::{cast, cast_to_i128, cast_to_u128}; use crate::math::constants::{ AMM_RESERVE_PRECISION, AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, - AMM_TO_QUOTE_PRECISION_RATIO_I128, K_PCT_LOWER_BOUND, K_PCT_SCALE, K_PCT_UPPER_BOUND, + AMM_TO_QUOTE_PRECISION_RATIO_I128, K_BPS_DECREASE_MAX, K_BPS_INCREASE_MAX, K_BPS_UPDATE_SCALE, MARK_PRICE_PRECISION, MARK_PRICE_PRECISION_I128, MARK_PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO_I128, PEG_PRECISION, PRICE_SPREAD_PRECISION, PRICE_SPREAD_PRECISION_U128, PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, @@ -461,6 +461,13 @@ pub fn calculate_budgeted_k_scale( budget: i128, mark_price: u128, ) -> ClearingHouseResult<(u128, u128)> { + // 0 - 100 + let update_intensity = cast_to_i128(min(market.amm.update_intensity, 100_u8))?; + + if update_intensity == 0 { + return Ok((1, 1)); + } + let mark_div_budget = cast_to_i128(mark_price)? .checked_div(budget) .ok_or_else(math_error!())?; @@ -504,34 +511,38 @@ pub fn calculate_budgeted_k_scale( } let (numerator, denominator) = if numerator > denominator { + let k_pct_upper_bound = K_BPS_UPDATE_SCALE + (K_BPS_INCREASE_MAX * update_intensity / 100); + let current_pct_change = multiply_i128(numerator, 1000) .ok_or_else(math_error!())? .checked_div(denominator) .ok_or_else(math_error!())?; - let maximum_pct_change = multiply_i128(K_PCT_UPPER_BOUND, 1000) + let maximum_pct_change = multiply_i128(k_pct_upper_bound, 1000) .ok_or_else(math_error!())? - .checked_div(K_PCT_SCALE) + .checked_div(K_BPS_UPDATE_SCALE) .ok_or_else(math_error!())?; if current_pct_change > maximum_pct_change { - (K_PCT_UPPER_BOUND, K_PCT_SCALE) + (k_pct_upper_bound, K_BPS_UPDATE_SCALE) } else { (numerator, denominator) } } else { + let k_pct_lower_bound = K_BPS_UPDATE_SCALE - (K_BPS_DECREASE_MAX * update_intensity / 100); + let current_pct_change = multiply_i128(numerator, 1000) .ok_or_else(math_error!())? .checked_div(denominator) .ok_or_else(math_error!())?; - let maximum_pct_change = multiply_i128(K_PCT_LOWER_BOUND, 1000) + let maximum_pct_change = multiply_i128(k_pct_lower_bound, 1000) .ok_or_else(math_error!())? - .checked_div(K_PCT_SCALE) + .checked_div(K_BPS_UPDATE_SCALE) .ok_or_else(math_error!())?; if current_pct_change < maximum_pct_change { - (K_PCT_LOWER_BOUND, K_PCT_SCALE) + (k_pct_lower_bound, K_BPS_UPDATE_SCALE) } else { (numerator, denominator) } diff --git a/programs/clearing_house/src/math/constants.rs b/programs/clearing_house/src/math/constants.rs index b396bcb5..09e07543 100644 --- a/programs/clearing_house/src/math/constants.rs +++ b/programs/clearing_house/src/math/constants.rs @@ -70,8 +70,12 @@ pub const MAXIMUM_MARGIN_RATIO: u32 = MARGIN_PRECISION as u32; pub const MINIMUM_MARGIN_RATIO: u32 = MARGIN_PRECISION as u32 / 50; // FORMULAIC REPEG / K -pub const K_PCT_SCALE: i128 = 10000; // expo = -4 +pub const K_BPS_UPDATE_SCALE: i128 = 1_000_000; // expo = -6 (represents 100%) + // hardcoded scale bounds for a single k update (.1% increase and .09% decrease). scaled by market update_intensity +pub const K_BPS_DECREASE_MAX: i128 = 900; // 9 bps decrease (900/K_BPS_UPDATE_SCALE) +pub const K_BPS_INCREASE_MAX: i128 = 1000; // 10 bps increase -// hardcoded scale bounds for a single update (.1% increase and .09% decrease) -pub const K_PCT_LOWER_BOUND: i128 = 9991; -pub const K_PCT_UPPER_BOUND: i128 = 10010; +pub const PEG_BPS_UPDATE_SCALE: u128 = 1_000_000; // expo = -6 (represents 100%) + // hardcoded scale bounds for a single repeg update. scaled by market update_intensity +pub const PEG_BPS_DECREASE_MAX: u128 = 1000; // 10 bps decrease +pub const PEG_BPS_INCREASE_MAX: u128 = 1000; // 10 bps increase diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index f727e3a1..7d190966 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -2,12 +2,14 @@ use crate::controller::amm::SwapDirection; use crate::error::*; use crate::math::amm; use crate::math::bn; +use crate::math::bn_operations::{multiply_i128, multiply_u128}; use crate::math::casting::{cast_to_i128, cast_to_u128}; use crate::math::constants::{ AMM_RESERVE_PRECISION, AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, AMM_TO_QUOTE_PRECISION_RATIO_I128, FUNDING_EXCESS_TO_QUOTE_RATIO, MARK_PRICE_PRECISION, - MARK_PRICE_PRECISION_I128, ONE_HOUR, PEG_PRECISION, PRICE_SPREAD_PRECISION, - PRICE_SPREAD_PRECISION_U128, PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, + MARK_PRICE_PRECISION_I128, ONE_HOUR, PEG_BPS_DECREASE_MAX, PEG_BPS_INCREASE_MAX, + PEG_BPS_UPDATE_SCALE, PEG_PRECISION, PRICE_SPREAD_PRECISION, PRICE_SPREAD_PRECISION_U128, + PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_DENOMINATOR, SHARE_OF_FEES_ALLOCATED_TO_CLEARING_HOUSE_NUMERATOR, TWENTYFOUR_HOUR, }; @@ -178,29 +180,48 @@ pub fn calculate_amm_target_price( let weight_denom = 100_u128; + let delay_penalty = max( + 0, + oracle_price_data + .delay + .checked_mul(max( + 1, + oracle_price_data + .delay + .checked_div(2) + .ok_or_else(math_error!())?, + )) + .ok_or_else(math_error!())?, + ); + let oracle_price_weight: u128 = cast_to_u128(max( 0, 50_i64 - .checked_sub(max(0, oracle_price_data.delay)) + .checked_sub(delay_penalty) .ok_or_else(math_error!())?, ))?; - let current_price_weight: u128 = weight_denom - .checked_sub(oracle_price_weight) - .ok_or_else(math_error!())?; - let target_price = oracle_price_normalised - .checked_mul(oracle_price_weight) - .ok_or_else(math_error!())? - .checked_div(weight_denom) - .ok_or_else(math_error!())? - .checked_add( - current_price - .checked_mul(current_price_weight) - .ok_or_else(math_error!())? - .checked_div(weight_denom) - .ok_or_else(math_error!())?, - ) - .ok_or_else(math_error!())?; + let target_price = if oracle_price_weight > 0 { + let current_price_weight: u128 = weight_denom + .checked_sub(oracle_price_weight) + .ok_or_else(math_error!())?; + + oracle_price_normalised + .checked_mul(oracle_price_weight) + .ok_or_else(math_error!())? + .checked_div(weight_denom) + .ok_or_else(math_error!())? + .checked_add( + current_price + .checked_mul(current_price_weight) + .ok_or_else(math_error!())? + .checked_div(weight_denom) + .ok_or_else(math_error!())?, + ) + .ok_or_else(math_error!())? + } else { + current_price + }; Ok(target_price) } @@ -219,6 +240,14 @@ pub fn calculate_budgeted_peg( target_price, )?; + // 0-100 + let update_intensity = cast_to_i128(min(market.amm.update_intensity, 100_u8))?; + + // return early + if optimal_peg == market.amm.peg_multiplier || update_intensity == 0 { + return Ok((market.amm.peg_multiplier, 0, *market)); + } + let delta_peg_sign = if market.amm.quote_asset_reserve > terminal_quote_reserves { 1 } else { @@ -289,9 +318,37 @@ pub fn calculate_budgeted_peg( full_budget_peg }; - let (repegged_market, candidate_cost) = adjust_peg_cost(market, candidate_peg)?; + // add bounds to single update + let capped_candidate_peg = if candidate_peg > market.amm.peg_multiplier { + let peg_upper_bound = market + .amm + .peg_multiplier + .checked_add( + multiply_u128(market.amm.peg_multiplier, PEG_BPS_INCREASE_MAX) + .ok_or_else(math_error!())? + .checked_div(PEG_BPS_UPDATE_SCALE) + .ok_or_else(math_error!())?, + ) + .ok_or_else(math_error!())?; + min(candidate_peg, peg_upper_bound) + } else { + let peg_lower_bound = market + .amm + .peg_multiplier + .checked_sub( + multiply_u128(market.amm.peg_multiplier, PEG_BPS_DECREASE_MAX) + .ok_or_else(math_error!())? + .checked_div(PEG_BPS_UPDATE_SCALE) + .ok_or_else(math_error!())?, + ) + .ok_or_else(math_error!())?; + + max(candidate_peg, peg_lower_bound) + }; + + let (repegged_market, candidate_cost) = adjust_peg_cost(market, capped_candidate_peg)?; - Ok((candidate_peg, candidate_cost, repegged_market)) + Ok((capped_candidate_peg, candidate_cost, repegged_market)) } pub fn adjust_peg_cost( diff --git a/programs/clearing_house/src/state/market.rs b/programs/clearing_house/src/state/market.rs index 65a302e3..73d47467 100644 --- a/programs/clearing_house/src/state/market.rs +++ b/programs/clearing_house/src/state/market.rs @@ -100,8 +100,13 @@ pub struct AMM { // upgrade-ability pub net_revenue_since_last_funding: i64, - pub padding2: u128, - pub padding3: u128, + pub update_intensity: u8, + + pub padding2: u8, + pub padding3: u16, + pub padding4: u32, + pub padding5: u64, + pub padding6: u128, } impl AMM { diff --git a/sdk/src/admin.ts b/sdk/src/admin.ts index 2178a508..c1290e2c 100644 --- a/sdk/src/admin.ts +++ b/sdk/src/admin.ts @@ -467,6 +467,23 @@ export class Admin extends ClearingHouse { }); } + public async updateFormulaicUpdateIntensity( + marketIndex: BN, + updateIntensity: number + ): Promise { + return await this.program.rpc.updateFormulaicUpdateIntensity( + marketIndex, + updateIntensity, + { + accounts: { + admin: this.wallet.publicKey, + state: await this.getStatePublicKey(), + markets: this.getStateAccount().markets, + }, + } + ); + } + public async updateMarginRatio( marketIndex: BN, marginRatioInitial: number, diff --git a/sdk/src/idl/clearing_house.json b/sdk/src/idl/clearing_house.json index 60ef605e..2b4e1cbc 100644 --- a/sdk/src/idl/clearing_house.json +++ b/sdk/src/idl/clearing_house.json @@ -1641,6 +1641,36 @@ ], "args": [] }, + { + "name": "updateFormulaicUpdateIntensity", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "markets", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u64" + }, + { + "name": "updateIntensity", + "type": "u8" + } + ] + }, { "name": "updateMarginRatio", "accounts": [ @@ -3300,12 +3330,28 @@ "name": "netRevenueSinceLastFunding", "type": "i64" }, + { + "name": "updateIntensity", + "type": "u8" + }, { "name": "padding2", - "type": "u128" + "type": "u8" }, { "name": "padding3", + "type": "u16" + }, + { + "name": "padding4", + "type": "u32" + }, + { + "name": "padding5", + "type": "u64" + }, + { + "name": "padding6", "type": "u128" } ] diff --git a/tests/formulaK.ts b/tests/formulaK.ts index 8cd8b4a4..89e56e8f 100644 --- a/tests/formulaK.ts +++ b/tests/formulaK.ts @@ -86,6 +86,11 @@ describe('formulaic curve (k)', () => { new BN(initialSOLPrice * PEG_PRECISION.toNumber()) ); + await clearingHouse.updateFormulaicUpdateIntensity( + Markets[0].marketIndex, + 100 + ); + await clearingHouse.initializeUserAccount(); userAccount = ClearingHouseUser.from( clearingHouse, diff --git a/tests/formulaPeg.ts b/tests/formulaPeg.ts index c5326fea..a87b58e7 100644 --- a/tests/formulaPeg.ts +++ b/tests/formulaPeg.ts @@ -226,6 +226,7 @@ describe('formulaic curve (repeg)', () => { periodicity, new BN(initialSOLPrice * PEG_PRECISION.toNumber()) ); + await clearingHouse.updateFormulaicUpdateIntensity(marketIndex, 100); await clearingHouse.initializeMarket( marketIndex.add(new BN(1)), @@ -236,6 +237,11 @@ describe('formulaic curve (repeg)', () => { new BN(110) ); + await clearingHouse.updateFormulaicUpdateIntensity( + marketIndex.add(new BN(1)), + 100 + ); + await clearingHouse.initializeUserAccount(); userAccount = ClearingHouseUser.from( clearingHouse, From 03b3a32d675f7602ae69a243bc08ca8402ba4f71 Mon Sep 17 00:00:00 2001 From: 0xbigz <0xbigz> Date: Fri, 22 Apr 2022 13:37:36 -0400 Subject: [PATCH 58/59] rename update_intensity to curve_update_intensity --- programs/clearing_house/src/lib.rs | 8 ++++---- programs/clearing_house/src/math/amm.rs | 8 ++++---- programs/clearing_house/src/math/constants.rs | 4 ++-- programs/clearing_house/src/math/repeg.rs | 4 ++-- programs/clearing_house/src/state/market.rs | 2 +- sdk/src/admin.ts | 12 ++++++++---- sdk/src/idl/clearing_house.json | 6 +++--- test-scripts/run-anchor-tests.sh | 2 +- tests/formulaK.ts | 5 +---- tests/formulaPeg.ts | 4 ++-- 10 files changed, 28 insertions(+), 27 deletions(-) diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index 7e75d056..e5647665 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -341,7 +341,7 @@ pub mod clearing_house { last_oracle_price: oracle_price, minimum_base_asset_trade_size: 10000000, net_revenue_since_last_funding: 0, - update_intensity: 0, + curve_update_intensity: 0, padding2: 0, padding3: 0, padding4: 0, @@ -2297,14 +2297,14 @@ pub mod clearing_house { #[access_control( market_initialized(&ctx.accounts.markets, market_index) )] - pub fn update_formulaic_update_intensity( + pub fn update_curve_update_intensity( ctx: Context, market_index: u64, - update_intensity: u8, + curve_update_intensity: u8, ) -> ProgramResult { let market = &mut ctx.accounts.markets.load_mut()?.markets[Markets::index_from_u64(market_index)]; - market.amm.update_intensity = update_intensity; + market.amm.curve_update_intensity = curve_update_intensity; Ok(()) } diff --git a/programs/clearing_house/src/math/amm.rs b/programs/clearing_house/src/math/amm.rs index 33e8c498..ff0dd2cf 100644 --- a/programs/clearing_house/src/math/amm.rs +++ b/programs/clearing_house/src/math/amm.rs @@ -462,9 +462,9 @@ pub fn calculate_budgeted_k_scale( mark_price: u128, ) -> ClearingHouseResult<(u128, u128)> { // 0 - 100 - let update_intensity = cast_to_i128(min(market.amm.update_intensity, 100_u8))?; + let curve_update_intensity = cast_to_i128(min(market.amm.curve_update_intensity, 100_u8))?; - if update_intensity == 0 { + if curve_update_intensity == 0 { return Ok((1, 1)); } @@ -511,7 +511,7 @@ pub fn calculate_budgeted_k_scale( } let (numerator, denominator) = if numerator > denominator { - let k_pct_upper_bound = K_BPS_UPDATE_SCALE + (K_BPS_INCREASE_MAX * update_intensity / 100); + let k_pct_upper_bound = K_BPS_UPDATE_SCALE + (K_BPS_INCREASE_MAX * curve_update_intensity / 100); let current_pct_change = multiply_i128(numerator, 1000) .ok_or_else(math_error!())? @@ -529,7 +529,7 @@ pub fn calculate_budgeted_k_scale( (numerator, denominator) } } else { - let k_pct_lower_bound = K_BPS_UPDATE_SCALE - (K_BPS_DECREASE_MAX * update_intensity / 100); + let k_pct_lower_bound = K_BPS_UPDATE_SCALE - (K_BPS_DECREASE_MAX * curve_update_intensity / 100); let current_pct_change = multiply_i128(numerator, 1000) .ok_or_else(math_error!())? diff --git a/programs/clearing_house/src/math/constants.rs b/programs/clearing_house/src/math/constants.rs index 09e07543..422ba2b1 100644 --- a/programs/clearing_house/src/math/constants.rs +++ b/programs/clearing_house/src/math/constants.rs @@ -71,11 +71,11 @@ pub const MINIMUM_MARGIN_RATIO: u32 = MARGIN_PRECISION as u32 / 50; // FORMULAIC REPEG / K pub const K_BPS_UPDATE_SCALE: i128 = 1_000_000; // expo = -6 (represents 100%) - // hardcoded scale bounds for a single k update (.1% increase and .09% decrease). scaled by market update_intensity + // hardcoded scale bounds for a single k update (.1% increase and .09% decrease). scaled by market curve_update_intensity pub const K_BPS_DECREASE_MAX: i128 = 900; // 9 bps decrease (900/K_BPS_UPDATE_SCALE) pub const K_BPS_INCREASE_MAX: i128 = 1000; // 10 bps increase pub const PEG_BPS_UPDATE_SCALE: u128 = 1_000_000; // expo = -6 (represents 100%) - // hardcoded scale bounds for a single repeg update. scaled by market update_intensity + // hardcoded scale bounds for a single repeg update. scaled by market curve_update_intensity pub const PEG_BPS_DECREASE_MAX: u128 = 1000; // 10 bps decrease pub const PEG_BPS_INCREASE_MAX: u128 = 1000; // 10 bps increase diff --git a/programs/clearing_house/src/math/repeg.rs b/programs/clearing_house/src/math/repeg.rs index 7d190966..7d94e60d 100644 --- a/programs/clearing_house/src/math/repeg.rs +++ b/programs/clearing_house/src/math/repeg.rs @@ -241,10 +241,10 @@ pub fn calculate_budgeted_peg( )?; // 0-100 - let update_intensity = cast_to_i128(min(market.amm.update_intensity, 100_u8))?; + let curve_update_intensity = cast_to_i128(min(market.amm.curve_update_intensity, 100_u8))?; // return early - if optimal_peg == market.amm.peg_multiplier || update_intensity == 0 { + if optimal_peg == market.amm.peg_multiplier || curve_update_intensity == 0 { return Ok((market.amm.peg_multiplier, 0, *market)); } diff --git a/programs/clearing_house/src/state/market.rs b/programs/clearing_house/src/state/market.rs index 73d47467..f3e3abd6 100644 --- a/programs/clearing_house/src/state/market.rs +++ b/programs/clearing_house/src/state/market.rs @@ -100,7 +100,7 @@ pub struct AMM { // upgrade-ability pub net_revenue_since_last_funding: i64, - pub update_intensity: u8, + pub curve_update_intensity: u8, pub padding2: u8, pub padding3: u16, diff --git a/sdk/src/admin.ts b/sdk/src/admin.ts index c1290e2c..a6345d33 100644 --- a/sdk/src/admin.ts +++ b/sdk/src/admin.ts @@ -28,6 +28,7 @@ import { getAdmin, getWebSocketClearingHouseConfig, } from './factory/clearingHouse'; +import { assert } from './assert/assert'; export class Admin extends ClearingHouse { public static from( @@ -467,13 +468,16 @@ export class Admin extends ClearingHouse { }); } - public async updateFormulaicUpdateIntensity( + public async updateCurveUpdateIntensity( marketIndex: BN, - updateIntensity: number + curveUpdateIntensity: number ): Promise { - return await this.program.rpc.updateFormulaicUpdateIntensity( + assert(curveUpdateIntensity >= 0 && curveUpdateIntensity <= 100); + assert(Number.isInteger(curveUpdateIntensity)); + + return await this.program.rpc.updateCurveUpdateIntensity( marketIndex, - updateIntensity, + curveUpdateIntensity, { accounts: { admin: this.wallet.publicKey, diff --git a/sdk/src/idl/clearing_house.json b/sdk/src/idl/clearing_house.json index 2b4e1cbc..c4aa9f28 100644 --- a/sdk/src/idl/clearing_house.json +++ b/sdk/src/idl/clearing_house.json @@ -1642,7 +1642,7 @@ "args": [] }, { - "name": "updateFormulaicUpdateIntensity", + "name": "updateCurveUpdateIntensity", "accounts": [ { "name": "admin", @@ -1666,7 +1666,7 @@ "type": "u64" }, { - "name": "updateIntensity", + "name": "curveUpdateIntensity", "type": "u8" } ] @@ -3331,7 +3331,7 @@ "type": "i64" }, { - "name": "updateIntensity", + "name": "curveUpdateIntensity", "type": "u8" }, { diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 5247788d..f72bae5d 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -4,7 +4,7 @@ if [ "$1" != "--skip-build" ] cp target/idl/clearing_house.json sdk/src/idl/ fi -test_files=(formulaK.ts formulaPeg.ts order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts makerOrder.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts expireOrders oracleOffsetOrders.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) +test_files=(formulaK.ts formulaPeg.ts order.ts orderReferrer.ts marketOrder.ts triggerOrders.ts stopLimits.ts userOrderId.ts makerOrder.ts roundInFavorBaseAsset.ts marketOrderBaseAssetAmount.ts expireOrders.ts oracleOffsetOrders.ts clearingHouse.ts pyth.ts userAccount.ts admin.ts updateK.ts adminWithdraw.ts curve.ts whitelist.ts fees.ts idempotentCurve.ts maxDeposit.ts maxPositions.ts maxReserves.ts twapDivergenceLiquidation.ts oraclePnlLiquidation.ts whaleLiquidation.ts roundInFavor.ts minimumTradeSize.ts cappedSymFunding.ts) for test_file in ${test_files[@]}; do export ANCHOR_TEST_FILE=${test_file} && anchor test --skip-build || exit 1; diff --git a/tests/formulaK.ts b/tests/formulaK.ts index 89e56e8f..af2bd30f 100644 --- a/tests/formulaK.ts +++ b/tests/formulaK.ts @@ -86,10 +86,7 @@ describe('formulaic curve (k)', () => { new BN(initialSOLPrice * PEG_PRECISION.toNumber()) ); - await clearingHouse.updateFormulaicUpdateIntensity( - Markets[0].marketIndex, - 100 - ); + await clearingHouse.updateCurveUpdateIntensity(Markets[0].marketIndex, 100); await clearingHouse.initializeUserAccount(); userAccount = ClearingHouseUser.from( diff --git a/tests/formulaPeg.ts b/tests/formulaPeg.ts index a87b58e7..971da4e5 100644 --- a/tests/formulaPeg.ts +++ b/tests/formulaPeg.ts @@ -226,7 +226,7 @@ describe('formulaic curve (repeg)', () => { periodicity, new BN(initialSOLPrice * PEG_PRECISION.toNumber()) ); - await clearingHouse.updateFormulaicUpdateIntensity(marketIndex, 100); + await clearingHouse.updateCurveUpdateIntensity(marketIndex, 100); await clearingHouse.initializeMarket( marketIndex.add(new BN(1)), @@ -237,7 +237,7 @@ describe('formulaic curve (repeg)', () => { new BN(110) ); - await clearingHouse.updateFormulaicUpdateIntensity( + await clearingHouse.updateCurveUpdateIntensity( marketIndex.add(new BN(1)), 100 ); From 441ffbe4d8113a59a6a0adb825fd24783dac359c Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Fri, 29 Apr 2022 10:13:30 -0700 Subject: [PATCH 59/59] add curve history as optional to open and close position --- programs/clearing_house/src/controller/amm.rs | 8 +++-- .../clearing_house/src/controller/funding.rs | 2 +- .../clearing_house/src/controller/orders.rs | 7 ++-- programs/clearing_house/src/error.rs | 2 ++ programs/clearing_house/src/lib.rs | 34 +++++++++++++------ .../clearing_house/src/optional_accounts.rs | 17 ++++++++++ sdk/src/clearingHouse.ts | 14 +++++++- sdk/src/idl/clearing_house.json | 5 +++ tests/formulaK.ts | 4 +-- 9 files changed, 74 insertions(+), 19 deletions(-) diff --git a/programs/clearing_house/src/controller/amm.rs b/programs/clearing_house/src/controller/amm.rs index 36d186b1..8cb718d2 100644 --- a/programs/clearing_house/src/controller/amm.rs +++ b/programs/clearing_house/src/controller/amm.rs @@ -11,6 +11,7 @@ use crate::state::history::curve::{ExtendedCurveHistory, ExtendedCurveRecord}; use crate::state::market::{Market, OraclePriceData, AMM}; use std::cell::RefMut; +use anchor_lang::AccountLoader; use solana_program::log::sol_log_compute_units; use std::cmp::{max, min}; @@ -102,7 +103,7 @@ pub fn formulaic_update_k( market: &mut Market, oracle_price_data: &OraclePriceData, funding_imbalance_cost: i128, - curve_history: Option<&mut RefMut>, + curve_history: Option<&AccountLoader>, now: i64, market_index: u64, trade_record: Option, @@ -134,7 +135,10 @@ pub fn formulaic_update_k( }; if budget != 0 && curve_history.is_some() { - let curve_history = curve_history.unwrap(); + let curve_history = &mut curve_history + .unwrap() + .load_mut() + .or(Err(ErrorCode::UnableToLoadAccountLoader))?; // single k scale is capped by .1% increase and .09% decrease (regardless of budget) let (k_scale_numerator, k_scale_denominator) = diff --git a/programs/clearing_house/src/controller/funding.rs b/programs/clearing_house/src/controller/funding.rs index 62c4b8aa..b18eb104 100644 --- a/programs/clearing_house/src/controller/funding.rs +++ b/programs/clearing_house/src/controller/funding.rs @@ -100,7 +100,7 @@ pub fn update_funding_rate( now: UnixTimestamp, clock_slot: u64, funding_rate_history: &mut RefMut, - curve_history: Option<&mut RefMut>, + curve_history: Option<&AccountLoader>, guard_rails: &OracleGuardRails, funding_paused: bool, precomputed_mark_price: Option, diff --git a/programs/clearing_house/src/controller/orders.rs b/programs/clearing_house/src/controller/orders.rs index e18600af..470bf09f 100644 --- a/programs/clearing_house/src/controller/orders.rs +++ b/programs/clearing_house/src/controller/orders.rs @@ -800,9 +800,6 @@ pub fn fill_order( let funding_rate_history = &mut funding_rate_history .load_mut() .or(Err(ErrorCode::UnableToLoadAccountLoader))?; - let extended_curve_history = &mut extended_curve_history - .load_mut() - .or(Err(ErrorCode::UnableToLoadAccountLoader))?; controller::funding::update_funding_rate( market_index, market, @@ -817,6 +814,10 @@ pub fn fill_order( Some(trade_record_id), )?; + let extended_curve_history = &mut extended_curve_history + .load_mut() + .or(Err(ErrorCode::UnableToLoadAccountLoader))?; + // if market_index >= 12 { // todo for soft launch controller::repeg::formulaic_repeg( diff --git a/programs/clearing_house/src/error.rs b/programs/clearing_house/src/error.rs index 3eab0fc9..304a543f 100644 --- a/programs/clearing_house/src/error.rs +++ b/programs/clearing_house/src/error.rs @@ -128,6 +128,8 @@ pub enum ErrorCode { CantExpireOrders, #[msg("AMM repeg mark price impact vs oracle too large")] InvalidRepegPriceImpact, + #[msg("Could not deserialize curve history")] + CouldNotDeserializeCurveHistory, } #[macro_export] diff --git a/programs/clearing_house/src/lib.rs b/programs/clearing_house/src/lib.rs index e5647665..d6312e61 100644 --- a/programs/clearing_house/src/lib.rs +++ b/programs/clearing_house/src/lib.rs @@ -35,7 +35,7 @@ declare_id!("AsW7LnXB9UA1uec9wi9MctYTgTz7YH9snhxd16GsFaGX"); pub mod clearing_house { use crate::math; use crate::optional_accounts::{ - get_discount_token, get_oracle_for_cancel_order_by_order_id, + get_discount_token, get_extended_curve_history, get_oracle_for_cancel_order_by_order_id, get_oracle_for_cancel_order_by_user_order_id, get_oracle_for_place_order, get_referrer, get_referrer_for_fill_order, }; @@ -763,6 +763,12 @@ pub mod clearing_house { [Markets::index_from_u64(market_index)]; let price_oracle = &ctx.accounts.oracle; let funding_rate_history = &mut ctx.accounts.funding_rate_history.load_mut()?; + let extended_curve_history_value = get_extended_curve_history( + ctx.remaining_accounts, + &ctx.accounts.state.extended_curve_history, + )?; + let extended_curve_history_ref = extended_curve_history_value.as_ref(); + controller::funding::update_funding_rate( market_index, market, @@ -770,12 +776,16 @@ pub mod clearing_house { now, clock_slot, funding_rate_history, - None, + extended_curve_history_ref, &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, Some(mark_price_after), Some(record_id), )?; + + if let Some(extended_curve_history) = extended_curve_history_value { + extended_curve_history.exit(ctx.program_id); + } } Ok(()) @@ -957,6 +967,12 @@ pub mod clearing_house { oracle_price: oracle_price_after, }); + let extended_curve_history_value = get_extended_curve_history( + ctx.remaining_accounts, + &ctx.accounts.state.extended_curve_history, + )?; + let extended_curve_history_ref = extended_curve_history_value.as_ref(); + // Try to update the funding rate at the end of every trade let funding_rate_history = &mut ctx.accounts.funding_rate_history.load_mut()?; controller::funding::update_funding_rate( @@ -966,13 +982,17 @@ pub mod clearing_house { now, clock_slot, funding_rate_history, - None, + extended_curve_history_ref, &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, Some(mark_price_after), Some(record_id), )?; + if let Some(extended_curve_history) = extended_curve_history_value { + extended_curve_history.exit(ctx.program_id); + } + Ok(()) } @@ -2088,12 +2108,6 @@ pub mod clearing_house { let clock_slot = clock.slot; let funding_rate_history = &mut ctx.accounts.funding_rate_history.load_mut()?; - let extended_curve_history = &mut ctx - .accounts - .extended_curve_history - .load_mut() - .or(Err(ErrorCode::UnableToLoadAccountLoader))?; - controller::funding::update_funding_rate( market_index, market, @@ -2101,7 +2115,7 @@ pub mod clearing_house { now, clock_slot, funding_rate_history, - Some(extended_curve_history), + Some(&ctx.accounts.extended_curve_history), &ctx.accounts.state.oracle_guard_rails, ctx.accounts.state.funding_paused, None, diff --git a/programs/clearing_house/src/optional_accounts.rs b/programs/clearing_house/src/optional_accounts.rs index 5204f242..ab4db732 100644 --- a/programs/clearing_house/src/optional_accounts.rs +++ b/programs/clearing_house/src/optional_accounts.rs @@ -1,6 +1,7 @@ use crate::context::{InitializeUserOptionalAccounts, ManagePositionOptionalAccounts, OrderParams}; use crate::error::{ClearingHouseResult, ErrorCode}; use crate::print_error; +use crate::state::history::curve::ExtendedCurveHistory; use crate::state::market::Markets; use crate::state::user::User; use crate::state::user_orders::UserOrders; @@ -256,3 +257,19 @@ pub fn get_oracle_for_cancel_order_by_user_order_id<'a, 'b, 'c, 'd>( Ok(oracle) } + +pub fn get_extended_curve_history<'a, 'b, 'c>( + accounts: &'a [AccountInfo<'b>], + extended_curve_history_key: &'c Pubkey, +) -> ClearingHouseResult>> { + let account_info_iter = &mut accounts.iter(); + let account_info = + account_info_iter.find(|account_info| account_info.key.eq(extended_curve_history_key)); + + match account_info { + None => Ok(None), + Some(account_info) => AccountLoader::try_from(account_info) + .map(Some) + .or(Err(ErrorCode::CouldNotDeserializeCurveHistory)), + } +} diff --git a/sdk/src/clearingHouse.ts b/sdk/src/clearingHouse.ts index f114b954..ad326b3c 100644 --- a/sdk/src/clearingHouse.ts +++ b/sdk/src/clearingHouse.ts @@ -659,10 +659,16 @@ export class ClearingHouse { }); } + const state = this.getStateAccount(); + remainingAccounts.push({ + pubkey: state.extendedCurveHistory, + isWritable: true, + isSigner: false, + }); + const priceOracle = this.getMarketsAccount().markets[marketIndex.toNumber()].amm.oracle; - const state = this.getStateAccount(); return await this.program.instruction.openPosition( direction, amount, @@ -1186,6 +1192,12 @@ export class ClearingHouse { } const state = this.getStateAccount(); + remainingAccounts.push({ + pubkey: state.extendedCurveHistory, + isWritable: true, + isSigner: false, + }); + return await this.program.instruction.closePosition( marketIndex, optionalAccounts, diff --git a/sdk/src/idl/clearing_house.json b/sdk/src/idl/clearing_house.json index c4aa9f28..dbae17fc 100644 --- a/sdk/src/idl/clearing_house.json +++ b/sdk/src/idl/clearing_house.json @@ -4356,6 +4356,11 @@ "code": 6061, "name": "InvalidRepegPriceImpact", "msg": "AMM repeg mark price impact vs oracle too large" + }, + { + "code": 6062, + "name": "CouldNotDeserializeCurveHistory", + "msg": "Could not deserialize curve history" } ] } \ No newline at end of file diff --git a/tests/formulaK.ts b/tests/formulaK.ts index af2bd30f..00603a0e 100644 --- a/tests/formulaK.ts +++ b/tests/formulaK.ts @@ -343,7 +343,7 @@ describe('formulaic curve (k)', () => { const curveHistoryAccount = clearingHouse.getCurveHistoryAccount(); const curveHistoryHead = curveHistoryAccount.head.toNumber(); - assert.ok(curveHistoryHead === 1); + assert.ok(curveHistoryHead === 2); const cRecord = curveHistoryAccount.curveRecords[curveHistoryHead - 1]; console.log( @@ -465,7 +465,7 @@ describe('formulaic curve (k)', () => { const curveHistoryAccount = clearingHouse.getCurveHistoryAccount(); const curveHistoryHead = curveHistoryAccount.head.toNumber(); - assert.ok(curveHistoryHead === 2); + assert.ok(curveHistoryHead === 3); const cRecord = curveHistoryAccount.curveRecords[curveHistoryHead - 1]; console.log(