@everyprotocol/every-cli 0.1.1 → 0.1.3
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 +11 -7
- package/abis/ElementRegistry.json +1 -1
- package/abis/IObjectMinter.json +1 -1
- package/abis/IObjectMinterAdmin.json +1 -1
- package/abis/IOmniRegistry.json +1 -1
- package/abis/ISet.json +1 -1
- package/abis/ISetRegistry.json +1 -1
- package/abis/ISetRegistryAdmin.json +1 -1
- package/abis/KindRegistry.json +1 -1
- package/abis/ObjectMinter.json +1 -1
- package/abis/OmniRegistry.json +1 -1
- package/abis/SetRegistry.json +1 -1
- package/dist/abi.js +5 -0
- package/dist/cmdgen.js +2 -2
- package/dist/cmds.js +7 -7
- package/dist/config.js +90 -1
- package/dist/index.js +6 -41
- package/dist/keystore.js +120 -0
- package/dist/matter.js +96 -0
- package/dist/mint.js +39 -25
- package/dist/options.js +26 -0
- package/dist/program.js +41 -0
- package/dist/relate.js +2 -2
- package/dist/substrate.js +57 -0
- package/dist/utils.js +149 -17
- package/dist/wallet.js +90 -0
- package/package.json +5 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import "@polkadot/api-augment/substrate";
|
|
2
|
+
import { decodeAddress } from "@polkadot/util-crypto";
|
|
3
|
+
import { u8aFixLength } from "@polkadot/util";
|
|
4
|
+
import "./options.js";
|
|
5
|
+
function createDeferred() {
|
|
6
|
+
let resolve;
|
|
7
|
+
let reject;
|
|
8
|
+
const promise = new Promise((res, rej) => {
|
|
9
|
+
resolve = res;
|
|
10
|
+
reject = rej;
|
|
11
|
+
});
|
|
12
|
+
return { promise, resolve, reject };
|
|
13
|
+
}
|
|
14
|
+
export async function submitTransaction(api, tx, pair) {
|
|
15
|
+
// const pTxn = Promise.withResolvers<Transaction>();
|
|
16
|
+
// const pReceipt = Promise.withResolvers<Receipt>();
|
|
17
|
+
const pTxn = createDeferred();
|
|
18
|
+
const pReceipt = createDeferred();
|
|
19
|
+
const accountId = u8aFixLength(decodeAddress(pair.address), 256);
|
|
20
|
+
const nonce = await api.rpc.system.accountNextIndex(accountId);
|
|
21
|
+
const unsub = await tx.signAndSend(pair, { nonce }, (result) => {
|
|
22
|
+
const { status, txHash, events, dispatchError } = result;
|
|
23
|
+
if (dispatchError) {
|
|
24
|
+
if (dispatchError.isModule) {
|
|
25
|
+
const meta = api.registry.findMetaError(dispatchError.asModule);
|
|
26
|
+
const msg = `${meta.section}.${meta.name}${meta.docs.length ? `: ${meta.docs.join(" ")}` : ""}`;
|
|
27
|
+
unsub();
|
|
28
|
+
pReceipt.reject(new Error(`DispatchError: ${msg}`));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
unsub();
|
|
32
|
+
pReceipt.reject(new Error(dispatchError.toString()));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (status.isReady) {
|
|
36
|
+
pTxn.resolve({ txHash: txHash.toHex(), receipt: pReceipt.promise });
|
|
37
|
+
}
|
|
38
|
+
if (status.isFinalized) {
|
|
39
|
+
unsub();
|
|
40
|
+
pReceipt.resolve({
|
|
41
|
+
txHash: txHash.toHex(),
|
|
42
|
+
blockHash: status.asFinalized.toHex(),
|
|
43
|
+
events: events,
|
|
44
|
+
result,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return await pTxn.promise;
|
|
49
|
+
}
|
|
50
|
+
export function findEvent(events, name) {
|
|
51
|
+
for (const record of events) {
|
|
52
|
+
if (record.event && record.event.method === name) {
|
|
53
|
+
return record.event;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
package/dist/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createPublicClient, createWalletClient, encodeAbiParameters, http } from "viem";
|
|
1
|
+
import { bytesToHex, createPublicClient, createWalletClient, encodeAbiParameters, http, } from "viem";
|
|
2
2
|
import { formatAbiParameter } from "abitype";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import path from "path";
|
|
@@ -8,6 +8,12 @@ import promptSync from "prompt-sync";
|
|
|
8
8
|
import os from "os";
|
|
9
9
|
import JSON5 from "json5";
|
|
10
10
|
import { fileURLToPath } from "url";
|
|
11
|
+
import { isHex, hexToU8a } from "@polkadot/util";
|
|
12
|
+
import { base64Decode } from "@polkadot/util-crypto/base64";
|
|
13
|
+
import { decodePair } from "@polkadot/keyring/pair/decode";
|
|
14
|
+
import { loadMergedConfig } from "./config.js";
|
|
15
|
+
import Keyring from "@polkadot/keyring";
|
|
16
|
+
import { UnifiedKeystore } from "./keystore.js";
|
|
11
17
|
export const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
18
|
export function version() {
|
|
13
19
|
const pkgPath = path.resolve(__dirname, "../package.json");
|
|
@@ -42,33 +48,45 @@ export function stringify(o) {
|
|
|
42
48
|
const replacer = (_key, value) => (typeof value === "bigint" ? value.toString() : value);
|
|
43
49
|
return JSON5.stringify(o, replacer);
|
|
44
50
|
}
|
|
45
|
-
export async function
|
|
46
|
-
const transport = http(uniConf.
|
|
51
|
+
export async function getClientsEth(uniConf, opts) {
|
|
52
|
+
const transport = http(uniConf.rpc);
|
|
47
53
|
const publicClient = createPublicClient({ transport });
|
|
48
|
-
const privateKey = await
|
|
54
|
+
const privateKey = await readPrivateKeyEth(opts);
|
|
49
55
|
const account = privateKeyToAccount(privateKey);
|
|
50
56
|
const walletClient = createWalletClient({ account, transport });
|
|
51
57
|
return { publicClient, walletClient };
|
|
52
58
|
}
|
|
53
|
-
export async function
|
|
59
|
+
export async function readPrivateKeyEth(opts) {
|
|
54
60
|
if (opts.privateKey) {
|
|
55
61
|
return opts.privateKey.startsWith("0x") ? opts.privateKey : `0x${opts.privateKey}`;
|
|
56
62
|
}
|
|
57
63
|
else if (opts.account) {
|
|
58
|
-
const keystorePath =
|
|
59
|
-
const keystore =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
const keystorePath = resolveKeystoreFile(opts.account, opts);
|
|
65
|
+
const keystore = loadKeystore(keystorePath);
|
|
66
|
+
if (keystore.crypto || keystore.Crypto) {
|
|
67
|
+
// for Ethereum keystores
|
|
68
|
+
const password = getPassword(opts);
|
|
69
|
+
const wallet = await Wallet.fromEncryptedJson(JSON.stringify(keystore), password);
|
|
70
|
+
return wallet.privateKey;
|
|
71
|
+
}
|
|
72
|
+
else if (keystore.encoding || keystore.meta) {
|
|
73
|
+
// for Substrate keystores
|
|
74
|
+
if (keystore.meta?.isEthereum || keystore.meta?.type === "ethereum") {
|
|
75
|
+
const password = getPassword(opts);
|
|
76
|
+
const pair = decodeSubstratePair(keystore, password);
|
|
77
|
+
return bytesToHex(pair.secretKey);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
throw new Error("Not an Ethereum account");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Not supported for now
|
|
85
|
+
throw new Error("Unknown keystore format");
|
|
86
|
+
}
|
|
69
87
|
}
|
|
70
88
|
else {
|
|
71
|
-
throw new Error(
|
|
89
|
+
throw new Error(`Neither account nor private key specified`);
|
|
72
90
|
}
|
|
73
91
|
}
|
|
74
92
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -88,3 +106,117 @@ export function checkArguments(raw, func) {
|
|
|
88
106
|
return arg;
|
|
89
107
|
});
|
|
90
108
|
}
|
|
109
|
+
export function resolveKeystoreDir(options) {
|
|
110
|
+
if (options.foundry) {
|
|
111
|
+
return path.join(os.homedir(), ".foundry", "keystores");
|
|
112
|
+
}
|
|
113
|
+
if (options.dir) {
|
|
114
|
+
return options.dir;
|
|
115
|
+
}
|
|
116
|
+
return path.join(os.homedir(), ".every", "keystores");
|
|
117
|
+
}
|
|
118
|
+
export function resolveKeystoreFile(name, options) {
|
|
119
|
+
const dir = resolveKeystoreDir(options);
|
|
120
|
+
return path.join(dir, name);
|
|
121
|
+
}
|
|
122
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
123
|
+
export function saveKeystore(json, name, options) {
|
|
124
|
+
const dir = resolveKeystoreDir(options);
|
|
125
|
+
if (!fs.existsSync(dir)) {
|
|
126
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
127
|
+
}
|
|
128
|
+
const file = path.join(dir, name);
|
|
129
|
+
if (fs.existsSync(file)) {
|
|
130
|
+
throw new Error(`File exists: ${file}`);
|
|
131
|
+
}
|
|
132
|
+
fs.writeFileSync(file, JSON.stringify(json));
|
|
133
|
+
console.log(`File saved: ${file}`);
|
|
134
|
+
}
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
136
|
+
export function loadKeystore(file) {
|
|
137
|
+
if (!fs.existsSync(file)) {
|
|
138
|
+
throw new Error(`Keystore file not found: ${file}`);
|
|
139
|
+
}
|
|
140
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
141
|
+
}
|
|
142
|
+
export function getPassword(opts) {
|
|
143
|
+
return opts.password
|
|
144
|
+
? opts.password
|
|
145
|
+
: opts.passwordFile
|
|
146
|
+
? fs.readFileSync(opts.passwordFile, "utf8").trim()
|
|
147
|
+
: promptSync({ sigint: true })("Password: ", { echo: "" });
|
|
148
|
+
}
|
|
149
|
+
export function getPasswordConfirm(opts) {
|
|
150
|
+
if (opts.password) {
|
|
151
|
+
return opts.password;
|
|
152
|
+
}
|
|
153
|
+
if (opts.passwordFile) {
|
|
154
|
+
return fs.readFileSync(opts.passwordFile, "utf8").trim();
|
|
155
|
+
}
|
|
156
|
+
const prompt = promptSync({ sigint: true });
|
|
157
|
+
const password = prompt("Password: ", { echo: "" });
|
|
158
|
+
const confirmation = prompt("Confirm: ", { echo: "" });
|
|
159
|
+
if (password !== confirmation) {
|
|
160
|
+
throw new Error(`Error: Passwords do not match`);
|
|
161
|
+
}
|
|
162
|
+
return password;
|
|
163
|
+
}
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
165
|
+
export function decodeSubstratePair(keystore, password) {
|
|
166
|
+
const encodedRaw = keystore.encoded;
|
|
167
|
+
let encodingType = keystore.encoding.type;
|
|
168
|
+
encodingType = !Array.isArray(encodingType) ? [encodingType] : encodingType;
|
|
169
|
+
const encoded = isHex(encodedRaw) ? hexToU8a(encodedRaw) : base64Decode(encodedRaw);
|
|
170
|
+
const decoded = decodePair(password, encoded, encodingType);
|
|
171
|
+
return decoded;
|
|
172
|
+
}
|
|
173
|
+
export function getSubstrateAccountPair(flags) {
|
|
174
|
+
const keyFile = resolveKeystoreFile(flags.account, flags);
|
|
175
|
+
const keyData = loadKeystore(keyFile);
|
|
176
|
+
const keyring = new Keyring();
|
|
177
|
+
const pair = keyring.createFromJson(keyData);
|
|
178
|
+
if (pair.isLocked) {
|
|
179
|
+
const password = getPassword(flags);
|
|
180
|
+
pair.unlock(password);
|
|
181
|
+
}
|
|
182
|
+
return pair;
|
|
183
|
+
}
|
|
184
|
+
export async function keystoreFromOptions(options) {
|
|
185
|
+
if (!options.account) {
|
|
186
|
+
throw new Error("Account must be specified with --account");
|
|
187
|
+
}
|
|
188
|
+
return keystoreFromAccount(options.account, options);
|
|
189
|
+
}
|
|
190
|
+
export async function keystoreFromAccount(account, options) {
|
|
191
|
+
const file = resolveKeystoreFile(account, options);
|
|
192
|
+
const keyData = loadKeystore(file);
|
|
193
|
+
const password = getPassword(options);
|
|
194
|
+
const keystore = await UnifiedKeystore.fromJSON(keyData, password);
|
|
195
|
+
return keystore;
|
|
196
|
+
}
|
|
197
|
+
export function getObserverConfig(options) {
|
|
198
|
+
const conf = loadMergedConfig();
|
|
199
|
+
let observerName;
|
|
200
|
+
const DEFAULT_OBSERVER = "localnet";
|
|
201
|
+
if (options.observer) {
|
|
202
|
+
observerName = options.observer;
|
|
203
|
+
}
|
|
204
|
+
else if (options.universe) {
|
|
205
|
+
const universe = conf.universes[options.universe];
|
|
206
|
+
if (!universe) {
|
|
207
|
+
throw new Error(`Universe '${options.universe}' not found in config. Available: ${Object.keys(conf.universes).join(", ")}`);
|
|
208
|
+
}
|
|
209
|
+
observerName = universe.observer;
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
observerName = DEFAULT_OBSERVER;
|
|
213
|
+
}
|
|
214
|
+
if (!observerName) {
|
|
215
|
+
throw new Error(`No observer resolved from options or config.`);
|
|
216
|
+
}
|
|
217
|
+
const observer = conf.observers[observerName];
|
|
218
|
+
if (!observer) {
|
|
219
|
+
throw new Error(`Observer '${observerName}' not found in config. Available: ${Object.keys(conf.observers).join(", ")}`);
|
|
220
|
+
}
|
|
221
|
+
return observer;
|
|
222
|
+
}
|
package/dist/wallet.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { Keyring } from "@polkadot/keyring";
|
|
3
|
+
import { mnemonicGenerate, cryptoWaitReady } from "@polkadot/util-crypto";
|
|
4
|
+
import { bytesToHex } from "viem";
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import { getPasswordConfirm, keystoreFromAccount, resolveKeystoreDir, saveKeystore } from "./utils.js";
|
|
7
|
+
const listCmd = new Command()
|
|
8
|
+
.name("list")
|
|
9
|
+
.description("List all wallets")
|
|
10
|
+
.option("-f, --foundry", "use foundry keystore directory (~/.foundry/keystores)")
|
|
11
|
+
.option("--dir <dir>", "specify a custom keystore directory")
|
|
12
|
+
.action(async (options) => {
|
|
13
|
+
const dir = resolveKeystoreDir(options);
|
|
14
|
+
const files = fs.readdirSync(dir);
|
|
15
|
+
files.forEach((file) => console.log(file));
|
|
16
|
+
});
|
|
17
|
+
const generateCmd = new Command()
|
|
18
|
+
.name("new")
|
|
19
|
+
.description("Generate a new wallet")
|
|
20
|
+
.option("-t, --type <type>", "key type (sr25519, ed25519, ethereum)", "sr25519")
|
|
21
|
+
.option("-p, --password <password>", "password to encrypt the keystore")
|
|
22
|
+
.option("-P, --password-file <file>", "password file")
|
|
23
|
+
.option("--dir <dir>", "specify keystore directory")
|
|
24
|
+
.argument("<name>", "name of the wallet")
|
|
25
|
+
.action(async (name, options) => {
|
|
26
|
+
const password = getPasswordConfirm(options);
|
|
27
|
+
await cryptoWaitReady();
|
|
28
|
+
const keyring = new Keyring();
|
|
29
|
+
const mnemonic = mnemonicGenerate();
|
|
30
|
+
const pair = keyring.addFromUri(mnemonic, { name }, options.type);
|
|
31
|
+
const json = pair.toJson(password);
|
|
32
|
+
saveKeystore(json, name, options);
|
|
33
|
+
});
|
|
34
|
+
const importCmd = new Command()
|
|
35
|
+
.name("import")
|
|
36
|
+
.description("Import a wallet from a secrete URI")
|
|
37
|
+
.option("-t, --type <type>", "key type (sr25519, ed25519, ethereum)", "sr25519")
|
|
38
|
+
.option("-p, --password <password>", "password to encrypt the keystore")
|
|
39
|
+
.option("-P, --password-file <file>", "password file")
|
|
40
|
+
.option("--dir <dir>", "specify a custom keystore directory")
|
|
41
|
+
.option("-f, --foundry", "use foundry keystore directory (~/.foundry/keystores)")
|
|
42
|
+
.argument("<name>", "name of the wallet")
|
|
43
|
+
.argument("<suri>", "secret URI")
|
|
44
|
+
.action(async (name, suri, options) => {
|
|
45
|
+
const password = getPasswordConfirm(options);
|
|
46
|
+
await cryptoWaitReady();
|
|
47
|
+
const keyring = new Keyring({ type: options.type });
|
|
48
|
+
const pair = keyring.addFromUri(suri);
|
|
49
|
+
const json = pair.toJson(password);
|
|
50
|
+
saveKeystore(json, name, options);
|
|
51
|
+
});
|
|
52
|
+
const inspectCmd = new Command()
|
|
53
|
+
.name("inspect")
|
|
54
|
+
.description("Inspect a wallet")
|
|
55
|
+
.option("-t, --type <type>", "key type (sr25519, ed25519, ethereum)", "sr25519")
|
|
56
|
+
.option("-p, --password <password>", "password to decrypt the keystore")
|
|
57
|
+
.option("-P, --password-file <file>", "file containing the password")
|
|
58
|
+
.option("-x, --decrypt", "also decrypt the private key", false)
|
|
59
|
+
.option("--dir <dir>", "specify a custom keystore directory")
|
|
60
|
+
.option("-f, --foundry", "use foundry keystore directory (~/.foundry/keystores)")
|
|
61
|
+
.argument("<name>", "name of the wallet")
|
|
62
|
+
.action(async (name, options) => {
|
|
63
|
+
const keystore = await keystoreFromAccount(name, options);
|
|
64
|
+
let decoded;
|
|
65
|
+
if (options.decrypt) {
|
|
66
|
+
decoded = await keystore.privateKey();
|
|
67
|
+
}
|
|
68
|
+
let dir = "~/.every/keystores";
|
|
69
|
+
if (options.foundry) {
|
|
70
|
+
dir = "~/.foundry/keystores";
|
|
71
|
+
}
|
|
72
|
+
else if (options.dir) {
|
|
73
|
+
dir = options.dir;
|
|
74
|
+
}
|
|
75
|
+
console.log(` Keystore: ${dir}/${name}`);
|
|
76
|
+
console.log(` Store Type: ${keystore.type()}`);
|
|
77
|
+
console.log(` Key Type: ${keystore.keyType()}`);
|
|
78
|
+
console.log(` Address: ${await keystore.address()}`);
|
|
79
|
+
console.log(` Public Key: ${bytesToHex(await keystore.publicKey())}`);
|
|
80
|
+
if (decoded) {
|
|
81
|
+
console.log(`Private Key: ${bytesToHex(decoded)}`);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
export const walletCmd = new Command()
|
|
85
|
+
.name("wallet")
|
|
86
|
+
.description("manage wallets")
|
|
87
|
+
.addCommand(listCmd)
|
|
88
|
+
.addCommand(generateCmd)
|
|
89
|
+
.addCommand(importCmd)
|
|
90
|
+
.addCommand(inspectCmd);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@everyprotocol/every-cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/",
|
|
7
7
|
"abis/",
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@iarna/toml": "^2.2.5",
|
|
31
|
+
"@polkadot/api": "^16.2.2",
|
|
32
|
+
"@polkadot/keyring": "^13.5.2",
|
|
33
|
+
"@polkadot/util": "^13.5.2",
|
|
34
|
+
"@polkadot/util-crypto": "^13.5.2",
|
|
31
35
|
"commander": "^13.1.0",
|
|
32
36
|
"ethers": "^6.14.0",
|
|
33
37
|
"json5": "^2.2.3",
|