Skip to content

Commit dc43396

Browse files
authored
aum cant go below zero (#1890)
* aum cant go below zero * keep it signed
1 parent eecbe20 commit dc43396

File tree

7 files changed

+58
-43
lines changed

7 files changed

+58
-43
lines changed

programs/drift/src/instructions/lp_admin.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use crate::state::perp_market_map::MarketSet;
2-
use crate::{controller, load_mut};
31
use crate::controller::token::{receive, send_from_program_vault_with_signature_seeds};
42
use crate::error::ErrorCode;
53
use crate::ids::{admin_hot_wallet, lp_pool_swap_wallet};
@@ -14,9 +12,11 @@ use crate::state::lp_pool::{
1412
CONSTITUENT_VAULT_PDA_SEED,
1513
};
1614
use crate::state::perp_market::PerpMarket;
15+
use crate::state::perp_market_map::MarketSet;
1716
use crate::state::spot_market::SpotMarket;
1817
use crate::state::state::State;
1918
use crate::validate;
19+
use crate::{controller, load_mut};
2020
use anchor_lang::prelude::*;
2121
use anchor_lang::Discriminator;
2222
use anchor_spl::associated_token::AssociatedToken;
@@ -893,7 +893,7 @@ pub fn handle_update_perp_market_lp_pool_status(
893893
msg!("perp market {}", perp_market.market_index);
894894
perp_market.lp_status = lp_status;
895895
amm_cache.update_perp_market_fields(&perp_market)?;
896-
896+
897897
Ok(())
898898
}
899899

@@ -980,7 +980,6 @@ pub fn handle_override_amm_cache_info<'c: 'info, 'info>(
980980
Ok(())
981981
}
982982

983-
984983
#[derive(Accounts)]
985984
#[instruction(
986985
name: [u8; 32],
@@ -1136,7 +1135,7 @@ pub struct UpdateConstituentParams<'info> {
11361135
pub struct UpdateConstituentStatus<'info> {
11371136
#[account(
11381137
mut,
1139-
constraint = admin.key() == state.admin
1138+
constraint = admin.key() == state.admin
11401139
)]
11411140
pub admin: Signer<'info>,
11421141
pub state: Box<Account<'info, State>>,

programs/drift/src/instructions/lp_pool.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ pub fn handle_update_lp_pool_aum<'c: 'info, 'info>(
220220
)?;
221221

222222
// Set USDC stable weight
223+
msg!("aum: {}", aum);
223224
let total_stable_target_base = aum
224225
.cast::<i128>()?
225226
.safe_sub(crypto_delta.abs())?
@@ -808,16 +809,15 @@ pub fn handle_lp_pool_add_liquidity<'c: 'info, 'info>(
808809

809810
ctx.accounts.lp_mint.reload()?;
810811
let lp_price_after = lp_pool.get_price(lp_pool.token_supply)?;
811-
if lp_price_before != 0 {
812-
let price_diff_percent = lp_price_after
813-
.abs_diff(lp_price_before)
814-
.safe_mul(PERCENTAGE_PRECISION)?
815-
.safe_div(lp_price_before)?;
812+
let price_diff = (lp_price_after.cast::<i128>()?).safe_sub(lp_price_before.cast::<i128>()?)?;
816813

814+
if lp_price_before > 0 && price_diff.signum() != 0 && in_fee_amount.signum() != 0 {
817815
validate!(
818-
price_diff_percent <= PERCENTAGE_PRECISION / 5,
816+
price_diff.signum() == in_fee_amount.signum() || price_diff == 0,
819817
ErrorCode::LpInvariantFailed,
820-
"Removing liquidity resulted in DLP token difference of > 5%"
818+
"Adding liquidity resulted in price direction != fee sign, price_diff: {}, in_fee_amount: {}",
819+
price_diff,
820+
in_fee_amount
821821
)?;
822822
}
823823

@@ -1192,15 +1192,14 @@ pub fn handle_lp_pool_remove_liquidity<'c: 'info, 'info>(
11921192
ctx.accounts.lp_mint.reload()?;
11931193
let lp_price_after = lp_pool.get_price(lp_pool.token_supply)?;
11941194

1195-
if lp_price_after != 0 {
1196-
let price_diff_percent = lp_price_after
1197-
.abs_diff(lp_price_before)
1198-
.safe_mul(PERCENTAGE_PRECISION)?
1199-
.safe_div(lp_price_before)?;
1195+
let price_diff = (lp_price_after.cast::<i128>()?).safe_sub(lp_price_before.cast::<i128>()?)?;
1196+
if price_diff.signum() != 0 && out_fee_amount.signum() != 0 {
12001197
validate!(
1201-
price_diff_percent <= PERCENTAGE_PRECISION / 5,
1198+
price_diff.signum() == out_fee_amount.signum(),
12021199
ErrorCode::LpInvariantFailed,
1203-
"Removing liquidity resulted in DLP token difference of > 5%"
1200+
"Removing liquidity resulted in price direction != fee sign, price_diff: {}, out_fee_amount: {}",
1201+
price_diff,
1202+
out_fee_amount
12041203
)?;
12051204
}
12061205

programs/drift/src/math/lp_pool.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ pub mod perp_lp_pool_settlement {
7979
let amount_to_send = ctx
8080
.quote_owed_from_lp
8181
.cast::<u64>()?
82-
.min(ctx.quote_constituent_token_balance)
82+
.min(
83+
ctx.quote_constituent_token_balance
84+
.saturating_sub(QUOTE_PRECISION_U64),
85+
)
8386
.min(ctx.max_settle_quote_amount);
8487

8588
Ok(SettlementResult {

programs/drift/src/state/lp_pool.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::error::{DriftResult, ErrorCode};
44
use crate::math::casting::Cast;
55
use crate::math::constants::{
66
BASE_PRECISION_I128, PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64,
7-
PERCENTAGE_PRECISION_U64, PRICE_PRECISION, QUOTE_PRECISION_I128,
7+
PERCENTAGE_PRECISION_U64, PRICE_PRECISION, QUOTE_PRECISION_I128, QUOTE_PRECISION_U64,
88
};
99
use crate::math::safe_math::SafeMath;
1010
use crate::math::safe_unwrap::SafeUnwrap;
@@ -300,13 +300,17 @@ impl LPPool {
300300
&self,
301301
out_spot_market: &SpotMarket,
302302
out_constituent: &Constituent,
303-
lp_burn_amount: u64,
303+
lp_to_burn: u64,
304304
out_oracle: &OraclePriceData,
305305
out_target_weight: i64,
306306
dlp_total_supply: u64,
307307
) -> DriftResult<(u64, u128, i64, i128)> {
308308
let lp_fee_to_charge_pct = self.min_mint_fee;
309-
// let lp_fee_to_charge_pct = self.get_mint_redeem_fee(now, false)?;
309+
let mut lp_burn_amount = lp_to_burn;
310+
if dlp_total_supply.saturating_sub(lp_burn_amount) <= QUOTE_PRECISION_U64 {
311+
lp_burn_amount = dlp_total_supply.saturating_sub(QUOTE_PRECISION_U64);
312+
}
313+
310314
let lp_fee_to_charge = lp_burn_amount
311315
.cast::<i128>()?
312316
.safe_mul(lp_fee_to_charge_pct.cast::<i128>()?)?
@@ -676,16 +680,26 @@ impl LPPool {
676680
.cast::<i64>()?;
677681
crypto_delta = crypto_delta.safe_add(constituent_target_notional.cast()?)?;
678682
}
679-
aum = aum.safe_add(constituent_aum)?;
683+
aum = aum.saturating_add(constituent_aum);
680684
}
681685

682686
msg!("Aum before quote owed from lp pool: {}", aum);
683687

688+
let mut total_quote_owed: i128 = 0;
684689
for cache_datum in amm_cache.iter() {
685-
aum = aum.saturating_sub(cache_datum.quote_owed_from_lp_pool as i128);
690+
total_quote_owed =
691+
total_quote_owed.safe_add(cache_datum.quote_owed_from_lp_pool as i128)?;
692+
}
693+
694+
if total_quote_owed > 0 {
695+
aum = aum
696+
.saturating_sub(total_quote_owed as i128)
697+
.max(QUOTE_PRECISION_I128);
698+
} else if total_quote_owed < 0 {
699+
aum = aum.saturating_add((-total_quote_owed) as i128);
686700
}
687701

688-
let aum_u128 = aum.max(0i128).cast::<u128>()?;
702+
let aum_u128 = aum.max(0).cast::<u128>()?;
689703
self.last_aum = aum_u128;
690704
self.last_aum_slot = slot;
691705

programs/drift/src/state/lp_pool/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ mod tests {
432432
let mut constituents_indexes_and_decimals_and_prices =
433433
vec![ConstituentIndexAndDecimalAndPrice {
434434
constituent_index: 1,
435-
decimals: 6,
435+
decimals: 9,
436436
price: 142_000_000,
437437
}];
438438

@@ -459,7 +459,7 @@ mod tests {
459459
.unwrap();
460460

461461
assert_eq!(target_zc_mut.len(), 1);
462-
assert_eq!(target_zc_mut.get(0).target_base, -1_000); // despite no aum, desire to reach target
462+
assert_eq!(target_zc_mut.get(0).target_base, -1_000_000); // despite no aum, desire to reach target
463463
assert_eq!(target_zc_mut.get(0).last_slot, now_ts);
464464
}
465465
}

tests/lpPool.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,7 @@ describe('LP Pool', () => {
685685
lpPoolKey
686686
)) as LPPoolAccount;
687687

688+
console.log(lpPool.lastAum.toString());
688689
assert(lpPool.lastAum.eq(new BN(1000).mul(QUOTE_PRECISION)));
689690

690691
// Should fail if we dont pass in the second constituent
@@ -1218,35 +1219,36 @@ describe('LP Pool', () => {
12181219
constituentVaultPublicKey
12191220
);
12201221

1221-
// Should have written fee pool amount owed to the amm cache and new constituent usdc balane should be 0
1222+
// Should have written fee pool amount owed to the amm cache and new constituent usdc balane should just be the quote precision to leave aum > 0
12221223
ammCache = (await adminClient.program.account.ammCache.fetch(
12231224
getAmmCachePublicKey(program.programId)
12241225
)) as AmmCache;
12251226
// No more usdc left in the constituent vault
1226-
assert(constituent.vaultTokenBalance.eq(ZERO));
1227-
assert(new BN(constituentVault.amount.toString()).eq(ZERO));
1227+
assert(constituent.vaultTokenBalance.eq(QUOTE_PRECISION));
1228+
assert(new BN(constituentVault.amount.toString()).eq(QUOTE_PRECISION));
12281229

12291230
// Should have recorded the amount left over to the amm cache and increased the amount in the fee pool
12301231
assert(
12311232
ammCache.cache[0].lastFeePoolTokenAmount.eq(
1232-
new BN(constituentUSDCBalanceBefore.toString())
1233+
new BN(constituentUSDCBalanceBefore.toString()).sub(QUOTE_PRECISION)
12331234
)
12341235
);
12351236
expect(
12361237
ammCache.cache[0].quoteOwedFromLpPool.toNumber()
12371238
).to.be.approximately(
12381239
expectedTransferAmount
12391240
.sub(new BN(constituentUSDCBalanceBefore.toString()))
1241+
.add(QUOTE_PRECISION)
12401242
.toNumber(),
12411243
1
12421244
);
12431245
assert(
12441246
adminClient
12451247
.getPerpMarketAccount(0)
12461248
.amm.feePool.scaledBalance.eq(
1247-
new BN(constituentUSDCBalanceBefore.toString()).mul(
1248-
SPOT_MARKET_BALANCE_PRECISION.div(QUOTE_PRECISION)
1249-
)
1249+
new BN(constituentUSDCBalanceBefore.toString())
1250+
.sub(QUOTE_PRECISION)
1251+
.mul(SPOT_MARKET_BALANCE_PRECISION.div(QUOTE_PRECISION))
12501252
)
12511253
);
12521254

@@ -1255,7 +1257,7 @@ describe('LP Pool', () => {
12551257
lpPool = (await adminClient.program.account.lpPool.fetch(
12561258
lpPoolKey
12571259
)) as LPPoolAccount;
1258-
assert(lpPool.lastAum.eq(ZERO));
1260+
assert(lpPool.lastAum.eq(QUOTE_PRECISION));
12591261
});
12601262

12611263
it('perp market will not transfer with the constituent vault if it is owed from dlp', async () => {
@@ -1303,8 +1305,8 @@ describe('LP Pool', () => {
13031305
expect(
13041306
ammCache.cache[0].quoteOwedFromLpPool.toNumber()
13051307
).to.be.approximately(owedAmount.divn(2).toNumber(), 1);
1306-
assert(constituent.vaultTokenBalance.eq(ZERO));
1307-
assert(lpPool.lastAum.eq(ZERO));
1308+
assert(constituent.vaultTokenBalance.eq(QUOTE_PRECISION));
1309+
assert(lpPool.lastAum.eq(QUOTE_PRECISION));
13081310

13091311
// Deposit here to DLP to make sure aum calc work with perp market debt
13101312
await overWriteMintAccount(
@@ -1368,7 +1370,7 @@ describe('LP Pool', () => {
13681370
const balanceBefore = constituent.vaultTokenBalance;
13691371
const owedAmount = ammCache.cache[0].quoteOwedFromLpPool;
13701372

1371-
// Give the perp market half of its owed amount
1373+
// Give the perp market double of its owed amount
13721374
const perpMarket = adminClient.getPerpMarketAccount(0);
13731375
perpMarket.amm.feePool.scaledBalance =
13741376
perpMarket.amm.feePool.scaledBalance.add(

tests/lpPoolCUs.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,10 @@ describe('LP Pool', () => {
103103
let bankrunContextWrapper: BankrunContextWrapper;
104104
let bulkAccountLoader: TestBulkAccountLoader;
105105

106-
let userLpTokenAccount: PublicKey;
106+
let _userLpTokenAccount: PublicKey;
107107
let adminClient: TestClient;
108108
let usdcMint: Keypair;
109109
let spotTokenMint: Keypair;
110-
let spotMarketOracle: PublicKey;
111110
let spotMarketOracle2: PublicKey;
112111

113112
let adminKeypair: Keypair;
@@ -166,7 +165,6 @@ describe('LP Pool', () => {
166165

167166
usdcMint = await mockUSDCMint(bankrunContextWrapper);
168167
spotTokenMint = await mockUSDCMint(bankrunContextWrapper);
169-
spotMarketOracle = await mockOracleNoProgram(bankrunContextWrapper, 200);
170168
spotMarketOracle2 = await mockOracleNoProgram(bankrunContextWrapper, 200);
171169

172170
const keypair = new Keypair();
@@ -267,7 +265,7 @@ describe('LP Pool', () => {
267265
lpPoolKey
268266
)) as LPPoolAccount;
269267

270-
userLpTokenAccount = await mockAtaTokenAccountForMint(
268+
_userLpTokenAccount = await mockAtaTokenAccountForMint(
271269
bankrunContextWrapper,
272270
lpPool.mint,
273271
new BN(0),

0 commit comments

Comments
 (0)