genlayer 0.38.8 → 0.38.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/.eslintignore +2 -0
  2. package/.github/workflows/cli-docs.yml +124 -0
  3. package/.github/workflows/publish.yml +55 -0
  4. package/.github/workflows/smoke.yml +27 -0
  5. package/.github/workflows/validate-code.yml +51 -0
  6. package/.prettierignore +19 -0
  7. package/.prettierrc +12 -0
  8. package/.release-it.json +66 -0
  9. package/CHANGELOG.md +545 -0
  10. package/CLAUDE.md +55 -0
  11. package/CONTRIBUTING.md +117 -0
  12. package/dist/index.js +221 -62
  13. package/docs/api-references/_meta.json +9 -0
  14. package/docs/api-references/accounts/_meta.json +3 -0
  15. package/docs/api-references/accounts/account/create.mdx +19 -0
  16. package/docs/api-references/accounts/account/export.mdx +19 -0
  17. package/docs/api-references/accounts/account/import.mdx +22 -0
  18. package/docs/api-references/accounts/account/list.mdx +15 -0
  19. package/docs/api-references/accounts/account/lock.mdx +16 -0
  20. package/docs/api-references/accounts/account/remove.mdx +20 -0
  21. package/docs/api-references/accounts/account/send.mdx +24 -0
  22. package/docs/api-references/accounts/account/show.mdx +17 -0
  23. package/docs/api-references/accounts/account/unlock.mdx +17 -0
  24. package/docs/api-references/accounts/account/use.mdx +19 -0
  25. package/docs/api-references/accounts/account.mdx +32 -0
  26. package/docs/api-references/configuration/_meta.json +4 -0
  27. package/docs/api-references/configuration/config/get.mdx +21 -0
  28. package/docs/api-references/configuration/config/reset.mdx +21 -0
  29. package/docs/api-references/configuration/config/set.mdx +21 -0
  30. package/docs/api-references/configuration/config.mdx +25 -0
  31. package/docs/api-references/configuration/network/info.mdx +15 -0
  32. package/docs/api-references/configuration/network/list.mdx +15 -0
  33. package/docs/api-references/configuration/network/set.mdx +21 -0
  34. package/docs/api-references/configuration/network.mdx +25 -0
  35. package/docs/api-references/contracts/_meta.json +7 -0
  36. package/docs/api-references/contracts/call.mdx +21 -0
  37. package/docs/api-references/contracts/code.mdx +20 -0
  38. package/docs/api-references/contracts/deploy.mdx +17 -0
  39. package/docs/api-references/contracts/schema.mdx +20 -0
  40. package/docs/api-references/contracts/write.mdx +21 -0
  41. package/docs/api-references/environment/_meta.json +7 -0
  42. package/docs/api-references/environment/init.mdx +20 -0
  43. package/docs/api-references/environment/new.mdx +21 -0
  44. package/docs/api-references/environment/stop.mdx +15 -0
  45. package/docs/api-references/environment/up.mdx +20 -0
  46. package/docs/api-references/environment/update/ollama.mdx +16 -0
  47. package/docs/api-references/environment/update.mdx +23 -0
  48. package/docs/api-references/index.mdx +35 -0
  49. package/docs/api-references/localnet/_meta.json +3 -0
  50. package/docs/api-references/localnet/localnet/validators/count.mdx +15 -0
  51. package/docs/api-references/localnet/localnet/validators/create-random.mdx +16 -0
  52. package/docs/api-references/localnet/localnet/validators/create.mdx +19 -0
  53. package/docs/api-references/localnet/localnet/validators/delete.mdx +16 -0
  54. package/docs/api-references/localnet/localnet/validators/get.mdx +16 -0
  55. package/docs/api-references/localnet/localnet/validators/update.mdx +23 -0
  56. package/docs/api-references/localnet/localnet/validators.mdx +28 -0
  57. package/docs/api-references/localnet/localnet.mdx +23 -0
  58. package/docs/api-references/staking/_meta.json +3 -0
  59. package/docs/api-references/staking/staking/active-validators.mdx +18 -0
  60. package/docs/api-references/staking/staking/banned-validators.mdx +18 -0
  61. package/docs/api-references/staking/staking/delegation-info.mdx +25 -0
  62. package/docs/api-references/staking/staking/delegator-claim.mdx +26 -0
  63. package/docs/api-references/staking/staking/delegator-exit.mdx +26 -0
  64. package/docs/api-references/staking/staking/delegator-join.mdx +26 -0
  65. package/docs/api-references/staking/staking/epoch-info.mdx +19 -0
  66. package/docs/api-references/staking/staking/prime-all.mdx +20 -0
  67. package/docs/api-references/staking/staking/quarantined-validators.mdx +18 -0
  68. package/docs/api-references/staking/staking/set-identity.mdx +33 -0
  69. package/docs/api-references/staking/staking/set-operator.mdx +26 -0
  70. package/docs/api-references/staking/staking/validator-claim.mdx +24 -0
  71. package/docs/api-references/staking/staking/validator-deposit.mdx +25 -0
  72. package/docs/api-references/staking/staking/validator-exit.mdx +25 -0
  73. package/docs/api-references/staking/staking/validator-history.mdx +29 -0
  74. package/docs/api-references/staking/staking/validator-info.mdx +25 -0
  75. package/docs/api-references/staking/staking/validator-join.mdx +22 -0
  76. package/docs/api-references/staking/staking/validator-prime.mdx +25 -0
  77. package/docs/api-references/staking/staking/validators.mdx +19 -0
  78. package/docs/api-references/staking/staking/wizard.mdx +20 -0
  79. package/docs/api-references/staking/staking.mdx +42 -0
  80. package/docs/api-references/transactions/_meta.json +6 -0
  81. package/docs/api-references/transactions/appeal-bond.mdx +20 -0
  82. package/docs/api-references/transactions/appeal.mdx +21 -0
  83. package/docs/api-references/transactions/receipt.mdx +25 -0
  84. package/docs/api-references/transactions/trace.mdx +21 -0
  85. package/docs/delegator-guide.md +203 -0
  86. package/docs/validator-guide.md +329 -0
  87. package/esbuild.config.dev.js +17 -0
  88. package/esbuild.config.js +22 -0
  89. package/esbuild.config.prod.js +17 -0
  90. package/eslint.config.js +60 -0
  91. package/package.json +2 -11
  92. package/renovate.json +22 -0
  93. package/scripts/generate-cli-docs.mjs +68 -5
  94. package/src/commands/account/create.ts +30 -0
  95. package/src/commands/account/export.ts +106 -0
  96. package/src/commands/account/import.ts +135 -0
  97. package/src/commands/account/index.ts +129 -0
  98. package/src/commands/account/list.ts +34 -0
  99. package/src/commands/account/lock.ts +39 -0
  100. package/src/commands/account/remove.ts +30 -0
  101. package/src/commands/account/send.ts +162 -0
  102. package/src/commands/account/show.ts +74 -0
  103. package/src/commands/account/unlock.ts +56 -0
  104. package/src/commands/account/use.ts +21 -0
  105. package/src/commands/config/getSetReset.ts +51 -0
  106. package/src/commands/config/index.ts +30 -0
  107. package/src/commands/contracts/call.ts +39 -0
  108. package/src/commands/contracts/code.ts +33 -0
  109. package/src/commands/contracts/deploy.ts +161 -0
  110. package/src/commands/contracts/index.ts +150 -0
  111. package/src/commands/contracts/schema.ts +31 -0
  112. package/src/commands/contracts/write.ts +49 -0
  113. package/src/commands/general/index.ts +45 -0
  114. package/src/commands/general/init.ts +180 -0
  115. package/src/commands/general/start.ts +128 -0
  116. package/src/commands/general/stop.ts +26 -0
  117. package/src/commands/localnet/index.ts +100 -0
  118. package/src/commands/localnet/validators.ts +269 -0
  119. package/src/commands/network/index.ts +29 -0
  120. package/src/commands/network/setNetwork.ts +77 -0
  121. package/src/commands/scaffold/index.ts +16 -0
  122. package/src/commands/scaffold/new.ts +34 -0
  123. package/src/commands/staking/StakingAction.ts +279 -0
  124. package/src/commands/staking/delegatorClaim.ts +41 -0
  125. package/src/commands/staking/delegatorExit.ts +56 -0
  126. package/src/commands/staking/delegatorJoin.ts +44 -0
  127. package/src/commands/staking/index.ts +357 -0
  128. package/src/commands/staking/setIdentity.ts +78 -0
  129. package/src/commands/staking/setOperator.ts +46 -0
  130. package/src/commands/staking/stakingInfo.ts +584 -0
  131. package/src/commands/staking/validatorClaim.ts +43 -0
  132. package/src/commands/staking/validatorDeposit.ts +48 -0
  133. package/src/commands/staking/validatorExit.ts +63 -0
  134. package/src/commands/staking/validatorHistory.ts +300 -0
  135. package/src/commands/staking/validatorJoin.ts +47 -0
  136. package/src/commands/staking/validatorPrime.ts +73 -0
  137. package/src/commands/staking/wizard.ts +809 -0
  138. package/src/commands/transactions/appeal.ts +83 -0
  139. package/src/commands/transactions/index.ts +60 -0
  140. package/src/commands/transactions/receipt.ts +90 -0
  141. package/src/commands/transactions/trace.ts +42 -0
  142. package/src/commands/update/index.ts +25 -0
  143. package/src/commands/update/ollama.ts +103 -0
  144. package/src/lib/actions/BaseAction.ts +301 -0
  145. package/src/lib/clients/jsonRpcClient.ts +41 -0
  146. package/src/lib/clients/system.ts +73 -0
  147. package/src/lib/config/ConfigFileManager.ts +194 -0
  148. package/src/lib/config/KeychainManager.ts +89 -0
  149. package/src/lib/config/simulator.ts +68 -0
  150. package/src/lib/config/text.ts +2 -0
  151. package/src/lib/errors/missingRequirement.ts +9 -0
  152. package/src/lib/errors/versionRequired.ts +9 -0
  153. package/src/lib/interfaces/ISimulatorService.ts +39 -0
  154. package/src/lib/services/simulator.ts +386 -0
  155. package/src/types/node-fetch.d.ts +1 -0
  156. package/tests/actions/appeal.test.ts +141 -0
  157. package/tests/actions/call.test.ts +94 -0
  158. package/tests/actions/code.test.ts +87 -0
  159. package/tests/actions/create.test.ts +65 -0
  160. package/tests/actions/deploy.test.ts +420 -0
  161. package/tests/actions/getSetReset.test.ts +88 -0
  162. package/tests/actions/init.test.ts +483 -0
  163. package/tests/actions/lock.test.ts +86 -0
  164. package/tests/actions/new.test.ts +80 -0
  165. package/tests/actions/ollama.test.ts +193 -0
  166. package/tests/actions/receipt.test.ts +261 -0
  167. package/tests/actions/schema.test.ts +94 -0
  168. package/tests/actions/setNetwork.test.ts +161 -0
  169. package/tests/actions/staking.test.ts +280 -0
  170. package/tests/actions/start.test.ts +257 -0
  171. package/tests/actions/stop.test.ts +77 -0
  172. package/tests/actions/unlock.test.ts +139 -0
  173. package/tests/actions/validators.test.ts +750 -0
  174. package/tests/actions/write.test.ts +102 -0
  175. package/tests/commands/account.test.ts +146 -0
  176. package/tests/commands/appeal.test.ts +97 -0
  177. package/tests/commands/call.test.ts +78 -0
  178. package/tests/commands/code.test.ts +69 -0
  179. package/tests/commands/config.test.ts +54 -0
  180. package/tests/commands/deploy.test.ts +83 -0
  181. package/tests/commands/init.test.ts +101 -0
  182. package/tests/commands/localnet.test.ts +131 -0
  183. package/tests/commands/network.test.ts +60 -0
  184. package/tests/commands/new.test.ts +68 -0
  185. package/tests/commands/parseArg.test.ts +156 -0
  186. package/tests/commands/receipt.test.ts +142 -0
  187. package/tests/commands/schema.test.ts +67 -0
  188. package/tests/commands/staking.test.ts +329 -0
  189. package/tests/commands/stop.test.ts +27 -0
  190. package/tests/commands/up.test.ts +105 -0
  191. package/tests/commands/update.test.ts +45 -0
  192. package/tests/commands/write.test.ts +76 -0
  193. package/tests/index.test.ts +56 -0
  194. package/tests/libs/baseAction.test.ts +535 -0
  195. package/tests/libs/configFileManager.test.ts +118 -0
  196. package/tests/libs/jsonRpcClient.test.ts +59 -0
  197. package/tests/libs/keychainManager.test.ts +156 -0
  198. package/tests/libs/platformCommands.test.ts +78 -0
  199. package/tests/libs/system.test.ts +148 -0
  200. package/tests/services/simulator.test.ts +789 -0
  201. package/tests/smoke.test.ts +134 -0
  202. package/tests/utils.ts +13 -0
  203. package/tsconfig.json +120 -0
  204. package/vitest.config.ts +13 -0
  205. package/vitest.smoke.config.ts +17 -0
@@ -0,0 +1,63 @@
1
+ import {StakingAction, StakingConfig} from "./StakingAction";
2
+ import type {Address} from "genlayer-js/types";
3
+ import {abi} from "genlayer-js";
4
+
5
+ export interface ValidatorExitOptions extends StakingConfig {
6
+ validator: string;
7
+ shares: string;
8
+ }
9
+
10
+ export class ValidatorExitAction extends StakingAction {
11
+ constructor() {
12
+ super();
13
+ }
14
+
15
+ async execute(options: ValidatorExitOptions): Promise<void> {
16
+ this.startSpinner("Initiating validator exit...");
17
+
18
+ try {
19
+ let shares: bigint;
20
+ try {
21
+ shares = BigInt(options.shares);
22
+ if (shares <= 0n) throw new Error("must be positive");
23
+ } catch {
24
+ this.failSpinner(`Invalid shares value: "${options.shares}". Must be a positive whole number.`);
25
+ return;
26
+ }
27
+
28
+ const validatorWallet = options.validator as Address;
29
+ const {walletClient, publicClient} = await this.getViemClients(options);
30
+
31
+ this.setSpinnerText(`Exiting validator ${validatorWallet} with ${shares} shares...`);
32
+
33
+ const hash = await walletClient.writeContract({
34
+ address: validatorWallet,
35
+ abi: abi.VALIDATOR_WALLET_ABI,
36
+ functionName: "validatorExit",
37
+ args: [shares],
38
+ });
39
+
40
+ const receipt = await publicClient.waitForTransactionReceipt({hash});
41
+
42
+ // Check epoch to determine note
43
+ const readClient = await this.getReadOnlyStakingClient(options);
44
+ const epochInfo = await readClient.getEpochInfo();
45
+ const isEpochZero = epochInfo.currentEpoch === 0n;
46
+
47
+ const output = {
48
+ transactionHash: receipt.transactionHash,
49
+ validator: validatorWallet,
50
+ sharesWithdrawn: shares.toString(),
51
+ blockNumber: receipt.blockNumber.toString(),
52
+ gasUsed: receipt.gasUsed.toString(),
53
+ note: isEpochZero
54
+ ? "Epoch 0: Withdrawal claimable immediately"
55
+ : "Withdrawal will be claimable after the unbonding period",
56
+ };
57
+
58
+ this.succeedSpinner("Exit initiated successfully!", output);
59
+ } catch (error: any) {
60
+ this.failSpinner("Failed to exit", error.message || error);
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,300 @@
1
+ import {StakingAction, StakingConfig, BUILT_IN_NETWORKS} from "./StakingAction";
2
+ import type {Address, GenLayerChain} from "genlayer-js/types";
3
+ import {createPublicClient, http} from "viem";
4
+ import Table from "cli-table3";
5
+ import chalk from "chalk";
6
+
7
+ // Event ABIs for log fetching
8
+ // v0.5: SlashedFromIdleness adds txStatus (uint8 enum) parameter
9
+ const SLASH_EVENT_ABI = {
10
+ type: "event",
11
+ name: "SlashedFromIdleness",
12
+ inputs: [
13
+ {name: "validator", type: "address", indexed: true},
14
+ {name: "txId", type: "bytes32", indexed: false},
15
+ {name: "epoch", type: "uint256", indexed: false},
16
+ {name: "percentage", type: "uint256", indexed: false},
17
+ {name: "txStatus", type: "uint8", indexed: false},
18
+ ],
19
+ } as const;
20
+
21
+ const REWARD_EVENT_ABI = {
22
+ type: "event",
23
+ name: "ValidatorPrime",
24
+ inputs: [
25
+ {name: "validator", type: "address", indexed: false},
26
+ {name: "epoch", type: "uint256", indexed: false},
27
+ {name: "validatorRewards", type: "uint256", indexed: false},
28
+ {name: "delegatorRewards", type: "uint256", indexed: false},
29
+ ],
30
+ } as const;
31
+
32
+ export interface ValidatorHistoryOptions extends StakingConfig {
33
+ validator?: string;
34
+ fromBlock?: string;
35
+ fromEpoch?: string;
36
+ epochs?: string;
37
+ limit?: string;
38
+ all?: boolean;
39
+ }
40
+
41
+ interface SlashEvent {
42
+ type: "slash";
43
+ epoch: bigint;
44
+ txId: string;
45
+ percentage: bigint;
46
+ blockNumber: bigint;
47
+ timestamp: Date;
48
+ }
49
+
50
+ interface RewardEvent {
51
+ type: "reward";
52
+ epoch: bigint;
53
+ validatorRewards: bigint;
54
+ delegatorRewards: bigint;
55
+ blockNumber: bigint;
56
+ timestamp: Date;
57
+ }
58
+
59
+ type HistoryEvent = SlashEvent | RewardEvent;
60
+
61
+ export class ValidatorHistoryAction extends StakingAction {
62
+ constructor() {
63
+ super();
64
+ }
65
+
66
+ private getNetworkForHistory(config: StakingConfig): GenLayerChain {
67
+ if (config.network) {
68
+ const network = BUILT_IN_NETWORKS[config.network];
69
+ if (!network) {
70
+ throw new Error(`Unknown network: ${config.network}`);
71
+ }
72
+ return network;
73
+ }
74
+ // Check global config
75
+ const globalNetwork = this.getConfig().network;
76
+ if (globalNetwork && BUILT_IN_NETWORKS[globalNetwork]) {
77
+ return BUILT_IN_NETWORKS[globalNetwork];
78
+ }
79
+ return BUILT_IN_NETWORKS["localnet"];
80
+ }
81
+
82
+ async execute(options: ValidatorHistoryOptions): Promise<void> {
83
+ this.startSpinner("Fetching validator history...");
84
+
85
+ try {
86
+ // Check network - localnet doesn't support eth_getLogs
87
+ const chain = this.getNetworkForHistory(options);
88
+ if (chain.id === 808080) {
89
+ this.failSpinner("validator-history requires testnet-asimov (localnet doesn't support event logs)");
90
+ return;
91
+ }
92
+
93
+ const client = await this.getReadOnlyStakingClient(options);
94
+ const validatorAddress = options.validator || (await this.getSignerAddress());
95
+
96
+ // Verify it's a validator
97
+ const isValidator = await client.isValidator(validatorAddress as Address);
98
+ if (!isValidator) {
99
+ this.failSpinner(`Address ${validatorAddress} is not a validator`);
100
+ return;
101
+ }
102
+
103
+ this.setSpinnerText("Fetching contract addresses...");
104
+
105
+ // Get addresses
106
+ const stakingAddress = client.getStakingContract().address;
107
+ const slashingAddress = await client.getSlashingAddress();
108
+
109
+ // Create public client for log fetching
110
+ const publicClient = createPublicClient({
111
+ chain,
112
+ transport: http(chain.rpcUrls.default.http[0]),
113
+ });
114
+
115
+ // Determine epoch range for filtering
116
+ const epochInfo = await client.getEpochInfo();
117
+ const currentEpoch = epochInfo.currentEpoch;
118
+ const defaultEpochs = 10n;
119
+
120
+ let minEpoch: bigint | null = null;
121
+ let fromBlock: bigint | "earliest" = "earliest";
122
+
123
+ if (options.fromBlock) {
124
+ // Explicit block takes precedence
125
+ fromBlock = BigInt(options.fromBlock);
126
+ } else if (options.fromEpoch) {
127
+ // Filter by starting epoch
128
+ minEpoch = BigInt(options.fromEpoch);
129
+ } else if (options.all) {
130
+ // Fetch all history (warn user)
131
+ console.log(chalk.yellow("Warning: Fetching all history from genesis. This may be slow for long-lived validators."));
132
+ console.log(chalk.yellow("Consider using --epochs <n> or --from-epoch <n> for faster queries.\n"));
133
+ } else {
134
+ // Default: last N epochs
135
+ const numEpochs = options.epochs ? BigInt(options.epochs) : defaultEpochs;
136
+ minEpoch = currentEpoch > numEpochs ? currentEpoch - numEpochs : 0n;
137
+ }
138
+
139
+ const limit = options.limit ? parseInt(options.limit) : 50;
140
+
141
+ this.setSpinnerText("Fetching slash events...");
142
+
143
+ // Fetch slash events (indexed by validator)
144
+ const slashLogs = await publicClient.getLogs({
145
+ address: slashingAddress as `0x${string}`,
146
+ event: SLASH_EVENT_ABI,
147
+ args: {validator: validatorAddress as `0x${string}`},
148
+ fromBlock,
149
+ toBlock: "latest",
150
+ });
151
+
152
+ this.setSpinnerText("Fetching reward events...");
153
+
154
+ // Fetch reward events (not indexed, need to filter client-side)
155
+ const rewardLogs = await publicClient.getLogs({
156
+ address: stakingAddress,
157
+ event: REWARD_EVENT_ABI,
158
+ fromBlock,
159
+ toBlock: "latest",
160
+ });
161
+
162
+ // Filter rewards to this validator
163
+ const filteredRewardLogs = rewardLogs.filter(
164
+ log => (log.args as any).validator?.toLowerCase() === validatorAddress.toLowerCase()
165
+ );
166
+
167
+ // Get unique block numbers to fetch timestamps
168
+ const allLogs = [...slashLogs, ...filteredRewardLogs];
169
+ const uniqueBlocks = [...new Set(allLogs.map(l => l.blockNumber))];
170
+
171
+ this.setSpinnerText("Fetching block timestamps...");
172
+
173
+ // Fetch block timestamps in batches
174
+ const blockTimestamps = new Map<bigint, Date>();
175
+ const BATCH_SIZE = 10;
176
+ for (let i = 0; i < uniqueBlocks.length; i += BATCH_SIZE) {
177
+ const batch = uniqueBlocks.slice(i, i + BATCH_SIZE);
178
+ const blocks = await Promise.all(
179
+ batch.map(blockNumber => publicClient.getBlock({blockNumber}))
180
+ );
181
+ blocks.forEach(block => {
182
+ blockTimestamps.set(block.number, new Date(Number(block.timestamp) * 1000));
183
+ });
184
+ }
185
+
186
+ // Transform to typed events
187
+ let slashEvents: SlashEvent[] = slashLogs.map(log => ({
188
+ type: "slash" as const,
189
+ epoch: (log.args as any).epoch as bigint,
190
+ txId: (log.args as any).txId as string,
191
+ percentage: (log.args as any).percentage as bigint,
192
+ blockNumber: log.blockNumber,
193
+ timestamp: blockTimestamps.get(log.blockNumber) || new Date(0),
194
+ }));
195
+
196
+ let rewardEvents: RewardEvent[] = filteredRewardLogs.map(log => ({
197
+ type: "reward" as const,
198
+ epoch: (log.args as any).epoch as bigint,
199
+ validatorRewards: (log.args as any).validatorRewards as bigint,
200
+ delegatorRewards: (log.args as any).delegatorRewards as bigint,
201
+ blockNumber: log.blockNumber,
202
+ timestamp: blockTimestamps.get(log.blockNumber) || new Date(0),
203
+ }));
204
+
205
+ // Filter by epoch if specified
206
+ if (minEpoch !== null) {
207
+ slashEvents = slashEvents.filter(e => e.epoch >= minEpoch!);
208
+ rewardEvents = rewardEvents.filter(e => e.epoch >= minEpoch!);
209
+ }
210
+
211
+ // Combine and sort by block number descending
212
+ const allEvents: HistoryEvent[] = [...slashEvents, ...rewardEvents];
213
+ allEvents.sort((a, b) => Number(b.blockNumber - a.blockNumber));
214
+
215
+ // Apply limit
216
+ const limitedEvents = allEvents.slice(0, limit);
217
+
218
+ // Calculate totals
219
+ const totalValidatorRewards = rewardEvents.reduce((sum, e) => sum + e.validatorRewards, 0n);
220
+ const totalDelegatorRewards = rewardEvents.reduce((sum, e) => sum + e.delegatorRewards, 0n);
221
+
222
+ this.stopSpinner();
223
+
224
+ // Display results
225
+ if (limitedEvents.length === 0) {
226
+ console.log(chalk.yellow("\nNo history events found for this validator.\n"));
227
+ return;
228
+ }
229
+
230
+ // Format timestamp as datetime
231
+ const formatTime = (date: Date): string => {
232
+ const month = String(date.getMonth() + 1).padStart(2, "0");
233
+ const day = String(date.getDate()).padStart(2, "0");
234
+ const hours = String(date.getHours()).padStart(2, "0");
235
+ const minutes = String(date.getMinutes()).padStart(2, "0");
236
+ return `${month}-${day} ${hours}:${minutes}`;
237
+ };
238
+
239
+ // Create table
240
+ const table = new Table({
241
+ head: [
242
+ chalk.cyan("Time"),
243
+ chalk.cyan("Epoch"),
244
+ chalk.cyan("Type"),
245
+ chalk.cyan("Details"),
246
+ chalk.cyan("GL TxId / Block"),
247
+ ],
248
+ style: {head: [], border: []},
249
+ });
250
+
251
+ for (const event of limitedEvents) {
252
+ if (event.type === "slash") {
253
+ const pct = Number(event.percentage) / 100; // basis points to %
254
+ table.push([
255
+ formatTime(event.timestamp),
256
+ event.epoch.toString(),
257
+ chalk.red("SLASH"),
258
+ `${pct.toFixed(2)}%`,
259
+ event.txId,
260
+ ]);
261
+ } else {
262
+ const valReward = client.formatStakingAmount(event.validatorRewards);
263
+ const delReward = client.formatStakingAmount(event.delegatorRewards);
264
+ table.push([
265
+ formatTime(event.timestamp),
266
+ event.epoch.toString(),
267
+ chalk.green("REWARD"),
268
+ `Val: ${valReward}, Del: ${delReward}`,
269
+ `block ${event.blockNumber}`,
270
+ ]);
271
+ }
272
+ }
273
+
274
+ console.log("");
275
+ console.log(chalk.bold(`History for ${validatorAddress}`));
276
+ console.log(table.toString());
277
+ console.log("");
278
+
279
+ // Summary
280
+ const epochRangeInfo = minEpoch !== null
281
+ ? `epochs ${minEpoch}-${currentEpoch}`
282
+ : options.fromBlock
283
+ ? `from block ${options.fromBlock}`
284
+ : "all epochs";
285
+ console.log(chalk.gray("Summary:"));
286
+ console.log(chalk.gray(` Range: ${epochRangeInfo}`));
287
+ console.log(chalk.gray(` Slash events: ${slashEvents.length}`));
288
+ console.log(chalk.gray(` Reward events: ${rewardEvents.length}`));
289
+ console.log(chalk.gray(` Total validator rewards: ${client.formatStakingAmount(totalValidatorRewards)}`));
290
+ console.log(chalk.gray(` Total delegator rewards: ${client.formatStakingAmount(totalDelegatorRewards)}`));
291
+ if (allEvents.length > limit) {
292
+ console.log(chalk.gray(` (showing ${limit} of ${allEvents.length} events)`));
293
+ }
294
+ console.log(chalk.gray(` Use --all to fetch complete history, --epochs <n> for last N epochs`));
295
+ console.log("");
296
+ } catch (error: any) {
297
+ this.failSpinner("Failed to get validator history", error.message || error);
298
+ }
299
+ }
300
+ }
@@ -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,73 @@
1
+ import {StakingAction, StakingConfig} from "./StakingAction";
2
+ import type {Address} from "genlayer-js/types";
3
+ import chalk from "chalk";
4
+
5
+ export interface ValidatorPrimeOptions extends StakingConfig {
6
+ validator: string;
7
+ }
8
+
9
+ export class ValidatorPrimeAction extends StakingAction {
10
+ constructor() {
11
+ super();
12
+ }
13
+
14
+ async execute(options: ValidatorPrimeOptions): Promise<void> {
15
+ this.startSpinner("Priming validator...");
16
+
17
+ try {
18
+ const client = await this.getStakingClient(options);
19
+
20
+ this.setSpinnerText(`Priming validator ${options.validator}...`);
21
+
22
+ const result = await client.validatorPrime({validator: options.validator as Address});
23
+
24
+ const output = {
25
+ transactionHash: result.transactionHash,
26
+ validator: options.validator,
27
+ blockNumber: result.blockNumber.toString(),
28
+ gasUsed: result.gasUsed.toString(),
29
+ };
30
+
31
+ this.succeedSpinner("Validator primed for next epoch!", output);
32
+ } catch (error: any) {
33
+ this.failSpinner("Failed to prime validator", error.message || error);
34
+ }
35
+ }
36
+
37
+ async primeAll(options: StakingConfig): Promise<void> {
38
+ this.startSpinner("Fetching validators...");
39
+
40
+ try {
41
+ const client = await this.getStakingClient(options);
42
+
43
+ // Get all validators from tree
44
+ this.setSpinnerText("Fetching validators...");
45
+ const allValidators = await this.getAllValidatorsFromTree(options);
46
+
47
+ this.stopSpinner();
48
+ console.log(`\nPriming ${allValidators.length} validators:\n`);
49
+
50
+ let succeeded = 0;
51
+ let skipped = 0;
52
+
53
+ for (const addr of allValidators) {
54
+ process.stdout.write(` ${addr} ... `);
55
+
56
+ try {
57
+ const result = await client.validatorPrime({validator: addr});
58
+ console.log(chalk.green(`primed ${result.transactionHash}`));
59
+ succeeded++;
60
+ } catch (error: any) {
61
+ const msg = error.message || String(error);
62
+ const shortErr = msg.length > 60 ? msg.slice(0, 57) + "..." : msg;
63
+ console.log(chalk.gray(`skipped: ${shortErr}`));
64
+ skipped++;
65
+ }
66
+ }
67
+
68
+ console.log(`\n${chalk.green(`${succeeded} primed`)}, ${chalk.gray(`${skipped} skipped`)}\n`);
69
+ } catch (error: any) {
70
+ this.failSpinner("Failed to prime validators", error.message || error);
71
+ }
72
+ }
73
+ }