@drift-labs/sdk 2.26.0-beta.0 → 2.26.0-beta.2
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/examples/phoenix.ts +40 -0
- package/lib/addresses/pda.d.ts +0 -1
- package/lib/adminClient.d.ts +0 -1
- package/lib/constants/numericConstants.d.ts +59 -61
- package/lib/constants/spotMarkets.d.ts +0 -1
- package/lib/dlob/DLOB.d.ts +38 -3
- package/lib/dlob/DLOB.js +69 -0
- package/lib/dlob/DLOBApiClient.js +1 -1
- package/lib/dlob/DLOBNode.d.ts +0 -1
- package/lib/dlob/DLOBSubscriber.d.ts +34 -0
- package/lib/dlob/DLOBSubscriber.js +100 -0
- package/lib/dlob/NodeList.d.ts +0 -1
- 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 -11
- package/lib/driftClient.js +158 -55
- package/lib/driftClientConfig.d.ts +3 -0
- package/lib/factory/bigNum.d.ts +7 -8
- package/lib/idl/drift.json +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/math/amm.d.ts +1 -2
- package/lib/math/auction.d.ts +0 -1
- package/lib/math/conversion.d.ts +1 -2
- package/lib/math/funding.d.ts +0 -1
- package/lib/math/insurance.d.ts +0 -1
- package/lib/math/margin.d.ts +0 -1
- package/lib/math/market.d.ts +0 -1
- package/lib/math/oracles.d.ts +0 -1
- package/lib/math/orders.d.ts +0 -1
- package/lib/math/position.d.ts +0 -1
- package/lib/math/repeg.d.ts +0 -1
- package/lib/math/spotBalance.d.ts +0 -1
- package/lib/math/spotMarket.d.ts +0 -1
- package/lib/math/spotPosition.d.ts +0 -1
- package/lib/math/trade.d.ts +0 -1
- package/lib/math/utils.d.ts +0 -1
- package/lib/oracles/pythClient.d.ts +1 -2
- package/lib/oracles/types.d.ts +0 -1
- package/lib/orderParams.d.ts +0 -1
- package/lib/phoenix/phoenixFulfillmentConfigMap.d.ts +10 -0
- package/lib/phoenix/phoenixFulfillmentConfigMap.js +17 -0
- package/lib/phoenix/phoenixSubscriber.d.ts +34 -0
- package/lib/phoenix/phoenixSubscriber.js +79 -0
- package/lib/serum/serumSubscriber.d.ts +5 -2
- package/lib/serum/serumSubscriber.js +22 -0
- package/lib/tokenFaucet.d.ts +0 -1
- package/lib/types.d.ts +1 -1
- package/lib/user.d.ts +0 -1
- package/package.json +1 -1
- package/src/assert/assert.js +9 -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 +3 -0
- package/src/phoenix/phoenixFulfillmentConfigMap.ts +26 -0
- package/src/phoenix/phoenixSubscriber.ts +156 -0
- package/src/serum/serumSubscriber.ts +27 -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
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Connection, PublicKey, SYSVAR_CLOCK_PUBKEY } from '@solana/web3.js';
|
|
2
|
+
import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
|
|
3
|
+
import {
|
|
4
|
+
MarketData,
|
|
5
|
+
Client,
|
|
6
|
+
deserializeMarketData,
|
|
7
|
+
deserializeClockData,
|
|
8
|
+
toNum,
|
|
9
|
+
getMarketUiLadder,
|
|
10
|
+
} from '@ellipsis-labs/phoenix-sdk';
|
|
11
|
+
import { PRICE_PRECISION } from '../constants/numericConstants';
|
|
12
|
+
import { BN } from '@coral-xyz/anchor';
|
|
13
|
+
|
|
14
|
+
export type PhoenixMarketSubscriberConfig = {
|
|
15
|
+
connection: Connection;
|
|
16
|
+
programId: PublicKey;
|
|
17
|
+
marketAddress: PublicKey;
|
|
18
|
+
accountSubscription:
|
|
19
|
+
| {
|
|
20
|
+
// enables use to add web sockets in the future
|
|
21
|
+
type: 'polling';
|
|
22
|
+
accountLoader: BulkAccountLoader;
|
|
23
|
+
}
|
|
24
|
+
| {
|
|
25
|
+
type: 'websocket';
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export class PhoenixSubscriber {
|
|
30
|
+
connection: Connection;
|
|
31
|
+
client: Client;
|
|
32
|
+
programId: PublicKey;
|
|
33
|
+
marketAddress: PublicKey;
|
|
34
|
+
subscriptionType: 'polling' | 'websocket';
|
|
35
|
+
accountLoader: BulkAccountLoader | undefined;
|
|
36
|
+
market: MarketData;
|
|
37
|
+
marketCallbackId: string | number;
|
|
38
|
+
clockCallbackId: string | number;
|
|
39
|
+
|
|
40
|
+
subscribed: boolean;
|
|
41
|
+
lastSlot: number;
|
|
42
|
+
lastUnixTimestamp: number;
|
|
43
|
+
|
|
44
|
+
public constructor(config: PhoenixMarketSubscriberConfig) {
|
|
45
|
+
this.connection = config.connection;
|
|
46
|
+
this.programId = config.programId;
|
|
47
|
+
this.marketAddress = config.marketAddress;
|
|
48
|
+
if (config.accountSubscription.type === 'polling') {
|
|
49
|
+
this.subscriptionType = 'polling';
|
|
50
|
+
this.accountLoader = config.accountSubscription.accountLoader;
|
|
51
|
+
} else {
|
|
52
|
+
this.subscriptionType = 'websocket';
|
|
53
|
+
}
|
|
54
|
+
this.lastSlot = 0;
|
|
55
|
+
this.lastUnixTimestamp = 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public async subscribe(): Promise<void> {
|
|
59
|
+
if (this.subscribed) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.market = deserializeMarketData(
|
|
64
|
+
(await this.connection.getAccountInfo(this.marketAddress, 'confirmed'))
|
|
65
|
+
.data
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const clock = deserializeClockData(
|
|
69
|
+
(await this.connection.getAccountInfo(SYSVAR_CLOCK_PUBKEY, 'confirmed'))
|
|
70
|
+
.data
|
|
71
|
+
);
|
|
72
|
+
this.lastUnixTimestamp = toNum(clock.unixTimestamp);
|
|
73
|
+
|
|
74
|
+
if (this.subscriptionType === 'websocket') {
|
|
75
|
+
this.marketCallbackId = this.connection.onAccountChange(
|
|
76
|
+
this.marketAddress,
|
|
77
|
+
(accountInfo, _ctx) => {
|
|
78
|
+
this.market = deserializeMarketData(accountInfo.data);
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
this.clockCallbackId = this.connection.onAccountChange(
|
|
82
|
+
SYSVAR_CLOCK_PUBKEY,
|
|
83
|
+
(accountInfo, ctx) => {
|
|
84
|
+
this.lastSlot = ctx.slot;
|
|
85
|
+
const clock = deserializeClockData(accountInfo.data);
|
|
86
|
+
this.lastUnixTimestamp = toNum(clock.unixTimestamp);
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
} else {
|
|
90
|
+
this.marketCallbackId = await this.accountLoader.addAccount(
|
|
91
|
+
this.marketAddress,
|
|
92
|
+
(buffer, slot) => {
|
|
93
|
+
this.lastSlot = slot;
|
|
94
|
+
this.market = deserializeMarketData(buffer);
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
this.clockCallbackId = await this.accountLoader.addAccount(
|
|
98
|
+
SYSVAR_CLOCK_PUBKEY,
|
|
99
|
+
(buffer, slot) => {
|
|
100
|
+
this.lastSlot = slot;
|
|
101
|
+
const clock = deserializeClockData(buffer);
|
|
102
|
+
this.lastUnixTimestamp = toNum(clock.unixTimestamp);
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.subscribed = true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public getBestBid(): BN {
|
|
111
|
+
const ladder = getMarketUiLadder(
|
|
112
|
+
this.market,
|
|
113
|
+
this.lastSlot,
|
|
114
|
+
this.lastUnixTimestamp,
|
|
115
|
+
1
|
|
116
|
+
);
|
|
117
|
+
return new BN(Math.floor(ladder.bids[0][0] * PRICE_PRECISION.toNumber()));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public getBestAsk(): BN {
|
|
121
|
+
const ladder = getMarketUiLadder(
|
|
122
|
+
this.market,
|
|
123
|
+
this.lastSlot,
|
|
124
|
+
this.lastUnixTimestamp,
|
|
125
|
+
1
|
|
126
|
+
);
|
|
127
|
+
return new BN(Math.floor(ladder.asks[0][0] * PRICE_PRECISION.toNumber()));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public async unsubscribe(): Promise<void> {
|
|
131
|
+
if (!this.subscribed) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// remove listeners
|
|
136
|
+
if (this.subscriptionType === 'websocket') {
|
|
137
|
+
await this.connection.removeAccountChangeListener(
|
|
138
|
+
this.marketCallbackId as number
|
|
139
|
+
);
|
|
140
|
+
await this.connection.removeAccountChangeListener(
|
|
141
|
+
this.clockCallbackId as number
|
|
142
|
+
);
|
|
143
|
+
} else {
|
|
144
|
+
this.accountLoader.removeAccount(
|
|
145
|
+
this.marketAddress,
|
|
146
|
+
this.marketCallbackId as string
|
|
147
|
+
);
|
|
148
|
+
this.accountLoader.removeAccount(
|
|
149
|
+
SYSVAR_CLOCK_PUBKEY,
|
|
150
|
+
this.clockCallbackId as string
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.subscribed = false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -4,8 +4,9 @@ import { Market, Orderbook } from '@project-serum/serum';
|
|
|
4
4
|
import { SerumMarketSubscriberConfig } from './types';
|
|
5
5
|
import { BN } from '@coral-xyz/anchor';
|
|
6
6
|
import { PRICE_PRECISION } from '../constants/numericConstants';
|
|
7
|
+
import { L2Level, L2OrderBookGenerator } from '../dlob/orderBookLevels';
|
|
7
8
|
|
|
8
|
-
export class SerumSubscriber {
|
|
9
|
+
export class SerumSubscriber implements L2OrderBookGenerator {
|
|
9
10
|
connection: Connection;
|
|
10
11
|
programId: PublicKey;
|
|
11
12
|
marketAddress: PublicKey;
|
|
@@ -112,6 +113,31 @@ export class SerumSubscriber {
|
|
|
112
113
|
return new BN(bestAsk[0] * PRICE_PRECISION.toNumber());
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
public getL2Bids(): Generator<L2Level> {
|
|
117
|
+
return this.getL2Levels('bids');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public getL2Asks(): Generator<L2Level> {
|
|
121
|
+
return this.getL2Levels('asks');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
*getL2Levels(side: 'bids' | 'asks'): Generator<L2Level> {
|
|
125
|
+
// @ts-ignore
|
|
126
|
+
const basePrecision = Math.pow(10, this.market._baseSplTokenDecimals);
|
|
127
|
+
const pricePrecision = PRICE_PRECISION.toNumber();
|
|
128
|
+
for (let { price, size } of this[side].items(side === 'bids')) {
|
|
129
|
+
price = new BN(price * pricePrecision);
|
|
130
|
+
size = new BN(size * basePrecision);
|
|
131
|
+
yield {
|
|
132
|
+
price,
|
|
133
|
+
size,
|
|
134
|
+
sources: {
|
|
135
|
+
serum: size,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
115
141
|
public async unsubscribe(): Promise<void> {
|
|
116
142
|
if (!this.subscribed) {
|
|
117
143
|
return;
|
|
@@ -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/types.ts
CHANGED
|
@@ -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;
|
package/tests/dlob/helpers.ts
CHANGED
|
@@ -254,6 +254,7 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
|
|
|
254
254
|
{
|
|
255
255
|
status: MarketStatus.ACTIVE,
|
|
256
256
|
assetTier: AssetTier.COLLATERAL,
|
|
257
|
+
name: [],
|
|
257
258
|
maxTokenDeposits: new BN(100),
|
|
258
259
|
marketIndex: 0,
|
|
259
260
|
pubkey: PublicKey.default,
|
|
@@ -328,6 +329,7 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
|
|
|
328
329
|
{
|
|
329
330
|
status: MarketStatus.ACTIVE,
|
|
330
331
|
assetTier: AssetTier.CROSS,
|
|
332
|
+
name: [],
|
|
331
333
|
maxTokenDeposits: new BN(100),
|
|
332
334
|
marketIndex: 1,
|
|
333
335
|
pubkey: PublicKey.default,
|
|
@@ -402,6 +404,7 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
|
|
|
402
404
|
{
|
|
403
405
|
status: MarketStatus.ACTIVE,
|
|
404
406
|
assetTier: AssetTier.PROTECTED,
|
|
407
|
+
name: [],
|
|
405
408
|
maxTokenDeposits: new BN(100),
|
|
406
409
|
marketIndex: 2,
|
|
407
410
|
pubkey: PublicKey.default,
|