@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/src/driftClient.ts
CHANGED
|
@@ -85,7 +85,7 @@ import { wrapInTx } from './tx/utils';
|
|
|
85
85
|
import { QUOTE_SPOT_MARKET_INDEX, ZERO } from './constants/numericConstants';
|
|
86
86
|
import { findDirectionToClose, positionIsAvailable } from './math/position';
|
|
87
87
|
import { getTokenAmount } from './math/spotBalance';
|
|
88
|
-
import { DEFAULT_USER_NAME, encodeName } from './userName';
|
|
88
|
+
import { decodeName, DEFAULT_USER_NAME, encodeName } from './userName';
|
|
89
89
|
import { OraclePriceData } from './oracles/types';
|
|
90
90
|
import { DriftClientConfig } from './driftClientConfig';
|
|
91
91
|
import { PollingDriftClientAccountSubscriber } from './accounts/pollingDriftClientAccountSubscriber';
|
|
@@ -118,9 +118,10 @@ export class DriftClient {
|
|
|
118
118
|
public program: Program;
|
|
119
119
|
provider: AnchorProvider;
|
|
120
120
|
opts?: ConfirmOptions;
|
|
121
|
-
users = new Map<
|
|
121
|
+
users = new Map<string, User>();
|
|
122
122
|
userStats?: UserStats;
|
|
123
123
|
activeSubAccountId: number;
|
|
124
|
+
activeAuthority: PublicKey;
|
|
124
125
|
userAccountSubscriptionConfig: UserSubscriptionConfig;
|
|
125
126
|
accountSubscriber: DriftClientAccountSubscriber;
|
|
126
127
|
eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
|
|
@@ -131,6 +132,9 @@ export class DriftClient {
|
|
|
131
132
|
authority: PublicKey;
|
|
132
133
|
marketLookupTable: PublicKey;
|
|
133
134
|
lookupTableAccount: AddressLookupTableAccount;
|
|
135
|
+
includeDelegates?: boolean;
|
|
136
|
+
authoritySubaccountMap?: Map<string, number[]>;
|
|
137
|
+
skipLoadUsers?: boolean;
|
|
134
138
|
|
|
135
139
|
public get isSubscribed() {
|
|
136
140
|
return this._isSubscribed && this.accountSubscriber.isSubscribed;
|
|
@@ -156,8 +160,35 @@ export class DriftClient {
|
|
|
156
160
|
);
|
|
157
161
|
|
|
158
162
|
this.authority = config.authority ?? this.wallet.publicKey;
|
|
159
|
-
|
|
160
|
-
this.
|
|
163
|
+
this.activeSubAccountId = config.activeSubAccountId ?? 0;
|
|
164
|
+
this.skipLoadUsers = config.skipLoadUsers ?? false;
|
|
165
|
+
|
|
166
|
+
if (config.includeDelegates && config.subAccountIds) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
'Can only pass one of includeDelegates or subAccountIds. If you want to specify subaccount ids for multiple authorities, pass authoritySubaccountMap instead'
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (config.authoritySubaccountMap && config.subAccountIds) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
'Can only pass one of authoritySubaccountMap or subAccountIds'
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (config.authoritySubaccountMap && config.includeDelegates) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
'Can only pass one of authoritySubaccountMap or includeDelegates'
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this.authoritySubaccountMap = config.authoritySubaccountMap
|
|
185
|
+
? config.authoritySubaccountMap
|
|
186
|
+
: config.subAccountIds
|
|
187
|
+
? new Map([[this.authority.toString(), config.subAccountIds]])
|
|
188
|
+
: new Map<string, number[]>();
|
|
189
|
+
|
|
190
|
+
this.includeDelegates = config.includeDelegates ?? false;
|
|
191
|
+
this.activeAuthority = this.authority;
|
|
161
192
|
this.userAccountSubscriptionConfig =
|
|
162
193
|
config.accountSubscription?.type === 'polling'
|
|
163
194
|
? {
|
|
@@ -167,7 +198,7 @@ export class DriftClient {
|
|
|
167
198
|
: {
|
|
168
199
|
type: 'websocket',
|
|
169
200
|
};
|
|
170
|
-
|
|
201
|
+
|
|
171
202
|
if (config.userStats) {
|
|
172
203
|
this.userStats = new UserStats({
|
|
173
204
|
driftClient: this,
|
|
@@ -229,23 +260,18 @@ export class DriftClient {
|
|
|
229
260
|
);
|
|
230
261
|
}
|
|
231
262
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
accountSubscriptionConfig: UserSubscriptionConfig
|
|
235
|
-
): void {
|
|
236
|
-
for (const subAccountId of subAccountIds) {
|
|
237
|
-
const user = this.createUser(subAccountId, accountSubscriptionConfig);
|
|
238
|
-
this.users.set(subAccountId, user);
|
|
239
|
-
}
|
|
263
|
+
public getUserMapKey(subAccountId: number, authority: PublicKey): string {
|
|
264
|
+
return `${subAccountId}_${authority.toString()}`;
|
|
240
265
|
}
|
|
241
266
|
|
|
242
267
|
createUser(
|
|
243
268
|
subAccountId: number,
|
|
244
|
-
accountSubscriptionConfig: UserSubscriptionConfig
|
|
269
|
+
accountSubscriptionConfig: UserSubscriptionConfig,
|
|
270
|
+
authority?: PublicKey
|
|
245
271
|
): User {
|
|
246
272
|
const userAccountPublicKey = getUserAccountPublicKeySync(
|
|
247
273
|
this.program.programId,
|
|
248
|
-
this.authority,
|
|
274
|
+
authority ?? this.authority,
|
|
249
275
|
subAccountId
|
|
250
276
|
);
|
|
251
277
|
|
|
@@ -257,15 +283,17 @@ export class DriftClient {
|
|
|
257
283
|
}
|
|
258
284
|
|
|
259
285
|
public async subscribe(): Promise<boolean> {
|
|
260
|
-
let subscribePromises = this.
|
|
286
|
+
let subscribePromises = [this.addAndSubscribeToUsers()].concat(
|
|
261
287
|
this.accountSubscriber.subscribe()
|
|
262
288
|
);
|
|
289
|
+
|
|
263
290
|
if (this.userStats !== undefined) {
|
|
264
291
|
subscribePromises = subscribePromises.concat(this.userStats.subscribe());
|
|
265
292
|
}
|
|
266
293
|
this.isSubscribed = (await Promise.all(subscribePromises)).reduce(
|
|
267
294
|
(success, prevSuccess) => success && prevSuccess
|
|
268
295
|
);
|
|
296
|
+
|
|
269
297
|
return this.isSubscribed;
|
|
270
298
|
}
|
|
271
299
|
|
|
@@ -448,12 +476,15 @@ export class DriftClient {
|
|
|
448
476
|
* @param newWallet
|
|
449
477
|
* @param subAccountIds
|
|
450
478
|
* @param activeSubAccountId
|
|
479
|
+
* @param includeDelegates
|
|
451
480
|
*/
|
|
452
481
|
public async updateWallet(
|
|
453
482
|
newWallet: IWallet,
|
|
454
|
-
subAccountIds
|
|
455
|
-
activeSubAccountId
|
|
456
|
-
|
|
483
|
+
subAccountIds?: number[],
|
|
484
|
+
activeSubAccountId?: number,
|
|
485
|
+
includeDelegates?: boolean,
|
|
486
|
+
authoritySubaccountMap?: Map<string, number[]>
|
|
487
|
+
): Promise<boolean> {
|
|
457
488
|
const newProvider = new AnchorProvider(
|
|
458
489
|
this.connection,
|
|
459
490
|
newWallet,
|
|
@@ -465,13 +496,42 @@ export class DriftClient {
|
|
|
465
496
|
newProvider
|
|
466
497
|
);
|
|
467
498
|
|
|
499
|
+
this.skipLoadUsers = false;
|
|
468
500
|
// Update provider for txSender with new wallet details
|
|
469
501
|
this.txSender.provider = newProvider;
|
|
470
|
-
|
|
471
502
|
this.wallet = newWallet;
|
|
472
503
|
this.provider = newProvider;
|
|
473
504
|
this.program = newProgram;
|
|
474
505
|
this.authority = newWallet.publicKey;
|
|
506
|
+
this.activeSubAccountId = activeSubAccountId;
|
|
507
|
+
this.userStatsAccountPublicKey = undefined;
|
|
508
|
+
this.includeDelegates = includeDelegates ?? false;
|
|
509
|
+
|
|
510
|
+
if (includeDelegates && subAccountIds) {
|
|
511
|
+
throw new Error(
|
|
512
|
+
'Can only pass one of includeDelegates or subAccountIds. If you want to specify subaccount ids for multiple authorities, pass authoritySubaccountMap instead'
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (authoritySubaccountMap && subAccountIds) {
|
|
517
|
+
throw new Error(
|
|
518
|
+
'Can only pass one of authoritySubaccountMap or subAccountIds'
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (authoritySubaccountMap && includeDelegates) {
|
|
523
|
+
throw new Error(
|
|
524
|
+
'Can only pass one of authoritySubaccountMap or includeDelegates'
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
this.authoritySubaccountMap = authoritySubaccountMap
|
|
529
|
+
? authoritySubaccountMap
|
|
530
|
+
: subAccountIds
|
|
531
|
+
? new Map([[this.authority.toString(), subAccountIds]])
|
|
532
|
+
: new Map<string, number[]>();
|
|
533
|
+
|
|
534
|
+
let success = true;
|
|
475
535
|
|
|
476
536
|
if (this.isSubscribed) {
|
|
477
537
|
await Promise.all(this.unsubscribeUsers());
|
|
@@ -481,50 +541,103 @@ export class DriftClient {
|
|
|
481
541
|
|
|
482
542
|
this.userStats = new UserStats({
|
|
483
543
|
driftClient: this,
|
|
484
|
-
userStatsAccountPublicKey: getUserStatsAccountPublicKey(
|
|
485
|
-
this.program.programId,
|
|
486
|
-
this.authority
|
|
487
|
-
),
|
|
544
|
+
userStatsAccountPublicKey: this.getUserStatsAccountPublicKey(),
|
|
488
545
|
accountSubscription: this.userAccountSubscriptionConfig,
|
|
489
546
|
});
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
this.users.clear();
|
|
493
|
-
this.createUsers(subAccountIds, this.userAccountSubscriptionConfig);
|
|
494
|
-
if (this.isSubscribed) {
|
|
495
|
-
await Promise.all(this.subscribeUsers());
|
|
496
547
|
|
|
497
|
-
if (this.userStats) {
|
|
498
548
|
await this.userStats.subscribe();
|
|
499
549
|
}
|
|
550
|
+
|
|
551
|
+
this.users.clear();
|
|
552
|
+
success = await this.addAndSubscribeToUsers();
|
|
500
553
|
}
|
|
501
554
|
|
|
502
|
-
|
|
503
|
-
this.userStatsAccountPublicKey = undefined;
|
|
555
|
+
return success;
|
|
504
556
|
}
|
|
505
557
|
|
|
506
|
-
public switchActiveUser(subAccountId: number) {
|
|
558
|
+
public switchActiveUser(subAccountId: number, authority?: PublicKey) {
|
|
507
559
|
this.activeSubAccountId = subAccountId;
|
|
560
|
+
this.activeAuthority = authority ?? this.authority;
|
|
508
561
|
}
|
|
509
562
|
|
|
510
|
-
public async addUser(
|
|
511
|
-
|
|
512
|
-
|
|
563
|
+
public async addUser(
|
|
564
|
+
subAccountId: number,
|
|
565
|
+
authority?: PublicKey
|
|
566
|
+
): Promise<boolean> {
|
|
567
|
+
authority = authority ?? this.authority;
|
|
568
|
+
const userKey = this.getUserMapKey(subAccountId, authority);
|
|
569
|
+
|
|
570
|
+
if (this.users.has(userKey) && this.users.get(userKey).isSubscribed) {
|
|
571
|
+
return true;
|
|
513
572
|
}
|
|
514
573
|
|
|
515
574
|
const user = this.createUser(
|
|
516
575
|
subAccountId,
|
|
517
|
-
this.userAccountSubscriptionConfig
|
|
576
|
+
this.userAccountSubscriptionConfig,
|
|
577
|
+
authority
|
|
518
578
|
);
|
|
519
|
-
|
|
520
|
-
|
|
579
|
+
|
|
580
|
+
const result = await user.subscribe();
|
|
581
|
+
|
|
582
|
+
if (result) {
|
|
583
|
+
this.users.set(userKey, user);
|
|
584
|
+
return true;
|
|
585
|
+
} else {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
521
588
|
}
|
|
522
589
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
590
|
+
/**
|
|
591
|
+
* Adds and subscribes to users based on params set by the constructor or by updateWallet.
|
|
592
|
+
*/
|
|
593
|
+
public async addAndSubscribeToUsers(): Promise<boolean> {
|
|
594
|
+
// save the rpc calls if driftclient is initialized without a real wallet
|
|
595
|
+
if (this.skipLoadUsers) return true;
|
|
596
|
+
|
|
597
|
+
let result = true;
|
|
598
|
+
|
|
599
|
+
if (this.authoritySubaccountMap && this.authoritySubaccountMap.size > 0) {
|
|
600
|
+
this.authoritySubaccountMap.forEach(async (value, key) => {
|
|
601
|
+
for (const subAccountId of value) {
|
|
602
|
+
result =
|
|
603
|
+
result && (await this.addUser(subAccountId, new PublicKey(key)));
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
if (this.activeSubAccountId == undefined) {
|
|
608
|
+
this.switchActiveUser(
|
|
609
|
+
[...this.authoritySubaccountMap.values()][0][0] ?? 0,
|
|
610
|
+
new PublicKey(
|
|
611
|
+
[...this.authoritySubaccountMap.keys()][0] ??
|
|
612
|
+
this.authority.toString()
|
|
613
|
+
)
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
} else {
|
|
617
|
+
const userAccounts =
|
|
618
|
+
(await this.getUserAccountsForAuthority(this.authority)) ?? [];
|
|
619
|
+
let delegatedAccounts = [];
|
|
620
|
+
|
|
621
|
+
if (this.includeDelegates) {
|
|
622
|
+
delegatedAccounts =
|
|
623
|
+
(await this.getUserAccountsForDelegate(this.authority)) ?? [];
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
for (const account of userAccounts.concat(delegatedAccounts)) {
|
|
627
|
+
result =
|
|
628
|
+
result &&
|
|
629
|
+
(await this.addUser(account.subAccountId, account.authority));
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (this.activeSubAccountId == undefined) {
|
|
633
|
+
this.switchActiveUser(
|
|
634
|
+
userAccounts.concat(delegatedAccounts)[0]?.subAccountId ?? 0,
|
|
635
|
+
userAccounts.concat(delegatedAccounts)[0]?.authority ?? this.authority
|
|
636
|
+
);
|
|
637
|
+
}
|
|
527
638
|
}
|
|
639
|
+
|
|
640
|
+
return result;
|
|
528
641
|
}
|
|
529
642
|
|
|
530
643
|
public async initializeUserAccount(
|
|
@@ -710,7 +823,7 @@ export class DriftClient {
|
|
|
710
823
|
subAccountId
|
|
711
824
|
);
|
|
712
825
|
|
|
713
|
-
await this.addUser(subAccountId);
|
|
826
|
+
await this.addUser(subAccountId, this.wallet.publicKey);
|
|
714
827
|
const remainingAccounts = this.getRemainingAccounts({
|
|
715
828
|
userAccounts: [this.getUserAccount(subAccountId)],
|
|
716
829
|
});
|
|
@@ -782,9 +895,9 @@ export class DriftClient {
|
|
|
782
895
|
},
|
|
783
896
|
]);
|
|
784
897
|
|
|
785
|
-
return programAccounts
|
|
786
|
-
(programAccount) => programAccount.account as UserAccount
|
|
787
|
-
|
|
898
|
+
return programAccounts
|
|
899
|
+
.map((programAccount) => programAccount.account as UserAccount)
|
|
900
|
+
.sort((a, b) => a.subAccountId - b.subAccountId);
|
|
788
901
|
}
|
|
789
902
|
|
|
790
903
|
public async getUserAccountsAndAddressesForAuthority(
|
|
@@ -818,9 +931,9 @@ export class DriftClient {
|
|
|
818
931
|
},
|
|
819
932
|
]);
|
|
820
933
|
|
|
821
|
-
return programAccounts
|
|
822
|
-
(programAccount) => programAccount.account as UserAccount
|
|
823
|
-
|
|
934
|
+
return programAccounts
|
|
935
|
+
.map((programAccount) => programAccount.account as UserAccount)
|
|
936
|
+
.sort((a, b) => a.subAccountId - b.subAccountId);
|
|
824
937
|
}
|
|
825
938
|
|
|
826
939
|
public async getReferredUserStatsAccountsByReferrer(
|
|
@@ -884,22 +997,33 @@ export class DriftClient {
|
|
|
884
997
|
this.opts
|
|
885
998
|
);
|
|
886
999
|
|
|
887
|
-
|
|
888
|
-
this.users.
|
|
1000
|
+
const userMapKey = this.getUserMapKey(subAccountId, this.wallet.publicKey);
|
|
1001
|
+
await this.users.get(userMapKey)?.unsubscribe();
|
|
1002
|
+
this.users.delete(userMapKey);
|
|
889
1003
|
|
|
890
1004
|
return txSig;
|
|
891
1005
|
}
|
|
892
1006
|
|
|
893
|
-
public getUser(subAccountId?: number): User {
|
|
1007
|
+
public getUser(subAccountId?: number, authority?: PublicKey): User {
|
|
894
1008
|
subAccountId = subAccountId ?? this.activeSubAccountId;
|
|
895
|
-
|
|
1009
|
+
authority = authority ?? this.activeAuthority;
|
|
1010
|
+
const userMapKey = this.getUserMapKey(subAccountId, authority);
|
|
1011
|
+
|
|
1012
|
+
if (!this.users.has(userMapKey)) {
|
|
896
1013
|
throw new Error(`Clearing House has no user for user id ${subAccountId}`);
|
|
897
1014
|
}
|
|
898
|
-
return this.users.get(
|
|
1015
|
+
return this.users.get(userMapKey);
|
|
899
1016
|
}
|
|
900
1017
|
|
|
901
1018
|
public getUsers(): User[] {
|
|
902
|
-
|
|
1019
|
+
// delegate users get added to the end
|
|
1020
|
+
return [...this.users.values()]
|
|
1021
|
+
.filter((acct) => acct.getUserAccount().authority.equals(this.authority))
|
|
1022
|
+
.concat(
|
|
1023
|
+
[...this.users.values()].filter(
|
|
1024
|
+
(acct) => !acct.getUserAccount().authority.equals(this.authority)
|
|
1025
|
+
)
|
|
1026
|
+
);
|
|
903
1027
|
}
|
|
904
1028
|
|
|
905
1029
|
public getUserStats(): UserStats {
|
|
@@ -927,17 +1051,23 @@ export class DriftClient {
|
|
|
927
1051
|
|
|
928
1052
|
this.userStatsAccountPublicKey = getUserStatsAccountPublicKey(
|
|
929
1053
|
this.program.programId,
|
|
930
|
-
this.
|
|
1054
|
+
this.activeAuthority
|
|
931
1055
|
);
|
|
932
1056
|
return this.userStatsAccountPublicKey;
|
|
933
1057
|
}
|
|
934
1058
|
|
|
935
|
-
public async getUserAccountPublicKey(
|
|
936
|
-
|
|
1059
|
+
public async getUserAccountPublicKey(
|
|
1060
|
+
subAccountId?: number,
|
|
1061
|
+
authority?: PublicKey
|
|
1062
|
+
): Promise<PublicKey> {
|
|
1063
|
+
return this.getUser(subAccountId, authority).userAccountPublicKey;
|
|
937
1064
|
}
|
|
938
1065
|
|
|
939
|
-
public getUserAccount(
|
|
940
|
-
|
|
1066
|
+
public getUserAccount(
|
|
1067
|
+
subAccountId?: number,
|
|
1068
|
+
authority?: PublicKey
|
|
1069
|
+
): UserAccount | undefined {
|
|
1070
|
+
return this.getUser(subAccountId, authority).getUserAccount();
|
|
941
1071
|
}
|
|
942
1072
|
|
|
943
1073
|
/**
|
|
@@ -1798,9 +1928,14 @@ export class DriftClient {
|
|
|
1798
1928
|
);
|
|
1799
1929
|
|
|
1800
1930
|
let remainingAccounts;
|
|
1801
|
-
|
|
1931
|
+
|
|
1932
|
+
const userMapKey = this.getUserMapKey(
|
|
1933
|
+
fromSubAccountId,
|
|
1934
|
+
this.wallet.publicKey
|
|
1935
|
+
);
|
|
1936
|
+
if (this.users.has(userMapKey)) {
|
|
1802
1937
|
remainingAccounts = this.getRemainingAccounts({
|
|
1803
|
-
userAccounts: [this.users.get(
|
|
1938
|
+
userAccounts: [this.users.get(userMapKey).getUserAccount()],
|
|
1804
1939
|
useMarketLastSlotCache: true,
|
|
1805
1940
|
writableSpotMarketIndexes: [marketIndex],
|
|
1806
1941
|
});
|
|
@@ -3039,7 +3174,7 @@ export class DriftClient {
|
|
|
3039
3174
|
user: UserAccount,
|
|
3040
3175
|
txParams?: TxParams
|
|
3041
3176
|
): Promise<TransactionSignature> {
|
|
3042
|
-
const { txSig } = await this.
|
|
3177
|
+
const { txSig } = await this.sendTransaction(
|
|
3043
3178
|
wrapInTx(
|
|
3044
3179
|
await this.getForceCancelOrdersIx(userAccountPublicKey, user),
|
|
3045
3180
|
txParams?.computeUnits,
|
|
@@ -3078,7 +3213,7 @@ export class DriftClient {
|
|
|
3078
3213
|
user: UserAccount,
|
|
3079
3214
|
txParams?: TxParams
|
|
3080
3215
|
): Promise<TransactionSignature> {
|
|
3081
|
-
const { txSig } = await this.
|
|
3216
|
+
const { txSig } = await this.sendTransaction(
|
|
3082
3217
|
wrapInTx(
|
|
3083
3218
|
await this.getUpdateUserIdleIx(userAccountPublicKey, user),
|
|
3084
3219
|
txParams?.computeUnits,
|
|
@@ -4738,6 +4873,36 @@ export class DriftClient {
|
|
|
4738
4873
|
return extendedInfo;
|
|
4739
4874
|
}
|
|
4740
4875
|
|
|
4876
|
+
/**
|
|
4877
|
+
* Returns the market index and type for a given market name
|
|
4878
|
+
* E.g. "SOL-PERP" -> { marketIndex: 0, marketType: MarketType.PERP }
|
|
4879
|
+
*
|
|
4880
|
+
* @param name
|
|
4881
|
+
*/
|
|
4882
|
+
getMarketIndexAndType(
|
|
4883
|
+
name: string
|
|
4884
|
+
): { marketIndex: number; marketType: MarketType } | undefined {
|
|
4885
|
+
for (const perpMarketAccount of this.getPerpMarketAccounts()) {
|
|
4886
|
+
if (decodeName(perpMarketAccount.name) === name) {
|
|
4887
|
+
return {
|
|
4888
|
+
marketIndex: perpMarketAccount.marketIndex,
|
|
4889
|
+
marketType: MarketType.PERP,
|
|
4890
|
+
};
|
|
4891
|
+
}
|
|
4892
|
+
}
|
|
4893
|
+
|
|
4894
|
+
for (const spotMarketAccount of this.getSpotMarketAccounts()) {
|
|
4895
|
+
if (decodeName(spotMarketAccount.name) === name) {
|
|
4896
|
+
return {
|
|
4897
|
+
marketIndex: spotMarketAccount.marketIndex,
|
|
4898
|
+
marketType: MarketType.SPOT,
|
|
4899
|
+
};
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
|
|
4903
|
+
return undefined;
|
|
4904
|
+
}
|
|
4905
|
+
|
|
4741
4906
|
sendTransaction(
|
|
4742
4907
|
tx: Transaction,
|
|
4743
4908
|
additionalSigners?: Array<Signer>,
|
package/src/driftClientConfig.ts
CHANGED
|
@@ -20,6 +20,9 @@ export type DriftClientConfig = {
|
|
|
20
20
|
env?: DriftEnv;
|
|
21
21
|
userStats?: boolean;
|
|
22
22
|
authority?: PublicKey; // explicitly pass an authority if signer is delegate
|
|
23
|
+
includeDelegates?: boolean; // flag for whether to load delegate accounts as well
|
|
24
|
+
authoritySubaccountMap?: Map<string, number[]>; // if passed this will override subAccountIds and includeDelegates
|
|
25
|
+
skipLoadUsers?: boolean; // if passed to constructor, no user accounts will be loaded. they will load if updateWallet is called afterwards.
|
|
23
26
|
};
|
|
24
27
|
|
|
25
28
|
export type DriftClientSubscriptionConfig =
|
package/src/idl/drift.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -70,6 +70,7 @@ export * from './dlob/DLOBOrders';
|
|
|
70
70
|
export * from './dlob/NodeList';
|
|
71
71
|
export * from './dlob/DLOBSubscriber';
|
|
72
72
|
export * from './dlob/types';
|
|
73
|
+
export * from './dlob/orderBookLevels';
|
|
73
74
|
export * from './userMap/userMap';
|
|
74
75
|
export * from './userMap/userStatsMap';
|
|
75
76
|
export * from './math/bankruptcy';
|
|
@@ -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,33 @@ 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 (const { price: priceNum, size: sizeNum } of this[side].items(
|
|
129
|
+
side === 'bids'
|
|
130
|
+
)) {
|
|
131
|
+
const price = new BN(priceNum * pricePrecision);
|
|
132
|
+
const size = new BN(sizeNum * basePrecision);
|
|
133
|
+
yield {
|
|
134
|
+
price,
|
|
135
|
+
size,
|
|
136
|
+
sources: {
|
|
137
|
+
serum: size,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
115
143
|
public async unsubscribe(): Promise<void> {
|
|
116
144
|
if (!this.subscribed) {
|
|
117
145
|
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;
|