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.
- package/package.json +1 -1
- package/scripts/postinstall.cjs +3 -3
- package/server/lib/base58.ts +1 -1
- 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 +44 -31
- 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 +167 -81
- package/server/methods/transaction/send-transaction.ts +29 -16
- package/server/methods/transaction/simulate-transaction.ts +3 -2
- package/server/rpc-server.ts +47 -34
- package/server/types.ts +9 -6
- 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/tx-store.ts +12 -8
- 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/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
|
@@ -45,12 +45,13 @@ export const simulateTransaction: RpcMethodHandler = (id, params, context) => {
|
|
|
45
45
|
: null,
|
|
46
46
|
},
|
|
47
47
|
});
|
|
48
|
-
} catch (error:
|
|
48
|
+
} catch (error: unknown) {
|
|
49
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
49
50
|
return context.createErrorResponse(
|
|
50
51
|
id,
|
|
51
52
|
-32003,
|
|
52
53
|
"Simulation failed",
|
|
53
|
-
|
|
54
|
+
message,
|
|
54
55
|
);
|
|
55
56
|
}
|
|
56
57
|
};
|
package/server/rpc-server.ts
CHANGED
|
@@ -35,8 +35,8 @@ export class LiteSVMRpcServer {
|
|
|
35
35
|
blockTime?: number;
|
|
36
36
|
preBalances?: number[];
|
|
37
37
|
postBalances?: number[];
|
|
38
|
-
preTokenBalances?:
|
|
39
|
-
postTokenBalances?:
|
|
38
|
+
preTokenBalances?: unknown[];
|
|
39
|
+
postTokenBalances?: unknown[];
|
|
40
40
|
}
|
|
41
41
|
> = new Map();
|
|
42
42
|
private store: TxStore;
|
|
@@ -95,7 +95,7 @@ export class LiteSVMRpcServer {
|
|
|
95
95
|
|
|
96
96
|
private createSuccessResponse(
|
|
97
97
|
id: string | number,
|
|
98
|
-
result:
|
|
98
|
+
result: unknown,
|
|
99
99
|
): JsonRpcResponse {
|
|
100
100
|
return {
|
|
101
101
|
jsonrpc: "2.0",
|
|
@@ -108,7 +108,7 @@ export class LiteSVMRpcServer {
|
|
|
108
108
|
id: string | number,
|
|
109
109
|
code: number,
|
|
110
110
|
message: string,
|
|
111
|
-
data?:
|
|
111
|
+
data?: unknown,
|
|
112
112
|
): JsonRpcResponse {
|
|
113
113
|
return {
|
|
114
114
|
jsonrpc: "2.0",
|
|
@@ -132,7 +132,7 @@ export class LiteSVMRpcServer {
|
|
|
132
132
|
},
|
|
133
133
|
getFaucet: () => this.faucet,
|
|
134
134
|
getTxCount: () => this.txCount,
|
|
135
|
-
registerMint: (mint:
|
|
135
|
+
registerMint: (mint: PublicKey | string) => {
|
|
136
136
|
try {
|
|
137
137
|
const pk =
|
|
138
138
|
typeof mint === "string" ? mint : new PublicKey(mint).toBase58();
|
|
@@ -140,7 +140,7 @@ export class LiteSVMRpcServer {
|
|
|
140
140
|
} catch {}
|
|
141
141
|
},
|
|
142
142
|
listMints: () => Array.from(this.knownMints),
|
|
143
|
-
registerProgram: (program:
|
|
143
|
+
registerProgram: (program: PublicKey | string) => {
|
|
144
144
|
try {
|
|
145
145
|
const pk =
|
|
146
146
|
typeof program === "string"
|
|
@@ -160,19 +160,30 @@ export class LiteSVMRpcServer {
|
|
|
160
160
|
blockTime: meta?.blockTime,
|
|
161
161
|
preBalances: meta?.preBalances,
|
|
162
162
|
postBalances: meta?.postBalances,
|
|
163
|
-
preTokenBalances: (
|
|
164
|
-
|
|
163
|
+
preTokenBalances: (
|
|
164
|
+
meta as { preTokenBalances?: unknown[] } | undefined
|
|
165
|
+
)?.preTokenBalances,
|
|
166
|
+
postTokenBalances: (
|
|
167
|
+
meta as { postTokenBalances?: unknown[] } | undefined
|
|
168
|
+
)?.postTokenBalances,
|
|
165
169
|
});
|
|
166
170
|
|
|
167
171
|
// Persist to SQLite for durability and history queries
|
|
168
172
|
try {
|
|
169
|
-
const msg
|
|
170
|
-
|
|
173
|
+
const msg = tx.message as unknown as {
|
|
174
|
+
staticAccountKeys?: unknown[];
|
|
175
|
+
accountKeys?: unknown[];
|
|
176
|
+
header?: unknown;
|
|
177
|
+
isAccountSigner?: (i: number) => boolean;
|
|
178
|
+
isAccountWritable?: (i: number) => boolean;
|
|
179
|
+
version?: number;
|
|
180
|
+
};
|
|
181
|
+
const rawKeys: unknown[] = Array.isArray(msg.staticAccountKeys)
|
|
171
182
|
? msg.staticAccountKeys
|
|
172
183
|
: Array.isArray(msg.accountKeys)
|
|
173
184
|
? msg.accountKeys
|
|
174
185
|
: [];
|
|
175
|
-
const keys: string[] = rawKeys.map((k
|
|
186
|
+
const keys: string[] = rawKeys.map((k) => {
|
|
176
187
|
try {
|
|
177
188
|
return typeof k === "string" ? k : (k as PublicKey).toBase58();
|
|
178
189
|
} catch {
|
|
@@ -226,18 +237,24 @@ export class LiteSVMRpcServer {
|
|
|
226
237
|
err: meta?.err ?? null,
|
|
227
238
|
rawBase64,
|
|
228
239
|
preBalances: Array.isArray(meta?.preBalances)
|
|
229
|
-
? meta
|
|
240
|
+
? (meta?.preBalances as number[])
|
|
230
241
|
: [],
|
|
231
242
|
postBalances: Array.isArray(meta?.postBalances)
|
|
232
|
-
? meta
|
|
233
|
-
: [],
|
|
234
|
-
logs: Array.isArray(meta?.logs) ? meta!.logs! : [],
|
|
235
|
-
preTokenBalances: Array.isArray((meta as any)?.preTokenBalances)
|
|
236
|
-
? (meta as any).preTokenBalances
|
|
237
|
-
: [],
|
|
238
|
-
postTokenBalances: Array.isArray((meta as any)?.postTokenBalances)
|
|
239
|
-
? (meta as any).postTokenBalances
|
|
243
|
+
? (meta?.postBalances as number[])
|
|
240
244
|
: [],
|
|
245
|
+
logs: Array.isArray(meta?.logs) ? (meta?.logs as string[]) : [],
|
|
246
|
+
preTokenBalances: (() => {
|
|
247
|
+
const arr = (
|
|
248
|
+
meta as { preTokenBalances?: unknown[] } | undefined
|
|
249
|
+
)?.preTokenBalances;
|
|
250
|
+
return Array.isArray(arr) ? arr : [];
|
|
251
|
+
})(),
|
|
252
|
+
postTokenBalances: (() => {
|
|
253
|
+
const arr = (
|
|
254
|
+
meta as { postTokenBalances?: unknown[] } | undefined
|
|
255
|
+
)?.postTokenBalances;
|
|
256
|
+
return Array.isArray(arr) ? arr : [];
|
|
257
|
+
})(),
|
|
241
258
|
accounts,
|
|
242
259
|
})
|
|
243
260
|
.catch(() => {});
|
|
@@ -262,7 +279,7 @@ export class LiteSVMRpcServer {
|
|
|
262
279
|
return null;
|
|
263
280
|
}
|
|
264
281
|
})
|
|
265
|
-
.filter(Boolean) as
|
|
282
|
+
.filter(Boolean) as import("../src/db/tx-store").AccountSnapshot[];
|
|
266
283
|
if (snapshots.length > 0)
|
|
267
284
|
this.store.upsertAccounts(snapshots).catch(() => {});
|
|
268
285
|
} catch {}
|
|
@@ -302,7 +319,7 @@ export class LiteSVMRpcServer {
|
|
|
302
319
|
|
|
303
320
|
getSignatureStatus(
|
|
304
321
|
signature: string,
|
|
305
|
-
): { slot: number; err:
|
|
322
|
+
): { slot: number; err: unknown | null } | null {
|
|
306
323
|
// Prefer local record for reliability
|
|
307
324
|
const rec = this.txRecords.get(signature);
|
|
308
325
|
if (rec) {
|
|
@@ -312,10 +329,10 @@ export class LiteSVMRpcServer {
|
|
|
312
329
|
const sigBytes = decodeBase58(signature);
|
|
313
330
|
const tx = this.svm.getTransaction(sigBytes);
|
|
314
331
|
if (!tx) return null;
|
|
315
|
-
|
|
316
|
-
let errVal: any = null;
|
|
332
|
+
let errVal: unknown = null;
|
|
317
333
|
try {
|
|
318
|
-
|
|
334
|
+
const raw = (tx as { err?: unknown }).err;
|
|
335
|
+
errVal = typeof raw === "function" ? (raw as () => unknown)() : raw;
|
|
319
336
|
} catch {
|
|
320
337
|
errVal = null;
|
|
321
338
|
}
|
|
@@ -350,13 +367,9 @@ export class LiteSVMRpcServer {
|
|
|
350
367
|
}
|
|
351
368
|
|
|
352
369
|
return result;
|
|
353
|
-
} catch (error:
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
-32603,
|
|
357
|
-
"Internal error",
|
|
358
|
-
error.message,
|
|
359
|
-
);
|
|
370
|
+
} catch (error: unknown) {
|
|
371
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
372
|
+
return this.createErrorResponse(id, -32603, "Internal error", message);
|
|
360
373
|
}
|
|
361
374
|
}
|
|
362
375
|
}
|
|
@@ -406,7 +419,7 @@ export function createLiteSVMRpcServer(port: number = 8899, host?: string) {
|
|
|
406
419
|
try {
|
|
407
420
|
console.log(
|
|
408
421
|
"RPC batch:",
|
|
409
|
-
body.map((b:
|
|
422
|
+
body.map((b: { method?: string }) => b.method),
|
|
410
423
|
);
|
|
411
424
|
} catch {}
|
|
412
425
|
}
|
|
@@ -432,7 +445,7 @@ export function createLiteSVMRpcServer(port: number = 8899, host?: string) {
|
|
|
432
445
|
headers: { "Content-Type": "application/json", ...corsHeaders },
|
|
433
446
|
});
|
|
434
447
|
}
|
|
435
|
-
} catch (
|
|
448
|
+
} catch (_error) {
|
|
436
449
|
return new Response(
|
|
437
450
|
JSON.stringify({
|
|
438
451
|
jsonrpc: "2.0",
|
package/server/types.ts
CHANGED
|
@@ -6,17 +6,17 @@ export interface JsonRpcRequest {
|
|
|
6
6
|
jsonrpc: "2.0";
|
|
7
7
|
id: string | number;
|
|
8
8
|
method: string;
|
|
9
|
-
params?:
|
|
9
|
+
params?: unknown;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface JsonRpcResponse {
|
|
13
13
|
jsonrpc: "2.0";
|
|
14
14
|
id: string | number;
|
|
15
|
-
result?:
|
|
15
|
+
result?: unknown;
|
|
16
16
|
error?: {
|
|
17
17
|
code: number;
|
|
18
18
|
message: string;
|
|
19
|
-
data?:
|
|
19
|
+
data?: unknown;
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -27,12 +27,15 @@ export interface RpcMethodContext {
|
|
|
27
27
|
store?: TxStore;
|
|
28
28
|
encodeBase58: (bytes: Uint8Array) => string;
|
|
29
29
|
decodeBase58: (str: string) => Uint8Array;
|
|
30
|
-
createSuccessResponse: (
|
|
30
|
+
createSuccessResponse: (
|
|
31
|
+
id: string | number,
|
|
32
|
+
result: unknown,
|
|
33
|
+
) => JsonRpcResponse;
|
|
31
34
|
createErrorResponse: (
|
|
32
35
|
id: string | number,
|
|
33
36
|
code: number,
|
|
34
37
|
message: string,
|
|
35
|
-
data?:
|
|
38
|
+
data?: unknown,
|
|
36
39
|
) => JsonRpcResponse;
|
|
37
40
|
notifySignature: (signature: string) => void;
|
|
38
41
|
getFaucet: () => Keypair;
|
|
@@ -69,6 +72,6 @@ export interface RpcMethodContext {
|
|
|
69
72
|
|
|
70
73
|
export type RpcMethodHandler = (
|
|
71
74
|
id: string | number,
|
|
72
|
-
params:
|
|
75
|
+
params: unknown[] | undefined,
|
|
73
76
|
context: RpcMethodContext,
|
|
74
77
|
) => JsonRpcResponse | Promise<JsonRpcResponse>;
|
package/server/ws-server.ts
CHANGED
|
@@ -14,7 +14,11 @@ export function createLiteSVMWebSocketServer(
|
|
|
14
14
|
const sockets = new Set<WebSocket>();
|
|
15
15
|
const pendingChecks = new Map<string, number>();
|
|
16
16
|
|
|
17
|
-
const sendSignatureNotification = (
|
|
17
|
+
const sendSignatureNotification = (
|
|
18
|
+
sig: string,
|
|
19
|
+
slot: number,
|
|
20
|
+
err: unknown,
|
|
21
|
+
) => {
|
|
18
22
|
const payload = {
|
|
19
23
|
jsonrpc: "2.0",
|
|
20
24
|
method: "signatureNotification",
|
|
@@ -25,14 +29,14 @@ export function createLiteSVMWebSocketServer(
|
|
|
25
29
|
for (const [id, sub] of subs.entries()) {
|
|
26
30
|
if (sub.type === "signature" && sub.signature === sig) {
|
|
27
31
|
try {
|
|
28
|
-
|
|
32
|
+
for (const s of sockets) {
|
|
29
33
|
s.send(
|
|
30
34
|
JSON.stringify({
|
|
31
35
|
...payload,
|
|
32
36
|
params: { ...payload.params, subscription: id },
|
|
33
37
|
}),
|
|
34
|
-
)
|
|
35
|
-
|
|
38
|
+
);
|
|
39
|
+
}
|
|
36
40
|
} catch {}
|
|
37
41
|
subs.delete(id);
|
|
38
42
|
}
|
|
@@ -70,7 +74,7 @@ export function createLiteSVMWebSocketServer(
|
|
|
70
74
|
port,
|
|
71
75
|
hostname: host || process.env.RPC_HOST || "127.0.0.1",
|
|
72
76
|
fetch(req, srv) {
|
|
73
|
-
if (srv.upgrade(req)) return undefined as
|
|
77
|
+
if (srv.upgrade(req)) return undefined as unknown as Response;
|
|
74
78
|
return new Response("Not a websocket", { status: 400 });
|
|
75
79
|
},
|
|
76
80
|
websocket: {
|
|
@@ -91,7 +95,7 @@ export function createLiteSVMWebSocketServer(
|
|
|
91
95
|
id,
|
|
92
96
|
method,
|
|
93
97
|
params = [],
|
|
94
|
-
} = msg as { id: number; method: string; params?:
|
|
98
|
+
} = msg as { id: number; method: string; params?: unknown[] };
|
|
95
99
|
if (method === "signatureSubscribe") {
|
|
96
100
|
const [signature] = params;
|
|
97
101
|
const subId = nextSubId++;
|
|
@@ -147,7 +151,7 @@ export function createLiteSVMWebSocketServer(
|
|
|
147
151
|
error: { code: -32601, message: `Method not found: ${method}` },
|
|
148
152
|
}),
|
|
149
153
|
);
|
|
150
|
-
} catch (
|
|
154
|
+
} catch (_e) {
|
|
151
155
|
try {
|
|
152
156
|
ws.send(
|
|
153
157
|
JSON.stringify({
|
package/src/api-server-entry.ts
CHANGED
|
@@ -33,13 +33,13 @@ async function main() {
|
|
|
33
33
|
process.exit(1);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const port = parseInt(args[portIndex + 1]
|
|
36
|
+
const port = parseInt(String(args[portIndex + 1]), 10);
|
|
37
37
|
const host =
|
|
38
38
|
hostIndex !== -1 && args[hostIndex + 1] ? args[hostIndex + 1] : undefined;
|
|
39
|
-
const configPath = args[configIndex + 1]
|
|
40
|
-
const rpcUrl = args[rpcIndex + 1]
|
|
41
|
-
const faucetUrl = args[faucetIndex + 1]
|
|
42
|
-
const workDir = args[workDirIndex + 1]
|
|
39
|
+
const configPath = String(args[configIndex + 1]);
|
|
40
|
+
const rpcUrl = String(args[rpcIndex + 1]);
|
|
41
|
+
const faucetUrl = String(args[faucetIndex + 1]);
|
|
42
|
+
const workDir = String(args[workDirIndex + 1]);
|
|
43
43
|
|
|
44
44
|
// Load configuration
|
|
45
45
|
await configManager.load(configPath);
|
|
@@ -4,8 +4,8 @@ import { parseFlags } from "../utils/args";
|
|
|
4
4
|
|
|
5
5
|
export async function airdropCommand(args: string[]) {
|
|
6
6
|
const { flags } = parseFlags(args);
|
|
7
|
-
const to = String(flags
|
|
8
|
-
const sol = Number(flags
|
|
7
|
+
const to = String(flags.to || "");
|
|
8
|
+
const sol = Number(flags.sol || 0);
|
|
9
9
|
const cfg = await readConfig();
|
|
10
10
|
const url = `http://localhost:${cfg.server.rpcPort}`;
|
|
11
11
|
if (!to || !sol) {
|
|
@@ -11,7 +11,7 @@ export async function configCommand(sub: string | undefined, args: string[]) {
|
|
|
11
11
|
switch (sub) {
|
|
12
12
|
case "init": {
|
|
13
13
|
const { flags } = parseFlags(args);
|
|
14
|
-
const force = !!flags
|
|
14
|
+
const force = !!flags.force;
|
|
15
15
|
await writeDefaultConfig({ force });
|
|
16
16
|
p.log.success("Wrote sf.config.json");
|
|
17
17
|
return;
|
|
@@ -28,7 +28,7 @@ export async function configCommand(sub: string | undefined, args: string[]) {
|
|
|
28
28
|
const updated = setConfigValue(cfg, key, value);
|
|
29
29
|
await Bun.write(
|
|
30
30
|
"sf.config.json",
|
|
31
|
-
JSON.stringify(updated, null, 2)
|
|
31
|
+
`${JSON.stringify(updated, null, 2)}\n`,
|
|
32
32
|
);
|
|
33
33
|
p.log.success(`Updated ${key}`);
|
|
34
34
|
return;
|
package/src/cli/commands/mint.ts
CHANGED
|
@@ -7,9 +7,9 @@ import { parseFlags } from "../utils/args";
|
|
|
7
7
|
// Usage: solforge mint --mint <mint> --to <owner> --amount <amount>
|
|
8
8
|
export async function mintCommand(args: string[]) {
|
|
9
9
|
const { flags } = parseFlags(args);
|
|
10
|
-
let mint = flags
|
|
11
|
-
let receiver = flags
|
|
12
|
-
let amountBase = flags
|
|
10
|
+
let mint = flags.mint as string | undefined;
|
|
11
|
+
let receiver = flags.to as string | undefined; // required: receiver address (ATA owner)
|
|
12
|
+
let amountBase = flags.amount as string | undefined; // optional direct base-units
|
|
13
13
|
let uiAmount = flags["ui-amount"] as string | undefined; // preferred UI units
|
|
14
14
|
|
|
15
15
|
const cfg = await readConfig();
|
|
@@ -6,12 +6,12 @@ export async function programCloneCommand(args: string[]) {
|
|
|
6
6
|
const { flags, rest } = parseFlags(args);
|
|
7
7
|
const programId = (
|
|
8
8
|
(rest[0] as string) ||
|
|
9
|
-
(flags
|
|
9
|
+
(flags.program as string) ||
|
|
10
10
|
""
|
|
11
11
|
).trim();
|
|
12
|
-
const configPath = flags
|
|
12
|
+
const configPath = flags.config as string | undefined;
|
|
13
13
|
const cfg = await readConfig(configPath);
|
|
14
|
-
const endpoint = (flags
|
|
14
|
+
const endpoint = (flags.endpoint as string) || cfg.clone.endpoint;
|
|
15
15
|
const withAccounts = !!flags["with-accounts"];
|
|
16
16
|
const accountsLimit = flags["accounts-limit"]
|
|
17
17
|
? Number(flags["accounts-limit"])
|
|
@@ -56,16 +56,14 @@ export async function programAccountsCloneCommand(args: string[]) {
|
|
|
56
56
|
const { flags, rest } = parseFlags(args);
|
|
57
57
|
const programId = (
|
|
58
58
|
(rest[0] as string) ||
|
|
59
|
-
(flags
|
|
59
|
+
(flags.program as string) ||
|
|
60
60
|
""
|
|
61
61
|
).trim();
|
|
62
|
-
const configPath = flags
|
|
62
|
+
const configPath = flags.config as string | undefined;
|
|
63
63
|
const cfg = await readConfig(configPath);
|
|
64
|
-
const endpoint = (flags
|
|
65
|
-
const limit = flags
|
|
66
|
-
const filters = flags
|
|
67
|
-
? safeJson(flags["filters"] as string)
|
|
68
|
-
: undefined;
|
|
64
|
+
const endpoint = (flags.endpoint as string) || cfg.clone.endpoint;
|
|
65
|
+
const limit = flags.limit ? Number(flags.limit) : undefined;
|
|
66
|
+
const filters = flags.filters ? safeJson(flags.filters as string) : undefined;
|
|
69
67
|
if (!programId) {
|
|
70
68
|
p.log.error(
|
|
71
69
|
"Usage: solforge program accounts clone <programId> [--endpoint URL] [--limit N] [--filters JSON]",
|
|
@@ -97,7 +95,7 @@ export async function programAccountsCloneCommand(args: string[]) {
|
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
97
|
|
|
100
|
-
function safeJson(s: string):
|
|
98
|
+
function safeJson(s: string): unknown {
|
|
101
99
|
try {
|
|
102
100
|
return JSON.parse(s);
|
|
103
101
|
} catch {
|
|
@@ -5,9 +5,9 @@ import { parseFlags } from "../utils/args";
|
|
|
5
5
|
|
|
6
6
|
export async function programLoadCommand(args: string[]) {
|
|
7
7
|
const { flags, rest } = parseFlags(args);
|
|
8
|
-
const programId = (rest[0] as string) || (flags
|
|
9
|
-
const fromFile = flags
|
|
10
|
-
const endpoint = flags
|
|
8
|
+
const programId = (rest[0] as string) || (flags.program as string);
|
|
9
|
+
const fromFile = flags.file as string | undefined;
|
|
10
|
+
const endpoint = flags.endpoint as string | undefined;
|
|
11
11
|
if (!programId) {
|
|
12
12
|
p.log.error(
|
|
13
13
|
"Usage: solforge program load <programId> [--file PATH | --endpoint URL]",
|
|
@@ -5,13 +5,11 @@ import { parseFlags } from "../utils/args";
|
|
|
5
5
|
|
|
6
6
|
export async function rpcStartCommand(args: string[]) {
|
|
7
7
|
const { flags } = parseFlags(args);
|
|
8
|
-
const cfg = await readConfig(flags
|
|
9
|
-
const rpcPort = Number(flags
|
|
8
|
+
const cfg = await readConfig(flags.config as string | undefined);
|
|
9
|
+
const rpcPort = Number(flags.port ?? cfg.server.rpcPort ?? 8899);
|
|
10
10
|
const wsPort = Number(flags["ws-port"] ?? cfg.server.wsPort ?? rpcPort + 1);
|
|
11
11
|
const host =
|
|
12
|
-
flags
|
|
13
|
-
? "0.0.0.0"
|
|
14
|
-
: (flags["host"] as string) || "127.0.0.1";
|
|
12
|
+
flags.network === true ? "0.0.0.0" : (flags.host as string) || "127.0.0.1";
|
|
15
13
|
const dbMode =
|
|
16
14
|
(flags["db-mode"] as string) || cfg.server.db.mode || "ephemeral";
|
|
17
15
|
const dbPath =
|
|
@@ -20,7 +18,7 @@ export async function rpcStartCommand(args: string[]) {
|
|
|
20
18
|
const guiEnabled =
|
|
21
19
|
flags["no-gui"] === true
|
|
22
20
|
? false
|
|
23
|
-
: flags
|
|
21
|
+
: flags.gui === true
|
|
24
22
|
? true
|
|
25
23
|
: cfg.gui.enabled !== false;
|
|
26
24
|
|
|
@@ -28,10 +26,12 @@ export async function rpcStartCommand(args: string[]) {
|
|
|
28
26
|
const guiMsg = guiEnabled ? `, GUI on ${guiPort}` : "";
|
|
29
27
|
s.start(`Starting RPC on ${host}:${rpcPort}, WS on ${wsPort}${guiMsg}...`);
|
|
30
28
|
try {
|
|
29
|
+
const mode: "ephemeral" | "persistent" =
|
|
30
|
+
dbMode === "persistent" ? "persistent" : "ephemeral";
|
|
31
31
|
const started = startRpcServers({
|
|
32
32
|
rpcPort,
|
|
33
33
|
wsPort,
|
|
34
|
-
dbMode:
|
|
34
|
+
dbMode: mode,
|
|
35
35
|
dbPath,
|
|
36
36
|
host,
|
|
37
37
|
guiEnabled,
|
|
@@ -5,7 +5,7 @@ import { parseFlags } from "../utils/args";
|
|
|
5
5
|
// Set the faucet as mint authority for an existing mint in LiteSVM (local-only)
|
|
6
6
|
export async function tokenAdoptAuthorityCommand(args: string[]) {
|
|
7
7
|
const { flags, rest } = parseFlags(args);
|
|
8
|
-
const mint = (rest[0] as string) || (flags
|
|
8
|
+
const mint = (rest[0] as string) || (flags.mint as string);
|
|
9
9
|
if (!mint) {
|
|
10
10
|
p.log.error("Usage: solforge token adopt-authority <mint>");
|
|
11
11
|
return;
|
|
@@ -8,7 +8,7 @@ import { parseFlags } from "../utils/args";
|
|
|
8
8
|
// solforge token clone <mint> --to <owner> --ui-amount <num> --decimals <d>
|
|
9
9
|
export async function tokenCloneCommand(args: string[]) {
|
|
10
10
|
const { flags, rest } = parseFlags(args);
|
|
11
|
-
const mint = ((rest[0] as string) || (flags
|
|
11
|
+
const mint = ((rest[0] as string) || (flags.mint as string) || "").trim();
|
|
12
12
|
if (!mint) {
|
|
13
13
|
p.log.error(
|
|
14
14
|
"Usage: solforge token clone <mint> [--amount <baseUnits> | --ui-amount <num>] [--endpoint URL]",
|
|
@@ -16,10 +16,10 @@ export async function tokenCloneCommand(args: string[]) {
|
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
const configPath = flags
|
|
19
|
+
const _owner = flags.to as string | undefined; // optional; defaults to faucet on server
|
|
20
|
+
const configPath = flags.config as string | undefined;
|
|
21
21
|
const cfg = await readConfig(configPath);
|
|
22
|
-
const endpoint = (flags
|
|
22
|
+
const endpoint = (flags.endpoint as string) || cfg.clone.endpoint;
|
|
23
23
|
const url = `http://localhost:${cfg.server.rpcPort}`;
|
|
24
24
|
const s = p.spinner();
|
|
25
25
|
s.start("Cloning mint into LiteSVM...");
|
|
@@ -75,7 +75,7 @@ export async function tokenCloneCommand(args: string[]) {
|
|
|
75
75
|
),
|
|
76
76
|
);
|
|
77
77
|
return;
|
|
78
|
-
} catch (adoptErr:
|
|
78
|
+
} catch (adoptErr: unknown) {
|
|
79
79
|
p.log.warn(
|
|
80
80
|
`Adopt authority failed: ${adoptErr?.message || String(adoptErr)}`,
|
|
81
81
|
);
|
|
@@ -85,7 +85,6 @@ export async function tokenCloneCommand(args: string[]) {
|
|
|
85
85
|
);
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
|
-
return;
|
|
89
88
|
} catch (e) {
|
|
90
89
|
s.stop("Clone failed");
|
|
91
90
|
p.log.error(String(e));
|
|
@@ -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);
|