genlayer 0.34.0 → 0.34.3

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 (128) hide show
  1. package/dist/index.js +13 -3
  2. package/package.json +11 -2
  3. package/.eslintignore +0 -2
  4. package/.github/workflows/cli-docs.yml +0 -121
  5. package/.github/workflows/publish-beta.yml +0 -41
  6. package/.github/workflows/publish.yml +0 -43
  7. package/.github/workflows/validate-code.yml +0 -47
  8. package/.prettierignore +0 -19
  9. package/.prettierrc +0 -12
  10. package/.release-it.json +0 -64
  11. package/CHANGELOG.md +0 -425
  12. package/CLAUDE.md +0 -55
  13. package/CONTRIBUTING.md +0 -117
  14. package/docker-compose.yml +0 -154
  15. package/docs/delegator-guide.md +0 -203
  16. package/docs/validator-guide.md +0 -329
  17. package/esbuild.config.dev.js +0 -17
  18. package/esbuild.config.js +0 -22
  19. package/esbuild.config.prod.js +0 -17
  20. package/eslint.config.js +0 -60
  21. package/renovate.json +0 -22
  22. package/src/commands/account/create.ts +0 -30
  23. package/src/commands/account/export.ts +0 -106
  24. package/src/commands/account/import.ts +0 -135
  25. package/src/commands/account/index.ts +0 -129
  26. package/src/commands/account/list.ts +0 -34
  27. package/src/commands/account/lock.ts +0 -39
  28. package/src/commands/account/remove.ts +0 -30
  29. package/src/commands/account/send.ts +0 -162
  30. package/src/commands/account/show.ts +0 -74
  31. package/src/commands/account/unlock.ts +0 -56
  32. package/src/commands/account/use.ts +0 -21
  33. package/src/commands/config/getSetReset.ts +0 -51
  34. package/src/commands/config/index.ts +0 -30
  35. package/src/commands/contracts/call.ts +0 -39
  36. package/src/commands/contracts/code.ts +0 -33
  37. package/src/commands/contracts/deploy.ts +0 -157
  38. package/src/commands/contracts/index.ts +0 -86
  39. package/src/commands/contracts/schema.ts +0 -31
  40. package/src/commands/contracts/write.ts +0 -49
  41. package/src/commands/general/index.ts +0 -45
  42. package/src/commands/general/init.ts +0 -179
  43. package/src/commands/general/start.ts +0 -116
  44. package/src/commands/general/stop.ts +0 -26
  45. package/src/commands/localnet/index.ts +0 -100
  46. package/src/commands/localnet/validators.ts +0 -269
  47. package/src/commands/network/index.ts +0 -29
  48. package/src/commands/network/setNetwork.ts +0 -77
  49. package/src/commands/scaffold/index.ts +0 -16
  50. package/src/commands/scaffold/new.ts +0 -34
  51. package/src/commands/staking/StakingAction.ts +0 -279
  52. package/src/commands/staking/delegatorClaim.ts +0 -41
  53. package/src/commands/staking/delegatorExit.ts +0 -56
  54. package/src/commands/staking/delegatorJoin.ts +0 -44
  55. package/src/commands/staking/index.ts +0 -357
  56. package/src/commands/staking/setIdentity.ts +0 -78
  57. package/src/commands/staking/setOperator.ts +0 -46
  58. package/src/commands/staking/stakingInfo.ts +0 -584
  59. package/src/commands/staking/validatorClaim.ts +0 -43
  60. package/src/commands/staking/validatorDeposit.ts +0 -48
  61. package/src/commands/staking/validatorExit.ts +0 -63
  62. package/src/commands/staking/validatorHistory.ts +0 -298
  63. package/src/commands/staking/validatorJoin.ts +0 -47
  64. package/src/commands/staking/validatorPrime.ts +0 -73
  65. package/src/commands/staking/wizard.ts +0 -809
  66. package/src/commands/transactions/appeal.ts +0 -39
  67. package/src/commands/transactions/index.ts +0 -39
  68. package/src/commands/transactions/receipt.ts +0 -90
  69. package/src/commands/update/index.ts +0 -25
  70. package/src/commands/update/ollama.ts +0 -103
  71. package/src/lib/actions/BaseAction.ts +0 -299
  72. package/src/lib/clients/jsonRpcClient.ts +0 -41
  73. package/src/lib/clients/system.ts +0 -73
  74. package/src/lib/config/ConfigFileManager.ts +0 -194
  75. package/src/lib/config/KeychainManager.ts +0 -89
  76. package/src/lib/config/simulator.ts +0 -68
  77. package/src/lib/config/text.ts +0 -2
  78. package/src/lib/errors/missingRequirement.ts +0 -9
  79. package/src/lib/errors/versionRequired.ts +0 -9
  80. package/src/lib/interfaces/ISimulatorService.ts +0 -37
  81. package/src/lib/services/simulator.ts +0 -351
  82. package/src/types/node-fetch.d.ts +0 -1
  83. package/tests/actions/appeal.test.ts +0 -99
  84. package/tests/actions/call.test.ts +0 -94
  85. package/tests/actions/code.test.ts +0 -87
  86. package/tests/actions/create.test.ts +0 -65
  87. package/tests/actions/deploy.test.ts +0 -420
  88. package/tests/actions/getSetReset.test.ts +0 -88
  89. package/tests/actions/init.test.ts +0 -467
  90. package/tests/actions/lock.test.ts +0 -86
  91. package/tests/actions/new.test.ts +0 -80
  92. package/tests/actions/ollama.test.ts +0 -193
  93. package/tests/actions/receipt.test.ts +0 -261
  94. package/tests/actions/schema.test.ts +0 -94
  95. package/tests/actions/setNetwork.test.ts +0 -160
  96. package/tests/actions/staking.test.ts +0 -279
  97. package/tests/actions/start.test.ts +0 -235
  98. package/tests/actions/stop.test.ts +0 -77
  99. package/tests/actions/unlock.test.ts +0 -139
  100. package/tests/actions/validators.test.ts +0 -750
  101. package/tests/actions/write.test.ts +0 -102
  102. package/tests/commands/account.test.ts +0 -146
  103. package/tests/commands/appeal.test.ts +0 -58
  104. package/tests/commands/call.test.ts +0 -78
  105. package/tests/commands/code.test.ts +0 -69
  106. package/tests/commands/config.test.ts +0 -54
  107. package/tests/commands/deploy.test.ts +0 -83
  108. package/tests/commands/init.test.ts +0 -101
  109. package/tests/commands/localnet.test.ts +0 -131
  110. package/tests/commands/network.test.ts +0 -60
  111. package/tests/commands/new.test.ts +0 -68
  112. package/tests/commands/receipt.test.ts +0 -142
  113. package/tests/commands/schema.test.ts +0 -67
  114. package/tests/commands/staking.test.ts +0 -329
  115. package/tests/commands/stop.test.ts +0 -27
  116. package/tests/commands/up.test.ts +0 -105
  117. package/tests/commands/update.test.ts +0 -45
  118. package/tests/commands/write.test.ts +0 -76
  119. package/tests/index.test.ts +0 -56
  120. package/tests/libs/baseAction.test.ts +0 -516
  121. package/tests/libs/configFileManager.test.ts +0 -117
  122. package/tests/libs/jsonRpcClient.test.ts +0 -59
  123. package/tests/libs/keychainManager.test.ts +0 -156
  124. package/tests/libs/system.test.ts +0 -148
  125. package/tests/services/simulator.test.ts +0 -705
  126. package/tests/utils.ts +0 -13
  127. package/tsconfig.json +0 -120
  128. package/vitest.config.ts +0 -12
@@ -1,269 +0,0 @@
1
- import inquirer from "inquirer";
2
- import { rpcClient } from "../../lib/clients/jsonRpcClient";
3
- import { BaseAction } from "../../lib/actions/BaseAction";
4
-
5
- export interface ValidatorOptions {
6
- address?: string;
7
- }
8
-
9
- export interface UpdateValidatorOptions {
10
- address: string;
11
- stake?: string;
12
- provider?: string;
13
- model?: string;
14
- config?: string;
15
- }
16
-
17
- export interface CreateRandomValidatorsOptions {
18
- count: string;
19
- providers: string[];
20
- models: string[];
21
- }
22
-
23
- export interface CreateValidatorOptions {
24
- stake: string;
25
- config?: string;
26
- model?: string;
27
- provider?: string;
28
- }
29
-
30
- export class ValidatorsAction extends BaseAction {
31
- public async getValidator(options: ValidatorOptions): Promise<void> {
32
- try {
33
- if (options.address) {
34
- this.startSpinner(`Fetching validator with address: ${options.address}`);
35
-
36
- const result = await rpcClient.request({
37
- method: "sim_getValidator",
38
- params: [options.address],
39
- });
40
-
41
- this.succeedSpinner(`Successfully fetched validator with address: ${options.address}`, result.result);
42
- } else {
43
- this.startSpinner(`Fetching all validators...`);
44
-
45
- const result = await rpcClient.request({
46
- method: "sim_getAllValidators",
47
- params: [],
48
- });
49
- this.succeedSpinner('Successfully fetched all validators.', result.result)
50
- }
51
- } catch (error) {
52
- this.failSpinner("Error fetching validators", error);
53
- }
54
- }
55
-
56
- public async deleteValidator(options: ValidatorOptions): Promise<void> {
57
- try {
58
- if (options.address) {
59
- await this.confirmPrompt(`This command will delete the validator with the address: ${options.address}. Do you want to continue?`);
60
- this.startSpinner(`Deleting validator with address: ${options.address}`);
61
-
62
- const result = await rpcClient.request({
63
- method: "sim_deleteValidator",
64
- params: [options.address],
65
- });
66
-
67
- this.succeedSpinner(`Deleted Address: ${result.result}`);
68
- } else {
69
- await this.confirmPrompt(`This command will delete all validators. Do you want to continue?`);
70
- this.startSpinner("Deleting all validators...");
71
-
72
- await rpcClient.request({
73
- method: "sim_deleteAllValidators",
74
- params: [],
75
- });
76
-
77
- this.succeedSpinner("Successfully deleted all validators");
78
- }
79
- } catch (error) {
80
- this.failSpinner("Error deleting validators", error);
81
- }
82
- }
83
-
84
- public async countValidators(): Promise<void> {
85
- try {
86
- this.startSpinner("Counting all validators...");
87
-
88
- const result = await rpcClient.request({
89
- method: "sim_countValidators",
90
- params: [],
91
- });
92
- this.succeedSpinner(`Total Validators: ${result.result}`);
93
- } catch (error) {
94
- this.failSpinner("Error counting validators", error);
95
- }
96
- }
97
-
98
- public async updateValidator(options: UpdateValidatorOptions): Promise<void> {
99
- try {
100
- this.startSpinner(`Fetching validator with address: ${options.address}...`);
101
- const currentValidator = await rpcClient.request({
102
- method: "sim_getValidator",
103
- params: [options.address],
104
- });
105
-
106
- this.log("Current Validator Details:", currentValidator.result);
107
-
108
- const parsedStake = options.stake
109
- ? parseInt(options.stake, 10)
110
- : currentValidator.result.stake;
111
-
112
- if (isNaN(parsedStake) || parsedStake < 0) {
113
- return this.failSpinner("Invalid stake value. Stake must be a positive integer.");
114
- }
115
-
116
- const updatedValidator = {
117
- address: options.address,
118
- stake: options.stake || currentValidator.result.stake,
119
- provider: options.provider || currentValidator.result.provider,
120
- model: options.model || currentValidator.result.model,
121
- config: options.config ? JSON.parse(options.config) : currentValidator.result.config,
122
- };
123
-
124
- this.log("Updated Validator Details:", updatedValidator);
125
-
126
- this.setSpinnerText('Updating Validator...');
127
-
128
- const result = await rpcClient.request({
129
- method: "sim_updateValidator",
130
- params: [
131
- updatedValidator.address,
132
- updatedValidator.stake,
133
- updatedValidator.provider,
134
- updatedValidator.model,
135
- updatedValidator.config,
136
- ],
137
- });
138
-
139
- this.succeedSpinner("Validator successfully updated", result.result);
140
- } catch (error) {
141
- this.failSpinner("Error updating validator", error);
142
- }
143
- }
144
-
145
- public async createRandomValidators(options: CreateRandomValidatorsOptions): Promise<void> {
146
- try {
147
- const count = parseInt(options.count, 10);
148
- if (isNaN(count) || count < 1) {
149
- return this.logError("Invalid count. Please provide a positive integer.");
150
- }
151
-
152
- this.startSpinner(`Creating ${count} random validator(s)...`);
153
- this.log(`Providers: ${options.providers.length > 0 ? options.providers.join(", ") : "All"}`);
154
- this.log(`Models: ${options.models.length > 0 ? options.models.join(", ") : "All"}`);
155
-
156
- const result = await rpcClient.request({
157
- method: "sim_createRandomValidators",
158
- params: [count, 1, 10, options.providers, options.models],
159
- });
160
-
161
- this.succeedSpinner("Random validators successfully created", result.result);
162
- } catch (error) {
163
- this.failSpinner("Error creating random validators", error);
164
- }
165
- }
166
-
167
- public async createValidator(options: CreateValidatorOptions): Promise<void> {
168
- try {
169
- const stake = parseInt(options.stake, 10);
170
- if (isNaN(stake) || stake < 1) {
171
- return this.logError("Invalid stake. Please provide a positive integer.");
172
- }
173
-
174
- if (options.model && !options.provider) {
175
- return this.logError("You must specify a provider if using a model.");
176
- }
177
-
178
- this.startSpinner("Fetching available providers and models...");
179
-
180
- const providersAndModels = await rpcClient.request({
181
- method: "sim_getProvidersAndModels",
182
- params: [],
183
- });
184
- this.stopSpinner();
185
-
186
- if (!providersAndModels.result || providersAndModels.result.length === 0) {
187
- return this.logError("No providers or models available.");
188
- }
189
-
190
- const availableProviders = [
191
- ...new Map(
192
- providersAndModels.result
193
- .filter((entry: any) => entry.is_available)
194
- .map((entry: any) => [entry.provider, entry])
195
- ).values(),
196
- ];
197
-
198
- let provider = options.provider
199
-
200
- if(!provider){
201
- const { selectedProvider } = await inquirer.prompt([
202
- {
203
- type: "list",
204
- name: "selectedProvider",
205
- message: "Select a provider:",
206
- choices: availableProviders.map((entry: any) => entry.provider),
207
- },
208
- ]);
209
-
210
- provider = selectedProvider;
211
- }
212
-
213
- const availableModels = providersAndModels.result.filter(
214
- (entry: any) => entry.provider === provider && entry.is_model_available
215
- );
216
-
217
- if (availableModels.length === 0) {
218
- return this.logError("No models available for the selected provider.");
219
- }
220
-
221
- let model = options.model;
222
-
223
- if(!model){
224
- const { selectedModel } = await inquirer.prompt([
225
- {
226
- type: "list",
227
- name: "selectedModel",
228
- message: "Select a model:",
229
- choices: availableModels.map((entry: any) => entry.model),
230
- },
231
- ]);
232
-
233
- model = selectedModel;
234
- }
235
-
236
- const modelDetails = availableModels.find(
237
- (entry: any) => entry.model === model
238
- );
239
-
240
- if (!modelDetails) {
241
- return this.logError("Selected model details not found.");
242
- }
243
-
244
- const config = options.config ? JSON.parse(options.config) : modelDetails.config;
245
- const params = [
246
- stake,
247
- modelDetails.provider,
248
- modelDetails.model,
249
- config,
250
- modelDetails.plugin,
251
- modelDetails.plugin_config,
252
- ]
253
-
254
- this.log("Validator details:", params);
255
- this.startSpinner('Creating validator...');
256
-
257
- const result = await rpcClient.request({
258
- method: "sim_createValidator",
259
- params,
260
- });
261
-
262
- this.succeedSpinner("Validator successfully created:", result.result);
263
- } catch (error) {
264
- this.failSpinner("Error creating validator", error);
265
- }
266
- }
267
- }
268
-
269
-
@@ -1,29 +0,0 @@
1
- import {Command} from "commander";
2
- import {NetworkActions} from "./setNetwork";
3
-
4
- export function initializeNetworkCommands(program: Command) {
5
- const networkActions = new NetworkActions();
6
-
7
- const network = program.command("network").description("Network configuration");
8
-
9
- // genlayer network set [name]
10
- network
11
- .command("set")
12
- .description("Set the network to use")
13
- .argument("[network]", "The network to set")
14
- .action((networkName?: string) => networkActions.setNetwork(networkName));
15
-
16
- // genlayer network info
17
- network
18
- .command("info")
19
- .description("Show current network configuration and contract addresses")
20
- .action(() => networkActions.showInfo());
21
-
22
- // genlayer network list
23
- network
24
- .command("list")
25
- .description("List available networks")
26
- .action(() => networkActions.listNetworks());
27
-
28
- return program;
29
- }
@@ -1,77 +0,0 @@
1
- import {BaseAction, BUILT_IN_NETWORKS, resolveNetwork} from "../../lib/actions/BaseAction";
2
- import inquirer, {DistinctQuestion} from "inquirer";
3
-
4
- const networks = Object.entries(BUILT_IN_NETWORKS).map(([alias, network]) => ({
5
- name: network.name,
6
- alias,
7
- value: network,
8
- }));
9
-
10
- export class NetworkActions extends BaseAction {
11
- constructor() {
12
- super();
13
- }
14
-
15
- async showInfo(): Promise<void> {
16
- const storedNetwork = this.getConfigByKey("network") || "localnet";
17
- const network = resolveNetwork(storedNetwork);
18
-
19
- const info: Record<string, string> = {
20
- alias: storedNetwork,
21
- name: network.name,
22
- chainId: network.id?.toString() || "unknown",
23
- rpc: network.rpcUrls?.default?.http?.[0] || "unknown",
24
- mainContract: network.consensusMainContract?.address || "not set",
25
- stakingContract: network.stakingContract?.address || "not set",
26
- };
27
-
28
- if (network.blockExplorers?.default?.url) {
29
- info.explorer = network.blockExplorers.default.url;
30
- }
31
-
32
- this.succeedSpinner("Current network", info);
33
- }
34
-
35
- async listNetworks(): Promise<void> {
36
- const currentNetwork = this.getConfigByKey("network") || "localnet";
37
-
38
- console.log("");
39
- for (const net of networks) {
40
- const marker = net.alias === currentNetwork ? "*" : " ";
41
- console.log(`${marker} ${net.alias.padEnd(16)} ${net.name}`);
42
- }
43
- console.log("");
44
- }
45
-
46
- async setNetwork(networkName?: string): Promise<void> {
47
- if (networkName || networkName === "") {
48
- if (!networks.some(n => n.name === networkName || n.alias === networkName)) {
49
- this.failSpinner(`Network ${networkName} not found`);
50
- return;
51
- }
52
- const selectedNetwork = networks.find(n => n.name === networkName || n.alias === networkName);
53
- if (!selectedNetwork) {
54
- this.failSpinner(`Network ${networkName} not found`);
55
- return;
56
- }
57
- this.writeConfig("network", selectedNetwork.alias);
58
- this.succeedSpinner(`Network successfully set to ${selectedNetwork.name}`);
59
- return;
60
- }
61
-
62
- const networkQuestions: DistinctQuestion[] = [
63
- {
64
- type: "list",
65
- name: "selectedNetwork",
66
- message: "Select which network do you want to use:",
67
- choices: networks.map(n => ({name: n.name, value: n.alias})),
68
- },
69
- ];
70
- const networkAnswer = await inquirer.prompt(networkQuestions);
71
- const selectedAlias = networkAnswer.selectedNetwork;
72
- const selectedNetwork = networks.find(n => n.alias === selectedAlias)!;
73
-
74
- this.writeConfig("network", selectedAlias);
75
- this.succeedSpinner(`Network successfully set to ${selectedNetwork.name}`);
76
- }
77
- }
@@ -1,16 +0,0 @@
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
- }
@@ -1,34 +0,0 @@
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
- }
@@ -1,279 +0,0 @@
1
- import {BaseAction, BUILT_IN_NETWORKS, resolveNetwork} from "../../lib/actions/BaseAction";
2
- import {createClient, createAccount, formatStakingAmount, parseStakingAmount, abi} from "genlayer-js";
3
- import type {GenLayerClient, GenLayerChain, Address} from "genlayer-js/types";
4
- import {readFileSync, existsSync} from "fs";
5
- import {ethers, ZeroAddress} from "ethers";
6
- import {createPublicClient, createWalletClient, http, type PublicClient, type WalletClient, type Chain, type Account} from "viem";
7
- import {privateKeyToAccount} from "viem/accounts";
8
-
9
- // Extended ABI for tree traversal (not in SDK)
10
- const STAKING_TREE_ABI = [
11
- {
12
- name: "validatorsRoot",
13
- type: "function",
14
- stateMutability: "view",
15
- inputs: [],
16
- outputs: [{name: "", type: "address"}],
17
- },
18
- ] as const;
19
-
20
- // Re-export for use by other staking commands
21
- export {BUILT_IN_NETWORKS};
22
-
23
- export interface StakingConfig {
24
- rpc?: string;
25
- stakingAddress?: string;
26
- network?: string;
27
- account?: string;
28
- password?: string;
29
- }
30
-
31
- export class StakingAction extends BaseAction {
32
- private _stakingClient: GenLayerClient<GenLayerChain> | null = null;
33
- private _passwordOverride: string | undefined;
34
-
35
- constructor() {
36
- super();
37
- }
38
-
39
- private getNetwork(config: StakingConfig): GenLayerChain {
40
- // Priority: --network option > global config > localnet default
41
- if (config.network) {
42
- const network = BUILT_IN_NETWORKS[config.network];
43
- if (!network) {
44
- throw new Error(`Unknown network: ${config.network}. Available: ${Object.keys(BUILT_IN_NETWORKS).join(", ")}`);
45
- }
46
- return {...network};
47
- }
48
-
49
- return resolveNetwork(this.getConfig().network);
50
- }
51
-
52
- protected async getStakingClient(config: StakingConfig): Promise<GenLayerClient<GenLayerChain>> {
53
- if (!this._stakingClient) {
54
- // Set account override if provided
55
- if (config.account) {
56
- this.accountOverride = config.account;
57
- }
58
- if (config.password) {
59
- this._passwordOverride = config.password;
60
- }
61
-
62
- const network = this.getNetwork(config);
63
-
64
- // Override staking address if provided
65
- if (config.stakingAddress) {
66
- network.stakingContract = {
67
- address: config.stakingAddress,
68
- abi: abi.STAKING_ABI,
69
- };
70
- }
71
-
72
- const privateKey = await this.getPrivateKeyForStaking();
73
- const account = createAccount(privateKey as `0x${string}`);
74
-
75
- this._stakingClient = createClient({
76
- chain: network,
77
- endpoint: config.rpc,
78
- account,
79
- });
80
- }
81
- return this._stakingClient;
82
- }
83
-
84
- protected async getReadOnlyStakingClient(config: StakingConfig): Promise<GenLayerClient<GenLayerChain>> {
85
- // Set account override if provided
86
- if (config.account) {
87
- this.accountOverride = config.account;
88
- }
89
-
90
- const network = this.getNetwork(config);
91
-
92
- if (config.stakingAddress) {
93
- network.stakingContract = {
94
- address: config.stakingAddress,
95
- abi: abi.STAKING_ABI,
96
- };
97
- }
98
-
99
- const accountName = this.resolveAccountName();
100
- const keystorePath = this.getKeystorePath(accountName);
101
-
102
- if (!existsSync(keystorePath)) {
103
- throw new Error(`Account '${accountName}' not found. Run 'genlayer account create --name ${accountName}' first.`);
104
- }
105
-
106
- const keystoreData = JSON.parse(readFileSync(keystorePath, "utf-8"));
107
- const addr = keystoreData.address as string;
108
- const normalizedAddress = (addr.startsWith("0x") ? addr : `0x${addr}`) as Address;
109
-
110
- return createClient({
111
- chain: network,
112
- endpoint: config.rpc,
113
- account: normalizedAddress,
114
- });
115
- }
116
-
117
- private async getPrivateKeyForStaking(): Promise<string> {
118
- const accountName = this.resolveAccountName();
119
- const keystorePath = this.getKeystorePath(accountName);
120
-
121
- if (!existsSync(keystorePath)) {
122
- throw new Error(`Account '${accountName}' not found. Run 'genlayer account create --name ${accountName}' first.`);
123
- }
124
-
125
- const keystoreJson = readFileSync(keystorePath, "utf-8");
126
- const keystoreData = JSON.parse(keystoreJson);
127
-
128
- if (!this.isValidKeystoreFormat(keystoreData)) {
129
- throw new Error("Invalid keystore format.");
130
- }
131
-
132
- const cachedKey = await this.keychainManager.getPrivateKey(accountName);
133
- if (cachedKey) {
134
- // Verify cached key matches keystore address - safety check
135
- const tempAccount = createAccount(cachedKey as `0x${string}`);
136
- const cachedAddress = tempAccount.address.toLowerCase();
137
- const keystoreAddress = `0x${keystoreData.address.toLowerCase().replace(/^0x/, '')}`;
138
-
139
- if (cachedAddress !== keystoreAddress) {
140
- // Cached key doesn't match keystore - invalidate it
141
- await this.keychainManager.removePrivateKey(accountName);
142
- // Fall through to prompt for password
143
- } else {
144
- return cachedKey;
145
- }
146
- }
147
-
148
- let password: string;
149
- if (this._passwordOverride) {
150
- password = this._passwordOverride;
151
- } else {
152
- this.stopSpinner();
153
- password = await this.promptPassword(`Enter password to unlock account '${accountName}':`);
154
- }
155
- this.startSpinner("Unlocking account...");
156
-
157
- const wallet = await ethers.Wallet.fromEncryptedJson(keystoreJson, password);
158
- return wallet.privateKey;
159
- }
160
-
161
- protected parseAmount(amount: string): bigint {
162
- return parseStakingAmount(amount);
163
- }
164
-
165
- protected formatAmount(amount: bigint): string {
166
- return formatStakingAmount(amount);
167
- }
168
-
169
- protected async getSignerAddress(): Promise<Address> {
170
- const accountName = this.resolveAccountName();
171
- const keystorePath = this.getKeystorePath(accountName);
172
- if (!existsSync(keystorePath)) {
173
- throw new Error(`Account '${accountName}' not found.`);
174
- }
175
- const keystoreData = JSON.parse(readFileSync(keystorePath, "utf-8"));
176
- const addr = keystoreData.address as string;
177
- return (addr.startsWith("0x") ? addr : `0x${addr}`) as Address;
178
- }
179
-
180
- /**
181
- * Get viem clients for direct contract interactions (e.g., ValidatorWallet calls)
182
- * Future: can be extended to support hardware wallets
183
- */
184
- protected async getViemClients(config: StakingConfig): Promise<{
185
- walletClient: WalletClient<any, Chain, Account>;
186
- publicClient: PublicClient;
187
- signerAddress: Address;
188
- }> {
189
- if (config.account) {
190
- this.accountOverride = config.account;
191
- }
192
- if (config.password) {
193
- this._passwordOverride = config.password;
194
- }
195
-
196
- const network = this.getNetwork(config);
197
- const rpcUrl = config.rpc || network.rpcUrls.default.http[0];
198
-
199
- const privateKey = await this.getPrivateKeyForStaking();
200
- const account = privateKeyToAccount(privateKey as `0x${string}`);
201
-
202
- const publicClient = createPublicClient({
203
- chain: network,
204
- transport: http(rpcUrl),
205
- });
206
-
207
- const walletClient = createWalletClient({
208
- chain: network,
209
- transport: http(rpcUrl),
210
- account,
211
- });
212
-
213
- return {
214
- walletClient,
215
- publicClient,
216
- signerAddress: account.address as Address,
217
- };
218
- }
219
-
220
- /**
221
- * Get all validators by traversing the validator tree.
222
- * This finds ALL validators including those not yet active/primed.
223
- */
224
- protected async getAllValidatorsFromTree(config: StakingConfig): Promise<Address[]> {
225
- const network = this.getNetwork(config);
226
- const rpcUrl = config.rpc || network.rpcUrls.default.http[0];
227
- const stakingAddress = config.stakingAddress || network.stakingContract?.address;
228
-
229
- if (!stakingAddress) {
230
- throw new Error("Staking contract address not configured");
231
- }
232
-
233
- const publicClient = createPublicClient({
234
- chain: network,
235
- transport: http(rpcUrl),
236
- });
237
-
238
- // Get the root of the validator tree
239
- const root = await publicClient.readContract({
240
- address: stakingAddress as `0x${string}`,
241
- abi: STAKING_TREE_ABI,
242
- functionName: "validatorsRoot",
243
- });
244
-
245
- if (root === ZeroAddress) {
246
- return [];
247
- }
248
-
249
- const validators: Address[] = [];
250
- const stack: string[] = [root as string];
251
- const visited = new Set<string>();
252
-
253
- // Use validatorView from SDK's ABI (has left/right fields)
254
- while (stack.length > 0) {
255
- const addr = stack.pop()!;
256
-
257
- if (addr === ZeroAddress || visited.has(addr.toLowerCase())) continue;
258
- visited.add(addr.toLowerCase());
259
-
260
- validators.push(addr as Address);
261
-
262
- const info = await publicClient.readContract({
263
- address: stakingAddress as `0x${string}`,
264
- abi: abi.STAKING_ABI,
265
- functionName: "validatorView",
266
- args: [addr as `0x${string}`],
267
- }) as {left: string; right: string};
268
-
269
- if (info.left !== ZeroAddress) {
270
- stack.push(info.left);
271
- }
272
- if (info.right !== ZeroAddress) {
273
- stack.push(info.right);
274
- }
275
- }
276
-
277
- return validators;
278
- }
279
- }