Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bigz/use-get_update_k_result-in-prepeg #294

Merged
merged 12 commits into from
Dec 16, 2022
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixes

- program: more precise update k in prepeg ([#294](https://github.com/drift-labs/protocol-v2/pull/294))
- program: allow duplicative reduce only orders ([#293](https://github.com/drift-labs/protocol-v2/pull/293))
- program: fix should_cancel_reduce_only_order
- ts-sdk: add Oracle OrderType to dlob idl
Expand Down
3 changes: 2 additions & 1 deletion programs/drift/src/controller/amm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,8 @@ pub fn formulaic_update_k(

let new_sqrt_k = bn::U192::from(market.amm.sqrt_k)
.safe_mul(bn::U192::from(k_scale_numerator))?
.safe_div(bn::U192::from(k_scale_denominator))?;
.safe_div(bn::U192::from(k_scale_denominator))?
.max(bn::U192::from(market.amm.user_lp_shares.safe_add(1)?));

let update_k_result = get_update_k_result(market, new_sqrt_k, true)?;

Expand Down
66 changes: 65 additions & 1 deletion programs/drift/src/controller/amm/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ fn formualic_k_tests() {
},
..PerpMarket::default()
};
let (new_terminal_quote_reserve, new_terminal_base_reserve) =
amm::calculate_terminal_reserves(&market.amm).unwrap();
market.amm.terminal_quote_asset_reserve = new_terminal_quote_reserve;
let (min_base_asset_reserve, max_base_asset_reserve) =
amm::calculate_bid_ask_bounds(market.amm.concentration_coef, new_terminal_base_reserve)
.unwrap();
market.amm.min_base_asset_reserve = min_base_asset_reserve;
market.amm.max_base_asset_reserve = max_base_asset_reserve;

let prev_sqrt_k = market.amm.sqrt_k;

Expand All @@ -139,8 +147,8 @@ fn formualic_k_tests() {
// positive means amm supossedly paid $500 in funding payments for interval
let funding_cost_2: i128 = (500 * QUOTE_PRECISION) as i128;
formulaic_update_k(&mut market, &oracle_price_data, funding_cost_2, now).unwrap();
assert!(prev_sqrt_k > market.amm.sqrt_k);
assert_eq!(market.amm.sqrt_k, 499500000000); // max k decrease (.1%)
assert!(prev_sqrt_k > market.amm.sqrt_k);
assert_eq!(market.amm.total_fee_minus_distributions, 1000014768); //$.014768 acquired from slippage increase

// negative means amm recieved $500 in funding payments for interval
Expand Down Expand Up @@ -247,6 +255,62 @@ fn iterative_no_bounds_formualic_k_tests() {
assert_eq!(market.amm.total_fee_minus_distributions, 985625040);
}

#[test]
fn decrease_k_up_to_user_lp_shares() {
let mut market = PerpMarket {
amm: AMM {
base_asset_reserve: 512295081967,
quote_asset_reserve: 488 * AMM_RESERVE_PRECISION,
sqrt_k: 500 * AMM_RESERVE_PRECISION,
user_lp_shares: 150 * AMM_RESERVE_PRECISION,
peg_multiplier: 50000000,
concentration_coef: MAX_CONCENTRATION_COEFFICIENT,
base_asset_amount_with_amm: -12295081967,
total_fee_minus_distributions: -100 * QUOTE_PRECISION as i128,
total_fee_withdrawn: 100 * QUOTE_PRECISION,
curve_update_intensity: 100,
..AMM::default()
},
..PerpMarket::default()
};
// let prev_sqrt_k = market.amm.sqrt_k;
let (new_terminal_quote_reserve, new_terminal_base_reserve) =
amm::calculate_terminal_reserves(&market.amm).unwrap();
market.amm.terminal_quote_asset_reserve = new_terminal_quote_reserve;
let (min_base_asset_reserve, max_base_asset_reserve) =
amm::calculate_bid_ask_bounds(market.amm.concentration_coef, new_terminal_base_reserve)
.unwrap();
market.amm.min_base_asset_reserve = min_base_asset_reserve;
market.amm.max_base_asset_reserve = max_base_asset_reserve;

// let reserve_price = market.amm.reserve_price().unwrap();
let now = 10000;
let oracle_price_data = OraclePriceData {
price: 50 * PRICE_PRECISION_I64,
confidence: 0,
delay: 2,
has_sufficient_number_of_data_points: true,
};

// negative funding cost
let mut count = 0;
let mut prev_k = market.amm.sqrt_k;
let mut new_k = 0;
while prev_k != new_k && count < 100000 {
let funding_cost = (QUOTE_PRECISION * 100000) as i128;
prev_k = market.amm.sqrt_k;
formulaic_update_k(&mut market, &oracle_price_data, funding_cost, now).unwrap();
new_k = market.amm.sqrt_k;
msg!("quote_asset_reserve:{}", market.amm.quote_asset_reserve);
msg!("new_k:{}", new_k);
count += 1
}

assert_eq!(market.amm.base_asset_amount_with_amm, -12295081967);
assert_eq!(market.amm.sqrt_k, 162234889619);
assert_eq!(market.amm.total_fee_minus_distributions, 29796232175);
}

#[test]
fn update_pool_balances_test_high_util_borrow() {
let mut market = PerpMarket {
Expand Down
55 changes: 32 additions & 23 deletions programs/drift/src/controller/repeg/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,23 @@ pub fn update_amm_test() {
base_spread: 250,
curve_update_intensity: 100,
max_spread: 55500,
concentration_coef: 1020710,

concentration_coef: 31020710, //unrealistic but for poc
..AMM::default()
},
status: MarketStatus::Initialized,
contract_tier: ContractTier::B,
margin_ratio_initial: 555, // max 1/.0555 = 18.018018018x leverage
..PerpMarket::default()
};
let (new_terminal_quote_reserve, new_terminal_base_reserve) =
amm::calculate_terminal_reserves(&market.amm).unwrap();
// market.amm.terminal_quote_asset_reserve = new_terminal_quote_reserve;
assert_eq!(new_terminal_quote_reserve, 64000000000);
let (min_base_asset_reserve, max_base_asset_reserve) =
amm::calculate_bid_ask_bounds(market.amm.concentration_coef, new_terminal_base_reserve)
.unwrap();
market.amm.min_base_asset_reserve = min_base_asset_reserve;
market.amm.max_base_asset_reserve = max_base_asset_reserve;

let state = State {
oracle_guard_rails: OracleGuardRails {
Expand Down Expand Up @@ -86,6 +94,7 @@ pub fn update_amm_test() {

let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap();

assert_eq!(market.amm.sqrt_k, 63936000000);
let is_oracle_valid = oracle::oracle_validity(
market.amm.historical_oracle_data.last_oracle_price_twap,
&oracle_price_data,
Expand All @@ -95,7 +104,7 @@ pub fn update_amm_test() {
== OracleValidity::Valid;

let reserve_price_after_prepeg = market.amm.reserve_price().unwrap();
assert_eq!(reserve_price_after_prepeg, 12743902020);
assert_eq!(reserve_price_after_prepeg, 12743902015);
assert_eq!(
market.amm.historical_oracle_data.last_oracle_price,
12400000000
Expand Down Expand Up @@ -131,8 +140,8 @@ pub fn update_amm_test() {
assert_eq!(-cost_of_update, profit);
assert!(is_oracle_valid);
assert!(profit < 0);
assert_eq!(peg, 13145260263);
assert_eq!(profit, -6158609290);
assert_eq!(peg, 13145260284);
assert_eq!(profit, -6158609264);

let reserve_price = market.amm.reserve_price().unwrap();
let (bid, ask) = market.amm.bid_ask_price(reserve_price).unwrap();
Expand All @@ -144,12 +153,12 @@ pub fn update_amm_test() {
(market.margin_ratio_initial * 100) as u32
);

assert_eq!(bid, 12038871128);
assert_eq!(bid, 12055425452);
assert!(bid < (oracle_price_data.price as u64));
assert_eq!(reserve_price, 12743902020);
assert_eq!(ask, 12746157690);
assert_eq!(reserve_price, 12743902015);
assert_eq!(ask, 12762712014);
assert!(ask >= (oracle_price_data.price as u64));
assert!((ask - bid) * 1000000 / reserve_price < market.amm.max_spread as u64);
assert!((ask - bid) * 1000000 / reserve_price == market.amm.max_spread as u64);
}

#[test]
Expand Down Expand Up @@ -462,14 +471,14 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() {
let prev_peg_multiplier = market.amm.peg_multiplier;
let prev_total_fee_minus_distributions = market.amm.total_fee_minus_distributions;
let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap();
assert_eq!(cost_of_update, 21459607); // amm loses when price decreases (given users are net short)
assert_eq!(cost_of_update, 21459587); // amm loses when price decreases (given users are net short)
assert_eq!(market.amm.sqrt_k, 63936000000); // k lowered since cost_of_update is positive and total_fee_minus_distributions negative
assert_eq!(market.amm.base_asset_reserve, 64935000000);
assert_eq!(market.amm.quote_asset_reserve, 62952369231);
assert_eq!(market.amm.terminal_quote_asset_reserve, 63937000015);
assert_eq!(market.amm.min_base_asset_reserve, 45208890032);
assert_eq!(market.amm.max_base_asset_reserve, 90417708155);
assert_eq!(market.amm.peg_multiplier, 19421869977);
assert_eq!(market.amm.base_asset_reserve, 64935000065);
assert_eq!(market.amm.quote_asset_reserve, 62952369167);
assert_eq!(market.amm.terminal_quote_asset_reserve, 63936999950);
assert_eq!(market.amm.min_base_asset_reserve, 45208890078);
assert_eq!(market.amm.max_base_asset_reserve, 90417708246);
assert_eq!(market.amm.peg_multiplier, 19421869997);
assert_eq!(market.amm.peg_multiplier < prev_peg_multiplier, true);
// assert_eq!(market.amm.total_fee_minus_distributions, -9978167413);
assert_eq!(
Expand All @@ -487,10 +496,10 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() {
let (bid, ask) = market.amm.bid_ask_price(mrk).unwrap();

let orc = oracle_price_data.price as u64;
assert_eq!(bid, 18817460574);
assert_eq!(bid, 18817460555);
assert_eq!(orc, 18820000000);
assert_eq!(mrk, 18828870870);
assert_eq!(ask, 18835818723);
assert_eq!(mrk, 18828870851);
assert_eq!(ask, 18835818704);

assert_eq!(bid <= orc, true);

Expand All @@ -503,16 +512,16 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() {
};

let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap();
assert_eq!(cost_of_update, 299407);
assert_eq!(cost_of_update, 299367);
assert_eq!(market.amm.long_spread, 378);
assert_eq!(market.amm.short_spread, 975 - 378);

let mrk = market.amm.reserve_price().unwrap();
let (bid, ask) = market.amm.bid_ask_price(mrk).unwrap();

assert_eq!(bid, 18817335418);
assert_eq!(mrk, 18828576078);
assert_eq!(ask, 18835693279);
assert_eq!(bid, 18817335401);
assert_eq!(mrk, 18828576061);
assert_eq!(ask, 18835693262);
assert_eq!((oracle_price_data.price as u64) > bid, true);
assert_eq!((oracle_price_data.price as u64) < ask, true);
}
8 changes: 8 additions & 0 deletions programs/drift/src/instructions/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,14 @@ pub fn handle_update_k(ctx: Context<AdminUpdateK>, sqrt_k: u128) -> Result<()> {
perp_market.amm.sqrt_k
)?;

validate!(
perp_market.amm.sqrt_k > perp_market.amm.user_lp_shares,
ErrorCode::InvalidUpdateK,
"cannot decrease sqrt_k={} below user_lp_shares={}",
perp_market.amm.sqrt_k,
perp_market.amm.user_lp_shares
)?;

perp_market.amm.total_fee_minus_distributions = perp_market
.amm
.total_fee_minus_distributions
Expand Down
19 changes: 4 additions & 15 deletions programs/drift/src/math/repeg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,22 +330,11 @@ pub fn adjust_amm(
let new_sqrt_k = market
.amm
.sqrt_k
.safe_sub(market.amm.sqrt_k.safe_div(1000)?)?;
.safe_sub(market.amm.sqrt_k.safe_div(1000)?)?
.max(market.amm.user_lp_shares.safe_add(1)?);

let new_base_asset_reserve = market
.amm
.base_asset_reserve
.safe_sub(market.amm.base_asset_reserve.safe_div(1000)?)?;
let new_quote_asset_reserve = market
.amm
.quote_asset_reserve
.safe_sub(market.amm.quote_asset_reserve.safe_div(1000)?)?;

let update_k_result = cp_curve::UpdateKResult {
sqrt_k: new_sqrt_k,
base_asset_reserve: new_base_asset_reserve,
quote_asset_reserve: new_quote_asset_reserve,
};
let update_k_result =
cp_curve::get_update_k_result(market, bn::U192::from(new_sqrt_k), true)?;

let adjustment_cost =
cp_curve::adjust_k_cost_and_update(&mut market_clone, &update_k_result)?;
Expand Down
20 changes: 18 additions & 2 deletions programs/drift/src/math/repeg/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,14 @@ fn calculate_optimal_peg_and_budget_2_test() {

..PerpMarket::default()
};
let (new_terminal_quote_reserve, new_terminal_base_reserve) =
amm::calculate_terminal_reserves(&market.amm).unwrap();
market.amm.terminal_quote_asset_reserve = new_terminal_quote_reserve;
let (min_base_asset_reserve, max_base_asset_reserve) =
amm::calculate_bid_ask_bounds(market.amm.concentration_coef, new_terminal_base_reserve)
.unwrap();
market.amm.min_base_asset_reserve = min_base_asset_reserve;
market.amm.max_base_asset_reserve = max_base_asset_reserve;

let oracle_price_data = OraclePriceData {
price: (17_800 * PRICE_PRECISION) as i64,
Expand Down Expand Up @@ -226,7 +234,7 @@ fn calculate_optimal_peg_and_budget_2_test() {
// test amm update
assert_eq!(market.amm.last_update_slot, 0);
let c = _update_amm(&mut market, &oracle_price_data, &state, 1, 1337).unwrap();
assert_eq!(c, 424);
assert_eq!(c, 442);
assert_eq!(market.amm.last_update_slot, 1337);
}

Expand Down Expand Up @@ -271,7 +279,7 @@ fn calc_adjust_amm_tests_repeg_in_favour() {
#[test]
fn calc_adjust_amm_tests_sufficent_fee_for_repeg() {
// btc-esque market
let market = PerpMarket {
let mut market = PerpMarket {
amm: AMM {
order_step_size: 1000,
base_asset_reserve: 60437939720095,
Expand Down Expand Up @@ -299,6 +307,14 @@ fn calc_adjust_amm_tests_sufficent_fee_for_repeg() {

..PerpMarket::default()
};
let (new_terminal_quote_reserve, new_terminal_base_reserve) =
amm::calculate_terminal_reserves(&market.amm).unwrap();
market.amm.terminal_quote_asset_reserve = new_terminal_quote_reserve;
let (min_base_asset_reserve, max_base_asset_reserve) =
amm::calculate_bid_ask_bounds(market.amm.concentration_coef, new_terminal_base_reserve)
.unwrap();
market.amm.min_base_asset_reserve = min_base_asset_reserve;
market.amm.max_base_asset_reserve = max_base_asset_reserve;

let px = 35768 * PRICE_PRECISION_U64 / 1000;
let optimal_peg = calculate_peg_from_target_price(
Expand Down
6 changes: 5 additions & 1 deletion programs/drift/src/state/perp_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,11 @@ impl AMM {
}

pub fn can_lower_k(&self) -> DriftResult<bool> {
let can_lower = self.base_asset_amount_with_amm.unsigned_abs() < self.sqrt_k / 4;
let (max_bids, max_asks) = amm::calculate_market_open_bids_asks(self)?;
let can_lower = self.base_asset_amount_with_amm.unsigned_abs()
< max_bids.unsigned_abs().min(max_asks.unsigned_abs())
&& self.base_asset_amount_with_amm.unsigned_abs()
< self.sqrt_k.safe_sub(self.user_lp_shares)?;
Ok(can_lower)
}

Expand Down