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,293 +0,0 @@
1
- import { type ChildProcess, spawn } from "node:child_process";
2
- import type {
3
- LocalnetConfig,
4
- OperationResult,
5
- ValidatorState,
6
- ValidatorStatus,
7
- } from "../types/config.js";
8
-
9
- export class ValidatorService {
10
- private process: ChildProcess | null = null;
11
- private state: ValidatorState;
12
- private config: LocalnetConfig;
13
-
14
- constructor(config: LocalnetConfig) {
15
- this.config = config;
16
- this.state = {
17
- status: "stopped",
18
- port: config.port,
19
- faucetPort: config.faucetPort,
20
- rpcUrl: `http://${config.bindAddress}:${config.port}`,
21
- wsUrl: `ws://${config.bindAddress}:${config.port}`,
22
- logs: [],
23
- };
24
- }
25
-
26
- /**
27
- * Start the validator with the given configuration
28
- */
29
- async start(
30
- programs: string[] = [],
31
- tokens: string[] = [],
32
- ): Promise<OperationResult<ValidatorState>> {
33
- if (this.state.status === "running") {
34
- return {
35
- success: false,
36
- error: "Validator is already running",
37
- data: this.state,
38
- };
39
- }
40
-
41
- try {
42
- this.updateStatus("starting");
43
-
44
- const args = this.buildValidatorArgs(programs, tokens);
45
-
46
- this.process = spawn("solana-test-validator", args, {
47
- stdio: ["pipe", "pipe", "pipe"],
48
- detached: false,
49
- });
50
-
51
- this.state.pid = this.process.pid;
52
- this.state.startTime = new Date();
53
-
54
- // Handle process events
55
- this.setupProcessHandlers();
56
-
57
- // Wait for validator to be ready
58
- await this.waitForReady();
59
-
60
- this.updateStatus("running");
61
-
62
- return {
63
- success: true,
64
- data: this.state,
65
- };
66
- } catch (error) {
67
- this.updateStatus("error");
68
- this.state.error = error instanceof Error ? error.message : String(error);
69
-
70
- return {
71
- success: false,
72
- error: this.state.error,
73
- data: this.state,
74
- };
75
- }
76
- }
77
-
78
- /**
79
- * Stop the validator
80
- */
81
- async stop(): Promise<OperationResult<ValidatorState>> {
82
- if (this.state.status === "stopped") {
83
- return {
84
- success: true,
85
- data: this.state,
86
- };
87
- }
88
-
89
- try {
90
- this.updateStatus("stopping");
91
-
92
- if (this.process) {
93
- this.process.kill("SIGTERM");
94
-
95
- // Wait for graceful shutdown
96
- await new Promise<void>((resolve) => {
97
- const timeout = setTimeout(() => {
98
- if (this.process) {
99
- this.process.kill("SIGKILL");
100
- }
101
- resolve();
102
- }, 5000);
103
-
104
- this.process?.on("exit", () => {
105
- clearTimeout(timeout);
106
- resolve();
107
- });
108
- });
109
- }
110
-
111
- this.cleanup();
112
- this.updateStatus("stopped");
113
-
114
- return {
115
- success: true,
116
- data: this.state,
117
- };
118
- } catch (error) {
119
- return {
120
- success: false,
121
- error: error instanceof Error ? error.message : String(error),
122
- data: this.state,
123
- };
124
- }
125
- }
126
-
127
- /**
128
- * Get current validator state
129
- */
130
- getState(): ValidatorState {
131
- return { ...this.state };
132
- }
133
-
134
- /**
135
- * Check if validator is running
136
- */
137
- isRunning(): boolean {
138
- return this.state.status === "running";
139
- }
140
-
141
- /**
142
- * Get recent logs
143
- */
144
- getLogs(count = 100): string[] {
145
- return this.state.logs.slice(-count);
146
- }
147
-
148
- /**
149
- * Build validator arguments based on configuration
150
- */
151
- private buildValidatorArgs(
152
- programs: string[] = [],
153
- tokens: string[] = [],
154
- ): string[] {
155
- const args: string[] = [];
156
-
157
- // Basic configuration
158
- args.push("--rpc-port", this.config.port.toString());
159
- args.push("--faucet-port", this.config.faucetPort.toString());
160
- args.push("--bind-address", this.config.bindAddress);
161
-
162
- if (this.config.reset) {
163
- args.push("--reset");
164
- }
165
-
166
- if (this.config.quiet) {
167
- args.push("--quiet");
168
- }
169
-
170
- if (this.config.ledgerPath) {
171
- args.push("--ledger", this.config.ledgerPath);
172
- }
173
-
174
- // Set log level
175
- args.push("--log");
176
-
177
- // Clone programs
178
- for (const programId of programs) {
179
- args.push("--clone", programId);
180
- }
181
-
182
- // Clone tokens (these would be mint addresses)
183
- for (const tokenMint of tokens) {
184
- args.push("--clone", tokenMint);
185
- }
186
-
187
- // Always specify mainnet as the source for cloning
188
- if (programs.length > 0 || tokens.length > 0) {
189
- args.push("--url", "https://api.mainnet-beta.solana.com");
190
- }
191
-
192
- return args;
193
- }
194
-
195
- /**
196
- * Setup process event handlers
197
- */
198
- private setupProcessHandlers(): void {
199
- if (!this.process) return;
200
-
201
- this.process.stdout?.on("data", (data: Buffer) => {
202
- const log = data.toString().trim();
203
- this.addLog(`[STDOUT] ${log}`);
204
- });
205
-
206
- this.process.stderr?.on("data", (data: Buffer) => {
207
- const log = data.toString().trim();
208
- this.addLog(`[STDERR] ${log}`);
209
- });
210
-
211
- this.process.on("error", (error) => {
212
- this.addLog(`[ERROR] ${error.message}`);
213
- this.updateStatus("error");
214
- this.state.error = error.message;
215
- });
216
-
217
- this.process.on("exit", (code, signal) => {
218
- this.addLog(`[EXIT] Process exited with code ${code}, signal ${signal}`);
219
- this.cleanup();
220
-
221
- if (this.state.status !== "stopping") {
222
- this.updateStatus(code === 0 ? "stopped" : "error");
223
- if (code !== 0) {
224
- this.state.error = `Process exited with code ${code}`;
225
- }
226
- }
227
- });
228
- }
229
-
230
- /**
231
- * Wait for validator to be ready
232
- */
233
- private async waitForReady(timeout = 30000): Promise<void> {
234
- const startTime = Date.now();
235
-
236
- while (Date.now() - startTime < timeout) {
237
- try {
238
- // Try to connect to the RPC endpoint
239
- const response = await fetch(this.state.rpcUrl, {
240
- method: "POST",
241
- headers: { "Content-Type": "application/json" },
242
- body: JSON.stringify({
243
- jsonrpc: "2.0",
244
- id: 1,
245
- method: "getHealth",
246
- }),
247
- });
248
-
249
- if (response.ok) {
250
- return; // Validator is ready
251
- }
252
- } catch (_error) {
253
- // Continue waiting
254
- }
255
-
256
- await new Promise((resolve) => setTimeout(resolve, 1000));
257
- }
258
-
259
- throw new Error("Validator failed to start within timeout period");
260
- }
261
-
262
- /**
263
- * Update validator status
264
- */
265
- private updateStatus(status: ValidatorStatus): void {
266
- this.state.status = status;
267
- this.addLog(`[STATUS] Validator status changed to: ${status}`);
268
- }
269
-
270
- /**
271
- * Add log entry
272
- */
273
- private addLog(message: string): void {
274
- const timestamp = new Date().toISOString();
275
- const logEntry = `[${timestamp}] ${message}`;
276
- this.state.logs.push(logEntry);
277
-
278
- // Keep only last 1000 log entries
279
- if (this.state.logs.length > 1000) {
280
- this.state.logs = this.state.logs.slice(-1000);
281
- }
282
- }
283
-
284
- /**
285
- * Clean up process references
286
- */
287
- private cleanup(): void {
288
- this.process = null;
289
- this.state.pid = undefined;
290
- this.state.startTime = undefined;
291
- this.state.error = undefined;
292
- }
293
- }
@@ -1,110 +0,0 @@
1
- import { z } from "zod";
2
-
3
- // Token configuration schema
4
- export const TokenConfigSchema = z.object({
5
- symbol: z.string().min(1, "Token symbol is required"),
6
- mainnetMint: z.string().min(1, "Mainnet mint address is required"),
7
- mintAuthority: z.string().optional(), // Path to keypair file
8
- mintAmount: z
9
- .number()
10
- .positive("Mint amount must be positive")
11
- .default(1000000), // Amount to mint to mint authority
12
- recipients: z
13
- .array(
14
- z.object({
15
- wallet: z.string().min(1, "Wallet address is required"),
16
- amount: z.number().positive("Amount must be positive"),
17
- }),
18
- )
19
- .default([]),
20
- cloneMetadata: z.boolean().default(true), // Whether to clone token metadata
21
- });
22
-
23
- // Program configuration schema
24
- export const ProgramConfigSchema = z.object({
25
- name: z.string().optional(),
26
- mainnetProgramId: z.string().min(1, "Program ID is required"),
27
- deployPath: z.string().optional(), // Optional path to local .so file
28
- upgradeable: z.boolean().default(false),
29
- cluster: z
30
- .enum(["mainnet-beta", "devnet", "testnet"])
31
- .default("mainnet-beta"),
32
- dependencies: z.array(z.string()).default([]), // Other program IDs this program depends on
33
- });
34
-
35
- // Localnet configuration schema
36
- export const LocalnetConfigSchema = z.object({
37
- airdropAmount: z.number().positive().default(100),
38
- faucetAccounts: z.array(z.string()).default([]),
39
- logLevel: z.enum(["trace", "debug", "info", "warn", "error"]).default("info"),
40
- port: z.number().int().min(1000).max(65535).default(8899),
41
- faucetPort: z.number().int().min(1000).max(65535).default(9900),
42
- reset: z.boolean().default(false),
43
- quiet: z.boolean().default(false),
44
- ledgerPath: z.string().optional(),
45
- bindAddress: z.string().default("127.0.0.1"),
46
- limitLedgerSize: z.number().int().positive().default(100000),
47
- rpc: z
48
- .string()
49
- .url("RPC must be a valid URL")
50
- .default("https://api.mainnet-beta.solana.com"),
51
- });
52
-
53
- // Complete configuration schema
54
- export const ConfigSchema = z.object({
55
- name: z.string().default("solforge-localnet"),
56
- description: z.string().optional(),
57
- tokens: z.array(TokenConfigSchema).default([]),
58
- programs: z.array(ProgramConfigSchema).default([]),
59
- localnet: LocalnetConfigSchema.default({}),
60
- });
61
-
62
- // Inferred TypeScript types
63
- export type TokenConfig = z.infer<typeof TokenConfigSchema>;
64
- export type ProgramConfig = z.infer<typeof ProgramConfigSchema>;
65
- export type LocalnetConfig = z.infer<typeof LocalnetConfigSchema>;
66
- export type Config = z.infer<typeof ConfigSchema>;
67
-
68
- // Validator status types
69
- export type ValidatorStatus =
70
- | "stopped"
71
- | "starting"
72
- | "running"
73
- | "stopping"
74
- | "error";
75
-
76
- export interface ValidatorState {
77
- status: ValidatorStatus;
78
- pid?: number;
79
- port: number;
80
- faucetPort: number;
81
- rpcUrl: string;
82
- wsUrl: string;
83
- startTime?: Date;
84
- logs: string[];
85
- error?: string;
86
- }
87
-
88
- // Operation result types
89
- export interface OperationResult<T = unknown> {
90
- success: boolean;
91
- data?: T;
92
- error?: string;
93
- details?: unknown;
94
- }
95
-
96
- export interface CloneResult {
97
- type: "program" | "token";
98
- id: string;
99
- name?: string;
100
- success: boolean;
101
- error?: string;
102
- }
103
-
104
- export interface ValidationResult {
105
- valid: boolean;
106
- errors: Array<{
107
- path: string;
108
- message: string;
109
- }>;
110
- }
@@ -1,110 +0,0 @@
1
- import { $ } from "bun";
2
- import chalk from "chalk";
3
-
4
- export interface CommandResult {
5
- success: boolean;
6
- stdout: string;
7
- stderr: string;
8
- exitCode: number;
9
- }
10
-
11
- /**
12
- * Execute a shell command and return the result
13
- */
14
- export async function runCommand(
15
- command: string,
16
- args: string[] = [],
17
- options: {
18
- silent?: boolean;
19
- jsonOutput?: boolean;
20
- debug?: boolean;
21
- } = {},
22
- ): Promise<CommandResult> {
23
- const { silent = false, jsonOutput = false, debug = false } = options;
24
-
25
- try {
26
- if (!silent || debug) {
27
- console.log(chalk.gray(`$ ${command} ${args.join(" ")}`));
28
- }
29
-
30
- const result = await $`${command} ${args}`.quiet();
31
-
32
- const stdout = result.stdout.toString();
33
- const stderr = result.stderr.toString();
34
- const exitCode = result.exitCode;
35
- const success = exitCode === 0;
36
-
37
- if (debug) {
38
- console.log(chalk.gray(`Exit code: ${exitCode}`));
39
- if (stdout) {
40
- console.log(chalk.gray(`Stdout: ${stdout}`));
41
- }
42
- if (stderr) {
43
- console.log(chalk.gray(`Stderr: ${stderr}`));
44
- }
45
- }
46
-
47
- if (!silent && !success) {
48
- console.error(chalk.red(`Command failed with exit code ${exitCode}`));
49
- if (stderr) {
50
- console.error(chalk.red(`Error: ${stderr}`));
51
- }
52
- }
53
-
54
- // If JSON output is expected, try to parse it
55
- let parsedOutput = stdout;
56
- if (jsonOutput && success && stdout.trim()) {
57
- try {
58
- parsedOutput = JSON.parse(stdout);
59
- } catch (_e) {
60
- // If JSON parsing fails, keep original stdout
61
- console.warn(
62
- chalk.yellow("Warning: Expected JSON output but got invalid JSON"),
63
- );
64
- }
65
- }
66
-
67
- return {
68
- success,
69
- stdout: parsedOutput,
70
- stderr,
71
- exitCode,
72
- };
73
- } catch (error) {
74
- const errorMessage = error instanceof Error ? error.message : String(error);
75
-
76
- if (!silent) {
77
- console.error(chalk.red(`Command execution failed: ${errorMessage}`));
78
- }
79
-
80
- return {
81
- success: false,
82
- stdout: "",
83
- stderr: errorMessage,
84
- exitCode: 1,
85
- };
86
- }
87
- }
88
-
89
- /**
90
- * Check if a command exists in PATH
91
- */
92
- export async function commandExists(command: string): Promise<boolean> {
93
- const result = await runCommand("which", [command], { silent: true });
94
- return result.success;
95
- }
96
-
97
- /**
98
- * Check if solana CLI tools are available
99
- */
100
- export async function checkSolanaTools(): Promise<{
101
- solana: boolean;
102
- splToken: boolean;
103
- }> {
104
- const [solana, splToken] = await Promise.all([
105
- commandExists("solana"),
106
- commandExists("spl-token"),
107
- ]);
108
-
109
- return { solana, splToken };
110
- }
@@ -1,115 +0,0 @@
1
- import { Keypair } from "@solana/web3.js";
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { join } from "node:path";
4
- import type { TokenConfig } from "../types/config.js";
5
-
6
- export interface ClonedToken {
7
- config: TokenConfig;
8
- mintAuthorityPath: string;
9
- modifiedAccountPath: string;
10
- metadataAccountPath?: string;
11
- mintAuthority: {
12
- publicKey: string;
13
- secretKey: number[];
14
- };
15
- }
16
-
17
- /**
18
- * Shared utility to load cloned tokens from the work directory.
19
- * This ensures consistent token loading across CLI and API server.
20
- */
21
- export async function loadClonedTokens(
22
- tokenConfigs: TokenConfig[],
23
- workDir: string = ".solforge",
24
- ): Promise<ClonedToken[]> {
25
- const clonedTokens: ClonedToken[] = [];
26
-
27
- // Load shared mint authority
28
- const sharedMintAuthorityPath = join(workDir, "shared-mint-authority.json");
29
- let sharedMintAuthority: { publicKey: string; secretKey: number[] } | null =
30
- null;
31
-
32
- if (existsSync(sharedMintAuthorityPath)) {
33
- try {
34
- const fileContent = JSON.parse(
35
- readFileSync(sharedMintAuthorityPath, "utf8"),
36
- );
37
-
38
- if (Array.isArray(fileContent)) {
39
- // New format: file contains just the secret key array
40
- const keypair = Keypair.fromSecretKey(new Uint8Array(fileContent));
41
- sharedMintAuthority = {
42
- publicKey: keypair.publicKey.toBase58(),
43
- secretKey: Array.from(keypair.secretKey),
44
- };
45
-
46
- // Check metadata for consistency
47
- const metadataPath = join(workDir, "shared-mint-authority-meta.json");
48
- if (existsSync(metadataPath)) {
49
- const metadata = JSON.parse(readFileSync(metadataPath, "utf8"));
50
- if (metadata.publicKey !== sharedMintAuthority.publicKey) {
51
- sharedMintAuthority.publicKey = metadata.publicKey;
52
- }
53
- }
54
- } else {
55
- // Old format: file contains {publicKey, secretKey}
56
- sharedMintAuthority = fileContent;
57
- }
58
- } catch (error) {
59
- console.error("Failed to load shared mint authority:", error);
60
- return [];
61
- }
62
- }
63
-
64
- if (!sharedMintAuthority) {
65
- return [];
66
- }
67
-
68
- // Load each token that has been cloned
69
- for (const tokenConfig of tokenConfigs) {
70
- const tokenDir = join(workDir, `token-${tokenConfig.symbol.toLowerCase()}`);
71
- const modifiedAccountPath = join(tokenDir, "modified.json");
72
- const metadataAccountPath = join(tokenDir, "metadata.json");
73
-
74
- // Check if this token has already been cloned
75
- if (existsSync(modifiedAccountPath)) {
76
- const clonedToken: ClonedToken = {
77
- config: tokenConfig,
78
- mintAuthorityPath: sharedMintAuthorityPath,
79
- modifiedAccountPath,
80
- mintAuthority: sharedMintAuthority,
81
- };
82
-
83
- // Add metadata path if it exists
84
- if (existsSync(metadataAccountPath)) {
85
- clonedToken.metadataAccountPath = metadataAccountPath;
86
- }
87
-
88
- clonedTokens.push(clonedToken);
89
- }
90
- }
91
-
92
- return clonedTokens;
93
- }
94
-
95
- /**
96
- * Find a cloned token by its mint address
97
- */
98
- export function findTokenByMint(
99
- clonedTokens: ClonedToken[],
100
- mintAddress: string,
101
- ): ClonedToken | undefined {
102
- return clonedTokens.find((token) => token.config.mainnetMint === mintAddress);
103
- }
104
-
105
- /**
106
- * Find a cloned token by its symbol
107
- */
108
- export function findTokenBySymbol(
109
- clonedTokens: ClonedToken[],
110
- symbol: string,
111
- ): ClonedToken | undefined {
112
- return clonedTokens.find(
113
- (token) => token.config.symbol.toLowerCase() === symbol.toLowerCase(),
114
- );
115
- }