polkadot-cli 0.6.1 → 0.7.0

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 (3) hide show
  1. package/README.md +37 -3
  2. package/dist/cli.mjs +478 -59
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -22,7 +22,8 @@ dot chain add kusama --rpc wss://kusama-rpc.polkadot.io
22
22
  dot chain add westend --light-client
23
23
 
24
24
  # List configured chains
25
- dot chain list
25
+ dot chains # shorthand
26
+ dot chain list # equivalent
26
27
 
27
28
  # Re-fetch metadata after a runtime upgrade
28
29
  dot chain update # updates default chain
@@ -43,7 +44,8 @@ Dev accounts (Alice, Bob, Charlie, Dave, Eve, Ferdie) are always available for t
43
44
 
44
45
  ```bash
45
46
  # List all accounts (dev + stored)
46
- dot account list
47
+ dot accounts # shorthand
48
+ dot account list # equivalent
47
49
 
48
50
  # Create a new account (generates a mnemonic)
49
51
  dot account create my-validator
@@ -144,10 +146,20 @@ Both dry-run and submission display the encoded call hex and a decoded human-rea
144
146
  Call: 0x0001076465616462656566
145
147
  Decode: System.remark(remark: 0xdeadbeef)
146
148
  Tx: 0xabc123...
147
- Block: #12345678 (0xdef...)
148
149
  Status: ok
149
150
  ```
150
151
 
152
+ #### Exit codes
153
+
154
+ The CLI exits with code **1** when a finalized transaction has a dispatch error (e.g. insufficient balance, bad origin). The full transaction output (events, explorer links) is still printed before the error so you can debug the failure. Module errors are formatted as `PalletName.ErrorVariant` (e.g. `Balances.InsufficientBalance`).
155
+
156
+ ```bash
157
+ dot tx Balances.transferKeepAlive 5FHneW46... 999999999999999999 --from alice
158
+ # ... events and explorer links ...
159
+ # Error: Transaction dispatch error: Balances.InsufficientBalance
160
+ echo $? # 1
161
+ ```
162
+
151
163
  #### Custom signed extensions
152
164
 
153
165
  Chains with non-standard signed extensions (e.g. `people-preview`) are auto-handled:
@@ -210,6 +222,27 @@ polkadot-cli aims to be the single tool for day-to-day chain interaction: storag
210
222
 
211
223
  Outside Polkadot, the closest comparable in terms of interactive UX is [near-cli-rs](https://github.com/near/near-cli-rs) (NEAR).
212
224
 
225
+ ## Update notifications
226
+
227
+ After each command, the CLI checks whether a newer version is available on npm and displays a notification:
228
+
229
+ ```
230
+ ╭───────────────────────────────────────────────╮
231
+ │ │
232
+ │ Update available! 0.6.2 → 0.7.0 │
233
+ │ Run npm i -g polkadot-cli to update │
234
+ │ │
235
+ ╰───────────────────────────────────────────────╯
236
+ ```
237
+
238
+ The version check runs in the background on startup and caches the result for 24 hours in `~/.polkadot/update-check.json`. It never blocks the CLI.
239
+
240
+ The notification is automatically suppressed when:
241
+
242
+ - `DOT_NO_UPDATE_CHECK=1` is set
243
+ - `CI` environment variable is set (any value)
244
+ - stdout is not a TTY (e.g. piped output)
245
+
213
246
  ## Configuration
214
247
 
215
248
  Config and metadata caches live in `~/.polkadot/`:
@@ -218,6 +251,7 @@ Config and metadata caches live in `~/.polkadot/`:
218
251
  ~/.polkadot/
219
252
  ├── config.json # chains and default chain
220
253
  ├── accounts.json # stored accounts (⚠️ secrets are NOT encrypted — see below)
254
+ ├── update-check.json # cached update check result
221
255
  └── chains/
222
256
  └── polkadot/
223
257
  └── metadata.bin # cached SCALE-encoded metadata
package/dist/cli.mjs CHANGED
@@ -5,7 +5,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
5
  // src/cli.ts
6
6
  import cac from "cac";
7
7
  // package.json
8
- var version = "0.6.1";
8
+ var version = "0.7.0";
9
9
 
10
10
  // src/config/accounts-store.ts
11
11
  import { access as access2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
@@ -216,6 +216,7 @@ var isTTY = process.stdout.isTTY ?? false;
216
216
  var RESET = isTTY ? "\x1B[0m" : "";
217
217
  var CYAN = isTTY ? "\x1B[36m" : "";
218
218
  var GREEN = isTTY ? "\x1B[32m" : "";
219
+ var RED = isTTY ? "\x1B[31m" : "";
219
220
  var YELLOW = isTTY ? "\x1B[33m" : "";
220
221
  var MAGENTA = isTTY ? "\x1B[35m" : "";
221
222
  var DIM = isTTY ? "\x1B[2m" : "";
@@ -316,10 +317,9 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
316
317
  Hex seed import (0x...) is not supported via CLI.${RESET}
317
318
  `.trimStart();
318
319
  function registerAccountCommands(cli) {
319
- cli.command("account [action] [name]", "Manage local accounts (create, import, list, remove)").option("--secret <value>", "Secret key (mnemonic or hex seed) for import").action(async (action, name, opts) => {
320
+ cli.command("account [action] [name]", "Manage local accounts (create, import, list, remove)").alias("accounts").option("--secret <value>", "Secret key (mnemonic or hex seed) for import").action(async (action, name, opts) => {
320
321
  if (!action) {
321
- console.log(ACCOUNT_HELP);
322
- return;
322
+ return accountList();
323
323
  }
324
324
  switch (action) {
325
325
  case "create":
@@ -696,10 +696,9 @@ ${BOLD}Examples:${RESET}
696
696
  $ dot chain remove kusama
697
697
  `.trimStart();
698
698
  function registerChainCommands(cli) {
699
- cli.command("chain [action] [name]", "Manage chains (add, remove, update, list, default)").action(async (action, name, opts) => {
699
+ cli.command("chain [action] [name]", "Manage chains (add, remove, update, list, default)").alias("chains").action(async (action, name, opts) => {
700
700
  if (!action) {
701
- console.log(CHAIN_HELP);
702
- return;
701
+ return chainList();
703
702
  }
704
703
  switch (action) {
705
704
  case "add":
@@ -1145,6 +1144,254 @@ function parseValue(arg) {
1145
1144
  return arg;
1146
1145
  }
1147
1146
 
1147
+ // src/commands/tx.ts
1148
+ import { getViewBuilder } from "@polkadot-api/view-builder";
1149
+ import { Binary } from "polkadot-api";
1150
+
1151
+ // src/core/explorers.ts
1152
+ var pjsAppsLink = (rpc, hash) => `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(rpc)}#/explorer/query/${hash}`;
1153
+ var papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=custom&endpoint=${encodeURIComponent(rpc)}`;
1154
+
1155
+ // src/commands/tx.ts
1156
+ function parseStructArgs(meta, fields, args, callLabel) {
1157
+ const fieldNames = Object.keys(fields);
1158
+ if (args.length !== fieldNames.length) {
1159
+ const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
1160
+ throw new Error(`${callLabel} takes ${fieldNames.length} argument(s): ${expected}
1161
+ ` + ` Got ${args.length} argument(s).`);
1162
+ }
1163
+ const result = {};
1164
+ for (let i = 0;i < fieldNames.length; i++) {
1165
+ const name = fieldNames[i];
1166
+ const entry = fields[name];
1167
+ result[name] = parseTypedArg(meta, entry, args[i]);
1168
+ }
1169
+ return result;
1170
+ }
1171
+ function normalizeValue(lookup, entry, value) {
1172
+ let resolved = entry;
1173
+ while (resolved.type === "lookupEntry") {
1174
+ resolved = resolved.value;
1175
+ }
1176
+ switch (resolved.type) {
1177
+ case "enum": {
1178
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && "type" in value) {
1179
+ const enumValue = value;
1180
+ const variant = resolved.value[enumValue.type];
1181
+ if (variant) {
1182
+ let innerEntry = variant;
1183
+ while (innerEntry.type === "lookupEntry") {
1184
+ innerEntry = innerEntry.value;
1185
+ }
1186
+ let normalizedInner = enumValue.value;
1187
+ if (innerEntry.type !== "array" && innerEntry.type !== "sequence" && innerEntry.type !== "void" && Array.isArray(normalizedInner) && normalizedInner.length === 1) {
1188
+ normalizedInner = normalizedInner[0];
1189
+ }
1190
+ if (normalizedInner !== undefined && innerEntry.type !== "void") {
1191
+ normalizedInner = normalizeValue(lookup, innerEntry, normalizedInner);
1192
+ }
1193
+ return { type: enumValue.type, value: normalizedInner };
1194
+ }
1195
+ }
1196
+ return value;
1197
+ }
1198
+ case "struct": {
1199
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
1200
+ const fields = resolved.value;
1201
+ const result = {};
1202
+ for (const [key, val] of Object.entries(value)) {
1203
+ if (key in fields) {
1204
+ result[key] = normalizeValue(lookup, fields[key], val);
1205
+ } else {
1206
+ result[key] = val;
1207
+ }
1208
+ }
1209
+ return result;
1210
+ }
1211
+ return value;
1212
+ }
1213
+ case "array":
1214
+ case "sequence": {
1215
+ let innerResolved = resolved.value;
1216
+ while (innerResolved.type === "lookupEntry") {
1217
+ innerResolved = innerResolved.value;
1218
+ }
1219
+ if (innerResolved.type === "primitive" && innerResolved.value === "u8" && typeof value === "string") {
1220
+ if (/^0x[0-9a-fA-F]*$/.test(value))
1221
+ return Binary.fromHex(value);
1222
+ return Binary.fromText(value);
1223
+ }
1224
+ if (Array.isArray(value)) {
1225
+ const innerEntry = resolved.value;
1226
+ return value.map((item) => normalizeValue(lookup, innerEntry, item));
1227
+ }
1228
+ return value;
1229
+ }
1230
+ case "tuple": {
1231
+ if (Array.isArray(value)) {
1232
+ const entries = resolved.value;
1233
+ return value.map((item, i) => i < entries.length ? normalizeValue(lookup, entries[i], item) : item);
1234
+ }
1235
+ return value;
1236
+ }
1237
+ case "option": {
1238
+ if (value !== null && value !== undefined) {
1239
+ return normalizeValue(lookup, resolved.value, value);
1240
+ }
1241
+ return;
1242
+ }
1243
+ case "primitive": {
1244
+ if (typeof value === "string") {
1245
+ const prim = resolved.value;
1246
+ switch (prim) {
1247
+ case "bool":
1248
+ return value === "true";
1249
+ case "u64":
1250
+ case "u128":
1251
+ case "u256":
1252
+ case "i64":
1253
+ case "i128":
1254
+ case "i256":
1255
+ return BigInt(value);
1256
+ case "u8":
1257
+ case "u16":
1258
+ case "u32":
1259
+ case "i8":
1260
+ case "i16":
1261
+ case "i32":
1262
+ return parseInt(value, 10);
1263
+ }
1264
+ }
1265
+ return value;
1266
+ }
1267
+ case "compact": {
1268
+ if (typeof value === "string") {
1269
+ return resolved.isBig ? BigInt(value) : parseInt(value, 10);
1270
+ }
1271
+ return value;
1272
+ }
1273
+ default:
1274
+ return value;
1275
+ }
1276
+ }
1277
+ function parseTypedArg(meta, entry, arg) {
1278
+ switch (entry.type) {
1279
+ case "primitive":
1280
+ return parsePrimitive(entry.value, arg);
1281
+ case "compact":
1282
+ return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
1283
+ case "AccountId32":
1284
+ case "AccountId20":
1285
+ return arg;
1286
+ case "option": {
1287
+ if (arg === "null" || arg === "undefined" || arg === "none") {
1288
+ return;
1289
+ }
1290
+ return parseTypedArg(meta, entry.value, arg);
1291
+ }
1292
+ case "enum": {
1293
+ if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
1294
+ const callCodec = meta.builder.buildDefinition(meta.lookup.call);
1295
+ return callCodec.dec(Binary.fromHex(arg).asBytes());
1296
+ }
1297
+ if (arg.startsWith("{")) {
1298
+ try {
1299
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1300
+ } catch {}
1301
+ }
1302
+ const variants = Object.keys(entry.value);
1303
+ if (variants.includes("Id")) {
1304
+ const idVariant = entry.value.Id;
1305
+ const innerType = idVariant.type === "lookupEntry" ? idVariant.value : idVariant;
1306
+ if (innerType.type === "AccountId32" && !arg.startsWith("{")) {
1307
+ return { type: "Id", value: arg };
1308
+ }
1309
+ }
1310
+ const matched = variants.find((v) => v.toLowerCase() === arg.toLowerCase());
1311
+ if (matched) {
1312
+ const variant = entry.value[matched];
1313
+ if (variant.type === "void") {
1314
+ return { type: matched };
1315
+ }
1316
+ }
1317
+ return parseValue(arg);
1318
+ }
1319
+ case "sequence":
1320
+ case "array": {
1321
+ const inner = entry.value;
1322
+ if (inner.type === "primitive" && inner.value === "u8") {
1323
+ if (/^0x[0-9a-fA-F]*$/.test(arg))
1324
+ return Binary.fromHex(arg);
1325
+ return Binary.fromText(arg);
1326
+ }
1327
+ if (arg.startsWith("[")) {
1328
+ try {
1329
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1330
+ } catch {}
1331
+ }
1332
+ if (/^0x[0-9a-fA-F]*$/.test(arg))
1333
+ return Binary.fromHex(arg);
1334
+ return parseValue(arg);
1335
+ }
1336
+ case "struct":
1337
+ if (arg.startsWith("{")) {
1338
+ try {
1339
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1340
+ } catch {}
1341
+ }
1342
+ return parseValue(arg);
1343
+ case "tuple":
1344
+ if (arg.startsWith("[")) {
1345
+ try {
1346
+ return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1347
+ } catch {}
1348
+ }
1349
+ return parseValue(arg);
1350
+ default:
1351
+ return parseValue(arg);
1352
+ }
1353
+ }
1354
+ function parsePrimitive(prim, arg) {
1355
+ switch (prim) {
1356
+ case "bool":
1357
+ return arg === "true";
1358
+ case "char":
1359
+ case "str":
1360
+ return arg;
1361
+ case "u8":
1362
+ case "u16":
1363
+ case "u32":
1364
+ case "i8":
1365
+ case "i16":
1366
+ case "i32":
1367
+ return parseInt(arg, 10);
1368
+ case "u64":
1369
+ case "u128":
1370
+ case "u256":
1371
+ case "i64":
1372
+ case "i128":
1373
+ case "i256":
1374
+ return BigInt(arg);
1375
+ default:
1376
+ return parseValue(arg);
1377
+ }
1378
+ }
1379
+ var PAPI_BUILTIN_EXTENSIONS = new Set([
1380
+ "CheckNonZeroSender",
1381
+ "CheckSpecVersion",
1382
+ "CheckTxVersion",
1383
+ "CheckGenesis",
1384
+ "CheckMortality",
1385
+ "CheckNonce",
1386
+ "CheckWeight",
1387
+ "ChargeTransactionPayment",
1388
+ "ChargeAssetTxPayment",
1389
+ "CheckMetadataHash",
1390
+ "StorageWeightReclaim",
1391
+ "PrevalidateAttests"
1392
+ ]);
1393
+ var NO_DEFAULT = Symbol("no-default");
1394
+
1148
1395
  // src/commands/query.ts
1149
1396
  var DEFAULT_LIMIT = 100;
1150
1397
  function registerQueryCommand(cli) {
@@ -1181,8 +1428,14 @@ function registerQueryCommand(cli) {
1181
1428
  }
1182
1429
  const unsafeApi = clientHandle.client.getUnsafeApi();
1183
1430
  const storageApi = unsafeApi.query[palletInfo.name][storageItem.name];
1184
- const parsedKeys = keys.map(parseValue);
1431
+ const parsedKeys = parseStorageKeys(meta, palletInfo.name, storageItem, keys);
1185
1432
  const format = opts.output ?? "pretty";
1433
+ if (format === "json") {
1434
+ console.error(`chain: ${chainName}`);
1435
+ } else {
1436
+ console.log(`${DIM}chain: ${chainName}${RESET}
1437
+ `);
1438
+ }
1186
1439
  if (storageItem.type === "map" && parsedKeys.length === 0) {
1187
1440
  const entries = await storageApi.getEntries();
1188
1441
  const limit = Number(opts.limit);
@@ -1205,16 +1458,46 @@ ${DIM}Showing ${limit} of ${entries.length} entries. Use --limit 0 for all.${RES
1205
1458
  }
1206
1459
  });
1207
1460
  }
1461
+ function parseStorageKeys(meta, palletName, storageItem, args) {
1462
+ if (storageItem.type === "plain" || storageItem.keyTypeId == null) {
1463
+ return args.map(parseValue);
1464
+ }
1465
+ if (args.length === 0)
1466
+ return [];
1467
+ const storageEntry = meta.builder.buildStorage(palletName, storageItem.name);
1468
+ const len = storageEntry.len;
1469
+ const keyEntry = meta.lookup(storageItem.keyTypeId);
1470
+ if (len === 1) {
1471
+ if (args.length === 1) {
1472
+ return [parseTypedArg(meta, keyEntry, args[0])];
1473
+ }
1474
+ if (keyEntry.type === "struct") {
1475
+ const label = `${palletName}.${storageItem.name} key`;
1476
+ return [parseStructArgs(meta, keyEntry.value, args, label)];
1477
+ }
1478
+ const typeDesc = describeType(meta.lookup, storageItem.keyTypeId);
1479
+ throw new Error(`${palletName}.${storageItem.name} key expects ${typeDesc}
1480
+ ` + ` Pass 1 argument. Got ${args.length}.`);
1481
+ }
1482
+ if (args.length !== len) {
1483
+ let typeDesc;
1484
+ if (keyEntry.type === "tuple") {
1485
+ typeDesc = keyEntry.value.map((e) => describeType(meta.lookup, e.id)).join(", ");
1486
+ } else {
1487
+ typeDesc = describeType(meta.lookup, storageItem.keyTypeId);
1488
+ }
1489
+ throw new Error(`${palletName}.${storageItem.name} expects ${len} key arg(s): (${typeDesc}). Got ${args.length}.`);
1490
+ }
1491
+ if (keyEntry.type === "tuple") {
1492
+ const entries = keyEntry.value;
1493
+ return entries.map((entry, i) => parseTypedArg(meta, entry, args[i]));
1494
+ }
1495
+ return args.map((arg) => parseTypedArg(meta, keyEntry, arg));
1496
+ }
1208
1497
 
1209
1498
  // src/commands/tx.ts
1210
- import { getViewBuilder } from "@polkadot-api/view-builder";
1211
- import { Binary } from "polkadot-api";
1212
-
1213
- // src/core/explorers.ts
1214
- var pjsAppsLink = (rpc, hash) => `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(rpc)}#/explorer/query/${hash}`;
1215
- var papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=custom&endpoint=${encodeURIComponent(rpc)}`;
1216
-
1217
- // src/commands/tx.ts
1499
+ import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
1500
+ import { Binary as Binary2 } from "polkadot-api";
1218
1501
  function registerTxCommand(cli) {
1219
1502
  cli.command("tx [target] [...args]", "Submit an extrinsic (e.g. Balances.transferKeepAlive <dest> <amount>)").option("--from <name>", "Account to sign with (required)").option("--dry-run", "Estimate fees without submitting").option("--encode", "Encode call to hex without signing or submitting").option("--ext <json>", `Custom signed extension values as JSON, e.g. '{"ExtName":{"value":...}}'`).action(async (target, args, opts) => {
1220
1503
  if (!target) {
@@ -1271,7 +1554,7 @@ function registerTxCommand(cli) {
1271
1554
  throw new Error(`Extra arguments are not allowed when submitting a raw call hex.
1272
1555
  ` + "Usage: dot tx 0x<call_hex> --from <account>");
1273
1556
  }
1274
- const callBinary = Binary.fromHex(target);
1557
+ const callBinary = Binary2.fromHex(target);
1275
1558
  tx = await unsafeApi.txFromCallData(callBinary);
1276
1559
  callHex = target;
1277
1560
  } else {
@@ -1291,7 +1574,7 @@ function registerTxCommand(cli) {
1291
1574
  const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
1292
1575
  const encodedArgs = codec.enc(callData);
1293
1576
  const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
1294
- console.log(Binary.fromBytes(fullCall).asHex());
1577
+ console.log(Binary2.fromBytes(fullCall).asHex());
1295
1578
  return;
1296
1579
  }
1297
1580
  tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
@@ -1320,12 +1603,13 @@ function registerTxCommand(cli) {
1320
1603
  console.log(` ${BOLD}Call:${RESET} ${callHex}`);
1321
1604
  console.log(` ${BOLD}Decode:${RESET} ${decodedStr}`);
1322
1605
  console.log(` ${BOLD}Tx:${RESET} ${result.txHash}`);
1323
- console.log(` ${BOLD}Block:${RESET} #${result.block.number} (${result.block.hash})`);
1606
+ let dispatchErrorMsg;
1324
1607
  if (result.ok) {
1325
1608
  console.log(` ${BOLD}Status:${RESET} ${GREEN}ok${RESET}`);
1326
1609
  } else {
1327
- console.log(` ${BOLD}Status:${RESET} ${YELLOW}dispatch error${RESET}`);
1328
- console.log(` ${BOLD}Error:${RESET} ${result.dispatchError.type}${result.dispatchError.value ? `: ${JSON.stringify(result.dispatchError.value)}` : ""}`);
1610
+ dispatchErrorMsg = formatDispatchError(result.dispatchError);
1611
+ console.log(` ${BOLD}Status:${RESET} ${RED}dispatch error${RESET}`);
1612
+ console.log(` ${BOLD}Error:${RESET} ${dispatchErrorMsg}`);
1329
1613
  }
1330
1614
  if (result.events && result.events.length > 0) {
1331
1615
  console.log(` ${BOLD}Events:${RESET}`);
@@ -1348,14 +1632,34 @@ function registerTxCommand(cli) {
1348
1632
  console.log(` ${DIM}PAPI${RESET} ${papiLink(rpcUrl, blockHash)}`);
1349
1633
  }
1350
1634
  console.log();
1635
+ if (!result.ok) {
1636
+ throw new CliError(`Transaction dispatch error: ${dispatchErrorMsg}`);
1637
+ }
1351
1638
  } finally {
1352
1639
  clientHandle?.destroy();
1353
1640
  }
1354
1641
  });
1355
1642
  }
1643
+ function formatDispatchError(err) {
1644
+ if (err.type === "Module" && err.value && typeof err.value === "object") {
1645
+ const mod = err.value;
1646
+ if (mod.type) {
1647
+ const inner = mod.value;
1648
+ if (inner && typeof inner === "object" && inner.type) {
1649
+ return `${mod.type}.${inner.type}`;
1650
+ }
1651
+ return mod.type;
1652
+ }
1653
+ }
1654
+ if (err.value !== undefined && err.value !== null) {
1655
+ const val = typeof err.value === "string" ? err.value : JSON.stringify(err.value);
1656
+ return `${err.type}: ${val}`;
1657
+ }
1658
+ return err.type;
1659
+ }
1356
1660
  function decodeCall(meta, callHex) {
1357
1661
  try {
1358
- const viewBuilder = getViewBuilder(meta.lookup);
1662
+ const viewBuilder = getViewBuilder2(meta.lookup);
1359
1663
  const decoded = viewBuilder.callDecoder(callHex);
1360
1664
  const palletName = decoded.pallet.value.name;
1361
1665
  const callName = decoded.call.value.name;
@@ -1472,30 +1776,30 @@ function parseCallArgs(meta, palletName, callName, args) {
1472
1776
  return;
1473
1777
  }
1474
1778
  if (variant.type === "struct") {
1475
- return parseStructArgs(meta, variant.value, args, `${palletName}.${callName}`);
1779
+ return parseStructArgs2(meta, variant.value, args, `${palletName}.${callName}`);
1476
1780
  }
1477
1781
  if (variant.type === "lookupEntry") {
1478
1782
  const inner = variant.value;
1479
1783
  if (inner.type === "struct") {
1480
- return parseStructArgs(meta, inner.value, args, `${palletName}.${callName}`);
1784
+ return parseStructArgs2(meta, inner.value, args, `${palletName}.${callName}`);
1481
1785
  }
1482
1786
  if (inner.type === "void")
1483
1787
  return;
1484
1788
  if (args.length !== 1) {
1485
1789
  throw new Error(`${palletName}.${callName} takes 1 argument (${describeType(meta.lookup, inner.id)}), but ${args.length} provided.`);
1486
1790
  }
1487
- return parseTypedArg(meta, inner, args[0]);
1791
+ return parseTypedArg2(meta, inner, args[0]);
1488
1792
  }
1489
1793
  if (variant.type === "tuple") {
1490
1794
  const entries = variant.value;
1491
1795
  if (args.length !== entries.length) {
1492
1796
  throw new Error(`${palletName}.${callName} takes ${entries.length} arguments, but ${args.length} provided.`);
1493
1797
  }
1494
- return entries.map((entry, i) => parseTypedArg(meta, entry, args[i]));
1798
+ return entries.map((entry, i) => parseTypedArg2(meta, entry, args[i]));
1495
1799
  }
1496
1800
  return args.length === 0 ? undefined : args.map(parseValue);
1497
1801
  }
1498
- function parseStructArgs(meta, fields, args, callLabel) {
1802
+ function parseStructArgs2(meta, fields, args, callLabel) {
1499
1803
  const fieldNames = Object.keys(fields);
1500
1804
  if (args.length !== fieldNames.length) {
1501
1805
  const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
@@ -1506,11 +1810,11 @@ function parseStructArgs(meta, fields, args, callLabel) {
1506
1810
  for (let i = 0;i < fieldNames.length; i++) {
1507
1811
  const name = fieldNames[i];
1508
1812
  const entry = fields[name];
1509
- result[name] = parseTypedArg(meta, entry, args[i]);
1813
+ result[name] = parseTypedArg2(meta, entry, args[i]);
1510
1814
  }
1511
1815
  return result;
1512
1816
  }
1513
- function normalizeValue(lookup, entry, value) {
1817
+ function normalizeValue2(lookup, entry, value) {
1514
1818
  let resolved = entry;
1515
1819
  while (resolved.type === "lookupEntry") {
1516
1820
  resolved = resolved.value;
@@ -1530,7 +1834,7 @@ function normalizeValue(lookup, entry, value) {
1530
1834
  normalizedInner = normalizedInner[0];
1531
1835
  }
1532
1836
  if (normalizedInner !== undefined && innerEntry.type !== "void") {
1533
- normalizedInner = normalizeValue(lookup, innerEntry, normalizedInner);
1837
+ normalizedInner = normalizeValue2(lookup, innerEntry, normalizedInner);
1534
1838
  }
1535
1839
  return { type: enumValue.type, value: normalizedInner };
1536
1840
  }
@@ -1543,7 +1847,7 @@ function normalizeValue(lookup, entry, value) {
1543
1847
  const result = {};
1544
1848
  for (const [key, val] of Object.entries(value)) {
1545
1849
  if (key in fields) {
1546
- result[key] = normalizeValue(lookup, fields[key], val);
1850
+ result[key] = normalizeValue2(lookup, fields[key], val);
1547
1851
  } else {
1548
1852
  result[key] = val;
1549
1853
  }
@@ -1560,25 +1864,25 @@ function normalizeValue(lookup, entry, value) {
1560
1864
  }
1561
1865
  if (innerResolved.type === "primitive" && innerResolved.value === "u8" && typeof value === "string") {
1562
1866
  if (/^0x[0-9a-fA-F]*$/.test(value))
1563
- return Binary.fromHex(value);
1564
- return Binary.fromText(value);
1867
+ return Binary2.fromHex(value);
1868
+ return Binary2.fromText(value);
1565
1869
  }
1566
1870
  if (Array.isArray(value)) {
1567
1871
  const innerEntry = resolved.value;
1568
- return value.map((item) => normalizeValue(lookup, innerEntry, item));
1872
+ return value.map((item) => normalizeValue2(lookup, innerEntry, item));
1569
1873
  }
1570
1874
  return value;
1571
1875
  }
1572
1876
  case "tuple": {
1573
1877
  if (Array.isArray(value)) {
1574
1878
  const entries = resolved.value;
1575
- return value.map((item, i) => i < entries.length ? normalizeValue(lookup, entries[i], item) : item);
1879
+ return value.map((item, i) => i < entries.length ? normalizeValue2(lookup, entries[i], item) : item);
1576
1880
  }
1577
1881
  return value;
1578
1882
  }
1579
1883
  case "option": {
1580
1884
  if (value !== null && value !== undefined) {
1581
- return normalizeValue(lookup, resolved.value, value);
1885
+ return normalizeValue2(lookup, resolved.value, value);
1582
1886
  }
1583
1887
  return;
1584
1888
  }
@@ -1616,10 +1920,10 @@ function normalizeValue(lookup, entry, value) {
1616
1920
  return value;
1617
1921
  }
1618
1922
  }
1619
- function parseTypedArg(meta, entry, arg) {
1923
+ function parseTypedArg2(meta, entry, arg) {
1620
1924
  switch (entry.type) {
1621
1925
  case "primitive":
1622
- return parsePrimitive(entry.value, arg);
1926
+ return parsePrimitive2(entry.value, arg);
1623
1927
  case "compact":
1624
1928
  return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
1625
1929
  case "AccountId32":
@@ -1629,16 +1933,16 @@ function parseTypedArg(meta, entry, arg) {
1629
1933
  if (arg === "null" || arg === "undefined" || arg === "none") {
1630
1934
  return;
1631
1935
  }
1632
- return parseTypedArg(meta, entry.value, arg);
1936
+ return parseTypedArg2(meta, entry.value, arg);
1633
1937
  }
1634
1938
  case "enum": {
1635
1939
  if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
1636
1940
  const callCodec = meta.builder.buildDefinition(meta.lookup.call);
1637
- return callCodec.dec(Binary.fromHex(arg).asBytes());
1941
+ return callCodec.dec(Binary2.fromHex(arg).asBytes());
1638
1942
  }
1639
1943
  if (arg.startsWith("{")) {
1640
1944
  try {
1641
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1945
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1642
1946
  } catch {}
1643
1947
  }
1644
1948
  const variants = Object.keys(entry.value);
@@ -1663,29 +1967,29 @@ function parseTypedArg(meta, entry, arg) {
1663
1967
  const inner = entry.value;
1664
1968
  if (inner.type === "primitive" && inner.value === "u8") {
1665
1969
  if (/^0x[0-9a-fA-F]*$/.test(arg))
1666
- return Binary.fromHex(arg);
1667
- return Binary.fromText(arg);
1970
+ return Binary2.fromHex(arg);
1971
+ return Binary2.fromText(arg);
1668
1972
  }
1669
1973
  if (arg.startsWith("[")) {
1670
1974
  try {
1671
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1975
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1672
1976
  } catch {}
1673
1977
  }
1674
1978
  if (/^0x[0-9a-fA-F]*$/.test(arg))
1675
- return Binary.fromHex(arg);
1979
+ return Binary2.fromHex(arg);
1676
1980
  return parseValue(arg);
1677
1981
  }
1678
1982
  case "struct":
1679
1983
  if (arg.startsWith("{")) {
1680
1984
  try {
1681
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1985
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1682
1986
  } catch {}
1683
1987
  }
1684
1988
  return parseValue(arg);
1685
1989
  case "tuple":
1686
1990
  if (arg.startsWith("[")) {
1687
1991
  try {
1688
- return normalizeValue(meta.lookup, entry, JSON.parse(arg));
1992
+ return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
1689
1993
  } catch {}
1690
1994
  }
1691
1995
  return parseValue(arg);
@@ -1693,7 +1997,7 @@ function parseTypedArg(meta, entry, arg) {
1693
1997
  return parseValue(arg);
1694
1998
  }
1695
1999
  }
1696
- function parsePrimitive(prim, arg) {
2000
+ function parsePrimitive2(prim, arg) {
1697
2001
  switch (prim) {
1698
2002
  case "bool":
1699
2003
  return arg === "true";
@@ -1718,7 +2022,7 @@ function parsePrimitive(prim, arg) {
1718
2022
  return parseValue(arg);
1719
2023
  }
1720
2024
  }
1721
- var PAPI_BUILTIN_EXTENSIONS = new Set([
2025
+ var PAPI_BUILTIN_EXTENSIONS2 = new Set([
1722
2026
  "CheckNonZeroSender",
1723
2027
  "CheckSpecVersion",
1724
2028
  "CheckTxVersion",
@@ -1748,12 +2052,12 @@ function parseExtOption(ext) {
1748
2052
  ` + `Expected format: '{"ExtName":{"value":...}}'`);
1749
2053
  }
1750
2054
  }
1751
- var NO_DEFAULT = Symbol("no-default");
2055
+ var NO_DEFAULT2 = Symbol("no-default");
1752
2056
  function buildCustomSignedExtensions(meta, userOverrides) {
1753
2057
  const result = {};
1754
2058
  const extensions = getSignedExtensions(meta);
1755
2059
  for (const ext of extensions) {
1756
- if (PAPI_BUILTIN_EXTENSIONS.has(ext.identifier))
2060
+ if (PAPI_BUILTIN_EXTENSIONS2.has(ext.identifier))
1757
2061
  continue;
1758
2062
  if (ext.identifier in userOverrides) {
1759
2063
  result[ext.identifier] = userOverrides[ext.identifier];
@@ -1763,10 +2067,10 @@ function buildCustomSignedExtensions(meta, userOverrides) {
1763
2067
  const addEntry = meta.lookup(ext.additionalSigned);
1764
2068
  const value = autoDefaultForType(valueEntry);
1765
2069
  const add = autoDefaultForType(addEntry);
1766
- if (value !== NO_DEFAULT || add !== NO_DEFAULT) {
2070
+ if (value !== NO_DEFAULT2 || add !== NO_DEFAULT2) {
1767
2071
  result[ext.identifier] = {
1768
- ...value !== NO_DEFAULT ? { value } : {},
1769
- ...add !== NO_DEFAULT ? { additionalSigned: add } : {}
2072
+ ...value !== NO_DEFAULT2 ? { value } : {},
2073
+ ...add !== NO_DEFAULT2 ? { additionalSigned: add } : {}
1770
2074
  };
1771
2075
  }
1772
2076
  }
@@ -1783,7 +2087,7 @@ function autoDefaultForType(entry) {
1783
2087
  return { type: "Disabled", value: undefined };
1784
2088
  }
1785
2089
  }
1786
- return NO_DEFAULT;
2090
+ return NO_DEFAULT2;
1787
2091
  }
1788
2092
  function watchTransaction(observable) {
1789
2093
  const spinner = new Spinner;
@@ -1810,7 +2114,7 @@ function watchTransaction(observable) {
1810
2114
  }
1811
2115
  break;
1812
2116
  case "finalized":
1813
- spinner.stop();
2117
+ spinner.succeed(`Finalized in block #${event.block.number}`);
1814
2118
  resolve(event);
1815
2119
  break;
1816
2120
  }
@@ -1823,6 +2127,110 @@ function watchTransaction(observable) {
1823
2127
  });
1824
2128
  }
1825
2129
 
2130
+ // src/core/update-notifier.ts
2131
+ import { readFileSync } from "node:fs";
2132
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
2133
+ import { join as join3 } from "node:path";
2134
+ var CACHE_FILE = "update-check.json";
2135
+ var STALE_MS = 24 * 60 * 60 * 1000;
2136
+ var FETCH_TIMEOUT_MS = 5000;
2137
+ var REGISTRY_URL = "https://registry.npmjs.org/polkadot-cli/latest";
2138
+ function parseSemver(v) {
2139
+ const clean = v.replace(/^v/, "").split("-")[0] ?? v;
2140
+ const parts = clean.split(".").map(Number);
2141
+ return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
2142
+ }
2143
+ function compareSemver(a, b) {
2144
+ const pa = parseSemver(a);
2145
+ const pb = parseSemver(b);
2146
+ for (let i = 0;i < 3; i++) {
2147
+ const left = pa[i] ?? 0;
2148
+ const right = pb[i] ?? 0;
2149
+ if (left < right)
2150
+ return -1;
2151
+ if (left > right)
2152
+ return 1;
2153
+ }
2154
+ return 0;
2155
+ }
2156
+ function isNewerVersion(current, latest) {
2157
+ return compareSemver(current, latest) < 0;
2158
+ }
2159
+ function buildNotificationBox(current, latest) {
2160
+ const YELLOW2 = "\x1B[33m";
2161
+ const GREEN2 = "\x1B[32m";
2162
+ const CYAN2 = "\x1B[36m";
2163
+ const RESET2 = "\x1B[0m";
2164
+ const BOLD2 = "\x1B[1m";
2165
+ const line1 = `Update available! ${YELLOW2}${current}${RESET2} → ${GREEN2}${BOLD2}${latest}${RESET2}`;
2166
+ const line2 = `Run ${CYAN2}npm i -g polkadot-cli${RESET2} to update`;
2167
+ const visibleLine1 = `Update available! ${current} → ${latest}`;
2168
+ const visibleLine2 = `Run npm i -g polkadot-cli to update`;
2169
+ const innerWidth = Math.max(visibleLine1.length, visibleLine2.length) + 4;
2170
+ const pad1 = " ".repeat(innerWidth - visibleLine1.length - 4);
2171
+ const pad2 = " ".repeat(innerWidth - visibleLine2.length - 4);
2172
+ const empty = " ".repeat(innerWidth);
2173
+ const top = `╭${"─".repeat(innerWidth)}╮`;
2174
+ const bot = `╰${"─".repeat(innerWidth)}╯`;
2175
+ return [
2176
+ top,
2177
+ `│${empty}│`,
2178
+ `│ ${line1}${pad1} │`,
2179
+ `│ ${line2}${pad2} │`,
2180
+ `│${empty}│`,
2181
+ bot
2182
+ ].join(`
2183
+ `);
2184
+ }
2185
+ function getCachePath() {
2186
+ return join3(getConfigDir(), CACHE_FILE);
2187
+ }
2188
+ function readCache() {
2189
+ try {
2190
+ const data = readFileSync(getCachePath(), "utf-8");
2191
+ return JSON.parse(data);
2192
+ } catch {
2193
+ return null;
2194
+ }
2195
+ }
2196
+ async function writeCache(cache) {
2197
+ try {
2198
+ await mkdir3(getConfigDir(), { recursive: true });
2199
+ await writeFile3(getCachePath(), `${JSON.stringify(cache)}
2200
+ `);
2201
+ } catch {}
2202
+ }
2203
+ function startBackgroundCheck(_currentVersion) {
2204
+ const cache = readCache();
2205
+ const now = Date.now();
2206
+ if (cache && now - cache.lastCheck < STALE_MS) {
2207
+ return;
2208
+ }
2209
+ fetch(REGISTRY_URL, {
2210
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
2211
+ }).then((res) => res.json()).then((data) => {
2212
+ const latestVersion = data.version;
2213
+ if (typeof latestVersion === "string") {
2214
+ writeCache({ lastCheck: now, latestVersion });
2215
+ }
2216
+ }).catch(() => {});
2217
+ }
2218
+ function getUpdateNotification(currentVersion) {
2219
+ if (process.env.DOT_NO_UPDATE_CHECK === "1")
2220
+ return null;
2221
+ if (process.env.CI)
2222
+ return null;
2223
+ if (!process.stderr.isTTY)
2224
+ return null;
2225
+ const cache = readCache();
2226
+ if (!cache)
2227
+ return null;
2228
+ if (isNewerVersion(currentVersion, cache.latestVersion)) {
2229
+ return buildNotificationBox(currentVersion, cache.latestVersion);
2230
+ }
2231
+ return null;
2232
+ }
2233
+
1826
2234
  // src/utils/errors.ts
1827
2235
  class CliError2 extends Error {
1828
2236
  constructor(message) {
@@ -1832,6 +2240,7 @@ class CliError2 extends Error {
1832
2240
  }
1833
2241
 
1834
2242
  // src/cli.ts
2243
+ startBackgroundCheck(version);
1835
2244
  var cli = cac("dot");
1836
2245
  cli.option("--chain <name>", "Target chain (default from config)");
1837
2246
  cli.option("--rpc <url>", "Override RPC endpoint for this call");
@@ -1848,6 +2257,13 @@ registerTxCommand(cli);
1848
2257
  registerHashCommand(cli);
1849
2258
  cli.help();
1850
2259
  cli.version(version);
2260
+ function showUpdateAndExit(code) {
2261
+ const note = getUpdateNotification(version);
2262
+ if (note)
2263
+ process.stderr.write(`${note}
2264
+ `);
2265
+ process.exit(code);
2266
+ }
1851
2267
  function handleError(err) {
1852
2268
  if (err instanceof CliError2) {
1853
2269
  console.error(`Error: ${err.message}`);
@@ -1856,16 +2272,19 @@ function handleError(err) {
1856
2272
  } else {
1857
2273
  console.error("An unexpected error occurred:", err);
1858
2274
  }
1859
- process.exit(1);
2275
+ showUpdateAndExit(1);
1860
2276
  }
1861
2277
  try {
1862
2278
  cli.parse(process.argv, { run: false });
1863
- if (!cli.matchedCommandName && !cli.options.help && !cli.options.version) {
2279
+ if (cli.options.version || cli.options.help) {
2280
+ showUpdateAndExit(0);
2281
+ } else if (!cli.matchedCommandName) {
1864
2282
  cli.outputHelp();
2283
+ showUpdateAndExit(0);
1865
2284
  } else {
1866
2285
  const result = cli.runMatchedCommand();
1867
2286
  if (result && typeof result.then === "function") {
1868
- result.then(() => process.exit(0), handleError);
2287
+ result.then(() => showUpdateAndExit(0), handleError);
1869
2288
  }
1870
2289
  }
1871
2290
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkadot-cli",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {