genlayer 0.30.0 → 0.32.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/CLAUDE.md +55 -0
  3. package/README.md +121 -8
  4. package/dist/index.js +8424 -2489
  5. package/docs/delegator-guide.md +203 -0
  6. package/docs/validator-guide.md +260 -0
  7. package/package.json +2 -2
  8. package/src/commands/account/create.ts +23 -0
  9. package/src/commands/account/import.ts +81 -0
  10. package/src/commands/account/index.ts +67 -0
  11. package/src/commands/{keygen → account}/lock.ts +6 -7
  12. package/src/commands/account/send.ts +150 -0
  13. package/src/commands/account/show.ts +60 -0
  14. package/src/commands/{keygen → account}/unlock.ts +12 -12
  15. package/src/commands/contracts/code.ts +33 -0
  16. package/src/commands/contracts/index.ts +10 -0
  17. package/src/commands/network/setNetwork.ts +5 -4
  18. package/src/commands/staking/StakingAction.ts +125 -0
  19. package/src/commands/staking/delegatorClaim.ts +41 -0
  20. package/src/commands/staking/delegatorExit.ts +50 -0
  21. package/src/commands/staking/delegatorJoin.ts +42 -0
  22. package/src/commands/staking/index.ts +224 -0
  23. package/src/commands/staking/setIdentity.ts +61 -0
  24. package/src/commands/staking/setOperator.ts +40 -0
  25. package/src/commands/staking/stakingInfo.ts +292 -0
  26. package/src/commands/staking/validatorClaim.ts +38 -0
  27. package/src/commands/staking/validatorDeposit.ts +35 -0
  28. package/src/commands/staking/validatorExit.ts +44 -0
  29. package/src/commands/staking/validatorJoin.ts +47 -0
  30. package/src/commands/staking/validatorPrime.ts +35 -0
  31. package/src/index.ts +4 -2
  32. package/src/lib/actions/BaseAction.ts +43 -10
  33. package/tests/actions/code.test.ts +87 -0
  34. package/tests/actions/create.test.ts +18 -18
  35. package/tests/actions/lock.test.ts +7 -7
  36. package/tests/actions/setNetwork.test.ts +18 -57
  37. package/tests/actions/staking.test.ts +323 -0
  38. package/tests/actions/unlock.test.ts +12 -12
  39. package/tests/commands/account.test.ts +121 -0
  40. package/tests/commands/code.test.ts +69 -0
  41. package/tests/commands/staking.test.ts +211 -0
  42. package/tests/index.test.ts +6 -2
  43. package/tests/libs/baseAction.test.ts +7 -1
  44. package/src/commands/keygen/create.ts +0 -23
  45. package/src/commands/keygen/index.ts +0 -39
  46. package/tests/commands/keygen.test.ts +0 -123
@@ -0,0 +1,35 @@
1
+ import {StakingAction, StakingConfig} from "./StakingAction";
2
+
3
+ export interface ValidatorDepositOptions extends StakingConfig {
4
+ amount: string;
5
+ }
6
+
7
+ export class ValidatorDepositAction extends StakingAction {
8
+ constructor() {
9
+ super();
10
+ }
11
+
12
+ async execute(options: ValidatorDepositOptions): Promise<void> {
13
+ this.startSpinner("Making validator deposit...");
14
+
15
+ try {
16
+ const client = await this.getStakingClient(options);
17
+ const amount = this.parseAmount(options.amount);
18
+
19
+ this.setSpinnerText(`Depositing ${this.formatAmount(amount)} to validator stake...`);
20
+
21
+ const result = await client.validatorDeposit({amount});
22
+
23
+ const output = {
24
+ transactionHash: result.transactionHash,
25
+ amount: this.formatAmount(amount),
26
+ blockNumber: result.blockNumber.toString(),
27
+ gasUsed: result.gasUsed.toString(),
28
+ };
29
+
30
+ this.succeedSpinner("Deposit successful!", output);
31
+ } catch (error: any) {
32
+ this.failSpinner("Failed to make deposit", error.message || error);
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,44 @@
1
+ import {StakingAction, StakingConfig} from "./StakingAction";
2
+
3
+ export interface ValidatorExitOptions extends StakingConfig {
4
+ shares: string;
5
+ }
6
+
7
+ export class ValidatorExitAction extends StakingAction {
8
+ constructor() {
9
+ super();
10
+ }
11
+
12
+ async execute(options: ValidatorExitOptions): Promise<void> {
13
+ this.startSpinner("Initiating validator exit...");
14
+
15
+ try {
16
+ let shares: bigint;
17
+ try {
18
+ shares = BigInt(options.shares);
19
+ if (shares <= 0n) throw new Error("must be positive");
20
+ } catch {
21
+ this.failSpinner(`Invalid shares value: "${options.shares}". Must be a positive whole number.`);
22
+ return;
23
+ }
24
+
25
+ const client = await this.getStakingClient(options);
26
+
27
+ this.setSpinnerText(`Exiting with ${shares} shares...`);
28
+
29
+ const result = await client.validatorExit({shares});
30
+
31
+ const output = {
32
+ transactionHash: result.transactionHash,
33
+ sharesWithdrawn: shares.toString(),
34
+ blockNumber: result.blockNumber.toString(),
35
+ gasUsed: result.gasUsed.toString(),
36
+ note: "Withdrawal will be claimable after the unbonding period",
37
+ };
38
+
39
+ this.succeedSpinner("Exit initiated successfully!", output);
40
+ } catch (error: any) {
41
+ this.failSpinner("Failed to exit", error.message || error);
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,47 @@
1
+ import {StakingAction, StakingConfig} from "./StakingAction";
2
+ import type {Address} from "genlayer-js/types";
3
+
4
+ export interface ValidatorJoinOptions extends StakingConfig {
5
+ amount: string;
6
+ operator?: string;
7
+ }
8
+
9
+ export class ValidatorJoinAction extends StakingAction {
10
+ constructor() {
11
+ super();
12
+ }
13
+
14
+ async execute(options: ValidatorJoinOptions): Promise<void> {
15
+ this.startSpinner("Creating a new validator...");
16
+
17
+ try {
18
+ const client = await this.getStakingClient(options);
19
+ const amount = this.parseAmount(options.amount);
20
+ const signerAddress = await this.getSignerAddress();
21
+
22
+ this.setSpinnerText(`Creating validator with ${this.formatAmount(amount)} stake...`);
23
+ this.log(` From: ${signerAddress}`);
24
+ if (options.operator) {
25
+ this.log(` Operator: ${options.operator}`);
26
+ }
27
+
28
+ const result = await client.validatorJoin({
29
+ amount,
30
+ operator: options.operator as Address | undefined,
31
+ });
32
+
33
+ const output = {
34
+ transactionHash: result.transactionHash,
35
+ validatorWallet: result.validatorWallet,
36
+ amount: result.amount,
37
+ operator: result.operator,
38
+ blockNumber: result.blockNumber.toString(),
39
+ gasUsed: result.gasUsed.toString(),
40
+ };
41
+
42
+ this.succeedSpinner("Validator created successfully!", output);
43
+ } catch (error: any) {
44
+ this.failSpinner("Failed to create validator", error.message || error);
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,35 @@
1
+ import {StakingAction, StakingConfig} from "./StakingAction";
2
+ import type {Address} from "genlayer-js/types";
3
+
4
+ export interface ValidatorPrimeOptions extends StakingConfig {
5
+ validator: string;
6
+ }
7
+
8
+ export class ValidatorPrimeAction extends StakingAction {
9
+ constructor() {
10
+ super();
11
+ }
12
+
13
+ async execute(options: ValidatorPrimeOptions): Promise<void> {
14
+ this.startSpinner("Priming validator...");
15
+
16
+ try {
17
+ const client = await this.getStakingClient(options);
18
+
19
+ this.setSpinnerText(`Priming validator ${options.validator}...`);
20
+
21
+ const result = await client.validatorPrime({validator: options.validator as Address});
22
+
23
+ const output = {
24
+ transactionHash: result.transactionHash,
25
+ validator: options.validator,
26
+ blockNumber: result.blockNumber.toString(),
27
+ gasUsed: result.gasUsed.toString(),
28
+ };
29
+
30
+ this.succeedSpinner("Validator primed for next epoch!", output);
31
+ } catch (error: any) {
32
+ this.failSpinner("Failed to prime validator", error.message || error);
33
+ }
34
+ }
35
+ }
package/src/index.ts CHANGED
@@ -3,7 +3,7 @@ import {program} from "commander";
3
3
  import {version} from "../package.json";
4
4
  import {CLI_DESCRIPTION} from "../src/lib/config/text";
5
5
  import {initializeGeneralCommands} from "../src/commands/general";
6
- import {initializeKeygenCommands} from "../src/commands/keygen";
6
+ import {initializeAccountCommands} from "../src/commands/account";
7
7
  import {initializeContractsCommands} from "../src/commands/contracts";
8
8
  import {initializeConfigCommands} from "../src/commands/config";
9
9
  import {initializeValidatorCommands} from "../src/commands/localnet";
@@ -11,11 +11,12 @@ import {initializeUpdateCommands} from "../src/commands/update";
11
11
  import {initializeScaffoldCommands} from "../src/commands/scaffold";
12
12
  import {initializeNetworkCommands} from "../src/commands/network";
13
13
  import {initializeTransactionsCommands} from "../src/commands/transactions";
14
+ import {initializeStakingCommands} from "../src/commands/staking";
14
15
 
15
16
  export function initializeCLI() {
16
17
  program.version(version).description(CLI_DESCRIPTION);
17
18
  initializeGeneralCommands(program);
18
- initializeKeygenCommands(program);
19
+ initializeAccountCommands(program);
19
20
  initializeContractsCommands(program);
20
21
  initializeConfigCommands(program);
21
22
  initializeUpdateCommands(program);
@@ -23,6 +24,7 @@ export function initializeCLI() {
23
24
  initializeScaffoldCommands(program);
24
25
  initializeNetworkCommands(program);
25
26
  initializeTransactionsCommands(program);
27
+ initializeStakingCommands(program);
26
28
  program.parse(process.argv);
27
29
  }
28
30
 
@@ -5,8 +5,43 @@ import chalk from "chalk";
5
5
  import inquirer from "inquirer";
6
6
  import { inspect } from "util";
7
7
  import {createClient, createAccount} from "genlayer-js";
8
- import {localnet} from "genlayer-js/chains";
8
+ import {localnet, studionet, testnetAsimov} from "genlayer-js/chains";
9
9
  import type {GenLayerClient, GenLayerChain, Hash, Address, Account} from "genlayer-js/types";
10
+
11
+ // Built-in networks - always resolve fresh from genlayer-js
12
+ export const BUILT_IN_NETWORKS: Record<string, GenLayerChain> = {
13
+ "localnet": localnet,
14
+ "studionet": studionet,
15
+ "testnet-asimov": testnetAsimov,
16
+ };
17
+
18
+ /**
19
+ * Resolves a stored network config to a fresh chain object.
20
+ * Handles both new format (alias string) and old format (JSON object) for backwards compat.
21
+ */
22
+ export function resolveNetwork(stored: string | undefined): GenLayerChain {
23
+ if (!stored) return localnet;
24
+
25
+ // Try as alias first (new format)
26
+ if (BUILT_IN_NETWORKS[stored]) {
27
+ return BUILT_IN_NETWORKS[stored];
28
+ }
29
+
30
+ // Backwards compat: try parsing as JSON (old format)
31
+ try {
32
+ const parsed = JSON.parse(stored);
33
+ // If it has a known name, use fresh version instead
34
+ const alias = Object.entries(BUILT_IN_NETWORKS)
35
+ .find(([_, chain]) => chain.name === parsed.name)?.[0];
36
+ if (alias) {
37
+ return BUILT_IN_NETWORKS[alias];
38
+ }
39
+ // Custom network - use as-is
40
+ return parsed;
41
+ } catch {
42
+ throw new Error(`Unknown network: ${stored}`);
43
+ }
44
+ }
10
45
  import { ethers } from "ethers";
11
46
  import { writeFileSync, existsSync, readFileSync } from "fs";
12
47
  import { KeystoreData } from "../interfaces/KeystoreData";
@@ -38,7 +73,6 @@ export class BaseAction extends ConfigFileManager {
38
73
  } catch (error) {
39
74
  if (attempt >= BaseAction.MAX_PASSWORD_ATTEMPTS) {
40
75
  this.failSpinner(`Maximum password attempts exceeded (${BaseAction.MAX_PASSWORD_ATTEMPTS}/${BaseAction.MAX_PASSWORD_ATTEMPTS}).`);
41
- process.exit(1);
42
76
  }
43
77
  return await this.decryptKeystore(keystoreData, attempt + 1);
44
78
  }
@@ -62,8 +96,7 @@ export class BaseAction extends ConfigFileManager {
62
96
 
63
97
  protected async getClient(rpcUrl?: string, readOnly: boolean = false): Promise<GenLayerClient<GenLayerChain>> {
64
98
  if (!this._genlayerClient) {
65
- const networkConfig = this.getConfig().network;
66
- const network = networkConfig ? JSON.parse(networkConfig) : localnet;
99
+ const network = resolveNetwork(this.getConfig().network);
67
100
  const account = await this.getAccount(readOnly);
68
101
  this._genlayerClient = createClient({
69
102
  chain: network,
@@ -88,7 +121,7 @@ export class BaseAction extends ConfigFileManager {
88
121
  keystoreData = JSON.parse(readFileSync(keypairPath, "utf-8"));
89
122
 
90
123
  if (!this.isValidKeystoreFormat(keystoreData)) {
91
- this.failSpinner("Invalid keystore format. Expected encrypted keystore file.");
124
+ this.failSpinner("Invalid keystore format. Expected encrypted keystore file.", undefined, false);
92
125
  await this.confirmPrompt("Would you like to create a new keypair?");
93
126
  decryptedPrivateKey = await this.createKeypair(BaseAction.DEFAULT_KEYSTORE_PATH, true);
94
127
  keypairPath = this.getConfigByKey("keyPairPath")!;
@@ -116,7 +149,6 @@ export class BaseAction extends ConfigFileManager {
116
149
 
117
150
  if (existsSync(finalOutputPath) && !overwrite) {
118
151
  this.failSpinner(`The file at ${finalOutputPath} already exists. Use the '--overwrite' option to replace it.`);
119
- process.exit(1);
120
152
  }
121
153
 
122
154
  const wallet = ethers.Wallet.createRandom();
@@ -126,12 +158,10 @@ export class BaseAction extends ConfigFileManager {
126
158
 
127
159
  if (password !== confirmPassword) {
128
160
  this.failSpinner("Passwords do not match");
129
- process.exit(1);
130
161
  }
131
162
 
132
163
  if (password.length < BaseAction.MIN_PASSWORD_LENGTH) {
133
164
  this.failSpinner(`Password must be at least ${BaseAction.MIN_PASSWORD_LENGTH} characters long`);
134
- process.exit(1);
135
165
  }
136
166
 
137
167
  const encryptedJson = await wallet.encrypt(password);
@@ -220,10 +250,13 @@ export class BaseAction extends ConfigFileManager {
220
250
  this.spinner.succeed(chalk.green(message));
221
251
  }
222
252
 
223
- protected failSpinner(message: string, error?:any): void {
253
+ protected failSpinner(message: string, error?: any, shouldExit = true): void {
224
254
  if (error) this.log("Error:", error);
225
- console.log('');
255
+ console.log("");
226
256
  this.spinner.fail(chalk.red(message));
257
+ if (shouldExit) {
258
+ process.exit(1);
259
+ }
227
260
  }
228
261
 
229
262
  protected stopSpinner(): void {
@@ -0,0 +1,87 @@
1
+ import {describe, test, vi, beforeEach, afterEach, expect} from "vitest";
2
+ import {createClient, createAccount} from "genlayer-js";
3
+ import {CodeAction} from "../../src/commands/contracts/code";
4
+
5
+ vi.mock("genlayer-js");
6
+
7
+ describe("CodeAction", () => {
8
+ let codeAction: CodeAction;
9
+ const mockClient = {
10
+ getContractCode: vi.fn(),
11
+ initializeConsensusSmartContract: vi.fn(),
12
+ };
13
+
14
+ const mockPrivateKey = "mocked_private_key";
15
+
16
+ beforeEach(() => {
17
+ vi.clearAllMocks();
18
+ vi.mocked(createClient).mockReturnValue(mockClient as any);
19
+ vi.mocked(createAccount).mockReturnValue({privateKey: mockPrivateKey} as any);
20
+ codeAction = new CodeAction();
21
+ vi.spyOn(codeAction as any, "getAccount").mockResolvedValue({privateKey: mockPrivateKey});
22
+
23
+ vi.spyOn(codeAction as any, "startSpinner").mockImplementation(() => {});
24
+ vi.spyOn(codeAction as any, "succeedSpinner").mockImplementation(() => {});
25
+ vi.spyOn(codeAction as any, "failSpinner").mockImplementation(() => {});
26
+ vi.spyOn(codeAction as any, "log").mockImplementation(() => {});
27
+ });
28
+
29
+ afterEach(() => {
30
+ vi.restoreAllMocks();
31
+ });
32
+
33
+ test("gets contract code successfully", async () => {
34
+ const mockResult = "0x600160...";
35
+
36
+ vi.mocked(mockClient.getContractCode).mockResolvedValue(mockResult as any);
37
+
38
+ await codeAction.code({
39
+ contractAddress: "0xMockedContract",
40
+ });
41
+
42
+ expect(mockClient.getContractCode).toHaveBeenCalledWith("0xMockedContract");
43
+ expect(codeAction["succeedSpinner"]).toHaveBeenCalledWith(
44
+ "Contract code retrieved successfully",
45
+ mockResult,
46
+ );
47
+ });
48
+
49
+ test("handles getContractCode errors", async () => {
50
+ vi.mocked(mockClient.getContractCode).mockRejectedValue(new Error("Mocked code error"));
51
+
52
+ await codeAction.code({contractAddress: "0xMockedContract"});
53
+
54
+ expect(codeAction["failSpinner"]).toHaveBeenCalledWith("Error retrieving contract code", expect.any(Error));
55
+ });
56
+
57
+ test("uses custom RPC URL when provided", async () => {
58
+ const mockResult = "0x600160...";
59
+ vi.mocked(mockClient.getContractCode).mockResolvedValue(mockResult as any);
60
+
61
+ await codeAction.code({
62
+ contractAddress: "0xMockedContract",
63
+ rpc: "https://custom-rpc-url.com",
64
+ });
65
+
66
+ expect(createClient).toHaveBeenCalledWith(
67
+ expect.objectContaining({
68
+ endpoint: "https://custom-rpc-url.com",
69
+ }),
70
+ );
71
+ expect(mockClient.getContractCode).toHaveBeenCalledWith("0xMockedContract");
72
+ expect(codeAction["succeedSpinner"]).toHaveBeenCalledWith(
73
+ "Contract code retrieved successfully",
74
+ mockResult,
75
+ );
76
+ });
77
+
78
+ test("initializes consensus smart contract", async () => {
79
+ vi.mocked(mockClient.getContractCode).mockResolvedValue("0x" as any);
80
+
81
+ await codeAction.code({contractAddress: "0xMockedContract"});
82
+
83
+ expect(mockClient.initializeConsensusSmartContract).toHaveBeenCalled();
84
+ });
85
+ });
86
+
87
+
@@ -1,18 +1,18 @@
1
1
  import {describe, test, vi, beforeEach, afterEach, expect} from "vitest";
2
- import {KeypairCreator} from "../../src/commands/keygen/create";
2
+ import {CreateAccountAction} from "../../src/commands/account/create";
3
3
 
4
- describe("KeypairCreator", () => {
5
- let keypairCreator: KeypairCreator;
4
+ describe("CreateAccountAction", () => {
5
+ let createAction: CreateAccountAction;
6
6
 
7
7
  beforeEach(() => {
8
8
  vi.clearAllMocks();
9
- keypairCreator = new KeypairCreator();
10
-
9
+ createAction = new CreateAccountAction();
10
+
11
11
  // Mock the BaseAction methods
12
- vi.spyOn(keypairCreator as any, "startSpinner").mockImplementation(() => {});
13
- vi.spyOn(keypairCreator as any, "succeedSpinner").mockImplementation(() => {});
14
- vi.spyOn(keypairCreator as any, "failSpinner").mockImplementation(() => {});
15
- vi.spyOn(keypairCreator as any, "createKeypair").mockResolvedValue("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef");
12
+ vi.spyOn(createAction as any, "startSpinner").mockImplementation(() => {});
13
+ vi.spyOn(createAction as any, "succeedSpinner").mockImplementation(() => {});
14
+ vi.spyOn(createAction as any, "failSpinner").mockImplementation(() => {});
15
+ vi.spyOn(createAction as any, "createKeypair").mockResolvedValue("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef");
16
16
  });
17
17
 
18
18
  afterEach(() => {
@@ -22,24 +22,24 @@ describe("KeypairCreator", () => {
22
22
  test("successfully creates and saves an encrypted keystore", async () => {
23
23
  const options = {output: "keypair.json", overwrite: false};
24
24
 
25
- await keypairCreator.createKeypairAction(options);
25
+ await createAction.execute(options);
26
26
 
27
- expect(keypairCreator["startSpinner"]).toHaveBeenCalledWith("Creating encrypted keystore...");
28
- expect(keypairCreator["createKeypair"]).toHaveBeenCalledWith(
29
- options.output,
27
+ expect(createAction["startSpinner"]).toHaveBeenCalledWith("Creating encrypted keystore...");
28
+ expect(createAction["createKeypair"]).toHaveBeenCalledWith(
29
+ options.output,
30
30
  options.overwrite
31
31
  );
32
- expect(keypairCreator["succeedSpinner"]).toHaveBeenCalledWith(
33
- "Encrypted keystore successfully created and saved to: keypair.json",
32
+ expect(createAction["succeedSpinner"]).toHaveBeenCalledWith(
33
+ "Account created and saved to: keypair.json",
34
34
  );
35
35
  });
36
36
 
37
37
  test("handles errors during keystore creation", async () => {
38
38
  const mockError = new Error("Mocked creation error");
39
- vi.spyOn(keypairCreator as any, "createKeypair").mockRejectedValue(mockError);
39
+ vi.spyOn(createAction as any, "createKeypair").mockRejectedValue(mockError);
40
40
 
41
- await keypairCreator.createKeypairAction({output: "keypair.json", overwrite: true});
41
+ await createAction.execute({output: "keypair.json", overwrite: true});
42
42
 
43
- expect(keypairCreator["failSpinner"]).toHaveBeenCalledWith("Failed to generate keystore", mockError);
43
+ expect(createAction["failSpinner"]).toHaveBeenCalledWith("Failed to create account", mockError);
44
44
  });
45
45
  });
@@ -1,12 +1,12 @@
1
1
  import {describe, test, vi, beforeEach, afterEach, expect} from "vitest";
2
- import {LockAction} from "../../src/commands/keygen/lock";
2
+ import {LockAccountAction} from "../../src/commands/account/lock";
3
3
 
4
- describe("LockAction", () => {
5
- let lockAction: LockAction;
4
+ describe("LockAccountAction", () => {
5
+ let lockAction: LockAccountAction;
6
6
 
7
7
  beforeEach(() => {
8
8
  vi.clearAllMocks();
9
- lockAction = new LockAction();
9
+ lockAction = new LockAccountAction();
10
10
 
11
11
  // Mock the BaseAction methods
12
12
  vi.spyOn(lockAction as any, "startSpinner").mockImplementation(() => {});
@@ -33,7 +33,7 @@ describe("LockAction", () => {
33
33
  expect(lockAction["keychainManager"].getPrivateKey).toHaveBeenCalled();
34
34
  expect(lockAction["setSpinnerText"]).toHaveBeenCalledWith("Removing private key from OS keychain...");
35
35
  expect(lockAction["keychainManager"].removePrivateKey).toHaveBeenCalled();
36
- expect(lockAction["succeedSpinner"]).toHaveBeenCalledWith("Wallet locked successfully! Your private key has been removed from the OS keychain.");
36
+ expect(lockAction["succeedSpinner"]).toHaveBeenCalledWith("Account locked! Private key removed from OS keychain.");
37
37
  });
38
38
 
39
39
  test("fails when keychain is not available", async () => {
@@ -51,7 +51,7 @@ describe("LockAction", () => {
51
51
 
52
52
  await lockAction.execute();
53
53
 
54
- expect(lockAction["succeedSpinner"]).toHaveBeenCalledWith("Wallet is already locked (no cached key found in OS keychain).");
54
+ expect(lockAction["succeedSpinner"]).toHaveBeenCalledWith("Account is already locked.");
55
55
  expect(lockAction["keychainManager"].removePrivateKey).not.toHaveBeenCalled();
56
56
  });
57
57
 
@@ -61,6 +61,6 @@ describe("LockAction", () => {
61
61
 
62
62
  await lockAction.execute();
63
63
 
64
- expect(lockAction["failSpinner"]).toHaveBeenCalledWith("Failed to lock wallet.", mockError);
64
+ expect(lockAction["failSpinner"]).toHaveBeenCalledWith("Failed to lock account.", mockError);
65
65
  });
66
66
  });
@@ -26,7 +26,7 @@ describe("NetworkActions", () => {
26
26
  test("setNetwork method sets network by valid name", async () => {
27
27
  await networkActions.setNetwork(localnet.name);
28
28
 
29
- expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", JSON.stringify(localnet));
29
+ expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", "localnet");
30
30
  expect(networkActions["succeedSpinner"]).toHaveBeenCalledWith(
31
31
  `Network successfully set to ${localnet.name}`,
32
32
  );
@@ -35,7 +35,7 @@ describe("NetworkActions", () => {
35
35
  test("setNetwork method sets network by valid alias", async () => {
36
36
  await networkActions.setNetwork("localnet");
37
37
 
38
- expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", JSON.stringify(localnet));
38
+ expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", "localnet");
39
39
  expect(networkActions["succeedSpinner"]).toHaveBeenCalledWith(
40
40
  `Network successfully set to ${localnet.name}`,
41
41
  );
@@ -44,7 +44,7 @@ describe("NetworkActions", () => {
44
44
  test("setNetwork method sets studionet by name", async () => {
45
45
  await networkActions.setNetwork(studionet.name);
46
46
 
47
- expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", JSON.stringify(studionet));
47
+ expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", "studionet");
48
48
  expect(networkActions["succeedSpinner"]).toHaveBeenCalledWith(
49
49
  `Network successfully set to ${studionet.name}`,
50
50
  );
@@ -53,7 +53,7 @@ describe("NetworkActions", () => {
53
53
  test("setNetwork method sets studionet by alias", async () => {
54
54
  await networkActions.setNetwork("studionet");
55
55
 
56
- expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", JSON.stringify(studionet));
56
+ expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", "studionet");
57
57
  expect(networkActions["succeedSpinner"]).toHaveBeenCalledWith(
58
58
  `Network successfully set to ${studionet.name}`,
59
59
  );
@@ -62,7 +62,7 @@ describe("NetworkActions", () => {
62
62
  test("setNetwork method sets testnet-asimov by name", async () => {
63
63
  await networkActions.setNetwork(testnetAsimov.name);
64
64
 
65
- expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", JSON.stringify(testnetAsimov));
65
+ expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", "testnet-asimov");
66
66
  expect(networkActions["succeedSpinner"]).toHaveBeenCalledWith(
67
67
  `Network successfully set to ${testnetAsimov.name}`,
68
68
  );
@@ -71,7 +71,7 @@ describe("NetworkActions", () => {
71
71
  test("setNetwork method sets testnet-asimov by alias", async () => {
72
72
  await networkActions.setNetwork("testnet-asimov");
73
73
 
74
- expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", JSON.stringify(testnetAsimov));
74
+ expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", "testnet-asimov");
75
75
  expect(networkActions["succeedSpinner"]).toHaveBeenCalledWith(
76
76
  `Network successfully set to ${testnetAsimov.name}`,
77
77
  );
@@ -94,14 +94,8 @@ describe("NetworkActions", () => {
94
94
  });
95
95
 
96
96
  test("setNetwork method prompts user when no network name provided", async () => {
97
- const mockSelectedNetwork = {
98
- name: localnet.name,
99
- alias: "localnet",
100
- value: localnet,
101
- };
102
-
103
97
  vi.mocked(inquirer.prompt).mockResolvedValue({
104
- selectedNetwork: mockSelectedNetwork,
98
+ selectedNetwork: "localnet",
105
99
  });
106
100
 
107
101
  await networkActions.setNetwork();
@@ -112,74 +106,41 @@ describe("NetworkActions", () => {
112
106
  name: "selectedNetwork",
113
107
  message: "Select which network do you want to use:",
114
108
  choices: [
115
- {
116
- name: localnet.name,
117
- alias: "localnet",
118
- value: localnet,
119
- },
120
- {
121
- name: studionet.name,
122
- alias: "studionet",
123
- value: studionet,
124
- },
125
- {
126
- name: testnetAsimov.name,
127
- alias: "testnet-asimov",
128
- value: testnetAsimov,
129
- },
109
+ {name: localnet.name, value: "localnet"},
110
+ {name: studionet.name, value: "studionet"},
111
+ {name: testnetAsimov.name, value: "testnet-asimov"},
130
112
  ],
131
113
  },
132
114
  ]);
133
- expect(networkActions["writeConfig"]).toHaveBeenCalledWith(
134
- "network",
135
- JSON.stringify(mockSelectedNetwork),
136
- );
115
+ expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", "localnet");
137
116
  expect(networkActions["succeedSpinner"]).toHaveBeenCalledWith(
138
- `Network successfully set to ${mockSelectedNetwork.name}`,
117
+ `Network successfully set to ${localnet.name}`,
139
118
  );
140
119
  });
141
120
 
142
121
  test("setNetwork method handles interactive selection of studionet", async () => {
143
- const mockSelectedNetwork = {
144
- name: studionet.name,
145
- alias: "studionet",
146
- value: studionet,
147
- };
148
-
149
122
  vi.mocked(inquirer.prompt).mockResolvedValue({
150
- selectedNetwork: mockSelectedNetwork,
123
+ selectedNetwork: "studionet",
151
124
  });
152
125
 
153
126
  await networkActions.setNetwork();
154
127
 
155
- expect(networkActions["writeConfig"]).toHaveBeenCalledWith(
156
- "network",
157
- JSON.stringify(mockSelectedNetwork),
158
- );
128
+ expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", "studionet");
159
129
  expect(networkActions["succeedSpinner"]).toHaveBeenCalledWith(
160
- `Network successfully set to ${mockSelectedNetwork.name}`,
130
+ `Network successfully set to ${studionet.name}`,
161
131
  );
162
132
  });
163
133
 
164
134
  test("setNetwork method handles interactive selection of testnet-asimov", async () => {
165
- const mockSelectedNetwork = {
166
- name: testnetAsimov.name,
167
- alias: "testnet-asimov",
168
- value: testnetAsimov,
169
- };
170
-
171
135
  vi.mocked(inquirer.prompt).mockResolvedValue({
172
- selectedNetwork: mockSelectedNetwork,
136
+ selectedNetwork: "testnet-asimov",
173
137
  });
174
138
 
175
139
  await networkActions.setNetwork();
176
140
 
177
- expect(networkActions["writeConfig"]).toHaveBeenCalledWith(
178
- "network",
179
- JSON.stringify(mockSelectedNetwork),
180
- );
141
+ expect(networkActions["writeConfig"]).toHaveBeenCalledWith("network", "testnet-asimov");
181
142
  expect(networkActions["succeedSpinner"]).toHaveBeenCalledWith(
182
- `Network successfully set to ${mockSelectedNetwork.name}`,
143
+ `Network successfully set to ${testnetAsimov.name}`,
183
144
  );
184
145
  });
185
146