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.
- package/package.json +1 -5
- package/start.cjs +19 -23
- package/docs/API.md +0 -379
- package/docs/CONFIGURATION.md +0 -407
- package/docs/bun-single-file-executable.md +0 -585
- package/docs/cli-plan.md +0 -154
- package/docs/data-indexing-plan.md +0 -214
- package/docs/gui-roadmap.md +0 -202
- package/scripts/decode-b58.ts +0 -10
- package/scripts/install.sh +0 -112
- package/server/index.ts +0 -5
- package/server/lib/base58.ts +0 -33
- package/server/lib/faucet.ts +0 -110
- package/server/lib/instruction-parser.ts +0 -328
- package/server/lib/parsers/spl-associated-token-account.ts +0 -50
- package/server/lib/parsers/spl-token.ts +0 -340
- package/server/lib/spl-token.ts +0 -57
- package/server/methods/TEMPLATE.md +0 -117
- package/server/methods/account/get-account-info.ts +0 -86
- package/server/methods/account/get-balance.ts +0 -23
- package/server/methods/account/get-multiple-accounts.ts +0 -84
- package/server/methods/account/get-parsed-account-info.ts +0 -17
- package/server/methods/account/index.ts +0 -12
- package/server/methods/account/parsers/index.ts +0 -52
- package/server/methods/account/parsers/loader-upgradeable.ts +0 -79
- package/server/methods/account/parsers/spl-token.ts +0 -256
- package/server/methods/account/parsers/system.ts +0 -4
- package/server/methods/account/request-airdrop.ts +0 -271
- package/server/methods/admin/adopt-mint-authority.ts +0 -94
- package/server/methods/admin/clone-program-accounts.ts +0 -55
- package/server/methods/admin/clone-program.ts +0 -152
- package/server/methods/admin/clone-token-accounts.ts +0 -117
- package/server/methods/admin/clone-token-mint.ts +0 -82
- package/server/methods/admin/create-mint.ts +0 -114
- package/server/methods/admin/create-token-account.ts +0 -137
- package/server/methods/admin/helpers.ts +0 -70
- package/server/methods/admin/index.ts +0 -10
- package/server/methods/admin/list-mints.ts +0 -21
- package/server/methods/admin/load-program.ts +0 -52
- package/server/methods/admin/mint-to.ts +0 -266
- package/server/methods/block/get-block-height.ts +0 -5
- package/server/methods/block/get-block.ts +0 -31
- package/server/methods/block/get-blocks-with-limit.ts +0 -19
- package/server/methods/block/get-latest-blockhash.ts +0 -12
- package/server/methods/block/get-slot.ts +0 -5
- package/server/methods/block/index.ts +0 -6
- package/server/methods/block/is-blockhash-valid.ts +0 -19
- package/server/methods/epoch/get-cluster-nodes.ts +0 -17
- package/server/methods/epoch/get-epoch-info.ts +0 -16
- package/server/methods/epoch/get-epoch-schedule.ts +0 -15
- package/server/methods/epoch/get-highest-snapshot-slot.ts +0 -9
- package/server/methods/epoch/get-leader-schedule.ts +0 -8
- package/server/methods/epoch/get-max-retransmit-slot.ts +0 -9
- package/server/methods/epoch/get-max-shred-insert-slot.ts +0 -9
- package/server/methods/epoch/get-slot-leader.ts +0 -6
- package/server/methods/epoch/get-slot-leaders.ts +0 -9
- package/server/methods/epoch/get-stake-activation.ts +0 -9
- package/server/methods/epoch/get-stake-minimum-delegation.ts +0 -9
- package/server/methods/epoch/get-vote-accounts.ts +0 -19
- package/server/methods/epoch/index.ts +0 -13
- package/server/methods/epoch/minimum-ledger-slot.ts +0 -5
- package/server/methods/fee/get-fee-calculator-for-blockhash.ts +0 -12
- package/server/methods/fee/get-fee-for-message.ts +0 -8
- package/server/methods/fee/get-fee-rate-governor.ts +0 -16
- package/server/methods/fee/get-fees.ts +0 -14
- package/server/methods/fee/get-recent-prioritization-fees.ts +0 -22
- package/server/methods/fee/index.ts +0 -5
- package/server/methods/get-address-lookup-table.ts +0 -27
- package/server/methods/index.ts +0 -265
- package/server/methods/performance/get-recent-performance-samples.ts +0 -25
- package/server/methods/performance/get-transaction-count.ts +0 -5
- package/server/methods/performance/index.ts +0 -2
- package/server/methods/program/get-block-commitment.ts +0 -9
- package/server/methods/program/get-block-production.ts +0 -14
- package/server/methods/program/get-block-time.ts +0 -21
- package/server/methods/program/get-blocks.ts +0 -11
- package/server/methods/program/get-first-available-block.ts +0 -9
- package/server/methods/program/get-genesis-hash.ts +0 -6
- package/server/methods/program/get-identity.ts +0 -6
- package/server/methods/program/get-inflation-governor.ts +0 -15
- package/server/methods/program/get-inflation-rate.ts +0 -10
- package/server/methods/program/get-inflation-reward.ts +0 -12
- package/server/methods/program/get-largest-accounts.ts +0 -8
- package/server/methods/program/get-parsed-program-accounts.ts +0 -12
- package/server/methods/program/get-parsed-token-accounts-by-delegate.ts +0 -12
- package/server/methods/program/get-parsed-token-accounts-by-owner.ts +0 -12
- package/server/methods/program/get-program-accounts.ts +0 -221
- package/server/methods/program/get-supply.ts +0 -13
- package/server/methods/program/get-token-account-balance.ts +0 -60
- package/server/methods/program/get-token-accounts-by-delegate.ts +0 -82
- package/server/methods/program/get-token-accounts-by-owner.ts +0 -416
- package/server/methods/program/get-token-largest-accounts.ts +0 -81
- package/server/methods/program/get-token-supply.ts +0 -39
- package/server/methods/program/index.ts +0 -21
- package/server/methods/solforge/index.ts +0 -158
- package/server/methods/system/get-health.ts +0 -5
- package/server/methods/system/get-minimum-balance-for-rent-exemption.ts +0 -13
- package/server/methods/system/get-version.ts +0 -9
- package/server/methods/system/index.ts +0 -3
- package/server/methods/transaction/get-confirmed-transaction.ts +0 -11
- package/server/methods/transaction/get-parsed-transaction.ts +0 -17
- package/server/methods/transaction/get-signature-statuses.ts +0 -79
- package/server/methods/transaction/get-signatures-for-address.ts +0 -41
- package/server/methods/transaction/get-transaction.ts +0 -639
- package/server/methods/transaction/index.ts +0 -7
- package/server/methods/transaction/inner-instructions.test.ts +0 -104
- package/server/methods/transaction/send-transaction.ts +0 -469
- package/server/methods/transaction/simulate-transaction.ts +0 -57
- package/server/rpc-server.ts +0 -521
- package/server/types.ts +0 -109
- package/server/ws-server.ts +0 -178
- package/src/api-server-entry.ts +0 -109
- package/src/cli/bootstrap.ts +0 -67
- package/src/cli/commands/airdrop.ts +0 -37
- package/src/cli/commands/config.ts +0 -39
- package/src/cli/commands/mint.ts +0 -187
- package/src/cli/commands/program-clone.ts +0 -122
- package/src/cli/commands/program-load.ts +0 -64
- package/src/cli/commands/rpc-start.ts +0 -49
- package/src/cli/commands/token-adopt-authority.ts +0 -37
- package/src/cli/commands/token-clone.ts +0 -112
- package/src/cli/commands/token-create.ts +0 -81
- package/src/cli/main.ts +0 -158
- package/src/cli/run-solforge.ts +0 -112
- package/src/cli/setup-utils.ts +0 -54
- package/src/cli/setup-wizard.ts +0 -258
- package/src/cli/utils/args.ts +0 -15
- package/src/commands/add-program.ts +0 -333
- package/src/commands/init.ts +0 -122
- package/src/commands/list.ts +0 -136
- package/src/commands/mint.ts +0 -287
- package/src/commands/start.ts +0 -881
- package/src/commands/status.ts +0 -99
- package/src/commands/stop.ts +0 -405
- package/src/config/index.ts +0 -146
- package/src/config/manager.ts +0 -157
- package/src/db/index.ts +0 -83
- package/src/db/schema/accounts.ts +0 -23
- package/src/db/schema/address-signatures.ts +0 -31
- package/src/db/schema/index.ts +0 -6
- package/src/db/schema/meta-kv.ts +0 -9
- package/src/db/schema/transactions.ts +0 -36
- package/src/db/schema/tx-account-states.ts +0 -23
- package/src/db/schema/tx-accounts.ts +0 -33
- package/src/db/tx-store.ts +0 -264
- package/src/gui/public/app.css +0 -1556
- package/src/gui/public/build/main.css +0 -1569
- package/src/gui/public/build/main.js +0 -303
- package/src/gui/public/build/main.js.txt +0 -231
- package/src/gui/public/index.html +0 -19
- package/src/gui/server.ts +0 -296
- package/src/gui/src/api.ts +0 -127
- package/src/gui/src/app.tsx +0 -441
- package/src/gui/src/components/airdrop-mint-form.tsx +0 -246
- package/src/gui/src/components/clone-program-modal.tsx +0 -202
- package/src/gui/src/components/clone-token-modal.tsx +0 -230
- package/src/gui/src/components/modal.tsx +0 -134
- package/src/gui/src/components/programs-panel.tsx +0 -124
- package/src/gui/src/components/status-panel.tsx +0 -136
- package/src/gui/src/components/tokens-panel.tsx +0 -122
- package/src/gui/src/hooks/use-interval.ts +0 -17
- package/src/gui/src/index.css +0 -557
- package/src/gui/src/main.tsx +0 -17
- package/src/index.ts +0 -216
- package/src/migrations-bundled.ts +0 -23
- package/src/rpc/start.ts +0 -44
- package/src/services/api-server.ts +0 -504
- package/src/services/port-manager.ts +0 -174
- package/src/services/process-registry.ts +0 -153
- package/src/services/program-cloner.ts +0 -317
- package/src/services/token-cloner.ts +0 -811
- package/src/services/validator.ts +0 -293
- package/src/types/config.ts +0 -110
- package/src/utils/shell.ts +0 -110
- package/src/utils/token-loader.ts +0 -115
package/src/config/manager.ts
DELETED
|
@@ -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;
|
package/src/db/schema/index.ts
DELETED
package/src/db/schema/meta-kv.ts
DELETED
|
@@ -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;
|
package/src/db/tx-store.ts
DELETED
|
@@ -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
|
-
}
|