Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions programs/drift/src/instructions/keeper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3114,10 +3114,11 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
)?;

// Send all revenues to the perp market fee pool
let precision_increase = SPOT_BALANCE_PRECISION.safe_div(QUOTE_PRECISION)?;
perp_market
.amm
.fee_pool
.increase_balance(amount_to_send as u128)?;
.increase_balance((amount_to_send as u128).safe_mul(precision_increase)?)?;

lp_pool.cumulative_usdc_sent_to_perp_markets = lp_pool
.cumulative_usdc_sent_to_perp_markets
Expand All @@ -3131,10 +3132,14 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
} else {
let amount_to_send = if cached_info.quote_owed_from_lp > 0 {
if amount_to_send > cached_info.quote_owed_from_lp as u64 {
let new_amount_to_send =
amount_to_send.safe_sub(cached_info.quote_owed_from_lp as u64)?;
cached_info.quote_owed_from_lp = 0;
amount_to_send - cached_info.quote_owed_from_lp as u64
new_amount_to_send
} else {
cached_info.quote_owed_from_lp -= amount_to_send as i64;
cached_info.quote_owed_from_lp = cached_info
.quote_owed_from_lp
.safe_sub(amount_to_send as i64)?;
0
}
} else {
Expand Down Expand Up @@ -3196,6 +3201,9 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
lp_pool.last_aum = lp_pool
.last_aum
.saturating_add(amount_to_send.cast::<u128>()?);
} else {
cached_info.last_fee_pool_token_amount = fee_pool_token_amount;
cached_info.last_net_pnl_pool_token_amount = net_pnl_pool_token_amount;
}
}

Expand Down
8 changes: 8 additions & 0 deletions sdk/src/adminClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,13 @@ export class AdminClient extends DriftClient {
sourceVault: PublicKey
): Promise<TransactionInstruction> {
const spotMarket = this.getQuoteSpotMarketAccount();
const remainingAccounts = [
{
pubkey: spotMarket.mint,
isWritable: false,
isSigner: false,
},
];

return await this.program.instruction.depositIntoPerpMarketFeePool(amount, {
accounts: {
Expand All @@ -1132,6 +1139,7 @@ export class AdminClient extends DriftClient {
spotMarketVault: spotMarket.vault,
tokenProgram: TOKEN_PROGRAM_ID,
},
remainingAccounts,
});
}

Expand Down
80 changes: 79 additions & 1 deletion tests/lpPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
SPOT_MARKET_BALANCE_PRECISION,
SpotBalanceType,
getTokenAmount,
TWO,
} from '../sdk/src';

import {
Expand Down Expand Up @@ -780,7 +781,7 @@ describe('LP Pool', () => {
);
});

it('will fail gracefully when trying to settle pnl from constituents to perp markets if not enough usdc in the constituent vault', async () => {
it('will settle gracefully when trying to settle pnl from constituents to perp markets if not enough usdc in the constituent vault', async () => {
let lpPool = (await adminClient.program.account.lpPool.fetch(
lpPoolKey
)) as LPPoolAccount;
Expand Down Expand Up @@ -876,8 +877,85 @@ describe('LP Pool', () => {
)
)
);
assert(
adminClient
.getPerpMarketAccount(0)
.amm.feePool.scaledBalance.eq(
new BN(constituentUSDCBalanceBefore.toString()).mul(
SPOT_MARKET_BALANCE_PRECISION.div(QUOTE_PRECISION)
)
)
);

// NAV should have gone down the max that is has
assert(lpPool.lastAum.eq(ZERO));
});

it('perp market will not transfer with the constituent vault if it is owed from dlp', async () => {
let ammCache = (await adminClient.program.account.ammCache.fetch(
getAmmCachePublicKey(program.programId)
)) as AmmCache;
const owedAmount = ammCache.cache[0].quoteOwedFromLp;

// Give the perp market half of its owed amount
const perpMarket = adminClient.getPerpMarketAccount(0);
perpMarket.amm.feePool.scaledBalance =
perpMarket.amm.feePool.scaledBalance.add(
owedAmount
.div(TWO)
.mul(SPOT_MARKET_BALANCE_PRECISION.div(QUOTE_PRECISION))
);
await overWritePerpMarket(
adminClient,
bankrunContextWrapper,
perpMarket.pubkey,
perpMarket
);

await adminClient.settlePerpToLpPool(encodeName(lpPoolName), [0, 1, 2]);

ammCache = (await adminClient.program.account.ammCache.fetch(
getAmmCachePublicKey(program.programId)
)) as AmmCache;
const constituent = (await adminClient.program.account.constituent.fetch(
getConstituentPublicKey(program.programId, lpPoolKey, 0)
)) as ConstituentAccount;

assert(ammCache.cache[0].quoteOwedFromLp.eq(owedAmount.divn(2)));
assert(constituent.tokenBalance.eq(ZERO));
});

it('perp market will transfer with the constituent vault if it should send more than its owed', async () => {
let ammCache = (await adminClient.program.account.ammCache.fetch(
getAmmCachePublicKey(program.programId)
)) as AmmCache;
const owedAmount = ammCache.cache[0].quoteOwedFromLp;

// Give the perp market half of its owed amount
const perpMarket = adminClient.getPerpMarketAccount(0);
perpMarket.amm.feePool.scaledBalance =
perpMarket.amm.feePool.scaledBalance.add(
owedAmount
.mul(TWO)
.mul(SPOT_MARKET_BALANCE_PRECISION.div(QUOTE_PRECISION))
);
await overWritePerpMarket(
adminClient,
bankrunContextWrapper,
perpMarket.pubkey,
perpMarket
);

await adminClient.settlePerpToLpPool(encodeName(lpPoolName), [0, 1, 2]);

ammCache = (await adminClient.program.account.ammCache.fetch(
getAmmCachePublicKey(program.programId)
)) as AmmCache;
const constituent = (await adminClient.program.account.constituent.fetch(
getConstituentPublicKey(program.programId, lpPoolKey, 0)
)) as ConstituentAccount;

assert(ammCache.cache[0].quoteOwedFromLp.eq(ZERO));
assert(constituent.tokenBalance.eq(owedAmount));
});
});