solforge 0.2.5 → 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.
Files changed (80) hide show
  1. package/package.json +1 -1
  2. package/scripts/postinstall.cjs +3 -3
  3. package/server/lib/base58.ts +1 -1
  4. package/server/lib/instruction-parser.ts +242 -0
  5. package/server/methods/account/get-account-info.ts +3 -7
  6. package/server/methods/account/get-balance.ts +3 -7
  7. package/server/methods/account/get-multiple-accounts.ts +2 -1
  8. package/server/methods/account/get-parsed-account-info.ts +3 -7
  9. package/server/methods/account/parsers/index.ts +2 -2
  10. package/server/methods/account/parsers/loader-upgradeable.ts +14 -1
  11. package/server/methods/account/parsers/spl-token.ts +29 -10
  12. package/server/methods/account/request-airdrop.ts +122 -86
  13. package/server/methods/admin/mint-to.ts +11 -38
  14. package/server/methods/block/get-block.ts +3 -7
  15. package/server/methods/block/get-blocks-with-limit.ts +3 -7
  16. package/server/methods/block/is-blockhash-valid.ts +3 -7
  17. package/server/methods/get-address-lookup-table.ts +3 -7
  18. package/server/methods/program/get-program-accounts.ts +9 -9
  19. package/server/methods/program/get-token-account-balance.ts +3 -7
  20. package/server/methods/program/get-token-accounts-by-delegate.ts +4 -3
  21. package/server/methods/program/get-token-accounts-by-owner.ts +54 -33
  22. package/server/methods/program/get-token-largest-accounts.ts +3 -2
  23. package/server/methods/program/get-token-supply.ts +3 -2
  24. package/server/methods/solforge/index.ts +9 -6
  25. package/server/methods/transaction/get-parsed-transaction.ts +3 -7
  26. package/server/methods/transaction/get-signature-statuses.ts +14 -7
  27. package/server/methods/transaction/get-signatures-for-address.ts +3 -7
  28. package/server/methods/transaction/get-transaction.ts +434 -287
  29. package/server/methods/transaction/inner-instructions.test.ts +63 -0
  30. package/server/methods/transaction/send-transaction.ts +248 -56
  31. package/server/methods/transaction/simulate-transaction.ts +3 -2
  32. package/server/rpc-server.ts +98 -61
  33. package/server/types.ts +65 -30
  34. package/server/ws-server.ts +11 -7
  35. package/src/api-server-entry.ts +5 -5
  36. package/src/cli/commands/airdrop.ts +2 -2
  37. package/src/cli/commands/config.ts +2 -2
  38. package/src/cli/commands/mint.ts +3 -3
  39. package/src/cli/commands/program-clone.ts +9 -11
  40. package/src/cli/commands/program-load.ts +3 -3
  41. package/src/cli/commands/rpc-start.ts +7 -7
  42. package/src/cli/commands/token-adopt-authority.ts +1 -1
  43. package/src/cli/commands/token-clone.ts +5 -6
  44. package/src/cli/commands/token-create.ts +5 -5
  45. package/src/cli/main.ts +33 -36
  46. package/src/cli/run-solforge.ts +3 -3
  47. package/src/cli/setup-wizard.ts +8 -6
  48. package/src/commands/add-program.ts +1 -1
  49. package/src/commands/init.ts +2 -2
  50. package/src/commands/mint.ts +5 -6
  51. package/src/commands/start.ts +10 -9
  52. package/src/commands/status.ts +1 -1
  53. package/src/commands/stop.ts +1 -1
  54. package/src/config/index.ts +33 -17
  55. package/src/config/manager.ts +3 -3
  56. package/src/db/index.ts +2 -2
  57. package/src/db/schema/index.ts +1 -0
  58. package/src/db/schema/transactions.ts +29 -22
  59. package/src/db/schema/tx-account-states.ts +21 -0
  60. package/src/db/tx-store.ts +113 -76
  61. package/src/gui/public/app.css +13 -13
  62. package/src/gui/server.ts +1 -1
  63. package/src/gui/src/api.ts +1 -1
  64. package/src/gui/src/app.tsx +49 -17
  65. package/src/gui/src/components/airdrop-mint-form.tsx +32 -8
  66. package/src/gui/src/components/clone-program-modal.tsx +25 -6
  67. package/src/gui/src/components/clone-token-modal.tsx +25 -6
  68. package/src/gui/src/components/modal.tsx +6 -1
  69. package/src/gui/src/components/status-panel.tsx +1 -1
  70. package/src/index.ts +19 -6
  71. package/src/migrations-bundled.ts +8 -2
  72. package/src/services/api-server.ts +41 -19
  73. package/src/services/port-manager.ts +7 -10
  74. package/src/services/process-registry.ts +4 -5
  75. package/src/services/program-cloner.ts +4 -4
  76. package/src/services/token-cloner.ts +4 -4
  77. package/src/services/validator.ts +2 -4
  78. package/src/types/config.ts +2 -2
  79. package/src/utils/shell.ts +1 -1
  80. package/src/utils/token-loader.ts +2 -2
@@ -8,13 +8,13 @@ import { parseFlags } from "../utils/args";
8
8
  // solforge token create --decimals <d> --owner <pubkey> [--mint <pubkey>] [--amount <baseUnits> | --ui-amount <num>]
9
9
  export async function tokenCreateCommand(args: string[]) {
10
10
  const { flags } = parseFlags(args);
11
- const decimals = flags["decimals"] ? Number(flags["decimals"]) : NaN;
12
- const owner = flags["owner"] as string | undefined;
13
- const mint = flags["mint"] as string | undefined;
14
- const amountBase = flags["amount"] as string | undefined;
11
+ const decimals = flags.decimals ? Number(flags.decimals) : NaN;
12
+ const owner = flags.owner as string | undefined;
13
+ const mint = flags.mint as string | undefined;
14
+ const amountBase = flags.amount as string | undefined;
15
15
  const uiAmount = flags["ui-amount"] as string | undefined;
16
16
 
17
- if (!isFinite(decimals)) {
17
+ if (!Number.isFinite(decimals)) {
18
18
  p.log.error("--decimals is required (0-18)");
19
19
  return;
20
20
  }
package/src/cli/main.ts CHANGED
@@ -1,24 +1,23 @@
1
1
  // Minimal, fast CLI router with @clack/prompts for UX
2
2
  import * as p from "@clack/prompts";
3
- // Load version for --version in both bun script and compiled binary
4
- // eslint-disable-next-line @typescript-eslint/consistent-type-imports
5
- import pkg from "../../package.json" assert { type: "json" };
3
+ // CLI version string; keep in sync with package.json if possible
4
+ const VERSION = "0.2.4";
6
5
 
7
6
  // Robust arg parsing for both bun script and compiled binary
8
7
  const known = new Set([
9
- "help",
10
- "-h",
11
- "--help",
12
- "version",
13
- "-v",
14
- "--version",
15
- "rpc",
16
- "start",
17
- "config",
18
- "airdrop",
19
- "mint",
20
- "token",
21
- "program",
8
+ "help",
9
+ "-h",
10
+ "--help",
11
+ "version",
12
+ "-v",
13
+ "--version",
14
+ "rpc",
15
+ "start",
16
+ "config",
17
+ "airdrop",
18
+ "mint",
19
+ "token",
20
+ "program",
22
21
  ]);
23
22
  const raw = Bun.argv.slice(1);
24
23
  const firstIdx = raw.findIndex((a) => known.has(String(a)));
@@ -27,22 +26,22 @@ const argv = firstIdx >= 0 ? raw.slice(firstIdx) : [];
27
26
  async function main() {
28
27
  const [cmd, sub, ...rest] = argv;
29
28
 
30
- if (!cmd) {
31
- const { runSolforge } = await import("./run-solforge");
32
- // Pass through any flags provided when no explicit command was given
33
- await runSolforge(raw);
34
- return;
35
- }
29
+ if (!cmd) {
30
+ const { runSolforge } = await import("./run-solforge");
31
+ // Pass through any flags provided when no explicit command was given
32
+ await runSolforge(raw);
33
+ return;
34
+ }
36
35
 
37
- if (cmd === "help" || cmd === "-h" || cmd === "--help") {
38
- printHelp();
39
- return;
40
- }
36
+ if (cmd === "help" || cmd === "-h" || cmd === "--help") {
37
+ printHelp();
38
+ return;
39
+ }
41
40
 
42
- if (cmd === "version" || cmd === "-v" || cmd === "--version") {
43
- printVersion();
44
- return;
45
- }
41
+ if (cmd === "version" || cmd === "-v" || cmd === "--version") {
42
+ printVersion();
43
+ return;
44
+ }
46
45
 
47
46
  // Alias: solforge start -> solforge rpc start
48
47
  if (cmd === "start") {
@@ -117,7 +116,7 @@ async function main() {
117
116
  }
118
117
 
119
118
  function printHelp() {
120
- console.log(`
119
+ console.log(`
121
120
  solforge <command>
122
121
 
123
122
  Commands:
@@ -142,14 +141,12 @@ Options:
142
141
  }
143
142
 
144
143
  async function unknownCommand(parts: (string | undefined)[]) {
145
- p.log.error(`Unknown command: ${parts.filter(Boolean).join(" ")}`);
146
- printHelp();
144
+ p.log.error(`Unknown command: ${parts.filter(Boolean).join(" ")}`);
145
+ printHelp();
147
146
  }
148
147
 
149
148
  function printVersion() {
150
- // Prefer package.json version if available
151
- const v = (pkg as any)?.version ?? "";
152
- console.log(String(v));
149
+ console.log(String(VERSION));
153
150
  }
154
151
 
155
152
  main();
@@ -15,7 +15,7 @@ const CONFIG_PATH = "sf.config.json";
15
15
 
16
16
  export async function runSolforge(args: string[] = []) {
17
17
  const { flags } = parseFlags(args);
18
- const ci = flags["ci"] === true || flags["y"] === true;
18
+ const ci = flags.ci === true || flags.y === true;
19
19
  const config = await ensureConfig(ci);
20
20
  await startWithConfig(config, args);
21
21
  }
@@ -53,9 +53,9 @@ async function ensureConfig(ci = false): Promise<SolforgeConfig> {
53
53
  async function startWithConfig(config: SolforgeConfig, args: string[] = []) {
54
54
  const { flags } = parseFlags(args);
55
55
  const host = String(
56
- flags["network"] === true
56
+ flags.network === true
57
57
  ? "0.0.0.0"
58
- : ((flags["host"] as string) ?? process.env.RPC_HOST ?? "127.0.0.1"),
58
+ : ((flags.host as string) ?? process.env.RPC_HOST ?? "127.0.0.1"),
59
59
  );
60
60
  const rpcPort = Number(config.server.rpcPort || defaultConfig.server.rpcPort);
61
61
  const wsPort = Number(config.server.wsPort || rpcPort + 1);
@@ -170,9 +170,10 @@ async function resolveTokens(selections: string[], existing: string[] = []) {
170
170
  const set = new Set(existing);
171
171
  for (const selection of selections) {
172
172
  if (selection === "__custom__") {
173
- (await collectCustomEntries("token mint address")).forEach((value) =>
174
- set.add(value),
175
- );
173
+ {
174
+ const values = await collectCustomEntries("token mint address");
175
+ for (const value of values) set.add(value);
176
+ }
176
177
  continue;
177
178
  }
178
179
  const preset = TOKEN_PRESETS.find((token) => token.value === selection);
@@ -193,9 +194,10 @@ async function resolvePrograms(selections: string[], existing: string[] = []) {
193
194
  const set = new Set(existing);
194
195
  for (const selection of selections) {
195
196
  if (selection === "__custom__") {
196
- (await collectCustomEntries("program id")).forEach((value) =>
197
- set.add(value),
198
- );
197
+ {
198
+ const values = await collectCustomEntries("program id");
199
+ for (const value of values) set.add(value);
200
+ }
199
201
  continue;
200
202
  }
201
203
  const preset = PROGRAM_PRESETS.find(
@@ -1,5 +1,5 @@
1
1
  import chalk from "chalk";
2
- import { existsSync } from "fs";
2
+ import { existsSync } from "node:fs";
3
3
  import inquirer from "inquirer";
4
4
  import { configManager } from "../config/manager.js";
5
5
  import { processRegistry } from "../services/process-registry.js";
@@ -1,7 +1,7 @@
1
1
  import chalk from "chalk";
2
- import { existsSync, writeFileSync } from "fs";
2
+ import { existsSync, writeFileSync } from "node:fs";
3
3
  import inquirer from "inquirer";
4
- import { resolve } from "path";
4
+ import { resolve } from "node:path";
5
5
  import type { Config } from "../types/config.js";
6
6
 
7
7
  const defaultConfig: Config = {
@@ -1,9 +1,8 @@
1
1
  import { input, select } from "@inquirer/prompts";
2
- import { Keypair, PublicKey } from "@solana/web3.js";
2
+ import { PublicKey } from "@solana/web3.js";
3
3
  import chalk from "chalk";
4
4
  import { Command } from "commander";
5
- import { existsSync, readFileSync } from "fs";
6
- import { join } from "path";
5
+ import { existsSync, readFileSync } from "node:fs";
7
6
  import type { TokenConfig } from "../types/config.js";
8
7
  import { runCommand } from "../utils/shell";
9
8
  import {
@@ -115,7 +114,7 @@ export const mintCommand = new Command()
115
114
  let amount: string;
116
115
  if (options.amount) {
117
116
  const num = parseFloat(options.amount);
118
- if (isNaN(num) || num <= 0) {
117
+ if (Number.isNaN(num) || num <= 0) {
119
118
  console.error(chalk.red("❌ Invalid amount"));
120
119
  process.exit(1);
121
120
  }
@@ -126,7 +125,7 @@ export const mintCommand = new Command()
126
125
  message: "Enter amount to mint:",
127
126
  validate: (value: string) => {
128
127
  const num = parseFloat(value);
129
- if (isNaN(num) || num <= 0) {
128
+ if (Number.isNaN(num) || num <= 0) {
130
129
  return "Please enter a valid positive number";
131
130
  }
132
131
  return true;
@@ -219,7 +218,7 @@ export async function mintTokenToWallet(
219
218
  break;
220
219
  }
221
220
  }
222
- } catch (error) {
221
+ } catch (_error) {
223
222
  // No existing accounts found or parsing error, will create new account
224
223
  }
225
224
  }
@@ -1,8 +1,8 @@
1
1
  import chalk from "chalk";
2
- import { spawn } from "child_process";
3
- import { existsSync, readFileSync } from "fs";
2
+ import { spawn } from "node:child_process";
3
+ import { existsSync, readFileSync } from "node:fs";
4
4
  import ora from "ora";
5
- import { join } from "path";
5
+ import { join } from "node:path";
6
6
  import { configManager } from "../config/manager.js";
7
7
  import { portManager } from "../services/port-manager.js";
8
8
  import type { RunningValidator } from "../services/process-registry.js";
@@ -392,7 +392,7 @@ export async function startCommand(
392
392
  if (pidResult.success && pidResult.stdout.trim()) {
393
393
  const pidLine = pidResult.stdout.trim().split("\n")[0];
394
394
  if (pidLine) {
395
- apiServerPid = parseInt(pidLine);
395
+ apiServerPid = parseInt(pidLine, 10);
396
396
  }
397
397
  }
398
398
  } else {
@@ -434,7 +434,7 @@ export async function startCommand(
434
434
  const runningValidator: RunningValidator = {
435
435
  id: validatorId,
436
436
  name: config.name,
437
- pid: validatorProcess.pid!,
437
+ pid: validatorProcess.pid,
438
438
  rpcPort: config.localnet.port,
439
439
  faucetPort: config.localnet.faucetPort,
440
440
  rpcUrl: `http://127.0.0.1:${config.localnet.port}`,
@@ -558,7 +558,7 @@ export async function startCommand(
558
558
  console.log(chalk.yellow("\n📦 Cloned programs:"));
559
559
  config.programs.forEach((program) => {
560
560
  const name =
561
- program.name || program.mainnetProgramId.slice(0, 8) + "...";
561
+ program.name || `${program.mainnetProgramId.slice(0, 8)}...`;
562
562
  console.log(chalk.gray(` - ${name}: ${program.mainnetProgramId}`));
563
563
  });
564
564
  }
@@ -778,8 +778,9 @@ async function waitForValidatorReady(
778
778
  /**
779
779
  * Airdrop SOL to the mint authority for fee payments
780
780
  */
781
+
781
782
  async function airdropSolToMintAuthority(
782
- clonedToken: any,
783
+ clonedToken: ClonedToken,
783
784
  rpcUrl: string,
784
785
  debug: boolean = false,
785
786
  ): Promise<void> {
@@ -813,7 +814,7 @@ async function airdropSolToMintAuthority(
813
814
  */
814
815
  async function checkExistingClonedTokens(
815
816
  tokens: TokenConfig[],
816
- tokenCloner: TokenCloner,
817
+ _tokenCloner: TokenCloner,
817
818
  ): Promise<{ existingTokens: ClonedToken[]; tokensToClone: TokenConfig[] }> {
818
819
  const existingTokens: ClonedToken[] = [];
819
820
  const tokensToClone: TokenConfig[] = [];
@@ -851,7 +852,7 @@ async function checkExistingClonedTokens(
851
852
  // Old format: file contains {publicKey, secretKey}
852
853
  sharedMintAuthority = fileContent;
853
854
  }
854
- } catch (error) {
855
+ } catch (_error) {
855
856
  // If we can't read the shared mint authority, treat all tokens as needing to be cloned
856
857
  sharedMintAuthority = null;
857
858
  }
@@ -90,7 +90,7 @@ export async function statusCommand(): Promise<void> {
90
90
  console.log(` 📝 Project: ${config.name}`);
91
91
  console.log(` 🪙 Tokens: ${config.tokens.length}`);
92
92
  console.log(` 📦 Programs: ${config.programs.length}`);
93
- } catch (error) {
93
+ } catch (_error) {
94
94
  console.log(` ❌ No valid configuration found`);
95
95
  console.log(` 💡 Run 'solforge init' to create one`);
96
96
  }
@@ -186,7 +186,7 @@ async function waitForProcessShutdown(
186
186
  process.kill(pid, 0);
187
187
  // If no error thrown, process is still running
188
188
  await new Promise((resolve) => setTimeout(resolve, 500));
189
- } catch (error) {
189
+ } catch (_error) {
190
190
  // Process is gone
191
191
  return { success: true };
192
192
  }
@@ -68,7 +68,7 @@ export async function writeDefaultConfig(opts: { force?: boolean } = {}) {
68
68
  mkdirSync(dir, { recursive: true });
69
69
  } catch {}
70
70
  }
71
- writeFileSync(p, JSON.stringify(defaultConfig, null, 2) + "\n");
71
+ writeFileSync(p, `${JSON.stringify(defaultConfig, null, 2)}\n`);
72
72
  }
73
73
 
74
74
  export async function writeConfig(
@@ -81,50 +81,66 @@ export async function writeConfig(
81
81
  mkdirSync(dir, { recursive: true });
82
82
  } catch {}
83
83
  }
84
- await Bun.write(path, JSON.stringify(config, null, 2) + "\n");
84
+ await Bun.write(path, `${JSON.stringify(config, null, 2)}\n`);
85
85
  }
86
86
 
87
- export function getConfigValue(cfg: any, path?: string) {
87
+ export function getConfigValue(
88
+ cfg: Record<string, unknown>,
89
+ path?: string,
90
+ ): unknown {
88
91
  if (!path) return cfg;
89
- return path.split(".").reduce((o, k) => (o ? o[k] : undefined), cfg);
92
+ let cur: unknown = cfg;
93
+ for (const k of path.split(".")) {
94
+ if (
95
+ cur &&
96
+ typeof cur === "object" &&
97
+ k in (cur as Record<string, unknown>)
98
+ ) {
99
+ cur = (cur as Record<string, unknown>)[k];
100
+ } else {
101
+ return undefined;
102
+ }
103
+ }
104
+ return cur;
90
105
  }
91
106
 
92
- export function setConfigValue<T extends Record<string, any>>(
107
+ export function setConfigValue<T extends Record<string, unknown>>(
93
108
  cfg: T,
94
109
  path: string,
95
- value: any,
110
+ value: unknown,
96
111
  ): T {
97
112
  const parts = path.split(".");
98
- let node: any = cfg;
113
+ let node: Record<string, unknown> = cfg;
99
114
  for (let i = 0; i < parts.length - 1; i++) {
100
115
  const k = parts[i];
101
116
  if (!node[k] || typeof node[k] !== "object") node[k] = {};
102
- node = node[k];
117
+ node = node[k] as Record<string, unknown>;
103
118
  }
104
119
  node[parts[parts.length - 1]] = coerceValue(value);
105
120
  return cfg;
106
121
  }
107
122
 
108
- function coerceValue(v: any) {
123
+ function coerceValue(v: unknown): unknown {
109
124
  if (v === "true") return true;
110
125
  if (v === "false") return false;
111
- if (v !== "" && !isNaN(Number(v))) return Number(v);
126
+ if (typeof v === "string" && v !== "" && !Number.isNaN(Number(v)))
127
+ return Number(v);
112
128
  try {
113
- return JSON.parse(v);
129
+ return typeof v === "string" ? JSON.parse(v) : v;
114
130
  } catch {
115
131
  return v;
116
132
  }
117
133
  }
118
134
 
119
135
  function deepMerge<T>(a: T, b: Partial<T>): T {
120
- if (Array.isArray(a) || Array.isArray(b)) return (b as any) ?? (a as any);
136
+ if (Array.isArray(a) || Array.isArray(b)) return (b ?? a) as unknown as T;
121
137
  if (typeof a === "object" && typeof b === "object" && a && b) {
122
- const out: any = { ...a };
138
+ const out: Record<string, unknown> = { ...(a as Record<string, unknown>) };
123
139
  for (const [k, v] of Object.entries(b)) {
124
- const ak = (a as any)[k];
125
- out[k] = deepMerge(ak, v as any);
140
+ const ak = (a as Record<string, unknown>)[k];
141
+ out[k] = deepMerge(ak as unknown, v as unknown) as unknown;
126
142
  }
127
- return out;
143
+ return out as unknown as T;
128
144
  }
129
- return (b as any) ?? (a as any);
145
+ return (b ?? a) as unknown as T;
130
146
  }
@@ -1,5 +1,5 @@
1
- import { existsSync, readFileSync, writeFileSync } from "fs";
2
- import { join, resolve } from "path";
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
3
  import type { Config, ValidationResult } from "../types/config.js";
4
4
  import { ConfigSchema } from "../types/config.js";
5
5
 
@@ -77,7 +77,7 @@ export class ConfigManager {
77
77
  /**
78
78
  * Validate a configuration object
79
79
  */
80
- validate(config: any): ValidationResult {
80
+ validate(config: unknown): ValidationResult {
81
81
  const result = ConfigSchema.safeParse(config);
82
82
 
83
83
  if (result.success) {
package/src/db/index.ts CHANGED
@@ -27,10 +27,10 @@ if (!PERSIST && DB_PATH !== ":memory:") {
27
27
  if (existsSync(DB_PATH)) unlinkSync(DB_PATH);
28
28
  } catch {}
29
29
  try {
30
- if (existsSync(DB_PATH + "-wal")) unlinkSync(DB_PATH + "-wal");
30
+ if (existsSync(`${DB_PATH}-wal`)) unlinkSync(`${DB_PATH}-wal`);
31
31
  } catch {}
32
32
  try {
33
- if (existsSync(DB_PATH + "-shm")) unlinkSync(DB_PATH + "-shm");
33
+ if (existsSync(`${DB_PATH}-shm`)) unlinkSync(`${DB_PATH}-shm`);
34
34
  } catch {}
35
35
  }
36
36
 
@@ -3,3 +3,4 @@ export * from "./address-signatures";
3
3
  export * from "./meta-kv";
4
4
  export * from "./transactions";
5
5
  export * from "./tx-accounts";
6
+ export * from "./tx-account-states";
@@ -1,28 +1,35 @@
1
1
  import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
2
2
 
3
3
  export const transactions = sqliteTable(
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
- },
23
- (t) => ({
24
- slotIdx: index("idx_transactions_slot").on(t.slot),
25
- }),
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
+ }),
26
33
  );
27
34
 
28
35
  export type TransactionRow = typeof transactions.$inferSelect;
@@ -0,0 +1,21 @@
1
+ import { index, primaryKey, sqliteTable, text } from "drizzle-orm/sqlite-core";
2
+
3
+ export const txAccountStates = sqliteTable(
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({ columns: [t.signature, t.address], name: "pk_tx_account_states" }),
15
+ addrIdx: index("idx_tx_account_states_address").on(t.address),
16
+ }),
17
+ );
18
+
19
+ export type TxAccountStateRow = typeof txAccountStates.$inferSelect;
20
+ export type NewTxAccountStateRow = typeof txAccountStates.$inferInsert;
21
+