@drift-labs/sdk 2.49.0-beta.9 → 2.52.0-beta.1
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/VERSION +1 -1
- package/lib/accounts/{mockUserAccountSubscriber.d.ts → basicUserAccountSubscriber.d.ts} +6 -2
- package/lib/accounts/{mockUserAccountSubscriber.js → basicUserAccountSubscriber.js} +13 -6
- package/lib/accounts/bulkAccountLoader.js +7 -1
- package/lib/accounts/oneShotUserAccountSubscriber.d.ts +17 -0
- package/lib/accounts/oneShotUserAccountSubscriber.js +48 -0
- package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.js +1 -0
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +17 -0
- package/lib/constants/numericConstants.d.ts +3 -0
- package/lib/constants/numericConstants.js +4 -1
- package/lib/constants/perpMarkets.js +40 -0
- package/lib/constants/spotMarkets.js +10 -0
- package/lib/decode/user.d.ts +3 -0
- package/lib/decode/user.js +329 -0
- package/lib/driftClient.d.ts +6 -1
- package/lib/driftClient.js +35 -10
- package/lib/examples/loadDlob.js +10 -5
- package/lib/idl/drift.json +32 -2
- package/lib/index.d.ts +3 -1
- package/lib/index.js +3 -1
- package/lib/math/state.d.ts +5 -0
- package/lib/math/state.js +27 -0
- package/lib/math/superStake.d.ts +43 -0
- package/lib/math/superStake.js +64 -22
- package/lib/orderSubscriber/OrderSubscriber.d.ts +3 -0
- package/lib/orderSubscriber/OrderSubscriber.js +17 -3
- package/lib/orderSubscriber/WebsocketSubscription.d.ts +1 -1
- package/lib/orderSubscriber/WebsocketSubscription.js +8 -6
- package/lib/orderSubscriber/types.d.ts +4 -1
- package/lib/types.d.ts +2 -1
- package/lib/user.d.ts +2 -1
- package/lib/user.js +13 -5
- package/lib/userMap/PollingSubscription.d.ts +15 -0
- package/lib/userMap/PollingSubscription.js +28 -0
- package/lib/userMap/WebsocketSubscription.d.ts +23 -0
- package/lib/userMap/WebsocketSubscription.js +41 -0
- package/lib/userMap/userMap.d.ts +16 -18
- package/lib/userMap/userMap.js +73 -34
- package/lib/userMap/userMapConfig.d.ts +21 -0
- package/lib/userMap/userMapConfig.js +2 -0
- package/lib/userStats.d.ts +1 -0
- package/lib/userStats.js +3 -0
- package/package.json +2 -1
- package/src/accounts/{mockUserAccountSubscriber.ts → basicUserAccountSubscriber.ts} +12 -6
- package/src/accounts/bulkAccountLoader.ts +10 -3
- package/src/accounts/oneShotUserAccountSubscriber.ts +64 -0
- package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +1 -0
- package/src/adminClient.ts +27 -0
- package/src/constants/numericConstants.ts +4 -0
- package/src/constants/perpMarkets.ts +40 -0
- package/src/constants/spotMarkets.ts +10 -0
- package/src/decode/user.ts +358 -0
- package/src/driftClient.ts +62 -15
- package/src/examples/loadDlob.ts +11 -6
- package/src/idl/drift.json +33 -3
- package/src/index.ts +3 -1
- package/src/math/state.ts +26 -0
- package/src/math/superStake.ts +108 -20
- package/src/orderSubscriber/OrderSubscriber.ts +33 -7
- package/src/orderSubscriber/WebsocketSubscription.ts +17 -16
- package/src/orderSubscriber/types.ts +15 -2
- package/src/types.ts +2 -1
- package/src/user.ts +19 -6
- package/src/userMap/PollingSubscription.ts +48 -0
- package/src/userMap/WebsocketSubscription.ts +76 -0
- package/src/userMap/userMap.ts +105 -70
- package/src/userMap/userMapConfig.ts +34 -0
- package/src/userStats.ts +8 -0
- package/tests/amm/test.ts +6 -3
- package/tests/decode/test.ts +266 -0
- package/tests/decode/userAccountBufferStrings.ts +102 -0
- package/tests/dlob/helpers.ts +1 -0
- package/tests/dlob/test.ts +10 -8
- package/tests/user/helpers.ts +0 -1
package/src/userMap/userMap.ts
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
DriftClient,
|
|
4
4
|
UserAccount,
|
|
5
5
|
OrderRecord,
|
|
6
|
-
UserSubscriptionConfig,
|
|
7
6
|
WrappedEvent,
|
|
8
7
|
DepositRecord,
|
|
9
8
|
FundingPaymentRecord,
|
|
@@ -14,11 +13,25 @@ import {
|
|
|
14
13
|
LPRecord,
|
|
15
14
|
StateAccount,
|
|
16
15
|
DLOB,
|
|
16
|
+
OneShotUserAccountSubscriber,
|
|
17
|
+
BN,
|
|
17
18
|
} from '..';
|
|
18
19
|
|
|
19
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
Commitment,
|
|
22
|
+
Connection,
|
|
23
|
+
PublicKey,
|
|
24
|
+
RpcResponseAndContext,
|
|
25
|
+
} from '@solana/web3.js';
|
|
20
26
|
import { Buffer } from 'buffer';
|
|
21
27
|
import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
|
|
28
|
+
import {
|
|
29
|
+
UserAccountFilterCriteria as UserFilterCriteria,
|
|
30
|
+
UserMapConfig,
|
|
31
|
+
} from './userMapConfig';
|
|
32
|
+
import { WebsocketSubscription } from './WebsocketSubscription';
|
|
33
|
+
import { PollingSubscription } from './PollingSubscription';
|
|
34
|
+
import { decodeUser } from '../decode/user';
|
|
22
35
|
|
|
23
36
|
export interface UserMapInterface {
|
|
24
37
|
subscribe(): Promise<void>;
|
|
@@ -32,59 +45,65 @@ export interface UserMapInterface {
|
|
|
32
45
|
values(): IterableIterator<User>;
|
|
33
46
|
}
|
|
34
47
|
|
|
35
|
-
// filter users that meet these criteria when passing into syncCallback
|
|
36
|
-
export type SyncCallbackCriteria = {
|
|
37
|
-
// only sync users that have open orders
|
|
38
|
-
hasOpenOrders: boolean;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
48
|
export class UserMap implements UserMapInterface {
|
|
42
49
|
private userMap = new Map<string, User>();
|
|
43
|
-
|
|
44
|
-
private
|
|
50
|
+
driftClient: DriftClient;
|
|
51
|
+
private connection: Connection;
|
|
52
|
+
private commitment: Commitment;
|
|
45
53
|
private includeIdle: boolean;
|
|
46
|
-
private lastNumberOfSubAccounts;
|
|
54
|
+
private lastNumberOfSubAccounts: BN;
|
|
55
|
+
private subscription: PollingSubscription | WebsocketSubscription;
|
|
47
56
|
private stateAccountUpdateCallback = async (state: StateAccount) => {
|
|
48
|
-
if (state.numberOfSubAccounts
|
|
57
|
+
if (!state.numberOfSubAccounts.eq(this.lastNumberOfSubAccounts)) {
|
|
49
58
|
await this.sync();
|
|
50
59
|
this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
|
|
51
60
|
}
|
|
52
61
|
};
|
|
53
|
-
private
|
|
54
|
-
private syncCallbackCriteria: SyncCallbackCriteria;
|
|
62
|
+
private decode;
|
|
55
63
|
|
|
56
64
|
private syncPromise?: Promise<void>;
|
|
57
65
|
private syncPromiseResolver: () => void;
|
|
58
66
|
|
|
59
67
|
/**
|
|
60
68
|
* Constructs a new UserMap instance.
|
|
61
|
-
*
|
|
62
|
-
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
63
|
-
* @param {UserSubscriptionConfig} accountSubscription - The UserSubscriptionConfig instance.
|
|
64
|
-
* @param {boolean} includeIdle - Whether idle users are subscribed to. Defaults to false to decrease # of user subscriptions.
|
|
65
|
-
* @param {(authorities: PublicKey[]) => Promise<void>} syncCallback - Called after `sync` completes, will pas in unique list of authorities. Useful for using it to sync UserStatsMap.
|
|
66
|
-
* @param {SyncCallbackCriteria} syncCallbackCriteria - The criteria for the sync callback. Defaults to having no filters
|
|
67
69
|
*/
|
|
68
|
-
constructor(
|
|
69
|
-
driftClient
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this.
|
|
76
|
-
|
|
77
|
-
this.includeIdle = includeIdle;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
70
|
+
constructor(config: UserMapConfig) {
|
|
71
|
+
this.driftClient = config.driftClient;
|
|
72
|
+
if (config.connection) {
|
|
73
|
+
this.connection = config.connection;
|
|
74
|
+
} else {
|
|
75
|
+
this.connection = this.driftClient.connection;
|
|
76
|
+
}
|
|
77
|
+
this.commitment =
|
|
78
|
+
config.subscriptionConfig.commitment ?? this.driftClient.opts.commitment;
|
|
79
|
+
this.includeIdle = config.includeIdle ?? false;
|
|
80
|
+
|
|
81
|
+
let decodeFn;
|
|
82
|
+
if (config.fastDecode ?? true) {
|
|
83
|
+
decodeFn = (name, buffer) => decodeUser(buffer);
|
|
84
|
+
} else {
|
|
85
|
+
decodeFn =
|
|
86
|
+
this.driftClient.program.account.user.coder.accounts.decodeUnchecked.bind(
|
|
87
|
+
this.driftClient.program.account.user.coder.accounts
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
this.decode = decodeFn;
|
|
91
|
+
|
|
92
|
+
if (config.subscriptionConfig.type === 'polling') {
|
|
93
|
+
this.subscription = new PollingSubscription({
|
|
94
|
+
userMap: this,
|
|
95
|
+
frequency: config.subscriptionConfig.frequency,
|
|
96
|
+
skipInitialLoad: config.skipInitialLoad,
|
|
97
|
+
});
|
|
98
|
+
} else {
|
|
99
|
+
this.subscription = new WebsocketSubscription({
|
|
100
|
+
userMap: this,
|
|
101
|
+
commitment: this.commitment,
|
|
102
|
+
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
103
|
+
skipInitialLoad: config.skipInitialLoad,
|
|
104
|
+
decodeFn,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
88
107
|
}
|
|
89
108
|
|
|
90
109
|
public async subscribe() {
|
|
@@ -100,17 +119,27 @@ export class UserMap implements UserMapInterface {
|
|
|
100
119
|
this.stateAccountUpdateCallback
|
|
101
120
|
);
|
|
102
121
|
|
|
103
|
-
await this.
|
|
122
|
+
await this.subscription.subscribe();
|
|
104
123
|
}
|
|
105
124
|
|
|
106
125
|
public async addPubkey(
|
|
107
126
|
userAccountPublicKey: PublicKey,
|
|
108
|
-
userAccount?: UserAccount
|
|
127
|
+
userAccount?: UserAccount,
|
|
128
|
+
slot?: number
|
|
109
129
|
) {
|
|
110
130
|
const user = new User({
|
|
111
131
|
driftClient: this.driftClient,
|
|
112
132
|
userAccountPublicKey,
|
|
113
|
-
accountSubscription:
|
|
133
|
+
accountSubscription: {
|
|
134
|
+
type: 'custom',
|
|
135
|
+
userAccountSubscriber: new OneShotUserAccountSubscriber(
|
|
136
|
+
this.driftClient.program,
|
|
137
|
+
userAccountPublicKey,
|
|
138
|
+
userAccount,
|
|
139
|
+
slot,
|
|
140
|
+
this.commitment
|
|
141
|
+
),
|
|
142
|
+
},
|
|
114
143
|
});
|
|
115
144
|
await user.subscribe(userAccount);
|
|
116
145
|
this.userMap.set(userAccountPublicKey.toString(), user);
|
|
@@ -216,14 +245,18 @@ export class UserMap implements UserMapInterface {
|
|
|
216
245
|
return this.userMap.size;
|
|
217
246
|
}
|
|
218
247
|
|
|
219
|
-
|
|
248
|
+
/**
|
|
249
|
+
* Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
|
|
250
|
+
* @param filterCriteria: Users must meet these criteria to be included
|
|
251
|
+
* @returns
|
|
252
|
+
*/
|
|
253
|
+
public getUniqueAuthorities(
|
|
254
|
+
filterCriteria?: UserFilterCriteria
|
|
255
|
+
): PublicKey[] {
|
|
220
256
|
const usersMeetingCriteria = Array.from(this.userMap.values()).filter(
|
|
221
257
|
(user) => {
|
|
222
258
|
let pass = true;
|
|
223
|
-
if (
|
|
224
|
-
useSyncCallbackCriteria &&
|
|
225
|
-
this.syncCallbackCriteria.hasOpenOrders
|
|
226
|
-
) {
|
|
259
|
+
if (filterCriteria && filterCriteria.hasOpenOrders) {
|
|
227
260
|
pass = pass && user.getUserAccount().hasOpenOrder;
|
|
228
261
|
}
|
|
229
262
|
return pass;
|
|
@@ -257,7 +290,7 @@ export class UserMap implements UserMapInterface {
|
|
|
257
290
|
const rpcRequestArgs = [
|
|
258
291
|
this.driftClient.program.programId.toBase58(),
|
|
259
292
|
{
|
|
260
|
-
commitment: this.
|
|
293
|
+
commitment: this.commitment,
|
|
261
294
|
filters,
|
|
262
295
|
encoding: 'base64',
|
|
263
296
|
withContext: true,
|
|
@@ -266,10 +299,7 @@ export class UserMap implements UserMapInterface {
|
|
|
266
299
|
|
|
267
300
|
const rpcJSONResponse: any =
|
|
268
301
|
// @ts-ignore
|
|
269
|
-
await this.
|
|
270
|
-
'getProgramAccounts',
|
|
271
|
-
rpcRequestArgs
|
|
272
|
-
);
|
|
302
|
+
await this.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
273
303
|
|
|
274
304
|
const rpcResponseAndContext: RpcResponseAndContext<
|
|
275
305
|
Array<{
|
|
@@ -296,34 +326,24 @@ export class UserMap implements UserMapInterface {
|
|
|
296
326
|
|
|
297
327
|
for (const [key, buffer] of programAccountBufferMap.entries()) {
|
|
298
328
|
if (!this.has(key)) {
|
|
299
|
-
const userAccount =
|
|
300
|
-
this.driftClient.program.account.user.coder.accounts.decode(
|
|
301
|
-
'User',
|
|
302
|
-
buffer
|
|
303
|
-
);
|
|
329
|
+
const userAccount = this.decode('User', buffer);
|
|
304
330
|
await this.addPubkey(new PublicKey(key), userAccount);
|
|
331
|
+
this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
|
|
305
332
|
}
|
|
333
|
+
// give event loop a chance to breathe
|
|
334
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
306
335
|
}
|
|
307
336
|
|
|
308
337
|
for (const [key, user] of this.userMap.entries()) {
|
|
309
338
|
if (!programAccountBufferMap.has(key)) {
|
|
310
339
|
await user.unsubscribe();
|
|
311
340
|
this.userMap.delete(key);
|
|
312
|
-
} else {
|
|
313
|
-
const userAccount =
|
|
314
|
-
this.driftClient.program.account.user.coder.accounts.decode(
|
|
315
|
-
'User',
|
|
316
|
-
programAccountBufferMap.get(key)
|
|
317
|
-
);
|
|
318
|
-
user.accountSubscriber.updateData(userAccount, slot);
|
|
319
341
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (this.syncCallback) {
|
|
323
|
-
await this.syncCallback(this.getUniqueAuthorities());
|
|
342
|
+
// give event loop a chance to breathe
|
|
343
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
324
344
|
}
|
|
325
345
|
} catch (e) {
|
|
326
|
-
console.error(`Error in UserMap.sync()
|
|
346
|
+
console.error(`Error in UserMap.sync():`);
|
|
327
347
|
console.error(e);
|
|
328
348
|
} finally {
|
|
329
349
|
this.syncPromiseResolver();
|
|
@@ -332,6 +352,8 @@ export class UserMap implements UserMapInterface {
|
|
|
332
352
|
}
|
|
333
353
|
|
|
334
354
|
public async unsubscribe() {
|
|
355
|
+
await this.subscription.unsubscribe();
|
|
356
|
+
|
|
335
357
|
for (const [key, user] of this.userMap.entries()) {
|
|
336
358
|
await user.unsubscribe();
|
|
337
359
|
this.userMap.delete(key);
|
|
@@ -345,4 +367,17 @@ export class UserMap implements UserMapInterface {
|
|
|
345
367
|
this.lastNumberOfSubAccounts = undefined;
|
|
346
368
|
}
|
|
347
369
|
}
|
|
370
|
+
|
|
371
|
+
public async updateUserAccount(
|
|
372
|
+
key: string,
|
|
373
|
+
userAccount: UserAccount,
|
|
374
|
+
slot: number
|
|
375
|
+
) {
|
|
376
|
+
if (!this.userMap.has(key)) {
|
|
377
|
+
this.addPubkey(new PublicKey(key), userAccount, slot);
|
|
378
|
+
} else {
|
|
379
|
+
const user = this.userMap.get(key);
|
|
380
|
+
user.accountSubscriber.updateData(userAccount, slot);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
348
383
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Commitment, Connection } from '@solana/web3.js';
|
|
2
|
+
import { DriftClient } from '../driftClient';
|
|
3
|
+
|
|
4
|
+
// passed into UserMap.getUniqueAuthorities to filter users
|
|
5
|
+
export type UserAccountFilterCriteria = {
|
|
6
|
+
// only return users that have open orders
|
|
7
|
+
hasOpenOrders: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type UserMapConfig = {
|
|
11
|
+
driftClient: DriftClient;
|
|
12
|
+
// connection object to use specifically for the UserMap. If undefined, will use the driftClient's connection
|
|
13
|
+
connection?: Connection;
|
|
14
|
+
subscriptionConfig:
|
|
15
|
+
| {
|
|
16
|
+
type: 'polling';
|
|
17
|
+
frequency: number;
|
|
18
|
+
commitment?: Commitment;
|
|
19
|
+
}
|
|
20
|
+
| {
|
|
21
|
+
type: 'websocket';
|
|
22
|
+
resubTimeoutMs?: number;
|
|
23
|
+
commitment?: Commitment;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// True to skip the initial load of userAccounts via getProgramAccounts
|
|
27
|
+
skipInitialLoad?: boolean;
|
|
28
|
+
|
|
29
|
+
// True to include idle users when loading. Defaults to false to decrease # of accounts subscribed to.
|
|
30
|
+
includeIdle?: boolean;
|
|
31
|
+
|
|
32
|
+
// Whether to skip loading available perp/spot positions and open orders
|
|
33
|
+
fastDecode?: boolean;
|
|
34
|
+
};
|
package/src/userStats.ts
CHANGED
|
@@ -82,4 +82,12 @@ export class UserStats {
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
+
|
|
86
|
+
public static getOldestActionTs(account: UserStatsAccount): number {
|
|
87
|
+
return Math.min(
|
|
88
|
+
account.lastFillerVolume30DTs.toNumber(),
|
|
89
|
+
account.lastMakerVolume30DTs.toNumber(),
|
|
90
|
+
account.lastTakerVolume30DTs.toNumber()
|
|
91
|
+
);
|
|
92
|
+
}
|
|
85
93
|
}
|
package/tests/amm/test.ts
CHANGED
|
@@ -361,7 +361,10 @@ describe('AMM Tests', () => {
|
|
|
361
361
|
|
|
362
362
|
console.log(terms2);
|
|
363
363
|
assert(terms2.effectiveLeverageCapped <= 1.000001);
|
|
364
|
-
assert(
|
|
364
|
+
assert(
|
|
365
|
+
terms2.inventorySpreadScale == 1.013527,
|
|
366
|
+
`got: ${terms2.inventorySpreadScale}`
|
|
367
|
+
);
|
|
365
368
|
assert(terms2.longSpread == 1146);
|
|
366
369
|
assert(terms2.shortSpread == 6686);
|
|
367
370
|
});
|
|
@@ -466,7 +469,7 @@ describe('AMM Tests', () => {
|
|
|
466
469
|
|
|
467
470
|
assert(markTwapLive.eq(new BN('1949826')));
|
|
468
471
|
assert(oracleTwapLive.eq(new BN('1942510')));
|
|
469
|
-
assert(est1.eq(new BN('15692')));
|
|
472
|
+
assert(est1.eq(new BN('15692')), `got: ${est1}`);
|
|
470
473
|
assert(est2.eq(new BN('15692')));
|
|
471
474
|
});
|
|
472
475
|
|
|
@@ -556,7 +559,7 @@ describe('AMM Tests', () => {
|
|
|
556
559
|
assert(markTwapLive.eq(new BN('1222131')));
|
|
557
560
|
assert(oracleTwapLive.eq(new BN('1222586')));
|
|
558
561
|
assert(est1.eq(est2));
|
|
559
|
-
assert(est2.eq(new BN('-1550')));
|
|
562
|
+
assert(est2.eq(new BN('-1550')), `got: ${est2}`);
|
|
560
563
|
});
|
|
561
564
|
|
|
562
565
|
it('orderbook L2 gen (no topOfBookQuoteAmounts, 10 numOrders, low liquidity)', async () => {
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { AnchorProvider, Idl, Program } from '@coral-xyz/anchor';
|
|
2
|
+
import driftIDL from '../../src/idl/drift.json';
|
|
3
|
+
import { Connection, Keypair } from '@solana/web3.js';
|
|
4
|
+
import { Wallet } from '../../src';
|
|
5
|
+
import {
|
|
6
|
+
DRIFT_PROGRAM_ID,
|
|
7
|
+
isSpotPositionAvailable,
|
|
8
|
+
isVariant,
|
|
9
|
+
Order,
|
|
10
|
+
PerpPosition,
|
|
11
|
+
positionIsAvailable,
|
|
12
|
+
SpotPosition,
|
|
13
|
+
} from '../../lib';
|
|
14
|
+
import { decodeUser } from '../../lib/decode/user';
|
|
15
|
+
import { assert } from 'chai';
|
|
16
|
+
import { userAccountBufferStrings } from './userAccountBufferStrings';
|
|
17
|
+
const sizeof = require('object-sizeof');
|
|
18
|
+
|
|
19
|
+
describe('Custom user decode', () => {
|
|
20
|
+
it('test', async () => {
|
|
21
|
+
const connection = new Connection('http://localhost:8899');
|
|
22
|
+
const wallet = new Wallet(new Keypair());
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
const provider = new AnchorProvider(connection, wallet);
|
|
25
|
+
const program = new Program(driftIDL as Idl, DRIFT_PROGRAM_ID, provider);
|
|
26
|
+
|
|
27
|
+
let totalAnchorSize = 0;
|
|
28
|
+
let totalCustomSize = 0;
|
|
29
|
+
let totalAnchorTime = 0;
|
|
30
|
+
let totalCustomTime = 0;
|
|
31
|
+
for (const [
|
|
32
|
+
i,
|
|
33
|
+
userAccountBufferString,
|
|
34
|
+
] of userAccountBufferStrings.entries()) {
|
|
35
|
+
const userAccountBuffer = Buffer.from(userAccountBufferString, 'base64');
|
|
36
|
+
const [anchorSize, customSize, anchorTime, customTime] =
|
|
37
|
+
testUserAccountDecode(program, userAccountBuffer, i);
|
|
38
|
+
totalAnchorSize += anchorSize;
|
|
39
|
+
totalCustomSize += customSize;
|
|
40
|
+
totalAnchorTime += anchorTime;
|
|
41
|
+
totalCustomTime += customTime;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log(`Total anchor size: ${totalAnchorSize}`);
|
|
45
|
+
console.log(`Total custom size: ${totalCustomSize}`);
|
|
46
|
+
console.log(`Total anchor time: ${totalAnchorTime}`);
|
|
47
|
+
console.log(`Total custom size: ${totalCustomTime}`);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
function testUserAccountDecode(program: Program, buffer: Buffer, i: number) {
|
|
52
|
+
console.log(`Testing user account decode ${i}`);
|
|
53
|
+
|
|
54
|
+
const anchorStartTimestamp = Date.now();
|
|
55
|
+
const anchorUserAccount = program.coder.accounts.decode('User', buffer);
|
|
56
|
+
const anchorEndTimestamp = Date.now();
|
|
57
|
+
const anchorTime = anchorEndTimestamp - anchorStartTimestamp;
|
|
58
|
+
|
|
59
|
+
const customStartTimestamp = Date.now();
|
|
60
|
+
const customUserAccount = decodeUser(buffer);
|
|
61
|
+
const customEndTimestamp = Date.now();
|
|
62
|
+
const customTime = customEndTimestamp - customStartTimestamp;
|
|
63
|
+
|
|
64
|
+
const anchorSize = sizeof(anchorUserAccount);
|
|
65
|
+
const customSize = sizeof(customUserAccount);
|
|
66
|
+
|
|
67
|
+
assert(anchorUserAccount.authority.equals(customUserAccount.authority));
|
|
68
|
+
assert(anchorUserAccount.delegate.equals(customUserAccount.delegate));
|
|
69
|
+
assert(arraysAreEqual(anchorUserAccount.name, customUserAccount.name));
|
|
70
|
+
|
|
71
|
+
const anchorSpotPositionGenerator = getSpotPositions(
|
|
72
|
+
anchorUserAccount.spotPositions
|
|
73
|
+
);
|
|
74
|
+
const customSpotPositionGenerator = getSpotPositions(
|
|
75
|
+
customUserAccount.spotPositions
|
|
76
|
+
);
|
|
77
|
+
for (const [anchorSpotPosition, customSpotPosition] of zipGenerator(
|
|
78
|
+
anchorSpotPositionGenerator,
|
|
79
|
+
customSpotPositionGenerator
|
|
80
|
+
)) {
|
|
81
|
+
testSpotPosition(anchorSpotPosition, customSpotPosition);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const anchorPerpPositionGenerator = getPerpPositions(
|
|
85
|
+
anchorUserAccount.perpPositions
|
|
86
|
+
);
|
|
87
|
+
const customPerpPositionGenerator = getPerpPositions(
|
|
88
|
+
customUserAccount.perpPositions
|
|
89
|
+
);
|
|
90
|
+
for (const [anchorPerpPosition, customPerpPosition] of zipGenerator(
|
|
91
|
+
anchorPerpPositionGenerator,
|
|
92
|
+
customPerpPositionGenerator
|
|
93
|
+
)) {
|
|
94
|
+
testPerpPosition(anchorPerpPosition, customPerpPosition);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const anchorOrderGenerator = getOrders(anchorUserAccount.orders);
|
|
98
|
+
const customOrderGenerator = getOrders(customUserAccount.orders);
|
|
99
|
+
for (const [anchorOrder, customOrder] of zipGenerator(
|
|
100
|
+
anchorOrderGenerator,
|
|
101
|
+
customOrderGenerator
|
|
102
|
+
)) {
|
|
103
|
+
testOrder(anchorOrder, customOrder);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
assert(
|
|
107
|
+
anchorUserAccount.lastAddPerpLpSharesTs.eq(
|
|
108
|
+
customUserAccount.lastAddPerpLpSharesTs
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
assert(anchorUserAccount.totalDeposits.eq(customUserAccount.totalDeposits));
|
|
112
|
+
assert(anchorUserAccount.totalWithdraws.eq(customUserAccount.totalWithdraws));
|
|
113
|
+
assert(
|
|
114
|
+
anchorUserAccount.totalSocialLoss.eq(customUserAccount.totalSocialLoss)
|
|
115
|
+
);
|
|
116
|
+
assert(anchorUserAccount.settledPerpPnl.eq(customUserAccount.settledPerpPnl));
|
|
117
|
+
assert(
|
|
118
|
+
anchorUserAccount.cumulativeSpotFees.eq(
|
|
119
|
+
customUserAccount.cumulativeSpotFees
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
assert(
|
|
123
|
+
anchorUserAccount.cumulativePerpFunding.eq(
|
|
124
|
+
customUserAccount.cumulativePerpFunding
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
assert(
|
|
128
|
+
anchorUserAccount.liquidationMarginFreed.eq(
|
|
129
|
+
customUserAccount.liquidationMarginFreed
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
assert(anchorUserAccount.lastActiveSlot.eq(customUserAccount.lastActiveSlot));
|
|
133
|
+
assert(anchorUserAccount.subAccountId === customUserAccount.subAccountId);
|
|
134
|
+
assert(anchorUserAccount.status === customUserAccount.status);
|
|
135
|
+
assert(
|
|
136
|
+
anchorUserAccount.nextLiquidationId === customUserAccount.nextLiquidationId
|
|
137
|
+
);
|
|
138
|
+
assert(anchorUserAccount.nextOrderId === customUserAccount.nextOrderId);
|
|
139
|
+
assert(anchorUserAccount.maxMarginRatio === customUserAccount.maxMarginRatio);
|
|
140
|
+
assert(
|
|
141
|
+
anchorUserAccount.isMarginTradingEnabled ===
|
|
142
|
+
customUserAccount.isMarginTradingEnabled
|
|
143
|
+
);
|
|
144
|
+
assert(anchorUserAccount.idle === customUserAccount.idle);
|
|
145
|
+
assert(anchorUserAccount.openOrders === customUserAccount.openOrders);
|
|
146
|
+
assert(anchorUserAccount.hasOpenOrder === customUserAccount.hasOpenOrder);
|
|
147
|
+
assert(anchorUserAccount.openAuctions === customUserAccount.openAuctions);
|
|
148
|
+
assert(anchorUserAccount.hasOpenAuction === customUserAccount.hasOpenAuction);
|
|
149
|
+
|
|
150
|
+
return [anchorSize, customSize, anchorTime, customTime];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function* getSpotPositions(spotPositions: SpotPosition[]) {
|
|
154
|
+
for (const spotPosition of spotPositions) {
|
|
155
|
+
if (!isSpotPositionAvailable(spotPosition)) {
|
|
156
|
+
yield spotPosition;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function testSpotPosition(anchor: SpotPosition, custom: SpotPosition) {
|
|
162
|
+
assert(anchor.marketIndex === custom.marketIndex);
|
|
163
|
+
assert(enumsAreEqual(anchor.balanceType, custom.balanceType));
|
|
164
|
+
assert(anchor.openOrders === custom.openOrders);
|
|
165
|
+
assert(anchor.scaledBalance.eq(custom.scaledBalance));
|
|
166
|
+
assert(anchor.openBids.eq(custom.openBids));
|
|
167
|
+
assert(anchor.openAsks.eq(custom.openAsks));
|
|
168
|
+
assert(anchor.cumulativeDeposits.eq(custom.cumulativeDeposits));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function* getPerpPositions(perpPositions: PerpPosition[]) {
|
|
172
|
+
for (const perpPosition of perpPositions) {
|
|
173
|
+
if (!positionIsAvailable(perpPosition)) {
|
|
174
|
+
yield perpPosition;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function testPerpPosition(anchor: PerpPosition, custom: PerpPosition) {
|
|
180
|
+
assert(anchor.baseAssetAmount.eq(custom.baseAssetAmount));
|
|
181
|
+
assert(anchor.lastCumulativeFundingRate.eq(custom.lastCumulativeFundingRate));
|
|
182
|
+
assert(anchor.marketIndex === custom.marketIndex);
|
|
183
|
+
assert(anchor.quoteAssetAmount.eq(custom.quoteAssetAmount));
|
|
184
|
+
assert(anchor.quoteEntryAmount.eq(custom.quoteEntryAmount));
|
|
185
|
+
assert(anchor.quoteBreakEvenAmount.eq(custom.quoteBreakEvenAmount));
|
|
186
|
+
assert(anchor.openBids.eq(custom.openBids));
|
|
187
|
+
assert(anchor.openAsks.eq(custom.openAsks));
|
|
188
|
+
assert(anchor.settledPnl.eq(custom.settledPnl));
|
|
189
|
+
assert(anchor.lpShares.eq(custom.lpShares));
|
|
190
|
+
assert(anchor.lastBaseAssetAmountPerLp.eq(custom.lastBaseAssetAmountPerLp));
|
|
191
|
+
assert(anchor.lastQuoteAssetAmountPerLp.eq(custom.lastQuoteAssetAmountPerLp));
|
|
192
|
+
assert(anchor.openOrders === custom.openOrders);
|
|
193
|
+
assert(anchor.perLpBase === custom.perLpBase);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function* getOrders(orders: Order[]) {
|
|
197
|
+
for (const order of orders) {
|
|
198
|
+
if (isVariant(order.status, 'open')) {
|
|
199
|
+
yield order;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function testOrder(anchor: Order, custom: Order) {
|
|
205
|
+
assert(enumsAreEqual(anchor.status, custom.status));
|
|
206
|
+
assert(enumsAreEqual(anchor.orderType, custom.orderType));
|
|
207
|
+
assert(enumsAreEqual(anchor.marketType, custom.marketType));
|
|
208
|
+
assert(anchor.slot.eq(custom.slot));
|
|
209
|
+
assert(anchor.orderId === custom.orderId);
|
|
210
|
+
assert(anchor.userOrderId === custom.userOrderId);
|
|
211
|
+
assert(anchor.marketIndex === custom.marketIndex);
|
|
212
|
+
assert(anchor.price.eq(custom.price));
|
|
213
|
+
assert(anchor.baseAssetAmount.eq(custom.baseAssetAmount));
|
|
214
|
+
assert(anchor.baseAssetAmountFilled.eq(custom.baseAssetAmountFilled));
|
|
215
|
+
assert(anchor.quoteAssetAmountFilled.eq(custom.quoteAssetAmountFilled));
|
|
216
|
+
assert(enumsAreEqual(anchor.direction, custom.direction));
|
|
217
|
+
assert(anchor.reduceOnly === custom.reduceOnly);
|
|
218
|
+
assert(anchor.triggerPrice.eq(custom.triggerPrice));
|
|
219
|
+
assert(enumsAreEqual(anchor.triggerCondition, custom.triggerCondition));
|
|
220
|
+
assert(
|
|
221
|
+
enumsAreEqual(
|
|
222
|
+
anchor.existingPositionDirection,
|
|
223
|
+
custom.existingPositionDirection
|
|
224
|
+
)
|
|
225
|
+
);
|
|
226
|
+
assert(anchor.postOnly === custom.postOnly);
|
|
227
|
+
assert(anchor.immediateOrCancel === custom.immediateOrCancel);
|
|
228
|
+
assert(anchor.oraclePriceOffset === custom.oraclePriceOffset);
|
|
229
|
+
assert(anchor.auctionDuration === custom.auctionDuration);
|
|
230
|
+
assert(anchor.auctionStartPrice.eq(custom.auctionStartPrice));
|
|
231
|
+
assert(anchor.auctionEndPrice.eq(custom.auctionEndPrice));
|
|
232
|
+
assert(anchor.maxTs.eq(custom.maxTs));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function enumsAreEqual(e1: any, e2: any) {
|
|
236
|
+
return JSON.stringify(e1) === JSON.stringify(e2);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function arraysAreEqual(arr1, arr2) {
|
|
240
|
+
if (arr1.length !== arr2.length) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
245
|
+
if (arr1[i] !== arr2[i]) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function* zipGenerator(gen1, gen2) {
|
|
254
|
+
let iter1 = gen1.next();
|
|
255
|
+
let iter2 = gen2.next();
|
|
256
|
+
|
|
257
|
+
while (!iter1.done && !iter2.done) {
|
|
258
|
+
yield [iter1.value, iter2.value];
|
|
259
|
+
iter1 = gen1.next();
|
|
260
|
+
iter2 = gen2.next();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (iter1.done !== iter2.done) {
|
|
264
|
+
throw new Error('Generators have different lengths');
|
|
265
|
+
}
|
|
266
|
+
}
|