Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ FRONTEND_BUILD_TARGET=final
VITE_JSON_RPC_SERVER_URL='http://127.0.0.1:4000/api' # if VITE_PROXY_ENABLED = 'true' change to '/api'
VITE_WS_SERVER_URL= 'ws://127.0.0.1:4000' # if VITE_PROXY_ENABLED = 'true' change to '/'
VITE_PLAUSIBLE_DOMAIN='studio.genlayer.com'
VITE_FINALITY_WINDOW_APPEAL_FAILED_REDUCTION=0.2


# GenVM Configuration
Expand Down Expand Up @@ -77,6 +78,9 @@ FRONTEND_BUILD_TARGET = 'final' # change to 'dev' to run in dev mode
HARDHAT_URL = 'http://hardhat'
HARDHAT_PORT = '8545'
HARDHAT_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
HARDHAT_CHAIN_ID=61999
COMPOSE_PROFILES='hardhat'

BACKEND_BUILD_TARGET = 'debug'
VITE_FINALITY_WINDOW=1
DEFAULT_CONSENSUS_MAX_ROTATIONS=3
2 changes: 1 addition & 1 deletion esbuild.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default {
banner: {
js: `const _importMetaUrl = new URL(import.meta.url).pathname;`,
},
external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "fs-extra"]
external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "fs-extra", "esbuild"]
},
watch: true,
};
2 changes: 1 addition & 1 deletion esbuild.config.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default {
banner: {
js: `const _importMetaUrl = new URL(import.meta.url).pathname;`,
},
external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "fs-extra"]
external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "fs-extra", "esbuild"]
},
watch: false,
};
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"dotenv": "^16.4.5",
"ethers": "^6.13.4",
"fs-extra": "^11.3.0",
"genlayer-js": "^0.6.0",
"genlayer-js": "^0.9.0",
"inquirer": "^12.0.0",
"node-fetch": "^3.0.0",
"open": "^10.1.0",
Expand Down
30 changes: 19 additions & 11 deletions src/commands/contracts/call.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import { createClient, createAccount } from "genlayer-js";
import { simulator } from "genlayer-js/chains";
import type { GenLayerClient } from "genlayer-js/types";
import { getPrivateKey } from "../../lib/accounts/getPrivateKey";
import { BaseAction } from "../../lib/actions/BaseAction";

export interface CallOptions {
args: any[];
}

export class CallAction extends BaseAction{
private genlayerClient: GenLayerClient<typeof simulator>;
private _genlayerClient: GenLayerClient<typeof simulator> | null = null;

constructor() {
super();
this.genlayerClient = createClient({
chain: simulator,
endpoint: process.env.VITE_JSON_RPC_SERVER_URL,
account: createAccount(getPrivateKey() as any),
});
}

private async getClient(): Promise<GenLayerClient<typeof simulator>> {
if (!this._genlayerClient) {
this._genlayerClient = createClient({
chain: simulator,
endpoint: process.env.VITE_JSON_RPC_SERVER_URL,
account: createAccount(await this.getPrivateKey() as any),
});
}
return this._genlayerClient;
}

async call({
Expand All @@ -29,9 +34,10 @@ export class CallAction extends BaseAction{
method: string;
args: any[];
}): Promise<void> {
const client = await this.getClient();
this.startSpinner(`Calling method ${method} on contract at ${contractAddress}...`);

const contractSchema = await this.genlayerClient.getContractSchema(contractAddress);
const contractSchema = await client.getContractSchema(contractAddress);

if(!contractSchema.methods.hasOwnProperty(method)){
this.failSpinner(`method ${method} not found.`);
Expand All @@ -50,7 +56,8 @@ export class CallAction extends BaseAction{

private async executeRead(contractAddress: string, method: string, args: any[]): Promise<void> {
try {
const result = await this.genlayerClient.readContract({
const client = await this.getClient();
const result = await client.readContract({
address: contractAddress as any,
functionName: method,
args,
Expand All @@ -63,13 +70,14 @@ export class CallAction extends BaseAction{

private async executeWrite(contractAddress: string, method: string, args: any[]): Promise<void> {
try {
const hash = await this.genlayerClient.writeContract({
const client = await this.getClient();
const hash = await client.writeContract({
address: contractAddress as any,
functionName: method,
args,
value: 0n,
});
const result = await this.genlayerClient.waitForTransactionReceipt({
const result = await client.waitForTransactionReceipt({
hash,
retries: 15,
interval: 2000,
Expand Down
29 changes: 18 additions & 11 deletions src/commands/contracts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import path from "path";
import { createClient, createAccount } from "genlayer-js";
import { simulator } from "genlayer-js/chains";
import type { GenLayerClient } from "genlayer-js/types";
import { getPrivateKey } from "../../lib/accounts/getPrivateKey";
import { BaseAction } from "../../lib/actions/BaseAction";
import { pathToFileURL } from "url";
import { TransactionStatus } from "genlayer-js/types";
Expand All @@ -15,16 +14,22 @@ export interface DeployOptions {
}

export class DeployAction extends BaseAction {
private genlayerClient: GenLayerClient<typeof simulator>;
private _genlayerClient: GenLayerClient<typeof simulator> | null = null;
private readonly deployDir = path.resolve(process.cwd(), "deploy");

constructor() {
super();
this.genlayerClient = createClient({
chain: simulator,
endpoint: process.env.VITE_JSON_RPC_SERVER_URL,
account: createAccount(getPrivateKey() as any),
});
}

private async getClient(): Promise<GenLayerClient<typeof simulator>> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@epsjunior Can we implement this getClient method in the base action and reuse it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds better, I'll do it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if (!this._genlayerClient) {
this._genlayerClient = createClient({
chain: simulator,
endpoint: process.env.VITE_JSON_RPC_SERVER_URL,
account: createAccount(await this.getPrivateKey() as any),
});
}
return this._genlayerClient;
}

private readContractCode(contractPath: string): string {
Expand Down Expand Up @@ -63,7 +68,8 @@ export class DeployAction extends BaseAction {
this.failSpinner(`No "default" function found in: ${filePath}`);
return
}
await module.default(this.genlayerClient);
const client = await this.getClient();
await module.default(client);
this.succeedSpinner(`Successfully executed: ${filePath}`);
} catch (error) {
this.failSpinner(`Error executing: ${filePath}`, error);
Expand Down Expand Up @@ -112,8 +118,9 @@ export class DeployAction extends BaseAction {

async deploy(options: DeployOptions): Promise<void> {
try {
const client = await this.getClient();
this.startSpinner("Setting up the deployment environment...");
await this.genlayerClient.initializeConsensusSmartContract();
await client.initializeConsensusSmartContract();

if (!options.contract) {
this.failSpinner("No contract specified for deployment.");
Expand All @@ -133,8 +140,8 @@ export class DeployAction extends BaseAction {
this.setSpinnerText("Starting contract deployment...");
this.log("Deployment Parameters:", deployParams);

const hash = (await this.genlayerClient.deployContract(deployParams)) as any;
const result = await this.genlayerClient.waitForTransactionReceipt({
const hash = (await client.deployContract(deployParams)) as any;
const result = await client.waitForTransactionReceipt({
hash,
retries: 15,
interval: 2000,
Expand Down
30 changes: 5 additions & 25 deletions src/commands/keygen/create.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,22 @@
import { writeFileSync, existsSync } from "fs";
import { ethers } from "ethers";
import { BaseAction } from "../../lib/actions/BaseAction";

export interface CreateKeypairOptions {
output: string;
overwrite: boolean;
}

export class KeypairCreator extends BaseAction{

export class KeypairCreator extends BaseAction {
constructor() {
super()
super();
}

createKeypairAction(options: CreateKeypairOptions) {
try {
this.startSpinner(`Creating keypair...`);
const outputPath = this.getFilePath(options.output);

if(existsSync(outputPath) && !options.overwrite) {
this.failSpinner(
`The file at ${outputPath} already exists. Use the '--overwrite' option to replace it.`
);
return;
}

const wallet = ethers.Wallet.createRandom();
const keypairData = {
address: wallet.address,
privateKey: wallet.privateKey,
};

writeFileSync(outputPath, JSON.stringify(keypairData, null, 2));

this.writeConfig('keyPairPath', outputPath);
this.succeedSpinner(`Keypair successfully created and saved to: ${outputPath}`);
this.keypairManager.createKeypair(options.output, options.overwrite);
this.succeedSpinner(`Keypair successfully created and saved to: ${options.output}`);
} catch (error) {
this.failSpinner("Failed to generate keypair:", error);
this.failSpinner("Failed to generate keypair", error);
}
}
}
43 changes: 43 additions & 0 deletions src/lib/accounts/KeypairManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { writeFileSync, existsSync, readFileSync } from "fs";
import { ethers } from "ethers";
import path from "path";
import { ConfigFileManager } from "../config/ConfigFileManager";

export class KeypairManager extends ConfigFileManager {
constructor() {
super();
}

getPrivateKey(): string | undefined {
const keypairPath = this.getConfigByKey("keyPairPath");

if (!keypairPath || !existsSync(keypairPath)) {
return ""
}

const keypairData = JSON.parse(readFileSync(keypairPath, "utf-8"));

if (!keypairData.privateKey) {
return ""
}

return keypairData.privateKey;
}

createKeypair(outputPath = "./keypair.json", overwrite: boolean = false): void {
const finalOutputPath = this.getFilePath(outputPath);

if(existsSync(finalOutputPath) && !overwrite) {
throw new Error(`The file at ${finalOutputPath} already exists. Use the '--overwrite' option to replace it.`);
}

const wallet = ethers.Wallet.createRandom();
const keypairData = {
address: wallet.address,
privateKey: wallet.privateKey,
};

writeFileSync(finalOutputPath, JSON.stringify(keypairData, null, 2));
this.writeConfig('keyPairPath', finalOutputPath);
}
}
21 changes: 0 additions & 21 deletions src/lib/accounts/getPrivateKey.ts

This file was deleted.

14 changes: 13 additions & 1 deletion src/lib/actions/BaseAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,26 @@ import { ConfigFileManager } from "../../lib/config/ConfigFileManager";
import ora, { Ora } from "ora";
import chalk from "chalk";
import inquirer from "inquirer";

import { KeypairManager } from "../accounts/KeypairManager";

export class BaseAction extends ConfigFileManager {
private spinner: Ora;
protected keypairManager: KeypairManager;

constructor() {
super()
this.spinner = ora({ text: "", spinner: "dots" });
this.keypairManager = new KeypairManager();
}

protected async getPrivateKey() {
const privateKey = this.keypairManager.getPrivateKey();
if (privateKey) {
return privateKey;
}
await this.confirmPrompt("Keypair file not found. Would you like to create a new keypair?");
this.keypairManager.createKeypair();
return this.keypairManager.getPrivateKey();
}

protected async confirmPrompt(message: string): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/config/simulator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const localnetCompatibleVersion = "v0.42.0";
export const localnetCompatibleVersion = "v0.51.0";
export const DEFAULT_JSON_RPC_URL = "http://localhost:4000/api";
export const CONTAINERS_NAME_PREFIX = "/genlayer-";
export const IMAGES_NAME_PREFIX = "yeagerai";
Expand Down
4 changes: 1 addition & 3 deletions tests/actions/call.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { describe, test, vi, beforeEach, afterEach, expect } from "vitest";
import { createClient, createAccount } from "genlayer-js";
import { CallAction } from "../../src/commands/contracts/call";
import { getPrivateKey } from "../../src/lib/accounts/getPrivateKey";

vi.mock("genlayer-js");
vi.mock("../../src/lib/accounts/getPrivateKey");

describe("CallAction", () => {
let callActions: CallAction;
Expand All @@ -21,8 +19,8 @@ describe("CallAction", () => {
vi.clearAllMocks();
vi.mocked(createClient).mockReturnValue(mockClient as any);
vi.mocked(createAccount).mockReturnValue({ privateKey: mockPrivateKey } as any);
vi.mocked(getPrivateKey).mockReturnValue(mockPrivateKey);
callActions = new CallAction();
vi.spyOn(callActions as any, "getPrivateKey").mockResolvedValue(mockPrivateKey);

vi.spyOn(callActions as any, "startSpinner").mockImplementation(() => {});
vi.spyOn(callActions as any, "succeedSpinner").mockImplementation(() => {});
Expand Down
Loading