@drift-labs/sdk 2.49.0-beta.9 → 2.52.0-beta.0
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} +2 -2
- package/lib/accounts/{mockUserAccountSubscriber.js → basicUserAccountSubscriber.js} +9 -6
- package/lib/adminClient.d.ts +1 -0
- package/lib/adminClient.js +8 -0
- 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 +5 -1
- package/lib/driftClient.js +28 -7
- package/lib/examples/loadDlob.js +10 -5
- package/lib/idl/drift.json +32 -2
- package/lib/index.d.ts +2 -1
- package/lib/index.js +2 -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/package.json +2 -1
- package/src/accounts/{mockUserAccountSubscriber.ts → basicUserAccountSubscriber.ts} +8 -6
- package/src/adminClient.ts +14 -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 +48 -7
- package/src/examples/loadDlob.ts +11 -6
- package/src/idl/drift.json +33 -3
- package/src/index.ts +2 -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 +103 -70
- package/src/userMap/userMapConfig.ts +34 -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
|
+
BasicUserAccountSubscriber,
|
|
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,25 @@ 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 BasicUserAccountSubscriber(
|
|
136
|
+
userAccountPublicKey,
|
|
137
|
+
userAccount,
|
|
138
|
+
slot
|
|
139
|
+
),
|
|
140
|
+
},
|
|
114
141
|
});
|
|
115
142
|
await user.subscribe(userAccount);
|
|
116
143
|
this.userMap.set(userAccountPublicKey.toString(), user);
|
|
@@ -216,14 +243,18 @@ export class UserMap implements UserMapInterface {
|
|
|
216
243
|
return this.userMap.size;
|
|
217
244
|
}
|
|
218
245
|
|
|
219
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
|
|
248
|
+
* @param filterCriteria: Users must meet these criteria to be included
|
|
249
|
+
* @returns
|
|
250
|
+
*/
|
|
251
|
+
public getUniqueAuthorities(
|
|
252
|
+
filterCriteria?: UserFilterCriteria
|
|
253
|
+
): PublicKey[] {
|
|
220
254
|
const usersMeetingCriteria = Array.from(this.userMap.values()).filter(
|
|
221
255
|
(user) => {
|
|
222
256
|
let pass = true;
|
|
223
|
-
if (
|
|
224
|
-
useSyncCallbackCriteria &&
|
|
225
|
-
this.syncCallbackCriteria.hasOpenOrders
|
|
226
|
-
) {
|
|
257
|
+
if (filterCriteria && filterCriteria.hasOpenOrders) {
|
|
227
258
|
pass = pass && user.getUserAccount().hasOpenOrder;
|
|
228
259
|
}
|
|
229
260
|
return pass;
|
|
@@ -257,7 +288,7 @@ export class UserMap implements UserMapInterface {
|
|
|
257
288
|
const rpcRequestArgs = [
|
|
258
289
|
this.driftClient.program.programId.toBase58(),
|
|
259
290
|
{
|
|
260
|
-
commitment: this.
|
|
291
|
+
commitment: this.commitment,
|
|
261
292
|
filters,
|
|
262
293
|
encoding: 'base64',
|
|
263
294
|
withContext: true,
|
|
@@ -266,10 +297,7 @@ export class UserMap implements UserMapInterface {
|
|
|
266
297
|
|
|
267
298
|
const rpcJSONResponse: any =
|
|
268
299
|
// @ts-ignore
|
|
269
|
-
await this.
|
|
270
|
-
'getProgramAccounts',
|
|
271
|
-
rpcRequestArgs
|
|
272
|
-
);
|
|
300
|
+
await this.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
273
301
|
|
|
274
302
|
const rpcResponseAndContext: RpcResponseAndContext<
|
|
275
303
|
Array<{
|
|
@@ -296,34 +324,24 @@ export class UserMap implements UserMapInterface {
|
|
|
296
324
|
|
|
297
325
|
for (const [key, buffer] of programAccountBufferMap.entries()) {
|
|
298
326
|
if (!this.has(key)) {
|
|
299
|
-
const userAccount =
|
|
300
|
-
this.driftClient.program.account.user.coder.accounts.decode(
|
|
301
|
-
'User',
|
|
302
|
-
buffer
|
|
303
|
-
);
|
|
327
|
+
const userAccount = this.decode('User', buffer);
|
|
304
328
|
await this.addPubkey(new PublicKey(key), userAccount);
|
|
329
|
+
this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
|
|
305
330
|
}
|
|
331
|
+
// give event loop a chance to breathe
|
|
332
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
306
333
|
}
|
|
307
334
|
|
|
308
335
|
for (const [key, user] of this.userMap.entries()) {
|
|
309
336
|
if (!programAccountBufferMap.has(key)) {
|
|
310
337
|
await user.unsubscribe();
|
|
311
338
|
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
339
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (this.syncCallback) {
|
|
323
|
-
await this.syncCallback(this.getUniqueAuthorities());
|
|
340
|
+
// give event loop a chance to breathe
|
|
341
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
324
342
|
}
|
|
325
343
|
} catch (e) {
|
|
326
|
-
console.error(`Error in UserMap.sync()
|
|
344
|
+
console.error(`Error in UserMap.sync():`);
|
|
327
345
|
console.error(e);
|
|
328
346
|
} finally {
|
|
329
347
|
this.syncPromiseResolver();
|
|
@@ -332,6 +350,8 @@ export class UserMap implements UserMapInterface {
|
|
|
332
350
|
}
|
|
333
351
|
|
|
334
352
|
public async unsubscribe() {
|
|
353
|
+
await this.subscription.unsubscribe();
|
|
354
|
+
|
|
335
355
|
for (const [key, user] of this.userMap.entries()) {
|
|
336
356
|
await user.unsubscribe();
|
|
337
357
|
this.userMap.delete(key);
|
|
@@ -345,4 +365,17 @@ export class UserMap implements UserMapInterface {
|
|
|
345
365
|
this.lastNumberOfSubAccounts = undefined;
|
|
346
366
|
}
|
|
347
367
|
}
|
|
368
|
+
|
|
369
|
+
public async updateUserAccount(
|
|
370
|
+
key: string,
|
|
371
|
+
userAccount: UserAccount,
|
|
372
|
+
slot: number
|
|
373
|
+
) {
|
|
374
|
+
if (!this.userMap.has(key)) {
|
|
375
|
+
this.addPubkey(new PublicKey(key), userAccount, slot);
|
|
376
|
+
} else {
|
|
377
|
+
const user = this.userMap.get(key);
|
|
378
|
+
user.accountSubscriber.updateData(userAccount, slot);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
348
381
|
}
|
|
@@ -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/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
|
+
}
|