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,153 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
|
|
5
|
-
export interface RunningValidator {
|
|
6
|
-
id: string;
|
|
7
|
-
name: string;
|
|
8
|
-
pid: number;
|
|
9
|
-
rpcPort: number;
|
|
10
|
-
faucetPort: number;
|
|
11
|
-
rpcUrl: string;
|
|
12
|
-
faucetUrl: string;
|
|
13
|
-
configPath: string;
|
|
14
|
-
startTime: Date;
|
|
15
|
-
status: "running" | "stopped" | "error";
|
|
16
|
-
apiServerPort?: number;
|
|
17
|
-
apiServerUrl?: string;
|
|
18
|
-
apiServerPid?: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class ProcessRegistry {
|
|
22
|
-
private registryPath: string;
|
|
23
|
-
|
|
24
|
-
constructor() {
|
|
25
|
-
// Store registry in user's home directory
|
|
26
|
-
this.registryPath = join(homedir(), ".solforge", "running-validators.json");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Get all running validators
|
|
31
|
-
*/
|
|
32
|
-
getRunning(): RunningValidator[] {
|
|
33
|
-
if (!existsSync(this.registryPath)) {
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
const content = readFileSync(this.registryPath, "utf-8");
|
|
39
|
-
const validators = JSON.parse(content) as RunningValidator[];
|
|
40
|
-
|
|
41
|
-
// Convert startTime strings back to Date objects
|
|
42
|
-
return validators.map((v) => ({
|
|
43
|
-
...v,
|
|
44
|
-
startTime: new Date(v.startTime),
|
|
45
|
-
}));
|
|
46
|
-
} catch {
|
|
47
|
-
return [];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Register a new running validator
|
|
53
|
-
*/
|
|
54
|
-
register(validator: RunningValidator): void {
|
|
55
|
-
const validators = this.getRunning();
|
|
56
|
-
|
|
57
|
-
// Remove any existing entry with the same ID
|
|
58
|
-
const updated = validators.filter((v) => v.id !== validator.id);
|
|
59
|
-
updated.push(validator);
|
|
60
|
-
|
|
61
|
-
this.save(updated);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Unregister a validator
|
|
66
|
-
*/
|
|
67
|
-
unregister(id: string): void {
|
|
68
|
-
const validators = this.getRunning();
|
|
69
|
-
const updated = validators.filter((v) => v.id !== id);
|
|
70
|
-
this.save(updated);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Update validator status
|
|
75
|
-
*/
|
|
76
|
-
updateStatus(id: string, status: RunningValidator["status"]): void {
|
|
77
|
-
const validators = this.getRunning();
|
|
78
|
-
const validator = validators.find((v) => v.id === id);
|
|
79
|
-
|
|
80
|
-
if (validator) {
|
|
81
|
-
validator.status = status;
|
|
82
|
-
this.save(validators);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Get validator by ID
|
|
88
|
-
*/
|
|
89
|
-
getById(id: string): RunningValidator | undefined {
|
|
90
|
-
return this.getRunning().find((v) => v.id === id);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Get validator by PID
|
|
95
|
-
*/
|
|
96
|
-
getByPid(pid: number): RunningValidator | undefined {
|
|
97
|
-
return this.getRunning().find((v) => v.pid === pid);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Get validator by port
|
|
102
|
-
*/
|
|
103
|
-
getByPort(port: number): RunningValidator | undefined {
|
|
104
|
-
return this.getRunning().find(
|
|
105
|
-
(v) => v.rpcPort === port || v.faucetPort === port,
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Check if a process is actually running
|
|
111
|
-
*/
|
|
112
|
-
async isProcessRunning(pid: number): Promise<boolean> {
|
|
113
|
-
try {
|
|
114
|
-
// Send signal 0 to check if process exists
|
|
115
|
-
process.kill(pid, 0);
|
|
116
|
-
return true;
|
|
117
|
-
} catch {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Clean up dead processes from registry
|
|
124
|
-
*/
|
|
125
|
-
async cleanup(): Promise<void> {
|
|
126
|
-
const validators = this.getRunning();
|
|
127
|
-
const active: RunningValidator[] = [];
|
|
128
|
-
|
|
129
|
-
for (const validator of validators) {
|
|
130
|
-
if (await this.isProcessRunning(validator.pid)) {
|
|
131
|
-
active.push(validator);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
this.save(active);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Save validators to registry file
|
|
140
|
-
*/
|
|
141
|
-
private save(validators: RunningValidator[]): void {
|
|
142
|
-
// Ensure directory exists
|
|
143
|
-
const dir = join(homedir(), ".solforge");
|
|
144
|
-
if (!existsSync(dir)) {
|
|
145
|
-
require("node:fs").mkdirSync(dir, { recursive: true });
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
writeFileSync(this.registryPath, JSON.stringify(validators, null, 2));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Singleton instance
|
|
153
|
-
export const processRegistry = new ProcessRegistry();
|
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
import { Connection, PublicKey } from "@solana/web3.js";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import type { ProgramConfig } from "../types/config.js";
|
|
6
|
-
import { runCommand } from "../utils/shell.js";
|
|
7
|
-
|
|
8
|
-
export class ProgramCloner {
|
|
9
|
-
private workDir: string;
|
|
10
|
-
|
|
11
|
-
constructor(workDir: string = ".solforge") {
|
|
12
|
-
this.workDir = workDir;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Clone programs for validator startup (saved as .so files)
|
|
17
|
-
*/
|
|
18
|
-
async clonePrograms(
|
|
19
|
-
programs: ProgramConfig[],
|
|
20
|
-
targetCluster: string = "mainnet-beta",
|
|
21
|
-
): Promise<
|
|
22
|
-
Array<{
|
|
23
|
-
success: boolean;
|
|
24
|
-
program: ProgramConfig;
|
|
25
|
-
error?: string;
|
|
26
|
-
filePath?: string;
|
|
27
|
-
}>
|
|
28
|
-
> {
|
|
29
|
-
console.log(chalk.cyan("\nš§ Cloning programs from mainnet..."));
|
|
30
|
-
|
|
31
|
-
if (!existsSync(this.workDir)) {
|
|
32
|
-
mkdirSync(this.workDir, { recursive: true });
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const programsDir = join(this.workDir, "programs");
|
|
36
|
-
if (!existsSync(programsDir)) {
|
|
37
|
-
mkdirSync(programsDir, { recursive: true });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const results = [];
|
|
41
|
-
|
|
42
|
-
for (const program of programs) {
|
|
43
|
-
console.log(
|
|
44
|
-
chalk.gray(
|
|
45
|
-
` š¦ Processing ${program.name || program.mainnetProgramId}...`,
|
|
46
|
-
),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
// Clone dependencies first
|
|
51
|
-
if (program.dependencies && program.dependencies.length > 0) {
|
|
52
|
-
console.log(
|
|
53
|
-
chalk.gray(
|
|
54
|
-
` š Cloning ${program.dependencies.length} dependencies...`,
|
|
55
|
-
),
|
|
56
|
-
);
|
|
57
|
-
for (const depId of program.dependencies) {
|
|
58
|
-
await this.cloneSingleProgram(depId, programsDir, targetCluster);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Clone the main program
|
|
63
|
-
const result = await this.cloneSingleProgram(
|
|
64
|
-
program.mainnetProgramId,
|
|
65
|
-
programsDir,
|
|
66
|
-
targetCluster,
|
|
67
|
-
program.name,
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
results.push({
|
|
71
|
-
success: true,
|
|
72
|
-
program,
|
|
73
|
-
filePath: result.filePath,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
console.log(chalk.gray(` ā Cloned to ${result.filePath}`));
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.error(
|
|
79
|
-
chalk.red(` ā Failed to clone ${program.mainnetProgramId}`),
|
|
80
|
-
);
|
|
81
|
-
console.error(
|
|
82
|
-
chalk.red(
|
|
83
|
-
` ${error instanceof Error ? error.message : String(error)}`,
|
|
84
|
-
),
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
results.push({
|
|
88
|
-
success: false,
|
|
89
|
-
program,
|
|
90
|
-
error: error instanceof Error ? error.message : String(error),
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const successful = results.filter((r) => r.success).length;
|
|
96
|
-
console.log(
|
|
97
|
-
chalk.cyan(`\nā
Cloned ${successful}/${programs.length} programs`),
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
return results;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Clone a single program from mainnet
|
|
105
|
-
*/
|
|
106
|
-
private async cloneSingleProgram(
|
|
107
|
-
programId: string,
|
|
108
|
-
outputDir: string,
|
|
109
|
-
cluster: string = "mainnet-beta",
|
|
110
|
-
name?: string,
|
|
111
|
-
): Promise<{ filePath: string }> {
|
|
112
|
-
const fileName = name
|
|
113
|
-
? `${name.toLowerCase().replace(/\s+/g, "-")}.so`
|
|
114
|
-
: `${programId}.so`;
|
|
115
|
-
const outputPath = join(outputDir, fileName);
|
|
116
|
-
|
|
117
|
-
// Skip if already exists
|
|
118
|
-
if (existsSync(outputPath)) {
|
|
119
|
-
return { filePath: outputPath };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Use solana account command to fetch program data
|
|
123
|
-
const rpcUrl = this.getClusterUrl(cluster);
|
|
124
|
-
const accountResult = await runCommand(
|
|
125
|
-
"solana",
|
|
126
|
-
["account", programId, "--output", "json", "--url", rpcUrl],
|
|
127
|
-
{ silent: true },
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
if (!accountResult.success) {
|
|
131
|
-
throw new Error(
|
|
132
|
-
`Failed to fetch program account: ${accountResult.stderr}`,
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
const accountData = JSON.parse(accountResult.stdout);
|
|
138
|
-
const programData = accountData.account.data;
|
|
139
|
-
|
|
140
|
-
if (!programData || programData[1] !== "base64") {
|
|
141
|
-
throw new Error("Invalid program data format");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Decode base64 program data
|
|
145
|
-
const binaryData = Buffer.from(programData[0], "base64");
|
|
146
|
-
|
|
147
|
-
// Write as .so file
|
|
148
|
-
writeFileSync(outputPath, binaryData);
|
|
149
|
-
|
|
150
|
-
return { filePath: outputPath };
|
|
151
|
-
} catch (error) {
|
|
152
|
-
throw new Error(
|
|
153
|
-
`Failed to process program data: ${
|
|
154
|
-
error instanceof Error ? error.message : String(error)
|
|
155
|
-
}`,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Generate validator arguments for cloned programs
|
|
162
|
-
*/
|
|
163
|
-
generateValidatorArgs(
|
|
164
|
-
clonedPrograms: Array<{
|
|
165
|
-
success: boolean;
|
|
166
|
-
program: ProgramConfig;
|
|
167
|
-
filePath?: string;
|
|
168
|
-
}>,
|
|
169
|
-
): string[] {
|
|
170
|
-
const args: string[] = [];
|
|
171
|
-
|
|
172
|
-
for (const result of clonedPrograms) {
|
|
173
|
-
if (result.success && result.filePath) {
|
|
174
|
-
args.push("--bpf-program");
|
|
175
|
-
args.push(result.program.mainnetProgramId);
|
|
176
|
-
args.push(result.filePath);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return args;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Deploy program to running validator (hot deployment)
|
|
185
|
-
*/
|
|
186
|
-
async deployToRunningValidator(
|
|
187
|
-
programId: string,
|
|
188
|
-
rpcUrl: string,
|
|
189
|
-
name?: string,
|
|
190
|
-
): Promise<{ success: boolean; deployedAddress?: string; error?: string }> {
|
|
191
|
-
try {
|
|
192
|
-
console.log(
|
|
193
|
-
chalk.cyan(`\nš Hot deploying program ${name || programId}...`),
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
// First, clone the program if we don't have it
|
|
197
|
-
const programsDir = join(this.workDir, "programs");
|
|
198
|
-
if (!existsSync(programsDir)) {
|
|
199
|
-
mkdirSync(programsDir, { recursive: true });
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const cloneResult = await this.cloneSingleProgram(
|
|
203
|
-
programId,
|
|
204
|
-
programsDir,
|
|
205
|
-
"mainnet-beta",
|
|
206
|
-
name,
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
// Deploy to running validator using solana program deploy
|
|
210
|
-
console.log(chalk.gray(" š¤ Deploying to validator..."));
|
|
211
|
-
|
|
212
|
-
const deployResult = await runCommand(
|
|
213
|
-
"solana",
|
|
214
|
-
[
|
|
215
|
-
"program",
|
|
216
|
-
"deploy",
|
|
217
|
-
cloneResult.filePath,
|
|
218
|
-
"--program-id",
|
|
219
|
-
programId,
|
|
220
|
-
"--url",
|
|
221
|
-
rpcUrl,
|
|
222
|
-
],
|
|
223
|
-
{ silent: false },
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
if (!deployResult.success) {
|
|
227
|
-
return {
|
|
228
|
-
success: false,
|
|
229
|
-
error: `Deployment failed: ${
|
|
230
|
-
deployResult.stderr || deployResult.stdout
|
|
231
|
-
}`,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
console.log(
|
|
236
|
-
chalk.green(` ā
Successfully deployed ${name || programId}`),
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
return {
|
|
240
|
-
success: true,
|
|
241
|
-
deployedAddress: programId,
|
|
242
|
-
};
|
|
243
|
-
} catch (error) {
|
|
244
|
-
return {
|
|
245
|
-
success: false,
|
|
246
|
-
error: error instanceof Error ? error.message : String(error),
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Get cluster RPC URL
|
|
253
|
-
*/
|
|
254
|
-
private getClusterUrl(cluster: string): string {
|
|
255
|
-
switch (cluster) {
|
|
256
|
-
case "mainnet-beta":
|
|
257
|
-
return "https://api.mainnet-beta.solana.com";
|
|
258
|
-
case "devnet":
|
|
259
|
-
return "https://api.devnet.solana.com";
|
|
260
|
-
case "testnet":
|
|
261
|
-
return "https://api.testnet.solana.com";
|
|
262
|
-
default:
|
|
263
|
-
return cluster; // Assume it's a custom URL
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Verify program exists on cluster
|
|
269
|
-
*/
|
|
270
|
-
async verifyProgram(
|
|
271
|
-
programId: string,
|
|
272
|
-
cluster: string = "mainnet-beta",
|
|
273
|
-
): Promise<boolean> {
|
|
274
|
-
try {
|
|
275
|
-
const connection = new Connection(this.getClusterUrl(cluster));
|
|
276
|
-
const programAccount = await connection.getAccountInfo(
|
|
277
|
-
new PublicKey(programId),
|
|
278
|
-
);
|
|
279
|
-
return programAccount?.executable;
|
|
280
|
-
} catch {
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Get program info from cluster
|
|
287
|
-
*/
|
|
288
|
-
async getProgramInfo(
|
|
289
|
-
programId: string,
|
|
290
|
-
cluster: string = "mainnet-beta",
|
|
291
|
-
): Promise<{
|
|
292
|
-
exists: boolean;
|
|
293
|
-
executable?: boolean;
|
|
294
|
-
owner?: string;
|
|
295
|
-
size?: number;
|
|
296
|
-
}> {
|
|
297
|
-
try {
|
|
298
|
-
const connection = new Connection(this.getClusterUrl(cluster));
|
|
299
|
-
const programAccount = await connection.getAccountInfo(
|
|
300
|
-
new PublicKey(programId),
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
if (!programAccount) {
|
|
304
|
-
return { exists: false };
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return {
|
|
308
|
-
exists: true,
|
|
309
|
-
executable: programAccount.executable,
|
|
310
|
-
owner: programAccount.owner.toBase58(),
|
|
311
|
-
size: programAccount.data.length,
|
|
312
|
-
};
|
|
313
|
-
} catch (_error) {
|
|
314
|
-
return { exists: false };
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|