solforge 0.2.12 → 0.2.13

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 (174) hide show
  1. package/package.json +1 -5
  2. package/docs/API.md +0 -379
  3. package/docs/CONFIGURATION.md +0 -407
  4. package/docs/bun-single-file-executable.md +0 -585
  5. package/docs/cli-plan.md +0 -154
  6. package/docs/data-indexing-plan.md +0 -214
  7. package/docs/gui-roadmap.md +0 -202
  8. package/scripts/decode-b58.ts +0 -10
  9. package/scripts/install.sh +0 -112
  10. package/server/index.ts +0 -5
  11. package/server/lib/base58.ts +0 -33
  12. package/server/lib/faucet.ts +0 -110
  13. package/server/lib/instruction-parser.ts +0 -328
  14. package/server/lib/parsers/spl-associated-token-account.ts +0 -50
  15. package/server/lib/parsers/spl-token.ts +0 -340
  16. package/server/lib/spl-token.ts +0 -57
  17. package/server/methods/TEMPLATE.md +0 -117
  18. package/server/methods/account/get-account-info.ts +0 -86
  19. package/server/methods/account/get-balance.ts +0 -23
  20. package/server/methods/account/get-multiple-accounts.ts +0 -84
  21. package/server/methods/account/get-parsed-account-info.ts +0 -17
  22. package/server/methods/account/index.ts +0 -12
  23. package/server/methods/account/parsers/index.ts +0 -52
  24. package/server/methods/account/parsers/loader-upgradeable.ts +0 -79
  25. package/server/methods/account/parsers/spl-token.ts +0 -256
  26. package/server/methods/account/parsers/system.ts +0 -4
  27. package/server/methods/account/request-airdrop.ts +0 -271
  28. package/server/methods/admin/adopt-mint-authority.ts +0 -94
  29. package/server/methods/admin/clone-program-accounts.ts +0 -55
  30. package/server/methods/admin/clone-program.ts +0 -152
  31. package/server/methods/admin/clone-token-accounts.ts +0 -117
  32. package/server/methods/admin/clone-token-mint.ts +0 -82
  33. package/server/methods/admin/create-mint.ts +0 -114
  34. package/server/methods/admin/create-token-account.ts +0 -137
  35. package/server/methods/admin/helpers.ts +0 -70
  36. package/server/methods/admin/index.ts +0 -10
  37. package/server/methods/admin/list-mints.ts +0 -21
  38. package/server/methods/admin/load-program.ts +0 -52
  39. package/server/methods/admin/mint-to.ts +0 -266
  40. package/server/methods/block/get-block-height.ts +0 -5
  41. package/server/methods/block/get-block.ts +0 -31
  42. package/server/methods/block/get-blocks-with-limit.ts +0 -19
  43. package/server/methods/block/get-latest-blockhash.ts +0 -12
  44. package/server/methods/block/get-slot.ts +0 -5
  45. package/server/methods/block/index.ts +0 -6
  46. package/server/methods/block/is-blockhash-valid.ts +0 -19
  47. package/server/methods/epoch/get-cluster-nodes.ts +0 -17
  48. package/server/methods/epoch/get-epoch-info.ts +0 -16
  49. package/server/methods/epoch/get-epoch-schedule.ts +0 -15
  50. package/server/methods/epoch/get-highest-snapshot-slot.ts +0 -9
  51. package/server/methods/epoch/get-leader-schedule.ts +0 -8
  52. package/server/methods/epoch/get-max-retransmit-slot.ts +0 -9
  53. package/server/methods/epoch/get-max-shred-insert-slot.ts +0 -9
  54. package/server/methods/epoch/get-slot-leader.ts +0 -6
  55. package/server/methods/epoch/get-slot-leaders.ts +0 -9
  56. package/server/methods/epoch/get-stake-activation.ts +0 -9
  57. package/server/methods/epoch/get-stake-minimum-delegation.ts +0 -9
  58. package/server/methods/epoch/get-vote-accounts.ts +0 -19
  59. package/server/methods/epoch/index.ts +0 -13
  60. package/server/methods/epoch/minimum-ledger-slot.ts +0 -5
  61. package/server/methods/fee/get-fee-calculator-for-blockhash.ts +0 -12
  62. package/server/methods/fee/get-fee-for-message.ts +0 -8
  63. package/server/methods/fee/get-fee-rate-governor.ts +0 -16
  64. package/server/methods/fee/get-fees.ts +0 -14
  65. package/server/methods/fee/get-recent-prioritization-fees.ts +0 -22
  66. package/server/methods/fee/index.ts +0 -5
  67. package/server/methods/get-address-lookup-table.ts +0 -27
  68. package/server/methods/index.ts +0 -265
  69. package/server/methods/performance/get-recent-performance-samples.ts +0 -25
  70. package/server/methods/performance/get-transaction-count.ts +0 -5
  71. package/server/methods/performance/index.ts +0 -2
  72. package/server/methods/program/get-block-commitment.ts +0 -9
  73. package/server/methods/program/get-block-production.ts +0 -14
  74. package/server/methods/program/get-block-time.ts +0 -21
  75. package/server/methods/program/get-blocks.ts +0 -11
  76. package/server/methods/program/get-first-available-block.ts +0 -9
  77. package/server/methods/program/get-genesis-hash.ts +0 -6
  78. package/server/methods/program/get-identity.ts +0 -6
  79. package/server/methods/program/get-inflation-governor.ts +0 -15
  80. package/server/methods/program/get-inflation-rate.ts +0 -10
  81. package/server/methods/program/get-inflation-reward.ts +0 -12
  82. package/server/methods/program/get-largest-accounts.ts +0 -8
  83. package/server/methods/program/get-parsed-program-accounts.ts +0 -12
  84. package/server/methods/program/get-parsed-token-accounts-by-delegate.ts +0 -12
  85. package/server/methods/program/get-parsed-token-accounts-by-owner.ts +0 -12
  86. package/server/methods/program/get-program-accounts.ts +0 -221
  87. package/server/methods/program/get-supply.ts +0 -13
  88. package/server/methods/program/get-token-account-balance.ts +0 -60
  89. package/server/methods/program/get-token-accounts-by-delegate.ts +0 -82
  90. package/server/methods/program/get-token-accounts-by-owner.ts +0 -416
  91. package/server/methods/program/get-token-largest-accounts.ts +0 -81
  92. package/server/methods/program/get-token-supply.ts +0 -39
  93. package/server/methods/program/index.ts +0 -21
  94. package/server/methods/solforge/index.ts +0 -158
  95. package/server/methods/system/get-health.ts +0 -5
  96. package/server/methods/system/get-minimum-balance-for-rent-exemption.ts +0 -13
  97. package/server/methods/system/get-version.ts +0 -9
  98. package/server/methods/system/index.ts +0 -3
  99. package/server/methods/transaction/get-confirmed-transaction.ts +0 -11
  100. package/server/methods/transaction/get-parsed-transaction.ts +0 -17
  101. package/server/methods/transaction/get-signature-statuses.ts +0 -79
  102. package/server/methods/transaction/get-signatures-for-address.ts +0 -41
  103. package/server/methods/transaction/get-transaction.ts +0 -639
  104. package/server/methods/transaction/index.ts +0 -7
  105. package/server/methods/transaction/inner-instructions.test.ts +0 -104
  106. package/server/methods/transaction/send-transaction.ts +0 -469
  107. package/server/methods/transaction/simulate-transaction.ts +0 -57
  108. package/server/rpc-server.ts +0 -521
  109. package/server/types.ts +0 -109
  110. package/server/ws-server.ts +0 -178
  111. package/src/api-server-entry.ts +0 -109
  112. package/src/cli/bootstrap.ts +0 -67
  113. package/src/cli/commands/airdrop.ts +0 -37
  114. package/src/cli/commands/config.ts +0 -39
  115. package/src/cli/commands/mint.ts +0 -187
  116. package/src/cli/commands/program-clone.ts +0 -122
  117. package/src/cli/commands/program-load.ts +0 -64
  118. package/src/cli/commands/rpc-start.ts +0 -49
  119. package/src/cli/commands/token-adopt-authority.ts +0 -37
  120. package/src/cli/commands/token-clone.ts +0 -112
  121. package/src/cli/commands/token-create.ts +0 -81
  122. package/src/cli/main.ts +0 -158
  123. package/src/cli/run-solforge.ts +0 -112
  124. package/src/cli/setup-utils.ts +0 -54
  125. package/src/cli/setup-wizard.ts +0 -258
  126. package/src/cli/utils/args.ts +0 -15
  127. package/src/commands/add-program.ts +0 -333
  128. package/src/commands/init.ts +0 -122
  129. package/src/commands/list.ts +0 -136
  130. package/src/commands/mint.ts +0 -287
  131. package/src/commands/start.ts +0 -881
  132. package/src/commands/status.ts +0 -99
  133. package/src/commands/stop.ts +0 -405
  134. package/src/config/index.ts +0 -146
  135. package/src/config/manager.ts +0 -157
  136. package/src/db/index.ts +0 -83
  137. package/src/db/schema/accounts.ts +0 -23
  138. package/src/db/schema/address-signatures.ts +0 -31
  139. package/src/db/schema/index.ts +0 -6
  140. package/src/db/schema/meta-kv.ts +0 -9
  141. package/src/db/schema/transactions.ts +0 -36
  142. package/src/db/schema/tx-account-states.ts +0 -23
  143. package/src/db/schema/tx-accounts.ts +0 -33
  144. package/src/db/tx-store.ts +0 -264
  145. package/src/gui/public/app.css +0 -1556
  146. package/src/gui/public/build/main.css +0 -1569
  147. package/src/gui/public/build/main.js +0 -303
  148. package/src/gui/public/build/main.js.txt +0 -231
  149. package/src/gui/public/index.html +0 -19
  150. package/src/gui/server.ts +0 -296
  151. package/src/gui/src/api.ts +0 -127
  152. package/src/gui/src/app.tsx +0 -441
  153. package/src/gui/src/components/airdrop-mint-form.tsx +0 -246
  154. package/src/gui/src/components/clone-program-modal.tsx +0 -202
  155. package/src/gui/src/components/clone-token-modal.tsx +0 -230
  156. package/src/gui/src/components/modal.tsx +0 -134
  157. package/src/gui/src/components/programs-panel.tsx +0 -124
  158. package/src/gui/src/components/status-panel.tsx +0 -136
  159. package/src/gui/src/components/tokens-panel.tsx +0 -122
  160. package/src/gui/src/hooks/use-interval.ts +0 -17
  161. package/src/gui/src/index.css +0 -557
  162. package/src/gui/src/main.tsx +0 -17
  163. package/src/index.ts +0 -216
  164. package/src/migrations-bundled.ts +0 -23
  165. package/src/rpc/start.ts +0 -44
  166. package/src/services/api-server.ts +0 -504
  167. package/src/services/port-manager.ts +0 -174
  168. package/src/services/process-registry.ts +0 -153
  169. package/src/services/program-cloner.ts +0 -317
  170. package/src/services/token-cloner.ts +0 -811
  171. package/src/services/validator.ts +0 -293
  172. package/src/types/config.ts +0 -110
  173. package/src/utils/shell.ts +0 -110
  174. 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
- }