solforge 0.2.6 → 0.2.8

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.
@@ -6,12 +6,13 @@ import {
6
6
  VersionedTransaction,
7
7
  } from "@solana/web3.js";
8
8
  import type { RpcMethodHandler } from "../../types";
9
+ import { sendTransaction as sendTxRpc } from "../transaction/send-transaction";
9
10
 
10
11
  /**
11
12
  * Implements the requestAirdrop RPC method
12
13
  * @see https://docs.solana.com/api/http#requestairdrop
13
14
  */
14
- export const requestAirdrop: RpcMethodHandler = (id, params, context) => {
15
+ export const requestAirdrop: RpcMethodHandler = async (id, params, context) => {
15
16
  const [pubkeyStr, lamports, _config] = params || [];
16
17
 
17
18
  try {
@@ -68,20 +69,48 @@ export const requestAirdrop: RpcMethodHandler = (id, params, context) => {
68
69
  : Array.isArray(msg.accountKeys)
69
70
  ? (msg.accountKeys as unknown[])
70
71
  : [];
71
- const staticKeys = rawKeys.map((k) => {
72
- try {
73
- return typeof k === "string" ? new PublicKey(k) : (k as PublicKey);
74
- } catch {
75
- return faucet.publicKey;
76
- }
77
- });
78
- const preBalances = staticKeys.map((pk) => {
79
- try {
80
- return Number(context.svm.getBalance(pk));
81
- } catch {
82
- return 0;
83
- }
84
- });
72
+ const staticKeys = rawKeys.map((k) => {
73
+ try {
74
+ return typeof k === "string" ? new PublicKey(k) : (k as PublicKey);
75
+ } catch {
76
+ return faucet.publicKey;
77
+ }
78
+ });
79
+ const preBalances = staticKeys.map((pk) => {
80
+ try {
81
+ return Number(context.svm.getBalance(pk));
82
+ } catch {
83
+ return 0;
84
+ }
85
+ });
86
+ const preAccountStates = staticKeys.map((pk) => {
87
+ try {
88
+ const addr = pk.toBase58();
89
+ const acc = context.svm.getAccount(pk);
90
+ if (!acc) return { address: addr, pre: null } as const;
91
+ return {
92
+ address: addr,
93
+ pre: {
94
+ lamports: Number(acc.lamports || 0n),
95
+ ownerProgram: new PublicKey(acc.owner).toBase58(),
96
+ executable: !!acc.executable,
97
+ rentEpoch: Number(acc.rentEpoch || 0),
98
+ dataLen: acc.data?.length ?? 0,
99
+ dataBase64: undefined,
100
+ lastSlot: Number(context.slot),
101
+ },
102
+ } as const;
103
+ } catch {
104
+ return { address: pk.toBase58(), pre: null } as const;
105
+ }
106
+ });
107
+ try {
108
+ if (process.env.DEBUG_TX_CAPTURE === "1") {
109
+ console.debug(
110
+ `[tx-capture] pre snapshots: keys=${staticKeys.length} captured=${preAccountStates.length}`,
111
+ );
112
+ }
113
+ } catch {}
85
114
  const toIndex = staticKeys.findIndex((pk) => pk.equals(toPubkey));
86
115
  const beforeTo =
87
116
  toIndex >= 0
@@ -94,60 +123,58 @@ export const requestAirdrop: RpcMethodHandler = (id, params, context) => {
94
123
  }
95
124
  })();
96
125
 
97
- const sendResult = context.svm.sendTransaction(tx);
98
- // Surface errors to aid debugging
99
- try {
100
- const rawErr = (sendResult as { err?: unknown }).err;
101
- const maybeErr =
102
- typeof rawErr === "function" ? (rawErr as () => unknown)() : rawErr;
103
- if (maybeErr) {
104
- let logsForErr: string[] = [];
105
- try {
106
- const sr = sendResult as {
107
- logs?: () => string[];
108
- meta?: () => { logs?: () => string[] } | undefined;
109
- };
110
- if (typeof sr?.logs === "function") logsForErr = sr.logs();
111
- else if (typeof sr?.meta === "function") {
112
- const m = sr.meta();
113
- const lg = m?.logs;
114
- if (typeof lg === "function") logsForErr = lg();
115
- }
116
- } catch {}
117
- console.warn(
118
- "[requestAirdrop] transfer failed. err=",
119
- maybeErr,
120
- " logs=\n",
121
- logsForErr.join("\n"),
122
- );
123
- }
124
- } catch {}
126
+ // Send via standard sendTransaction RPC to unify capture + persistence
127
+ const rawB64 = Buffer.from(tx.serialize()).toString("base64");
128
+ const resp = await (sendTxRpc as RpcMethodHandler)(id, [rawB64], context);
129
+ if ((resp as any)?.error) return resp;
130
+ // Surface errors to aid debugging
131
+ const sendResult = undefined as unknown as { err?: unknown };
132
+ // Any send errors would have been returned by send-transaction already
125
133
 
126
- let signature = tx.signatures[0]
127
- ? context.encodeBase58(tx.signatures[0])
128
- : context.encodeBase58(new Uint8Array(64).fill(0));
129
- context.notifySignature(signature);
130
- // Compute post balances and capture logs if available for explorer detail view
131
- let postBalances = staticKeys.map((pk) => {
132
- try {
133
- return Number(context.svm.getBalance(pk));
134
- } catch {
135
- return 0;
136
- }
137
- });
138
- let logs: string[] = [];
139
- try {
140
- const sr = sendResult as {
141
- logs?: () => string[];
142
- meta?: () => { logs?: () => string[] } | undefined;
143
- };
144
- if (typeof sr?.logs === "function") logs = sr.logs();
145
- else if (typeof sr?.meta === "function") {
146
- const m = sr.meta();
147
- const lg = m?.logs;
148
- if (typeof lg === "function") logs = lg();
149
- }
150
- } catch {}
134
+ let signature = String((resp as any)?.result || "");
135
+ if (!signature) {
136
+ signature = tx.signatures[0]
137
+ ? context.encodeBase58(tx.signatures[0])
138
+ : context.encodeBase58(new Uint8Array(64).fill(0));
139
+ }
140
+ try { context.notifySignature(signature); } catch {}
141
+ // Compute post balances and capture logs if available for explorer detail view
142
+ let postBalances = staticKeys.map((pk) => {
143
+ try {
144
+ return Number(context.svm.getBalance(pk));
145
+ } catch {
146
+ return 0;
147
+ }
148
+ });
149
+ const postAccountStates = staticKeys.map((pk) => {
150
+ try {
151
+ const addr = pk.toBase58();
152
+ const acc = context.svm.getAccount(pk);
153
+ if (!acc) return { address: addr, post: null } as const;
154
+ return {
155
+ address: addr,
156
+ post: {
157
+ lamports: Number(acc.lamports || 0n),
158
+ ownerProgram: new PublicKey(acc.owner).toBase58(),
159
+ executable: !!acc.executable,
160
+ rentEpoch: Number(acc.rentEpoch || 0),
161
+ dataLen: acc.data?.length ?? 0,
162
+ dataBase64: undefined,
163
+ lastSlot: Number(context.slot),
164
+ },
165
+ } as const;
166
+ } catch {
167
+ return { address: pk.toBase58(), post: null } as const;
168
+ }
169
+ });
170
+ try {
171
+ if (process.env.DEBUG_TX_CAPTURE === "1") {
172
+ console.debug(
173
+ `[tx-capture] post snapshots: keys=${staticKeys.length} captured=${postAccountStates.length}`,
174
+ );
175
+ }
176
+ } catch {}
177
+ // Parsing, recording etc. are performed by send-transaction
151
178
  // Verify recipient received lamports; retry once if not
152
179
  const afterTo =
153
180
  toIndex >= 0
@@ -207,22 +234,18 @@ export const requestAirdrop: RpcMethodHandler = (id, params, context) => {
207
234
  }
208
235
 
209
236
  // Try to capture error again for accurate status reporting
210
- let recErr: unknown = null;
211
- try {
212
- const rawErrFun = (sendResult as { err?: unknown }).err;
213
- recErr =
214
- typeof rawErrFun === "function"
215
- ? (rawErrFun as () => unknown)()
216
- : rawErrFun;
217
- } catch {}
218
- context.recordTransaction(signature, tx, {
219
- logs,
220
- fee: 5000,
221
- blockTime: Math.floor(Date.now() / 1000),
222
- preBalances,
223
- postBalances,
224
- err: recErr,
225
- });
237
+ // No additional error capture; send-transaction has already recorded it
238
+ // Pre/post snapshots are still useful for account cache; we can upsert
239
+ try {
240
+ const snapshots = new Map<string, { pre?: any; post?: any }>();
241
+ for (const s of preAccountStates) snapshots.set(s.address, { pre: s.pre || null });
242
+ for (const s of postAccountStates) {
243
+ const e = snapshots.get(s.address) || {};
244
+ e.post = s.post || null;
245
+ snapshots.set(s.address, e);
246
+ }
247
+ // Not persisted here; DB already has the transaction via send-transaction
248
+ } catch {}
226
249
 
227
250
  return context.createSuccessResponse(id, signature);
228
251
  } catch (error: unknown) {
@@ -16,6 +16,7 @@ import {
16
16
  VersionedTransaction,
17
17
  } from "@solana/web3.js";
18
18
  import type { RpcMethodHandler } from "../../types";
19
+ import { sendTransaction as sendTxRpc } from "../transaction/send-transaction";
19
20
 
20
21
  // Mint via a real SPL Token transaction signed by faucet (must be mint authority)
21
22
  export const solforgeMintTo: RpcMethodHandler = async (id, params, context) => {
@@ -148,16 +149,15 @@ export const solforgeMintTo: RpcMethodHandler = async (id, params, context) => {
148
149
  }
149
150
  } catch {}
150
151
 
151
- // Send transaction via svm
152
- const _res = context.svm.sendTransaction(vtx);
153
- // Compute signature (base58) from the signed transaction
154
- let signatureStr = "";
155
- try {
156
- const sigBytes = vtx.signatures?.[0];
157
- if (sigBytes)
158
- signatureStr = context.encodeBase58(new Uint8Array(sigBytes));
159
- } catch {}
160
- if (!signatureStr) signatureStr = `mint:${ata.toBase58()}:${Date.now()}`;
152
+ // Send via the standard RPC sendTransaction path to unify capture/parsing
153
+ const rawBase64ForRpc = Buffer.from(vtx.serialize()).toString("base64");
154
+ const rpcResp = await (sendTxRpc as RpcMethodHandler)(
155
+ id,
156
+ [rawBase64ForRpc],
157
+ context,
158
+ );
159
+ if ((rpcResp as any)?.error) return rpcResp;
160
+ const signatureStr = String((rpcResp as any)?.result || "");
161
161
 
162
162
  // Token balance deltas (pre/post) for ATA
163
163
  type UiTokenAmount = {
@@ -229,34 +229,7 @@ export const solforgeMintTo: RpcMethodHandler = async (id, params, context) => {
229
229
  ];
230
230
  } catch {}
231
231
 
232
- // Insert into DB for explorer via context.recordTransaction for richer details
233
- try {
234
- const rawBase64 = Buffer.from(vtx.serialize()).toString("base64");
235
- const postBalances = trackedKeys.map((pk) => {
236
- try {
237
- return Number(context.svm.getBalance(pk) || 0n);
238
- } catch {
239
- return 0;
240
- }
241
- });
242
- const logs: string[] = ["spl-token mintToChecked"];
243
- try {
244
- (vtx as unknown as { serialize: () => Uint8Array }).serialize = () =>
245
- Buffer.from(rawBase64, "base64");
246
- } catch {}
247
- context.recordTransaction(signatureStr, vtx, {
248
- logs,
249
- fee: 0,
250
- blockTime: Math.floor(Date.now() / 1000),
251
- preBalances,
252
- postBalances,
253
- preTokenBalances,
254
- postTokenBalances,
255
- });
256
- } catch {}
257
- try {
258
- context.notifySignature(signatureStr);
259
- } catch {}
232
+ // send-transaction already records/announces signature and persists to DB
260
233
 
261
234
  return context.createSuccessResponse(id, {
262
235
  ok: true,