@drift-labs/sdk 2.0.20 → 2.1.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.
- package/lib/dlob/DLOB.d.ts +8 -3
- package/lib/dlob/DLOB.js +115 -56
- package/lib/dlob/NodeList.d.ts +1 -0
- package/lib/dlob/NodeList.js +3 -0
- package/package.json +1 -1
- package/src/assert/assert.js +9 -0
- package/src/dlob/DLOB.ts +144 -72
- package/src/dlob/NodeList.ts +7 -0
- package/src/examples/makeTradeExample.js +157 -0
- package/src/token/index.js +38 -0
- package/src/tx/types.js +2 -0
- package/src/tx/utils.js +17 -0
- package/src/util/computeUnits.js +27 -0
- package/src/util/getTokenAddress.js +9 -0
- package/src/util/promiseTimeout.js +14 -0
- package/src/util/tps.js +27 -0
package/lib/dlob/DLOB.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// <reference types="bn.js" />
|
|
2
2
|
import { NodeList } from './NodeList';
|
|
3
|
-
import { MarketType, BN, DriftClient, Order, SpotMarketAccount, PerpMarketAccount, OraclePriceData, SlotSubscriber, MarketTypeStr, StateAccount, UserMap } from '..';
|
|
3
|
+
import { MarketType, BN, DriftClient, Order, SpotMarketAccount, PerpMarketAccount, OraclePriceData, SlotSubscriber, MarketTypeStr, StateAccount, UserMap, OrderRecord, OrderActionRecord } from '..';
|
|
4
4
|
import { PublicKey } from '@solana/web3.js';
|
|
5
|
-
import { DLOBNode, TriggerOrderNode } from '..';
|
|
5
|
+
import { DLOBNode, DLOBNodeType, TriggerOrderNode } from '..';
|
|
6
6
|
import { DLOBOrders } from './DLOBOrders';
|
|
7
7
|
export declare type MarketNodeLists = {
|
|
8
8
|
limit: {
|
|
@@ -43,10 +43,15 @@ export declare class DLOB {
|
|
|
43
43
|
*/
|
|
44
44
|
initFromUserMap(userMap: UserMap): Promise<boolean>;
|
|
45
45
|
initFromOrders(dlobOrders: DLOBOrders): boolean;
|
|
46
|
+
handleOrderRecord(record: OrderRecord): void;
|
|
47
|
+
handleOrderActionRecord(record: OrderActionRecord): void;
|
|
46
48
|
insertOrder(order: Order, userAccount: PublicKey, onInsert?: OrderBookCallback): void;
|
|
47
49
|
addOrderList(marketType: MarketTypeStr, marketIndex: number): void;
|
|
50
|
+
updateOrder(order: Order, userAccount: PublicKey, cumulativeBaseAssetAmountFilled: BN, onUpdate?: OrderBookCallback): void;
|
|
48
51
|
trigger(order: Order, userAccount: PublicKey, onTrigger?: OrderBookCallback): void;
|
|
52
|
+
delete(order: Order, userAccount: PublicKey, onDelete?: OrderBookCallback): void;
|
|
49
53
|
getListForOrder(order: Order): NodeList<any> | undefined;
|
|
54
|
+
getOrder(orderId: number, userAccount: PublicKey): Order | undefined;
|
|
50
55
|
findNodesToFill(marketIndex: number, fallbackBid: BN | undefined, fallbackAsk: BN | undefined, slot: number, ts: number, marketType: MarketType, oraclePriceData: OraclePriceData, stateAccount: StateAccount, marketAccount: PerpMarketAccount | SpotMarketAccount): NodeToFill[];
|
|
51
56
|
findLimitOrderNodesToFill(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, isAmmPaused: boolean, fallbackAsk: BN | undefined, fallbackBid: BN | undefined): NodeToFill[];
|
|
52
57
|
findMarketNodesToFill(marketIndex: number, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData, isAmmPaused: boolean, fallbackAsk: BN | undefined, fallbackBid?: BN | undefined): NodeToFill[];
|
|
@@ -71,6 +76,6 @@ export declare class DLOB {
|
|
|
71
76
|
findNodesToTrigger(marketIndex: number, slot: number, oraclePrice: BN, marketType: MarketType, stateAccount: StateAccount): NodeToTrigger[];
|
|
72
77
|
printTopOfOrderLists(sdkConfig: any, driftClient: DriftClient, slotSubscriber: SlotSubscriber, marketIndex: number, marketType: MarketType): void;
|
|
73
78
|
getDLOBOrders(): DLOBOrders;
|
|
74
|
-
|
|
79
|
+
getNodeLists(): Generator<NodeList<DLOBNodeType>>;
|
|
75
80
|
}
|
|
76
81
|
export {};
|
package/lib/dlob/DLOB.js
CHANGED
|
@@ -63,6 +63,56 @@ class DLOB {
|
|
|
63
63
|
this.initialized = true;
|
|
64
64
|
return true;
|
|
65
65
|
}
|
|
66
|
+
handleOrderRecord(record) {
|
|
67
|
+
this.insertOrder(record.order, record.user);
|
|
68
|
+
}
|
|
69
|
+
handleOrderActionRecord(record) {
|
|
70
|
+
if ((0, __1.isOneOfVariant)(record.action, ['place', 'expire'])) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if ((0, __1.isVariant)(record.action, 'trigger')) {
|
|
74
|
+
if (record.taker !== null) {
|
|
75
|
+
const takerOrder = this.getOrder(record.takerOrderId, record.taker);
|
|
76
|
+
if (takerOrder) {
|
|
77
|
+
this.trigger(takerOrder, record.taker);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (record.maker !== null) {
|
|
81
|
+
const makerOrder = this.getOrder(record.makerOrderId, record.maker);
|
|
82
|
+
if (makerOrder) {
|
|
83
|
+
this.trigger(makerOrder, record.maker);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if ((0, __1.isVariant)(record.action, 'fill')) {
|
|
88
|
+
if (record.taker !== null) {
|
|
89
|
+
const takerOrder = this.getOrder(record.takerOrderId, record.taker);
|
|
90
|
+
if (takerOrder) {
|
|
91
|
+
this.updateOrder(takerOrder, record.taker, record.takerOrderCumulativeBaseAssetAmountFilled);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (record.maker !== null) {
|
|
95
|
+
const makerOrder = this.getOrder(record.makerOrderId, record.maker);
|
|
96
|
+
if (makerOrder) {
|
|
97
|
+
this.updateOrder(makerOrder, record.maker, record.makerOrderCumulativeBaseAssetAmountFilled);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if ((0, __1.isVariant)(record.action, 'cancel')) {
|
|
102
|
+
if (record.taker !== null) {
|
|
103
|
+
const takerOrder = this.getOrder(record.takerOrderId, record.taker);
|
|
104
|
+
if (takerOrder) {
|
|
105
|
+
this.delete(takerOrder, record.taker);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (record.maker !== null) {
|
|
109
|
+
const makerOrder = this.getOrder(record.makerOrderId, record.maker);
|
|
110
|
+
if (makerOrder) {
|
|
111
|
+
this.delete(makerOrder, record.maker);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
66
116
|
insertOrder(order, userAccount, onInsert) {
|
|
67
117
|
var _a;
|
|
68
118
|
if ((0, __1.isVariant)(order.status, 'init')) {
|
|
@@ -102,11 +152,29 @@ class DLOB {
|
|
|
102
152
|
},
|
|
103
153
|
});
|
|
104
154
|
}
|
|
155
|
+
updateOrder(order, userAccount, cumulativeBaseAssetAmountFilled, onUpdate) {
|
|
156
|
+
var _a;
|
|
157
|
+
if (order.baseAssetAmount.eq(cumulativeBaseAssetAmountFilled)) {
|
|
158
|
+
this.delete(order, userAccount);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (order.baseAssetAmountFilled.eq(cumulativeBaseAssetAmountFilled)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
order.baseAssetAmountFilled = cumulativeBaseAssetAmountFilled;
|
|
165
|
+
(_a = this.getListForOrder(order)) === null || _a === void 0 ? void 0 : _a.update(order, userAccount);
|
|
166
|
+
if (onUpdate) {
|
|
167
|
+
onUpdate();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
105
170
|
trigger(order, userAccount, onTrigger) {
|
|
106
171
|
var _a;
|
|
107
172
|
if ((0, __1.isVariant)(order.status, 'init')) {
|
|
108
173
|
return;
|
|
109
174
|
}
|
|
175
|
+
if ((0, __1.isTriggered)(order)) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
110
178
|
const marketType = (0, __1.getVariant)(order.marketType);
|
|
111
179
|
const triggerList = this.orderLists.get(marketType).get(order.marketIndex)
|
|
112
180
|
.trigger[(0, __1.isVariant)(order.triggerCondition, 'above') ? 'above' : 'below'];
|
|
@@ -116,6 +184,16 @@ class DLOB {
|
|
|
116
184
|
onTrigger();
|
|
117
185
|
}
|
|
118
186
|
}
|
|
187
|
+
delete(order, userAccount, onDelete) {
|
|
188
|
+
var _a;
|
|
189
|
+
if ((0, __1.isVariant)(order.status, 'init')) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
(_a = this.getListForOrder(order)) === null || _a === void 0 ? void 0 : _a.remove(order, userAccount);
|
|
193
|
+
if (onDelete) {
|
|
194
|
+
onDelete();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
119
197
|
getListForOrder(order) {
|
|
120
198
|
const isInactiveTriggerOrder = (0, __1.mustBeTriggered)(order) && !(0, __1.isTriggered)(order);
|
|
121
199
|
let type;
|
|
@@ -144,6 +222,15 @@ class DLOB {
|
|
|
144
222
|
}
|
|
145
223
|
return this.orderLists.get(marketType).get(order.marketIndex)[type][subType];
|
|
146
224
|
}
|
|
225
|
+
getOrder(orderId, userAccount) {
|
|
226
|
+
for (const nodeList of this.getNodeLists()) {
|
|
227
|
+
const node = nodeList.get(orderId, userAccount);
|
|
228
|
+
if (node) {
|
|
229
|
+
return node.order;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
147
234
|
findNodesToFill(marketIndex, fallbackBid, fallbackAsk, slot, ts, marketType, oraclePriceData, stateAccount, marketAccount) {
|
|
148
235
|
if ((0, exchangeStatus_1.fillPaused)(stateAccount, marketAccount)) {
|
|
149
236
|
return [];
|
|
@@ -644,66 +731,38 @@ class DLOB {
|
|
|
644
731
|
}
|
|
645
732
|
}
|
|
646
733
|
getDLOBOrders() {
|
|
647
|
-
|
|
648
|
-
for (const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
734
|
+
const dlobOrders = [];
|
|
735
|
+
for (const nodeList of this.getNodeLists()) {
|
|
736
|
+
for (const node of nodeList.getGenerator()) {
|
|
737
|
+
dlobOrders.push({
|
|
738
|
+
user: node.userAccount,
|
|
739
|
+
order: node.order,
|
|
740
|
+
});
|
|
741
|
+
}
|
|
653
742
|
}
|
|
654
743
|
return dlobOrders;
|
|
655
744
|
}
|
|
656
|
-
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
for (const node of nodeLists.market.ask.getGenerator()) {
|
|
677
|
-
orders.push({
|
|
678
|
-
user: node.userAccount,
|
|
679
|
-
order: node.order,
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
for (const node of nodeLists.floatingLimit.bid.getGenerator()) {
|
|
683
|
-
orders.push({
|
|
684
|
-
user: node.userAccount,
|
|
685
|
-
order: node.order,
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
for (const node of nodeLists.floatingLimit.ask.getGenerator()) {
|
|
689
|
-
orders.push({
|
|
690
|
-
user: node.userAccount,
|
|
691
|
-
order: node.order,
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
for (const node of nodeLists.trigger.below.getGenerator()) {
|
|
695
|
-
orders.push({
|
|
696
|
-
user: node.userAccount,
|
|
697
|
-
order: node.order,
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
for (const node of nodeLists.trigger.above.getGenerator()) {
|
|
701
|
-
orders.push({
|
|
702
|
-
user: node.userAccount,
|
|
703
|
-
order: node.order,
|
|
704
|
-
});
|
|
745
|
+
*getNodeLists() {
|
|
746
|
+
for (const [_, nodeLists] of this.orderLists.get('perp')) {
|
|
747
|
+
yield nodeLists.limit.bid;
|
|
748
|
+
yield nodeLists.limit.ask;
|
|
749
|
+
yield nodeLists.market.bid;
|
|
750
|
+
yield nodeLists.market.ask;
|
|
751
|
+
yield nodeLists.floatingLimit.bid;
|
|
752
|
+
yield nodeLists.floatingLimit.ask;
|
|
753
|
+
yield nodeLists.trigger.above;
|
|
754
|
+
yield nodeLists.trigger.below;
|
|
755
|
+
}
|
|
756
|
+
for (const [_, nodeLists] of this.orderLists.get('spot')) {
|
|
757
|
+
yield nodeLists.limit.bid;
|
|
758
|
+
yield nodeLists.limit.ask;
|
|
759
|
+
yield nodeLists.market.bid;
|
|
760
|
+
yield nodeLists.market.ask;
|
|
761
|
+
yield nodeLists.floatingLimit.bid;
|
|
762
|
+
yield nodeLists.floatingLimit.ask;
|
|
763
|
+
yield nodeLists.trigger.above;
|
|
764
|
+
yield nodeLists.trigger.below;
|
|
705
765
|
}
|
|
706
|
-
return orders;
|
|
707
766
|
}
|
|
708
767
|
}
|
|
709
768
|
exports.DLOB = DLOB;
|
package/lib/dlob/NodeList.d.ts
CHANGED
|
@@ -21,6 +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
25
|
print(): void;
|
|
25
26
|
printTop(): void;
|
|
26
27
|
}
|
package/lib/dlob/NodeList.js
CHANGED
|
@@ -104,6 +104,9 @@ 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));
|
|
109
|
+
}
|
|
107
110
|
print() {
|
|
108
111
|
let currentNode = this.head;
|
|
109
112
|
while (currentNode !== undefined) {
|
package/package.json
CHANGED
package/src/dlob/DLOB.ts
CHANGED
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
isTriggered,
|
|
25
25
|
getLimitPrice,
|
|
26
26
|
UserMap,
|
|
27
|
+
OrderRecord,
|
|
28
|
+
OrderActionRecord,
|
|
27
29
|
} from '..';
|
|
28
30
|
import { PublicKey } from '@solana/web3.js';
|
|
29
31
|
import { DLOBNode, DLOBNodeType, TriggerOrderNode } from '..';
|
|
@@ -131,6 +133,68 @@ export class DLOB {
|
|
|
131
133
|
return true;
|
|
132
134
|
}
|
|
133
135
|
|
|
136
|
+
public handleOrderRecord(record: OrderRecord): void {
|
|
137
|
+
this.insertOrder(record.order, record.user);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public handleOrderActionRecord(record: OrderActionRecord): void {
|
|
141
|
+
if (isOneOfVariant(record.action, ['place', 'expire'])) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (isVariant(record.action, 'trigger')) {
|
|
146
|
+
if (record.taker !== null) {
|
|
147
|
+
const takerOrder = this.getOrder(record.takerOrderId, record.taker);
|
|
148
|
+
if (takerOrder) {
|
|
149
|
+
this.trigger(takerOrder, record.taker);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (record.maker !== null) {
|
|
154
|
+
const makerOrder = this.getOrder(record.makerOrderId, record.maker);
|
|
155
|
+
if (makerOrder) {
|
|
156
|
+
this.trigger(makerOrder, record.maker);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} else if (isVariant(record.action, 'fill')) {
|
|
160
|
+
if (record.taker !== null) {
|
|
161
|
+
const takerOrder = this.getOrder(record.takerOrderId, record.taker);
|
|
162
|
+
if (takerOrder) {
|
|
163
|
+
this.updateOrder(
|
|
164
|
+
takerOrder,
|
|
165
|
+
record.taker,
|
|
166
|
+
record.takerOrderCumulativeBaseAssetAmountFilled
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (record.maker !== null) {
|
|
172
|
+
const makerOrder = this.getOrder(record.makerOrderId, record.maker);
|
|
173
|
+
if (makerOrder) {
|
|
174
|
+
this.updateOrder(
|
|
175
|
+
makerOrder,
|
|
176
|
+
record.maker,
|
|
177
|
+
record.makerOrderCumulativeBaseAssetAmountFilled
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} else if (isVariant(record.action, 'cancel')) {
|
|
182
|
+
if (record.taker !== null) {
|
|
183
|
+
const takerOrder = this.getOrder(record.takerOrderId, record.taker);
|
|
184
|
+
if (takerOrder) {
|
|
185
|
+
this.delete(takerOrder, record.taker);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (record.maker !== null) {
|
|
190
|
+
const makerOrder = this.getOrder(record.makerOrderId, record.maker);
|
|
191
|
+
if (makerOrder) {
|
|
192
|
+
this.delete(makerOrder, record.maker);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
134
198
|
public insertOrder(
|
|
135
199
|
order: Order,
|
|
136
200
|
userAccount: PublicKey,
|
|
@@ -179,6 +243,29 @@ export class DLOB {
|
|
|
179
243
|
});
|
|
180
244
|
}
|
|
181
245
|
|
|
246
|
+
public updateOrder(
|
|
247
|
+
order: Order,
|
|
248
|
+
userAccount: PublicKey,
|
|
249
|
+
cumulativeBaseAssetAmountFilled: BN,
|
|
250
|
+
onUpdate?: OrderBookCallback
|
|
251
|
+
): void {
|
|
252
|
+
if (order.baseAssetAmount.eq(cumulativeBaseAssetAmountFilled)) {
|
|
253
|
+
this.delete(order, userAccount);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (order.baseAssetAmountFilled.eq(cumulativeBaseAssetAmountFilled)) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
order.baseAssetAmountFilled = cumulativeBaseAssetAmountFilled;
|
|
262
|
+
this.getListForOrder(order)?.update(order, userAccount);
|
|
263
|
+
|
|
264
|
+
if (onUpdate) {
|
|
265
|
+
onUpdate();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
182
269
|
public trigger(
|
|
183
270
|
order: Order,
|
|
184
271
|
userAccount: PublicKey,
|
|
@@ -188,6 +275,10 @@ export class DLOB {
|
|
|
188
275
|
return;
|
|
189
276
|
}
|
|
190
277
|
|
|
278
|
+
if (isTriggered(order)) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
191
282
|
const marketType = getVariant(order.marketType) as MarketTypeStr;
|
|
192
283
|
|
|
193
284
|
const triggerList = this.orderLists.get(marketType).get(order.marketIndex)
|
|
@@ -200,6 +291,21 @@ export class DLOB {
|
|
|
200
291
|
}
|
|
201
292
|
}
|
|
202
293
|
|
|
294
|
+
public delete(
|
|
295
|
+
order: Order,
|
|
296
|
+
userAccount: PublicKey,
|
|
297
|
+
onDelete?: OrderBookCallback
|
|
298
|
+
): void {
|
|
299
|
+
if (isVariant(order.status, 'init')) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
this.getListForOrder(order)?.remove(order, userAccount);
|
|
304
|
+
if (onDelete) {
|
|
305
|
+
onDelete();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
203
309
|
public getListForOrder(order: Order): NodeList<any> | undefined {
|
|
204
310
|
const isInactiveTriggerOrder =
|
|
205
311
|
mustBeTriggered(order) && !isTriggered(order);
|
|
@@ -233,6 +339,17 @@ export class DLOB {
|
|
|
233
339
|
];
|
|
234
340
|
}
|
|
235
341
|
|
|
342
|
+
public getOrder(orderId: number, userAccount: PublicKey): Order | undefined {
|
|
343
|
+
for (const nodeList of this.getNodeLists()) {
|
|
344
|
+
const node = nodeList.get(orderId, userAccount);
|
|
345
|
+
if (node) {
|
|
346
|
+
return node.order;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return undefined;
|
|
351
|
+
}
|
|
352
|
+
|
|
236
353
|
public findNodesToFill(
|
|
237
354
|
marketIndex: number,
|
|
238
355
|
fallbackBid: BN | undefined,
|
|
@@ -1164,86 +1281,41 @@ export class DLOB {
|
|
|
1164
1281
|
}
|
|
1165
1282
|
|
|
1166
1283
|
public getDLOBOrders(): DLOBOrders {
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
for (const [marketIndex, nodeLists] of this.orderLists.get('perp')) {
|
|
1170
|
-
dlobOrders = dlobOrders.concat(
|
|
1171
|
-
this.getOrdersForMarket(marketIndex, MarketType.PERP, nodeLists)
|
|
1172
|
-
);
|
|
1173
|
-
}
|
|
1284
|
+
const dlobOrders: DLOBOrders = [];
|
|
1174
1285
|
|
|
1175
|
-
for (const
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1286
|
+
for (const nodeList of this.getNodeLists()) {
|
|
1287
|
+
for (const node of nodeList.getGenerator()) {
|
|
1288
|
+
dlobOrders.push({
|
|
1289
|
+
user: node.userAccount,
|
|
1290
|
+
order: node.order,
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1179
1293
|
}
|
|
1180
1294
|
|
|
1181
1295
|
return dlobOrders;
|
|
1182
1296
|
}
|
|
1183
1297
|
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
order: node.order,
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
for (const node of nodeLists.limit.ask.getGenerator()) {
|
|
1199
|
-
orders.push({
|
|
1200
|
-
user: node.userAccount,
|
|
1201
|
-
order: node.order,
|
|
1202
|
-
});
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
for (const node of nodeLists.market.bid.getGenerator()) {
|
|
1206
|
-
orders.push({
|
|
1207
|
-
user: node.userAccount,
|
|
1208
|
-
order: node.order,
|
|
1209
|
-
});
|
|
1298
|
+
*getNodeLists(): Generator<NodeList<DLOBNodeType>> {
|
|
1299
|
+
for (const [_, nodeLists] of this.orderLists.get('perp')) {
|
|
1300
|
+
yield nodeLists.limit.bid;
|
|
1301
|
+
yield nodeLists.limit.ask;
|
|
1302
|
+
yield nodeLists.market.bid;
|
|
1303
|
+
yield nodeLists.market.ask;
|
|
1304
|
+
yield nodeLists.floatingLimit.bid;
|
|
1305
|
+
yield nodeLists.floatingLimit.ask;
|
|
1306
|
+
yield nodeLists.trigger.above;
|
|
1307
|
+
yield nodeLists.trigger.below;
|
|
1210
1308
|
}
|
|
1211
1309
|
|
|
1212
|
-
for (const
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1310
|
+
for (const [_, nodeLists] of this.orderLists.get('spot')) {
|
|
1311
|
+
yield nodeLists.limit.bid;
|
|
1312
|
+
yield nodeLists.limit.ask;
|
|
1313
|
+
yield nodeLists.market.bid;
|
|
1314
|
+
yield nodeLists.market.ask;
|
|
1315
|
+
yield nodeLists.floatingLimit.bid;
|
|
1316
|
+
yield nodeLists.floatingLimit.ask;
|
|
1317
|
+
yield nodeLists.trigger.above;
|
|
1318
|
+
yield nodeLists.trigger.below;
|
|
1217
1319
|
}
|
|
1218
|
-
|
|
1219
|
-
for (const node of nodeLists.floatingLimit.bid.getGenerator()) {
|
|
1220
|
-
orders.push({
|
|
1221
|
-
user: node.userAccount,
|
|
1222
|
-
order: node.order,
|
|
1223
|
-
});
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
for (const node of nodeLists.floatingLimit.ask.getGenerator()) {
|
|
1227
|
-
orders.push({
|
|
1228
|
-
user: node.userAccount,
|
|
1229
|
-
order: node.order,
|
|
1230
|
-
});
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
for (const node of nodeLists.trigger.below.getGenerator()) {
|
|
1234
|
-
orders.push({
|
|
1235
|
-
user: node.userAccount,
|
|
1236
|
-
order: node.order,
|
|
1237
|
-
});
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
for (const node of nodeLists.trigger.above.getGenerator()) {
|
|
1241
|
-
orders.push({
|
|
1242
|
-
user: node.userAccount,
|
|
1243
|
-
order: node.order,
|
|
1244
|
-
});
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
return orders;
|
|
1248
1320
|
}
|
|
1249
1321
|
}
|
package/src/dlob/NodeList.ts
CHANGED
|
@@ -146,6 +146,13 @@ 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));
|
|
154
|
+
}
|
|
155
|
+
|
|
149
156
|
public print(): void {
|
|
150
157
|
let currentNode = this.head;
|
|
151
158
|
while (currentNode !== undefined) {
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
var __awaiter =
|
|
3
|
+
(this && this.__awaiter) ||
|
|
4
|
+
function (thisArg, _arguments, P, generator) {
|
|
5
|
+
function adopt(value) {
|
|
6
|
+
return value instanceof P
|
|
7
|
+
? value
|
|
8
|
+
: new P(function (resolve) {
|
|
9
|
+
resolve(value);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
13
|
+
function fulfilled(value) {
|
|
14
|
+
try {
|
|
15
|
+
step(generator.next(value));
|
|
16
|
+
} catch (e) {
|
|
17
|
+
reject(e);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function rejected(value) {
|
|
21
|
+
try {
|
|
22
|
+
step(generator['throw'](value));
|
|
23
|
+
} catch (e) {
|
|
24
|
+
reject(e);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function step(result) {
|
|
28
|
+
result.done
|
|
29
|
+
? resolve(result.value)
|
|
30
|
+
: adopt(result.value).then(fulfilled, rejected);
|
|
31
|
+
}
|
|
32
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
36
|
+
exports.getTokenAddress = void 0;
|
|
37
|
+
const anchor_1 = require('@project-serum/anchor');
|
|
38
|
+
const __1 = require('..');
|
|
39
|
+
const spl_token_1 = require('@solana/spl-token');
|
|
40
|
+
const web3_js_1 = require('@solana/web3.js');
|
|
41
|
+
const __2 = require('..');
|
|
42
|
+
const banks_1 = require('../constants/spotMarkets');
|
|
43
|
+
const getTokenAddress = (mintAddress, userPubKey) => {
|
|
44
|
+
return spl_token_1.Token.getAssociatedTokenAddress(
|
|
45
|
+
new web3_js_1.PublicKey(`ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL`),
|
|
46
|
+
spl_token_1.TOKEN_PROGRAM_ID,
|
|
47
|
+
new web3_js_1.PublicKey(mintAddress),
|
|
48
|
+
new web3_js_1.PublicKey(userPubKey)
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
exports.getTokenAddress = getTokenAddress;
|
|
52
|
+
const main = () =>
|
|
53
|
+
__awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
+
// Initialize Drift SDK
|
|
55
|
+
const sdkConfig = __2.initialize({ env: 'devnet' });
|
|
56
|
+
// Set up the Wallet and Provider
|
|
57
|
+
const privateKey = process.env.BOT_PRIVATE_KEY; // stored as an array string
|
|
58
|
+
const keypair = web3_js_1.Keypair.fromSecretKey(
|
|
59
|
+
Uint8Array.from(JSON.parse(privateKey))
|
|
60
|
+
);
|
|
61
|
+
const wallet = new __1.Wallet(keypair);
|
|
62
|
+
// Set up the Connection
|
|
63
|
+
const rpcAddress = process.env.RPC_ADDRESS; // can use: https://api.devnet.solana.com for devnet; https://api.mainnet-beta.solana.com for mainnet;
|
|
64
|
+
const connection = new web3_js_1.Connection(rpcAddress);
|
|
65
|
+
// Set up the Provider
|
|
66
|
+
const provider = new anchor_1.AnchorProvider(
|
|
67
|
+
connection,
|
|
68
|
+
wallet,
|
|
69
|
+
anchor_1.AnchorProvider.defaultOptions()
|
|
70
|
+
);
|
|
71
|
+
// Check SOL Balance
|
|
72
|
+
const lamportsBalance = yield connection.getBalance(wallet.publicKey);
|
|
73
|
+
console.log('SOL balance:', lamportsBalance / Math.pow(10, 9));
|
|
74
|
+
// Misc. other things to set up
|
|
75
|
+
const usdcTokenAddress = yield exports.getTokenAddress(
|
|
76
|
+
sdkConfig.USDC_MINT_ADDRESS,
|
|
77
|
+
wallet.publicKey.toString()
|
|
78
|
+
);
|
|
79
|
+
// Set up the Drift Clearing House
|
|
80
|
+
const clearingHousePublicKey = new web3_js_1.PublicKey(
|
|
81
|
+
sdkConfig.DRIFT_PROGRAM_ID
|
|
82
|
+
);
|
|
83
|
+
const clearingHouse = new __2.ClearingHouse({
|
|
84
|
+
connection,
|
|
85
|
+
wallet: provider.wallet,
|
|
86
|
+
programID: clearingHousePublicKey,
|
|
87
|
+
});
|
|
88
|
+
yield clearingHouse.subscribe();
|
|
89
|
+
// Set up Clearing House user client
|
|
90
|
+
const user = new __2.ClearingHouseUser({
|
|
91
|
+
clearingHouse,
|
|
92
|
+
userAccountPublicKey: yield clearingHouse.getUserAccountPublicKey(),
|
|
93
|
+
});
|
|
94
|
+
//// Check if clearing house account exists for the current wallet
|
|
95
|
+
const userAccountExists = yield user.exists();
|
|
96
|
+
if (!userAccountExists) {
|
|
97
|
+
//// Create a Clearing House account by Depositing some USDC ($10,000 in this case)
|
|
98
|
+
const depositAmount = new anchor_1.BN(10000).mul(__2.QUOTE_PRECISION);
|
|
99
|
+
yield clearingHouse.initializeUserAccountAndDepositCollateral(
|
|
100
|
+
depositAmount,
|
|
101
|
+
yield exports.getTokenAddress(
|
|
102
|
+
usdcTokenAddress.toString(),
|
|
103
|
+
wallet.publicKey.toString()
|
|
104
|
+
),
|
|
105
|
+
banks_1.SpotMarkets['devnet'][0].marketIndex
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
yield user.subscribe();
|
|
109
|
+
// Get current price
|
|
110
|
+
const solMarketInfo = sdkConfig.PERP_MARKETS.find(
|
|
111
|
+
(market) => market.baseAssetSymbol === 'SOL'
|
|
112
|
+
);
|
|
113
|
+
const currentMarketPrice = __2.calculateMarkPrice(
|
|
114
|
+
clearingHouse.getMarketAccount(solMarketInfo.marketIndex),
|
|
115
|
+
undefined
|
|
116
|
+
);
|
|
117
|
+
const formattedPrice = __2.convertToNumber(
|
|
118
|
+
currentMarketPrice,
|
|
119
|
+
__2.PRICE_PRECISION
|
|
120
|
+
);
|
|
121
|
+
console.log(`Current Market Price is $${formattedPrice}`);
|
|
122
|
+
// Estimate the slippage for a $5000 LONG trade
|
|
123
|
+
const solMarketAccount = clearingHouse.getMarketAccount(
|
|
124
|
+
solMarketInfo.marketIndex
|
|
125
|
+
);
|
|
126
|
+
const longAmount = new anchor_1.BN(5000).mul(__2.QUOTE_PRECISION);
|
|
127
|
+
const slippage = __2.convertToNumber(
|
|
128
|
+
__2.calculateTradeSlippage(
|
|
129
|
+
__2.PositionDirection.LONG,
|
|
130
|
+
longAmount,
|
|
131
|
+
solMarketAccount,
|
|
132
|
+
'quote',
|
|
133
|
+
undefined
|
|
134
|
+
)[0],
|
|
135
|
+
__2.PRICE_PRECISION
|
|
136
|
+
);
|
|
137
|
+
console.log(
|
|
138
|
+
`Slippage for a $5000 LONG on the SOL market would be $${slippage}`
|
|
139
|
+
);
|
|
140
|
+
// Make a $5000 LONG trade
|
|
141
|
+
yield clearingHouse.openPosition(
|
|
142
|
+
__2.PositionDirection.LONG,
|
|
143
|
+
longAmount,
|
|
144
|
+
solMarketInfo.marketIndex
|
|
145
|
+
);
|
|
146
|
+
console.log(`LONGED $5000 SOL`);
|
|
147
|
+
// Reduce the position by $2000
|
|
148
|
+
const reduceAmount = new anchor_1.BN(2000).mul(__2.QUOTE_PRECISION);
|
|
149
|
+
yield clearingHouse.openPosition(
|
|
150
|
+
__2.PositionDirection.SHORT,
|
|
151
|
+
reduceAmount,
|
|
152
|
+
solMarketInfo.marketIndex
|
|
153
|
+
);
|
|
154
|
+
// Close the rest of the position
|
|
155
|
+
yield clearingHouse.closePosition(solMarketInfo.marketIndex);
|
|
156
|
+
});
|
|
157
|
+
main();
|
|
@@ -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/tx/types.js
ADDED
package/src/tx/utils.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapInTx = void 0;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const COMPUTE_UNITS_DEFAULT = 200000;
|
|
6
|
+
function wrapInTx(instruction, computeUnits = 600000 // TODO, requires less code change
|
|
7
|
+
) {
|
|
8
|
+
const tx = new web3_js_1.Transaction();
|
|
9
|
+
if (computeUnits != COMPUTE_UNITS_DEFAULT) {
|
|
10
|
+
tx.add(web3_js_1.ComputeBudgetProgram.requestUnits({
|
|
11
|
+
units: computeUnits,
|
|
12
|
+
additionalFee: 0,
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
return tx.add(instruction);
|
|
16
|
+
}
|
|
17
|
+
exports.wrapInTx = wrapInTx;
|
|
@@ -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;
|
package/src/util/tps.js
ADDED
|
@@ -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;
|