solforge 0.2.5 → 0.2.7
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/postinstall.cjs +3 -3
- package/server/lib/base58.ts +1 -1
- package/server/lib/instruction-parser.ts +242 -0
- package/server/methods/account/get-account-info.ts +3 -7
- package/server/methods/account/get-balance.ts +3 -7
- package/server/methods/account/get-multiple-accounts.ts +2 -1
- package/server/methods/account/get-parsed-account-info.ts +3 -7
- package/server/methods/account/parsers/index.ts +2 -2
- package/server/methods/account/parsers/loader-upgradeable.ts +14 -1
- package/server/methods/account/parsers/spl-token.ts +29 -10
- package/server/methods/account/request-airdrop.ts +122 -86
- package/server/methods/admin/mint-to.ts +11 -38
- package/server/methods/block/get-block.ts +3 -7
- package/server/methods/block/get-blocks-with-limit.ts +3 -7
- package/server/methods/block/is-blockhash-valid.ts +3 -7
- package/server/methods/get-address-lookup-table.ts +3 -7
- package/server/methods/program/get-program-accounts.ts +9 -9
- package/server/methods/program/get-token-account-balance.ts +3 -7
- package/server/methods/program/get-token-accounts-by-delegate.ts +4 -3
- package/server/methods/program/get-token-accounts-by-owner.ts +54 -33
- package/server/methods/program/get-token-largest-accounts.ts +3 -2
- package/server/methods/program/get-token-supply.ts +3 -2
- package/server/methods/solforge/index.ts +9 -6
- package/server/methods/transaction/get-parsed-transaction.ts +3 -7
- package/server/methods/transaction/get-signature-statuses.ts +14 -7
- package/server/methods/transaction/get-signatures-for-address.ts +3 -7
- package/server/methods/transaction/get-transaction.ts +434 -287
- package/server/methods/transaction/inner-instructions.test.ts +63 -0
- package/server/methods/transaction/send-transaction.ts +248 -56
- package/server/methods/transaction/simulate-transaction.ts +3 -2
- package/server/rpc-server.ts +98 -61
- package/server/types.ts +65 -30
- package/server/ws-server.ts +11 -7
- package/src/api-server-entry.ts +5 -5
- package/src/cli/commands/airdrop.ts +2 -2
- package/src/cli/commands/config.ts +2 -2
- package/src/cli/commands/mint.ts +3 -3
- package/src/cli/commands/program-clone.ts +9 -11
- package/src/cli/commands/program-load.ts +3 -3
- package/src/cli/commands/rpc-start.ts +7 -7
- package/src/cli/commands/token-adopt-authority.ts +1 -1
- package/src/cli/commands/token-clone.ts +5 -6
- package/src/cli/commands/token-create.ts +5 -5
- package/src/cli/main.ts +33 -36
- package/src/cli/run-solforge.ts +3 -3
- package/src/cli/setup-wizard.ts +8 -6
- package/src/commands/add-program.ts +1 -1
- package/src/commands/init.ts +2 -2
- package/src/commands/mint.ts +5 -6
- package/src/commands/start.ts +10 -9
- package/src/commands/status.ts +1 -1
- package/src/commands/stop.ts +1 -1
- package/src/config/index.ts +33 -17
- package/src/config/manager.ts +3 -3
- package/src/db/index.ts +2 -2
- package/src/db/schema/index.ts +1 -0
- package/src/db/schema/transactions.ts +29 -22
- package/src/db/schema/tx-account-states.ts +21 -0
- package/src/db/tx-store.ts +113 -76
- package/src/gui/public/app.css +13 -13
- package/src/gui/server.ts +1 -1
- package/src/gui/src/api.ts +1 -1
- package/src/gui/src/app.tsx +49 -17
- package/src/gui/src/components/airdrop-mint-form.tsx +32 -8
- package/src/gui/src/components/clone-program-modal.tsx +25 -6
- package/src/gui/src/components/clone-token-modal.tsx +25 -6
- package/src/gui/src/components/modal.tsx +6 -1
- package/src/gui/src/components/status-panel.tsx +1 -1
- package/src/index.ts +19 -6
- package/src/migrations-bundled.ts +8 -2
- package/src/services/api-server.ts +41 -19
- package/src/services/port-manager.ts +7 -10
- package/src/services/process-registry.ts +4 -5
- package/src/services/program-cloner.ts +4 -4
- package/src/services/token-cloner.ts +4 -4
- package/src/services/validator.ts +2 -4
- package/src/types/config.ts +2 -2
- package/src/utils/shell.ts +1 -1
- package/src/utils/token-loader.ts +2 -2
|
@@ -1,85 +1,141 @@
|
|
|
1
1
|
import { VersionedTransaction } from "@solana/web3.js";
|
|
2
2
|
import type { RpcMethodHandler } from "../../types";
|
|
3
|
+
import { parseInstruction } from "../../lib/instruction-parser";
|
|
3
4
|
|
|
4
5
|
export const getTransaction: RpcMethodHandler = async (id, params, context) => {
|
|
5
|
-
|
|
6
|
-
|
|
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 {}
|
|
7
12
|
|
|
8
13
|
try {
|
|
9
14
|
const rec = context.getRecordedTransaction(signature);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
45
74
|
|
|
46
|
-
const msg
|
|
47
|
-
|
|
75
|
+
const msg = tx.message as unknown as {
|
|
76
|
+
staticAccountKeys?: unknown[];
|
|
77
|
+
accountKeys?: unknown[];
|
|
78
|
+
compiledInstructions?: unknown[];
|
|
79
|
+
instructions?: unknown[];
|
|
80
|
+
header?: unknown;
|
|
81
|
+
recentBlockhash?: string;
|
|
82
|
+
version?: number;
|
|
83
|
+
addressTableLookups?: unknown[];
|
|
84
|
+
isAccountSigner?: (i: number) => boolean;
|
|
85
|
+
isAccountWritable?: (i: number) => boolean;
|
|
86
|
+
};
|
|
87
|
+
const rawKeys1: unknown[] = Array.isArray(msg.staticAccountKeys)
|
|
48
88
|
? msg.staticAccountKeys
|
|
49
89
|
: Array.isArray(msg.accountKeys)
|
|
50
90
|
? msg.accountKeys
|
|
51
91
|
: [];
|
|
52
|
-
const accountKeys = rawKeys1.map((k
|
|
92
|
+
const accountKeys = rawKeys1.map((k) => {
|
|
53
93
|
try {
|
|
54
|
-
return typeof k === "string"
|
|
94
|
+
return typeof k === "string"
|
|
95
|
+
? k
|
|
96
|
+
: (k as { toBase58: () => string }).toBase58();
|
|
55
97
|
} catch {
|
|
56
98
|
return String(k);
|
|
57
99
|
}
|
|
58
100
|
});
|
|
59
|
-
const compiled = Array.isArray(msg.compiledInstructions)
|
|
101
|
+
const compiled: unknown[] = Array.isArray(msg.compiledInstructions)
|
|
60
102
|
? msg.compiledInstructions
|
|
61
103
|
: Array.isArray(msg.instructions)
|
|
62
104
|
? msg.instructions
|
|
63
105
|
: [];
|
|
64
|
-
const instructions = compiled.map((ci
|
|
106
|
+
const instructions = compiled.map((ci) => {
|
|
107
|
+
const c = ci as {
|
|
108
|
+
programIdIndex: number;
|
|
109
|
+
accountKeyIndexes?: number[];
|
|
110
|
+
accounts?: number[];
|
|
111
|
+
data: Uint8Array | number[];
|
|
112
|
+
};
|
|
65
113
|
const dataBytes: Uint8Array =
|
|
66
|
-
|
|
114
|
+
c.data instanceof Uint8Array ? c.data : Buffer.from(c.data);
|
|
67
115
|
return {
|
|
68
|
-
programIdIndex:
|
|
69
|
-
accounts: Array.from(
|
|
116
|
+
programIdIndex: c.programIdIndex,
|
|
117
|
+
accounts: Array.from(c.accountKeyIndexes || c.accounts || []),
|
|
70
118
|
data: context.encodeBase58(dataBytes),
|
|
71
119
|
};
|
|
72
120
|
});
|
|
73
|
-
const addressTableLookups = (
|
|
74
|
-
(
|
|
121
|
+
const addressTableLookups = (
|
|
122
|
+
Array.isArray(msg.addressTableLookups) ? msg.addressTableLookups : []
|
|
123
|
+
).map((l) => {
|
|
124
|
+
const a = l as {
|
|
125
|
+
accountKey?: { toBase58?: () => string } | string;
|
|
126
|
+
writableIndexes?: number[];
|
|
127
|
+
readonlyIndexes?: number[];
|
|
128
|
+
};
|
|
129
|
+
return {
|
|
75
130
|
accountKey:
|
|
76
|
-
typeof
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
131
|
+
typeof (a.accountKey as { toBase58?: unknown })?.toBase58 ===
|
|
132
|
+
"function"
|
|
133
|
+
? (a.accountKey as { toBase58: () => string }).toBase58()
|
|
134
|
+
: String(a.accountKey),
|
|
135
|
+
writableIndexes: Array.from(a.writableIndexes || []),
|
|
136
|
+
readonlyIndexes: Array.from(a.readonlyIndexes || []),
|
|
137
|
+
};
|
|
138
|
+
});
|
|
83
139
|
const header = msg.header || {
|
|
84
140
|
numRequiredSignatures: tx.signatures.length,
|
|
85
141
|
numReadonlySignedAccounts: 0,
|
|
@@ -88,7 +144,7 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
|
|
|
88
144
|
const recentBlockhash = msg.recentBlockhash || "";
|
|
89
145
|
|
|
90
146
|
const isV0 = typeof msg.version === "number" ? msg.version === 0 : true;
|
|
91
|
-
const result
|
|
147
|
+
const result = {
|
|
92
148
|
slot: rec.slot,
|
|
93
149
|
transaction: {
|
|
94
150
|
signatures: [signature],
|
|
@@ -101,80 +157,102 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
|
|
|
101
157
|
},
|
|
102
158
|
},
|
|
103
159
|
version: isV0 ? 0 : "legacy",
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
+
};
|
|
123
197
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
+
}
|
|
178
256
|
|
|
179
257
|
return context.createSuccessResponse(id, result);
|
|
180
258
|
}
|
|
@@ -182,46 +260,75 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
|
|
|
182
260
|
// Fallback: persistent store
|
|
183
261
|
try {
|
|
184
262
|
const row = await context.store?.getTransaction(signature);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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);
|
|
311
|
+
const msg = tx.message as unknown as {
|
|
312
|
+
staticAccountKeys?: unknown[];
|
|
313
|
+
accountKeys?: unknown[];
|
|
314
|
+
compiledInstructions?: unknown[];
|
|
315
|
+
instructions?: unknown[];
|
|
316
|
+
header?: unknown;
|
|
317
|
+
recentBlockhash?: string;
|
|
318
|
+
addressTableLookups?: unknown[];
|
|
319
|
+
isAccountSigner?: (i: number) => boolean;
|
|
320
|
+
isAccountWritable?: (i: number) => boolean;
|
|
321
|
+
};
|
|
322
|
+
const rawKeys2: unknown[] = Array.isArray(msg.staticAccountKeys)
|
|
218
323
|
? msg.staticAccountKeys
|
|
219
324
|
: Array.isArray(msg.accountKeys)
|
|
220
325
|
? msg.accountKeys
|
|
221
326
|
: [];
|
|
222
|
-
const accountKeys = rawKeys2.map((k
|
|
327
|
+
const accountKeys = rawKeys2.map((k) => {
|
|
223
328
|
try {
|
|
224
|
-
return typeof k === "string"
|
|
329
|
+
return typeof k === "string"
|
|
330
|
+
? k
|
|
331
|
+
: (k as { toBase58: () => string }).toBase58();
|
|
225
332
|
} catch {
|
|
226
333
|
return String(k);
|
|
227
334
|
}
|
|
@@ -231,53 +338,31 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
|
|
|
231
338
|
numReadonlySignedAccounts: 0,
|
|
232
339
|
numReadonlyUnsignedAccounts: 0,
|
|
233
340
|
};
|
|
234
|
-
const compiled = Array.isArray(msg.compiledInstructions)
|
|
341
|
+
const compiled: unknown[] = Array.isArray(msg.compiledInstructions)
|
|
235
342
|
? msg.compiledInstructions
|
|
236
343
|
: Array.isArray(msg.instructions)
|
|
237
344
|
? msg.instructions
|
|
238
345
|
: [];
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
});
|
|
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
|
+
});
|
|
281
366
|
const accountKeysParsed = accountKeys.map(
|
|
282
367
|
(pk: string, i: number) => ({
|
|
283
368
|
pubkey: pk,
|
|
@@ -291,47 +376,86 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
|
|
|
291
376
|
: i < (header?.numRequiredSignatures ?? 0),
|
|
292
377
|
}),
|
|
293
378
|
);
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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 {}
|
|
435
|
+
|
|
436
|
+
return context.createSuccessResponse(id, result);
|
|
437
|
+
} else {
|
|
324
438
|
const raw = Buffer.from(row.rawBase64, "base64");
|
|
325
439
|
const tx = VersionedTransaction.deserialize(raw);
|
|
326
|
-
const msg
|
|
327
|
-
|
|
440
|
+
const msg = tx.message as unknown as {
|
|
441
|
+
staticAccountKeys?: unknown[];
|
|
442
|
+
accountKeys?: unknown[];
|
|
443
|
+
compiledInstructions?: unknown[];
|
|
444
|
+
instructions?: unknown[];
|
|
445
|
+
header?: unknown;
|
|
446
|
+
recentBlockhash?: string;
|
|
447
|
+
addressTableLookups?: unknown[];
|
|
448
|
+
};
|
|
449
|
+
const rawKeys3: unknown[] = Array.isArray(msg.staticAccountKeys)
|
|
328
450
|
? msg.staticAccountKeys
|
|
329
451
|
: Array.isArray(msg.accountKeys)
|
|
330
452
|
? msg.accountKeys
|
|
331
453
|
: [];
|
|
332
|
-
const accountKeys = rawKeys3.map((k
|
|
454
|
+
const accountKeys = rawKeys3.map((k) => {
|
|
333
455
|
try {
|
|
334
|
-
return typeof k === "string"
|
|
456
|
+
return typeof k === "string"
|
|
457
|
+
? k
|
|
458
|
+
: (k as { toBase58: () => string }).toBase58();
|
|
335
459
|
} catch {
|
|
336
460
|
return String(k);
|
|
337
461
|
}
|
|
@@ -341,54 +465,81 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
|
|
|
341
465
|
numReadonlySignedAccounts: 0,
|
|
342
466
|
numReadonlyUnsignedAccounts: 0,
|
|
343
467
|
};
|
|
344
|
-
const compiled = Array.isArray(msg.compiledInstructions)
|
|
468
|
+
const compiled: unknown[] = Array.isArray(msg.compiledInstructions)
|
|
345
469
|
? msg.compiledInstructions
|
|
346
470
|
: Array.isArray(msg.instructions)
|
|
347
471
|
? msg.instructions
|
|
348
472
|
: [];
|
|
349
|
-
const instructions = compiled.map((ci
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
473
|
+
const instructions = compiled.map((ci) => {
|
|
474
|
+
const c = ci as {
|
|
475
|
+
programIdIndex: number;
|
|
476
|
+
accountKeyIndexes?: number[];
|
|
477
|
+
accounts?: number[];
|
|
478
|
+
data: Uint8Array | number[];
|
|
479
|
+
};
|
|
480
|
+
return {
|
|
481
|
+
programIdIndex: c.programIdIndex,
|
|
482
|
+
accounts: Array.from(c.accountKeyIndexes || c.accounts || []),
|
|
483
|
+
data: context.encodeBase58(
|
|
484
|
+
c.data instanceof Uint8Array ? c.data : Buffer.from(c.data),
|
|
485
|
+
),
|
|
486
|
+
};
|
|
487
|
+
});
|
|
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
|
+
}
|
|
386
528
|
}
|
|
387
529
|
} catch {}
|
|
388
530
|
|
|
389
531
|
// Fallback to LiteSVM history when no local record exists
|
|
390
532
|
const sigBytes = context.decodeBase58(signature);
|
|
391
|
-
const
|
|
533
|
+
const getTx = (
|
|
534
|
+
context.svm as unknown as {
|
|
535
|
+
getTransaction?: (
|
|
536
|
+
sig: Uint8Array,
|
|
537
|
+
) =>
|
|
538
|
+
| { logs: () => string[]; err: () => unknown }
|
|
539
|
+
| { meta: () => { logs: () => string[] }; err: () => unknown };
|
|
540
|
+
}
|
|
541
|
+
).getTransaction;
|
|
542
|
+
const txh = typeof getTx === "function" ? getTx(sigBytes) : undefined;
|
|
392
543
|
if (!txh) return context.createSuccessResponse(id, null);
|
|
393
544
|
|
|
394
545
|
const isError = "err" in txh;
|
|
@@ -417,12 +568,8 @@ export const getTransaction: RpcMethodHandler = async (id, params, context) => {
|
|
|
417
568
|
},
|
|
418
569
|
blockTime: Math.floor(Date.now() / 1000),
|
|
419
570
|
});
|
|
420
|
-
} catch (error:
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
-32602,
|
|
424
|
-
"Invalid params",
|
|
425
|
-
error.message,
|
|
426
|
-
);
|
|
571
|
+
} catch (error: unknown) {
|
|
572
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
573
|
+
return context.createErrorResponse(id, -32602, "Invalid params", message);
|
|
427
574
|
}
|
|
428
575
|
};
|