/
GpaBuilder.ts
120 lines (93 loc) · 3.18 KB
/
GpaBuilder.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { Buffer } from 'buffer';
import {
GetProgramAccountsConfig,
GetProgramAccountsFilter,
PublicKey,
} from '@solana/web3.js';
import base58 from 'bs58';
import BN from 'bn.js';
import { GmaBuilder, GmaBuilderOptions } from './GmaBuilder';
import { Metaplex } from '@/Metaplex';
import { UnparsedAccount } from '@/types';
export type GpaSortCallback = (
a: UnparsedAccount,
b: UnparsedAccount
) => number;
export class GpaBuilder {
/** The connection instance to use when fetching accounts. */
protected readonly metaplex: Metaplex;
/** The public key of the program we want to retrieve accounts from. */
protected readonly programId: PublicKey;
/** The configs to use when fetching program accounts. */
protected config: GetProgramAccountsConfig = {};
/** When provided, reorder accounts using this callback. */
protected sortCallback?: GpaSortCallback;
constructor(metaplex: Metaplex, programId: PublicKey) {
this.metaplex = metaplex;
this.programId = programId;
}
mergeConfig(config: GetProgramAccountsConfig) {
this.config = { ...this.config, ...config };
return this;
}
slice(offset: number, length: number) {
this.config.dataSlice = { offset, length };
return this;
}
withoutData() {
return this.slice(0, 0);
}
addFilter(...filters: GetProgramAccountsFilter[]) {
if (!this.config.filters) {
this.config.filters = [];
}
this.config.filters.push(...filters);
return this;
}
where(offset: number, bytes: string | Buffer | PublicKey | BN | number) {
if (Buffer.isBuffer(bytes)) {
bytes = base58.encode(bytes);
} else if (typeof bytes === 'object' && 'toBase58' in bytes) {
bytes = bytes.toBase58();
} else if (BN.isBN(bytes)) {
bytes = base58.encode(bytes.toArray());
} else if (typeof bytes !== 'string') {
bytes = base58.encode(new BN(bytes, 'le').toArray());
}
return this.addFilter({ memcmp: { offset, bytes } });
}
whereSize(dataSize: number) {
return this.addFilter({ dataSize });
}
sortUsing(callback: GpaSortCallback) {
this.sortCallback = callback;
return this;
}
async get(): Promise<UnparsedAccount[]> {
const accounts = await this.metaplex
.rpc()
.getProgramAccounts(this.programId, this.config);
if (this.sortCallback) {
accounts.sort(this.sortCallback);
}
return accounts;
}
async getAndMap<T>(callback: (account: UnparsedAccount) => T): Promise<T[]> {
return (await this.get()).map(callback);
}
async getPublicKeys(): Promise<PublicKey[]> {
return this.getAndMap((account) => account.publicKey);
}
async getDataAsPublicKeys(): Promise<PublicKey[]> {
// TODO(loris): Throw a custom Metaplex error if the data is not a public key.
return this.getAndMap((account) => new PublicKey(account.data));
}
async getMultipleAccounts(
callback?: (account: UnparsedAccount) => PublicKey,
options?: GmaBuilderOptions
): Promise<GmaBuilder> {
// TODO(loris): Throw a custom Metaplex error if the data is not a public key.
const cb = callback ?? ((account) => new PublicKey(account.data));
return new GmaBuilder(this.metaplex, await this.getAndMap(cb), options);
}
}