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