@drift-labs/sdk 2.98.0-beta.11 → 2.98.0-beta.13
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/browser/driftClient.js +3 -0
- package/lib/browser/index.d.ts +1 -0
- package/lib/browser/index.js +1 -0
- package/lib/browser/memcmp.d.ts +3 -0
- package/lib/browser/memcmp.js +28 -1
- package/lib/browser/userMap/referrerMap.d.ts +45 -0
- package/lib/browser/userMap/referrerMap.js +186 -0
- package/lib/node/driftClient.js +3 -0
- package/lib/node/index.d.ts +1 -0
- package/lib/node/index.js +1 -0
- package/lib/node/memcmp.d.ts +3 -0
- package/lib/node/memcmp.js +28 -1
- package/lib/node/userMap/referrerMap.d.ts +45 -0
- package/lib/node/userMap/referrerMap.js +186 -0
- package/package.json +1 -1
- package/src/driftClient.ts +18 -0
- package/src/index.ts +1 -0
- package/src/memcmp.ts +27 -0
- package/src/userMap/referrerMap.ts +273 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.98.0-beta.
|
|
1
|
+
2.98.0-beta.13
|
|
@@ -3880,6 +3880,9 @@ class DriftClient {
|
|
|
3880
3880
|
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
3881
3881
|
const isSolMarket = spotMarketAccount.mint.equals(spotMarkets_1.WRAPPED_SOL_MINT);
|
|
3882
3882
|
const createWSOLTokenAccount = isSolMarket && collateralAccountPublicKey.equals(this.wallet.publicKey);
|
|
3883
|
+
// create associated token account because it may not exist
|
|
3884
|
+
const associatedTokenAccountPublicKey = (0, spl_token_1.getAssociatedTokenAddressSync)(spotMarketAccount.mint, this.wallet.publicKey, true);
|
|
3885
|
+
addIfStakeIxs.push(await (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(this.wallet.publicKey, associatedTokenAccountPublicKey, this.wallet.publicKey, spotMarketAccount.mint));
|
|
3883
3886
|
let tokenAccount;
|
|
3884
3887
|
if (!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))) {
|
|
3885
3888
|
addIfStakeIxs.push(await this.getInitializeUserStatsIx());
|
package/lib/browser/index.d.ts
CHANGED
|
@@ -106,6 +106,7 @@ export * from './dlob/DLOBApiClient';
|
|
|
106
106
|
export * from './dlob/types';
|
|
107
107
|
export * from './dlob/orderBookLevels';
|
|
108
108
|
export * from './userMap/userMap';
|
|
109
|
+
export * from './userMap/referrerMap';
|
|
109
110
|
export * from './userMap/userStatsMap';
|
|
110
111
|
export * from './userMap/userMapConfig';
|
|
111
112
|
export * from './math/bankruptcy';
|
package/lib/browser/index.js
CHANGED
|
@@ -129,6 +129,7 @@ __exportStar(require("./dlob/DLOBApiClient"), exports);
|
|
|
129
129
|
__exportStar(require("./dlob/types"), exports);
|
|
130
130
|
__exportStar(require("./dlob/orderBookLevels"), exports);
|
|
131
131
|
__exportStar(require("./userMap/userMap"), exports);
|
|
132
|
+
__exportStar(require("./userMap/referrerMap"), exports);
|
|
132
133
|
__exportStar(require("./userMap/userStatsMap"), exports);
|
|
133
134
|
__exportStar(require("./userMap/userMapConfig"), exports);
|
|
134
135
|
__exportStar(require("./math/bankruptcy"), exports);
|
package/lib/browser/memcmp.d.ts
CHANGED
|
@@ -5,3 +5,6 @@ export declare function getUserWithOrderFilter(): MemcmpFilter;
|
|
|
5
5
|
export declare function getUserWithAuctionFilter(): MemcmpFilter;
|
|
6
6
|
export declare function getUserThatHasBeenLP(): MemcmpFilter;
|
|
7
7
|
export declare function getUserWithName(name: string): MemcmpFilter;
|
|
8
|
+
export declare function getUserStatsFilter(): MemcmpFilter;
|
|
9
|
+
export declare function getUserStatsIsReferredFilter(): MemcmpFilter;
|
|
10
|
+
export declare function getUserStatsIsReferredOrReferrerFilter(): MemcmpFilter;
|
package/lib/browser/memcmp.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getUserWithName = exports.getUserThatHasBeenLP = exports.getUserWithAuctionFilter = exports.getUserWithOrderFilter = exports.getNonIdleUserFilter = exports.getUserFilter = void 0;
|
|
6
|
+
exports.getUserStatsIsReferredOrReferrerFilter = exports.getUserStatsIsReferredFilter = exports.getUserStatsFilter = exports.getUserWithName = exports.getUserThatHasBeenLP = exports.getUserWithAuctionFilter = exports.getUserWithOrderFilter = exports.getNonIdleUserFilter = exports.getUserFilter = void 0;
|
|
7
7
|
const bs58_1 = __importDefault(require("bs58"));
|
|
8
8
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
9
9
|
const userName_1 = require("./userName");
|
|
@@ -61,3 +61,30 @@ function getUserWithName(name) {
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
exports.getUserWithName = getUserWithName;
|
|
64
|
+
function getUserStatsFilter() {
|
|
65
|
+
return {
|
|
66
|
+
memcmp: {
|
|
67
|
+
offset: 0,
|
|
68
|
+
bytes: bs58_1.default.encode(anchor_1.BorshAccountsCoder.accountDiscriminator('UserStats')),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
exports.getUserStatsFilter = getUserStatsFilter;
|
|
73
|
+
function getUserStatsIsReferredFilter() {
|
|
74
|
+
return {
|
|
75
|
+
memcmp: {
|
|
76
|
+
offset: 188,
|
|
77
|
+
bytes: bs58_1.default.encode(Buffer.from(Uint8Array.from([2]))),
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
exports.getUserStatsIsReferredFilter = getUserStatsIsReferredFilter;
|
|
82
|
+
function getUserStatsIsReferredOrReferrerFilter() {
|
|
83
|
+
return {
|
|
84
|
+
memcmp: {
|
|
85
|
+
offset: 188,
|
|
86
|
+
bytes: bs58_1.default.encode(Buffer.from(Uint8Array.from([3]))),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
exports.getUserStatsIsReferredOrReferrerFilter = getUserStatsIsReferredOrReferrerFilter;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { MemcmpFilter } from '@solana/web3.js';
|
|
2
|
+
import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
|
|
3
|
+
import { DriftClient } from '../driftClient';
|
|
4
|
+
import { ReferrerInfo } from '../types';
|
|
5
|
+
export declare class ReferrerMap {
|
|
6
|
+
/**
|
|
7
|
+
* map from authority pubkey to ReferrerInfo.
|
|
8
|
+
* - if a user has not been entered into the map, the value is undefined
|
|
9
|
+
* - if a user has no referrer, the value is null
|
|
10
|
+
* - if a user has a referrer, the value is a ReferrerInfo object
|
|
11
|
+
*/
|
|
12
|
+
private referrerMap;
|
|
13
|
+
private driftClient;
|
|
14
|
+
private bulkAccountLoader;
|
|
15
|
+
private parallelSync;
|
|
16
|
+
private fetchPromise?;
|
|
17
|
+
private fetchPromiseResolver;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new UserStatsMap instance.
|
|
20
|
+
*
|
|
21
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
22
|
+
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
23
|
+
*/
|
|
24
|
+
constructor(driftClient: DriftClient, bulkAccountLoader?: BulkAccountLoader, parallelSync?: boolean);
|
|
25
|
+
/**
|
|
26
|
+
* Subscribe to all UserStats accounts.
|
|
27
|
+
*/
|
|
28
|
+
subscribe(): Promise<void>;
|
|
29
|
+
has(authorityPublicKey: string): boolean;
|
|
30
|
+
get(authorityPublicKey: string): ReferrerInfo | undefined | null;
|
|
31
|
+
addReferrerInfo(authority: string, referrerInfo?: ReferrerInfo | null): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Enforce that a UserStats will exist for the given authorityPublicKey,
|
|
34
|
+
* reading one from the blockchain if necessary.
|
|
35
|
+
* @param authorityPublicKey
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
38
|
+
mustGet(authorityPublicKey: string): Promise<ReferrerInfo | null | undefined>;
|
|
39
|
+
values(): IterableIterator<ReferrerInfo | null>;
|
|
40
|
+
size(): number;
|
|
41
|
+
sync(): Promise<void>;
|
|
42
|
+
syncAll(): Promise<void>;
|
|
43
|
+
syncReferrer(referrerFilter: MemcmpFilter): Promise<void>;
|
|
44
|
+
unsubscribe(): Promise<void>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReferrerMap = void 0;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const bulkAccountLoader_1 = require("../accounts/bulkAccountLoader");
|
|
6
|
+
const userStats_1 = require("../userStats");
|
|
7
|
+
const pda_1 = require("../addresses/pda");
|
|
8
|
+
const memcmp_1 = require("../memcmp");
|
|
9
|
+
const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes");
|
|
10
|
+
const DEFAULT_PUBLIC_KEY = web3_js_1.PublicKey.default.toBase58();
|
|
11
|
+
class ReferrerMap {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new UserStatsMap instance.
|
|
14
|
+
*
|
|
15
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
16
|
+
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
17
|
+
*/
|
|
18
|
+
constructor(driftClient, bulkAccountLoader, parallelSync) {
|
|
19
|
+
/**
|
|
20
|
+
* map from authority pubkey to ReferrerInfo.
|
|
21
|
+
* - if a user has not been entered into the map, the value is undefined
|
|
22
|
+
* - if a user has no referrer, the value is null
|
|
23
|
+
* - if a user has a referrer, the value is a ReferrerInfo object
|
|
24
|
+
*/
|
|
25
|
+
this.referrerMap = new Map();
|
|
26
|
+
this.driftClient = driftClient;
|
|
27
|
+
if (!bulkAccountLoader) {
|
|
28
|
+
bulkAccountLoader = new bulkAccountLoader_1.BulkAccountLoader(driftClient.connection, driftClient.opts.commitment, 0);
|
|
29
|
+
}
|
|
30
|
+
this.bulkAccountLoader = bulkAccountLoader;
|
|
31
|
+
this.parallelSync = parallelSync !== undefined ? parallelSync : true;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Subscribe to all UserStats accounts.
|
|
35
|
+
*/
|
|
36
|
+
async subscribe() {
|
|
37
|
+
if (this.size() > 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
await this.driftClient.subscribe();
|
|
41
|
+
await this.sync();
|
|
42
|
+
}
|
|
43
|
+
has(authorityPublicKey) {
|
|
44
|
+
return this.referrerMap.has(authorityPublicKey);
|
|
45
|
+
}
|
|
46
|
+
get(authorityPublicKey) {
|
|
47
|
+
return this.referrerMap.get(authorityPublicKey);
|
|
48
|
+
}
|
|
49
|
+
async addReferrerInfo(authority, referrerInfo) {
|
|
50
|
+
if (referrerInfo || referrerInfo === null) {
|
|
51
|
+
this.referrerMap.set(authority, referrerInfo);
|
|
52
|
+
}
|
|
53
|
+
else if (referrerInfo === undefined) {
|
|
54
|
+
const userStat = new userStats_1.UserStats({
|
|
55
|
+
driftClient: this.driftClient,
|
|
56
|
+
userStatsAccountPublicKey: (0, pda_1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, new web3_js_1.PublicKey(authority)),
|
|
57
|
+
accountSubscription: {
|
|
58
|
+
type: 'polling',
|
|
59
|
+
accountLoader: this.bulkAccountLoader,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
await userStat.fetchAccounts();
|
|
63
|
+
const newReferrerInfo = userStat.getReferrerInfo();
|
|
64
|
+
if (newReferrerInfo) {
|
|
65
|
+
this.referrerMap.set(authority.toString(), newReferrerInfo);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.referrerMap.set(authority.toString(), null);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Enforce that a UserStats will exist for the given authorityPublicKey,
|
|
74
|
+
* reading one from the blockchain if necessary.
|
|
75
|
+
* @param authorityPublicKey
|
|
76
|
+
* @returns
|
|
77
|
+
*/
|
|
78
|
+
async mustGet(authorityPublicKey) {
|
|
79
|
+
if (!this.has(authorityPublicKey)) {
|
|
80
|
+
await this.addReferrerInfo(authorityPublicKey);
|
|
81
|
+
}
|
|
82
|
+
return this.get(authorityPublicKey);
|
|
83
|
+
}
|
|
84
|
+
values() {
|
|
85
|
+
return this.referrerMap.values();
|
|
86
|
+
}
|
|
87
|
+
size() {
|
|
88
|
+
return this.referrerMap.size;
|
|
89
|
+
}
|
|
90
|
+
async sync() {
|
|
91
|
+
if (this.fetchPromise) {
|
|
92
|
+
return this.fetchPromise;
|
|
93
|
+
}
|
|
94
|
+
this.fetchPromise = new Promise((resolver) => {
|
|
95
|
+
this.fetchPromiseResolver = resolver;
|
|
96
|
+
});
|
|
97
|
+
try {
|
|
98
|
+
if (this.parallelSync) {
|
|
99
|
+
await Promise.all([
|
|
100
|
+
this.syncAll(),
|
|
101
|
+
this.syncReferrer((0, memcmp_1.getUserStatsIsReferredFilter)()),
|
|
102
|
+
this.syncReferrer((0, memcmp_1.getUserStatsIsReferredOrReferrerFilter)()),
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
await this.syncAll();
|
|
107
|
+
await this.syncReferrer((0, memcmp_1.getUserStatsIsReferredFilter)());
|
|
108
|
+
await this.syncReferrer((0, memcmp_1.getUserStatsIsReferredOrReferrerFilter)());
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
console.error('error in referrerMap.sync', e);
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
this.fetchPromiseResolver();
|
|
116
|
+
this.fetchPromise = undefined;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async syncAll() {
|
|
120
|
+
const rpcRequestArgs = [
|
|
121
|
+
this.driftClient.program.programId.toBase58(),
|
|
122
|
+
{
|
|
123
|
+
commitment: this.driftClient.opts.commitment,
|
|
124
|
+
filters: [(0, memcmp_1.getUserStatsFilter)()],
|
|
125
|
+
encoding: 'base64',
|
|
126
|
+
dataSlice: {
|
|
127
|
+
offset: 0,
|
|
128
|
+
length: 0,
|
|
129
|
+
},
|
|
130
|
+
withContext: true,
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
const rpcJSONResponse =
|
|
134
|
+
// @ts-ignore
|
|
135
|
+
await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
136
|
+
const rpcResponseAndContext = rpcJSONResponse.result;
|
|
137
|
+
for (const account of rpcResponseAndContext.value) {
|
|
138
|
+
// only add if it isn't already in the map
|
|
139
|
+
// so that if syncReferrer already set it, we dont overwrite
|
|
140
|
+
if (!this.has(account.pubkey)) {
|
|
141
|
+
this.addReferrerInfo(account.pubkey, null);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async syncReferrer(referrerFilter) {
|
|
146
|
+
const rpcRequestArgs = [
|
|
147
|
+
this.driftClient.program.programId.toBase58(),
|
|
148
|
+
{
|
|
149
|
+
commitment: this.driftClient.opts.commitment,
|
|
150
|
+
filters: [(0, memcmp_1.getUserStatsFilter)(), referrerFilter],
|
|
151
|
+
encoding: 'base64',
|
|
152
|
+
dataSlice: {
|
|
153
|
+
offset: 0,
|
|
154
|
+
length: 72,
|
|
155
|
+
},
|
|
156
|
+
withContext: true,
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
const rpcJSONResponse =
|
|
160
|
+
// @ts-ignore
|
|
161
|
+
await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
162
|
+
const rpcResponseAndContext = rpcJSONResponse.result;
|
|
163
|
+
const batchSize = 1000;
|
|
164
|
+
for (let i = 0; i < rpcResponseAndContext.value.length; i += batchSize) {
|
|
165
|
+
const batch = rpcResponseAndContext.value.slice(i, i + batchSize);
|
|
166
|
+
await Promise.all(batch.map(async (programAccount) => {
|
|
167
|
+
// @ts-ignore
|
|
168
|
+
const buffer = Buffer.from(programAccount.account.data[0], programAccount.account.data[1]);
|
|
169
|
+
const authority = bytes_1.bs58.encode(buffer.subarray(8, 40));
|
|
170
|
+
const referrer = bytes_1.bs58.encode(buffer.subarray(40, 72));
|
|
171
|
+
const referrerKey = new web3_js_1.PublicKey(referrer);
|
|
172
|
+
this.addReferrerInfo(authority, referrer === DEFAULT_PUBLIC_KEY
|
|
173
|
+
? null
|
|
174
|
+
: {
|
|
175
|
+
referrer: (0, pda_1.getUserAccountPublicKeySync)(this.driftClient.program.programId, referrerKey, 0),
|
|
176
|
+
referrerStats: (0, pda_1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, referrerKey),
|
|
177
|
+
});
|
|
178
|
+
}));
|
|
179
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async unsubscribe() {
|
|
183
|
+
this.referrerMap.clear();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.ReferrerMap = ReferrerMap;
|
package/lib/node/driftClient.js
CHANGED
|
@@ -3880,6 +3880,9 @@ class DriftClient {
|
|
|
3880
3880
|
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
3881
3881
|
const isSolMarket = spotMarketAccount.mint.equals(spotMarkets_1.WRAPPED_SOL_MINT);
|
|
3882
3882
|
const createWSOLTokenAccount = isSolMarket && collateralAccountPublicKey.equals(this.wallet.publicKey);
|
|
3883
|
+
// create associated token account because it may not exist
|
|
3884
|
+
const associatedTokenAccountPublicKey = (0, spl_token_1.getAssociatedTokenAddressSync)(spotMarketAccount.mint, this.wallet.publicKey, true);
|
|
3885
|
+
addIfStakeIxs.push(await (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(this.wallet.publicKey, associatedTokenAccountPublicKey, this.wallet.publicKey, spotMarketAccount.mint));
|
|
3883
3886
|
let tokenAccount;
|
|
3884
3887
|
if (!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))) {
|
|
3885
3888
|
addIfStakeIxs.push(await this.getInitializeUserStatsIx());
|
package/lib/node/index.d.ts
CHANGED
|
@@ -106,6 +106,7 @@ export * from './dlob/DLOBApiClient';
|
|
|
106
106
|
export * from './dlob/types';
|
|
107
107
|
export * from './dlob/orderBookLevels';
|
|
108
108
|
export * from './userMap/userMap';
|
|
109
|
+
export * from './userMap/referrerMap';
|
|
109
110
|
export * from './userMap/userStatsMap';
|
|
110
111
|
export * from './userMap/userMapConfig';
|
|
111
112
|
export * from './math/bankruptcy';
|
package/lib/node/index.js
CHANGED
|
@@ -129,6 +129,7 @@ __exportStar(require("./dlob/DLOBApiClient"), exports);
|
|
|
129
129
|
__exportStar(require("./dlob/types"), exports);
|
|
130
130
|
__exportStar(require("./dlob/orderBookLevels"), exports);
|
|
131
131
|
__exportStar(require("./userMap/userMap"), exports);
|
|
132
|
+
__exportStar(require("./userMap/referrerMap"), exports);
|
|
132
133
|
__exportStar(require("./userMap/userStatsMap"), exports);
|
|
133
134
|
__exportStar(require("./userMap/userMapConfig"), exports);
|
|
134
135
|
__exportStar(require("./math/bankruptcy"), exports);
|
package/lib/node/memcmp.d.ts
CHANGED
|
@@ -5,3 +5,6 @@ export declare function getUserWithOrderFilter(): MemcmpFilter;
|
|
|
5
5
|
export declare function getUserWithAuctionFilter(): MemcmpFilter;
|
|
6
6
|
export declare function getUserThatHasBeenLP(): MemcmpFilter;
|
|
7
7
|
export declare function getUserWithName(name: string): MemcmpFilter;
|
|
8
|
+
export declare function getUserStatsFilter(): MemcmpFilter;
|
|
9
|
+
export declare function getUserStatsIsReferredFilter(): MemcmpFilter;
|
|
10
|
+
export declare function getUserStatsIsReferredOrReferrerFilter(): MemcmpFilter;
|
package/lib/node/memcmp.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getUserWithName = exports.getUserThatHasBeenLP = exports.getUserWithAuctionFilter = exports.getUserWithOrderFilter = exports.getNonIdleUserFilter = exports.getUserFilter = void 0;
|
|
6
|
+
exports.getUserStatsIsReferredOrReferrerFilter = exports.getUserStatsIsReferredFilter = exports.getUserStatsFilter = exports.getUserWithName = exports.getUserThatHasBeenLP = exports.getUserWithAuctionFilter = exports.getUserWithOrderFilter = exports.getNonIdleUserFilter = exports.getUserFilter = void 0;
|
|
7
7
|
const bs58_1 = __importDefault(require("bs58"));
|
|
8
8
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
9
9
|
const userName_1 = require("./userName");
|
|
@@ -61,3 +61,30 @@ function getUserWithName(name) {
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
exports.getUserWithName = getUserWithName;
|
|
64
|
+
function getUserStatsFilter() {
|
|
65
|
+
return {
|
|
66
|
+
memcmp: {
|
|
67
|
+
offset: 0,
|
|
68
|
+
bytes: bs58_1.default.encode(anchor_1.BorshAccountsCoder.accountDiscriminator('UserStats')),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
exports.getUserStatsFilter = getUserStatsFilter;
|
|
73
|
+
function getUserStatsIsReferredFilter() {
|
|
74
|
+
return {
|
|
75
|
+
memcmp: {
|
|
76
|
+
offset: 188,
|
|
77
|
+
bytes: bs58_1.default.encode(Buffer.from(Uint8Array.from([2]))),
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
exports.getUserStatsIsReferredFilter = getUserStatsIsReferredFilter;
|
|
82
|
+
function getUserStatsIsReferredOrReferrerFilter() {
|
|
83
|
+
return {
|
|
84
|
+
memcmp: {
|
|
85
|
+
offset: 188,
|
|
86
|
+
bytes: bs58_1.default.encode(Buffer.from(Uint8Array.from([3]))),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
exports.getUserStatsIsReferredOrReferrerFilter = getUserStatsIsReferredOrReferrerFilter;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { MemcmpFilter } from '@solana/web3.js';
|
|
2
|
+
import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
|
|
3
|
+
import { DriftClient } from '../driftClient';
|
|
4
|
+
import { ReferrerInfo } from '../types';
|
|
5
|
+
export declare class ReferrerMap {
|
|
6
|
+
/**
|
|
7
|
+
* map from authority pubkey to ReferrerInfo.
|
|
8
|
+
* - if a user has not been entered into the map, the value is undefined
|
|
9
|
+
* - if a user has no referrer, the value is null
|
|
10
|
+
* - if a user has a referrer, the value is a ReferrerInfo object
|
|
11
|
+
*/
|
|
12
|
+
private referrerMap;
|
|
13
|
+
private driftClient;
|
|
14
|
+
private bulkAccountLoader;
|
|
15
|
+
private parallelSync;
|
|
16
|
+
private fetchPromise?;
|
|
17
|
+
private fetchPromiseResolver;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new UserStatsMap instance.
|
|
20
|
+
*
|
|
21
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
22
|
+
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
23
|
+
*/
|
|
24
|
+
constructor(driftClient: DriftClient, bulkAccountLoader?: BulkAccountLoader, parallelSync?: boolean);
|
|
25
|
+
/**
|
|
26
|
+
* Subscribe to all UserStats accounts.
|
|
27
|
+
*/
|
|
28
|
+
subscribe(): Promise<void>;
|
|
29
|
+
has(authorityPublicKey: string): boolean;
|
|
30
|
+
get(authorityPublicKey: string): ReferrerInfo | undefined | null;
|
|
31
|
+
addReferrerInfo(authority: string, referrerInfo?: ReferrerInfo | null): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Enforce that a UserStats will exist for the given authorityPublicKey,
|
|
34
|
+
* reading one from the blockchain if necessary.
|
|
35
|
+
* @param authorityPublicKey
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
38
|
+
mustGet(authorityPublicKey: string): Promise<ReferrerInfo | null | undefined>;
|
|
39
|
+
values(): IterableIterator<ReferrerInfo | null>;
|
|
40
|
+
size(): number;
|
|
41
|
+
sync(): Promise<void>;
|
|
42
|
+
syncAll(): Promise<void>;
|
|
43
|
+
syncReferrer(referrerFilter: MemcmpFilter): Promise<void>;
|
|
44
|
+
unsubscribe(): Promise<void>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReferrerMap = void 0;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const bulkAccountLoader_1 = require("../accounts/bulkAccountLoader");
|
|
6
|
+
const userStats_1 = require("../userStats");
|
|
7
|
+
const pda_1 = require("../addresses/pda");
|
|
8
|
+
const memcmp_1 = require("../memcmp");
|
|
9
|
+
const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes");
|
|
10
|
+
const DEFAULT_PUBLIC_KEY = web3_js_1.PublicKey.default.toBase58();
|
|
11
|
+
class ReferrerMap {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new UserStatsMap instance.
|
|
14
|
+
*
|
|
15
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
16
|
+
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
17
|
+
*/
|
|
18
|
+
constructor(driftClient, bulkAccountLoader, parallelSync) {
|
|
19
|
+
/**
|
|
20
|
+
* map from authority pubkey to ReferrerInfo.
|
|
21
|
+
* - if a user has not been entered into the map, the value is undefined
|
|
22
|
+
* - if a user has no referrer, the value is null
|
|
23
|
+
* - if a user has a referrer, the value is a ReferrerInfo object
|
|
24
|
+
*/
|
|
25
|
+
this.referrerMap = new Map();
|
|
26
|
+
this.driftClient = driftClient;
|
|
27
|
+
if (!bulkAccountLoader) {
|
|
28
|
+
bulkAccountLoader = new bulkAccountLoader_1.BulkAccountLoader(driftClient.connection, driftClient.opts.commitment, 0);
|
|
29
|
+
}
|
|
30
|
+
this.bulkAccountLoader = bulkAccountLoader;
|
|
31
|
+
this.parallelSync = parallelSync !== undefined ? parallelSync : true;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Subscribe to all UserStats accounts.
|
|
35
|
+
*/
|
|
36
|
+
async subscribe() {
|
|
37
|
+
if (this.size() > 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
await this.driftClient.subscribe();
|
|
41
|
+
await this.sync();
|
|
42
|
+
}
|
|
43
|
+
has(authorityPublicKey) {
|
|
44
|
+
return this.referrerMap.has(authorityPublicKey);
|
|
45
|
+
}
|
|
46
|
+
get(authorityPublicKey) {
|
|
47
|
+
return this.referrerMap.get(authorityPublicKey);
|
|
48
|
+
}
|
|
49
|
+
async addReferrerInfo(authority, referrerInfo) {
|
|
50
|
+
if (referrerInfo || referrerInfo === null) {
|
|
51
|
+
this.referrerMap.set(authority, referrerInfo);
|
|
52
|
+
}
|
|
53
|
+
else if (referrerInfo === undefined) {
|
|
54
|
+
const userStat = new userStats_1.UserStats({
|
|
55
|
+
driftClient: this.driftClient,
|
|
56
|
+
userStatsAccountPublicKey: (0, pda_1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, new web3_js_1.PublicKey(authority)),
|
|
57
|
+
accountSubscription: {
|
|
58
|
+
type: 'polling',
|
|
59
|
+
accountLoader: this.bulkAccountLoader,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
await userStat.fetchAccounts();
|
|
63
|
+
const newReferrerInfo = userStat.getReferrerInfo();
|
|
64
|
+
if (newReferrerInfo) {
|
|
65
|
+
this.referrerMap.set(authority.toString(), newReferrerInfo);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.referrerMap.set(authority.toString(), null);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Enforce that a UserStats will exist for the given authorityPublicKey,
|
|
74
|
+
* reading one from the blockchain if necessary.
|
|
75
|
+
* @param authorityPublicKey
|
|
76
|
+
* @returns
|
|
77
|
+
*/
|
|
78
|
+
async mustGet(authorityPublicKey) {
|
|
79
|
+
if (!this.has(authorityPublicKey)) {
|
|
80
|
+
await this.addReferrerInfo(authorityPublicKey);
|
|
81
|
+
}
|
|
82
|
+
return this.get(authorityPublicKey);
|
|
83
|
+
}
|
|
84
|
+
values() {
|
|
85
|
+
return this.referrerMap.values();
|
|
86
|
+
}
|
|
87
|
+
size() {
|
|
88
|
+
return this.referrerMap.size;
|
|
89
|
+
}
|
|
90
|
+
async sync() {
|
|
91
|
+
if (this.fetchPromise) {
|
|
92
|
+
return this.fetchPromise;
|
|
93
|
+
}
|
|
94
|
+
this.fetchPromise = new Promise((resolver) => {
|
|
95
|
+
this.fetchPromiseResolver = resolver;
|
|
96
|
+
});
|
|
97
|
+
try {
|
|
98
|
+
if (this.parallelSync) {
|
|
99
|
+
await Promise.all([
|
|
100
|
+
this.syncAll(),
|
|
101
|
+
this.syncReferrer((0, memcmp_1.getUserStatsIsReferredFilter)()),
|
|
102
|
+
this.syncReferrer((0, memcmp_1.getUserStatsIsReferredOrReferrerFilter)()),
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
await this.syncAll();
|
|
107
|
+
await this.syncReferrer((0, memcmp_1.getUserStatsIsReferredFilter)());
|
|
108
|
+
await this.syncReferrer((0, memcmp_1.getUserStatsIsReferredOrReferrerFilter)());
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
console.error('error in referrerMap.sync', e);
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
this.fetchPromiseResolver();
|
|
116
|
+
this.fetchPromise = undefined;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async syncAll() {
|
|
120
|
+
const rpcRequestArgs = [
|
|
121
|
+
this.driftClient.program.programId.toBase58(),
|
|
122
|
+
{
|
|
123
|
+
commitment: this.driftClient.opts.commitment,
|
|
124
|
+
filters: [(0, memcmp_1.getUserStatsFilter)()],
|
|
125
|
+
encoding: 'base64',
|
|
126
|
+
dataSlice: {
|
|
127
|
+
offset: 0,
|
|
128
|
+
length: 0,
|
|
129
|
+
},
|
|
130
|
+
withContext: true,
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
const rpcJSONResponse =
|
|
134
|
+
// @ts-ignore
|
|
135
|
+
await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
136
|
+
const rpcResponseAndContext = rpcJSONResponse.result;
|
|
137
|
+
for (const account of rpcResponseAndContext.value) {
|
|
138
|
+
// only add if it isn't already in the map
|
|
139
|
+
// so that if syncReferrer already set it, we dont overwrite
|
|
140
|
+
if (!this.has(account.pubkey)) {
|
|
141
|
+
this.addReferrerInfo(account.pubkey, null);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async syncReferrer(referrerFilter) {
|
|
146
|
+
const rpcRequestArgs = [
|
|
147
|
+
this.driftClient.program.programId.toBase58(),
|
|
148
|
+
{
|
|
149
|
+
commitment: this.driftClient.opts.commitment,
|
|
150
|
+
filters: [(0, memcmp_1.getUserStatsFilter)(), referrerFilter],
|
|
151
|
+
encoding: 'base64',
|
|
152
|
+
dataSlice: {
|
|
153
|
+
offset: 0,
|
|
154
|
+
length: 72,
|
|
155
|
+
},
|
|
156
|
+
withContext: true,
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
const rpcJSONResponse =
|
|
160
|
+
// @ts-ignore
|
|
161
|
+
await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
162
|
+
const rpcResponseAndContext = rpcJSONResponse.result;
|
|
163
|
+
const batchSize = 1000;
|
|
164
|
+
for (let i = 0; i < rpcResponseAndContext.value.length; i += batchSize) {
|
|
165
|
+
const batch = rpcResponseAndContext.value.slice(i, i + batchSize);
|
|
166
|
+
await Promise.all(batch.map(async (programAccount) => {
|
|
167
|
+
// @ts-ignore
|
|
168
|
+
const buffer = Buffer.from(programAccount.account.data[0], programAccount.account.data[1]);
|
|
169
|
+
const authority = bytes_1.bs58.encode(buffer.subarray(8, 40));
|
|
170
|
+
const referrer = bytes_1.bs58.encode(buffer.subarray(40, 72));
|
|
171
|
+
const referrerKey = new web3_js_1.PublicKey(referrer);
|
|
172
|
+
this.addReferrerInfo(authority, referrer === DEFAULT_PUBLIC_KEY
|
|
173
|
+
? null
|
|
174
|
+
: {
|
|
175
|
+
referrer: (0, pda_1.getUserAccountPublicKeySync)(this.driftClient.program.programId, referrerKey, 0),
|
|
176
|
+
referrerStats: (0, pda_1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, referrerKey),
|
|
177
|
+
});
|
|
178
|
+
}));
|
|
179
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async unsubscribe() {
|
|
183
|
+
this.referrerMap.clear();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.ReferrerMap = ReferrerMap;
|
package/package.json
CHANGED
package/src/driftClient.ts
CHANGED
|
@@ -11,11 +11,13 @@ import bs58 from 'bs58';
|
|
|
11
11
|
import {
|
|
12
12
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
13
13
|
createAssociatedTokenAccountInstruction,
|
|
14
|
+
createAssociatedTokenAccountIdempotentInstruction,
|
|
14
15
|
createCloseAccountInstruction,
|
|
15
16
|
createInitializeAccountInstruction,
|
|
16
17
|
getAssociatedTokenAddress,
|
|
17
18
|
TOKEN_2022_PROGRAM_ID,
|
|
18
19
|
TOKEN_PROGRAM_ID,
|
|
20
|
+
getAssociatedTokenAddressSync,
|
|
19
21
|
} from '@solana/spl-token';
|
|
20
22
|
import {
|
|
21
23
|
DriftClientMetricsEvents,
|
|
@@ -7407,6 +7409,22 @@ export class DriftClient {
|
|
|
7407
7409
|
const createWSOLTokenAccount =
|
|
7408
7410
|
isSolMarket && collateralAccountPublicKey.equals(this.wallet.publicKey);
|
|
7409
7411
|
|
|
7412
|
+
// create associated token account because it may not exist
|
|
7413
|
+
const associatedTokenAccountPublicKey = getAssociatedTokenAddressSync(
|
|
7414
|
+
spotMarketAccount.mint,
|
|
7415
|
+
this.wallet.publicKey,
|
|
7416
|
+
true
|
|
7417
|
+
);
|
|
7418
|
+
|
|
7419
|
+
addIfStakeIxs.push(
|
|
7420
|
+
await createAssociatedTokenAccountIdempotentInstruction(
|
|
7421
|
+
this.wallet.publicKey,
|
|
7422
|
+
associatedTokenAccountPublicKey,
|
|
7423
|
+
this.wallet.publicKey,
|
|
7424
|
+
spotMarketAccount.mint
|
|
7425
|
+
)
|
|
7426
|
+
);
|
|
7427
|
+
|
|
7410
7428
|
let tokenAccount;
|
|
7411
7429
|
|
|
7412
7430
|
if (
|
package/src/index.ts
CHANGED
|
@@ -107,6 +107,7 @@ export * from './dlob/DLOBApiClient';
|
|
|
107
107
|
export * from './dlob/types';
|
|
108
108
|
export * from './dlob/orderBookLevels';
|
|
109
109
|
export * from './userMap/userMap';
|
|
110
|
+
export * from './userMap/referrerMap';
|
|
110
111
|
export * from './userMap/userStatsMap';
|
|
111
112
|
export * from './userMap/userMapConfig';
|
|
112
113
|
export * from './math/bankruptcy';
|
package/src/memcmp.ts
CHANGED
|
@@ -56,3 +56,30 @@ export function getUserWithName(name: string): MemcmpFilter {
|
|
|
56
56
|
},
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
export function getUserStatsFilter(): MemcmpFilter {
|
|
61
|
+
return {
|
|
62
|
+
memcmp: {
|
|
63
|
+
offset: 0,
|
|
64
|
+
bytes: bs58.encode(BorshAccountsCoder.accountDiscriminator('UserStats')),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getUserStatsIsReferredFilter(): MemcmpFilter {
|
|
70
|
+
return {
|
|
71
|
+
memcmp: {
|
|
72
|
+
offset: 188,
|
|
73
|
+
bytes: bs58.encode(Buffer.from(Uint8Array.from([2]))),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getUserStatsIsReferredOrReferrerFilter(): MemcmpFilter {
|
|
79
|
+
return {
|
|
80
|
+
memcmp: {
|
|
81
|
+
offset: 188,
|
|
82
|
+
bytes: bs58.encode(Buffer.from(Uint8Array.from([3]))),
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MemcmpFilter,
|
|
3
|
+
PublicKey,
|
|
4
|
+
RpcResponseAndContext,
|
|
5
|
+
} from '@solana/web3.js';
|
|
6
|
+
import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
|
|
7
|
+
import { DriftClient } from '../driftClient';
|
|
8
|
+
import { ReferrerInfo } from '../types';
|
|
9
|
+
import { UserStats } from '../userStats';
|
|
10
|
+
import {
|
|
11
|
+
getUserAccountPublicKeySync,
|
|
12
|
+
getUserStatsAccountPublicKey,
|
|
13
|
+
} from '../addresses/pda';
|
|
14
|
+
import {
|
|
15
|
+
getUserStatsFilter,
|
|
16
|
+
getUserStatsIsReferredFilter,
|
|
17
|
+
getUserStatsIsReferredOrReferrerFilter,
|
|
18
|
+
} from '../memcmp';
|
|
19
|
+
import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes';
|
|
20
|
+
|
|
21
|
+
const DEFAULT_PUBLIC_KEY = PublicKey.default.toBase58();
|
|
22
|
+
|
|
23
|
+
export class ReferrerMap {
|
|
24
|
+
/**
|
|
25
|
+
* map from authority pubkey to ReferrerInfo.
|
|
26
|
+
* - if a user has not been entered into the map, the value is undefined
|
|
27
|
+
* - if a user has no referrer, the value is null
|
|
28
|
+
* - if a user has a referrer, the value is a ReferrerInfo object
|
|
29
|
+
*/
|
|
30
|
+
private referrerMap = new Map<string, ReferrerInfo | null>();
|
|
31
|
+
private driftClient: DriftClient;
|
|
32
|
+
private bulkAccountLoader: BulkAccountLoader;
|
|
33
|
+
private parallelSync: boolean;
|
|
34
|
+
|
|
35
|
+
private fetchPromise?: Promise<void>;
|
|
36
|
+
private fetchPromiseResolver: () => void;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new UserStatsMap instance.
|
|
40
|
+
*
|
|
41
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
42
|
+
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
43
|
+
*/
|
|
44
|
+
constructor(
|
|
45
|
+
driftClient: DriftClient,
|
|
46
|
+
bulkAccountLoader?: BulkAccountLoader,
|
|
47
|
+
parallelSync?: boolean
|
|
48
|
+
) {
|
|
49
|
+
this.driftClient = driftClient;
|
|
50
|
+
if (!bulkAccountLoader) {
|
|
51
|
+
bulkAccountLoader = new BulkAccountLoader(
|
|
52
|
+
driftClient.connection,
|
|
53
|
+
driftClient.opts.commitment,
|
|
54
|
+
0
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
this.bulkAccountLoader = bulkAccountLoader;
|
|
58
|
+
this.parallelSync = parallelSync !== undefined ? parallelSync : true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Subscribe to all UserStats accounts.
|
|
63
|
+
*/
|
|
64
|
+
public async subscribe() {
|
|
65
|
+
if (this.size() > 0) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await this.driftClient.subscribe();
|
|
70
|
+
await this.sync();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public has(authorityPublicKey: string): boolean {
|
|
74
|
+
return this.referrerMap.has(authorityPublicKey);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public get(authorityPublicKey: string): ReferrerInfo | undefined | null {
|
|
78
|
+
return this.referrerMap.get(authorityPublicKey);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public async addReferrerInfo(
|
|
82
|
+
authority: string,
|
|
83
|
+
referrerInfo?: ReferrerInfo | null
|
|
84
|
+
) {
|
|
85
|
+
if (referrerInfo || referrerInfo === null) {
|
|
86
|
+
this.referrerMap.set(authority, referrerInfo);
|
|
87
|
+
} else if (referrerInfo === undefined) {
|
|
88
|
+
const userStat = new UserStats({
|
|
89
|
+
driftClient: this.driftClient,
|
|
90
|
+
userStatsAccountPublicKey: getUserStatsAccountPublicKey(
|
|
91
|
+
this.driftClient.program.programId,
|
|
92
|
+
new PublicKey(authority)
|
|
93
|
+
),
|
|
94
|
+
accountSubscription: {
|
|
95
|
+
type: 'polling',
|
|
96
|
+
accountLoader: this.bulkAccountLoader,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
await userStat.fetchAccounts();
|
|
100
|
+
|
|
101
|
+
const newReferrerInfo = userStat.getReferrerInfo();
|
|
102
|
+
|
|
103
|
+
if (newReferrerInfo) {
|
|
104
|
+
this.referrerMap.set(authority.toString(), newReferrerInfo);
|
|
105
|
+
} else {
|
|
106
|
+
this.referrerMap.set(authority.toString(), null);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Enforce that a UserStats will exist for the given authorityPublicKey,
|
|
113
|
+
* reading one from the blockchain if necessary.
|
|
114
|
+
* @param authorityPublicKey
|
|
115
|
+
* @returns
|
|
116
|
+
*/
|
|
117
|
+
public async mustGet(
|
|
118
|
+
authorityPublicKey: string
|
|
119
|
+
): Promise<ReferrerInfo | null | undefined> {
|
|
120
|
+
if (!this.has(authorityPublicKey)) {
|
|
121
|
+
await this.addReferrerInfo(authorityPublicKey);
|
|
122
|
+
}
|
|
123
|
+
return this.get(authorityPublicKey);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public values(): IterableIterator<ReferrerInfo | null> {
|
|
127
|
+
return this.referrerMap.values();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public size(): number {
|
|
131
|
+
return this.referrerMap.size;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public async sync(): Promise<void> {
|
|
135
|
+
if (this.fetchPromise) {
|
|
136
|
+
return this.fetchPromise;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.fetchPromise = new Promise((resolver) => {
|
|
140
|
+
this.fetchPromiseResolver = resolver;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
if (this.parallelSync) {
|
|
145
|
+
await Promise.all([
|
|
146
|
+
this.syncAll(),
|
|
147
|
+
this.syncReferrer(getUserStatsIsReferredFilter()),
|
|
148
|
+
this.syncReferrer(getUserStatsIsReferredOrReferrerFilter()),
|
|
149
|
+
]);
|
|
150
|
+
} else {
|
|
151
|
+
await this.syncAll();
|
|
152
|
+
await this.syncReferrer(getUserStatsIsReferredFilter());
|
|
153
|
+
await this.syncReferrer(getUserStatsIsReferredOrReferrerFilter());
|
|
154
|
+
}
|
|
155
|
+
} catch (e) {
|
|
156
|
+
console.error('error in referrerMap.sync', e);
|
|
157
|
+
} finally {
|
|
158
|
+
this.fetchPromiseResolver();
|
|
159
|
+
this.fetchPromise = undefined;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public async syncAll(): Promise<void> {
|
|
164
|
+
const rpcRequestArgs = [
|
|
165
|
+
this.driftClient.program.programId.toBase58(),
|
|
166
|
+
{
|
|
167
|
+
commitment: this.driftClient.opts.commitment,
|
|
168
|
+
filters: [getUserStatsFilter()],
|
|
169
|
+
encoding: 'base64',
|
|
170
|
+
dataSlice: {
|
|
171
|
+
offset: 0,
|
|
172
|
+
length: 0,
|
|
173
|
+
},
|
|
174
|
+
withContext: true,
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
const rpcJSONResponse: any =
|
|
179
|
+
// @ts-ignore
|
|
180
|
+
await this.driftClient.connection._rpcRequest(
|
|
181
|
+
'getProgramAccounts',
|
|
182
|
+
rpcRequestArgs
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const rpcResponseAndContext: RpcResponseAndContext<
|
|
186
|
+
Array<{
|
|
187
|
+
pubkey: string;
|
|
188
|
+
account: {
|
|
189
|
+
data: [string, string];
|
|
190
|
+
};
|
|
191
|
+
}>
|
|
192
|
+
> = rpcJSONResponse.result;
|
|
193
|
+
|
|
194
|
+
for (const account of rpcResponseAndContext.value) {
|
|
195
|
+
// only add if it isn't already in the map
|
|
196
|
+
// so that if syncReferrer already set it, we dont overwrite
|
|
197
|
+
if (!this.has(account.pubkey)) {
|
|
198
|
+
this.addReferrerInfo(account.pubkey, null);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async syncReferrer(referrerFilter: MemcmpFilter): Promise<void> {
|
|
204
|
+
const rpcRequestArgs = [
|
|
205
|
+
this.driftClient.program.programId.toBase58(),
|
|
206
|
+
{
|
|
207
|
+
commitment: this.driftClient.opts.commitment,
|
|
208
|
+
filters: [getUserStatsFilter(), referrerFilter],
|
|
209
|
+
encoding: 'base64',
|
|
210
|
+
dataSlice: {
|
|
211
|
+
offset: 0,
|
|
212
|
+
length: 72,
|
|
213
|
+
},
|
|
214
|
+
withContext: true,
|
|
215
|
+
},
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
const rpcJSONResponse: any =
|
|
219
|
+
// @ts-ignore
|
|
220
|
+
await this.driftClient.connection._rpcRequest(
|
|
221
|
+
'getProgramAccounts',
|
|
222
|
+
rpcRequestArgs
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const rpcResponseAndContext: RpcResponseAndContext<
|
|
226
|
+
Array<{
|
|
227
|
+
pubkey: string;
|
|
228
|
+
account: {
|
|
229
|
+
data: [string, string];
|
|
230
|
+
};
|
|
231
|
+
}>
|
|
232
|
+
> = rpcJSONResponse.result;
|
|
233
|
+
|
|
234
|
+
const batchSize = 1000;
|
|
235
|
+
for (let i = 0; i < rpcResponseAndContext.value.length; i += batchSize) {
|
|
236
|
+
const batch = rpcResponseAndContext.value.slice(i, i + batchSize);
|
|
237
|
+
await Promise.all(
|
|
238
|
+
batch.map(async (programAccount) => {
|
|
239
|
+
// @ts-ignore
|
|
240
|
+
const buffer = Buffer.from(
|
|
241
|
+
programAccount.account.data[0],
|
|
242
|
+
programAccount.account.data[1]
|
|
243
|
+
);
|
|
244
|
+
const authority = bs58.encode(buffer.subarray(8, 40));
|
|
245
|
+
const referrer = bs58.encode(buffer.subarray(40, 72));
|
|
246
|
+
|
|
247
|
+
const referrerKey = new PublicKey(referrer);
|
|
248
|
+
this.addReferrerInfo(
|
|
249
|
+
authority,
|
|
250
|
+
referrer === DEFAULT_PUBLIC_KEY
|
|
251
|
+
? null
|
|
252
|
+
: {
|
|
253
|
+
referrer: getUserAccountPublicKeySync(
|
|
254
|
+
this.driftClient.program.programId,
|
|
255
|
+
referrerKey,
|
|
256
|
+
0
|
|
257
|
+
),
|
|
258
|
+
referrerStats: getUserStatsAccountPublicKey(
|
|
259
|
+
this.driftClient.program.programId,
|
|
260
|
+
referrerKey
|
|
261
|
+
),
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
})
|
|
265
|
+
);
|
|
266
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
public async unsubscribe() {
|
|
271
|
+
this.referrerMap.clear();
|
|
272
|
+
}
|
|
273
|
+
}
|