@aztec/cli 2.1.2 → 2.1.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.
@@ -58,7 +58,8 @@ export const stagingIgnitionL2ChainConfig = {
58
58
  ],
59
59
  autoUpdate: 'config-and-version',
60
60
  autoUpdateUrl: 'https://storage.googleapis.com/aztec-testnet/auto-update/staging-ignition.json',
61
- maxTxPoolSize: 100_000_000,
61
+ maxTxPoolSize: 0,
62
+ publicMetricsOptOut: false,
62
63
  publicIncludeMetrics,
63
64
  publicMetricsCollectorUrl: 'https://telemetry.alpha-testnet.aztec-labs.com/v1/metrics',
64
65
  publicMetricsCollectFrom: [
@@ -125,6 +126,7 @@ export const stagingPublicL2ChainConfig = {
125
126
  ],
126
127
  autoUpdate: 'config-and-version',
127
128
  autoUpdateUrl: 'https://storage.googleapis.com/aztec-testnet/auto-update/staging-public.json',
129
+ publicMetricsOptOut: false,
128
130
  publicIncludeMetrics,
129
131
  publicMetricsCollectorUrl: 'https://telemetry.alpha-testnet.aztec-labs.com/v1/metrics',
130
132
  publicMetricsCollectFrom: [
@@ -150,6 +152,45 @@ export const stagingPublicL2ChainConfig = {
150
152
  ...DefaultSlashConfig,
151
153
  ...DefaultNetworkDBMapSizeConfig
152
154
  };
155
+ export const nextNetL2ChainConfig = {
156
+ l1ChainId: 11155111,
157
+ testAccounts: true,
158
+ sponsoredFPC: true,
159
+ p2pEnabled: true,
160
+ disableTransactions: false,
161
+ p2pBootstrapNodes: [],
162
+ seqMinTxsPerBlock: 0,
163
+ seqMaxTxsPerBlock: 8,
164
+ realProofs: true,
165
+ snapshotsUrls: [],
166
+ autoUpdate: 'config-and-version',
167
+ autoUpdateUrl: '',
168
+ publicMetricsOptOut: true,
169
+ publicIncludeMetrics,
170
+ publicMetricsCollectorUrl: '',
171
+ publicMetricsCollectFrom: [
172
+ ''
173
+ ],
174
+ maxTxPoolSize: 100_000_000,
175
+ txPoolDeleteTxsAfterReorg: false,
176
+ // Deployment stuff
177
+ /** How many seconds an L1 slot lasts. */ ethereumSlotDuration: 12,
178
+ /** How many seconds an L2 slots lasts (must be multiple of ethereum slot duration). */ aztecSlotDuration: 36,
179
+ /** How many L2 slots an epoch lasts. */ aztecEpochDuration: 32,
180
+ /** The target validator committee size. */ aztecTargetCommitteeSize: 48,
181
+ /** The number of epochs to lag behind the current epoch for validator selection. */ lagInEpochs: DefaultL1ContractsConfig.lagInEpochs,
182
+ /** The local ejection threshold for a validator. Stricter than ejectionThreshold but local to a specific rollup */ localEjectionThreshold: DefaultL1ContractsConfig.localEjectionThreshold,
183
+ /** The number of epochs after an epoch ends that proofs are still accepted. */ aztecProofSubmissionEpochs: 1,
184
+ /** The deposit amount for a validator */ activationThreshold: DefaultL1ContractsConfig.activationThreshold,
185
+ /** The minimum stake for a validator. */ ejectionThreshold: DefaultL1ContractsConfig.ejectionThreshold,
186
+ /** The slashing round size */ slashingRoundSizeInEpochs: DefaultL1ContractsConfig.slashingRoundSizeInEpochs,
187
+ /** Governance proposing round size */ governanceProposerRoundSize: DefaultL1ContractsConfig.governanceProposerRoundSize,
188
+ /** The mana target for the rollup */ manaTarget: DefaultL1ContractsConfig.manaTarget,
189
+ /** The proving cost per mana */ provingCostPerMana: DefaultL1ContractsConfig.provingCostPerMana,
190
+ /** Exit delay for stakers */ exitDelaySeconds: DefaultL1ContractsConfig.exitDelaySeconds,
191
+ ...DefaultSlashConfig,
192
+ ...DefaultNetworkDBMapSizeConfig
193
+ };
153
194
  export const testnetL2ChainConfig = {
154
195
  l1ChainId: 11155111,
155
196
  testAccounts: false,
@@ -166,6 +207,7 @@ export const testnetL2ChainConfig = {
166
207
  autoUpdate: 'config-and-version',
167
208
  autoUpdateUrl: 'https://storage.googleapis.com/aztec-testnet/auto-update/testnet.json',
168
209
  maxTxPoolSize: 100_000_000,
210
+ publicMetricsOptOut: false,
169
211
  publicIncludeMetrics,
170
212
  publicMetricsCollectorUrl: 'https://telemetry.alpha-testnet.aztec-labs.com/v1/metrics',
171
213
  publicMetricsCollectFrom: [
@@ -235,12 +277,14 @@ export const mainnetL2ChainConfig = {
235
277
  ],
236
278
  autoUpdate: 'notify',
237
279
  autoUpdateUrl: 'https://storage.googleapis.com/aztec-mainnet/auto-update/mainnet.json',
238
- maxTxPoolSize: 100_000_000,
280
+ maxTxPoolSize: 0,
281
+ publicMetricsOptOut: true,
239
282
  publicIncludeMetrics,
240
283
  publicMetricsCollectorUrl: 'https://telemetry.alpha-testnet.aztec-labs.com/v1/metrics',
241
284
  publicMetricsCollectFrom: [
242
285
  'sequencer'
243
286
  ],
287
+ blobAllowEmptySources: true,
244
288
  /** How many seconds an L1 slot lasts. */ ethereumSlotDuration: 12,
245
289
  /** How many seconds an L2 slots lasts (must be multiple of ethereum slot duration). */ aztecSlotDuration: 72,
246
290
  /** How many L2 slots an epoch lasts. */ aztecEpochDuration: 32,
@@ -252,7 +296,7 @@ export const mainnetL2ChainConfig = {
252
296
  slashingRoundSizeInEpochs: 4,
253
297
  slashingExecutionDelayInRounds: 28,
254
298
  slashingLifetimeInRounds: 34,
255
- slashingVetoer: EthAddress.ZERO,
299
+ slashingVetoer: EthAddress.fromString('0xBbB4aF368d02827945748b28CD4b2D42e4A37480'),
256
300
  slashingOffsetInRounds: 2,
257
301
  slashingDisableDuration: 259_200,
258
302
  slasherFlavor: 'tally',
@@ -285,6 +329,45 @@ export const mainnetL2ChainConfig = {
285
329
  sentinelEnabled: true,
286
330
  ...DefaultNetworkDBMapSizeConfig
287
331
  };
332
+ export const devnetL2ChainConfig = {
333
+ l1ChainId: 11155111,
334
+ testAccounts: true,
335
+ sponsoredFPC: true,
336
+ p2pEnabled: true,
337
+ disableTransactions: false,
338
+ p2pBootstrapNodes: [],
339
+ seqMinTxsPerBlock: 0,
340
+ seqMaxTxsPerBlock: 8,
341
+ realProofs: false,
342
+ snapshotsUrls: [],
343
+ autoUpdate: 'config-and-version',
344
+ autoUpdateUrl: '',
345
+ publicMetricsOptOut: true,
346
+ publicIncludeMetrics,
347
+ publicMetricsCollectorUrl: '',
348
+ publicMetricsCollectFrom: [
349
+ ''
350
+ ],
351
+ maxTxPoolSize: 100_000_000,
352
+ txPoolDeleteTxsAfterReorg: true,
353
+ // Deployment stuff
354
+ /** How many seconds an L1 slot lasts. */ ethereumSlotDuration: 12,
355
+ /** How many seconds an L2 slots lasts (must be multiple of ethereum slot duration). */ aztecSlotDuration: 36,
356
+ /** How many L2 slots an epoch lasts. */ aztecEpochDuration: 8,
357
+ /** The target validator committee size. */ aztecTargetCommitteeSize: 1,
358
+ /** The number of epochs to lag behind the current epoch for validator selection. */ lagInEpochs: 1,
359
+ /** The local ejection threshold for a validator. Stricter than ejectionThreshold but local to a specific rollup */ localEjectionThreshold: DefaultL1ContractsConfig.localEjectionThreshold,
360
+ /** The number of epochs after an epoch ends that proofs are still accepted. */ aztecProofSubmissionEpochs: 1,
361
+ /** The deposit amount for a validator */ activationThreshold: DefaultL1ContractsConfig.activationThreshold,
362
+ /** The minimum stake for a validator. */ ejectionThreshold: DefaultL1ContractsConfig.ejectionThreshold,
363
+ /** The slashing round size */ slashingRoundSizeInEpochs: DefaultL1ContractsConfig.slashingRoundSizeInEpochs,
364
+ /** Governance proposing round size */ governanceProposerRoundSize: DefaultL1ContractsConfig.governanceProposerRoundSize,
365
+ /** The mana target for the rollup */ manaTarget: DefaultL1ContractsConfig.manaTarget,
366
+ /** The proving cost per mana */ provingCostPerMana: DefaultL1ContractsConfig.provingCostPerMana,
367
+ /** Exit delay for stakers */ exitDelaySeconds: DefaultL1ContractsConfig.exitDelaySeconds,
368
+ ...DefaultSlashConfig,
369
+ ...DefaultNetworkDBMapSizeConfig
370
+ };
288
371
  export function getL2ChainConfig(networkName) {
289
372
  let config;
290
373
  if (networkName === 'staging-public') {
@@ -355,6 +438,7 @@ export function enrichEnvironmentWithChainConfig(networkName) {
355
438
  if (config.publicMetricsCollectFrom) {
356
439
  enrichVar('PUBLIC_OTEL_COLLECT_FROM', config.publicMetricsCollectFrom.join(','));
357
440
  }
441
+ enrichVar('PUBLIC_OTEL_OPT_OUT', config.publicMetricsOptOut.toString());
358
442
  // Deployment stuff
359
443
  enrichVar('ETHEREUM_SLOT_DURATION', config.ethereumSlotDuration.toString());
360
444
  enrichVar('AZTEC_SLOT_DURATION', config.aztecSlotDuration.toString());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/cli",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./contracts": "./dest/cmds/contracts/index.js",
@@ -71,18 +71,18 @@
71
71
  ]
72
72
  },
73
73
  "dependencies": {
74
- "@aztec/archiver": "2.1.2",
75
- "@aztec/aztec.js": "2.1.2",
76
- "@aztec/constants": "2.1.2",
77
- "@aztec/entrypoints": "2.1.2",
78
- "@aztec/ethereum": "2.1.2",
79
- "@aztec/foundation": "2.1.2",
80
- "@aztec/l1-artifacts": "2.1.2",
81
- "@aztec/node-keystore": "2.1.2",
82
- "@aztec/node-lib": "2.1.2",
83
- "@aztec/p2p": "2.1.2",
84
- "@aztec/stdlib": "2.1.2",
85
- "@aztec/world-state": "2.1.2",
74
+ "@aztec/archiver": "2.1.3",
75
+ "@aztec/aztec.js": "2.1.3",
76
+ "@aztec/constants": "2.1.3",
77
+ "@aztec/entrypoints": "2.1.3",
78
+ "@aztec/ethereum": "2.1.3",
79
+ "@aztec/foundation": "2.1.3",
80
+ "@aztec/l1-artifacts": "2.1.3",
81
+ "@aztec/node-keystore": "2.1.3",
82
+ "@aztec/node-lib": "2.1.3",
83
+ "@aztec/p2p": "2.1.3",
84
+ "@aztec/stdlib": "2.1.3",
85
+ "@aztec/world-state": "2.1.3",
86
86
  "@ethersproject/wallet": "^5.8.0",
87
87
  "@iarna/toml": "^2.2.5",
88
88
  "@libp2p/peer-id-factory": "^3.0.4",
@@ -96,8 +96,8 @@
96
96
  "viem": "npm:@spalladino/viem@2.38.2-eip7594.0"
97
97
  },
98
98
  "devDependencies": {
99
- "@aztec/accounts": "2.1.2",
100
- "@aztec/protocol-contracts": "2.1.2",
99
+ "@aztec/accounts": "2.1.3",
100
+ "@aztec/protocol-contracts": "2.1.3",
101
101
  "@jest/globals": "^30.0.0",
102
102
  "@types/jest": "^30.0.0",
103
103
  "@types/lodash.chunk": "^4.2.9",
@@ -113,15 +113,15 @@
113
113
  "typescript": "^5.3.3"
114
114
  },
115
115
  "peerDependencies": {
116
- "@aztec/accounts": "2.1.2",
117
- "@aztec/bb-prover": "2.1.2",
118
- "@aztec/ethereum": "2.1.2",
119
- "@aztec/l1-artifacts": "2.1.2",
120
- "@aztec/noir-contracts.js": "2.1.2",
121
- "@aztec/noir-protocol-circuits-types": "2.1.2",
122
- "@aztec/noir-test-contracts.js": "2.1.2",
123
- "@aztec/protocol-contracts": "2.1.2",
124
- "@aztec/stdlib": "2.1.2"
116
+ "@aztec/accounts": "2.1.3",
117
+ "@aztec/bb-prover": "2.1.3",
118
+ "@aztec/ethereum": "2.1.3",
119
+ "@aztec/l1-artifacts": "2.1.3",
120
+ "@aztec/noir-contracts.js": "2.1.3",
121
+ "@aztec/noir-protocol-circuits-types": "2.1.3",
122
+ "@aztec/noir-test-contracts.js": "2.1.3",
123
+ "@aztec/protocol-contracts": "2.1.3",
124
+ "@aztec/stdlib": "2.1.3"
125
125
  },
126
126
  "files": [
127
127
  "dest",
@@ -30,7 +30,6 @@ export async function addValidatorKeys(existing: string, options: AddValidatorKe
30
30
  addressIndex,
31
31
  ikm,
32
32
  blsPath,
33
- blsOnly,
34
33
  json,
35
34
  feeRecipient: feeRecipientOpt,
36
35
  coinbase: coinbaseOpt,
@@ -75,7 +74,6 @@ export async function addValidatorKeys(existing: string, options: AddValidatorKe
75
74
  mnemonic: mnemonicToUse,
76
75
  ikm,
77
76
  blsPath,
78
- blsOnly,
79
77
  feeRecipient,
80
78
  coinbase,
81
79
  remoteSigner,
@@ -7,7 +7,7 @@ import { parseAztecAddress, parseEthereumAddress, parseHex, parseOptionalInteger
7
7
  export function injectCommands(program: Command, log: LogFn) {
8
8
  const group = program
9
9
  .command('validator-keys')
10
- .aliases(['valKeys'])
10
+ .aliases(['valKeys', 'valkeys'])
11
11
  .description('Manage validator keystores for node operators');
12
12
 
13
13
  group
@@ -29,13 +29,23 @@ export function injectCommands(program: Command, log: LogFn) {
29
29
  .option('--remote-signer <url>', 'Default remote signer URL for accounts in this file')
30
30
  .option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', value => parseHex(value, 32))
31
31
  .option('--bls-path <path>', 'EIP-2334 path (default m/12381/3600/0/0/0)')
32
- .option('--bls-only', 'Generate only BLS keys')
33
32
  .option(
34
33
  '--password <str>',
35
34
  'Password for writing keystore files (ETH JSON V3 and BLS EIP-2335). Empty string allowed',
36
35
  )
37
36
  .option('--out-dir <dir>', 'Output directory for generated keystore file(s)')
38
37
  .option('--json', 'Echo resulting JSON to stdout')
38
+ .option('--staker-output', 'Generate staker output JSON files for each attester')
39
+ .option('--gse-address <address>', 'GSE contract address (required with --staker-output)', parseEthereumAddress)
40
+ .option('--l1-rpc-urls <urls>', 'L1 RPC URLs (comma-separated, required with --staker-output)', value =>
41
+ value.split(','),
42
+ )
43
+ .option(
44
+ '-c, --l1-chain-id <number>',
45
+ 'L1 chain ID (required with --staker-output)',
46
+ value => parseInt(value),
47
+ 31337,
48
+ )
39
49
  .requiredOption('--fee-recipient <address>', 'Aztec address that will receive fees', parseAztecAddress)
40
50
  .action(async options => {
41
51
  const { newValidatorKeystore } = await import('./new.js');
@@ -62,7 +72,6 @@ export function injectCommands(program: Command, log: LogFn) {
62
72
  .option('--remote-signer <url>', 'Default remote signer URL for accounts in this file')
63
73
  .option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', value => parseHex(value, 32))
64
74
  .option('--bls-path <path>', 'EIP-2334 path (default m/12381/3600/0/0/0)')
65
- .option('--bls-only', 'Generate only BLS keys')
66
75
  .option('--empty', 'Generate an empty skeleton without keys')
67
76
  .option(
68
77
  '--password <str>',
@@ -76,6 +85,25 @@ export function injectCommands(program: Command, log: LogFn) {
76
85
  await addValidatorKeys(existing, options, log);
77
86
  });
78
87
 
88
+ group
89
+ .command('staker')
90
+ .summary('Generate staking JSON from keystore')
91
+ .description(
92
+ 'Reads a validator keystore and outputs staking data with BLS public keys for each attester (skips mnemonics)',
93
+ )
94
+ .requiredOption('--from <keystore>', 'Path to keystore JSON file')
95
+ .option('--password <password>', 'Password for decrypting encrypted keystores (if not specified in keystore file)')
96
+ .requiredOption('--gse-address <address>', 'GSE contract address', parseEthereumAddress)
97
+ .option('--l1-rpc-urls <urls>', 'L1 RPC URLs (comma-separated)', value => value.split(','), [
98
+ 'http://localhost:8545',
99
+ ])
100
+ .option('-c, --l1-chain-id <number>', 'L1 chain ID', value => parseInt(value), 31337)
101
+ .option('--output <file>', 'Output file path (if not specified, JSON is written to stdout)')
102
+ .action(async options => {
103
+ const { generateStakerJson } = await import('./staker.js');
104
+ await generateStakerJson(options, log);
105
+ });
106
+
79
107
  // top-level convenience: aztec generate-bls-keypair
80
108
  program
81
109
  .command('generate-bls-keypair')
@@ -1,9 +1,13 @@
1
+ import { prettyPrintJSON } from '@aztec/cli/utils';
2
+ import { GSEContract, createEthereumChain } from '@aztec/ethereum';
1
3
  import type { EthAddress } from '@aztec/foundation/eth-address';
2
4
  import type { LogFn } from '@aztec/foundation/log';
3
5
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
6
 
5
7
  import { wordlist } from '@scure/bip39/wordlists/english.js';
6
- import { dirname } from 'path';
8
+ import { writeFile } from 'fs/promises';
9
+ import { basename, dirname, join } from 'path';
10
+ import { createPublicClient, fallback, http } from 'viem';
7
11
  import { generateMnemonic, mnemonicToAccount } from 'viem/accounts';
8
12
 
9
13
  import {
@@ -15,6 +19,7 @@ import {
15
19
  writeEthJsonV3ToFile,
16
20
  writeKeystoreFile,
17
21
  } from './shared.js';
22
+ import { processAttesterAccounts } from './staker.js';
18
23
 
19
24
  export type NewValidatorKeystoreOptions = {
20
25
  dataDir?: string;
@@ -28,7 +33,6 @@ export type NewValidatorKeystoreOptions = {
28
33
  separatePublisher?: boolean;
29
34
  ikm?: string;
30
35
  blsPath?: string;
31
- blsOnly?: boolean;
32
36
  password?: string;
33
37
  outDir?: string;
34
38
  json?: boolean;
@@ -36,6 +40,10 @@ export type NewValidatorKeystoreOptions = {
36
40
  coinbase?: EthAddress;
37
41
  remoteSigner?: string;
38
42
  fundingAccount?: EthAddress;
43
+ stakerOutput?: boolean;
44
+ gseAddress?: EthAddress;
45
+ l1RpcUrls?: string[];
46
+ l1ChainId?: number;
39
47
  };
40
48
 
41
49
  export async function newValidatorKeystore(options: NewValidatorKeystoreOptions, log: LogFn) {
@@ -51,14 +59,30 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
51
59
  feeRecipient,
52
60
  remoteSigner,
53
61
  fundingAccount,
54
- blsOnly,
55
62
  blsPath,
56
63
  ikm,
57
64
  mnemonic: _mnemonic,
58
65
  password,
59
66
  outDir,
67
+ stakerOutput,
68
+ gseAddress,
69
+ l1RpcUrls,
70
+ l1ChainId,
60
71
  } = options;
61
72
 
73
+ // Validate staker output requirements
74
+ if (stakerOutput) {
75
+ if (!gseAddress) {
76
+ throw new Error('--gse-address is required when using --staker-output');
77
+ }
78
+ if (!l1RpcUrls || l1RpcUrls.length === 0) {
79
+ throw new Error('--l1-rpc-urls is required when using --staker-output');
80
+ }
81
+ if (l1ChainId === undefined) {
82
+ throw new Error('--l1-chain-id is required when using --staker-output');
83
+ }
84
+ }
85
+
62
86
  if (remoteSigner && !_mnemonic) {
63
87
  throw new Error(
64
88
  'Using --remote-signer requires a deterministic key source. Provide --mnemonic to derive keys, or omit --remote-signer to write new private keys to keystore.',
@@ -67,6 +91,14 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
67
91
 
68
92
  const mnemonic = _mnemonic ?? generateMnemonic(wordlist);
69
93
 
94
+ if (!_mnemonic && !json) {
95
+ log('No mnemonic provided, generating new one...');
96
+ log(`Using new mnemonic:`);
97
+ log('');
98
+ log(mnemonic);
99
+ log('');
100
+ }
101
+
70
102
  const validatorCount = typeof count === 'number' && Number.isFinite(count) && count > 0 ? Math.floor(count) : 1;
71
103
  const { outputPath } = await resolveKeystoreOutputPath(dataDir, file);
72
104
 
@@ -78,7 +110,6 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
78
110
  mnemonic,
79
111
  ikm,
80
112
  blsPath,
81
- blsOnly,
82
113
  feeRecipient,
83
114
  coinbase,
84
115
  remoteSigner,
@@ -99,15 +130,66 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
99
130
 
100
131
  await writeKeystoreFile(outputPath, keystore);
101
132
 
102
- maybePrintJson(log, json, keystore as unknown as Record<string, any>);
103
- if (!json) {
133
+ // Generate staker outputs if requested
134
+ const allStakerOutputs: any[] = [];
135
+ if (stakerOutput && gseAddress && l1RpcUrls && l1ChainId !== undefined) {
136
+ const chain = createEthereumChain(l1RpcUrls, l1ChainId);
137
+ const publicClient = createPublicClient({
138
+ chain: chain.chainInfo,
139
+ transport: fallback(l1RpcUrls.map(url => http(url))),
140
+ });
141
+ const gse = new GSEContract(publicClient, gseAddress);
142
+
143
+ const keystoreOutDir = outDir && outDir.length > 0 ? outDir : dirname(outputPath);
144
+ // Extract keystore base name without extension for unique staker output filenames
145
+ const keystoreBaseName = basename(outputPath, '.json');
146
+
147
+ // Process each validator
148
+ for (let i = 0; i < validators.length; i++) {
149
+ const validator = validators[i];
150
+ const outputs = await processAttesterAccounts(validator.attester, gse, password);
151
+
152
+ // Save each attester's staker output
153
+ for (let j = 0; j < outputs.length; j++) {
154
+ const attesterIndex = i + 1;
155
+ const stakerOutputPath = join(
156
+ keystoreOutDir,
157
+ `${keystoreBaseName}_attester${attesterIndex}_staker_output.json`,
158
+ );
159
+ await writeFile(stakerOutputPath, prettyPrintJSON(outputs[j]), 'utf-8');
160
+ allStakerOutputs.push(outputs[j]);
161
+ }
162
+ }
163
+ }
164
+
165
+ const outputData = !_mnemonic ? { ...keystore, generatedMnemonic: mnemonic } : keystore;
166
+
167
+ // Handle JSON output
168
+ if (json) {
169
+ if (stakerOutput && allStakerOutputs.length > 0) {
170
+ const combinedOutput = {
171
+ keystore: outputData,
172
+ staker: allStakerOutputs,
173
+ };
174
+ maybePrintJson(log, json, combinedOutput as unknown as Record<string, any>);
175
+ } else {
176
+ maybePrintJson(log, json, outputData as unknown as Record<string, any>);
177
+ }
178
+ } else {
104
179
  log(`Wrote validator keystore to ${outputPath}`);
180
+ if (stakerOutput && allStakerOutputs.length > 0) {
181
+ const keystoreOutDir = outDir && outDir.length > 0 ? outDir : dirname(outputPath);
182
+ log(`Wrote ${allStakerOutputs.length} staker output file(s) to ${keystoreOutDir}`);
183
+ log('');
184
+ }
105
185
  }
106
186
 
107
- // Always print a concise summary of public keys (addresses and BLS pubkeys)
108
- logValidatorSummaries(log, summaries);
187
+ // print a concise summary of public keys (addresses and BLS pubkeys) if no --json options was selected
188
+ if (!json) {
189
+ logValidatorSummaries(log, summaries);
190
+ }
109
191
 
110
- if (!blsOnly && mnemonic && remoteSigner) {
192
+ if (mnemonic && remoteSigner && !json) {
111
193
  for (let i = 0; i < validatorCount; i++) {
112
194
  const addrIdx = addressIndex + i;
113
195
  const acct = mnemonicToAccount(mnemonic, {
@@ -117,4 +199,10 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
117
199
  log(`attester address: ${acct.address} remoteSignerUrl: ${remoteSigner}`);
118
200
  }
119
201
  }
202
+
203
+ // Log staker outputs if not in JSON mode
204
+ if (!json && stakerOutput && allStakerOutputs.length > 0) {
205
+ log('\nStaker outputs:');
206
+ log(prettyPrintJSON(allStakerOutputs));
207
+ }
120
208
  }
@@ -23,7 +23,6 @@ export type BuildValidatorsInput = {
23
23
  mnemonic: string;
24
24
  ikm?: string;
25
25
  blsPath?: string;
26
- blsOnly?: boolean;
27
26
  feeRecipient: AztecAddress;
28
27
  coinbase?: EthAddress;
29
28
  remoteSigner?: string;
@@ -69,7 +68,6 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
69
68
  mnemonic,
70
69
  ikm,
71
70
  blsPath,
72
- blsOnly,
73
71
  feeRecipient,
74
72
  coinbase,
75
73
  remoteSigner,
@@ -85,15 +83,9 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
85
83
  const basePath = blsPath ?? defaultBlsPath;
86
84
  const perValidatorPath = withValidatorIndex(basePath, addressIndex);
87
85
 
88
- const blsPrivKey = blsOnly || ikm || mnemonic ? deriveBlsPrivateKey(mnemonic, ikm, perValidatorPath) : undefined;
86
+ const blsPrivKey = ikm || mnemonic ? deriveBlsPrivateKey(mnemonic, ikm, perValidatorPath) : undefined;
89
87
  const blsPubCompressed = blsPrivKey ? await computeBlsPublicKeyCompressed(blsPrivKey) : undefined;
90
88
 
91
- if (blsOnly) {
92
- const attester = { bls: blsPrivKey! };
93
- summaries.push({ attesterBls: blsPubCompressed });
94
- return { attester, feeRecipient } as ValidatorKeyStore;
95
- }
96
-
97
89
  const ethAttester = deriveEthAttester(mnemonic, accountIndex, addressIndex, remoteSigner);
98
90
  const attester = blsPrivKey ? { eth: ethAttester, bls: blsPrivKey } : ethAttester;
99
91