solforge 0.2.4 → 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/README.md +471 -79
- package/cli.cjs +106 -78
- package/package.json +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/postinstall.cjs +69 -61
- 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 +61 -35
- 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 +15 -8
- package/src/api-server-entry.ts +91 -91
- 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 +8 -5
- 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 +38 -37
- package/src/cli/run-solforge.ts +20 -6
- package/src/cli/setup-wizard.ts +8 -6
- package/src/commands/add-program.ts +324 -328
- package/src/commands/init.ts +106 -106
- package/src/commands/list.ts +125 -125
- package/src/commands/mint.ts +247 -248
- package/src/commands/start.ts +837 -833
- package/src/commands/status.ts +80 -80
- package/src/commands/stop.ts +381 -382
- package/src/config/index.ts +33 -17
- package/src/config/manager.ts +150 -150
- package/src/db/index.ts +2 -2
- package/src/db/tx-store.ts +12 -8
- package/src/gui/public/app.css +1556 -1
- package/src/gui/public/build/main.css +1569 -1
- package/src/gui/server.ts +21 -22
- package/src/gui/src/api.ts +1 -1
- package/src/gui/src/app.tsx +96 -45
- package/src/gui/src/components/airdrop-mint-form.tsx +49 -19
- package/src/gui/src/components/clone-program-modal.tsx +31 -12
- package/src/gui/src/components/clone-token-modal.tsx +32 -13
- package/src/gui/src/components/modal.tsx +18 -11
- package/src/gui/src/components/programs-panel.tsx +27 -15
- package/src/gui/src/components/status-panel.tsx +32 -18
- package/src/gui/src/components/tokens-panel.tsx +25 -19
- package/src/gui/src/index.css +491 -463
- package/src/index.ts +177 -149
- package/src/rpc/start.ts +1 -1
- package/src/services/api-server.ts +494 -475
- package/src/services/port-manager.ts +164 -167
- package/src/services/process-registry.ts +144 -145
- package/src/services/program-cloner.ts +312 -312
- package/src/services/token-cloner.ts +799 -797
- package/src/services/validator.ts +288 -290
- package/src/types/config.ts +72 -72
- package/src/utils/shell.ts +75 -75
- package/src/utils/token-loader.ts +78 -78
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
|
@@ -6,6 +6,7 @@ type Sub = { id: number; type: "signature"; signature: string };
|
|
|
6
6
|
export function createLiteSVMWebSocketServer(
|
|
7
7
|
rpcServer: LiteSVMRpcServer,
|
|
8
8
|
port: number = 8900,
|
|
9
|
+
host?: string,
|
|
9
10
|
) {
|
|
10
11
|
let nextSubId = 1;
|
|
11
12
|
const subs = new Map<number, Sub>();
|
|
@@ -13,7 +14,11 @@ export function createLiteSVMWebSocketServer(
|
|
|
13
14
|
const sockets = new Set<WebSocket>();
|
|
14
15
|
const pendingChecks = new Map<string, number>();
|
|
15
16
|
|
|
16
|
-
const sendSignatureNotification = (
|
|
17
|
+
const sendSignatureNotification = (
|
|
18
|
+
sig: string,
|
|
19
|
+
slot: number,
|
|
20
|
+
err: unknown,
|
|
21
|
+
) => {
|
|
17
22
|
const payload = {
|
|
18
23
|
jsonrpc: "2.0",
|
|
19
24
|
method: "signatureNotification",
|
|
@@ -24,14 +29,14 @@ export function createLiteSVMWebSocketServer(
|
|
|
24
29
|
for (const [id, sub] of subs.entries()) {
|
|
25
30
|
if (sub.type === "signature" && sub.signature === sig) {
|
|
26
31
|
try {
|
|
27
|
-
|
|
32
|
+
for (const s of sockets) {
|
|
28
33
|
s.send(
|
|
29
34
|
JSON.stringify({
|
|
30
35
|
...payload,
|
|
31
36
|
params: { ...payload.params, subscription: id },
|
|
32
37
|
}),
|
|
33
|
-
)
|
|
34
|
-
|
|
38
|
+
);
|
|
39
|
+
}
|
|
35
40
|
} catch {}
|
|
36
41
|
subs.delete(id);
|
|
37
42
|
}
|
|
@@ -67,8 +72,9 @@ export function createLiteSVMWebSocketServer(
|
|
|
67
72
|
|
|
68
73
|
const server: Server = Bun.serve({
|
|
69
74
|
port,
|
|
75
|
+
hostname: host || process.env.RPC_HOST || "127.0.0.1",
|
|
70
76
|
fetch(req, srv) {
|
|
71
|
-
if (srv.upgrade(req)) return undefined as
|
|
77
|
+
if (srv.upgrade(req)) return undefined as unknown as Response;
|
|
72
78
|
return new Response("Not a websocket", { status: 400 });
|
|
73
79
|
},
|
|
74
80
|
websocket: {
|
|
@@ -89,7 +95,7 @@ export function createLiteSVMWebSocketServer(
|
|
|
89
95
|
id,
|
|
90
96
|
method,
|
|
91
97
|
params = [],
|
|
92
|
-
} = msg as { id: number; method: string; params?:
|
|
98
|
+
} = msg as { id: number; method: string; params?: unknown[] };
|
|
93
99
|
if (method === "signatureSubscribe") {
|
|
94
100
|
const [signature] = params;
|
|
95
101
|
const subId = nextSubId++;
|
|
@@ -145,7 +151,7 @@ export function createLiteSVMWebSocketServer(
|
|
|
145
151
|
error: { code: -32601, message: `Method not found: ${method}` },
|
|
146
152
|
}),
|
|
147
153
|
);
|
|
148
|
-
} catch (
|
|
154
|
+
} catch (_e) {
|
|
149
155
|
try {
|
|
150
156
|
ws.send(
|
|
151
157
|
JSON.stringify({
|
|
@@ -160,7 +166,8 @@ export function createLiteSVMWebSocketServer(
|
|
|
160
166
|
},
|
|
161
167
|
});
|
|
162
168
|
|
|
163
|
-
|
|
169
|
+
const hostname = (host || process.env.RPC_HOST || "127.0.0.1").toString();
|
|
170
|
+
console.log(`📣 LiteSVM RPC PubSub running on ws://${hostname}:${port}`);
|
|
164
171
|
return {
|
|
165
172
|
wsServer: server,
|
|
166
173
|
stop: () => {
|
package/src/api-server-entry.ts
CHANGED
|
@@ -1,109 +1,109 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { APIServer } from "./services/api-server.js";
|
|
4
|
-
import { configManager } from "./config/manager.js";
|
|
5
3
|
import chalk from "chalk";
|
|
4
|
+
import { configManager } from "./config/manager.js";
|
|
5
|
+
import { APIServer } from "./services/api-server.js";
|
|
6
6
|
|
|
7
7
|
async function main() {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
try {
|
|
9
|
+
// Parse command line arguments
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
const portIndex = args.indexOf("--port");
|
|
12
|
+
const hostIndex = args.indexOf("--host");
|
|
13
|
+
const configIndex = args.indexOf("--config");
|
|
14
|
+
const rpcIndex = args.indexOf("--rpc-url");
|
|
15
|
+
const faucetIndex = args.indexOf("--faucet-url");
|
|
16
|
+
const workDirIndex = args.indexOf("--work-dir");
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
18
|
+
if (
|
|
19
|
+
portIndex === -1 ||
|
|
20
|
+
configIndex === -1 ||
|
|
21
|
+
rpcIndex === -1 ||
|
|
22
|
+
faucetIndex === -1 ||
|
|
23
|
+
workDirIndex === -1 ||
|
|
24
|
+
!args[portIndex + 1] ||
|
|
25
|
+
!args[configIndex + 1] ||
|
|
26
|
+
!args[rpcIndex + 1] ||
|
|
27
|
+
!args[faucetIndex + 1] ||
|
|
28
|
+
!args[workDirIndex + 1]
|
|
29
|
+
) {
|
|
30
|
+
console.error(
|
|
31
|
+
"Usage: api-server-entry --port <port> --config <config-path> --rpc-url <url> --faucet-url <url> --work-dir <dir> [--host <host>]",
|
|
32
|
+
);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
const port = parseInt(String(args[portIndex + 1]), 10);
|
|
37
|
+
const host =
|
|
38
|
+
hostIndex !== -1 && args[hostIndex + 1] ? args[hostIndex + 1] : undefined;
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
// Load configuration
|
|
45
|
+
await configManager.load(configPath);
|
|
46
|
+
const config = configManager.getConfig();
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
// Create and start API server
|
|
49
|
+
const apiServer = new APIServer({
|
|
50
|
+
port,
|
|
51
|
+
host,
|
|
52
|
+
validatorRpcUrl: rpcUrl,
|
|
53
|
+
validatorFaucetUrl: faucetUrl,
|
|
54
|
+
config,
|
|
55
|
+
workDir,
|
|
56
|
+
});
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
const result = await apiServer.start();
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
if (result.success) {
|
|
61
|
+
console.log(chalk.green(`🚀 API Server started on port ${port}`));
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
// Keep the process alive
|
|
64
|
+
process.on("SIGTERM", async () => {
|
|
65
|
+
console.log(
|
|
66
|
+
chalk.yellow("📡 API Server received SIGTERM, shutting down..."),
|
|
67
|
+
);
|
|
68
|
+
await apiServer.stop();
|
|
69
|
+
process.exit(0);
|
|
70
|
+
});
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
process.on("SIGINT", async () => {
|
|
73
|
+
console.log(
|
|
74
|
+
chalk.yellow("📡 API Server received SIGINT, shutting down..."),
|
|
75
|
+
);
|
|
76
|
+
await apiServer.stop();
|
|
77
|
+
process.exit(0);
|
|
78
|
+
});
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
80
|
+
// Keep process alive
|
|
81
|
+
setInterval(() => {}, 1000);
|
|
82
|
+
} else {
|
|
83
|
+
console.error(
|
|
84
|
+
chalk.red(`❌ Failed to start API server: ${result.error}`),
|
|
85
|
+
);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error(
|
|
90
|
+
chalk.red(
|
|
91
|
+
`❌ API Server error: ${
|
|
92
|
+
error instanceof Error ? error.message : String(error)
|
|
93
|
+
}`,
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
main().catch((error) => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
console.error(
|
|
102
|
+
chalk.red(
|
|
103
|
+
`❌ Fatal error: ${
|
|
104
|
+
error instanceof Error ? error.message : String(error)
|
|
105
|
+
}`,
|
|
106
|
+
),
|
|
107
|
+
);
|
|
108
|
+
process.exit(1);
|
|
109
109
|
});
|
|
@@ -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,10 +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
|
-
const host =
|
|
11
|
+
const host =
|
|
12
|
+
flags.network === true ? "0.0.0.0" : (flags.host as string) || "127.0.0.1";
|
|
12
13
|
const dbMode =
|
|
13
14
|
(flags["db-mode"] as string) || cfg.server.db.mode || "ephemeral";
|
|
14
15
|
const dbPath =
|
|
@@ -17,7 +18,7 @@ export async function rpcStartCommand(args: string[]) {
|
|
|
17
18
|
const guiEnabled =
|
|
18
19
|
flags["no-gui"] === true
|
|
19
20
|
? false
|
|
20
|
-
: flags
|
|
21
|
+
: flags.gui === true
|
|
21
22
|
? true
|
|
22
23
|
: cfg.gui.enabled !== false;
|
|
23
24
|
|
|
@@ -25,10 +26,12 @@ export async function rpcStartCommand(args: string[]) {
|
|
|
25
26
|
const guiMsg = guiEnabled ? `, GUI on ${guiPort}` : "";
|
|
26
27
|
s.start(`Starting RPC on ${host}:${rpcPort}, WS on ${wsPort}${guiMsg}...`);
|
|
27
28
|
try {
|
|
29
|
+
const mode: "ephemeral" | "persistent" =
|
|
30
|
+
dbMode === "persistent" ? "persistent" : "ephemeral";
|
|
28
31
|
const started = startRpcServers({
|
|
29
32
|
rpcPort,
|
|
30
33
|
wsPort,
|
|
31
|
-
dbMode:
|
|
34
|
+
dbMode: mode,
|
|
32
35
|
dbPath,
|
|
33
36
|
host,
|
|
34
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;
|