polkadot-cli 0.6.2 → 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.
- package/README.md +26 -2
- package/dist/cli.mjs +452 -55
- 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
|
|
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
|
|
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
|
|
@@ -220,6 +222,27 @@ polkadot-cli aims to be the single tool for day-to-day chain interaction: storag
|
|
|
220
222
|
|
|
221
223
|
Outside Polkadot, the closest comparable in terms of interactive UX is [near-cli-rs](https://github.com/near/near-cli-rs) (NEAR).
|
|
222
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
|
+
|
|
223
246
|
## Configuration
|
|
224
247
|
|
|
225
248
|
Config and metadata caches live in `~/.polkadot/`:
|
|
@@ -228,6 +251,7 @@ Config and metadata caches live in `~/.polkadot/`:
|
|
|
228
251
|
~/.polkadot/
|
|
229
252
|
├── config.json # chains and default chain
|
|
230
253
|
├── accounts.json # stored accounts (⚠️ secrets are NOT encrypted — see below)
|
|
254
|
+
├── update-check.json # cached update check result
|
|
231
255
|
└── chains/
|
|
232
256
|
└── polkadot/
|
|
233
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.
|
|
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";
|
|
@@ -317,10 +317,9 @@ ${YELLOW}Note: Secrets are stored unencrypted in ~/.polkadot/accounts.json.
|
|
|
317
317
|
Hex seed import (0x...) is not supported via CLI.${RESET}
|
|
318
318
|
`.trimStart();
|
|
319
319
|
function registerAccountCommands(cli) {
|
|
320
|
-
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) => {
|
|
321
321
|
if (!action) {
|
|
322
|
-
|
|
323
|
-
return;
|
|
322
|
+
return accountList();
|
|
324
323
|
}
|
|
325
324
|
switch (action) {
|
|
326
325
|
case "create":
|
|
@@ -697,10 +696,9 @@ ${BOLD}Examples:${RESET}
|
|
|
697
696
|
$ dot chain remove kusama
|
|
698
697
|
`.trimStart();
|
|
699
698
|
function registerChainCommands(cli) {
|
|
700
|
-
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) => {
|
|
701
700
|
if (!action) {
|
|
702
|
-
|
|
703
|
-
return;
|
|
701
|
+
return chainList();
|
|
704
702
|
}
|
|
705
703
|
switch (action) {
|
|
706
704
|
case "add":
|
|
@@ -1146,6 +1144,254 @@ function parseValue(arg) {
|
|
|
1146
1144
|
return arg;
|
|
1147
1145
|
}
|
|
1148
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
|
+
|
|
1149
1395
|
// src/commands/query.ts
|
|
1150
1396
|
var DEFAULT_LIMIT = 100;
|
|
1151
1397
|
function registerQueryCommand(cli) {
|
|
@@ -1182,8 +1428,14 @@ function registerQueryCommand(cli) {
|
|
|
1182
1428
|
}
|
|
1183
1429
|
const unsafeApi = clientHandle.client.getUnsafeApi();
|
|
1184
1430
|
const storageApi = unsafeApi.query[palletInfo.name][storageItem.name];
|
|
1185
|
-
const parsedKeys = keys
|
|
1431
|
+
const parsedKeys = parseStorageKeys(meta, palletInfo.name, storageItem, keys);
|
|
1186
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
|
+
}
|
|
1187
1439
|
if (storageItem.type === "map" && parsedKeys.length === 0) {
|
|
1188
1440
|
const entries = await storageApi.getEntries();
|
|
1189
1441
|
const limit = Number(opts.limit);
|
|
@@ -1206,16 +1458,46 @@ ${DIM}Showing ${limit} of ${entries.length} entries. Use --limit 0 for all.${RES
|
|
|
1206
1458
|
}
|
|
1207
1459
|
});
|
|
1208
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
|
+
}
|
|
1209
1497
|
|
|
1210
1498
|
// src/commands/tx.ts
|
|
1211
|
-
import { getViewBuilder } from "@polkadot-api/view-builder";
|
|
1212
|
-
import { Binary } from "polkadot-api";
|
|
1213
|
-
|
|
1214
|
-
// src/core/explorers.ts
|
|
1215
|
-
var pjsAppsLink = (rpc, hash) => `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(rpc)}#/explorer/query/${hash}`;
|
|
1216
|
-
var papiLink = (rpc, hash) => `https://dev.papi.how/explorer/${hash}#networkId=custom&endpoint=${encodeURIComponent(rpc)}`;
|
|
1217
|
-
|
|
1218
|
-
// src/commands/tx.ts
|
|
1499
|
+
import { getViewBuilder as getViewBuilder2 } from "@polkadot-api/view-builder";
|
|
1500
|
+
import { Binary as Binary2 } from "polkadot-api";
|
|
1219
1501
|
function registerTxCommand(cli) {
|
|
1220
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) => {
|
|
1221
1503
|
if (!target) {
|
|
@@ -1272,7 +1554,7 @@ function registerTxCommand(cli) {
|
|
|
1272
1554
|
throw new Error(`Extra arguments are not allowed when submitting a raw call hex.
|
|
1273
1555
|
` + "Usage: dot tx 0x<call_hex> --from <account>");
|
|
1274
1556
|
}
|
|
1275
|
-
const callBinary =
|
|
1557
|
+
const callBinary = Binary2.fromHex(target);
|
|
1276
1558
|
tx = await unsafeApi.txFromCallData(callBinary);
|
|
1277
1559
|
callHex = target;
|
|
1278
1560
|
} else {
|
|
@@ -1292,7 +1574,7 @@ function registerTxCommand(cli) {
|
|
|
1292
1574
|
const { codec, location } = meta.builder.buildCall(palletInfo.name, callInfo.name);
|
|
1293
1575
|
const encodedArgs = codec.enc(callData);
|
|
1294
1576
|
const fullCall = new Uint8Array([location[0], location[1], ...encodedArgs]);
|
|
1295
|
-
console.log(
|
|
1577
|
+
console.log(Binary2.fromBytes(fullCall).asHex());
|
|
1296
1578
|
return;
|
|
1297
1579
|
}
|
|
1298
1580
|
tx = unsafeApi.tx[palletInfo.name][callInfo.name](callData);
|
|
@@ -1377,7 +1659,7 @@ function formatDispatchError(err) {
|
|
|
1377
1659
|
}
|
|
1378
1660
|
function decodeCall(meta, callHex) {
|
|
1379
1661
|
try {
|
|
1380
|
-
const viewBuilder =
|
|
1662
|
+
const viewBuilder = getViewBuilder2(meta.lookup);
|
|
1381
1663
|
const decoded = viewBuilder.callDecoder(callHex);
|
|
1382
1664
|
const palletName = decoded.pallet.value.name;
|
|
1383
1665
|
const callName = decoded.call.value.name;
|
|
@@ -1494,30 +1776,30 @@ function parseCallArgs(meta, palletName, callName, args) {
|
|
|
1494
1776
|
return;
|
|
1495
1777
|
}
|
|
1496
1778
|
if (variant.type === "struct") {
|
|
1497
|
-
return
|
|
1779
|
+
return parseStructArgs2(meta, variant.value, args, `${palletName}.${callName}`);
|
|
1498
1780
|
}
|
|
1499
1781
|
if (variant.type === "lookupEntry") {
|
|
1500
1782
|
const inner = variant.value;
|
|
1501
1783
|
if (inner.type === "struct") {
|
|
1502
|
-
return
|
|
1784
|
+
return parseStructArgs2(meta, inner.value, args, `${palletName}.${callName}`);
|
|
1503
1785
|
}
|
|
1504
1786
|
if (inner.type === "void")
|
|
1505
1787
|
return;
|
|
1506
1788
|
if (args.length !== 1) {
|
|
1507
1789
|
throw new Error(`${palletName}.${callName} takes 1 argument (${describeType(meta.lookup, inner.id)}), but ${args.length} provided.`);
|
|
1508
1790
|
}
|
|
1509
|
-
return
|
|
1791
|
+
return parseTypedArg2(meta, inner, args[0]);
|
|
1510
1792
|
}
|
|
1511
1793
|
if (variant.type === "tuple") {
|
|
1512
1794
|
const entries = variant.value;
|
|
1513
1795
|
if (args.length !== entries.length) {
|
|
1514
1796
|
throw new Error(`${palletName}.${callName} takes ${entries.length} arguments, but ${args.length} provided.`);
|
|
1515
1797
|
}
|
|
1516
|
-
return entries.map((entry, i) =>
|
|
1798
|
+
return entries.map((entry, i) => parseTypedArg2(meta, entry, args[i]));
|
|
1517
1799
|
}
|
|
1518
1800
|
return args.length === 0 ? undefined : args.map(parseValue);
|
|
1519
1801
|
}
|
|
1520
|
-
function
|
|
1802
|
+
function parseStructArgs2(meta, fields, args, callLabel) {
|
|
1521
1803
|
const fieldNames = Object.keys(fields);
|
|
1522
1804
|
if (args.length !== fieldNames.length) {
|
|
1523
1805
|
const expected = fieldNames.map((name) => `${name}: ${describeType(meta.lookup, fields[name].id)}`).join(", ");
|
|
@@ -1528,11 +1810,11 @@ function parseStructArgs(meta, fields, args, callLabel) {
|
|
|
1528
1810
|
for (let i = 0;i < fieldNames.length; i++) {
|
|
1529
1811
|
const name = fieldNames[i];
|
|
1530
1812
|
const entry = fields[name];
|
|
1531
|
-
result[name] =
|
|
1813
|
+
result[name] = parseTypedArg2(meta, entry, args[i]);
|
|
1532
1814
|
}
|
|
1533
1815
|
return result;
|
|
1534
1816
|
}
|
|
1535
|
-
function
|
|
1817
|
+
function normalizeValue2(lookup, entry, value) {
|
|
1536
1818
|
let resolved = entry;
|
|
1537
1819
|
while (resolved.type === "lookupEntry") {
|
|
1538
1820
|
resolved = resolved.value;
|
|
@@ -1552,7 +1834,7 @@ function normalizeValue(lookup, entry, value) {
|
|
|
1552
1834
|
normalizedInner = normalizedInner[0];
|
|
1553
1835
|
}
|
|
1554
1836
|
if (normalizedInner !== undefined && innerEntry.type !== "void") {
|
|
1555
|
-
normalizedInner =
|
|
1837
|
+
normalizedInner = normalizeValue2(lookup, innerEntry, normalizedInner);
|
|
1556
1838
|
}
|
|
1557
1839
|
return { type: enumValue.type, value: normalizedInner };
|
|
1558
1840
|
}
|
|
@@ -1565,7 +1847,7 @@ function normalizeValue(lookup, entry, value) {
|
|
|
1565
1847
|
const result = {};
|
|
1566
1848
|
for (const [key, val] of Object.entries(value)) {
|
|
1567
1849
|
if (key in fields) {
|
|
1568
|
-
result[key] =
|
|
1850
|
+
result[key] = normalizeValue2(lookup, fields[key], val);
|
|
1569
1851
|
} else {
|
|
1570
1852
|
result[key] = val;
|
|
1571
1853
|
}
|
|
@@ -1582,25 +1864,25 @@ function normalizeValue(lookup, entry, value) {
|
|
|
1582
1864
|
}
|
|
1583
1865
|
if (innerResolved.type === "primitive" && innerResolved.value === "u8" && typeof value === "string") {
|
|
1584
1866
|
if (/^0x[0-9a-fA-F]*$/.test(value))
|
|
1585
|
-
return
|
|
1586
|
-
return
|
|
1867
|
+
return Binary2.fromHex(value);
|
|
1868
|
+
return Binary2.fromText(value);
|
|
1587
1869
|
}
|
|
1588
1870
|
if (Array.isArray(value)) {
|
|
1589
1871
|
const innerEntry = resolved.value;
|
|
1590
|
-
return value.map((item) =>
|
|
1872
|
+
return value.map((item) => normalizeValue2(lookup, innerEntry, item));
|
|
1591
1873
|
}
|
|
1592
1874
|
return value;
|
|
1593
1875
|
}
|
|
1594
1876
|
case "tuple": {
|
|
1595
1877
|
if (Array.isArray(value)) {
|
|
1596
1878
|
const entries = resolved.value;
|
|
1597
|
-
return value.map((item, i) => i < entries.length ?
|
|
1879
|
+
return value.map((item, i) => i < entries.length ? normalizeValue2(lookup, entries[i], item) : item);
|
|
1598
1880
|
}
|
|
1599
1881
|
return value;
|
|
1600
1882
|
}
|
|
1601
1883
|
case "option": {
|
|
1602
1884
|
if (value !== null && value !== undefined) {
|
|
1603
|
-
return
|
|
1885
|
+
return normalizeValue2(lookup, resolved.value, value);
|
|
1604
1886
|
}
|
|
1605
1887
|
return;
|
|
1606
1888
|
}
|
|
@@ -1638,10 +1920,10 @@ function normalizeValue(lookup, entry, value) {
|
|
|
1638
1920
|
return value;
|
|
1639
1921
|
}
|
|
1640
1922
|
}
|
|
1641
|
-
function
|
|
1923
|
+
function parseTypedArg2(meta, entry, arg) {
|
|
1642
1924
|
switch (entry.type) {
|
|
1643
1925
|
case "primitive":
|
|
1644
|
-
return
|
|
1926
|
+
return parsePrimitive2(entry.value, arg);
|
|
1645
1927
|
case "compact":
|
|
1646
1928
|
return entry.isBig ? BigInt(arg) : parseInt(arg, 10);
|
|
1647
1929
|
case "AccountId32":
|
|
@@ -1651,16 +1933,16 @@ function parseTypedArg(meta, entry, arg) {
|
|
|
1651
1933
|
if (arg === "null" || arg === "undefined" || arg === "none") {
|
|
1652
1934
|
return;
|
|
1653
1935
|
}
|
|
1654
|
-
return
|
|
1936
|
+
return parseTypedArg2(meta, entry.value, arg);
|
|
1655
1937
|
}
|
|
1656
1938
|
case "enum": {
|
|
1657
1939
|
if (/^0x[0-9a-fA-F]+$/.test(arg) && meta.lookup.call != null && entry.id === meta.lookup.call) {
|
|
1658
1940
|
const callCodec = meta.builder.buildDefinition(meta.lookup.call);
|
|
1659
|
-
return callCodec.dec(
|
|
1941
|
+
return callCodec.dec(Binary2.fromHex(arg).asBytes());
|
|
1660
1942
|
}
|
|
1661
1943
|
if (arg.startsWith("{")) {
|
|
1662
1944
|
try {
|
|
1663
|
-
return
|
|
1945
|
+
return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
|
|
1664
1946
|
} catch {}
|
|
1665
1947
|
}
|
|
1666
1948
|
const variants = Object.keys(entry.value);
|
|
@@ -1685,29 +1967,29 @@ function parseTypedArg(meta, entry, arg) {
|
|
|
1685
1967
|
const inner = entry.value;
|
|
1686
1968
|
if (inner.type === "primitive" && inner.value === "u8") {
|
|
1687
1969
|
if (/^0x[0-9a-fA-F]*$/.test(arg))
|
|
1688
|
-
return
|
|
1689
|
-
return
|
|
1970
|
+
return Binary2.fromHex(arg);
|
|
1971
|
+
return Binary2.fromText(arg);
|
|
1690
1972
|
}
|
|
1691
1973
|
if (arg.startsWith("[")) {
|
|
1692
1974
|
try {
|
|
1693
|
-
return
|
|
1975
|
+
return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
|
|
1694
1976
|
} catch {}
|
|
1695
1977
|
}
|
|
1696
1978
|
if (/^0x[0-9a-fA-F]*$/.test(arg))
|
|
1697
|
-
return
|
|
1979
|
+
return Binary2.fromHex(arg);
|
|
1698
1980
|
return parseValue(arg);
|
|
1699
1981
|
}
|
|
1700
1982
|
case "struct":
|
|
1701
1983
|
if (arg.startsWith("{")) {
|
|
1702
1984
|
try {
|
|
1703
|
-
return
|
|
1985
|
+
return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
|
|
1704
1986
|
} catch {}
|
|
1705
1987
|
}
|
|
1706
1988
|
return parseValue(arg);
|
|
1707
1989
|
case "tuple":
|
|
1708
1990
|
if (arg.startsWith("[")) {
|
|
1709
1991
|
try {
|
|
1710
|
-
return
|
|
1992
|
+
return normalizeValue2(meta.lookup, entry, JSON.parse(arg));
|
|
1711
1993
|
} catch {}
|
|
1712
1994
|
}
|
|
1713
1995
|
return parseValue(arg);
|
|
@@ -1715,7 +1997,7 @@ function parseTypedArg(meta, entry, arg) {
|
|
|
1715
1997
|
return parseValue(arg);
|
|
1716
1998
|
}
|
|
1717
1999
|
}
|
|
1718
|
-
function
|
|
2000
|
+
function parsePrimitive2(prim, arg) {
|
|
1719
2001
|
switch (prim) {
|
|
1720
2002
|
case "bool":
|
|
1721
2003
|
return arg === "true";
|
|
@@ -1740,7 +2022,7 @@ function parsePrimitive(prim, arg) {
|
|
|
1740
2022
|
return parseValue(arg);
|
|
1741
2023
|
}
|
|
1742
2024
|
}
|
|
1743
|
-
var
|
|
2025
|
+
var PAPI_BUILTIN_EXTENSIONS2 = new Set([
|
|
1744
2026
|
"CheckNonZeroSender",
|
|
1745
2027
|
"CheckSpecVersion",
|
|
1746
2028
|
"CheckTxVersion",
|
|
@@ -1770,12 +2052,12 @@ function parseExtOption(ext) {
|
|
|
1770
2052
|
` + `Expected format: '{"ExtName":{"value":...}}'`);
|
|
1771
2053
|
}
|
|
1772
2054
|
}
|
|
1773
|
-
var
|
|
2055
|
+
var NO_DEFAULT2 = Symbol("no-default");
|
|
1774
2056
|
function buildCustomSignedExtensions(meta, userOverrides) {
|
|
1775
2057
|
const result = {};
|
|
1776
2058
|
const extensions = getSignedExtensions(meta);
|
|
1777
2059
|
for (const ext of extensions) {
|
|
1778
|
-
if (
|
|
2060
|
+
if (PAPI_BUILTIN_EXTENSIONS2.has(ext.identifier))
|
|
1779
2061
|
continue;
|
|
1780
2062
|
if (ext.identifier in userOverrides) {
|
|
1781
2063
|
result[ext.identifier] = userOverrides[ext.identifier];
|
|
@@ -1785,10 +2067,10 @@ function buildCustomSignedExtensions(meta, userOverrides) {
|
|
|
1785
2067
|
const addEntry = meta.lookup(ext.additionalSigned);
|
|
1786
2068
|
const value = autoDefaultForType(valueEntry);
|
|
1787
2069
|
const add = autoDefaultForType(addEntry);
|
|
1788
|
-
if (value !==
|
|
2070
|
+
if (value !== NO_DEFAULT2 || add !== NO_DEFAULT2) {
|
|
1789
2071
|
result[ext.identifier] = {
|
|
1790
|
-
...value !==
|
|
1791
|
-
...add !==
|
|
2072
|
+
...value !== NO_DEFAULT2 ? { value } : {},
|
|
2073
|
+
...add !== NO_DEFAULT2 ? { additionalSigned: add } : {}
|
|
1792
2074
|
};
|
|
1793
2075
|
}
|
|
1794
2076
|
}
|
|
@@ -1805,7 +2087,7 @@ function autoDefaultForType(entry) {
|
|
|
1805
2087
|
return { type: "Disabled", value: undefined };
|
|
1806
2088
|
}
|
|
1807
2089
|
}
|
|
1808
|
-
return
|
|
2090
|
+
return NO_DEFAULT2;
|
|
1809
2091
|
}
|
|
1810
2092
|
function watchTransaction(observable) {
|
|
1811
2093
|
const spinner = new Spinner;
|
|
@@ -1845,6 +2127,110 @@ function watchTransaction(observable) {
|
|
|
1845
2127
|
});
|
|
1846
2128
|
}
|
|
1847
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
|
+
|
|
1848
2234
|
// src/utils/errors.ts
|
|
1849
2235
|
class CliError2 extends Error {
|
|
1850
2236
|
constructor(message) {
|
|
@@ -1854,6 +2240,7 @@ class CliError2 extends Error {
|
|
|
1854
2240
|
}
|
|
1855
2241
|
|
|
1856
2242
|
// src/cli.ts
|
|
2243
|
+
startBackgroundCheck(version);
|
|
1857
2244
|
var cli = cac("dot");
|
|
1858
2245
|
cli.option("--chain <name>", "Target chain (default from config)");
|
|
1859
2246
|
cli.option("--rpc <url>", "Override RPC endpoint for this call");
|
|
@@ -1870,6 +2257,13 @@ registerTxCommand(cli);
|
|
|
1870
2257
|
registerHashCommand(cli);
|
|
1871
2258
|
cli.help();
|
|
1872
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
|
+
}
|
|
1873
2267
|
function handleError(err) {
|
|
1874
2268
|
if (err instanceof CliError2) {
|
|
1875
2269
|
console.error(`Error: ${err.message}`);
|
|
@@ -1878,16 +2272,19 @@ function handleError(err) {
|
|
|
1878
2272
|
} else {
|
|
1879
2273
|
console.error("An unexpected error occurred:", err);
|
|
1880
2274
|
}
|
|
1881
|
-
|
|
2275
|
+
showUpdateAndExit(1);
|
|
1882
2276
|
}
|
|
1883
2277
|
try {
|
|
1884
2278
|
cli.parse(process.argv, { run: false });
|
|
1885
|
-
if (
|
|
2279
|
+
if (cli.options.version || cli.options.help) {
|
|
2280
|
+
showUpdateAndExit(0);
|
|
2281
|
+
} else if (!cli.matchedCommandName) {
|
|
1886
2282
|
cli.outputHelp();
|
|
2283
|
+
showUpdateAndExit(0);
|
|
1887
2284
|
} else {
|
|
1888
2285
|
const result = cli.runMatchedCommand();
|
|
1889
2286
|
if (result && typeof result.then === "function") {
|
|
1890
|
-
result.then(() =>
|
|
2287
|
+
result.then(() => showUpdateAndExit(0), handleError);
|
|
1891
2288
|
}
|
|
1892
2289
|
}
|
|
1893
2290
|
} catch (err) {
|