genlayer 0.38.8 → 0.38.10
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/.eslintignore +2 -0
- package/.github/workflows/cli-docs.yml +124 -0
- package/.github/workflows/publish.yml +55 -0
- package/.github/workflows/smoke.yml +27 -0
- package/.github/workflows/validate-code.yml +51 -0
- package/.prettierignore +19 -0
- package/.prettierrc +12 -0
- package/.release-it.json +66 -0
- package/CHANGELOG.md +545 -0
- package/CLAUDE.md +55 -0
- package/CONTRIBUTING.md +117 -0
- package/dist/index.js +221 -62
- package/docs/api-references/_meta.json +9 -0
- package/docs/api-references/accounts/_meta.json +3 -0
- package/docs/api-references/accounts/account/create.mdx +19 -0
- package/docs/api-references/accounts/account/export.mdx +19 -0
- package/docs/api-references/accounts/account/import.mdx +22 -0
- package/docs/api-references/accounts/account/list.mdx +15 -0
- package/docs/api-references/accounts/account/lock.mdx +16 -0
- package/docs/api-references/accounts/account/remove.mdx +20 -0
- package/docs/api-references/accounts/account/send.mdx +24 -0
- package/docs/api-references/accounts/account/show.mdx +17 -0
- package/docs/api-references/accounts/account/unlock.mdx +17 -0
- package/docs/api-references/accounts/account/use.mdx +19 -0
- package/docs/api-references/accounts/account.mdx +32 -0
- package/docs/api-references/configuration/_meta.json +4 -0
- package/docs/api-references/configuration/config/get.mdx +21 -0
- package/docs/api-references/configuration/config/reset.mdx +21 -0
- package/docs/api-references/configuration/config/set.mdx +21 -0
- package/docs/api-references/configuration/config.mdx +25 -0
- package/docs/api-references/configuration/network/info.mdx +15 -0
- package/docs/api-references/configuration/network/list.mdx +15 -0
- package/docs/api-references/configuration/network/set.mdx +21 -0
- package/docs/api-references/configuration/network.mdx +25 -0
- package/docs/api-references/contracts/_meta.json +7 -0
- package/docs/api-references/contracts/call.mdx +21 -0
- package/docs/api-references/contracts/code.mdx +20 -0
- package/docs/api-references/contracts/deploy.mdx +17 -0
- package/docs/api-references/contracts/schema.mdx +20 -0
- package/docs/api-references/contracts/write.mdx +21 -0
- package/docs/api-references/environment/_meta.json +7 -0
- package/docs/api-references/environment/init.mdx +20 -0
- package/docs/api-references/environment/new.mdx +21 -0
- package/docs/api-references/environment/stop.mdx +15 -0
- package/docs/api-references/environment/up.mdx +20 -0
- package/docs/api-references/environment/update/ollama.mdx +16 -0
- package/docs/api-references/environment/update.mdx +23 -0
- package/docs/api-references/index.mdx +35 -0
- package/docs/api-references/localnet/_meta.json +3 -0
- package/docs/api-references/localnet/localnet/validators/count.mdx +15 -0
- package/docs/api-references/localnet/localnet/validators/create-random.mdx +16 -0
- package/docs/api-references/localnet/localnet/validators/create.mdx +19 -0
- package/docs/api-references/localnet/localnet/validators/delete.mdx +16 -0
- package/docs/api-references/localnet/localnet/validators/get.mdx +16 -0
- package/docs/api-references/localnet/localnet/validators/update.mdx +23 -0
- package/docs/api-references/localnet/localnet/validators.mdx +28 -0
- package/docs/api-references/localnet/localnet.mdx +23 -0
- package/docs/api-references/staking/_meta.json +3 -0
- package/docs/api-references/staking/staking/active-validators.mdx +18 -0
- package/docs/api-references/staking/staking/banned-validators.mdx +18 -0
- package/docs/api-references/staking/staking/delegation-info.mdx +25 -0
- package/docs/api-references/staking/staking/delegator-claim.mdx +26 -0
- package/docs/api-references/staking/staking/delegator-exit.mdx +26 -0
- package/docs/api-references/staking/staking/delegator-join.mdx +26 -0
- package/docs/api-references/staking/staking/epoch-info.mdx +19 -0
- package/docs/api-references/staking/staking/prime-all.mdx +20 -0
- package/docs/api-references/staking/staking/quarantined-validators.mdx +18 -0
- package/docs/api-references/staking/staking/set-identity.mdx +33 -0
- package/docs/api-references/staking/staking/set-operator.mdx +26 -0
- package/docs/api-references/staking/staking/validator-claim.mdx +24 -0
- package/docs/api-references/staking/staking/validator-deposit.mdx +25 -0
- package/docs/api-references/staking/staking/validator-exit.mdx +25 -0
- package/docs/api-references/staking/staking/validator-history.mdx +29 -0
- package/docs/api-references/staking/staking/validator-info.mdx +25 -0
- package/docs/api-references/staking/staking/validator-join.mdx +22 -0
- package/docs/api-references/staking/staking/validator-prime.mdx +25 -0
- package/docs/api-references/staking/staking/validators.mdx +19 -0
- package/docs/api-references/staking/staking/wizard.mdx +20 -0
- package/docs/api-references/staking/staking.mdx +42 -0
- package/docs/api-references/transactions/_meta.json +6 -0
- package/docs/api-references/transactions/appeal-bond.mdx +20 -0
- package/docs/api-references/transactions/appeal.mdx +21 -0
- package/docs/api-references/transactions/receipt.mdx +25 -0
- package/docs/api-references/transactions/trace.mdx +21 -0
- package/docs/delegator-guide.md +203 -0
- package/docs/validator-guide.md +329 -0
- package/esbuild.config.dev.js +17 -0
- package/esbuild.config.js +22 -0
- package/esbuild.config.prod.js +17 -0
- package/eslint.config.js +60 -0
- package/package.json +2 -11
- package/renovate.json +22 -0
- package/scripts/generate-cli-docs.mjs +68 -5
- package/src/commands/account/create.ts +30 -0
- package/src/commands/account/export.ts +106 -0
- package/src/commands/account/import.ts +135 -0
- package/src/commands/account/index.ts +129 -0
- package/src/commands/account/list.ts +34 -0
- package/src/commands/account/lock.ts +39 -0
- package/src/commands/account/remove.ts +30 -0
- package/src/commands/account/send.ts +162 -0
- package/src/commands/account/show.ts +74 -0
- package/src/commands/account/unlock.ts +56 -0
- package/src/commands/account/use.ts +21 -0
- package/src/commands/config/getSetReset.ts +51 -0
- package/src/commands/config/index.ts +30 -0
- package/src/commands/contracts/call.ts +39 -0
- package/src/commands/contracts/code.ts +33 -0
- package/src/commands/contracts/deploy.ts +161 -0
- package/src/commands/contracts/index.ts +150 -0
- package/src/commands/contracts/schema.ts +31 -0
- package/src/commands/contracts/write.ts +49 -0
- package/src/commands/general/index.ts +45 -0
- package/src/commands/general/init.ts +180 -0
- package/src/commands/general/start.ts +128 -0
- package/src/commands/general/stop.ts +26 -0
- package/src/commands/localnet/index.ts +100 -0
- package/src/commands/localnet/validators.ts +269 -0
- package/src/commands/network/index.ts +29 -0
- package/src/commands/network/setNetwork.ts +77 -0
- package/src/commands/scaffold/index.ts +16 -0
- package/src/commands/scaffold/new.ts +34 -0
- package/src/commands/staking/StakingAction.ts +279 -0
- package/src/commands/staking/delegatorClaim.ts +41 -0
- package/src/commands/staking/delegatorExit.ts +56 -0
- package/src/commands/staking/delegatorJoin.ts +44 -0
- package/src/commands/staking/index.ts +357 -0
- package/src/commands/staking/setIdentity.ts +78 -0
- package/src/commands/staking/setOperator.ts +46 -0
- package/src/commands/staking/stakingInfo.ts +584 -0
- package/src/commands/staking/validatorClaim.ts +43 -0
- package/src/commands/staking/validatorDeposit.ts +48 -0
- package/src/commands/staking/validatorExit.ts +63 -0
- package/src/commands/staking/validatorHistory.ts +300 -0
- package/src/commands/staking/validatorJoin.ts +47 -0
- package/src/commands/staking/validatorPrime.ts +73 -0
- package/src/commands/staking/wizard.ts +809 -0
- package/src/commands/transactions/appeal.ts +83 -0
- package/src/commands/transactions/index.ts +60 -0
- package/src/commands/transactions/receipt.ts +90 -0
- package/src/commands/transactions/trace.ts +42 -0
- package/src/commands/update/index.ts +25 -0
- package/src/commands/update/ollama.ts +103 -0
- package/src/lib/actions/BaseAction.ts +301 -0
- package/src/lib/clients/jsonRpcClient.ts +41 -0
- package/src/lib/clients/system.ts +73 -0
- package/src/lib/config/ConfigFileManager.ts +194 -0
- package/src/lib/config/KeychainManager.ts +89 -0
- package/src/lib/config/simulator.ts +68 -0
- package/src/lib/config/text.ts +2 -0
- package/src/lib/errors/missingRequirement.ts +9 -0
- package/src/lib/errors/versionRequired.ts +9 -0
- package/src/lib/interfaces/ISimulatorService.ts +39 -0
- package/src/lib/services/simulator.ts +386 -0
- package/src/types/node-fetch.d.ts +1 -0
- package/tests/actions/appeal.test.ts +141 -0
- package/tests/actions/call.test.ts +94 -0
- package/tests/actions/code.test.ts +87 -0
- package/tests/actions/create.test.ts +65 -0
- package/tests/actions/deploy.test.ts +420 -0
- package/tests/actions/getSetReset.test.ts +88 -0
- package/tests/actions/init.test.ts +483 -0
- package/tests/actions/lock.test.ts +86 -0
- package/tests/actions/new.test.ts +80 -0
- package/tests/actions/ollama.test.ts +193 -0
- package/tests/actions/receipt.test.ts +261 -0
- package/tests/actions/schema.test.ts +94 -0
- package/tests/actions/setNetwork.test.ts +161 -0
- package/tests/actions/staking.test.ts +280 -0
- package/tests/actions/start.test.ts +257 -0
- package/tests/actions/stop.test.ts +77 -0
- package/tests/actions/unlock.test.ts +139 -0
- package/tests/actions/validators.test.ts +750 -0
- package/tests/actions/write.test.ts +102 -0
- package/tests/commands/account.test.ts +146 -0
- package/tests/commands/appeal.test.ts +97 -0
- package/tests/commands/call.test.ts +78 -0
- package/tests/commands/code.test.ts +69 -0
- package/tests/commands/config.test.ts +54 -0
- package/tests/commands/deploy.test.ts +83 -0
- package/tests/commands/init.test.ts +101 -0
- package/tests/commands/localnet.test.ts +131 -0
- package/tests/commands/network.test.ts +60 -0
- package/tests/commands/new.test.ts +68 -0
- package/tests/commands/parseArg.test.ts +156 -0
- package/tests/commands/receipt.test.ts +142 -0
- package/tests/commands/schema.test.ts +67 -0
- package/tests/commands/staking.test.ts +329 -0
- package/tests/commands/stop.test.ts +27 -0
- package/tests/commands/up.test.ts +105 -0
- package/tests/commands/update.test.ts +45 -0
- package/tests/commands/write.test.ts +76 -0
- package/tests/index.test.ts +56 -0
- package/tests/libs/baseAction.test.ts +535 -0
- package/tests/libs/configFileManager.test.ts +118 -0
- package/tests/libs/jsonRpcClient.test.ts +59 -0
- package/tests/libs/keychainManager.test.ts +156 -0
- package/tests/libs/platformCommands.test.ts +78 -0
- package/tests/libs/system.test.ts +148 -0
- package/tests/services/simulator.test.ts +789 -0
- package/tests/smoke.test.ts +134 -0
- package/tests/utils.ts +13 -0
- package/tsconfig.json +120 -0
- package/vitest.config.ts +13 -0
- package/vitest.smoke.config.ts +17 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import {BaseAction} from "../../lib/actions/BaseAction";
|
|
4
|
+
import {pathToFileURL} from "url";
|
|
5
|
+
import {TransactionStatus} from "genlayer-js/types";
|
|
6
|
+
import {buildSync} from "esbuild";
|
|
7
|
+
|
|
8
|
+
export interface DeployOptions {
|
|
9
|
+
contract?: string;
|
|
10
|
+
args?: any[];
|
|
11
|
+
rpc?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DeployScriptsOptions {
|
|
15
|
+
rpc?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class DeployAction extends BaseAction {
|
|
19
|
+
private readonly deployDir = path.resolve(process.cwd(), "deploy");
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private readContractCode(contractPath: string): string {
|
|
26
|
+
if (!fs.existsSync(contractPath)) {
|
|
27
|
+
throw new Error(`Contract file not found: ${contractPath}`);
|
|
28
|
+
}
|
|
29
|
+
return fs.readFileSync(contractPath, "utf-8");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private async executeTsScript(filePath: string, rpcUrl?: string): Promise<void> {
|
|
33
|
+
const outFile = filePath.replace(/\.ts$/, ".compiled.js");
|
|
34
|
+
this.startSpinner(`Transpiling TypeScript file: ${filePath}`);
|
|
35
|
+
try {
|
|
36
|
+
buildSync({
|
|
37
|
+
entryPoints: [filePath],
|
|
38
|
+
outfile: outFile,
|
|
39
|
+
bundle: false,
|
|
40
|
+
platform: "node",
|
|
41
|
+
format: "esm",
|
|
42
|
+
target: "es2020",
|
|
43
|
+
sourcemap: false,
|
|
44
|
+
});
|
|
45
|
+
await this.executeJsScript(filePath, outFile, rpcUrl);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
this.failSpinner(`Error executing: ${filePath}`, error);
|
|
48
|
+
} finally {
|
|
49
|
+
fs.unlinkSync(outFile);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private async executeJsScript(
|
|
54
|
+
filePath: string,
|
|
55
|
+
transpiledFilePath?: string,
|
|
56
|
+
rpcUrl?: string,
|
|
57
|
+
): Promise<void> {
|
|
58
|
+
this.startSpinner(`Executing file: ${filePath}`);
|
|
59
|
+
try {
|
|
60
|
+
const module = await import(pathToFileURL(transpiledFilePath || filePath).href);
|
|
61
|
+
if (!module.default || typeof module.default !== "function") {
|
|
62
|
+
this.failSpinner(`No "default" function found in: ${filePath}`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const client = await this.getClient(rpcUrl);
|
|
66
|
+
await module.default(client);
|
|
67
|
+
this.succeedSpinner(`Successfully executed: ${filePath}`);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
this.failSpinner(`Error executing: ${filePath}`, error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async deployScripts(options?: DeployScriptsOptions) {
|
|
74
|
+
this.startSpinner("Searching for deploy scripts...");
|
|
75
|
+
if (!fs.existsSync(this.deployDir)) {
|
|
76
|
+
this.failSpinner("No deploy folder found.");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const files = fs
|
|
80
|
+
.readdirSync(this.deployDir)
|
|
81
|
+
.filter(file => file.endsWith(".ts") || file.endsWith(".js"))
|
|
82
|
+
.sort((a, b) => {
|
|
83
|
+
const numA = parseInt(a.split("_")[0]);
|
|
84
|
+
const numB = parseInt(b.split("_")[0]);
|
|
85
|
+
|
|
86
|
+
if (!isNaN(numA) && !isNaN(numB)) return numA - numB;
|
|
87
|
+
if (!isNaN(numA)) return -1;
|
|
88
|
+
if (!isNaN(numB)) return 1;
|
|
89
|
+
return a.localeCompare(b);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (files.length === 0) {
|
|
93
|
+
this.failSpinner("No deploy scripts found.");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.setSpinnerText(`Found ${files.length} deploy scripts. Executing...`);
|
|
98
|
+
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
const filePath = path.resolve(this.deployDir, file);
|
|
101
|
+
this.setSpinnerText(`Executing script: ${filePath}`);
|
|
102
|
+
try {
|
|
103
|
+
if (file.endsWith(".ts")) {
|
|
104
|
+
await this.executeTsScript(filePath, options?.rpc);
|
|
105
|
+
} else {
|
|
106
|
+
await this.executeJsScript(filePath, undefined, options?.rpc);
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
this.failSpinner(`Error executing script: ${filePath}`, error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async deploy(options: DeployOptions): Promise<void> {
|
|
115
|
+
try {
|
|
116
|
+
const client = await this.getClient(options.rpc);
|
|
117
|
+
this.startSpinner("Setting up the deployment environment...");
|
|
118
|
+
await client.initializeConsensusSmartContract();
|
|
119
|
+
|
|
120
|
+
if (!options.contract) {
|
|
121
|
+
this.failSpinner("No contract specified for deployment.");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this.setSpinnerText("Reading contract code...");
|
|
125
|
+
const contractCode = this.readContractCode(options.contract);
|
|
126
|
+
|
|
127
|
+
if (!contractCode) {
|
|
128
|
+
this.failSpinner("Contract code is empty.");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const leaderOnly = false;
|
|
133
|
+
const deployParams: any = {code: contractCode, args: options.args, leaderOnly};
|
|
134
|
+
|
|
135
|
+
this.setSpinnerText("Starting contract deployment...");
|
|
136
|
+
this.log("Deployment Parameters:", deployParams);
|
|
137
|
+
|
|
138
|
+
const hash = (await client.deployContract(deployParams)) as any;
|
|
139
|
+
this.log("Deployment Transaction Hash:", hash);
|
|
140
|
+
const result = await client.waitForTransactionReceipt({
|
|
141
|
+
hash,
|
|
142
|
+
retries: 50,
|
|
143
|
+
interval: 5000,
|
|
144
|
+
status: TransactionStatus.ACCEPTED,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
this.log("Deployment Receipt:", result);
|
|
148
|
+
|
|
149
|
+
const contractAddress =
|
|
150
|
+
result.data?.contract_address ?? // localnet/studio
|
|
151
|
+
(result.txDataDecoded as any)?.contractAddress; // testnet
|
|
152
|
+
|
|
153
|
+
this.succeedSpinner("Contract deployed successfully.", {
|
|
154
|
+
"Transaction Hash": hash,
|
|
155
|
+
"Contract Address": contractAddress,
|
|
156
|
+
});
|
|
157
|
+
} catch (error) {
|
|
158
|
+
this.failSpinner("Error deploying contract", error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {Command} from "commander";
|
|
2
|
+
import {CalldataAddress} from "genlayer-js/types";
|
|
3
|
+
import {DeployAction, DeployOptions, DeployScriptsOptions} from "./deploy";
|
|
4
|
+
import {CallAction, CallOptions} from "./call";
|
|
5
|
+
import {WriteAction, WriteOptions} from "./write";
|
|
6
|
+
import {SchemaAction, SchemaOptions} from "./schema";
|
|
7
|
+
import {CodeAction, CodeOptions} from "./code";
|
|
8
|
+
|
|
9
|
+
const ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
10
|
+
const ADDR_PREFIX_RE = /^addr#([0-9a-fA-F]{40})$/;
|
|
11
|
+
const BYTES_PREFIX_RE = /^b#([0-9a-fA-F]*)$/;
|
|
12
|
+
const HEX_RE = /^0x[0-9a-fA-F]+$/;
|
|
13
|
+
|
|
14
|
+
function hexToBytes(hex: string): Uint8Array {
|
|
15
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
16
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
17
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
18
|
+
}
|
|
19
|
+
return bytes;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function coerceValue(value: unknown): unknown {
|
|
23
|
+
if (value === null) return null;
|
|
24
|
+
if (typeof value === "boolean") return value;
|
|
25
|
+
if (typeof value === "number") {
|
|
26
|
+
if (Number.isSafeInteger(value)) return value;
|
|
27
|
+
return BigInt(value);
|
|
28
|
+
}
|
|
29
|
+
if (Array.isArray(value)) return value.map(coerceValue);
|
|
30
|
+
if (typeof value === "object" && value !== null) {
|
|
31
|
+
const result: Record<string, unknown> = {};
|
|
32
|
+
for (const [k, v] of Object.entries(value)) {
|
|
33
|
+
result[k] = coerceValue(v);
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
if (typeof value === "string") return parseScalar(value);
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function parseScalar(value: string): unknown {
|
|
42
|
+
if (value === "null") return null;
|
|
43
|
+
if (value === "true") return true;
|
|
44
|
+
if (value === "false") return false;
|
|
45
|
+
|
|
46
|
+
const addrMatch = value.match(ADDR_PREFIX_RE);
|
|
47
|
+
if (addrMatch) return new CalldataAddress(hexToBytes(addrMatch[1]));
|
|
48
|
+
if (ADDRESS_RE.test(value)) return new CalldataAddress(hexToBytes(value.slice(2)));
|
|
49
|
+
|
|
50
|
+
const bytesMatch = value.match(BYTES_PREFIX_RE);
|
|
51
|
+
if (bytesMatch) return hexToBytes(bytesMatch[1]);
|
|
52
|
+
|
|
53
|
+
if (HEX_RE.test(value)) return BigInt(value);
|
|
54
|
+
if (!isNaN(Number(value)) && Number.isSafeInteger(Number(value))) return Number(value);
|
|
55
|
+
if (!isNaN(Number(value))) return BigInt(value);
|
|
56
|
+
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function parseArg(value: string, previous: any[] = []): any[] {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(value);
|
|
63
|
+
if (typeof parsed === "object" || Array.isArray(parsed)) {
|
|
64
|
+
return [...previous, coerceValue(parsed)];
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
// not JSON, fall through to scalar parsing
|
|
68
|
+
}
|
|
69
|
+
return [...previous, parseScalar(value)];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const ARGS_HELP = [
|
|
73
|
+
"Contract arguments. Supported types:",
|
|
74
|
+
" bool: true, false",
|
|
75
|
+
" null: null",
|
|
76
|
+
" int: 42, -1, 0x1a (large values auto-use BigInt)",
|
|
77
|
+
' str: hello, "multi word"',
|
|
78
|
+
" address: 0x6857...a0 (40 hex chars) or addr#6857...a0",
|
|
79
|
+
" bytes: b#deadbeef",
|
|
80
|
+
' array: \'[1, 2, "three"]\'',
|
|
81
|
+
' dict: \'{"key": "value"}\'',
|
|
82
|
+
].join("\n");
|
|
83
|
+
|
|
84
|
+
export function initializeContractsCommands(program: Command) {
|
|
85
|
+
program
|
|
86
|
+
.command("deploy")
|
|
87
|
+
.description("Deploy intelligent contracts")
|
|
88
|
+
.option("--contract <contractPath>", "Path to the smart contract to deploy")
|
|
89
|
+
.option("--rpc <rpcUrl>", "RPC URL for the network")
|
|
90
|
+
.option("--args <args...>", ARGS_HELP, parseArg, [])
|
|
91
|
+
.action(async (options: DeployOptions) => {
|
|
92
|
+
const deployer = new DeployAction();
|
|
93
|
+
if (options.contract) {
|
|
94
|
+
await deployer.deploy(options);
|
|
95
|
+
} else {
|
|
96
|
+
const deployScriptsOptions: DeployScriptsOptions = {rpc: options.rpc};
|
|
97
|
+
await deployer.deployScripts(deployScriptsOptions);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
program
|
|
102
|
+
.command("call <contractAddress> <method>")
|
|
103
|
+
.description("Call a contract method without sending a transaction or changing the state")
|
|
104
|
+
.option("--rpc <rpcUrl>", "RPC URL for the network")
|
|
105
|
+
.option(
|
|
106
|
+
"--args <args...>",
|
|
107
|
+
ARGS_HELP,
|
|
108
|
+
parseArg,
|
|
109
|
+
[],
|
|
110
|
+
)
|
|
111
|
+
.action(async (contractAddress: string, method: string, options: CallOptions) => {
|
|
112
|
+
const callAction = new CallAction();
|
|
113
|
+
await callAction.call({contractAddress, method, ...options});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
program
|
|
117
|
+
.command("write <contractAddress> <method>")
|
|
118
|
+
.description("Sends a transaction to a contract method that modifies the state")
|
|
119
|
+
.option("--rpc <rpcUrl>", "RPC URL for the network")
|
|
120
|
+
.option(
|
|
121
|
+
"--args <args...>",
|
|
122
|
+
ARGS_HELP,
|
|
123
|
+
parseArg,
|
|
124
|
+
[],
|
|
125
|
+
)
|
|
126
|
+
.action(async (contractAddress: string, method: string, options: WriteOptions) => {
|
|
127
|
+
const writeAction = new WriteAction();
|
|
128
|
+
await writeAction.write({contractAddress, method, ...options});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
program
|
|
132
|
+
.command("schema <contractAddress>")
|
|
133
|
+
.description("Get the schema for a deployed contract")
|
|
134
|
+
.option("--rpc <rpcUrl>", "RPC URL for the network")
|
|
135
|
+
.action(async (contractAddress: string, options: SchemaOptions) => {
|
|
136
|
+
const schemaAction = new SchemaAction();
|
|
137
|
+
await schemaAction.schema({contractAddress, ...options});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
program
|
|
141
|
+
.command("code <contractAddress>")
|
|
142
|
+
.description("Get the source for a deployed contract")
|
|
143
|
+
.option("--rpc <rpcUrl>", "RPC URL for the network")
|
|
144
|
+
.action(async (contractAddress: string, options: CodeOptions) => {
|
|
145
|
+
const codeAction = new CodeAction();
|
|
146
|
+
await codeAction.code({contractAddress, ...options});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return program;
|
|
150
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {BaseAction} from "../../lib/actions/BaseAction";
|
|
2
|
+
import type {Address} from "genlayer-js/types";
|
|
3
|
+
|
|
4
|
+
export interface SchemaOptions {
|
|
5
|
+
rpc?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class SchemaAction extends BaseAction {
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async schema({
|
|
14
|
+
contractAddress,
|
|
15
|
+
rpc,
|
|
16
|
+
}: {
|
|
17
|
+
contractAddress: string;
|
|
18
|
+
rpc?: string;
|
|
19
|
+
}): Promise<void> {
|
|
20
|
+
const client = await this.getClient(rpc, true);
|
|
21
|
+
await client.initializeConsensusSmartContract();
|
|
22
|
+
this.startSpinner(`Getting schema for contract at ${contractAddress}...`);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const result = await client.getContractSchema(contractAddress as Address);
|
|
26
|
+
this.succeedSpinner("Contract schema retrieved successfully", result);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
this.failSpinner("Error retrieving contract schema", error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// import {simulator} from "genlayer-js/chains";
|
|
2
|
+
// import type {GenLayerClient} from "genlayer-js/types";
|
|
3
|
+
import {BaseAction} from "../../lib/actions/BaseAction";
|
|
4
|
+
|
|
5
|
+
export interface WriteOptions {
|
|
6
|
+
args: any[];
|
|
7
|
+
rpc?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class WriteAction extends BaseAction {
|
|
11
|
+
constructor() {
|
|
12
|
+
super();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async write({
|
|
16
|
+
contractAddress,
|
|
17
|
+
method,
|
|
18
|
+
args,
|
|
19
|
+
rpc,
|
|
20
|
+
}: {
|
|
21
|
+
contractAddress: string;
|
|
22
|
+
method: string;
|
|
23
|
+
args: any[];
|
|
24
|
+
rpc?: string;
|
|
25
|
+
}): Promise<void> {
|
|
26
|
+
const client = await this.getClient(rpc);
|
|
27
|
+
await client.initializeConsensusSmartContract();
|
|
28
|
+
this.startSpinner(`Calling write method ${method} on contract at ${contractAddress}...`);
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const hash = await client.writeContract({
|
|
32
|
+
address: contractAddress as any,
|
|
33
|
+
functionName: method,
|
|
34
|
+
args,
|
|
35
|
+
value: 0n,
|
|
36
|
+
});
|
|
37
|
+
this.log("Write Transaction Hash:", hash);
|
|
38
|
+
|
|
39
|
+
const result = await client.waitForTransactionReceipt({
|
|
40
|
+
hash,
|
|
41
|
+
retries: 100,
|
|
42
|
+
interval: 5000,
|
|
43
|
+
});
|
|
44
|
+
this.succeedSpinner("Write operation successfully executed", result);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
this.failSpinner("Error during write operation", error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
import simulatorService from "../../lib/services/simulator";
|
|
4
|
+
import { InitAction, InitActionOptions } from "./init";
|
|
5
|
+
import { StartAction, StartActionOptions } from "./start";
|
|
6
|
+
import {localnetCompatibleVersion} from "../../lib/config/simulator";
|
|
7
|
+
import {StopAction} from "./stop";
|
|
8
|
+
|
|
9
|
+
export function initializeGeneralCommands(program: Command) {
|
|
10
|
+
program
|
|
11
|
+
.command("init")
|
|
12
|
+
.description("Initialize the GenLayer Environment")
|
|
13
|
+
.option("--numValidators <numValidators>", "Number of validators", "5")
|
|
14
|
+
.option("--headless", "Headless mode", false)
|
|
15
|
+
.option("--reset-db", "Reset Database", false)
|
|
16
|
+
.option("--localnet-version <localnetVersion>", `Select a specific localnet version (minimum: ${localnetCompatibleVersion})`, localnetCompatibleVersion)
|
|
17
|
+
.option("--ollama", "Enable Ollama container", false)
|
|
18
|
+
.action(async (options: InitActionOptions) => {
|
|
19
|
+
const initAction = new InitAction();
|
|
20
|
+
await initAction.execute(options)
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
program
|
|
24
|
+
.command("up")
|
|
25
|
+
.description("Starts GenLayer's simulator")
|
|
26
|
+
.option("--reset-validators", "Remove all current validators and create new random ones", false)
|
|
27
|
+
.option("--numValidators <numValidators>", "Number of validators", "5")
|
|
28
|
+
.option("--headless", "Headless mode", false)
|
|
29
|
+
.option("--reset-db", "Reset Database", false)
|
|
30
|
+
.option("--ollama", "Enable Ollama container", false)
|
|
31
|
+
.action(async (options: StartActionOptions) => {
|
|
32
|
+
const startAction = new StartAction();
|
|
33
|
+
await startAction.execute(options);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
program
|
|
37
|
+
.command("stop")
|
|
38
|
+
.description("Stop all running localnet services.")
|
|
39
|
+
.action(async () => {
|
|
40
|
+
const stopAction = new StopAction();
|
|
41
|
+
await stopAction.stop();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return program;
|
|
45
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import {DistinctQuestion} from "inquirer";
|
|
3
|
+
import {ISimulatorService} from "../../lib/interfaces/ISimulatorService";
|
|
4
|
+
import {AI_PROVIDERS_CONFIG, AiProviders, localnetCompatibleVersion} from "../../lib/config/simulator";
|
|
5
|
+
import {OllamaAction} from "../update/ollama";
|
|
6
|
+
import {BaseAction} from "../../lib/actions/BaseAction";
|
|
7
|
+
import {SimulatorService} from "../../lib/services/simulator";
|
|
8
|
+
|
|
9
|
+
export interface InitActionOptions {
|
|
10
|
+
numValidators: number;
|
|
11
|
+
headless: boolean;
|
|
12
|
+
resetDb: boolean;
|
|
13
|
+
localnetVersion: string;
|
|
14
|
+
ollama: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getRequirementsErrorMessage({docker}: Record<string, boolean>): string {
|
|
18
|
+
if (!docker) {
|
|
19
|
+
return "Docker is not installed. Please install Docker and try again.\n";
|
|
20
|
+
}
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getVersionErrorMessage({docker, node}: Record<string, string>): string {
|
|
25
|
+
let message = "";
|
|
26
|
+
|
|
27
|
+
if (docker) {
|
|
28
|
+
message += `Docker version ${docker} or higher is required. Please update Docker and try again.\n`;
|
|
29
|
+
}
|
|
30
|
+
if (node) {
|
|
31
|
+
message += `Node version ${node} or higher is required. Please update Node and try again.\n`;
|
|
32
|
+
}
|
|
33
|
+
return message;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class InitAction extends BaseAction {
|
|
37
|
+
private simulatorService: ISimulatorService;
|
|
38
|
+
constructor() {
|
|
39
|
+
super();
|
|
40
|
+
this.simulatorService = new SimulatorService();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public async execute(options: InitActionOptions): Promise<void> {
|
|
44
|
+
try {
|
|
45
|
+
this.simulatorService.setComposeOptions(options.headless, options.ollama);
|
|
46
|
+
let localnetVersion = options.localnetVersion;
|
|
47
|
+
if (localnetVersion !== "latest") {
|
|
48
|
+
localnetVersion = this.simulatorService.normalizeLocalnetVersion(localnetVersion);
|
|
49
|
+
|
|
50
|
+
if (this.simulatorService.compareVersions(localnetVersion, localnetCompatibleVersion) < 0) {
|
|
51
|
+
this.failSpinner(`Localnet version ${localnetVersion} is not supported. Minimum required version is ${localnetCompatibleVersion}. Please use a newer version.`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.startSpinner("Checking CLI version...");
|
|
57
|
+
await this.simulatorService.checkCliVersion();
|
|
58
|
+
|
|
59
|
+
const isRunning = await this.simulatorService.isLocalnetRunning();
|
|
60
|
+
|
|
61
|
+
this.setSpinnerText("Checking installation requirements...");
|
|
62
|
+
const requirementsInstalled = await this.simulatorService.checkInstallRequirements();
|
|
63
|
+
const requirementErrorMessage = getRequirementsErrorMessage(requirementsInstalled);
|
|
64
|
+
if (requirementErrorMessage) {
|
|
65
|
+
this.failSpinner(requirementErrorMessage);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.setSpinnerText("Checking version requirements...");
|
|
70
|
+
const missingVersions = await this.simulatorService.checkVersionRequirements();
|
|
71
|
+
const versionErrorMessage = getVersionErrorMessage(missingVersions);
|
|
72
|
+
if (versionErrorMessage) {
|
|
73
|
+
this.failSpinner(versionErrorMessage);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.stopSpinner();
|
|
77
|
+
|
|
78
|
+
const confirmMessage = isRunning
|
|
79
|
+
? `GenLayer Localnet is already running and this command is going to reset GenLayer docker images, containers and volumes, providers API Keys, and GenLayer database (accounts, transactions, validators and logs). Contract code (gpy files) will be kept. Do you want to continue?`
|
|
80
|
+
: `This command is going to reset GenLayer docker images, containers and volumes, providers API Keys, and GenLayer database (accounts, transactions, validators and logs). Contract code (gpy files) will be kept. Do you want to continue?`;
|
|
81
|
+
|
|
82
|
+
await this.confirmPrompt(confirmMessage);
|
|
83
|
+
|
|
84
|
+
this.logInfo(`Initializing GenLayer CLI with ${options.numValidators} validators`);
|
|
85
|
+
|
|
86
|
+
// Reset Docker containers, images, and volumes
|
|
87
|
+
this.startSpinner("Resetting Docker containers, images, and volumes...");
|
|
88
|
+
await this.simulatorService.resetDockerContainers();
|
|
89
|
+
await this.simulatorService.resetDockerImages();
|
|
90
|
+
await this.simulatorService.resetDockerVolumes();
|
|
91
|
+
this.stopSpinner();
|
|
92
|
+
|
|
93
|
+
const llmQuestions: DistinctQuestion[] = [
|
|
94
|
+
{
|
|
95
|
+
type: "checkbox",
|
|
96
|
+
name: "selectedLlmProviders",
|
|
97
|
+
message: "Select which LLM providers do you want to use:",
|
|
98
|
+
choices: this.simulatorService.getAiProvidersOptions(true, options.ollama ? [] : ["ollama"]),
|
|
99
|
+
validate: answer => (answer.length < 1 ? "You must choose at least one option." : true),
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
const llmProvidersAnswer = await inquirer.prompt(llmQuestions);
|
|
103
|
+
const selectedLlmProviders = llmProvidersAnswer.selectedLlmProviders as AiProviders[];
|
|
104
|
+
|
|
105
|
+
let defaultOllamaModel = this.getConfig().defaultOllamaModel;
|
|
106
|
+
const aiProvidersEnvVars: Record<string, string> = {};
|
|
107
|
+
const configurableAiProviders = selectedLlmProviders.filter(
|
|
108
|
+
(provider: AiProviders) => AI_PROVIDERS_CONFIG[provider].envVar,
|
|
109
|
+
);
|
|
110
|
+
for (const provider of configurableAiProviders) {
|
|
111
|
+
const providerConfig = AI_PROVIDERS_CONFIG[provider];
|
|
112
|
+
const keyQuestion: DistinctQuestion[] = [
|
|
113
|
+
{
|
|
114
|
+
type: "input",
|
|
115
|
+
name: providerConfig.cliOptionValue,
|
|
116
|
+
message: `Please enter your ${providerConfig.name} API Key:`,
|
|
117
|
+
validate: (value: string) =>
|
|
118
|
+
value.length ? true : `Please enter a valid API Key for ${providerConfig.name}.`,
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
const apiKeyAnswer = await inquirer.prompt(keyQuestion);
|
|
122
|
+
aiProvidersEnvVars[providerConfig.envVar!] = apiKeyAnswer[providerConfig.cliOptionValue];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.startSpinner("Configuring GenLayer Localnet environment...");
|
|
126
|
+
this.simulatorService.addConfigToEnvFile(aiProvidersEnvVars);
|
|
127
|
+
this.simulatorService.addConfigToEnvFile({LOCALNETVERSION: localnetVersion});
|
|
128
|
+
|
|
129
|
+
this.setSpinnerText("Running GenLayer Localnet...");
|
|
130
|
+
this.simulatorService.setupLocalhostAccess();
|
|
131
|
+
await this.simulatorService.runSimulator();
|
|
132
|
+
|
|
133
|
+
this.setSpinnerText("Waiting for localnet to be ready...");
|
|
134
|
+
const {initialized, errorCode, errorMessage} = await this.simulatorService.waitForSimulatorToBeReady();
|
|
135
|
+
if (!initialized) {
|
|
136
|
+
if (errorCode === "ERROR") {
|
|
137
|
+
this.failSpinner(`Unable to initialize the GenLayer Localnet: ${errorMessage}`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (errorCode === "TIMEOUT") {
|
|
141
|
+
this.failSpinner(
|
|
142
|
+
"The localnet is taking too long to initialize. Please try again after the localnet is ready.",
|
|
143
|
+
);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this.stopSpinner();
|
|
149
|
+
|
|
150
|
+
if (selectedLlmProviders.includes("ollama")) {
|
|
151
|
+
const ollamaAction = new OllamaAction();
|
|
152
|
+
if (!defaultOllamaModel) {
|
|
153
|
+
this.writeConfig("defaultOllamaModel", "llama3");
|
|
154
|
+
defaultOllamaModel = "llama3";
|
|
155
|
+
}
|
|
156
|
+
await ollamaAction.updateModel(defaultOllamaModel);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.startSpinner("Initializing validators...");
|
|
160
|
+
await this.simulatorService.deleteAllValidators();
|
|
161
|
+
await this.simulatorService.createRandomValidators(Number(options.numValidators), selectedLlmProviders);
|
|
162
|
+
|
|
163
|
+
if (options.resetDb) {
|
|
164
|
+
this.setSpinnerText("Cleaning database...");
|
|
165
|
+
await this.simulatorService.cleanDatabase();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let successMessage = "GenLayer Localnet initialized successfully! ";
|
|
169
|
+
if (!options.headless) {
|
|
170
|
+
successMessage += `Go to ${this.simulatorService.getFrontendUrl()} in your browser to access it.`;
|
|
171
|
+
}
|
|
172
|
+
if (!options.headless) {
|
|
173
|
+
await this.simulatorService.openFrontend();
|
|
174
|
+
}
|
|
175
|
+
this.succeedSpinner(successMessage);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
this.failSpinner("An error occurred during initialization.", error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|