genlayer 0.12.4 → 0.13.0
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/.env.example +4 -0
- package/CHANGELOG.md +8 -0
- package/dist/index.js +17970 -17628
- package/esbuild.config.dev.js +1 -2
- package/esbuild.config.prod.js +1 -1
- package/eslint.config.js +2 -1
- package/package.json +5 -3
- package/src/commands/contracts/call.ts +23 -33
- package/src/commands/contracts/deploy.ts +109 -33
- package/src/commands/contracts/index.ts +14 -3
- package/src/commands/general/init.ts +0 -1
- package/src/commands/keygen/create.ts +5 -25
- package/src/commands/scaffold/index.ts +16 -0
- package/src/commands/scaffold/new.ts +34 -0
- package/src/index.ts +2 -0
- package/src/lib/accounts/KeypairManager.ts +43 -0
- package/src/lib/actions/BaseAction.ts +34 -7
- package/src/lib/config/simulator.ts +2 -2
- package/templates/default/LICENSE +21 -0
- package/templates/default/README.md +101 -0
- package/templates/default/__init__.py +0 -0
- package/templates/default/app/.env.example +2 -0
- package/templates/default/app/.vscode/extensions.json +3 -0
- package/templates/default/app/README.md +5 -0
- package/templates/default/app/index.html +17 -0
- package/templates/default/app/package-lock.json +4920 -0
- package/templates/default/app/package.json +23 -0
- package/templates/default/app/postcss.config.js +6 -0
- package/templates/default/app/public/favicon.png +0 -0
- package/templates/default/app/src/App.vue +16 -0
- package/templates/default/app/src/components/Address.vue +38 -0
- package/templates/default/app/src/components/BetsScreen.vue +329 -0
- package/templates/default/app/src/logic/FootballBets.js +100 -0
- package/templates/default/app/src/main.js +5 -0
- package/templates/default/app/src/services/genlayer.js +19 -0
- package/templates/default/app/src/style.css +3 -0
- package/templates/default/app/tailwind.config.js +8 -0
- package/templates/default/app/vite.config.js +7 -0
- package/templates/default/config/__init__.py +0 -0
- package/templates/default/config/genlayer_config.py +14 -0
- package/templates/default/contracts/__init__.py +0 -0
- package/templates/default/contracts/football_bets.py +119 -0
- package/templates/default/deploy/deployScript.ts +31 -0
- package/templates/default/package-lock.json +3231 -0
- package/templates/default/package.json +7 -0
- package/templates/default/requirements.txt +6 -0
- package/templates/default/test/__init__.py +0 -0
- package/templates/default/test/football_bets_get_contract_schema_for_code.py +124 -0
- package/templates/default/test/test_football_bet_success_draw.py +108 -0
- package/templates/default/test/test_football_bet_success_win.py +106 -0
- package/templates/default/test/test_football_bet_unsuccess.py +107 -0
- package/templates/default/tools/__init__.py +0 -0
- package/templates/default/tools/accounts.py +5 -0
- package/templates/default/tools/calldata.py +224 -0
- package/templates/default/tools/request.py +134 -0
- package/templates/default/tools/response.py +52 -0
- package/templates/default/tools/structure.py +39 -0
- package/templates/default/tools/transactions.py +28 -0
- package/templates/default/tools/types.py +214 -0
- package/templates/default/tsconfig.json +7 -0
- package/tests/actions/call.test.ts +39 -79
- package/tests/actions/create.test.ts +11 -72
- package/tests/actions/deploy.test.ts +201 -33
- package/tests/actions/new.test.ts +80 -0
- package/tests/commands/call.test.ts +6 -1
- package/tests/commands/deploy.test.ts +12 -1
- package/tests/commands/new.test.ts +68 -0
- package/tests/index.test.ts +4 -0
- package/tests/libs/accounts/KeypairManager.test.ts +110 -0
- package/tests/libs/baseAction.test.ts +40 -0
- package/vitest.config.ts +1 -1
- package/src/lib/accounts/getPrivateKey.ts +0 -21
- package/tests/libs/getPrivateKey.test.ts +0 -96
package/esbuild.config.dev.js
CHANGED
|
@@ -11,8 +11,7 @@ export default {
|
|
|
11
11
|
banner: {
|
|
12
12
|
js: `const _importMetaUrl = new URL(import.meta.url).pathname;`,
|
|
13
13
|
},
|
|
14
|
-
external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2"]
|
|
15
|
-
|
|
14
|
+
external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "fs-extra", "esbuild"]
|
|
16
15
|
},
|
|
17
16
|
watch: true,
|
|
18
17
|
};
|
package/esbuild.config.prod.js
CHANGED
|
@@ -11,7 +11,7 @@ export default {
|
|
|
11
11
|
banner: {
|
|
12
12
|
js: `const _importMetaUrl = new URL(import.meta.url).pathname;`,
|
|
13
13
|
},
|
|
14
|
-
external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2"]
|
|
14
|
+
external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "fs-extra", "esbuild"]
|
|
15
15
|
},
|
|
16
16
|
watch: false,
|
|
17
17
|
};
|
package/eslint.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genlayer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "GenLayer Command Line Tool",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@release-it/conventional-changelog": "^10.0.0",
|
|
39
39
|
"@types/dockerode": "^3.3.31",
|
|
40
|
+
"@types/fs-extra": "^11.0.4",
|
|
40
41
|
"@types/inquirer": "^9.0.7",
|
|
41
42
|
"@types/node": "^22.0.0",
|
|
42
43
|
"@types/sinon": "^17.0.3",
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
"@typescript-eslint/parser": "^8.0.0",
|
|
46
47
|
"@vitest/coverage-v8": "^2.1.4",
|
|
47
48
|
"cross-env": "^7.0.3",
|
|
48
|
-
"esbuild": "
|
|
49
|
+
"esbuild": ">=0.25.0",
|
|
49
50
|
"eslint": "^9.0.0",
|
|
50
51
|
"eslint-config-prettier": "^10.0.0",
|
|
51
52
|
"eslint-import-resolver-typescript": "^3.6.1",
|
|
@@ -62,7 +63,8 @@
|
|
|
62
63
|
"dockerode": "^4.0.2",
|
|
63
64
|
"dotenv": "^16.4.5",
|
|
64
65
|
"ethers": "^6.13.4",
|
|
65
|
-
"
|
|
66
|
+
"fs-extra": "^11.3.0",
|
|
67
|
+
"genlayer-js": "^0.9.0",
|
|
66
68
|
"inquirer": "^12.0.0",
|
|
67
69
|
"node-fetch": "^3.0.0",
|
|
68
70
|
"open": "^10.1.0",
|
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
import { createClient, createAccount } from "genlayer-js";
|
|
2
1
|
import { simulator } from "genlayer-js/chains";
|
|
3
2
|
import type { GenLayerClient } from "genlayer-js/types";
|
|
4
|
-
import {
|
|
3
|
+
import { BaseAction } from "../../lib/actions/BaseAction";
|
|
5
4
|
|
|
6
5
|
export interface CallOptions {
|
|
7
6
|
args: any[];
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
export class CallAction {
|
|
11
|
-
private genlayerClient: GenLayerClient<typeof simulator>;
|
|
12
|
-
|
|
9
|
+
export class CallAction extends BaseAction{
|
|
13
10
|
constructor() {
|
|
14
|
-
|
|
15
|
-
chain: simulator,
|
|
16
|
-
endpoint: process.env.VITE_JSON_RPC_SERVER_URL,
|
|
17
|
-
account: createAccount(getPrivateKey() as any),
|
|
18
|
-
});
|
|
11
|
+
super();
|
|
19
12
|
}
|
|
20
13
|
|
|
21
14
|
async call({
|
|
@@ -27,61 +20,58 @@ export class CallAction {
|
|
|
27
20
|
method: string;
|
|
28
21
|
args: any[];
|
|
29
22
|
}): Promise<void> {
|
|
30
|
-
|
|
23
|
+
const client = await this.getClient();
|
|
24
|
+
this.startSpinner(`Calling method ${method} on contract at ${contractAddress}...`);
|
|
31
25
|
|
|
32
|
-
const contractSchema = await
|
|
26
|
+
const contractSchema = await client.getContractSchema(contractAddress);
|
|
33
27
|
|
|
34
28
|
if(!contractSchema.methods.hasOwnProperty(method)){
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
this.failSpinner(`method ${method} not found.`);
|
|
30
|
+
return
|
|
37
31
|
}
|
|
38
32
|
|
|
39
33
|
const readonly = contractSchema.methods[method as any].readonly;
|
|
40
34
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
} else {
|
|
45
|
-
await this.executeWrite(contractAddress, method, args);
|
|
46
|
-
}
|
|
47
|
-
} catch (error) {
|
|
48
|
-
console.error("Error calling contract method:", error);
|
|
49
|
-
throw error;
|
|
35
|
+
if (readonly) {
|
|
36
|
+
await this.executeRead(contractAddress, method, args);
|
|
37
|
+
return
|
|
50
38
|
}
|
|
39
|
+
|
|
40
|
+
await this.executeWrite(contractAddress, method, args);
|
|
51
41
|
}
|
|
52
42
|
|
|
53
43
|
private async executeRead(contractAddress: string, method: string, args: any[]): Promise<void> {
|
|
54
44
|
try {
|
|
55
|
-
const
|
|
45
|
+
const client = await this.getClient();
|
|
46
|
+
const result = await client.readContract({
|
|
56
47
|
address: contractAddress as any,
|
|
57
48
|
functionName: method,
|
|
58
49
|
args,
|
|
59
50
|
});
|
|
60
|
-
|
|
51
|
+
this.succeedSpinner("Read operation successfully executed", result);
|
|
61
52
|
} catch (error) {
|
|
62
|
-
|
|
63
|
-
throw error;
|
|
53
|
+
this.failSpinner("Error during read operation", error);
|
|
64
54
|
}
|
|
65
55
|
}
|
|
66
56
|
|
|
67
57
|
private async executeWrite(contractAddress: string, method: string, args: any[]): Promise<void> {
|
|
68
58
|
try {
|
|
69
|
-
const
|
|
59
|
+
const client = await this.getClient();
|
|
60
|
+
const hash = await client.writeContract({
|
|
70
61
|
address: contractAddress as any,
|
|
71
62
|
functionName: method,
|
|
72
63
|
args,
|
|
73
64
|
value: 0n,
|
|
74
65
|
});
|
|
75
|
-
const result = await
|
|
66
|
+
const result = await client.waitForTransactionReceipt({
|
|
76
67
|
hash,
|
|
77
68
|
retries: 15,
|
|
78
69
|
interval: 2000,
|
|
79
70
|
});
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
this.log("Write transaction hash:", hash);
|
|
72
|
+
this.succeedSpinner("Write operation successfully executed", result);
|
|
82
73
|
} catch (error) {
|
|
83
|
-
|
|
84
|
-
throw error;
|
|
74
|
+
this.failSpinner("Error during write operation", error);
|
|
85
75
|
}
|
|
86
76
|
}
|
|
87
77
|
}
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
|
-
import
|
|
2
|
+
import path from "path";
|
|
3
3
|
import { simulator } from "genlayer-js/chains";
|
|
4
4
|
import type { GenLayerClient } from "genlayer-js/types";
|
|
5
|
-
import {
|
|
5
|
+
import { BaseAction } from "../../lib/actions/BaseAction";
|
|
6
|
+
import { pathToFileURL } from "url";
|
|
7
|
+
import { TransactionStatus } from "genlayer-js/types";
|
|
8
|
+
import { buildSync } from "esbuild";
|
|
6
9
|
|
|
7
10
|
export interface DeployOptions {
|
|
8
11
|
contract?: string;
|
|
9
|
-
// network: string;
|
|
10
12
|
args?: any[];
|
|
11
|
-
kwargs?: string;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export class DeployAction {
|
|
15
|
-
private
|
|
15
|
+
export class DeployAction extends BaseAction {
|
|
16
|
+
private readonly deployDir = path.resolve(process.cwd(), "deploy");
|
|
16
17
|
|
|
17
18
|
constructor() {
|
|
18
|
-
|
|
19
|
-
chain: simulator,
|
|
20
|
-
endpoint: process.env.VITE_JSON_RPC_SERVER_URL,
|
|
21
|
-
account: createAccount(getPrivateKey() as any),
|
|
22
|
-
});
|
|
19
|
+
super();
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
private readContractCode(contractPath: string): string {
|
|
@@ -29,44 +26,123 @@ export class DeployAction {
|
|
|
29
26
|
return fs.readFileSync(contractPath, "utf-8");
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
async
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
private async executeTsScript(filePath: string): Promise<void> {
|
|
30
|
+
const outFile = filePath.replace(/\.ts$/, ".compiled.js");
|
|
31
|
+
this.startSpinner(`Transpiling TypeScript file: ${filePath}`);
|
|
32
|
+
try {
|
|
33
|
+
buildSync({
|
|
34
|
+
entryPoints: [filePath],
|
|
35
|
+
outfile: outFile,
|
|
36
|
+
bundle: false,
|
|
37
|
+
platform: "node",
|
|
38
|
+
format: "esm",
|
|
39
|
+
target: "es2020",
|
|
40
|
+
sourcemap: false,
|
|
41
|
+
});
|
|
42
|
+
await this.executeJsScript(filePath, outFile);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
this.failSpinner(`Error executing: ${filePath}`, error);
|
|
45
|
+
} finally {
|
|
46
|
+
fs.unlinkSync(outFile);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
36
49
|
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
private async executeJsScript(filePath: string, transpiledFilePath?: string): Promise<void> {
|
|
51
|
+
this.startSpinner(`Executing file: ${filePath}`);
|
|
52
|
+
try {
|
|
53
|
+
const module = await import(pathToFileURL(transpiledFilePath || filePath).href);
|
|
54
|
+
if (!module.default || typeof module.default !== "function") {
|
|
55
|
+
this.failSpinner(`No "default" function found in: ${filePath}`);
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
const client = await this.getClient();
|
|
59
|
+
await module.default(client);
|
|
60
|
+
this.succeedSpinner(`Successfully executed: ${filePath}`);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
this.failSpinner(`Error executing: ${filePath}`, error);
|
|
39
63
|
}
|
|
64
|
+
}
|
|
40
65
|
|
|
41
|
-
|
|
42
|
-
|
|
66
|
+
async deployScripts() {
|
|
67
|
+
this.startSpinner("Searching for deploy scripts...");
|
|
68
|
+
if (!fs.existsSync(this.deployDir)) {
|
|
69
|
+
this.failSpinner("No deploy folder found.");
|
|
43
70
|
return;
|
|
44
71
|
}
|
|
72
|
+
const files = fs.readdirSync(this.deployDir)
|
|
73
|
+
.filter(file => file.endsWith(".ts") || file.endsWith(".js"))
|
|
74
|
+
.sort((a, b) => {
|
|
75
|
+
const numA = parseInt(a.split("_")[0]);
|
|
76
|
+
const numB = parseInt(b.split("_")[0]);
|
|
45
77
|
|
|
46
|
-
|
|
78
|
+
if (!isNaN(numA) && !isNaN(numB)) return numA - numB;
|
|
79
|
+
if (!isNaN(numA)) return -1;
|
|
80
|
+
if (!isNaN(numB)) return 1;
|
|
81
|
+
return a.localeCompare(b);
|
|
82
|
+
});
|
|
47
83
|
|
|
48
|
-
if (
|
|
49
|
-
|
|
84
|
+
if (files.length === 0) {
|
|
85
|
+
this.failSpinner("No deploy scripts found.");
|
|
50
86
|
return;
|
|
51
87
|
}
|
|
52
88
|
|
|
53
|
-
|
|
54
|
-
let deployParams: any = { code: contractCode, args: options.args, leaderOnly };
|
|
89
|
+
this.setSpinnerText(`Found ${files.length} deploy scripts. Executing...`);
|
|
55
90
|
|
|
56
|
-
|
|
57
|
-
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
const filePath = path.resolve(this.deployDir, file);
|
|
93
|
+
this.setSpinnerText(`Executing script: ${filePath}`);
|
|
94
|
+
try {
|
|
95
|
+
if (file.endsWith(".ts")) {
|
|
96
|
+
await this.executeTsScript(filePath);
|
|
97
|
+
} else {
|
|
98
|
+
await this.executeJsScript(filePath);
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
this.failSpinner(`Error executing script: ${filePath}`, error);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
58
105
|
|
|
106
|
+
async deploy(options: DeployOptions): Promise<void> {
|
|
59
107
|
try {
|
|
60
|
-
const
|
|
108
|
+
const client = await this.getClient();
|
|
109
|
+
this.startSpinner("Setting up the deployment environment...");
|
|
110
|
+
await client.initializeConsensusSmartContract();
|
|
111
|
+
|
|
112
|
+
if (!options.contract) {
|
|
113
|
+
this.failSpinner("No contract specified for deployment.");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.setSpinnerText("Reading contract code...");
|
|
117
|
+
const contractCode = this.readContractCode(options.contract);
|
|
118
|
+
|
|
119
|
+
if (!contractCode) {
|
|
120
|
+
this.failSpinner("Contract code is empty.");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const leaderOnly = false;
|
|
125
|
+
const deployParams: any = { code: contractCode, args: options.args, leaderOnly };
|
|
126
|
+
|
|
127
|
+
this.setSpinnerText("Starting contract deployment...");
|
|
128
|
+
this.log("Deployment Parameters:", deployParams);
|
|
129
|
+
|
|
130
|
+
const hash = (await client.deployContract(deployParams)) as any;
|
|
131
|
+
const result = await client.waitForTransactionReceipt({
|
|
132
|
+
hash,
|
|
133
|
+
retries: 15,
|
|
134
|
+
interval: 2000,
|
|
135
|
+
status: TransactionStatus.ACCEPTED,
|
|
136
|
+
});
|
|
61
137
|
|
|
62
|
-
|
|
138
|
+
this.log("Deployment Receipt:", result);
|
|
63
139
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
140
|
+
this.succeedSpinner("Contract deployed successfully.", {
|
|
141
|
+
"Transaction Hash": hash,
|
|
142
|
+
"Contract Address": result.data?.contract_address,
|
|
143
|
+
});
|
|
67
144
|
} catch (error) {
|
|
68
|
-
|
|
69
|
-
throw new Error("Contract deployment failed.");
|
|
145
|
+
this.failSpinner("Error deploying contract", error);
|
|
70
146
|
}
|
|
71
147
|
}
|
|
72
148
|
}
|
|
@@ -2,6 +2,13 @@ import { Command } from "commander";
|
|
|
2
2
|
import { DeployAction, DeployOptions } from "./deploy";
|
|
3
3
|
import { CallAction, CallOptions } from "./call";
|
|
4
4
|
|
|
5
|
+
function parseArg(value: string, previous: any[] = []): any[] {
|
|
6
|
+
if (value === "true") return [...previous, true];
|
|
7
|
+
if (value === "false") return [...previous, false];
|
|
8
|
+
if (!isNaN(Number(value))) return [...previous, Number(value)];
|
|
9
|
+
return [...previous, value];
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
export function initializeContractsCommands(program: Command) {
|
|
6
13
|
|
|
7
14
|
program
|
|
@@ -9,16 +16,20 @@ export function initializeContractsCommands(program: Command) {
|
|
|
9
16
|
.description("Deploy intelligent contracts")
|
|
10
17
|
.option("--contract <contractPath>", "Path to the smart contract to deploy")
|
|
11
18
|
// .option("--network <networkName>", "Specify the network (e.g., testnet)", "localnet")
|
|
12
|
-
.option("--args <args...>", "Positional arguments for the contract (space-separated, use quotes for multi-word arguments)", [])
|
|
19
|
+
.option("--args <args...>", "Positional arguments for the contract (space-separated, use quotes for multi-word arguments)", parseArg, [])
|
|
13
20
|
.action(async (options: DeployOptions) => {
|
|
14
21
|
const deployer = new DeployAction();
|
|
15
|
-
|
|
22
|
+
if(options.contract){
|
|
23
|
+
await deployer.deploy(options);
|
|
24
|
+
}else {
|
|
25
|
+
await deployer.deployScripts();
|
|
26
|
+
}
|
|
16
27
|
});
|
|
17
28
|
|
|
18
29
|
program
|
|
19
30
|
.command("call <contractAddress> <method>")
|
|
20
31
|
.description("Call a contract method")
|
|
21
|
-
.option("--args <args...>", "Positional arguments for the method (space-separated, use quotes for multi-word arguments)", [])
|
|
32
|
+
.option("--args <args...>", "Positional arguments for the method (space-separated, use quotes for multi-word arguments)", parseArg, [])
|
|
22
33
|
.action(async (contractAddress: string, method: string, options: CallOptions) => {
|
|
23
34
|
const caller = new CallAction();
|
|
24
35
|
await caller.call({ contractAddress, method, ...options });
|
|
@@ -94,7 +94,6 @@ export class InitAction extends BaseAction {
|
|
|
94
94
|
const selectedLlmProviders = llmProvidersAnswer.selectedLlmProviders as AiProviders[];
|
|
95
95
|
|
|
96
96
|
let defaultOllamaModel = this.getConfig().defaultOllamaModel;
|
|
97
|
-
AI_PROVIDERS_CONFIG.ollama.hint = `(This will download and run a local instance of ${defaultOllamaModel})`;
|
|
98
97
|
const aiProvidersEnvVars: Record<string, string> = {};
|
|
99
98
|
const configurableAiProviders = selectedLlmProviders.filter(
|
|
100
99
|
(provider: AiProviders) => AI_PROVIDERS_CONFIG[provider].envVar
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { writeFileSync, existsSync } from "fs";
|
|
2
|
-
import { ethers } from "ethers";
|
|
3
1
|
import { BaseAction } from "../../lib/actions/BaseAction";
|
|
4
2
|
|
|
5
3
|
export interface CreateKeypairOptions {
|
|
@@ -7,36 +5,18 @@ export interface CreateKeypairOptions {
|
|
|
7
5
|
overwrite: boolean;
|
|
8
6
|
}
|
|
9
7
|
|
|
10
|
-
export class KeypairCreator extends BaseAction{
|
|
11
|
-
|
|
8
|
+
export class KeypairCreator extends BaseAction {
|
|
12
9
|
constructor() {
|
|
13
|
-
super()
|
|
10
|
+
super();
|
|
14
11
|
}
|
|
15
12
|
|
|
16
13
|
createKeypairAction(options: CreateKeypairOptions) {
|
|
17
14
|
try {
|
|
18
15
|
this.startSpinner(`Creating keypair...`);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if(existsSync(outputPath) && !options.overwrite) {
|
|
22
|
-
this.failSpinner(
|
|
23
|
-
`The file at ${outputPath} already exists. Use the '--overwrite' option to replace it.`
|
|
24
|
-
);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const wallet = ethers.Wallet.createRandom();
|
|
29
|
-
const keypairData = {
|
|
30
|
-
address: wallet.address,
|
|
31
|
-
privateKey: wallet.privateKey,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
writeFileSync(outputPath, JSON.stringify(keypairData, null, 2));
|
|
35
|
-
|
|
36
|
-
this.writeConfig('keyPairPath', outputPath);
|
|
37
|
-
this.succeedSpinner(`Keypair successfully created and saved to: ${outputPath}`);
|
|
16
|
+
this.keypairManager.createKeypair(options.output, options.overwrite);
|
|
17
|
+
this.succeedSpinner(`Keypair successfully created and saved to: ${options.output}`);
|
|
38
18
|
} catch (error) {
|
|
39
|
-
this.failSpinner("Failed to generate keypair
|
|
19
|
+
this.failSpinner("Failed to generate keypair", error);
|
|
40
20
|
}
|
|
41
21
|
}
|
|
42
22
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { NewAction } from "./new";
|
|
3
|
+
|
|
4
|
+
export function initializeScaffoldCommands(program: Command) {
|
|
5
|
+
program
|
|
6
|
+
.command("new <projectName>")
|
|
7
|
+
.description("Create a new GenLayer project using the default template")
|
|
8
|
+
.option("--path <directory>", "Specify the directory for the new project", ".")
|
|
9
|
+
.option("--overwrite", "Overwrite existing directory if it exists", false)
|
|
10
|
+
.action(async (projectName, options) => {
|
|
11
|
+
const newProjectAction = new NewAction();
|
|
12
|
+
await newProjectAction.createProject(projectName, options);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
return program;
|
|
16
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { BaseAction } from "../../lib/actions/BaseAction";
|
|
5
|
+
|
|
6
|
+
export class NewAction extends BaseAction {
|
|
7
|
+
private templatePath: string;
|
|
8
|
+
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const basePath = path.resolve(path.dirname(__filename), "..");
|
|
13
|
+
this.templatePath = path.join(basePath, "templates", "default");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async createProject(projectName: string, options: { path: string; overwrite: boolean }) {
|
|
17
|
+
const targetPath = path.resolve(options.path, projectName);
|
|
18
|
+
|
|
19
|
+
if (fs.existsSync(targetPath) && !options.overwrite) {
|
|
20
|
+
return this.failSpinner(
|
|
21
|
+
`Project directory "${targetPath}" already exists. Use --overwrite to replace it.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
this.startSpinner(`Creating new GenLayer project: ${projectName}`);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
fs.copySync(this.templatePath, targetPath);
|
|
29
|
+
this.succeedSpinner(`Project "${projectName}" created successfully at ${targetPath}`);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
this.failSpinner(`Error creating project "${projectName}"`, error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { initializeContractsCommands } from "../src/commands/contracts";
|
|
|
8
8
|
import { initializeConfigCommands } from "../src/commands/config";
|
|
9
9
|
import {initializeValidatorCommands} from "../src/commands/validators";
|
|
10
10
|
import { initializeUpdateCommands } from "../src/commands/update";
|
|
11
|
+
import { initializeScaffoldCommands } from "../src/commands/scaffold";
|
|
11
12
|
|
|
12
13
|
export function initializeCLI() {
|
|
13
14
|
program.version(version).description(CLI_DESCRIPTION);
|
|
@@ -17,6 +18,7 @@ export function initializeCLI() {
|
|
|
17
18
|
initializeConfigCommands(program);
|
|
18
19
|
initializeUpdateCommands(program)
|
|
19
20
|
initializeValidatorCommands(program);
|
|
21
|
+
initializeScaffoldCommands(program);
|
|
20
22
|
program.parse(process.argv);
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { writeFileSync, existsSync, readFileSync } from "fs";
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { ConfigFileManager } from "../config/ConfigFileManager";
|
|
5
|
+
|
|
6
|
+
export class KeypairManager extends ConfigFileManager {
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getPrivateKey(): string | undefined {
|
|
12
|
+
const keypairPath = this.getConfigByKey("keyPairPath");
|
|
13
|
+
|
|
14
|
+
if (!keypairPath || !existsSync(keypairPath)) {
|
|
15
|
+
return ""
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const keypairData = JSON.parse(readFileSync(keypairPath, "utf-8"));
|
|
19
|
+
|
|
20
|
+
if (!keypairData.privateKey) {
|
|
21
|
+
return ""
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return keypairData.privateKey;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
createKeypair(outputPath = "./keypair.json", overwrite: boolean = false): void {
|
|
28
|
+
const finalOutputPath = this.getFilePath(outputPath);
|
|
29
|
+
|
|
30
|
+
if(existsSync(finalOutputPath) && !overwrite) {
|
|
31
|
+
throw new Error(`The file at ${finalOutputPath} already exists. Use the '--overwrite' option to replace it.`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const wallet = ethers.Wallet.createRandom();
|
|
35
|
+
const keypairData = {
|
|
36
|
+
address: wallet.address,
|
|
37
|
+
privateKey: wallet.privateKey,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
writeFileSync(finalOutputPath, JSON.stringify(keypairData, null, 2));
|
|
41
|
+
this.writeConfig('keyPairPath', finalOutputPath);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -2,14 +2,41 @@ import { ConfigFileManager } from "../../lib/config/ConfigFileManager";
|
|
|
2
2
|
import ora, { Ora } from "ora";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import inquirer from "inquirer";
|
|
5
|
-
|
|
5
|
+
import { KeypairManager } from "../accounts/KeypairManager";
|
|
6
|
+
import { createClient, createAccount } from "genlayer-js";
|
|
7
|
+
import { localnet } from "genlayer-js/chains";
|
|
8
|
+
import type { GenLayerClient } from "genlayer-js/types";
|
|
6
9
|
|
|
7
10
|
export class BaseAction extends ConfigFileManager {
|
|
11
|
+
protected keypairManager: KeypairManager;
|
|
8
12
|
private spinner: Ora;
|
|
13
|
+
private _genlayerClient: GenLayerClient<typeof localnet> | null = null;
|
|
9
14
|
|
|
10
15
|
constructor() {
|
|
11
16
|
super()
|
|
12
17
|
this.spinner = ora({ text: "", spinner: "dots" });
|
|
18
|
+
this.keypairManager = new KeypairManager();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected async getClient(): Promise<GenLayerClient<typeof localnet>> {
|
|
22
|
+
if (!this._genlayerClient) {
|
|
23
|
+
this._genlayerClient = createClient({
|
|
24
|
+
chain: localnet,
|
|
25
|
+
endpoint: process.env.VITE_JSON_RPC_SERVER_URL,
|
|
26
|
+
account: createAccount(await this.getPrivateKey() as any),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return this._genlayerClient;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
protected async getPrivateKey() {
|
|
33
|
+
const privateKey = this.keypairManager.getPrivateKey();
|
|
34
|
+
if (privateKey) {
|
|
35
|
+
return privateKey;
|
|
36
|
+
}
|
|
37
|
+
await this.confirmPrompt("Keypair file not found. Would you like to create a new keypair?");
|
|
38
|
+
this.keypairManager.createKeypair();
|
|
39
|
+
return this.keypairManager.getPrivateKey();
|
|
13
40
|
}
|
|
14
41
|
|
|
15
42
|
protected async confirmPrompt(message: string): Promise<void> {
|
|
@@ -47,27 +74,27 @@ export class BaseAction extends ConfigFileManager {
|
|
|
47
74
|
|
|
48
75
|
protected log(message: string, data?: any): void {
|
|
49
76
|
console.log(chalk.white(`\n${message}`));
|
|
50
|
-
if (data) console.log(this.formatOutput(data));
|
|
77
|
+
if (data !== undefined) console.log(this.formatOutput(data));
|
|
51
78
|
}
|
|
52
79
|
|
|
53
80
|
protected logSuccess(message: string, data?: any): void {
|
|
54
81
|
console.log(chalk.green(`\n✔ ${message}`));
|
|
55
|
-
if (data) console.log(chalk.green(this.formatOutput(data)));
|
|
82
|
+
if (data !== undefined) console.log(chalk.green(this.formatOutput(data)));
|
|
56
83
|
}
|
|
57
84
|
|
|
58
85
|
protected logInfo(message: string, data?: any): void {
|
|
59
86
|
console.log(chalk.blue(`\nℹ ${message}`));
|
|
60
|
-
if (data) console.log(chalk.blue(this.formatOutput(data)));
|
|
87
|
+
if (data !== undefined) console.log(chalk.blue(this.formatOutput(data)));
|
|
61
88
|
}
|
|
62
89
|
|
|
63
90
|
protected logWarning(message: string, data?: any): void {
|
|
64
91
|
console.log(chalk.yellow(`\n⚠ ${message}`));
|
|
65
|
-
if (data) console.log(chalk.yellow(this.formatOutput(data)));
|
|
92
|
+
if (data !== undefined) console.log(chalk.yellow(this.formatOutput(data)));
|
|
66
93
|
}
|
|
67
94
|
|
|
68
95
|
protected logError(message: string, error?: any): void {
|
|
69
96
|
console.error(chalk.red(`\n✖ ${message}`));
|
|
70
|
-
if (error) console.error(chalk.red(this.formatOutput(error)));
|
|
97
|
+
if (error !== undefined) console.error(chalk.red(this.formatOutput(error)));
|
|
71
98
|
}
|
|
72
99
|
|
|
73
100
|
protected startSpinner(message: string) {
|
|
@@ -76,7 +103,7 @@ export class BaseAction extends ConfigFileManager {
|
|
|
76
103
|
}
|
|
77
104
|
|
|
78
105
|
protected succeedSpinner(message: string, data?: any): void {
|
|
79
|
-
if (data) this.log('Result:', data);
|
|
106
|
+
if (data !== undefined) this.log('Result:', data);
|
|
80
107
|
this.spinner.succeed(chalk.green(message));
|
|
81
108
|
}
|
|
82
109
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const localnetCompatibleVersion = "v0.
|
|
1
|
+
export const localnetCompatibleVersion = "v0.51.0";
|
|
2
2
|
export const DEFAULT_JSON_RPC_URL = "http://localhost:4000/api";
|
|
3
3
|
export const CONTAINERS_NAME_PREFIX = "/genlayer-";
|
|
4
4
|
export const IMAGES_NAME_PREFIX = "yeagerai";
|
|
@@ -32,7 +32,7 @@ export type AiProvidersConfigType = {
|
|
|
32
32
|
export const AI_PROVIDERS_CONFIG: AiProvidersConfigType = {
|
|
33
33
|
ollama: {
|
|
34
34
|
name: "Ollama",
|
|
35
|
-
hint: "(
|
|
35
|
+
hint: "(By default, this will download and run a local instance of Llama 3)",
|
|
36
36
|
cliOptionValue: "ollama",
|
|
37
37
|
},
|
|
38
38
|
openai: {
|