solforge 0.2.8 → 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.
@@ -25,51 +25,51 @@ export const sendTransaction: RpcMethodHandler = (id, params, context) => {
25
25
  : Array.isArray(msg.accountKeys)
26
26
  ? msg.accountKeys
27
27
  : [];
28
- const staticKeys = rawKeys
29
- .map((k) => {
30
- try {
31
- return typeof k === "string" ? new PublicKey(k) : (k as PublicKey);
32
- } catch {
33
- return undefined;
34
- }
35
- })
36
- .filter(Boolean) as PublicKey[];
37
- // Pre snapshots and balances
38
- const preBalances = staticKeys.map((pk) => {
39
- try {
40
- return Number(context.svm.getBalance(pk));
41
- } catch {
42
- return 0;
43
- }
44
- });
45
- const preAccountStates = staticKeys.map((pk) => {
46
- try {
47
- const addr = pk.toBase58();
48
- const acc = context.svm.getAccount(pk);
49
- if (!acc) return { address: addr, pre: null } as const;
50
- return {
51
- address: addr,
52
- pre: {
53
- lamports: Number(acc.lamports || 0n),
54
- ownerProgram: new PublicKey(acc.owner).toBase58(),
55
- executable: !!acc.executable,
56
- rentEpoch: Number(acc.rentEpoch || 0),
57
- dataLen: acc.data?.length ?? 0,
58
- dataBase64: undefined,
59
- lastSlot: Number(context.slot),
60
- },
61
- } as const;
62
- } catch {
63
- return { address: pk.toBase58(), pre: null } as const;
64
- }
65
- });
66
- try {
67
- if (process.env.DEBUG_TX_CAPTURE === "1") {
68
- console.debug(
69
- `[tx-capture] pre snapshots: keys=${staticKeys.length} captured=${preAccountStates.length}`,
70
- );
71
- }
72
- } catch {}
28
+ const staticKeys = rawKeys
29
+ .map((k) => {
30
+ try {
31
+ return typeof k === "string" ? new PublicKey(k) : (k as PublicKey);
32
+ } catch {
33
+ return undefined;
34
+ }
35
+ })
36
+ .filter(Boolean) as PublicKey[];
37
+ // Pre snapshots and balances
38
+ const preBalances = staticKeys.map((pk) => {
39
+ try {
40
+ return Number(context.svm.getBalance(pk));
41
+ } catch {
42
+ return 0;
43
+ }
44
+ });
45
+ const preAccountStates = staticKeys.map((pk) => {
46
+ try {
47
+ const addr = pk.toBase58();
48
+ const acc = context.svm.getAccount(pk);
49
+ if (!acc) return { address: addr, pre: null } as const;
50
+ return {
51
+ address: addr,
52
+ pre: {
53
+ lamports: Number(acc.lamports || 0n),
54
+ ownerProgram: new PublicKey(acc.owner).toBase58(),
55
+ executable: !!acc.executable,
56
+ rentEpoch: Number(acc.rentEpoch || 0),
57
+ dataLen: acc.data?.length ?? 0,
58
+ dataBase64: undefined,
59
+ lastSlot: Number(context.slot),
60
+ },
61
+ } as const;
62
+ } catch {
63
+ return { address: pk.toBase58(), pre: null } as const;
64
+ }
65
+ });
66
+ try {
67
+ if (process.env.DEBUG_TX_CAPTURE === "1") {
68
+ console.debug(
69
+ `[tx-capture] pre snapshots: keys=${staticKeys.length} captured=${preAccountStates.length}`,
70
+ );
71
+ }
72
+ } catch {}
73
73
 
74
74
  // Collect SPL token accounts from instructions for pre/post token balance snapshots
75
75
  const msgAny = msg as unknown as {
@@ -89,13 +89,21 @@ export const sendTransaction: RpcMethodHandler = (id, params, context) => {
89
89
  // 1) Collect from compiled ixs (best-effort)
90
90
  for (const ci of compiled) {
91
91
  try {
92
- const pid = staticKeys[(ci as any).programIdIndex]?.toBase58();
92
+ const rec = ci as Record<string, unknown>;
93
+ const pidIdx =
94
+ typeof rec.programIdIndex === "number"
95
+ ? rec.programIdIndex
96
+ : undefined;
97
+ const pid = pidIdx != null ? staticKeys[pidIdx]?.toBase58() : undefined;
93
98
  if (!pid || !tokenProgramIds.has(pid)) continue;
94
- const accIdxs: number[] = Array.isArray((ci as any).accountKeyIndexes)
95
- ? (ci as any).accountKeyIndexes
96
- : Array.isArray((ci as any).accounts)
97
- ? (ci as any).accounts
99
+ const accSrc = Array.isArray(rec.accountKeyIndexes)
100
+ ? (rec.accountKeyIndexes as unknown[])
101
+ : Array.isArray(rec.accounts)
102
+ ? (rec.accounts as unknown[])
98
103
  : [];
104
+ const accIdxs: number[] = accSrc.filter(
105
+ (v): v is number => typeof v === "number",
106
+ );
99
107
  for (const ix of accIdxs) {
100
108
  const addr = staticKeys[ix]?.toBase58();
101
109
  if (addr) tokenAccountSet.add(addr);
@@ -108,7 +116,10 @@ export const sendTransaction: RpcMethodHandler = (id, params, context) => {
108
116
  const acc = context.svm.getAccount(pk);
109
117
  if (!acc) continue;
110
118
  const ownerStr = new PublicKey(acc.owner).toBase58();
111
- if (tokenProgramIds.has(ownerStr) && (acc.data?.length ?? 0) >= ACCOUNT_SIZE) {
119
+ if (
120
+ tokenProgramIds.has(ownerStr) &&
121
+ (acc.data?.length ?? 0) >= ACCOUNT_SIZE
122
+ ) {
112
123
  tokenAccountSet.add(pk.toBase58());
113
124
  }
114
125
  } catch {}
@@ -171,7 +182,7 @@ export const sendTransaction: RpcMethodHandler = (id, params, context) => {
171
182
  } catch {}
172
183
  }
173
184
 
174
- const result = context.svm.sendTransaction(tx);
185
+ const result = context.svm.sendTransaction(tx);
175
186
 
176
187
  try {
177
188
  const rawErr = (result as { err?: unknown }).err;
@@ -187,46 +198,46 @@ export const sendTransaction: RpcMethodHandler = (id, params, context) => {
187
198
  }
188
199
  } catch {}
189
200
 
190
- const signature = tx.signatures[0]
191
- ? context.encodeBase58(tx.signatures[0])
192
- : context.encodeBase58(new Uint8Array(64).fill(0));
193
- context.notifySignature(signature);
194
- // Snapshot post balances and capture logs for rich view
195
- const postBalances = staticKeys.map((pk) => {
196
- try {
197
- return Number(context.svm.getBalance(pk));
198
- } catch {
199
- return 0;
200
- }
201
- });
202
- const postAccountStates = staticKeys.map((pk) => {
203
- try {
204
- const addr = pk.toBase58();
205
- const acc = context.svm.getAccount(pk);
206
- if (!acc) return { address: addr, post: null } as const;
207
- return {
208
- address: addr,
209
- post: {
210
- lamports: Number(acc.lamports || 0n),
211
- ownerProgram: new PublicKey(acc.owner).toBase58(),
212
- executable: !!acc.executable,
213
- rentEpoch: Number(acc.rentEpoch || 0),
214
- dataLen: acc.data?.length ?? 0,
215
- dataBase64: undefined,
216
- lastSlot: Number(context.slot),
217
- },
218
- } as const;
219
- } catch {
220
- return { address: pk.toBase58(), post: null } as const;
221
- }
222
- });
223
- try {
224
- if (process.env.DEBUG_TX_CAPTURE === "1") {
225
- console.debug(
226
- `[tx-capture] post snapshots: keys=${staticKeys.length} captured=${postAccountStates.length}`,
227
- );
228
- }
229
- } catch {}
201
+ const signature = tx.signatures[0]
202
+ ? context.encodeBase58(tx.signatures[0])
203
+ : context.encodeBase58(new Uint8Array(64).fill(0));
204
+ context.notifySignature(signature);
205
+ // Snapshot post balances and capture logs for rich view
206
+ const postBalances = staticKeys.map((pk) => {
207
+ try {
208
+ return Number(context.svm.getBalance(pk));
209
+ } catch {
210
+ return 0;
211
+ }
212
+ });
213
+ const postAccountStates = staticKeys.map((pk) => {
214
+ try {
215
+ const addr = pk.toBase58();
216
+ const acc = context.svm.getAccount(pk);
217
+ if (!acc) return { address: addr, post: null } as const;
218
+ return {
219
+ address: addr,
220
+ post: {
221
+ lamports: Number(acc.lamports || 0n),
222
+ ownerProgram: new PublicKey(acc.owner).toBase58(),
223
+ executable: !!acc.executable,
224
+ rentEpoch: Number(acc.rentEpoch || 0),
225
+ dataLen: acc.data?.length ?? 0,
226
+ dataBase64: undefined,
227
+ lastSlot: Number(context.slot),
228
+ },
229
+ } as const;
230
+ } catch {
231
+ return { address: pk.toBase58(), post: null } as const;
232
+ }
233
+ });
234
+ try {
235
+ if (process.env.DEBUG_TX_CAPTURE === "1") {
236
+ console.debug(
237
+ `[tx-capture] post snapshots: keys=${staticKeys.length} captured=${postAccountStates.length}`,
238
+ );
239
+ }
240
+ } catch {}
230
241
  // Post token balances (scan token accounts among static keys)
231
242
  const postTokenBalances: unknown[] = [];
232
243
  for (const addr of tokenAccountSet) {
@@ -278,150 +289,172 @@ export const sendTransaction: RpcMethodHandler = (id, params, context) => {
278
289
  }
279
290
  } catch {}
280
291
  }
281
- let logs: string[] = [];
282
- let innerInstructions: unknown[] = [];
283
- let computeUnits: number | null = null;
284
- let returnData: { programId: string; dataBase64: string } | null = null;
285
- try {
286
- const DBG = process.env.DEBUG_TX_CAPTURE === "1";
287
- const r: any = result as any;
288
- // Logs can be on TransactionMetadata or in meta() for failures
289
- try {
290
- if (typeof r?.logs === "function") logs = r.logs();
291
- } catch {}
292
- let metaObj: any | undefined;
293
- // Success shape: methods on result
294
- if (
295
- typeof r?.innerInstructions === "function" ||
296
- typeof r?.computeUnitsConsumed === "function" ||
297
- typeof r?.returnData === "function"
298
- ) {
299
- metaObj = r;
300
- }
301
- // Failed shape: meta() returns TransactionMetadata
302
- if (!metaObj && typeof r?.meta === "function") {
303
- try {
304
- metaObj = r.meta();
305
- if (!logs.length && typeof metaObj?.logs === "function") {
306
- logs = metaObj.logs();
307
- }
308
- } catch (e) {
309
- if (DBG)
310
- console.debug("[tx-capture] meta() threw while extracting:", e);
311
- }
312
- }
313
- // Extract richer metadata from whichever object exposes it
314
- if (metaObj) {
315
- try {
316
- const inner = metaObj.innerInstructions?.();
317
- if (Array.isArray(inner)) {
318
- innerInstructions = inner.map((group: any, index: number) => {
319
- const instructions = Array.isArray(group)
320
- ? group
321
- .map((ii: any) => {
322
- try {
323
- const inst = ii.instruction?.();
324
- const accIdxs: number[] = Array.from(
325
- inst?.accounts?.() || [],
326
- );
327
- const dataBytes: Uint8Array =
328
- inst?.data?.() || new Uint8Array();
329
- return {
330
- programIdIndex: Number(
331
- inst?.programIdIndex?.() ?? 0,
332
- ),
333
- accounts: accIdxs,
334
- data: context.encodeBase58(dataBytes),
335
- stackHeight: Number(ii.stackHeight?.() ?? 0),
336
- };
337
- } catch {
338
- return null;
339
- }
340
- })
341
- .filter(Boolean)
342
- : [];
343
- return { index, instructions };
344
- });
345
- }
346
- } catch (e) {
347
- if (DBG)
348
- console.debug(
349
- "[tx-capture] innerInstructions extraction failed:",
350
- e,
351
- );
352
- }
353
- try {
354
- const cu = metaObj.computeUnitsConsumed?.();
355
- if (typeof cu === "bigint") computeUnits = Number(cu);
356
- } catch (e) {
357
- if (DBG)
358
- console.debug(
359
- "[tx-capture] computeUnitsConsumed extraction failed:",
360
- e,
361
- );
362
- }
363
- try {
364
- const rd = metaObj.returnData?.();
365
- if (rd) {
366
- const pid = new PublicKey(rd.programId()).toBase58();
367
- const dataB64 = Buffer.from(rd.data()).toString("base64");
368
- returnData = { programId: pid, dataBase64: dataB64 };
369
- }
370
- } catch (e) {
371
- if (DBG)
372
- console.debug(
373
- "[tx-capture] returnData extraction failed:",
374
- e,
375
- );
376
- }
377
- } else if (DBG) {
378
- console.debug(
379
- "[tx-capture] no metadata object found on result shape",
380
- );
381
- }
382
- } catch {}
383
- try {
384
- if (process.env.DEBUG_TX_CAPTURE === "1") {
385
- console.debug(
386
- `[tx-capture] sendTransaction meta: logs=${logs.length} innerGroups=${Array.isArray(innerInstructions) ? innerInstructions.length : 0} computeUnits=${computeUnits} returnData=${returnData ? "yes" : "no"}`,
387
- );
388
- }
389
- } catch {}
390
- context.recordTransaction(signature, tx, {
391
- logs,
392
- fee: 5000,
393
- blockTime: Math.floor(Date.now() / 1000),
394
- preBalances,
395
- postBalances,
396
- preTokenBalances,
397
- postTokenBalances,
398
- innerInstructions,
399
- computeUnits,
400
- returnData,
401
- accountStates: (() => {
402
- try {
403
- const byAddr = new Map<string, { pre?: any; post?: any }>();
404
- for (const s of preAccountStates)
405
- byAddr.set(s.address, { pre: s.pre || null });
406
- for (const s of postAccountStates) {
407
- const e = byAddr.get(s.address) || {};
408
- e.post = s.post || null;
409
- byAddr.set(s.address, e);
410
- }
411
- return Array.from(byAddr.entries()).map(([address, v]) => ({
412
- address,
413
- pre: v.pre || null,
414
- post: v.post || null,
415
- }));
416
- } catch {
417
- return [] as Array<{
418
- address: string;
419
- pre?: unknown;
420
- post?: unknown;
421
- }>;
422
- }
423
- })(),
424
- });
292
+ let logs: string[] = [];
293
+ let innerInstructions: unknown[] = [];
294
+ let computeUnits: number | null = null;
295
+ let returnData: { programId: string; dataBase64: string } | null = null;
296
+ try {
297
+ const DBG = process.env.DEBUG_TX_CAPTURE === "1";
298
+ // biome-ignore lint/suspicious/noExplicitAny: Result shape depends on litesvm version; guarded access
299
+ const r: any = result as any;
300
+ // Logs can be on TransactionMetadata or in meta() for failures
301
+ try {
302
+ if (typeof r?.logs === "function") logs = r.logs();
303
+ } catch {}
304
+ let metaObj: unknown | undefined;
305
+ // Success shape: methods on result
306
+ if (
307
+ typeof r?.innerInstructions === "function" ||
308
+ typeof r?.computeUnitsConsumed === "function" ||
309
+ typeof r?.returnData === "function"
310
+ ) {
311
+ metaObj = r;
312
+ }
313
+ // Failed shape: meta() returns TransactionMetadata
314
+ if (!metaObj && typeof r?.meta === "function") {
315
+ try {
316
+ metaObj = r.meta();
317
+ if (!logs.length && typeof metaObj?.logs === "function") {
318
+ logs = metaObj.logs();
319
+ }
320
+ } catch (e) {
321
+ if (DBG)
322
+ console.debug("[tx-capture] meta() threw while extracting:", e);
323
+ }
324
+ }
325
+ // Extract richer metadata from whichever object exposes it
326
+ if (metaObj) {
327
+ try {
328
+ const inner = (
329
+ metaObj as { innerInstructions?: () => unknown } | undefined
330
+ )?.innerInstructions?.();
331
+ if (Array.isArray(inner)) {
332
+ innerInstructions = inner.map((group, index: number) => {
333
+ const instructions = Array.isArray(group)
334
+ ? (group as unknown[])
335
+ .map((ii) => {
336
+ try {
337
+ const inst = (
338
+ ii as { instruction?: () => unknown }
339
+ ).instruction?.();
340
+ const accSrc = (
341
+ inst as { accounts?: () => unknown } | undefined
342
+ )?.accounts?.();
343
+ const accIdxs: number[] = Array.isArray(accSrc)
344
+ ? (accSrc as unknown[]).filter(
345
+ (v): v is number => typeof v === "number",
346
+ )
347
+ : [];
348
+ const dataVal = (
349
+ inst as { data?: () => unknown } | undefined
350
+ )?.data?.();
351
+ const dataBytes: Uint8Array =
352
+ dataVal instanceof Uint8Array
353
+ ? dataVal
354
+ : new Uint8Array();
355
+ return {
356
+ programIdIndex: Number(
357
+ (
358
+ inst as
359
+ | { programIdIndex?: () => unknown }
360
+ | undefined
361
+ )?.programIdIndex?.() ?? 0,
362
+ ),
363
+ accounts: accIdxs,
364
+ data: context.encodeBase58(dataBytes),
365
+ stackHeight: Number(
366
+ (
367
+ ii as { stackHeight?: () => unknown }
368
+ ).stackHeight?.() ?? 0,
369
+ ),
370
+ };
371
+ } catch {
372
+ return null;
373
+ }
374
+ })
375
+ .filter(Boolean)
376
+ : [];
377
+ return { index, instructions };
378
+ });
379
+ }
380
+ } catch (e) {
381
+ if (DBG)
382
+ console.debug(
383
+ "[tx-capture] innerInstructions extraction failed:",
384
+ e,
385
+ );
386
+ }
387
+ try {
388
+ const cu = (
389
+ metaObj as { computeUnitsConsumed?: () => unknown } | undefined
390
+ )?.computeUnitsConsumed?.();
391
+ if (typeof cu === "bigint") computeUnits = Number(cu);
392
+ } catch (e) {
393
+ if (DBG)
394
+ console.debug(
395
+ "[tx-capture] computeUnitsConsumed extraction failed:",
396
+ e,
397
+ );
398
+ }
399
+ try {
400
+ const rd = (
401
+ metaObj as { returnData?: () => unknown } | undefined
402
+ )?.returnData?.();
403
+ if (rd) {
404
+ const pid = new PublicKey(rd.programId()).toBase58();
405
+ const dataB64 = Buffer.from(rd.data()).toString("base64");
406
+ returnData = { programId: pid, dataBase64: dataB64 };
407
+ }
408
+ } catch (e) {
409
+ if (DBG)
410
+ console.debug("[tx-capture] returnData extraction failed:", e);
411
+ }
412
+ } else if (DBG) {
413
+ console.debug("[tx-capture] no metadata object found on result shape");
414
+ }
415
+ } catch {}
416
+ try {
417
+ if (process.env.DEBUG_TX_CAPTURE === "1") {
418
+ console.debug(
419
+ `[tx-capture] sendTransaction meta: logs=${logs.length} innerGroups=${Array.isArray(innerInstructions) ? innerInstructions.length : 0} computeUnits=${computeUnits} returnData=${returnData ? "yes" : "no"}`,
420
+ );
421
+ }
422
+ } catch {}
423
+ context.recordTransaction(signature, tx, {
424
+ logs,
425
+ fee: 5000,
426
+ blockTime: Math.floor(Date.now() / 1000),
427
+ preBalances,
428
+ postBalances,
429
+ preTokenBalances,
430
+ postTokenBalances,
431
+ innerInstructions,
432
+ computeUnits,
433
+ returnData,
434
+ accountStates: (() => {
435
+ try {
436
+ const byAddr = new Map<string, { pre?: unknown; post?: unknown }>();
437
+ for (const s of preAccountStates)
438
+ byAddr.set(s.address, { pre: s.pre || null });
439
+ for (const s of postAccountStates) {
440
+ const e = byAddr.get(s.address) || {};
441
+ e.post = s.post || null;
442
+ byAddr.set(s.address, e);
443
+ }
444
+ return Array.from(byAddr.entries()).map(([address, v]) => ({
445
+ address,
446
+ pre: v.pre || null,
447
+ post: v.post || null,
448
+ }));
449
+ } catch {
450
+ return [] as Array<{
451
+ address: string;
452
+ pre?: unknown;
453
+ post?: unknown;
454
+ }>;
455
+ }
456
+ })(),
457
+ });
425
458
 
426
459
  return context.createSuccessResponse(id, signature);
427
460
  } catch (error: unknown) {