@drift-labs/sdk 0.2.0-master.3 → 0.2.0-master.30

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 (208) hide show
  1. package/README.md +13 -13
  2. package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
  3. package/lib/accounts/bulkUserStatsSubscription.js +21 -0
  4. package/lib/accounts/bulkUserSubscription.js +0 -1
  5. package/lib/accounts/fetch.d.ts +2 -1
  6. package/lib/accounts/fetch.js +9 -1
  7. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +16 -16
  8. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +31 -28
  9. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  10. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  11. package/lib/accounts/types.d.ts +22 -11
  12. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +17 -17
  13. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +37 -35
  14. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  15. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  16. package/lib/addresses/marketAddresses.d.ts +1 -3
  17. package/lib/addresses/pda.d.ts +10 -4
  18. package/lib/addresses/pda.js +51 -14
  19. package/lib/admin.d.ts +34 -22
  20. package/lib/admin.js +182 -73
  21. package/lib/clearingHouse.d.ts +120 -42
  22. package/lib/clearingHouse.js +1505 -254
  23. package/lib/clearingHouseConfig.d.ts +4 -4
  24. package/lib/clearingHouseUser.d.ts +50 -38
  25. package/lib/clearingHouseUser.js +410 -190
  26. package/lib/clearingHouseUserStats.d.ts +18 -0
  27. package/lib/clearingHouseUserStats.js +49 -0
  28. package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
  29. package/lib/clearingHouseUserStatsConfig.js +2 -0
  30. package/lib/config.d.ts +7 -9
  31. package/lib/config.js +21 -21
  32. package/lib/constants/numericConstants.d.ts +18 -12
  33. package/lib/constants/numericConstants.js +28 -21
  34. package/lib/constants/perpMarkets.d.ts +18 -0
  35. package/lib/constants/{markets.js → perpMarkets.js} +7 -7
  36. package/lib/constants/spotMarkets.d.ts +19 -0
  37. package/lib/constants/spotMarkets.js +53 -0
  38. package/lib/dlob/DLOB.d.ts +73 -0
  39. package/lib/dlob/DLOB.js +553 -0
  40. package/lib/dlob/DLOBNode.d.ts +52 -0
  41. package/lib/dlob/DLOBNode.js +82 -0
  42. package/lib/dlob/NodeList.d.ts +26 -0
  43. package/lib/dlob/NodeList.js +138 -0
  44. package/lib/events/eventList.js +3 -0
  45. package/lib/events/eventSubscriber.d.ts +4 -2
  46. package/lib/events/eventSubscriber.js +16 -9
  47. package/lib/events/fetchLogs.d.ts +10 -1
  48. package/lib/events/fetchLogs.js +27 -7
  49. package/lib/events/pollingLogProvider.d.ts +2 -1
  50. package/lib/events/pollingLogProvider.js +6 -2
  51. package/lib/events/sort.js +8 -11
  52. package/lib/events/types.d.ts +7 -2
  53. package/lib/events/types.js +5 -0
  54. package/lib/examples/makeTradeExample.js +20 -8
  55. package/lib/factory/bigNum.d.ts +1 -0
  56. package/lib/factory/bigNum.js +34 -10
  57. package/lib/idl/clearing_house.json +4313 -1433
  58. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  59. package/lib/index.d.ts +21 -6
  60. package/lib/index.js +25 -6
  61. package/lib/math/amm.d.ts +8 -5
  62. package/lib/math/amm.js +68 -46
  63. package/lib/math/auction.js +4 -1
  64. package/lib/math/conversion.js +1 -1
  65. package/lib/math/funding.d.ts +6 -6
  66. package/lib/math/funding.js +11 -10
  67. package/lib/math/insurance.d.ts +4 -0
  68. package/lib/math/insurance.js +27 -0
  69. package/lib/math/margin.d.ts +11 -0
  70. package/lib/math/margin.js +77 -0
  71. package/lib/math/market.d.ts +14 -9
  72. package/lib/math/market.js +69 -7
  73. package/lib/math/oracles.d.ts +4 -0
  74. package/lib/math/oracles.js +36 -8
  75. package/lib/math/orders.d.ts +6 -2
  76. package/lib/math/orders.js +78 -3
  77. package/lib/math/position.d.ts +21 -13
  78. package/lib/math/position.js +76 -36
  79. package/lib/math/repeg.js +14 -5
  80. package/lib/math/spotBalance.d.ts +22 -0
  81. package/lib/math/spotBalance.js +193 -0
  82. package/lib/math/spotMarket.d.ts +4 -0
  83. package/lib/math/spotMarket.js +8 -0
  84. package/lib/math/spotPosition.d.ts +6 -0
  85. package/lib/math/spotPosition.js +23 -0
  86. package/lib/math/trade.d.ts +10 -10
  87. package/lib/math/trade.js +22 -29
  88. package/lib/oracles/pythClient.js +1 -1
  89. package/lib/oracles/quoteAssetOracleClient.js +1 -1
  90. package/lib/oracles/switchboardClient.js +1 -1
  91. package/lib/orderParams.d.ts +14 -5
  92. package/lib/orderParams.js +12 -92
  93. package/lib/serum/serumSubscriber.d.ts +23 -0
  94. package/lib/serum/serumSubscriber.js +41 -0
  95. package/lib/serum/types.d.ts +11 -0
  96. package/lib/serum/types.js +2 -0
  97. package/lib/slot/SlotSubscriber.d.ts +7 -0
  98. package/lib/slot/SlotSubscriber.js +3 -0
  99. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
  100. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
  101. package/lib/tx/retryTxSender.d.ts +1 -1
  102. package/lib/tx/retryTxSender.js +13 -4
  103. package/lib/tx/types.d.ts +1 -1
  104. package/lib/tx/utils.js +1 -1
  105. package/lib/types.d.ts +474 -123
  106. package/lib/types.js +99 -5
  107. package/lib/userMap/userMap.d.ts +25 -0
  108. package/lib/userMap/userMap.js +73 -0
  109. package/lib/userMap/userStatsMap.d.ts +19 -0
  110. package/lib/userMap/userStatsMap.js +68 -0
  111. package/lib/util/computeUnits.js +1 -1
  112. package/lib/util/getTokenAddress.d.ts +2 -0
  113. package/lib/util/getTokenAddress.js +9 -0
  114. package/package.json +9 -5
  115. package/src/accounts/bulkUserStatsSubscription.ts +33 -0
  116. package/src/accounts/bulkUserSubscription.ts +0 -1
  117. package/src/accounts/fetch.ts +27 -2
  118. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +46 -42
  119. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  120. package/src/accounts/types.ts +31 -11
  121. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +64 -59
  122. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  123. package/src/addresses/marketAddresses.ts +1 -2
  124. package/src/addresses/pda.ts +88 -14
  125. package/src/admin.ts +333 -128
  126. package/src/assert/assert.js +9 -0
  127. package/src/clearingHouse.ts +2464 -458
  128. package/src/clearingHouseConfig.ts +4 -3
  129. package/src/clearingHouseUser.ts +747 -291
  130. package/src/clearingHouseUserStats.ts +75 -0
  131. package/src/clearingHouseUserStatsConfig.ts +18 -0
  132. package/src/config.ts +30 -31
  133. package/src/constants/numericConstants.ts +41 -25
  134. package/src/constants/{markets.ts → perpMarkets.ts} +10 -10
  135. package/src/constants/spotMarkets.ts +72 -0
  136. package/src/dlob/DLOB.ts +868 -0
  137. package/src/dlob/DLOBNode.ts +162 -0
  138. package/src/dlob/NodeList.ts +185 -0
  139. package/src/events/eventList.js +77 -0
  140. package/src/events/eventList.ts +3 -0
  141. package/src/events/eventSubscriber.ts +20 -12
  142. package/src/events/fetchLogs.ts +35 -8
  143. package/src/events/pollingLogProvider.ts +10 -2
  144. package/src/events/sort.ts +11 -15
  145. package/src/events/types.ts +16 -1
  146. package/src/examples/makeTradeExample.js +157 -0
  147. package/src/examples/makeTradeExample.ts +32 -14
  148. package/src/factory/bigNum.ts +42 -13
  149. package/src/idl/clearing_house.json +4313 -1433
  150. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  151. package/src/index.ts +21 -6
  152. package/src/math/amm.ts +136 -66
  153. package/src/math/auction.ts +5 -1
  154. package/src/math/conversion.ts +2 -2
  155. package/src/math/funding.ts +20 -18
  156. package/src/math/insurance.ts +35 -0
  157. package/src/math/margin.ts +127 -0
  158. package/src/math/market.ts +138 -12
  159. package/src/math/oracles.ts +63 -9
  160. package/src/math/orders.ts +138 -4
  161. package/src/math/position.ts +119 -58
  162. package/src/math/repeg.ts +16 -6
  163. package/src/math/spotBalance.ts +316 -0
  164. package/src/math/spotMarket.ts +9 -0
  165. package/src/math/spotPosition.ts +47 -0
  166. package/src/math/trade.ts +43 -49
  167. package/src/oracles/pythClient.ts +2 -2
  168. package/src/oracles/quoteAssetOracleClient.ts +2 -2
  169. package/src/oracles/switchboardClient.ts +2 -2
  170. package/src/orderParams.ts +24 -137
  171. package/src/serum/serumSubscriber.ts +80 -0
  172. package/src/serum/types.ts +13 -0
  173. package/src/slot/SlotSubscriber.ts +11 -1
  174. package/src/token/index.js +38 -0
  175. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
  176. package/src/tx/retryTxSender.ts +16 -5
  177. package/src/tx/types.js +2 -0
  178. package/src/tx/types.ts +2 -1
  179. package/src/tx/utils.js +17 -0
  180. package/src/tx/utils.ts +1 -1
  181. package/src/types.ts +477 -125
  182. package/src/userMap/userMap.ts +100 -0
  183. package/src/userMap/userStatsMap.ts +110 -0
  184. package/src/util/computeUnits.js +21 -11
  185. package/src/util/computeUnits.ts +1 -1
  186. package/src/util/getTokenAddress.js +9 -0
  187. package/src/util/getTokenAddress.ts +18 -0
  188. package/src/util/promiseTimeout.js +14 -0
  189. package/src/util/tps.js +27 -0
  190. package/tests/bn/test.ts +12 -3
  191. package/tests/dlob/helpers.ts +374 -0
  192. package/tests/dlob/test.ts +2865 -0
  193. package/lib/constants/banks.d.ts +0 -16
  194. package/lib/constants/banks.js +0 -34
  195. package/lib/constants/markets.d.ts +0 -19
  196. package/lib/math/bankBalance.d.ts +0 -9
  197. package/lib/math/bankBalance.js +0 -75
  198. package/lib/math/state.d.ts +0 -8
  199. package/lib/math/state.js +0 -15
  200. package/lib/orders.d.ts +0 -8
  201. package/lib/orders.js +0 -134
  202. package/src/constants/banks.ts +0 -43
  203. package/src/math/bankBalance.ts +0 -112
  204. package/src/math/state.ts +0 -14
  205. package/src/math/utils.js +0 -27
  206. package/src/math/utils.js.map +0 -1
  207. package/src/orders.ts +0 -244
  208. package/src/util/computeUnits.js.map +0 -1
@@ -0,0 +1,162 @@
1
+ import {
2
+ isOneOfVariant,
3
+ AMM_RESERVE_PRECISION,
4
+ BN,
5
+ convertToNumber,
6
+ getLimitPrice,
7
+ isVariant,
8
+ SpotMarketAccount,
9
+ PerpMarketAccount,
10
+ PRICE_PRECISION,
11
+ OraclePriceData,
12
+ Order,
13
+ ZERO,
14
+ } from '..';
15
+ import { PublicKey } from '@solana/web3.js';
16
+ import { getOrderSignature } from './NodeList';
17
+
18
+ export interface DLOBNode {
19
+ getPrice(oraclePriceData: OraclePriceData, slot: number): BN;
20
+ isVammNode(): boolean;
21
+ order: Order | undefined;
22
+ haveFilled: boolean;
23
+ userAccount: PublicKey | undefined;
24
+ market: SpotMarketAccount | PerpMarketAccount;
25
+ }
26
+
27
+ export abstract class OrderNode implements DLOBNode {
28
+ order: Order;
29
+ market: SpotMarketAccount | PerpMarketAccount;
30
+ userAccount: PublicKey;
31
+ sortValue: BN;
32
+ haveFilled = false;
33
+ haveTrigger = false;
34
+
35
+ constructor(
36
+ order: Order,
37
+ market: SpotMarketAccount | PerpMarketAccount,
38
+ userAccount: PublicKey
39
+ ) {
40
+ this.order = order;
41
+ this.market = market;
42
+ this.userAccount = userAccount;
43
+ this.sortValue = this.getSortValue(order);
44
+ }
45
+
46
+ abstract getSortValue(order: Order): BN;
47
+
48
+ public getLabel(): string {
49
+ let msg = `Order ${getOrderSignature(
50
+ this.order.orderId,
51
+ this.userAccount
52
+ )}`;
53
+ msg += ` ${isVariant(this.order.direction, 'long') ? 'LONG' : 'SHORT'} `;
54
+ msg += `${convertToNumber(
55
+ this.order.baseAssetAmount,
56
+ AMM_RESERVE_PRECISION
57
+ ).toFixed(3)}`;
58
+ if (this.order.price.gt(ZERO)) {
59
+ msg += ` @ ${convertToNumber(this.order.price, PRICE_PRECISION).toFixed(
60
+ 3
61
+ )}`;
62
+ }
63
+ if (this.order.triggerPrice.gt(ZERO)) {
64
+ msg += ` ${
65
+ isVariant(this.order.triggerCondition, 'below') ? 'BELOW' : 'ABOVE'
66
+ }`;
67
+ msg += ` ${convertToNumber(
68
+ this.order.triggerPrice,
69
+ PRICE_PRECISION
70
+ ).toFixed(3)}`;
71
+ }
72
+ return msg;
73
+ }
74
+
75
+ getPrice(oraclePriceData: OraclePriceData, slot: number): BN {
76
+ if (isOneOfVariant(this.order.marketType, ['spot'])) {
77
+ return getLimitPrice(this.order, oraclePriceData, slot);
78
+ } else if (isOneOfVariant(this.order.marketType, ['perp'])) {
79
+ return getLimitPrice(
80
+ this.order,
81
+ oraclePriceData,
82
+ slot,
83
+ this.market as PerpMarketAccount
84
+ );
85
+ } else {
86
+ console.error(`Unknown market type: ${this.order.marketType}`);
87
+ }
88
+ }
89
+
90
+ isVammNode(): boolean {
91
+ return false;
92
+ }
93
+ }
94
+
95
+ export class LimitOrderNode extends OrderNode {
96
+ next?: LimitOrderNode;
97
+ previous?: LimitOrderNode;
98
+
99
+ getSortValue(order: Order): BN {
100
+ return order.price;
101
+ }
102
+ }
103
+
104
+ export class FloatingLimitOrderNode extends OrderNode {
105
+ next?: FloatingLimitOrderNode;
106
+ previous?: FloatingLimitOrderNode;
107
+
108
+ getSortValue(order: Order): BN {
109
+ return order.oraclePriceOffset;
110
+ }
111
+ }
112
+
113
+ export class MarketOrderNode extends OrderNode {
114
+ next?: MarketOrderNode;
115
+ previous?: MarketOrderNode;
116
+
117
+ getSortValue(order: Order): BN {
118
+ return order.slot;
119
+ }
120
+ }
121
+
122
+ export class TriggerOrderNode extends OrderNode {
123
+ next?: TriggerOrderNode;
124
+ previous?: TriggerOrderNode;
125
+
126
+ getSortValue(order: Order): BN {
127
+ return order.triggerPrice;
128
+ }
129
+ }
130
+
131
+ export type DLOBNodeMap = {
132
+ limit: LimitOrderNode;
133
+ floatingLimit: FloatingLimitOrderNode;
134
+ market: MarketOrderNode;
135
+ trigger: TriggerOrderNode;
136
+ };
137
+
138
+ export type DLOBNodeType =
139
+ | 'limit'
140
+ | 'floatingLimit'
141
+ | 'market'
142
+ | ('trigger' & keyof DLOBNodeMap);
143
+
144
+ export function createNode<T extends DLOBNodeType>(
145
+ nodeType: T,
146
+ order: Order,
147
+ market: SpotMarketAccount | PerpMarketAccount,
148
+ userAccount: PublicKey
149
+ ): DLOBNodeMap[T] {
150
+ switch (nodeType) {
151
+ case 'floatingLimit':
152
+ return new FloatingLimitOrderNode(order, market, userAccount);
153
+ case 'limit':
154
+ return new LimitOrderNode(order, market, userAccount);
155
+ case 'market':
156
+ return new MarketOrderNode(order, market, userAccount);
157
+ case 'trigger':
158
+ return new TriggerOrderNode(order, market, userAccount);
159
+ default:
160
+ throw Error(`Unknown DLOBNode type ${nodeType}`);
161
+ }
162
+ }
@@ -0,0 +1,185 @@
1
+ import {
2
+ BN,
3
+ isVariant,
4
+ MarketTypeStr,
5
+ Order,
6
+ PerpMarketAccount,
7
+ SpotMarketAccount,
8
+ } from '..';
9
+ import { PublicKey } from '@solana/web3.js';
10
+ import { createNode, DLOBNode, DLOBNodeMap } from './DLOBNode';
11
+
12
+ export type SortDirection = 'asc' | 'desc';
13
+
14
+ export function getOrderSignature(orderId: BN, userAccount: PublicKey): string {
15
+ return `${userAccount.toString()}-${orderId.toString()}`;
16
+ }
17
+
18
+ export interface DLOBNodeGenerator {
19
+ getGenerator(): Generator<DLOBNode>;
20
+ }
21
+
22
+ export class NodeList<NodeType extends keyof DLOBNodeMap>
23
+ implements DLOBNodeGenerator
24
+ {
25
+ head?: DLOBNodeMap[NodeType];
26
+ length = 0;
27
+ nodeMap = new Map<string, DLOBNodeMap[NodeType]>();
28
+
29
+ constructor(
30
+ private nodeType: NodeType,
31
+ private sortDirection: SortDirection
32
+ ) {}
33
+
34
+ public insert(
35
+ order: Order,
36
+ marketType: MarketTypeStr,
37
+ market: PerpMarketAccount | SpotMarketAccount,
38
+ userAccount: PublicKey
39
+ ): void {
40
+ if (isVariant(order.status, 'init')) {
41
+ return;
42
+ }
43
+
44
+ if (marketType === 'spot') {
45
+ market = market as SpotMarketAccount;
46
+ } else if (marketType === 'perp') {
47
+ market = market as PerpMarketAccount;
48
+ }
49
+
50
+ const newNode = createNode(this.nodeType, order, market, userAccount);
51
+
52
+ const orderId = getOrderSignature(order.orderId, userAccount);
53
+ if (this.nodeMap.has(orderId)) {
54
+ return;
55
+ }
56
+ this.nodeMap.set(orderId, newNode);
57
+
58
+ this.length += 1;
59
+
60
+ if (this.head === undefined) {
61
+ this.head = newNode;
62
+ return;
63
+ }
64
+
65
+ if (this.prependNode(this.head, newNode)) {
66
+ this.head.previous = newNode;
67
+ newNode.next = this.head;
68
+ this.head = newNode;
69
+ return;
70
+ }
71
+
72
+ let currentNode = this.head;
73
+ while (
74
+ currentNode.next !== undefined &&
75
+ !this.prependNode(currentNode.next, newNode)
76
+ ) {
77
+ currentNode = currentNode.next;
78
+ }
79
+
80
+ newNode.next = currentNode.next;
81
+ if (currentNode.next !== undefined) {
82
+ newNode.next.previous = newNode;
83
+ }
84
+ currentNode.next = newNode;
85
+ newNode.previous = currentNode;
86
+ }
87
+
88
+ prependNode(
89
+ currentNode: DLOBNodeMap[NodeType],
90
+ newNode: DLOBNodeMap[NodeType]
91
+ ): boolean {
92
+ const currentOrder = currentNode.order;
93
+ const newOrder = newNode.order;
94
+
95
+ const currentOrderSortPrice = currentNode.sortValue;
96
+ const newOrderSortPrice = newNode.sortValue;
97
+
98
+ if (newOrderSortPrice.eq(currentOrderSortPrice)) {
99
+ return newOrder.ts.lt(currentOrder.ts);
100
+ }
101
+
102
+ if (this.sortDirection === 'asc') {
103
+ return newOrderSortPrice.lt(currentOrderSortPrice);
104
+ } else {
105
+ return newOrderSortPrice.gt(currentOrderSortPrice);
106
+ }
107
+ }
108
+
109
+ public update(order: Order, userAccount: PublicKey): void {
110
+ const orderId = getOrderSignature(order.orderId, userAccount);
111
+ if (this.nodeMap.has(orderId)) {
112
+ const node = this.nodeMap.get(orderId);
113
+ Object.assign(node.order, order);
114
+ node.haveFilled = false;
115
+ }
116
+ }
117
+
118
+ public remove(order: Order, userAccount: PublicKey): void {
119
+ const orderId = getOrderSignature(order.orderId, userAccount);
120
+ if (this.nodeMap.has(orderId)) {
121
+ const node = this.nodeMap.get(orderId);
122
+ if (node.next) {
123
+ node.next.previous = node.previous;
124
+ }
125
+ if (node.previous) {
126
+ node.previous.next = node.next;
127
+ }
128
+
129
+ if (this.head && node.order.orderId.eq(this.head.order.orderId)) {
130
+ this.head = node.next;
131
+ }
132
+
133
+ node.previous = undefined;
134
+ node.next = undefined;
135
+
136
+ this.nodeMap.delete(orderId);
137
+
138
+ this.length--;
139
+ }
140
+ }
141
+
142
+ *getGenerator(): Generator<DLOBNode> {
143
+ let node = this.head;
144
+ while (node !== undefined) {
145
+ yield node;
146
+ node = node.next;
147
+ }
148
+ }
149
+
150
+ public has(order: Order, userAccount: PublicKey): boolean {
151
+ return this.nodeMap.has(getOrderSignature(order.orderId, userAccount));
152
+ }
153
+
154
+ public print(): void {
155
+ let currentNode = this.head;
156
+ while (currentNode !== undefined) {
157
+ console.log(currentNode.getLabel());
158
+ currentNode = currentNode.next;
159
+ }
160
+ }
161
+
162
+ public printTop(): void {
163
+ if (this.head) {
164
+ console.log(this.sortDirection.toUpperCase(), this.head.getLabel());
165
+ } else {
166
+ console.log('---');
167
+ }
168
+ }
169
+ }
170
+
171
+ export function* getVammNodeGenerator(
172
+ price: BN | undefined
173
+ ): Generator<DLOBNode> {
174
+ if (!price) {
175
+ return;
176
+ }
177
+ yield {
178
+ getPrice: () => price,
179
+ isVammNode: () => true,
180
+ order: undefined,
181
+ market: undefined,
182
+ userAccount: undefined,
183
+ haveFilled: false,
184
+ };
185
+ }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventList = void 0;
4
+ class Node {
5
+ constructor(event, next, prev) {
6
+ this.event = event;
7
+ this.next = next;
8
+ this.prev = prev;
9
+ }
10
+ }
11
+ class EventList {
12
+ constructor(eventType, maxSize, sortFn, orderDirection) {
13
+ this.eventType = eventType;
14
+ this.maxSize = maxSize;
15
+ this.sortFn = sortFn;
16
+ this.orderDirection = orderDirection;
17
+ this.size = 0;
18
+ }
19
+ insert(event) {
20
+ this.size++;
21
+ const newNode = new Node(event);
22
+ if (this.head === undefined) {
23
+ this.head = this.tail = newNode;
24
+ return;
25
+ }
26
+ if (this.sortFn(this.head.event, newNode.event) ===
27
+ (this.orderDirection === 'asc' ? 'less than' : 'greater than')) {
28
+ this.head.prev = newNode;
29
+ newNode.next = this.head;
30
+ this.head = newNode;
31
+ }
32
+ else {
33
+ let currentNode = this.head;
34
+ while (currentNode.next !== undefined &&
35
+ this.sortFn(currentNode.next.event, newNode.event) !==
36
+ (this.orderDirection === 'asc' ? 'less than' : 'greater than')) {
37
+ currentNode = currentNode.next;
38
+ }
39
+ newNode.next = currentNode.next;
40
+ if (currentNode.next !== undefined) {
41
+ newNode.next.prev = newNode;
42
+ }
43
+ currentNode.next = newNode;
44
+ newNode.prev = currentNode;
45
+ }
46
+ if (this.size > this.maxSize) {
47
+ this.detach();
48
+ }
49
+ }
50
+ detach() {
51
+ const node = this.tail;
52
+ if (node.prev !== undefined) {
53
+ node.prev.next = node.next;
54
+ }
55
+ else {
56
+ this.head = node.next;
57
+ }
58
+ if (node.next !== undefined) {
59
+ node.next.prev = node.prev;
60
+ }
61
+ else {
62
+ this.tail = node.prev;
63
+ }
64
+ this.size--;
65
+ }
66
+ toArray() {
67
+ return Array.from(this);
68
+ }
69
+ *[Symbol.iterator]() {
70
+ let node = this.head;
71
+ while (node) {
72
+ yield node.event;
73
+ node = node.next;
74
+ }
75
+ }
76
+ }
77
+ exports.EventList = EventList;
@@ -53,7 +53,10 @@ export class EventList<Type extends EventType> {
53
53
  newNode.next = currentNode.next;
54
54
  if (currentNode.next !== undefined) {
55
55
  newNode.next.prev = newNode;
56
+ } else {
57
+ this.tail = newNode;
56
58
  }
59
+
57
60
  currentNode.next = newNode;
58
61
  newNode.prev = currentNode;
59
62
  }
@@ -25,6 +25,8 @@ export class EventSubscriber {
25
25
  private awaitTxResolver = new Map<string, () => void>();
26
26
  private logProvider: LogProvider;
27
27
  public eventEmitter: StrictEventEmitter<EventEmitter, EventSubscriberEvents>;
28
+ private lastSeenSlot: number;
29
+ public lastSeenTxSig: string;
28
30
 
29
31
  public constructor(
30
32
  private connection: Connection,
@@ -62,19 +64,22 @@ export class EventSubscriber {
62
64
  }
63
65
  }
64
66
 
65
- public subscribe(): boolean {
66
- if (this.logProvider.isSubscribed()) {
67
- return true;
68
- }
67
+ public async subscribe(): Promise<boolean> {
68
+ try {
69
+ if (this.logProvider.isSubscribed()) {
70
+ return true;
71
+ }
69
72
 
70
- this.fetchPreviousTx().catch((e) => {
73
+ this.logProvider.subscribe((txSig, slot, logs) => {
74
+ this.handleTxLogs(txSig, slot, logs);
75
+ }, true);
76
+
77
+ return true;
78
+ } catch (e) {
71
79
  console.error('Error fetching previous txs in event subscriber');
72
80
  console.error(e);
73
- });
74
-
75
- return this.logProvider.subscribe((txSig, slot, logs) => {
76
- this.handleTxLogs(txSig, slot, logs);
77
- });
81
+ return false;
82
+ }
78
83
  }
79
84
 
80
85
  private handleTxLogs(
@@ -98,11 +103,14 @@ export class EventSubscriber {
98
103
  this.awaitTxResolver.delete(txSig);
99
104
  }
100
105
 
106
+ if (slot > this.lastSeenSlot) {
107
+ this.lastSeenTxSig = txSig;
108
+ }
101
109
  this.txEventCache.add(txSig, wrappedEvents);
102
110
  }
103
111
 
104
- private async fetchPreviousTx(): Promise<void> {
105
- if (!this.options.untilTx) {
112
+ public async fetchPreviousTx(fetchMax?: boolean): Promise<void> {
113
+ if (!this.options.untilTx && !fetchMax) {
106
114
  return;
107
115
  }
108
116
 
@@ -1,14 +1,18 @@
1
+ import { Program } from '@project-serum/anchor';
1
2
  import {
2
3
  Connection,
3
4
  Finality,
4
5
  PublicKey,
5
6
  TransactionSignature,
6
7
  } from '@solana/web3.js';
8
+ import { WrappedEvents } from './types';
7
9
 
8
10
  type Log = { txSig: TransactionSignature; slot: number; logs: string[] };
9
11
  type FetchLogsResponse = {
10
12
  earliestTx: string;
11
13
  mostRecentTx: string;
14
+ earliestSlot: number;
15
+ mostRecentSlot: number;
12
16
  transactionLogs: Log[];
13
17
  };
14
18
 
@@ -17,19 +21,21 @@ export async function fetchLogs(
17
21
  programId: PublicKey,
18
22
  finality: Finality,
19
23
  beforeTx?: TransactionSignature,
20
- untilTx?: TransactionSignature
21
- ): Promise<FetchLogsResponse | undefined> {
24
+ untilTx?: TransactionSignature,
25
+ limit?: number
26
+ ): Promise<FetchLogsResponse> {
22
27
  const signatures = await connection.getSignaturesForAddress(
23
28
  programId,
24
29
  {
25
30
  before: beforeTx,
26
31
  until: untilTx,
32
+ limit,
27
33
  },
28
34
  finality
29
35
  );
30
36
 
31
37
  const sortedSignatures = signatures.sort((a, b) =>
32
- a.slot < b.slot ? -1 : 1
38
+ a.slot === b.slot ? 0 : a.slot < b.slot ? -1 : 1
33
39
  );
34
40
 
35
41
  const filteredSignatures = sortedSignatures.filter(
@@ -61,14 +67,15 @@ export async function fetchLogs(
61
67
  )
62
68
  ).flat();
63
69
 
64
- const earliestTx = filteredSignatures[0].signature;
65
- const mostRecentTx =
66
- filteredSignatures[filteredSignatures.length - 1].signature;
70
+ const earliest = filteredSignatures[0];
71
+ const mostRecent = filteredSignatures[filteredSignatures.length - 1];
67
72
 
68
73
  return {
69
74
  transactionLogs: transactionLogs,
70
- earliestTx: earliestTx,
71
- mostRecentTx: mostRecentTx,
75
+ earliestTx: earliest.signature,
76
+ mostRecentTx: mostRecent.signature,
77
+ earliestSlot: earliest.slot,
78
+ mostRecentSlot: mostRecent.slot,
72
79
  };
73
80
  }
74
81
 
@@ -78,3 +85,23 @@ function chunk<T>(array: readonly T[], size: number): T[][] {
78
85
  .map((_, index) => index * size)
79
86
  .map((begin) => array.slice(begin, begin + size));
80
87
  }
88
+
89
+ export class LogParser {
90
+ private program: Program;
91
+
92
+ constructor(program: Program) {
93
+ this.program = program;
94
+ }
95
+
96
+ public parseEventsFromLogs(event: Log): WrappedEvents {
97
+ const records: WrappedEvents = [];
98
+ // @ts-ignore
99
+ this.program._events._eventParser.parseLogs(event.logs, (eventLog) => {
100
+ eventLog.data.txSig = event.txSig;
101
+ eventLog.data.slot = event.slot;
102
+ eventLog.data.eventType = eventLog.name;
103
+ records.push(eventLog.data);
104
+ });
105
+ return records;
106
+ }
107
+ }
@@ -13,6 +13,7 @@ export class PollingLogProvider implements LogProvider {
13
13
  private intervalId: NodeJS.Timer;
14
14
  private mostRecentSeenTx?: TransactionSignature;
15
15
  private mutex: number;
16
+ private firstFetch = true;
16
17
 
17
18
  public constructor(
18
19
  private connection: Connection,
@@ -23,7 +24,10 @@ export class PollingLogProvider implements LogProvider {
23
24
  this.finality = commitment === 'finalized' ? 'finalized' : 'confirmed';
24
25
  }
25
26
 
26
- public subscribe(callback: logProviderCallback): boolean {
27
+ public subscribe(
28
+ callback: logProviderCallback,
29
+ skipHistory?: boolean
30
+ ): boolean {
27
31
  if (this.intervalId) {
28
32
  return true;
29
33
  }
@@ -40,9 +44,13 @@ export class PollingLogProvider implements LogProvider {
40
44
  this.programId,
41
45
  this.finality,
42
46
  undefined,
43
- this.mostRecentSeenTx
47
+ this.mostRecentSeenTx,
48
+ // If skipping history, only fetch one log back, not the maximum amount available
49
+ skipHistory && this.firstFetch ? 1 : undefined
44
50
  );
45
51
 
52
+ this.firstFetch = false;
53
+
46
54
  if (response === undefined) {
47
55
  return;
48
56
  }
@@ -6,8 +6,8 @@ import {
6
6
  SortFn,
7
7
  Event,
8
8
  } from './types';
9
- import { OrderRecord } from '../types';
10
- import { PublicKey, ZERO } from '../index';
9
+ import { OrderActionRecord } from '../types';
10
+ import { ZERO } from '../index';
11
11
 
12
12
  function clientSortAscFn(): 'less than' {
13
13
  return 'less than';
@@ -24,21 +24,17 @@ function defaultBlockchainSortFn(
24
24
  return currentEvent.slot <= newEvent.slot ? 'less than' : 'greater than';
25
25
  }
26
26
 
27
- function orderRecordSortFn(
28
- currentEvent: Event<OrderRecord>,
29
- newEvent: Event<OrderRecord>
27
+ function orderActionRecordSortFn(
28
+ currentEvent: Event<OrderActionRecord>,
29
+ newEvent: Event<OrderActionRecord>
30
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)) {
31
+ const currentEventMarketIndex = currentEvent.marketIndex;
32
+ const newEventMarketIndex = newEvent.marketIndex;
33
+ if (currentEventMarketIndex !== newEventMarketIndex) {
38
34
  return currentEvent.ts.lte(newEvent.ts) ? 'less than' : 'greater than';
39
35
  }
40
36
 
41
- if (currentEvent.fillRecordId.gt(ZERO) && newEvent.fillRecordId.gt(ZERO)) {
37
+ if (currentEvent.fillRecordId?.gt(ZERO) && newEvent.fillRecordId?.gt(ZERO)) {
42
38
  return currentEvent.fillRecordId.lte(newEvent.fillRecordId)
43
39
  ? 'less than'
44
40
  : 'greater than';
@@ -57,8 +53,8 @@ export function getSortFn(
57
53
  }
58
54
 
59
55
  switch (eventType) {
60
- case 'OrderRecord':
61
- return orderRecordSortFn;
56
+ case 'OrderActionRecord':
57
+ return orderActionRecordSortFn;
62
58
  default:
63
59
  return defaultBlockchainSortFn;
64
60
  }