@drift-labs/sdk 0.1.36-master.7 → 0.2.0-master.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.
Files changed (197) hide show
  1. package/lib/accounts/bulkAccountLoader.d.ts +6 -6
  2. package/lib/accounts/bulkAccountLoader.js +81 -95
  3. package/lib/accounts/bulkUserSubscription.js +13 -57
  4. package/lib/accounts/fetch.d.ts +4 -0
  5. package/lib/accounts/fetch.js +18 -0
  6. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +34 -38
  7. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +224 -224
  8. package/lib/accounts/pollingOracleSubscriber.d.ts +3 -3
  9. package/lib/accounts/pollingOracleSubscriber.js +37 -49
  10. package/lib/accounts/pollingTokenAccountSubscriber.d.ts +3 -3
  11. package/lib/accounts/pollingTokenAccountSubscriber.js +35 -50
  12. package/lib/accounts/pollingUserAccountSubscriber.d.ts +7 -13
  13. package/lib/accounts/pollingUserAccountSubscriber.js +71 -134
  14. package/lib/accounts/types.d.ts +34 -41
  15. package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -4
  16. package/lib/accounts/webSocketAccountSubscriber.js +39 -35
  17. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +33 -28
  18. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +153 -185
  19. package/lib/accounts/webSocketUserAccountSubscriber.d.ts +5 -11
  20. package/lib/accounts/webSocketUserAccountSubscriber.js +22 -67
  21. package/lib/addresses/marketAddresses.d.ts +4 -0
  22. package/lib/addresses/marketAddresses.js +15 -0
  23. package/lib/addresses/pda.d.ts +12 -0
  24. package/lib/addresses/pda.js +83 -0
  25. package/lib/admin.d.ts +8 -17
  26. package/lib/admin.js +366 -558
  27. package/lib/clearingHouse.d.ts +84 -109
  28. package/lib/clearingHouse.js +778 -899
  29. package/lib/clearingHouseConfig.d.ts +34 -0
  30. package/lib/clearingHouseConfig.js +2 -0
  31. package/lib/clearingHouseUser.d.ts +19 -22
  32. package/lib/clearingHouseUser.js +155 -141
  33. package/lib/clearingHouseUserConfig.d.ts +14 -0
  34. package/lib/clearingHouseUserConfig.js +2 -0
  35. package/lib/config.d.ts +12 -0
  36. package/lib/config.js +35 -4
  37. package/lib/constants/banks.d.ts +16 -0
  38. package/lib/constants/banks.js +34 -0
  39. package/lib/constants/markets.d.ts +8 -3
  40. package/lib/constants/markets.js +13 -206
  41. package/lib/constants/numericConstants.d.ts +16 -0
  42. package/lib/constants/numericConstants.js +22 -6
  43. package/lib/events/eventList.d.ts +22 -0
  44. package/lib/events/eventList.js +77 -0
  45. package/lib/events/eventSubscriber.d.ts +34 -0
  46. package/lib/events/eventSubscriber.js +126 -0
  47. package/lib/events/fetchLogs.d.ts +13 -0
  48. package/lib/events/fetchLogs.js +39 -0
  49. package/lib/events/pollingLogProvider.d.ts +15 -0
  50. package/lib/events/pollingLogProvider.js +53 -0
  51. package/lib/events/sort.d.ts +2 -0
  52. package/lib/events/sort.js +44 -0
  53. package/lib/events/txEventCache.d.ts +24 -0
  54. package/lib/events/txEventCache.js +71 -0
  55. package/lib/events/types.d.ts +49 -0
  56. package/lib/events/types.js +20 -0
  57. package/lib/events/webSocketLogProvider.d.ts +12 -0
  58. package/lib/events/webSocketLogProvider.js +30 -0
  59. package/lib/examples/makeTradeExample.js +26 -27
  60. package/lib/factory/bigNum.d.ts +112 -0
  61. package/lib/factory/bigNum.js +356 -0
  62. package/lib/factory/oracleClient.d.ts +1 -2
  63. package/lib/factory/oracleClient.js +6 -2
  64. package/lib/idl/clearing_house.json +2213 -2951
  65. package/lib/index.d.ts +12 -4
  66. package/lib/index.js +12 -4
  67. package/lib/math/amm.d.ts +19 -29
  68. package/lib/math/amm.js +129 -179
  69. package/lib/math/auction.d.ts +5 -0
  70. package/lib/math/auction.js +39 -0
  71. package/lib/math/bankBalance.d.ts +9 -0
  72. package/lib/math/bankBalance.js +75 -0
  73. package/lib/math/conversion.d.ts +0 -1
  74. package/lib/math/conversion.js +1 -5
  75. package/lib/math/funding.d.ts +6 -6
  76. package/lib/math/funding.js +158 -175
  77. package/lib/math/market.d.ts +6 -6
  78. package/lib/math/market.js +10 -9
  79. package/lib/math/orders.d.ts +5 -0
  80. package/lib/math/orders.js +31 -1
  81. package/lib/math/position.d.ts +7 -5
  82. package/lib/math/position.js +27 -27
  83. package/lib/math/repeg.d.ts +22 -0
  84. package/lib/math/repeg.js +128 -0
  85. package/lib/math/trade.d.ts +5 -4
  86. package/lib/math/trade.js +29 -21
  87. package/lib/mockUSDCFaucet.js +87 -116
  88. package/lib/oracles/oracleClientCache.d.ts +8 -0
  89. package/lib/oracles/oracleClientCache.js +19 -0
  90. package/lib/oracles/pythClient.d.ts +3 -5
  91. package/lib/oracles/pythClient.js +12 -31
  92. package/lib/oracles/quoteAssetOracleClient.d.ts +9 -0
  93. package/lib/oracles/quoteAssetOracleClient.js +21 -0
  94. package/lib/oracles/switchboardClient.d.ts +3 -5
  95. package/lib/oracles/switchboardClient.js +29 -50
  96. package/lib/oracles/types.d.ts +6 -1
  97. package/lib/orders.d.ts +6 -6
  98. package/lib/orders.js +10 -9
  99. package/lib/slot/SlotSubscriber.d.ts +12 -0
  100. package/lib/slot/SlotSubscriber.js +23 -0
  101. package/lib/tx/retryTxSender.d.ts +2 -2
  102. package/lib/tx/retryTxSender.js +108 -123
  103. package/lib/tx/types.d.ts +5 -1
  104. package/lib/tx/utils.d.ts +1 -1
  105. package/lib/tx/utils.js +11 -2
  106. package/lib/types.d.ts +105 -109
  107. package/lib/types.js +13 -1
  108. package/lib/userName.d.ts +4 -0
  109. package/lib/userName.js +20 -0
  110. package/lib/util/computeUnits.js +10 -21
  111. package/lib/util/tps.js +11 -22
  112. package/lib/wallet.js +7 -20
  113. package/package.json +10 -3
  114. package/src/accounts/bulkAccountLoader.ts +21 -15
  115. package/src/accounts/bulkUserSubscription.ts +1 -45
  116. package/src/accounts/fetch.ts +33 -0
  117. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +249 -214
  118. package/src/accounts/pollingOracleSubscriber.ts +16 -8
  119. package/src/accounts/pollingTokenAccountSubscriber.ts +11 -8
  120. package/src/accounts/pollingUserAccountSubscriber.ts +21 -86
  121. package/src/accounts/types.ts +41 -70
  122. package/src/accounts/webSocketAccountSubscriber.ts +33 -16
  123. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +190 -240
  124. package/src/accounts/webSocketUserAccountSubscriber.ts +11 -71
  125. package/src/addresses/marketAddresses.ts +18 -0
  126. package/src/addresses/pda.ts +118 -0
  127. package/src/admin.ts +205 -346
  128. package/src/clearingHouse.ts +918 -961
  129. package/src/clearingHouseConfig.ts +37 -0
  130. package/src/clearingHouseUser.ts +275 -185
  131. package/src/clearingHouseUserConfig.ts +18 -0
  132. package/src/config.ts +54 -1
  133. package/src/constants/banks.ts +43 -0
  134. package/src/constants/markets.ts +16 -207
  135. package/src/constants/numericConstants.ts +33 -5
  136. package/src/events/eventList.ts +94 -0
  137. package/src/events/eventSubscriber.ts +194 -0
  138. package/src/events/fetchLogs.ts +80 -0
  139. package/src/events/pollingLogProvider.ts +79 -0
  140. package/src/events/sort.ts +65 -0
  141. package/src/events/txEventCache.ts +74 -0
  142. package/src/events/types.ts +98 -0
  143. package/src/events/webSocketLogProvider.ts +38 -0
  144. package/src/examples/makeTradeExample.ts +20 -11
  145. package/src/factory/bigNum.ts +507 -0
  146. package/src/factory/oracleClient.ts +7 -4
  147. package/src/idl/clearing_house.json +2213 -2951
  148. package/src/index.ts +12 -4
  149. package/src/math/amm.ts +229 -245
  150. package/src/math/auction.ts +39 -0
  151. package/src/math/bankBalance.ts +112 -0
  152. package/src/math/conversion.ts +1 -11
  153. package/src/math/funding.ts +12 -9
  154. package/src/math/market.ts +37 -30
  155. package/src/math/orders.ts +38 -0
  156. package/src/math/position.ts +48 -35
  157. package/src/math/repeg.ts +175 -0
  158. package/src/math/trade.ts +45 -35
  159. package/src/math/utils.js +27 -0
  160. package/src/math/utils.js.map +1 -0
  161. package/src/oracles/oracleClientCache.ts +20 -0
  162. package/src/oracles/pythClient.ts +4 -11
  163. package/src/oracles/quoteAssetOracleClient.ts +25 -0
  164. package/src/oracles/switchboardClient.ts +11 -24
  165. package/src/oracles/types.ts +7 -1
  166. package/src/orders.ts +35 -20
  167. package/src/slot/SlotSubscriber.ts +32 -0
  168. package/src/tx/retryTxSender.ts +6 -4
  169. package/src/tx/types.ts +6 -1
  170. package/src/tx/utils.ts +22 -3
  171. package/src/types.ts +108 -122
  172. package/src/userName.ts +20 -0
  173. package/src/util/computeUnits.js +17 -0
  174. package/src/util/computeUnits.js.map +1 -0
  175. package/tests/bn/test.ts +255 -0
  176. package/tsconfig.json +12 -12
  177. package/lib/addresses.d.ts +0 -10
  178. package/lib/addresses.js +0 -93
  179. package/lib/constants/accounts.d.ts +0 -15
  180. package/lib/constants/accounts.js +0 -18
  181. package/lib/factory/clearingHouse.d.ts +0 -35
  182. package/lib/factory/clearingHouse.js +0 -81
  183. package/lib/factory/clearingHouseUser.d.ts +0 -19
  184. package/lib/factory/clearingHouseUser.js +0 -34
  185. package/lib/math/insuranceFund.d.ts +0 -15
  186. package/lib/math/insuranceFund.js +0 -33
  187. package/lib/settlement.d.ts +0 -4
  188. package/lib/settlement.js +0 -10
  189. package/lib/tx/defaultTxSender.d.ts +0 -8
  190. package/lib/tx/defaultTxSender.js +0 -12
  191. package/src/addresses.ts +0 -82
  192. package/src/constants/accounts.ts +0 -26
  193. package/src/factory/clearingHouse.ts +0 -173
  194. package/src/factory/clearingHouseUser.ts +0 -73
  195. package/src/math/insuranceFund.ts +0 -29
  196. package/src/settlement.ts +0 -9
  197. package/src/tx/defaultTxSender.ts +0 -24
@@ -0,0 +1,194 @@
1
+ import { Connection, TransactionSignature } from '@solana/web3.js';
2
+ import { Program } from '@project-serum/anchor';
3
+ import {
4
+ DefaultEventSubscriptionOptions,
5
+ EventSubscriptionOptions,
6
+ EventType,
7
+ WrappedEvents,
8
+ EventMap,
9
+ LogProvider,
10
+ EventSubscriberEvents,
11
+ } from './types';
12
+ import { TxEventCache } from './txEventCache';
13
+ import { EventList } from './eventList';
14
+ import { PollingLogProvider } from './pollingLogProvider';
15
+ import { fetchLogs } from './fetchLogs';
16
+ import { WebSocketLogProvider } from './webSocketLogProvider';
17
+ import { EventEmitter } from 'events';
18
+ import StrictEventEmitter from 'strict-event-emitter-types';
19
+ import { getSortFn } from './sort';
20
+
21
+ export class EventSubscriber {
22
+ private eventListMap: Map<EventType, EventList<EventType>>;
23
+ private txEventCache: TxEventCache;
24
+ private awaitTxPromises = new Map<string, Promise<void>>();
25
+ private awaitTxResolver = new Map<string, () => void>();
26
+ private logProvider: LogProvider;
27
+ public eventEmitter: StrictEventEmitter<EventEmitter, EventSubscriberEvents>;
28
+
29
+ public constructor(
30
+ private connection: Connection,
31
+ private program: Program,
32
+ private options: EventSubscriptionOptions = DefaultEventSubscriptionOptions
33
+ ) {
34
+ this.options = Object.assign({}, DefaultEventSubscriptionOptions, options);
35
+ this.txEventCache = new TxEventCache(this.options.maxTx);
36
+ this.eventListMap = new Map<EventType, EventList<EventType>>();
37
+ for (const eventType of this.options.eventTypes) {
38
+ this.eventListMap.set(
39
+ eventType,
40
+ new EventList(
41
+ eventType,
42
+ this.options.maxEventsPerType,
43
+ getSortFn(this.options.orderBy, this.options.orderDir, eventType),
44
+ this.options.orderDir
45
+ )
46
+ );
47
+ }
48
+ this.eventEmitter = new EventEmitter();
49
+ if (this.options.logProviderConfig.type === 'websocket') {
50
+ this.logProvider = new WebSocketLogProvider(
51
+ this.connection,
52
+ this.program.programId,
53
+ this.options.commitment
54
+ );
55
+ } else {
56
+ this.logProvider = new PollingLogProvider(
57
+ this.connection,
58
+ this.program.programId,
59
+ options.commitment,
60
+ this.options.logProviderConfig.frequency
61
+ );
62
+ }
63
+ }
64
+
65
+ public subscribe(): boolean {
66
+ if (this.logProvider.isSubscribed()) {
67
+ return true;
68
+ }
69
+
70
+ this.fetchPreviousTx().catch((e) => {
71
+ console.error('Error fetching previous txs in event subscriber');
72
+ console.error(e);
73
+ });
74
+
75
+ return this.logProvider.subscribe((txSig, slot, logs) => {
76
+ this.handleTxLogs(txSig, slot, logs);
77
+ });
78
+ }
79
+
80
+ private handleTxLogs(
81
+ txSig: TransactionSignature,
82
+ slot: number,
83
+ logs: string[]
84
+ ): void {
85
+ if (this.txEventCache.has(txSig)) {
86
+ return;
87
+ }
88
+
89
+ const wrappedEvents = this.parseEventsFromLogs(txSig, slot, logs);
90
+ for (const wrappedEvent of wrappedEvents) {
91
+ this.eventListMap.get(wrappedEvent.eventType).insert(wrappedEvent);
92
+ this.eventEmitter.emit('newEvent', wrappedEvent);
93
+ }
94
+
95
+ if (this.awaitTxPromises.has(txSig)) {
96
+ this.awaitTxPromises.delete(txSig);
97
+ this.awaitTxResolver.get(txSig)();
98
+ this.awaitTxResolver.delete(txSig);
99
+ }
100
+
101
+ this.txEventCache.add(txSig, wrappedEvents);
102
+ }
103
+
104
+ private async fetchPreviousTx(): Promise<void> {
105
+ if (!this.options.untilTx) {
106
+ return;
107
+ }
108
+
109
+ let txFetched = 0;
110
+ let beforeTx: TransactionSignature = undefined;
111
+ const untilTx: TransactionSignature = this.options.untilTx;
112
+ while (txFetched < this.options.maxTx) {
113
+ const response = await fetchLogs(
114
+ this.connection,
115
+ this.program.programId,
116
+ this.options.commitment === 'finalized' ? 'finalized' : 'confirmed',
117
+ beforeTx,
118
+ untilTx
119
+ );
120
+
121
+ if (response === undefined) {
122
+ break;
123
+ }
124
+
125
+ txFetched += response.transactionLogs.length;
126
+ beforeTx = response.earliestTx;
127
+
128
+ for (const { txSig, slot, logs } of response.transactionLogs) {
129
+ this.handleTxLogs(txSig, slot, logs);
130
+ }
131
+ }
132
+ }
133
+
134
+ public async unsubscribe(): Promise<boolean> {
135
+ return await this.logProvider.unsubscribe();
136
+ }
137
+
138
+ private parseEventsFromLogs(
139
+ txSig: TransactionSignature,
140
+ slot: number,
141
+ logs: string[]
142
+ ): WrappedEvents {
143
+ const records = [];
144
+ // @ts-ignore
145
+ this.program._events._eventParser.parseLogs(logs, (event) => {
146
+ const expectRecordType = this.eventListMap.has(event.name);
147
+ if (expectRecordType) {
148
+ event.data.txSig = txSig;
149
+ event.data.slot = slot;
150
+ event.data.eventType = event.name;
151
+ records.push(event.data);
152
+ }
153
+ });
154
+ return records;
155
+ }
156
+
157
+ public awaitTx(txSig: TransactionSignature): Promise<void> {
158
+ if (this.awaitTxPromises.has(txSig)) {
159
+ return this.awaitTxPromises.get(txSig);
160
+ }
161
+
162
+ if (this.txEventCache.has(txSig)) {
163
+ return Promise.resolve();
164
+ }
165
+
166
+ const promise = new Promise<void>((resolve) => {
167
+ this.awaitTxResolver.set(txSig, resolve);
168
+ });
169
+ this.awaitTxPromises.set(txSig, promise);
170
+ return promise;
171
+ }
172
+
173
+ public getEventList<Type extends keyof EventMap>(
174
+ eventType: Type
175
+ ): EventList<Type> {
176
+ return this.eventListMap.get(eventType) as EventList<Type>;
177
+ }
178
+
179
+ /**
180
+ * This requires the EventList be cast to an array, which requires reallocation of memory.
181
+ * Would bias to using getEventList over getEvents
182
+ *
183
+ * @param eventType
184
+ */
185
+ public getEventsArray<Type extends EventType>(
186
+ eventType: Type
187
+ ): EventMap[Type][] {
188
+ return this.eventListMap.get(eventType).toArray() as EventMap[Type][];
189
+ }
190
+
191
+ public getEventsByTx(txSig: TransactionSignature): WrappedEvents | undefined {
192
+ return this.txEventCache.get(txSig);
193
+ }
194
+ }
@@ -0,0 +1,80 @@
1
+ import {
2
+ Connection,
3
+ Finality,
4
+ PublicKey,
5
+ TransactionSignature,
6
+ } from '@solana/web3.js';
7
+
8
+ type Log = { txSig: TransactionSignature; slot: number; logs: string[] };
9
+ type FetchLogsResponse = {
10
+ earliestTx: string;
11
+ mostRecentTx: string;
12
+ transactionLogs: Log[];
13
+ };
14
+
15
+ export async function fetchLogs(
16
+ connection: Connection,
17
+ programId: PublicKey,
18
+ finality: Finality,
19
+ beforeTx?: TransactionSignature,
20
+ untilTx?: TransactionSignature
21
+ ): Promise<FetchLogsResponse | undefined> {
22
+ const signatures = await connection.getSignaturesForAddress(
23
+ programId,
24
+ {
25
+ before: beforeTx,
26
+ until: untilTx,
27
+ },
28
+ finality
29
+ );
30
+
31
+ const sortedSignatures = signatures.sort((a, b) =>
32
+ a.slot < b.slot ? -1 : 1
33
+ );
34
+
35
+ const filteredSignatures = sortedSignatures.filter(
36
+ (signature) => !signature.err
37
+ );
38
+
39
+ if (filteredSignatures.length === 0) {
40
+ return undefined;
41
+ }
42
+
43
+ const chunkedSignatures = chunk(filteredSignatures, 100);
44
+
45
+ const transactionLogs = (
46
+ await Promise.all(
47
+ chunkedSignatures.map(async (chunk) => {
48
+ const transactions = await connection.getTransactions(
49
+ chunk.map((confirmedSignature) => confirmedSignature.signature),
50
+ finality
51
+ );
52
+
53
+ return transactions.map((transaction) => {
54
+ return {
55
+ txSig: transaction.transaction.signatures[0],
56
+ slot: transaction.slot,
57
+ logs: transaction.meta.logMessages,
58
+ };
59
+ });
60
+ })
61
+ )
62
+ ).flat();
63
+
64
+ const earliestTx = filteredSignatures[0].signature;
65
+ const mostRecentTx =
66
+ filteredSignatures[filteredSignatures.length - 1].signature;
67
+
68
+ return {
69
+ transactionLogs: transactionLogs,
70
+ earliestTx: earliestTx,
71
+ mostRecentTx: mostRecentTx,
72
+ };
73
+ }
74
+
75
+ function chunk<T>(array: readonly T[], size: number): T[][] {
76
+ return new Array(Math.ceil(array.length / size))
77
+ .fill(null)
78
+ .map((_, index) => index * size)
79
+ .map((begin) => array.slice(begin, begin + size));
80
+ }
@@ -0,0 +1,79 @@
1
+ import { LogProvider, logProviderCallback } from './types';
2
+ import {
3
+ Commitment,
4
+ Connection,
5
+ Finality,
6
+ PublicKey,
7
+ TransactionSignature,
8
+ } from '@solana/web3.js';
9
+ import { fetchLogs } from './fetchLogs';
10
+
11
+ export class PollingLogProvider implements LogProvider {
12
+ private finality: Finality;
13
+ private intervalId: NodeJS.Timer;
14
+ private mostRecentSeenTx?: TransactionSignature;
15
+ private mutex: number;
16
+
17
+ public constructor(
18
+ private connection: Connection,
19
+ private programId: PublicKey,
20
+ commitment: Commitment,
21
+ private frequency = 15 * 1000
22
+ ) {
23
+ this.finality = commitment === 'finalized' ? 'finalized' : 'confirmed';
24
+ }
25
+
26
+ public subscribe(callback: logProviderCallback): boolean {
27
+ if (this.intervalId) {
28
+ return true;
29
+ }
30
+
31
+ this.intervalId = setInterval(async () => {
32
+ if (this.mutex === 1) {
33
+ return;
34
+ }
35
+ this.mutex = 1;
36
+
37
+ try {
38
+ const response = await fetchLogs(
39
+ this.connection,
40
+ this.programId,
41
+ this.finality,
42
+ undefined,
43
+ this.mostRecentSeenTx
44
+ );
45
+
46
+ if (response === undefined) {
47
+ return;
48
+ }
49
+
50
+ const { mostRecentTx, transactionLogs } = response;
51
+
52
+ for (const { txSig, slot, logs } of transactionLogs) {
53
+ callback(txSig, slot, logs);
54
+ }
55
+
56
+ this.mostRecentSeenTx = mostRecentTx;
57
+ } catch (e) {
58
+ console.error('PollingLogProvider threw an Error');
59
+ console.error(e);
60
+ } finally {
61
+ this.mutex = 0;
62
+ }
63
+ }, this.frequency);
64
+
65
+ return true;
66
+ }
67
+
68
+ public isSubscribed(): boolean {
69
+ return this.intervalId !== undefined;
70
+ }
71
+
72
+ public async unsubscribe(): Promise<boolean> {
73
+ if (this.intervalId !== undefined) {
74
+ clearInterval(this.intervalId);
75
+ this.intervalId = undefined;
76
+ }
77
+ return true;
78
+ }
79
+ }
@@ -0,0 +1,65 @@
1
+ import {
2
+ EventMap,
3
+ EventSubscriptionOrderBy,
4
+ EventSubscriptionOrderDirection,
5
+ EventType,
6
+ SortFn,
7
+ Event,
8
+ } from './types';
9
+ import { OrderRecord } from '../types';
10
+ import { PublicKey, ZERO } from '../index';
11
+
12
+ function clientSortAscFn(): 'less than' {
13
+ return 'less than';
14
+ }
15
+
16
+ function clientSortDescFn(): 'greater than' {
17
+ return 'greater than';
18
+ }
19
+
20
+ function defaultBlockchainSortFn(
21
+ currentEvent: EventMap[EventType],
22
+ newEvent: EventMap[EventType]
23
+ ): 'less than' | 'greater than' {
24
+ return currentEvent.slot <= newEvent.slot ? 'less than' : 'greater than';
25
+ }
26
+
27
+ function orderRecordSortFn(
28
+ currentEvent: Event<OrderRecord>,
29
+ newEvent: Event<OrderRecord>
30
+ ): 'less than' | 'greater than' {
31
+ const currentEventMarketIndex = !currentEvent.maker.equals(PublicKey.default)
32
+ ? currentEvent.makerOrder.marketIndex
33
+ : currentEvent.takerOrder.marketIndex;
34
+ const newEventMarketIndex = !newEvent.maker.equals(PublicKey.default)
35
+ ? newEvent.makerOrder.marketIndex
36
+ : newEvent.takerOrder.marketIndex;
37
+ if (!currentEventMarketIndex.eq(newEventMarketIndex)) {
38
+ return currentEvent.ts.lte(newEvent.ts) ? 'less than' : 'greater than';
39
+ }
40
+
41
+ if (currentEvent.fillRecordId.gt(ZERO) && newEvent.fillRecordId.gt(ZERO)) {
42
+ return currentEvent.fillRecordId.lte(newEvent.fillRecordId)
43
+ ? 'less than'
44
+ : 'greater than';
45
+ } else {
46
+ return currentEvent.ts.lte(newEvent.ts) ? 'less than' : 'greater than';
47
+ }
48
+ }
49
+
50
+ export function getSortFn(
51
+ orderBy: EventSubscriptionOrderBy,
52
+ orderDir: EventSubscriptionOrderDirection,
53
+ eventType: EventType
54
+ ): SortFn {
55
+ if (orderBy === 'client') {
56
+ return orderDir === 'asc' ? clientSortAscFn : clientSortDescFn;
57
+ }
58
+
59
+ switch (eventType) {
60
+ case 'OrderRecord':
61
+ return orderRecordSortFn;
62
+ default:
63
+ return defaultBlockchainSortFn;
64
+ }
65
+ }
@@ -0,0 +1,74 @@
1
+ import { WrappedEvent, EventType } from './types';
2
+
3
+ class Node {
4
+ constructor(
5
+ public key: string,
6
+ public value: WrappedEvent<EventType>[],
7
+ public next?: Node,
8
+ public prev?: Node
9
+ ) {}
10
+ }
11
+
12
+ // lru cache
13
+ export class TxEventCache {
14
+ size = 0;
15
+ head?: Node;
16
+ tail?: Node;
17
+ cacheMap: { [key: string]: Node } = {};
18
+
19
+ constructor(public maxTx = 1024) {}
20
+
21
+ public add(key: string, events: WrappedEvent<EventType>[]): void {
22
+ const existingNode = this.cacheMap[key];
23
+ if (existingNode) {
24
+ this.detach(existingNode);
25
+ this.size--;
26
+ } else if (this.size === this.maxTx) {
27
+ delete this.cacheMap[this.tail.key];
28
+ this.detach(this.tail);
29
+ this.size--;
30
+ }
31
+
32
+ // Write to head of LinkedList
33
+ if (!this.head) {
34
+ this.head = this.tail = new Node(key, events);
35
+ } else {
36
+ const node = new Node(key, events, this.head);
37
+ this.head.prev = node;
38
+ this.head = node;
39
+ }
40
+
41
+ // update cacheMap with LinkedList key and Node reference
42
+ this.cacheMap[key] = this.head;
43
+ this.size++;
44
+ }
45
+
46
+ public has(key: string): boolean {
47
+ return this.cacheMap.hasOwnProperty(key);
48
+ }
49
+
50
+ public get(key: string): WrappedEvent<EventType>[] | undefined {
51
+ return this.cacheMap[key]?.value;
52
+ }
53
+
54
+ detach(node: Node): void {
55
+ if (node.prev !== undefined) {
56
+ node.prev.next = node.next;
57
+ } else {
58
+ this.head = node.next;
59
+ }
60
+
61
+ if (node.next !== undefined) {
62
+ node.next.prev = node.prev;
63
+ } else {
64
+ this.tail = node.prev;
65
+ }
66
+ }
67
+
68
+ public clear(): void {
69
+ this.head = undefined;
70
+ this.tail = undefined;
71
+ this.size = 0;
72
+ this.cacheMap = {};
73
+ }
74
+ }
@@ -0,0 +1,98 @@
1
+ import { Commitment, TransactionSignature } from '@solana/web3.js';
2
+ import {
3
+ DepositRecord,
4
+ FundingPaymentRecord,
5
+ FundingRateRecord,
6
+ LiquidationRecord,
7
+ OrderRecord,
8
+ } from '../index';
9
+
10
+ export type EventSubscriptionOptions = {
11
+ eventTypes?: EventType[];
12
+ maxEventsPerType?: number;
13
+ orderBy?: EventSubscriptionOrderBy;
14
+ orderDir?: EventSubscriptionOrderDirection;
15
+ commitment?: Commitment;
16
+ maxTx?: number;
17
+ logProviderConfig?: LogProviderConfig;
18
+ // when the subscription starts, client might want to backtrack and fetch old tx's
19
+ // this specifies how far to backtrack
20
+ untilTx?: TransactionSignature;
21
+ };
22
+
23
+ export const DefaultEventSubscriptionOptions: EventSubscriptionOptions = {
24
+ eventTypes: [
25
+ 'DepositRecord',
26
+ 'FundingPaymentRecord',
27
+ 'LiquidationRecord',
28
+ 'OrderRecord',
29
+ 'FundingRateRecord',
30
+ ],
31
+ maxEventsPerType: 4096,
32
+ orderBy: 'blockchain',
33
+ orderDir: 'asc',
34
+ commitment: 'confirmed',
35
+ maxTx: 4096,
36
+ logProviderConfig: {
37
+ type: 'websocket',
38
+ },
39
+ };
40
+
41
+ // Whether we sort events based on order blockchain produced events or client receives events
42
+ export type EventSubscriptionOrderBy = 'blockchain' | 'client';
43
+ export type EventSubscriptionOrderDirection = 'asc' | 'desc';
44
+
45
+ export type Event<T> = T & {
46
+ txSig: TransactionSignature;
47
+ slot: number;
48
+ };
49
+
50
+ export type WrappedEvent<Type extends EventType> = EventMap[Type] & {
51
+ eventType: Type;
52
+ };
53
+
54
+ export type WrappedEvents = WrappedEvent<EventType>[];
55
+
56
+ export type EventMap = {
57
+ DepositRecord: Event<DepositRecord>;
58
+ FundingPaymentRecord: Event<FundingPaymentRecord>;
59
+ LiquidationRecord: Event<LiquidationRecord>;
60
+ FundingRateRecord: Event<FundingRateRecord>;
61
+ OrderRecord: Event<OrderRecord>;
62
+ };
63
+
64
+ export type EventType = keyof EventMap;
65
+
66
+ export interface EventSubscriberEvents {
67
+ newEvent: (event: WrappedEvent<EventType>) => void;
68
+ }
69
+
70
+ export type SortFn = (
71
+ currentRecord: EventMap[EventType],
72
+ newRecord: EventMap[EventType]
73
+ ) => 'less than' | 'greater than';
74
+
75
+ export type logProviderCallback = (
76
+ txSig: TransactionSignature,
77
+ slot: number,
78
+ logs: string[]
79
+ ) => void;
80
+
81
+ export interface LogProvider {
82
+ isSubscribed(): boolean;
83
+ subscribe(callback: logProviderCallback): boolean;
84
+ unsubscribe(): Promise<boolean>;
85
+ }
86
+
87
+ export type WebSocketLogProviderConfig = {
88
+ type: 'websocket';
89
+ };
90
+
91
+ export type PollingLogProviderConfig = {
92
+ type: 'polling';
93
+ frequency: number;
94
+ };
95
+
96
+ export type LogProviderConfig =
97
+ | WebSocketLogProviderConfig
98
+ | PollingLogProviderConfig;
@@ -0,0 +1,38 @@
1
+ import { LogProvider, logProviderCallback } from './types';
2
+ import { Commitment, Connection, PublicKey } from '@solana/web3.js';
3
+
4
+ export class WebSocketLogProvider implements LogProvider {
5
+ private subscriptionId: number;
6
+ public constructor(
7
+ private connection: Connection,
8
+ private programId: PublicKey,
9
+ private commitment: Commitment
10
+ ) {}
11
+
12
+ public subscribe(callback: logProviderCallback): boolean {
13
+ if (this.subscriptionId) {
14
+ return true;
15
+ }
16
+
17
+ this.subscriptionId = this.connection.onLogs(
18
+ this.programId,
19
+ (logs, ctx) => {
20
+ callback(logs.signature, ctx.slot, logs.logs);
21
+ },
22
+ this.commitment
23
+ );
24
+ return true;
25
+ }
26
+
27
+ public isSubscribed(): boolean {
28
+ return this.subscriptionId !== undefined;
29
+ }
30
+
31
+ public async unsubscribe(): Promise<boolean> {
32
+ if (this.subscriptionId !== undefined) {
33
+ await this.connection.removeOnLogsListener(this.subscriptionId);
34
+ this.subscriptionId = undefined;
35
+ }
36
+ return true;
37
+ }
38
+ }
@@ -7,13 +7,13 @@ import {
7
7
  ClearingHouse,
8
8
  ClearingHouseUser,
9
9
  initialize,
10
- Markets,
11
10
  PositionDirection,
12
11
  convertToNumber,
13
12
  calculateTradeSlippage,
14
13
  MARK_PRICE_PRECISION,
15
14
  QUOTE_PRECISION,
16
15
  } from '..';
16
+ import { Banks } from '../constants/banks';
17
17
 
18
18
  export const getTokenAddress = (
19
19
  mintAddress: string,
@@ -63,15 +63,18 @@ const main = async () => {
63
63
  const clearingHousePublicKey = new PublicKey(
64
64
  sdkConfig.CLEARING_HOUSE_PROGRAM_ID
65
65
  );
66
- const clearingHouse = ClearingHouse.from(
66
+ const clearingHouse = new ClearingHouse({
67
67
  connection,
68
- provider.wallet,
69
- clearingHousePublicKey
70
- );
68
+ wallet: provider.wallet,
69
+ programID: clearingHousePublicKey,
70
+ });
71
71
  await clearingHouse.subscribe();
72
72
 
73
73
  // Set up Clearing House user client
74
- const user = ClearingHouseUser.from(clearingHouse, wallet.publicKey);
74
+ const user = new ClearingHouseUser({
75
+ clearingHouse,
76
+ userAccountPublicKey: await clearingHouse.getUserAccountPublicKey(),
77
+ });
75
78
 
76
79
  //// Check if clearing house account exists for the current wallet
77
80
  const userAccountExists = await user.exists();
@@ -84,19 +87,21 @@ const main = async () => {
84
87
  await getTokenAddress(
85
88
  usdcTokenAddress.toString(),
86
89
  wallet.publicKey.toString()
87
- )
90
+ ),
91
+ Banks['devnet'][0].bankIndex
88
92
  );
89
93
  }
90
94
 
91
95
  await user.subscribe();
92
96
 
93
97
  // Get current price
94
- const solMarketInfo = Markets.find(
98
+ const solMarketInfo = sdkConfig.MARKETS.find(
95
99
  (market) => market.baseAssetSymbol === 'SOL'
96
100
  );
97
101
 
98
102
  const currentMarketPrice = calculateMarkPrice(
99
- clearingHouse.getMarket(solMarketInfo.marketIndex)
103
+ clearingHouse.getMarketAccount(solMarketInfo.marketIndex),
104
+ undefined
100
105
  );
101
106
 
102
107
  const formattedPrice = convertToNumber(
@@ -107,14 +112,18 @@ const main = async () => {
107
112
  console.log(`Current Market Price is $${formattedPrice}`);
108
113
 
109
114
  // Estimate the slippage for a $5000 LONG trade
110
- const solMarketAccount = clearingHouse.getMarket(solMarketInfo.marketIndex);
115
+ const solMarketAccount = clearingHouse.getMarketAccount(
116
+ solMarketInfo.marketIndex
117
+ );
111
118
 
112
119
  const longAmount = new BN(5000).mul(QUOTE_PRECISION);
113
120
  const slippage = convertToNumber(
114
121
  calculateTradeSlippage(
115
122
  PositionDirection.LONG,
116
123
  longAmount,
117
- solMarketAccount
124
+ solMarketAccount,
125
+ 'quote',
126
+ undefined
118
127
  )[0],
119
128
  MARK_PRICE_PRECISION
120
129
  );