@drift-labs/sdk 2.25.4 → 2.26.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.
Files changed (90) hide show
  1. package/lib/accounts/pollingUserAccountSubscriber.d.ts +5 -4
  2. package/lib/accounts/pollingUserAccountSubscriber.js +35 -48
  3. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +4 -4
  4. package/lib/accounts/pollingUserStatsAccountSubscriber.js +30 -47
  5. package/lib/accounts/types.d.ts +2 -1
  6. package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
  7. package/lib/accounts/webSocketAccountSubscriber.js +4 -3
  8. package/lib/accounts/webSocketUserAccountSubscriber.d.ts +1 -0
  9. package/lib/accounts/webSocketUserAccountSubscriber.js +9 -0
  10. package/lib/addresses/pda.d.ts +2 -0
  11. package/lib/addresses/pda.js +8 -1
  12. package/lib/adminClient.d.ts +2 -0
  13. package/lib/adminClient.js +20 -0
  14. package/lib/constants/numericConstants.d.ts +61 -59
  15. package/lib/constants/spotMarkets.d.ts +1 -0
  16. package/lib/dlob/DLOB.d.ts +1 -0
  17. package/lib/dlob/DLOBApiClient.d.ts +14 -0
  18. package/lib/dlob/DLOBApiClient.js +34 -0
  19. package/lib/dlob/DLOBNode.d.ts +1 -0
  20. package/lib/dlob/DLOBSubscriber.d.ts +19 -0
  21. package/lib/dlob/DLOBSubscriber.js +42 -0
  22. package/lib/dlob/NodeList.d.ts +1 -0
  23. package/lib/dlob/types.d.ts +16 -0
  24. package/lib/dlob/types.js +2 -0
  25. package/lib/driftClient.d.ts +14 -10
  26. package/lib/driftClient.js +124 -28
  27. package/lib/examples/loadDlob.js +2 -4
  28. package/lib/factory/bigNum.d.ts +8 -7
  29. package/lib/idl/drift.json +264 -82
  30. package/lib/index.d.ts +2 -0
  31. package/lib/index.js +2 -0
  32. package/lib/math/amm.d.ts +2 -1
  33. package/lib/math/auction.d.ts +1 -0
  34. package/lib/math/conversion.d.ts +2 -1
  35. package/lib/math/funding.d.ts +1 -0
  36. package/lib/math/insurance.d.ts +1 -0
  37. package/lib/math/margin.d.ts +1 -0
  38. package/lib/math/market.d.ts +1 -0
  39. package/lib/math/oracles.d.ts +1 -0
  40. package/lib/math/orders.d.ts +1 -0
  41. package/lib/math/position.d.ts +1 -0
  42. package/lib/math/repeg.d.ts +1 -0
  43. package/lib/math/spotBalance.d.ts +1 -0
  44. package/lib/math/spotMarket.d.ts +1 -0
  45. package/lib/math/spotPosition.d.ts +1 -0
  46. package/lib/math/trade.d.ts +1 -0
  47. package/lib/math/utils.d.ts +1 -0
  48. package/lib/oracles/pythClient.d.ts +2 -1
  49. package/lib/oracles/types.d.ts +1 -0
  50. package/lib/orderParams.d.ts +1 -0
  51. package/lib/serum/serumSubscriber.d.ts +1 -0
  52. package/lib/slot/SlotSubscriber.js +3 -0
  53. package/lib/tokenFaucet.d.ts +1 -0
  54. package/lib/tx/retryTxSender.d.ts +2 -1
  55. package/lib/tx/retryTxSender.js +5 -1
  56. package/lib/tx/types.d.ts +4 -1
  57. package/lib/types.d.ts +12 -0
  58. package/lib/user.d.ts +1 -0
  59. package/lib/userMap/userMap.d.ts +17 -6
  60. package/lib/userMap/userMap.js +74 -46
  61. package/lib/userMap/userStatsMap.d.ts +6 -3
  62. package/lib/userMap/userStatsMap.js +30 -30
  63. package/package.json +2 -1
  64. package/src/accounts/pollingUserAccountSubscriber.ts +51 -63
  65. package/src/accounts/pollingUserStatsAccountSubscriber.ts +46 -62
  66. package/src/accounts/types.ts +2 -1
  67. package/src/accounts/webSocketAccountSubscriber.ts +4 -3
  68. package/src/accounts/webSocketUserAccountSubscriber.ts +10 -0
  69. package/src/addresses/pda.ts +13 -0
  70. package/src/adminClient.ts +34 -0
  71. package/src/dlob/DLOBApiClient.ts +37 -0
  72. package/src/dlob/DLOBSubscriber.ts +57 -0
  73. package/src/dlob/types.ts +20 -0
  74. package/src/driftClient.ts +216 -59
  75. package/src/examples/loadDlob.ts +2 -4
  76. package/src/idl/drift.json +264 -82
  77. package/src/index.ts +2 -0
  78. package/src/slot/SlotSubscriber.ts +4 -0
  79. package/src/tx/retryTxSender.ts +19 -2
  80. package/src/tx/types.ts +13 -0
  81. package/src/types.ts +12 -0
  82. package/src/userMap/userMap.ts +111 -60
  83. package/src/userMap/userStatsMap.ts +43 -44
  84. package/tests/dlob/helpers.ts +3 -1
  85. package/src/assert/assert.js +0 -9
  86. package/src/token/index.js +0 -38
  87. package/src/util/computeUnits.js +0 -27
  88. package/src/util/getTokenAddress.js +0 -9
  89. package/src/util/promiseTimeout.js +0 -14
  90. package/src/util/tps.js +0 -27
@@ -1,7 +1,8 @@
1
- import { User, DriftClient, OrderRecord, UserSubscriptionConfig, WrappedEvent } from '..';
1
+ import { User, DriftClient, UserAccount, OrderRecord, UserSubscriptionConfig, WrappedEvent, DLOB } from '..';
2
2
  import { PublicKey } from '@solana/web3.js';
3
3
  export interface UserMapInterface {
4
- fetchAllUsers(): Promise<void>;
4
+ subscribe(): Promise<void>;
5
+ unsubscribe(): Promise<void>;
5
6
  addPubkey(userAccountPublicKey: PublicKey): Promise<void>;
6
7
  has(key: string): boolean;
7
8
  get(key: string): User | undefined;
@@ -14,9 +15,12 @@ export declare class UserMap implements UserMapInterface {
14
15
  private userMap;
15
16
  private driftClient;
16
17
  private accountSubscription;
17
- constructor(driftClient: DriftClient, accountSubscription: UserSubscriptionConfig);
18
- fetchAllUsers(includeIdle?: boolean): Promise<void>;
19
- addPubkey(userAccountPublicKey: PublicKey): Promise<void>;
18
+ private includeIdle;
19
+ private lastNumberOfSubAccounts;
20
+ private syncCallback;
21
+ constructor(driftClient: DriftClient, accountSubscription: UserSubscriptionConfig, includeIdle?: boolean);
22
+ subscribe(): Promise<void>;
23
+ addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount): Promise<void>;
20
24
  has(key: string): boolean;
21
25
  /**
22
26
  * gets the User for a particular userAccountPublicKey, if no User exists, undefined is returned
@@ -36,9 +40,16 @@ export declare class UserMap implements UserMapInterface {
36
40
  * @returns authority PublicKey | undefined
37
41
  */
38
42
  getUserAuthority(key: string): PublicKey | undefined;
43
+ /**
44
+ * implements the {@link DLOBSource} interface
45
+ * create a DLOB from all the subscribed users
46
+ * @param slot
47
+ */
48
+ getDLOB(slot: number): Promise<DLOB>;
39
49
  updateWithOrderRecord(record: OrderRecord): Promise<void>;
40
50
  updateWithEventRecord(record: WrappedEvent<any>): Promise<void>;
41
51
  values(): IterableIterator<User>;
42
52
  size(): number;
43
- sync(includeIdle?: boolean): Promise<void>;
53
+ sync(): Promise<void>;
54
+ unsubscribe(): Promise<void>;
44
55
  }
@@ -6,46 +6,38 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.UserMap = void 0;
7
7
  const __1 = require("..");
8
8
  const web3_js_1 = require("@solana/web3.js");
9
+ const buffer_1 = require("buffer");
9
10
  const bs58_1 = __importDefault(require("bs58"));
10
11
  class UserMap {
11
- constructor(driftClient, accountSubscription) {
12
+ constructor(driftClient, accountSubscription, includeIdle = true) {
12
13
  this.userMap = new Map();
14
+ this.syncCallback = async (state) => {
15
+ if (state.numberOfSubAccounts !== this.lastNumberOfSubAccounts) {
16
+ await this.sync();
17
+ this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
18
+ }
19
+ };
13
20
  this.driftClient = driftClient;
14
21
  this.accountSubscription = accountSubscription;
22
+ this.includeIdle = includeIdle;
15
23
  }
16
- async fetchAllUsers(includeIdle = true) {
17
- const userArray = [];
18
- const userAccountArray = [];
19
- const programUserAccounts = await this.driftClient.fetchAllUserAccounts(includeIdle);
20
- for (const programUserAccount of programUserAccounts) {
21
- if (this.userMap.has(programUserAccount.publicKey.toString())) {
22
- continue;
23
- }
24
- const user = new __1.User({
25
- driftClient: this.driftClient,
26
- userAccountPublicKey: programUserAccount.publicKey,
27
- accountSubscription: this.accountSubscription,
28
- });
29
- userArray.push(user);
30
- userAccountArray.push(programUserAccount.account);
31
- }
32
- if (this.accountSubscription.type === 'polling') {
33
- await (0, __1.bulkPollingUserSubscribe)(userArray, this.accountSubscription.accountLoader);
34
- }
35
- else {
36
- await Promise.all(userArray.map((user, i) => user.subscribe(userAccountArray[i])));
37
- }
38
- for (const user of userArray) {
39
- this.userMap.set(user.getUserAccountPublicKey().toString(), user);
40
- }
24
+ async subscribe() {
25
+ if (this.size() > 0) {
26
+ return;
27
+ }
28
+ await this.driftClient.subscribe();
29
+ this.lastNumberOfSubAccounts =
30
+ this.driftClient.getStateAccount().numberOfSubAccounts;
31
+ this.driftClient.eventEmitter.on('stateAccountUpdate', this.syncCallback);
32
+ await this.sync();
41
33
  }
42
- async addPubkey(userAccountPublicKey) {
34
+ async addPubkey(userAccountPublicKey, userAccount) {
43
35
  const user = new __1.User({
44
36
  driftClient: this.driftClient,
45
37
  userAccountPublicKey,
46
38
  accountSubscription: this.accountSubscription,
47
39
  });
48
- await user.subscribe();
40
+ await user.subscribe(userAccount);
49
41
  this.userMap.set(userAccountPublicKey.toString(), user);
50
42
  }
51
43
  has(key) {
@@ -83,6 +75,16 @@ class UserMap {
83
75
  }
84
76
  return chUser.getUserAccount().authority;
85
77
  }
78
+ /**
79
+ * implements the {@link DLOBSource} interface
80
+ * create a DLOB from all the subscribed users
81
+ * @param slot
82
+ */
83
+ async getDLOB(slot) {
84
+ const dlob = new __1.DLOB();
85
+ await dlob.initFromUserMap(this, slot);
86
+ return dlob;
87
+ }
86
88
  async updateWithOrderRecord(record) {
87
89
  if (!this.has(record.user.toString())) {
88
90
  await this.addPubkey(record.user);
@@ -134,9 +136,9 @@ class UserMap {
134
136
  size() {
135
137
  return this.userMap.size;
136
138
  }
137
- async sync(includeIdle = true) {
139
+ async sync() {
138
140
  let filters = undefined;
139
- if (!includeIdle) {
141
+ if (!this.includeIdle) {
140
142
  filters = [
141
143
  {
142
144
  memcmp: {
@@ -146,29 +148,55 @@ class UserMap {
146
148
  },
147
149
  ];
148
150
  }
149
- const programAccounts = await this.driftClient.connection.getProgramAccounts(this.driftClient.program.programId, {
150
- commitment: this.driftClient.connection.commitment,
151
- filters: [
152
- {
153
- memcmp: this.driftClient.program.coder.accounts.memcmp('User'),
154
- },
155
- ...(Array.isArray(filters) ? filters : []),
156
- ],
157
- });
158
- const programAccountMap = new Map();
159
- for (const programAccount of programAccounts) {
160
- programAccountMap.set(programAccount.pubkey.toString(), programAccount.account);
161
- }
162
- for (const key of programAccountMap.keys()) {
151
+ const rpcRequestArgs = [
152
+ this.driftClient.program.programId.toBase58(),
153
+ {
154
+ commitment: this.driftClient.connection.commitment,
155
+ filters: [
156
+ {
157
+ memcmp: this.driftClient.program.coder.accounts.memcmp('User'),
158
+ },
159
+ ...(Array.isArray(filters) ? filters : []),
160
+ ],
161
+ encoding: 'base64',
162
+ withContext: true,
163
+ },
164
+ ];
165
+ // @ts-ignore
166
+ const rpcJSONResponse = await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
167
+ const rpcResponseAndContext = rpcJSONResponse.result;
168
+ const slot = rpcResponseAndContext.context.slot;
169
+ const programAccountBufferMap = new Map();
170
+ for (const programAccount of rpcResponseAndContext.value) {
171
+ programAccountBufferMap.set(programAccount.pubkey.toString(),
172
+ // @ts-ignore
173
+ buffer_1.Buffer.from(programAccount.account.data[0], programAccount.account.data[1]));
174
+ }
175
+ for (const [key, buffer] of programAccountBufferMap.entries()) {
163
176
  if (!this.has(key)) {
164
- await this.addPubkey(new web3_js_1.PublicKey(key));
177
+ const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', buffer);
178
+ await this.addPubkey(new web3_js_1.PublicKey(key), userAccount);
165
179
  }
166
180
  }
167
181
  for (const [key, user] of this.userMap.entries()) {
168
- if (!programAccountMap.has(key)) {
182
+ if (!programAccountBufferMap.has(key)) {
169
183
  await user.unsubscribe();
170
184
  this.userMap.delete(key);
171
185
  }
186
+ else {
187
+ const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', programAccountBufferMap.get(key));
188
+ user.accountSubscriber.updateData(userAccount, slot);
189
+ }
190
+ }
191
+ }
192
+ async unsubscribe() {
193
+ for (const [key, user] of this.userMap.entries()) {
194
+ await user.unsubscribe();
195
+ this.userMap.delete(key);
196
+ }
197
+ if (this.lastNumberOfSubAccounts) {
198
+ this.driftClient.eventEmitter.removeListener('stateAccountUpdate', this.syncCallback);
199
+ this.lastNumberOfSubAccounts = undefined;
172
200
  }
173
201
  }
174
202
  }
@@ -1,4 +1,4 @@
1
- import { DriftClient, OrderRecord, UserStats, UserStatsSubscriptionConfig, WrappedEvent } from '..';
1
+ import { DriftClient, OrderRecord, UserStatsAccount, UserStats, UserStatsSubscriptionConfig, WrappedEvent } from '..';
2
2
  import { PublicKey } from '@solana/web3.js';
3
3
  import { UserMap } from './userMap';
4
4
  export declare class UserStatsMap {
@@ -8,9 +8,11 @@ export declare class UserStatsMap {
8
8
  private userStatsMap;
9
9
  private driftClient;
10
10
  private accountSubscription;
11
+ private lastNumberOfAuthorities;
12
+ private syncCallback;
11
13
  constructor(driftClient: DriftClient, accountSubscription: UserStatsSubscriptionConfig);
12
- fetchAllUserStats(): Promise<void>;
13
- addUserStat(authority: PublicKey): Promise<void>;
14
+ subscribe(): Promise<void>;
15
+ addUserStat(authority: PublicKey, userStatsAccount?: UserStatsAccount): Promise<void>;
14
16
  updateWithOrderRecord(record: OrderRecord, userMap: UserMap): Promise<void>;
15
17
  updateWithEventRecord(record: WrappedEvent<any>, userMap?: UserMap): Promise<void>;
16
18
  has(authorityPublicKey: string): boolean;
@@ -19,4 +21,5 @@ export declare class UserStatsMap {
19
21
  values(): IterableIterator<UserStats>;
20
22
  size(): number;
21
23
  sync(): Promise<void>;
24
+ unsubscribe(): Promise<void>;
22
25
  }
@@ -9,49 +9,38 @@ class UserStatsMap {
9
9
  * map from authority pubkey to UserStats
10
10
  */
11
11
  this.userStatsMap = new Map();
12
+ this.syncCallback = async (state) => {
13
+ if (state.numberOfAuthorities !== this.lastNumberOfAuthorities) {
14
+ await this.sync();
15
+ this.lastNumberOfAuthorities = state.numberOfAuthorities;
16
+ }
17
+ };
12
18
  this.driftClient = driftClient;
13
19
  this.accountSubscription = accountSubscription;
14
20
  }
15
- async fetchAllUserStats() {
16
- const userStatArray = [];
17
- const userStatsAccountArray = [];
18
- const programUserAccounts = (await this.driftClient.program.account.userStats.all());
19
- for (const programUserAccount of programUserAccounts) {
20
- const userStat = programUserAccount.account;
21
- if (this.userStatsMap.has(userStat.authority.toString())) {
22
- continue;
23
- }
24
- const chUserStat = new __1.UserStats({
25
- driftClient: this.driftClient,
26
- userStatsAccountPublicKey: programUserAccount.publicKey,
27
- accountSubscription: this.accountSubscription,
28
- });
29
- userStatArray.push(chUserStat);
30
- userStatsAccountArray.push(userStat);
31
- }
32
- if (this.accountSubscription.type === 'polling') {
33
- await (0, __1.bulkPollingUserStatsSubscribe)(userStatArray, this.accountSubscription.accountLoader);
34
- }
35
- else {
36
- await Promise.all(userStatArray.map((userStat, i) => userStat.subscribe(userStatsAccountArray[i])));
37
- }
38
- for (const userStat of userStatArray) {
39
- this.userStatsMap.set(userStat.getAccount().authority.toString(), userStat);
40
- }
21
+ async subscribe() {
22
+ if (this.size() > 0) {
23
+ return;
24
+ }
25
+ await this.driftClient.subscribe();
26
+ this.lastNumberOfAuthorities =
27
+ this.driftClient.getStateAccount().numberOfAuthorities;
28
+ this.driftClient.eventEmitter.on('stateAccountUpdate', this.syncCallback);
29
+ await this.sync();
41
30
  }
42
- async addUserStat(authority) {
31
+ async addUserStat(authority, userStatsAccount) {
43
32
  const userStat = new __1.UserStats({
44
33
  driftClient: this.driftClient,
45
34
  userStatsAccountPublicKey: (0, __1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, authority),
46
35
  accountSubscription: this.accountSubscription,
47
36
  });
48
- await userStat.subscribe();
37
+ await userStat.subscribe(userStatsAccount);
49
38
  this.userStatsMap.set(authority.toString(), userStat);
50
39
  }
51
40
  async updateWithOrderRecord(record, userMap) {
52
41
  const user = await userMap.mustGet(record.user.toString());
53
42
  if (!this.has(user.getUserAccount().authority.toString())) {
54
- this.addUserStat(user.getUserAccount().authority);
43
+ await this.addUserStat(user.getUserAccount().authority);
55
44
  }
56
45
  }
57
46
  async updateWithEventRecord(record, userMap) {
@@ -152,9 +141,20 @@ class UserStatsMap {
152
141
  }
153
142
  for (const key of programAccountMap.keys()) {
154
143
  if (!this.has(key)) {
155
- await this.addUserStat(new web3_js_1.PublicKey(key));
144
+ const userStatsAccount = this.driftClient.program.account.userStats.coder.accounts.decode('UserStats', programAccountMap.get(key).data);
145
+ await this.addUserStat(new web3_js_1.PublicKey(key), userStatsAccount);
156
146
  }
157
147
  }
158
148
  }
149
+ async unsubscribe() {
150
+ for (const [key, userStats] of this.userStatsMap.entries()) {
151
+ await userStats.unsubscribe();
152
+ this.userStatsMap.delete(key);
153
+ }
154
+ if (this.lastNumberOfAuthorities) {
155
+ this.driftClient.eventEmitter.removeListener('stateAccountUpdate', this.syncCallback);
156
+ this.lastNumberOfAuthorities = undefined;
157
+ }
158
+ }
159
159
  }
160
160
  exports.UserStatsMap = UserStatsMap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.25.4",
3
+ "version": "2.26.0-beta.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -34,6 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@coral-xyz/anchor": "0.26.0",
37
+ "@ellipsis-labs/phoenix-sdk": "^1.3.2",
37
38
  "@project-serum/serum": "^0.13.38",
38
39
  "@pythnetwork/client": "2.5.3",
39
40
  "@solana/spl-token": "^0.1.6",
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  DataAndSlot,
3
- AccountToPoll,
4
3
  NotSubscribedError,
5
4
  UserAccountEvents,
6
5
  UserAccountSubscriber,
@@ -11,7 +10,6 @@ import { EventEmitter } from 'events';
11
10
  import { PublicKey } from '@solana/web3.js';
12
11
  import { UserAccount } from '../types';
13
12
  import { BulkAccountLoader } from './bulkAccountLoader';
14
- import { capitalize } from './utils';
15
13
 
16
14
  export class PollingUserAccountSubscriber implements UserAccountSubscriber {
17
15
  isSubscribed: boolean;
@@ -20,7 +18,7 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
20
18
  userAccountPublicKey: PublicKey;
21
19
 
22
20
  accountLoader: BulkAccountLoader;
23
- accountsToPoll = new Map<string, AccountToPoll>();
21
+ callbackId?: string;
24
22
  errorCallbackId?: string;
25
23
 
26
24
  user?: DataAndSlot<UserAccount>;
@@ -37,15 +35,19 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
37
35
  this.userAccountPublicKey = userAccountPublicKey;
38
36
  }
39
37
 
40
- async subscribe(): Promise<boolean> {
38
+ async subscribe(userAccount?: UserAccount): Promise<boolean> {
41
39
  if (this.isSubscribed) {
42
40
  return true;
43
41
  }
44
42
 
43
+ if (userAccount) {
44
+ this.user = { data: userAccount, slot: undefined };
45
+ }
46
+
45
47
  await this.addToAccountLoader();
46
48
 
47
49
  await this.fetchIfUnloaded();
48
- if (this.doAccountsExist()) {
50
+ if (this.doesAccountExist()) {
49
51
  this.eventEmitter.emit('update');
50
52
  }
51
53
 
@@ -54,34 +56,30 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
54
56
  }
55
57
 
56
58
  async addToAccountLoader(): Promise<void> {
57
- if (this.accountsToPoll.size > 0) {
59
+ if (this.callbackId) {
58
60
  return;
59
61
  }
60
62
 
61
- this.accountsToPoll.set(this.userAccountPublicKey.toString(), {
62
- key: 'user',
63
- publicKey: this.userAccountPublicKey,
64
- eventType: 'userAccountUpdate',
65
- });
63
+ this.callbackId = await this.accountLoader.addAccount(
64
+ this.userAccountPublicKey,
65
+ (buffer, slot: number) => {
66
+ if (!buffer) {
67
+ return;
68
+ }
66
69
 
67
- for (const [_, accountToPoll] of this.accountsToPoll) {
68
- accountToPoll.callbackId = await this.accountLoader.addAccount(
69
- accountToPoll.publicKey,
70
- (buffer, slot) => {
71
- if (!buffer) {
72
- return;
73
- }
74
-
75
- const account = this.program.account[
76
- accountToPoll.key
77
- ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
78
- this[accountToPoll.key] = { data: account, slot };
79
- // @ts-ignore
80
- this.eventEmitter.emit(accountToPoll.eventType, account);
81
- this.eventEmitter.emit('update');
70
+ if (this.user && this.user.slot > slot) {
71
+ return;
82
72
  }
83
- );
84
- }
73
+
74
+ const account = this.program.account.user.coder.accounts.decode(
75
+ 'User',
76
+ buffer
77
+ );
78
+ this.user = { data: account, slot };
79
+ this.eventEmitter.emit('userAccountUpdate', account);
80
+ this.eventEmitter.emit('update');
81
+ }
82
+ );
85
83
 
86
84
  this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
87
85
  this.eventEmitter.emit('error', error);
@@ -89,43 +87,28 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
89
87
  }
90
88
 
91
89
  async fetchIfUnloaded(): Promise<void> {
92
- let shouldFetch = false;
93
- for (const [_, accountToPoll] of this.accountsToPoll) {
94
- if (this[accountToPoll.key] === undefined) {
95
- shouldFetch = true;
96
- break;
97
- }
98
- }
99
-
100
- if (shouldFetch) {
90
+ if (this.user === undefined) {
101
91
  await this.fetch();
102
92
  }
103
93
  }
104
94
 
105
95
  async fetch(): Promise<void> {
106
96
  await this.accountLoader.load();
107
- for (const [_, accountToPoll] of this.accountsToPoll) {
108
- const { buffer, slot } = this.accountLoader.getBufferAndSlot(
109
- accountToPoll.publicKey
97
+ const { buffer, slot } = this.accountLoader.getBufferAndSlot(
98
+ this.userAccountPublicKey
99
+ );
100
+ const currentSlot = this.user?.slot ?? 0;
101
+ if (buffer && slot > currentSlot) {
102
+ const account = this.program.account.user.coder.accounts.decode(
103
+ 'User',
104
+ buffer
110
105
  );
111
- if (buffer) {
112
- const account = this.program.account[
113
- accountToPoll.key
114
- ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
115
- this[accountToPoll.key] = { data: account, slot };
116
- }
106
+ this.user = { data: account, slot };
117
107
  }
118
108
  }
119
109
 
120
- doAccountsExist(): boolean {
121
- let success = true;
122
- for (const [_, accountToPoll] of this.accountsToPoll) {
123
- if (!this[accountToPoll.key]) {
124
- success = false;
125
- break;
126
- }
127
- }
128
- return success;
110
+ doesAccountExist(): boolean {
111
+ return this.user !== undefined;
129
112
  }
130
113
 
131
114
  async unsubscribe(): Promise<void> {
@@ -133,18 +116,15 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
133
116
  return;
134
117
  }
135
118
 
136
- for (const [_, accountToPoll] of this.accountsToPoll) {
137
- this.accountLoader.removeAccount(
138
- accountToPoll.publicKey,
139
- accountToPoll.callbackId
140
- );
141
- }
119
+ this.accountLoader.removeAccount(
120
+ this.userAccountPublicKey,
121
+ this.callbackId
122
+ );
123
+ this.callbackId = undefined;
142
124
 
143
125
  this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
144
126
  this.errorCallbackId = undefined;
145
127
 
146
- this.accountsToPoll.clear();
147
-
148
128
  this.isSubscribed = false;
149
129
  }
150
130
 
@@ -160,4 +140,12 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
160
140
  this.assertIsSubscribed();
161
141
  return this.user;
162
142
  }
143
+
144
+ public updateData(userAccount: UserAccount, slot: number): void {
145
+ if (!this.user || this.user.slot < slot) {
146
+ this.user = { data: userAccount, slot };
147
+ this.eventEmitter.emit('userAccountUpdate', userAccount);
148
+ this.eventEmitter.emit('update');
149
+ }
150
+ }
163
151
  }