@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.
- package/README.md +13 -13
- package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
- package/lib/accounts/bulkUserStatsSubscription.js +21 -0
- package/lib/accounts/bulkUserSubscription.js +0 -1
- package/lib/accounts/fetch.d.ts +2 -1
- package/lib/accounts/fetch.js +9 -1
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +16 -16
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +31 -28
- package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
- package/lib/accounts/types.d.ts +22 -11
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +17 -17
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +37 -35
- package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
- package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
- package/lib/addresses/marketAddresses.d.ts +1 -3
- package/lib/addresses/pda.d.ts +10 -4
- package/lib/addresses/pda.js +51 -14
- package/lib/admin.d.ts +34 -22
- package/lib/admin.js +182 -73
- package/lib/clearingHouse.d.ts +120 -42
- package/lib/clearingHouse.js +1505 -254
- package/lib/clearingHouseConfig.d.ts +4 -4
- package/lib/clearingHouseUser.d.ts +50 -38
- package/lib/clearingHouseUser.js +410 -190
- package/lib/clearingHouseUserStats.d.ts +18 -0
- package/lib/clearingHouseUserStats.js +49 -0
- package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
- package/lib/clearingHouseUserStatsConfig.js +2 -0
- package/lib/config.d.ts +7 -9
- package/lib/config.js +21 -21
- package/lib/constants/numericConstants.d.ts +18 -12
- package/lib/constants/numericConstants.js +28 -21
- package/lib/constants/perpMarkets.d.ts +18 -0
- package/lib/constants/{markets.js → perpMarkets.js} +7 -7
- package/lib/constants/spotMarkets.d.ts +19 -0
- package/lib/constants/spotMarkets.js +53 -0
- package/lib/dlob/DLOB.d.ts +73 -0
- package/lib/dlob/DLOB.js +553 -0
- package/lib/dlob/DLOBNode.d.ts +52 -0
- package/lib/dlob/DLOBNode.js +82 -0
- package/lib/dlob/NodeList.d.ts +26 -0
- package/lib/dlob/NodeList.js +138 -0
- package/lib/events/eventList.js +3 -0
- package/lib/events/eventSubscriber.d.ts +4 -2
- package/lib/events/eventSubscriber.js +16 -9
- package/lib/events/fetchLogs.d.ts +10 -1
- package/lib/events/fetchLogs.js +27 -7
- package/lib/events/pollingLogProvider.d.ts +2 -1
- package/lib/events/pollingLogProvider.js +6 -2
- package/lib/events/sort.js +8 -11
- package/lib/events/types.d.ts +7 -2
- package/lib/events/types.js +5 -0
- package/lib/examples/makeTradeExample.js +20 -8
- package/lib/factory/bigNum.d.ts +1 -0
- package/lib/factory/bigNum.js +34 -10
- package/lib/idl/clearing_house.json +4313 -1433
- package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
- package/lib/index.d.ts +21 -6
- package/lib/index.js +25 -6
- package/lib/math/amm.d.ts +8 -5
- package/lib/math/amm.js +68 -46
- package/lib/math/auction.js +4 -1
- package/lib/math/conversion.js +1 -1
- package/lib/math/funding.d.ts +6 -6
- package/lib/math/funding.js +11 -10
- package/lib/math/insurance.d.ts +4 -0
- package/lib/math/insurance.js +27 -0
- package/lib/math/margin.d.ts +11 -0
- package/lib/math/margin.js +77 -0
- package/lib/math/market.d.ts +14 -9
- package/lib/math/market.js +69 -7
- package/lib/math/oracles.d.ts +4 -0
- package/lib/math/oracles.js +36 -8
- package/lib/math/orders.d.ts +6 -2
- package/lib/math/orders.js +78 -3
- package/lib/math/position.d.ts +21 -13
- package/lib/math/position.js +76 -36
- package/lib/math/repeg.js +14 -5
- package/lib/math/spotBalance.d.ts +22 -0
- package/lib/math/spotBalance.js +193 -0
- package/lib/math/spotMarket.d.ts +4 -0
- package/lib/math/spotMarket.js +8 -0
- package/lib/math/spotPosition.d.ts +6 -0
- package/lib/math/spotPosition.js +23 -0
- package/lib/math/trade.d.ts +10 -10
- package/lib/math/trade.js +22 -29
- package/lib/oracles/pythClient.js +1 -1
- package/lib/oracles/quoteAssetOracleClient.js +1 -1
- package/lib/oracles/switchboardClient.js +1 -1
- package/lib/orderParams.d.ts +14 -5
- package/lib/orderParams.js +12 -92
- package/lib/serum/serumSubscriber.d.ts +23 -0
- package/lib/serum/serumSubscriber.js +41 -0
- package/lib/serum/types.d.ts +11 -0
- package/lib/serum/types.js +2 -0
- package/lib/slot/SlotSubscriber.d.ts +7 -0
- package/lib/slot/SlotSubscriber.js +3 -0
- package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
- package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
- package/lib/tx/retryTxSender.d.ts +1 -1
- package/lib/tx/retryTxSender.js +13 -4
- package/lib/tx/types.d.ts +1 -1
- package/lib/tx/utils.js +1 -1
- package/lib/types.d.ts +474 -123
- package/lib/types.js +99 -5
- package/lib/userMap/userMap.d.ts +25 -0
- package/lib/userMap/userMap.js +73 -0
- package/lib/userMap/userStatsMap.d.ts +19 -0
- package/lib/userMap/userStatsMap.js +68 -0
- package/lib/util/computeUnits.js +1 -1
- package/lib/util/getTokenAddress.d.ts +2 -0
- package/lib/util/getTokenAddress.js +9 -0
- package/package.json +9 -5
- package/src/accounts/bulkUserStatsSubscription.ts +33 -0
- package/src/accounts/bulkUserSubscription.ts +0 -1
- package/src/accounts/fetch.ts +27 -2
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +46 -42
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
- package/src/accounts/types.ts +31 -11
- package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +64 -59
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
- package/src/addresses/marketAddresses.ts +1 -2
- package/src/addresses/pda.ts +88 -14
- package/src/admin.ts +333 -128
- package/src/assert/assert.js +9 -0
- package/src/clearingHouse.ts +2464 -458
- package/src/clearingHouseConfig.ts +4 -3
- package/src/clearingHouseUser.ts +747 -291
- package/src/clearingHouseUserStats.ts +75 -0
- package/src/clearingHouseUserStatsConfig.ts +18 -0
- package/src/config.ts +30 -31
- package/src/constants/numericConstants.ts +41 -25
- package/src/constants/{markets.ts → perpMarkets.ts} +10 -10
- package/src/constants/spotMarkets.ts +72 -0
- package/src/dlob/DLOB.ts +868 -0
- package/src/dlob/DLOBNode.ts +162 -0
- package/src/dlob/NodeList.ts +185 -0
- package/src/events/eventList.js +77 -0
- package/src/events/eventList.ts +3 -0
- package/src/events/eventSubscriber.ts +20 -12
- package/src/events/fetchLogs.ts +35 -8
- package/src/events/pollingLogProvider.ts +10 -2
- package/src/events/sort.ts +11 -15
- package/src/events/types.ts +16 -1
- package/src/examples/makeTradeExample.js +157 -0
- package/src/examples/makeTradeExample.ts +32 -14
- package/src/factory/bigNum.ts +42 -13
- package/src/idl/clearing_house.json +4313 -1433
- package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
- package/src/index.ts +21 -6
- package/src/math/amm.ts +136 -66
- package/src/math/auction.ts +5 -1
- package/src/math/conversion.ts +2 -2
- package/src/math/funding.ts +20 -18
- package/src/math/insurance.ts +35 -0
- package/src/math/margin.ts +127 -0
- package/src/math/market.ts +138 -12
- package/src/math/oracles.ts +63 -9
- package/src/math/orders.ts +138 -4
- package/src/math/position.ts +119 -58
- package/src/math/repeg.ts +16 -6
- package/src/math/spotBalance.ts +316 -0
- package/src/math/spotMarket.ts +9 -0
- package/src/math/spotPosition.ts +47 -0
- package/src/math/trade.ts +43 -49
- package/src/oracles/pythClient.ts +2 -2
- package/src/oracles/quoteAssetOracleClient.ts +2 -2
- package/src/oracles/switchboardClient.ts +2 -2
- package/src/orderParams.ts +24 -137
- package/src/serum/serumSubscriber.ts +80 -0
- package/src/serum/types.ts +13 -0
- package/src/slot/SlotSubscriber.ts +11 -1
- package/src/token/index.js +38 -0
- package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
- package/src/tx/retryTxSender.ts +16 -5
- package/src/tx/types.js +2 -0
- package/src/tx/types.ts +2 -1
- package/src/tx/utils.js +17 -0
- package/src/tx/utils.ts +1 -1
- package/src/types.ts +477 -125
- package/src/userMap/userMap.ts +100 -0
- package/src/userMap/userStatsMap.ts +110 -0
- package/src/util/computeUnits.js +21 -11
- package/src/util/computeUnits.ts +1 -1
- package/src/util/getTokenAddress.js +9 -0
- package/src/util/getTokenAddress.ts +18 -0
- package/src/util/promiseTimeout.js +14 -0
- package/src/util/tps.js +27 -0
- package/tests/bn/test.ts +12 -3
- package/tests/dlob/helpers.ts +374 -0
- package/tests/dlob/test.ts +2865 -0
- package/lib/constants/banks.d.ts +0 -16
- package/lib/constants/banks.js +0 -34
- package/lib/constants/markets.d.ts +0 -19
- package/lib/math/bankBalance.d.ts +0 -9
- package/lib/math/bankBalance.js +0 -75
- package/lib/math/state.d.ts +0 -8
- package/lib/math/state.js +0 -15
- package/lib/orders.d.ts +0 -8
- package/lib/orders.js +0 -134
- package/src/constants/banks.ts +0 -43
- package/src/math/bankBalance.ts +0 -112
- package/src/math/state.ts +0 -14
- package/src/math/utils.js +0 -27
- package/src/math/utils.js.map +0 -1
- package/src/orders.ts +0 -244
- 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;
|
package/src/events/eventList.ts
CHANGED
|
@@ -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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
public async subscribe(): Promise<boolean> {
|
|
68
|
+
try {
|
|
69
|
+
if (this.logProvider.isSubscribed()) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
69
72
|
|
|
70
|
-
|
|
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
|
-
|
|
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
|
|
package/src/events/fetchLogs.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
65
|
-
const
|
|
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:
|
|
71
|
-
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(
|
|
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
|
}
|
package/src/events/sort.ts
CHANGED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
SortFn,
|
|
7
7
|
Event,
|
|
8
8
|
} from './types';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
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
|
|
28
|
-
currentEvent: Event<
|
|
29
|
-
newEvent: Event<
|
|
27
|
+
function orderActionRecordSortFn(
|
|
28
|
+
currentEvent: Event<OrderActionRecord>,
|
|
29
|
+
newEvent: Event<OrderActionRecord>
|
|
30
30
|
): 'less than' | 'greater than' {
|
|
31
|
-
const currentEventMarketIndex =
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
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 '
|
|
61
|
-
return
|
|
56
|
+
case 'OrderActionRecord':
|
|
57
|
+
return orderActionRecordSortFn;
|
|
62
58
|
default:
|
|
63
59
|
return defaultBlockchainSortFn;
|
|
64
60
|
}
|