@drift-labs/sdk 2.12.0 → 2.13.0-beta.1
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/lib/constants/numericConstants.d.ts +2 -0
- package/lib/constants/numericConstants.js +3 -1
- package/lib/driftClient.d.ts +9 -0
- package/lib/driftClient.js +49 -0
- package/lib/examples/loadDlob.d.ts +1 -0
- package/lib/examples/loadDlob.js +54 -0
- package/lib/idl/drift.json +1 -1
- package/lib/math/market.d.ts +5 -0
- package/lib/math/market.js +18 -1
- package/lib/math/oracles.d.ts +2 -2
- package/lib/math/oracles.js +20 -8
- package/lib/math/orders.js +1 -1
- package/lib/math/spotBalance.d.ts +5 -0
- package/lib/math/spotBalance.js +30 -3
- package/lib/math/trade.js +8 -2
- package/lib/user.d.ts +12 -6
- package/lib/user.js +124 -72
- package/package.json +1 -1
- package/src/constants/numericConstants.ts +2 -0
- package/src/driftClient.ts +64 -0
- package/src/examples/loadDlob.ts +82 -0
- package/src/idl/drift.json +1 -1
- package/src/math/market.ts +51 -0
- package/src/math/oracles.ts +30 -16
- package/src/math/orders.ts +2 -2
- package/src/math/spotBalance.ts +51 -3
- package/src/math/trade.ts +10 -2
- package/src/user.ts +267 -138
- package/tests/amm/test.ts +3 -2
- package/src/examples/makeTradeExample.js +0 -157
package/src/user.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
QUOTE_SPOT_MARKET_INDEX,
|
|
27
27
|
TEN,
|
|
28
28
|
OPEN_ORDER_MARGIN_REQUIREMENT,
|
|
29
|
+
FIVE_MINUTE,
|
|
29
30
|
} from './constants/numericConstants';
|
|
30
31
|
import {
|
|
31
32
|
UserAccountSubscriber,
|
|
@@ -40,11 +41,10 @@ import {
|
|
|
40
41
|
calculateUnrealizedAssetWeight,
|
|
41
42
|
calculateMarketMarginRatio,
|
|
42
43
|
PositionDirection,
|
|
43
|
-
calculateTradeSlippage,
|
|
44
44
|
BN,
|
|
45
45
|
SpotMarketAccount,
|
|
46
46
|
getTokenValue,
|
|
47
|
-
|
|
47
|
+
getStrictTokenValue,
|
|
48
48
|
} from '.';
|
|
49
49
|
import {
|
|
50
50
|
getTokenAmount,
|
|
@@ -66,6 +66,8 @@ import {
|
|
|
66
66
|
isSpotPositionAvailable,
|
|
67
67
|
} from './math/spotPosition';
|
|
68
68
|
|
|
69
|
+
import { calculateLiveOracleTwap } from './math/oracles';
|
|
70
|
+
|
|
69
71
|
export class User {
|
|
70
72
|
driftClient: DriftClient;
|
|
71
73
|
userAccountPublicKey: PublicKey;
|
|
@@ -388,7 +390,8 @@ export class User {
|
|
|
388
390
|
*/
|
|
389
391
|
public getMarginRequirement(
|
|
390
392
|
marginCategory: MarginCategory,
|
|
391
|
-
liquidationBuffer?: BN
|
|
393
|
+
liquidationBuffer?: BN,
|
|
394
|
+
strict = false
|
|
392
395
|
): BN {
|
|
393
396
|
return this.getTotalPerpPositionValue(
|
|
394
397
|
marginCategory,
|
|
@@ -399,7 +402,8 @@ export class User {
|
|
|
399
402
|
undefined,
|
|
400
403
|
marginCategory,
|
|
401
404
|
liquidationBuffer,
|
|
402
|
-
true
|
|
405
|
+
true,
|
|
406
|
+
strict
|
|
403
407
|
)
|
|
404
408
|
);
|
|
405
409
|
}
|
|
@@ -408,7 +412,7 @@ export class User {
|
|
|
408
412
|
* @returns The initial margin requirement in USDC. : QUOTE_PRECISION
|
|
409
413
|
*/
|
|
410
414
|
public getInitialMarginRequirement(): BN {
|
|
411
|
-
return this.getMarginRequirement('Initial');
|
|
415
|
+
return this.getMarginRequirement('Initial', undefined, true);
|
|
412
416
|
}
|
|
413
417
|
|
|
414
418
|
/**
|
|
@@ -500,8 +504,11 @@ export class User {
|
|
|
500
504
|
marketIndex?: number,
|
|
501
505
|
marginCategory?: MarginCategory,
|
|
502
506
|
liquidationBuffer?: BN,
|
|
503
|
-
includeOpenOrders?: boolean
|
|
507
|
+
includeOpenOrders?: boolean,
|
|
508
|
+
strict = false,
|
|
509
|
+
now?: BN
|
|
504
510
|
): BN {
|
|
511
|
+
now = now || new BN(new Date().getTime() / 1000);
|
|
505
512
|
return this.getUserAccount().spotPositions.reduce(
|
|
506
513
|
(totalLiabilityValue, spotPosition) => {
|
|
507
514
|
if (
|
|
@@ -557,7 +564,9 @@ export class User {
|
|
|
557
564
|
oraclePriceData,
|
|
558
565
|
spotMarketAccount,
|
|
559
566
|
marginCategory,
|
|
560
|
-
liquidationBuffer
|
|
567
|
+
liquidationBuffer,
|
|
568
|
+
strict,
|
|
569
|
+
now
|
|
561
570
|
);
|
|
562
571
|
return totalLiabilityValue.add(liabilityValue);
|
|
563
572
|
} else {
|
|
@@ -579,7 +588,9 @@ export class User {
|
|
|
579
588
|
oraclePriceData,
|
|
580
589
|
spotMarketAccount,
|
|
581
590
|
marginCategory,
|
|
582
|
-
liquidationBuffer
|
|
591
|
+
liquidationBuffer,
|
|
592
|
+
strict,
|
|
593
|
+
now
|
|
583
594
|
);
|
|
584
595
|
|
|
585
596
|
newTotalLiabilityValue =
|
|
@@ -619,13 +630,32 @@ export class User {
|
|
|
619
630
|
oraclePriceData: OraclePriceData,
|
|
620
631
|
spotMarketAccount: SpotMarketAccount,
|
|
621
632
|
marginCategory?: MarginCategory,
|
|
622
|
-
liquidationBuffer?: BN
|
|
633
|
+
liquidationBuffer?: BN,
|
|
634
|
+
strict = false,
|
|
635
|
+
now?: BN
|
|
623
636
|
): BN {
|
|
624
|
-
let liabilityValue =
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
637
|
+
let liabilityValue = null;
|
|
638
|
+
|
|
639
|
+
if (strict && spotMarketAccount.marketIndex != QUOTE_SPOT_MARKET_INDEX) {
|
|
640
|
+
const estOracleTwap = calculateLiveOracleTwap(
|
|
641
|
+
spotMarketAccount.historicalOracleData,
|
|
642
|
+
oraclePriceData,
|
|
643
|
+
now,
|
|
644
|
+
FIVE_MINUTE // 5MIN
|
|
645
|
+
);
|
|
646
|
+
liabilityValue = getStrictTokenValue(
|
|
647
|
+
tokenAmount,
|
|
648
|
+
spotMarketAccount.decimals,
|
|
649
|
+
oraclePriceData,
|
|
650
|
+
estOracleTwap
|
|
651
|
+
);
|
|
652
|
+
} else {
|
|
653
|
+
liabilityValue = getTokenValue(
|
|
654
|
+
tokenAmount,
|
|
655
|
+
spotMarketAccount.decimals,
|
|
656
|
+
oraclePriceData
|
|
657
|
+
);
|
|
658
|
+
}
|
|
629
659
|
|
|
630
660
|
if (marginCategory !== undefined) {
|
|
631
661
|
let weight = calculateLiabilityWeight(
|
|
@@ -653,8 +683,11 @@ export class User {
|
|
|
653
683
|
public getSpotMarketAssetValue(
|
|
654
684
|
marketIndex?: number,
|
|
655
685
|
marginCategory?: MarginCategory,
|
|
656
|
-
includeOpenOrders?: boolean
|
|
686
|
+
includeOpenOrders?: boolean,
|
|
687
|
+
strict = false,
|
|
688
|
+
now?: BN
|
|
657
689
|
): BN {
|
|
690
|
+
now = now || new BN(new Date().getTime() / 1000);
|
|
658
691
|
return this.getUserAccount().spotPositions.reduce(
|
|
659
692
|
(totalAssetValue, spotPosition) => {
|
|
660
693
|
if (
|
|
@@ -698,7 +731,9 @@ export class User {
|
|
|
698
731
|
tokenAmount,
|
|
699
732
|
oraclePriceData,
|
|
700
733
|
spotMarketAccount,
|
|
701
|
-
marginCategory
|
|
734
|
+
marginCategory,
|
|
735
|
+
strict,
|
|
736
|
+
now
|
|
702
737
|
);
|
|
703
738
|
return totalAssetValue.add(assetValue);
|
|
704
739
|
} else {
|
|
@@ -719,7 +754,9 @@ export class User {
|
|
|
719
754
|
worstCaseTokenAmount,
|
|
720
755
|
oraclePriceData,
|
|
721
756
|
spotMarketAccount,
|
|
722
|
-
marginCategory
|
|
757
|
+
marginCategory,
|
|
758
|
+
strict,
|
|
759
|
+
now
|
|
723
760
|
);
|
|
724
761
|
|
|
725
762
|
newTotalAssetValue = newTotalAssetValue.add(baseAssetValue);
|
|
@@ -741,13 +778,31 @@ export class User {
|
|
|
741
778
|
tokenAmount: BN,
|
|
742
779
|
oraclePriceData: OraclePriceData,
|
|
743
780
|
spotMarketAccount: SpotMarketAccount,
|
|
744
|
-
marginCategory?: MarginCategory
|
|
781
|
+
marginCategory?: MarginCategory,
|
|
782
|
+
strict = false,
|
|
783
|
+
now?: BN
|
|
745
784
|
): BN {
|
|
746
|
-
let assetValue =
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
785
|
+
let assetValue = null;
|
|
786
|
+
if (strict && spotMarketAccount.marketIndex != QUOTE_SPOT_MARKET_INDEX) {
|
|
787
|
+
const estOracleTwap = calculateLiveOracleTwap(
|
|
788
|
+
spotMarketAccount.historicalOracleData,
|
|
789
|
+
oraclePriceData,
|
|
790
|
+
now,
|
|
791
|
+
FIVE_MINUTE // 5MIN
|
|
792
|
+
);
|
|
793
|
+
assetValue = getStrictTokenValue(
|
|
794
|
+
tokenAmount,
|
|
795
|
+
spotMarketAccount.decimals,
|
|
796
|
+
oraclePriceData,
|
|
797
|
+
estOracleTwap
|
|
798
|
+
);
|
|
799
|
+
} else {
|
|
800
|
+
assetValue = getTokenValue(
|
|
801
|
+
tokenAmount,
|
|
802
|
+
spotMarketAccount.decimals,
|
|
803
|
+
oraclePriceData
|
|
804
|
+
);
|
|
805
|
+
}
|
|
751
806
|
|
|
752
807
|
if (marginCategory !== undefined) {
|
|
753
808
|
const weight = calculateAssetWeight(
|
|
@@ -805,17 +860,12 @@ export class User {
|
|
|
805
860
|
} else if (totalCollateral.lte(ZERO)) {
|
|
806
861
|
health = 0;
|
|
807
862
|
} else {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
const maintenanceRatio =
|
|
815
|
-
(maintenanceMarginReq.toNumber() / totalCollateral.toNumber()) *
|
|
816
|
-
marginRatio;
|
|
817
|
-
|
|
818
|
-
const healthP1 = Math.max(0, (marginRatio - maintenanceRatio) * 100) + 1;
|
|
863
|
+
const healthP1 =
|
|
864
|
+
Math.max(
|
|
865
|
+
0,
|
|
866
|
+
(1 - maintenanceMarginReq.toNumber() / totalCollateral.toNumber()) *
|
|
867
|
+
100
|
|
868
|
+
) + 1;
|
|
819
869
|
|
|
820
870
|
health = Math.min(1, Math.log(healthP1) / Math.log(100)) * 100;
|
|
821
871
|
if (health > 1) {
|
|
@@ -1017,19 +1067,32 @@ export class User {
|
|
|
1017
1067
|
}
|
|
1018
1068
|
|
|
1019
1069
|
/**
|
|
1020
|
-
* calculates current user leverage
|
|
1070
|
+
* calculates current user leverage which is (total liability size) / (net asset value)
|
|
1021
1071
|
* @returns : Precision TEN_THOUSAND
|
|
1022
1072
|
*/
|
|
1023
1073
|
public getLeverage(): BN {
|
|
1024
|
-
const
|
|
1074
|
+
const totalPerpLiability = this.getTotalPerpPositionValue(
|
|
1075
|
+
undefined,
|
|
1076
|
+
undefined,
|
|
1077
|
+
true
|
|
1078
|
+
);
|
|
1079
|
+
const totalSpotLiability = this.getSpotMarketLiabilityValue(
|
|
1080
|
+
undefined,
|
|
1081
|
+
undefined,
|
|
1082
|
+
undefined,
|
|
1083
|
+
true
|
|
1084
|
+
);
|
|
1085
|
+
|
|
1086
|
+
const totalLiabilityValue = totalPerpLiability.add(totalSpotLiability);
|
|
1025
1087
|
|
|
1026
1088
|
const totalAssetValue = this.getTotalAssetValue();
|
|
1089
|
+
const netAssetValue = totalAssetValue.sub(totalSpotLiability);
|
|
1027
1090
|
|
|
1028
|
-
if (
|
|
1091
|
+
if (netAssetValue.eq(ZERO)) {
|
|
1029
1092
|
return ZERO;
|
|
1030
1093
|
}
|
|
1031
1094
|
|
|
1032
|
-
return totalLiabilityValue.mul(TEN_THOUSAND).div(
|
|
1095
|
+
return totalLiabilityValue.mul(TEN_THOUSAND).div(netAssetValue);
|
|
1033
1096
|
}
|
|
1034
1097
|
|
|
1035
1098
|
getTotalLiabilityValue(marginCategory?: MarginCategory): BN {
|
|
@@ -1060,12 +1123,27 @@ export class User {
|
|
|
1060
1123
|
): BN {
|
|
1061
1124
|
const market = this.driftClient.getPerpMarketAccount(marketIndex);
|
|
1062
1125
|
|
|
1126
|
+
const totalPerpLiability = this.getTotalPerpPositionValue(
|
|
1127
|
+
undefined,
|
|
1128
|
+
undefined,
|
|
1129
|
+
true
|
|
1130
|
+
);
|
|
1131
|
+
const totalSpotLiability = this.getSpotMarketLiabilityValue(
|
|
1132
|
+
undefined,
|
|
1133
|
+
undefined,
|
|
1134
|
+
undefined,
|
|
1135
|
+
true
|
|
1136
|
+
);
|
|
1137
|
+
|
|
1063
1138
|
const totalAssetValue = this.getTotalAssetValue();
|
|
1064
|
-
|
|
1139
|
+
|
|
1140
|
+
const netAssetValue = totalAssetValue.sub(totalSpotLiability);
|
|
1141
|
+
|
|
1142
|
+
if (netAssetValue.eq(ZERO)) {
|
|
1065
1143
|
return ZERO;
|
|
1066
1144
|
}
|
|
1067
1145
|
|
|
1068
|
-
const totalLiabilityValue =
|
|
1146
|
+
const totalLiabilityValue = totalPerpLiability.add(totalSpotLiability);
|
|
1069
1147
|
|
|
1070
1148
|
const marginRatio = calculateMarketMarginRatio(
|
|
1071
1149
|
market,
|
|
@@ -1083,7 +1161,7 @@ export class User {
|
|
|
1083
1161
|
return totalLiabilityValue
|
|
1084
1162
|
.add(additionalLiabilities)
|
|
1085
1163
|
.mul(TEN_THOUSAND)
|
|
1086
|
-
.div(
|
|
1164
|
+
.div(netAssetValue);
|
|
1087
1165
|
}
|
|
1088
1166
|
|
|
1089
1167
|
/**
|
|
@@ -1091,15 +1169,28 @@ export class User {
|
|
|
1091
1169
|
* @returns : Precision TEN_THOUSAND
|
|
1092
1170
|
*/
|
|
1093
1171
|
public getMarginRatio(marginCategory?: MarginCategory): BN {
|
|
1094
|
-
const
|
|
1172
|
+
const totalPerpLiability = this.getTotalPerpPositionValue(
|
|
1173
|
+
undefined,
|
|
1174
|
+
undefined,
|
|
1175
|
+
true
|
|
1176
|
+
);
|
|
1177
|
+
const totalSpotLiability = this.getSpotMarketLiabilityValue(
|
|
1178
|
+
undefined,
|
|
1179
|
+
undefined,
|
|
1180
|
+
undefined,
|
|
1181
|
+
true
|
|
1182
|
+
);
|
|
1183
|
+
|
|
1184
|
+
const totalLiabilityValue = totalPerpLiability.add(totalSpotLiability);
|
|
1095
1185
|
|
|
1096
1186
|
if (totalLiabilityValue.eq(ZERO)) {
|
|
1097
1187
|
return BN_MAX;
|
|
1098
1188
|
}
|
|
1099
1189
|
|
|
1100
1190
|
const totalAssetValue = this.getTotalAssetValue(marginCategory);
|
|
1191
|
+
const netAssetValue = totalAssetValue.sub(totalSpotLiability);
|
|
1101
1192
|
|
|
1102
|
-
return
|
|
1193
|
+
return netAssetValue.mul(TEN_THOUSAND).div(totalLiabilityValue);
|
|
1103
1194
|
}
|
|
1104
1195
|
|
|
1105
1196
|
public canBeLiquidated(): boolean {
|
|
@@ -1246,7 +1337,7 @@ export class User {
|
|
|
1246
1337
|
for 10x long, BTC down $400:
|
|
1247
1338
|
3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
|
|
1248
1339
|
|
|
1249
|
-
const totalCollateral = this.getTotalCollateral();
|
|
1340
|
+
const totalCollateral = this.getTotalCollateral('Maintenance');
|
|
1250
1341
|
|
|
1251
1342
|
// calculate the total position value ignoring any value from the target market of the trade
|
|
1252
1343
|
const totalPositionValueExcludingTargetMarket =
|
|
@@ -1267,7 +1358,7 @@ export class User {
|
|
|
1267
1358
|
marketIndex: perpPosition.marketIndex,
|
|
1268
1359
|
baseAssetAmount: proposedBaseAssetAmount,
|
|
1269
1360
|
remainderBaseAssetAmount: 0,
|
|
1270
|
-
quoteAssetAmount:
|
|
1361
|
+
quoteAssetAmount: currentPerpPosition.quoteAssetAmount,
|
|
1271
1362
|
lastCumulativeFundingRate: ZERO,
|
|
1272
1363
|
quoteBreakEvenAmount: new BN(0),
|
|
1273
1364
|
quoteEntryAmount: new BN(0),
|
|
@@ -1296,37 +1387,27 @@ export class User {
|
|
|
1296
1387
|
const totalPositionValueAfterTrade =
|
|
1297
1388
|
totalPositionValueExcludingTargetMarket.add(proposedPerpPositionValue);
|
|
1298
1389
|
|
|
1299
|
-
const
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
)
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
)
|
|
1321
|
-
.div(MARGIN_PRECISION);
|
|
1322
|
-
totalMarginRequirement = totalMarginRequirement.add(
|
|
1323
|
-
marketMarginRequirement
|
|
1324
|
-
);
|
|
1325
|
-
}
|
|
1326
|
-
return totalMarginRequirement;
|
|
1327
|
-
},
|
|
1328
|
-
ZERO
|
|
1329
|
-
);
|
|
1390
|
+
const marginRequirementOfAll = this.getMaintenanceMarginRequirement();
|
|
1391
|
+
const positionValue = calculateBaseAssetValueWithOracle(
|
|
1392
|
+
market,
|
|
1393
|
+
proposedPerpPosition,
|
|
1394
|
+
this.getOracleDataForPerpMarket(market.marketIndex)
|
|
1395
|
+
);
|
|
1396
|
+
const marginRequirementOfTargetMarket = positionValue
|
|
1397
|
+
.mul(
|
|
1398
|
+
new BN(
|
|
1399
|
+
calculateMarketMarginRatio(
|
|
1400
|
+
market,
|
|
1401
|
+
proposedPerpPosition.baseAssetAmount.abs(),
|
|
1402
|
+
'Maintenance'
|
|
1403
|
+
)
|
|
1404
|
+
)
|
|
1405
|
+
)
|
|
1406
|
+
.div(MARGIN_PRECISION);
|
|
1407
|
+
|
|
1408
|
+
const marginRequirementExcludingTargetMarket = marginRequirementOfAll.sub(
|
|
1409
|
+
marginRequirementOfTargetMarket
|
|
1410
|
+
);
|
|
1330
1411
|
|
|
1331
1412
|
const freeCollateralExcludingTargetMarket = totalCollateral.sub(
|
|
1332
1413
|
marginRequirementExcludingTargetMarket
|
|
@@ -1340,70 +1421,62 @@ export class User {
|
|
|
1340
1421
|
return new BN(-1);
|
|
1341
1422
|
}
|
|
1342
1423
|
|
|
1343
|
-
const
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
proposedPerpPosition.baseAssetAmount.abs(),
|
|
1351
|
-
'Maintenance'
|
|
1352
|
-
)
|
|
1353
|
-
)
|
|
1424
|
+
const marginRequirementTargetMarket = proposedPerpPositionValue
|
|
1425
|
+
.mul(
|
|
1426
|
+
new BN(
|
|
1427
|
+
calculateMarketMarginRatio(
|
|
1428
|
+
market,
|
|
1429
|
+
proposedPerpPosition.baseAssetAmount.abs(),
|
|
1430
|
+
'Maintenance'
|
|
1354
1431
|
)
|
|
1355
|
-
|
|
1356
|
-
)
|
|
1432
|
+
)
|
|
1433
|
+
)
|
|
1434
|
+
.div(MARGIN_PRECISION);
|
|
1435
|
+
|
|
1436
|
+
const marginRequirementAfterTrade =
|
|
1437
|
+
marginRequirementExcludingTargetMarket.add(marginRequirementTargetMarket);
|
|
1357
1438
|
const freeCollateralAfterTrade = totalCollateral.sub(
|
|
1358
1439
|
marginRequirementAfterTrade
|
|
1359
1440
|
);
|
|
1360
1441
|
|
|
1361
|
-
const
|
|
1362
|
-
|
|
1363
|
-
|
|
1442
|
+
const marketMaxMaintLeverage = new BN(
|
|
1443
|
+
TEN_THOUSAND.mul(TEN_THOUSAND).toNumber() /
|
|
1444
|
+
calculateMarketMarginRatio(
|
|
1445
|
+
market,
|
|
1446
|
+
proposedPerpPosition.baseAssetAmount,
|
|
1447
|
+
'Maintenance'
|
|
1448
|
+
)
|
|
1364
1449
|
);
|
|
1365
1450
|
|
|
1366
1451
|
let priceDelta;
|
|
1367
1452
|
if (proposedBaseAssetAmount.lt(ZERO)) {
|
|
1368
1453
|
priceDelta = freeCollateralAfterTrade
|
|
1369
|
-
.mul(
|
|
1370
|
-
.div(
|
|
1454
|
+
.mul(marketMaxMaintLeverage) // precision is TEN_THOUSAND
|
|
1455
|
+
.div(marketMaxMaintLeverage.add(TEN_THOUSAND))
|
|
1371
1456
|
.mul(PRICE_TO_QUOTE_PRECISION)
|
|
1372
1457
|
.mul(AMM_RESERVE_PRECISION)
|
|
1373
1458
|
.div(proposedBaseAssetAmount);
|
|
1374
1459
|
} else {
|
|
1375
1460
|
priceDelta = freeCollateralAfterTrade
|
|
1376
|
-
.mul(
|
|
1377
|
-
.div(
|
|
1461
|
+
.mul(marketMaxMaintLeverage) // precision is TEN_THOUSAND
|
|
1462
|
+
.div(marketMaxMaintLeverage.sub(TEN_THOUSAND))
|
|
1378
1463
|
.mul(PRICE_TO_QUOTE_PRECISION)
|
|
1379
1464
|
.mul(AMM_RESERVE_PRECISION)
|
|
1380
1465
|
.div(proposedBaseAssetAmount);
|
|
1381
1466
|
}
|
|
1382
1467
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
this.driftClient.getPerpMarketAccount(perpPosition.marketIndex),
|
|
1387
|
-
this.getOracleDataForPerpMarket(perpPosition.marketIndex)
|
|
1388
|
-
);
|
|
1389
|
-
} else {
|
|
1390
|
-
const direction = positionBaseSizeChange.gt(ZERO)
|
|
1391
|
-
? PositionDirection.LONG
|
|
1392
|
-
: PositionDirection.SHORT;
|
|
1393
|
-
markPriceAfterTrade = calculateTradeSlippage(
|
|
1394
|
-
direction,
|
|
1395
|
-
positionBaseSizeChange.abs(),
|
|
1396
|
-
this.driftClient.getPerpMarketAccount(perpPosition.marketIndex),
|
|
1397
|
-
'base',
|
|
1398
|
-
this.getOracleDataForPerpMarket(perpPosition.marketIndex)
|
|
1399
|
-
)[3]; // newPrice after swap
|
|
1400
|
-
}
|
|
1468
|
+
const currentPrice = this.getOracleDataForPerpMarket(
|
|
1469
|
+
perpPosition.marketIndex
|
|
1470
|
+
).price;
|
|
1401
1471
|
|
|
1402
|
-
if (
|
|
1472
|
+
if (
|
|
1473
|
+
priceDelta.gt(currentPrice) &&
|
|
1474
|
+
proposedPerpPosition.baseAssetAmount.gte(ZERO)
|
|
1475
|
+
) {
|
|
1403
1476
|
return new BN(-1);
|
|
1404
1477
|
}
|
|
1405
1478
|
|
|
1406
|
-
return
|
|
1479
|
+
return currentPrice.sub(priceDelta);
|
|
1407
1480
|
}
|
|
1408
1481
|
|
|
1409
1482
|
/**
|
|
@@ -1585,21 +1658,29 @@ export class User {
|
|
|
1585
1658
|
|
|
1586
1659
|
const totalAssetValue = this.getTotalAssetValue();
|
|
1587
1660
|
|
|
1588
|
-
const
|
|
1661
|
+
const totalPerpPositionLiability = currentPerpPositionAfterTrade
|
|
1589
1662
|
.add(totalPositionAfterTradeExcludingTargetMarket)
|
|
1590
1663
|
.abs();
|
|
1591
1664
|
|
|
1592
|
-
const
|
|
1593
|
-
|
|
1665
|
+
const totalSpotLiability = this.getSpotMarketLiabilityValue(
|
|
1666
|
+
undefined,
|
|
1667
|
+
undefined,
|
|
1668
|
+
undefined,
|
|
1669
|
+
includeOpenOrders
|
|
1594
1670
|
);
|
|
1595
1671
|
|
|
1596
|
-
|
|
1672
|
+
const totalLiabilitiesAfterTrade =
|
|
1673
|
+
totalPerpPositionLiability.add(totalSpotLiability);
|
|
1674
|
+
|
|
1675
|
+
const netAssetValue = totalAssetValue.sub(totalSpotLiability);
|
|
1676
|
+
|
|
1677
|
+
if (netAssetValue.eq(ZERO)) {
|
|
1597
1678
|
return ZERO;
|
|
1598
1679
|
}
|
|
1599
1680
|
|
|
1600
1681
|
const newLeverage = totalLiabilitiesAfterTrade
|
|
1601
1682
|
.mul(TEN_THOUSAND)
|
|
1602
|
-
.div(
|
|
1683
|
+
.div(netAssetValue);
|
|
1603
1684
|
|
|
1604
1685
|
return newLeverage;
|
|
1605
1686
|
}
|
|
@@ -1627,7 +1708,8 @@ export class User {
|
|
|
1627
1708
|
const nowTs = new BN(Math.floor(Date.now() / 1000));
|
|
1628
1709
|
const spotMarket = this.driftClient.getSpotMarketAccount(marketIndex);
|
|
1629
1710
|
|
|
1630
|
-
|
|
1711
|
+
// eslint-disable-next-line prefer-const
|
|
1712
|
+
let { borrowLimit, withdrawLimit } = calculateWithdrawLimit(
|
|
1631
1713
|
spotMarket,
|
|
1632
1714
|
nowTs
|
|
1633
1715
|
);
|
|
@@ -1636,6 +1718,12 @@ export class User {
|
|
|
1636
1718
|
const oracleData = this.getOracleDataForSpotMarket(marketIndex);
|
|
1637
1719
|
const precisionIncrease = TEN.pow(new BN(spotMarket.decimals - 6));
|
|
1638
1720
|
|
|
1721
|
+
const { canBypass, depositAmount: userDepositAmount } =
|
|
1722
|
+
this.canBypassWithdrawLimits(marketIndex);
|
|
1723
|
+
if (canBypass) {
|
|
1724
|
+
withdrawLimit = BN.max(withdrawLimit, userDepositAmount);
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1639
1727
|
const amountWithdrawable = freeCollateral
|
|
1640
1728
|
.mul(MARGIN_PRECISION)
|
|
1641
1729
|
.div(new BN(spotMarket.initialAssetWeight))
|
|
@@ -1643,22 +1731,8 @@ export class User {
|
|
|
1643
1731
|
.div(oracleData.price)
|
|
1644
1732
|
.mul(precisionIncrease);
|
|
1645
1733
|
|
|
1646
|
-
const userSpotPosition = this.getUserAccount().spotPositions.find(
|
|
1647
|
-
(spotPosition) =>
|
|
1648
|
-
isVariant(spotPosition.balanceType, 'deposit') &&
|
|
1649
|
-
spotPosition.marketIndex == marketIndex
|
|
1650
|
-
);
|
|
1651
|
-
|
|
1652
|
-
const userSpotBalance = userSpotPosition
|
|
1653
|
-
? getTokenAmount(
|
|
1654
|
-
userSpotPosition.scaledBalance,
|
|
1655
|
-
this.driftClient.getSpotMarketAccount(marketIndex),
|
|
1656
|
-
SpotBalanceType.DEPOSIT
|
|
1657
|
-
)
|
|
1658
|
-
: ZERO;
|
|
1659
|
-
|
|
1660
1734
|
const maxWithdrawValue = BN.min(
|
|
1661
|
-
BN.min(amountWithdrawable,
|
|
1735
|
+
BN.min(amountWithdrawable, userDepositAmount),
|
|
1662
1736
|
withdrawLimit.abs()
|
|
1663
1737
|
);
|
|
1664
1738
|
|
|
@@ -1671,7 +1745,7 @@ export class User {
|
|
|
1671
1745
|
false
|
|
1672
1746
|
);
|
|
1673
1747
|
|
|
1674
|
-
const freeCollatAfterWithdraw =
|
|
1748
|
+
const freeCollatAfterWithdraw = userDepositAmount.gt(ZERO)
|
|
1675
1749
|
? freeCollateral.sub(weightedAssetValue)
|
|
1676
1750
|
: freeCollateral;
|
|
1677
1751
|
|
|
@@ -1691,6 +1765,61 @@ export class User {
|
|
|
1691
1765
|
}
|
|
1692
1766
|
}
|
|
1693
1767
|
|
|
1768
|
+
public canBypassWithdrawLimits(marketIndex: number): {
|
|
1769
|
+
canBypass: boolean;
|
|
1770
|
+
netDeposits: BN;
|
|
1771
|
+
depositAmount: BN;
|
|
1772
|
+
maxDepositAmount: BN;
|
|
1773
|
+
} {
|
|
1774
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(marketIndex);
|
|
1775
|
+
const maxDepositAmount = spotMarket.withdrawGuardThreshold.div(new BN(10));
|
|
1776
|
+
const position = this.getSpotPosition(marketIndex);
|
|
1777
|
+
|
|
1778
|
+
const netDeposits = this.getUserAccount().totalDeposits.sub(
|
|
1779
|
+
this.getUserAccount().totalWithdraws
|
|
1780
|
+
);
|
|
1781
|
+
|
|
1782
|
+
if (!position) {
|
|
1783
|
+
return {
|
|
1784
|
+
canBypass: false,
|
|
1785
|
+
maxDepositAmount,
|
|
1786
|
+
depositAmount: ZERO,
|
|
1787
|
+
netDeposits,
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
if (isVariant(position.balanceType, 'borrow')) {
|
|
1792
|
+
return {
|
|
1793
|
+
canBypass: false,
|
|
1794
|
+
maxDepositAmount,
|
|
1795
|
+
netDeposits,
|
|
1796
|
+
depositAmount: ZERO,
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
const depositAmount = getTokenAmount(
|
|
1801
|
+
position.scaledBalance,
|
|
1802
|
+
spotMarket,
|
|
1803
|
+
'deposit'
|
|
1804
|
+
);
|
|
1805
|
+
|
|
1806
|
+
if (netDeposits.lt(ZERO)) {
|
|
1807
|
+
return {
|
|
1808
|
+
canBypass: false,
|
|
1809
|
+
maxDepositAmount,
|
|
1810
|
+
depositAmount: ZERO,
|
|
1811
|
+
netDeposits,
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
return {
|
|
1816
|
+
canBypass: depositAmount.lt(maxDepositAmount),
|
|
1817
|
+
maxDepositAmount,
|
|
1818
|
+
netDeposits,
|
|
1819
|
+
depositAmount,
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1694
1823
|
/**
|
|
1695
1824
|
* Get the total position value, excluding any position coming from the given target market
|
|
1696
1825
|
* @param marketToIgnore
|
package/tests/amm/test.ts
CHANGED
|
@@ -206,9 +206,10 @@ describe('AMM Tests', () => {
|
|
|
206
206
|
mockAmm.historicalOracleData.lastOraclePriceTwapTs = now.sub(new BN(11));
|
|
207
207
|
|
|
208
208
|
const liveOracleTwap = calculateLiveOracleTwap(
|
|
209
|
-
mockAmm,
|
|
209
|
+
mockAmm.historicalOracleData,
|
|
210
210
|
oraclePriceData,
|
|
211
|
-
now
|
|
211
|
+
now,
|
|
212
|
+
mockAmm.fundingPeriod
|
|
212
213
|
);
|
|
213
214
|
console.log('liveOracleTwap:', liveOracleTwap.toNumber());
|
|
214
215
|
assert(liveOracleTwap.eq(new BN(13539488)));
|