solforge 0.1.7 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/.agi/agi.sqlite +0 -0
  2. package/.claude/settings.local.json +9 -0
  3. package/.github/workflows/release-binaries.yml +133 -0
  4. package/.tmp/.787ebcdbf7b8fde8-00000000.hm +0 -0
  5. package/.tmp/.bffe6efebdf8aedc-00000000.hm +0 -0
  6. package/AGENTS.md +271 -0
  7. package/CLAUDE.md +106 -0
  8. package/PROJECT_STRUCTURE.md +124 -0
  9. package/README.md +367 -393
  10. package/SOLANA_KIT_GUIDE.md +251 -0
  11. package/SOLFORGE.md +119 -0
  12. package/biome.json +34 -0
  13. package/bun.lock +743 -0
  14. package/docs/bun-single-file-executable.md +585 -0
  15. package/docs/cli-plan.md +154 -0
  16. package/docs/data-indexing-plan.md +214 -0
  17. package/docs/gui-roadmap.md +202 -0
  18. package/drizzle/0000_friendly_millenium_guard.sql +53 -0
  19. package/drizzle/0001_stale_sentinels.sql +2 -0
  20. package/drizzle/meta/0000_snapshot.json +329 -0
  21. package/drizzle/meta/0001_snapshot.json +345 -0
  22. package/drizzle/meta/_journal.json +20 -0
  23. package/drizzle.config.ts +12 -0
  24. package/index.ts +21 -0
  25. package/mint.sh +47 -0
  26. package/package.json +45 -69
  27. package/postcss.config.js +6 -0
  28. package/rpc-server.ts.backup +519 -0
  29. package/server/index.ts +5 -0
  30. package/server/lib/base58.ts +33 -0
  31. package/server/lib/faucet.ts +110 -0
  32. package/server/lib/spl-token.ts +57 -0
  33. package/server/methods/TEMPLATE.md +117 -0
  34. package/server/methods/account/get-account-info.ts +90 -0
  35. package/server/methods/account/get-balance.ts +27 -0
  36. package/server/methods/account/get-multiple-accounts.ts +83 -0
  37. package/server/methods/account/get-parsed-account-info.ts +21 -0
  38. package/server/methods/account/index.ts +12 -0
  39. package/server/methods/account/parsers/index.ts +52 -0
  40. package/server/methods/account/parsers/loader-upgradeable.ts +66 -0
  41. package/server/methods/account/parsers/spl-token.ts +237 -0
  42. package/server/methods/account/parsers/system.ts +4 -0
  43. package/server/methods/account/request-airdrop.ts +219 -0
  44. package/server/methods/admin/adopt-mint-authority.ts +94 -0
  45. package/server/methods/admin/clone-program-accounts.ts +55 -0
  46. package/server/methods/admin/clone-program.ts +152 -0
  47. package/server/methods/admin/clone-token-accounts.ts +117 -0
  48. package/server/methods/admin/clone-token-mint.ts +82 -0
  49. package/server/methods/admin/create-mint.ts +114 -0
  50. package/server/methods/admin/create-token-account.ts +137 -0
  51. package/server/methods/admin/helpers.ts +70 -0
  52. package/server/methods/admin/index.ts +10 -0
  53. package/server/methods/admin/list-mints.ts +21 -0
  54. package/server/methods/admin/load-program.ts +52 -0
  55. package/server/methods/admin/mint-to.ts +278 -0
  56. package/server/methods/block/get-block-height.ts +5 -0
  57. package/server/methods/block/get-block.ts +35 -0
  58. package/server/methods/block/get-blocks-with-limit.ts +23 -0
  59. package/server/methods/block/get-latest-blockhash.ts +12 -0
  60. package/server/methods/block/get-slot.ts +5 -0
  61. package/server/methods/block/index.ts +6 -0
  62. package/server/methods/block/is-blockhash-valid.ts +23 -0
  63. package/server/methods/epoch/get-cluster-nodes.ts +17 -0
  64. package/server/methods/epoch/get-epoch-info.ts +16 -0
  65. package/server/methods/epoch/get-epoch-schedule.ts +15 -0
  66. package/server/methods/epoch/get-highest-snapshot-slot.ts +9 -0
  67. package/server/methods/epoch/get-leader-schedule.ts +8 -0
  68. package/server/methods/epoch/get-max-retransmit-slot.ts +9 -0
  69. package/server/methods/epoch/get-max-shred-insert-slot.ts +9 -0
  70. package/server/methods/epoch/get-slot-leader.ts +6 -0
  71. package/server/methods/epoch/get-slot-leaders.ts +9 -0
  72. package/server/methods/epoch/get-stake-activation.ts +9 -0
  73. package/server/methods/epoch/get-stake-minimum-delegation.ts +9 -0
  74. package/server/methods/epoch/get-vote-accounts.ts +19 -0
  75. package/server/methods/epoch/index.ts +13 -0
  76. package/server/methods/epoch/minimum-ledger-slot.ts +5 -0
  77. package/server/methods/fee/get-fee-calculator-for-blockhash.ts +12 -0
  78. package/server/methods/fee/get-fee-for-message.ts +8 -0
  79. package/server/methods/fee/get-fee-rate-governor.ts +16 -0
  80. package/server/methods/fee/get-fees.ts +14 -0
  81. package/server/methods/fee/get-recent-prioritization-fees.ts +22 -0
  82. package/server/methods/fee/index.ts +5 -0
  83. package/server/methods/get-address-lookup-table.ts +31 -0
  84. package/server/methods/index.ts +265 -0
  85. package/server/methods/performance/get-recent-performance-samples.ts +25 -0
  86. package/server/methods/performance/get-transaction-count.ts +5 -0
  87. package/server/methods/performance/index.ts +2 -0
  88. package/server/methods/program/get-block-commitment.ts +9 -0
  89. package/server/methods/program/get-block-production.ts +14 -0
  90. package/server/methods/program/get-block-time.ts +21 -0
  91. package/server/methods/program/get-blocks.ts +11 -0
  92. package/server/methods/program/get-first-available-block.ts +9 -0
  93. package/server/methods/program/get-genesis-hash.ts +6 -0
  94. package/server/methods/program/get-identity.ts +6 -0
  95. package/server/methods/program/get-inflation-governor.ts +15 -0
  96. package/server/methods/program/get-inflation-rate.ts +10 -0
  97. package/server/methods/program/get-inflation-reward.ts +12 -0
  98. package/server/methods/program/get-largest-accounts.ts +8 -0
  99. package/server/methods/program/get-parsed-program-accounts.ts +12 -0
  100. package/server/methods/program/get-parsed-token-accounts-by-delegate.ts +12 -0
  101. package/server/methods/program/get-parsed-token-accounts-by-owner.ts +12 -0
  102. package/server/methods/program/get-program-accounts.ts +221 -0
  103. package/server/methods/program/get-supply.ts +13 -0
  104. package/server/methods/program/get-token-account-balance.ts +64 -0
  105. package/server/methods/program/get-token-accounts-by-delegate.ts +81 -0
  106. package/server/methods/program/get-token-accounts-by-owner.ts +390 -0
  107. package/server/methods/program/get-token-largest-accounts.ts +80 -0
  108. package/server/methods/program/get-token-supply.ts +38 -0
  109. package/server/methods/program/index.ts +21 -0
  110. package/server/methods/solforge/index.ts +155 -0
  111. package/server/methods/system/get-health.ts +5 -0
  112. package/server/methods/system/get-minimum-balance-for-rent-exemption.ts +13 -0
  113. package/server/methods/system/get-version.ts +9 -0
  114. package/server/methods/system/index.ts +3 -0
  115. package/server/methods/transaction/get-confirmed-transaction.ts +11 -0
  116. package/server/methods/transaction/get-parsed-transaction.ts +21 -0
  117. package/server/methods/transaction/get-signature-statuses.ts +72 -0
  118. package/server/methods/transaction/get-signatures-for-address.ts +45 -0
  119. package/server/methods/transaction/get-transaction.ts +428 -0
  120. package/server/methods/transaction/index.ts +7 -0
  121. package/server/methods/transaction/send-transaction.ts +232 -0
  122. package/server/methods/transaction/simulate-transaction.ts +56 -0
  123. package/server/rpc-server.ts +474 -0
  124. package/server/types.ts +74 -0
  125. package/server/ws-server.ts +171 -0
  126. package/sf.config.json +38 -0
  127. package/src/cli/bootstrap.ts +67 -0
  128. package/src/cli/commands/airdrop.ts +37 -0
  129. package/src/cli/commands/config.ts +39 -0
  130. package/src/cli/commands/mint.ts +187 -0
  131. package/src/cli/commands/program-clone.ts +124 -0
  132. package/src/cli/commands/program-load.ts +64 -0
  133. package/src/cli/commands/rpc-start.ts +46 -0
  134. package/src/cli/commands/token-adopt-authority.ts +37 -0
  135. package/src/cli/commands/token-clone.ts +113 -0
  136. package/src/cli/commands/token-create.ts +81 -0
  137. package/src/cli/main.ts +130 -0
  138. package/src/cli/run-solforge.ts +98 -0
  139. package/src/cli/setup-utils.ts +54 -0
  140. package/src/cli/setup-wizard.ts +256 -0
  141. package/src/cli/utils/args.ts +15 -0
  142. package/src/config/index.ts +130 -0
  143. package/src/db/index.ts +83 -0
  144. package/src/db/schema/accounts.ts +23 -0
  145. package/src/db/schema/address-signatures.ts +31 -0
  146. package/src/db/schema/index.ts +5 -0
  147. package/src/db/schema/meta-kv.ts +9 -0
  148. package/src/db/schema/transactions.ts +29 -0
  149. package/src/db/schema/tx-accounts.ts +33 -0
  150. package/src/db/tx-store.ts +229 -0
  151. package/src/gui/public/app.css +1 -0
  152. package/src/gui/public/index.html +19 -0
  153. package/src/gui/server.ts +297 -0
  154. package/src/gui/src/api.ts +127 -0
  155. package/src/gui/src/app.tsx +390 -0
  156. package/src/gui/src/components/airdrop-mint-form.tsx +216 -0
  157. package/src/gui/src/components/clone-program-modal.tsx +183 -0
  158. package/src/gui/src/components/clone-token-modal.tsx +211 -0
  159. package/src/gui/src/components/modal.tsx +127 -0
  160. package/src/gui/src/components/programs-panel.tsx +112 -0
  161. package/src/gui/src/components/status-panel.tsx +122 -0
  162. package/src/gui/src/components/tokens-panel.tsx +116 -0
  163. package/src/gui/src/hooks/use-interval.ts +17 -0
  164. package/src/gui/src/index.css +529 -0
  165. package/src/gui/src/main.tsx +17 -0
  166. package/src/migrations-bundled.ts +17 -0
  167. package/src/rpc/start.ts +44 -0
  168. package/tailwind.config.js +27 -0
  169. package/test-client.ts +120 -0
  170. package/tmp/inspect-html.ts +4 -0
  171. package/tmp/response-test.ts +5 -0
  172. package/tmp/test-html.ts +5 -0
  173. package/tmp/test-server.ts +13 -0
  174. package/tsconfig.json +24 -23
  175. package/LICENSE +0 -21
  176. package/scripts/postinstall.cjs +0 -103
  177. package/src/api-server-entry.ts +0 -109
  178. package/src/commands/add-program.ts +0 -337
  179. package/src/commands/init.ts +0 -122
  180. package/src/commands/list.ts +0 -136
  181. package/src/commands/mint.ts +0 -288
  182. package/src/commands/start.ts +0 -877
  183. package/src/commands/status.ts +0 -99
  184. package/src/commands/stop.ts +0 -406
  185. package/src/config/manager.ts +0 -157
  186. package/src/index.ts +0 -188
  187. package/src/services/api-server.ts +0 -485
  188. package/src/services/port-manager.ts +0 -177
  189. package/src/services/process-registry.ts +0 -154
  190. package/src/services/program-cloner.ts +0 -317
  191. package/src/services/token-cloner.ts +0 -809
  192. package/src/services/validator.ts +0 -295
  193. package/src/types/config.ts +0 -110
  194. package/src/utils/shell.ts +0 -110
  195. package/src/utils/token-loader.ts +0 -115
@@ -0,0 +1,9 @@
1
+ import type { RpcMethodHandler } from "../../types";
2
+
3
+ export const getVersion: RpcMethodHandler = (id, _params, _context) => {
4
+ return {
5
+ jsonrpc: "2.0",
6
+ id,
7
+ result: { "solana-core": "1.18.0", "feature-set": 1 },
8
+ };
9
+ };
@@ -0,0 +1,3 @@
1
+ export { getHealth } from "./get-health";
2
+ export { getMinimumBalanceForRentExemption } from "./get-minimum-balance-for-rent-exemption";
3
+ export { getVersion } from "./get-version";
@@ -0,0 +1,11 @@
1
+ import type { RpcMethodHandler } from "../../types";
2
+ import { getTransaction } from "./get-transaction";
3
+
4
+ export const getConfirmedTransaction: RpcMethodHandler = async (
5
+ id,
6
+ params,
7
+ context,
8
+ ) => {
9
+ // Alias to getTransaction for older clients
10
+ return getTransaction(id, params, context);
11
+ };
@@ -0,0 +1,21 @@
1
+ import type { RpcMethodHandler } from "../../types";
2
+ import { getTransaction } from "./get-transaction";
3
+
4
+ export const getParsedTransaction: RpcMethodHandler = async (
5
+ id,
6
+ params,
7
+ context,
8
+ ) => {
9
+ const [signature, config] = params || [];
10
+ const cfg = { ...(config || {}), encoding: "jsonParsed" };
11
+ try {
12
+ return await getTransaction(id, [signature, cfg], context);
13
+ } catch (error: any) {
14
+ return context.createErrorResponse(
15
+ id,
16
+ -32603,
17
+ "Internal error",
18
+ error.message,
19
+ );
20
+ }
21
+ };
@@ -0,0 +1,72 @@
1
+ import type { RpcMethodHandler } from "../../types";
2
+
3
+ export const getSignatureStatuses: RpcMethodHandler = async (
4
+ id,
5
+ params,
6
+ context,
7
+ ) => {
8
+ const [signatures] = params;
9
+
10
+ let persisted: Map<string, { slot: number; err: any | null }> = new Map();
11
+ try {
12
+ persisted = (await context.store?.getStatuses(signatures)) || new Map();
13
+ } catch {}
14
+
15
+ const statuses = signatures.map((sig: string) => {
16
+ try {
17
+ // Prefer locally recorded transactions for reliability with CLI tooling
18
+ const rec = context.getRecordedTransaction(sig);
19
+ if (rec) {
20
+ const errVal: any = rec.err ?? null;
21
+ const status = errVal ? { Err: errVal } : { Ok: null };
22
+ return {
23
+ slot: rec.slot,
24
+ confirmations: errVal ? 0 : null,
25
+ err: errVal,
26
+ confirmationStatus: errVal ? "processed" : "finalized",
27
+ status,
28
+ };
29
+ }
30
+ const db = persisted.get(sig);
31
+ if (db) {
32
+ const errVal: any = db.err ?? null;
33
+ const status = errVal ? { Err: errVal } : { Ok: null };
34
+ return {
35
+ slot: db.slot,
36
+ confirmations: errVal ? 0 : null,
37
+ err: errVal,
38
+ confirmationStatus: errVal ? "processed" : "finalized",
39
+ status,
40
+ };
41
+ }
42
+
43
+ const sigBytes = context.decodeBase58(sig);
44
+ const tx = (context.svm as any).getTransaction(sigBytes);
45
+ if (!tx) return null;
46
+
47
+ let errVal: any = null;
48
+ try {
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ errVal = "err" in tx ? (tx as any).err() : null;
51
+ } catch {
52
+ errVal = null;
53
+ }
54
+ const status = errVal ? { Err: errVal } : { Ok: null };
55
+
56
+ return {
57
+ slot: Number(context.slot),
58
+ confirmations: errVal ? 0 : null,
59
+ err: errVal,
60
+ confirmationStatus: errVal ? "processed" : "finalized",
61
+ status,
62
+ };
63
+ } catch {
64
+ return null;
65
+ }
66
+ });
67
+
68
+ return context.createSuccessResponse(id, {
69
+ context: { slot: Number(context.slot) },
70
+ value: statuses,
71
+ });
72
+ };
@@ -0,0 +1,45 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import type { RpcMethodHandler } from "../../types";
3
+
4
+ export const getSignaturesForAddress: RpcMethodHandler = async (
5
+ id,
6
+ params,
7
+ context,
8
+ ) => {
9
+ try {
10
+ const [address, config] = params || [];
11
+ if (typeof address !== "string") throw new Error("Invalid address");
12
+ // Validate pubkey
13
+ new PublicKey(address);
14
+ const limit = Math.max(1, Math.min(Number(config?.limit ?? 1000), 1000));
15
+ const before =
16
+ typeof config?.before === "string" ? config.before : undefined;
17
+ const until = typeof config?.until === "string" ? config.until : undefined;
18
+
19
+ if (!context.store) return context.createSuccessResponse(id, []);
20
+ try {
21
+ const entries = await context.store.getSignaturesForAddress(address, {
22
+ before,
23
+ until,
24
+ limit,
25
+ });
26
+ return context.createSuccessResponse(id, entries);
27
+ } catch (e) {
28
+ try {
29
+ console.warn("[rpc] getSignaturesForAddress: db read failed", e);
30
+ } catch {}
31
+ // Graceful fallback: return empty list instead of error
32
+ return context.createSuccessResponse(id, []);
33
+ }
34
+ } catch (error: any) {
35
+ try {
36
+ console.error("[rpc] getSignaturesForAddress error", error);
37
+ } catch {}
38
+ return context.createErrorResponse(
39
+ id,
40
+ -32603,
41
+ "Internal error",
42
+ error.message,
43
+ );
44
+ }
45
+ };
@@ -0,0 +1,428 @@
1
+ import { VersionedTransaction } from "@solana/web3.js";
2
+ import type { RpcMethodHandler } from "../../types";
3
+
4
+ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
5
+ const [signature, config] = params || [];
6
+ const encoding = config?.encoding ?? "json";
7
+
8
+ try {
9
+ const rec = context.getRecordedTransaction(signature);
10
+ if (rec) {
11
+ const tx = rec.tx as any;
12
+ if (encoding === "base64") {
13
+ const raw = Buffer.from(tx.serialize()).toString("base64");
14
+ // Top-level version is required by some clients
15
+ const isV0 =
16
+ typeof (tx.message as any)?.version === "number"
17
+ ? (tx.message as any).version === 0
18
+ : true;
19
+ return context.createSuccessResponse(id, {
20
+ slot: rec.slot,
21
+ transaction: [raw, "base64"],
22
+ version: isV0 ? 0 : "legacy",
23
+ meta: {
24
+ status: rec.err ? { Err: rec.err } : { Ok: null },
25
+ err: rec.err ?? null,
26
+ fee: rec.fee,
27
+ loadedAddresses: { writable: [], readonly: [] },
28
+ preBalances: Array.isArray(rec.preBalances) ? rec.preBalances : [],
29
+ postBalances: Array.isArray(rec.postBalances)
30
+ ? rec.postBalances
31
+ : [],
32
+ innerInstructions: [],
33
+ logMessages: rec.logs || [],
34
+ preTokenBalances: Array.isArray((rec as any).preTokenBalances)
35
+ ? (rec as any).preTokenBalances
36
+ : [],
37
+ postTokenBalances: Array.isArray((rec as any).postTokenBalances)
38
+ ? (rec as any).postTokenBalances
39
+ : [],
40
+ rewards: [],
41
+ },
42
+ blockTime: rec.blockTime,
43
+ });
44
+ }
45
+
46
+ const msg: any = tx.message as any;
47
+ const rawKeys1: any[] = Array.isArray(msg.staticAccountKeys)
48
+ ? msg.staticAccountKeys
49
+ : Array.isArray(msg.accountKeys)
50
+ ? msg.accountKeys
51
+ : [];
52
+ const accountKeys = rawKeys1.map((k: any) => {
53
+ try {
54
+ return typeof k === "string" ? k : k.toBase58();
55
+ } catch {
56
+ return String(k);
57
+ }
58
+ });
59
+ const compiled = Array.isArray(msg.compiledInstructions)
60
+ ? msg.compiledInstructions
61
+ : Array.isArray(msg.instructions)
62
+ ? msg.instructions
63
+ : [];
64
+ const instructions = compiled.map((ci: any) => {
65
+ const dataBytes: Uint8Array =
66
+ ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data);
67
+ return {
68
+ programIdIndex: ci.programIdIndex,
69
+ accounts: Array.from(ci.accountKeyIndexes || ci.accounts || []),
70
+ data: context.encodeBase58(dataBytes),
71
+ };
72
+ });
73
+ const addressTableLookups = (msg.addressTableLookups || []).map(
74
+ (l: any) => ({
75
+ accountKey:
76
+ typeof l.accountKey?.toBase58 === "function"
77
+ ? l.accountKey.toBase58()
78
+ : String(l.accountKey),
79
+ writableIndexes: Array.from(l.writableIndexes || []),
80
+ readonlyIndexes: Array.from(l.readonlyIndexes || []),
81
+ }),
82
+ );
83
+ const header = msg.header || {
84
+ numRequiredSignatures: tx.signatures.length,
85
+ numReadonlySignedAccounts: 0,
86
+ numReadonlyUnsignedAccounts: 0,
87
+ };
88
+ const recentBlockhash = msg.recentBlockhash || "";
89
+
90
+ const isV0 = typeof msg.version === "number" ? msg.version === 0 : true;
91
+ const result: any = {
92
+ slot: rec.slot,
93
+ transaction: {
94
+ signatures: [signature],
95
+ message: {
96
+ accountKeys,
97
+ header,
98
+ recentBlockhash,
99
+ instructions,
100
+ addressTableLookups,
101
+ },
102
+ },
103
+ version: isV0 ? 0 : "legacy",
104
+ meta: {
105
+ status: rec.err ? { Err: rec.err } : { Ok: null },
106
+ err: rec.err ?? null,
107
+ fee: rec.fee,
108
+ loadedAddresses: { writable: [], readonly: [] },
109
+ preBalances: Array.isArray(rec.preBalances) ? rec.preBalances : [],
110
+ postBalances: Array.isArray(rec.postBalances) ? rec.postBalances : [],
111
+ innerInstructions: [],
112
+ logMessages: rec.logs || [],
113
+ preTokenBalances: Array.isArray((rec as any).preTokenBalances)
114
+ ? (rec as any).preTokenBalances
115
+ : [],
116
+ postTokenBalances: Array.isArray((rec as any).postTokenBalances)
117
+ ? (rec as any).postTokenBalances
118
+ : [],
119
+ rewards: [],
120
+ },
121
+ blockTime: rec.blockTime,
122
+ };
123
+
124
+ if (encoding === "jsonParsed") {
125
+ const SYSTEM_PROGRAM_ID = "11111111111111111111111111111111";
126
+ const accountKeysParsed = accountKeys.map((pk: string, i: number) => ({
127
+ pubkey: pk,
128
+ signer:
129
+ typeof msg.isAccountSigner === "function"
130
+ ? !!msg.isAccountSigner(i)
131
+ : i < (header?.numRequiredSignatures ?? 0),
132
+ writable:
133
+ typeof msg.isAccountWritable === "function"
134
+ ? !!msg.isAccountWritable(i)
135
+ : i < (header?.numRequiredSignatures ?? 0),
136
+ }));
137
+ const parsedInstructions = compiled.map((ci: any) => {
138
+ const programId = accountKeys[ci.programIdIndex];
139
+ let parsed: any;
140
+ try {
141
+ const data: Uint8Array =
142
+ ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data);
143
+ if (programId === SYSTEM_PROGRAM_ID && data.length >= 12) {
144
+ const dv = new DataView(
145
+ data.buffer,
146
+ data.byteOffset,
147
+ data.byteLength,
148
+ );
149
+ const discriminator = dv.getUint32(0, true);
150
+ if (
151
+ discriminator === 2 &&
152
+ (ci.accountKeyIndexes?.length ?? 0) >= 2
153
+ ) {
154
+ const lamports = Number(dv.getBigUint64(4, true));
155
+ const source = accountKeys[ci.accountKeyIndexes[0]];
156
+ const destination = accountKeys[ci.accountKeyIndexes[1]];
157
+ parsed = {
158
+ type: "transfer",
159
+ info: { source, destination, lamports },
160
+ };
161
+ }
162
+ }
163
+ } catch {}
164
+ if (parsed) return { program: "system", programId, parsed };
165
+ return {
166
+ programId,
167
+ accounts: (ci.accountKeyIndexes || []).map(
168
+ (ix: number) => accountKeys[ix],
169
+ ),
170
+ data: context.encodeBase58(
171
+ ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data),
172
+ ),
173
+ };
174
+ });
175
+ result.transaction.message.accountKeys = accountKeysParsed;
176
+ result.transaction.message.instructions = parsedInstructions;
177
+ }
178
+
179
+ return context.createSuccessResponse(id, result);
180
+ }
181
+
182
+ // Fallback: persistent store
183
+ try {
184
+ const row = await context.store?.getTransaction(signature);
185
+ if (row) {
186
+ const errVal = row.errJson ? JSON.parse(row.errJson) : null;
187
+ const preBalances = JSON.parse(row.preBalancesJson || "[]");
188
+ const postBalances = JSON.parse(row.postBalancesJson || "[]");
189
+ const logs = JSON.parse(row.logsJson || "[]");
190
+ const versionVal =
191
+ row.version === "0" || row.version === 0 ? 0 : row.version;
192
+ if (encoding === "base64") {
193
+ return context.createSuccessResponse(id, {
194
+ slot: Number(row.slot),
195
+ transaction: [row.rawBase64, "base64"],
196
+ version: versionVal,
197
+ meta: {
198
+ status: errVal ? { Err: errVal } : { Ok: null },
199
+ err: errVal,
200
+ fee: Number(row.fee),
201
+ loadedAddresses: { writable: [], readonly: [] },
202
+ preBalances,
203
+ postBalances,
204
+ innerInstructions: [],
205
+ logMessages: logs,
206
+ preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
207
+ postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
208
+ rewards: [],
209
+ },
210
+ blockTime: row.blockTime ? Number(row.blockTime) : null,
211
+ });
212
+ } else if (encoding === "jsonParsed") {
213
+ // Build jsonParsed similar to in-memory path
214
+ const raw = Buffer.from(row.rawBase64, "base64");
215
+ const tx = VersionedTransaction.deserialize(raw);
216
+ const msg: any = tx.message as any;
217
+ const rawKeys2: any[] = Array.isArray(msg.staticAccountKeys)
218
+ ? msg.staticAccountKeys
219
+ : Array.isArray(msg.accountKeys)
220
+ ? msg.accountKeys
221
+ : [];
222
+ const accountKeys = rawKeys2.map((k: any) => {
223
+ try {
224
+ return typeof k === "string" ? k : k.toBase58();
225
+ } catch {
226
+ return String(k);
227
+ }
228
+ });
229
+ const header = msg.header || {
230
+ numRequiredSignatures: tx.signatures.length,
231
+ numReadonlySignedAccounts: 0,
232
+ numReadonlyUnsignedAccounts: 0,
233
+ };
234
+ const compiled = Array.isArray(msg.compiledInstructions)
235
+ ? msg.compiledInstructions
236
+ : Array.isArray(msg.instructions)
237
+ ? msg.instructions
238
+ : [];
239
+ const parsedInstructions = compiled.map((ci: any) => {
240
+ const programId = accountKeys[ci.programIdIndex];
241
+ let parsed: any;
242
+ try {
243
+ const data: Uint8Array =
244
+ ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data);
245
+ // Minimal system transfer parser
246
+ if (
247
+ programId === "11111111111111111111111111111111" &&
248
+ data.length >= 12
249
+ ) {
250
+ const dv = new DataView(
251
+ data.buffer,
252
+ data.byteOffset,
253
+ data.byteLength,
254
+ );
255
+ const discriminator = dv.getUint32(0, true);
256
+ if (
257
+ discriminator === 2 &&
258
+ (ci.accountKeyIndexes?.length ?? 0) >= 2
259
+ ) {
260
+ const lamports = Number(dv.getBigUint64(4, true));
261
+ const source = accountKeys[ci.accountKeyIndexes[0]];
262
+ const destination = accountKeys[ci.accountKeyIndexes[1]];
263
+ parsed = {
264
+ type: "transfer",
265
+ info: { source, destination, lamports },
266
+ };
267
+ }
268
+ }
269
+ } catch {}
270
+ if (parsed) return { program: "system", programId, parsed };
271
+ return {
272
+ programId,
273
+ accounts: (ci.accountKeyIndexes || []).map(
274
+ (ix: number) => accountKeys[ix],
275
+ ),
276
+ data: context.encodeBase58(
277
+ ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data),
278
+ ),
279
+ };
280
+ });
281
+ const accountKeysParsed = accountKeys.map(
282
+ (pk: string, i: number) => ({
283
+ pubkey: pk,
284
+ signer:
285
+ typeof msg.isAccountSigner === "function"
286
+ ? !!msg.isAccountSigner(i)
287
+ : i < (header?.numRequiredSignatures ?? 0),
288
+ writable:
289
+ typeof msg.isAccountWritable === "function"
290
+ ? !!msg.isAccountWritable(i)
291
+ : i < (header?.numRequiredSignatures ?? 0),
292
+ }),
293
+ );
294
+ const result: any = {
295
+ slot: Number(row.slot),
296
+ transaction: {
297
+ signatures: [signature],
298
+ message: {
299
+ accountKeys: accountKeysParsed,
300
+ header,
301
+ recentBlockhash: msg.recentBlockhash || "",
302
+ instructions: parsedInstructions,
303
+ addressTableLookups: msg.addressTableLookups || [],
304
+ },
305
+ },
306
+ version: row.version === "0" || row.version === 0 ? 0 : row.version,
307
+ meta: {
308
+ status: errVal ? { Err: errVal } : { Ok: null },
309
+ err: errVal,
310
+ fee: Number(row.fee),
311
+ loadedAddresses: { writable: [], readonly: [] },
312
+ preBalances,
313
+ postBalances,
314
+ innerInstructions: [],
315
+ logMessages: logs,
316
+ preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
317
+ postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
318
+ rewards: [],
319
+ },
320
+ blockTime: row.blockTime ? Number(row.blockTime) : null,
321
+ };
322
+ return context.createSuccessResponse(id, result);
323
+ } else {
324
+ const raw = Buffer.from(row.rawBase64, "base64");
325
+ const tx = VersionedTransaction.deserialize(raw);
326
+ const msg: any = tx.message as any;
327
+ const rawKeys3: any[] = Array.isArray(msg.staticAccountKeys)
328
+ ? msg.staticAccountKeys
329
+ : Array.isArray(msg.accountKeys)
330
+ ? msg.accountKeys
331
+ : [];
332
+ const accountKeys = rawKeys3.map((k: any) => {
333
+ try {
334
+ return typeof k === "string" ? k : k.toBase58();
335
+ } catch {
336
+ return String(k);
337
+ }
338
+ });
339
+ const header = msg.header || {
340
+ numRequiredSignatures: tx.signatures.length,
341
+ numReadonlySignedAccounts: 0,
342
+ numReadonlyUnsignedAccounts: 0,
343
+ };
344
+ const compiled = Array.isArray(msg.compiledInstructions)
345
+ ? msg.compiledInstructions
346
+ : Array.isArray(msg.instructions)
347
+ ? msg.instructions
348
+ : [];
349
+ const instructions = compiled.map((ci: any) => ({
350
+ programIdIndex: ci.programIdIndex,
351
+ accounts: Array.from(ci.accountKeyIndexes || ci.accounts || []),
352
+ data: context.encodeBase58(
353
+ ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data),
354
+ ),
355
+ }));
356
+ const result: any = {
357
+ slot: Number(row.slot),
358
+ transaction: {
359
+ signatures: [signature],
360
+ message: {
361
+ accountKeys,
362
+ header,
363
+ recentBlockhash: msg.recentBlockhash || "",
364
+ instructions,
365
+ addressTableLookups: msg.addressTableLookups || [],
366
+ },
367
+ },
368
+ version: versionVal,
369
+ meta: {
370
+ status: errVal ? { Err: errVal } : { Ok: null },
371
+ err: errVal,
372
+ fee: Number(row.fee),
373
+ loadedAddresses: { writable: [], readonly: [] },
374
+ preBalances,
375
+ postBalances,
376
+ innerInstructions: [],
377
+ logMessages: logs,
378
+ preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
379
+ postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
380
+ rewards: [],
381
+ },
382
+ blockTime: row.blockTime ? Number(row.blockTime) : null,
383
+ };
384
+ return context.createSuccessResponse(id, result);
385
+ }
386
+ }
387
+ } catch {}
388
+
389
+ // Fallback to LiteSVM history when no local record exists
390
+ const sigBytes = context.decodeBase58(signature);
391
+ const txh = (context.svm as any).getTransaction(sigBytes);
392
+ if (!txh) return context.createSuccessResponse(id, null);
393
+
394
+ const isError = "err" in txh;
395
+ const logs = isError ? txh.meta().logs() : txh.logs();
396
+ const errVal = isError ? txh.err() : null;
397
+ const status = isError ? { Err: errVal } : { Ok: null };
398
+ const isV0 = true;
399
+ return context.createSuccessResponse(id, {
400
+ slot: Number(context.slot),
401
+ transaction: {
402
+ signatures: [signature],
403
+ },
404
+ version: isV0 ? 0 : "legacy",
405
+ meta: {
406
+ status,
407
+ err: errVal,
408
+ fee: 5000,
409
+ loadedAddresses: { writable: [], readonly: [] },
410
+ preBalances: [],
411
+ postBalances: [],
412
+ innerInstructions: [],
413
+ logMessages: logs,
414
+ preTokenBalances: [],
415
+ postTokenBalances: [],
416
+ rewards: [],
417
+ },
418
+ blockTime: Math.floor(Date.now() / 1000),
419
+ });
420
+ } catch (error: any) {
421
+ return context.createErrorResponse(
422
+ id,
423
+ -32602,
424
+ "Invalid params",
425
+ error.message,
426
+ );
427
+ }
428
+ };
@@ -0,0 +1,7 @@
1
+ export { getConfirmedTransaction } from "./get-confirmed-transaction";
2
+ export { getParsedTransaction } from "./get-parsed-transaction";
3
+ export { getSignatureStatuses } from "./get-signature-statuses";
4
+ export { getSignaturesForAddress } from "./get-signatures-for-address";
5
+ export { getTransaction } from "./get-transaction";
6
+ export { sendTransaction } from "./send-transaction";
7
+ export { simulateTransaction } from "./simulate-transaction";