@drift-labs/sdk 2.42.0-beta.0 → 2.42.0-beta.10
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/VERSION +1 -1
- package/bun.lockb +0 -0
- package/lib/addresses/pda.d.ts +1 -0
- package/lib/adminClient.d.ts +1 -0
- package/lib/constants/numericConstants.d.ts +62 -59
- package/lib/constants/numericConstants.js +2 -1
- package/lib/constants/spotMarkets.d.ts +1 -0
- package/lib/dlob/DLOB.d.ts +10 -16
- package/lib/dlob/DLOB.js +9 -39
- package/lib/dlob/DLOBNode.d.ts +1 -0
- package/lib/dlob/NodeList.d.ts +1 -0
- package/lib/dlob/orderBookLevels.d.ts +2 -1
- package/lib/driftClient.d.ts +12 -4
- package/lib/driftClient.js +51 -38
- package/lib/factory/bigNum.d.ts +8 -7
- package/lib/jupiter/jupiterClient.d.ts +1 -0
- package/lib/marinade/index.d.ts +1 -0
- package/lib/math/amm.d.ts +2 -1
- package/lib/math/auction.d.ts +1 -0
- package/lib/math/conversion.d.ts +2 -1
- package/lib/math/funding.d.ts +1 -0
- package/lib/math/funding.js +2 -1
- package/lib/math/insurance.d.ts +1 -0
- package/lib/math/margin.d.ts +1 -0
- package/lib/math/market.d.ts +2 -1
- package/lib/math/market.js +3 -2
- package/lib/math/oracles.d.ts +1 -0
- package/lib/math/orders.d.ts +1 -0
- package/lib/math/position.d.ts +1 -0
- package/lib/math/repeg.d.ts +1 -0
- package/lib/math/spotBalance.d.ts +3 -2
- package/lib/math/spotMarket.d.ts +2 -1
- package/lib/math/spotMarket.js +9 -3
- package/lib/math/spotPosition.d.ts +4 -3
- package/lib/math/spotPosition.js +18 -7
- package/lib/math/superStake.d.ts +1 -0
- package/lib/math/trade.d.ts +1 -0
- package/lib/math/utils.d.ts +1 -0
- package/lib/oracles/pythClient.d.ts +2 -1
- package/lib/oracles/strictOraclePrice.d.ts +1 -0
- package/lib/oracles/types.d.ts +1 -0
- package/lib/orderParams.d.ts +1 -0
- package/lib/phoenix/phoenixSubscriber.d.ts +1 -0
- package/lib/serum/serumSubscriber.d.ts +1 -0
- package/lib/tokenFaucet.d.ts +1 -0
- package/lib/types.d.ts +7 -0
- package/lib/types.js +2 -0
- package/lib/user.d.ts +15 -3
- package/lib/user.js +85 -28
- package/package.json +2 -1
- package/src/constants/numericConstants.ts +1 -0
- package/src/dlob/DLOB.ts +23 -67
- package/src/driftClient.ts +79 -53
- package/src/math/funding.ts +6 -1
- package/src/math/market.ts +12 -7
- package/src/math/spotMarket.ts +13 -3
- package/src/math/spotPosition.ts +29 -7
- package/src/types.ts +2 -0
- package/src/user.ts +136 -34
- package/tests/amm/test.ts +7 -5
- package/tests/auctions/test.ts +22 -11
- package/tests/dlob/helpers.ts +15 -13
- package/tests/tx/priorityFeeCalculator.ts +1 -1
- package/tests/user/test.ts +171 -24
|
@@ -83,6 +83,7 @@ export const AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO =
|
|
|
83
83
|
export const MARGIN_PRECISION = TEN_THOUSAND;
|
|
84
84
|
export const BID_ASK_SPREAD_PRECISION = new BN(1000000); // 10^6
|
|
85
85
|
export const LIQUIDATION_PCT_PRECISION = TEN_THOUSAND;
|
|
86
|
+
export const FUNDING_RATE_OFFSET_DENOMINATOR = new BN(5000);
|
|
86
87
|
|
|
87
88
|
export const FIVE_MINUTE = new BN(60 * 5);
|
|
88
89
|
export const ONE_HOUR = new BN(60 * 60);
|
package/src/dlob/DLOB.ts
CHANGED
|
@@ -73,6 +73,14 @@ export type MarketNodeLists = {
|
|
|
73
73
|
|
|
74
74
|
type OrderBookCallback = () => void;
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Receives a DLOBNode and is expected to return true if the node should
|
|
78
|
+
* be taken into account when generating, or false otherwise.
|
|
79
|
+
*
|
|
80
|
+
* Currently used in getRestingLimitBids and getRestingLimitAsks.
|
|
81
|
+
*/
|
|
82
|
+
export type DLOBFilterFcn = (node: DLOBNode) => boolean;
|
|
83
|
+
|
|
76
84
|
export type NodeToFill = {
|
|
77
85
|
node: DLOBNode;
|
|
78
86
|
makerNodes: DLOBNode[];
|
|
@@ -1065,7 +1073,8 @@ export class DLOB {
|
|
|
1065
1073
|
currentDLOBNode: DLOBNode,
|
|
1066
1074
|
slot: number,
|
|
1067
1075
|
oraclePriceData: OraclePriceData
|
|
1068
|
-
) => boolean
|
|
1076
|
+
) => boolean,
|
|
1077
|
+
filterFcn?: DLOBFilterFcn
|
|
1069
1078
|
): Generator<DLOBNode> {
|
|
1070
1079
|
const generators = generatorList.map((generator) => {
|
|
1071
1080
|
return {
|
|
@@ -1102,6 +1111,11 @@ export class DLOB {
|
|
|
1102
1111
|
continue;
|
|
1103
1112
|
}
|
|
1104
1113
|
|
|
1114
|
+
if (filterFcn && filterFcn(bestGenerator.next.value)) {
|
|
1115
|
+
bestGenerator.next = bestGenerator.generator.next();
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1105
1119
|
yield bestGenerator.next.value;
|
|
1106
1120
|
bestGenerator.next = bestGenerator.generator.next();
|
|
1107
1121
|
} else {
|
|
@@ -1114,7 +1128,8 @@ export class DLOB {
|
|
|
1114
1128
|
marketIndex: number,
|
|
1115
1129
|
slot: number,
|
|
1116
1130
|
marketType: MarketType,
|
|
1117
|
-
oraclePriceData: OraclePriceData
|
|
1131
|
+
oraclePriceData: OraclePriceData,
|
|
1132
|
+
filterFcn?: DLOBFilterFcn
|
|
1118
1133
|
): Generator<DLOBNode> {
|
|
1119
1134
|
if (isVariant(marketType, 'spot') && !oraclePriceData) {
|
|
1120
1135
|
throw new Error('Must provide OraclePriceData to get spot asks');
|
|
@@ -1142,46 +1157,17 @@ export class DLOB {
|
|
|
1142
1157
|
return bestNode
|
|
1143
1158
|
.getPrice(oraclePriceData, slot)
|
|
1144
1159
|
.lt(currentNode.getPrice(oraclePriceData, slot));
|
|
1145
|
-
}
|
|
1160
|
+
},
|
|
1161
|
+
filterFcn
|
|
1146
1162
|
);
|
|
1147
1163
|
}
|
|
1148
1164
|
|
|
1149
|
-
/**
|
|
1150
|
-
* Filters the limit asks that are resting and do not cross fallback bid
|
|
1151
|
-
* Taking orders can only fill against orders that meet this criteria
|
|
1152
|
-
*
|
|
1153
|
-
* @returns
|
|
1154
|
-
*/
|
|
1155
|
-
*getMakerLimitAsks(
|
|
1156
|
-
marketIndex: number,
|
|
1157
|
-
slot: number,
|
|
1158
|
-
marketType: MarketType,
|
|
1159
|
-
oraclePriceData: OraclePriceData,
|
|
1160
|
-
fallbackBid?: BN
|
|
1161
|
-
): Generator<DLOBNode> {
|
|
1162
|
-
const isPerpMarket = isVariant(marketType, 'perp');
|
|
1163
|
-
for (const node of this.getRestingLimitAsks(
|
|
1164
|
-
marketIndex,
|
|
1165
|
-
slot,
|
|
1166
|
-
marketType,
|
|
1167
|
-
oraclePriceData
|
|
1168
|
-
)) {
|
|
1169
|
-
if (
|
|
1170
|
-
isPerpMarket &&
|
|
1171
|
-
fallbackBid &&
|
|
1172
|
-
node.getPrice(oraclePriceData, slot).lte(fallbackBid)
|
|
1173
|
-
) {
|
|
1174
|
-
continue;
|
|
1175
|
-
}
|
|
1176
|
-
yield node;
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
1165
|
*getRestingLimitBids(
|
|
1181
1166
|
marketIndex: number,
|
|
1182
1167
|
slot: number,
|
|
1183
1168
|
marketType: MarketType,
|
|
1184
|
-
oraclePriceData: OraclePriceData
|
|
1169
|
+
oraclePriceData: OraclePriceData,
|
|
1170
|
+
filterFcn?: DLOBFilterFcn
|
|
1185
1171
|
): Generator<DLOBNode> {
|
|
1186
1172
|
if (isVariant(marketType, 'spot') && !oraclePriceData) {
|
|
1187
1173
|
throw new Error('Must provide OraclePriceData to get spot bids');
|
|
@@ -1209,41 +1195,11 @@ export class DLOB {
|
|
|
1209
1195
|
return bestNode
|
|
1210
1196
|
.getPrice(oraclePriceData, slot)
|
|
1211
1197
|
.gt(currentNode.getPrice(oraclePriceData, slot));
|
|
1212
|
-
}
|
|
1198
|
+
},
|
|
1199
|
+
filterFcn
|
|
1213
1200
|
);
|
|
1214
1201
|
}
|
|
1215
1202
|
|
|
1216
|
-
/**
|
|
1217
|
-
* Filters the limit bids that are post only, have been place for sufficiently long or are below the fallback ask
|
|
1218
|
-
* Market orders can only fill against orders that meet this criteria
|
|
1219
|
-
*
|
|
1220
|
-
* @returns
|
|
1221
|
-
*/
|
|
1222
|
-
*getMakerLimitBids(
|
|
1223
|
-
marketIndex: number,
|
|
1224
|
-
slot: number,
|
|
1225
|
-
marketType: MarketType,
|
|
1226
|
-
oraclePriceData: OraclePriceData,
|
|
1227
|
-
fallbackAsk?: BN
|
|
1228
|
-
): Generator<DLOBNode> {
|
|
1229
|
-
const isPerpMarket = isVariant(marketType, 'perp');
|
|
1230
|
-
for (const node of this.getRestingLimitBids(
|
|
1231
|
-
marketIndex,
|
|
1232
|
-
slot,
|
|
1233
|
-
marketType,
|
|
1234
|
-
oraclePriceData
|
|
1235
|
-
)) {
|
|
1236
|
-
if (
|
|
1237
|
-
isPerpMarket &&
|
|
1238
|
-
fallbackAsk &&
|
|
1239
|
-
node.getPrice(oraclePriceData, slot).gte(fallbackAsk)
|
|
1240
|
-
) {
|
|
1241
|
-
continue;
|
|
1242
|
-
}
|
|
1243
|
-
yield node;
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
1203
|
*getAsks(
|
|
1248
1204
|
marketIndex: number,
|
|
1249
1205
|
fallbackAsk: BN | undefined,
|
package/src/driftClient.ts
CHANGED
|
@@ -539,6 +539,10 @@ export class DriftClient {
|
|
|
539
539
|
this.activeSubAccountId = activeSubAccountId;
|
|
540
540
|
this.userStatsAccountPublicKey = undefined;
|
|
541
541
|
this.includeDelegates = includeDelegates ?? false;
|
|
542
|
+
const walletSupportsVersionedTxns =
|
|
543
|
+
//@ts-ignore
|
|
544
|
+
this.wallet.supportedTransactionVersions?.size ?? 0 > 1;
|
|
545
|
+
this.txVersion = walletSupportsVersionedTxns ? 0 : 'legacy';
|
|
542
546
|
|
|
543
547
|
if (includeDelegates && subAccountIds) {
|
|
544
548
|
throw new Error(
|
|
@@ -588,13 +592,30 @@ export class DriftClient {
|
|
|
588
592
|
return success;
|
|
589
593
|
}
|
|
590
594
|
|
|
591
|
-
public switchActiveUser(subAccountId: number, authority?: PublicKey) {
|
|
595
|
+
public async switchActiveUser(subAccountId: number, authority?: PublicKey) {
|
|
596
|
+
const authorityChanged = authority && !this.authority?.equals(authority);
|
|
597
|
+
|
|
592
598
|
this.activeSubAccountId = subAccountId;
|
|
593
599
|
this.authority = authority ?? this.authority;
|
|
594
600
|
this.userStatsAccountPublicKey = getUserStatsAccountPublicKey(
|
|
595
601
|
this.program.programId,
|
|
596
602
|
this.authority
|
|
597
603
|
);
|
|
604
|
+
|
|
605
|
+
/* If changing the user authority ie switching from delegate to non-delegate account, need to re-subscribe to the user stats account */
|
|
606
|
+
if (authorityChanged) {
|
|
607
|
+
if (this.userStats && this.userStats.isSubscribed) {
|
|
608
|
+
await this.userStats.unsubscribe();
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
this.userStats = new UserStats({
|
|
612
|
+
driftClient: this,
|
|
613
|
+
userStatsAccountPublicKey: this.userStatsAccountPublicKey,
|
|
614
|
+
accountSubscription: this.userAccountSubscriptionConfig,
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
this.userStats.subscribe();
|
|
618
|
+
}
|
|
598
619
|
}
|
|
599
620
|
|
|
600
621
|
public async addUser(
|
|
@@ -854,21 +875,48 @@ export class DriftClient {
|
|
|
854
875
|
}
|
|
855
876
|
|
|
856
877
|
public async updateUserCustomMarginRatio(
|
|
878
|
+
updates: { marginRatio: number; subAccountId: number }[]
|
|
879
|
+
): Promise<TransactionSignature> {
|
|
880
|
+
const ixs = await Promise.all(
|
|
881
|
+
updates.map(async ({ marginRatio, subAccountId }) => {
|
|
882
|
+
const ix = await this.getUpdateUserCustomMarginRatioIx(
|
|
883
|
+
marginRatio,
|
|
884
|
+
subAccountId
|
|
885
|
+
);
|
|
886
|
+
return ix;
|
|
887
|
+
})
|
|
888
|
+
);
|
|
889
|
+
|
|
890
|
+
const tx = await this.buildTransaction(ixs, this.txParams);
|
|
891
|
+
|
|
892
|
+
const { txSig } = await this.sendTransaction(tx, [], this.opts);
|
|
893
|
+
return txSig;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
public async getUpdateUserCustomMarginRatioIx(
|
|
857
897
|
marginRatio: number,
|
|
858
898
|
subAccountId = 0
|
|
859
|
-
): Promise<
|
|
860
|
-
const
|
|
899
|
+
): Promise<TransactionInstruction> {
|
|
900
|
+
const userAccountPublicKey = getUserAccountPublicKeySync(
|
|
901
|
+
this.program.programId,
|
|
902
|
+
this.wallet.publicKey,
|
|
903
|
+
subAccountId
|
|
904
|
+
);
|
|
905
|
+
|
|
906
|
+
await this.addUser(subAccountId, this.wallet.publicKey);
|
|
907
|
+
|
|
908
|
+
const ix = this.program.instruction.updateUserCustomMarginRatio(
|
|
861
909
|
subAccountId,
|
|
862
910
|
marginRatio,
|
|
863
911
|
{
|
|
864
912
|
accounts: {
|
|
865
|
-
user:
|
|
913
|
+
user: userAccountPublicKey,
|
|
866
914
|
authority: this.wallet.publicKey,
|
|
867
915
|
},
|
|
868
916
|
}
|
|
869
917
|
);
|
|
870
|
-
|
|
871
|
-
return
|
|
918
|
+
|
|
919
|
+
return ix;
|
|
872
920
|
}
|
|
873
921
|
|
|
874
922
|
public async getUpdateUserMarginTradingEnabledIx(
|
|
@@ -909,31 +957,18 @@ export class DriftClient {
|
|
|
909
957
|
}
|
|
910
958
|
|
|
911
959
|
public async updateUserMarginTradingEnabled(
|
|
912
|
-
marginTradingEnabled: boolean
|
|
913
|
-
subAccountId = 0
|
|
960
|
+
updates: { marginTradingEnabled: boolean; subAccountId: number }[]
|
|
914
961
|
): Promise<TransactionSignature> {
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
962
|
+
const ixs = await Promise.all(
|
|
963
|
+
updates.map(async ({ marginTradingEnabled, subAccountId }) => {
|
|
964
|
+
return await this.getUpdateUserMarginTradingEnabledIx(
|
|
965
|
+
marginTradingEnabled,
|
|
966
|
+
subAccountId
|
|
967
|
+
);
|
|
968
|
+
})
|
|
919
969
|
);
|
|
920
970
|
|
|
921
|
-
await this.
|
|
922
|
-
const remainingAccounts = this.getRemainingAccounts({
|
|
923
|
-
userAccounts: [this.getUserAccount(subAccountId)],
|
|
924
|
-
});
|
|
925
|
-
|
|
926
|
-
const tx = await this.program.transaction.updateUserMarginTradingEnabled(
|
|
927
|
-
subAccountId,
|
|
928
|
-
marginTradingEnabled,
|
|
929
|
-
{
|
|
930
|
-
accounts: {
|
|
931
|
-
user: userAccountPublicKey,
|
|
932
|
-
authority: this.wallet.publicKey,
|
|
933
|
-
},
|
|
934
|
-
remainingAccounts,
|
|
935
|
-
}
|
|
936
|
-
);
|
|
971
|
+
const tx = await this.buildTransaction(ixs, this.txParams);
|
|
937
972
|
|
|
938
973
|
const { txSig } = await this.sendTransaction(tx, [], this.opts);
|
|
939
974
|
return txSig;
|
|
@@ -1557,13 +1592,6 @@ export class DriftClient {
|
|
|
1557
1592
|
subAccountId?: number,
|
|
1558
1593
|
reduceOnly = false
|
|
1559
1594
|
): Promise<TransactionSignature> {
|
|
1560
|
-
const tx = new Transaction();
|
|
1561
|
-
tx.add(
|
|
1562
|
-
ComputeBudgetProgram.setComputeUnitLimit({
|
|
1563
|
-
units: 600_000,
|
|
1564
|
-
})
|
|
1565
|
-
);
|
|
1566
|
-
|
|
1567
1595
|
const additionalSigners: Array<Signer> = [];
|
|
1568
1596
|
|
|
1569
1597
|
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
@@ -1575,6 +1603,8 @@ export class DriftClient {
|
|
|
1575
1603
|
const createWSOLTokenAccount =
|
|
1576
1604
|
isSolMarket && associatedTokenAccount.equals(signerAuthority);
|
|
1577
1605
|
|
|
1606
|
+
const instructions = [];
|
|
1607
|
+
|
|
1578
1608
|
if (createWSOLTokenAccount) {
|
|
1579
1609
|
const { ixs, pubkey } = await this.getWrappedSolAccountCreationIxs(
|
|
1580
1610
|
amount,
|
|
@@ -1583,9 +1613,7 @@ export class DriftClient {
|
|
|
1583
1613
|
|
|
1584
1614
|
associatedTokenAccount = pubkey;
|
|
1585
1615
|
|
|
1586
|
-
|
|
1587
|
-
tx.add(ix);
|
|
1588
|
-
});
|
|
1616
|
+
instructions.push(...ixs);
|
|
1589
1617
|
}
|
|
1590
1618
|
|
|
1591
1619
|
const depositCollateralIx = await this.getDepositInstruction(
|
|
@@ -1597,11 +1625,11 @@ export class DriftClient {
|
|
|
1597
1625
|
true
|
|
1598
1626
|
);
|
|
1599
1627
|
|
|
1600
|
-
|
|
1628
|
+
instructions.push(depositCollateralIx);
|
|
1601
1629
|
|
|
1602
1630
|
// Close the wrapped sol account at the end of the transaction
|
|
1603
1631
|
if (createWSOLTokenAccount) {
|
|
1604
|
-
|
|
1632
|
+
instructions.push(
|
|
1605
1633
|
createCloseAccountInstruction(
|
|
1606
1634
|
associatedTokenAccount,
|
|
1607
1635
|
signerAuthority,
|
|
@@ -1611,6 +1639,10 @@ export class DriftClient {
|
|
|
1611
1639
|
);
|
|
1612
1640
|
}
|
|
1613
1641
|
|
|
1642
|
+
const txParams = { ...this.txParams, computeUnits: 600_000 };
|
|
1643
|
+
|
|
1644
|
+
const tx = await this.buildTransaction(instructions, txParams);
|
|
1645
|
+
|
|
1614
1646
|
const { txSig, slot } = await this.sendTransaction(
|
|
1615
1647
|
tx,
|
|
1616
1648
|
additionalSigners,
|
|
@@ -2425,8 +2457,7 @@ export class DriftClient {
|
|
|
2425
2457
|
makerInfo?: MakerInfo | MakerInfo[],
|
|
2426
2458
|
txParams?: TxParams,
|
|
2427
2459
|
bracketOrdersParams = new Array<OptionalOrderParams>(),
|
|
2428
|
-
referrerInfo?: ReferrerInfo
|
|
2429
|
-
useVersionedTx = true
|
|
2460
|
+
referrerInfo?: ReferrerInfo
|
|
2430
2461
|
): Promise<{ txSig: TransactionSignature; signedFillTx: Transaction }> {
|
|
2431
2462
|
const marketIndex = orderParams.marketIndex;
|
|
2432
2463
|
const orderId = userAccount.nextOrderId;
|
|
@@ -2452,12 +2483,8 @@ export class DriftClient {
|
|
|
2452
2483
|
referrerInfo
|
|
2453
2484
|
);
|
|
2454
2485
|
|
|
2455
|
-
const walletSupportsVersionedTxns =
|
|
2456
|
-
//@ts-ignore
|
|
2457
|
-
this.wallet.supportedTransactionVersions?.size ?? 0 > 1;
|
|
2458
|
-
|
|
2459
2486
|
// use versioned transactions if there is a lookup table account and wallet is compatible
|
|
2460
|
-
if (
|
|
2487
|
+
if (this.txVersion === 0) {
|
|
2461
2488
|
const versionedMarketOrderTx = await this.buildTransaction(
|
|
2462
2489
|
[placePerpOrderIx].concat(bracketOrderIxs),
|
|
2463
2490
|
txParams,
|
|
@@ -5835,17 +5862,16 @@ export class DriftClient {
|
|
|
5835
5862
|
opts?: ConfirmOptions,
|
|
5836
5863
|
preSigned?: boolean
|
|
5837
5864
|
): Promise<TxSigAndSlot> {
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
tx as Transaction,
|
|
5865
|
+
if (tx instanceof VersionedTransaction) {
|
|
5866
|
+
return this.txSender.sendVersionedTransaction(
|
|
5867
|
+
tx as VersionedTransaction,
|
|
5842
5868
|
additionalSigners,
|
|
5843
5869
|
opts,
|
|
5844
5870
|
preSigned
|
|
5845
5871
|
);
|
|
5846
5872
|
} else {
|
|
5847
|
-
return this.txSender.
|
|
5848
|
-
tx as
|
|
5873
|
+
return this.txSender.send(
|
|
5874
|
+
tx as Transaction,
|
|
5849
5875
|
additionalSigners,
|
|
5850
5876
|
opts,
|
|
5851
5877
|
preSigned
|
package/src/math/funding.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
QUOTE_PRECISION,
|
|
6
6
|
ZERO,
|
|
7
7
|
ONE,
|
|
8
|
+
FUNDING_RATE_OFFSET_DENOMINATOR,
|
|
8
9
|
} from '../constants/numericConstants';
|
|
9
10
|
import { PerpMarketAccount, isVariant } from '../types';
|
|
10
11
|
import { OraclePriceData } from '../oracles/types';
|
|
@@ -156,7 +157,11 @@ export async function calculateAllEstimatedFundingRate(
|
|
|
156
157
|
// }
|
|
157
158
|
|
|
158
159
|
const twapSpread = markTwap.sub(oracleTwap);
|
|
159
|
-
const
|
|
160
|
+
const twapSpreadWithOffset = twapSpread.add(
|
|
161
|
+
oracleTwap.abs().div(FUNDING_RATE_OFFSET_DENOMINATOR)
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const twapSpreadPct = twapSpreadWithOffset
|
|
160
165
|
.mul(PRICE_PRECISION)
|
|
161
166
|
.mul(new BN(100))
|
|
162
167
|
.div(oracleTwap);
|
package/src/math/market.ts
CHANGED
|
@@ -129,17 +129,22 @@ export function calculateOracleSpread(
|
|
|
129
129
|
export function calculateMarketMarginRatio(
|
|
130
130
|
market: PerpMarketAccount,
|
|
131
131
|
size: BN,
|
|
132
|
-
marginCategory: MarginCategory
|
|
132
|
+
marginCategory: MarginCategory,
|
|
133
|
+
customMarginRatio = 0
|
|
133
134
|
): number {
|
|
134
135
|
let marginRatio;
|
|
135
136
|
switch (marginCategory) {
|
|
136
137
|
case 'Initial': {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
// use lowest leverage between max allowed and optional user custom max
|
|
139
|
+
marginRatio = Math.max(
|
|
140
|
+
calculateSizePremiumLiabilityWeight(
|
|
141
|
+
size,
|
|
142
|
+
new BN(market.imfFactor),
|
|
143
|
+
new BN(market.marginRatioInitial),
|
|
144
|
+
MARGIN_PRECISION
|
|
145
|
+
).toNumber(),
|
|
146
|
+
customMarginRatio
|
|
147
|
+
);
|
|
143
148
|
break;
|
|
144
149
|
}
|
|
145
150
|
case 'Maintenance': {
|
package/src/math/spotMarket.ts
CHANGED
|
@@ -24,8 +24,11 @@ export function calculateSpotMarketMarginRatio(
|
|
|
24
24
|
oraclePrice: BN,
|
|
25
25
|
marginCategory: MarginCategory,
|
|
26
26
|
size: BN,
|
|
27
|
-
balanceType: SpotBalanceType
|
|
27
|
+
balanceType: SpotBalanceType,
|
|
28
|
+
customMarginRatio = 0
|
|
28
29
|
): number {
|
|
30
|
+
let marginRatio;
|
|
31
|
+
|
|
29
32
|
if (isVariant(balanceType, 'deposit')) {
|
|
30
33
|
const assetWeight = calculateAssetWeight(
|
|
31
34
|
size,
|
|
@@ -33,13 +36,20 @@ export function calculateSpotMarketMarginRatio(
|
|
|
33
36
|
market,
|
|
34
37
|
marginCategory
|
|
35
38
|
);
|
|
36
|
-
|
|
39
|
+
marginRatio = MARGIN_PRECISION.sub(assetWeight).toNumber();
|
|
37
40
|
} else {
|
|
38
41
|
const liabilityWeight = calculateLiabilityWeight(
|
|
39
42
|
size,
|
|
40
43
|
market,
|
|
41
44
|
marginCategory
|
|
42
45
|
);
|
|
43
|
-
|
|
46
|
+
marginRatio = liabilityWeight.sub(MARGIN_PRECISION).toNumber();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (marginCategory === 'Initial') {
|
|
50
|
+
// use lowest leverage between max allowed and optional user custom max
|
|
51
|
+
return Math.max(marginRatio, customMarginRatio);
|
|
44
52
|
}
|
|
53
|
+
|
|
54
|
+
return marginRatio;
|
|
45
55
|
}
|
package/src/math/spotPosition.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MarginCategory, SpotMarketAccount, SpotPosition } from '../types';
|
|
2
2
|
import {
|
|
3
|
+
QUOTE_SPOT_MARKET_INDEX,
|
|
3
4
|
SPOT_MARKET_WEIGHT_PRECISION,
|
|
4
5
|
ZERO,
|
|
5
6
|
} from '../constants/numericConstants';
|
|
@@ -31,7 +32,8 @@ export function getWorstCaseTokenAmounts(
|
|
|
31
32
|
spotPosition: SpotPosition,
|
|
32
33
|
spotMarketAccount: SpotMarketAccount,
|
|
33
34
|
strictOraclePrice: StrictOraclePrice,
|
|
34
|
-
marginCategory: MarginCategory
|
|
35
|
+
marginCategory: MarginCategory,
|
|
36
|
+
customMarginRatio?: number
|
|
35
37
|
): OrderFillSimulation {
|
|
36
38
|
const tokenAmount = getSignedTokenAmount(
|
|
37
39
|
getTokenAmount(
|
|
@@ -54,7 +56,8 @@ export function getWorstCaseTokenAmounts(
|
|
|
54
56
|
tokenValue,
|
|
55
57
|
strictOraclePrice.current,
|
|
56
58
|
spotMarketAccount,
|
|
57
|
-
marginCategory
|
|
59
|
+
marginCategory,
|
|
60
|
+
customMarginRatio
|
|
58
61
|
);
|
|
59
62
|
return {
|
|
60
63
|
tokenAmount,
|
|
@@ -72,7 +75,8 @@ export function getWorstCaseTokenAmounts(
|
|
|
72
75
|
spotPosition.openBids,
|
|
73
76
|
strictOraclePrice,
|
|
74
77
|
spotMarketAccount,
|
|
75
|
-
marginCategory
|
|
78
|
+
marginCategory,
|
|
79
|
+
customMarginRatio
|
|
76
80
|
);
|
|
77
81
|
const asksSimulation = simulateOrderFill(
|
|
78
82
|
tokenAmount,
|
|
@@ -80,7 +84,8 @@ export function getWorstCaseTokenAmounts(
|
|
|
80
84
|
spotPosition.openAsks,
|
|
81
85
|
strictOraclePrice,
|
|
82
86
|
spotMarketAccount,
|
|
83
|
-
marginCategory
|
|
87
|
+
marginCategory,
|
|
88
|
+
customMarginRatio
|
|
84
89
|
);
|
|
85
90
|
|
|
86
91
|
if (
|
|
@@ -99,7 +104,8 @@ export function calculateWeightedTokenValue(
|
|
|
99
104
|
tokenValue: BN,
|
|
100
105
|
oraclePrice: BN,
|
|
101
106
|
spotMarket: SpotMarketAccount,
|
|
102
|
-
marginCategory: MarginCategory
|
|
107
|
+
marginCategory: MarginCategory,
|
|
108
|
+
customMarginRatio?: number
|
|
103
109
|
): { weight: BN; weightedTokenValue: BN } {
|
|
104
110
|
let weight: BN;
|
|
105
111
|
if (tokenValue.gte(ZERO)) {
|
|
@@ -117,6 +123,20 @@ export function calculateWeightedTokenValue(
|
|
|
117
123
|
);
|
|
118
124
|
}
|
|
119
125
|
|
|
126
|
+
if (
|
|
127
|
+
marginCategory === 'Initial' &&
|
|
128
|
+
customMarginRatio &&
|
|
129
|
+
spotMarket.marketIndex !== QUOTE_SPOT_MARKET_INDEX
|
|
130
|
+
) {
|
|
131
|
+
const userCustomAssetWeight = tokenValue.gte(ZERO)
|
|
132
|
+
? BN.max(ZERO, SPOT_MARKET_WEIGHT_PRECISION.subn(customMarginRatio))
|
|
133
|
+
: SPOT_MARKET_WEIGHT_PRECISION.addn(customMarginRatio);
|
|
134
|
+
|
|
135
|
+
weight = tokenValue.gte(ZERO)
|
|
136
|
+
? BN.min(weight, userCustomAssetWeight)
|
|
137
|
+
: BN.max(weight, userCustomAssetWeight);
|
|
138
|
+
}
|
|
139
|
+
|
|
120
140
|
return {
|
|
121
141
|
weight: weight,
|
|
122
142
|
weightedTokenValue: tokenValue
|
|
@@ -131,7 +151,8 @@ export function simulateOrderFill(
|
|
|
131
151
|
openOrders: BN,
|
|
132
152
|
strictOraclePrice: StrictOraclePrice,
|
|
133
153
|
spotMarket: SpotMarketAccount,
|
|
134
|
-
marginCategory: MarginCategory
|
|
154
|
+
marginCategory: MarginCategory,
|
|
155
|
+
customMarginRatio?: number
|
|
135
156
|
): OrderFillSimulation {
|
|
136
157
|
const ordersValue = getTokenValue(openOrders.neg(), spotMarket.decimals, {
|
|
137
158
|
price: strictOraclePrice.max(),
|
|
@@ -145,7 +166,8 @@ export function simulateOrderFill(
|
|
|
145
166
|
tokenValueAfterFill,
|
|
146
167
|
strictOraclePrice.current,
|
|
147
168
|
spotMarket,
|
|
148
|
-
marginCategory
|
|
169
|
+
marginCategory,
|
|
170
|
+
customMarginRatio
|
|
149
171
|
);
|
|
150
172
|
|
|
151
173
|
const freeCollateralContribution =
|
package/src/types.ts
CHANGED
|
@@ -200,6 +200,8 @@ export class StakeAction {
|
|
|
200
200
|
static readonly UNSTAKE_REQUEST = { unstakeRequest: {} };
|
|
201
201
|
static readonly UNSTAKE_CANCEL_REQUEST = { unstakeCancelRequest: {} };
|
|
202
202
|
static readonly UNSTAKE = { unstake: {} };
|
|
203
|
+
static readonly UNSTAKE_TRANSFER = { unstakeTransfer: {} };
|
|
204
|
+
static readonly STAKE_TRANSFER = { stakeTransfer: {} };
|
|
203
205
|
}
|
|
204
206
|
|
|
205
207
|
export function isVariant(object: unknown, type: string) {
|