@everyprotocol/every-cli 0.1.3 → 0.1.4
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/.every.toml +3 -3
- package/dist/abi.js +1 -0
- package/dist/cmdgen.js +51 -139
- package/dist/cmds/balance.js +43 -0
- package/dist/cmds/config.js +46 -0
- package/dist/cmds/kind.js +14 -0
- package/dist/cmds/matter.js +82 -0
- package/dist/cmds/minter.js +31 -0
- package/dist/cmds/object.js +175 -0
- package/dist/cmds/relation.js +16 -0
- package/dist/cmds/set.js +31 -0
- package/dist/cmds/unique.js +14 -0
- package/dist/cmds/universe.js +21 -0
- package/dist/cmds/value.js +14 -0
- package/dist/{wallet.js → cmds/wallet.js} +19 -19
- package/dist/commander-patch.js +64 -0
- package/dist/config.js +18 -93
- package/dist/ethereum.js +33 -0
- package/dist/from-opts.js +161 -0
- package/dist/index.js +2 -1
- package/dist/logger.js +39 -0
- package/dist/parsers.js +59 -0
- package/dist/program.js +28 -39
- package/dist/substrate.js +24 -18
- package/dist/utils.js +144 -182
- package/package.json +4 -1
- package/dist/cmds.js +0 -132
- package/dist/matter.js +0 -96
- package/dist/mint.js +0 -78
- package/dist/options.js +0 -26
- package/dist/relate.js +0 -104
package/dist/cmds/set.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Argument, Command } from "commander";
|
|
2
|
+
import { abi } from "../abi.js";
|
|
3
|
+
import { CommandGenDefaults, getCommandGen, makeFuncName } from "../cmdgen.js";
|
|
4
|
+
const adminCmdConfig = {
|
|
5
|
+
getFuncName: (cmdName) => `${cmdName}Set`,
|
|
6
|
+
getAbiFuncs: (funcName) => abi.funcs.setRegistryAdmin.filter((i) => i.name == funcName),
|
|
7
|
+
// eslint-disable-next-line
|
|
8
|
+
getAbiNonFuncs: (funcName) => abi.nonFuncs.setRegistry,
|
|
9
|
+
// eslint-disable-next-line
|
|
10
|
+
getContract: (conf, args, abiFunc) => args[0],
|
|
11
|
+
// eslint-disable-next-line
|
|
12
|
+
getFuncArgs: (args, abiFunc) => args.slice(1),
|
|
13
|
+
getCmdArgs: (abiFunc) => [
|
|
14
|
+
new Argument(`<contract>`, "address of the set contract"),
|
|
15
|
+
...CommandGenDefaults.getCmdArgs(abiFunc),
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
const userCmdConfig = {
|
|
19
|
+
getFuncName: (cmdName) => makeFuncName(cmdName, `set`),
|
|
20
|
+
getAbiFuncs: (funcName) => abi.funcs.setRegistry.filter((i) => i.name == funcName),
|
|
21
|
+
// eslint-disable-next-line
|
|
22
|
+
getAbiNonFuncs: (funcName) => abi.nonFuncs.setRegistry,
|
|
23
|
+
// eslint-disable-next-line
|
|
24
|
+
getContract: (conf, args, abiFunc) => conf.contracts.SetRegistry,
|
|
25
|
+
};
|
|
26
|
+
const userCmds = "owner,descriptor,snapshot".split(",");
|
|
27
|
+
const adminCmds = "register,update,upgrade,touch".split(",");
|
|
28
|
+
export const setCmd = new Command("set")
|
|
29
|
+
.description("manage sets")
|
|
30
|
+
.addCommands(adminCmds.map(getCommandGen(adminCmdConfig)))
|
|
31
|
+
.addCommands(userCmds.map(getCommandGen(userCmdConfig)));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { abi } from "../abi.js";
|
|
3
|
+
import { getCommandGen, makeFuncName } from "../cmdgen.js";
|
|
4
|
+
const cmdGenConfig = {
|
|
5
|
+
getFuncName: (cmdName) => makeFuncName(cmdName, `unique`),
|
|
6
|
+
getAbiFuncs: (funcName) => abi.funcs.elemRegistry.filter((i) => i.name == funcName),
|
|
7
|
+
// eslint-disable-next-line
|
|
8
|
+
getAbiNonFuncs: (funcName) => abi.nonFuncs.elemRegistry,
|
|
9
|
+
// eslint-disable-next-line
|
|
10
|
+
getContract: (conf, args, abiFunc) => conf.contracts.ElementRegistry,
|
|
11
|
+
};
|
|
12
|
+
const cmdGen = getCommandGen(cmdGenConfig);
|
|
13
|
+
const subCmds = "register,upgrade,touch,transfer,owner,descriptor,snapshot".split(",");
|
|
14
|
+
export const uniqueCmd = new Command("unique").description("manage uniques").addCommands(subCmds.map(cmdGen));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import "@polkadot/api-augment/substrate";
|
|
3
|
+
import { submitSubTxUI } from "../substrate.js";
|
|
4
|
+
import { network } from "../commander-patch.js";
|
|
5
|
+
import { parseAccountId, parseBigInt } from "../parsers.js";
|
|
6
|
+
import { Logger } from "../logger.js";
|
|
7
|
+
const universeStartCmd = new Command("start")
|
|
8
|
+
.description("Start a universe")
|
|
9
|
+
.argument("<universe>", "Universe ID", parseBigInt)
|
|
10
|
+
.argument("<horizon>", "Block number", parseBigInt)
|
|
11
|
+
.argument("<herald>", "Herald address (SS58 or 0x hex)", parseAccountId)
|
|
12
|
+
.addOption(network)
|
|
13
|
+
.addKeystoreOptions()
|
|
14
|
+
.addOutputOptions()
|
|
15
|
+
.subWriteAction(async function (api, pair, universe, horizon, herald) {
|
|
16
|
+
const call = api.tx.every.canonicalStart(universe, horizon, herald);
|
|
17
|
+
const tx = api.tx.sudo.sudo(call);
|
|
18
|
+
const console = new Logger(this.opts());
|
|
19
|
+
await submitSubTxUI(api, tx, pair, console);
|
|
20
|
+
});
|
|
21
|
+
export const universeCmd = new Command("universe").description("manage universes").addCommand(universeStartCmd);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { abi } from "../abi.js";
|
|
3
|
+
import { getCommandGen, makeFuncName } from "../cmdgen.js";
|
|
4
|
+
const cmdGenConfig = {
|
|
5
|
+
getFuncName: (cmdName) => makeFuncName(cmdName, `value`),
|
|
6
|
+
getAbiFuncs: (funcName) => abi.funcs.elemRegistry.filter((i) => i.name == funcName),
|
|
7
|
+
// eslint-disable-next-line
|
|
8
|
+
getAbiNonFuncs: (funcName) => abi.nonFuncs.elemRegistry,
|
|
9
|
+
// eslint-disable-next-line
|
|
10
|
+
getContract: (conf, args, abiFunc) => conf.contracts.ElementRegistry,
|
|
11
|
+
};
|
|
12
|
+
const cmdGen = getCommandGen(cmdGenConfig);
|
|
13
|
+
const subCmds = "register,upgrade,touch,transfer,owner,descriptor,snapshot".split(",");
|
|
14
|
+
export const valueCmd = new Command("value").description("manage values").addCommands(subCmds.map(cmdGen));
|
|
@@ -3,18 +3,19 @@ import { Keyring } from "@polkadot/keyring";
|
|
|
3
3
|
import { mnemonicGenerate, cryptoWaitReady } from "@polkadot/util-crypto";
|
|
4
4
|
import { bytesToHex } from "viem";
|
|
5
5
|
import * as fs from "fs";
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import { FromOpts } from "../from-opts.js";
|
|
7
|
+
import { saveJson } from "../utils.js";
|
|
8
|
+
const walletListCmd = new Command()
|
|
8
9
|
.name("list")
|
|
9
10
|
.description("List all wallets")
|
|
10
11
|
.option("-f, --foundry", "use foundry keystore directory (~/.foundry/keystores)")
|
|
11
12
|
.option("--dir <dir>", "specify a custom keystore directory")
|
|
12
13
|
.action(async (options) => {
|
|
13
|
-
const dir =
|
|
14
|
+
const dir = FromOpts.getKeystoreDir(options);
|
|
14
15
|
const files = fs.readdirSync(dir);
|
|
15
16
|
files.forEach((file) => console.log(file));
|
|
16
17
|
});
|
|
17
|
-
const
|
|
18
|
+
const walletNewCmd = new Command()
|
|
18
19
|
.name("new")
|
|
19
20
|
.description("Generate a new wallet")
|
|
20
21
|
.option("-t, --type <type>", "key type (sr25519, ed25519, ethereum)", "sr25519")
|
|
@@ -23,15 +24,16 @@ const generateCmd = new Command()
|
|
|
23
24
|
.option("--dir <dir>", "specify keystore directory")
|
|
24
25
|
.argument("<name>", "name of the wallet")
|
|
25
26
|
.action(async (name, options) => {
|
|
26
|
-
const password =
|
|
27
|
+
const password = FromOpts.confirmPassword(options);
|
|
27
28
|
await cryptoWaitReady();
|
|
28
29
|
const keyring = new Keyring();
|
|
29
30
|
const mnemonic = mnemonicGenerate();
|
|
30
31
|
const pair = keyring.addFromUri(mnemonic, { name }, options.type);
|
|
31
32
|
const json = pair.toJson(password);
|
|
32
|
-
|
|
33
|
+
const dir = FromOpts.getKeystoreDir(options);
|
|
34
|
+
saveJson(json, dir, name);
|
|
33
35
|
});
|
|
34
|
-
const
|
|
36
|
+
const walletImportCmd = new Command()
|
|
35
37
|
.name("import")
|
|
36
38
|
.description("Import a wallet from a secrete URI")
|
|
37
39
|
.option("-t, --type <type>", "key type (sr25519, ed25519, ethereum)", "sr25519")
|
|
@@ -42,14 +44,15 @@ const importCmd = new Command()
|
|
|
42
44
|
.argument("<name>", "name of the wallet")
|
|
43
45
|
.argument("<suri>", "secret URI")
|
|
44
46
|
.action(async (name, suri, options) => {
|
|
45
|
-
const password =
|
|
47
|
+
const password = FromOpts.confirmPassword(options);
|
|
46
48
|
await cryptoWaitReady();
|
|
47
49
|
const keyring = new Keyring({ type: options.type });
|
|
48
50
|
const pair = keyring.addFromUri(suri);
|
|
49
51
|
const json = pair.toJson(password);
|
|
50
|
-
|
|
52
|
+
const dir = FromOpts.getKeystoreDir(options);
|
|
53
|
+
saveJson(json, dir, name);
|
|
51
54
|
});
|
|
52
|
-
const
|
|
55
|
+
const walletInspectCmd = new Command()
|
|
53
56
|
.name("inspect")
|
|
54
57
|
.description("Inspect a wallet")
|
|
55
58
|
.option("-t, --type <type>", "key type (sr25519, ed25519, ethereum)", "sr25519")
|
|
@@ -60,11 +63,8 @@ const inspectCmd = new Command()
|
|
|
60
63
|
.option("-f, --foundry", "use foundry keystore directory (~/.foundry/keystores)")
|
|
61
64
|
.argument("<name>", "name of the wallet")
|
|
62
65
|
.action(async (name, options) => {
|
|
63
|
-
const keystore = await
|
|
64
|
-
|
|
65
|
-
if (options.decrypt) {
|
|
66
|
-
decoded = await keystore.privateKey();
|
|
67
|
-
}
|
|
66
|
+
const keystore = await FromOpts.getKeystore(options, name);
|
|
67
|
+
const decoded = options.decrypt ? await keystore.privateKey() : undefined;
|
|
68
68
|
let dir = "~/.every/keystores";
|
|
69
69
|
if (options.foundry) {
|
|
70
70
|
dir = "~/.foundry/keystores";
|
|
@@ -84,7 +84,7 @@ const inspectCmd = new Command()
|
|
|
84
84
|
export const walletCmd = new Command()
|
|
85
85
|
.name("wallet")
|
|
86
86
|
.description("manage wallets")
|
|
87
|
-
.addCommand(
|
|
88
|
-
.addCommand(
|
|
89
|
-
.addCommand(
|
|
90
|
-
.addCommand(
|
|
87
|
+
.addCommand(walletListCmd)
|
|
88
|
+
.addCommand(walletNewCmd)
|
|
89
|
+
.addCommand(walletImportCmd)
|
|
90
|
+
.addCommand(walletInspectCmd);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Command, Option } from "commander";
|
|
2
|
+
import { FromOpts } from "./from-opts.js";
|
|
3
|
+
export const account = new Option("-a, --account <account>", "Name of the keystore");
|
|
4
|
+
export const password = new Option("-p, --password [password]", "Password to decrypt the keystore");
|
|
5
|
+
export const passwordFile = new Option("-P, --password-file <file>", "File containing the keystore password");
|
|
6
|
+
export const privateKey = new Option("-k, --private-key <key>", "Private key to sign the transaction");
|
|
7
|
+
export const foundry = new Option("-f, --foundry", "Use foundry keystores (~/.foundry/keystores)");
|
|
8
|
+
export const universe = new Option("-u, --universe <universe>", "Universe name").default("anvil");
|
|
9
|
+
export const network = new Option("-n, --network <network>", "Network name").default("dev");
|
|
10
|
+
export const json = new Option("-j, --json [file]", "Output result as JSON to stdout or file, implies --quiet");
|
|
11
|
+
export const quiet = new Option("-q, --quiet", "Suppress info messages");
|
|
12
|
+
export const noQuiet = new Option("--no-quiet", "Force info messages even when --json is set");
|
|
13
|
+
export const keystoreOptions = [account, password, passwordFile, foundry];
|
|
14
|
+
export const outputOptions = [json, quiet, noQuiet];
|
|
15
|
+
export const writeOptions = [universe, account, password, passwordFile, foundry];
|
|
16
|
+
Command.prototype.addCommands = function (commands) {
|
|
17
|
+
commands.forEach((cmd) => this.addCommand(cmd));
|
|
18
|
+
return this;
|
|
19
|
+
};
|
|
20
|
+
Command.prototype.addArguments = function (arg) {
|
|
21
|
+
arg.forEach((arg) => this.addArgument(arg));
|
|
22
|
+
return this;
|
|
23
|
+
};
|
|
24
|
+
Command.prototype.addOptions = function (options) {
|
|
25
|
+
options.forEach((opt) => this.addOption(opt));
|
|
26
|
+
return this;
|
|
27
|
+
};
|
|
28
|
+
Command.prototype.addKeystoreOptions = function () {
|
|
29
|
+
return this.addOptions([account, password, passwordFile, foundry]);
|
|
30
|
+
};
|
|
31
|
+
Command.prototype.addOutputOptions = function () {
|
|
32
|
+
return this.addOptions([json, quiet, noQuiet]);
|
|
33
|
+
};
|
|
34
|
+
Command.prototype.addWriteOptions = function () {
|
|
35
|
+
return this.addKeystoreOptions().addOption(universe);
|
|
36
|
+
};
|
|
37
|
+
Command.prototype.subReadAction = function (fn) {
|
|
38
|
+
// eslint-disable-next-line
|
|
39
|
+
return this.action(async function (...args) {
|
|
40
|
+
const api = await FromOpts.getSubstrateApi(this.opts());
|
|
41
|
+
try {
|
|
42
|
+
return await fn.call(this, api, ...args);
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await api.disconnect().catch(() => { });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
Command.prototype.subWriteAction = function (fn) {
|
|
50
|
+
// eslint-disable-next-line
|
|
51
|
+
async function _action(...args) {
|
|
52
|
+
const opts = this.opts();
|
|
53
|
+
const api = await FromOpts.getSubstrateApi(opts);
|
|
54
|
+
try {
|
|
55
|
+
const keystore = await FromOpts.getKeystore(opts);
|
|
56
|
+
const pair = await keystore.pair();
|
|
57
|
+
return await fn.call(this, api, pair, ...args);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
await api.disconnect().catch(() => { });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return this.action(_action);
|
|
64
|
+
};
|
package/dist/config.js
CHANGED
|
@@ -1,34 +1,9 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import os from "os";
|
|
4
|
-
import { parse as parseTOML } from "@iarna/toml";
|
|
5
4
|
import * as TOML from "@iarna/toml";
|
|
6
|
-
import { __dirname } from "./utils.js";
|
|
7
|
-
// export interface Universe {
|
|
8
|
-
// name: string;
|
|
9
|
-
// id: number;
|
|
10
|
-
// rpc: string;
|
|
11
|
-
// explorer: string;
|
|
12
|
-
// observer: string;
|
|
13
|
-
// contracts: {
|
|
14
|
-
// setRegistry: string;
|
|
15
|
-
// omniRegistry: string;
|
|
16
|
-
// kindRegistry: string;
|
|
17
|
-
// elementRegistry: string;
|
|
18
|
-
// objectMinter: string;
|
|
19
|
-
// };
|
|
20
|
-
// }
|
|
21
|
-
// export interface Observer {
|
|
22
|
-
// name: string;
|
|
23
|
-
// rpc: string;
|
|
24
|
-
// explorer: string;
|
|
25
|
-
// gateway: string;
|
|
26
|
-
// }
|
|
27
|
-
// export interface Config {
|
|
28
|
-
// universes: Record<string, Universe>;
|
|
29
|
-
// observers: Record<string, Observer>;
|
|
30
|
-
// }
|
|
31
5
|
import { z } from "zod";
|
|
6
|
+
import { __dirname } from "./utils.js";
|
|
32
7
|
export const ContractsSchema = z.object({
|
|
33
8
|
SetRegistry: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "Must be an Ethereum address"),
|
|
34
9
|
OmniRegistry: z.string().regex(/^0x[0-9a-fA-F]{40}$/),
|
|
@@ -57,18 +32,24 @@ export function loadConfig(file) {
|
|
|
57
32
|
const raw = TOML.parse(text);
|
|
58
33
|
return ConfigSchema.parse(raw);
|
|
59
34
|
}
|
|
60
|
-
const configPaths = [
|
|
61
|
-
path.resolve(__dirname, "../.every.toml"),
|
|
62
|
-
path.resolve(os.homedir(), ".every.toml"),
|
|
63
|
-
path.resolve(process.cwd(), ".every.toml"),
|
|
64
|
-
];
|
|
65
35
|
export function loadMergedConfig() {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
36
|
+
const [config] = _loadMergedConfig();
|
|
37
|
+
return config;
|
|
38
|
+
}
|
|
39
|
+
export function _loadMergedConfig() {
|
|
40
|
+
const search = [
|
|
41
|
+
...new Set([
|
|
42
|
+
path.resolve(__dirname, "../.every.toml"),
|
|
43
|
+
path.resolve(os.homedir(), ".every.toml"),
|
|
44
|
+
path.resolve(process.cwd(), ".every.toml"),
|
|
45
|
+
]),
|
|
46
|
+
];
|
|
47
|
+
const files = search.filter((f) => fs.existsSync(f));
|
|
48
|
+
if (files.length === 0) {
|
|
49
|
+
throw new Error(`No config file found. Searched in:\n ${search.join("\n ")}`);
|
|
70
50
|
}
|
|
71
|
-
|
|
51
|
+
const merged = { universes: {}, observers: {} };
|
|
52
|
+
for (const file of files) {
|
|
72
53
|
const raw = loadConfig(file);
|
|
73
54
|
if (raw.universes) {
|
|
74
55
|
for (const [name, uni] of Object.entries(raw.universes)) {
|
|
@@ -87,61 +68,5 @@ export function loadMergedConfig() {
|
|
|
87
68
|
}
|
|
88
69
|
}
|
|
89
70
|
}
|
|
90
|
-
return ConfigSchema.parse(merged);
|
|
91
|
-
}
|
|
92
|
-
// Cache
|
|
93
|
-
let CONFIG_CACHED = null;
|
|
94
|
-
export function getUniverseConfig(opts) {
|
|
95
|
-
const config = loadProtocolConfig();
|
|
96
|
-
const universeName = opts.universe || "local";
|
|
97
|
-
const universe = config.universes[universeName];
|
|
98
|
-
if (!universe) {
|
|
99
|
-
const available = Object.keys(config.universes).join(", ");
|
|
100
|
-
throw new Error(`Universe "${universeName}" not found. Available: ${available}`);
|
|
101
|
-
}
|
|
102
|
-
return universe;
|
|
103
|
-
}
|
|
104
|
-
function loadProtocolConfig() {
|
|
105
|
-
if (CONFIG_CACHED)
|
|
106
|
-
return CONFIG_CACHED;
|
|
107
|
-
const configPaths = new Set([
|
|
108
|
-
path.resolve(__dirname, "../.every.toml"),
|
|
109
|
-
path.resolve(os.homedir(), ".every.toml"),
|
|
110
|
-
path.resolve(process.cwd(), ".every.toml"),
|
|
111
|
-
]);
|
|
112
|
-
const mergedConfig = {
|
|
113
|
-
universes: {},
|
|
114
|
-
};
|
|
115
|
-
for (const configPath of configPaths) {
|
|
116
|
-
if (!fs.existsSync(configPath))
|
|
117
|
-
continue;
|
|
118
|
-
try {
|
|
119
|
-
const raw = fs.readFileSync(configPath, "utf8");
|
|
120
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
121
|
-
const parsed = parseTOML(raw);
|
|
122
|
-
if (parsed.universes) {
|
|
123
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
124
|
-
for (const [name, uni] of Object.entries(parsed.universes)) {
|
|
125
|
-
// console.log(name, uni);
|
|
126
|
-
mergedConfig.universes[name] = {
|
|
127
|
-
name: name,
|
|
128
|
-
id: uni.id,
|
|
129
|
-
rpc: uni.rpc,
|
|
130
|
-
explorer: uni.explorer,
|
|
131
|
-
observer: uni.observer,
|
|
132
|
-
contracts: uni.contracts || {},
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
console.log(`Loaded configuration from ${configPath}`);
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
console.warn(`Failed to load ${configPath}:`, err);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (Object.keys(mergedConfig.universes).length === 0) {
|
|
143
|
-
console.warn("No universe configurations found");
|
|
144
|
-
}
|
|
145
|
-
CONFIG_CACHED = mergedConfig;
|
|
146
|
-
return CONFIG_CACHED;
|
|
71
|
+
return [ConfigSchema.parse(merged), files];
|
|
147
72
|
}
|
package/dist/ethereum.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { parseEventLogs } from "viem";
|
|
2
|
+
import columnify from "columnify";
|
|
3
|
+
import { stringify as j11 } from "json11";
|
|
4
|
+
export async function submitSimulation(simulation, publicClient, walletClient, console) {
|
|
5
|
+
console.log("Transaction sending...");
|
|
6
|
+
const { request } = await publicClient.simulateContract(simulation);
|
|
7
|
+
const hash = await walletClient.writeContract(request);
|
|
8
|
+
console.log(`Transaction sent: ${hash}`);
|
|
9
|
+
console.log("Waiting for confirmation...");
|
|
10
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
11
|
+
console.log(`Confirmed in: block ${receipt.blockNumber}, hash ${receipt.blockHash}`);
|
|
12
|
+
const output = [];
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
const events = [];
|
|
15
|
+
if (receipt.logs && receipt.logs.length > 0) {
|
|
16
|
+
const parsedLogs = parseEventLogs({ abi: simulation.abi, logs: receipt.logs });
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
parsedLogs.forEach((log, i) => {
|
|
19
|
+
output.push([i, log.eventName, j11(log.args)]);
|
|
20
|
+
events.push({ name: log.eventName, data: log.args });
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
if (output.length > 0) {
|
|
24
|
+
console.log("Events emitted");
|
|
25
|
+
const termWidth = process.stdout.columns || 80;
|
|
26
|
+
const config = {
|
|
27
|
+
2: { maxWidth: Math.max(60, termWidth - 20) },
|
|
28
|
+
};
|
|
29
|
+
console.log(columnify(output, { showHeaders: false, truncateMarker: "", config }));
|
|
30
|
+
}
|
|
31
|
+
const result = { transaction: hash, block: { number: receipt.blockNumber, hash: receipt.blockHash }, events };
|
|
32
|
+
console.result(result);
|
|
33
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { bytesToHex, createPublicClient, createWalletClient, http } from "viem";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { Wallet } from "ethers";
|
|
5
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
6
|
+
import promptSync from "prompt-sync";
|
|
7
|
+
import os from "os";
|
|
8
|
+
import { loadMergedConfig } from "./config.js";
|
|
9
|
+
import Keyring from "@polkadot/keyring";
|
|
10
|
+
import { UnifiedKeystore } from "./keystore.js";
|
|
11
|
+
import { decodePairFromJson, loadJson } from "./utils.js";
|
|
12
|
+
import { ApiPromise, WsProvider } from "@polkadot/api";
|
|
13
|
+
export const FromOpts = {
|
|
14
|
+
getKeystoreDir: function (opts) {
|
|
15
|
+
if (opts.foundry) {
|
|
16
|
+
return path.join(os.homedir(), ".foundry", "keystores");
|
|
17
|
+
}
|
|
18
|
+
if (opts.dir) {
|
|
19
|
+
return opts.dir;
|
|
20
|
+
}
|
|
21
|
+
return path.join(os.homedir(), ".every", "keystores");
|
|
22
|
+
},
|
|
23
|
+
getKeystoreFile: function (opts, name) {
|
|
24
|
+
const dir = FromOpts.getKeystoreDir(opts);
|
|
25
|
+
return path.join(dir, name);
|
|
26
|
+
},
|
|
27
|
+
getKeystore: async function (opts, account) {
|
|
28
|
+
const name = account ?? opts.account;
|
|
29
|
+
if (!name) {
|
|
30
|
+
throw new Error(`account not specified`);
|
|
31
|
+
}
|
|
32
|
+
const keyFile = FromOpts.getKeystoreFile(opts, account ?? opts.account);
|
|
33
|
+
const keyData = loadJson(keyFile);
|
|
34
|
+
const password = FromOpts.getPassword(opts);
|
|
35
|
+
const keystore = await UnifiedKeystore.fromJSON(keyData, password);
|
|
36
|
+
return keystore;
|
|
37
|
+
},
|
|
38
|
+
getPassword: function (opts) {
|
|
39
|
+
return opts.password
|
|
40
|
+
? opts.password
|
|
41
|
+
: opts.passwordFile
|
|
42
|
+
? fs.readFileSync(opts.passwordFile, "utf8").trim()
|
|
43
|
+
: promptSync({ sigint: true })("Enter keystore password: ", { echo: "" });
|
|
44
|
+
},
|
|
45
|
+
confirmPassword: function (opts) {
|
|
46
|
+
if (opts.password) {
|
|
47
|
+
return opts.password;
|
|
48
|
+
}
|
|
49
|
+
if (opts.passwordFile) {
|
|
50
|
+
return fs.readFileSync(opts.passwordFile, "utf8").trim();
|
|
51
|
+
}
|
|
52
|
+
const prompt = promptSync({ sigint: true });
|
|
53
|
+
const password = prompt("Enter keystore password: ", { echo: "" });
|
|
54
|
+
const confirmation = prompt("Re-enter to confirm: ", { echo: "" });
|
|
55
|
+
if (password !== confirmation) {
|
|
56
|
+
throw new Error(`Error: Passwords do not match`);
|
|
57
|
+
}
|
|
58
|
+
return password;
|
|
59
|
+
},
|
|
60
|
+
getUniverseConfig: function (opts) {
|
|
61
|
+
const config = loadMergedConfig();
|
|
62
|
+
const universeName = opts.universe;
|
|
63
|
+
const universe = config.universes[universeName];
|
|
64
|
+
if (!universe) {
|
|
65
|
+
const available = Object.keys(config.universes).join(", ");
|
|
66
|
+
throw new Error(`Universe "${universeName}" not found. Available: ${available}`);
|
|
67
|
+
}
|
|
68
|
+
return universe;
|
|
69
|
+
},
|
|
70
|
+
getObserverConfig: function (options) {
|
|
71
|
+
const conf = loadMergedConfig();
|
|
72
|
+
let observerName;
|
|
73
|
+
const DEFAULT_OBSERVER = "localnet";
|
|
74
|
+
if (options.network) {
|
|
75
|
+
observerName = options.network;
|
|
76
|
+
}
|
|
77
|
+
else if (options.universe) {
|
|
78
|
+
const universe = conf.universes[options.universe];
|
|
79
|
+
if (!universe) {
|
|
80
|
+
throw new Error(`Universe '${options.universe}' not found in config. Available: ${Object.keys(conf.universes).join(", ")}`);
|
|
81
|
+
}
|
|
82
|
+
observerName = universe.observer;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
observerName = DEFAULT_OBSERVER;
|
|
86
|
+
}
|
|
87
|
+
if (!observerName) {
|
|
88
|
+
throw new Error(`No observer resolved from options or config.`);
|
|
89
|
+
}
|
|
90
|
+
const observer = conf.observers[observerName];
|
|
91
|
+
if (!observer) {
|
|
92
|
+
throw new Error(`Observer '${observerName}' not found in config. Available: ${Object.keys(conf.observers).join(", ")}`);
|
|
93
|
+
}
|
|
94
|
+
return observer;
|
|
95
|
+
},
|
|
96
|
+
getPair: async function (opts) {
|
|
97
|
+
const keyFile = FromOpts.getKeystoreFile(opts, opts.account);
|
|
98
|
+
const keyData = loadJson(keyFile);
|
|
99
|
+
const keyring = new Keyring();
|
|
100
|
+
const pair = keyring.createFromJson(keyData);
|
|
101
|
+
if (pair.isLocked) {
|
|
102
|
+
const password = FromOpts.getPassword(opts);
|
|
103
|
+
pair.unlock(password);
|
|
104
|
+
}
|
|
105
|
+
return pair;
|
|
106
|
+
},
|
|
107
|
+
getPrivateKey: async function (opts) {
|
|
108
|
+
if (opts.privateKey) {
|
|
109
|
+
return opts.privateKey.startsWith("0x") ? opts.privateKey : `0x${opts.privateKey}`;
|
|
110
|
+
}
|
|
111
|
+
else if (opts.account) {
|
|
112
|
+
const keyFile = FromOpts.getKeystoreFile(opts, opts.account);
|
|
113
|
+
const keyData = loadJson(keyFile);
|
|
114
|
+
if (keyData.crypto || keyData.Crypto) {
|
|
115
|
+
// for Ethereum keystores
|
|
116
|
+
const password = FromOpts.getPassword(opts);
|
|
117
|
+
const wallet = await Wallet.fromEncryptedJson(JSON.stringify(keyData), password);
|
|
118
|
+
return wallet.privateKey;
|
|
119
|
+
}
|
|
120
|
+
else if (keyData.encoding || keyData.meta) {
|
|
121
|
+
// for Substrate keystores, must be ethereum type
|
|
122
|
+
if (keyData.meta?.isEthereum || keyData.meta?.type === "ethereum") {
|
|
123
|
+
const password = FromOpts.getPassword(opts);
|
|
124
|
+
const pair = decodePairFromJson(keyData, password);
|
|
125
|
+
return bytesToHex(pair.secretKey);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
throw new Error("Not an Ethereum account");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// Not supported for now
|
|
133
|
+
throw new Error("Unknown keystore format");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
throw new Error(`Neither account nor private key specified`);
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
toWriteEthereum: async function (opts) {
|
|
141
|
+
const conf = FromOpts.getUniverseConfig(opts);
|
|
142
|
+
const transport = http(conf.rpc);
|
|
143
|
+
const publicClient = createPublicClient({ transport });
|
|
144
|
+
const privateKey = await FromOpts.getPrivateKey(opts);
|
|
145
|
+
const account = privateKeyToAccount(privateKey);
|
|
146
|
+
const walletClient = createWalletClient({ account, transport });
|
|
147
|
+
return { publicClient, walletClient, conf };
|
|
148
|
+
},
|
|
149
|
+
toReadEthereum: function (opts) {
|
|
150
|
+
const conf = FromOpts.getUniverseConfig(opts);
|
|
151
|
+
const transport = http(conf.rpc);
|
|
152
|
+
const publicClient = createPublicClient({ transport });
|
|
153
|
+
return { publicClient, conf };
|
|
154
|
+
},
|
|
155
|
+
getSubstrateApi: async function (opts) {
|
|
156
|
+
const conf = FromOpts.getObserverConfig(opts);
|
|
157
|
+
const provider = new WsProvider(conf.rpc);
|
|
158
|
+
const api = await ApiPromise.create({ provider, noInitWarn: true });
|
|
159
|
+
return api;
|
|
160
|
+
},
|
|
161
|
+
};
|
package/dist/index.js
CHANGED
package/dist/logger.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import { stringify as j11 } from "json11";
|
|
3
|
+
import path from "path";
|
|
4
|
+
export class Logger {
|
|
5
|
+
quiet;
|
|
6
|
+
json;
|
|
7
|
+
constructor(opts = {}) {
|
|
8
|
+
this.quiet = opts.quiet ?? !!opts.json;
|
|
9
|
+
this.json = opts.json ?? false;
|
|
10
|
+
}
|
|
11
|
+
log(...args) {
|
|
12
|
+
if (!this.quiet)
|
|
13
|
+
console.error(...args);
|
|
14
|
+
}
|
|
15
|
+
warn(...args) {
|
|
16
|
+
if (!this.quiet)
|
|
17
|
+
console.error("[warn]", ...args);
|
|
18
|
+
}
|
|
19
|
+
error(...args) {
|
|
20
|
+
console.error("[error]", ...args);
|
|
21
|
+
}
|
|
22
|
+
result(data) {
|
|
23
|
+
const opts = {
|
|
24
|
+
quote: `"`,
|
|
25
|
+
quoteNames: true,
|
|
26
|
+
withBigInt: false,
|
|
27
|
+
};
|
|
28
|
+
if (this.json) {
|
|
29
|
+
if (typeof this.json === "string") {
|
|
30
|
+
const dir = path.dirname(this.json);
|
|
31
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
32
|
+
fs.writeFileSync(this.json, j11(data, opts));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.log(j11(data, opts)); // stdout
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
package/dist/parsers.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { u8aFixLength } from "@polkadot/util";
|
|
2
|
+
import { decodeAddress } from "@polkadot/util-crypto";
|
|
3
|
+
import { InvalidArgumentError } from "commander";
|
|
4
|
+
export function parseBigInt(arg) {
|
|
5
|
+
try {
|
|
6
|
+
return BigInt(arg);
|
|
7
|
+
}
|
|
8
|
+
catch (e /* eslint-disable-line */) {
|
|
9
|
+
throw new InvalidArgumentError("invalid bigint");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function parseAccountId(address) {
|
|
13
|
+
try {
|
|
14
|
+
return u8aFixLength(decodeAddress(address), 256);
|
|
15
|
+
}
|
|
16
|
+
catch (e /* eslint-disable-line */) {
|
|
17
|
+
throw new InvalidArgumentError("invalid account ID");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function parseNode4(arg) {
|
|
21
|
+
const parts = arg.split(".");
|
|
22
|
+
let [data, grant, set, id] = [0n, 0n, 0n, 0n];
|
|
23
|
+
if (parts.length == 2) {
|
|
24
|
+
set = BigInt(parts[0]);
|
|
25
|
+
id = BigInt(parts[1]);
|
|
26
|
+
}
|
|
27
|
+
else if (parts.length == 3) {
|
|
28
|
+
grant = BigInt(parts[0]);
|
|
29
|
+
set = BigInt(parts[1]);
|
|
30
|
+
id = BigInt(parts[2]);
|
|
31
|
+
}
|
|
32
|
+
else if (parts.length == 4) {
|
|
33
|
+
data = BigInt(parts[0]);
|
|
34
|
+
grant = BigInt(parts[1]);
|
|
35
|
+
set = BigInt(parts[2]);
|
|
36
|
+
id = BigInt(parts[3]);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
throw new InvalidArgumentError("invalid Node4");
|
|
40
|
+
}
|
|
41
|
+
return (data << 192n) | (grant << 128n) | (set << 64n) | id;
|
|
42
|
+
}
|
|
43
|
+
export function parseNode3(arg) {
|
|
44
|
+
const parts = arg.split(".");
|
|
45
|
+
let [grant, set, id] = [0n, 0n, 0n];
|
|
46
|
+
if (parts.length == 2) {
|
|
47
|
+
set = BigInt(parts[0]);
|
|
48
|
+
id = BigInt(parts[1]);
|
|
49
|
+
}
|
|
50
|
+
else if (parts.length == 3) {
|
|
51
|
+
grant = BigInt(parts[0]);
|
|
52
|
+
set = BigInt(parts[1]);
|
|
53
|
+
id = BigInt(parts[2]);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
throw new InvalidArgumentError("invalid Node3");
|
|
57
|
+
}
|
|
58
|
+
return (grant << 128n) | (set << 64n) | id;
|
|
59
|
+
}
|