Skip to content

Commit

Permalink
program: more precise update k in prepeg (#294)
Browse files Browse the repository at this point in the history
* bigz/use-get_update_k_result-in-prepeg

* fix sqrt_k vs user_lp_shares

* add max check for new_sqrt_k in formulaic_update_k

* better lower_k function

* program: fix should_cancel_reduce_only_order

* program: tweak sqrt k validation rounding to be 15

* program: allow duplicate reduce only orders (#293)

* program: place order does not alter users order base asset amount

* fix race condition in serumTests

* update CHANGELOG.md

* sdk: accept makerInfo in sendMarketOrderAndGetSignedFillTx

* v2.6.0-beta.1

* update CHANGELOG.md

Co-authored-by: Chris Heaney <[email protected]>
  • Loading branch information
0xbigz and crispheaney committed Dec 16, 2022
1 parent ff26a0b commit 7e46d40
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 43 deletions.
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

0 comments on commit 7e46d40

Please sign in to comment.