solforge 0.2.12 → 0.2.14
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/package.json +1 -5
- package/start.cjs +19 -23
- package/docs/API.md +0 -379
- package/docs/CONFIGURATION.md +0 -407
- package/docs/bun-single-file-executable.md +0 -585
- package/docs/cli-plan.md +0 -154
- package/docs/data-indexing-plan.md +0 -214
- package/docs/gui-roadmap.md +0 -202
- package/scripts/decode-b58.ts +0 -10
- package/scripts/install.sh +0 -112
- package/server/index.ts +0 -5
- package/server/lib/base58.ts +0 -33
- package/server/lib/faucet.ts +0 -110
- package/server/lib/instruction-parser.ts +0 -328
- package/server/lib/parsers/spl-associated-token-account.ts +0 -50
- package/server/lib/parsers/spl-token.ts +0 -340
- package/server/lib/spl-token.ts +0 -57
- package/server/methods/TEMPLATE.md +0 -117
- package/server/methods/account/get-account-info.ts +0 -86
- package/server/methods/account/get-balance.ts +0 -23
- package/server/methods/account/get-multiple-accounts.ts +0 -84
- package/server/methods/account/get-parsed-account-info.ts +0 -17
- package/server/methods/account/index.ts +0 -12
- package/server/methods/account/parsers/index.ts +0 -52
- package/server/methods/account/parsers/loader-upgradeable.ts +0 -79
- package/server/methods/account/parsers/spl-token.ts +0 -256
- package/server/methods/account/parsers/system.ts +0 -4
- package/server/methods/account/request-airdrop.ts +0 -271
- package/server/methods/admin/adopt-mint-authority.ts +0 -94
- package/server/methods/admin/clone-program-accounts.ts +0 -55
- package/server/methods/admin/clone-program.ts +0 -152
- package/server/methods/admin/clone-token-accounts.ts +0 -117
- package/server/methods/admin/clone-token-mint.ts +0 -82
- package/server/methods/admin/create-mint.ts +0 -114
- package/server/methods/admin/create-token-account.ts +0 -137
- package/server/methods/admin/helpers.ts +0 -70
- package/server/methods/admin/index.ts +0 -10
- package/server/methods/admin/list-mints.ts +0 -21
- package/server/methods/admin/load-program.ts +0 -52
- package/server/methods/admin/mint-to.ts +0 -266
- package/server/methods/block/get-block-height.ts +0 -5
- package/server/methods/block/get-block.ts +0 -31
- package/server/methods/block/get-blocks-with-limit.ts +0 -19
- package/server/methods/block/get-latest-blockhash.ts +0 -12
- package/server/methods/block/get-slot.ts +0 -5
- package/server/methods/block/index.ts +0 -6
- package/server/methods/block/is-blockhash-valid.ts +0 -19
- package/server/methods/epoch/get-cluster-nodes.ts +0 -17
- package/server/methods/epoch/get-epoch-info.ts +0 -16
- package/server/methods/epoch/get-epoch-schedule.ts +0 -15
- package/server/methods/epoch/get-highest-snapshot-slot.ts +0 -9
- package/server/methods/epoch/get-leader-schedule.ts +0 -8
- package/server/methods/epoch/get-max-retransmit-slot.ts +0 -9
- package/server/methods/epoch/get-max-shred-insert-slot.ts +0 -9
- package/server/methods/epoch/get-slot-leader.ts +0 -6
- package/server/methods/epoch/get-slot-leaders.ts +0 -9
- package/server/methods/epoch/get-stake-activation.ts +0 -9
- package/server/methods/epoch/get-stake-minimum-delegation.ts +0 -9
- package/server/methods/epoch/get-vote-accounts.ts +0 -19
- package/server/methods/epoch/index.ts +0 -13
- package/server/methods/epoch/minimum-ledger-slot.ts +0 -5
- package/server/methods/fee/get-fee-calculator-for-blockhash.ts +0 -12
- package/server/methods/fee/get-fee-for-message.ts +0 -8
- package/server/methods/fee/get-fee-rate-governor.ts +0 -16
- package/server/methods/fee/get-fees.ts +0 -14
- package/server/methods/fee/get-recent-prioritization-fees.ts +0 -22
- package/server/methods/fee/index.ts +0 -5
- package/server/methods/get-address-lookup-table.ts +0 -27
- package/server/methods/index.ts +0 -265
- package/server/methods/performance/get-recent-performance-samples.ts +0 -25
- package/server/methods/performance/get-transaction-count.ts +0 -5
- package/server/methods/performance/index.ts +0 -2
- package/server/methods/program/get-block-commitment.ts +0 -9
- package/server/methods/program/get-block-production.ts +0 -14
- package/server/methods/program/get-block-time.ts +0 -21
- package/server/methods/program/get-blocks.ts +0 -11
- package/server/methods/program/get-first-available-block.ts +0 -9
- package/server/methods/program/get-genesis-hash.ts +0 -6
- package/server/methods/program/get-identity.ts +0 -6
- package/server/methods/program/get-inflation-governor.ts +0 -15
- package/server/methods/program/get-inflation-rate.ts +0 -10
- package/server/methods/program/get-inflation-reward.ts +0 -12
- package/server/methods/program/get-largest-accounts.ts +0 -8
- package/server/methods/program/get-parsed-program-accounts.ts +0 -12
- package/server/methods/program/get-parsed-token-accounts-by-delegate.ts +0 -12
- package/server/methods/program/get-parsed-token-accounts-by-owner.ts +0 -12
- package/server/methods/program/get-program-accounts.ts +0 -221
- package/server/methods/program/get-supply.ts +0 -13
- package/server/methods/program/get-token-account-balance.ts +0 -60
- package/server/methods/program/get-token-accounts-by-delegate.ts +0 -82
- package/server/methods/program/get-token-accounts-by-owner.ts +0 -416
- package/server/methods/program/get-token-largest-accounts.ts +0 -81
- package/server/methods/program/get-token-supply.ts +0 -39
- package/server/methods/program/index.ts +0 -21
- package/server/methods/solforge/index.ts +0 -158
- package/server/methods/system/get-health.ts +0 -5
- package/server/methods/system/get-minimum-balance-for-rent-exemption.ts +0 -13
- package/server/methods/system/get-version.ts +0 -9
- package/server/methods/system/index.ts +0 -3
- package/server/methods/transaction/get-confirmed-transaction.ts +0 -11
- package/server/methods/transaction/get-parsed-transaction.ts +0 -17
- package/server/methods/transaction/get-signature-statuses.ts +0 -79
- package/server/methods/transaction/get-signatures-for-address.ts +0 -41
- package/server/methods/transaction/get-transaction.ts +0 -639
- package/server/methods/transaction/index.ts +0 -7
- package/server/methods/transaction/inner-instructions.test.ts +0 -104
- package/server/methods/transaction/send-transaction.ts +0 -469
- package/server/methods/transaction/simulate-transaction.ts +0 -57
- package/server/rpc-server.ts +0 -521
- package/server/types.ts +0 -109
- package/server/ws-server.ts +0 -178
- package/src/api-server-entry.ts +0 -109
- package/src/cli/bootstrap.ts +0 -67
- package/src/cli/commands/airdrop.ts +0 -37
- package/src/cli/commands/config.ts +0 -39
- package/src/cli/commands/mint.ts +0 -187
- package/src/cli/commands/program-clone.ts +0 -122
- package/src/cli/commands/program-load.ts +0 -64
- package/src/cli/commands/rpc-start.ts +0 -49
- package/src/cli/commands/token-adopt-authority.ts +0 -37
- package/src/cli/commands/token-clone.ts +0 -112
- package/src/cli/commands/token-create.ts +0 -81
- package/src/cli/main.ts +0 -158
- package/src/cli/run-solforge.ts +0 -112
- package/src/cli/setup-utils.ts +0 -54
- package/src/cli/setup-wizard.ts +0 -258
- package/src/cli/utils/args.ts +0 -15
- package/src/commands/add-program.ts +0 -333
- package/src/commands/init.ts +0 -122
- package/src/commands/list.ts +0 -136
- package/src/commands/mint.ts +0 -287
- package/src/commands/start.ts +0 -881
- package/src/commands/status.ts +0 -99
- package/src/commands/stop.ts +0 -405
- package/src/config/index.ts +0 -146
- package/src/config/manager.ts +0 -157
- package/src/db/index.ts +0 -83
- package/src/db/schema/accounts.ts +0 -23
- package/src/db/schema/address-signatures.ts +0 -31
- package/src/db/schema/index.ts +0 -6
- package/src/db/schema/meta-kv.ts +0 -9
- package/src/db/schema/transactions.ts +0 -36
- package/src/db/schema/tx-account-states.ts +0 -23
- package/src/db/schema/tx-accounts.ts +0 -33
- package/src/db/tx-store.ts +0 -264
- package/src/gui/public/app.css +0 -1556
- package/src/gui/public/build/main.css +0 -1569
- package/src/gui/public/build/main.js +0 -303
- package/src/gui/public/build/main.js.txt +0 -231
- package/src/gui/public/index.html +0 -19
- package/src/gui/server.ts +0 -296
- package/src/gui/src/api.ts +0 -127
- package/src/gui/src/app.tsx +0 -441
- package/src/gui/src/components/airdrop-mint-form.tsx +0 -246
- package/src/gui/src/components/clone-program-modal.tsx +0 -202
- package/src/gui/src/components/clone-token-modal.tsx +0 -230
- package/src/gui/src/components/modal.tsx +0 -134
- package/src/gui/src/components/programs-panel.tsx +0 -124
- package/src/gui/src/components/status-panel.tsx +0 -136
- package/src/gui/src/components/tokens-panel.tsx +0 -122
- package/src/gui/src/hooks/use-interval.ts +0 -17
- package/src/gui/src/index.css +0 -557
- package/src/gui/src/main.tsx +0 -17
- package/src/index.ts +0 -216
- package/src/migrations-bundled.ts +0 -23
- package/src/rpc/start.ts +0 -44
- package/src/services/api-server.ts +0 -504
- package/src/services/port-manager.ts +0 -174
- package/src/services/process-registry.ts +0 -153
- package/src/services/program-cloner.ts +0 -317
- package/src/services/token-cloner.ts +0 -811
- package/src/services/validator.ts +0 -293
- package/src/types/config.ts +0 -110
- package/src/utils/shell.ts +0 -110
- package/src/utils/token-loader.ts +0 -115
|
@@ -1,416 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ACCOUNT_SIZE,
|
|
3
|
-
getAssociatedTokenAddressSync,
|
|
4
|
-
TOKEN_2022_PROGRAM_ID,
|
|
5
|
-
TOKEN_PROGRAM_ID,
|
|
6
|
-
unpackAccount,
|
|
7
|
-
unpackMint,
|
|
8
|
-
} from "@solana/spl-token";
|
|
9
|
-
import { type AccountInfo, PublicKey } from "@solana/web3.js";
|
|
10
|
-
import type { RpcMethodHandler } from "../../types";
|
|
11
|
-
|
|
12
|
-
export const getTokenAccountsByOwner: RpcMethodHandler = async (
|
|
13
|
-
id,
|
|
14
|
-
params,
|
|
15
|
-
context,
|
|
16
|
-
) => {
|
|
17
|
-
try {
|
|
18
|
-
const [ownerStr, filter, config] = params || [];
|
|
19
|
-
if (!ownerStr) throw new Error("owner public key required");
|
|
20
|
-
const owner = new PublicKey(ownerStr);
|
|
21
|
-
const wantMint: string | null = filter?.mint ? String(filter.mint) : null;
|
|
22
|
-
const requestedProgramId: string | null = filter?.programId
|
|
23
|
-
? String(filter.programId)
|
|
24
|
-
: null;
|
|
25
|
-
const encoding: string = config?.encoding || "jsonParsed";
|
|
26
|
-
const classicId = TOKEN_PROGRAM_ID.toBase58();
|
|
27
|
-
const token2022Id = TOKEN_2022_PROGRAM_ID.toBase58();
|
|
28
|
-
const programIds = requestedProgramId
|
|
29
|
-
? [requestedProgramId]
|
|
30
|
-
: [classicId, token2022Id];
|
|
31
|
-
|
|
32
|
-
// Query DB for accounts owned by both SPL Token programs (classic + 2022)
|
|
33
|
-
const rows: Array<{ address: string; lastSlot: number }> = [];
|
|
34
|
-
for (const programId of programIds) {
|
|
35
|
-
try {
|
|
36
|
-
const found =
|
|
37
|
-
(await context.store?.getAccountsByOwner(programId, 50_000)) || [];
|
|
38
|
-
rows.push(...found);
|
|
39
|
-
} catch (dbErr) {
|
|
40
|
-
try {
|
|
41
|
-
console.warn("[rpc] getTokenAccountsByOwner: db read failed", dbErr);
|
|
42
|
-
} catch {}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const out: unknown[] = [];
|
|
47
|
-
const seen = new Set<string>();
|
|
48
|
-
for (const r of rows) {
|
|
49
|
-
if (seen.has(r.address)) continue;
|
|
50
|
-
try {
|
|
51
|
-
const pk = new PublicKey(r.address);
|
|
52
|
-
const acc = context.svm.getAccount(pk);
|
|
53
|
-
if (!acc) continue;
|
|
54
|
-
if ((acc.data?.length ?? 0) < 165) continue;
|
|
55
|
-
let ownerPk: PublicKey;
|
|
56
|
-
try {
|
|
57
|
-
// acc.owner may already be a PublicKey in LiteSVM
|
|
58
|
-
const rawOwner = (acc as { owner?: unknown }).owner;
|
|
59
|
-
ownerPk =
|
|
60
|
-
rawOwner &&
|
|
61
|
-
typeof (rawOwner as { toBase58?: unknown }).toBase58 === "function"
|
|
62
|
-
? (rawOwner as PublicKey)
|
|
63
|
-
: new PublicKey(String(rawOwner));
|
|
64
|
-
} catch {
|
|
65
|
-
ownerPk = TOKEN_PROGRAM_ID; // fallback avoids throw; unpackAccount will fail if wrong and be skipped
|
|
66
|
-
}
|
|
67
|
-
const programPk = ownerPk.equals(TOKEN_2022_PROGRAM_ID)
|
|
68
|
-
? TOKEN_2022_PROGRAM_ID
|
|
69
|
-
: TOKEN_PROGRAM_ID;
|
|
70
|
-
const dec = unpackAccount(
|
|
71
|
-
pk,
|
|
72
|
-
{ data: Buffer.from(acc.data), owner: ownerPk },
|
|
73
|
-
programPk,
|
|
74
|
-
);
|
|
75
|
-
const decMint = dec.mint.toBase58();
|
|
76
|
-
const decOwner = dec.owner.toBase58();
|
|
77
|
-
if (decOwner !== owner.toBase58()) continue;
|
|
78
|
-
if (wantMint && decMint !== wantMint) continue;
|
|
79
|
-
if (encoding === "jsonParsed") {
|
|
80
|
-
let decimals = 0;
|
|
81
|
-
try {
|
|
82
|
-
const mintAcc = context.svm.getAccount(dec.mint);
|
|
83
|
-
const mintOwnerPk = mintAcc
|
|
84
|
-
? ((): PublicKey => {
|
|
85
|
-
const ro = (mintAcc as { owner?: unknown }).owner;
|
|
86
|
-
return ro &&
|
|
87
|
-
typeof (ro as { toBase58?: unknown }).toBase58 ===
|
|
88
|
-
"function"
|
|
89
|
-
? (ro as PublicKey)
|
|
90
|
-
: new PublicKey(String(ro));
|
|
91
|
-
})()
|
|
92
|
-
: programPk;
|
|
93
|
-
const info = mintAcc
|
|
94
|
-
? unpackMint(
|
|
95
|
-
dec.mint,
|
|
96
|
-
{
|
|
97
|
-
data: Buffer.from(mintAcc.data),
|
|
98
|
-
owner: mintOwnerPk,
|
|
99
|
-
} as AccountInfo<Buffer>,
|
|
100
|
-
mintOwnerPk,
|
|
101
|
-
)
|
|
102
|
-
: null;
|
|
103
|
-
decimals = info?.decimals ?? 0;
|
|
104
|
-
} catch {}
|
|
105
|
-
const amount = BigInt(dec.amount?.toString?.() ?? dec.amount ?? 0);
|
|
106
|
-
const ui = decimals >= 0 ? Number(amount) / 10 ** decimals : null;
|
|
107
|
-
const state = dec.isFrozen
|
|
108
|
-
? "frozen"
|
|
109
|
-
: dec.isInitialized
|
|
110
|
-
? "initialized"
|
|
111
|
-
: "uninitialized";
|
|
112
|
-
const amountUi = {
|
|
113
|
-
amount: amount.toString(),
|
|
114
|
-
decimals,
|
|
115
|
-
uiAmount: ui,
|
|
116
|
-
uiAmountString: (ui ?? 0).toString(),
|
|
117
|
-
};
|
|
118
|
-
// delegatedAmount as UiTokenAmount per RPC schema
|
|
119
|
-
const delegated = BigInt(
|
|
120
|
-
dec.delegatedAmount?.toString?.() ?? dec.delegatedAmount ?? 0n,
|
|
121
|
-
);
|
|
122
|
-
const delegatedUiAmount =
|
|
123
|
-
decimals >= 0 ? Number(delegated) / 10 ** decimals : null;
|
|
124
|
-
const delegatedAmount = {
|
|
125
|
-
amount: delegated.toString(),
|
|
126
|
-
decimals,
|
|
127
|
-
uiAmount: delegatedUiAmount,
|
|
128
|
-
uiAmountString: (delegatedUiAmount ?? 0).toString(),
|
|
129
|
-
};
|
|
130
|
-
// rentExemptReserve only for native (wrapped SOL) accounts; value in lamports (9 decimals)
|
|
131
|
-
let rentExemptReserve: {
|
|
132
|
-
amount: string;
|
|
133
|
-
decimals: number;
|
|
134
|
-
uiAmount: number | null;
|
|
135
|
-
uiAmountString: string;
|
|
136
|
-
} | null = null;
|
|
137
|
-
if (dec.isNative) {
|
|
138
|
-
const lamports = BigInt(
|
|
139
|
-
dec.rentExemptReserve?.toString?.() ??
|
|
140
|
-
dec.rentExemptReserve ??
|
|
141
|
-
0n,
|
|
142
|
-
);
|
|
143
|
-
const lamportsUi = Number(lamports) / 1_000_000_000;
|
|
144
|
-
rentExemptReserve = {
|
|
145
|
-
amount: lamports.toString(),
|
|
146
|
-
decimals: 9,
|
|
147
|
-
uiAmount: lamportsUi,
|
|
148
|
-
uiAmountString: lamportsUi.toString(),
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
const programLabel = programPk.equals(TOKEN_2022_PROGRAM_ID)
|
|
152
|
-
? "spl-token-2022"
|
|
153
|
-
: "spl-token";
|
|
154
|
-
out.push({
|
|
155
|
-
pubkey: r.address,
|
|
156
|
-
account: {
|
|
157
|
-
lamports: Number(acc.lamports || 0n),
|
|
158
|
-
owner: ownerPk.toBase58(),
|
|
159
|
-
executable: !!acc.executable,
|
|
160
|
-
rentEpoch: Number(acc.rentEpoch || 0),
|
|
161
|
-
data: {
|
|
162
|
-
program: programLabel,
|
|
163
|
-
parsed: {
|
|
164
|
-
type: "account",
|
|
165
|
-
info: {
|
|
166
|
-
mint: decMint,
|
|
167
|
-
owner: decOwner,
|
|
168
|
-
tokenAmount: amountUi,
|
|
169
|
-
state,
|
|
170
|
-
isNative: !!dec.isNative,
|
|
171
|
-
delegatedAmount: delegatedAmount,
|
|
172
|
-
delegate: dec.delegate ? dec.delegate.toBase58() : null,
|
|
173
|
-
rentExemptReserve,
|
|
174
|
-
closeAuthority: dec.closeAuthority
|
|
175
|
-
? dec.closeAuthority.toBase58()
|
|
176
|
-
: null,
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
space: acc.data?.length ?? 0,
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
seen.add(r.address);
|
|
184
|
-
} else {
|
|
185
|
-
out.push({
|
|
186
|
-
pubkey: r.address,
|
|
187
|
-
account: {
|
|
188
|
-
lamports: Number(acc.lamports || 0n),
|
|
189
|
-
owner: ownerPk.toBase58(),
|
|
190
|
-
executable: !!acc.executable,
|
|
191
|
-
rentEpoch: Number(acc.rentEpoch || 0),
|
|
192
|
-
data: [
|
|
193
|
-
Buffer.from(acc.data).toString("base64"),
|
|
194
|
-
"base64",
|
|
195
|
-
] as const,
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
seen.add(r.address);
|
|
199
|
-
}
|
|
200
|
-
} catch {}
|
|
201
|
-
}
|
|
202
|
-
// Fallback: probe known mints' ATAs for this owner
|
|
203
|
-
try {
|
|
204
|
-
const mints = context.listMints ? context.listMints() : [];
|
|
205
|
-
for (const m of mints) {
|
|
206
|
-
try {
|
|
207
|
-
const mintPk = new PublicKey(m);
|
|
208
|
-
const mintAcc = context.svm.getAccount(mintPk);
|
|
209
|
-
const mintOwnerPk = mintAcc
|
|
210
|
-
? ((): PublicKey => {
|
|
211
|
-
const ro = (mintAcc as { owner?: unknown }).owner;
|
|
212
|
-
return ro &&
|
|
213
|
-
typeof (ro as { toBase58?: unknown }).toBase58 === "function"
|
|
214
|
-
? (ro as PublicKey)
|
|
215
|
-
: new PublicKey(String(ro));
|
|
216
|
-
})()
|
|
217
|
-
: TOKEN_PROGRAM_ID;
|
|
218
|
-
// Determine which token program this mint belongs to
|
|
219
|
-
const programForMint = mintOwnerPk.equals(TOKEN_2022_PROGRAM_ID)
|
|
220
|
-
? TOKEN_2022_PROGRAM_ID
|
|
221
|
-
: TOKEN_PROGRAM_ID;
|
|
222
|
-
// If a specific programId filter was requested, honor it strictly
|
|
223
|
-
if (
|
|
224
|
-
requestedProgramId &&
|
|
225
|
-
programForMint.toBase58() !== requestedProgramId
|
|
226
|
-
) {
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const ata = getAssociatedTokenAddressSync(
|
|
231
|
-
mintPk,
|
|
232
|
-
owner,
|
|
233
|
-
true,
|
|
234
|
-
programForMint,
|
|
235
|
-
);
|
|
236
|
-
const acc = context.svm.getAccount(ata);
|
|
237
|
-
if (!acc || (acc.data?.length ?? 0) < ACCOUNT_SIZE) continue;
|
|
238
|
-
if (seen.has(ata.toBase58())) continue;
|
|
239
|
-
const rawOwner = (acc as { owner?: unknown }).owner;
|
|
240
|
-
const ownerPk: PublicKey =
|
|
241
|
-
rawOwner &&
|
|
242
|
-
typeof (rawOwner as { toBase58?: unknown }).toBase58 === "function"
|
|
243
|
-
? (rawOwner as PublicKey)
|
|
244
|
-
: new PublicKey(String(rawOwner));
|
|
245
|
-
const programPk = ownerPk.equals(TOKEN_2022_PROGRAM_ID)
|
|
246
|
-
? TOKEN_2022_PROGRAM_ID
|
|
247
|
-
: TOKEN_PROGRAM_ID;
|
|
248
|
-
// If a programId filter was provided, skip non-matching accounts
|
|
249
|
-
if (
|
|
250
|
-
requestedProgramId &&
|
|
251
|
-
programPk.toBase58() !== requestedProgramId
|
|
252
|
-
) {
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
const dec = unpackAccount(
|
|
256
|
-
ata,
|
|
257
|
-
{ data: Buffer.from(acc.data), owner: ownerPk },
|
|
258
|
-
programPk,
|
|
259
|
-
);
|
|
260
|
-
const decMint = dec.mint.toBase58();
|
|
261
|
-
const decOwner = dec.owner.toBase58();
|
|
262
|
-
if (decOwner !== owner.toBase58()) continue;
|
|
263
|
-
let decimals = 0;
|
|
264
|
-
try {
|
|
265
|
-
const mintAcc = context.svm.getAccount(dec.mint);
|
|
266
|
-
const mintOwnerPk = mintAcc
|
|
267
|
-
? ((): PublicKey => {
|
|
268
|
-
const ro = (mintAcc as { owner?: unknown }).owner;
|
|
269
|
-
return ro &&
|
|
270
|
-
typeof (ro as { toBase58?: unknown }).toBase58 ===
|
|
271
|
-
"function"
|
|
272
|
-
? (ro as PublicKey)
|
|
273
|
-
: new PublicKey(String(ro));
|
|
274
|
-
})()
|
|
275
|
-
: programPk;
|
|
276
|
-
const info = mintAcc
|
|
277
|
-
? unpackMint(
|
|
278
|
-
dec.mint,
|
|
279
|
-
{
|
|
280
|
-
data: Buffer.from(mintAcc.data),
|
|
281
|
-
owner: mintOwnerPk,
|
|
282
|
-
} as AccountInfo<Buffer>,
|
|
283
|
-
mintOwnerPk,
|
|
284
|
-
)
|
|
285
|
-
: null;
|
|
286
|
-
decimals = info?.decimals ?? 0;
|
|
287
|
-
} catch {}
|
|
288
|
-
const amount = BigInt(dec.amount?.toString?.() ?? dec.amount ?? 0);
|
|
289
|
-
const ui = decimals >= 0 ? Number(amount) / 10 ** decimals : null;
|
|
290
|
-
const state = dec.isFrozen
|
|
291
|
-
? "frozen"
|
|
292
|
-
: dec.isInitialized
|
|
293
|
-
? "initialized"
|
|
294
|
-
: "uninitialized";
|
|
295
|
-
const amountUi = {
|
|
296
|
-
amount: amount.toString(),
|
|
297
|
-
decimals,
|
|
298
|
-
uiAmount: ui,
|
|
299
|
-
uiAmountString: (ui ?? 0).toString(),
|
|
300
|
-
};
|
|
301
|
-
const programLabel = programPk.equals(TOKEN_2022_PROGRAM_ID)
|
|
302
|
-
? "spl-token-2022"
|
|
303
|
-
: "spl-token";
|
|
304
|
-
out.push({
|
|
305
|
-
pubkey: ata.toBase58(),
|
|
306
|
-
account: {
|
|
307
|
-
lamports: Number(acc.lamports || 0n),
|
|
308
|
-
owner: ownerPk.toBase58(),
|
|
309
|
-
executable: !!acc.executable,
|
|
310
|
-
rentEpoch: Number(acc.rentEpoch || 0),
|
|
311
|
-
data: {
|
|
312
|
-
program: programLabel,
|
|
313
|
-
parsed: {
|
|
314
|
-
type: "account",
|
|
315
|
-
info: {
|
|
316
|
-
mint: decMint,
|
|
317
|
-
owner: decOwner,
|
|
318
|
-
tokenAmount: amountUi,
|
|
319
|
-
state,
|
|
320
|
-
isNative: !!dec.isNative,
|
|
321
|
-
},
|
|
322
|
-
},
|
|
323
|
-
space: acc.data?.length ?? 0,
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
});
|
|
327
|
-
seen.add(ata.toBase58());
|
|
328
|
-
} catch {}
|
|
329
|
-
}
|
|
330
|
-
} catch {}
|
|
331
|
-
|
|
332
|
-
// Deduplicate: prefer the canonical ATA for each (owner,mint) under the mint's token program
|
|
333
|
-
if (encoding === "jsonParsed") {
|
|
334
|
-
try {
|
|
335
|
-
const pick = new Map<string, number>();
|
|
336
|
-
for (let i = 0; i < out.length; i++) {
|
|
337
|
-
const entry = out[i];
|
|
338
|
-
const pubkey: string = entry.pubkey;
|
|
339
|
-
const info = entry.account?.data?.parsed?.info;
|
|
340
|
-
const mintStr: string | undefined = info?.mint;
|
|
341
|
-
const ownerStr: string | undefined = info?.owner;
|
|
342
|
-
if (!mintStr || !ownerStr) continue;
|
|
343
|
-
let canonical = pubkey;
|
|
344
|
-
try {
|
|
345
|
-
const mintPk = new PublicKey(mintStr);
|
|
346
|
-
const ownerPk = new PublicKey(ownerStr);
|
|
347
|
-
const mintAcc = context.svm.getAccount(mintPk);
|
|
348
|
-
const mintOwnerPk = mintAcc
|
|
349
|
-
? ((): PublicKey => {
|
|
350
|
-
const ro = (mintAcc as { owner?: unknown }).owner;
|
|
351
|
-
return ro &&
|
|
352
|
-
typeof (ro as { toBase58?: unknown }).toBase58 ===
|
|
353
|
-
"function"
|
|
354
|
-
? (ro as PublicKey)
|
|
355
|
-
: new PublicKey(String(ro));
|
|
356
|
-
})()
|
|
357
|
-
: TOKEN_PROGRAM_ID;
|
|
358
|
-
const programForMint = mintOwnerPk.equals(TOKEN_2022_PROGRAM_ID)
|
|
359
|
-
? TOKEN_2022_PROGRAM_ID
|
|
360
|
-
: TOKEN_PROGRAM_ID;
|
|
361
|
-
const ata = getAssociatedTokenAddressSync(
|
|
362
|
-
mintPk,
|
|
363
|
-
ownerPk,
|
|
364
|
-
true,
|
|
365
|
-
programForMint,
|
|
366
|
-
);
|
|
367
|
-
canonical = ata.toBase58();
|
|
368
|
-
} catch {}
|
|
369
|
-
const key = `${ownerStr}:${mintStr}`;
|
|
370
|
-
const prev = pick.get(key);
|
|
371
|
-
if (prev === undefined) {
|
|
372
|
-
// First time; keep if canonical, otherwise tentatively keep
|
|
373
|
-
pick.set(key, i);
|
|
374
|
-
// Prefer canonical if current isn't canonical but later we may find it
|
|
375
|
-
if (pubkey !== canonical) pick.set(`${key}:prefer`, -1);
|
|
376
|
-
} else {
|
|
377
|
-
// If previous wasn't canonical and this one is, replace
|
|
378
|
-
const prefer = pick.get(`${key}:prefer`) === -1;
|
|
379
|
-
if (prefer && pubkey === canonical) {
|
|
380
|
-
pick.set(key, i);
|
|
381
|
-
pick.delete(`${key}:prefer`);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Build filtered list keeping only chosen indices
|
|
387
|
-
const keep = new Set<number>(
|
|
388
|
-
Array.from(pick.values()).filter(
|
|
389
|
-
(v) => typeof v === "number" && v >= 0,
|
|
390
|
-
),
|
|
391
|
-
);
|
|
392
|
-
const filtered: unknown[] = [];
|
|
393
|
-
for (let i = 0; i < out.length; i++) {
|
|
394
|
-
const e = out[i];
|
|
395
|
-
const info = e.account?.data?.parsed?.info;
|
|
396
|
-
const key =
|
|
397
|
-
info?.owner && info?.mint ? `${info.owner}:${info.mint}` : null;
|
|
398
|
-
if (key && keep.has(i)) filtered.push(e);
|
|
399
|
-
else if (!key) filtered.push(e); // non-parsed or unexpected shape
|
|
400
|
-
}
|
|
401
|
-
out.length = 0;
|
|
402
|
-
out.push(...filtered);
|
|
403
|
-
} catch {}
|
|
404
|
-
}
|
|
405
|
-
return context.createSuccessResponse(id, {
|
|
406
|
-
context: { slot: Number(context.slot) },
|
|
407
|
-
value: out,
|
|
408
|
-
});
|
|
409
|
-
} catch (e: unknown) {
|
|
410
|
-
try {
|
|
411
|
-
console.error("[rpc] getTokenAccountsByOwner error", e);
|
|
412
|
-
} catch {}
|
|
413
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
414
|
-
return context.createErrorResponse(id, -32603, "Internal error", message);
|
|
415
|
-
}
|
|
416
|
-
};
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
TOKEN_2022_PROGRAM_ID,
|
|
3
|
-
TOKEN_PROGRAM_ID,
|
|
4
|
-
unpackAccount,
|
|
5
|
-
unpackMint,
|
|
6
|
-
} from "@solana/spl-token";
|
|
7
|
-
import { PublicKey } from "@solana/web3.js";
|
|
8
|
-
import type { RpcMethodHandler } from "../../types";
|
|
9
|
-
|
|
10
|
-
export const getTokenLargestAccounts: RpcMethodHandler = async (
|
|
11
|
-
id,
|
|
12
|
-
params,
|
|
13
|
-
context,
|
|
14
|
-
) => {
|
|
15
|
-
const [mintStr] = params || [];
|
|
16
|
-
try {
|
|
17
|
-
const mint = new PublicKey(mintStr);
|
|
18
|
-
const mintAcc = context.svm.getAccount(mint);
|
|
19
|
-
const programPk =
|
|
20
|
-
mintAcc && new PublicKey(mintAcc.owner).equals(TOKEN_2022_PROGRAM_ID)
|
|
21
|
-
? TOKEN_2022_PROGRAM_ID
|
|
22
|
-
: TOKEN_PROGRAM_ID;
|
|
23
|
-
const mintInfo = mintAcc
|
|
24
|
-
? unpackMint(
|
|
25
|
-
mint,
|
|
26
|
-
{
|
|
27
|
-
data: Buffer.from(mintAcc.data),
|
|
28
|
-
owner: new PublicKey(mintAcc.owner),
|
|
29
|
-
},
|
|
30
|
-
programPk,
|
|
31
|
-
)
|
|
32
|
-
: null;
|
|
33
|
-
const decimals = mintInfo?.decimals ?? 0;
|
|
34
|
-
// Scan DB list of SPL token accounts and pick those with this mint
|
|
35
|
-
const rows =
|
|
36
|
-
(await context.store?.getAccountsByOwner(
|
|
37
|
-
TOKEN_PROGRAM_ID.toBase58(),
|
|
38
|
-
50_000,
|
|
39
|
-
)) || [];
|
|
40
|
-
const list: Array<{ address: string; amount: bigint }> = [];
|
|
41
|
-
for (const r of rows) {
|
|
42
|
-
try {
|
|
43
|
-
const acc = context.svm.getAccount(new PublicKey(r.address));
|
|
44
|
-
if (!acc) continue;
|
|
45
|
-
if ((acc.data?.length ?? 0) < 165) continue;
|
|
46
|
-
const accOwnerPk = new PublicKey(acc.owner);
|
|
47
|
-
const accProgramPk = accOwnerPk.equals(TOKEN_2022_PROGRAM_ID)
|
|
48
|
-
? TOKEN_2022_PROGRAM_ID
|
|
49
|
-
: TOKEN_PROGRAM_ID;
|
|
50
|
-
const dec = unpackAccount(
|
|
51
|
-
new PublicKey(r.address),
|
|
52
|
-
{ data: Buffer.from(acc.data), owner: accOwnerPk },
|
|
53
|
-
accProgramPk,
|
|
54
|
-
);
|
|
55
|
-
if (!dec.mint.equals(mint)) continue;
|
|
56
|
-
const amount = BigInt(dec.amount?.toString?.() ?? dec.amount ?? 0);
|
|
57
|
-
list.push({ address: r.address, amount });
|
|
58
|
-
} catch {}
|
|
59
|
-
}
|
|
60
|
-
list.sort((a, b) =>
|
|
61
|
-
a.amount < b.amount ? 1 : a.amount > b.amount ? -1 : 0,
|
|
62
|
-
);
|
|
63
|
-
const top = list.slice(0, 20).map((e) => {
|
|
64
|
-
const ui = Number(e.amount) / 10 ** decimals;
|
|
65
|
-
return {
|
|
66
|
-
address: e.address,
|
|
67
|
-
amount: e.amount.toString(),
|
|
68
|
-
decimals,
|
|
69
|
-
uiAmount: ui,
|
|
70
|
-
uiAmountString: ui.toString(),
|
|
71
|
-
};
|
|
72
|
-
});
|
|
73
|
-
return context.createSuccessResponse(id, {
|
|
74
|
-
context: { slot: Number(context.slot) },
|
|
75
|
-
value: top,
|
|
76
|
-
});
|
|
77
|
-
} catch (e: unknown) {
|
|
78
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
79
|
-
return context.createErrorResponse(id, -32602, "Invalid params", message);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
TOKEN_2022_PROGRAM_ID,
|
|
3
|
-
TOKEN_PROGRAM_ID,
|
|
4
|
-
unpackMint,
|
|
5
|
-
} from "@solana/spl-token";
|
|
6
|
-
import { PublicKey } from "@solana/web3.js";
|
|
7
|
-
import type { RpcMethodHandler } from "../../types";
|
|
8
|
-
|
|
9
|
-
export const getTokenSupply: RpcMethodHandler = (id, params, context) => {
|
|
10
|
-
const [mintStr] = params || [];
|
|
11
|
-
try {
|
|
12
|
-
const mint = new PublicKey(mintStr);
|
|
13
|
-
const acc = context.svm.getAccount(mint);
|
|
14
|
-
if (!acc) return context.createErrorResponse(id, -32602, "Mint not found");
|
|
15
|
-
const ownerPk = new PublicKey(acc.owner);
|
|
16
|
-
const programPk = ownerPk.equals(TOKEN_2022_PROGRAM_ID)
|
|
17
|
-
? TOKEN_2022_PROGRAM_ID
|
|
18
|
-
: TOKEN_PROGRAM_ID;
|
|
19
|
-
const info = unpackMint(
|
|
20
|
-
mint,
|
|
21
|
-
{ data: Buffer.from(acc.data), owner: ownerPk },
|
|
22
|
-
programPk,
|
|
23
|
-
);
|
|
24
|
-
const supply = BigInt(info.supply?.toString?.() ?? info.supply ?? 0);
|
|
25
|
-
const ui = Number(supply) / 10 ** info.decimals;
|
|
26
|
-
return context.createSuccessResponse(id, {
|
|
27
|
-
context: { slot: Number(context.slot) },
|
|
28
|
-
value: {
|
|
29
|
-
amount: supply.toString(),
|
|
30
|
-
decimals: info.decimals,
|
|
31
|
-
uiAmount: ui,
|
|
32
|
-
uiAmountString: ui.toString(),
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
} catch (e: unknown) {
|
|
36
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
37
|
-
return context.createErrorResponse(id, -32602, "Invalid params", message);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export { getBlockCommitment } from "./get-block-commitment";
|
|
2
|
-
export { getBlockProduction } from "./get-block-production";
|
|
3
|
-
export { getBlockTime } from "./get-block-time";
|
|
4
|
-
export { getBlocks } from "./get-blocks";
|
|
5
|
-
export { getFirstAvailableBlock } from "./get-first-available-block";
|
|
6
|
-
export { getGenesisHash } from "./get-genesis-hash";
|
|
7
|
-
export { getIdentity } from "./get-identity";
|
|
8
|
-
export { getInflationGovernor } from "./get-inflation-governor";
|
|
9
|
-
export { getInflationRate } from "./get-inflation-rate";
|
|
10
|
-
export { getInflationReward } from "./get-inflation-reward";
|
|
11
|
-
export { getLargestAccounts } from "./get-largest-accounts";
|
|
12
|
-
export { getParsedProgramAccounts } from "./get-parsed-program-accounts";
|
|
13
|
-
export { getParsedTokenAccountsByDelegate } from "./get-parsed-token-accounts-by-delegate";
|
|
14
|
-
export { getParsedTokenAccountsByOwner } from "./get-parsed-token-accounts-by-owner";
|
|
15
|
-
export { getProgramAccounts } from "./get-program-accounts";
|
|
16
|
-
export { getSupply } from "./get-supply";
|
|
17
|
-
export { getTokenAccountBalance } from "./get-token-account-balance";
|
|
18
|
-
export { getTokenAccountsByDelegate } from "./get-token-accounts-by-delegate";
|
|
19
|
-
export { getTokenAccountsByOwner } from "./get-token-accounts-by-owner";
|
|
20
|
-
export { getTokenLargestAccounts } from "./get-token-largest-accounts";
|
|
21
|
-
export { getTokenSupply } from "./get-token-supply";
|