@drift-labs/sdk 0.2.0-master.39 → 0.2.0-master.40
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/accounts/bulkAccountLoader.d.ts +2 -0
- package/lib/accounts/bulkAccountLoader.js +34 -28
- package/lib/accounts/types.d.ts +0 -1
- package/lib/driftClient.js +26 -10
- package/lib/types.d.ts +1 -0
- package/package.json +5 -4
- package/src/accounts/bulkAccountLoader.ts +39 -33
- package/src/driftClient.ts +40 -10
- package/src/types.ts +1 -0
- package/src/assert/assert.js +0 -9
- package/src/events/eventList.js +0 -77
- package/src/examples/makeTradeExample.js +0 -157
- package/src/token/index.js +0 -38
- package/src/tx/types.js +0 -2
- package/src/tx/utils.js +0 -17
- package/src/util/computeUnits.js +0 -27
- package/src/util/getTokenAddress.js +0 -9
- package/src/util/promiseTimeout.js +0 -14
- package/src/util/tps.js +0 -27
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Commitment, Connection, PublicKey } from '@solana/web3.js';
|
|
3
3
|
import { BufferAndSlot } from './types';
|
|
4
|
+
import { Mutex } from 'async-mutex';
|
|
4
5
|
declare type AccountToLoad = {
|
|
5
6
|
publicKey: PublicKey;
|
|
6
7
|
callbacks: Map<string, (buffer: Buffer, slot: number) => void>;
|
|
@@ -14,6 +15,7 @@ export declare class BulkAccountLoader {
|
|
|
14
15
|
errorCallbacks: Map<string, (e: any) => void>;
|
|
15
16
|
intervalId?: NodeJS.Timer;
|
|
16
17
|
loadPromise?: Promise<void>;
|
|
18
|
+
loadPromiseLock: Mutex;
|
|
17
19
|
loadPromiseResolver: () => void;
|
|
18
20
|
lastTimeLoadingPromiseCleared: number;
|
|
19
21
|
mostRecentSlot: number;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BulkAccountLoader = void 0;
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
5
|
const promiseTimeout_1 = require("../util/promiseTimeout");
|
|
6
|
+
const async_mutex_1 = require("async-mutex");
|
|
6
7
|
const GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE = 99;
|
|
7
8
|
const oneMinute = 60 * 1000;
|
|
8
9
|
class BulkAccountLoader {
|
|
@@ -10,6 +11,7 @@ class BulkAccountLoader {
|
|
|
10
11
|
this.accountsToLoad = new Map();
|
|
11
12
|
this.bufferAndSlotMap = new Map();
|
|
12
13
|
this.errorCallbacks = new Map();
|
|
14
|
+
this.loadPromiseLock = new async_mutex_1.Mutex();
|
|
13
15
|
this.lastTimeLoadingPromiseCleared = Date.now();
|
|
14
16
|
this.mostRecentSlot = 0;
|
|
15
17
|
this.connection = connection;
|
|
@@ -36,7 +38,9 @@ class BulkAccountLoader {
|
|
|
36
38
|
this.startPolling();
|
|
37
39
|
}
|
|
38
40
|
// if a new account needs to be polled, remove the cached loadPromise in case client calls load immediately after
|
|
39
|
-
this.
|
|
41
|
+
this.loadPromiseLock.runExclusive(() => {
|
|
42
|
+
this.loadPromise = undefined;
|
|
43
|
+
});
|
|
40
44
|
return callbackId;
|
|
41
45
|
}
|
|
42
46
|
removeAccount(publicKey, callbackId) {
|
|
@@ -66,36 +70,38 @@ class BulkAccountLoader {
|
|
|
66
70
|
.map((begin) => array.slice(begin, begin + size));
|
|
67
71
|
}
|
|
68
72
|
async load() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.
|
|
73
|
+
await this.loadPromiseLock.runExclusive(async () => {
|
|
74
|
+
if (this.loadPromise) {
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
if (now - this.lastTimeLoadingPromiseCleared > oneMinute) {
|
|
77
|
+
this.loadPromise = undefined;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
return this.loadPromise;
|
|
81
|
+
}
|
|
73
82
|
}
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
this.loadPromise = new Promise((resolver) => {
|
|
84
|
+
this.loadPromiseResolver = resolver;
|
|
85
|
+
});
|
|
86
|
+
this.lastTimeLoadingPromiseCleared = Date.now();
|
|
87
|
+
try {
|
|
88
|
+
const chunks = this.chunks(Array.from(this.accountsToLoad.values()), GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE);
|
|
89
|
+
await Promise.all(chunks.map((chunk) => {
|
|
90
|
+
return this.loadChunk(chunk);
|
|
91
|
+
}));
|
|
76
92
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const chunks = this.chunks(Array.from(this.accountsToLoad.values()), GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE);
|
|
84
|
-
await Promise.all(chunks.map((chunk) => {
|
|
85
|
-
return this.loadChunk(chunk);
|
|
86
|
-
}));
|
|
87
|
-
}
|
|
88
|
-
catch (e) {
|
|
89
|
-
console.error(`Error in bulkAccountLoader.load()`);
|
|
90
|
-
console.error(e);
|
|
91
|
-
for (const [_, callback] of this.errorCallbacks) {
|
|
92
|
-
callback(e);
|
|
93
|
+
catch (e) {
|
|
94
|
+
console.error(`Error in bulkAccountLoader.load()`);
|
|
95
|
+
console.error(e);
|
|
96
|
+
for (const [_, callback] of this.errorCallbacks) {
|
|
97
|
+
callback(e);
|
|
98
|
+
}
|
|
93
99
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
100
|
+
finally {
|
|
101
|
+
this.loadPromiseResolver();
|
|
102
|
+
this.loadPromise = undefined;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
99
105
|
}
|
|
100
106
|
async loadChunk(accountsToLoad) {
|
|
101
107
|
if (accountsToLoad.length === 0) {
|
package/lib/accounts/types.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
/// <reference types="node" />
|
|
3
2
|
import { SpotMarketAccount, PerpMarketAccount, OracleSource, StateAccount, UserAccount, UserStatsAccount } from '../types';
|
|
4
3
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
5
4
|
import { EventEmitter } from 'events';
|
package/lib/driftClient.js
CHANGED
|
@@ -127,9 +127,9 @@ class DriftClient {
|
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
async subscribe() {
|
|
130
|
-
|
|
130
|
+
let subscribePromises = this.subscribeUsers().concat(this.accountSubscriber.subscribe());
|
|
131
131
|
if (this.userStats !== undefined) {
|
|
132
|
-
subscribePromises.concat(this.userStats.subscribe());
|
|
132
|
+
subscribePromises = subscribePromises.concat(this.userStats.subscribe());
|
|
133
133
|
}
|
|
134
134
|
this.isSubscribed = (await Promise.all(subscribePromises)).reduce((success, prevSuccess) => success && prevSuccess);
|
|
135
135
|
return this.isSubscribed;
|
|
@@ -141,18 +141,18 @@ class DriftClient {
|
|
|
141
141
|
* Forces the accountSubscriber to fetch account updates from rpc
|
|
142
142
|
*/
|
|
143
143
|
async fetchAccounts() {
|
|
144
|
-
|
|
144
|
+
let promises = [...this.users.values()]
|
|
145
145
|
.map((user) => user.fetchAccounts())
|
|
146
146
|
.concat(this.accountSubscriber.fetch());
|
|
147
147
|
if (this.userStats) {
|
|
148
|
-
promises.concat(this.userStats.fetchAccounts());
|
|
148
|
+
promises = promises.concat(this.userStats.fetchAccounts());
|
|
149
149
|
}
|
|
150
150
|
await Promise.all(promises);
|
|
151
151
|
}
|
|
152
152
|
async unsubscribe() {
|
|
153
|
-
|
|
153
|
+
let unsubscribePromises = this.unsubscribeUsers().concat(this.accountSubscriber.unsubscribe());
|
|
154
154
|
if (this.userStats !== undefined) {
|
|
155
|
-
unsubscribePromises.concat(this.userStats.unsubscribe());
|
|
155
|
+
unsubscribePromises = unsubscribePromises.concat(this.userStats.unsubscribe());
|
|
156
156
|
}
|
|
157
157
|
await Promise.all(unsubscribePromises);
|
|
158
158
|
this.isSubscribed = false;
|
|
@@ -221,11 +221,22 @@ class DriftClient {
|
|
|
221
221
|
this.authority = newWallet.publicKey;
|
|
222
222
|
if (this.isSubscribed) {
|
|
223
223
|
await Promise.all(this.unsubscribeUsers());
|
|
224
|
+
if (this.userStats) {
|
|
225
|
+
await this.userStats.unsubscribe();
|
|
226
|
+
this.userStats = new userStats_1.UserStats({
|
|
227
|
+
driftClient: this,
|
|
228
|
+
userStatsAccountPublicKey: (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.authority),
|
|
229
|
+
accountSubscription: this.userAccountSubscriptionConfig,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
224
232
|
}
|
|
225
233
|
this.users.clear();
|
|
226
234
|
this.createUsers(subAccountIds, this.userAccountSubscriptionConfig);
|
|
227
235
|
if (this.isSubscribed) {
|
|
228
236
|
await Promise.all(this.subscribeUsers());
|
|
237
|
+
if (this.userStats) {
|
|
238
|
+
await this.userStats.subscribe();
|
|
239
|
+
}
|
|
229
240
|
}
|
|
230
241
|
this.activeSubAccountId = activeSubAccountId;
|
|
231
242
|
this.userStatsAccountPublicKey = undefined;
|
|
@@ -245,8 +256,9 @@ class DriftClient {
|
|
|
245
256
|
const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(subAccountId, name, referrerInfo);
|
|
246
257
|
const tx = new web3_js_1.Transaction();
|
|
247
258
|
if (subAccountId === 0) {
|
|
248
|
-
|
|
249
|
-
|
|
259
|
+
if (!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))) {
|
|
260
|
+
tx.add(await this.getInitializeUserStatsIx());
|
|
261
|
+
}
|
|
250
262
|
}
|
|
251
263
|
tx.add(initializeUserAccountIx);
|
|
252
264
|
const { txSig } = await this.txSender.send(tx, [], this.opts);
|
|
@@ -706,7 +718,9 @@ class DriftClient {
|
|
|
706
718
|
? await this.getTransferDepositIx(amount, marketIndex, fromSubAccountId, subAccountId)
|
|
707
719
|
: await this.getDepositInstruction(amount, marketIndex, userTokenAccount, subAccountId, false, false);
|
|
708
720
|
if (subAccountId === 0) {
|
|
709
|
-
|
|
721
|
+
if (!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))) {
|
|
722
|
+
tx.add(await this.getInitializeUserStatsIx());
|
|
723
|
+
}
|
|
710
724
|
}
|
|
711
725
|
tx.add(initializeUserAccountIx).add(depositCollateralIx);
|
|
712
726
|
// Close the wrapped sol account at the end of the transaction
|
|
@@ -723,7 +737,9 @@ class DriftClient {
|
|
|
723
737
|
const depositCollateralIx = await this.getDepositInstruction(amount, marketIndex, associateTokenPublicKey, subAccountId, false, false);
|
|
724
738
|
const tx = new web3_js_1.Transaction().add(createAssociatedAccountIx).add(mintToIx);
|
|
725
739
|
if (subAccountId === 0) {
|
|
726
|
-
|
|
740
|
+
if (!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))) {
|
|
741
|
+
tx.add(await this.getInitializeUserStatsIx());
|
|
742
|
+
}
|
|
727
743
|
}
|
|
728
744
|
tx.add(initializeUserAccountIx).add(depositCollateralIx);
|
|
729
745
|
const txSig = await this.program.provider.sendAndConfirm(tx, []);
|
package/lib/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drift-labs/sdk",
|
|
3
|
-
"version": "0.2.0-master.
|
|
3
|
+
"version": "0.2.0-master.40",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"author": "crispheaney",
|
|
@@ -33,12 +33,13 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@project-serum/serum": "^0.13.38",
|
|
37
36
|
"@project-serum/anchor": "0.25.0-beta.1",
|
|
37
|
+
"@project-serum/serum": "^0.13.38",
|
|
38
38
|
"@pythnetwork/client": "2.5.3",
|
|
39
39
|
"@solana/spl-token": "^0.1.6",
|
|
40
40
|
"@solana/web3.js": "1.41.0",
|
|
41
41
|
"@switchboard-xyz/switchboard-v2": "^0.0.67",
|
|
42
|
+
"async-mutex": "^0.4.0",
|
|
42
43
|
"strict-event-emitter-types": "^2.0.0",
|
|
43
44
|
"uuid": "^8.3.2"
|
|
44
45
|
},
|
|
@@ -53,8 +54,8 @@
|
|
|
53
54
|
"eslint-config-prettier": "^8.3.0",
|
|
54
55
|
"eslint-plugin-prettier": "^3.4.0",
|
|
55
56
|
"mocha": "^10.0.0",
|
|
56
|
-
"
|
|
57
|
-
"
|
|
57
|
+
"prettier": "^2.4.1",
|
|
58
|
+
"ts-node": "^10.8.0"
|
|
58
59
|
},
|
|
59
60
|
"description": "SDK for Drift Protocol v1",
|
|
60
61
|
"engines": {
|
|
@@ -2,6 +2,7 @@ import { Commitment, Connection, PublicKey } from '@solana/web3.js';
|
|
|
2
2
|
import { v4 as uuidv4 } from 'uuid';
|
|
3
3
|
import { BufferAndSlot } from './types';
|
|
4
4
|
import { promiseTimeout } from '../util/promiseTimeout';
|
|
5
|
+
import { Mutex } from 'async-mutex';
|
|
5
6
|
|
|
6
7
|
type AccountToLoad = {
|
|
7
8
|
publicKey: PublicKey;
|
|
@@ -22,6 +23,7 @@ export class BulkAccountLoader {
|
|
|
22
23
|
intervalId?: NodeJS.Timer;
|
|
23
24
|
// to handle clients spamming load
|
|
24
25
|
loadPromise?: Promise<void>;
|
|
26
|
+
loadPromiseLock: Mutex = new Mutex();
|
|
25
27
|
loadPromiseResolver: () => void;
|
|
26
28
|
lastTimeLoadingPromiseCleared = Date.now();
|
|
27
29
|
mostRecentSlot = 0;
|
|
@@ -64,7 +66,9 @@ export class BulkAccountLoader {
|
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
// if a new account needs to be polled, remove the cached loadPromise in case client calls load immediately after
|
|
67
|
-
this.
|
|
69
|
+
this.loadPromiseLock.runExclusive(() => {
|
|
70
|
+
this.loadPromise = undefined;
|
|
71
|
+
});
|
|
68
72
|
|
|
69
73
|
return callbackId;
|
|
70
74
|
}
|
|
@@ -101,41 +105,43 @@ export class BulkAccountLoader {
|
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
public async load(): Promise<void> {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
this.
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
await this.loadPromiseLock.runExclusive(async () => {
|
|
109
|
+
if (this.loadPromise) {
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
if (now - this.lastTimeLoadingPromiseCleared > oneMinute) {
|
|
112
|
+
this.loadPromise = undefined;
|
|
113
|
+
} else {
|
|
114
|
+
return this.loadPromise;
|
|
115
|
+
}
|
|
110
116
|
}
|
|
111
|
-
}
|
|
112
117
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
118
|
+
this.loadPromise = new Promise((resolver) => {
|
|
119
|
+
this.loadPromiseResolver = resolver;
|
|
120
|
+
});
|
|
121
|
+
this.lastTimeLoadingPromiseCleared = Date.now();
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const chunks = this.chunks(
|
|
125
|
+
Array.from(this.accountsToLoad.values()),
|
|
126
|
+
GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
await Promise.all(
|
|
130
|
+
chunks.map((chunk) => {
|
|
131
|
+
return this.loadChunk(chunk);
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
} catch (e) {
|
|
135
|
+
console.error(`Error in bulkAccountLoader.load()`);
|
|
136
|
+
console.error(e);
|
|
137
|
+
for (const [_, callback] of this.errorCallbacks) {
|
|
138
|
+
callback(e);
|
|
139
|
+
}
|
|
140
|
+
} finally {
|
|
141
|
+
this.loadPromiseResolver();
|
|
142
|
+
this.loadPromise = undefined;
|
|
134
143
|
}
|
|
135
|
-
}
|
|
136
|
-
this.loadPromiseResolver();
|
|
137
|
-
this.loadPromise = undefined;
|
|
138
|
-
}
|
|
144
|
+
});
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
async loadChunk(accountsToLoad: AccountToLoad[]): Promise<void> {
|
package/src/driftClient.ts
CHANGED
|
@@ -230,11 +230,11 @@ export class DriftClient {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
public async subscribe(): Promise<boolean> {
|
|
233
|
-
|
|
233
|
+
let subscribePromises = this.subscribeUsers().concat(
|
|
234
234
|
this.accountSubscriber.subscribe()
|
|
235
235
|
);
|
|
236
236
|
if (this.userStats !== undefined) {
|
|
237
|
-
subscribePromises.concat(this.userStats.subscribe());
|
|
237
|
+
subscribePromises = subscribePromises.concat(this.userStats.subscribe());
|
|
238
238
|
}
|
|
239
239
|
this.isSubscribed = (await Promise.all(subscribePromises)).reduce(
|
|
240
240
|
(success, prevSuccess) => success && prevSuccess
|
|
@@ -250,21 +250,23 @@ export class DriftClient {
|
|
|
250
250
|
* Forces the accountSubscriber to fetch account updates from rpc
|
|
251
251
|
*/
|
|
252
252
|
public async fetchAccounts(): Promise<void> {
|
|
253
|
-
|
|
253
|
+
let promises = [...this.users.values()]
|
|
254
254
|
.map((user) => user.fetchAccounts())
|
|
255
255
|
.concat(this.accountSubscriber.fetch());
|
|
256
256
|
if (this.userStats) {
|
|
257
|
-
promises.concat(this.userStats.fetchAccounts());
|
|
257
|
+
promises = promises.concat(this.userStats.fetchAccounts());
|
|
258
258
|
}
|
|
259
259
|
await Promise.all(promises);
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
public async unsubscribe(): Promise<void> {
|
|
263
|
-
|
|
263
|
+
let unsubscribePromises = this.unsubscribeUsers().concat(
|
|
264
264
|
this.accountSubscriber.unsubscribe()
|
|
265
265
|
);
|
|
266
266
|
if (this.userStats !== undefined) {
|
|
267
|
-
unsubscribePromises.concat(
|
|
267
|
+
unsubscribePromises = unsubscribePromises.concat(
|
|
268
|
+
this.userStats.unsubscribe()
|
|
269
|
+
);
|
|
268
270
|
}
|
|
269
271
|
await Promise.all(unsubscribePromises);
|
|
270
272
|
this.isSubscribed = false;
|
|
@@ -378,11 +380,28 @@ export class DriftClient {
|
|
|
378
380
|
|
|
379
381
|
if (this.isSubscribed) {
|
|
380
382
|
await Promise.all(this.unsubscribeUsers());
|
|
383
|
+
|
|
384
|
+
if (this.userStats) {
|
|
385
|
+
await this.userStats.unsubscribe();
|
|
386
|
+
|
|
387
|
+
this.userStats = new UserStats({
|
|
388
|
+
driftClient: this,
|
|
389
|
+
userStatsAccountPublicKey: getUserStatsAccountPublicKey(
|
|
390
|
+
this.program.programId,
|
|
391
|
+
this.authority
|
|
392
|
+
),
|
|
393
|
+
accountSubscription: this.userAccountSubscriptionConfig,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
381
396
|
}
|
|
382
397
|
this.users.clear();
|
|
383
398
|
this.createUsers(subAccountIds, this.userAccountSubscriptionConfig);
|
|
384
399
|
if (this.isSubscribed) {
|
|
385
400
|
await Promise.all(this.subscribeUsers());
|
|
401
|
+
|
|
402
|
+
if (this.userStats) {
|
|
403
|
+
await this.userStats.subscribe();
|
|
404
|
+
}
|
|
386
405
|
}
|
|
387
406
|
|
|
388
407
|
this.activeSubAccountId = activeSubAccountId;
|
|
@@ -420,8 +439,11 @@ export class DriftClient {
|
|
|
420
439
|
|
|
421
440
|
const tx = new Transaction();
|
|
422
441
|
if (subAccountId === 0) {
|
|
423
|
-
|
|
424
|
-
|
|
442
|
+
if (
|
|
443
|
+
!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))
|
|
444
|
+
) {
|
|
445
|
+
tx.add(await this.getInitializeUserStatsIx());
|
|
446
|
+
}
|
|
425
447
|
}
|
|
426
448
|
tx.add(initializeUserAccountIx);
|
|
427
449
|
const { txSig } = await this.txSender.send(tx, [], this.opts);
|
|
@@ -1143,7 +1165,11 @@ export class DriftClient {
|
|
|
1143
1165
|
);
|
|
1144
1166
|
|
|
1145
1167
|
if (subAccountId === 0) {
|
|
1146
|
-
|
|
1168
|
+
if (
|
|
1169
|
+
!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))
|
|
1170
|
+
) {
|
|
1171
|
+
tx.add(await this.getInitializeUserStatsIx());
|
|
1172
|
+
}
|
|
1147
1173
|
}
|
|
1148
1174
|
tx.add(initializeUserAccountIx).add(depositCollateralIx);
|
|
1149
1175
|
|
|
@@ -1202,7 +1228,11 @@ export class DriftClient {
|
|
|
1202
1228
|
const tx = new Transaction().add(createAssociatedAccountIx).add(mintToIx);
|
|
1203
1229
|
|
|
1204
1230
|
if (subAccountId === 0) {
|
|
1205
|
-
|
|
1231
|
+
if (
|
|
1232
|
+
!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))
|
|
1233
|
+
) {
|
|
1234
|
+
tx.add(await this.getInitializeUserStatsIx());
|
|
1235
|
+
}
|
|
1206
1236
|
}
|
|
1207
1237
|
tx.add(initializeUserAccountIx).add(depositCollateralIx);
|
|
1208
1238
|
|
package/src/types.ts
CHANGED
package/src/assert/assert.js
DELETED
package/src/events/eventList.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
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;
|
|
@@ -1,157 +0,0 @@
|
|
|
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.CLEARING_HOUSE_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();
|
package/src/token/index.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
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
DELETED
package/src/tx/utils.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
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;
|
package/src/util/computeUnits.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
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;
|
|
@@ -1,9 +0,0 @@
|
|
|
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;
|
|
@@ -1,14 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
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;
|