solforge 0.2.8 → 0.2.10

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.
@@ -3,74 +3,88 @@ import type { RpcMethodHandler } from "../../types";
3
3
  import { parseInstruction } from "../../lib/instruction-parser";
4
4
 
5
5
  export const getTransaction: RpcMethodHandler = async (id, params, context) => {
6
- const [signature, config] = params || [];
7
- const encoding = config?.encoding ?? "json";
8
- const DBG = process.env.DEBUG_TX_CAPTURE === "1";
9
- try {
10
- if (DBG) console.debug(`[tx-capture] getTransaction request: sig=${signature} enc=${encoding}`);
11
- } catch {}
6
+ const [signature, config] = params || [];
7
+ const encoding = config?.encoding ?? "json";
8
+ const DBG = process.env.DEBUG_TX_CAPTURE === "1";
9
+ try {
10
+ if (DBG)
11
+ console.debug(
12
+ `[tx-capture] getTransaction request: sig=${signature} enc=${encoding}`,
13
+ );
14
+ } catch {}
12
15
 
13
16
  try {
14
17
  const rec = context.getRecordedTransaction(signature);
15
- if (rec) {
16
- try {
17
- if (DBG)
18
- console.debug(
19
- `[tx-capture] getTransaction hit memory: logs=${rec.logs?.length || 0} inner=${Array.isArray((rec as any).innerInstructions) ? (rec as any).innerInstructions.length : 0}`,
20
- );
21
- } catch {}
22
- const tx = rec.tx;
23
- if (encoding === "base64") {
24
- const raw = Buffer.from(tx.serialize()).toString("base64");
25
- // Top-level version is required by some clients
26
- const isV0 = (() => {
27
- const m = tx.message as unknown as { version?: number };
28
- return typeof m?.version === "number" ? m.version === 0 : true;
29
- })();
30
- return context.createSuccessResponse(id, {
31
- slot: rec.slot,
32
- transaction: [raw, "base64"],
33
- version: isV0 ? 0 : "legacy",
34
- meta: {
35
- status: rec.err ? { Err: rec.err } : { Ok: null },
36
- err: rec.err ?? null,
37
- fee: rec.fee,
38
- loadedAddresses: { writable: [], readonly: [] },
39
- preBalances: Array.isArray(rec.preBalances) ? rec.preBalances : [],
40
- postBalances: Array.isArray(rec.postBalances)
41
- ? rec.postBalances
42
- : [],
43
- innerInstructions: Array.isArray((rec as any).innerInstructions)
44
- ? (rec as any).innerInstructions
45
- : [],
46
- logMessages: rec.logs || [],
47
- preTokenBalances: (() => {
48
- const arr = (rec as unknown as { preTokenBalances?: unknown[] })
49
- .preTokenBalances;
50
- return Array.isArray(arr) ? arr : [];
51
- })(),
52
- postTokenBalances: (() => {
53
- const arr = (rec as unknown as { postTokenBalances?: unknown[] })
54
- .postTokenBalances;
55
- return Array.isArray(arr) ? arr : [];
56
- })(),
57
- computeUnitsConsumed:
58
- typeof (rec as any).computeUnits === "number"
59
- ? (rec as any).computeUnits
60
- : null,
61
- returnData: (() => {
62
- const rd = (rec as any).returnData as
63
- | { programId: string; dataBase64: string }
64
- | null
65
- | undefined;
66
- if (!rd) return null;
67
- return { programId: rd.programId, data: [rd.dataBase64, "base64"] };
68
- })(),
69
- rewards: [],
70
- },
71
- blockTime: rec.blockTime,
72
- });
73
- }
18
+ if (rec) {
19
+ try {
20
+ if (DBG) {
21
+ const innerLen = (() => {
22
+ try {
23
+ const v = (rec as unknown as { innerInstructions?: unknown })
24
+ .innerInstructions;
25
+ return Array.isArray(v) ? v.length : 0;
26
+ } catch {
27
+ return 0;
28
+ }
29
+ })();
30
+ console.debug(
31
+ `[tx-capture] getTransaction hit memory: logs=${rec.logs?.length || 0} inner=${innerLen}`,
32
+ );
33
+ }
34
+ } catch {}
35
+ const tx = rec.tx;
36
+ if (encoding === "base64") {
37
+ const raw = Buffer.from(tx.serialize()).toString("base64");
38
+ // Top-level version is required by some clients
39
+ const isV0 = (() => {
40
+ const m = tx.message as unknown as { version?: number };
41
+ return typeof m?.version === "number" ? m.version === 0 : true;
42
+ })();
43
+ return context.createSuccessResponse(id, {
44
+ slot: rec.slot,
45
+ transaction: [raw, "base64"],
46
+ version: isV0 ? 0 : "legacy",
47
+ meta: {
48
+ status: rec.err ? { Err: rec.err } : { Ok: null },
49
+ err: rec.err ?? null,
50
+ fee: rec.fee,
51
+ loadedAddresses: { writable: [], readonly: [] },
52
+ preBalances: Array.isArray(rec.preBalances) ? rec.preBalances : [],
53
+ postBalances: Array.isArray(rec.postBalances)
54
+ ? rec.postBalances
55
+ : [],
56
+ innerInstructions: Array.isArray(rec.innerInstructions)
57
+ ? rec.innerInstructions
58
+ : [],
59
+ logMessages: rec.logs || [],
60
+ preTokenBalances: (() => {
61
+ const arr = (rec as unknown as { preTokenBalances?: unknown[] })
62
+ .preTokenBalances;
63
+ return Array.isArray(arr) ? arr : [];
64
+ })(),
65
+ postTokenBalances: (() => {
66
+ const arr = (rec as unknown as { postTokenBalances?: unknown[] })
67
+ .postTokenBalances;
68
+ return Array.isArray(arr) ? arr : [];
69
+ })(),
70
+ computeUnitsConsumed:
71
+ typeof rec.computeUnits === "number" ? rec.computeUnits : null,
72
+ returnData: (() => {
73
+ const rd = rec.returnData as
74
+ | { programId: string; dataBase64: string }
75
+ | null
76
+ | undefined;
77
+ if (!rd) return null;
78
+ return {
79
+ programId: rd.programId,
80
+ data: [rd.dataBase64, "base64"],
81
+ };
82
+ })(),
83
+ rewards: [],
84
+ },
85
+ blockTime: rec.blockTime,
86
+ });
87
+ }
74
88
 
75
89
  const msg = tx.message as unknown as {
76
90
  staticAccountKeys?: unknown[];
@@ -157,102 +171,134 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
157
171
  },
158
172
  },
159
173
  version: isV0 ? 0 : "legacy",
160
- meta: {
161
- status: rec.err ? { Err: rec.err } : { Ok: null },
162
- err: rec.err ?? null,
163
- fee: rec.fee,
164
- loadedAddresses: { writable: [], readonly: [] },
165
- preBalances: Array.isArray(rec.preBalances) ? rec.preBalances : [],
166
- postBalances: Array.isArray(rec.postBalances) ? rec.postBalances : [],
167
- innerInstructions: Array.isArray((rec as any).innerInstructions)
168
- ? (rec as any).innerInstructions
169
- : [],
170
- logMessages: rec.logs || [],
171
- preTokenBalances: (() => {
172
- const arr = (rec as unknown as { preTokenBalances?: unknown[] })
173
- .preTokenBalances;
174
- return Array.isArray(arr) ? arr : [];
175
- })(),
176
- postTokenBalances: (() => {
177
- const arr = (rec as unknown as { postTokenBalances?: unknown[] })
178
- .postTokenBalances;
179
- return Array.isArray(arr) ? arr : [];
180
- })(),
181
- computeUnitsConsumed:
182
- typeof (rec as any).computeUnits === "number"
183
- ? (rec as any).computeUnits
184
- : null,
185
- returnData: (() => {
186
- const rd = (rec as any).returnData as
187
- | { programId: string; dataBase64: string }
188
- | null
189
- | undefined;
190
- if (!rd) return null;
191
- return { programId: rd.programId, data: [rd.dataBase64, "base64"] };
192
- })(),
193
- rewards: [],
194
- },
195
- blockTime: rec.blockTime,
196
- };
174
+ meta: {
175
+ status: rec.err ? { Err: rec.err } : { Ok: null },
176
+ err: rec.err ?? null,
177
+ fee: rec.fee,
178
+ loadedAddresses: { writable: [], readonly: [] },
179
+ preBalances: Array.isArray(rec.preBalances) ? rec.preBalances : [],
180
+ postBalances: Array.isArray(rec.postBalances) ? rec.postBalances : [],
181
+ innerInstructions: Array.isArray(rec.innerInstructions)
182
+ ? rec.innerInstructions
183
+ : [],
184
+ logMessages: rec.logs || [],
185
+ preTokenBalances: (() => {
186
+ const arr = (rec as unknown as { preTokenBalances?: unknown[] })
187
+ .preTokenBalances;
188
+ return Array.isArray(arr) ? arr : [];
189
+ })(),
190
+ postTokenBalances: (() => {
191
+ const arr = (rec as unknown as { postTokenBalances?: unknown[] })
192
+ .postTokenBalances;
193
+ return Array.isArray(arr) ? arr : [];
194
+ })(),
195
+ computeUnitsConsumed:
196
+ typeof rec.computeUnits === "number" ? rec.computeUnits : null,
197
+ returnData: (() => {
198
+ const rd = rec.returnData as
199
+ | { programId: string; dataBase64: string }
200
+ | null
201
+ | undefined;
202
+ if (!rd) return null;
203
+ return { programId: rd.programId, data: [rd.dataBase64, "base64"] };
204
+ })(),
205
+ rewards: [],
206
+ },
207
+ blockTime: rec.blockTime,
208
+ };
197
209
 
198
- if (encoding === "jsonParsed") {
199
- const accountKeysParsed = accountKeys.map((pk: string, i: number) => ({
200
- pubkey: pk,
201
- signer:
202
- typeof msg.isAccountSigner === "function"
203
- ? !!msg.isAccountSigner(i)
204
- : i < (header?.numRequiredSignatures ?? 0),
205
- writable:
206
- typeof msg.isAccountWritable === "function"
207
- ? !!msg.isAccountWritable(i)
208
- : i < (header?.numRequiredSignatures ?? 0),
209
- }));
210
- const parsedInstructions = compiled.map((ci) => {
211
- const c = ci as {
212
- programIdIndex: number;
213
- accountKeyIndexes?: number[];
214
- accounts?: number[];
215
- data: Uint8Array | number[];
216
- };
217
- const dataBytes: Uint8Array =
218
- c.data instanceof Uint8Array ? c.data : Buffer.from(c.data);
219
- const accountsIdx = Array.from(
220
- c.accountKeyIndexes || c.accounts || [],
221
- );
222
- const programId = accountKeys[c.programIdIndex];
223
- return parseInstruction(
224
- programId,
225
- accountsIdx,
226
- context.encodeBase58(dataBytes),
227
- accountKeys,
228
- );
229
- });
230
- (result.transaction.message as { accountKeys: unknown[] }).accountKeys =
231
- accountKeysParsed;
232
- (
233
- result.transaction.message as { instructions: unknown[] }
234
- ).instructions = parsedInstructions as unknown[];
235
- // Parse inner instructions using the same parser
236
- try {
237
- const inner = (result.meta as any)?.innerInstructions as
238
- | Array<{ index: number; instructions: any[] }>
239
- | undefined;
240
- if (Array.isArray(inner)) {
241
- const parsedInner = inner.map((group) => ({
242
- index: group.index,
243
- instructions: (group.instructions || []).map((ii) => {
244
- const accountsIdx = Array.isArray(ii.accounts)
245
- ? ii.accounts
246
- : [];
247
- const dataB58 = typeof ii.data === "string" ? ii.data : String(ii.data ?? "");
248
- const pid = accountKeys[ii.programIdIndex ?? 0] || accountKeys[0];
249
- return parseInstruction(pid, accountsIdx, dataB58, accountKeys);
250
- }),
251
- }));
252
- (result.meta as any).innerInstructions = parsedInner;
253
- }
254
- } catch {}
255
- }
210
+ if (encoding === "jsonParsed") {
211
+ const accountKeysParsed = accountKeys.map((pk: string, i: number) => ({
212
+ pubkey: pk,
213
+ signer:
214
+ typeof msg.isAccountSigner === "function"
215
+ ? !!msg.isAccountSigner(i)
216
+ : i < (header?.numRequiredSignatures ?? 0),
217
+ writable:
218
+ typeof msg.isAccountWritable === "function"
219
+ ? !!msg.isAccountWritable(i)
220
+ : i < (header?.numRequiredSignatures ?? 0),
221
+ }));
222
+ // Collect token balance hints: (mint, decimals) pairs to help identify mint when keys are missing
223
+ const preTbs = ((result as unknown as { meta?: { preTokenBalances?: unknown[] } })?.meta?.preTokenBalances || []) as Array<{
224
+ mint?: string;
225
+ uiTokenAmount?: { decimals?: number };
226
+ }>;
227
+ const postTbs = ((result as unknown as { meta?: { postTokenBalances?: unknown[] } })?.meta?.postTokenBalances || []) as Array<{
228
+ mint?: string;
229
+ uiTokenAmount?: { decimals?: number };
230
+ }>;
231
+ const tokenBalanceHints: Array<{ mint: string; decimals: number }> = [];
232
+ for (const tb of [...preTbs, ...postTbs]) {
233
+ try {
234
+ const mint = tb?.mint ? String(tb.mint) : "";
235
+ const decimals = Number(tb?.uiTokenAmount?.decimals ?? 0);
236
+ if (mint) tokenBalanceHints.push({ mint, decimals });
237
+ } catch {}
238
+ }
239
+ const parsedInstructions = compiled.map((ci) => {
240
+ const c = ci as {
241
+ programIdIndex: number;
242
+ accountKeyIndexes?: number[];
243
+ accounts?: number[];
244
+ data: Uint8Array | number[];
245
+ };
246
+ const dataBytes: Uint8Array =
247
+ c.data instanceof Uint8Array ? c.data : Buffer.from(c.data);
248
+ const accountsIdx = Array.from(
249
+ c.accountKeyIndexes || c.accounts || [],
250
+ );
251
+ const programId = accountKeys[c.programIdIndex];
252
+ return parseInstruction(
253
+ programId,
254
+ accountsIdx,
255
+ context.encodeBase58(dataBytes),
256
+ accountKeys,
257
+ tokenBalanceHints,
258
+ );
259
+ });
260
+ (result.transaction.message as { accountKeys: unknown[] }).accountKeys =
261
+ accountKeysParsed;
262
+ (
263
+ result.transaction.message as { instructions: unknown[] }
264
+ ).instructions = parsedInstructions as unknown[];
265
+ // Parse inner instructions using the same parser
266
+ try {
267
+ const inner = (
268
+ result.meta as unknown as {
269
+ innerInstructions?: unknown;
270
+ }
271
+ )?.innerInstructions as
272
+ | Array<{ index: number; instructions: unknown[] }>
273
+ | undefined;
274
+ if (Array.isArray(inner)) {
275
+ const parsedInner = inner.map((group) => ({
276
+ index: group.index,
277
+ instructions: (group.instructions || []).map((ii) => {
278
+ const accountsIdx = Array.isArray(ii.accounts)
279
+ ? ii.accounts
280
+ : [];
281
+ const dataB58 =
282
+ typeof ii.data === "string" ? ii.data : String(ii.data ?? "");
283
+ const pid =
284
+ accountKeys[ii.programIdIndex ?? 0] || accountKeys[0];
285
+ return parseInstruction(
286
+ pid,
287
+ accountsIdx,
288
+ dataB58,
289
+ accountKeys,
290
+ tokenBalanceHints,
291
+ );
292
+ }),
293
+ }));
294
+ (
295
+ result as unknown as {
296
+ meta: { innerInstructions?: unknown };
297
+ }
298
+ ).meta.innerInstructions = parsedInner as unknown[];
299
+ }
300
+ } catch {}
301
+ }
256
302
 
257
303
  return context.createSuccessResponse(id, result);
258
304
  }
@@ -260,54 +306,54 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
260
306
  // Fallback: persistent store
261
307
  try {
262
308
  const row = await context.store?.getTransaction(signature);
263
- if (row) {
264
- try {
265
- if (DBG)
266
- console.debug(
267
- `[tx-capture] getTransaction hit sqlite: slot=${row.slot} logs=${JSON.parse(row.logsJson || '[]').length} inner=${JSON.parse(row.innerInstructionsJson || '[]').length}`,
268
- );
269
- } catch {}
270
- const errVal = row.errJson ? JSON.parse(row.errJson) : null;
271
- const preBalances = JSON.parse(row.preBalancesJson || "[]");
272
- const postBalances = JSON.parse(row.postBalancesJson || "[]");
273
- const logs = JSON.parse(row.logsJson || "[]");
274
- const inner = JSON.parse(row.innerInstructionsJson || "[]");
275
- const versionVal =
276
- row.version === "0" || row.version === 0 ? 0 : row.version;
277
- if (encoding === "base64") {
278
- return context.createSuccessResponse(id, {
279
- slot: Number(row.slot),
280
- transaction: [row.rawBase64, "base64"],
281
- version: versionVal,
282
- meta: {
283
- status: errVal ? { Err: errVal } : { Ok: null },
284
- err: errVal,
285
- fee: Number(row.fee),
286
- loadedAddresses: { writable: [], readonly: [] },
287
- preBalances,
288
- postBalances,
289
- innerInstructions: Array.isArray(inner) ? inner : [],
290
- logMessages: logs,
291
- preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
292
- postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
293
- computeUnitsConsumed:
294
- row.computeUnits != null ? Number(row.computeUnits) : null,
295
- returnData: (() => {
296
- if (row.returnDataProgramId && row.returnDataBase64)
297
- return {
298
- programId: row.returnDataProgramId,
299
- data: [row.returnDataBase64, "base64"],
300
- };
301
- return null;
302
- })(),
303
- rewards: [],
304
- },
305
- blockTime: row.blockTime ? Number(row.blockTime) : null,
306
- });
307
- } else if (encoding === "jsonParsed") {
308
- // Build jsonParsed similar to in-memory path
309
- const raw = Buffer.from(row.rawBase64, "base64");
310
- const tx = VersionedTransaction.deserialize(raw);
309
+ if (row) {
310
+ try {
311
+ if (DBG)
312
+ console.debug(
313
+ `[tx-capture] getTransaction hit sqlite: slot=${row.slot} logs=${JSON.parse(row.logsJson || "[]").length} inner=${JSON.parse(row.innerInstructionsJson || "[]").length}`,
314
+ );
315
+ } catch {}
316
+ const errVal = row.errJson ? JSON.parse(row.errJson) : null;
317
+ const preBalances = JSON.parse(row.preBalancesJson || "[]");
318
+ const postBalances = JSON.parse(row.postBalancesJson || "[]");
319
+ const logs = JSON.parse(row.logsJson || "[]");
320
+ const inner = JSON.parse(row.innerInstructionsJson || "[]");
321
+ const versionVal =
322
+ row.version === "0" || row.version === 0 ? 0 : row.version;
323
+ if (encoding === "base64") {
324
+ return context.createSuccessResponse(id, {
325
+ slot: Number(row.slot),
326
+ transaction: [row.rawBase64, "base64"],
327
+ version: versionVal,
328
+ meta: {
329
+ status: errVal ? { Err: errVal } : { Ok: null },
330
+ err: errVal,
331
+ fee: Number(row.fee),
332
+ loadedAddresses: { writable: [], readonly: [] },
333
+ preBalances,
334
+ postBalances,
335
+ innerInstructions: Array.isArray(inner) ? inner : [],
336
+ logMessages: logs,
337
+ preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
338
+ postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
339
+ computeUnitsConsumed:
340
+ row.computeUnits != null ? Number(row.computeUnits) : null,
341
+ returnData: (() => {
342
+ if (row.returnDataProgramId && row.returnDataBase64)
343
+ return {
344
+ programId: row.returnDataProgramId,
345
+ data: [row.returnDataBase64, "base64"],
346
+ };
347
+ return null;
348
+ })(),
349
+ rewards: [],
350
+ },
351
+ blockTime: row.blockTime ? Number(row.blockTime) : null,
352
+ });
353
+ } else if (encoding === "jsonParsed") {
354
+ // Build jsonParsed similar to in-memory path
355
+ const raw = Buffer.from(row.rawBase64, "base64");
356
+ const tx = VersionedTransaction.deserialize(raw);
311
357
  const msg = tx.message as unknown as {
312
358
  staticAccountKeys?: unknown[];
313
359
  accountKeys?: unknown[];
@@ -343,26 +389,26 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
343
389
  : Array.isArray(msg.instructions)
344
390
  ? msg.instructions
345
391
  : [];
346
- const parsedInstructions = compiled.map((ci) => {
347
- const c = ci as {
348
- programIdIndex: number;
349
- accountKeyIndexes?: number[];
350
- accounts?: number[];
351
- data: Uint8Array | number[];
352
- };
353
- const dataBytes: Uint8Array =
354
- c.data instanceof Uint8Array ? c.data : Buffer.from(c.data);
355
- const accountsIdx = Array.from(
356
- c.accountKeyIndexes || c.accounts || [],
357
- );
358
- const programId = accountKeys[c.programIdIndex];
359
- return parseInstruction(
360
- programId,
361
- accountsIdx,
362
- context.encodeBase58(dataBytes),
363
- accountKeys,
364
- );
365
- });
392
+ const parsedInstructions = compiled.map((ci) => {
393
+ const c = ci as {
394
+ programIdIndex: number;
395
+ accountKeyIndexes?: number[];
396
+ accounts?: number[];
397
+ data: Uint8Array | number[];
398
+ };
399
+ const dataBytes: Uint8Array =
400
+ c.data instanceof Uint8Array ? c.data : Buffer.from(c.data);
401
+ const accountsIdx = Array.from(
402
+ c.accountKeyIndexes || c.accounts || [],
403
+ );
404
+ const programId = accountKeys[c.programIdIndex];
405
+ return parseInstruction(
406
+ programId,
407
+ accountsIdx,
408
+ context.encodeBase58(dataBytes),
409
+ accountKeys,
410
+ );
411
+ });
366
412
  const accountKeysParsed = accountKeys.map(
367
413
  (pk: string, i: number) => ({
368
414
  pubkey: pk,
@@ -376,65 +422,83 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
376
422
  : i < (header?.numRequiredSignatures ?? 0),
377
423
  }),
378
424
  );
379
- const result = {
380
- slot: Number(row.slot),
381
- transaction: {
382
- signatures: [signature],
383
- message: {
384
- accountKeys: accountKeysParsed,
385
- header,
386
- recentBlockhash: msg.recentBlockhash || "",
387
- instructions: parsedInstructions,
388
- addressTableLookups: msg.addressTableLookups || [],
389
- },
390
- },
391
- version: row.version === "0" || row.version === 0 ? 0 : row.version,
392
- meta: {
393
- status: errVal ? { Err: errVal } : { Ok: null },
394
- err: errVal,
395
- fee: Number(row.fee),
396
- loadedAddresses: { writable: [], readonly: [] },
397
- preBalances,
398
- postBalances,
399
- innerInstructions: Array.isArray(inner) ? inner : [],
400
- logMessages: logs,
401
- preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
402
- postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
403
- computeUnitsConsumed:
404
- row.computeUnits != null ? Number(row.computeUnits) : null,
405
- returnData: (() => {
406
- if (row.returnDataProgramId && row.returnDataBase64)
407
- return {
408
- programId: row.returnDataProgramId,
409
- data: [row.returnDataBase64, "base64"],
410
- };
411
- return null;
412
- })(),
413
- rewards: [],
414
- },
415
- blockTime: row.blockTime ? Number(row.blockTime) : null,
416
- };
417
- // Also parse inner instructions from DB row
418
- try {
419
- const innerSrc = JSON.parse(row.innerInstructionsJson || "[]");
420
- const innerParsed = (innerSrc as any[]).map((group) => ({
421
- index: Number(group.index || 0),
422
- instructions: Array.isArray(group.instructions)
423
- ? group.instructions.map((ii: any) =>
424
- parseInstruction(
425
- accountKeys[ii.programIdIndex ?? 0] || accountKeys[0],
426
- Array.isArray(ii.accounts) ? ii.accounts : [],
427
- typeof ii.data === "string" ? ii.data : String(ii.data ?? ""),
428
- accountKeys,
429
- ),
430
- )
431
- : [],
432
- }));
433
- (result as any).meta.innerInstructions = innerParsed;
434
- } catch {}
425
+ const result = {
426
+ slot: Number(row.slot),
427
+ transaction: {
428
+ signatures: [signature],
429
+ message: {
430
+ accountKeys: accountKeysParsed,
431
+ header,
432
+ recentBlockhash: msg.recentBlockhash || "",
433
+ instructions: parsedInstructions,
434
+ addressTableLookups: msg.addressTableLookups || [],
435
+ },
436
+ },
437
+ version: row.version === "0" || row.version === 0 ? 0 : row.version,
438
+ meta: {
439
+ status: errVal ? { Err: errVal } : { Ok: null },
440
+ err: errVal,
441
+ fee: Number(row.fee),
442
+ loadedAddresses: { writable: [], readonly: [] },
443
+ preBalances,
444
+ postBalances,
445
+ innerInstructions: Array.isArray(inner) ? inner : [],
446
+ logMessages: logs,
447
+ preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
448
+ postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
449
+ computeUnitsConsumed:
450
+ row.computeUnits != null ? Number(row.computeUnits) : null,
451
+ returnData: (() => {
452
+ if (row.returnDataProgramId && row.returnDataBase64)
453
+ return {
454
+ programId: row.returnDataProgramId,
455
+ data: [row.returnDataBase64, "base64"],
456
+ };
457
+ return null;
458
+ })(),
459
+ rewards: [],
460
+ },
461
+ blockTime: row.blockTime ? Number(row.blockTime) : null,
462
+ };
463
+ // Also parse inner instructions from DB row
464
+ try {
465
+ const innerSrc = JSON.parse(row.innerInstructionsJson || "[]");
466
+ const groups: unknown[] = Array.isArray(innerSrc) ? innerSrc : [];
467
+ const innerParsed = groups.map((group) => {
468
+ const g = group as Record<string, unknown>;
469
+ const instrSrc = Array.isArray(g.instructions)
470
+ ? (g.instructions as unknown[])
471
+ : [];
472
+ const instructions = instrSrc.map((ii) => {
473
+ const r = ii as Record<string, unknown>;
474
+ const pidIdx =
475
+ typeof r.programIdIndex === "number" ? r.programIdIndex : 0;
476
+ const accountsIdx = Array.isArray(r.accounts)
477
+ ? (r.accounts as unknown[]).filter(
478
+ (v): v is number => typeof v === "number",
479
+ )
480
+ : [];
481
+ const dataB58 =
482
+ typeof r.data === "string" ? r.data : String(r.data ?? "");
483
+ return parseInstruction(
484
+ accountKeys[pidIdx] || accountKeys[0],
485
+ accountsIdx,
486
+ dataB58,
487
+ accountKeys,
488
+ );
489
+ });
490
+ return {
491
+ index: Number(g.index || 0),
492
+ instructions,
493
+ };
494
+ });
495
+ (
496
+ result as unknown as { meta: { innerInstructions?: unknown } }
497
+ ).meta.innerInstructions = innerParsed as unknown[];
498
+ } catch {}
435
499
 
436
- return context.createSuccessResponse(id, result);
437
- } else {
500
+ return context.createSuccessResponse(id, result);
501
+ } else {
438
502
  const raw = Buffer.from(row.rawBase64, "base64");
439
503
  const tx = VersionedTransaction.deserialize(raw);
440
504
  const msg = tx.message as unknown as {
@@ -485,46 +549,46 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
485
549
  ),
486
550
  };
487
551
  });
488
- const result = {
489
- slot: Number(row.slot),
490
- transaction: {
491
- signatures: [signature],
492
- message: {
493
- accountKeys,
494
- header,
495
- recentBlockhash: msg.recentBlockhash || "",
496
- instructions,
497
- addressTableLookups: msg.addressTableLookups || [],
498
- },
499
- },
500
- version: versionVal,
501
- meta: {
502
- status: errVal ? { Err: errVal } : { Ok: null },
503
- err: errVal,
504
- fee: Number(row.fee),
505
- loadedAddresses: { writable: [], readonly: [] },
506
- preBalances,
507
- postBalances,
508
- innerInstructions: Array.isArray(inner) ? inner : [],
509
- logMessages: logs,
510
- preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
511
- postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
512
- computeUnitsConsumed:
513
- row.computeUnits != null ? Number(row.computeUnits) : null,
514
- returnData: (() => {
515
- if (row.returnDataProgramId && row.returnDataBase64)
516
- return {
517
- programId: row.returnDataProgramId,
518
- data: [row.returnDataBase64, "base64"],
519
- };
520
- return null;
521
- })(),
522
- rewards: [],
523
- },
524
- blockTime: row.blockTime ? Number(row.blockTime) : null,
525
- };
526
- return context.createSuccessResponse(id, result);
527
- }
552
+ const result = {
553
+ slot: Number(row.slot),
554
+ transaction: {
555
+ signatures: [signature],
556
+ message: {
557
+ accountKeys,
558
+ header,
559
+ recentBlockhash: msg.recentBlockhash || "",
560
+ instructions,
561
+ addressTableLookups: msg.addressTableLookups || [],
562
+ },
563
+ },
564
+ version: versionVal,
565
+ meta: {
566
+ status: errVal ? { Err: errVal } : { Ok: null },
567
+ err: errVal,
568
+ fee: Number(row.fee),
569
+ loadedAddresses: { writable: [], readonly: [] },
570
+ preBalances,
571
+ postBalances,
572
+ innerInstructions: Array.isArray(inner) ? inner : [],
573
+ logMessages: logs,
574
+ preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
575
+ postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
576
+ computeUnitsConsumed:
577
+ row.computeUnits != null ? Number(row.computeUnits) : null,
578
+ returnData: (() => {
579
+ if (row.returnDataProgramId && row.returnDataBase64)
580
+ return {
581
+ programId: row.returnDataProgramId,
582
+ data: [row.returnDataBase64, "base64"],
583
+ };
584
+ return null;
585
+ })(),
586
+ rewards: [],
587
+ },
588
+ blockTime: row.blockTime ? Number(row.blockTime) : null,
589
+ };
590
+ return context.createSuccessResponse(id, result);
591
+ }
528
592
  }
529
593
  } catch {}
530
594