@drift-labs/sdk 2.26.0-beta.0 → 2.26.0-beta.2

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 (71) hide show
  1. package/examples/phoenix.ts +40 -0
  2. package/lib/addresses/pda.d.ts +0 -1
  3. package/lib/adminClient.d.ts +0 -1
  4. package/lib/constants/numericConstants.d.ts +59 -61
  5. package/lib/constants/spotMarkets.d.ts +0 -1
  6. package/lib/dlob/DLOB.d.ts +38 -3
  7. package/lib/dlob/DLOB.js +69 -0
  8. package/lib/dlob/DLOBApiClient.js +1 -1
  9. package/lib/dlob/DLOBNode.d.ts +0 -1
  10. package/lib/dlob/DLOBSubscriber.d.ts +34 -0
  11. package/lib/dlob/DLOBSubscriber.js +100 -0
  12. package/lib/dlob/NodeList.d.ts +0 -1
  13. package/lib/dlob/orderBookLevels.d.ts +44 -0
  14. package/lib/dlob/orderBookLevels.js +137 -0
  15. package/lib/dlob/types.d.ts +2 -0
  16. package/lib/driftClient.d.ts +28 -11
  17. package/lib/driftClient.js +158 -55
  18. package/lib/driftClientConfig.d.ts +3 -0
  19. package/lib/factory/bigNum.d.ts +7 -8
  20. package/lib/idl/drift.json +1 -1
  21. package/lib/index.d.ts +3 -0
  22. package/lib/index.js +3 -0
  23. package/lib/math/amm.d.ts +1 -2
  24. package/lib/math/auction.d.ts +0 -1
  25. package/lib/math/conversion.d.ts +1 -2
  26. package/lib/math/funding.d.ts +0 -1
  27. package/lib/math/insurance.d.ts +0 -1
  28. package/lib/math/margin.d.ts +0 -1
  29. package/lib/math/market.d.ts +0 -1
  30. package/lib/math/oracles.d.ts +0 -1
  31. package/lib/math/orders.d.ts +0 -1
  32. package/lib/math/position.d.ts +0 -1
  33. package/lib/math/repeg.d.ts +0 -1
  34. package/lib/math/spotBalance.d.ts +0 -1
  35. package/lib/math/spotMarket.d.ts +0 -1
  36. package/lib/math/spotPosition.d.ts +0 -1
  37. package/lib/math/trade.d.ts +0 -1
  38. package/lib/math/utils.d.ts +0 -1
  39. package/lib/oracles/pythClient.d.ts +1 -2
  40. package/lib/oracles/types.d.ts +0 -1
  41. package/lib/orderParams.d.ts +0 -1
  42. package/lib/phoenix/phoenixFulfillmentConfigMap.d.ts +10 -0
  43. package/lib/phoenix/phoenixFulfillmentConfigMap.js +17 -0
  44. package/lib/phoenix/phoenixSubscriber.d.ts +34 -0
  45. package/lib/phoenix/phoenixSubscriber.js +79 -0
  46. package/lib/serum/serumSubscriber.d.ts +5 -2
  47. package/lib/serum/serumSubscriber.js +22 -0
  48. package/lib/tokenFaucet.d.ts +0 -1
  49. package/lib/types.d.ts +1 -1
  50. package/lib/user.d.ts +0 -1
  51. package/package.json +1 -1
  52. package/src/assert/assert.js +9 -0
  53. package/src/dlob/DLOB.ts +177 -17
  54. package/src/dlob/DLOBApiClient.ts +3 -1
  55. package/src/dlob/DLOBSubscriber.ts +141 -0
  56. package/src/dlob/orderBookLevels.ts +243 -0
  57. package/src/dlob/types.ts +2 -0
  58. package/src/driftClient.ts +231 -66
  59. package/src/driftClientConfig.ts +3 -0
  60. package/src/idl/drift.json +1 -1
  61. package/src/index.ts +3 -0
  62. package/src/phoenix/phoenixFulfillmentConfigMap.ts +26 -0
  63. package/src/phoenix/phoenixSubscriber.ts +156 -0
  64. package/src/serum/serumSubscriber.ts +27 -1
  65. package/src/token/index.js +38 -0
  66. package/src/types.ts +1 -0
  67. package/src/util/computeUnits.js +27 -0
  68. package/src/util/getTokenAddress.js +9 -0
  69. package/src/util/promiseTimeout.js +14 -0
  70. package/src/util/tps.js +27 -0
  71. package/tests/dlob/helpers.ts +3 -0
@@ -0,0 +1,156 @@
1
+ import { Connection, PublicKey, SYSVAR_CLOCK_PUBKEY } from '@solana/web3.js';
2
+ import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
3
+ import {
4
+ MarketData,
5
+ Client,
6
+ deserializeMarketData,
7
+ deserializeClockData,
8
+ toNum,
9
+ getMarketUiLadder,
10
+ } from '@ellipsis-labs/phoenix-sdk';
11
+ import { PRICE_PRECISION } from '../constants/numericConstants';
12
+ import { BN } from '@coral-xyz/anchor';
13
+
14
+ export type PhoenixMarketSubscriberConfig = {
15
+ connection: Connection;
16
+ programId: PublicKey;
17
+ marketAddress: PublicKey;
18
+ accountSubscription:
19
+ | {
20
+ // enables use to add web sockets in the future
21
+ type: 'polling';
22
+ accountLoader: BulkAccountLoader;
23
+ }
24
+ | {
25
+ type: 'websocket';
26
+ };
27
+ };
28
+
29
+ export class PhoenixSubscriber {
30
+ connection: Connection;
31
+ client: Client;
32
+ programId: PublicKey;
33
+ marketAddress: PublicKey;
34
+ subscriptionType: 'polling' | 'websocket';
35
+ accountLoader: BulkAccountLoader | undefined;
36
+ market: MarketData;
37
+ marketCallbackId: string | number;
38
+ clockCallbackId: string | number;
39
+
40
+ subscribed: boolean;
41
+ lastSlot: number;
42
+ lastUnixTimestamp: number;
43
+
44
+ public constructor(config: PhoenixMarketSubscriberConfig) {
45
+ this.connection = config.connection;
46
+ this.programId = config.programId;
47
+ this.marketAddress = config.marketAddress;
48
+ if (config.accountSubscription.type === 'polling') {
49
+ this.subscriptionType = 'polling';
50
+ this.accountLoader = config.accountSubscription.accountLoader;
51
+ } else {
52
+ this.subscriptionType = 'websocket';
53
+ }
54
+ this.lastSlot = 0;
55
+ this.lastUnixTimestamp = 0;
56
+ }
57
+
58
+ public async subscribe(): Promise<void> {
59
+ if (this.subscribed) {
60
+ return;
61
+ }
62
+
63
+ this.market = deserializeMarketData(
64
+ (await this.connection.getAccountInfo(this.marketAddress, 'confirmed'))
65
+ .data
66
+ );
67
+
68
+ const clock = deserializeClockData(
69
+ (await this.connection.getAccountInfo(SYSVAR_CLOCK_PUBKEY, 'confirmed'))
70
+ .data
71
+ );
72
+ this.lastUnixTimestamp = toNum(clock.unixTimestamp);
73
+
74
+ if (this.subscriptionType === 'websocket') {
75
+ this.marketCallbackId = this.connection.onAccountChange(
76
+ this.marketAddress,
77
+ (accountInfo, _ctx) => {
78
+ this.market = deserializeMarketData(accountInfo.data);
79
+ }
80
+ );
81
+ this.clockCallbackId = this.connection.onAccountChange(
82
+ SYSVAR_CLOCK_PUBKEY,
83
+ (accountInfo, ctx) => {
84
+ this.lastSlot = ctx.slot;
85
+ const clock = deserializeClockData(accountInfo.data);
86
+ this.lastUnixTimestamp = toNum(clock.unixTimestamp);
87
+ }
88
+ );
89
+ } else {
90
+ this.marketCallbackId = await this.accountLoader.addAccount(
91
+ this.marketAddress,
92
+ (buffer, slot) => {
93
+ this.lastSlot = slot;
94
+ this.market = deserializeMarketData(buffer);
95
+ }
96
+ );
97
+ this.clockCallbackId = await this.accountLoader.addAccount(
98
+ SYSVAR_CLOCK_PUBKEY,
99
+ (buffer, slot) => {
100
+ this.lastSlot = slot;
101
+ const clock = deserializeClockData(buffer);
102
+ this.lastUnixTimestamp = toNum(clock.unixTimestamp);
103
+ }
104
+ );
105
+ }
106
+
107
+ this.subscribed = true;
108
+ }
109
+
110
+ public getBestBid(): BN {
111
+ const ladder = getMarketUiLadder(
112
+ this.market,
113
+ this.lastSlot,
114
+ this.lastUnixTimestamp,
115
+ 1
116
+ );
117
+ return new BN(Math.floor(ladder.bids[0][0] * PRICE_PRECISION.toNumber()));
118
+ }
119
+
120
+ public getBestAsk(): BN {
121
+ const ladder = getMarketUiLadder(
122
+ this.market,
123
+ this.lastSlot,
124
+ this.lastUnixTimestamp,
125
+ 1
126
+ );
127
+ return new BN(Math.floor(ladder.asks[0][0] * PRICE_PRECISION.toNumber()));
128
+ }
129
+
130
+ public async unsubscribe(): Promise<void> {
131
+ if (!this.subscribed) {
132
+ return;
133
+ }
134
+
135
+ // remove listeners
136
+ if (this.subscriptionType === 'websocket') {
137
+ await this.connection.removeAccountChangeListener(
138
+ this.marketCallbackId as number
139
+ );
140
+ await this.connection.removeAccountChangeListener(
141
+ this.clockCallbackId as number
142
+ );
143
+ } else {
144
+ this.accountLoader.removeAccount(
145
+ this.marketAddress,
146
+ this.marketCallbackId as string
147
+ );
148
+ this.accountLoader.removeAccount(
149
+ SYSVAR_CLOCK_PUBKEY,
150
+ this.clockCallbackId as string
151
+ );
152
+ }
153
+
154
+ this.subscribed = false;
155
+ }
156
+ }
@@ -4,8 +4,9 @@ import { Market, Orderbook } from '@project-serum/serum';
4
4
  import { SerumMarketSubscriberConfig } from './types';
5
5
  import { BN } from '@coral-xyz/anchor';
6
6
  import { PRICE_PRECISION } from '../constants/numericConstants';
7
+ import { L2Level, L2OrderBookGenerator } from '../dlob/orderBookLevels';
7
8
 
8
- export class SerumSubscriber {
9
+ export class SerumSubscriber implements L2OrderBookGenerator {
9
10
  connection: Connection;
10
11
  programId: PublicKey;
11
12
  marketAddress: PublicKey;
@@ -112,6 +113,31 @@ export class SerumSubscriber {
112
113
  return new BN(bestAsk[0] * PRICE_PRECISION.toNumber());
113
114
  }
114
115
 
116
+ public getL2Bids(): Generator<L2Level> {
117
+ return this.getL2Levels('bids');
118
+ }
119
+
120
+ public getL2Asks(): Generator<L2Level> {
121
+ return this.getL2Levels('asks');
122
+ }
123
+
124
+ *getL2Levels(side: 'bids' | 'asks'): Generator<L2Level> {
125
+ // @ts-ignore
126
+ const basePrecision = Math.pow(10, this.market._baseSplTokenDecimals);
127
+ const pricePrecision = PRICE_PRECISION.toNumber();
128
+ for (let { price, size } of this[side].items(side === 'bids')) {
129
+ price = new BN(price * pricePrecision);
130
+ size = new BN(size * basePrecision);
131
+ yield {
132
+ price,
133
+ size,
134
+ sources: {
135
+ serum: size,
136
+ },
137
+ };
138
+ }
139
+ }
140
+
115
141
  public async unsubscribe(): Promise<void> {
116
142
  if (!this.subscribed) {
117
143
  return;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseTokenAccount = void 0;
4
+ const spl_token_1 = require("@solana/spl-token");
5
+ const web3_js_1 = require("@solana/web3.js");
6
+ function parseTokenAccount(data) {
7
+ const accountInfo = spl_token_1.AccountLayout.decode(data);
8
+ accountInfo.mint = new web3_js_1.PublicKey(accountInfo.mint);
9
+ accountInfo.owner = new web3_js_1.PublicKey(accountInfo.owner);
10
+ accountInfo.amount = spl_token_1.u64.fromBuffer(accountInfo.amount);
11
+ if (accountInfo.delegateOption === 0) {
12
+ accountInfo.delegate = null;
13
+ // eslint-disable-next-line new-cap
14
+ accountInfo.delegatedAmount = new spl_token_1.u64(0);
15
+ }
16
+ else {
17
+ accountInfo.delegate = new web3_js_1.PublicKey(accountInfo.delegate);
18
+ accountInfo.delegatedAmount = spl_token_1.u64.fromBuffer(accountInfo.delegatedAmount);
19
+ }
20
+ accountInfo.isInitialized = accountInfo.state !== 0;
21
+ accountInfo.isFrozen = accountInfo.state === 2;
22
+ if (accountInfo.isNativeOption === 1) {
23
+ accountInfo.rentExemptReserve = spl_token_1.u64.fromBuffer(accountInfo.isNative);
24
+ accountInfo.isNative = true;
25
+ }
26
+ else {
27
+ accountInfo.rentExemptReserve = null;
28
+ accountInfo.isNative = false;
29
+ }
30
+ if (accountInfo.closeAuthorityOption === 0) {
31
+ accountInfo.closeAuthority = null;
32
+ }
33
+ else {
34
+ accountInfo.closeAuthority = new web3_js_1.PublicKey(accountInfo.closeAuthority);
35
+ }
36
+ return accountInfo;
37
+ }
38
+ exports.parseTokenAccount = parseTokenAccount;
package/src/types.ts CHANGED
@@ -585,6 +585,7 @@ export type HistoricalIndexData = {
585
585
  export type SpotMarketAccount = {
586
586
  status: MarketStatus;
587
587
  assetTier: AssetTier;
588
+ name: number[];
588
589
 
589
590
  marketIndex: number;
590
591
  pubkey: PublicKey;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.findComputeUnitConsumption = void 0;
13
+ function findComputeUnitConsumption(programId, connection, txSignature, commitment = 'confirmed') {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ const tx = yield connection.getTransaction(txSignature, { commitment });
16
+ const computeUnits = [];
17
+ const regex = new RegExp(`Program ${programId.toString()} consumed ([0-9]{0,6}) of ([0-9]{0,7}) compute units`);
18
+ tx.meta.logMessages.forEach((logMessage) => {
19
+ const match = logMessage.match(regex);
20
+ if (match && match[1]) {
21
+ computeUnits.push(match[1]);
22
+ }
23
+ });
24
+ return computeUnits;
25
+ });
26
+ }
27
+ exports.findComputeUnitConsumption = findComputeUnitConsumption;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTokenAddress = void 0;
4
+ const spl_token_1 = require("@solana/spl-token");
5
+ const web3_js_1 = require("@solana/web3.js");
6
+ const getTokenAddress = (mintAddress, userPubKey) => {
7
+ return spl_token_1.Token.getAssociatedTokenAddress(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, spl_token_1.TOKEN_PROGRAM_ID, new web3_js_1.PublicKey(mintAddress), new web3_js_1.PublicKey(userPubKey));
8
+ };
9
+ exports.getTokenAddress = getTokenAddress;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.promiseTimeout = void 0;
4
+ function promiseTimeout(promise, timeoutMs) {
5
+ let timeoutId;
6
+ const timeoutPromise = new Promise((resolve) => {
7
+ timeoutId = setTimeout(() => resolve(null), timeoutMs);
8
+ });
9
+ return Promise.race([promise, timeoutPromise]).then((result) => {
10
+ clearTimeout(timeoutId);
11
+ return result;
12
+ });
13
+ }
14
+ exports.promiseTimeout = promiseTimeout;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.estimateTps = void 0;
13
+ function estimateTps(programId, connection, failed) {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ let signatures = yield connection.getSignaturesForAddress(programId, undefined, 'finalized');
16
+ if (failed) {
17
+ signatures = signatures.filter((signature) => signature.err);
18
+ }
19
+ const numberOfSignatures = signatures.length;
20
+ if (numberOfSignatures === 0) {
21
+ return 0;
22
+ }
23
+ return (numberOfSignatures /
24
+ (signatures[0].blockTime - signatures[numberOfSignatures - 1].blockTime));
25
+ });
26
+ }
27
+ exports.estimateTps = estimateTps;
@@ -254,6 +254,7 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
254
254
  {
255
255
  status: MarketStatus.ACTIVE,
256
256
  assetTier: AssetTier.COLLATERAL,
257
+ name: [],
257
258
  maxTokenDeposits: new BN(100),
258
259
  marketIndex: 0,
259
260
  pubkey: PublicKey.default,
@@ -328,6 +329,7 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
328
329
  {
329
330
  status: MarketStatus.ACTIVE,
330
331
  assetTier: AssetTier.CROSS,
332
+ name: [],
331
333
  maxTokenDeposits: new BN(100),
332
334
  marketIndex: 1,
333
335
  pubkey: PublicKey.default,
@@ -402,6 +404,7 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
402
404
  {
403
405
  status: MarketStatus.ACTIVE,
404
406
  assetTier: AssetTier.PROTECTED,
407
+ name: [],
405
408
  maxTokenDeposits: new BN(100),
406
409
  marketIndex: 2,
407
410
  pubkey: PublicKey.default,