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
package/server/ws-server.ts
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import type { Server } from "bun";
|
|
2
|
-
import type { LiteSVMRpcServer } from "./rpc-server";
|
|
3
|
-
|
|
4
|
-
type Sub = { id: number; type: "signature"; signature: string };
|
|
5
|
-
|
|
6
|
-
export function createLiteSVMWebSocketServer(
|
|
7
|
-
rpcServer: LiteSVMRpcServer,
|
|
8
|
-
port: number = 8900,
|
|
9
|
-
host?: string,
|
|
10
|
-
) {
|
|
11
|
-
let nextSubId = 1;
|
|
12
|
-
const subs = new Map<number, Sub>();
|
|
13
|
-
|
|
14
|
-
const sockets = new Set<WebSocket>();
|
|
15
|
-
const pendingChecks = new Map<string, number>();
|
|
16
|
-
|
|
17
|
-
const sendSignatureNotification = (
|
|
18
|
-
sig: string,
|
|
19
|
-
slot: number,
|
|
20
|
-
err: unknown,
|
|
21
|
-
) => {
|
|
22
|
-
const payload = {
|
|
23
|
-
jsonrpc: "2.0",
|
|
24
|
-
method: "signatureNotification",
|
|
25
|
-
params: {
|
|
26
|
-
result: { context: { slot }, value: { err } },
|
|
27
|
-
},
|
|
28
|
-
} as const;
|
|
29
|
-
for (const [id, sub] of subs.entries()) {
|
|
30
|
-
if (sub.type === "signature" && sub.signature === sig) {
|
|
31
|
-
try {
|
|
32
|
-
for (const s of sockets) {
|
|
33
|
-
s.send(
|
|
34
|
-
JSON.stringify({
|
|
35
|
-
...payload,
|
|
36
|
-
params: { ...payload.params, subscription: id },
|
|
37
|
-
}),
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
} catch {}
|
|
41
|
-
subs.delete(id);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const scheduleSignatureCheck = (sig: string) => {
|
|
47
|
-
if (pendingChecks.has(sig)) return;
|
|
48
|
-
pendingChecks.set(sig, 0);
|
|
49
|
-
const tick = () => {
|
|
50
|
-
const tries = (pendingChecks.get(sig) ?? 0) + 1;
|
|
51
|
-
pendingChecks.set(sig, tries);
|
|
52
|
-
const status = rpcServer.getSignatureStatus(sig);
|
|
53
|
-
if (status) {
|
|
54
|
-
pendingChecks.delete(sig);
|
|
55
|
-
sendSignatureNotification(sig, status.slot, status.err);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
if (tries < 60) {
|
|
59
|
-
setTimeout(tick, 25);
|
|
60
|
-
} else {
|
|
61
|
-
pendingChecks.delete(sig);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
setTimeout(tick, 10);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const notifySignature = (sig: string) => {
|
|
68
|
-
scheduleSignatureCheck(sig);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const unsubscribe = rpcServer.onSignatureRecorded(notifySignature);
|
|
72
|
-
|
|
73
|
-
const server: Server = Bun.serve({
|
|
74
|
-
port,
|
|
75
|
-
hostname: host || process.env.RPC_HOST || "127.0.0.1",
|
|
76
|
-
fetch(req, srv) {
|
|
77
|
-
if (srv.upgrade(req)) return undefined as unknown as Response;
|
|
78
|
-
return new Response("Not a websocket", { status: 400 });
|
|
79
|
-
},
|
|
80
|
-
websocket: {
|
|
81
|
-
open(ws) {
|
|
82
|
-
sockets.add(ws);
|
|
83
|
-
},
|
|
84
|
-
close(ws) {
|
|
85
|
-
sockets.delete(ws);
|
|
86
|
-
},
|
|
87
|
-
message(ws, data) {
|
|
88
|
-
try {
|
|
89
|
-
const msg = JSON.parse(
|
|
90
|
-
typeof data === "string"
|
|
91
|
-
? data
|
|
92
|
-
: Buffer.from(data as ArrayBuffer).toString("utf8"),
|
|
93
|
-
);
|
|
94
|
-
const {
|
|
95
|
-
id,
|
|
96
|
-
method,
|
|
97
|
-
params = [],
|
|
98
|
-
} = msg as { id: number; method: string; params?: unknown[] };
|
|
99
|
-
if (method === "signatureSubscribe") {
|
|
100
|
-
const [signature] = params;
|
|
101
|
-
const subId = nextSubId++;
|
|
102
|
-
subs.set(subId, { id: subId, type: "signature", signature });
|
|
103
|
-
// Respond with subscription id
|
|
104
|
-
ws.send(JSON.stringify({ jsonrpc: "2.0", id, result: subId }));
|
|
105
|
-
// If already have a status, notify immediately
|
|
106
|
-
const status = rpcServer.getSignatureStatus(signature);
|
|
107
|
-
if (status) {
|
|
108
|
-
ws.send(
|
|
109
|
-
JSON.stringify({
|
|
110
|
-
jsonrpc: "2.0",
|
|
111
|
-
method: "signatureNotification",
|
|
112
|
-
params: {
|
|
113
|
-
result: {
|
|
114
|
-
context: { slot: status.slot },
|
|
115
|
-
value: { err: status.err },
|
|
116
|
-
},
|
|
117
|
-
subscription: subId,
|
|
118
|
-
},
|
|
119
|
-
}),
|
|
120
|
-
);
|
|
121
|
-
subs.delete(subId);
|
|
122
|
-
}
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (method === "signatureUnsubscribe") {
|
|
126
|
-
const [subId] = params;
|
|
127
|
-
subs.delete(subId);
|
|
128
|
-
ws.send(JSON.stringify({ jsonrpc: "2.0", id, result: true }));
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
// Stub other subs to succeed without notifications
|
|
132
|
-
if (
|
|
133
|
-
method === "logsSubscribe" ||
|
|
134
|
-
method === "slotSubscribe" ||
|
|
135
|
-
method === "programSubscribe" ||
|
|
136
|
-
method === "blockSubscribe"
|
|
137
|
-
) {
|
|
138
|
-
const subId = nextSubId++;
|
|
139
|
-
ws.send(JSON.stringify({ jsonrpc: "2.0", id, result: subId }));
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
if (method === "ping") {
|
|
143
|
-
ws.send(JSON.stringify({ jsonrpc: "2.0", id, result: null }));
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
// Method not found (ws)
|
|
147
|
-
ws.send(
|
|
148
|
-
JSON.stringify({
|
|
149
|
-
jsonrpc: "2.0",
|
|
150
|
-
id,
|
|
151
|
-
error: { code: -32601, message: `Method not found: ${method}` },
|
|
152
|
-
}),
|
|
153
|
-
);
|
|
154
|
-
} catch (_e) {
|
|
155
|
-
try {
|
|
156
|
-
ws.send(
|
|
157
|
-
JSON.stringify({
|
|
158
|
-
jsonrpc: "2.0",
|
|
159
|
-
id: null,
|
|
160
|
-
error: { code: -32700, message: "Parse error" },
|
|
161
|
-
}),
|
|
162
|
-
);
|
|
163
|
-
} catch {}
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
const hostname = (host || process.env.RPC_HOST || "127.0.0.1").toString();
|
|
170
|
-
console.log(`📣 LiteSVM RPC PubSub running on ws://${hostname}:${port}`);
|
|
171
|
-
return {
|
|
172
|
-
wsServer: server,
|
|
173
|
-
stop: () => {
|
|
174
|
-
unsubscribe();
|
|
175
|
-
server.stop(true);
|
|
176
|
-
},
|
|
177
|
-
};
|
|
178
|
-
}
|
package/src/api-server-entry.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import chalk from "chalk";
|
|
4
|
-
import { configManager } from "./config/manager.js";
|
|
5
|
-
import { APIServer } from "./services/api-server.js";
|
|
6
|
-
|
|
7
|
-
async function main() {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
-
// Load configuration
|
|
45
|
-
await configManager.load(configPath);
|
|
46
|
-
const config = configManager.getConfig();
|
|
47
|
-
|
|
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
|
-
|
|
58
|
-
const result = await apiServer.start();
|
|
59
|
-
|
|
60
|
-
if (result.success) {
|
|
61
|
-
console.log(chalk.green(`🚀 API Server started on port ${port}`));
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
99
|
-
|
|
100
|
-
main().catch((error) => {
|
|
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
|
-
});
|
package/src/cli/bootstrap.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
|
-
import { defaultConfig, type SolforgeConfig } from "../config";
|
|
3
|
-
|
|
4
|
-
export async function bootstrapEnvironment(
|
|
5
|
-
config: SolforgeConfig,
|
|
6
|
-
host: string,
|
|
7
|
-
rpcPort: number,
|
|
8
|
-
) {
|
|
9
|
-
const url = `http://${host}:${rpcPort}`;
|
|
10
|
-
const endpoint = config.clone.endpoint || defaultConfig.clone.endpoint;
|
|
11
|
-
|
|
12
|
-
for (const mint of config.clone.tokens || []) {
|
|
13
|
-
await withSpinner(`Cloning token ${mint}`, async () => {
|
|
14
|
-
await callRpc(url, "solforgeAdminCloneTokenMint", [mint, { endpoint }]);
|
|
15
|
-
await callRpc(url, "solforgeAdoptMintAuthority", [mint]);
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
for (const programId of config.clone.programs || []) {
|
|
20
|
-
await withSpinner(`Cloning program ${programId}`, async () => {
|
|
21
|
-
await callRpc(url, "solforgeAdminCloneProgram", [
|
|
22
|
-
programId,
|
|
23
|
-
{ endpoint },
|
|
24
|
-
]);
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
for (const { address, amountSol } of config.bootstrap?.airdrops || []) {
|
|
29
|
-
await withSpinner(
|
|
30
|
-
`Airdropping ${amountSol} SOL to ${address}`,
|
|
31
|
-
async () => {
|
|
32
|
-
const lamports = Math.round(amountSol * 1_000_000_000);
|
|
33
|
-
await callRpc(url, "requestAirdrop", [address, lamports]);
|
|
34
|
-
},
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function withSpinner(task: string, action: () => Promise<void>) {
|
|
40
|
-
const spin = p.spinner();
|
|
41
|
-
spin.start(`${task}...`);
|
|
42
|
-
try {
|
|
43
|
-
await action();
|
|
44
|
-
spin.stop(`${task} done`);
|
|
45
|
-
} catch (error) {
|
|
46
|
-
spin.stop(`${task} failed`);
|
|
47
|
-
p.log.error(String(error));
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function callRpc(url: string, method: string, params: unknown[]) {
|
|
52
|
-
const res = await fetch(url, {
|
|
53
|
-
method: "POST",
|
|
54
|
-
headers: { "content-type": "application/json" },
|
|
55
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: Date.now(), method, params }),
|
|
56
|
-
});
|
|
57
|
-
if (!res.ok) throw new Error(`${method} HTTP ${res.status}`);
|
|
58
|
-
const json = await res.json();
|
|
59
|
-
if (json?.error) {
|
|
60
|
-
const message = json.error?.message || `${method} failed`;
|
|
61
|
-
const detail = json.error?.data
|
|
62
|
-
? `: ${JSON.stringify(json.error.data)}`
|
|
63
|
-
: "";
|
|
64
|
-
throw new Error(message + detail);
|
|
65
|
-
}
|
|
66
|
-
return json.result;
|
|
67
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
|
-
import { readConfig } from "../../config";
|
|
3
|
-
import { parseFlags } from "../utils/args";
|
|
4
|
-
|
|
5
|
-
export async function airdropCommand(args: string[]) {
|
|
6
|
-
const { flags } = parseFlags(args);
|
|
7
|
-
const to = String(flags.to || "");
|
|
8
|
-
const sol = Number(flags.sol || 0);
|
|
9
|
-
const cfg = await readConfig();
|
|
10
|
-
const url = `http://localhost:${cfg.server.rpcPort}`;
|
|
11
|
-
if (!to || !sol) {
|
|
12
|
-
p.log.error("Usage: solforge airdrop --to <pubkey> --sol <amount>");
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
const lamports = BigInt(Math.floor(sol * 1_000_000_000));
|
|
16
|
-
const body = {
|
|
17
|
-
jsonrpc: "2.0",
|
|
18
|
-
id: 1,
|
|
19
|
-
method: "requestAirdrop",
|
|
20
|
-
params: [to, Number(lamports)],
|
|
21
|
-
};
|
|
22
|
-
const s = p.spinner();
|
|
23
|
-
s.start("Requesting airdrop...");
|
|
24
|
-
try {
|
|
25
|
-
const res = await fetch(url, {
|
|
26
|
-
method: "POST",
|
|
27
|
-
headers: { "content-type": "application/json" },
|
|
28
|
-
body: JSON.stringify(body),
|
|
29
|
-
});
|
|
30
|
-
const json = await res.json();
|
|
31
|
-
s.stop("Airdrop requested");
|
|
32
|
-
console.log(JSON.stringify(json, null, 2));
|
|
33
|
-
} catch (e) {
|
|
34
|
-
s.stop("Airdrop failed");
|
|
35
|
-
p.log.error(String(e));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
|
-
import {
|
|
3
|
-
getConfigValue,
|
|
4
|
-
readConfig,
|
|
5
|
-
setConfigValue,
|
|
6
|
-
writeDefaultConfig,
|
|
7
|
-
} from "../../config";
|
|
8
|
-
import { parseFlags } from "../utils/args";
|
|
9
|
-
|
|
10
|
-
export async function configCommand(sub: string | undefined, args: string[]) {
|
|
11
|
-
switch (sub) {
|
|
12
|
-
case "init": {
|
|
13
|
-
const { flags } = parseFlags(args);
|
|
14
|
-
const force = !!flags.force;
|
|
15
|
-
await writeDefaultConfig({ force });
|
|
16
|
-
p.log.success("Wrote sf.config.json");
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
case "get": {
|
|
20
|
-
const key = args[0];
|
|
21
|
-
const cfg = await readConfig();
|
|
22
|
-
console.log(getConfigValue(cfg, key));
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
case "set": {
|
|
26
|
-
const [key, value] = args;
|
|
27
|
-
const cfg = await readConfig();
|
|
28
|
-
const updated = setConfigValue(cfg, key, value);
|
|
29
|
-
await Bun.write(
|
|
30
|
-
"sf.config.json",
|
|
31
|
-
`${JSON.stringify(updated, null, 2)}\n`,
|
|
32
|
-
);
|
|
33
|
-
p.log.success(`Updated ${key}`);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
default:
|
|
37
|
-
p.log.error("Usage: solforge config <init|get|set>");
|
|
38
|
-
}
|
|
39
|
-
}
|
package/src/cli/commands/mint.ts
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
|
-
import { readConfig } from "../../config";
|
|
3
|
-
import { parseFlags } from "../utils/args";
|
|
4
|
-
// No network fetch; decimals are read from LiteSVM via RPC getTokenSupply
|
|
5
|
-
|
|
6
|
-
// Create/overwrite a token account (ATA) with a specified amount (base units)
|
|
7
|
-
// Usage: solforge mint --mint <mint> --to <owner> --amount <amount>
|
|
8
|
-
export async function mintCommand(args: string[]) {
|
|
9
|
-
const { flags } = parseFlags(args);
|
|
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
|
-
let uiAmount = flags["ui-amount"] as string | undefined; // preferred UI units
|
|
14
|
-
|
|
15
|
-
const cfg = await readConfig();
|
|
16
|
-
const url = `http://localhost:${cfg.server.rpcPort}`;
|
|
17
|
-
|
|
18
|
-
// Get known mints from server for selection
|
|
19
|
-
let knownMints: string[] = [];
|
|
20
|
-
try {
|
|
21
|
-
const resList = await fetch(url, {
|
|
22
|
-
method: "POST",
|
|
23
|
-
headers: { "content-type": "application/json" },
|
|
24
|
-
body: JSON.stringify({
|
|
25
|
-
jsonrpc: "2.0",
|
|
26
|
-
id: 1,
|
|
27
|
-
method: "solforgeListMints",
|
|
28
|
-
params: [],
|
|
29
|
-
}),
|
|
30
|
-
});
|
|
31
|
-
const j = await resList.json();
|
|
32
|
-
if (!j.error && Array.isArray(j.result)) knownMints = j.result;
|
|
33
|
-
} catch {}
|
|
34
|
-
|
|
35
|
-
if (!mint) {
|
|
36
|
-
if (knownMints.length > 0) {
|
|
37
|
-
const choice = (await p.select({
|
|
38
|
-
message: "Select mint",
|
|
39
|
-
options: knownMints.map((m) => ({ value: m, label: m })),
|
|
40
|
-
})) as string | symbol | null;
|
|
41
|
-
if (!choice || typeof choice !== "string") return;
|
|
42
|
-
mint = choice;
|
|
43
|
-
} else {
|
|
44
|
-
p.log.error("No known mints. Clone or create a token first.");
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Receiver (ATA owner) is required
|
|
50
|
-
if (!receiver) {
|
|
51
|
-
receiver = (await p.text({
|
|
52
|
-
message: "Receiver public key (ATA owner)",
|
|
53
|
-
placeholder: "<receiver public key>",
|
|
54
|
-
validate: (v) =>
|
|
55
|
-
v && v.length >= 32 ? undefined : "Enter a valid public key",
|
|
56
|
-
})) as string;
|
|
57
|
-
if (!receiver) return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Amount selection (prefer UI units)
|
|
61
|
-
if (!amountBase && !uiAmount) {
|
|
62
|
-
uiAmount = (await p.text({
|
|
63
|
-
message: "Amount (UI units)",
|
|
64
|
-
placeholder: "1000",
|
|
65
|
-
validate: (v) =>
|
|
66
|
-
v && !Number.isNaN(Number(v)) ? undefined : "Enter a number",
|
|
67
|
-
})) as string;
|
|
68
|
-
if (!uiAmount) return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// If UI amount provided, get decimals from LiteSVM via getTokenSupply
|
|
72
|
-
if (!amountBase && uiAmount) {
|
|
73
|
-
let decimals = 0;
|
|
74
|
-
try {
|
|
75
|
-
const res = await fetch(url, {
|
|
76
|
-
method: "POST",
|
|
77
|
-
headers: { "content-type": "application/json" },
|
|
78
|
-
body: JSON.stringify({
|
|
79
|
-
jsonrpc: "2.0",
|
|
80
|
-
id: 2,
|
|
81
|
-
method: "getTokenSupply",
|
|
82
|
-
params: [mint],
|
|
83
|
-
}),
|
|
84
|
-
});
|
|
85
|
-
const j = await res.json();
|
|
86
|
-
const d = j?.result?.value?.decimals ?? j?.result?.decimals;
|
|
87
|
-
if (typeof d === "number") decimals = d;
|
|
88
|
-
} catch {}
|
|
89
|
-
amountBase = toBaseUnits(uiAmount, decimals);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const s = p.spinner();
|
|
93
|
-
s.start("Minting via real transaction...");
|
|
94
|
-
try {
|
|
95
|
-
const res = await fetch(url, {
|
|
96
|
-
method: "POST",
|
|
97
|
-
headers: { "content-type": "application/json" },
|
|
98
|
-
body: JSON.stringify({
|
|
99
|
-
jsonrpc: "2.0",
|
|
100
|
-
id: 1,
|
|
101
|
-
method: "solforgeMintTo",
|
|
102
|
-
params: [mint, receiver, amountBase],
|
|
103
|
-
}),
|
|
104
|
-
});
|
|
105
|
-
const json = await res.json();
|
|
106
|
-
if (json.error) {
|
|
107
|
-
const msg = String(json.error.message || "mint failed");
|
|
108
|
-
// Offer admin fallback when faucet is not mint authority
|
|
109
|
-
if (/no faucet authority|authority/i.test(msg)) {
|
|
110
|
-
const choice = (await p.select({
|
|
111
|
-
message: "Mint authority is not faucet. Choose action:",
|
|
112
|
-
options: [
|
|
113
|
-
{
|
|
114
|
-
value: "adopt",
|
|
115
|
-
label: "Adopt faucet as authority (local) and mint (real tx)",
|
|
116
|
-
},
|
|
117
|
-
{ value: "admin", label: "Admin set-balance (no real tx)" },
|
|
118
|
-
{ value: "cancel", label: "Cancel" },
|
|
119
|
-
],
|
|
120
|
-
})) as "adopt" | "admin" | "cancel" | symbol | null;
|
|
121
|
-
if (!choice || choice === "cancel") throw new Error(msg);
|
|
122
|
-
if (choice === "adopt") {
|
|
123
|
-
s.message("Adopting faucet as authority...");
|
|
124
|
-
const resAdopt = await fetch(url, {
|
|
125
|
-
method: "POST",
|
|
126
|
-
headers: { "content-type": "application/json" },
|
|
127
|
-
body: JSON.stringify({
|
|
128
|
-
jsonrpc: "2.0",
|
|
129
|
-
id: 2,
|
|
130
|
-
method: "solforgeAdoptMintAuthority",
|
|
131
|
-
params: [mint],
|
|
132
|
-
}),
|
|
133
|
-
});
|
|
134
|
-
const jA = await resAdopt.json();
|
|
135
|
-
if (jA.error)
|
|
136
|
-
throw new Error(jA.error.message || "adopt authority failed");
|
|
137
|
-
s.message("Minting via real transaction...");
|
|
138
|
-
const resRetry = await fetch(url, {
|
|
139
|
-
method: "POST",
|
|
140
|
-
headers: { "content-type": "application/json" },
|
|
141
|
-
body: JSON.stringify({
|
|
142
|
-
jsonrpc: "2.0",
|
|
143
|
-
id: 3,
|
|
144
|
-
method: "solforgeMintTo",
|
|
145
|
-
params: [mint, receiver, amountBase],
|
|
146
|
-
}),
|
|
147
|
-
});
|
|
148
|
-
const jR = await resRetry.json();
|
|
149
|
-
if (jR.error) throw new Error(jR.error.message || "mint failed");
|
|
150
|
-
s.stop("Minted");
|
|
151
|
-
console.log(JSON.stringify(jR.result, null, 2));
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
if (choice === "admin") {
|
|
155
|
-
const res2 = await fetch(url, {
|
|
156
|
-
method: "POST",
|
|
157
|
-
headers: { "content-type": "application/json" },
|
|
158
|
-
body: JSON.stringify({
|
|
159
|
-
jsonrpc: "2.0",
|
|
160
|
-
id: 4,
|
|
161
|
-
method: "solforgeCreateTokenAccount",
|
|
162
|
-
params: [mint, receiver, amountBase],
|
|
163
|
-
}),
|
|
164
|
-
});
|
|
165
|
-
const json2 = await res2.json();
|
|
166
|
-
if (json2.error)
|
|
167
|
-
throw new Error(json2.error.message || "admin mint failed");
|
|
168
|
-
s.stop("Minted (admin)");
|
|
169
|
-
console.log(JSON.stringify(json2.result, null, 2));
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
throw new Error(msg);
|
|
174
|
-
}
|
|
175
|
-
s.stop("Minted");
|
|
176
|
-
console.log(JSON.stringify(json.result, null, 2));
|
|
177
|
-
} catch (e) {
|
|
178
|
-
s.stop("Mint failed");
|
|
179
|
-
p.log.error(String(e));
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function toBaseUnits(ui: string, decimals: number): string {
|
|
184
|
-
const [i, f = ""] = String(ui).split(".");
|
|
185
|
-
const frac = (f + "0".repeat(decimals)).slice(0, decimals);
|
|
186
|
-
return BigInt(i + (decimals ? frac : "")).toString();
|
|
187
|
-
}
|