solforge 0.2.12 → 0.2.14

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 (175) hide show
  1. package/package.json +1 -5
  2. package/start.cjs +19 -23
  3. package/docs/API.md +0 -379
  4. package/docs/CONFIGURATION.md +0 -407
  5. package/docs/bun-single-file-executable.md +0 -585
  6. package/docs/cli-plan.md +0 -154
  7. package/docs/data-indexing-plan.md +0 -214
  8. package/docs/gui-roadmap.md +0 -202
  9. package/scripts/decode-b58.ts +0 -10
  10. package/scripts/install.sh +0 -112
  11. package/server/index.ts +0 -5
  12. package/server/lib/base58.ts +0 -33
  13. package/server/lib/faucet.ts +0 -110
  14. package/server/lib/instruction-parser.ts +0 -328
  15. package/server/lib/parsers/spl-associated-token-account.ts +0 -50
  16. package/server/lib/parsers/spl-token.ts +0 -340
  17. package/server/lib/spl-token.ts +0 -57
  18. package/server/methods/TEMPLATE.md +0 -117
  19. package/server/methods/account/get-account-info.ts +0 -86
  20. package/server/methods/account/get-balance.ts +0 -23
  21. package/server/methods/account/get-multiple-accounts.ts +0 -84
  22. package/server/methods/account/get-parsed-account-info.ts +0 -17
  23. package/server/methods/account/index.ts +0 -12
  24. package/server/methods/account/parsers/index.ts +0 -52
  25. package/server/methods/account/parsers/loader-upgradeable.ts +0 -79
  26. package/server/methods/account/parsers/spl-token.ts +0 -256
  27. package/server/methods/account/parsers/system.ts +0 -4
  28. package/server/methods/account/request-airdrop.ts +0 -271
  29. package/server/methods/admin/adopt-mint-authority.ts +0 -94
  30. package/server/methods/admin/clone-program-accounts.ts +0 -55
  31. package/server/methods/admin/clone-program.ts +0 -152
  32. package/server/methods/admin/clone-token-accounts.ts +0 -117
  33. package/server/methods/admin/clone-token-mint.ts +0 -82
  34. package/server/methods/admin/create-mint.ts +0 -114
  35. package/server/methods/admin/create-token-account.ts +0 -137
  36. package/server/methods/admin/helpers.ts +0 -70
  37. package/server/methods/admin/index.ts +0 -10
  38. package/server/methods/admin/list-mints.ts +0 -21
  39. package/server/methods/admin/load-program.ts +0 -52
  40. package/server/methods/admin/mint-to.ts +0 -266
  41. package/server/methods/block/get-block-height.ts +0 -5
  42. package/server/methods/block/get-block.ts +0 -31
  43. package/server/methods/block/get-blocks-with-limit.ts +0 -19
  44. package/server/methods/block/get-latest-blockhash.ts +0 -12
  45. package/server/methods/block/get-slot.ts +0 -5
  46. package/server/methods/block/index.ts +0 -6
  47. package/server/methods/block/is-blockhash-valid.ts +0 -19
  48. package/server/methods/epoch/get-cluster-nodes.ts +0 -17
  49. package/server/methods/epoch/get-epoch-info.ts +0 -16
  50. package/server/methods/epoch/get-epoch-schedule.ts +0 -15
  51. package/server/methods/epoch/get-highest-snapshot-slot.ts +0 -9
  52. package/server/methods/epoch/get-leader-schedule.ts +0 -8
  53. package/server/methods/epoch/get-max-retransmit-slot.ts +0 -9
  54. package/server/methods/epoch/get-max-shred-insert-slot.ts +0 -9
  55. package/server/methods/epoch/get-slot-leader.ts +0 -6
  56. package/server/methods/epoch/get-slot-leaders.ts +0 -9
  57. package/server/methods/epoch/get-stake-activation.ts +0 -9
  58. package/server/methods/epoch/get-stake-minimum-delegation.ts +0 -9
  59. package/server/methods/epoch/get-vote-accounts.ts +0 -19
  60. package/server/methods/epoch/index.ts +0 -13
  61. package/server/methods/epoch/minimum-ledger-slot.ts +0 -5
  62. package/server/methods/fee/get-fee-calculator-for-blockhash.ts +0 -12
  63. package/server/methods/fee/get-fee-for-message.ts +0 -8
  64. package/server/methods/fee/get-fee-rate-governor.ts +0 -16
  65. package/server/methods/fee/get-fees.ts +0 -14
  66. package/server/methods/fee/get-recent-prioritization-fees.ts +0 -22
  67. package/server/methods/fee/index.ts +0 -5
  68. package/server/methods/get-address-lookup-table.ts +0 -27
  69. package/server/methods/index.ts +0 -265
  70. package/server/methods/performance/get-recent-performance-samples.ts +0 -25
  71. package/server/methods/performance/get-transaction-count.ts +0 -5
  72. package/server/methods/performance/index.ts +0 -2
  73. package/server/methods/program/get-block-commitment.ts +0 -9
  74. package/server/methods/program/get-block-production.ts +0 -14
  75. package/server/methods/program/get-block-time.ts +0 -21
  76. package/server/methods/program/get-blocks.ts +0 -11
  77. package/server/methods/program/get-first-available-block.ts +0 -9
  78. package/server/methods/program/get-genesis-hash.ts +0 -6
  79. package/server/methods/program/get-identity.ts +0 -6
  80. package/server/methods/program/get-inflation-governor.ts +0 -15
  81. package/server/methods/program/get-inflation-rate.ts +0 -10
  82. package/server/methods/program/get-inflation-reward.ts +0 -12
  83. package/server/methods/program/get-largest-accounts.ts +0 -8
  84. package/server/methods/program/get-parsed-program-accounts.ts +0 -12
  85. package/server/methods/program/get-parsed-token-accounts-by-delegate.ts +0 -12
  86. package/server/methods/program/get-parsed-token-accounts-by-owner.ts +0 -12
  87. package/server/methods/program/get-program-accounts.ts +0 -221
  88. package/server/methods/program/get-supply.ts +0 -13
  89. package/server/methods/program/get-token-account-balance.ts +0 -60
  90. package/server/methods/program/get-token-accounts-by-delegate.ts +0 -82
  91. package/server/methods/program/get-token-accounts-by-owner.ts +0 -416
  92. package/server/methods/program/get-token-largest-accounts.ts +0 -81
  93. package/server/methods/program/get-token-supply.ts +0 -39
  94. package/server/methods/program/index.ts +0 -21
  95. package/server/methods/solforge/index.ts +0 -158
  96. package/server/methods/system/get-health.ts +0 -5
  97. package/server/methods/system/get-minimum-balance-for-rent-exemption.ts +0 -13
  98. package/server/methods/system/get-version.ts +0 -9
  99. package/server/methods/system/index.ts +0 -3
  100. package/server/methods/transaction/get-confirmed-transaction.ts +0 -11
  101. package/server/methods/transaction/get-parsed-transaction.ts +0 -17
  102. package/server/methods/transaction/get-signature-statuses.ts +0 -79
  103. package/server/methods/transaction/get-signatures-for-address.ts +0 -41
  104. package/server/methods/transaction/get-transaction.ts +0 -639
  105. package/server/methods/transaction/index.ts +0 -7
  106. package/server/methods/transaction/inner-instructions.test.ts +0 -104
  107. package/server/methods/transaction/send-transaction.ts +0 -469
  108. package/server/methods/transaction/simulate-transaction.ts +0 -57
  109. package/server/rpc-server.ts +0 -521
  110. package/server/types.ts +0 -109
  111. package/server/ws-server.ts +0 -178
  112. package/src/api-server-entry.ts +0 -109
  113. package/src/cli/bootstrap.ts +0 -67
  114. package/src/cli/commands/airdrop.ts +0 -37
  115. package/src/cli/commands/config.ts +0 -39
  116. package/src/cli/commands/mint.ts +0 -187
  117. package/src/cli/commands/program-clone.ts +0 -122
  118. package/src/cli/commands/program-load.ts +0 -64
  119. package/src/cli/commands/rpc-start.ts +0 -49
  120. package/src/cli/commands/token-adopt-authority.ts +0 -37
  121. package/src/cli/commands/token-clone.ts +0 -112
  122. package/src/cli/commands/token-create.ts +0 -81
  123. package/src/cli/main.ts +0 -158
  124. package/src/cli/run-solforge.ts +0 -112
  125. package/src/cli/setup-utils.ts +0 -54
  126. package/src/cli/setup-wizard.ts +0 -258
  127. package/src/cli/utils/args.ts +0 -15
  128. package/src/commands/add-program.ts +0 -333
  129. package/src/commands/init.ts +0 -122
  130. package/src/commands/list.ts +0 -136
  131. package/src/commands/mint.ts +0 -287
  132. package/src/commands/start.ts +0 -881
  133. package/src/commands/status.ts +0 -99
  134. package/src/commands/stop.ts +0 -405
  135. package/src/config/index.ts +0 -146
  136. package/src/config/manager.ts +0 -157
  137. package/src/db/index.ts +0 -83
  138. package/src/db/schema/accounts.ts +0 -23
  139. package/src/db/schema/address-signatures.ts +0 -31
  140. package/src/db/schema/index.ts +0 -6
  141. package/src/db/schema/meta-kv.ts +0 -9
  142. package/src/db/schema/transactions.ts +0 -36
  143. package/src/db/schema/tx-account-states.ts +0 -23
  144. package/src/db/schema/tx-accounts.ts +0 -33
  145. package/src/db/tx-store.ts +0 -264
  146. package/src/gui/public/app.css +0 -1556
  147. package/src/gui/public/build/main.css +0 -1569
  148. package/src/gui/public/build/main.js +0 -303
  149. package/src/gui/public/build/main.js.txt +0 -231
  150. package/src/gui/public/index.html +0 -19
  151. package/src/gui/server.ts +0 -296
  152. package/src/gui/src/api.ts +0 -127
  153. package/src/gui/src/app.tsx +0 -441
  154. package/src/gui/src/components/airdrop-mint-form.tsx +0 -246
  155. package/src/gui/src/components/clone-program-modal.tsx +0 -202
  156. package/src/gui/src/components/clone-token-modal.tsx +0 -230
  157. package/src/gui/src/components/modal.tsx +0 -134
  158. package/src/gui/src/components/programs-panel.tsx +0 -124
  159. package/src/gui/src/components/status-panel.tsx +0 -136
  160. package/src/gui/src/components/tokens-panel.tsx +0 -122
  161. package/src/gui/src/hooks/use-interval.ts +0 -17
  162. package/src/gui/src/index.css +0 -557
  163. package/src/gui/src/main.tsx +0 -17
  164. package/src/index.ts +0 -216
  165. package/src/migrations-bundled.ts +0 -23
  166. package/src/rpc/start.ts +0 -44
  167. package/src/services/api-server.ts +0 -504
  168. package/src/services/port-manager.ts +0 -174
  169. package/src/services/process-registry.ts +0 -153
  170. package/src/services/program-cloner.ts +0 -317
  171. package/src/services/token-cloner.ts +0 -811
  172. package/src/services/validator.ts +0 -293
  173. package/src/types/config.ts +0 -110
  174. package/src/utils/shell.ts +0 -110
  175. package/src/utils/token-loader.ts +0 -115
@@ -1,157 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import type { Config, ValidationResult } from "../types/config.js";
4
- import { ConfigSchema } from "../types/config.js";
5
-
6
- export class ConfigManager {
7
- private config: Config | null = null;
8
- private configPath: string | null = null;
9
-
10
- /**
11
- * Load configuration from a file path
12
- */
13
- async load(configPath: string): Promise<Config> {
14
- try {
15
- const fullPath = resolve(configPath);
16
-
17
- if (!existsSync(fullPath)) {
18
- throw new Error(`Configuration file not found: ${fullPath}`);
19
- }
20
-
21
- const configContent = readFileSync(fullPath, "utf-8");
22
- const rawConfig = JSON.parse(configContent);
23
-
24
- // Validate and parse with Zod
25
- const result = ConfigSchema.safeParse(rawConfig);
26
-
27
- if (!result.success) {
28
- const errors = result.error.issues.map((issue) => ({
29
- path: issue.path.join("."),
30
- message: issue.message,
31
- }));
32
- throw new Error(
33
- `Configuration validation failed:\n${errors
34
- .map((e) => ` - ${e.path}: ${e.message}`)
35
- .join("\n")}`,
36
- );
37
- }
38
-
39
- this.config = result.data;
40
- this.configPath = fullPath;
41
-
42
- return this.config;
43
- } catch (error) {
44
- if (error instanceof SyntaxError) {
45
- throw new Error(`Invalid JSON in configuration file: ${error.message}`);
46
- }
47
- throw error;
48
- }
49
- }
50
-
51
- /**
52
- * Save current configuration to file
53
- */
54
- async save(configPath?: string): Promise<void> {
55
- if (!this.config) {
56
- throw new Error("No configuration loaded");
57
- }
58
-
59
- const targetPath = configPath || this.configPath;
60
- if (!targetPath) {
61
- throw new Error("No configuration path specified");
62
- }
63
-
64
- try {
65
- const configContent = JSON.stringify(this.config, null, 2);
66
- writeFileSync(targetPath, configContent, "utf-8");
67
- this.configPath = targetPath;
68
- } catch (error) {
69
- throw new Error(
70
- `Failed to save configuration: ${
71
- error instanceof Error ? error.message : String(error)
72
- }`,
73
- );
74
- }
75
- }
76
-
77
- /**
78
- * Validate a configuration object
79
- */
80
- validate(config: unknown): ValidationResult {
81
- const result = ConfigSchema.safeParse(config);
82
-
83
- if (result.success) {
84
- return { valid: true, errors: [] };
85
- }
86
-
87
- const errors = result.error.issues.map((issue) => ({
88
- path: issue.path.join("."),
89
- message: issue.message,
90
- }));
91
-
92
- return { valid: false, errors };
93
- }
94
-
95
- /**
96
- * Create a default configuration
97
- */
98
- createDefault(): Config {
99
- const defaultConfig = ConfigSchema.parse({});
100
- this.config = defaultConfig;
101
- return defaultConfig;
102
- }
103
-
104
- /**
105
- * Get current configuration
106
- */
107
- getConfig(): Config {
108
- if (!this.config) {
109
- throw new Error("No configuration loaded. Call load() first.");
110
- }
111
- return this.config;
112
- }
113
-
114
- /**
115
- * Update configuration
116
- */
117
- updateConfig(updates: Partial<Config>): Config {
118
- if (!this.config) {
119
- throw new Error("No configuration loaded. Call load() first.");
120
- }
121
-
122
- const updated = { ...this.config, ...updates };
123
- const result = ConfigSchema.safeParse(updated);
124
-
125
- if (!result.success) {
126
- const errors = result.error.issues.map((issue) => ({
127
- path: issue.path.join("."),
128
- message: issue.message,
129
- }));
130
- throw new Error(
131
- `Configuration update validation failed:\n${errors
132
- .map((e) => ` - ${e.path}: ${e.message}`)
133
- .join("\n")}`,
134
- );
135
- }
136
-
137
- this.config = result.data;
138
- return this.config;
139
- }
140
-
141
- /**
142
- * Get configuration file path
143
- */
144
- getConfigPath(): string | null {
145
- return this.configPath;
146
- }
147
-
148
- /**
149
- * Check if configuration is loaded
150
- */
151
- isLoaded(): boolean {
152
- return this.config !== null;
153
- }
154
- }
155
-
156
- // Singleton instance
157
- export const configManager = new ConfigManager();
package/src/db/index.ts DELETED
@@ -1,83 +0,0 @@
1
- import Database from "bun:sqlite";
2
- import { existsSync, mkdirSync, unlinkSync } from "node:fs";
3
- import { dirname } from "node:path";
4
- import { drizzle } from "drizzle-orm/bun-sqlite";
5
- import { migrate } from "drizzle-orm/bun-sqlite/migrator";
6
- import { bundledMigrations } from "../migrations-bundled";
7
- import * as schema from "./schema";
8
-
9
- // DB path is configurable; default to project-local hidden folder
10
- const DB_PATH = process.env.SOLFORGE_DB_PATH || ".solforge/db.db";
11
-
12
- // Ensure directory exists
13
- try {
14
- mkdirSync(dirname(DB_PATH), { recursive: true });
15
- } catch {}
16
-
17
- // Ephemeral by default (on-disk SQLite, cleared on each start).
18
- // Set SOLFORGE_DB_MODE=persistent or SOLFORGE_DB_PERSIST=1 for persistent DB.
19
- const PERSIST =
20
- process.env.SOLFORGE_DB_MODE === "persistent" ||
21
- process.env.SOLFORGE_DB_PERSIST === "1";
22
- try {
23
- mkdirSync(dirname(DB_PATH), { recursive: true });
24
- } catch {}
25
- if (!PERSIST && DB_PATH !== ":memory:") {
26
- try {
27
- if (existsSync(DB_PATH)) unlinkSync(DB_PATH);
28
- } catch {}
29
- try {
30
- if (existsSync(`${DB_PATH}-wal`)) unlinkSync(`${DB_PATH}-wal`);
31
- } catch {}
32
- try {
33
- if (existsSync(`${DB_PATH}-shm`)) unlinkSync(`${DB_PATH}-shm`);
34
- } catch {}
35
- }
36
-
37
- // Create SQLite connection and apply performance PRAGMAs
38
- export const sqlite = new Database(DB_PATH);
39
- try {
40
- // Use DELETE journal to avoid VNODE/WAL issues across environments
41
- sqlite.exec("PRAGMA journal_mode=DELETE;");
42
- sqlite.exec("PRAGMA synchronous=NORMAL;");
43
- sqlite.exec("PRAGMA temp_store=MEMORY;");
44
- sqlite.exec("PRAGMA busy_timeout=1000;");
45
- } catch {}
46
-
47
- // Drizzle database instance with typed schema
48
- export const db = drizzle(sqlite, { schema });
49
-
50
- export type { SQLiteDatabase } from "drizzle-orm/sqlite-core";
51
- export * as dbSchema from "./schema";
52
-
53
- // Run Drizzle migrations on app start (Bun + SQLite)
54
- const migrationsFolder = process.env.DRIZZLE_MIGRATIONS || "drizzle";
55
- try {
56
- // Prefer folder-based migrations when available (dev/uncompiled)
57
- if (existsSync(migrationsFolder)) {
58
- await migrate(db, { migrationsFolder });
59
- console.log("✅ Database migrations completed (folder)");
60
- } else {
61
- // Bundled mode: apply embedded SQL files if schema isn't present
62
- const haveTx = sqlite
63
- .query(
64
- "SELECT name FROM sqlite_master WHERE type='table' AND name='transactions'",
65
- )
66
- .get() as { name?: string } | undefined;
67
- if (!haveTx?.name) {
68
- for (const m of bundledMigrations) {
69
- try {
70
- const sql = await Bun.file(m.path).text();
71
- sqlite.exec(sql);
72
- console.log(`✅ Applied bundled migration: ${m.name}`);
73
- } catch (e) {
74
- console.error(`❌ Failed bundled migration: ${m.name}`, e);
75
- throw e;
76
- }
77
- }
78
- }
79
- console.log("✅ Database migrations completed (bundled)");
80
- }
81
- } catch (error) {
82
- console.error("❌ Database migration failed:", error);
83
- }
@@ -1,23 +0,0 @@
1
- import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
2
-
3
- export const accounts = sqliteTable(
4
- "accounts",
5
- {
6
- address: text("address").primaryKey(),
7
- lamports: integer("lamports").notNull(),
8
- ownerProgram: text("owner_program").notNull(),
9
- executable: integer("executable").notNull(), // 0 or 1
10
- rentEpoch: integer("rent_epoch").notNull(),
11
- dataLen: integer("data_len").notNull(),
12
- // Optional raw bytes; disabled by default in runtime
13
- dataBase64: text("data_base64"),
14
- lastSlot: integer("last_slot").notNull(),
15
- },
16
- (t) => ({
17
- ownerIdx: index("idx_accounts_owner").on(t.ownerProgram),
18
- lastSlotIdx: index("idx_accounts_last_slot").on(t.lastSlot),
19
- }),
20
- );
21
-
22
- export type AccountRow = typeof accounts.$inferSelect;
23
- export type NewAccountRow = typeof accounts.$inferInsert;
@@ -1,31 +0,0 @@
1
- import {
2
- index,
3
- integer,
4
- primaryKey,
5
- sqliteTable,
6
- text,
7
- } from "drizzle-orm/sqlite-core";
8
-
9
- export const addressSignatures = sqliteTable(
10
- "address_signatures",
11
- {
12
- address: text("address").notNull(),
13
- signature: text("signature").notNull(),
14
- slot: integer("slot").notNull(),
15
- err: integer("err").notNull(), // 0 or 1
16
- blockTime: integer("block_time"),
17
- },
18
- (t) => ({
19
- pk: primaryKey({
20
- columns: [t.address, t.signature],
21
- name: "pk_address_signatures",
22
- }),
23
- addressSlotIdx: index("idx_address_signatures_addr_slot").on(
24
- t.address,
25
- t.slot,
26
- ),
27
- }),
28
- );
29
-
30
- export type AddressSignatureRow = typeof addressSignatures.$inferSelect;
31
- export type NewAddressSignatureRow = typeof addressSignatures.$inferInsert;
@@ -1,6 +0,0 @@
1
- export * from "./accounts";
2
- export * from "./address-signatures";
3
- export * from "./meta-kv";
4
- export * from "./transactions";
5
- export * from "./tx-accounts";
6
- export * from "./tx-account-states";
@@ -1,9 +0,0 @@
1
- import { sqliteTable, text } from "drizzle-orm/sqlite-core";
2
-
3
- export const metaKv = sqliteTable("meta_kv", {
4
- key: text("key").primaryKey(),
5
- value: text("value").notNull(),
6
- });
7
-
8
- export type MetaKvRow = typeof metaKv.$inferSelect;
9
- export type NewMetaKvRow = typeof metaKv.$inferInsert;
@@ -1,36 +0,0 @@
1
- import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
2
-
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
- // 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
- );
34
-
35
- export type TransactionRow = typeof transactions.$inferSelect;
36
- export type NewTransactionRow = typeof transactions.$inferInsert;
@@ -1,23 +0,0 @@
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({
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
- }),
20
- );
21
-
22
- export type TxAccountStateRow = typeof txAccountStates.$inferSelect;
23
- export type NewTxAccountStateRow = typeof txAccountStates.$inferInsert;
@@ -1,33 +0,0 @@
1
- import {
2
- index,
3
- integer,
4
- primaryKey,
5
- sqliteTable,
6
- text,
7
- } from "drizzle-orm/sqlite-core";
8
-
9
- export const txAccounts = sqliteTable(
10
- "tx_accounts",
11
- {
12
- signature: text("signature").notNull(),
13
- accountIndex: integer("account_index").notNull(),
14
- address: text("address").notNull(),
15
- signer: integer("signer").notNull(), // 0 or 1
16
- writable: integer("writable").notNull(), // 0 or 1
17
- programIdIndex: integer("program_id_index"),
18
- },
19
- (t) => ({
20
- pk: primaryKey({
21
- columns: [t.signature, t.accountIndex],
22
- name: "pk_tx_accounts",
23
- }),
24
- addressIdx: index("idx_tx_accounts_address").on(t.address),
25
- addressSigIdx: index("idx_tx_accounts_address_signature").on(
26
- t.address,
27
- t.signature,
28
- ),
29
- }),
30
- );
31
-
32
- export type TxAccountRow = typeof txAccounts.$inferSelect;
33
- export type NewTxAccountRow = typeof txAccounts.$inferInsert;
@@ -1,264 +0,0 @@
1
- import { and, desc, eq, gt, inArray, lt } from "drizzle-orm";
2
- import { db } from "./index";
3
- import { accounts } from "./schema/accounts";
4
- import { addressSignatures } from "./schema/address-signatures";
5
- import { transactions } from "./schema/transactions";
6
- import { txAccounts } from "./schema/tx-accounts";
7
- import { txAccountStates } from "./schema/tx-account-states";
8
-
9
- export type InsertTxBundle = {
10
- signature: string;
11
- slot: number;
12
- blockTime?: number;
13
- version: 0 | "legacy";
14
- fee: number;
15
- err: unknown | null;
16
- rawBase64: string;
17
- preBalances: number[];
18
- postBalances: number[];
19
- logs: string[];
20
- innerInstructions?: unknown[];
21
- computeUnits?: number | bigint | null;
22
- returnData?: { programId: string; dataBase64: string } | null;
23
- accounts: Array<{
24
- address: string;
25
- index: number;
26
- signer: boolean;
27
- writable: boolean;
28
- programIdIndex?: number;
29
- }>;
30
- preTokenBalances?: unknown[];
31
- postTokenBalances?: unknown[];
32
- accountStates?: Array<{
33
- address: string;
34
- pre?: Partial<AccountSnapshot> | null;
35
- post?: Partial<AccountSnapshot> | null;
36
- }>;
37
- };
38
-
39
- export type AccountSnapshot = {
40
- address: string;
41
- lamports: number;
42
- ownerProgram: string;
43
- executable: boolean;
44
- rentEpoch: number;
45
- dataLen: number;
46
- dataBase64?: string | null;
47
- lastSlot: number;
48
- };
49
-
50
- export class TxStore {
51
- async insertTransactionBundle(bundle: InsertTxBundle): Promise<void> {
52
- const errJson = bundle.err ? JSON.stringify(bundle.err) : null;
53
- await db.transaction(async (tx) => {
54
- await tx
55
- .insert(transactions)
56
- .values({
57
- signature: bundle.signature,
58
- slot: bundle.slot,
59
- blockTime: bundle.blockTime ?? null,
60
- version: String(bundle.version),
61
- errJson,
62
- fee: bundle.fee,
63
- rawBase64: bundle.rawBase64,
64
- preBalancesJson: JSON.stringify(bundle.preBalances ?? []),
65
- postBalancesJson: JSON.stringify(bundle.postBalances ?? []),
66
- logsJson: JSON.stringify(bundle.logs ?? []),
67
- preTokenBalancesJson: JSON.stringify(bundle.preTokenBalances ?? []),
68
- postTokenBalancesJson: JSON.stringify(bundle.postTokenBalances ?? []),
69
- innerInstructionsJson: JSON.stringify(bundle.innerInstructions ?? []),
70
- computeUnits:
71
- bundle.computeUnits == null ? null : Number(bundle.computeUnits),
72
- returnDataProgramId: bundle.returnData?.programId ?? null,
73
- returnDataBase64: bundle.returnData?.dataBase64 ?? null,
74
- })
75
- .onConflictDoNothing();
76
-
77
- if (Array.isArray(bundle.accounts) && bundle.accounts.length > 0) {
78
- await tx
79
- .insert(txAccounts)
80
- .values(
81
- bundle.accounts.map((a) => ({
82
- signature: bundle.signature,
83
- accountIndex: a.index,
84
- address: a.address,
85
- signer: a.signer ? 1 : 0,
86
- writable: a.writable ? 1 : 0,
87
- programIdIndex: a.programIdIndex ?? null,
88
- })),
89
- )
90
- .onConflictDoNothing();
91
-
92
- await tx
93
- .insert(addressSignatures)
94
- .values(
95
- bundle.accounts.map((a) => ({
96
- address: a.address,
97
- signature: bundle.signature,
98
- slot: bundle.slot,
99
- err: errJson ? 1 : 0,
100
- blockTime: bundle.blockTime ?? null,
101
- })),
102
- )
103
- .onConflictDoNothing();
104
- }
105
-
106
- if (
107
- Array.isArray(bundle.accountStates) &&
108
- bundle.accountStates.length > 0
109
- ) {
110
- await tx
111
- .insert(txAccountStates)
112
- .values(
113
- bundle.accountStates.map((s) => ({
114
- signature: bundle.signature,
115
- address: s.address,
116
- preJson: s.pre ? JSON.stringify(s.pre) : null,
117
- postJson: s.post ? JSON.stringify(s.post) : null,
118
- })),
119
- )
120
- .onConflictDoNothing();
121
- }
122
- });
123
- }
124
-
125
- async upsertAccounts(snapshots: AccountSnapshot[]): Promise<void> {
126
- if (!Array.isArray(snapshots) || snapshots.length === 0) return;
127
- // SQLite upsert via onConflictDoUpdate
128
- for (const s of snapshots) {
129
- await db
130
- .insert(accounts)
131
- .values({
132
- address: s.address,
133
- lamports: s.lamports,
134
- ownerProgram: s.ownerProgram,
135
- executable: s.executable ? 1 : 0,
136
- rentEpoch: s.rentEpoch,
137
- dataLen: s.dataLen,
138
- dataBase64: s.dataBase64 ?? null,
139
- lastSlot: s.lastSlot,
140
- })
141
- .onConflictDoUpdate({
142
- target: accounts.address,
143
- set: {
144
- lamports: s.lamports,
145
- ownerProgram: s.ownerProgram,
146
- executable: s.executable ? 1 : 0,
147
- rentEpoch: s.rentEpoch,
148
- dataLen: s.dataLen,
149
- dataBase64: s.dataBase64 ?? null,
150
- lastSlot: s.lastSlot,
151
- },
152
- });
153
- }
154
- }
155
-
156
- async getTransaction(signature: string) {
157
- const rows = await db
158
- .select()
159
- .from(transactions)
160
- .where(eq(transactions.signature, signature))
161
- .limit(1);
162
- return rows[0] || null;
163
- }
164
-
165
- async getStatuses(signatures: string[]) {
166
- if (!Array.isArray(signatures) || signatures.length === 0)
167
- return new Map<string, { slot: number; err: unknown | null }>();
168
- const results = await db
169
- .select({
170
- signature: transactions.signature,
171
- slot: transactions.slot,
172
- errJson: transactions.errJson,
173
- })
174
- .from(transactions)
175
- .where(inArraySafe(transactions.signature, signatures));
176
- const map = new Map<string, { slot: number; err: unknown | null }>();
177
- for (const r of results)
178
- map.set(r.signature, {
179
- slot: Number(r.slot),
180
- err: r.errJson ? safeParse(r.errJson) : null,
181
- });
182
- return map;
183
- }
184
-
185
- async getSignaturesForAddress(
186
- address: string,
187
- opts: { before?: string; until?: string; limit?: number } = {},
188
- ) {
189
- let beforeSlot: number | undefined;
190
- let untilSlot: number | undefined;
191
- if (opts.before) {
192
- const row = await this.getTransaction(opts.before);
193
- beforeSlot = row?.slot ? Number(row.slot) : undefined;
194
- }
195
- if (opts.until) {
196
- const row = await this.getTransaction(opts.until);
197
- untilSlot = row?.slot ? Number(row.slot) : undefined;
198
- }
199
- const limit = Math.min(Math.max(opts.limit ?? 1000, 1), 1000);
200
-
201
- const whereClauses = [eq(addressSignatures.address, address)] as unknown[];
202
- if (typeof beforeSlot === "number")
203
- whereClauses.push(lt(addressSignatures.slot, beforeSlot));
204
- if (typeof untilSlot === "number")
205
- whereClauses.push(gt(addressSignatures.slot, untilSlot));
206
-
207
- const rows = await db
208
- .select({
209
- signature: addressSignatures.signature,
210
- slot: addressSignatures.slot,
211
- blockTime: addressSignatures.blockTime,
212
- err: addressSignatures.err,
213
- })
214
- .from(addressSignatures)
215
- .where(and(...whereClauses))
216
- .orderBy(desc(addressSignatures.slot))
217
- .limit(limit);
218
-
219
- return rows.map((r) => ({
220
- signature: r.signature,
221
- slot: Number(r.slot),
222
- err: r.err ? {} : null,
223
- memo: null as null,
224
- blockTime: r.blockTime ? Number(r.blockTime) : null,
225
- confirmationStatus: r.err ? "processed" : "confirmed",
226
- }));
227
- }
228
-
229
- async getAccountsByOwner(ownerProgram: string, limit = 1000) {
230
- const rows = await db
231
- .select()
232
- .from(accounts)
233
- .where(eq(accounts.ownerProgram, ownerProgram))
234
- .orderBy(desc(accounts.lastSlot))
235
- .limit(Math.min(Math.max(limit, 1), 1000));
236
- return rows;
237
- }
238
-
239
- async getBlockTimeForSlot(slot: number): Promise<number | null> {
240
- const rows = await db
241
- .select({ bt: transactions.blockTime })
242
- .from(transactions)
243
- .where(eq(transactions.slot, slot))
244
- .limit(1);
245
- const r = rows[0];
246
- return r?.bt != null ? Number(r.bt) : null;
247
- }
248
- }
249
-
250
- function safeParse<T = unknown>(s: string): T | null {
251
- try {
252
- return JSON.parse(s) as T;
253
- } catch {
254
- return null;
255
- }
256
- }
257
-
258
- function inArraySafe<T>(col: unknown, arr: T[]) {
259
- return arr.length > 0
260
- ? // biome-ignore lint/suspicious/noExplicitAny: Drizzle generic typing workaround
261
- (inArray as unknown as (c: unknown, a: T[]) => any)(col, arr)
262
- : // biome-ignore lint/suspicious/noExplicitAny: Force an always-false predicate without over-constraining types
263
- eq(col as any, "__never__");
264
- }