@drift-labs/sdk 2.18.0 → 2.20.0-beta.0

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.
@@ -47,12 +47,12 @@ exports.DevnetPerpMarkets = [
47
47
  {
48
48
  fullName: 'Bonk',
49
49
  category: ['Meme'],
50
- symbol: 'BONK-PERP',
51
- baseAssetSymbol: 'BONK',
50
+ symbol: '1MBONK-PERP',
51
+ baseAssetSymbol: '1MBONK',
52
52
  marketIndex: 4,
53
53
  oracle: new web3_js_1.PublicKey('6bquU99ktV1VRiHDr8gMhDFt3kMfhCQo5nfNrg2Urvsn'),
54
54
  launchTs: 1677068931000,
55
- oracleSource: __1.OracleSource.PYTH_1000,
55
+ oracleSource: __1.OracleSource.PYTH_1M,
56
56
  },
57
57
  ];
58
58
  exports.MainnetPerpMarkets = [
@@ -96,6 +96,16 @@ exports.MainnetPerpMarkets = [
96
96
  launchTs: 1675802661000,
97
97
  oracleSource: __1.OracleSource.PYTH,
98
98
  },
99
+ {
100
+ fullName: 'Bonk',
101
+ category: ['Meme'],
102
+ symbol: '1MBONK-PERP',
103
+ baseAssetSymbol: '1MBONK',
104
+ marketIndex: 4,
105
+ oracle: new web3_js_1.PublicKey('8ihFLu5FimgTQ1Unh4dVyEHUGodJ5gJQCrQf4KUVB9bN'),
106
+ launchTs: 1677690149000,
107
+ oracleSource: __1.OracleSource.PYTH_1M,
108
+ },
99
109
  ];
100
110
  exports.PerpMarkets = {
101
111
  devnet: exports.DevnetPerpMarkets,
package/lib/dlob/DLOB.js CHANGED
@@ -286,8 +286,9 @@ class DLOB {
286
286
  }
287
287
  }
288
288
  getOrder(orderId, userAccount) {
289
+ const orderSignature = NodeList_1.getOrderSignature(orderId, userAccount);
289
290
  for (const nodeList of this.getNodeLists()) {
290
- const node = nodeList.get(orderId, userAccount);
291
+ const node = nodeList.get(orderSignature);
291
292
  if (node) {
292
293
  return node.order;
293
294
  }
@@ -761,20 +762,32 @@ class DLOB {
761
762
  determineMakerAndTaker(askNode, bidNode) {
762
763
  const askSlot = askNode.order.slot.add(new __1.BN(askNode.order.auctionDuration));
763
764
  const bidSlot = bidNode.order.slot.add(new __1.BN(bidNode.order.auctionDuration));
764
- if (askSlot.lte(bidSlot) && !bidNode.order.postOnly) {
765
+ if (bidNode.order.postOnly && askNode.order.postOnly) {
766
+ return undefined;
767
+ }
768
+ else if (bidNode.order.postOnly) {
769
+ return {
770
+ takerNode: askNode,
771
+ makerNode: bidNode,
772
+ };
773
+ }
774
+ else if (askNode.order.postOnly) {
765
775
  return {
766
776
  takerNode: bidNode,
767
777
  makerNode: askNode,
768
778
  };
769
779
  }
770
- else if (bidSlot.lte(askSlot) && !askNode.order.postOnly) {
780
+ else if (askSlot.lte(bidSlot)) {
771
781
  return {
772
- takerNode: askNode,
773
- makerNode: bidNode,
782
+ takerNode: bidNode,
783
+ makerNode: askNode,
774
784
  };
775
785
  }
776
786
  else {
777
- return undefined;
787
+ return {
788
+ takerNode: askNode,
789
+ makerNode: bidNode,
790
+ };
778
791
  }
779
792
  }
780
793
  getBestAsk(marketIndex, fallbackAsk, slot, marketType, oraclePriceData) {
@@ -21,7 +21,7 @@ export declare class NodeList<NodeType extends keyof DLOBNodeMap> implements DLO
21
21
  remove(order: Order, userAccount: PublicKey): void;
22
22
  getGenerator(): Generator<DLOBNode>;
23
23
  has(order: Order, userAccount: PublicKey): boolean;
24
- get(orderId: number, userAccount: PublicKey): DLOBNodeMap[NodeType] | undefined;
24
+ get(orderSignature: string): DLOBNodeMap[NodeType] | undefined;
25
25
  print(): void;
26
26
  printTop(): void;
27
27
  }
@@ -104,8 +104,8 @@ class NodeList {
104
104
  has(order, userAccount) {
105
105
  return this.nodeMap.has(getOrderSignature(order.orderId, userAccount));
106
106
  }
107
- get(orderId, userAccount) {
108
- return this.nodeMap.get(getOrderSignature(orderId, userAccount));
107
+ get(orderSignature) {
108
+ return this.nodeMap.get(orderSignature);
109
109
  }
110
110
  print() {
111
111
  let currentNode = this.head;
@@ -186,8 +186,8 @@ export declare class DriftClient {
186
186
  getCancelOrderByUserIdIx(userOrderId: number): Promise<TransactionInstruction>;
187
187
  cancelOrders(marketType?: MarketType, marketIndex?: number, direction?: PositionDirection, txParams?: TxParams): Promise<TransactionSignature>;
188
188
  getCancelOrdersIx(marketType: MarketType | null, marketIndex: number | null, direction: PositionDirection | null): Promise<TransactionInstruction>;
189
- fillPerpOrder(userAccountPublicKey: PublicKey, user: UserAccount, order?: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo, txParams?: TxParams): Promise<TransactionSignature>;
190
- getFillPerpOrderIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, order: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo): Promise<TransactionInstruction>;
189
+ fillPerpOrder(userAccountPublicKey: PublicKey, user: UserAccount, order?: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, txParams?: TxParams): Promise<TransactionSignature>;
190
+ getFillPerpOrderIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, order: Pick<Order, 'marketIndex' | 'orderId'>, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo): Promise<TransactionInstruction>;
191
191
  placeSpotOrder(orderParams: OptionalOrderParams, txParams?: TxParams): Promise<TransactionSignature>;
192
192
  getPlaceSpotOrderIx(orderParams: OptionalOrderParams): Promise<TransactionInstruction>;
193
193
  fillSpotOrder(userAccountPublicKey: PublicKey, user: UserAccount, order?: Order, fulfillmentConfig?: SerumV3FulfillmentConfigAccount, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo, txParams?: TxParams): Promise<TransactionSignature>;
@@ -1261,41 +1261,49 @@ class DriftClient {
1261
1261
  const marketIndex = order
1262
1262
  ? order.marketIndex
1263
1263
  : userAccount.orders.find((order) => order.orderId === userAccount.nextOrderId - 1).marketIndex;
1264
+ makerInfo = Array.isArray(makerInfo)
1265
+ ? makerInfo
1266
+ : makerInfo
1267
+ ? [makerInfo]
1268
+ : [];
1264
1269
  const userAccounts = [userAccount];
1265
- if (makerInfo !== undefined) {
1266
- userAccounts.push(makerInfo.makerUserAccount);
1270
+ for (const maker of makerInfo) {
1271
+ userAccounts.push(maker.makerUserAccount);
1267
1272
  }
1268
1273
  const remainingAccounts = this.getRemainingAccounts({
1269
1274
  userAccounts,
1270
1275
  writablePerpMarketIndexes: [marketIndex],
1271
1276
  });
1272
- if (makerInfo) {
1277
+ for (const maker of makerInfo) {
1273
1278
  remainingAccounts.push({
1274
- pubkey: makerInfo.maker,
1279
+ pubkey: maker.maker,
1275
1280
  isWritable: true,
1276
1281
  isSigner: false,
1277
1282
  });
1278
1283
  remainingAccounts.push({
1279
- pubkey: makerInfo.makerStats,
1284
+ pubkey: maker.makerStats,
1280
1285
  isWritable: true,
1281
1286
  isSigner: false,
1282
1287
  });
1283
1288
  }
1284
1289
  if (referrerInfo) {
1285
- remainingAccounts.push({
1286
- pubkey: referrerInfo.referrer,
1287
- isWritable: true,
1288
- isSigner: false,
1289
- });
1290
- remainingAccounts.push({
1291
- pubkey: referrerInfo.referrerStats,
1292
- isWritable: true,
1293
- isSigner: false,
1294
- });
1290
+ const referrerIsMaker = makerInfo.find((maker) => maker.maker.equals(referrerInfo.referrer)) !==
1291
+ undefined;
1292
+ if (!referrerIsMaker) {
1293
+ remainingAccounts.push({
1294
+ pubkey: referrerInfo.referrer,
1295
+ isWritable: true,
1296
+ isSigner: false,
1297
+ });
1298
+ remainingAccounts.push({
1299
+ pubkey: referrerInfo.referrerStats,
1300
+ isWritable: true,
1301
+ isSigner: false,
1302
+ });
1303
+ }
1295
1304
  }
1296
1305
  const orderId = order.orderId;
1297
- const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
1298
- return await this.program.instruction.fillPerpOrder(orderId, makerOrderId, {
1306
+ return await this.program.instruction.fillPerpOrder(orderId, null, {
1299
1307
  accounts: {
1300
1308
  state: await this.getStatePublicKey(),
1301
1309
  filler: fillerPublicKey,
@@ -10,9 +10,12 @@ function getOracleClient(oracleSource, connection) {
10
10
  if (types_1.isVariant(oracleSource, 'pyth')) {
11
11
  return new pythClient_1.PythClient(connection);
12
12
  }
13
- if (types_1.isVariant(oracleSource, 'pyth1000')) {
13
+ if (types_1.isVariant(oracleSource, 'pyth1K')) {
14
14
  return new pythClient_1.PythClient(connection, new anchor_1.BN(1000));
15
15
  }
16
+ if (types_1.isVariant(oracleSource, 'pyth1M')) {
17
+ return new pythClient_1.PythClient(connection, new anchor_1.BN(1000000));
18
+ }
16
19
  // if (isVariant(oracleSource, 'switchboard')) {
17
20
  // return new SwitchboardClient(connection);
18
21
  // }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.18.0",
2
+ "version": "2.20.0-beta.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -6207,6 +6207,7 @@
6207
6207
  {
6208
6208
  "name": "Match",
6209
6209
  "fields": [
6210
+ "publicKey",
6210
6211
  {
6211
6212
  "defined": "usize"
6212
6213
  }
@@ -6237,14 +6238,17 @@
6237
6238
  {
6238
6239
  "name": "Pyth"
6239
6240
  },
6240
- {
6241
- "name": "Pyth1000"
6242
- },
6243
6241
  {
6244
6242
  "name": "Switchboard"
6245
6243
  },
6246
6244
  {
6247
6245
  "name": "QuoteAsset"
6246
+ },
6247
+ {
6248
+ "name": "Pyth1K"
6249
+ },
6250
+ {
6251
+ "name": "Pyth1M"
6248
6252
  }
6249
6253
  ]
6250
6254
  }
@@ -8601,6 +8605,56 @@
8601
8605
  "code": 6227,
8602
8606
  "name": "TierViolationLiquidatingPerpPnl",
8603
8607
  "msg": "TierViolationLiquidatingPerpPnl"
8608
+ },
8609
+ {
8610
+ "code": 6228,
8611
+ "name": "CouldNotLoadUserData",
8612
+ "msg": "CouldNotLoadUserData"
8613
+ },
8614
+ {
8615
+ "code": 6229,
8616
+ "name": "UserWrongMutability",
8617
+ "msg": "UserWrongMutability"
8618
+ },
8619
+ {
8620
+ "code": 6230,
8621
+ "name": "InvalidUserAccount",
8622
+ "msg": "InvalidUserAccount"
8623
+ },
8624
+ {
8625
+ "code": 6231,
8626
+ "name": "CouldNotLoadUserStatsData",
8627
+ "msg": "CouldNotLoadUserData"
8628
+ },
8629
+ {
8630
+ "code": 6232,
8631
+ "name": "UserStatsWrongMutability",
8632
+ "msg": "UserWrongMutability"
8633
+ },
8634
+ {
8635
+ "code": 6233,
8636
+ "name": "InvalidUserStatsAccount",
8637
+ "msg": "InvalidUserAccount"
8638
+ },
8639
+ {
8640
+ "code": 6234,
8641
+ "name": "UserNotFound",
8642
+ "msg": "UserNotFound"
8643
+ },
8644
+ {
8645
+ "code": 6235,
8646
+ "name": "UnableToLoadUserAccount",
8647
+ "msg": "UnableToLoadUserAccount"
8648
+ },
8649
+ {
8650
+ "code": 6236,
8651
+ "name": "UserStatsNotFound",
8652
+ "msg": "UserStatsNotFound"
8653
+ },
8654
+ {
8655
+ "code": 6237,
8656
+ "name": "UnableToLoadUserStatsAccount",
8657
+ "msg": "UnableToLoadUserStatsAccount"
8604
8658
  }
8605
8659
  ]
8606
8660
  }
package/lib/math/amm.js CHANGED
@@ -203,26 +203,21 @@ function calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBas
203
203
  exports.calculateMarketOpenBidAsk = calculateMarketOpenBidAsk;
204
204
  function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, directionalSpread, maxSpread) {
205
205
  if (baseAssetAmountWithAmm.eq(numericConstants_1.ZERO)) {
206
- return 0;
206
+ return 1;
207
207
  }
208
- const defaultLargeBidAskFactor = numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(new anchor_1.BN(10));
208
+ const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(new anchor_1.BN(10));
209
209
  // inventory skew
210
210
  const [openBids, openAsks] = calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve);
211
- const minSideLiquidity = anchor_1.BN.max(new anchor_1.BN(1), anchor_1.BN.min(openBids.abs(), openAsks.abs()));
212
- const inventoryScaleMaxBN = anchor_1.BN.max(defaultLargeBidAskFactor, new anchor_1.BN(maxSpread / 2)
211
+ const minSideLiquidity = anchor_1.BN.min(openBids.abs(), openAsks.abs());
212
+ const inventoryScaleBN = anchor_1.BN.min(baseAssetAmountWithAmm
213
+ .mul(numericConstants_1.PERCENTAGE_PRECISION)
214
+ .div(anchor_1.BN.max(minSideLiquidity, numericConstants_1.ONE))
215
+ .abs(), numericConstants_1.PERCENTAGE_PRECISION);
216
+ const inventoryScaleMaxBN = anchor_1.BN.max(MAX_BID_ASK_INVENTORY_SKEW_FACTOR, new anchor_1.BN(maxSpread)
213
217
  .mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
214
218
  .div(new anchor_1.BN(Math.max(directionalSpread, 1))));
215
- const inventoryScaleBN = baseAssetAmountWithAmm
216
- .mul(anchor_1.BN.max(baseAssetAmountWithAmm.abs(), numericConstants_1.BASE_PRECISION))
217
- .div(numericConstants_1.BASE_PRECISION)
218
- .mul(defaultLargeBidAskFactor)
219
- .div(minSideLiquidity)
220
- .abs();
221
- const inventoryScale = anchor_1.BN.min(inventoryScaleMaxBN, inventoryScaleBN).toNumber() /
222
- numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
223
- const inventoryScaleMax = inventoryScaleMaxBN.toNumber() / numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
224
- const inventorySpreadScale = Math.min(inventoryScaleMax, 1 + inventoryScale);
225
- return inventorySpreadScale;
219
+ const inventoryScaleCapped = anchor_1.BN.min(inventoryScaleMaxBN, numericConstants_1.BID_ASK_SPREAD_PRECISION.add(inventoryScaleMaxBN.mul(inventoryScaleBN).div(numericConstants_1.PERCENTAGE_PRECISION))).toNumber() / numericConstants_1.BID_ASK_SPREAD_PRECISION.toNumber();
220
+ return inventoryScaleCapped;
226
221
  }
227
222
  exports.calculateInventoryScale = calculateInventoryScale;
228
223
  function calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, netBaseAssetAmount, reservePrice, totalFeeMinusDistributions) {
package/lib/math/trade.js CHANGED
@@ -499,7 +499,7 @@ function calculateEstimatedSpotEntryPrice(assetType, amount, direction, market,
499
499
  .div(basePrecision);
500
500
  cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
501
501
  cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
502
- worstPrice = dlobLimitOrder;
502
+ worstPrice = dlobLimitOrderPrice;
503
503
  dlobLimitOrder = dlobLimitOrders.next().value;
504
504
  }
505
505
  else {
@@ -8,16 +8,17 @@ export declare class SerumSubscriber {
8
8
  connection: Connection;
9
9
  programId: PublicKey;
10
10
  marketAddress: PublicKey;
11
- accountLoader: BulkAccountLoader;
11
+ subscriptionType: 'polling' | 'websocket';
12
+ accountLoader: BulkAccountLoader | undefined;
12
13
  market: Market;
13
14
  subscribed: boolean;
14
15
  asksAddress: PublicKey;
15
16
  asks: Orderbook;
16
- asksCallbackId: string;
17
+ asksCallbackId: string | number;
17
18
  lastAsksSlot: number;
18
19
  bidsAddress: PublicKey;
19
20
  bids: Orderbook;
20
- bidsCallbackId: string;
21
+ bidsCallbackId: string | number;
21
22
  lastBidsSlot: number;
22
23
  constructor(config: SerumMarketSubscriberConfig);
23
24
  subscribe(): Promise<void>;
@@ -9,7 +9,13 @@ class SerumSubscriber {
9
9
  this.connection = config.connection;
10
10
  this.programId = config.programId;
11
11
  this.marketAddress = config.marketAddress;
12
- this.accountLoader = config.accountSubscription.accountLoader;
12
+ if (config.accountSubscription.type === 'polling') {
13
+ this.subscriptionType = 'polling';
14
+ this.accountLoader = config.accountSubscription.accountLoader;
15
+ }
16
+ else {
17
+ this.subscriptionType = 'websocket';
18
+ }
13
19
  }
14
20
  async subscribe() {
15
21
  if (this.subscribed) {
@@ -18,16 +24,32 @@ class SerumSubscriber {
18
24
  this.market = await serum_1.Market.load(this.connection, this.marketAddress, undefined, this.programId);
19
25
  this.asksAddress = this.market.asksAddress;
20
26
  this.asks = await this.market.loadAsks(this.connection);
21
- this.asksCallbackId = await this.accountLoader.addAccount(this.asksAddress, (buffer, slot) => {
22
- this.lastAsksSlot = slot;
23
- this.asks = serum_1.Orderbook.decode(this.market, buffer);
24
- });
27
+ if (this.subscriptionType === 'websocket') {
28
+ this.asksCallbackId = this.connection.onAccountChange(this.asksAddress, (accountInfo, ctx) => {
29
+ this.lastAsksSlot = ctx.slot;
30
+ this.asks = serum_1.Orderbook.decode(this.market, accountInfo.data);
31
+ });
32
+ }
33
+ else {
34
+ this.asksCallbackId = await this.accountLoader.addAccount(this.asksAddress, (buffer, slot) => {
35
+ this.lastAsksSlot = slot;
36
+ this.asks = serum_1.Orderbook.decode(this.market, buffer);
37
+ });
38
+ }
25
39
  this.bidsAddress = this.market.bidsAddress;
26
40
  this.bids = await this.market.loadBids(this.connection);
27
- this.bidsCallbackId = await this.accountLoader.addAccount(this.bidsAddress, (buffer, slot) => {
28
- this.lastBidsSlot = slot;
29
- this.bids = serum_1.Orderbook.decode(this.market, buffer);
30
- });
41
+ if (this.subscriptionType === 'websocket') {
42
+ this.bidsCallbackId = this.connection.onAccountChange(this.bidsAddress, (accountInfo, ctx) => {
43
+ this.lastBidsSlot = ctx.slot;
44
+ this.bids = serum_1.Orderbook.decode(this.market, accountInfo.data);
45
+ });
46
+ }
47
+ else {
48
+ this.bidsCallbackId = await this.accountLoader.addAccount(this.bidsAddress, (buffer, slot) => {
49
+ this.lastBidsSlot = slot;
50
+ this.bids = serum_1.Orderbook.decode(this.market, buffer);
51
+ });
52
+ }
31
53
  this.subscribed = true;
32
54
  }
33
55
  getBestBid() {
@@ -48,8 +70,15 @@ class SerumSubscriber {
48
70
  if (!this.subscribed) {
49
71
  return;
50
72
  }
51
- this.accountLoader.removeAccount(this.asksAddress, this.asksCallbackId);
52
- this.accountLoader.removeAccount(this.bidsAddress, this.bidsCallbackId);
73
+ // remove listeners
74
+ if (this.subscriptionType === 'websocket') {
75
+ await this.connection.removeAccountChangeListener(this.asksCallbackId);
76
+ await this.connection.removeAccountChangeListener(this.bidsCallbackId);
77
+ }
78
+ else {
79
+ this.accountLoader.removeAccount(this.asksAddress, this.asksCallbackId);
80
+ this.accountLoader.removeAccount(this.bidsAddress, this.bidsCallbackId);
81
+ }
53
82
  this.subscribed = false;
54
83
  }
55
84
  }
@@ -7,5 +7,7 @@ export declare type SerumMarketSubscriberConfig = {
7
7
  accountSubscription: {
8
8
  type: 'polling';
9
9
  accountLoader: BulkAccountLoader;
10
+ } | {
11
+ type: 'websocket';
10
12
  };
11
13
  };
package/lib/types.d.ts CHANGED
@@ -130,8 +130,11 @@ export declare class OracleSource {
130
130
  static readonly PYTH: {
131
131
  pyth: {};
132
132
  };
133
- static readonly PYTH_1000: {
134
- pyth1000: {};
133
+ static readonly PYTH_1K: {
134
+ pyth1K: {};
135
+ };
136
+ static readonly PYTH_1M: {
137
+ pyth1M: {};
135
138
  };
136
139
  static readonly QUOTE_ASSET: {
137
140
  quoteAsset: {};
@@ -925,7 +928,7 @@ export declare type MakerInfo = {
925
928
  maker: PublicKey;
926
929
  makerStats: PublicKey;
927
930
  makerUserAccount: UserAccount;
928
- order: Order;
931
+ order?: Order;
929
932
  };
930
933
  export declare type TakerInfo = {
931
934
  taker: PublicKey;
package/lib/types.js CHANGED
@@ -78,7 +78,8 @@ class OracleSource {
78
78
  }
79
79
  exports.OracleSource = OracleSource;
80
80
  OracleSource.PYTH = { pyth: {} };
81
- OracleSource.PYTH_1000 = { pyth1000: {} };
81
+ OracleSource.PYTH_1K = { pyth1K: {} };
82
+ OracleSource.PYTH_1M = { pyth1M: {} };
82
83
  // static readonly SWITCHBOARD = { switchboard: {} };
83
84
  OracleSource.QUOTE_ASSET = { quoteAsset: {} };
84
85
  class OrderType {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.18.0",
3
+ "version": "2.20.0-beta.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -57,12 +57,12 @@ export const DevnetPerpMarkets: PerpMarketConfig[] = [
57
57
  {
58
58
  fullName: 'Bonk',
59
59
  category: ['Meme'],
60
- symbol: 'BONK-PERP',
61
- baseAssetSymbol: 'BONK',
60
+ symbol: '1MBONK-PERP',
61
+ baseAssetSymbol: '1MBONK',
62
62
  marketIndex: 4,
63
63
  oracle: new PublicKey('6bquU99ktV1VRiHDr8gMhDFt3kMfhCQo5nfNrg2Urvsn'),
64
64
  launchTs: 1677068931000,
65
- oracleSource: OracleSource.PYTH_1000,
65
+ oracleSource: OracleSource.PYTH_1M,
66
66
  },
67
67
  ];
68
68
 
@@ -107,6 +107,16 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
107
107
  launchTs: 1675802661000,
108
108
  oracleSource: OracleSource.PYTH,
109
109
  },
110
+ {
111
+ fullName: 'Bonk',
112
+ category: ['Meme'],
113
+ symbol: '1MBONK-PERP',
114
+ baseAssetSymbol: '1MBONK',
115
+ marketIndex: 4,
116
+ oracle: new PublicKey('8ihFLu5FimgTQ1Unh4dVyEHUGodJ5gJQCrQf4KUVB9bN'),
117
+ launchTs: 1677690149000,
118
+ oracleSource: OracleSource.PYTH_1M,
119
+ },
110
120
  ];
111
121
 
112
122
  export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
package/src/dlob/DLOB.ts CHANGED
@@ -450,8 +450,9 @@ export class DLOB {
450
450
  }
451
451
 
452
452
  public getOrder(orderId: number, userAccount: PublicKey): Order | undefined {
453
+ const orderSignature = getOrderSignature(orderId, userAccount);
453
454
  for (const nodeList of this.getNodeLists()) {
454
- const node = nodeList.get(orderId, userAccount);
455
+ const node = nodeList.get(orderSignature);
455
456
  if (node) {
456
457
  return node.order;
457
458
  }
@@ -1383,18 +1384,29 @@ export class DLOB {
1383
1384
  const bidSlot = bidNode.order.slot.add(
1384
1385
  new BN(bidNode.order.auctionDuration)
1385
1386
  );
1386
- if (askSlot.lte(bidSlot) && !bidNode.order.postOnly) {
1387
+
1388
+ if (bidNode.order.postOnly && askNode.order.postOnly) {
1389
+ return undefined;
1390
+ } else if (bidNode.order.postOnly) {
1391
+ return {
1392
+ takerNode: askNode,
1393
+ makerNode: bidNode,
1394
+ };
1395
+ } else if (askNode.order.postOnly) {
1387
1396
  return {
1388
1397
  takerNode: bidNode,
1389
1398
  makerNode: askNode,
1390
1399
  };
1391
- } else if (bidSlot.lte(askSlot) && !askNode.order.postOnly) {
1400
+ } else if (askSlot.lte(bidSlot)) {
1401
+ return {
1402
+ takerNode: bidNode,
1403
+ makerNode: askNode,
1404
+ };
1405
+ } else {
1392
1406
  return {
1393
1407
  takerNode: askNode,
1394
1408
  makerNode: bidNode,
1395
1409
  };
1396
- } else {
1397
- return undefined;
1398
1410
  }
1399
1411
  }
1400
1412
 
@@ -146,11 +146,8 @@ export class NodeList<NodeType extends keyof DLOBNodeMap>
146
146
  return this.nodeMap.has(getOrderSignature(order.orderId, userAccount));
147
147
  }
148
148
 
149
- public get(
150
- orderId: number,
151
- userAccount: PublicKey
152
- ): DLOBNodeMap[NodeType] | undefined {
153
- return this.nodeMap.get(getOrderSignature(orderId, userAccount));
149
+ public get(orderSignature: string): DLOBNodeMap[NodeType] | undefined {
150
+ return this.nodeMap.get(orderSignature);
154
151
  }
155
152
 
156
153
  public print(): void {
@@ -2214,7 +2214,7 @@ export class DriftClient {
2214
2214
  userAccountPublicKey: PublicKey,
2215
2215
  user: UserAccount,
2216
2216
  order?: Pick<Order, 'marketIndex' | 'orderId'>,
2217
- makerInfo?: MakerInfo,
2217
+ makerInfo?: MakerInfo | MakerInfo[],
2218
2218
  referrerInfo?: ReferrerInfo,
2219
2219
  txParams?: TxParams
2220
2220
  ): Promise<TransactionSignature> {
@@ -2240,7 +2240,7 @@ export class DriftClient {
2240
2240
  userAccountPublicKey: PublicKey,
2241
2241
  userAccount: UserAccount,
2242
2242
  order: Pick<Order, 'marketIndex' | 'orderId'>,
2243
- makerInfo?: MakerInfo,
2243
+ makerInfo?: MakerInfo | MakerInfo[],
2244
2244
  referrerInfo?: ReferrerInfo
2245
2245
  ): Promise<TransactionInstruction> {
2246
2246
  const userStatsPublicKey = getUserStatsAccountPublicKey(
@@ -2257,45 +2257,54 @@ export class DriftClient {
2257
2257
  (order) => order.orderId === userAccount.nextOrderId - 1
2258
2258
  ).marketIndex;
2259
2259
 
2260
+ makerInfo = Array.isArray(makerInfo)
2261
+ ? makerInfo
2262
+ : makerInfo
2263
+ ? [makerInfo]
2264
+ : [];
2265
+
2260
2266
  const userAccounts = [userAccount];
2261
- if (makerInfo !== undefined) {
2262
- userAccounts.push(makerInfo.makerUserAccount);
2267
+ for (const maker of makerInfo) {
2268
+ userAccounts.push(maker.makerUserAccount);
2263
2269
  }
2264
2270
  const remainingAccounts = this.getRemainingAccounts({
2265
2271
  userAccounts,
2266
2272
  writablePerpMarketIndexes: [marketIndex],
2267
2273
  });
2268
2274
 
2269
- if (makerInfo) {
2275
+ for (const maker of makerInfo) {
2270
2276
  remainingAccounts.push({
2271
- pubkey: makerInfo.maker,
2277
+ pubkey: maker.maker,
2272
2278
  isWritable: true,
2273
2279
  isSigner: false,
2274
2280
  });
2275
2281
  remainingAccounts.push({
2276
- pubkey: makerInfo.makerStats,
2282
+ pubkey: maker.makerStats,
2277
2283
  isWritable: true,
2278
2284
  isSigner: false,
2279
2285
  });
2280
2286
  }
2281
2287
 
2282
2288
  if (referrerInfo) {
2283
- remainingAccounts.push({
2284
- pubkey: referrerInfo.referrer,
2285
- isWritable: true,
2286
- isSigner: false,
2287
- });
2288
- remainingAccounts.push({
2289
- pubkey: referrerInfo.referrerStats,
2290
- isWritable: true,
2291
- isSigner: false,
2292
- });
2289
+ const referrerIsMaker =
2290
+ makerInfo.find((maker) => maker.maker.equals(referrerInfo.referrer)) !==
2291
+ undefined;
2292
+ if (!referrerIsMaker) {
2293
+ remainingAccounts.push({
2294
+ pubkey: referrerInfo.referrer,
2295
+ isWritable: true,
2296
+ isSigner: false,
2297
+ });
2298
+ remainingAccounts.push({
2299
+ pubkey: referrerInfo.referrerStats,
2300
+ isWritable: true,
2301
+ isSigner: false,
2302
+ });
2303
+ }
2293
2304
  }
2294
2305
 
2295
2306
  const orderId = order.orderId;
2296
- const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
2297
-
2298
- return await this.program.instruction.fillPerpOrder(orderId, makerOrderId, {
2307
+ return await this.program.instruction.fillPerpOrder(orderId, null, {
2299
2308
  accounts: {
2300
2309
  state: await this.getStatePublicKey(),
2301
2310
  filler: fillerPublicKey,
@@ -14,10 +14,14 @@ export function getOracleClient(
14
14
  return new PythClient(connection);
15
15
  }
16
16
 
17
- if (isVariant(oracleSource, 'pyth1000')) {
17
+ if (isVariant(oracleSource, 'pyth1K')) {
18
18
  return new PythClient(connection, new BN(1000));
19
19
  }
20
20
 
21
+ if (isVariant(oracleSource, 'pyth1M')) {
22
+ return new PythClient(connection, new BN(1000000));
23
+ }
24
+
21
25
  // if (isVariant(oracleSource, 'switchboard')) {
22
26
  // return new SwitchboardClient(connection);
23
27
  // }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.18.0",
2
+ "version": "2.20.0-beta.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -6207,6 +6207,7 @@
6207
6207
  {
6208
6208
  "name": "Match",
6209
6209
  "fields": [
6210
+ "publicKey",
6210
6211
  {
6211
6212
  "defined": "usize"
6212
6213
  }
@@ -6237,14 +6238,17 @@
6237
6238
  {
6238
6239
  "name": "Pyth"
6239
6240
  },
6240
- {
6241
- "name": "Pyth1000"
6242
- },
6243
6241
  {
6244
6242
  "name": "Switchboard"
6245
6243
  },
6246
6244
  {
6247
6245
  "name": "QuoteAsset"
6246
+ },
6247
+ {
6248
+ "name": "Pyth1K"
6249
+ },
6250
+ {
6251
+ "name": "Pyth1M"
6248
6252
  }
6249
6253
  ]
6250
6254
  }
@@ -8601,6 +8605,56 @@
8601
8605
  "code": 6227,
8602
8606
  "name": "TierViolationLiquidatingPerpPnl",
8603
8607
  "msg": "TierViolationLiquidatingPerpPnl"
8608
+ },
8609
+ {
8610
+ "code": 6228,
8611
+ "name": "CouldNotLoadUserData",
8612
+ "msg": "CouldNotLoadUserData"
8613
+ },
8614
+ {
8615
+ "code": 6229,
8616
+ "name": "UserWrongMutability",
8617
+ "msg": "UserWrongMutability"
8618
+ },
8619
+ {
8620
+ "code": 6230,
8621
+ "name": "InvalidUserAccount",
8622
+ "msg": "InvalidUserAccount"
8623
+ },
8624
+ {
8625
+ "code": 6231,
8626
+ "name": "CouldNotLoadUserStatsData",
8627
+ "msg": "CouldNotLoadUserData"
8628
+ },
8629
+ {
8630
+ "code": 6232,
8631
+ "name": "UserStatsWrongMutability",
8632
+ "msg": "UserWrongMutability"
8633
+ },
8634
+ {
8635
+ "code": 6233,
8636
+ "name": "InvalidUserStatsAccount",
8637
+ "msg": "InvalidUserAccount"
8638
+ },
8639
+ {
8640
+ "code": 6234,
8641
+ "name": "UserNotFound",
8642
+ "msg": "UserNotFound"
8643
+ },
8644
+ {
8645
+ "code": 6235,
8646
+ "name": "UnableToLoadUserAccount",
8647
+ "msg": "UnableToLoadUserAccount"
8648
+ },
8649
+ {
8650
+ "code": 6236,
8651
+ "name": "UserStatsNotFound",
8652
+ "msg": "UserStatsNotFound"
8653
+ },
8654
+ {
8655
+ "code": 6237,
8656
+ "name": "UnableToLoadUserStatsAccount",
8657
+ "msg": "UnableToLoadUserStatsAccount"
8604
8658
  }
8605
8659
  ]
8606
8660
  }
package/src/math/amm.ts CHANGED
@@ -11,7 +11,6 @@ import {
11
11
  MARGIN_PRECISION,
12
12
  PRICE_DIV_PEG,
13
13
  PERCENTAGE_PRECISION,
14
- BASE_PRECISION,
15
14
  DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT,
16
15
  TWO,
17
16
  } from '../constants/numericConstants';
@@ -362,10 +361,12 @@ export function calculateInventoryScale(
362
361
  maxSpread: number
363
362
  ): number {
364
363
  if (baseAssetAmountWithAmm.eq(ZERO)) {
365
- return 0;
364
+ return 1;
366
365
  }
367
366
 
368
- const defaultLargeBidAskFactor = BID_ASK_SPREAD_PRECISION.mul(new BN(10));
367
+ const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = BID_ASK_SPREAD_PRECISION.mul(
368
+ new BN(10)
369
+ );
369
370
  // inventory skew
370
371
  const [openBids, openAsks] = calculateMarketOpenBidAsk(
371
372
  baseAssetReserve,
@@ -373,34 +374,32 @@ export function calculateInventoryScale(
373
374
  maxBaseAssetReserve
374
375
  );
375
376
 
376
- const minSideLiquidity = BN.max(
377
- new BN(1),
378
- BN.min(openBids.abs(), openAsks.abs())
377
+ const minSideLiquidity = BN.min(openBids.abs(), openAsks.abs());
378
+
379
+ const inventoryScaleBN = BN.min(
380
+ baseAssetAmountWithAmm
381
+ .mul(PERCENTAGE_PRECISION)
382
+ .div(BN.max(minSideLiquidity, ONE))
383
+ .abs(),
384
+ PERCENTAGE_PRECISION
379
385
  );
380
386
 
381
387
  const inventoryScaleMaxBN = BN.max(
382
- defaultLargeBidAskFactor,
383
- new BN(maxSpread / 2)
388
+ MAX_BID_ASK_INVENTORY_SKEW_FACTOR,
389
+ new BN(maxSpread)
384
390
  .mul(BID_ASK_SPREAD_PRECISION)
385
391
  .div(new BN(Math.max(directionalSpread, 1)))
386
392
  );
387
393
 
388
- const inventoryScaleBN = baseAssetAmountWithAmm
389
- .mul(BN.max(baseAssetAmountWithAmm.abs(), BASE_PRECISION))
390
- .div(BASE_PRECISION)
391
- .mul(defaultLargeBidAskFactor)
392
- .div(minSideLiquidity)
393
- .abs();
394
-
395
- const inventoryScale =
396
- BN.min(inventoryScaleMaxBN, inventoryScaleBN).toNumber() /
397
- BID_ASK_SPREAD_PRECISION.toNumber();
398
-
399
- const inventoryScaleMax =
400
- inventoryScaleMaxBN.toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
401
- const inventorySpreadScale = Math.min(inventoryScaleMax, 1 + inventoryScale);
394
+ const inventoryScaleCapped =
395
+ BN.min(
396
+ inventoryScaleMaxBN,
397
+ BID_ASK_SPREAD_PRECISION.add(
398
+ inventoryScaleMaxBN.mul(inventoryScaleBN).div(PERCENTAGE_PRECISION)
399
+ )
400
+ ).toNumber() / BID_ASK_SPREAD_PRECISION.toNumber();
402
401
 
403
- return inventorySpreadScale;
402
+ return inventoryScaleCapped;
404
403
  }
405
404
 
406
405
  export function calculateEffectiveLeverage(
package/src/math/trade.ts CHANGED
@@ -782,7 +782,7 @@ export function calculateEstimatedSpotEntryPrice(
782
782
  cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled);
783
783
  cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled);
784
784
 
785
- worstPrice = dlobLimitOrder;
785
+ worstPrice = dlobLimitOrderPrice;
786
786
 
787
787
  dlobLimitOrder = dlobLimitOrders.next().value;
788
788
  } else {
@@ -9,26 +9,32 @@ export class SerumSubscriber {
9
9
  connection: Connection;
10
10
  programId: PublicKey;
11
11
  marketAddress: PublicKey;
12
- accountLoader: BulkAccountLoader;
12
+ subscriptionType: 'polling' | 'websocket';
13
+ accountLoader: BulkAccountLoader | undefined;
13
14
  market: Market;
14
15
 
15
16
  subscribed: boolean;
16
17
 
17
18
  asksAddress: PublicKey;
18
19
  asks: Orderbook;
19
- asksCallbackId: string;
20
+ asksCallbackId: string | number;
20
21
  lastAsksSlot: number;
21
22
 
22
23
  bidsAddress: PublicKey;
23
24
  bids: Orderbook;
24
- bidsCallbackId: string;
25
+ bidsCallbackId: string | number;
25
26
  lastBidsSlot: number;
26
27
 
27
28
  public constructor(config: SerumMarketSubscriberConfig) {
28
29
  this.connection = config.connection;
29
30
  this.programId = config.programId;
30
31
  this.marketAddress = config.marketAddress;
31
- this.accountLoader = config.accountSubscription.accountLoader;
32
+ if (config.accountSubscription.type === 'polling') {
33
+ this.subscriptionType = 'polling';
34
+ this.accountLoader = config.accountSubscription.accountLoader;
35
+ } else {
36
+ this.subscriptionType = 'websocket';
37
+ }
32
38
  }
33
39
 
34
40
  public async subscribe(): Promise<void> {
@@ -46,24 +52,44 @@ export class SerumSubscriber {
46
52
  this.asksAddress = this.market.asksAddress;
47
53
  this.asks = await this.market.loadAsks(this.connection);
48
54
 
49
- this.asksCallbackId = await this.accountLoader.addAccount(
50
- this.asksAddress,
51
- (buffer, slot) => {
52
- this.lastAsksSlot = slot;
53
- this.asks = Orderbook.decode(this.market, buffer);
54
- }
55
- );
55
+ if (this.subscriptionType === 'websocket') {
56
+ this.asksCallbackId = this.connection.onAccountChange(
57
+ this.asksAddress,
58
+ (accountInfo, ctx) => {
59
+ this.lastAsksSlot = ctx.slot;
60
+ this.asks = Orderbook.decode(this.market, accountInfo.data);
61
+ }
62
+ );
63
+ } else {
64
+ this.asksCallbackId = await this.accountLoader.addAccount(
65
+ this.asksAddress,
66
+ (buffer, slot) => {
67
+ this.lastAsksSlot = slot;
68
+ this.asks = Orderbook.decode(this.market, buffer);
69
+ }
70
+ );
71
+ }
56
72
 
57
73
  this.bidsAddress = this.market.bidsAddress;
58
74
  this.bids = await this.market.loadBids(this.connection);
59
75
 
60
- this.bidsCallbackId = await this.accountLoader.addAccount(
61
- this.bidsAddress,
62
- (buffer, slot) => {
63
- this.lastBidsSlot = slot;
64
- this.bids = Orderbook.decode(this.market, buffer);
65
- }
66
- );
76
+ if (this.subscriptionType === 'websocket') {
77
+ this.bidsCallbackId = this.connection.onAccountChange(
78
+ this.bidsAddress,
79
+ (accountInfo, ctx) => {
80
+ this.lastBidsSlot = ctx.slot;
81
+ this.bids = Orderbook.decode(this.market, accountInfo.data);
82
+ }
83
+ );
84
+ } else {
85
+ this.bidsCallbackId = await this.accountLoader.addAccount(
86
+ this.bidsAddress,
87
+ (buffer, slot) => {
88
+ this.lastBidsSlot = slot;
89
+ this.bids = Orderbook.decode(this.market, buffer);
90
+ }
91
+ );
92
+ }
67
93
 
68
94
  this.subscribed = true;
69
95
  }
@@ -91,8 +117,24 @@ export class SerumSubscriber {
91
117
  return;
92
118
  }
93
119
 
94
- this.accountLoader.removeAccount(this.asksAddress, this.asksCallbackId);
95
- this.accountLoader.removeAccount(this.bidsAddress, this.bidsCallbackId);
120
+ // remove listeners
121
+ if (this.subscriptionType === 'websocket') {
122
+ await this.connection.removeAccountChangeListener(
123
+ this.asksCallbackId as number
124
+ );
125
+ await this.connection.removeAccountChangeListener(
126
+ this.bidsCallbackId as number
127
+ );
128
+ } else {
129
+ this.accountLoader.removeAccount(
130
+ this.asksAddress,
131
+ this.asksCallbackId as string
132
+ );
133
+ this.accountLoader.removeAccount(
134
+ this.bidsAddress,
135
+ this.bidsCallbackId as string
136
+ );
137
+ }
96
138
 
97
139
  this.subscribed = false;
98
140
  }
@@ -5,9 +5,13 @@ export type SerumMarketSubscriberConfig = {
5
5
  connection: Connection;
6
6
  programId: PublicKey;
7
7
  marketAddress: PublicKey;
8
- accountSubscription: {
9
- // enables use to add web sockets in the future
10
- type: 'polling';
11
- accountLoader: BulkAccountLoader;
12
- };
8
+ accountSubscription:
9
+ | {
10
+ // enables use to add web sockets in the future
11
+ type: 'polling';
12
+ accountLoader: BulkAccountLoader;
13
+ }
14
+ | {
15
+ type: 'websocket';
16
+ };
13
17
  };
package/src/types.ts CHANGED
@@ -76,7 +76,8 @@ export class DepositDirection {
76
76
 
77
77
  export class OracleSource {
78
78
  static readonly PYTH = { pyth: {} };
79
- static readonly PYTH_1000 = { pyth1000: {} };
79
+ static readonly PYTH_1K = { pyth1K: {} };
80
+ static readonly PYTH_1M = { pyth1M: {} };
80
81
  // static readonly SWITCHBOARD = { switchboard: {} };
81
82
  static readonly QUOTE_ASSET = { quoteAsset: {} };
82
83
  }
@@ -901,7 +902,7 @@ export type MakerInfo = {
901
902
  maker: PublicKey;
902
903
  makerStats: PublicKey;
903
904
  makerUserAccount: UserAccount;
904
- order: Order;
905
+ order?: Order;
905
906
  };
906
907
 
907
908
  export type TakerInfo = {
package/tests/amm/test.ts CHANGED
@@ -6,8 +6,10 @@ import {
6
6
  QUOTE_PRECISION,
7
7
  calculateSpreadBN,
8
8
  ZERO,
9
+ ONE,
9
10
  calculateLiveOracleStd,
10
11
  calculateLiveOracleTwap,
12
+ calculateInventoryScale,
11
13
  } from '../../src';
12
14
  import { mockPerpMarkets } from '../dlob/helpers';
13
15
 
@@ -36,6 +38,176 @@ class AMMSpreadTerms {
36
38
  }
37
39
 
38
40
  describe('AMM Tests', () => {
41
+ it('Spread Maths', () => {
42
+ let iscale = calculateInventoryScale(
43
+ ZERO,
44
+ AMM_RESERVE_PRECISION,
45
+ AMM_RESERVE_PRECISION.div(new BN(2)),
46
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
47
+ 250,
48
+ 30000,
49
+ );
50
+ assert(iscale == 1);
51
+
52
+ iscale = calculateInventoryScale(
53
+ ONE,
54
+ AMM_RESERVE_PRECISION,
55
+ AMM_RESERVE_PRECISION.div(new BN(2)),
56
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
57
+ 250,
58
+ 30000,
59
+ );
60
+ assert(iscale == 1);
61
+
62
+ let baa = new BN(1000);
63
+ iscale = calculateInventoryScale(
64
+ baa,
65
+ AMM_RESERVE_PRECISION.add(baa),
66
+ AMM_RESERVE_PRECISION.div(new BN(2)),
67
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
68
+ 250,
69
+ 30000,
70
+ );
71
+ console.log(iscale);
72
+ assert(iscale == 1.00024);
73
+
74
+ baa = new BN(100000);
75
+ iscale = calculateInventoryScale(
76
+ baa,
77
+ AMM_RESERVE_PRECISION.add(baa),
78
+ AMM_RESERVE_PRECISION.div(new BN(2)),
79
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
80
+ 250,
81
+ 30000,
82
+ );
83
+ console.log(iscale);
84
+ assert(iscale == 1.024);
85
+
86
+ baa = new BN(1000000);
87
+ iscale = calculateInventoryScale(
88
+ baa,
89
+ AMM_RESERVE_PRECISION.add(baa),
90
+ AMM_RESERVE_PRECISION.div(new BN(2)),
91
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
92
+ 250,
93
+ 30000,
94
+ );
95
+ console.log(iscale);
96
+ assert(iscale == 1.24048);
97
+
98
+ baa = new BN(10000000); // 2%
99
+ iscale = calculateInventoryScale(
100
+ baa,
101
+ AMM_RESERVE_PRECISION.add(baa),
102
+ AMM_RESERVE_PRECISION.div(new BN(2)),
103
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
104
+ 250,
105
+ 30000,
106
+ );
107
+ console.log(iscale);
108
+ assert(iscale == 3.44896);
109
+
110
+ baa = new BN(50000000); // 10%
111
+ iscale = calculateInventoryScale(
112
+ baa,
113
+ AMM_RESERVE_PRECISION.add(baa),
114
+ AMM_RESERVE_PRECISION.div(new BN(2)),
115
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
116
+ 250,
117
+ 30000,
118
+ );
119
+ console.log(iscale);
120
+ assert(iscale == 14.33332);
121
+
122
+
123
+ baa = AMM_RESERVE_PRECISION.div(new BN(4)); // 50%
124
+ iscale = calculateInventoryScale(
125
+ baa,
126
+ AMM_RESERVE_PRECISION.add(baa),
127
+ AMM_RESERVE_PRECISION.div(new BN(2)),
128
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
129
+ 250,
130
+ 30000,
131
+ );
132
+ console.log(iscale);
133
+ assert(iscale == 120); //100%
134
+
135
+ baa = AMM_RESERVE_PRECISION.div(new BN(4)); // 50%
136
+ iscale = calculateInventoryScale(
137
+ baa,
138
+ AMM_RESERVE_PRECISION.add(baa),
139
+ AMM_RESERVE_PRECISION.div(new BN(2)),
140
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
141
+ 250,
142
+ 30000 * 2,
143
+ );
144
+ console.log(iscale);
145
+ assert(iscale == 120 * 2); //100%
146
+
147
+ baa = AMM_RESERVE_PRECISION.div(new BN(5)); // <50%
148
+ iscale = calculateInventoryScale(
149
+ baa,
150
+ AMM_RESERVE_PRECISION.add(baa),
151
+ AMM_RESERVE_PRECISION.div(new BN(2)),
152
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
153
+ 250,
154
+ 30000 * 2,
155
+ );
156
+ assert(iscale == 160.99984);
157
+
158
+ baa = new BN(855329058);
159
+ iscale = calculateInventoryScale(
160
+ baa,
161
+ AMM_RESERVE_PRECISION.add(baa),
162
+ AMM_RESERVE_PRECISION.div(new BN(2)),
163
+ AMM_RESERVE_PRECISION,
164
+ 250,
165
+ 30000,
166
+ ); // >100%
167
+ assert(iscale == 120);
168
+ assert(250*iscale == 30000);
169
+
170
+ iscale = calculateInventoryScale(
171
+ baa,
172
+ AMM_RESERVE_PRECISION.add(baa), // ~85%
173
+ AMM_RESERVE_PRECISION.div(new BN(2)),
174
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
175
+ 250,
176
+ 30000,
177
+ );
178
+ assert(iscale == 120);
179
+ assert(250*iscale == 30000);
180
+
181
+ baa = new BN(-855329058); // ~85%
182
+ iscale = calculateInventoryScale(
183
+ baa,
184
+ AMM_RESERVE_PRECISION.add(baa),
185
+ AMM_RESERVE_PRECISION.div(new BN(2)),
186
+ AMM_RESERVE_PRECISION.mul(new BN(3)).div(new BN(2)),
187
+ 250,
188
+ 30000,
189
+ );
190
+ assert(iscale == 120);
191
+ assert(250*iscale == 30000);
192
+
193
+ // 'bonk' scale
194
+ iscale = calculateInventoryScale(
195
+ new BN('30228000000000000'),
196
+ new BN('2496788386034912600'),
197
+ new BN('2443167585342470000'),
198
+ new BN('2545411471321696000'),
199
+ 3500,
200
+ 100000,
201
+ );
202
+ console.log(iscale);
203
+ console.log(3500*iscale/1e6);
204
+ assert(iscale == 18.762285);
205
+ assert(3500*iscale/1e6 == 0.06566799749999999); //6.5%
206
+
207
+
208
+
209
+ });
210
+
39
211
  it('Various Spreads', () => {
40
212
  const baseSpread: number = 0.025 * 1e6;
41
213
  const lastOracleReservePriceSpreadPct: BN = ZERO;
@@ -150,9 +322,9 @@ describe('AMM Tests', () => {
150
322
 
151
323
  console.log(terms2);
152
324
  assert(terms2.effectiveLeverageCapped >= 1.0002);
153
- assert(terms2.inventorySpreadScale == 10);
154
- assert(terms2.longSpread == 798);
155
- assert(terms2.shortSpread == 46702);
325
+ assert(terms2.inventorySpreadScale == 1.73492);
326
+ assert(terms2.longSpread == 4262);
327
+ assert(terms2.shortSpread == 43238);
156
328
  });
157
329
 
158
330
  it('Corner Case Spreads', () => {
@@ -183,8 +355,8 @@ describe('AMM Tests', () => {
183
355
 
184
356
  console.log(terms2);
185
357
  assert(terms2.effectiveLeverageCapped <= 1.000001);
186
- assert(terms2.inventorySpreadScale == 1.117371);
187
- assert(terms2.longSpread == 1263);
358
+ assert(terms2.inventorySpreadScale == 1.013527);
359
+ assert(terms2.longSpread == 1146);
188
360
  assert(terms2.shortSpread == 6686);
189
361
  });
190
362
 
@@ -5207,7 +5207,8 @@ describe('DLOB Spot Tests', () => {
5207
5207
  expect(nodesToFillAfter[1].makerNode?.order?.orderId).to.equal(3);
5208
5208
  });
5209
5209
 
5210
- it('Test limit orders skipping more recent post onlys', () => {
5210
+ // add back if dlob checks limit order age again
5211
+ it.skip('Test limit orders skipping more recent post onlys', () => {
5211
5212
  const vAsk = new BN(15);
5212
5213
  const vBid = new BN(8);
5213
5214