solforge 0.2.5 → 0.2.6

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 (73) hide show
  1. package/package.json +1 -1
  2. package/scripts/postinstall.cjs +3 -3
  3. package/server/lib/base58.ts +1 -1
  4. package/server/methods/account/get-account-info.ts +3 -7
  5. package/server/methods/account/get-balance.ts +3 -7
  6. package/server/methods/account/get-multiple-accounts.ts +2 -1
  7. package/server/methods/account/get-parsed-account-info.ts +3 -7
  8. package/server/methods/account/parsers/index.ts +2 -2
  9. package/server/methods/account/parsers/loader-upgradeable.ts +14 -1
  10. package/server/methods/account/parsers/spl-token.ts +29 -10
  11. package/server/methods/account/request-airdrop.ts +44 -31
  12. package/server/methods/block/get-block.ts +3 -7
  13. package/server/methods/block/get-blocks-with-limit.ts +3 -7
  14. package/server/methods/block/is-blockhash-valid.ts +3 -7
  15. package/server/methods/get-address-lookup-table.ts +3 -7
  16. package/server/methods/program/get-program-accounts.ts +9 -9
  17. package/server/methods/program/get-token-account-balance.ts +3 -7
  18. package/server/methods/program/get-token-accounts-by-delegate.ts +4 -3
  19. package/server/methods/program/get-token-accounts-by-owner.ts +54 -33
  20. package/server/methods/program/get-token-largest-accounts.ts +3 -2
  21. package/server/methods/program/get-token-supply.ts +3 -2
  22. package/server/methods/solforge/index.ts +9 -6
  23. package/server/methods/transaction/get-parsed-transaction.ts +3 -7
  24. package/server/methods/transaction/get-signature-statuses.ts +14 -7
  25. package/server/methods/transaction/get-signatures-for-address.ts +3 -7
  26. package/server/methods/transaction/get-transaction.ts +167 -81
  27. package/server/methods/transaction/send-transaction.ts +29 -16
  28. package/server/methods/transaction/simulate-transaction.ts +3 -2
  29. package/server/rpc-server.ts +47 -34
  30. package/server/types.ts +9 -6
  31. package/server/ws-server.ts +11 -7
  32. package/src/api-server-entry.ts +5 -5
  33. package/src/cli/commands/airdrop.ts +2 -2
  34. package/src/cli/commands/config.ts +2 -2
  35. package/src/cli/commands/mint.ts +3 -3
  36. package/src/cli/commands/program-clone.ts +9 -11
  37. package/src/cli/commands/program-load.ts +3 -3
  38. package/src/cli/commands/rpc-start.ts +7 -7
  39. package/src/cli/commands/token-adopt-authority.ts +1 -1
  40. package/src/cli/commands/token-clone.ts +5 -6
  41. package/src/cli/commands/token-create.ts +5 -5
  42. package/src/cli/main.ts +33 -36
  43. package/src/cli/run-solforge.ts +3 -3
  44. package/src/cli/setup-wizard.ts +8 -6
  45. package/src/commands/add-program.ts +1 -1
  46. package/src/commands/init.ts +2 -2
  47. package/src/commands/mint.ts +5 -6
  48. package/src/commands/start.ts +10 -9
  49. package/src/commands/status.ts +1 -1
  50. package/src/commands/stop.ts +1 -1
  51. package/src/config/index.ts +33 -17
  52. package/src/config/manager.ts +3 -3
  53. package/src/db/index.ts +2 -2
  54. package/src/db/tx-store.ts +12 -8
  55. package/src/gui/public/app.css +13 -13
  56. package/src/gui/server.ts +1 -1
  57. package/src/gui/src/api.ts +1 -1
  58. package/src/gui/src/app.tsx +49 -17
  59. package/src/gui/src/components/airdrop-mint-form.tsx +32 -8
  60. package/src/gui/src/components/clone-program-modal.tsx +25 -6
  61. package/src/gui/src/components/clone-token-modal.tsx +25 -6
  62. package/src/gui/src/components/modal.tsx +6 -1
  63. package/src/gui/src/components/status-panel.tsx +1 -1
  64. package/src/index.ts +19 -6
  65. package/src/services/api-server.ts +41 -19
  66. package/src/services/port-manager.ts +7 -10
  67. package/src/services/process-registry.ts +4 -5
  68. package/src/services/program-cloner.ts +4 -4
  69. package/src/services/token-cloner.ts +4 -4
  70. package/src/services/validator.ts +2 -4
  71. package/src/types/config.ts +2 -2
  72. package/src/utils/shell.ts +1 -1
  73. package/src/utils/token-loader.ts +2 -2
@@ -1,14 +1,20 @@
1
- import { type ChangeEvent, type FormEvent, useMemo, useState } from "react";
1
+ import {
2
+ type ChangeEvent,
3
+ type FormEvent,
4
+ useId,
5
+ useMemo,
6
+ useState,
7
+ } from "react";
2
8
  import type { TokenSummary } from "../api";
3
9
 
4
10
  interface Props {
5
11
  tokens: TokenSummary[];
6
- onAirdrop: (address: string, lamports: string) => Promise<string | void>;
12
+ onAirdrop: (address: string, lamports: string) => Promise<string | undefined>;
7
13
  onMint: (
8
14
  mint: string,
9
15
  owner: string,
10
16
  amountRaw: string,
11
- ) => Promise<string | void>;
17
+ ) => Promise<string | undefined>;
12
18
  }
13
19
 
14
20
  const SOL_OPTION = {
@@ -53,6 +59,11 @@ export function AirdropMintForm({ tokens, onAirdrop, onMint }: Props) {
53
59
  const [error, setError] = useState<string | null>(null);
54
60
  const [message, setMessage] = useState<string | null>(null);
55
61
 
62
+ const uid = useId();
63
+ const recipientId = `${uid}-recipient`;
64
+ const assetId = `${uid}-asset`;
65
+ const amountId = `${uid}-amount`;
66
+
56
67
  const options = useMemo(() => {
57
68
  const tokenOpts = tokens.map((token) => ({
58
69
  value: token.mint,
@@ -87,8 +98,9 @@ export function AirdropMintForm({ tokens, onAirdrop, onMint }: Props) {
87
98
  try {
88
99
  const note = await submit();
89
100
  setMessage(note);
90
- } catch (err: any) {
91
- setError(err?.message ?? String(err));
101
+ } catch (err) {
102
+ const message = err instanceof Error ? err.message : String(err);
103
+ setError(message);
92
104
  } finally {
93
105
  setPending(false);
94
106
  }
@@ -116,11 +128,15 @@ export function AirdropMintForm({ tokens, onAirdrop, onMint }: Props) {
116
128
 
117
129
  <div className="grid gap-4 lg:grid-cols-3">
118
130
  <div className="space-y-2">
119
- <label className="block text-xs font-semibold text-gray-400 uppercase tracking-wider">
131
+ <label
132
+ htmlFor={recipientId}
133
+ className="block text-xs font-semibold text-gray-400 uppercase tracking-wider"
134
+ >
120
135
  Recipient Address
121
136
  </label>
122
137
  <div className="relative">
123
138
  <input
139
+ id={recipientId}
124
140
  value={recipient}
125
141
  onChange={(event: ChangeEvent<HTMLInputElement>) =>
126
142
  setRecipient(event.target.value)
@@ -133,11 +149,15 @@ export function AirdropMintForm({ tokens, onAirdrop, onMint }: Props) {
133
149
  </div>
134
150
 
135
151
  <div className="space-y-2">
136
- <label className="block text-xs font-semibold text-gray-400 uppercase tracking-wider">
152
+ <label
153
+ htmlFor={assetId}
154
+ className="block text-xs font-semibold text-gray-400 uppercase tracking-wider"
155
+ >
137
156
  Asset
138
157
  </label>
139
158
  <div className="relative">
140
159
  <select
160
+ id={assetId}
141
161
  value={asset}
142
162
  onChange={(event: ChangeEvent<HTMLSelectElement>) =>
143
163
  setAsset(event.target.value)
@@ -155,11 +175,15 @@ export function AirdropMintForm({ tokens, onAirdrop, onMint }: Props) {
155
175
  </div>
156
176
 
157
177
  <div className="space-y-2">
158
- <label className="block text-xs font-semibold text-gray-400 uppercase tracking-wider">
178
+ <label
179
+ htmlFor={amountId}
180
+ className="block text-xs font-semibold text-gray-400 uppercase tracking-wider"
181
+ >
159
182
  Amount
160
183
  </label>
161
184
  <div className="relative">
162
185
  <input
186
+ id={amountId}
163
187
  value={amount}
164
188
  onChange={(event: ChangeEvent<HTMLInputElement>) =>
165
189
  setAmount(event.target.value)
@@ -1,4 +1,4 @@
1
- import { type ChangeEvent, useState } from "react";
1
+ import { type ChangeEvent, useId, useState } from "react";
2
2
  import { Modal } from "./modal";
3
3
 
4
4
  interface Props {
@@ -13,6 +13,9 @@ interface Props {
13
13
  }
14
14
 
15
15
  export function CloneProgramModal({ isOpen, onClose, onSubmit }: Props) {
16
+ const programIdId = useId();
17
+ const endpointId = useId();
18
+ const accountLimitId = useId();
16
19
  const [programId, setProgramId] = useState("");
17
20
  const [endpoint, setEndpoint] = useState("");
18
21
  const [withAccounts, setWithAccounts] = useState(true);
@@ -37,8 +40,12 @@ export function CloneProgramModal({ isOpen, onClose, onSubmit }: Props) {
37
40
  setProgramId("");
38
41
  setEndpoint("");
39
42
  setAccountsLimit("100");
40
- } catch (err: any) {
41
- setError(err?.message ?? String(err));
43
+ } catch (err: unknown) {
44
+ const message =
45
+ err && typeof err === "object" && "message" in err
46
+ ? String((err as { message?: unknown }).message)
47
+ : String(err);
48
+ setError(message);
42
49
  } finally {
43
50
  setPending(false);
44
51
  }
@@ -92,11 +99,15 @@ export function CloneProgramModal({ isOpen, onClose, onSubmit }: Props) {
92
99
  >
93
100
  <div className="space-y-5">
94
101
  <div className="space-y-2">
95
- <label className="block text-xs font-semibold text-gray-400 uppercase tracking-wider">
102
+ <label
103
+ htmlFor={programIdId}
104
+ className="block text-xs font-semibold text-gray-400 uppercase tracking-wider"
105
+ >
96
106
  Program ID *
97
107
  </label>
98
108
  <div className="relative">
99
109
  <input
110
+ id={programIdId}
100
111
  value={programId}
101
112
  onChange={(event: ChangeEvent<HTMLInputElement>) =>
102
113
  setProgramId(event.target.value)
@@ -109,11 +120,15 @@ export function CloneProgramModal({ isOpen, onClose, onSubmit }: Props) {
109
120
  </div>
110
121
 
111
122
  <div className="space-y-2">
112
- <label className="block text-xs font-semibold text-gray-400 uppercase tracking-wider">
123
+ <label
124
+ htmlFor={endpointId}
125
+ className="block text-xs font-semibold text-gray-400 uppercase tracking-wider"
126
+ >
113
127
  RPC Endpoint (Optional)
114
128
  </label>
115
129
  <div className="relative">
116
130
  <input
131
+ id={endpointId}
117
132
  value={endpoint}
118
133
  onChange={(event: ChangeEvent<HTMLInputElement>) =>
119
134
  setEndpoint(event.target.value)
@@ -147,11 +162,15 @@ export function CloneProgramModal({ isOpen, onClose, onSubmit }: Props) {
147
162
 
148
163
  {withAccounts && (
149
164
  <div className="ml-8 space-y-2 pt-2 border-t border-white/5">
150
- <label className="block text-xs font-semibold text-gray-400 uppercase tracking-wider">
165
+ <label
166
+ htmlFor={accountLimitId}
167
+ className="block text-xs font-semibold text-gray-400 uppercase tracking-wider"
168
+ >
151
169
  Account Limit
152
170
  </label>
153
171
  <div className="relative">
154
172
  <input
173
+ id={accountLimitId}
155
174
  value={accountsLimit}
156
175
  onChange={(event: ChangeEvent<HTMLInputElement>) =>
157
176
  setAccountsLimit(event.target.value)
@@ -1,4 +1,4 @@
1
- import { type ChangeEvent, useState } from "react";
1
+ import { type ChangeEvent, useId, useState } from "react";
2
2
  import { Modal } from "./modal";
3
3
 
4
4
  interface Props {
@@ -14,6 +14,9 @@ interface Props {
14
14
  }
15
15
 
16
16
  export function CloneTokenModal({ isOpen, onClose, onSubmit }: Props) {
17
+ const mintId = useId();
18
+ const endpointId = useId();
19
+ const holdersId = useId();
17
20
  const [mint, setMint] = useState("");
18
21
  const [endpoint, setEndpoint] = useState("");
19
22
  // Default OFF to avoid hitting public RPC rate limits by cloning holders.
@@ -42,8 +45,12 @@ export function CloneTokenModal({ isOpen, onClose, onSubmit }: Props) {
42
45
  setEndpoint("");
43
46
  setHolders("20");
44
47
  setAllAccounts(false);
45
- } catch (err: any) {
46
- setError(err?.message ?? String(err));
48
+ } catch (err: unknown) {
49
+ const message =
50
+ err && typeof err === "object" && "message" in err
51
+ ? String((err as { message?: unknown }).message)
52
+ : String(err);
53
+ setError(message);
47
54
  } finally {
48
55
  setPending(false);
49
56
  }
@@ -97,11 +104,15 @@ export function CloneTokenModal({ isOpen, onClose, onSubmit }: Props) {
97
104
  >
98
105
  <div className="space-y-5">
99
106
  <div className="space-y-2">
100
- <label className="block text-xs font-semibold text-gray-400 uppercase tracking-wider">
107
+ <label
108
+ htmlFor={mintId}
109
+ className="block text-xs font-semibold text-gray-400 uppercase tracking-wider"
110
+ >
101
111
  Mint Address *
102
112
  </label>
103
113
  <div className="relative">
104
114
  <input
115
+ id={mintId}
105
116
  value={mint}
106
117
  onChange={(event: ChangeEvent<HTMLInputElement>) =>
107
118
  setMint(event.target.value)
@@ -114,11 +125,15 @@ export function CloneTokenModal({ isOpen, onClose, onSubmit }: Props) {
114
125
  </div>
115
126
 
116
127
  <div className="space-y-2">
117
- <label className="block text-xs font-semibold text-gray-400 uppercase tracking-wider">
128
+ <label
129
+ htmlFor={endpointId}
130
+ className="block text-xs font-semibold text-gray-400 uppercase tracking-wider"
131
+ >
118
132
  RPC Endpoint (Optional)
119
133
  </label>
120
134
  <div className="relative">
121
135
  <input
136
+ id={endpointId}
122
137
  value={endpoint}
123
138
  onChange={(event: ChangeEvent<HTMLInputElement>) =>
124
139
  setEndpoint(event.target.value)
@@ -173,11 +188,15 @@ export function CloneTokenModal({ isOpen, onClose, onSubmit }: Props) {
173
188
 
174
189
  {!allAccounts && (
175
190
  <div className="space-y-2">
176
- <label className="block text-xs font-semibold text-gray-400 uppercase tracking-wider">
191
+ <label
192
+ htmlFor={holdersId}
193
+ className="block text-xs font-semibold text-gray-400 uppercase tracking-wider"
194
+ >
177
195
  Top Holders Limit
178
196
  </label>
179
197
  <div className="relative">
180
198
  <input
199
+ id={holdersId}
181
200
  value={holders}
182
201
  onChange={(event: ChangeEvent<HTMLInputElement>) =>
183
202
  setHolders(event.target.value)
@@ -40,9 +40,14 @@ export function Modal({
40
40
  return (
41
41
  <div className="fixed inset-0 z-50 flex items-center justify-center p-4 animate-fadeIn">
42
42
  {/* Backdrop */}
43
- <div
43
+ <button
44
+ type="button"
44
45
  className="absolute inset-0 bg-black/80 backdrop-blur-sm"
46
+ aria-label="Close modal"
45
47
  onClick={onClose}
48
+ onKeyDown={(e) => {
49
+ if (e.key === "Enter" || e.key === " ") onClose();
50
+ }}
46
51
  />
47
52
 
48
53
  {/* Modal */}
@@ -61,7 +61,7 @@ export function StatusPanel({ status, loading, onRefresh }: Props) {
61
61
  <StatusCard
62
62
  title="Faucet Balance"
63
63
  value={`${status.faucet.sol.toFixed(3)} SOL`}
64
- subtitle={status.faucet.address.slice(0, 10) + "…"}
64
+ subtitle={`${status.faucet.address.slice(0, 10)}…`}
65
65
  icon="fa-wallet"
66
66
  color="green"
67
67
  />
package/src/index.ts CHANGED
@@ -2,20 +2,33 @@
2
2
 
3
3
  // Suppress bigint-buffer warning
4
4
  const originalStderrWrite = process.stderr.write.bind(process.stderr);
5
- process.stderr.write = (chunk: any, encoding?: any, callback?: any) => {
5
+ process.stderr.write = ((
6
+ chunk: unknown,
7
+ encoding?: unknown,
8
+ callback?: unknown,
9
+ ) => {
6
10
  if (
7
11
  typeof chunk === "string" &&
8
12
  chunk.includes("bigint: Failed to load bindings")
9
13
  ) {
10
14
  return true; // Suppress this specific warning
11
15
  }
12
- return originalStderrWrite(chunk, encoding, callback);
13
- };
16
+ const writer = originalStderrWrite as unknown as (
17
+ chunk: string | Uint8Array,
18
+ encoding?: BufferEncoding,
19
+ callback?: ((err?: Error) => void) | undefined,
20
+ ) => boolean;
21
+ return writer(
22
+ chunk as string | Uint8Array,
23
+ encoding as BufferEncoding | undefined,
24
+ callback as ((err?: Error) => void) | undefined,
25
+ );
26
+ }) as typeof process.stderr.write;
14
27
 
15
28
  import chalk from "chalk";
16
29
  import { Command } from "commander";
17
- import { existsSync } from "fs";
18
- import { resolve } from "path";
30
+ import { existsSync } from "node:fs";
31
+ import { resolve } from "node:path";
19
32
  import packageJson from "../package.json" with { type: "json" };
20
33
  import { addProgramCommand } from "./commands/add-program.js";
21
34
  import { initCommand } from "./commands/init.js";
@@ -123,7 +136,7 @@ program
123
136
  const config = configManager.getConfig();
124
137
 
125
138
  const apiServer = new APIServer({
126
- port: parseInt(options.port),
139
+ port: parseInt(options.port, 10),
127
140
  host: options.host,
128
141
  validatorRpcUrl: options.rpcUrl,
129
142
  validatorFaucetUrl: options.faucetUrl,
@@ -1,12 +1,11 @@
1
1
  import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
2
- import { Connection, Keypair, PublicKey } from "@solana/web3.js";
2
+ import { Connection, PublicKey } from "@solana/web3.js";
3
3
  import chalk from "chalk";
4
- import { ChildProcess, spawn } from "child_process";
5
4
  import cors from "cors";
6
5
  import express from "express";
7
- import { existsSync, readFileSync } from "fs";
8
- import type { Server } from "http";
9
- import { join } from "path";
6
+ import { existsSync } from "node:fs";
7
+ import type { Server } from "node:http";
8
+ import { join } from "node:path";
10
9
  import { mintTokenToWallet as mintTokenToWalletShared } from "../commands/mint.js";
11
10
  import type { Config } from "../types/config.js";
12
11
  import { runCommand } from "../utils/shell.js";
@@ -31,8 +30,6 @@ export class APIServer {
31
30
  private app: express.Application;
32
31
  private server: Server | null = null;
33
32
  private config: APIServerConfig;
34
- private tokenCloner: TokenCloner;
35
- private programCloner: ProgramCloner;
36
33
  private connection: Connection;
37
34
 
38
35
  constructor(config: APIServerConfig) {
@@ -51,7 +48,7 @@ export class APIServer {
51
48
  this.app.use(express.json());
52
49
 
53
50
  // Request logging
54
- this.app.use((req, res, next) => {
51
+ this.app.use((req, _res, next) => {
55
52
  console.log(chalk.gray(`🌐 API: ${req.method} ${req.path}`));
56
53
  next();
57
54
  });
@@ -61,12 +58,12 @@ export class APIServer {
61
58
  const router = express.Router();
62
59
 
63
60
  // Health check
64
- router.get("/health", (req, res) => {
61
+ router.get("/health", (_req, res) => {
65
62
  res.json({ status: "ok", timestamp: new Date().toISOString() });
66
63
  });
67
64
 
68
65
  // Get validator info
69
- router.get("/validator/info", async (req, res) => {
66
+ router.get("/validator/info", async (_req, res) => {
70
67
  try {
71
68
  const version = await this.connection.getVersion();
72
69
  const blockHeight = await this.connection.getBlockHeight();
@@ -88,7 +85,7 @@ export class APIServer {
88
85
  });
89
86
 
90
87
  // Get all cloned tokens
91
- router.get("/tokens", async (req, res) => {
88
+ router.get("/tokens", async (_req, res) => {
92
89
  try {
93
90
  const clonedTokens = await this.getClonedTokens();
94
91
  res.json({
@@ -110,7 +107,7 @@ export class APIServer {
110
107
  });
111
108
 
112
109
  // Get all cloned programs
113
- router.get("/programs", async (req, res) => {
110
+ router.get("/programs", async (_req, res) => {
114
111
  try {
115
112
  const clonedPrograms = await this.getClonedPrograms();
116
113
  res.json({
@@ -233,7 +230,10 @@ export class APIServer {
233
230
  // Get recent transactions
234
231
  router.get("/transactions/recent", async (req, res) => {
235
232
  try {
236
- const limit = Math.min(parseInt(req.query.limit as string) || 10, 100);
233
+ const limit = Math.min(
234
+ parseInt(req.query.limit as string, 10) || 10,
235
+ 100,
236
+ );
237
237
  const signatures = await this.connection.getSignaturesForAddress(
238
238
  new PublicKey("11111111111111111111111111111111"), // System program
239
239
  { limit },
@@ -254,7 +254,7 @@ export class APIServer {
254
254
  this.app.use("/api", router);
255
255
 
256
256
  // 404 handler
257
- this.app.use("*", (req, res) => {
257
+ this.app.use("*", (_req, res) => {
258
258
  res.status(404).json({ error: "Endpoint not found" });
259
259
  });
260
260
  }
@@ -296,7 +296,13 @@ export class APIServer {
296
296
  mintAddress: string,
297
297
  walletAddress: string,
298
298
  amount: number,
299
- ): Promise<any> {
299
+ ): Promise<{
300
+ success: true;
301
+ symbol: string;
302
+ amount: number;
303
+ walletAddress: string;
304
+ mintAddress: string;
305
+ }> {
300
306
  const clonedTokens = await this.getClonedTokens();
301
307
  const token = findTokenByMint(clonedTokens, mintAddress);
302
308
 
@@ -321,7 +327,18 @@ export class APIServer {
321
327
  };
322
328
  }
323
329
 
324
- private async getWalletBalances(walletAddress: string): Promise<any> {
330
+ private async getWalletBalances(walletAddress: string): Promise<{
331
+ walletAddress: string;
332
+ solBalance: { lamports: number; sol: number };
333
+ tokenBalances: Array<{
334
+ mint: string;
335
+ symbol: string;
336
+ balance: string;
337
+ decimals: number;
338
+ uiAmount: number | null;
339
+ }>;
340
+ timestamp: string;
341
+ }> {
325
342
  try {
326
343
  const publicKey = new PublicKey(walletAddress);
327
344
 
@@ -371,7 +388,7 @@ export class APIServer {
371
388
  }
372
389
  }
373
390
  }
374
- } catch (error) {}
391
+ } catch (_error) {}
375
392
  }
376
393
 
377
394
  return {
@@ -395,7 +412,12 @@ export class APIServer {
395
412
  private async airdropSol(
396
413
  walletAddress: string,
397
414
  amount: number,
398
- ): Promise<any> {
415
+ ): Promise<{
416
+ success: true;
417
+ amount: number;
418
+ walletAddress: string;
419
+ signature: string;
420
+ }> {
399
421
  const result = await runCommand(
400
422
  "solana",
401
423
  [
@@ -477,6 +499,6 @@ export class APIServer {
477
499
  }
478
500
 
479
501
  isRunning(): boolean {
480
- return this.server !== null && this.server.listening;
502
+ return this.server?.listening;
481
503
  }
482
504
  }
@@ -7,7 +7,6 @@ export interface PortAllocation {
7
7
 
8
8
  export class PortManager {
9
9
  private readonly defaultRpcPort = 8899;
10
- private readonly defaultFaucetPort = 9900;
11
10
  private readonly portRangeStart = 8000;
12
11
  private readonly portRangeEnd = 9999;
13
12
 
@@ -147,19 +146,17 @@ export class PortManager {
147
146
  */
148
147
  private async checkPortActuallyFree(port: number): Promise<boolean> {
149
148
  return new Promise((resolve) => {
150
- const net = require("net");
149
+ const net = require("node:net");
151
150
  const server = net.createServer();
152
151
 
153
- server.listen(port, (err: any) => {
154
- if (err) {
155
- resolve(false);
156
- } else {
157
- server.once("close", () => resolve(true));
158
- server.close();
159
- }
152
+ server.once("listening", () => {
153
+ server.once("close", () => resolve(true));
154
+ server.close();
160
155
  });
161
156
 
162
- server.on("error", () => resolve(false));
157
+ server.once("error", () => resolve(false));
158
+
159
+ server.listen(port);
163
160
  });
164
161
  }
165
162
 
@@ -1,7 +1,6 @@
1
- import { existsSync, readFileSync, writeFileSync } from "fs";
2
- import { homedir } from "os";
3
- import { join } from "path";
4
- import type { Config } from "../types/config.js";
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
5
4
 
6
5
  export interface RunningValidator {
7
6
  id: string;
@@ -143,7 +142,7 @@ export class ProcessRegistry {
143
142
  // Ensure directory exists
144
143
  const dir = join(homedir(), ".solforge");
145
144
  if (!existsSync(dir)) {
146
- require("fs").mkdirSync(dir, { recursive: true });
145
+ require("node:fs").mkdirSync(dir, { recursive: true });
147
146
  }
148
147
 
149
148
  writeFileSync(this.registryPath, JSON.stringify(validators, null, 2));
@@ -1,7 +1,7 @@
1
1
  import { Connection, PublicKey } from "@solana/web3.js";
2
2
  import chalk from "chalk";
3
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
4
- import { join } from "path";
3
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
5
  import type { ProgramConfig } from "../types/config.js";
6
6
  import { runCommand } from "../utils/shell.js";
7
7
 
@@ -276,7 +276,7 @@ export class ProgramCloner {
276
276
  const programAccount = await connection.getAccountInfo(
277
277
  new PublicKey(programId),
278
278
  );
279
- return programAccount !== null && programAccount.executable;
279
+ return programAccount?.executable;
280
280
  } catch {
281
281
  return false;
282
282
  }
@@ -310,7 +310,7 @@ export class ProgramCloner {
310
310
  owner: programAccount.owner.toBase58(),
311
311
  size: programAccount.data.length,
312
312
  };
313
- } catch (error) {
313
+ } catch (_error) {
314
314
  return { exists: false };
315
315
  }
316
316
  }
@@ -1,7 +1,7 @@
1
1
  import { Keypair, PublicKey } from "@solana/web3.js";
2
2
  import chalk from "chalk";
3
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
4
- import { join, resolve } from "path";
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
5
  import type { TokenConfig } from "../types/config.js";
6
6
  import { runCommand } from "../utils/shell.js";
7
7
 
@@ -614,7 +614,7 @@ export class TokenCloner {
614
614
  );
615
615
  }
616
616
  }
617
- } catch (error) {
617
+ } catch (_error) {
618
618
  if (debug) {
619
619
  console.log(
620
620
  chalk.gray(` ℹ️ No existing accounts found or parsing error`),
@@ -749,7 +749,7 @@ export class TokenCloner {
749
749
  break;
750
750
  }
751
751
  }
752
- } catch (error) {
752
+ } catch (_error) {
753
753
  // No existing accounts found or parsing error, will create new account
754
754
  }
755
755
  }
@@ -1,6 +1,4 @@
1
- import { type ChildProcess, spawn } from "child_process";
2
- import { existsSync } from "fs";
3
- import { join } from "path";
1
+ import { type ChildProcess, spawn } from "node:child_process";
4
2
  import type {
5
3
  LocalnetConfig,
6
4
  OperationResult,
@@ -251,7 +249,7 @@ export class ValidatorService {
251
249
  if (response.ok) {
252
250
  return; // Validator is ready
253
251
  }
254
- } catch (error) {
252
+ } catch (_error) {
255
253
  // Continue waiting
256
254
  }
257
255
 
@@ -86,11 +86,11 @@ export interface ValidatorState {
86
86
  }
87
87
 
88
88
  // Operation result types
89
- export interface OperationResult<T = any> {
89
+ export interface OperationResult<T = unknown> {
90
90
  success: boolean;
91
91
  data?: T;
92
92
  error?: string;
93
- details?: any;
93
+ details?: unknown;
94
94
  }
95
95
 
96
96
  export interface CloneResult {
@@ -56,7 +56,7 @@ export async function runCommand(
56
56
  if (jsonOutput && success && stdout.trim()) {
57
57
  try {
58
58
  parsedOutput = JSON.parse(stdout);
59
- } catch (e) {
59
+ } catch (_e) {
60
60
  // If JSON parsing fails, keep original stdout
61
61
  console.warn(
62
62
  chalk.yellow("Warning: Expected JSON output but got invalid JSON"),
@@ -1,6 +1,6 @@
1
1
  import { Keypair } from "@solana/web3.js";
2
- import { existsSync, readFileSync } from "fs";
3
- import { join } from "path";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
4
  import type { TokenConfig } from "../types/config.js";
5
5
 
6
6
  export interface ClonedToken {