@clanker-chain/clanker-cli 2026.9.8-2 → 2026.9.8
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.
- package/bin/clanker.mjs +8 -22
- package/lib/openclaw-wire.mjs +0 -41
- package/lib/setup.mjs +39 -158
- package/package.json +1 -1
- package/lib/operator-key.mjs +0 -50
package/bin/clanker.mjs
CHANGED
|
@@ -26,8 +26,6 @@ import { resolveForRead, resolveOperatorKey, resolveReadIdentity } from "../lib/
|
|
|
26
26
|
import { runSetup } from "../lib/setup.mjs";
|
|
27
27
|
import { runDoctor } from "../lib/doctor.mjs";
|
|
28
28
|
import {
|
|
29
|
-
botIdentityCard,
|
|
30
|
-
botIdentityJson,
|
|
31
29
|
hubConnectChecklist,
|
|
32
30
|
wireOpenClawMqtt,
|
|
33
31
|
} from "../lib/openclaw-wire.mjs";
|
|
@@ -617,30 +615,18 @@ async function main() {
|
|
|
617
615
|
keyPath: result.key_path,
|
|
618
616
|
});
|
|
619
617
|
if (hasFlag(flags, "--json")) {
|
|
620
|
-
printJson({
|
|
621
|
-
...result,
|
|
622
|
-
openclaw: wire,
|
|
623
|
-
bot_identity: botIdentityJson({
|
|
624
|
-
botId: botLabel,
|
|
625
|
-
keyPath: result.key_path,
|
|
626
|
-
openclawConfigPath: wire.path,
|
|
627
|
-
}),
|
|
628
|
-
});
|
|
618
|
+
printJson({ ...result, openclaw: wire });
|
|
629
619
|
} else {
|
|
630
620
|
console.log(c.green(`Minted bot ${botLabel} under ${operatorLabel}`));
|
|
631
621
|
console.log(`botKey: ${result.bot_key}`);
|
|
622
|
+
console.log(`key file: ${result.key_path}`);
|
|
623
|
+
console.log(`also: ${result.clanker_key_path}`);
|
|
632
624
|
console.log(`tx: ${result.tx}`);
|
|
633
|
-
console.log(
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
keyPath: result.key_path,
|
|
639
|
-
openclawPath: wire.path,
|
|
640
|
-
created: wire.created,
|
|
641
|
-
})) {
|
|
642
|
-
console.log(line);
|
|
643
|
-
}
|
|
625
|
+
console.log(
|
|
626
|
+
wire.created
|
|
627
|
+
? c.green(`Wrote ${wire.path}`)
|
|
628
|
+
: c.green(`Updated channels.mqtt in ${wire.path}`),
|
|
629
|
+
);
|
|
644
630
|
nextHint(
|
|
645
631
|
hubConnectChecklist({
|
|
646
632
|
botId: botLabel,
|
package/lib/openclaw-wire.mjs
CHANGED
|
@@ -94,7 +94,6 @@ export function wireOpenClawMqtt(opts) {
|
|
|
94
94
|
export function hubConnectChecklist(opts) {
|
|
95
95
|
const pin = opts.pluginPin ?? OPENCLAW_PLUGIN_PIN;
|
|
96
96
|
return [
|
|
97
|
-
"Your bot login is already wired — install plugins and restart OpenClaw.",
|
|
98
97
|
`openclaw plugins install @clanker-chain/mqtt-channel-plugin@${pin}`,
|
|
99
98
|
`openclaw plugins install @clanker-chain/mqtt-tools@${pin}`,
|
|
100
99
|
"Enable plugin ids mqtt + mqtt-tools (already set in openclaw.json if we wired it)",
|
|
@@ -103,43 +102,3 @@ export function hubConnectChecklist(opts) {
|
|
|
103
102
|
"DM openclaw.france.prod-1 to smoke the mesh",
|
|
104
103
|
];
|
|
105
104
|
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Plain-language card: what the bot uses vs operator mint key.
|
|
109
|
-
* @param {{
|
|
110
|
-
* botId: string,
|
|
111
|
-
* operatorId: string,
|
|
112
|
-
* keyPath: string,
|
|
113
|
-
* openclawPath: string,
|
|
114
|
-
* created?: boolean,
|
|
115
|
-
* }} opts
|
|
116
|
-
* @returns {string[]}
|
|
117
|
-
*/
|
|
118
|
-
export function botIdentityCard(opts) {
|
|
119
|
-
const openclawState = opts.created
|
|
120
|
-
? `${opts.openclawPath} (channels.mqtt created)`
|
|
121
|
-
: `${opts.openclawPath} (channels.mqtt updated)`;
|
|
122
|
-
return [
|
|
123
|
-
"Bot identity (what OpenClaw uses to CONNECT):",
|
|
124
|
-
`bot id: ${opts.botId}`,
|
|
125
|
-
`operator: ${opts.operatorId}`,
|
|
126
|
-
`bot key: ${opts.keyPath}`,
|
|
127
|
-
`openclaw: ${openclawState}`,
|
|
128
|
-
"Do not give the bot ~/.clanker/op.key — that key is only for mint/transfer.",
|
|
129
|
-
];
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Structured bot_identity for --json mint output.
|
|
134
|
-
* @param {{ botId: string, keyPath: string, openclawConfigPath: string }} opts
|
|
135
|
-
*/
|
|
136
|
-
export function botIdentityJson(opts) {
|
|
137
|
-
return {
|
|
138
|
-
botId: opts.botId,
|
|
139
|
-
keyPath: opts.keyPath,
|
|
140
|
-
openclawConfigPath: opts.openclawConfigPath,
|
|
141
|
-
warnOperatorKey:
|
|
142
|
-
"Do not give the bot ~/.clanker/op.key — that key is only for mint/transfer.",
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
package/lib/setup.mjs
CHANGED
|
@@ -33,11 +33,6 @@ import {
|
|
|
33
33
|
exportFoundryKey,
|
|
34
34
|
resolveFoundryAddress,
|
|
35
35
|
} from "./foundry.mjs";
|
|
36
|
-
import {
|
|
37
|
-
BASE_SEPOLIA_FAUCET_URL,
|
|
38
|
-
consumerFundHints,
|
|
39
|
-
generateOperatorKeyFile,
|
|
40
|
-
} from "./operator-key.mjs";
|
|
41
36
|
import { c, nextHint } from "./ui.mjs";
|
|
42
37
|
import { join } from "node:path";
|
|
43
38
|
|
|
@@ -57,7 +52,6 @@ export function parseSetupFlags(argv) {
|
|
|
57
52
|
let skipChainCheck = false;
|
|
58
53
|
let foundryAccount = null;
|
|
59
54
|
let exportKey = false;
|
|
60
|
-
let generateKey = false;
|
|
61
55
|
|
|
62
56
|
for (let i = 0; i < argv.length; i += 1) {
|
|
63
57
|
const a = argv[i];
|
|
@@ -69,7 +63,6 @@ export function parseSetupFlags(argv) {
|
|
|
69
63
|
else if (a === "--from-block" && argv[i + 1]) fromBlock = BigInt(argv[++i]);
|
|
70
64
|
else if (a === "--foundry-account" && argv[i + 1]) foundryAccount = argv[++i];
|
|
71
65
|
else if (a === "--export-key") exportKey = true;
|
|
72
|
-
else if (a === "--generate-key") generateKey = true;
|
|
73
66
|
else if (a === "--force") force = true;
|
|
74
67
|
else if (a === "--yes" || a === "-y") yes = true;
|
|
75
68
|
else if (a === "--skip-key") skipKey = true;
|
|
@@ -89,7 +82,6 @@ export function parseSetupFlags(argv) {
|
|
|
89
82
|
skipChainCheck,
|
|
90
83
|
foundryAccount,
|
|
91
84
|
exportKey,
|
|
92
|
-
generateKey,
|
|
93
85
|
};
|
|
94
86
|
}
|
|
95
87
|
|
|
@@ -211,7 +203,7 @@ export async function runSetupNonInteractive(argv, opts = {}) {
|
|
|
211
203
|
if (!flags.preset || !PRESETS[flags.preset]) {
|
|
212
204
|
throw new Error(
|
|
213
205
|
"Non-interactive setup requires --preset sepolia|local (stdin is not a TTY). " +
|
|
214
|
-
"Also pass --operator <label> and --
|
|
206
|
+
"Also pass --operator <label> and --address 0x…",
|
|
215
207
|
);
|
|
216
208
|
}
|
|
217
209
|
if (!flags.operator) {
|
|
@@ -226,23 +218,6 @@ export async function runSetupNonInteractive(argv, opts = {}) {
|
|
|
226
218
|
inheritStdio: false,
|
|
227
219
|
};
|
|
228
220
|
|
|
229
|
-
if (flags.generateKey) {
|
|
230
|
-
if (flags.skipKey) {
|
|
231
|
-
throw new Error("Cannot combine --generate-key with --skip-key");
|
|
232
|
-
}
|
|
233
|
-
const dest = keyFile || defaultOperatorKeyPath(home);
|
|
234
|
-
const created = generateOperatorKeyFile(dest, {
|
|
235
|
-
force: flags.force,
|
|
236
|
-
});
|
|
237
|
-
keyFile = created.path;
|
|
238
|
-
if (address && getAddress(address) !== getAddress(created.address)) {
|
|
239
|
-
throw new Error(
|
|
240
|
-
`Generated key address ${created.address} does not match --address ${getAddress(address)}`,
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
address = created.address;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
221
|
if (flags.foundryAccount) {
|
|
247
222
|
if (!address) {
|
|
248
223
|
address = resolveFoundryAddress(flags.foundryAccount, castOpts);
|
|
@@ -263,7 +238,7 @@ export async function runSetupNonInteractive(argv, opts = {}) {
|
|
|
263
238
|
if (!address && env.OPERATOR_PRIVATE_KEY) address = addressFromEnv(env);
|
|
264
239
|
if (!address) {
|
|
265
240
|
throw new Error(
|
|
266
|
-
"Non-interactive setup requires --
|
|
241
|
+
"Non-interactive setup requires --address 0x…, --key-file, OPERATOR_PRIVATE_KEY, or --foundry-account",
|
|
267
242
|
);
|
|
268
243
|
}
|
|
269
244
|
|
|
@@ -330,13 +305,9 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
330
305
|
});
|
|
331
306
|
|
|
332
307
|
clack.intro(c.bold("clanker setup"));
|
|
333
|
-
clack.log.step(
|
|
334
|
-
"Sets up your operator identity (org account) and network for OpenClaw bots.",
|
|
335
|
-
);
|
|
308
|
+
clack.log.step("Creates ~/.clanker/config.json (network) and operator.json (identity).");
|
|
336
309
|
clack.log.message(
|
|
337
|
-
c.dim(
|
|
338
|
-
"New here? Create a key file — no wallet app or Foundry required. Mint needs a little test ETH later.",
|
|
339
|
-
),
|
|
310
|
+
c.dim("whoami works from owner without a key; mint/revoke need a key pointer later."),
|
|
340
311
|
);
|
|
341
312
|
clack.log.message(c.dim(`Profile: ${home}`));
|
|
342
313
|
console.log("");
|
|
@@ -395,28 +366,6 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
395
366
|
|
|
396
367
|
let address = flags.address ?? null;
|
|
397
368
|
let foundryAccountUsed = null;
|
|
398
|
-
let generatedKeyFile = null;
|
|
399
|
-
if (!address && flags.generateKey) {
|
|
400
|
-
const dest = flags.keyFile || defaultOperatorKeyPath(home);
|
|
401
|
-
let forceGen = flags.force;
|
|
402
|
-
if (existsSync(dest) && !forceGen) {
|
|
403
|
-
forceGen = cancelIf(
|
|
404
|
-
await clack.confirm({
|
|
405
|
-
message: `Overwrite existing ${dest}?`,
|
|
406
|
-
initialValue: false,
|
|
407
|
-
}),
|
|
408
|
-
);
|
|
409
|
-
if (!forceGen) {
|
|
410
|
-
clack.cancel("Aborted.");
|
|
411
|
-
process.exit(0);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
const created = generateOperatorKeyFile(dest, { force: true });
|
|
415
|
-
generatedKeyFile = created.path;
|
|
416
|
-
address = created.address;
|
|
417
|
-
clack.log.success(`Created operator key at ${created.path}`);
|
|
418
|
-
clack.log.info(`Your operator address: ${created.address}`);
|
|
419
|
-
}
|
|
420
369
|
if (!address && flags.keyFile) {
|
|
421
370
|
address = addressFromKeyFile(flags.keyFile);
|
|
422
371
|
clack.log.info(`Address from --key-file: ${address}`);
|
|
@@ -439,95 +388,23 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
439
388
|
);
|
|
440
389
|
if (useOp) address = hints.operator.owner;
|
|
441
390
|
}
|
|
442
|
-
if (!address) {
|
|
391
|
+
if (!address && hints.foundryAccounts.length) {
|
|
443
392
|
const options = [
|
|
444
|
-
{
|
|
445
|
-
value:
|
|
446
|
-
label:
|
|
447
|
-
hint: "
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
value: "__keyfile__",
|
|
451
|
-
label: "Use an existing key file…",
|
|
452
|
-
hint: "path to a 0x private key file",
|
|
453
|
-
},
|
|
393
|
+
...hints.foundryAccounts.map((n) => ({
|
|
394
|
+
value: n,
|
|
395
|
+
label: n,
|
|
396
|
+
hint: "resolve via cast wallet address",
|
|
397
|
+
})),
|
|
398
|
+
{ value: "__paste__", label: "Paste an address…", hint: "0x…" },
|
|
454
399
|
];
|
|
455
|
-
if (hints.foundryAccounts.length) {
|
|
456
|
-
for (const n of hints.foundryAccounts) {
|
|
457
|
-
options.push({
|
|
458
|
-
value: n,
|
|
459
|
-
label: `Foundry: ${n}`,
|
|
460
|
-
hint: "advanced — cast wallet",
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
} else {
|
|
464
|
-
options.push({
|
|
465
|
-
value: "__foundry_missing__",
|
|
466
|
-
label: "Foundry account…",
|
|
467
|
-
hint: "advanced — install Foundry first",
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
options.push({
|
|
471
|
-
value: "__paste__",
|
|
472
|
-
label: "Paste an address…",
|
|
473
|
-
hint: "read-only unless you add a key later",
|
|
474
|
-
});
|
|
475
|
-
|
|
476
400
|
const pick = cancelIf(
|
|
477
401
|
await clack.select({
|
|
478
|
-
message: "
|
|
402
|
+
message: "Operator owner source",
|
|
479
403
|
options,
|
|
480
|
-
initialValue:
|
|
404
|
+
initialValue: hints.foundryAccounts[0],
|
|
481
405
|
}),
|
|
482
406
|
);
|
|
483
|
-
|
|
484
|
-
if (pick === "__generate__") {
|
|
485
|
-
const dest = defaultOperatorKeyPath(home);
|
|
486
|
-
let forceGen = flags.force;
|
|
487
|
-
if (existsSync(dest) && !forceGen) {
|
|
488
|
-
forceGen = cancelIf(
|
|
489
|
-
await clack.confirm({
|
|
490
|
-
message: `Overwrite existing ${dest}?`,
|
|
491
|
-
initialValue: false,
|
|
492
|
-
}),
|
|
493
|
-
);
|
|
494
|
-
if (!forceGen) {
|
|
495
|
-
clack.cancel("Aborted.");
|
|
496
|
-
process.exit(0);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
const created = generateOperatorKeyFile(dest, { force: true });
|
|
500
|
-
generatedKeyFile = created.path;
|
|
501
|
-
address = created.address;
|
|
502
|
-
clack.log.success(`Created operator key at ${created.path}`);
|
|
503
|
-
clack.log.info(`Your operator address: ${created.address}`);
|
|
504
|
-
} else if (pick === "__keyfile__") {
|
|
505
|
-
const path = cancelIf(
|
|
506
|
-
await clack.text({
|
|
507
|
-
message: "Path to operator key file",
|
|
508
|
-
placeholder: join(home, "op.key"),
|
|
509
|
-
validate: (v) =>
|
|
510
|
-
v && String(v).trim() && existsSync(String(v).trim())
|
|
511
|
-
? undefined
|
|
512
|
-
: "File not found",
|
|
513
|
-
}),
|
|
514
|
-
);
|
|
515
|
-
generatedKeyFile = String(path).trim();
|
|
516
|
-
address = addressFromKeyFile(generatedKeyFile);
|
|
517
|
-
clack.log.info(`Address from key file: ${address}`);
|
|
518
|
-
} else if (pick === "__foundry_missing__") {
|
|
519
|
-
clack.log.warn(
|
|
520
|
-
"Foundry (cast) is not available. Install https://book.getfoundry.sh/ or choose Create a new operator key.",
|
|
521
|
-
);
|
|
522
|
-
address = cancelIf(
|
|
523
|
-
await clack.text({
|
|
524
|
-
message: "Operator owner address",
|
|
525
|
-
placeholder: "0x…",
|
|
526
|
-
validate: (v) =>
|
|
527
|
-
/^0x[0-9a-fA-F]{40}$/.test(v || "") ? undefined : "Need 0x + 40 hex",
|
|
528
|
-
}),
|
|
529
|
-
);
|
|
530
|
-
} else if (pick === "__paste__") {
|
|
407
|
+
if (pick === "__paste__") {
|
|
531
408
|
address = cancelIf(
|
|
532
409
|
await clack.text({
|
|
533
410
|
message: "Operator owner address",
|
|
@@ -559,6 +436,16 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
559
436
|
}
|
|
560
437
|
}
|
|
561
438
|
}
|
|
439
|
+
if (!address) {
|
|
440
|
+
address = cancelIf(
|
|
441
|
+
await clack.text({
|
|
442
|
+
message: "Operator owner address",
|
|
443
|
+
placeholder: "0x…",
|
|
444
|
+
validate: (v) =>
|
|
445
|
+
/^0x[0-9a-fA-F]{40}$/.test(v || "") ? undefined : "Need 0x + 40 hex",
|
|
446
|
+
}),
|
|
447
|
+
);
|
|
448
|
+
}
|
|
562
449
|
address = getAddress(address);
|
|
563
450
|
|
|
564
451
|
let label = flags.operator || hints.operator?.label || null;
|
|
@@ -596,7 +483,7 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
596
483
|
throw err;
|
|
597
484
|
}
|
|
598
485
|
|
|
599
|
-
let keyFile = flags.keyFile
|
|
486
|
+
let keyFile = flags.keyFile;
|
|
600
487
|
let keyEnv = flags.keyEnv;
|
|
601
488
|
let skipKey = flags.skipKey;
|
|
602
489
|
if (!skipKey && !keyFile && !keyEnv) {
|
|
@@ -692,16 +579,7 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
692
579
|
if (!key) {
|
|
693
580
|
nextHint([
|
|
694
581
|
"clanker whoami",
|
|
695
|
-
"clanker setup
|
|
696
|
-
]);
|
|
697
|
-
} else if (preset === "sepolia" && generatedKeyFile) {
|
|
698
|
-
nextHint(consumerFundHints({ address, label }));
|
|
699
|
-
} else if (preset === "sepolia") {
|
|
700
|
-
nextHint([
|
|
701
|
-
`If this address needs test ETH: ${BASE_SEPOLIA_FAUCET_URL}`,
|
|
702
|
-
"clanker doctor",
|
|
703
|
-
"clanker whoami",
|
|
704
|
-
`clanker bot mint <label>`,
|
|
582
|
+
"clanker setup --key-file ~/.clanker/op.key --force # when you need mint",
|
|
705
583
|
]);
|
|
706
584
|
} else {
|
|
707
585
|
nextHint(["clanker whoami", `clanker bot mint <label>`]);
|
|
@@ -715,20 +593,23 @@ export async function runSetupInteractive(argv, opts = {}) {
|
|
|
715
593
|
export async function runSetup(argv, opts = {}) {
|
|
716
594
|
const flags = parseSetupFlags(argv);
|
|
717
595
|
const isTTY = opts.isTTY ?? Boolean(input.isTTY);
|
|
718
|
-
const hasIdentitySource = Boolean(
|
|
719
|
-
flags.address ||
|
|
720
|
-
flags.keyFile ||
|
|
721
|
-
flags.generateKey ||
|
|
722
|
-
flags.foundryAccount ||
|
|
723
|
-
opts.env?.OPERATOR_PRIVATE_KEY ||
|
|
724
|
-
process.env.OPERATOR_PRIVATE_KEY,
|
|
725
|
-
);
|
|
726
596
|
|
|
727
|
-
if (
|
|
597
|
+
if (
|
|
598
|
+
!isTTY ||
|
|
599
|
+
(flags.yes && flags.preset && flags.operator && (flags.address || flags.keyFile))
|
|
600
|
+
) {
|
|
728
601
|
if (!isTTY && !(flags.preset && flags.operator)) {
|
|
729
602
|
return runSetupNonInteractive(argv, opts);
|
|
730
603
|
}
|
|
731
|
-
if (
|
|
604
|
+
if (
|
|
605
|
+
flags.yes &&
|
|
606
|
+
flags.preset &&
|
|
607
|
+
flags.operator &&
|
|
608
|
+
(flags.address ||
|
|
609
|
+
flags.keyFile ||
|
|
610
|
+
opts.env?.OPERATOR_PRIVATE_KEY ||
|
|
611
|
+
process.env.OPERATOR_PRIVATE_KEY)
|
|
612
|
+
) {
|
|
732
613
|
return runSetupNonInteractive(argv, opts);
|
|
733
614
|
}
|
|
734
615
|
}
|
package/package.json
CHANGED
package/lib/operator-key.mjs
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Consumer-path operator key helpers (generate local op.key).
|
|
3
|
-
* Never logs private keys.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
7
|
-
import { dirname } from "node:path";
|
|
8
|
-
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
|
|
9
|
-
import { defaultOperatorKeyPath } from "./foundry.mjs";
|
|
10
|
-
|
|
11
|
-
export { defaultOperatorKeyPath };
|
|
12
|
-
|
|
13
|
-
/** Coinbase Developer Platform faucet UI (Base Sepolia). */
|
|
14
|
-
export const BASE_SEPOLIA_FAUCET_URL =
|
|
15
|
-
"https://portal.cdp.coinbase.com/products/faucet";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Generate a new secp256k1 key and write it to destPath (mode 0o600).
|
|
19
|
-
* Does not print the key.
|
|
20
|
-
*
|
|
21
|
-
* @param {string} destPath
|
|
22
|
-
* @param {{ force?: boolean }} [opts]
|
|
23
|
-
* @returns {{ path: string, address: string }}
|
|
24
|
-
*/
|
|
25
|
-
export function generateOperatorKeyFile(destPath, opts = {}) {
|
|
26
|
-
if (existsSync(destPath) && !opts.force) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
`Key file already exists: ${destPath} (pass --force to overwrite)`,
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
const key = generatePrivateKey();
|
|
32
|
-
const address = privateKeyToAccount(key).address;
|
|
33
|
-
mkdirSync(dirname(destPath), { recursive: true });
|
|
34
|
-
writeFileSync(destPath, `${key}\n`, { mode: 0o600 });
|
|
35
|
-
return { path: destPath, address };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Plain-language next steps after creating an operator key on Sepolia.
|
|
40
|
-
* @param {{ address: string, label?: string }} opts
|
|
41
|
-
*/
|
|
42
|
-
export function consumerFundHints(opts) {
|
|
43
|
-
const mintLabel = opts.label ? ` ${opts.label}` : "";
|
|
44
|
-
return [
|
|
45
|
-
`Your operator address is ${opts.address} — fund it with Base Sepolia ETH: ${BASE_SEPOLIA_FAUCET_URL}`,
|
|
46
|
-
"clanker doctor",
|
|
47
|
-
`clanker operator mint${mintLabel} --yes`,
|
|
48
|
-
"clanker bot mint <bot_label> --yes",
|
|
49
|
-
];
|
|
50
|
-
}
|