solforge 0.2.11 → 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 (175) hide show
  1. package/package.json +4 -8
  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 -152
  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
  175. /package/{start.js → start.cjs} +0 -0
@@ -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
- }
File without changes