@drift-labs/sdk 2.145.0 → 2.146.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +4 -0
- package/VERSION +1 -1
- package/lib/browser/accounts/grpcMultiUserAccountSubscriber.js +8 -1
- package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.d.ts +99 -7
- package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.js +435 -144
- package/lib/browser/adminClient.d.ts +5 -1
- package/lib/browser/adminClient.js +57 -23
- package/lib/browser/constants/numericConstants.d.ts +2 -0
- package/lib/browser/constants/numericConstants.js +5 -1
- package/lib/browser/constants/perpMarkets.js +0 -2
- package/lib/browser/decode/user.js +4 -0
- package/lib/browser/driftClient.d.ts +25 -10
- package/lib/browser/driftClient.js +238 -41
- package/lib/browser/driftClientConfig.d.ts +7 -2
- package/lib/browser/idl/drift.json +245 -22
- package/lib/browser/index.d.ts +4 -0
- package/lib/browser/index.js +9 -1
- package/lib/browser/marginCalculation.d.ts +86 -0
- package/lib/browser/marginCalculation.js +209 -0
- package/lib/browser/math/margin.d.ts +1 -1
- package/lib/browser/math/margin.js +8 -1
- package/lib/browser/math/position.d.ts +1 -0
- package/lib/browser/math/position.js +10 -2
- package/lib/browser/math/spotPosition.d.ts +1 -1
- package/lib/browser/math/spotPosition.js +3 -2
- package/lib/browser/math/superStake.d.ts +3 -2
- package/lib/browser/types.d.ts +13 -0
- package/lib/browser/types.js +12 -1
- package/lib/browser/user.d.ts +59 -11
- package/lib/browser/user.js +348 -43
- package/lib/node/accounts/grpcMultiUserAccountSubscriber.d.ts.map +1 -1
- package/lib/node/accounts/grpcMultiUserAccountSubscriber.js +8 -1
- package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts +99 -7
- package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts.map +1 -1
- package/lib/node/accounts/webSocketProgramAccountSubscriberV2.js +435 -144
- package/lib/node/adminClient.d.ts +5 -1
- package/lib/node/adminClient.d.ts.map +1 -1
- package/lib/node/adminClient.js +57 -23
- package/lib/node/constants/numericConstants.d.ts +2 -0
- package/lib/node/constants/numericConstants.d.ts.map +1 -1
- package/lib/node/constants/numericConstants.js +5 -1
- package/lib/node/constants/perpMarkets.d.ts.map +1 -1
- package/lib/node/constants/perpMarkets.js +0 -2
- package/lib/node/decode/user.d.ts.map +1 -1
- package/lib/node/decode/user.js +4 -0
- package/lib/node/driftClient.d.ts +25 -10
- package/lib/node/driftClient.d.ts.map +1 -1
- package/lib/node/driftClient.js +238 -41
- package/lib/node/driftClientConfig.d.ts +7 -2
- package/lib/node/driftClientConfig.d.ts.map +1 -1
- package/lib/node/idl/drift.json +245 -22
- package/lib/node/index.d.ts +4 -0
- package/lib/node/index.d.ts.map +1 -1
- package/lib/node/index.js +9 -1
- package/lib/node/marginCalculation.d.ts +87 -0
- package/lib/node/marginCalculation.d.ts.map +1 -0
- package/lib/node/marginCalculation.js +209 -0
- package/lib/node/math/margin.d.ts +1 -1
- package/lib/node/math/margin.d.ts.map +1 -1
- package/lib/node/math/margin.js +8 -1
- package/lib/node/math/position.d.ts +1 -0
- package/lib/node/math/position.d.ts.map +1 -1
- package/lib/node/math/position.js +10 -2
- package/lib/node/math/spotPosition.d.ts +1 -1
- package/lib/node/math/spotPosition.d.ts.map +1 -1
- package/lib/node/math/spotPosition.js +3 -2
- package/lib/node/math/superStake.d.ts +3 -2
- package/lib/node/math/superStake.d.ts.map +1 -1
- package/lib/node/types.d.ts +13 -0
- package/lib/node/types.d.ts.map +1 -1
- package/lib/node/types.js +12 -1
- package/lib/node/user.d.ts +59 -11
- package/lib/node/user.d.ts.map +1 -1
- package/lib/node/user.js +348 -43
- package/package.json +1 -1
- package/scripts/deposit-isolated-positions.ts +110 -0
- package/scripts/single-grpc-client-test.ts +71 -21
- package/scripts/withdraw-isolated-positions.ts +174 -0
- package/src/accounts/grpcMultiUserAccountSubscriber.ts +8 -1
- package/src/accounts/webSocketProgramAccountSubscriberV2.ts +566 -167
- package/src/adminClient.ts +74 -25
- package/src/constants/numericConstants.ts +5 -0
- package/src/constants/perpMarkets.ts +0 -3
- package/src/decode/user.ts +7 -1
- package/src/driftClient.ts +465 -52
- package/src/driftClientConfig.ts +15 -8
- package/src/idl/drift.json +246 -23
- package/src/index.ts +4 -0
- package/src/margin/README.md +143 -0
- package/src/marginCalculation.ts +306 -0
- package/src/math/margin.ts +13 -1
- package/src/math/position.ts +12 -2
- package/src/math/spotPosition.ts +6 -2
- package/src/types.ts +16 -0
- package/src/user.ts +623 -81
- package/tests/amm/test.ts +1 -1
- package/tests/dlob/helpers.ts +6 -3
- package/tests/user/getMarginCalculation.ts +405 -0
- package/tests/user/test.ts +0 -7
package/lib/browser/user.js
CHANGED
|
@@ -26,6 +26,7 @@ const tiers_1 = require("./math/tiers");
|
|
|
26
26
|
const strictOraclePrice_1 = require("./oracles/strictOraclePrice");
|
|
27
27
|
const fuel_1 = require("./math/fuel");
|
|
28
28
|
const grpcUserAccountSubscriber_1 = require("./accounts/grpcUserAccountSubscriber");
|
|
29
|
+
const marginCalculation_1 = require("./marginCalculation");
|
|
29
30
|
class User {
|
|
30
31
|
get isSubscribed() {
|
|
31
32
|
return this._isSubscribed && this.accountSubscriber.isSubscribed;
|
|
@@ -186,8 +187,19 @@ class User {
|
|
|
186
187
|
lastQuoteAssetAmountPerLp: numericConstants_1.ZERO,
|
|
187
188
|
perLpBase: 0,
|
|
188
189
|
maxMarginRatio: 0,
|
|
190
|
+
positionFlag: 0,
|
|
191
|
+
isolatedPositionScaledBalance: numericConstants_1.ZERO,
|
|
189
192
|
};
|
|
190
193
|
}
|
|
194
|
+
getIsolatePerpPositionTokenAmount(perpMarketIndex) {
|
|
195
|
+
const perpPosition = this.getPerpPosition(perpMarketIndex);
|
|
196
|
+
const perpMarket = this.driftClient.getPerpMarketAccount(perpMarketIndex);
|
|
197
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(perpMarket.quoteSpotMarketIndex);
|
|
198
|
+
if (perpPosition === undefined) {
|
|
199
|
+
return numericConstants_1.ZERO;
|
|
200
|
+
}
|
|
201
|
+
return (0, spotBalance_2.getTokenAmount)(perpPosition.isolatedPositionScaledBalance, spotMarket, types_2.SpotBalanceType.DEPOSIT);
|
|
202
|
+
}
|
|
191
203
|
getClonedPosition(position) {
|
|
192
204
|
const clonedPosition = Object.assign({}, position);
|
|
193
205
|
return clonedPosition;
|
|
@@ -286,36 +298,54 @@ class User {
|
|
|
286
298
|
* calculates Free Collateral = Total collateral - margin requirement
|
|
287
299
|
* @returns : Precision QUOTE_PRECISION
|
|
288
300
|
*/
|
|
289
|
-
getFreeCollateral(marginCategory = 'Initial', enterHighLeverageMode =
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
301
|
+
getFreeCollateral(marginCategory = 'Initial', enterHighLeverageMode = false, perpMarketIndex) {
|
|
302
|
+
const marginCalc = this.getMarginCalculation(marginCategory, {
|
|
303
|
+
enteringHighLeverage: enterHighLeverageMode,
|
|
304
|
+
});
|
|
305
|
+
if (perpMarketIndex !== undefined) {
|
|
306
|
+
return marginCalc.getIsolatedFreeCollateral(perpMarketIndex);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
return marginCalc.getCrossFreeCollateral();
|
|
310
|
+
}
|
|
296
311
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
312
|
+
getMarginRequirement(marginCategory, liquidationBuffer, strict, includeOpenOrders, enteringHighLeverage, perpMarketIndex) {
|
|
313
|
+
const marginCalc = this.getMarginCalculation(marginCategory, {
|
|
314
|
+
strict,
|
|
315
|
+
includeOpenOrders,
|
|
316
|
+
enteringHighLeverage,
|
|
317
|
+
liquidationBuffer,
|
|
318
|
+
});
|
|
319
|
+
// If perpMarketIndex is provided, compute only for that market index
|
|
320
|
+
if (perpMarketIndex !== undefined) {
|
|
321
|
+
const isolatedMarginCalculation = marginCalc.isolatedMarginCalculations.get(perpMarketIndex);
|
|
322
|
+
const { marginRequirement } = isolatedMarginCalculation;
|
|
323
|
+
return marginRequirement;
|
|
324
|
+
}
|
|
325
|
+
// Default: Cross margin requirement
|
|
326
|
+
// TODO: should we be using plus buffer sometimes?
|
|
327
|
+
return marginCalc.marginRequirement;
|
|
302
328
|
}
|
|
303
329
|
/**
|
|
304
330
|
* @returns The initial margin requirement in USDC. : QUOTE_PRECISION
|
|
305
331
|
*/
|
|
306
|
-
getInitialMarginRequirement(enterHighLeverageMode =
|
|
307
|
-
return this.getMarginRequirement('Initial', undefined,
|
|
332
|
+
getInitialMarginRequirement(enterHighLeverageMode = false, perpMarketIndex) {
|
|
333
|
+
return this.getMarginRequirement('Initial', undefined, false, undefined, enterHighLeverageMode, perpMarketIndex);
|
|
308
334
|
}
|
|
309
335
|
/**
|
|
310
336
|
* @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION
|
|
311
337
|
*/
|
|
312
|
-
getMaintenanceMarginRequirement(liquidationBuffer) {
|
|
313
|
-
return this.getMarginRequirement('Maintenance', liquidationBuffer
|
|
338
|
+
getMaintenanceMarginRequirement(liquidationBuffer, perpMarketIndex) {
|
|
339
|
+
return this.getMarginRequirement('Maintenance', liquidationBuffer, true, // strict default
|
|
340
|
+
true, // includeOpenOrders default
|
|
341
|
+
false, // enteringHighLeverage default
|
|
342
|
+
perpMarketIndex);
|
|
314
343
|
}
|
|
315
344
|
getActivePerpPositionsForUserAccount(userAccount) {
|
|
316
345
|
return userAccount.perpPositions.filter((pos) => !pos.baseAssetAmount.eq(numericConstants_1.ZERO) ||
|
|
317
346
|
!pos.quoteAssetAmount.eq(numericConstants_1.ZERO) ||
|
|
318
|
-
!(pos.openOrders == 0)
|
|
347
|
+
!(pos.openOrders == 0) ||
|
|
348
|
+
pos.isolatedPositionScaledBalance.gt(numericConstants_1.ZERO));
|
|
319
349
|
}
|
|
320
350
|
getActivePerpPositions() {
|
|
321
351
|
const userAccount = this.getUserAccount();
|
|
@@ -353,6 +383,8 @@ class User {
|
|
|
353
383
|
.filter((pos) => marketIndex !== undefined ? pos.marketIndex === marketIndex : true)
|
|
354
384
|
.reduce((unrealizedPnl, perpPosition) => {
|
|
355
385
|
const market = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
|
|
386
|
+
if (!market)
|
|
387
|
+
return unrealizedPnl;
|
|
356
388
|
const oraclePriceData = this.getMMOracleDataForPerpMarket(market.marketIndex);
|
|
357
389
|
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(market.quoteSpotMarketIndex);
|
|
358
390
|
const quoteOraclePriceData = this.getOracleDataForSpotMarket(market.quoteSpotMarketIndex);
|
|
@@ -553,8 +585,17 @@ class User {
|
|
|
553
585
|
* calculates TotalCollateral: collateral + unrealized pnl
|
|
554
586
|
* @returns : Precision QUOTE_PRECISION
|
|
555
587
|
*/
|
|
556
|
-
getTotalCollateral(marginCategory = 'Initial', strict = false, includeOpenOrders = true, liquidationBuffer) {
|
|
557
|
-
|
|
588
|
+
getTotalCollateral(marginCategory = 'Initial', strict = false, includeOpenOrders = true, liquidationBuffer, perpMarketIndex) {
|
|
589
|
+
const marginCalc = this.getMarginCalculation(marginCategory, {
|
|
590
|
+
strict,
|
|
591
|
+
includeOpenOrders,
|
|
592
|
+
liquidationBuffer,
|
|
593
|
+
});
|
|
594
|
+
if (perpMarketIndex !== undefined) {
|
|
595
|
+
return marginCalc.isolatedMarginCalculations.get(perpMarketIndex)
|
|
596
|
+
.totalCollateral;
|
|
597
|
+
}
|
|
598
|
+
return marginCalc.totalCollateral;
|
|
558
599
|
}
|
|
559
600
|
getLiquidationBuffer() {
|
|
560
601
|
// if user being liq'd, can continue to be liq'd until total collateral above the margin requirement plus buffer
|
|
@@ -568,12 +609,24 @@ class User {
|
|
|
568
609
|
* calculates User Health by comparing total collateral and maint. margin requirement
|
|
569
610
|
* @returns : number (value from [0, 100])
|
|
570
611
|
*/
|
|
571
|
-
getHealth() {
|
|
572
|
-
|
|
612
|
+
getHealth(perpMarketIndex) {
|
|
613
|
+
const marginCalc = this.getMarginCalculation('Maintenance');
|
|
614
|
+
if (this.isCrossMarginBeingLiquidated(marginCalc) && !perpMarketIndex) {
|
|
573
615
|
return 0;
|
|
574
616
|
}
|
|
575
|
-
|
|
576
|
-
|
|
617
|
+
let totalCollateral;
|
|
618
|
+
let maintenanceMarginReq;
|
|
619
|
+
if (perpMarketIndex) {
|
|
620
|
+
const isolatedMarginCalc = marginCalc.isolatedMarginCalculations.get(perpMarketIndex);
|
|
621
|
+
if (isolatedMarginCalc) {
|
|
622
|
+
totalCollateral = isolatedMarginCalc.totalCollateral;
|
|
623
|
+
maintenanceMarginReq = isolatedMarginCalc.marginRequirement;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
totalCollateral = marginCalc.totalCollateral;
|
|
628
|
+
maintenanceMarginReq = marginCalc.marginRequirement;
|
|
629
|
+
}
|
|
577
630
|
let health;
|
|
578
631
|
if (maintenanceMarginReq.eq(numericConstants_1.ZERO) && totalCollateral.gte(numericConstants_1.ZERO)) {
|
|
579
632
|
health = 100;
|
|
@@ -589,6 +642,8 @@ class User {
|
|
|
589
642
|
}
|
|
590
643
|
calculateWeightedPerpPositionLiability(perpPosition, marginCategory, liquidationBuffer, includeOpenOrders, strict = false, enteringHighLeverage = undefined) {
|
|
591
644
|
const market = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
|
|
645
|
+
if (!market)
|
|
646
|
+
return numericConstants_1.ZERO;
|
|
592
647
|
let valuationPrice = this.getOracleDataForPerpMarket(market.marketIndex).price;
|
|
593
648
|
if ((0, types_1.isVariant)(market.status, 'settlement')) {
|
|
594
649
|
valuationPrice = market.expiryPrice;
|
|
@@ -732,8 +787,8 @@ class User {
|
|
|
732
787
|
* calculates current user leverage which is (total liability size) / (net asset value)
|
|
733
788
|
* @returns : Precision TEN_THOUSAND
|
|
734
789
|
*/
|
|
735
|
-
getLeverage(includeOpenOrders = true) {
|
|
736
|
-
return this.calculateLeverageFromComponents(this.getLeverageComponents(includeOpenOrders));
|
|
790
|
+
getLeverage(includeOpenOrders = true, perpMarketIndex) {
|
|
791
|
+
return this.calculateLeverageFromComponents(this.getLeverageComponents(includeOpenOrders, undefined, perpMarketIndex));
|
|
737
792
|
}
|
|
738
793
|
calculateLeverageFromComponents({ perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue, }) {
|
|
739
794
|
const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
|
|
@@ -744,7 +799,24 @@ class User {
|
|
|
744
799
|
}
|
|
745
800
|
return totalLiabilityValue.mul(numericConstants_1.TEN_THOUSAND).div(netAssetValue);
|
|
746
801
|
}
|
|
747
|
-
getLeverageComponents(includeOpenOrders = true, marginCategory = undefined) {
|
|
802
|
+
getLeverageComponents(includeOpenOrders = true, marginCategory = undefined, perpMarketIndex) {
|
|
803
|
+
if (perpMarketIndex) {
|
|
804
|
+
const perpPosition = this.getPerpPositionOrEmpty(perpMarketIndex);
|
|
805
|
+
const perpLiability = this.calculateWeightedPerpPositionLiability(perpPosition, marginCategory, undefined, includeOpenOrders);
|
|
806
|
+
const perpMarket = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
|
|
807
|
+
const oraclePriceData = this.getOracleDataForPerpMarket(perpPosition.marketIndex);
|
|
808
|
+
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(perpMarket.quoteSpotMarketIndex);
|
|
809
|
+
const quoteOraclePriceData = this.getOracleDataForSpotMarket(perpMarket.quoteSpotMarketIndex);
|
|
810
|
+
const strictOracle = new strictOraclePrice_1.StrictOraclePrice(quoteOraclePriceData.price, quoteOraclePriceData.twap);
|
|
811
|
+
const positionUnrealizedPnl = (0, position_2.calculatePositionPNL)(perpMarket, perpPosition, true, oraclePriceData);
|
|
812
|
+
const spotAssetValue = (0, spotBalance_1.getStrictTokenValue)(perpPosition.isolatedPositionScaledBalance, quoteSpotMarket.decimals, strictOracle);
|
|
813
|
+
return {
|
|
814
|
+
perpLiabilityValue: perpLiability,
|
|
815
|
+
perpPnl: positionUnrealizedPnl,
|
|
816
|
+
spotAssetValue,
|
|
817
|
+
spotLiabilityValue: numericConstants_1.ZERO,
|
|
818
|
+
};
|
|
819
|
+
}
|
|
748
820
|
const perpLiability = this.getTotalPerpPositionLiability(marginCategory, undefined, includeOpenOrders);
|
|
749
821
|
const perpPnl = this.getUnrealizedPNL(true, undefined, marginCategory);
|
|
750
822
|
const { totalAssetValue: spotAssetValue, totalLiabilityValue: spotLiabilityValue, } = this.getSpotMarketAssetAndLiabilityValue(undefined, marginCategory, undefined, includeOpenOrders);
|
|
@@ -893,20 +965,78 @@ class User {
|
|
|
893
965
|
return netAssetValue.mul(numericConstants_1.TEN_THOUSAND).div(totalLiabilityValue);
|
|
894
966
|
}
|
|
895
967
|
canBeLiquidated() {
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
const
|
|
899
|
-
const
|
|
900
|
-
return
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
968
|
+
// Deprecated signature retained for backward compatibility in type only
|
|
969
|
+
// but implementation now delegates to the new Map-based API and returns cross margin status.
|
|
970
|
+
const map = this.getLiquidationStatuses();
|
|
971
|
+
const cross = map.get('cross');
|
|
972
|
+
return cross
|
|
973
|
+
? { ...cross, liquidationStatuses: map }
|
|
974
|
+
: {
|
|
975
|
+
canBeLiquidated: false,
|
|
976
|
+
marginRequirement: numericConstants_1.ZERO,
|
|
977
|
+
totalCollateral: numericConstants_1.ZERO,
|
|
978
|
+
liquidationStatuses: map,
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* New API: Returns liquidation status for cross and each isolated perp position.
|
|
983
|
+
* Map keys:
|
|
984
|
+
* - 'cross' for cross margin
|
|
985
|
+
* - marketIndex (number) for each isolated perp position
|
|
986
|
+
*/
|
|
987
|
+
getLiquidationStatuses(marginCalc) {
|
|
988
|
+
// If not provided, use buffer-aware calc for canBeLiquidated checks
|
|
989
|
+
if (!marginCalc) {
|
|
990
|
+
const liquidationBuffer = this.getLiquidationBuffer();
|
|
991
|
+
marginCalc = this.getMarginCalculation('Maintenance', {
|
|
992
|
+
liquidationBuffer,
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
const result = new Map();
|
|
996
|
+
// Cross margin status
|
|
997
|
+
const crossTotalCollateral = marginCalc.totalCollateral;
|
|
998
|
+
const crossMarginRequirement = marginCalc.marginRequirement;
|
|
999
|
+
result.set('cross', {
|
|
1000
|
+
canBeLiquidated: crossTotalCollateral.lt(crossMarginRequirement),
|
|
1001
|
+
marginRequirement: crossMarginRequirement,
|
|
1002
|
+
totalCollateral: crossTotalCollateral,
|
|
1003
|
+
});
|
|
1004
|
+
// Isolated positions status
|
|
1005
|
+
for (const [marketIndex, isoCalc,] of marginCalc.isolatedMarginCalculations) {
|
|
1006
|
+
const isoTotalCollateral = isoCalc.totalCollateral;
|
|
1007
|
+
const isoMarginRequirement = isoCalc.marginRequirement;
|
|
1008
|
+
result.set(marketIndex, {
|
|
1009
|
+
canBeLiquidated: isoTotalCollateral.lt(isoMarginRequirement),
|
|
1010
|
+
marginRequirement: isoMarginRequirement,
|
|
1011
|
+
totalCollateral: isoTotalCollateral,
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
return result;
|
|
905
1015
|
}
|
|
906
|
-
isBeingLiquidated() {
|
|
907
|
-
|
|
1016
|
+
isBeingLiquidated(marginCalc) {
|
|
1017
|
+
// Consider on-chain flags OR computed margin status (cross or any isolated)
|
|
1018
|
+
const hasOnChainFlag = (this.getUserAccount().status &
|
|
908
1019
|
(types_1.UserStatus.BEING_LIQUIDATED | types_1.UserStatus.BANKRUPT)) >
|
|
909
|
-
0
|
|
1020
|
+
0;
|
|
1021
|
+
const calc = marginCalc !== null && marginCalc !== void 0 ? marginCalc : this.getMarginCalculation('Maintenance');
|
|
1022
|
+
return (hasOnChainFlag ||
|
|
1023
|
+
this.isCrossMarginBeingLiquidated(calc) ||
|
|
1024
|
+
this.isIsolatedMarginBeingLiquidated(calc));
|
|
1025
|
+
}
|
|
1026
|
+
/** Returns true if cross margin is currently below maintenance requirement (no buffer). */
|
|
1027
|
+
isCrossMarginBeingLiquidated(marginCalc) {
|
|
1028
|
+
const calc = marginCalc !== null && marginCalc !== void 0 ? marginCalc : this.getMarginCalculation('Maintenance');
|
|
1029
|
+
return calc.totalCollateral.lt(calc.marginRequirement);
|
|
1030
|
+
}
|
|
1031
|
+
/** Returns true if any isolated perp position is currently below its maintenance requirement (no buffer). */
|
|
1032
|
+
isIsolatedMarginBeingLiquidated(marginCalc) {
|
|
1033
|
+
const calc = marginCalc !== null && marginCalc !== void 0 ? marginCalc : this.getMarginCalculation('Maintenance');
|
|
1034
|
+
for (const [, isoCalc] of calc.isolatedMarginCalculations) {
|
|
1035
|
+
if (isoCalc.totalCollateral.lt(isoCalc.marginRequirement)) {
|
|
1036
|
+
return true;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return false;
|
|
910
1040
|
}
|
|
911
1041
|
hasStatus(status) {
|
|
912
1042
|
return (this.getUserAccount().status & status) > 0;
|
|
@@ -997,14 +1127,36 @@ class User {
|
|
|
997
1127
|
* @param offsetCollateral // allows calculating the liquidation price after this offset collateral is added to the user's account (e.g. : what will the liquidation price be for this position AFTER I deposit $x worth of collateral)
|
|
998
1128
|
* @returns Precision : PRICE_PRECISION
|
|
999
1129
|
*/
|
|
1000
|
-
liquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO, estimatedEntryPrice = numericConstants_1.ZERO, marginCategory = 'Maintenance', includeOpenOrders = false, offsetCollateral = numericConstants_1.ZERO, enteringHighLeverage =
|
|
1001
|
-
const
|
|
1002
|
-
const marginRequirement = this.getMarginRequirement(marginCategory, undefined, false, includeOpenOrders, enteringHighLeverage);
|
|
1003
|
-
let freeCollateral = anchor_1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(marginRequirement)).add(offsetCollateral);
|
|
1130
|
+
liquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO, estimatedEntryPrice = numericConstants_1.ZERO, marginCategory = 'Maintenance', includeOpenOrders = false, offsetCollateral = numericConstants_1.ZERO, enteringHighLeverage = false, marginType) {
|
|
1131
|
+
const market = this.driftClient.getPerpMarketAccount(marketIndex);
|
|
1004
1132
|
const oracle = this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
|
|
1005
1133
|
const oraclePrice = this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
|
|
1006
|
-
const market = this.driftClient.getPerpMarketAccount(marketIndex);
|
|
1007
1134
|
const currentPerpPosition = this.getPerpPositionOrEmpty(marketIndex);
|
|
1135
|
+
if (marginType === 'Isolated') {
|
|
1136
|
+
const marginCalculation = this.getMarginCalculation(marginCategory, {
|
|
1137
|
+
strict: false,
|
|
1138
|
+
includeOpenOrders,
|
|
1139
|
+
enteringHighLeverage,
|
|
1140
|
+
});
|
|
1141
|
+
const isolatedMarginCalculation = marginCalculation.isolatedMarginCalculations.get(marketIndex);
|
|
1142
|
+
const { totalCollateral, marginRequirement } = isolatedMarginCalculation;
|
|
1143
|
+
const freeCollateral = anchor_1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(marginRequirement)).add(offsetCollateral);
|
|
1144
|
+
const freeCollateralDelta = this.calculateFreeCollateralDeltaForPerp(market, currentPerpPosition, positionBaseSizeChange, oraclePrice, marginCategory, includeOpenOrders, enteringHighLeverage);
|
|
1145
|
+
if (freeCollateralDelta.eq(numericConstants_1.ZERO)) {
|
|
1146
|
+
return new anchor_1.BN(-1);
|
|
1147
|
+
}
|
|
1148
|
+
const liqPriceDelta = freeCollateral
|
|
1149
|
+
.mul(numericConstants_1.QUOTE_PRECISION)
|
|
1150
|
+
.div(freeCollateralDelta);
|
|
1151
|
+
const liqPrice = oraclePrice.sub(liqPriceDelta);
|
|
1152
|
+
if (liqPrice.lt(numericConstants_1.ZERO)) {
|
|
1153
|
+
return new anchor_1.BN(-1);
|
|
1154
|
+
}
|
|
1155
|
+
return liqPrice;
|
|
1156
|
+
}
|
|
1157
|
+
const totalCollateral = this.getTotalCollateral(marginCategory, false, includeOpenOrders);
|
|
1158
|
+
const marginRequirement = this.getMarginRequirement(marginCategory, undefined, false, includeOpenOrders, enteringHighLeverage);
|
|
1159
|
+
let freeCollateral = anchor_1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(marginRequirement)).add(offsetCollateral);
|
|
1008
1160
|
positionBaseSizeChange = (0, orders_1.standardizeBaseAssetAmount)(positionBaseSizeChange, market.amm.orderStepSize);
|
|
1009
1161
|
const freeCollateralChangeFromNewPosition = this.calculateEntriesEffectOnFreeCollateral(market, oraclePrice, currentPerpPosition, positionBaseSizeChange, estimatedEntryPrice, includeOpenOrders, enteringHighLeverage);
|
|
1010
1162
|
freeCollateral = freeCollateral.add(freeCollateralChangeFromNewPosition);
|
|
@@ -2002,5 +2154,158 @@ class User {
|
|
|
2002
2154
|
activeSpotPositions: activeSpotMarkets,
|
|
2003
2155
|
};
|
|
2004
2156
|
}
|
|
2157
|
+
/**
|
|
2158
|
+
* Compute a consolidated margin snapshot once, without caching.
|
|
2159
|
+
* Consumers can use this to avoid duplicating work across separate calls.
|
|
2160
|
+
*/
|
|
2161
|
+
// TODO: need another param to tell it give it back leverage compnents
|
|
2162
|
+
// TODO: change get leverage functions need to pull the right values from
|
|
2163
|
+
getMarginCalculation(marginCategory = 'Initial', opts) {
|
|
2164
|
+
var _a, _b, _c;
|
|
2165
|
+
const strict = (_a = opts === null || opts === void 0 ? void 0 : opts.strict) !== null && _a !== void 0 ? _a : false;
|
|
2166
|
+
const enteringHighLeverage = (_b = opts === null || opts === void 0 ? void 0 : opts.enteringHighLeverage) !== null && _b !== void 0 ? _b : false;
|
|
2167
|
+
const includeOpenOrders = (_c = opts === null || opts === void 0 ? void 0 : opts.includeOpenOrders) !== null && _c !== void 0 ? _c : true; // TODO: remove this ??
|
|
2168
|
+
const marginBuffer = opts === null || opts === void 0 ? void 0 : opts.liquidationBuffer; // treat as MARGIN_BUFFER ratio if provided
|
|
2169
|
+
const marginRatioOverride = opts === null || opts === void 0 ? void 0 : opts.marginRatioOverride;
|
|
2170
|
+
// Equivalent to on-chain user_custom_margin_ratio
|
|
2171
|
+
let userCustomMarginRatio = marginCategory === 'Initial' ? this.getUserAccount().maxMarginRatio : 0;
|
|
2172
|
+
if (marginRatioOverride !== undefined) {
|
|
2173
|
+
userCustomMarginRatio = Math.max(userCustomMarginRatio, marginRatioOverride);
|
|
2174
|
+
}
|
|
2175
|
+
// Initialize calc via JS mirror of Rust MarginCalculation
|
|
2176
|
+
const ctx = marginCalculation_1.MarginContext.standard(marginCategory)
|
|
2177
|
+
.strictMode(strict)
|
|
2178
|
+
.setMarginBuffer(marginBuffer)
|
|
2179
|
+
.setMarginRatioOverride(userCustomMarginRatio);
|
|
2180
|
+
const calc = new marginCalculation_1.MarginCalculation(ctx);
|
|
2181
|
+
// SPOT POSITIONS
|
|
2182
|
+
// TODO: include open orders in the worst-case simulation in the same way on both spot and perp positions
|
|
2183
|
+
for (const spotPosition of this.getUserAccount().spotPositions) {
|
|
2184
|
+
if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition))
|
|
2185
|
+
continue;
|
|
2186
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
2187
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
|
|
2188
|
+
const twap5 = strict
|
|
2189
|
+
? (0, oracles_1.calculateLiveOracleTwap)(spotMarket.historicalOracleData, oraclePriceData, new anchor_1.BN(Math.floor(Date.now() / 1000)), numericConstants_1.FIVE_MINUTE)
|
|
2190
|
+
: undefined;
|
|
2191
|
+
const strictOracle = new strictOraclePrice_1.StrictOraclePrice(oraclePriceData.price, twap5);
|
|
2192
|
+
if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
|
|
2193
|
+
const tokenAmount = (0, spotBalance_1.getSignedTokenAmount)((0, spotBalance_2.getTokenAmount)(spotPosition.scaledBalance, spotMarket, spotPosition.balanceType), spotPosition.balanceType);
|
|
2194
|
+
if ((0, types_1.isVariant)(spotPosition.balanceType, 'deposit')) {
|
|
2195
|
+
// add deposit value to total collateral
|
|
2196
|
+
const tokenValue = (0, spotBalance_1.getStrictTokenValue)(tokenAmount, spotMarket.decimals, strictOracle);
|
|
2197
|
+
calc.addCrossMarginTotalCollateral(tokenValue);
|
|
2198
|
+
}
|
|
2199
|
+
else {
|
|
2200
|
+
// borrow on quote contributes to margin requirement
|
|
2201
|
+
const tokenValueAbs = (0, spotBalance_1.getStrictTokenValue)(tokenAmount, spotMarket.decimals, strictOracle).abs();
|
|
2202
|
+
calc.addCrossMarginRequirement(tokenValueAbs, tokenValueAbs);
|
|
2203
|
+
calc.addSpotLiability();
|
|
2204
|
+
}
|
|
2205
|
+
continue;
|
|
2206
|
+
}
|
|
2207
|
+
// Non-quote spot: worst-case simulation
|
|
2208
|
+
const { tokenAmount: worstCaseTokenAmount, ordersValue: worstCaseOrdersValue, tokenValue: worstCaseTokenValue, weightedTokenValue: worstCaseWeightedTokenValue, } = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarket, strictOracle, marginCategory, userCustomMarginRatio, includeOpenOrders);
|
|
2209
|
+
// open order IM
|
|
2210
|
+
calc.addCrossMarginRequirement(new anchor_1.BN(spotPosition.openOrders).mul(numericConstants_1.OPEN_ORDER_MARGIN_REQUIREMENT), numericConstants_1.ZERO);
|
|
2211
|
+
if (worstCaseTokenAmount.gt(numericConstants_1.ZERO)) {
|
|
2212
|
+
// asset side increases total collateral (weighted)
|
|
2213
|
+
calc.addCrossMarginTotalCollateral(worstCaseWeightedTokenValue);
|
|
2214
|
+
}
|
|
2215
|
+
else if (worstCaseTokenAmount.lt(numericConstants_1.ZERO)) {
|
|
2216
|
+
// liability side increases margin requirement (weighted >= abs(token_value))
|
|
2217
|
+
const liabilityWeighted = worstCaseWeightedTokenValue.abs();
|
|
2218
|
+
calc.addCrossMarginRequirement(liabilityWeighted, worstCaseTokenValue.abs());
|
|
2219
|
+
calc.addSpotLiability();
|
|
2220
|
+
calc.addSpotLiabilityValue(worstCaseTokenValue.abs());
|
|
2221
|
+
}
|
|
2222
|
+
else if (spotPosition.openOrders !== 0) {
|
|
2223
|
+
calc.addSpotLiability();
|
|
2224
|
+
calc.addSpotLiabilityValue(worstCaseTokenValue.abs());
|
|
2225
|
+
}
|
|
2226
|
+
// orders value contributes to collateral or requirement
|
|
2227
|
+
if (worstCaseOrdersValue.gt(numericConstants_1.ZERO)) {
|
|
2228
|
+
calc.addCrossMarginTotalCollateral(worstCaseOrdersValue);
|
|
2229
|
+
}
|
|
2230
|
+
else if (worstCaseOrdersValue.lt(numericConstants_1.ZERO)) {
|
|
2231
|
+
const absVal = worstCaseOrdersValue.abs();
|
|
2232
|
+
calc.addCrossMarginRequirement(absVal, absVal);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
// PERP POSITIONS
|
|
2236
|
+
for (const marketPosition of this.getActivePerpPositions()) {
|
|
2237
|
+
const market = this.driftClient.getPerpMarketAccount(marketPosition.marketIndex);
|
|
2238
|
+
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(market.quoteSpotMarketIndex);
|
|
2239
|
+
const quoteOraclePriceData = this.getOracleDataForSpotMarket(market.quoteSpotMarketIndex);
|
|
2240
|
+
const oraclePriceData = this.getOracleDataForPerpMarket(market.marketIndex);
|
|
2241
|
+
// Worst-case perp liability and weighted pnl
|
|
2242
|
+
const { worstCaseBaseAssetAmount, worstCaseLiabilityValue } = (0, margin_1.calculateWorstCasePerpLiabilityValue)(marketPosition, market, oraclePriceData.price, includeOpenOrders);
|
|
2243
|
+
// margin ratio for this perp
|
|
2244
|
+
const customMarginRatio = Math.max(this.getUserAccount().maxMarginRatio, marketPosition.maxMarginRatio);
|
|
2245
|
+
let marginRatio = new anchor_1.BN((0, market_1.calculateMarketMarginRatio)(market, worstCaseBaseAssetAmount.abs(), marginCategory, customMarginRatio, this.isHighLeverageMode(marginCategory) || enteringHighLeverage));
|
|
2246
|
+
if ((0, types_1.isVariant)(market.status, 'settlement')) {
|
|
2247
|
+
marginRatio = numericConstants_1.ZERO;
|
|
2248
|
+
}
|
|
2249
|
+
// convert liability to quote value and apply margin ratio
|
|
2250
|
+
const quotePrice = strict
|
|
2251
|
+
? anchor_1.BN.max(quoteOraclePriceData.price, quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min)
|
|
2252
|
+
: quoteOraclePriceData.price;
|
|
2253
|
+
let perpMarginRequirement = worstCaseLiabilityValue
|
|
2254
|
+
.mul(quotePrice)
|
|
2255
|
+
.div(numericConstants_1.PRICE_PRECISION)
|
|
2256
|
+
.mul(marginRatio)
|
|
2257
|
+
.div(numericConstants_1.MARGIN_PRECISION);
|
|
2258
|
+
// add open orders IM
|
|
2259
|
+
perpMarginRequirement = perpMarginRequirement.add(new anchor_1.BN(marketPosition.openOrders).mul(numericConstants_1.OPEN_ORDER_MARGIN_REQUIREMENT));
|
|
2260
|
+
// weighted unrealized pnl
|
|
2261
|
+
let positionUnrealizedPnl = (0, position_2.calculatePositionPNL)(market, marketPosition, true, oraclePriceData);
|
|
2262
|
+
let pnlQuotePrice;
|
|
2263
|
+
if (strict && positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
|
|
2264
|
+
pnlQuotePrice = anchor_1.BN.min(quoteOraclePriceData.price, quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min);
|
|
2265
|
+
}
|
|
2266
|
+
else if (strict && positionUnrealizedPnl.lt(numericConstants_1.ZERO)) {
|
|
2267
|
+
pnlQuotePrice = anchor_1.BN.max(quoteOraclePriceData.price, quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min);
|
|
2268
|
+
}
|
|
2269
|
+
else {
|
|
2270
|
+
pnlQuotePrice = quoteOraclePriceData.price;
|
|
2271
|
+
}
|
|
2272
|
+
positionUnrealizedPnl = positionUnrealizedPnl
|
|
2273
|
+
.mul(pnlQuotePrice)
|
|
2274
|
+
.div(numericConstants_1.PRICE_PRECISION);
|
|
2275
|
+
// Add perp contribution: isolated vs cross
|
|
2276
|
+
const isIsolated = this.isPerpPositionIsolated(marketPosition);
|
|
2277
|
+
if (isIsolated) {
|
|
2278
|
+
// derive isolated quote deposit value, mirroring on-chain logic
|
|
2279
|
+
let depositValue = numericConstants_1.ZERO;
|
|
2280
|
+
if (marketPosition.isolatedPositionScaledBalance.gt(numericConstants_1.ZERO)) {
|
|
2281
|
+
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(market.quoteSpotMarketIndex);
|
|
2282
|
+
const quoteOraclePriceData = this.getOracleDataForSpotMarket(market.quoteSpotMarketIndex);
|
|
2283
|
+
const strictQuote = new strictOraclePrice_1.StrictOraclePrice(quoteOraclePriceData.price, strict
|
|
2284
|
+
? quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min
|
|
2285
|
+
: undefined);
|
|
2286
|
+
const quoteTokenAmount = (0, spotBalance_2.getTokenAmount)(marketPosition.isolatedPositionScaledBalance, quoteSpotMarket, types_2.SpotBalanceType.DEPOSIT);
|
|
2287
|
+
depositValue = (0, spotBalance_1.getStrictTokenValue)(quoteTokenAmount, quoteSpotMarket.decimals, strictQuote);
|
|
2288
|
+
}
|
|
2289
|
+
calc.addIsolatedMarginCalculation(market.marketIndex, depositValue, positionUnrealizedPnl, worstCaseLiabilityValue, perpMarginRequirement);
|
|
2290
|
+
calc.addPerpLiability();
|
|
2291
|
+
calc.addPerpLiabilityValue(worstCaseLiabilityValue);
|
|
2292
|
+
}
|
|
2293
|
+
else {
|
|
2294
|
+
// cross: add to global requirement and collateral
|
|
2295
|
+
calc.addCrossMarginRequirement(perpMarginRequirement, worstCaseLiabilityValue);
|
|
2296
|
+
calc.addCrossMarginTotalCollateral(positionUnrealizedPnl);
|
|
2297
|
+
const hasPerpLiability = !marketPosition.baseAssetAmount.eq(numericConstants_1.ZERO) ||
|
|
2298
|
+
marketPosition.quoteAssetAmount.lt(numericConstants_1.ZERO) ||
|
|
2299
|
+
marketPosition.openOrders !== 0;
|
|
2300
|
+
if (hasPerpLiability) {
|
|
2301
|
+
calc.addPerpLiability();
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
return calc;
|
|
2306
|
+
}
|
|
2307
|
+
isPerpPositionIsolated(perpPosition) {
|
|
2308
|
+
return (perpPosition.positionFlag & types_2.PositionFlag.IsolatedPosition) !== 0;
|
|
2309
|
+
}
|
|
2005
2310
|
}
|
|
2006
2311
|
exports.User = User;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grpcMultiUserAccountSubscriber.d.ts","sourceRoot":"","sources":["../../../src/accounts/grpcMultiUserAccountSubscriber.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,WAAW,EAEX,SAAS,EAET,qBAAqB,EACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAW,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE1E,qBAAa,8BAA8B;IAC1C,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,eAAe,CAA0C;IAEjE,OAAO,CAAC,QAAQ,CAA+C;IAC/D,OAAO,CAAC,SAAS,CAGb;IACJ,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,aAAa,CAAC,CAAgC;IACtD,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,sBAAsB,CAA4C;IAC1E,OAAO,CAAC,WAAW,CAAc;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB,OAAO,CAAC,mBAAmB,CAgBzB;gBAGD,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,EACxB,SAAS,CAAC,EAAE,SAAS,EACrB,eAAe,CAAC,EAAE,0BAA0B,CAAC,WAAW,CAAC;IAQ7C,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IA0ChC,OAAO,CAAC,oBAAoB,EAAE,SAAS,GAAG,qBAAqB;
|
|
1
|
+
{"version":3,"file":"grpcMultiUserAccountSubscriber.d.ts","sourceRoot":"","sources":["../../../src/accounts/grpcMultiUserAccountSubscriber.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,WAAW,EAEX,SAAS,EAET,qBAAqB,EACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAW,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE1E,qBAAa,8BAA8B;IAC1C,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,eAAe,CAA0C;IAEjE,OAAO,CAAC,QAAQ,CAA+C;IAC/D,OAAO,CAAC,SAAS,CAGb;IACJ,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,aAAa,CAAC,CAAgC;IACtD,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,sBAAsB,CAA4C;IAC1E,OAAO,CAAC,WAAW,CAAc;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB,OAAO,CAAC,mBAAmB,CAgBzB;gBAGD,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,EACxB,SAAS,CAAC,EAAE,SAAS,EACrB,eAAe,CAAC,EAAE,0BAA0B,CAAC,WAAW,CAAC;IAQ7C,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IA0ChC,OAAO,CAAC,oBAAoB,EAAE,SAAS,GAAG,qBAAqB;IAqGtE,OAAO,CAAC,aAAa;YAOP,YAAY;CAmE1B"}
|
|
@@ -76,7 +76,10 @@ class grpcMultiUserAccountSubscriber {
|
|
|
76
76
|
this.listeners.set(key, new Set());
|
|
77
77
|
this.keyToPk.set(key, userAccountPublicKey);
|
|
78
78
|
this.pendingAddKeys.add(key);
|
|
79
|
-
this.
|
|
79
|
+
if (this.isMultiSubscribed) {
|
|
80
|
+
// only schedule flush if already subscribed to the multi-subscriber
|
|
81
|
+
this.scheduleFlush();
|
|
82
|
+
}
|
|
80
83
|
}
|
|
81
84
|
};
|
|
82
85
|
const perUser = {
|
|
@@ -110,6 +113,10 @@ class grpcMultiUserAccountSubscriber {
|
|
|
110
113
|
this.updateData(account, 0);
|
|
111
114
|
},
|
|
112
115
|
updateData(userAccount, slot) {
|
|
116
|
+
const existingData = parent.userData.get(key);
|
|
117
|
+
if (existingData && existingData.slot > slot) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
113
120
|
parent.userData.set(key, { data: userAccount, slot });
|
|
114
121
|
perUserEmitter.emit('userAccountUpdate', userAccount);
|
|
115
122
|
perUserEmitter.emit('update');
|