solforge 0.2.7 → 0.2.9
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 -1
- package/scripts/decode-b58.ts +10 -0
- package/server/lib/instruction-parser.ts +305 -219
- package/server/lib/parsers/spl-associated-token-account.ts +50 -0
- package/server/lib/parsers/spl-token.ts +315 -0
- package/server/methods/account/request-airdrop.ts +121 -105
- package/server/methods/admin/mint-to.ts +29 -14
- package/server/methods/transaction/get-transaction.ts +390 -326
- package/server/methods/transaction/inner-instructions.test.ts +91 -50
- package/server/methods/transaction/send-transaction.ts +281 -236
- package/server/rpc-server.ts +84 -74
- package/server/types.ts +56 -56
- package/src/db/schema/transactions.ts +29 -29
- package/src/db/schema/tx-account-states.ts +16 -14
- package/src/db/tx-store.ts +97 -99
- package/src/migrations-bundled.ts +4 -4
|
@@ -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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
437
|
-
|
|
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
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
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
|
|