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.
Files changed (175) hide show
  1. package/package.json +1 -5
  2. package/start.cjs +19 -23
  3. package/docs/API.md +0 -379
  4. package/docs/CONFIGURATION.md +0 -407
  5. package/docs/bun-single-file-executable.md +0 -585
  6. package/docs/cli-plan.md +0 -154
  7. package/docs/data-indexing-plan.md +0 -214
  8. package/docs/gui-roadmap.md +0 -202
  9. package/scripts/decode-b58.ts +0 -10
  10. package/scripts/install.sh +0 -112
  11. package/server/index.ts +0 -5
  12. package/server/lib/base58.ts +0 -33
  13. package/server/lib/faucet.ts +0 -110
  14. package/server/lib/instruction-parser.ts +0 -328
  15. package/server/lib/parsers/spl-associated-token-account.ts +0 -50
  16. package/server/lib/parsers/spl-token.ts +0 -340
  17. package/server/lib/spl-token.ts +0 -57
  18. package/server/methods/TEMPLATE.md +0 -117
  19. package/server/methods/account/get-account-info.ts +0 -86
  20. package/server/methods/account/get-balance.ts +0 -23
  21. package/server/methods/account/get-multiple-accounts.ts +0 -84
  22. package/server/methods/account/get-parsed-account-info.ts +0 -17
  23. package/server/methods/account/index.ts +0 -12
  24. package/server/methods/account/parsers/index.ts +0 -52
  25. package/server/methods/account/parsers/loader-upgradeable.ts +0 -79
  26. package/server/methods/account/parsers/spl-token.ts +0 -256
  27. package/server/methods/account/parsers/system.ts +0 -4
  28. package/server/methods/account/request-airdrop.ts +0 -271
  29. package/server/methods/admin/adopt-mint-authority.ts +0 -94
  30. package/server/methods/admin/clone-program-accounts.ts +0 -55
  31. package/server/methods/admin/clone-program.ts +0 -152
  32. package/server/methods/admin/clone-token-accounts.ts +0 -117
  33. package/server/methods/admin/clone-token-mint.ts +0 -82
  34. package/server/methods/admin/create-mint.ts +0 -114
  35. package/server/methods/admin/create-token-account.ts +0 -137
  36. package/server/methods/admin/helpers.ts +0 -70
  37. package/server/methods/admin/index.ts +0 -10
  38. package/server/methods/admin/list-mints.ts +0 -21
  39. package/server/methods/admin/load-program.ts +0 -52
  40. package/server/methods/admin/mint-to.ts +0 -266
  41. package/server/methods/block/get-block-height.ts +0 -5
  42. package/server/methods/block/get-block.ts +0 -31
  43. package/server/methods/block/get-blocks-with-limit.ts +0 -19
  44. package/server/methods/block/get-latest-blockhash.ts +0 -12
  45. package/server/methods/block/get-slot.ts +0 -5
  46. package/server/methods/block/index.ts +0 -6
  47. package/server/methods/block/is-blockhash-valid.ts +0 -19
  48. package/server/methods/epoch/get-cluster-nodes.ts +0 -17
  49. package/server/methods/epoch/get-epoch-info.ts +0 -16
  50. package/server/methods/epoch/get-epoch-schedule.ts +0 -15
  51. package/server/methods/epoch/get-highest-snapshot-slot.ts +0 -9
  52. package/server/methods/epoch/get-leader-schedule.ts +0 -8
  53. package/server/methods/epoch/get-max-retransmit-slot.ts +0 -9
  54. package/server/methods/epoch/get-max-shred-insert-slot.ts +0 -9
  55. package/server/methods/epoch/get-slot-leader.ts +0 -6
  56. package/server/methods/epoch/get-slot-leaders.ts +0 -9
  57. package/server/methods/epoch/get-stake-activation.ts +0 -9
  58. package/server/methods/epoch/get-stake-minimum-delegation.ts +0 -9
  59. package/server/methods/epoch/get-vote-accounts.ts +0 -19
  60. package/server/methods/epoch/index.ts +0 -13
  61. package/server/methods/epoch/minimum-ledger-slot.ts +0 -5
  62. package/server/methods/fee/get-fee-calculator-for-blockhash.ts +0 -12
  63. package/server/methods/fee/get-fee-for-message.ts +0 -8
  64. package/server/methods/fee/get-fee-rate-governor.ts +0 -16
  65. package/server/methods/fee/get-fees.ts +0 -14
  66. package/server/methods/fee/get-recent-prioritization-fees.ts +0 -22
  67. package/server/methods/fee/index.ts +0 -5
  68. package/server/methods/get-address-lookup-table.ts +0 -27
  69. package/server/methods/index.ts +0 -265
  70. package/server/methods/performance/get-recent-performance-samples.ts +0 -25
  71. package/server/methods/performance/get-transaction-count.ts +0 -5
  72. package/server/methods/performance/index.ts +0 -2
  73. package/server/methods/program/get-block-commitment.ts +0 -9
  74. package/server/methods/program/get-block-production.ts +0 -14
  75. package/server/methods/program/get-block-time.ts +0 -21
  76. package/server/methods/program/get-blocks.ts +0 -11
  77. package/server/methods/program/get-first-available-block.ts +0 -9
  78. package/server/methods/program/get-genesis-hash.ts +0 -6
  79. package/server/methods/program/get-identity.ts +0 -6
  80. package/server/methods/program/get-inflation-governor.ts +0 -15
  81. package/server/methods/program/get-inflation-rate.ts +0 -10
  82. package/server/methods/program/get-inflation-reward.ts +0 -12
  83. package/server/methods/program/get-largest-accounts.ts +0 -8
  84. package/server/methods/program/get-parsed-program-accounts.ts +0 -12
  85. package/server/methods/program/get-parsed-token-accounts-by-delegate.ts +0 -12
  86. package/server/methods/program/get-parsed-token-accounts-by-owner.ts +0 -12
  87. package/server/methods/program/get-program-accounts.ts +0 -221
  88. package/server/methods/program/get-supply.ts +0 -13
  89. package/server/methods/program/get-token-account-balance.ts +0 -60
  90. package/server/methods/program/get-token-accounts-by-delegate.ts +0 -82
  91. package/server/methods/program/get-token-accounts-by-owner.ts +0 -416
  92. package/server/methods/program/get-token-largest-accounts.ts +0 -81
  93. package/server/methods/program/get-token-supply.ts +0 -39
  94. package/server/methods/program/index.ts +0 -21
  95. package/server/methods/solforge/index.ts +0 -158
  96. package/server/methods/system/get-health.ts +0 -5
  97. package/server/methods/system/get-minimum-balance-for-rent-exemption.ts +0 -13
  98. package/server/methods/system/get-version.ts +0 -9
  99. package/server/methods/system/index.ts +0 -3
  100. package/server/methods/transaction/get-confirmed-transaction.ts +0 -11
  101. package/server/methods/transaction/get-parsed-transaction.ts +0 -17
  102. package/server/methods/transaction/get-signature-statuses.ts +0 -79
  103. package/server/methods/transaction/get-signatures-for-address.ts +0 -41
  104. package/server/methods/transaction/get-transaction.ts +0 -639
  105. package/server/methods/transaction/index.ts +0 -7
  106. package/server/methods/transaction/inner-instructions.test.ts +0 -104
  107. package/server/methods/transaction/send-transaction.ts +0 -469
  108. package/server/methods/transaction/simulate-transaction.ts +0 -57
  109. package/server/rpc-server.ts +0 -521
  110. package/server/types.ts +0 -109
  111. package/server/ws-server.ts +0 -178
  112. package/src/api-server-entry.ts +0 -109
  113. package/src/cli/bootstrap.ts +0 -67
  114. package/src/cli/commands/airdrop.ts +0 -37
  115. package/src/cli/commands/config.ts +0 -39
  116. package/src/cli/commands/mint.ts +0 -187
  117. package/src/cli/commands/program-clone.ts +0 -122
  118. package/src/cli/commands/program-load.ts +0 -64
  119. package/src/cli/commands/rpc-start.ts +0 -49
  120. package/src/cli/commands/token-adopt-authority.ts +0 -37
  121. package/src/cli/commands/token-clone.ts +0 -112
  122. package/src/cli/commands/token-create.ts +0 -81
  123. package/src/cli/main.ts +0 -158
  124. package/src/cli/run-solforge.ts +0 -112
  125. package/src/cli/setup-utils.ts +0 -54
  126. package/src/cli/setup-wizard.ts +0 -258
  127. package/src/cli/utils/args.ts +0 -15
  128. package/src/commands/add-program.ts +0 -333
  129. package/src/commands/init.ts +0 -122
  130. package/src/commands/list.ts +0 -136
  131. package/src/commands/mint.ts +0 -287
  132. package/src/commands/start.ts +0 -881
  133. package/src/commands/status.ts +0 -99
  134. package/src/commands/stop.ts +0 -405
  135. package/src/config/index.ts +0 -146
  136. package/src/config/manager.ts +0 -157
  137. package/src/db/index.ts +0 -83
  138. package/src/db/schema/accounts.ts +0 -23
  139. package/src/db/schema/address-signatures.ts +0 -31
  140. package/src/db/schema/index.ts +0 -6
  141. package/src/db/schema/meta-kv.ts +0 -9
  142. package/src/db/schema/transactions.ts +0 -36
  143. package/src/db/schema/tx-account-states.ts +0 -23
  144. package/src/db/schema/tx-accounts.ts +0 -33
  145. package/src/db/tx-store.ts +0 -264
  146. package/src/gui/public/app.css +0 -1556
  147. package/src/gui/public/build/main.css +0 -1569
  148. package/src/gui/public/build/main.js +0 -303
  149. package/src/gui/public/build/main.js.txt +0 -231
  150. package/src/gui/public/index.html +0 -19
  151. package/src/gui/server.ts +0 -296
  152. package/src/gui/src/api.ts +0 -127
  153. package/src/gui/src/app.tsx +0 -441
  154. package/src/gui/src/components/airdrop-mint-form.tsx +0 -246
  155. package/src/gui/src/components/clone-program-modal.tsx +0 -202
  156. package/src/gui/src/components/clone-token-modal.tsx +0 -230
  157. package/src/gui/src/components/modal.tsx +0 -134
  158. package/src/gui/src/components/programs-panel.tsx +0 -124
  159. package/src/gui/src/components/status-panel.tsx +0 -136
  160. package/src/gui/src/components/tokens-panel.tsx +0 -122
  161. package/src/gui/src/hooks/use-interval.ts +0 -17
  162. package/src/gui/src/index.css +0 -557
  163. package/src/gui/src/main.tsx +0 -17
  164. package/src/index.ts +0 -216
  165. package/src/migrations-bundled.ts +0 -23
  166. package/src/rpc/start.ts +0 -44
  167. package/src/services/api-server.ts +0 -504
  168. package/src/services/port-manager.ts +0 -174
  169. package/src/services/process-registry.ts +0 -153
  170. package/src/services/program-cloner.ts +0 -317
  171. package/src/services/token-cloner.ts +0 -811
  172. package/src/services/validator.ts +0 -293
  173. package/src/types/config.ts +0 -110
  174. package/src/utils/shell.ts +0 -110
  175. package/src/utils/token-loader.ts +0 -115
@@ -1,340 +0,0 @@
1
- import type { PublicKey, TransactionInstruction } from "@solana/web3.js";
2
- import {
3
- decodeMintToCheckedInstruction,
4
- decodeTransferInstruction,
5
- decodeTransferCheckedInstruction,
6
- decodeInitializeAccount3Instruction,
7
- decodeInitializeImmutableOwnerInstruction,
8
- decodeTransferCheckedInstructionUnchecked,
9
- decodeTransferInstructionUnchecked,
10
- decodeInitializeAccount3InstructionUnchecked,
11
- decodeInitializeImmutableOwnerInstructionUnchecked,
12
- } from "@solana/spl-token";
13
- import { u8 } from "@solana/buffer-layout";
14
- import { PublicKey as PK } from "@solana/web3.js";
15
- import {
16
- TOKEN_PROGRAM_ID as TOKEN_PROGRAM_V1,
17
- TOKEN_2022_PROGRAM_ID as TOKEN_PROGRAM_2022,
18
- getAssociatedTokenAddressSync,
19
- } from "@solana/spl-token";
20
-
21
- // Keep shape compatible with instruction-parser
22
- export type ParsedInstruction =
23
- | {
24
- program: string;
25
- programId: string;
26
- parsed: { type: string; info: unknown };
27
- }
28
- | { programId: string; accounts: string[]; data: string };
29
-
30
- function ok(programId: string, type: string, info: unknown): ParsedInstruction {
31
- // Use a single label for both SPL v1 and Token-2022 for compatibility with UIs
32
- return { program: "spl-token", programId, parsed: { type, info } };
33
- }
34
-
35
- function asBase58(pk: PublicKey | undefined): string | undefined {
36
- try {
37
- return pk ? pk.toBase58() : undefined;
38
- } catch {
39
- return undefined;
40
- }
41
- }
42
-
43
- export function tryParseSplToken(
44
- ix: TransactionInstruction,
45
- programIdStr: string,
46
- _accountKeys: string[],
47
- dataBase58: string,
48
- tokenBalanceHints?: Array<{ mint: string; decimals: number }>,
49
- ): ParsedInstruction | null {
50
- try {
51
- // Accept both SPL Token and Token-2022 program ids
52
- // We pass the actual program id to decoders for strict match
53
- const programPk = new PK(programIdStr);
54
- // Don't early-return on program id; allow both programs
55
-
56
- // MintToChecked
57
- try {
58
- const m = decodeMintToCheckedInstruction(ix, programPk);
59
- const amount = m.data.amount;
60
- const decimals = m.data.decimals;
61
- const amtStr =
62
- typeof amount === "bigint" ? amount.toString() : String(amount);
63
- const base = BigInt(10) ** BigInt(decimals);
64
- const whole = BigInt(amtStr) / base;
65
- const frac = BigInt(amtStr) % base;
66
- const fracStr = frac
67
- .toString()
68
- .padStart(decimals, "0")
69
- .replace(/0+$/, "");
70
- const uiStr = fracStr.length ? `${whole}.${fracStr}` : `${whole}`;
71
- return ok(programIdStr, "mintToChecked", {
72
- account: asBase58(m.keys.destination.pubkey),
73
- mint: asBase58(m.keys.mint.pubkey),
74
- mintAuthority: asBase58(m.keys.authority.pubkey),
75
- tokenAmount: {
76
- amount: amtStr,
77
- decimals,
78
- uiAmount: Number(uiStr),
79
- uiAmountString: uiStr,
80
- },
81
- });
82
- } catch {}
83
-
84
- // Transfer / TransferChecked (strict)
85
- try {
86
- const t = decodeTransferInstruction(ix, programPk);
87
- const amt = t.data.amount;
88
- return ok(programIdStr, "transfer", {
89
- amount: typeof amt === "bigint" ? amt.toString() : String(amt),
90
- source: asBase58(t.keys.source.pubkey),
91
- destination: asBase58(t.keys.destination.pubkey),
92
- authority: asBase58(t.keys.owner.pubkey),
93
- });
94
- } catch {}
95
-
96
- try {
97
- const t = decodeTransferCheckedInstruction(ix, programPk);
98
- const amt = t.data.amount;
99
- const decimals = t.data.decimals;
100
- return ok(programIdStr, "transferChecked", {
101
- tokenAmount: {
102
- amount: typeof amt === "bigint" ? amt.toString() : String(amt),
103
- decimals,
104
- },
105
- source: asBase58(t.keys.source.pubkey),
106
- destination: asBase58(t.keys.destination.pubkey),
107
- authority: asBase58(t.keys.owner.pubkey),
108
- mint: asBase58(t.keys.mint.pubkey),
109
- });
110
- } catch {}
111
-
112
- // InitializeAccount3 (strict)
113
- try {
114
- const a = decodeInitializeAccount3Instruction(ix, programPk);
115
- return ok(programIdStr, "initializeAccount3", {
116
- account: asBase58(a.keys.account.pubkey),
117
- mint: asBase58(a.keys.mint.pubkey),
118
- owner: asBase58(a.data.owner),
119
- });
120
- } catch {}
121
-
122
- // InitializeImmutableOwner
123
- try {
124
- const im = decodeInitializeImmutableOwnerInstruction(ix, programPk);
125
- return ok(programIdStr, "initializeImmutableOwner", {
126
- account: asBase58(im.keys.account.pubkey),
127
- });
128
- } catch {}
129
-
130
- // GetAccountDataSize: decode extension types (u16 little-endian sequence)
131
- try {
132
- const bytes = Buffer.from(bs58decode(dataBase58));
133
- const t = u8().decode(bytes);
134
- // 21 corresponds to TokenInstruction.GetAccountDataSize
135
- if (t === 21) {
136
- const mint = asBase58(ix.keys[0]?.pubkey);
137
- const extCodes: number[] = [];
138
- for (let i = 1; i + 1 < bytes.length; i += 2) {
139
- const code = bytes[i] | (bytes[i + 1] << 8);
140
- extCodes.push(code);
141
- }
142
- const extMap: Record<number, string> = {
143
- 7: "immutableOwner",
144
- 8: "memoTransfer",
145
- 9: "nonTransferable",
146
- 12: "permanentDelegate",
147
- 14: "transferHook",
148
- 15: "transferHookAccount",
149
- 18: "metadataPointer",
150
- 19: "tokenMetadata",
151
- 20: "groupPointer",
152
- 21: "tokenGroup",
153
- 22: "groupMemberPointer",
154
- 23: "tokenGroupMember",
155
- 25: "scaledUiAmountConfig",
156
- 26: "pausableConfig",
157
- 27: "pausableAccount",
158
- };
159
- const extensionTypes = extCodes.map((c) => extMap[c] || String(c));
160
- return ok(programIdStr, "getAccountDataSize", { mint, extensionTypes });
161
- }
162
- } catch {}
163
-
164
- // Unchecked fallbacks: decode data fields even if keys/validation missing
165
- try {
166
- const raw = bs58decode(dataBase58);
167
- const op = raw[0];
168
- // Transfer
169
- if (op === 3) {
170
- const t = decodeTransferInstructionUnchecked(ix);
171
- const amt = t.data.amount;
172
- return ok(programIdStr, "transfer", {
173
- amount: typeof amt === "bigint" ? amt.toString() : String(amt),
174
- source: asBase58(t.keys.source?.pubkey),
175
- destination: asBase58(t.keys.destination?.pubkey),
176
- authority: asBase58(t.keys.owner?.pubkey),
177
- });
178
- }
179
- // TransferChecked
180
- if (op === 12) {
181
- const t = decodeTransferCheckedInstructionUnchecked(ix);
182
- const amt = t.data.amount;
183
- const decimals = t.data.decimals;
184
- const hintMint = (() => {
185
- try {
186
- const dec = Number(decimals);
187
- const candidates = (tokenBalanceHints || []).filter(
188
- (h) => Number(h.decimals) === dec,
189
- );
190
- if (candidates.length === 1) return candidates[0].mint;
191
- // Prefer non-zero decimals over 0 (filters out native 4uQe mint in many cases)
192
- const nonZero = candidates.filter((c) => c.decimals > 0);
193
- if (nonZero.length === 1) return nonZero[0].mint;
194
- // Fall back to first candidate if multiple
195
- return candidates[0]?.mint;
196
- } catch {
197
- return undefined;
198
- }
199
- })();
200
- return ok(programIdStr, "transferChecked", {
201
- tokenAmount: {
202
- amount: typeof amt === "bigint" ? amt.toString() : String(amt),
203
- decimals,
204
- },
205
- source: asBase58(t.keys.source?.pubkey),
206
- destination: asBase58(t.keys.destination?.pubkey),
207
- authority: asBase58(t.keys.owner?.pubkey),
208
- mint: asBase58(t.keys.mint?.pubkey) || hintMint,
209
- });
210
- }
211
- // InitializeAccount3
212
- if (op === 18) {
213
- const a = decodeInitializeAccount3InstructionUnchecked(ix);
214
- const hintMint = (() => {
215
- try {
216
- // Prefer single non-zero-decimals mint in this tx
217
- const nonZero = (tokenBalanceHints || []).filter(
218
- (h) => h.decimals > 0,
219
- );
220
- if (nonZero.length === 1) return nonZero[0].mint;
221
- // Fall back to first available mint
222
- return (tokenBalanceHints || [])[0]?.mint;
223
- } catch {
224
- return undefined;
225
- }
226
- })();
227
- const ownerStr = asBase58(a.data.owner);
228
- const mintStr = asBase58(a.keys.mint?.pubkey) || hintMint;
229
- let accountStr = asBase58(a.keys.account?.pubkey);
230
- try {
231
- if (!accountStr && ownerStr && mintStr) {
232
- const ownerPk = new PK(ownerStr);
233
- const mintPk = new PK(mintStr);
234
- const programId =
235
- programIdStr === TOKEN_PROGRAM_2022.toBase58()
236
- ? TOKEN_PROGRAM_2022
237
- : TOKEN_PROGRAM_V1;
238
- const ata = getAssociatedTokenAddressSync(
239
- mintPk,
240
- ownerPk,
241
- true,
242
- programId,
243
- );
244
- accountStr = ata.toBase58();
245
- }
246
- } catch {}
247
- return ok(programIdStr, "initializeAccount3", {
248
- account: accountStr,
249
- mint: mintStr,
250
- owner: ownerStr,
251
- });
252
- }
253
- // InitializeImmutableOwner
254
- if (op === 22) {
255
- const im = decodeInitializeImmutableOwnerInstructionUnchecked(ix);
256
- return ok(programIdStr, "initializeImmutableOwner", {
257
- account: asBase58(im.keys.account?.pubkey),
258
- });
259
- }
260
- } catch {}
261
-
262
- // Fallback: classify by TokenInstruction opcode (first byte) when nothing else matched
263
- try {
264
- const raw = bs58decode(dataBase58);
265
- if (raw.length > 0) {
266
- const op = raw[0];
267
- const map: Record<number, string> = {
268
- 0: "initializeMint",
269
- 1: "initializeAccount",
270
- 2: "initializeMultisig",
271
- 3: "transfer",
272
- 4: "approve",
273
- 5: "revoke",
274
- 6: "setAuthority",
275
- 7: "mintTo",
276
- 8: "burn",
277
- 9: "closeAccount",
278
- 10: "freezeAccount",
279
- 11: "thawAccount",
280
- 12: "transferChecked",
281
- 13: "approveChecked",
282
- 14: "mintToChecked",
283
- 15: "burnChecked",
284
- 16: "initializeAccount2",
285
- 17: "syncNative",
286
- 18: "initializeAccount3",
287
- 19: "initializeMultisig2",
288
- 20: "initializeMint2",
289
- 21: "getAccountDataSize",
290
- 22: "initializeImmutableOwner",
291
- 23: "amountToUiAmount",
292
- 24: "uiAmountToAmount",
293
- 25: "initializeMintCloseAuthority",
294
- 26: "transferFeeExtension",
295
- 27: "confidentialTransferExtension",
296
- 28: "defaultAccountStateExtension",
297
- 29: "reallocate",
298
- 30: "memoTransferExtension",
299
- 31: "createNativeMint",
300
- 32: "initializeNonTransferableMint",
301
- 33: "interestBearingMintExtension",
302
- 34: "cpiGuardExtension",
303
- 35: "initializePermanentDelegate",
304
- 36: "transferHookExtension",
305
- 39: "metadataPointerExtension",
306
- 40: "groupPointerExtension",
307
- 41: "groupMemberPointerExtension",
308
- 43: "scaledUiAmountExtension",
309
- 44: "pausableExtension",
310
- };
311
- const type = map[op];
312
- if (type) return ok(programIdStr, type, {});
313
- }
314
- } catch {}
315
-
316
- // Unknown SPL token instruction (unrecognized opcode)
317
- return null;
318
- } catch {
319
- return null;
320
- }
321
- }
322
-
323
- // Local base58 decode to avoid importing from sibling file
324
- const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
325
- const BASE = BigInt(ALPHABET.length);
326
- function bs58decode(str: string): Uint8Array {
327
- let num = 0n;
328
- for (const char of str) {
329
- const index = ALPHABET.indexOf(char);
330
- if (index === -1) throw new Error("Invalid base58 character");
331
- num = num * BASE + BigInt(index);
332
- }
333
- const bytes: number[] = [];
334
- while (num > 0n) {
335
- bytes.unshift(Number(num % 256n));
336
- num = num / 256n;
337
- }
338
- for (let i = 0; i < str.length && str[i] === "1"; i++) bytes.unshift(0);
339
- return new Uint8Array(bytes.length > 0 ? bytes : [0]);
340
- }
@@ -1,57 +0,0 @@
1
- import { PublicKey } from "@solana/web3.js";
2
-
3
- export const TOKEN_PROGRAM_ID = new PublicKey(
4
- "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
5
- );
6
-
7
- // Minimal SPL Token v1 layouts
8
- export type DecodedMint = {
9
- supply: bigint;
10
- decimals: number;
11
- isInitialized: boolean;
12
- };
13
-
14
- export type DecodedTokenAccount = {
15
- mint: string;
16
- owner: string;
17
- amount: bigint;
18
- state: number;
19
- delegatedAmount: bigint;
20
- };
21
-
22
- export function decodeMint(data: Uint8Array): DecodedMint | null {
23
- if (!data || data.length < 82) return null;
24
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
25
- // supply at offset 36? Common layout: 4 (opt) + 32 (auth) + 8 (supply) + 1 (dec) + 1 (init)
26
- const supply = view.getBigUint64(36, true);
27
- const decimals = view.getUint8(44);
28
- const isInitialized = view.getUint8(45) !== 0;
29
- return { supply, decimals, isInitialized };
30
- }
31
-
32
- export function decodeTokenAccount(
33
- data: Uint8Array,
34
- ): DecodedTokenAccount | null {
35
- if (!data || data.length < 165) return null;
36
- const mint = new PublicKey(data.slice(0, 32)).toBase58();
37
- const owner = new PublicKey(data.slice(32, 64)).toBase58();
38
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
39
- const amount = view.getBigUint64(64, true);
40
- const state = data[108];
41
- const delegatedAmount = view.getBigUint64(124, true);
42
- return { mint, owner, amount, state, delegatedAmount };
43
- }
44
-
45
- export function formatUiAmount(amount: bigint, decimals: number) {
46
- const base = BigInt(10) ** BigInt(decimals);
47
- const whole = amount / base;
48
- const frac = amount % base;
49
- const fracStr = frac.toString().padStart(decimals, "0").replace(/0+$/, "");
50
- const ui = fracStr.length > 0 ? `${whole}.${fracStr}` : `${whole}`;
51
- return {
52
- amount: amount.toString(),
53
- decimals,
54
- uiAmount: Number(ui),
55
- uiAmountString: ui,
56
- };
57
- }
@@ -1,117 +0,0 @@
1
- # RPC Method Template
2
-
3
- Use this template when adding new RPC methods to SolForge.
4
-
5
- ## File Template
6
-
7
- ```typescript
8
- import { PublicKey } from "@solana/web3.js"; // Only if needed
9
- import type { RpcMethodHandler } from "../types"; // Adjust path based on location
10
-
11
- /**
12
- * Implements the methodName RPC method
13
- * @see https://docs.solana.com/api/http#methodname
14
- */
15
- export const methodName: RpcMethodHandler = (id, params, context) => {
16
- // 1. Parse parameters
17
- const [param1, param2, config] = params;
18
-
19
- // 2. Validate parameters (if needed)
20
- if (!param1) {
21
- return context.createErrorResponse(
22
- id,
23
- -32602,
24
- "Missing required parameter: param1"
25
- );
26
- }
27
-
28
- try {
29
- // 3. Execute the RPC logic
30
- const result = context.svm.someOperation();
31
-
32
- // 4. Format and return the response
33
- return context.createSuccessResponse(id, {
34
- context: {
35
- slot: Number(context.slot),
36
- apiVersion: "1.18.0" // If needed
37
- },
38
- value: result
39
- });
40
- } catch (error: any) {
41
- // 5. Handle errors appropriately
42
- return context.createErrorResponse(
43
- id,
44
- -32603, // Use appropriate error code
45
- "Operation failed",
46
- error.message
47
- );
48
- }
49
- };
50
- ```
51
-
52
- ## Common Patterns
53
-
54
- ### Methods that modify state
55
- ```typescript
56
- // After successful operation, increment slot/blockHeight
57
- if (success) {
58
- // This is handled in the main server now
59
- // Just return success response
60
- return context.createSuccessResponse(id, signature);
61
- }
62
- ```
63
-
64
- ### Methods with optional config
65
- ```typescript
66
- const [address, config] = params;
67
- const encoding = config?.encoding || "base64";
68
- const commitment = config?.commitment || "confirmed";
69
- ```
70
-
71
- ### Batch operations
72
- ```typescript
73
- const results = addresses.map(addr => {
74
- try {
75
- // Process each item
76
- return processItem(addr);
77
- } catch {
78
- // Return null for failed items
79
- return null;
80
- }
81
- });
82
- ```
83
-
84
- ## Error Codes Reference
85
-
86
- - `-32700`: Parse error (JSON parsing failed)
87
- - `-32600`: Invalid request (not a valid JSON-RPC request)
88
- - `-32601`: Method not found
89
- - `-32602`: Invalid params
90
- - `-32603`: Internal error
91
- - `-32000`: Generic server error
92
- - `-32001`: Resource not found
93
- - `-32002`: Resource unavailable
94
- - `-32003`: Transaction rejected
95
- - `-32004`: Method not supported
96
- - `-32005`: Limit exceeded
97
-
98
- ## Checklist
99
-
100
- Before committing a new RPC method:
101
-
102
- - [ ] Method follows naming convention (camelCase)
103
- - [ ] File uses kebab-case naming
104
- - [ ] Proper TypeScript types used
105
- - [ ] Error handling implemented
106
- - [ ] Success case tested
107
- - [ ] Error cases tested
108
- - [ ] Added to methods/index.ts
109
- - [ ] Documentation updated in README.md
110
- - [ ] Follows SolForge patterns
111
-
112
- ## Examples in Codebase
113
-
114
- - Simple query: See `get-balance.ts`
115
- - Complex response: See `get-account-info.ts`
116
- - Transaction handling: See `send-transaction.ts`
117
- - Batch operation: See `get-multiple-accounts.ts`
@@ -1,86 +0,0 @@
1
- import { PublicKey } from "@solana/web3.js";
2
- import type { RpcMethodHandler } from "../../types";
3
- import { parseAccountJson } from "./parsers";
4
-
5
- /**
6
- * Implements the getAccountInfo RPC method
7
- * @see https://docs.solana.com/api/http#getaccountinfo
8
- */
9
- export const getAccountInfo: RpcMethodHandler = async (id, params, context) => {
10
- const [pubkeyStr, config] = params;
11
- const encoding = config?.encoding || "base64";
12
-
13
- try {
14
- const pubkey = new PublicKey(pubkeyStr);
15
- const account = context.svm.getAccount(pubkey);
16
-
17
- if (!account) {
18
- return context.createSuccessResponse(id, {
19
- context: { slot: Number(context.slot) },
20
- value: null,
21
- });
22
- }
23
-
24
- const owner = new PublicKey(account.owner).toBase58();
25
- // Opportunistic index update
26
- try {
27
- await context.store?.upsertAccounts([
28
- {
29
- address: pubkey.toBase58(),
30
- lamports: Number(account.lamports || 0n),
31
- ownerProgram: owner,
32
- executable: !!account.executable,
33
- rentEpoch: Number(account.rentEpoch || 0),
34
- dataLen: account.data?.length ?? 0,
35
- dataBase64: undefined,
36
- lastSlot: Number(context.slot),
37
- },
38
- ]);
39
- } catch {}
40
-
41
- if (encoding === "jsonParsed") {
42
- const parsed = parseAccountJson(
43
- pubkey,
44
- {
45
- owner: new PublicKey(account.owner),
46
- data: account.data ? new Uint8Array(account.data) : new Uint8Array(),
47
- lamports: account.lamports,
48
- executable: account.executable,
49
- rentEpoch: account.rentEpoch,
50
- },
51
- context,
52
- );
53
-
54
- return context.createSuccessResponse(id, {
55
- context: { slot: Number(context.slot) },
56
- value: {
57
- lamports: Number(account.lamports),
58
- owner,
59
- executable: account.executable,
60
- rentEpoch: Number(account.rentEpoch || 0),
61
- data: parsed || {
62
- program: "unknown",
63
- parsed: null,
64
- space: account.data?.length ?? 0,
65
- },
66
- },
67
- });
68
- }
69
-
70
- const accountInfo = {
71
- lamports: Number(account.lamports),
72
- owner,
73
- data: [Buffer.from(account.data).toString("base64"), "base64"] as const,
74
- executable: account.executable,
75
- rentEpoch: Number(account.rentEpoch || 0),
76
- };
77
-
78
- return context.createSuccessResponse(id, {
79
- context: { slot: Number(context.slot) },
80
- value: accountInfo,
81
- });
82
- } catch (error: unknown) {
83
- const message = error instanceof Error ? error.message : String(error);
84
- return context.createErrorResponse(id, -32602, "Invalid params", message);
85
- }
86
- };
@@ -1,23 +0,0 @@
1
- import { PublicKey } from "@solana/web3.js";
2
- import type { RpcMethodHandler } from "../../types";
3
-
4
- /**
5
- * Implements the getBalance RPC method
6
- * @see https://docs.solana.com/api/http#getbalance
7
- */
8
- export const getBalance: RpcMethodHandler = (id, params, context) => {
9
- const [pubkeyStr] = params;
10
-
11
- try {
12
- const pubkey = new PublicKey(pubkeyStr);
13
- const balance = context.svm.getBalance(pubkey);
14
-
15
- return context.createSuccessResponse(id, {
16
- context: { slot: Number(context.slot) },
17
- value: Number(balance || 0n),
18
- });
19
- } catch (error: unknown) {
20
- const message = error instanceof Error ? error.message : String(error);
21
- return context.createErrorResponse(id, -32602, "Invalid params", message);
22
- }
23
- };
@@ -1,84 +0,0 @@
1
- import { PublicKey } from "@solana/web3.js";
2
- import type { RpcMethodHandler } from "../../types";
3
- import type { AccountSnapshot } from "../../../src/db/tx-store";
4
-
5
- /**
6
- * Implements the getMultipleAccounts RPC method
7
- * @see https://docs.solana.com/api/http#getmultipleaccounts
8
- */
9
- export const getMultipleAccounts: RpcMethodHandler = async (
10
- id,
11
- params,
12
- context,
13
- ) => {
14
- const [pubkeys, config] = params;
15
- const encoding = config?.encoding || "base64";
16
-
17
- const accounts = pubkeys.map((pubkeyStr: string) => {
18
- try {
19
- const pubkey = new PublicKey(pubkeyStr);
20
- const account = context.svm.getAccount(pubkey);
21
-
22
- if (!account) {
23
- return null;
24
- }
25
-
26
- const owner = new PublicKey(account.owner).toBase58();
27
- if (encoding === "jsonParsed") {
28
- const program =
29
- owner === "11111111111111111111111111111111" ? "system" : "unknown";
30
- const space = account.data?.length ?? 0;
31
- return {
32
- lamports: Number(account.lamports),
33
- owner,
34
- executable: account.executable,
35
- rentEpoch: Number(account.rentEpoch || 0),
36
- data: {
37
- program,
38
- parsed: program === "system" ? { type: "account", info: {} } : null,
39
- space,
40
- },
41
- };
42
- }
43
-
44
- return {
45
- lamports: Number(account.lamports),
46
- owner,
47
- data: [Buffer.from(account.data).toString("base64"), "base64"] as const,
48
- executable: account.executable,
49
- rentEpoch: Number(account.rentEpoch || 0),
50
- };
51
- } catch {
52
- return null;
53
- }
54
- });
55
-
56
- // Opportunistic index update
57
- try {
58
- const snaps: AccountSnapshot[] = [];
59
- for (const pubkeyStr of pubkeys) {
60
- try {
61
- const pubkey = new PublicKey(pubkeyStr);
62
- const acc = context.svm.getAccount(pubkey);
63
- if (!acc) continue;
64
- const owner = new PublicKey(acc.owner).toBase58();
65
- snaps.push({
66
- address: pubkey.toBase58(),
67
- lamports: Number(acc.lamports || 0n),
68
- ownerProgram: owner,
69
- executable: !!acc.executable,
70
- rentEpoch: Number(acc.rentEpoch || 0),
71
- dataLen: acc.data?.length ?? 0,
72
- dataBase64: undefined,
73
- lastSlot: Number(context.slot),
74
- });
75
- } catch {}
76
- }
77
- if (snaps.length) await context.store?.upsertAccounts(snaps);
78
- } catch {}
79
-
80
- return context.createSuccessResponse(id, {
81
- context: { slot: Number(context.slot) },
82
- value: accounts,
83
- });
84
- };