solforge 0.2.12 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -5
- package/start.cjs +19 -23
- package/docs/API.md +0 -379
- package/docs/CONFIGURATION.md +0 -407
- package/docs/bun-single-file-executable.md +0 -585
- package/docs/cli-plan.md +0 -154
- package/docs/data-indexing-plan.md +0 -214
- package/docs/gui-roadmap.md +0 -202
- package/scripts/decode-b58.ts +0 -10
- package/scripts/install.sh +0 -112
- package/server/index.ts +0 -5
- package/server/lib/base58.ts +0 -33
- package/server/lib/faucet.ts +0 -110
- package/server/lib/instruction-parser.ts +0 -328
- package/server/lib/parsers/spl-associated-token-account.ts +0 -50
- package/server/lib/parsers/spl-token.ts +0 -340
- package/server/lib/spl-token.ts +0 -57
- package/server/methods/TEMPLATE.md +0 -117
- package/server/methods/account/get-account-info.ts +0 -86
- package/server/methods/account/get-balance.ts +0 -23
- package/server/methods/account/get-multiple-accounts.ts +0 -84
- package/server/methods/account/get-parsed-account-info.ts +0 -17
- package/server/methods/account/index.ts +0 -12
- package/server/methods/account/parsers/index.ts +0 -52
- package/server/methods/account/parsers/loader-upgradeable.ts +0 -79
- package/server/methods/account/parsers/spl-token.ts +0 -256
- package/server/methods/account/parsers/system.ts +0 -4
- package/server/methods/account/request-airdrop.ts +0 -271
- package/server/methods/admin/adopt-mint-authority.ts +0 -94
- package/server/methods/admin/clone-program-accounts.ts +0 -55
- package/server/methods/admin/clone-program.ts +0 -152
- package/server/methods/admin/clone-token-accounts.ts +0 -117
- package/server/methods/admin/clone-token-mint.ts +0 -82
- package/server/methods/admin/create-mint.ts +0 -114
- package/server/methods/admin/create-token-account.ts +0 -137
- package/server/methods/admin/helpers.ts +0 -70
- package/server/methods/admin/index.ts +0 -10
- package/server/methods/admin/list-mints.ts +0 -21
- package/server/methods/admin/load-program.ts +0 -52
- package/server/methods/admin/mint-to.ts +0 -266
- package/server/methods/block/get-block-height.ts +0 -5
- package/server/methods/block/get-block.ts +0 -31
- package/server/methods/block/get-blocks-with-limit.ts +0 -19
- package/server/methods/block/get-latest-blockhash.ts +0 -12
- package/server/methods/block/get-slot.ts +0 -5
- package/server/methods/block/index.ts +0 -6
- package/server/methods/block/is-blockhash-valid.ts +0 -19
- package/server/methods/epoch/get-cluster-nodes.ts +0 -17
- package/server/methods/epoch/get-epoch-info.ts +0 -16
- package/server/methods/epoch/get-epoch-schedule.ts +0 -15
- package/server/methods/epoch/get-highest-snapshot-slot.ts +0 -9
- package/server/methods/epoch/get-leader-schedule.ts +0 -8
- package/server/methods/epoch/get-max-retransmit-slot.ts +0 -9
- package/server/methods/epoch/get-max-shred-insert-slot.ts +0 -9
- package/server/methods/epoch/get-slot-leader.ts +0 -6
- package/server/methods/epoch/get-slot-leaders.ts +0 -9
- package/server/methods/epoch/get-stake-activation.ts +0 -9
- package/server/methods/epoch/get-stake-minimum-delegation.ts +0 -9
- package/server/methods/epoch/get-vote-accounts.ts +0 -19
- package/server/methods/epoch/index.ts +0 -13
- package/server/methods/epoch/minimum-ledger-slot.ts +0 -5
- package/server/methods/fee/get-fee-calculator-for-blockhash.ts +0 -12
- package/server/methods/fee/get-fee-for-message.ts +0 -8
- package/server/methods/fee/get-fee-rate-governor.ts +0 -16
- package/server/methods/fee/get-fees.ts +0 -14
- package/server/methods/fee/get-recent-prioritization-fees.ts +0 -22
- package/server/methods/fee/index.ts +0 -5
- package/server/methods/get-address-lookup-table.ts +0 -27
- package/server/methods/index.ts +0 -265
- package/server/methods/performance/get-recent-performance-samples.ts +0 -25
- package/server/methods/performance/get-transaction-count.ts +0 -5
- package/server/methods/performance/index.ts +0 -2
- package/server/methods/program/get-block-commitment.ts +0 -9
- package/server/methods/program/get-block-production.ts +0 -14
- package/server/methods/program/get-block-time.ts +0 -21
- package/server/methods/program/get-blocks.ts +0 -11
- package/server/methods/program/get-first-available-block.ts +0 -9
- package/server/methods/program/get-genesis-hash.ts +0 -6
- package/server/methods/program/get-identity.ts +0 -6
- package/server/methods/program/get-inflation-governor.ts +0 -15
- package/server/methods/program/get-inflation-rate.ts +0 -10
- package/server/methods/program/get-inflation-reward.ts +0 -12
- package/server/methods/program/get-largest-accounts.ts +0 -8
- package/server/methods/program/get-parsed-program-accounts.ts +0 -12
- package/server/methods/program/get-parsed-token-accounts-by-delegate.ts +0 -12
- package/server/methods/program/get-parsed-token-accounts-by-owner.ts +0 -12
- package/server/methods/program/get-program-accounts.ts +0 -221
- package/server/methods/program/get-supply.ts +0 -13
- package/server/methods/program/get-token-account-balance.ts +0 -60
- package/server/methods/program/get-token-accounts-by-delegate.ts +0 -82
- package/server/methods/program/get-token-accounts-by-owner.ts +0 -416
- package/server/methods/program/get-token-largest-accounts.ts +0 -81
- package/server/methods/program/get-token-supply.ts +0 -39
- package/server/methods/program/index.ts +0 -21
- package/server/methods/solforge/index.ts +0 -158
- package/server/methods/system/get-health.ts +0 -5
- package/server/methods/system/get-minimum-balance-for-rent-exemption.ts +0 -13
- package/server/methods/system/get-version.ts +0 -9
- package/server/methods/system/index.ts +0 -3
- package/server/methods/transaction/get-confirmed-transaction.ts +0 -11
- package/server/methods/transaction/get-parsed-transaction.ts +0 -17
- package/server/methods/transaction/get-signature-statuses.ts +0 -79
- package/server/methods/transaction/get-signatures-for-address.ts +0 -41
- package/server/methods/transaction/get-transaction.ts +0 -639
- package/server/methods/transaction/index.ts +0 -7
- package/server/methods/transaction/inner-instructions.test.ts +0 -104
- package/server/methods/transaction/send-transaction.ts +0 -469
- package/server/methods/transaction/simulate-transaction.ts +0 -57
- package/server/rpc-server.ts +0 -521
- package/server/types.ts +0 -109
- package/server/ws-server.ts +0 -178
- package/src/api-server-entry.ts +0 -109
- package/src/cli/bootstrap.ts +0 -67
- package/src/cli/commands/airdrop.ts +0 -37
- package/src/cli/commands/config.ts +0 -39
- package/src/cli/commands/mint.ts +0 -187
- package/src/cli/commands/program-clone.ts +0 -122
- package/src/cli/commands/program-load.ts +0 -64
- package/src/cli/commands/rpc-start.ts +0 -49
- package/src/cli/commands/token-adopt-authority.ts +0 -37
- package/src/cli/commands/token-clone.ts +0 -112
- package/src/cli/commands/token-create.ts +0 -81
- package/src/cli/main.ts +0 -158
- package/src/cli/run-solforge.ts +0 -112
- package/src/cli/setup-utils.ts +0 -54
- package/src/cli/setup-wizard.ts +0 -258
- package/src/cli/utils/args.ts +0 -15
- package/src/commands/add-program.ts +0 -333
- package/src/commands/init.ts +0 -122
- package/src/commands/list.ts +0 -136
- package/src/commands/mint.ts +0 -287
- package/src/commands/start.ts +0 -881
- package/src/commands/status.ts +0 -99
- package/src/commands/stop.ts +0 -405
- package/src/config/index.ts +0 -146
- package/src/config/manager.ts +0 -157
- package/src/db/index.ts +0 -83
- package/src/db/schema/accounts.ts +0 -23
- package/src/db/schema/address-signatures.ts +0 -31
- package/src/db/schema/index.ts +0 -6
- package/src/db/schema/meta-kv.ts +0 -9
- package/src/db/schema/transactions.ts +0 -36
- package/src/db/schema/tx-account-states.ts +0 -23
- package/src/db/schema/tx-accounts.ts +0 -33
- package/src/db/tx-store.ts +0 -264
- package/src/gui/public/app.css +0 -1556
- package/src/gui/public/build/main.css +0 -1569
- package/src/gui/public/build/main.js +0 -303
- package/src/gui/public/build/main.js.txt +0 -231
- package/src/gui/public/index.html +0 -19
- package/src/gui/server.ts +0 -296
- package/src/gui/src/api.ts +0 -127
- package/src/gui/src/app.tsx +0 -441
- package/src/gui/src/components/airdrop-mint-form.tsx +0 -246
- package/src/gui/src/components/clone-program-modal.tsx +0 -202
- package/src/gui/src/components/clone-token-modal.tsx +0 -230
- package/src/gui/src/components/modal.tsx +0 -134
- package/src/gui/src/components/programs-panel.tsx +0 -124
- package/src/gui/src/components/status-panel.tsx +0 -136
- package/src/gui/src/components/tokens-panel.tsx +0 -122
- package/src/gui/src/hooks/use-interval.ts +0 -17
- package/src/gui/src/index.css +0 -557
- package/src/gui/src/main.tsx +0 -17
- package/src/index.ts +0 -216
- package/src/migrations-bundled.ts +0 -23
- package/src/rpc/start.ts +0 -44
- package/src/services/api-server.ts +0 -504
- package/src/services/port-manager.ts +0 -174
- package/src/services/process-registry.ts +0 -153
- package/src/services/program-cloner.ts +0 -317
- package/src/services/token-cloner.ts +0 -811
- package/src/services/validator.ts +0 -293
- package/src/types/config.ts +0 -110
- package/src/utils/shell.ts +0 -110
- package/src/utils/token-loader.ts +0 -115
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
|
-
import { readConfig, writeConfig } from "../../config";
|
|
3
|
-
import { parseFlags } from "../utils/args";
|
|
4
|
-
|
|
5
|
-
export async function programCloneCommand(args: string[]) {
|
|
6
|
-
const { flags, rest } = parseFlags(args);
|
|
7
|
-
const programId = (
|
|
8
|
-
(rest[0] as string) ||
|
|
9
|
-
(flags.program as string) ||
|
|
10
|
-
""
|
|
11
|
-
).trim();
|
|
12
|
-
const configPath = flags.config as string | undefined;
|
|
13
|
-
const cfg = await readConfig(configPath);
|
|
14
|
-
const endpoint = (flags.endpoint as string) || cfg.clone.endpoint;
|
|
15
|
-
const withAccounts = !!flags["with-accounts"];
|
|
16
|
-
const accountsLimit = flags["accounts-limit"]
|
|
17
|
-
? Number(flags["accounts-limit"])
|
|
18
|
-
: undefined;
|
|
19
|
-
if (!programId) {
|
|
20
|
-
p.log.error(
|
|
21
|
-
"Usage: solforge program clone <programId> [--endpoint URL] [--with-accounts] [--accounts-limit N]",
|
|
22
|
-
);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const url = `http://localhost:${cfg.server.rpcPort}`;
|
|
26
|
-
const s = p.spinner();
|
|
27
|
-
s.start("Cloning program...");
|
|
28
|
-
try {
|
|
29
|
-
const res = await fetch(url, {
|
|
30
|
-
method: "POST",
|
|
31
|
-
headers: { "content-type": "application/json" },
|
|
32
|
-
body: JSON.stringify({
|
|
33
|
-
jsonrpc: "2.0",
|
|
34
|
-
id: 1,
|
|
35
|
-
method: "solforgeAdminCloneProgram",
|
|
36
|
-
params: [programId, { endpoint, withAccounts, accountsLimit }],
|
|
37
|
-
}),
|
|
38
|
-
});
|
|
39
|
-
const json = await res.json();
|
|
40
|
-
if (json.error) {
|
|
41
|
-
const details = json.error.data
|
|
42
|
-
? `\nDetails: ${JSON.stringify(json.error.data)}`
|
|
43
|
-
: "";
|
|
44
|
-
throw new Error((json.error.message || "program clone failed") + details);
|
|
45
|
-
}
|
|
46
|
-
s.stop("Program cloned");
|
|
47
|
-
console.log(JSON.stringify(json.result, null, 2));
|
|
48
|
-
await recordProgramClone(configPath, programId);
|
|
49
|
-
} catch (e) {
|
|
50
|
-
s.stop("Clone failed");
|
|
51
|
-
p.log.error(String(e));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export async function programAccountsCloneCommand(args: string[]) {
|
|
56
|
-
const { flags, rest } = parseFlags(args);
|
|
57
|
-
const programId = (
|
|
58
|
-
(rest[0] as string) ||
|
|
59
|
-
(flags.program as string) ||
|
|
60
|
-
""
|
|
61
|
-
).trim();
|
|
62
|
-
const configPath = flags.config as string | undefined;
|
|
63
|
-
const cfg = await readConfig(configPath);
|
|
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;
|
|
67
|
-
if (!programId) {
|
|
68
|
-
p.log.error(
|
|
69
|
-
"Usage: solforge program accounts clone <programId> [--endpoint URL] [--limit N] [--filters JSON]",
|
|
70
|
-
);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
const url = `http://localhost:${cfg.server.rpcPort}`;
|
|
74
|
-
const s = p.spinner();
|
|
75
|
-
s.start("Cloning program accounts...");
|
|
76
|
-
try {
|
|
77
|
-
const res = await fetch(url, {
|
|
78
|
-
method: "POST",
|
|
79
|
-
headers: { "content-type": "application/json" },
|
|
80
|
-
body: JSON.stringify({
|
|
81
|
-
jsonrpc: "2.0",
|
|
82
|
-
id: 1,
|
|
83
|
-
method: "solforgeAdminCloneProgramAccounts",
|
|
84
|
-
params: [programId, { endpoint, limit, filters }],
|
|
85
|
-
}),
|
|
86
|
-
});
|
|
87
|
-
const json = await res.json();
|
|
88
|
-
if (json.error)
|
|
89
|
-
throw new Error(json.error.message || "program accounts clone failed");
|
|
90
|
-
s.stop("Program accounts cloned");
|
|
91
|
-
console.log(JSON.stringify(json.result, null, 2));
|
|
92
|
-
} catch (e) {
|
|
93
|
-
s.stop("Clone failed");
|
|
94
|
-
p.log.error(String(e));
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function safeJson(s: string): unknown {
|
|
99
|
-
try {
|
|
100
|
-
return JSON.parse(s);
|
|
101
|
-
} catch {
|
|
102
|
-
return undefined;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async function recordProgramClone(
|
|
107
|
-
configPath: string | undefined,
|
|
108
|
-
programId: string,
|
|
109
|
-
) {
|
|
110
|
-
try {
|
|
111
|
-
const cfg = await readConfig(configPath);
|
|
112
|
-
const next = new Set(cfg.clone.programs ?? []);
|
|
113
|
-
if (!next.has(programId)) {
|
|
114
|
-
next.add(programId);
|
|
115
|
-
cfg.clone.programs = Array.from(next);
|
|
116
|
-
await writeConfig(cfg, configPath ?? "sf.config.json");
|
|
117
|
-
p.log.info(`Added ${programId} to clone programs in config`);
|
|
118
|
-
}
|
|
119
|
-
} catch (error) {
|
|
120
|
-
console.warn(`[config] Failed to update clone programs: ${String(error)}`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
|
-
import { Connection, PublicKey } from "@solana/web3.js";
|
|
3
|
-
import { readConfig } from "../../config";
|
|
4
|
-
import { parseFlags } from "../utils/args";
|
|
5
|
-
|
|
6
|
-
export async function programLoadCommand(args: string[]) {
|
|
7
|
-
const { flags, rest } = parseFlags(args);
|
|
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
|
-
if (!programId) {
|
|
12
|
-
p.log.error(
|
|
13
|
-
"Usage: solforge program load <programId> [--file PATH | --endpoint URL]",
|
|
14
|
-
);
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
let base64: string | undefined;
|
|
18
|
-
try {
|
|
19
|
-
if (fromFile) {
|
|
20
|
-
const bytes = await Bun.file(fromFile).arrayBuffer();
|
|
21
|
-
base64 = Buffer.from(bytes).toString("base64");
|
|
22
|
-
} else if (endpoint) {
|
|
23
|
-
// Fetch ProgramData from endpoint
|
|
24
|
-
const conn = new Connection(endpoint, "confirmed");
|
|
25
|
-
const pid = new PublicKey(programId);
|
|
26
|
-
const info = await conn.getAccountInfo(pid, "confirmed");
|
|
27
|
-
if (!info) throw new Error("Program account not found on endpoint");
|
|
28
|
-
// Program account should be upgradeable; fetch ProgramData and extract bytes after header
|
|
29
|
-
// Heuristic: delegate parsing to server if unsure. Here, try raw first.
|
|
30
|
-
base64 = Buffer.from(info.data as Buffer).toString("base64");
|
|
31
|
-
} else {
|
|
32
|
-
p.log.error("Either --file or --endpoint must be provided");
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
} catch (e) {
|
|
36
|
-
p.log.error(`Failed to read ELF: ${String(e)}`);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const cfg = await readConfig();
|
|
41
|
-
const url = `http://localhost:${cfg.server.rpcPort}`;
|
|
42
|
-
const s = p.spinner();
|
|
43
|
-
s.start("Loading program into LiteSVM...");
|
|
44
|
-
try {
|
|
45
|
-
const res = await fetch(url, {
|
|
46
|
-
method: "POST",
|
|
47
|
-
headers: { "content-type": "application/json" },
|
|
48
|
-
body: JSON.stringify({
|
|
49
|
-
jsonrpc: "2.0",
|
|
50
|
-
id: 1,
|
|
51
|
-
method: "solforgeLoadProgram",
|
|
52
|
-
params: [programId, base64],
|
|
53
|
-
}),
|
|
54
|
-
});
|
|
55
|
-
const json = await res.json();
|
|
56
|
-
if (json.error)
|
|
57
|
-
throw new Error(json.error.message || "program load failed");
|
|
58
|
-
s.stop("Program loaded");
|
|
59
|
-
console.log(JSON.stringify(json.result, null, 2));
|
|
60
|
-
} catch (e) {
|
|
61
|
-
s.stop("Load failed");
|
|
62
|
-
p.log.error(String(e));
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
|
-
import { readConfig } from "../../config";
|
|
3
|
-
import { startRpcServers } from "../../rpc/start";
|
|
4
|
-
import { parseFlags } from "../utils/args";
|
|
5
|
-
|
|
6
|
-
export async function rpcStartCommand(args: string[]) {
|
|
7
|
-
const { flags } = parseFlags(args);
|
|
8
|
-
const cfg = await readConfig(flags.config as string | undefined);
|
|
9
|
-
const rpcPort = Number(flags.port ?? cfg.server.rpcPort ?? 8899);
|
|
10
|
-
const wsPort = Number(flags["ws-port"] ?? cfg.server.wsPort ?? rpcPort + 1);
|
|
11
|
-
const host =
|
|
12
|
-
flags.network === true ? "0.0.0.0" : (flags.host as string) || "127.0.0.1";
|
|
13
|
-
const dbMode =
|
|
14
|
-
(flags["db-mode"] as string) || cfg.server.db.mode || "ephemeral";
|
|
15
|
-
const dbPath =
|
|
16
|
-
(flags["db-path"] as string) || cfg.server.db.path || ".solforge/db.db";
|
|
17
|
-
const guiPort = Number(flags["gui-port"] ?? cfg.gui.port ?? 42069);
|
|
18
|
-
const guiEnabled =
|
|
19
|
-
flags["no-gui"] === true
|
|
20
|
-
? false
|
|
21
|
-
: flags.gui === true
|
|
22
|
-
? true
|
|
23
|
-
: cfg.gui.enabled !== false;
|
|
24
|
-
|
|
25
|
-
const s = p.spinner();
|
|
26
|
-
const guiMsg = guiEnabled ? `, GUI on ${guiPort}` : "";
|
|
27
|
-
s.start(`Starting RPC on ${host}:${rpcPort}, WS on ${wsPort}${guiMsg}...`);
|
|
28
|
-
try {
|
|
29
|
-
const mode: "ephemeral" | "persistent" =
|
|
30
|
-
dbMode === "persistent" ? "persistent" : "ephemeral";
|
|
31
|
-
const started = startRpcServers({
|
|
32
|
-
rpcPort,
|
|
33
|
-
wsPort,
|
|
34
|
-
dbMode: mode,
|
|
35
|
-
dbPath,
|
|
36
|
-
host,
|
|
37
|
-
guiEnabled,
|
|
38
|
-
guiPort,
|
|
39
|
-
});
|
|
40
|
-
s.stop("RPC started");
|
|
41
|
-
console.log(`HTTP: http://${host}:${started.rpcPort}`);
|
|
42
|
-
console.log(`WS: ws://${host}:${started.wsPort}`);
|
|
43
|
-
if (started.guiPort) console.log(`GUI: http://${host}:${started.guiPort}`);
|
|
44
|
-
} catch (e) {
|
|
45
|
-
s.stop("Failed to start RPC");
|
|
46
|
-
p.log.error(String(e));
|
|
47
|
-
process.exitCode = 1;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
|
-
import { readConfig } from "../../config";
|
|
3
|
-
import { parseFlags } from "../utils/args";
|
|
4
|
-
|
|
5
|
-
// Set the faucet as mint authority for an existing mint in LiteSVM (local-only)
|
|
6
|
-
export async function tokenAdoptAuthorityCommand(args: string[]) {
|
|
7
|
-
const { flags, rest } = parseFlags(args);
|
|
8
|
-
const mint = (rest[0] as string) || (flags.mint as string);
|
|
9
|
-
if (!mint) {
|
|
10
|
-
p.log.error("Usage: solforge token adopt-authority <mint>");
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
const cfg = await readConfig();
|
|
14
|
-
const url = `http://localhost:${cfg.server.rpcPort}`;
|
|
15
|
-
const s = p.spinner();
|
|
16
|
-
s.start("Adopting faucet as mint authority...");
|
|
17
|
-
try {
|
|
18
|
-
const res = await fetch(url, {
|
|
19
|
-
method: "POST",
|
|
20
|
-
headers: { "content-type": "application/json" },
|
|
21
|
-
body: JSON.stringify({
|
|
22
|
-
jsonrpc: "2.0",
|
|
23
|
-
id: 1,
|
|
24
|
-
method: "solforgeAdoptMintAuthority",
|
|
25
|
-
params: [mint],
|
|
26
|
-
}),
|
|
27
|
-
});
|
|
28
|
-
const json = await res.json();
|
|
29
|
-
if (json.error)
|
|
30
|
-
throw new Error(json.error.message || "adopt authority failed");
|
|
31
|
-
s.stop("Authority updated");
|
|
32
|
-
console.log(JSON.stringify(json.result, null, 2));
|
|
33
|
-
} catch (e) {
|
|
34
|
-
s.stop("Failed");
|
|
35
|
-
p.log.error(String(e));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
|
-
import { readConfig, writeConfig } from "../../config";
|
|
3
|
-
import { parseFlags } from "../utils/args";
|
|
4
|
-
|
|
5
|
-
// Simplified token "clone": create an ATA locally with the requested amount.
|
|
6
|
-
// Usage:
|
|
7
|
-
// solforge token clone <mint> --to <owner> --amount <baseUnits>
|
|
8
|
-
// solforge token clone <mint> --to <owner> --ui-amount <num> --decimals <d>
|
|
9
|
-
export async function tokenCloneCommand(args: string[]) {
|
|
10
|
-
const { flags, rest } = parseFlags(args);
|
|
11
|
-
const mint = ((rest[0] as string) || (flags.mint as string) || "").trim();
|
|
12
|
-
if (!mint) {
|
|
13
|
-
p.log.error(
|
|
14
|
-
"Usage: solforge token clone <mint> [--amount <baseUnits> | --ui-amount <num>] [--endpoint URL]",
|
|
15
|
-
);
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const _owner = flags.to as string | undefined; // optional; defaults to faucet on server
|
|
20
|
-
const configPath = flags.config as string | undefined;
|
|
21
|
-
const cfg = await readConfig(configPath);
|
|
22
|
-
const endpoint = (flags.endpoint as string) || cfg.clone.endpoint;
|
|
23
|
-
const url = `http://localhost:${cfg.server.rpcPort}`;
|
|
24
|
-
const s = p.spinner();
|
|
25
|
-
s.start("Cloning mint into LiteSVM...");
|
|
26
|
-
try {
|
|
27
|
-
// First, mirror the mint account so downstream reads work
|
|
28
|
-
const resMint = await fetch(url, {
|
|
29
|
-
method: "POST",
|
|
30
|
-
headers: { "content-type": "application/json" },
|
|
31
|
-
body: JSON.stringify({
|
|
32
|
-
jsonrpc: "2.0",
|
|
33
|
-
id: 1,
|
|
34
|
-
method: "solforgeAdminCloneTokenMint",
|
|
35
|
-
params: [mint, { endpoint }],
|
|
36
|
-
}),
|
|
37
|
-
});
|
|
38
|
-
const jsonMint = await resMint.json();
|
|
39
|
-
if (jsonMint.error) {
|
|
40
|
-
const data = jsonMint.error.data
|
|
41
|
-
? `\nDetails: ${JSON.stringify(jsonMint.error.data)}`
|
|
42
|
-
: "";
|
|
43
|
-
throw new Error((jsonMint.error.message || "clone mint failed") + data);
|
|
44
|
-
}
|
|
45
|
-
// Record mint in config immediately after a successful clone
|
|
46
|
-
await recordTokenClone(configPath, mint);
|
|
47
|
-
// Try to adopt faucet as mint authority for local usage (do not fail the command if this step fails)
|
|
48
|
-
try {
|
|
49
|
-
s.message("Adopting faucet as authority...");
|
|
50
|
-
const resAdopt = await fetch(url, {
|
|
51
|
-
method: "POST",
|
|
52
|
-
headers: { "content-type": "application/json" },
|
|
53
|
-
body: JSON.stringify({
|
|
54
|
-
jsonrpc: "2.0",
|
|
55
|
-
id: 3,
|
|
56
|
-
method: "solforgeAdoptMintAuthority",
|
|
57
|
-
params: [mint],
|
|
58
|
-
}),
|
|
59
|
-
});
|
|
60
|
-
const jsonAdopt = await resAdopt.json();
|
|
61
|
-
if (jsonAdopt.error) {
|
|
62
|
-
p.log.warn(jsonAdopt.error.message || "adopt authority failed");
|
|
63
|
-
s.stop("Mint cloned (authority unchanged)");
|
|
64
|
-
console.log(
|
|
65
|
-
JSON.stringify({ mint: jsonMint.result, adopt: null }, null, 2),
|
|
66
|
-
);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
s.stop("Mint cloned and authority adopted");
|
|
70
|
-
console.log(
|
|
71
|
-
JSON.stringify(
|
|
72
|
-
{ mint: jsonMint.result, adopt: jsonAdopt.result },
|
|
73
|
-
null,
|
|
74
|
-
2,
|
|
75
|
-
),
|
|
76
|
-
);
|
|
77
|
-
return;
|
|
78
|
-
} catch (adoptErr: unknown) {
|
|
79
|
-
p.log.warn(
|
|
80
|
-
`Adopt authority failed: ${adoptErr?.message || String(adoptErr)}`,
|
|
81
|
-
);
|
|
82
|
-
s.stop("Mint cloned (authority unchanged)");
|
|
83
|
-
console.log(
|
|
84
|
-
JSON.stringify({ mint: jsonMint.result, adopt: null }, null, 2),
|
|
85
|
-
);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
} catch (e) {
|
|
89
|
-
s.stop("Clone failed");
|
|
90
|
-
p.log.error(String(e));
|
|
91
|
-
p.log.info(
|
|
92
|
-
"Token clone mirrors an on-chain mint and requires network access to --endpoint. For an offline token, use 'solforge token create'.",
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// intentionally no UI conversion here; clone mirrors the on-chain mint only
|
|
98
|
-
|
|
99
|
-
async function recordTokenClone(configPath: string | undefined, mint: string) {
|
|
100
|
-
try {
|
|
101
|
-
const cfg = await readConfig(configPath);
|
|
102
|
-
const next = new Set(cfg.clone.tokens ?? []);
|
|
103
|
-
if (!next.has(mint)) {
|
|
104
|
-
next.add(mint);
|
|
105
|
-
cfg.clone.tokens = Array.from(next);
|
|
106
|
-
await writeConfig(cfg, configPath ?? "sf.config.json");
|
|
107
|
-
p.log.info(`Added ${mint} to clone tokens in config`);
|
|
108
|
-
}
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.warn(`[config] Failed to update clone tokens: ${String(error)}`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
|
-
import { readConfig } from "../../config";
|
|
3
|
-
import { parseFlags } from "../utils/args";
|
|
4
|
-
|
|
5
|
-
// Create a new local SPL mint with given decimals and (optional) owner authority.
|
|
6
|
-
// Also optionally create the owner's ATA with a starting balance (base units or UI amount).
|
|
7
|
-
// Usage:
|
|
8
|
-
// solforge token create --decimals <d> --owner <pubkey> [--mint <pubkey>] [--amount <baseUnits> | --ui-amount <num>]
|
|
9
|
-
export async function tokenCreateCommand(args: string[]) {
|
|
10
|
-
const { flags } = parseFlags(args);
|
|
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
|
-
const uiAmount = flags["ui-amount"] as string | undefined;
|
|
16
|
-
|
|
17
|
-
if (!Number.isFinite(decimals)) {
|
|
18
|
-
p.log.error("--decimals is required (0-18)");
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (!owner) {
|
|
22
|
-
p.log.error("--owner <pubkey> is required");
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const cfg = await readConfig();
|
|
27
|
-
const url = `http://localhost:${cfg.server.rpcPort}`;
|
|
28
|
-
const s = p.spinner();
|
|
29
|
-
s.start("Creating mint...");
|
|
30
|
-
try {
|
|
31
|
-
const res = await fetch(url, {
|
|
32
|
-
method: "POST",
|
|
33
|
-
headers: { "content-type": "application/json" },
|
|
34
|
-
body: JSON.stringify({
|
|
35
|
-
jsonrpc: "2.0",
|
|
36
|
-
id: 1,
|
|
37
|
-
method: "solforgeCreateMint",
|
|
38
|
-
params: [mint ?? null, Number(decimals), owner],
|
|
39
|
-
}),
|
|
40
|
-
});
|
|
41
|
-
const json = await res.json();
|
|
42
|
-
if (json.error) throw new Error(json.error.message || "create mint failed");
|
|
43
|
-
const createdMint = json.result.mint as string;
|
|
44
|
-
|
|
45
|
-
if (amountBase || uiAmount) {
|
|
46
|
-
s.message("Creating owner ATA with balance...");
|
|
47
|
-
const base =
|
|
48
|
-
amountBase ??
|
|
49
|
-
(uiAmount ? toBaseUnits(uiAmount, Number(decimals)) : undefined);
|
|
50
|
-
const res2 = await fetch(url, {
|
|
51
|
-
method: "POST",
|
|
52
|
-
headers: { "content-type": "application/json" },
|
|
53
|
-
body: JSON.stringify({
|
|
54
|
-
jsonrpc: "2.0",
|
|
55
|
-
id: 2,
|
|
56
|
-
method: "solforgeCreateTokenAccount",
|
|
57
|
-
params: [createdMint, owner, String(base)],
|
|
58
|
-
}),
|
|
59
|
-
});
|
|
60
|
-
const json2 = await res2.json();
|
|
61
|
-
if (json2.error)
|
|
62
|
-
throw new Error(json2.error.message || "create token account failed");
|
|
63
|
-
s.stop("Token created");
|
|
64
|
-
console.log(
|
|
65
|
-
JSON.stringify({ mint: json.result, account: json2.result }, null, 2),
|
|
66
|
-
);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
s.stop("Mint created");
|
|
70
|
-
console.log(JSON.stringify(json.result, null, 2));
|
|
71
|
-
} catch (e) {
|
|
72
|
-
s.stop("Create failed");
|
|
73
|
-
p.log.error(String(e));
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function toBaseUnits(ui: string, decimals: number): string {
|
|
78
|
-
const [i, f = ""] = String(ui).split(".");
|
|
79
|
-
const frac = (f + "0".repeat(decimals)).slice(0, decimals);
|
|
80
|
-
return BigInt(i + (decimals ? frac : "")).toString();
|
|
81
|
-
}
|
package/src/cli/main.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
// Minimal, fast CLI router with @clack/prompts for UX
|
|
2
|
-
import * as p from "@clack/prompts";
|
|
3
|
-
import { readFileSync } from "fs";
|
|
4
|
-
import { join, dirname } from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
|
|
7
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const pkgPath = join(__dirname, "../../package.json");
|
|
9
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
10
|
-
const VERSION = pkg.version;
|
|
11
|
-
|
|
12
|
-
// Robust arg parsing for both bun script and compiled binary
|
|
13
|
-
const known = new Set([
|
|
14
|
-
"help",
|
|
15
|
-
"-h",
|
|
16
|
-
"--help",
|
|
17
|
-
"version",
|
|
18
|
-
"-v",
|
|
19
|
-
"--version",
|
|
20
|
-
"rpc",
|
|
21
|
-
"start",
|
|
22
|
-
"config",
|
|
23
|
-
"airdrop",
|
|
24
|
-
"mint",
|
|
25
|
-
"token",
|
|
26
|
-
"program",
|
|
27
|
-
]);
|
|
28
|
-
const raw = Bun.argv.slice(1);
|
|
29
|
-
const firstIdx = raw.findIndex((a) => known.has(String(a)));
|
|
30
|
-
const argv = firstIdx >= 0 ? raw.slice(firstIdx) : [];
|
|
31
|
-
|
|
32
|
-
async function main() {
|
|
33
|
-
const [cmd, sub, ...rest] = argv;
|
|
34
|
-
|
|
35
|
-
if (!cmd) {
|
|
36
|
-
const { runSolforge } = await import("./run-solforge");
|
|
37
|
-
// Pass through any flags provided when no explicit command was given
|
|
38
|
-
await runSolforge(raw);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (cmd === "help" || cmd === "-h" || cmd === "--help") {
|
|
43
|
-
printHelp();
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (cmd === "version" || cmd === "-v" || cmd === "--version") {
|
|
48
|
-
printVersion();
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Alias: solforge start -> solforge rpc start
|
|
53
|
-
if (cmd === "start") {
|
|
54
|
-
// Run the full Solforge flow (config ensure + bootstrap + servers)
|
|
55
|
-
const { runSolforge } = await import("./run-solforge");
|
|
56
|
-
await runSolforge(rest);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
switch (cmd) {
|
|
61
|
-
case "rpc": {
|
|
62
|
-
if (sub === "start") {
|
|
63
|
-
const { rpcStartCommand } = await import("./commands/rpc-start");
|
|
64
|
-
return rpcStartCommand(rest);
|
|
65
|
-
}
|
|
66
|
-
return unknownCommand([cmd, sub]);
|
|
67
|
-
}
|
|
68
|
-
case "config": {
|
|
69
|
-
const { configCommand } = await import("./commands/config");
|
|
70
|
-
return configCommand(sub, rest);
|
|
71
|
-
}
|
|
72
|
-
case "airdrop": {
|
|
73
|
-
const { airdropCommand } = await import("./commands/airdrop");
|
|
74
|
-
return airdropCommand(rest);
|
|
75
|
-
}
|
|
76
|
-
case "mint": {
|
|
77
|
-
const { mintCommand } = await import("./commands/mint");
|
|
78
|
-
return mintCommand(rest);
|
|
79
|
-
}
|
|
80
|
-
case "token": {
|
|
81
|
-
if (sub === "clone") {
|
|
82
|
-
const { tokenCloneCommand } = await import("./commands/token-clone");
|
|
83
|
-
return tokenCloneCommand(rest);
|
|
84
|
-
}
|
|
85
|
-
if (sub === "create") {
|
|
86
|
-
const { tokenCreateCommand } = await import("./commands/token-create");
|
|
87
|
-
return tokenCreateCommand(rest);
|
|
88
|
-
}
|
|
89
|
-
if (sub === "adopt-authority") {
|
|
90
|
-
const { tokenAdoptAuthorityCommand } = await import(
|
|
91
|
-
"./commands/token-adopt-authority"
|
|
92
|
-
);
|
|
93
|
-
return tokenAdoptAuthorityCommand(rest);
|
|
94
|
-
}
|
|
95
|
-
return unknownCommand([cmd, sub]);
|
|
96
|
-
}
|
|
97
|
-
case "program": {
|
|
98
|
-
if (sub === "clone") {
|
|
99
|
-
const { programCloneCommand } = await import(
|
|
100
|
-
"./commands/program-clone"
|
|
101
|
-
);
|
|
102
|
-
return programCloneCommand(rest);
|
|
103
|
-
}
|
|
104
|
-
if (sub === "load") {
|
|
105
|
-
const { programLoadCommand } = await import("./commands/program-load");
|
|
106
|
-
return programLoadCommand(rest);
|
|
107
|
-
}
|
|
108
|
-
if (sub === "accounts") {
|
|
109
|
-
const [_, __, ...tail] = argv.slice(2); // re-read to check deep subcommand
|
|
110
|
-
if (tail[0] === "clone") {
|
|
111
|
-
const { programAccountsCloneCommand } = await import(
|
|
112
|
-
"./commands/program-clone"
|
|
113
|
-
);
|
|
114
|
-
return programAccountsCloneCommand(tail.slice(1));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return unknownCommand([cmd, sub]);
|
|
118
|
-
}
|
|
119
|
-
default:
|
|
120
|
-
return unknownCommand([cmd, sub]);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function printHelp() {
|
|
125
|
-
console.log(`
|
|
126
|
-
solforge <command>
|
|
127
|
-
|
|
128
|
-
Commands:
|
|
129
|
-
(no command) Run setup then start RPC & WS servers
|
|
130
|
-
rpc start Start RPC & WS servers
|
|
131
|
-
start Alias for 'rpc start'
|
|
132
|
-
config init Create sf.config.json in CWD
|
|
133
|
-
config get <key> Read a config value (dot path)
|
|
134
|
-
config set <k> <v> Set a config value
|
|
135
|
-
airdrop --to <pubkey> --sol <amount> Airdrop SOL via RPC faucet
|
|
136
|
-
mint Interactive: pick mint, receiver, amount
|
|
137
|
-
token clone <mint> Clone SPL token mint + accounts
|
|
138
|
-
program clone <programId> Clone program code (and optionally accounts)
|
|
139
|
-
program accounts clone <programId> Clone accounts owned by program
|
|
140
|
-
|
|
141
|
-
Options:
|
|
142
|
-
-h, --help Show help
|
|
143
|
-
-v, --version Show version
|
|
144
|
-
--network Bind servers to 0.0.0.0 (LAN access)
|
|
145
|
-
-y, --ci Non-interactive; auto-accept prompts (use existing config)
|
|
146
|
-
`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async function unknownCommand(parts: (string | undefined)[]) {
|
|
150
|
-
p.log.error(`Unknown command: ${parts.filter(Boolean).join(" ")}`);
|
|
151
|
-
printHelp();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function printVersion() {
|
|
155
|
-
console.log(String(VERSION));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
main();
|