@drift-labs/sdk 2.26.0-beta.1 → 2.26.0-beta.3
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/config.d.ts +1 -0
- package/lib/config.js +2 -0
- package/lib/constants/spotMarkets.d.ts +1 -0
- package/lib/constants/spotMarkets.js +1 -0
- package/lib/dlob/DLOB.d.ts +38 -2
- package/lib/dlob/DLOB.js +69 -0
- package/lib/dlob/DLOBApiClient.js +1 -1
- package/lib/dlob/DLOBSubscriber.d.ts +34 -0
- package/lib/dlob/DLOBSubscriber.js +100 -0
- package/lib/dlob/orderBookLevels.d.ts +44 -0
- package/lib/dlob/orderBookLevels.js +137 -0
- package/lib/dlob/types.d.ts +2 -0
- package/lib/driftClient.d.ts +28 -10
- package/lib/driftClient.js +158 -55
- package/lib/driftClientConfig.d.ts +3 -0
- package/lib/idl/drift.json +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/serum/serumSubscriber.d.ts +5 -1
- package/lib/serum/serumSubscriber.js +22 -0
- package/lib/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/assert/assert.js +9 -0
- package/src/config.ts +3 -0
- package/src/constants/spotMarkets.ts +4 -0
- package/src/dlob/DLOB.ts +177 -17
- package/src/dlob/DLOBApiClient.ts +3 -1
- package/src/dlob/DLOBSubscriber.ts +141 -0
- package/src/dlob/orderBookLevels.ts +243 -0
- package/src/dlob/types.ts +2 -0
- package/src/driftClient.ts +231 -66
- package/src/driftClientConfig.ts +3 -0
- package/src/idl/drift.json +1 -1
- package/src/index.ts +1 -0
- package/src/serum/serumSubscriber.ts +29 -1
- package/src/token/index.js +38 -0
- package/src/types.ts +1 -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/tests/dlob/helpers.ts +3 -0
package/lib/config.d.ts
CHANGED
package/lib/config.js
CHANGED
|
@@ -10,6 +10,7 @@ exports.configs = {
|
|
|
10
10
|
DRIFT_PROGRAM_ID: 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH',
|
|
11
11
|
USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
|
|
12
12
|
SERUM_V3: 'DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY',
|
|
13
|
+
PHOENIX: 'PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY',
|
|
13
14
|
V2_ALPHA_TICKET_MINT_ADDRESS: 'DeEiGWfCMP9psnLGkxGrBBMEAW5Jv8bBGMN8DCtFRCyB',
|
|
14
15
|
PERP_MARKETS: perpMarkets_1.DevnetPerpMarkets,
|
|
15
16
|
SPOT_MARKETS: spotMarkets_1.DevnetSpotMarkets,
|
|
@@ -21,6 +22,7 @@ exports.configs = {
|
|
|
21
22
|
DRIFT_PROGRAM_ID: 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH',
|
|
22
23
|
USDC_MINT_ADDRESS: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
23
24
|
SERUM_V3: 'srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX',
|
|
25
|
+
PHOENIX: 'PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY',
|
|
24
26
|
V2_ALPHA_TICKET_MINT_ADDRESS: 'Cmvhycb6LQvvzaShGw4iDHRLzeSSryioAsU98DSSkMNa',
|
|
25
27
|
PERP_MARKETS: perpMarkets_1.MainnetPerpMarkets,
|
|
26
28
|
SPOT_MARKETS: spotMarkets_1.MainnetSpotMarkets,
|
|
@@ -24,6 +24,7 @@ exports.DevnetSpotMarkets = [
|
|
|
24
24
|
precision: numericConstants_1.LAMPORTS_PRECISION,
|
|
25
25
|
precisionExp: numericConstants_1.LAMPORTS_EXP,
|
|
26
26
|
serumMarket: new web3_js_1.PublicKey('8N37SsnTu8RYxtjrV9SStjkkwVhmU8aCWhLvwduAPEKW'),
|
|
27
|
+
phoenixMarket: new web3_js_1.PublicKey('78ehDnHgbkFxqXZwdFxa8HK7saX58GymeX2wNGdkqYLp'),
|
|
27
28
|
},
|
|
28
29
|
{
|
|
29
30
|
symbol: 'BTC',
|
package/lib/dlob/DLOB.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { NodeList } from './NodeList';
|
|
2
|
-
import {
|
|
2
|
+
import { BN, DLOBNode, DLOBNodeType, DriftClient, MarketType, MarketTypeStr, OraclePriceData, Order, OrderActionRecord, OrderRecord, PerpMarketAccount, SlotSubscriber, SpotMarketAccount, StateAccount, TriggerOrderNode, UserMap } from '..';
|
|
3
3
|
import { PublicKey } from '@solana/web3.js';
|
|
4
|
-
import { DLOBNode, DLOBNodeType, TriggerOrderNode } from '..';
|
|
5
4
|
import { DLOBOrders } from './DLOBOrders';
|
|
5
|
+
import { L2OrderBook, L2OrderBookGenerator, L3OrderBook } from './orderBookLevels';
|
|
6
6
|
export type MarketNodeLists = {
|
|
7
7
|
restingLimit: {
|
|
8
8
|
ask: NodeList<'restingLimit'>;
|
|
@@ -99,5 +99,41 @@ export declare class DLOB {
|
|
|
99
99
|
printTopOfOrderLists(sdkConfig: any, driftClient: DriftClient, slotSubscriber: SlotSubscriber, marketIndex: number, marketType: MarketType): void;
|
|
100
100
|
getDLOBOrders(): DLOBOrders;
|
|
101
101
|
getNodeLists(): Generator<NodeList<DLOBNodeType>>;
|
|
102
|
+
/**
|
|
103
|
+
* Get an L2 view of the order book for a given market.
|
|
104
|
+
*
|
|
105
|
+
* @param marketIndex
|
|
106
|
+
* @param marketType
|
|
107
|
+
* @param slot
|
|
108
|
+
* @param oraclePriceData
|
|
109
|
+
* @param depth how many levels of the order book to return
|
|
110
|
+
* @param fallbackAsk best ask for fallback liquidity, only relevant for perps
|
|
111
|
+
* @param fallbackBid best bid for fallback liquidity, only relevant for perps
|
|
112
|
+
* @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber}
|
|
113
|
+
*/
|
|
114
|
+
getL2({ marketIndex, marketType, slot, oraclePriceData, depth, fallbackAsk, fallbackBid, fallbackL2Generators, }: {
|
|
115
|
+
marketIndex: number;
|
|
116
|
+
marketType: MarketType;
|
|
117
|
+
slot: number;
|
|
118
|
+
oraclePriceData: OraclePriceData;
|
|
119
|
+
depth: number;
|
|
120
|
+
fallbackAsk?: BN;
|
|
121
|
+
fallbackBid?: BN;
|
|
122
|
+
fallbackL2Generators?: L2OrderBookGenerator[];
|
|
123
|
+
}): L2OrderBook;
|
|
124
|
+
/**
|
|
125
|
+
* Get an L3 view of the order book for a given market. Does not include fallback liquidity sources
|
|
126
|
+
*
|
|
127
|
+
* @param marketIndex
|
|
128
|
+
* @param marketType
|
|
129
|
+
* @param slot
|
|
130
|
+
* @param oraclePriceData
|
|
131
|
+
*/
|
|
132
|
+
getL3({ marketIndex, marketType, slot, oraclePriceData, }: {
|
|
133
|
+
marketIndex: number;
|
|
134
|
+
marketType: MarketType;
|
|
135
|
+
slot: number;
|
|
136
|
+
oraclePriceData: OraclePriceData;
|
|
137
|
+
}): L3OrderBook;
|
|
102
138
|
}
|
|
103
139
|
export {};
|
package/lib/dlob/DLOB.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.DLOB = void 0;
|
|
|
4
4
|
const NodeList_1 = require("./NodeList");
|
|
5
5
|
const __1 = require("..");
|
|
6
6
|
const exchangeStatus_1 = require("../math/exchangeStatus");
|
|
7
|
+
const orderBookLevels_1 = require("./orderBookLevels");
|
|
7
8
|
const SUPPORTED_ORDER_TYPES = [
|
|
8
9
|
'market',
|
|
9
10
|
'limit',
|
|
@@ -954,5 +955,73 @@ class DLOB {
|
|
|
954
955
|
yield nodeLists.trigger.below;
|
|
955
956
|
}
|
|
956
957
|
}
|
|
958
|
+
/**
|
|
959
|
+
* Get an L2 view of the order book for a given market.
|
|
960
|
+
*
|
|
961
|
+
* @param marketIndex
|
|
962
|
+
* @param marketType
|
|
963
|
+
* @param slot
|
|
964
|
+
* @param oraclePriceData
|
|
965
|
+
* @param depth how many levels of the order book to return
|
|
966
|
+
* @param fallbackAsk best ask for fallback liquidity, only relevant for perps
|
|
967
|
+
* @param fallbackBid best bid for fallback liquidity, only relevant for perps
|
|
968
|
+
* @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber}
|
|
969
|
+
*/
|
|
970
|
+
getL2({ marketIndex, marketType, slot, oraclePriceData, depth, fallbackAsk, fallbackBid, fallbackL2Generators = [], }) {
|
|
971
|
+
const makerAskL2LevelGenerator = (0, orderBookLevels_1.getL2GeneratorFromDLOBNodes)(this.getMakerLimitAsks(marketIndex, slot, marketType, oraclePriceData, fallbackBid), oraclePriceData, slot);
|
|
972
|
+
const fallbackAskGenerators = fallbackL2Generators.map((fallbackL2Generator) => {
|
|
973
|
+
return fallbackL2Generator.getL2Asks();
|
|
974
|
+
});
|
|
975
|
+
const askL2LevelGenerator = (0, orderBookLevels_1.mergeL2LevelGenerators)([makerAskL2LevelGenerator, ...fallbackAskGenerators], (a, b) => {
|
|
976
|
+
return a.price.lt(b.price);
|
|
977
|
+
});
|
|
978
|
+
const asks = (0, orderBookLevels_1.createL2Levels)(askL2LevelGenerator, depth);
|
|
979
|
+
const makerBidGenerator = (0, orderBookLevels_1.getL2GeneratorFromDLOBNodes)(this.getMakerLimitBids(marketIndex, slot, marketType, oraclePriceData, fallbackAsk), oraclePriceData, slot);
|
|
980
|
+
const fallbackBidGenerators = fallbackL2Generators.map((fallbackOrders) => {
|
|
981
|
+
return fallbackOrders.getL2Bids();
|
|
982
|
+
});
|
|
983
|
+
const bidL2LevelGenerator = (0, orderBookLevels_1.mergeL2LevelGenerators)([makerBidGenerator, ...fallbackBidGenerators], (a, b) => {
|
|
984
|
+
return a.price.gt(b.price);
|
|
985
|
+
});
|
|
986
|
+
const bids = (0, orderBookLevels_1.createL2Levels)(bidL2LevelGenerator, depth);
|
|
987
|
+
return {
|
|
988
|
+
bids,
|
|
989
|
+
asks,
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Get an L3 view of the order book for a given market. Does not include fallback liquidity sources
|
|
994
|
+
*
|
|
995
|
+
* @param marketIndex
|
|
996
|
+
* @param marketType
|
|
997
|
+
* @param slot
|
|
998
|
+
* @param oraclePriceData
|
|
999
|
+
*/
|
|
1000
|
+
getL3({ marketIndex, marketType, slot, oraclePriceData, }) {
|
|
1001
|
+
const bids = [];
|
|
1002
|
+
const asks = [];
|
|
1003
|
+
const restingAsks = this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData);
|
|
1004
|
+
for (const ask of restingAsks) {
|
|
1005
|
+
asks.push({
|
|
1006
|
+
price: ask.getPrice(oraclePriceData, slot),
|
|
1007
|
+
size: ask.order.baseAssetAmount.sub(ask.order.baseAssetAmountFilled),
|
|
1008
|
+
maker: ask.userAccount,
|
|
1009
|
+
orderId: ask.order.orderId,
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
const restingBids = this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData);
|
|
1013
|
+
for (const bid of restingBids) {
|
|
1014
|
+
bids.push({
|
|
1015
|
+
price: bid.getPrice(oraclePriceData, slot),
|
|
1016
|
+
size: bid.order.baseAssetAmount.sub(bid.order.baseAssetAmountFilled),
|
|
1017
|
+
maker: bid.userAccount,
|
|
1018
|
+
orderId: bid.order.orderId,
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
return {
|
|
1022
|
+
bids,
|
|
1023
|
+
asks,
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
957
1026
|
}
|
|
958
1027
|
exports.DLOB = DLOB;
|
|
@@ -16,7 +16,7 @@ class DLOBApiClient {
|
|
|
16
16
|
async getDLOB(slot) {
|
|
17
17
|
const r = await (0, node_fetch_1.default)(this.url);
|
|
18
18
|
if (!r.ok) {
|
|
19
|
-
throw new Error(`Failed to fetch DLOB from ${this.url}`);
|
|
19
|
+
throw new Error(`Failed to fetch DLOB from ${this.url}. Status: ${r.status}, ${r.statusText}`);
|
|
20
20
|
}
|
|
21
21
|
const resp = await r.json();
|
|
22
22
|
const responseSlot = resp['slot'];
|
|
@@ -4,7 +4,11 @@ import { DLOB } from './DLOB';
|
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
5
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
6
6
|
import { DLOBSource, DLOBSubscriberEvents, DLOBSubscriptionConfig, SlotSource } from './types';
|
|
7
|
+
import { DriftClient } from '../driftClient';
|
|
8
|
+
import { MarketType } from '../types';
|
|
9
|
+
import { L2OrderBook, L2OrderBookGenerator, L3OrderBook } from './orderBookLevels';
|
|
7
10
|
export declare class DLOBSubscriber {
|
|
11
|
+
driftClient: DriftClient;
|
|
8
12
|
dlobSource: DLOBSource;
|
|
9
13
|
slotSource: SlotSource;
|
|
10
14
|
updateFrequency: number;
|
|
@@ -15,5 +19,35 @@ export declare class DLOBSubscriber {
|
|
|
15
19
|
subscribe(): Promise<void>;
|
|
16
20
|
updateDLOB(): Promise<void>;
|
|
17
21
|
getDLOB(): DLOB;
|
|
22
|
+
/**
|
|
23
|
+
* Get the L2 order book for a given market.
|
|
24
|
+
*
|
|
25
|
+
* @param marketName e.g. "SOL-PERP" or "SOL". If not provided, marketIndex and marketType must be provided.
|
|
26
|
+
* @param marketIndex
|
|
27
|
+
* @param marketType
|
|
28
|
+
* @param depth Number of orders to include in the order book. Defaults to 10.
|
|
29
|
+
* @param includeVamm Whether to include the VAMM orders in the order book. Defaults to false. If true, creates vAMM generator {@link getVammL2Generator} and adds it to fallbackL2Generators.
|
|
30
|
+
* @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber}
|
|
31
|
+
*/
|
|
32
|
+
getL2({ marketName, marketIndex, marketType, depth, includeVamm, fallbackL2Generators, }: {
|
|
33
|
+
marketName?: string;
|
|
34
|
+
marketIndex?: number;
|
|
35
|
+
marketType?: MarketType;
|
|
36
|
+
depth?: number;
|
|
37
|
+
includeVamm?: boolean;
|
|
38
|
+
fallbackL2Generators?: L2OrderBookGenerator[];
|
|
39
|
+
}): L2OrderBook;
|
|
40
|
+
/**
|
|
41
|
+
* Get the L3 order book for a given market.
|
|
42
|
+
*
|
|
43
|
+
* @param marketName e.g. "SOL-PERP" or "SOL". If not provided, marketIndex and marketType must be provided.
|
|
44
|
+
* @param marketIndex
|
|
45
|
+
* @param marketType
|
|
46
|
+
*/
|
|
47
|
+
getL3({ marketName, marketIndex, marketType, }: {
|
|
48
|
+
marketName?: string;
|
|
49
|
+
marketIndex?: number;
|
|
50
|
+
marketType?: MarketType;
|
|
51
|
+
}): L3OrderBook;
|
|
18
52
|
unsubscribe(): Promise<void>;
|
|
19
53
|
}
|
|
@@ -3,9 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DLOBSubscriber = void 0;
|
|
4
4
|
const DLOB_1 = require("./DLOB");
|
|
5
5
|
const events_1 = require("events");
|
|
6
|
+
const types_1 = require("../types");
|
|
7
|
+
const orderBookLevels_1 = require("./orderBookLevels");
|
|
8
|
+
const market_1 = require("../math/market");
|
|
6
9
|
class DLOBSubscriber {
|
|
7
10
|
constructor(config) {
|
|
8
11
|
this.dlob = new DLOB_1.DLOB();
|
|
12
|
+
this.driftClient = config.driftClient;
|
|
9
13
|
this.dlobSource = config.dlobSource;
|
|
10
14
|
this.slotSource = config.slotSource;
|
|
11
15
|
this.updateFrequency = config.updateFrequency;
|
|
@@ -32,6 +36,102 @@ class DLOBSubscriber {
|
|
|
32
36
|
getDLOB() {
|
|
33
37
|
return this.dlob;
|
|
34
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Get the L2 order book for a given market.
|
|
41
|
+
*
|
|
42
|
+
* @param marketName e.g. "SOL-PERP" or "SOL". If not provided, marketIndex and marketType must be provided.
|
|
43
|
+
* @param marketIndex
|
|
44
|
+
* @param marketType
|
|
45
|
+
* @param depth Number of orders to include in the order book. Defaults to 10.
|
|
46
|
+
* @param includeVamm Whether to include the VAMM orders in the order book. Defaults to false. If true, creates vAMM generator {@link getVammL2Generator} and adds it to fallbackL2Generators.
|
|
47
|
+
* @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber}
|
|
48
|
+
*/
|
|
49
|
+
getL2({ marketName, marketIndex, marketType, depth = 10, includeVamm = false, fallbackL2Generators = [], }) {
|
|
50
|
+
if (marketName) {
|
|
51
|
+
const derivedMarketInfo = this.driftClient.getMarketIndexAndType(marketName);
|
|
52
|
+
if (!derivedMarketInfo) {
|
|
53
|
+
throw new Error(`Market ${marketName} not found`);
|
|
54
|
+
}
|
|
55
|
+
marketIndex = derivedMarketInfo.marketIndex;
|
|
56
|
+
marketType = derivedMarketInfo.marketType;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
if (marketIndex === undefined || marketType === undefined) {
|
|
60
|
+
throw new Error('Either marketName or marketIndex and marketType must be provided');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
let oraclePriceData;
|
|
64
|
+
let fallbackBid;
|
|
65
|
+
let fallbackAsk;
|
|
66
|
+
const isPerp = (0, types_1.isVariant)(marketType, 'perp');
|
|
67
|
+
if (isPerp) {
|
|
68
|
+
const perpMarketAccount = this.driftClient.getPerpMarketAccount(marketIndex);
|
|
69
|
+
oraclePriceData = this.driftClient.getOraclePriceDataAndSlot(perpMarketAccount.amm.oracle);
|
|
70
|
+
fallbackBid = (0, market_1.calculateBidPrice)(perpMarketAccount, oraclePriceData);
|
|
71
|
+
fallbackAsk = (0, market_1.calculateAskPrice)(perpMarketAccount, oraclePriceData);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
oraclePriceData =
|
|
75
|
+
this.driftClient.getOracleDataForSpotMarket(marketIndex);
|
|
76
|
+
}
|
|
77
|
+
if (isPerp && includeVamm) {
|
|
78
|
+
fallbackL2Generators = [
|
|
79
|
+
(0, orderBookLevels_1.getVammL2Generator)({
|
|
80
|
+
marketAccount: this.driftClient.getPerpMarketAccount(marketIndex),
|
|
81
|
+
oraclePriceData,
|
|
82
|
+
numOrders: depth,
|
|
83
|
+
}),
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
return this.dlob.getL2({
|
|
87
|
+
marketIndex,
|
|
88
|
+
marketType,
|
|
89
|
+
depth,
|
|
90
|
+
oraclePriceData,
|
|
91
|
+
slot: this.slotSource.getSlot(),
|
|
92
|
+
fallbackBid,
|
|
93
|
+
fallbackAsk,
|
|
94
|
+
fallbackL2Generators: fallbackL2Generators,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get the L3 order book for a given market.
|
|
99
|
+
*
|
|
100
|
+
* @param marketName e.g. "SOL-PERP" or "SOL". If not provided, marketIndex and marketType must be provided.
|
|
101
|
+
* @param marketIndex
|
|
102
|
+
* @param marketType
|
|
103
|
+
*/
|
|
104
|
+
getL3({ marketName, marketIndex, marketType, }) {
|
|
105
|
+
if (marketName) {
|
|
106
|
+
const derivedMarketInfo = this.driftClient.getMarketIndexAndType(marketName);
|
|
107
|
+
if (!derivedMarketInfo) {
|
|
108
|
+
throw new Error(`Market ${marketName} not found`);
|
|
109
|
+
}
|
|
110
|
+
marketIndex = derivedMarketInfo.marketIndex;
|
|
111
|
+
marketType = derivedMarketInfo.marketType;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
if (marketIndex === undefined || marketType === undefined) {
|
|
115
|
+
throw new Error('Either marketName or marketIndex and marketType must be provided');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
let oraclePriceData;
|
|
119
|
+
const isPerp = (0, types_1.isVariant)(marketType, 'perp');
|
|
120
|
+
if (isPerp) {
|
|
121
|
+
oraclePriceData =
|
|
122
|
+
this.driftClient.getOracleDataForPerpMarket(marketIndex);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
oraclePriceData =
|
|
126
|
+
this.driftClient.getOracleDataForSpotMarket(marketIndex);
|
|
127
|
+
}
|
|
128
|
+
return this.dlob.getL3({
|
|
129
|
+
marketIndex,
|
|
130
|
+
marketType,
|
|
131
|
+
oraclePriceData,
|
|
132
|
+
slot: this.slotSource.getSlot(),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
35
135
|
async unsubscribe() {
|
|
36
136
|
if (this.intervalId) {
|
|
37
137
|
clearInterval(this.intervalId);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { BN, DLOBNode, OraclePriceData, PerpMarketAccount } from '..';
|
|
2
|
+
import { PublicKey } from '@solana/web3.js';
|
|
3
|
+
type liquiditySource = 'serum' | 'vamm' | 'dlob';
|
|
4
|
+
export type L2Level = {
|
|
5
|
+
price: BN;
|
|
6
|
+
size: BN;
|
|
7
|
+
sources: {
|
|
8
|
+
[key in liquiditySource]?: BN;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export type L2OrderBook = {
|
|
12
|
+
asks: L2Level[];
|
|
13
|
+
bids: L2Level[];
|
|
14
|
+
};
|
|
15
|
+
export interface L2OrderBookGenerator {
|
|
16
|
+
getL2Asks(): Generator<L2Level>;
|
|
17
|
+
getL2Bids(): Generator<L2Level>;
|
|
18
|
+
}
|
|
19
|
+
export type L3Level = {
|
|
20
|
+
price: BN;
|
|
21
|
+
size: BN;
|
|
22
|
+
maker: PublicKey;
|
|
23
|
+
orderId: number;
|
|
24
|
+
};
|
|
25
|
+
export type L3OrderBook = {
|
|
26
|
+
asks: L3Level[];
|
|
27
|
+
bids: L3Level[];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Get an {@link Generator<L2Level>} generator from a {@link Generator<DLOBNode>}
|
|
31
|
+
* @param dlobNodes e.g. {@link DLOB#getMakerLimitAsks} or {@link DLOB#getMakerLimitBids}
|
|
32
|
+
* @param oraclePriceData
|
|
33
|
+
* @param slot
|
|
34
|
+
*/
|
|
35
|
+
export declare function getL2GeneratorFromDLOBNodes(dlobNodes: Generator<DLOBNode>, oraclePriceData: OraclePriceData, slot: number): Generator<L2Level>;
|
|
36
|
+
export declare function mergeL2LevelGenerators(l2LevelGenerators: Generator<L2Level>[], compare: (a: L2Level, b: L2Level) => boolean): Generator<L2Level>;
|
|
37
|
+
export declare function createL2Levels(generator: Generator<L2Level>, depth: number): L2Level[];
|
|
38
|
+
export declare function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, }: {
|
|
39
|
+
marketAccount: PerpMarketAccount;
|
|
40
|
+
oraclePriceData: OraclePriceData;
|
|
41
|
+
numOrders: number;
|
|
42
|
+
now?: BN;
|
|
43
|
+
}): L2OrderBookGenerator;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getVammL2Generator = exports.createL2Levels = exports.mergeL2LevelGenerators = exports.getL2GeneratorFromDLOBNodes = void 0;
|
|
4
|
+
const __1 = require("..");
|
|
5
|
+
/**
|
|
6
|
+
* Get an {@link Generator<L2Level>} generator from a {@link Generator<DLOBNode>}
|
|
7
|
+
* @param dlobNodes e.g. {@link DLOB#getMakerLimitAsks} or {@link DLOB#getMakerLimitBids}
|
|
8
|
+
* @param oraclePriceData
|
|
9
|
+
* @param slot
|
|
10
|
+
*/
|
|
11
|
+
function* getL2GeneratorFromDLOBNodes(dlobNodes, oraclePriceData, slot) {
|
|
12
|
+
for (const dlobNode of dlobNodes) {
|
|
13
|
+
const size = dlobNode.order.baseAssetAmount.sub(dlobNode.order.baseAssetAmountFilled);
|
|
14
|
+
yield {
|
|
15
|
+
size,
|
|
16
|
+
price: dlobNode.getPrice(oraclePriceData, slot),
|
|
17
|
+
sources: {
|
|
18
|
+
dlob: size,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.getL2GeneratorFromDLOBNodes = getL2GeneratorFromDLOBNodes;
|
|
24
|
+
function* mergeL2LevelGenerators(l2LevelGenerators, compare) {
|
|
25
|
+
const generators = l2LevelGenerators.map((generator) => {
|
|
26
|
+
return {
|
|
27
|
+
generator,
|
|
28
|
+
next: generator.next(),
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
let next;
|
|
32
|
+
do {
|
|
33
|
+
next = generators.reduce((best, next) => {
|
|
34
|
+
if (next.next.done) {
|
|
35
|
+
return best;
|
|
36
|
+
}
|
|
37
|
+
if (!best) {
|
|
38
|
+
return next;
|
|
39
|
+
}
|
|
40
|
+
if (compare(next.next.value, best.next.value)) {
|
|
41
|
+
return next;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
return best;
|
|
45
|
+
}
|
|
46
|
+
}, undefined);
|
|
47
|
+
if (next) {
|
|
48
|
+
yield next.next.value;
|
|
49
|
+
next.next = next.generator.next();
|
|
50
|
+
}
|
|
51
|
+
} while (next !== undefined);
|
|
52
|
+
}
|
|
53
|
+
exports.mergeL2LevelGenerators = mergeL2LevelGenerators;
|
|
54
|
+
function createL2Levels(generator, depth) {
|
|
55
|
+
const levels = [];
|
|
56
|
+
for (const level of generator) {
|
|
57
|
+
const price = level.price;
|
|
58
|
+
const size = level.size;
|
|
59
|
+
if (levels.length > 0 && levels[levels.length - 1].price.eq(price)) {
|
|
60
|
+
const currentLevel = levels[levels.length - 1];
|
|
61
|
+
currentLevel.size.add(size);
|
|
62
|
+
for (const [source, size] of Object.entries(level.sources)) {
|
|
63
|
+
if (currentLevel.sources[source]) {
|
|
64
|
+
currentLevel.sources[source] = currentLevel.sources[source].add(size);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
currentLevel.sources[source] = size;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (levels.length === depth) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
levels.push(level);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return levels;
|
|
79
|
+
}
|
|
80
|
+
exports.createL2Levels = createL2Levels;
|
|
81
|
+
function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, }) {
|
|
82
|
+
const updatedAmm = (0, __1.calculateUpdatedAMM)(marketAccount.amm, oraclePriceData);
|
|
83
|
+
const [openBids, openAsks] = (0, __1.calculateMarketOpenBidAsk)(updatedAmm.baseAssetReserve, updatedAmm.minBaseAssetReserve, updatedAmm.maxBaseAssetReserve, updatedAmm.orderStepSize);
|
|
84
|
+
now = now !== null && now !== void 0 ? now : new __1.BN(Date.now() / 1000);
|
|
85
|
+
const [bidReserves, askReserves] = (0, __1.calculateSpreadReserves)(updatedAmm, oraclePriceData, now);
|
|
86
|
+
let numBids = 0;
|
|
87
|
+
const baseSize = openBids.div(new __1.BN(numOrders));
|
|
88
|
+
const bidAmm = {
|
|
89
|
+
baseAssetReserve: bidReserves.baseAssetReserve,
|
|
90
|
+
quoteAssetReserve: bidReserves.quoteAssetReserve,
|
|
91
|
+
sqrtK: updatedAmm.sqrtK,
|
|
92
|
+
pegMultiplier: updatedAmm.pegMultiplier,
|
|
93
|
+
};
|
|
94
|
+
const getL2Bids = function* () {
|
|
95
|
+
while (numBids < numOrders) {
|
|
96
|
+
const [afterSwapQuoteReserves, afterSwapBaseReserves] = (0, __1.calculateAmmReservesAfterSwap)(bidAmm, 'base', baseSize, __1.SwapDirection.ADD);
|
|
97
|
+
const quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(bidAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), bidAmm.pegMultiplier, __1.SwapDirection.ADD);
|
|
98
|
+
const price = quoteSwapped.mul(__1.BASE_PRECISION).div(baseSize);
|
|
99
|
+
bidAmm.baseAssetReserve = afterSwapBaseReserves;
|
|
100
|
+
bidAmm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
101
|
+
yield {
|
|
102
|
+
price,
|
|
103
|
+
size: baseSize,
|
|
104
|
+
sources: { vamm: baseSize },
|
|
105
|
+
};
|
|
106
|
+
numBids++;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
let numAsks = 0;
|
|
110
|
+
const askSize = openAsks.abs().div(new __1.BN(numOrders));
|
|
111
|
+
const askAmm = {
|
|
112
|
+
baseAssetReserve: askReserves.baseAssetReserve,
|
|
113
|
+
quoteAssetReserve: askReserves.quoteAssetReserve,
|
|
114
|
+
sqrtK: updatedAmm.sqrtK,
|
|
115
|
+
pegMultiplier: updatedAmm.pegMultiplier,
|
|
116
|
+
};
|
|
117
|
+
const getL2Asks = function* () {
|
|
118
|
+
while (numAsks < numOrders) {
|
|
119
|
+
const [afterSwapQuoteReserves, afterSwapBaseReserves] = (0, __1.calculateAmmReservesAfterSwap)(askAmm, 'base', askSize, __1.SwapDirection.REMOVE);
|
|
120
|
+
const quoteSwapped = (0, __1.calculateQuoteAssetAmountSwapped)(askAmm.quoteAssetReserve.sub(afterSwapQuoteReserves).abs(), askAmm.pegMultiplier, __1.SwapDirection.REMOVE);
|
|
121
|
+
const price = quoteSwapped.mul(__1.BASE_PRECISION).div(askSize);
|
|
122
|
+
askAmm.baseAssetReserve = afterSwapBaseReserves;
|
|
123
|
+
askAmm.quoteAssetReserve = afterSwapQuoteReserves;
|
|
124
|
+
yield {
|
|
125
|
+
price,
|
|
126
|
+
size: askSize,
|
|
127
|
+
sources: { vamm: baseSize },
|
|
128
|
+
};
|
|
129
|
+
numAsks++;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
getL2Bids,
|
|
134
|
+
getL2Asks,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
exports.getVammL2Generator = getVammL2Generator;
|
package/lib/dlob/types.d.ts
CHANGED
package/lib/driftClient.d.ts
CHANGED
|
@@ -31,9 +31,10 @@ export declare class DriftClient {
|
|
|
31
31
|
program: Program;
|
|
32
32
|
provider: AnchorProvider;
|
|
33
33
|
opts?: ConfirmOptions;
|
|
34
|
-
users: Map<
|
|
34
|
+
users: Map<string, User>;
|
|
35
35
|
userStats?: UserStats;
|
|
36
36
|
activeSubAccountId: number;
|
|
37
|
+
activeAuthority: PublicKey;
|
|
37
38
|
userAccountSubscriptionConfig: UserSubscriptionConfig;
|
|
38
39
|
accountSubscriber: DriftClientAccountSubscriber;
|
|
39
40
|
eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
|
|
@@ -44,11 +45,14 @@ export declare class DriftClient {
|
|
|
44
45
|
authority: PublicKey;
|
|
45
46
|
marketLookupTable: PublicKey;
|
|
46
47
|
lookupTableAccount: AddressLookupTableAccount;
|
|
48
|
+
includeDelegates?: boolean;
|
|
49
|
+
authoritySubaccountMap?: Map<string, number[]>;
|
|
50
|
+
skipLoadUsers?: boolean;
|
|
47
51
|
get isSubscribed(): boolean;
|
|
48
52
|
set isSubscribed(val: boolean);
|
|
49
53
|
constructor(config: DriftClientConfig);
|
|
50
|
-
|
|
51
|
-
createUser(subAccountId: number, accountSubscriptionConfig: UserSubscriptionConfig): User;
|
|
54
|
+
getUserMapKey(subAccountId: number, authority: PublicKey): string;
|
|
55
|
+
createUser(subAccountId: number, accountSubscriptionConfig: UserSubscriptionConfig, authority?: PublicKey): User;
|
|
52
56
|
subscribe(): Promise<boolean>;
|
|
53
57
|
subscribeUsers(): Promise<boolean>[];
|
|
54
58
|
/**
|
|
@@ -90,11 +94,15 @@ export declare class DriftClient {
|
|
|
90
94
|
* @param newWallet
|
|
91
95
|
* @param subAccountIds
|
|
92
96
|
* @param activeSubAccountId
|
|
97
|
+
* @param includeDelegates
|
|
93
98
|
*/
|
|
94
|
-
updateWallet(newWallet: IWallet, subAccountIds?: number[], activeSubAccountId?: number): Promise<
|
|
95
|
-
switchActiveUser(subAccountId: number): void;
|
|
96
|
-
addUser(subAccountId: number): Promise<
|
|
97
|
-
|
|
99
|
+
updateWallet(newWallet: IWallet, subAccountIds?: number[], activeSubAccountId?: number, includeDelegates?: boolean, authoritySubaccountMap?: Map<string, number[]>): Promise<boolean>;
|
|
100
|
+
switchActiveUser(subAccountId: number, authority?: PublicKey): void;
|
|
101
|
+
addUser(subAccountId: number, authority?: PublicKey): Promise<boolean>;
|
|
102
|
+
/**
|
|
103
|
+
* Adds and subscribes to users based on params set by the constructor or by updateWallet.
|
|
104
|
+
*/
|
|
105
|
+
addAndSubscribeToUsers(): Promise<boolean>;
|
|
98
106
|
initializeUserAccount(subAccountId?: number, name?: string, referrerInfo?: ReferrerInfo): Promise<[TransactionSignature, PublicKey]>;
|
|
99
107
|
getInitializeUserInstructions(subAccountId?: number, name?: string, referrerInfo?: ReferrerInfo): Promise<[PublicKey, TransactionInstruction]>;
|
|
100
108
|
getInitializeUserStatsIx(): Promise<TransactionInstruction>;
|
|
@@ -110,14 +118,14 @@ export declare class DriftClient {
|
|
|
110
118
|
getReferredUserStatsAccountsByReferrer(referrer: PublicKey): Promise<UserStatsAccount[]>;
|
|
111
119
|
getReferrerNameAccountsForAuthority(authority: PublicKey): Promise<ReferrerNameAccount[]>;
|
|
112
120
|
deleteUser(subAccountId?: number, txParams?: TxParams): Promise<TransactionSignature>;
|
|
113
|
-
getUser(subAccountId?: number): User;
|
|
121
|
+
getUser(subAccountId?: number, authority?: PublicKey): User;
|
|
114
122
|
getUsers(): User[];
|
|
115
123
|
getUserStats(): UserStats;
|
|
116
124
|
fetchReferrerNameAccount(name: string): Promise<ReferrerNameAccount | undefined>;
|
|
117
125
|
userStatsAccountPublicKey: PublicKey;
|
|
118
126
|
getUserStatsAccountPublicKey(): PublicKey;
|
|
119
|
-
getUserAccountPublicKey(): Promise<PublicKey>;
|
|
120
|
-
getUserAccount(subAccountId?: number): UserAccount | undefined;
|
|
127
|
+
getUserAccountPublicKey(subAccountId?: number, authority?: PublicKey): Promise<PublicKey>;
|
|
128
|
+
getUserAccount(subAccountId?: number, authority?: PublicKey): UserAccount | undefined;
|
|
121
129
|
/**
|
|
122
130
|
* Forces a fetch to rpc before returning accounts. Useful for anchor tests.
|
|
123
131
|
* @param subAccountId
|
|
@@ -373,6 +381,16 @@ export declare class DriftClient {
|
|
|
373
381
|
resolvePerpPnlDeficit(spotMarketIndex: number, perpMarketIndex: number, txParams?: TxParams): Promise<TransactionSignature>;
|
|
374
382
|
getResolvePerpPnlDeficitIx(spotMarketIndex: number, perpMarketIndex: number): Promise<TransactionInstruction>;
|
|
375
383
|
getPerpMarketExtendedInfo(marketIndex: number): PerpMarketExtendedInfo;
|
|
384
|
+
/**
|
|
385
|
+
* Returns the market index and type for a given market name
|
|
386
|
+
* E.g. "SOL-PERP" -> { marketIndex: 0, marketType: MarketType.PERP }
|
|
387
|
+
*
|
|
388
|
+
* @param name
|
|
389
|
+
*/
|
|
390
|
+
getMarketIndexAndType(name: string): {
|
|
391
|
+
marketIndex: number;
|
|
392
|
+
marketType: MarketType;
|
|
393
|
+
} | undefined;
|
|
376
394
|
sendTransaction(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions, preSigned?: boolean): Promise<TxSigAndSlot>;
|
|
377
395
|
}
|
|
378
396
|
export {};
|