@clanker-chain/clanker-cli 2026.9.7-1

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/.env.example ADDED
@@ -0,0 +1,14 @@
1
+ # Optional: copy to clanker-cli/.env after Sepolia deploy.
2
+ # chain/.env is also loaded by scripts/with-sepolia-env.sh.
3
+
4
+ BASE_SEPOLIA_RPC_URL=https://sepolia.base.org
5
+ CHAIN_RPC_URL=https://sepolia.base.org
6
+
7
+ # Set after `npm run deploy:sepolia` in chain/
8
+ REGISTRY_ADDRESS=0xYourDeployedRegistryHere
9
+
10
+ # Operator key for mint-* (hex). Prefer exporting temporarily in the shell
11
+ # instead of storing here long-term. Must own the operator on-chain.
12
+ # Never use Anvil account #0 on Sepolia — the CLI refuses it on non-local RPCs.
13
+ # Preferred: clanker init --preset sepolia (see docs/operator-cli.md).
14
+ # OPERATOR_PRIVATE_KEY=0x...
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * On-chain identity commands (ClankerIdentity contract).
4
+ */
5
+
6
+ import {
7
+ createPublicClient,
8
+ createWalletClient,
9
+ defineChain,
10
+ http,
11
+ } from "viem";
12
+ import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
13
+ import { clankerIdentityAbi } from "../lib/clanker-identity-abi.mjs";
14
+ import { assertSafeBotLabel, writeBotKeyFiles } from "../lib/keys.mjs";
15
+ import { harnessSnippet, writeOperator } from "../lib/profile.mjs";
16
+ import { labelToId } from "../lib/identity-query.mjs";
17
+ import { resolveOperatorKey } from "../lib/resolve.mjs";
18
+
19
+ async function chainFromRpc(rpc) {
20
+ const publicClient = createPublicClient({ transport: http(rpc) });
21
+ const id = await publicClient.getChainId();
22
+ return defineChain({
23
+ id,
24
+ name: `clanker-chain-${id}`,
25
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
26
+ rpcUrls: { default: { http: [rpc] } },
27
+ });
28
+ }
29
+
30
+ async function walletFromKey(rpc, key) {
31
+ const account = privateKeyToAccount(key);
32
+ const transport = http(rpc);
33
+ const chain = await chainFromRpc(rpc);
34
+ return {
35
+ account,
36
+ public: createPublicClient({ chain, transport }),
37
+ wallet: createWalletClient({ account, chain, transport }),
38
+ };
39
+ }
40
+
41
+ async function waitOk(pub, hash) {
42
+ const receipt = await pub.waitForTransactionReceipt({ hash });
43
+ if (receipt.status !== "success") {
44
+ throw new Error(`Transaction reverted: ${hash}`);
45
+ }
46
+ return receipt;
47
+ }
48
+
49
+ /**
50
+ * Shared resolve for mutating chain commands (Anvil-guarded).
51
+ * @param {string[]} argv
52
+ */
53
+ export function resolveForWrite(argv) {
54
+ return resolveOperatorKey(argv);
55
+ }
56
+
57
+ export async function chainMintOperator(label, argv, opts = {}) {
58
+ const { key, address, network, keyPointer } = resolveOperatorKey(argv, opts);
59
+ const { public: pub, wallet } = await walletFromKey(network.rpc, key);
60
+ const operatorFee = await pub.readContract({
61
+ address: network.registry,
62
+ abi: clankerIdentityAbi,
63
+ functionName: "operatorFee",
64
+ });
65
+ const hash = await wallet.writeContract({
66
+ address: network.registry,
67
+ abi: clankerIdentityAbi,
68
+ functionName: "registerOperator",
69
+ args: [label],
70
+ value: operatorFee,
71
+ });
72
+ await waitOk(pub, hash);
73
+
74
+ writeOperator(
75
+ { label, owner: address, key: opts.keyPointer ?? keyPointer },
76
+ opts.home ?? network.home,
77
+ );
78
+
79
+ return {
80
+ ok: true,
81
+ label,
82
+ operator_id: labelToId(label),
83
+ owner: address,
84
+ tx: hash,
85
+ };
86
+ }
87
+
88
+ export async function chainMintBot(botLabel, operatorLabel, argv, opts = {}) {
89
+ assertSafeBotLabel(botLabel);
90
+ const { key, network } = resolveOperatorKey(argv, opts);
91
+ const { public: pub, wallet } = await walletFromKey(network.rpc, key);
92
+ let botPrivateKey;
93
+ for (let i = 0; i < argv.length; i += 1) {
94
+ const a = argv[i];
95
+ if (a === "--bot-key" && argv[i + 1]) {
96
+ botPrivateKey = argv[++i];
97
+ }
98
+ }
99
+ if (!botPrivateKey) {
100
+ botPrivateKey = generatePrivateKey();
101
+ }
102
+ const botAccount = privateKeyToAccount(botPrivateKey);
103
+ const operatorIdBytes = labelToId(operatorLabel);
104
+
105
+ // Persist the key BEFORE broadcasting: registration costs ETH.
106
+ const { openclawPath, clankerPath } = writeBotKeyFiles(
107
+ botLabel,
108
+ botPrivateKey,
109
+ {
110
+ env: opts.env,
111
+ openclawKeysDir: opts.openclawKeysDir,
112
+ clankerKeysDir: opts.clankerKeysDir,
113
+ },
114
+ );
115
+
116
+ const botFee = await pub.readContract({
117
+ address: network.registry,
118
+ abi: clankerIdentityAbi,
119
+ functionName: "botFee",
120
+ });
121
+ const hash = await wallet.writeContract({
122
+ address: network.registry,
123
+ abi: clankerIdentityAbi,
124
+ functionName: "registerBot",
125
+ args: [operatorIdBytes, botLabel, botAccount.address],
126
+ value: botFee,
127
+ });
128
+ await waitOk(pub, hash);
129
+
130
+ const channelsMqtt = harnessSnippet({
131
+ botId: botLabel,
132
+ operatorId: operatorLabel,
133
+ network,
134
+ });
135
+
136
+ return {
137
+ ok: true,
138
+ bot_id: botLabel,
139
+ operator_id: operatorLabel,
140
+ bot_key: botAccount.address,
141
+ key_path: openclawPath,
142
+ clanker_key_path: clankerPath,
143
+ tx: hash,
144
+ channels_mqtt: channelsMqtt,
145
+ };
146
+ }
147
+
148
+ export async function chainRotateBotKey(botLabel, newKey, argv, opts = {}) {
149
+ const { key, network } = resolveOperatorKey(argv, opts);
150
+ const { public: pub, wallet } = await walletFromKey(network.rpc, key);
151
+ const botIdBytes = labelToId(botLabel);
152
+ const hash = await wallet.writeContract({
153
+ address: network.registry,
154
+ abi: clankerIdentityAbi,
155
+ functionName: "rotateBotKey",
156
+ args: [botIdBytes, newKey],
157
+ });
158
+ await waitOk(pub, hash);
159
+ return { ok: true, bot_id: botLabel, new_key: newKey, tx: hash };
160
+ }
161
+
162
+ export async function chainRevokeBot(botLabel, argv, opts = {}) {
163
+ const { key, network } = resolveOperatorKey(argv, opts);
164
+ const { public: pub, wallet } = await walletFromKey(network.rpc, key);
165
+ const botIdBytes = labelToId(botLabel);
166
+ const hash = await wallet.writeContract({
167
+ address: network.registry,
168
+ abi: clankerIdentityAbi,
169
+ functionName: "revokeBot",
170
+ args: [botIdBytes],
171
+ });
172
+ await waitOk(pub, hash);
173
+ return { ok: true, bot_id: botLabel, tx: hash };
174
+ }
175
+
176
+ export async function chainProposeOperatorTransfer(label, newOwner, argv, opts = {}) {
177
+ const { key, network } = resolveOperatorKey(argv, opts);
178
+ const { public: pub, wallet } = await walletFromKey(network.rpc, key);
179
+ const id = labelToId(label);
180
+ const hash = await wallet.writeContract({
181
+ address: network.registry,
182
+ abi: clankerIdentityAbi,
183
+ functionName: "proposeOperatorTransfer",
184
+ args: [id, newOwner],
185
+ });
186
+ await waitOk(pub, hash);
187
+ return { ok: true, label, proposed: newOwner, tx: hash };
188
+ }
189
+
190
+ export async function chainAcceptOperatorTransfer(label, argv, opts = {}) {
191
+ const { key, address, network, keyPointer } = resolveOperatorKey(argv, opts);
192
+ const { public: pub, wallet } = await walletFromKey(network.rpc, key);
193
+ const id = labelToId(label);
194
+ const hash = await wallet.writeContract({
195
+ address: network.registry,
196
+ abi: clankerIdentityAbi,
197
+ functionName: "acceptOperatorTransfer",
198
+ args: [id],
199
+ });
200
+ await waitOk(pub, hash);
201
+ writeOperator(
202
+ {
203
+ label,
204
+ owner: address,
205
+ key: opts.keyPointer ?? keyPointer,
206
+ },
207
+ opts.home ?? network.home,
208
+ );
209
+ return { ok: true, label, owner: address, tx: hash };
210
+ }
211
+
212
+ export { labelToId };