polkadot-cli 0.2.1 → 0.4.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 (2) hide show
  1. package/dist/cli.mjs +207 -19
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -275,6 +275,13 @@ function findPallet(meta, palletName) {
275
275
  const pallets = listPallets(meta);
276
276
  return pallets.find((p) => p.name.toLowerCase() === palletName.toLowerCase());
277
277
  }
278
+ function getSignedExtensions(meta) {
279
+ const byVersion = meta.unified.extrinsic.signedExtensions;
280
+ const versionKeys = Object.keys(byVersion);
281
+ if (versionKeys.length === 0)
282
+ return [];
283
+ return byVersion[Number(versionKeys[0])] ?? [];
284
+ }
278
285
  function getPalletNames(meta) {
279
286
  return meta.unified.pallets.map((p) => p.name);
280
287
  }
@@ -1061,41 +1068,64 @@ async function accountRemove(name) {
1061
1068
 
1062
1069
  // src/commands/tx.ts
1063
1070
  import { Binary } from "polkadot-api";
1071
+ import { getViewBuilder } from "@polkadot-api/view-builder";
1064
1072
  function registerTxCommand(cli) {
1065
- 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").action(async (target, args, opts) => {
1073
+ 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("--ext <json>", `Custom signed extension values as JSON, e.g. '{"ExtName":{"value":...}}'`).action(async (target, args, opts) => {
1066
1074
  if (!target || !opts.from) {
1067
- console.log("Usage: dot tx <Pallet.Call> [...args] --from <account> [--dry-run]");
1075
+ console.log("Usage: dot tx <Pallet.Call|0xCallHex> [...args] --from <account> [--dry-run]");
1068
1076
  console.log("");
1069
1077
  console.log("Examples:");
1070
1078
  console.log(" $ dot tx Balances.transferKeepAlive 5FHn... 1000000000000 --from alice");
1071
1079
  console.log(" $ dot tx System.remark 0xdeadbeef --from alice --dry-run");
1080
+ console.log(" $ dot tx 0x0001076465616462656566 --from alice");
1072
1081
  return;
1073
1082
  }
1083
+ const isRawCall = /^0x[0-9a-fA-F]+$/.test(target);
1074
1084
  const config = await loadConfig();
1075
1085
  const { name: chainName, chain: chainConfig } = resolveChain(config, opts.chain);
1076
- const { pallet, item: callName } = parseTarget(target);
1077
1086
  const signer = await resolveAccountSigner(opts.from);
1078
1087
  const clientHandle = await createChainClient(chainName, chainConfig, opts.rpc);
1079
1088
  try {
1080
1089
  const meta = await getOrFetchMetadata(chainName, clientHandle);
1081
- const palletNames = getPalletNames(meta);
1082
- const palletInfo = findPallet(meta, pallet);
1083
- if (!palletInfo) {
1084
- throw new Error(suggestMessage("pallet", pallet, palletNames));
1085
- }
1086
- const callInfo = palletInfo.calls.find((c) => c.name.toLowerCase() === callName.toLowerCase());
1087
- if (!callInfo) {
1088
- const callNames = palletInfo.calls.map((c) => c.name);
1089
- throw new Error(suggestMessage(`call in ${palletInfo.name}`, callName, callNames));
1090
- }
1091
- const callData = parseCallArgs(meta, palletInfo.name, callInfo.name, args);
1090
+ const userExtOverrides = parseExtOption(opts.ext);
1091
+ const customSignedExtensions = buildCustomSignedExtensions(meta, userExtOverrides);
1092
+ const txOptions = Object.keys(customSignedExtensions).length > 0 ? { customSignedExtensions } : undefined;
1092
1093
  const unsafeApi = clientHandle.client.getUnsafeApi();
1093
- const tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
1094
+ let tx;
1095
+ let callHex;
1096
+ if (isRawCall) {
1097
+ if (args.length > 0) {
1098
+ throw new Error(`Extra arguments are not allowed when submitting a raw call hex.
1099
+ ` + "Usage: dot tx 0x<call_hex> --from <account>");
1100
+ }
1101
+ const callBinary = Binary.fromHex(target);
1102
+ tx = await unsafeApi.txFromCallData(callBinary);
1103
+ callHex = target;
1104
+ } else {
1105
+ const { pallet, item: callName } = parseTarget(target);
1106
+ const palletNames = getPalletNames(meta);
1107
+ const palletInfo = findPallet(meta, pallet);
1108
+ if (!palletInfo) {
1109
+ throw new Error(suggestMessage("pallet", pallet, palletNames));
1110
+ }
1111
+ const callInfo = palletInfo.calls.find((c) => c.name.toLowerCase() === callName.toLowerCase());
1112
+ if (!callInfo) {
1113
+ const callNames = palletInfo.calls.map((c) => c.name);
1114
+ throw new Error(suggestMessage(`call in ${palletInfo.name}`, callName, callNames));
1115
+ }
1116
+ const callData = parseCallArgs(meta, palletInfo.name, callInfo.name, args);
1117
+ tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
1118
+ const encodedCall = await tx.getEncodedData();
1119
+ callHex = encodedCall.asHex();
1120
+ }
1121
+ const decodedStr = decodeCall(meta, callHex);
1094
1122
  if (opts.dryRun) {
1095
1123
  const signerAddress = toSs58(signer.publicKey);
1096
- console.log(` ${BOLD}From:${RESET} ${opts.from} (${signerAddress})`);
1124
+ console.log(` ${BOLD}From:${RESET} ${opts.from} (${signerAddress})`);
1125
+ console.log(` ${BOLD}Call:${RESET} ${callHex}`);
1126
+ console.log(` ${BOLD}Decode:${RESET} ${decodedStr}`);
1097
1127
  try {
1098
- const fees = await tx.getEstimatedFees(signer.publicKey);
1128
+ const fees = await tx.getEstimatedFees(signer.publicKey, txOptions);
1099
1129
  console.log(` ${BOLD}Estimated fees:${RESET} ${fees}`);
1100
1130
  } catch (err) {
1101
1131
  console.log(` ${BOLD}Estimated fees:${RESET} ${YELLOW}unable to estimate${RESET}`);
@@ -1104,8 +1134,10 @@ function registerTxCommand(cli) {
1104
1134
  return;
1105
1135
  }
1106
1136
  console.log("Signing and submitting...");
1107
- const result = await tx.signAndSubmit(signer);
1137
+ const result = await tx.signAndSubmit(signer, txOptions);
1108
1138
  console.log();
1139
+ console.log(` ${BOLD}Call:${RESET} ${callHex}`);
1140
+ console.log(` ${BOLD}Decode:${RESET} ${decodedStr}`);
1109
1141
  console.log(` ${BOLD}Tx:${RESET} ${result.txHash}`);
1110
1142
  if (result.block) {
1111
1143
  console.log(` ${BOLD}Block:${RESET} #${result.block.number} (${result.block.hash})`);
@@ -1135,6 +1167,95 @@ function registerTxCommand(cli) {
1135
1167
  }
1136
1168
  });
1137
1169
  }
1170
+ function decodeCall(meta, callHex) {
1171
+ try {
1172
+ const viewBuilder = getViewBuilder(meta.lookup);
1173
+ const decoded = viewBuilder.callDecoder(callHex);
1174
+ const palletName = decoded.pallet.value.name;
1175
+ const callName = decoded.call.value.name;
1176
+ const argsStr = formatDecodedArgs(decoded.args.value);
1177
+ return `${palletName}.${callName}${argsStr}`;
1178
+ } catch {
1179
+ return "(unable to decode)";
1180
+ }
1181
+ }
1182
+ function formatDecodedArgs(decoded) {
1183
+ return formatDecoded(decoded);
1184
+ }
1185
+ function formatDecoded(d) {
1186
+ switch (d.codec) {
1187
+ case "_void":
1188
+ return "";
1189
+ case "bool":
1190
+ return d.value.toString();
1191
+ case "str":
1192
+ case "char":
1193
+ return d.value;
1194
+ case "u8":
1195
+ case "u16":
1196
+ case "u32":
1197
+ case "i8":
1198
+ case "i16":
1199
+ case "i32":
1200
+ case "compactNumber":
1201
+ return d.value.toString();
1202
+ case "u64":
1203
+ case "u128":
1204
+ case "u256":
1205
+ case "i64":
1206
+ case "i128":
1207
+ case "i256":
1208
+ case "compactBn":
1209
+ return d.value.toString();
1210
+ case "bitSequence":
1211
+ return `0x${Buffer.from(d.value.bytes).toString("hex")}`;
1212
+ case "AccountId":
1213
+ return d.value.address;
1214
+ case "ethAccount":
1215
+ return d.value;
1216
+ case "Bytes":
1217
+ return d.value;
1218
+ case "BytesArray":
1219
+ return d.value;
1220
+ case "Enum": {
1221
+ const inner = formatDecoded(d.value.value);
1222
+ if (!inner)
1223
+ return d.value.type;
1224
+ return `${d.value.type}(${inner})`;
1225
+ }
1226
+ case "Struct": {
1227
+ const entries = Object.entries(d.value);
1228
+ if (entries.length === 0)
1229
+ return " {}";
1230
+ const fields = entries.map(([k, v]) => `${k}: ${formatDecoded(v)}`).join(", ");
1231
+ return ` { ${fields} }`;
1232
+ }
1233
+ case "Tuple": {
1234
+ const items = d.value.map(formatDecoded).join(", ");
1235
+ return `(${items})`;
1236
+ }
1237
+ case "Option": {
1238
+ if (d.value.codec === "_void")
1239
+ return "None";
1240
+ return formatDecoded(d.value);
1241
+ }
1242
+ case "Result": {
1243
+ if (d.value.success) {
1244
+ return `Ok(${formatDecoded(d.value.value)})`;
1245
+ }
1246
+ return `Err(${formatDecoded(d.value.value)})`;
1247
+ }
1248
+ case "Sequence":
1249
+ case "Array": {
1250
+ const items = d.value.map(formatDecoded);
1251
+ if (items.length === 0)
1252
+ return "[]";
1253
+ return `[${items.join(", ")}]`;
1254
+ }
1255
+ default:
1256
+ return String(d.value ?? "");
1257
+ }
1258
+ }
1138
1259
  function formatEventValue(v) {
1139
1260
  if (typeof v === "bigint")
1140
1261
  return v.toString();
@@ -1301,6 +1422,73 @@ function parsePrimitive(prim, arg) {
1301
1422
  return parseValue(arg);
1302
1423
  }
1303
1424
  }
1425
+ var PAPI_BUILTIN_EXTENSIONS = new Set([
1426
+ "CheckNonZeroSender",
1427
+ "CheckSpecVersion",
1428
+ "CheckTxVersion",
1429
+ "CheckGenesis",
1430
+ "CheckMortality",
1431
+ "CheckNonce",
1432
+ "CheckWeight",
1433
+ "ChargeTransactionPayment",
1434
+ "ChargeAssetTxPayment",
1435
+ "CheckMetadataHash",
1436
+ "StorageWeightReclaim",
1437
+ "PrevalidateAttests"
1438
+ ]);
1439
+ function parseExtOption(ext) {
1440
+ if (!ext)
1441
+ return {};
1442
+ try {
1443
+ const parsed = JSON.parse(ext);
1444
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1445
+ throw new Error(`--ext must be a JSON object, e.g. '{"ExtName":{"value":...}}'`);
1446
+ }
1447
+ return parsed;
1448
+ } catch (err) {
1449
+ if (err.message?.startsWith("--ext"))
1450
+ throw err;
1451
+ throw new Error(`Failed to parse --ext JSON: ${err.message}
1452
+ ` + `Expected format: '{"ExtName":{"value":...}}'`);
1453
+ }
1454
+ }
1455
+ var NO_DEFAULT = Symbol("no-default");
1456
+ function buildCustomSignedExtensions(meta, userOverrides) {
1457
+ const result = {};
1458
+ const extensions = getSignedExtensions(meta);
1459
+ for (const ext of extensions) {
1460
+ if (PAPI_BUILTIN_EXTENSIONS.has(ext.identifier))
1461
+ continue;
1462
+ if (ext.identifier in userOverrides) {
1463
+ result[ext.identifier] = userOverrides[ext.identifier];
1464
+ continue;
1465
+ }
1466
+ const valueEntry = meta.lookup(ext.type);
1467
+ const addEntry = meta.lookup(ext.additionalSigned);
1468
+ const value = autoDefaultForType(valueEntry);
1469
+ const add = autoDefaultForType(addEntry);
1470
+ if (value !== NO_DEFAULT || add !== NO_DEFAULT) {
1471
+ result[ext.identifier] = {
1472
+ ...value !== NO_DEFAULT ? { value } : {},
1473
+ ...add !== NO_DEFAULT ? { additionalSigned: add } : {}
1474
+ };
1475
+ }
1476
+ }
1477
+ return result;
1478
+ }
1479
+ function autoDefaultForType(entry) {
1480
+ if (entry.type === "void")
1481
+ return new Uint8Array([]);
1482
+ if (entry.type === "option")
1483
+ return;
1484
+ if (entry.type === "enum") {
1485
+ const variants = entry.value;
1486
+ if ("Disabled" in variants) {
1487
+ return { type: "Disabled", value: undefined };
1488
+ }
1489
+ }
1490
+ return NO_DEFAULT;
1491
+ }
1304
1492
 
1305
1493
  // src/utils/errors.ts
1306
1494
  class CliError2 extends Error {
@@ -1325,7 +1513,7 @@ registerConstCommand(cli);
1325
1513
  registerAccountCommands(cli);
1326
1514
  registerTxCommand(cli);
1327
1515
  cli.help();
1328
- cli.version("0.2.1");
1516
+ cli.version("0.3.0");
1329
1517
  function handleError(err) {
1330
1518
  if (err instanceof CliError2) {
1331
1519
  console.error(`Error: ${err.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkadot-cli",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "CLI tool for querying Polkadot-ecosystem on-chain state",
5
5
  "type": "module",
6
6
  "bin": {