Skip to content

Commit e842635

Browse files
authored
Merge pull request #419 from drift-labs/master
userPnlSettler: settle negative pnl users if beyond threshold
2 parents 22169c3 + f03b957 commit e842635

File tree

5 files changed

+200
-97
lines changed

5 files changed

+200
-97
lines changed

build_all.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
cd drift-common/protocol/sdk && yarn && yarn build && cd ../../..
2+
cd drift-common/common-ts && yarn && yarn build && cd ../..
3+
yarn && yarn build

src/bots/userPnlSettler.ts

Lines changed: 70 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -392,65 +392,85 @@ export class UserPnlSettlerBot implements Bot {
392392
perpMarketAndOracleData[perpMarketIdx].marketAccount.pnlPool;
393393
let pnlToSettleWithUser = ZERO;
394394
let pnlPoolTookenAmount = ZERO;
395+
let settleUser = false;
395396
if (userUnsettledPnl.gt(ZERO)) {
396397
pnlPoolTookenAmount = getTokenAmount(
397398
pnlPool.scaledBalance,
398399
spotMarketAndOracleData[pnlPool.marketIndex].marketAccount,
399400
SpotBalanceType.DEPOSIT
400401
);
401402
pnlToSettleWithUser = BN.min(userUnsettledPnl, pnlPoolTookenAmount);
402-
}
403-
404-
if (pnlToSettleWithUser.gt(ZERO)) {
405-
const netUserPnl = calculateNetUserPnl(
406-
perpMarketAndOracleData[perpMarketIdx].marketAccount,
407-
perpMarketAndOracleData[perpMarketIdx].oraclePriceData
408-
);
409-
let maxPnlPoolExcess = ZERO;
410-
if (netUserPnl.lt(pnlPoolTookenAmount)) {
411-
maxPnlPoolExcess = pnlPoolTookenAmount.sub(
412-
BN.max(netUserPnl, ZERO)
403+
if (pnlToSettleWithUser.gt(ZERO)) {
404+
const netUserPnl = calculateNetUserPnl(
405+
perpMarketAndOracleData[perpMarketIdx].marketAccount,
406+
perpMarketAndOracleData[perpMarketIdx].oraclePriceData
413407
);
408+
let maxPnlPoolExcess = ZERO;
409+
if (netUserPnl.lt(pnlPoolTookenAmount)) {
410+
maxPnlPoolExcess = pnlPoolTookenAmount.sub(
411+
BN.max(netUserPnl, ZERO)
412+
);
413+
}
414+
415+
// we're only allowed to settle positive pnl if pnl pool is in excess
416+
if (maxPnlPoolExcess.lte(ZERO)) {
417+
logger.warn(
418+
`Want to settle positive PnL for user ${user
419+
.getUserAccountPublicKey()
420+
.toBase58()} in market ${perpMarketIdx}, but maxPnlPoolExcess is: (${convertToNumber(
421+
maxPnlPoolExcess,
422+
QUOTE_PRECISION
423+
)})`
424+
);
425+
continue;
426+
}
427+
428+
const netUserPnlImbalance = calculateNetUserPnlImbalance(
429+
perpMarketAndOracleData[perpMarketIdx].marketAccount,
430+
spotMarketAndOracleData[spotMarketIdx].marketAccount,
431+
perpMarketAndOracleData[perpMarketIdx].oraclePriceData
432+
);
433+
if (netUserPnlImbalance.gt(ZERO)) {
434+
logger.warn(
435+
`Want to settle positive PnL for user ${user
436+
.getUserAccountPublicKey()
437+
.toBase58()} in market ${perpMarketIdx}, protocol's AMM lacks excess PnL (${convertToNumber(
438+
netUserPnlImbalance,
439+
QUOTE_PRECISION
440+
)})`
441+
);
442+
continue;
443+
}
414444
}
415445

416-
// we're only allowed to settle positive pnl if pnl pool is in excess
417-
if (maxPnlPoolExcess.lte(ZERO)) {
446+
// only settle user pnl if they have enough collateral
447+
if (user.canBeLiquidated().canBeLiquidated) {
418448
logger.warn(
419-
`Want to settle positive PnL for user ${user
449+
`Want to settle negative PnL for user ${user
420450
.getUserAccountPublicKey()
421-
.toBase58()} in market ${perpMarketIdx}, but maxPnlPoolExcess is: (${convertToNumber(
422-
maxPnlPoolExcess,
423-
QUOTE_PRECISION
424-
)})`
451+
.toBase58()}, but they have insufficient collateral`
425452
);
426453
continue;
427454
}
428-
429-
const netUserPnlImbalance = calculateNetUserPnlImbalance(
430-
perpMarketAndOracleData[perpMarketIdx].marketAccount,
431-
spotMarketAndOracleData[spotMarketIdx].marketAccount,
432-
perpMarketAndOracleData[perpMarketIdx].oraclePriceData
433-
);
434-
if (netUserPnlImbalance.gt(ZERO)) {
435-
logger.warn(
436-
`Want to settle positive PnL for user ${user
455+
settleUser = true;
456+
} else {
457+
// only settle negative pnl if unsettled pnl is material
458+
if (userUnsettledPnl.abs().gte(this.minPnlToSettle.abs())) {
459+
logger.info(
460+
`Settling negative pnl for user ${user
437461
.getUserAccountPublicKey()
438-
.toBase58()} in market ${perpMarketIdx}, protocol's AMM lacks excess PnL (${convertToNumber(
439-
netUserPnlImbalance,
462+
.toBase58()} in market ${
463+
settleePosition.marketIndex
464+
} with unsettled pnl: ${convertToNumber(
465+
userUnsettledPnl,
440466
QUOTE_PRECISION
441-
)})`
467+
)}`
442468
);
443-
continue;
469+
settleUser = true;
444470
}
445471
}
446472

447-
// only settle user pnl if they have enough collateral
448-
if (user.canBeLiquidated().canBeLiquidated) {
449-
logger.warn(
450-
`Want to settle negative PnL for user ${user
451-
.getUserAccountPublicKey()
452-
.toBase58()}, but they have insufficient collateral`
453-
);
473+
if (!settleUser) {
454474
continue;
455475
}
456476

@@ -648,19 +668,16 @@ export class UserPnlSettlerBot implements Bot {
648668
[],
649669
this.driftClient.opts
650670
);
651-
logger.info(
652-
`Settle PNL tx sent in ${Date.now() - sendTxStart}ms, response: ${
653-
txSig.txSig
654-
}`
655-
);
671+
const sendTxDuration = Date.now() - sendTxStart;
656672
success = true;
657673

658674
this.logTxAndSlotForUsers(
659675
txSig,
660676
marketIndex,
661677
users.map(
662678
({ settleeUserAccountPublicKey }) => settleeUserAccountPublicKey
663-
)
679+
),
680+
sendTxDuration
664681
);
665682
}
666683
} catch (err) {
@@ -698,13 +715,18 @@ export class UserPnlSettlerBot implements Bot {
698715
private logTxAndSlotForUsers(
699716
txSigAndSlot: TxSigAndSlot,
700717
marketIndex: number,
701-
userAccountPublicKeys: Array<PublicKey>
718+
userAccountPublicKeys: Array<PublicKey>,
719+
sendTxDuration: number
702720
) {
703721
const txSig = txSigAndSlot.txSig;
722+
let userStr = '';
723+
let countUsers = 0;
704724
for (const userAccountPublicKey of userAccountPublicKeys) {
705-
logger.info(
706-
`Settled pnl user ${userAccountPublicKey.toBase58()} in market ${marketIndex} https://solana.fm/tx/${txSig}`
707-
);
725+
userStr += `${userAccountPublicKey.toBase58()}, `;
726+
countUsers++;
708727
}
728+
logger.info(
729+
`Settled pnl in market ${marketIndex}. For ${countUsers} users: ${userStr}. Took: ${sendTxDuration}ms. https://solana.fm/tx/${txSig}`
730+
);
709731
}
710732
}

src/experimental-bots/entrypoint.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -416,13 +416,18 @@ const runBot = async () => {
416416
});
417417
await userMap.subscribe();
418418

419-
const signedMsgMaker = new SwiftPlacer(driftClient, userMap, {
420-
rpcEndpoint: endpoint,
421-
commit: '',
422-
driftEnv: config.global.driftEnv!,
423-
driftPid: driftPublicKey.toBase58(),
424-
walletAuthority: wallet.publicKey.toBase58(),
425-
});
419+
const signedMsgMaker = new SwiftPlacer(
420+
driftClient,
421+
slotSubscriber,
422+
userMap,
423+
{
424+
rpcEndpoint: endpoint,
425+
commit: '',
426+
driftEnv: config.global.driftEnv!,
427+
driftPid: driftPublicKey.toBase58(),
428+
walletAuthority: wallet.publicKey.toBase58(),
429+
}
430+
);
426431
bots.push(signedMsgMaker);
427432
}
428433

0 commit comments

Comments
 (0)