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/update amm revenue pool logic #398

Merged
merged 11 commits into from
Mar 22, 2023
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
### Features

- sdk: add isUserBankrupt ([#399](https://github.com/drift-labs/protocol-v2/pull/399))
- program: update revenue pool fund settlement logic ([#398](https://github.com/drift-labs/protocol-v2/pull/398))

### Fixes

Expand Down
54 changes: 43 additions & 11 deletions programs/drift/src/controller/amm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ pub fn update_pool_balances(
.total_fee_minus_distributions
.safe_sub(market.amm.total_fee_withdrawn.cast()?)?;

if terminal_state_surplus < 0 {
if terminal_state_surplus < FEE_POOL_TO_REVENUE_POOL_THRESHOLD.cast()? {
// market can perform withdraw from revenue pool
if spot_market.insurance_fund.last_revenue_settle_ts
> market.insurance_claim.last_revenue_withdraw_ts
Expand All @@ -484,6 +484,7 @@ pub fn update_pool_balances(
let max_revenue_withdraw_allowed = market
.insurance_claim
.max_revenue_withdraw_per_period
.cast::<i64>()?
.safe_sub(market.insurance_claim.revenue_withdraw_since_last_settle)?
.cast::<u128>()?;

Expand Down Expand Up @@ -518,28 +519,59 @@ pub fn update_pool_balances(
market.insurance_claim.last_revenue_withdraw_ts = now;
}
} else {
let revenue_pool_transfer = get_total_fee_lower_bound(market)?
.cast::<i128>()?
.safe_add(market.amm.total_liquidation_fee.cast()?)?
.safe_sub(market.amm.total_fee_withdrawn.cast()?)?
let fee_pool_threshold = amm_fee_pool_token_amount_after
.saturating_sub(
FEE_POOL_TO_REVENUE_POOL_THRESHOLD.safe_add(market.amm.total_social_loss)?,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you add social loss here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if a market has had social loss, its indication it needs to maintain a larger fee pool for internal insurance (rather than giving to external insurance)

)
.cast()?;

let total_liq_fees_for_revenue_pool: i128 = market
.amm
.total_liquidation_fee
.min(
amm_fee_pool_token_amount_after
.saturating_sub(FEE_POOL_TO_REVENUE_POOL_THRESHOLD)
market
.insurance_claim
.quote_settled_insurance
.safe_add(market.insurance_claim.quote_max_insurance)?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this add?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both numbers are positive cumulative numbers for insurance

cumulative liquidation fees are only given for revenue pool up to the current external insurance levels provided + net losses already paid for by external insurance

.cast()?,
)
.max(0)
.unsigned_abs();
.cast()?;

let max_revenue_to_settle = market
.insurance_claim
.revenue_withdraw_since_last_settle
.safe_add(
market
.insurance_claim
.max_revenue_withdraw_per_period
.cast()?,
)?
.min(market.amm.net_revenue_since_last_funding)
.max(0);

let total_fee_for_if = get_total_fee_lower_bound(market)?.cast::<i128>()?;

let revenue_pool_transfer = total_fee_for_if
.safe_add(total_liq_fees_for_revenue_pool)?
.safe_sub(market.amm.total_fee_withdrawn.cast()?)?
.min(fee_pool_threshold)
.min(max_revenue_to_settle.cast()?);

transfer_spot_balance_to_revenue_pool(
revenue_pool_transfer,
revenue_pool_transfer.unsigned_abs(),
spot_market,
&mut market.amm.fee_pool,
)?;

market.insurance_claim.revenue_withdraw_since_last_settle = market
.insurance_claim
.revenue_withdraw_since_last_settle
.safe_sub(revenue_pool_transfer.cast()?)?;

market.amm.total_fee_withdrawn = market
.amm
.total_fee_withdrawn
.safe_add(revenue_pool_transfer)?;
.safe_add(revenue_pool_transfer.unsigned_abs())?;
}
}

Expand Down
146 changes: 138 additions & 8 deletions programs/drift/src/controller/amm/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::math::constants::{
AMM_RESERVE_PRECISION, MAX_CONCENTRATION_COEFFICIENT, PRICE_PRECISION_I64, QUOTE_PRECISION,
QUOTE_SPOT_MARKET_INDEX, SPOT_BALANCE_PRECISION, SPOT_CUMULATIVE_INTEREST_PRECISION,
};
use crate::state::perp_market::PoolBalance;
use crate::state::perp_market::{InsuranceClaim, PoolBalance};

#[test]
fn concentration_coef_tests() {
Expand Down Expand Up @@ -556,7 +556,7 @@ fn update_pool_balances_fee_to_revenue_test() {
total_mm_fee: 990 * QUOTE_PRECISION as i128,
total_fee_minus_distributions: 1000 * QUOTE_PRECISION as i128,
total_liquidation_fee: QUOTE_PRECISION,

net_revenue_since_last_funding: 10000 * QUOTE_PRECISION as i64,
curve_update_intensity: 100,

fee_pool: PoolBalance {
Expand All @@ -571,6 +571,11 @@ fn update_pool_balances_fee_to_revenue_test() {
market_index: QUOTE_SPOT_MARKET_INDEX,
..PoolBalance::default()
},
insurance_claim: InsuranceClaim {
quote_max_insurance: 0, // no liq fees for revenue pool
max_revenue_withdraw_per_period: 1000 * QUOTE_PRECISION as u64,
..InsuranceClaim::default()
},
..PerpMarket::default()
};
let now = 33928058;
Expand Down Expand Up @@ -627,14 +632,139 @@ fn update_pool_balances_fee_to_revenue_test() {
market.amm.fee_pool.scaled_balance = prev_fee_pool_2;
update_pool_balances(&mut market, &mut spot_market, &spot_position, 0, now).unwrap();

assert_eq!(market.amm.fee_pool.scaled_balance, 294000000000000000); // > FEE_POOL_TO_REVENUE_POOL_THRESHOLD
assert_eq!(market.pnl_pool.scaled_balance, 50000000000000000);
assert_eq!(spot_market.revenue_pool.scaled_balance, 6000000000000000);
assert_eq!(market.amm.total_fee_withdrawn, 5000000);
assert_eq!(spot_market.revenue_pool.scaled_balance, 5000000000000000);
assert_eq!(market.amm.fee_pool.scaled_balance, 295000000000000000); // > FEE_POOL_TO_REVENUE_POOL_THRESHOLD

assert!(market.amm.fee_pool.scaled_balance < prev_fee_pool_2);
assert_eq!(market.pnl_pool.scaled_balance, prev_pnl_pool);
assert!(spot_market.revenue_pool.scaled_balance > prev_rev_pool);

market.insurance_claim.quote_max_insurance = 1; // add min insurance
update_pool_balances(&mut market, &mut spot_market, &spot_position, 0, now).unwrap();
assert_eq!(market.amm.total_fee_withdrawn, 5000001);
assert_eq!(spot_market.revenue_pool.scaled_balance, 5000001000000000);

market.insurance_claim.quote_max_insurance = 100000000; // add lots of insurance
update_pool_balances(&mut market, &mut spot_market, &spot_position, 0, now).unwrap();
assert_eq!(market.amm.total_fee_withdrawn, 6000000);
assert_eq!(spot_market.revenue_pool.scaled_balance, 6000000000000000);
}

#[test]
fn update_pool_balances_fee_to_revenue_low_amm_revenue_test() {
let mut market = PerpMarket {
amm: AMM {
base_asset_reserve: 5122950819670000,
quote_asset_reserve: 488 * AMM_RESERVE_PRECISION,
sqrt_k: 500 * AMM_RESERVE_PRECISION,
peg_multiplier: 50000,
base_asset_amount_with_amm: -122950819670000,

total_exchange_fee: 10 * QUOTE_PRECISION,
total_fee: 10 * QUOTE_PRECISION as i128,
total_mm_fee: 990 * QUOTE_PRECISION as i128,
total_fee_minus_distributions: 1000 * QUOTE_PRECISION as i128,
total_liquidation_fee: QUOTE_PRECISION,
net_revenue_since_last_funding: QUOTE_PRECISION as i64,
curve_update_intensity: 100,

fee_pool: PoolBalance {
scaled_balance: 50 * QUOTE_PRECISION * SPOT_BALANCE_PRECISION,
market_index: QUOTE_SPOT_MARKET_INDEX,
..PoolBalance::default()
},
..AMM::default()
},
pnl_pool: PoolBalance {
scaled_balance: 50 * QUOTE_PRECISION * SPOT_BALANCE_PRECISION,
market_index: QUOTE_SPOT_MARKET_INDEX,
..PoolBalance::default()
},
insurance_claim: InsuranceClaim {
quote_max_insurance: 0, // no liq fees for revenue pool
max_revenue_withdraw_per_period: 1000 * QUOTE_PRECISION as u64,
..InsuranceClaim::default()
},
..PerpMarket::default()
};
let now = 33928058;

let mut spot_market = SpotMarket {
deposit_balance: 100 * QUOTE_PRECISION * SPOT_BALANCE_PRECISION,
cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
cumulative_borrow_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
revenue_pool: PoolBalance::default(),
..SpotMarket::default()
};

let prev_fee_pool = market.amm.fee_pool.scaled_balance;
let prev_pnl_pool = market.amm.fee_pool.scaled_balance;
let prev_rev_pool = spot_market.revenue_pool.scaled_balance;

assert_eq!(market.amm.total_fee_withdrawn, 0);

assert_eq!(
get_token_amount(
market.amm.fee_pool.balance(),
&spot_market,
&SpotBalanceType::Deposit
)
.unwrap(),
50 * QUOTE_PRECISION
);

assert_eq!(
get_token_amount(
spot_market.deposit_balance,
&spot_market,
&SpotBalanceType::Deposit
)
.unwrap(),
100 * QUOTE_PRECISION
);

let spot_position = SpotPosition::default();
update_pool_balances(&mut market, &mut spot_market, &spot_position, 0, now).unwrap();

assert_eq!(market.amm.fee_pool.scaled_balance, 50000000000000000); // under FEE_POOL_TO_REVENUE_POOL_THRESHOLD
assert_eq!(market.pnl_pool.scaled_balance, 50000000000000000);
assert_eq!(spot_market.revenue_pool.scaled_balance, 0);
assert_eq!(market.amm.total_fee_withdrawn, 0);

assert!(market.amm.fee_pool.scaled_balance == prev_fee_pool);
assert_eq!(market.pnl_pool.scaled_balance, prev_pnl_pool);
assert!(spot_market.revenue_pool.scaled_balance == prev_rev_pool);

// add FEE_POOL_TO_REVENUE_POOL_THRESHOLD
let prev_fee_pool_2 =
(FEE_POOL_TO_REVENUE_POOL_THRESHOLD + 50 * QUOTE_PRECISION) * SPOT_BALANCE_PRECISION;
market.amm.fee_pool.scaled_balance = prev_fee_pool_2;
update_pool_balances(&mut market, &mut spot_market, &spot_position, 0, now).unwrap();

assert_eq!(market.pnl_pool.scaled_balance, 50000000000000000);
assert_eq!(market.amm.total_fee_withdrawn, 1000000);
assert_eq!(spot_market.revenue_pool.scaled_balance, 1000000000000000);
assert_eq!(market.amm.fee_pool.scaled_balance, 299000000000000000); // > FEE_POOL_TO_REVENUE_POOL_THRESHOLD

assert!(market.amm.fee_pool.scaled_balance < prev_fee_pool_2);
assert_eq!(market.pnl_pool.scaled_balance, prev_pnl_pool);
assert!(spot_market.revenue_pool.scaled_balance > prev_rev_pool);

market.insurance_claim.quote_max_insurance = 1; // add min insurance
market.amm.net_revenue_since_last_funding = 1;

update_pool_balances(&mut market, &mut spot_market, &spot_position, 0, now).unwrap();
assert_eq!(market.amm.total_fee_withdrawn, 1000001);
assert_eq!(spot_market.revenue_pool.scaled_balance, 1000001000000000);

market.insurance_claim.quote_max_insurance = 100000000; // add lots of insurance
market.amm.net_revenue_since_last_funding = 100000000;

update_pool_balances(&mut market, &mut spot_market, &spot_position, 0, now).unwrap();
assert_eq!(market.amm.total_fee_withdrawn, 6000000);
assert_eq!(spot_market.revenue_pool.scaled_balance, 6000000000000000);
}

#[test]
Expand Down Expand Up @@ -817,7 +947,7 @@ fn update_pool_balances_revenue_to_fee_test() {
assert_eq!(market.amm.total_fee_withdrawn, 0);
assert_eq!(
market.insurance_claim.revenue_withdraw_since_last_settle,
market.insurance_claim.max_revenue_withdraw_per_period
market.insurance_claim.max_revenue_withdraw_per_period as i64
);
assert_eq!(market.insurance_claim.last_revenue_withdraw_ts, 33928058);

Expand All @@ -830,7 +960,7 @@ fn update_pool_balances_revenue_to_fee_test() {
assert_eq!(spot_market.revenue_pool.scaled_balance, 9800000001000);
assert_eq!(
market.insurance_claim.revenue_withdraw_since_last_settle,
market.insurance_claim.max_revenue_withdraw_per_period
market.insurance_claim.max_revenue_withdraw_per_period as i64
);
assert_eq!(market.insurance_claim.last_revenue_withdraw_ts, 33928058);

Expand All @@ -843,7 +973,7 @@ fn update_pool_balances_revenue_to_fee_test() {
assert_eq!(spot_market.revenue_pool.scaled_balance, 9800000001000);
assert_eq!(
market.insurance_claim.revenue_withdraw_since_last_settle,
market.insurance_claim.max_revenue_withdraw_per_period
market.insurance_claim.max_revenue_withdraw_per_period as i64
);
assert_eq!(market.insurance_claim.last_revenue_withdraw_ts, 33928058);

Expand Down Expand Up @@ -909,6 +1039,6 @@ fn update_pool_balances_revenue_to_fee_test() {
assert_eq!(spot_market.revenue_pool.scaled_balance, 9600000001000);
assert_eq!(
market.insurance_claim.revenue_withdraw_since_last_settle,
market.insurance_claim.max_revenue_withdraw_per_period
market.insurance_claim.max_revenue_withdraw_per_period as i64
);
}
10 changes: 8 additions & 2 deletions programs/drift/src/controller/insurance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ pub fn settle_revenue_to_insurance_fund(
.max(1),
)?
.cast::<u128>()?;
let capped_token_pct_amount = token_amount.safe_div(5)?;
let capped_token_pct_amount = token_amount.safe_div(10)?;
token_amount = capped_token_pct_amount.min(capped_apr_amount);
}

Expand Down Expand Up @@ -695,7 +695,13 @@ pub fn resolve_perp_pnl_deficit(
let max_revenue_withdraw_per_period = market
.insurance_claim
.max_revenue_withdraw_per_period
.safe_sub(market.insurance_claim.revenue_withdraw_since_last_settle)?
.cast::<i128>()?
.safe_sub(
market
.insurance_claim
.revenue_withdraw_since_last_settle
.cast()?,
)?
.cast::<i128>()?;
validate!(
max_revenue_withdraw_per_period > 0,
Expand Down
2 changes: 1 addition & 1 deletion programs/drift/src/state/perp_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ impl PerpMarket {
#[derive(Default, Eq, PartialEq, Debug)]
#[repr(C)]
pub struct InsuranceClaim {
pub revenue_withdraw_since_last_settle: u64,
pub revenue_withdraw_since_last_settle: i64,
pub max_revenue_withdraw_per_period: u64,
pub quote_max_insurance: u64,
pub quote_settled_insurance: u64,
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/idl/drift.json
Original file line number Diff line number Diff line change
Expand Up @@ -5119,7 +5119,7 @@
"fields": [
{
"name": "revenueWithdrawSinceLastSettle",
"type": "u64"
"type": "i64"
},
{
"name": "maxRevenueWithdrawPerPeriod",
Expand Down
3 changes: 2 additions & 1 deletion tests/delistMarket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,8 @@ describe('delist market', () => {
'totalExchangeFee:',
marketAfter.amm.totalExchangeFee.toString()
);
assert(marketAfter.amm.feePool.scaledBalance.eq(new BN(21567000)));
assert(marketAfter.amm.feePool.scaledBalance.eq(new BN(64700000)));

// assert(marketAfter.amm.totalExchangeFee.eq(new BN(43134)));
assert(marketAfter.amm.totalExchangeFee.eq(new BN(129401)));
});
Expand Down
Loading