solforge 0.2.8 → 0.2.10
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 +310 -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
package/server/rpc-server.ts
CHANGED
|
@@ -150,36 +150,34 @@ export class LiteSVMRpcServer {
|
|
|
150
150
|
} catch {}
|
|
151
151
|
},
|
|
152
152
|
listPrograms: () => Array.from(this.knownPrograms),
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
} catch {}
|
|
153
|
+
recordTransaction: (signature, tx, meta) => {
|
|
154
|
+
this.txRecords.set(signature, {
|
|
155
|
+
tx,
|
|
156
|
+
logs: meta?.logs || [],
|
|
157
|
+
err: meta?.err ?? null,
|
|
158
|
+
fee: meta?.fee ?? 5000,
|
|
159
|
+
slot: Number(this.slot),
|
|
160
|
+
blockTime: meta?.blockTime,
|
|
161
|
+
preBalances: meta?.preBalances,
|
|
162
|
+
postBalances: meta?.postBalances,
|
|
163
|
+
preTokenBalances: (
|
|
164
|
+
meta as { preTokenBalances?: unknown[] } | undefined
|
|
165
|
+
)?.preTokenBalances,
|
|
166
|
+
postTokenBalances: (
|
|
167
|
+
meta as { postTokenBalances?: unknown[] } | undefined
|
|
168
|
+
)?.postTokenBalances,
|
|
169
|
+
innerInstructions: meta?.innerInstructions || [],
|
|
170
|
+
computeUnits:
|
|
171
|
+
meta?.computeUnits == null ? null : Number(meta.computeUnits),
|
|
172
|
+
returnData: meta?.returnData ?? null,
|
|
173
|
+
});
|
|
174
|
+
try {
|
|
175
|
+
if (process.env.DEBUG_TX_CAPTURE === "1") {
|
|
176
|
+
console.debug(
|
|
177
|
+
`[tx-capture] recordTransaction: sig=${signature} slot=${this.slot} logs=${meta?.logs?.length || 0} inner=${Array.isArray(meta?.innerInstructions) ? meta?.innerInstructions?.length : 0} cu=${meta?.computeUnits ?? null} returnData=${meta?.returnData ? "yes" : "no"}`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
} catch {}
|
|
183
181
|
|
|
184
182
|
// Persist to SQLite for durability and history queries
|
|
185
183
|
try {
|
|
@@ -240,82 +238,81 @@ export class LiteSVMRpcServer {
|
|
|
240
238
|
: "legacy"
|
|
241
239
|
: 0;
|
|
242
240
|
const rawBase64 = Buffer.from(tx.serialize()).toString("base64");
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
})
|
|
284
|
-
.catch(() => {});
|
|
241
|
+
this.store
|
|
242
|
+
.insertTransactionBundle({
|
|
243
|
+
signature,
|
|
244
|
+
slot: Number(this.slot),
|
|
245
|
+
blockTime: meta?.blockTime,
|
|
246
|
+
version,
|
|
247
|
+
fee: Number(meta?.fee ?? 5000),
|
|
248
|
+
err: meta?.err ?? null,
|
|
249
|
+
rawBase64,
|
|
250
|
+
preBalances: Array.isArray(meta?.preBalances)
|
|
251
|
+
? (meta?.preBalances as number[])
|
|
252
|
+
: [],
|
|
253
|
+
postBalances: Array.isArray(meta?.postBalances)
|
|
254
|
+
? (meta?.postBalances as number[])
|
|
255
|
+
: [],
|
|
256
|
+
logs: Array.isArray(meta?.logs) ? (meta?.logs as string[]) : [],
|
|
257
|
+
preTokenBalances: (() => {
|
|
258
|
+
const arr = (
|
|
259
|
+
meta as { preTokenBalances?: unknown[] } | undefined
|
|
260
|
+
)?.preTokenBalances;
|
|
261
|
+
return Array.isArray(arr) ? arr : [];
|
|
262
|
+
})(),
|
|
263
|
+
postTokenBalances: (() => {
|
|
264
|
+
const arr = (
|
|
265
|
+
meta as { postTokenBalances?: unknown[] } | undefined
|
|
266
|
+
)?.postTokenBalances;
|
|
267
|
+
return Array.isArray(arr) ? arr : [];
|
|
268
|
+
})(),
|
|
269
|
+
innerInstructions: Array.isArray(meta?.innerInstructions)
|
|
270
|
+
? meta?.innerInstructions
|
|
271
|
+
: [],
|
|
272
|
+
computeUnits:
|
|
273
|
+
meta?.computeUnits == null ? null : Number(meta.computeUnits),
|
|
274
|
+
returnData: meta?.returnData ?? null,
|
|
275
|
+
accounts,
|
|
276
|
+
accountStates: Array.isArray(meta?.accountStates)
|
|
277
|
+
? meta?.accountStates
|
|
278
|
+
: [],
|
|
279
|
+
})
|
|
280
|
+
.catch(() => {});
|
|
285
281
|
|
|
286
282
|
// Upsert account snapshots for static keys
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
283
|
+
const snapshots = keys
|
|
284
|
+
.map((addr) => {
|
|
285
|
+
try {
|
|
286
|
+
const acc = this.svm.getAccount(new PublicKey(addr));
|
|
287
|
+
if (!acc) return null;
|
|
288
|
+
const ownerStr = new PublicKey(acc.owner).toBase58();
|
|
289
|
+
let dataBase64: string | undefined;
|
|
290
|
+
// Store raw data for SPL Token accounts to reflect balance changes
|
|
291
|
+
try {
|
|
292
|
+
if (
|
|
293
|
+
ownerStr ===
|
|
294
|
+
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ||
|
|
295
|
+
ownerStr === "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
|
|
296
|
+
) {
|
|
297
|
+
if (acc.data && acc.data.length > 0) {
|
|
298
|
+
dataBase64 = Buffer.from(acc.data).toString("base64");
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
} catch {}
|
|
302
|
+
return {
|
|
303
|
+
address: addr,
|
|
304
|
+
lamports: Number(acc.lamports || 0n),
|
|
305
|
+
ownerProgram: ownerStr,
|
|
306
|
+
executable: !!acc.executable,
|
|
307
|
+
rentEpoch: Number(acc.rentEpoch || 0),
|
|
308
|
+
dataLen: acc.data?.length ?? 0,
|
|
309
|
+
dataBase64,
|
|
310
|
+
lastSlot: Number(this.slot),
|
|
311
|
+
};
|
|
312
|
+
} catch {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
})
|
|
319
316
|
.filter(Boolean) as import("../src/db/tx-store").AccountSnapshot[];
|
|
320
317
|
if (snapshots.length > 0)
|
|
321
318
|
this.store.upsertAccounts(snapshots).catch(() => {});
|
package/server/types.ts
CHANGED
|
@@ -44,62 +44,62 @@ export interface RpcMethodContext {
|
|
|
44
44
|
listMints?: () => string[];
|
|
45
45
|
registerProgram?: (program: PublicKey | string) => void;
|
|
46
46
|
listPrograms?: () => string[];
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
47
|
+
recordTransaction: (
|
|
48
|
+
signature: string,
|
|
49
|
+
tx: VersionedTransaction,
|
|
50
|
+
meta?: {
|
|
51
|
+
logs?: string[];
|
|
52
|
+
err?: unknown;
|
|
53
|
+
fee?: number;
|
|
54
|
+
blockTime?: number;
|
|
55
|
+
preBalances?: number[];
|
|
56
|
+
postBalances?: number[];
|
|
57
|
+
preTokenBalances?: unknown[];
|
|
58
|
+
postTokenBalances?: unknown[];
|
|
59
|
+
innerInstructions?: unknown[];
|
|
60
|
+
computeUnits?: number | bigint | null;
|
|
61
|
+
returnData?: { programId: string; dataBase64: string } | null;
|
|
62
|
+
// Optional rich per-account snapshots captured around execution
|
|
63
|
+
accountStates?: Array<{
|
|
64
|
+
address: string;
|
|
65
|
+
pre?: {
|
|
66
|
+
lamports?: number;
|
|
67
|
+
ownerProgram?: string;
|
|
68
|
+
executable?: boolean;
|
|
69
|
+
rentEpoch?: number;
|
|
70
|
+
dataLen?: number;
|
|
71
|
+
dataBase64?: string | null;
|
|
72
|
+
lastSlot?: number;
|
|
73
|
+
} | null;
|
|
74
|
+
post?: {
|
|
75
|
+
lamports?: number;
|
|
76
|
+
ownerProgram?: string;
|
|
77
|
+
executable?: boolean;
|
|
78
|
+
rentEpoch?: number;
|
|
79
|
+
dataLen?: number;
|
|
80
|
+
dataBase64?: string | null;
|
|
81
|
+
lastSlot?: number;
|
|
82
|
+
} | null;
|
|
83
|
+
}>;
|
|
84
|
+
},
|
|
85
|
+
) => void;
|
|
86
|
+
getRecordedTransaction: (signature: string) =>
|
|
87
|
+
| {
|
|
88
|
+
tx: VersionedTransaction;
|
|
89
|
+
logs: string[];
|
|
90
|
+
err: unknown;
|
|
91
|
+
fee: number;
|
|
92
|
+
slot: number;
|
|
93
|
+
blockTime?: number;
|
|
94
|
+
preBalances?: number[];
|
|
95
|
+
postBalances?: number[];
|
|
96
|
+
preTokenBalances?: unknown[];
|
|
97
|
+
postTokenBalances?: unknown[];
|
|
98
|
+
innerInstructions?: unknown[];
|
|
99
|
+
computeUnits?: number | null;
|
|
100
|
+
returnData?: { programId: string; dataBase64: string } | null;
|
|
101
|
+
}
|
|
102
|
+
| undefined;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
export type RpcMethodHandler = (
|
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
2
2
|
|
|
3
3
|
export const transactions = sqliteTable(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
4
|
+
"transactions",
|
|
5
|
+
{
|
|
6
|
+
signature: text("signature").primaryKey(),
|
|
7
|
+
slot: integer("slot").notNull(),
|
|
8
|
+
blockTime: integer("block_time"),
|
|
9
|
+
version: text("version").notNull(), // 0 | "legacy"
|
|
10
|
+
errJson: text("err_json"),
|
|
11
|
+
fee: integer("fee").notNull(),
|
|
12
|
+
rawBase64: text("raw_base64").notNull(),
|
|
13
|
+
preBalancesJson: text("pre_balances_json").notNull(),
|
|
14
|
+
postBalancesJson: text("post_balances_json").notNull(),
|
|
15
|
+
logsJson: text("logs_json").notNull(),
|
|
16
|
+
preTokenBalancesJson: text("pre_token_balances_json")
|
|
17
|
+
.default("[]")
|
|
18
|
+
.notNull(),
|
|
19
|
+
postTokenBalancesJson: text("post_token_balances_json")
|
|
20
|
+
.default("[]")
|
|
21
|
+
.notNull(),
|
|
22
|
+
// Additional rich metadata captured after execution
|
|
23
|
+
innerInstructionsJson: text("inner_instructions_json")
|
|
24
|
+
.default("[]")
|
|
25
|
+
.notNull(),
|
|
26
|
+
computeUnits: integer("compute_units"),
|
|
27
|
+
returnDataProgramId: text("return_data_program_id"),
|
|
28
|
+
returnDataBase64: text("return_data_base64"),
|
|
29
|
+
},
|
|
30
|
+
(t) => ({
|
|
31
|
+
slotIdx: index("idx_transactions_slot").on(t.slot),
|
|
32
|
+
}),
|
|
33
33
|
);
|
|
34
34
|
|
|
35
35
|
export type TransactionRow = typeof transactions.$inferSelect;
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { index, primaryKey, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
2
2
|
|
|
3
3
|
export const txAccountStates = sqliteTable(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
"tx_account_states",
|
|
5
|
+
{
|
|
6
|
+
signature: text("signature").notNull(),
|
|
7
|
+
address: text("address").notNull(),
|
|
8
|
+
// JSON blobs capturing minimal account snapshot
|
|
9
|
+
// { lamports, ownerProgram, executable, rentEpoch, dataLen, dataBase64? }
|
|
10
|
+
preJson: text("pre_json"),
|
|
11
|
+
postJson: text("post_json"),
|
|
12
|
+
},
|
|
13
|
+
(t) => ({
|
|
14
|
+
pk: primaryKey({
|
|
15
|
+
columns: [t.signature, t.address],
|
|
16
|
+
name: "pk_tx_account_states",
|
|
17
|
+
}),
|
|
18
|
+
addrIdx: index("idx_tx_account_states_address").on(t.address),
|
|
19
|
+
}),
|
|
17
20
|
);
|
|
18
21
|
|
|
19
22
|
export type TxAccountStateRow = typeof txAccountStates.$inferSelect;
|
|
20
23
|
export type NewTxAccountStateRow = typeof txAccountStates.$inferInsert;
|
|
21
|
-
|