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.
- package/package.json +1 -1
- package/scripts/decode-b58.ts +5 -1
- package/server/lib/instruction-parser.ts +302 -238
- package/server/lib/parsers/spl-associated-token-account.ts +36 -30
- package/server/lib/parsers/spl-token.ts +285 -142
- 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 +269 -236
- package/server/rpc-server.ts +101 -104
- 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
|
@@ -69,48 +69,48 @@ export const requestAirdrop: RpcMethodHandler = async (id, params, context) => {
|
|
|
69
69
|
: Array.isArray(msg.accountKeys)
|
|
70
70
|
? (msg.accountKeys as unknown[])
|
|
71
71
|
: [];
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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 {}
|
|
114
114
|
const toIndex = staticKeys.findIndex((pk) => pk.equals(toPubkey));
|
|
115
115
|
const beforeTo =
|
|
116
116
|
toIndex >= 0
|
|
@@ -123,58 +123,73 @@ export const requestAirdrop: RpcMethodHandler = async (id, params, context) => {
|
|
|
123
123
|
}
|
|
124
124
|
})();
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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 (
|
|
130
|
+
resp &&
|
|
131
|
+
typeof resp === "object" &&
|
|
132
|
+
"error" in (resp as Record<string, unknown>) &&
|
|
133
|
+
(resp as Record<string, unknown>).error != null
|
|
134
|
+
) {
|
|
135
|
+
return resp;
|
|
136
|
+
}
|
|
137
|
+
// Any send errors would have been returned by send-transaction already
|
|
133
138
|
|
|
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
|
-
|
|
139
|
+
let signature = (() => {
|
|
140
|
+
try {
|
|
141
|
+
const r = resp as Record<string, unknown>;
|
|
142
|
+
const v = r?.result;
|
|
143
|
+
return v == null ? "" : String(v);
|
|
144
|
+
} catch {
|
|
145
|
+
return "";
|
|
146
|
+
}
|
|
147
|
+
})();
|
|
148
|
+
if (!signature) {
|
|
149
|
+
signature = tx.signatures[0]
|
|
150
|
+
? context.encodeBase58(tx.signatures[0])
|
|
151
|
+
: context.encodeBase58(new Uint8Array(64).fill(0));
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
context.notifySignature(signature);
|
|
155
|
+
} catch {}
|
|
156
|
+
// Compute post balances and capture logs if available for explorer detail view
|
|
157
|
+
let postBalances = staticKeys.map((pk) => {
|
|
158
|
+
try {
|
|
159
|
+
return Number(context.svm.getBalance(pk));
|
|
160
|
+
} catch {
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
const postAccountStates = staticKeys.map((pk) => {
|
|
165
|
+
try {
|
|
166
|
+
const addr = pk.toBase58();
|
|
167
|
+
const acc = context.svm.getAccount(pk);
|
|
168
|
+
if (!acc) return { address: addr, post: null } as const;
|
|
169
|
+
return {
|
|
170
|
+
address: addr,
|
|
171
|
+
post: {
|
|
172
|
+
lamports: Number(acc.lamports || 0n),
|
|
173
|
+
ownerProgram: new PublicKey(acc.owner).toBase58(),
|
|
174
|
+
executable: !!acc.executable,
|
|
175
|
+
rentEpoch: Number(acc.rentEpoch || 0),
|
|
176
|
+
dataLen: acc.data?.length ?? 0,
|
|
177
|
+
dataBase64: undefined,
|
|
178
|
+
lastSlot: Number(context.slot),
|
|
179
|
+
},
|
|
180
|
+
} as const;
|
|
181
|
+
} catch {
|
|
182
|
+
return { address: pk.toBase58(), post: null } as const;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
try {
|
|
186
|
+
if (process.env.DEBUG_TX_CAPTURE === "1") {
|
|
187
|
+
console.debug(
|
|
188
|
+
`[tx-capture] post snapshots: keys=${staticKeys.length} captured=${postAccountStates.length}`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
} catch {}
|
|
192
|
+
// Parsing, recording etc. are performed by send-transaction
|
|
178
193
|
// Verify recipient received lamports; retry once if not
|
|
179
194
|
const afterTo =
|
|
180
195
|
toIndex >= 0
|
|
@@ -234,18 +249,19 @@ export const requestAirdrop: RpcMethodHandler = async (id, params, context) => {
|
|
|
234
249
|
}
|
|
235
250
|
|
|
236
251
|
// Try to capture error again for accurate status reporting
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
252
|
+
// No additional error capture; send-transaction has already recorded it
|
|
253
|
+
// Pre/post snapshots are still useful for account cache; we can upsert
|
|
254
|
+
try {
|
|
255
|
+
const snapshots = new Map<string, { pre?: unknown; post?: unknown }>();
|
|
256
|
+
for (const s of preAccountStates)
|
|
257
|
+
snapshots.set(s.address, { pre: s.pre || null });
|
|
258
|
+
for (const s of postAccountStates) {
|
|
259
|
+
const e = snapshots.get(s.address) || {};
|
|
260
|
+
e.post = s.post || null;
|
|
261
|
+
snapshots.set(s.address, e);
|
|
262
|
+
}
|
|
263
|
+
// Not persisted here; DB already has the transaction via send-transaction
|
|
264
|
+
} catch {}
|
|
249
265
|
|
|
250
266
|
return context.createSuccessResponse(id, signature);
|
|
251
267
|
} catch (error: unknown) {
|
|
@@ -126,7 +126,7 @@ export const solforgeMintTo: RpcMethodHandler = async (id, params, context) => {
|
|
|
126
126
|
|
|
127
127
|
// Capture preBalances for primary accounts referenced and token pre amount
|
|
128
128
|
const trackedKeys = [faucet.publicKey, ata, mint, owner];
|
|
129
|
-
const
|
|
129
|
+
const _preBalances = trackedKeys.map((pk) => {
|
|
130
130
|
try {
|
|
131
131
|
return Number(context.svm.getBalance(pk) || 0n);
|
|
132
132
|
} catch {
|
|
@@ -149,15 +149,30 @@ export const solforgeMintTo: RpcMethodHandler = async (id, params, context) => {
|
|
|
149
149
|
}
|
|
150
150
|
} catch {}
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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 (
|
|
160
|
+
rpcResp &&
|
|
161
|
+
typeof rpcResp === "object" &&
|
|
162
|
+
"error" in (rpcResp as Record<string, unknown>) &&
|
|
163
|
+
(rpcResp as Record<string, unknown>).error != null
|
|
164
|
+
) {
|
|
165
|
+
return rpcResp;
|
|
166
|
+
}
|
|
167
|
+
const signatureStr = (() => {
|
|
168
|
+
try {
|
|
169
|
+
const r = rpcResp as Record<string, unknown>;
|
|
170
|
+
const v = r?.result;
|
|
171
|
+
return v == null ? "" : String(v);
|
|
172
|
+
} catch {
|
|
173
|
+
return "";
|
|
174
|
+
}
|
|
175
|
+
})();
|
|
161
176
|
|
|
162
177
|
// Token balance deltas (pre/post) for ATA
|
|
163
178
|
type UiTokenAmount = {
|
|
@@ -172,8 +187,8 @@ export const solforgeMintTo: RpcMethodHandler = async (id, params, context) => {
|
|
|
172
187
|
owner: string;
|
|
173
188
|
uiTokenAmount: UiTokenAmount;
|
|
174
189
|
};
|
|
175
|
-
|
|
176
|
-
let
|
|
190
|
+
const _preTokenBalances: TokenBalance[] = [];
|
|
191
|
+
let _postTokenBalances: TokenBalance[] = [];
|
|
177
192
|
try {
|
|
178
193
|
const decs = decsForMint;
|
|
179
194
|
const ui = (n: bigint) => ({
|
|
@@ -219,7 +234,7 @@ export const solforgeMintTo: RpcMethodHandler = async (id, params, context) => {
|
|
|
219
234
|
uiTokenAmount: ui(preAmt),
|
|
220
235
|
},
|
|
221
236
|
];
|
|
222
|
-
|
|
237
|
+
_postTokenBalances = [
|
|
223
238
|
{
|
|
224
239
|
accountIndex: ataIndex >= 0 ? ataIndex : 0,
|
|
225
240
|
mint: mint.toBase58(),
|
|
@@ -229,7 +244,7 @@ export const solforgeMintTo: RpcMethodHandler = async (id, params, context) => {
|
|
|
229
244
|
];
|
|
230
245
|
} catch {}
|
|
231
246
|
|
|
232
|
-
|
|
247
|
+
// send-transaction already records/announces signature and persists to DB
|
|
233
248
|
|
|
234
249
|
return context.createSuccessResponse(id, {
|
|
235
250
|
ok: true,
|