@drift-labs/sdk 2.42.0-beta.1 → 2.42.0-beta.11

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.
Files changed (66) hide show
  1. package/VERSION +1 -1
  2. package/bun.lockb +0 -0
  3. package/lib/addresses/pda.d.ts +1 -0
  4. package/lib/adminClient.d.ts +1 -0
  5. package/lib/constants/numericConstants.d.ts +62 -59
  6. package/lib/constants/numericConstants.js +2 -1
  7. package/lib/constants/spotMarkets.d.ts +1 -0
  8. package/lib/dlob/DLOB.d.ts +10 -16
  9. package/lib/dlob/DLOB.js +9 -39
  10. package/lib/dlob/DLOBNode.d.ts +1 -0
  11. package/lib/dlob/NodeList.d.ts +1 -0
  12. package/lib/dlob/orderBookLevels.d.ts +2 -1
  13. package/lib/driftClient.d.ts +12 -4
  14. package/lib/driftClient.js +51 -38
  15. package/lib/events/fetchLogs.js +2 -0
  16. package/lib/factory/bigNum.d.ts +8 -7
  17. package/lib/jupiter/jupiterClient.d.ts +1 -0
  18. package/lib/marinade/index.d.ts +1 -0
  19. package/lib/math/amm.d.ts +2 -1
  20. package/lib/math/auction.d.ts +1 -0
  21. package/lib/math/conversion.d.ts +2 -1
  22. package/lib/math/funding.d.ts +1 -0
  23. package/lib/math/funding.js +2 -1
  24. package/lib/math/insurance.d.ts +1 -0
  25. package/lib/math/margin.d.ts +1 -0
  26. package/lib/math/market.d.ts +2 -1
  27. package/lib/math/market.js +3 -2
  28. package/lib/math/oracles.d.ts +1 -0
  29. package/lib/math/orders.d.ts +1 -0
  30. package/lib/math/position.d.ts +1 -0
  31. package/lib/math/repeg.d.ts +1 -0
  32. package/lib/math/spotBalance.d.ts +3 -2
  33. package/lib/math/spotMarket.d.ts +2 -1
  34. package/lib/math/spotMarket.js +9 -3
  35. package/lib/math/spotPosition.d.ts +4 -3
  36. package/lib/math/spotPosition.js +18 -7
  37. package/lib/math/superStake.d.ts +1 -0
  38. package/lib/math/trade.d.ts +1 -0
  39. package/lib/math/utils.d.ts +1 -0
  40. package/lib/oracles/pythClient.d.ts +2 -1
  41. package/lib/oracles/strictOraclePrice.d.ts +1 -0
  42. package/lib/oracles/types.d.ts +1 -0
  43. package/lib/orderParams.d.ts +1 -0
  44. package/lib/phoenix/phoenixSubscriber.d.ts +1 -0
  45. package/lib/serum/serumSubscriber.d.ts +1 -0
  46. package/lib/tokenFaucet.d.ts +1 -0
  47. package/lib/types.d.ts +7 -0
  48. package/lib/types.js +2 -0
  49. package/lib/user.d.ts +3 -2
  50. package/lib/user.js +29 -26
  51. package/package.json +2 -1
  52. package/src/constants/numericConstants.ts +1 -0
  53. package/src/dlob/DLOB.ts +23 -67
  54. package/src/driftClient.ts +79 -53
  55. package/src/events/fetchLogs.ts +3 -0
  56. package/src/math/funding.ts +6 -1
  57. package/src/math/market.ts +12 -7
  58. package/src/math/spotMarket.ts +13 -3
  59. package/src/math/spotPosition.ts +29 -7
  60. package/src/types.ts +2 -0
  61. package/src/user.ts +64 -31
  62. package/tests/amm/test.ts +7 -5
  63. package/tests/auctions/test.ts +22 -11
  64. package/tests/dlob/helpers.ts +15 -13
  65. package/tests/tx/priorityFeeCalculator.ts +1 -1
  66. package/tests/user/test.ts +171 -24
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,
@@ -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<TransactionSignature> {
860
- const tx = await this.program.transaction.updateUserCustomMarginRatio(
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: await this.getUserAccountPublicKey(),
913
+ user: userAccountPublicKey,
866
914
  authority: this.wallet.publicKey,
867
915
  },
868
916
  }
869
917
  );
870
- const { txSig } = await this.sendTransaction(tx, [], this.opts);
871
- return txSig;
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 userAccountPublicKey = getUserAccountPublicKeySync(
916
- this.program.programId,
917
- this.wallet.publicKey,
918
- subAccountId
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.addUser(subAccountId, this.wallet.publicKey);
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
- ixs.forEach((ix) => {
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
- tx.add(depositCollateralIx);
1628
+ instructions.push(depositCollateralIx);
1601
1629
 
1602
1630
  // Close the wrapped sol account at the end of the transaction
1603
1631
  if (createWSOLTokenAccount) {
1604
- tx.add(
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 (walletSupportsVersionedTxns && useVersionedTx) {
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
- // @ts-ignore
5839
- if (!tx.message) {
5840
- return this.txSender.send(
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.sendVersionedTransaction(
5848
- tx as VersionedTransaction,
5873
+ return this.txSender.send(
5874
+ tx as Transaction,
5849
5875
  additionalSigners,
5850
5876
  opts,
5851
5877
  preSigned
@@ -150,6 +150,9 @@ export class LogParser {
150
150
 
151
151
  public parseEventsFromLogs(event: Log): WrappedEvents {
152
152
  const records: WrappedEvents = [];
153
+
154
+ if (!event.logs) return records;
155
+
153
156
  // @ts-ignore
154
157
  const eventGenerator = this.program._events._eventParser.parseLogs(
155
158
  event.logs,
@@ -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 twapSpreadPct = twapSpread
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);
@@ -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
- marginRatio = calculateSizePremiumLiabilityWeight(
138
- size,
139
- new BN(market.imfFactor),
140
- new BN(market.marginRatioInitial),
141
- MARGIN_PRECISION
142
- ).toNumber();
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': {
@@ -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
- return MARGIN_PRECISION.sub(assetWeight).toNumber();
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
- return liabilityWeight.sub(MARGIN_PRECISION).toNumber();
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
  }
@@ -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) {