solforge 0.2.6 → 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/server/lib/instruction-parser.ts +242 -0
- package/server/methods/account/request-airdrop.ts +107 -84
- package/server/methods/admin/mint-to.ts +11 -38
- package/server/methods/transaction/get-transaction.ts +328 -267
- package/server/methods/transaction/inner-instructions.test.ts +63 -0
- package/server/methods/transaction/send-transaction.ts +230 -51
- package/server/rpc-server.ts +72 -48
- package/server/types.ts +56 -24
- 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 +103 -70
- package/src/migrations-bundled.ts +8 -2
package/package.json
CHANGED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AddressLookupTableInstruction,
|
|
3
|
+
ComputeBudgetInstruction,
|
|
4
|
+
PublicKey,
|
|
5
|
+
StakeInstruction,
|
|
6
|
+
SystemInstruction,
|
|
7
|
+
SystemProgram,
|
|
8
|
+
TransactionInstruction,
|
|
9
|
+
VoteInstruction,
|
|
10
|
+
} from "@solana/web3.js";
|
|
11
|
+
import { decodeBase58 as _decodeBase58 } from "./base58";
|
|
12
|
+
|
|
13
|
+
export type ParsedInstruction =
|
|
14
|
+
| { program: string; programId: string; parsed: { type: string; info: any } }
|
|
15
|
+
| { programId: string; accounts: string[]; data: string };
|
|
16
|
+
|
|
17
|
+
function makeIx(
|
|
18
|
+
programId: string,
|
|
19
|
+
accountKeys: string[],
|
|
20
|
+
accounts: number[],
|
|
21
|
+
dataBase58: string,
|
|
22
|
+
): TransactionInstruction {
|
|
23
|
+
const keys = accounts.map((i) => ({
|
|
24
|
+
pubkey: new PublicKey(accountKeys[i] || SystemProgram.programId),
|
|
25
|
+
isSigner: false,
|
|
26
|
+
isWritable: false,
|
|
27
|
+
}));
|
|
28
|
+
const data = Buffer.from(_decodeBase58(dataBase58));
|
|
29
|
+
return new TransactionInstruction({ programId: new PublicKey(programId), keys, data });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function ok(program: string, programId: string, type: string, info: any): ParsedInstruction {
|
|
33
|
+
return { program, programId, parsed: { type, info } };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function parseInstruction(
|
|
37
|
+
programId: string,
|
|
38
|
+
accounts: number[],
|
|
39
|
+
dataBase58: string,
|
|
40
|
+
accountKeys: string[],
|
|
41
|
+
): ParsedInstruction {
|
|
42
|
+
try {
|
|
43
|
+
const pid = new PublicKey(programId);
|
|
44
|
+
const ix = makeIx(programId, accountKeys, accounts, dataBase58);
|
|
45
|
+
|
|
46
|
+
// System Program
|
|
47
|
+
if (pid.equals(SystemProgram.programId)) {
|
|
48
|
+
const t = SystemInstruction.decodeInstructionType(ix);
|
|
49
|
+
switch (t) {
|
|
50
|
+
case "Create": {
|
|
51
|
+
const p = SystemInstruction.decodeCreateAccount(ix);
|
|
52
|
+
return ok("system", programId, "createAccount", {
|
|
53
|
+
fromPubkey: p.fromPubkey.toBase58(),
|
|
54
|
+
newAccountPubkey: p.newAccountPubkey.toBase58(),
|
|
55
|
+
lamports: Number(p.lamports),
|
|
56
|
+
space: Number(p.space),
|
|
57
|
+
programId: p.programId.toBase58(),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
case "Transfer": {
|
|
61
|
+
const p = SystemInstruction.decodeTransfer(ix);
|
|
62
|
+
return ok("system", programId, "transfer", {
|
|
63
|
+
source: p.fromPubkey.toBase58(),
|
|
64
|
+
destination: p.toPubkey.toBase58(),
|
|
65
|
+
lamports: Number(p.lamports),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
case "TransferWithSeed": {
|
|
69
|
+
const p = SystemInstruction.decodeTransferWithSeed(ix);
|
|
70
|
+
return ok("system", programId, "transferWithSeed", {
|
|
71
|
+
fromPubkey: p.fromPubkey.toBase58(),
|
|
72
|
+
basePubkey: p.basePubkey.toBase58(),
|
|
73
|
+
toPubkey: p.toPubkey.toBase58(),
|
|
74
|
+
lamports: Number(p.lamports),
|
|
75
|
+
seed: p.seed,
|
|
76
|
+
programId: p.programId.toBase58(),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
case "Allocate": {
|
|
80
|
+
const p = SystemInstruction.decodeAllocate(ix);
|
|
81
|
+
return ok("system", programId, "allocate", {
|
|
82
|
+
accountPubkey: p.accountPubkey.toBase58(),
|
|
83
|
+
space: Number(p.space),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
case "AllocateWithSeed": {
|
|
87
|
+
const p = SystemInstruction.decodeAllocateWithSeed(ix);
|
|
88
|
+
return ok("system", programId, "allocateWithSeed", {
|
|
89
|
+
accountPubkey: p.accountPubkey.toBase58(),
|
|
90
|
+
basePubkey: p.basePubkey.toBase58(),
|
|
91
|
+
seed: p.seed,
|
|
92
|
+
space: Number(p.space),
|
|
93
|
+
programId: p.programId.toBase58(),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
case "Assign": {
|
|
97
|
+
const p = SystemInstruction.decodeAssign(ix);
|
|
98
|
+
return ok("system", programId, "assign", {
|
|
99
|
+
accountPubkey: p.accountPubkey.toBase58(),
|
|
100
|
+
programId: p.programId.toBase58(),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
case "AssignWithSeed": {
|
|
104
|
+
const p = SystemInstruction.decodeAssignWithSeed(ix);
|
|
105
|
+
return ok("system", programId, "assignWithSeed", {
|
|
106
|
+
accountPubkey: p.accountPubkey.toBase58(),
|
|
107
|
+
basePubkey: p.basePubkey.toBase58(),
|
|
108
|
+
seed: p.seed,
|
|
109
|
+
programId: p.programId.toBase58(),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
case "InitializeNonceAccount":
|
|
113
|
+
case "AdvanceNonceAccount":
|
|
114
|
+
case "WithdrawNonceAccount":
|
|
115
|
+
case "AuthorizeNonceAccount":
|
|
116
|
+
case "CreateWithSeed":
|
|
117
|
+
case "UpgradeNonceAccount": {
|
|
118
|
+
// For brevity: rely on type only; details can be added if needed
|
|
119
|
+
return ok("system", programId, t, {});
|
|
120
|
+
}
|
|
121
|
+
default:
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Compute Budget
|
|
127
|
+
try {
|
|
128
|
+
const t = ComputeBudgetInstruction.decodeInstructionType(ix);
|
|
129
|
+
switch (t) {
|
|
130
|
+
case "SetComputeUnitLimit": {
|
|
131
|
+
const p = ComputeBudgetInstruction.decodeSetComputeUnitLimit(ix);
|
|
132
|
+
return ok("computeBudget", programId, "setComputeUnitLimit", { units: Number(p.units) });
|
|
133
|
+
}
|
|
134
|
+
case "SetComputeUnitPrice": {
|
|
135
|
+
const p = ComputeBudgetInstruction.decodeSetComputeUnitPrice(ix);
|
|
136
|
+
return ok("computeBudget", programId, "setComputeUnitPrice", { microLamports: Number(p.microLamports) });
|
|
137
|
+
}
|
|
138
|
+
case "RequestHeapFrame": {
|
|
139
|
+
const p = ComputeBudgetInstruction.decodeRequestHeapFrame(ix);
|
|
140
|
+
return ok("computeBudget", programId, "requestHeapFrame", { bytes: Number(p.bytes) });
|
|
141
|
+
}
|
|
142
|
+
case "RequestUnits": {
|
|
143
|
+
const p = ComputeBudgetInstruction.decodeRequestUnits(ix);
|
|
144
|
+
return ok("computeBudget", programId, "requestUnits", { units: Number(p.units), additionalFee: Number(p.additionalFee) });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} catch {}
|
|
148
|
+
|
|
149
|
+
// Stake
|
|
150
|
+
try {
|
|
151
|
+
const t = StakeInstruction.decodeInstructionType(ix);
|
|
152
|
+
switch (t) {
|
|
153
|
+
case "Initialize":
|
|
154
|
+
return ok("stake", programId, "initialize", {});
|
|
155
|
+
case "Delegate": {
|
|
156
|
+
const p = StakeInstruction.decodeDelegate(ix);
|
|
157
|
+
return ok("stake", programId, "delegate", {
|
|
158
|
+
stakePubkey: p.stakePubkey.toBase58(),
|
|
159
|
+
votePubkey: p.votePubkey.toBase58(),
|
|
160
|
+
authorizedPubkey: p.authorizedPubkey.toBase58(),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
case "Authorize": {
|
|
164
|
+
const p = StakeInstruction.decodeAuthorize(ix);
|
|
165
|
+
return ok("stake", programId, "authorize", {
|
|
166
|
+
stakePubkey: p.stakePubkey.toBase58(),
|
|
167
|
+
authorizedPubkey: p.authorizedPubkey.toBase58(),
|
|
168
|
+
newAuthorizedPubkey: p.newAuthorizedPubkey.toBase58(),
|
|
169
|
+
stakeAuthorizationType: p.stakeAuthorizationType.index,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
case "AuthorizeWithSeed":
|
|
173
|
+
return ok("stake", programId, "authorizeWithSeed", {});
|
|
174
|
+
case "Split":
|
|
175
|
+
return ok("stake", programId, "split", {});
|
|
176
|
+
case "Withdraw":
|
|
177
|
+
return ok("stake", programId, "withdraw", {});
|
|
178
|
+
case "Deactivate":
|
|
179
|
+
return ok("stake", programId, "deactivate", {});
|
|
180
|
+
case "Merge":
|
|
181
|
+
return ok("stake", programId, "merge", {});
|
|
182
|
+
}
|
|
183
|
+
} catch {}
|
|
184
|
+
|
|
185
|
+
// Vote
|
|
186
|
+
try {
|
|
187
|
+
const t = VoteInstruction.decodeInstructionType(ix);
|
|
188
|
+
switch (t) {
|
|
189
|
+
case "InitializeAccount":
|
|
190
|
+
return ok("vote", programId, "initialize", {});
|
|
191
|
+
case "Authorize":
|
|
192
|
+
return ok("vote", programId, "authorize", {});
|
|
193
|
+
case "AuthorizeWithSeed":
|
|
194
|
+
return ok("vote", programId, "authorizeWithSeed", {});
|
|
195
|
+
case "Withdraw":
|
|
196
|
+
return ok("vote", programId, "withdraw", {});
|
|
197
|
+
default:
|
|
198
|
+
return ok("vote", programId, t, {});
|
|
199
|
+
}
|
|
200
|
+
} catch {}
|
|
201
|
+
|
|
202
|
+
// Address Lookup Table
|
|
203
|
+
try {
|
|
204
|
+
const t = AddressLookupTableInstruction.decodeInstructionType(ix);
|
|
205
|
+
switch (t) {
|
|
206
|
+
case "CreateLookupTable":
|
|
207
|
+
case "ExtendLookupTable":
|
|
208
|
+
case "CloseLookupTable":
|
|
209
|
+
case "FreezeLookupTable":
|
|
210
|
+
case "DeactivateLookupTable":
|
|
211
|
+
return ok("address-lookup-table", programId, t, {});
|
|
212
|
+
}
|
|
213
|
+
} catch {}
|
|
214
|
+
|
|
215
|
+
// Memo program: parse utf8 memo if possible
|
|
216
|
+
if (
|
|
217
|
+
programId === "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" ||
|
|
218
|
+
programId === "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo"
|
|
219
|
+
) {
|
|
220
|
+
try {
|
|
221
|
+
const bytes = _decodeBase58(dataBase58);
|
|
222
|
+
const memo = new TextDecoder().decode(bytes);
|
|
223
|
+
return ok("spl-memo", programId, "memo", { memo });
|
|
224
|
+
} catch {}
|
|
225
|
+
return ok("spl-memo", programId, "memo", {});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Fallback: unknown program, return raw
|
|
229
|
+
return {
|
|
230
|
+
programId,
|
|
231
|
+
accounts: accounts.map((i) => accountKeys[i] || ""),
|
|
232
|
+
data: dataBase58,
|
|
233
|
+
};
|
|
234
|
+
} catch {
|
|
235
|
+
return {
|
|
236
|
+
programId,
|
|
237
|
+
accounts: accounts.map((i) => accountKeys[i] || ""),
|
|
238
|
+
data: dataBase58,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
@@ -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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
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,
|