@everyprotocol/every-cli 0.1.0 → 0.1.2
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 +8 -2
- package/LICENSE +7 -0
- package/README.md +99 -0
- package/dist/cmdgen.js +2 -2
- package/dist/cmds.js +7 -7
- package/dist/config.js +4 -1
- package/dist/index.js +6 -41
- package/dist/matter.js +152 -0
- package/dist/mint.js +2 -2
- package/dist/program.js +41 -0
- package/dist/relate.js +2 -2
- package/dist/utils.js +108 -17
- package/dist/wallet.js +108 -0
- package/package.json +5 -1
package/.every.toml
CHANGED
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
# This file can be overridden by placing a .every.toml file in your home directory
|
|
3
3
|
# or in the current working directory
|
|
4
4
|
[universes.local]
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
id = 31337
|
|
6
|
+
rpc = "http://127.0.0.1:8545"
|
|
7
|
+
explorer = "http://127.0.0.1:8545"
|
|
8
|
+
|
|
9
|
+
[universes.local.observer]
|
|
10
|
+
rpc = "ws://localhost:9944"
|
|
11
|
+
gateway = "http://every.im.local"
|
|
12
|
+
explorer = "http://every.im.local"
|
|
7
13
|
|
|
8
14
|
[universes.local.contracts]
|
|
9
15
|
SetRegistry = "0x854C35Fd2b65fE9fcE71dddE91De8c3e1A7Dc8Ae"
|
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2025 "Every Protocol" Project Steward.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1 +1,100 @@
|
|
|
1
|
+
# Every CLI
|
|
1
2
|
|
|
3
|
+
A command-line interface for interacting with the Every Protocol.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add -g @everyprotocol/every-cli
|
|
9
|
+
npm install -g @everyprotocol/every-cli
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
> **Note:** You normally don’t need to configure anything manually.
|
|
15
|
+
> A default configuration is bundled with support for all chains officially supported by the protocol.
|
|
16
|
+
|
|
17
|
+
The CLI looks for configuration in the following locations (in order of precedence):
|
|
18
|
+
1. `.every.toml` in the current directory
|
|
19
|
+
2. `.every.toml` in your home directory
|
|
20
|
+
3. The default configuration bundled with the package
|
|
21
|
+
|
|
22
|
+
Example configuration:
|
|
23
|
+
|
|
24
|
+
```toml
|
|
25
|
+
[universes.local]
|
|
26
|
+
rpc_url = "http://localhost:8545"
|
|
27
|
+
contracts.KindRegistry = "0x5FbDB2315678afecb367f032d93F642f64180aa3"
|
|
28
|
+
contracts.SetRegistry = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"
|
|
29
|
+
contracts.ElementRegistry = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"
|
|
30
|
+
contracts.OmniRegistry = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
|
|
31
|
+
contracts.ObjectMinter = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"
|
|
32
|
+
|
|
33
|
+
[universes.testnet]
|
|
34
|
+
rpc_url = "https://testnet-rpc.example.com"
|
|
35
|
+
contracts.KindRegistry = "0x..."
|
|
36
|
+
# Add other contract addresses as needed
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
The CLI provides commands for interacting with different aspects of the Every Protocol:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
every --help
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Main Commands
|
|
48
|
+
|
|
49
|
+
- `every kind` - Manage kinds
|
|
50
|
+
- `every set` - Manage sets
|
|
51
|
+
- `every relation` - Manage relations
|
|
52
|
+
- `every unique` - Manage uniques
|
|
53
|
+
- `every value` - Manage values
|
|
54
|
+
- `every object` - Create and interact with objects
|
|
55
|
+
- `every mintpolicy` - Manage mint policies
|
|
56
|
+
|
|
57
|
+
### Examples
|
|
58
|
+
|
|
59
|
+
#### Minting an Object
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
every object mint 17.1 0xYourAddress
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Relating objects
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
every object relate 17.1 42 18.2
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Viewing Object Information
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
every object owner 17.1
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Signing Transactions
|
|
78
|
+
|
|
79
|
+
Most write operations require signing with a wallet or private key. You can provide your private key or use a keystore:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
-k, --private-key <key> Private key to sign the transaction
|
|
83
|
+
-a, --account <account> Name of the keystore to sign the transaction
|
|
84
|
+
-p, --password [password] Password to decrypt the keystore
|
|
85
|
+
-f, --password-file <file> File containing the password to decrypt the keystore
|
|
86
|
+
--foundry Use keystore from Foundry directory (~/.foundry/keystores)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Using a private key
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
every object mint 17.1 0xRecipient --private-key 0xYourPrivateKey
|
|
93
|
+
```
|
|
94
|
+
#### Using a keystore
|
|
95
|
+
```bash
|
|
96
|
+
every object mint 17.1 0xRecipient --account myaccount
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
[MIT](LICENSE)
|
package/dist/cmdgen.js
CHANGED
|
@@ -71,7 +71,7 @@ const setContractObjectCmdConfig = {
|
|
|
71
71
|
const rawArgs = checkArguments(ctx.cmd.args, ctx.cmdAbi);
|
|
72
72
|
const [set, id] = rawArgs[0].split(".");
|
|
73
73
|
const args = [JSON5.parse(id), ...rawArgs.slice(1)];
|
|
74
|
-
const publicClient = createPublicClient({ transport: http(ctx.conf.
|
|
74
|
+
const publicClient = createPublicClient({ transport: http(ctx.conf.rpc) });
|
|
75
75
|
const address = (await publicClient.readContract({
|
|
76
76
|
address: ctx.conf.contracts["SetRegistry"],
|
|
77
77
|
abi: abi.setContract,
|
|
@@ -85,7 +85,7 @@ const objectUriTxnPrepare = async function (ctx) {
|
|
|
85
85
|
const rawArgs = checkArguments(ctx.cmd.args, ctx.cmdAbi);
|
|
86
86
|
// const [set, id] = rawArgs[0].split(".");
|
|
87
87
|
const set = rawArgs[0].split(".")[0];
|
|
88
|
-
const publicClient = createPublicClient({ transport: http(ctx.conf.
|
|
88
|
+
const publicClient = createPublicClient({ transport: http(ctx.conf.rpc) });
|
|
89
89
|
const address = (await publicClient.readContract({
|
|
90
90
|
address: ctx.conf.contracts["SetRegistry"],
|
|
91
91
|
abi: abi.setContract,
|
package/dist/cmds.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Argument, Command, Option } from "commander";
|
|
2
2
|
import { createPublicClient, http, parseEventLogs, stringify } from "viem";
|
|
3
3
|
import { getUniverseConfig } from "./config.js";
|
|
4
|
-
import { checkArguments,
|
|
4
|
+
import { checkArguments, getClientsEth } from "./utils.js";
|
|
5
5
|
export function defaultReadFunctionOptions() {
|
|
6
6
|
const options = [];
|
|
7
7
|
options.push(new Option("-u, --universe <universe>", "universe name").default("local"));
|
|
8
|
-
options.push(new Option("--
|
|
8
|
+
options.push(new Option("--dry-run", "Simulate the command without sending a transaction"));
|
|
9
9
|
return options;
|
|
10
10
|
}
|
|
11
11
|
export function defaultWriteFunctionOptions() {
|
|
@@ -14,9 +14,9 @@ export function defaultWriteFunctionOptions() {
|
|
|
14
14
|
options.push(new Option("-k, --private-key <key>", "private key to sign the transaction"));
|
|
15
15
|
options.push(new Option("-a, --account <account>", "name of the keystore to sign the transaction"));
|
|
16
16
|
options.push(new Option("-p, --password [password]", "password to decrypt the keystore"));
|
|
17
|
-
options.push(new Option("
|
|
18
|
-
options.push(new Option("--foundry", "use keystore from Foundry directory (~/.foundry/keystores)"));
|
|
19
|
-
options.push(new Option("--
|
|
17
|
+
options.push(new Option("--password-file <file>", "file containing the password to decrypt the keystore"));
|
|
18
|
+
options.push(new Option("-f, --foundry", "use keystore from Foundry directory (~/.foundry/keystores)"));
|
|
19
|
+
options.push(new Option("--dry-run", "Simulate the command without sending a transaction"));
|
|
20
20
|
return options;
|
|
21
21
|
}
|
|
22
22
|
const defaultConfig = {
|
|
@@ -40,12 +40,12 @@ const defaultConfig = {
|
|
|
40
40
|
const abi = [ctx.txnAbi, ...ctx.nonFuncs];
|
|
41
41
|
const functionName = ctx.txnAbi.name;
|
|
42
42
|
if (isRead) {
|
|
43
|
-
const publicClient = createPublicClient({ transport: http(ctx.conf.
|
|
43
|
+
const publicClient = createPublicClient({ transport: http(ctx.conf.rpc) });
|
|
44
44
|
const result = await publicClient.readContract({ address, abi, functionName, args });
|
|
45
45
|
console.log(`Result:`, result);
|
|
46
46
|
}
|
|
47
47
|
else {
|
|
48
|
-
const { publicClient, walletClient } = await
|
|
48
|
+
const { publicClient, walletClient } = await getClientsEth(ctx.conf, opts);
|
|
49
49
|
const account = walletClient.account;
|
|
50
50
|
console.log({
|
|
51
51
|
isRead,
|
package/dist/config.js
CHANGED
|
@@ -39,7 +39,10 @@ function loadProtocolConfig() {
|
|
|
39
39
|
// console.log(name, uni);
|
|
40
40
|
mergedConfig.universes[name] = {
|
|
41
41
|
name: name,
|
|
42
|
-
|
|
42
|
+
id: uni.id,
|
|
43
|
+
rpc: uni.rpc,
|
|
44
|
+
explorer: uni.explorer,
|
|
45
|
+
observer: uni.observer,
|
|
43
46
|
contracts: uni.contracts || {},
|
|
44
47
|
};
|
|
45
48
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,43 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const kindCmd = new RenamingCommand().name("kind").description("manage kinds").addCommands(subCmds.kind);
|
|
9
|
-
const setCmd = new RenamingCommand().name("set").description("manage sets").addCommands(subCmds.set);
|
|
10
|
-
const relationCmd = new RenamingCommand()
|
|
11
|
-
.name("relation")
|
|
12
|
-
.description("manage relations")
|
|
13
|
-
.addCommands(subCmds.relation);
|
|
14
|
-
const uniqueCmd = new RenamingCommand().name("unique").description("manage uniques").addCommands(subCmds.unique);
|
|
15
|
-
const valueCmd = new RenamingCommand().name("value").description("manage values").addCommands(subCmds.value);
|
|
16
|
-
const objectCmd = new RenamingCommand()
|
|
17
|
-
.name("object")
|
|
18
|
-
.description("create and interact with objects")
|
|
19
|
-
.addCommands(subCmds.object);
|
|
20
|
-
const mintPolicyCmd = new RenamingCommand()
|
|
21
|
-
.name("mintpolicy")
|
|
22
|
-
.description("manage mint policies")
|
|
23
|
-
.addCommands(subCmds.mintpolicy);
|
|
24
|
-
const program = new Command()
|
|
25
|
-
.name("every")
|
|
26
|
-
.description("CLI for interacting with Every Protocol")
|
|
27
|
-
.version(version())
|
|
28
|
-
.showHelpAfterError(true);
|
|
29
|
-
program.addCommand(kindCmd);
|
|
30
|
-
program.addCommand(setCmd);
|
|
31
|
-
program.addCommand(relationCmd);
|
|
32
|
-
program.addCommand(uniqueCmd);
|
|
33
|
-
program.addCommand(valueCmd);
|
|
34
|
-
program.addCommand(objectCmd);
|
|
35
|
-
program.addCommand(mintPolicyCmd);
|
|
36
|
-
try {
|
|
37
|
-
await program.parseAsync();
|
|
38
|
-
}
|
|
39
|
-
catch (e) {
|
|
40
|
-
console.error(e.message);
|
|
41
|
-
}
|
|
2
|
+
import { program } from "./program.js";
|
|
3
|
+
try {
|
|
4
|
+
await program.parseAsync();
|
|
5
|
+
}
|
|
6
|
+
catch (e) {
|
|
7
|
+
console.error(e.message);
|
|
42
8
|
}
|
|
43
|
-
await main();
|
package/dist/matter.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { ApiPromise, WsProvider } from "@polkadot/api";
|
|
3
|
+
import "@polkadot/api-augment/substrate";
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { getUniverseConfig } from "./config.js";
|
|
7
|
+
import { getSubstrateAccountPair } from "./utils.js";
|
|
8
|
+
import { decodeAddress } from "@polkadot/util-crypto";
|
|
9
|
+
import { u8aFixLength } from "@polkadot/util";
|
|
10
|
+
function createDeferred() {
|
|
11
|
+
let resolve;
|
|
12
|
+
let reject;
|
|
13
|
+
const promise = new Promise((res, rej) => {
|
|
14
|
+
resolve = res;
|
|
15
|
+
reject = rej;
|
|
16
|
+
});
|
|
17
|
+
return { promise, resolve, reject };
|
|
18
|
+
}
|
|
19
|
+
async function submitTransaction(api, tx, pair) {
|
|
20
|
+
// const pTxn = Promise.withResolvers<Transaction>();
|
|
21
|
+
// const pReceipt = Promise.withResolvers<Receipt>();
|
|
22
|
+
const pTxn = createDeferred();
|
|
23
|
+
const pReceipt = createDeferred();
|
|
24
|
+
const accountId = u8aFixLength(decodeAddress(pair.address), 256);
|
|
25
|
+
const nonce = await api.rpc.system.accountNextIndex(accountId);
|
|
26
|
+
const unsub = await tx.signAndSend(pair, { nonce }, ({ events = [], status, txHash, txIndex, blockNumber }) => {
|
|
27
|
+
if (status.isReady) {
|
|
28
|
+
pTxn.resolve({ txHash: txHash.toHex(), receipt: pReceipt.promise });
|
|
29
|
+
}
|
|
30
|
+
else if (status.isFinalized) {
|
|
31
|
+
pReceipt.resolve({
|
|
32
|
+
txHash: txHash.toHex(),
|
|
33
|
+
blockHash: status.asFinalized.toHex(),
|
|
34
|
+
txIndex,
|
|
35
|
+
blockNumber,
|
|
36
|
+
events: events,
|
|
37
|
+
});
|
|
38
|
+
unsub();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return await pTxn.promise;
|
|
42
|
+
}
|
|
43
|
+
function findEvent(events, name) {
|
|
44
|
+
for (const record of events) {
|
|
45
|
+
if (record.event && record.event.method === name) {
|
|
46
|
+
return record.event;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function guessContentType(filePath) {
|
|
52
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
53
|
+
switch (ext) {
|
|
54
|
+
case ".txt":
|
|
55
|
+
return "text/plain";
|
|
56
|
+
case ".json":
|
|
57
|
+
return "application/json";
|
|
58
|
+
case ".wasm":
|
|
59
|
+
return "application/wasm";
|
|
60
|
+
case ".jpg":
|
|
61
|
+
case ".jpeg":
|
|
62
|
+
return "image/jpeg";
|
|
63
|
+
case ".png":
|
|
64
|
+
return "image/png";
|
|
65
|
+
default:
|
|
66
|
+
return "application/octet-stream";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function genMatterCommand() {
|
|
70
|
+
const cmd = new Command().name("matter").description("Manage matters");
|
|
71
|
+
cmd
|
|
72
|
+
.command("register")
|
|
73
|
+
.description("Register matter on the Substrate chain")
|
|
74
|
+
.argument("<files...>", "Path to the file(s) containing the matter content")
|
|
75
|
+
.option("-c, --content-type <type>", "Default content type")
|
|
76
|
+
.option("-h, --hasher <number>", "Default hasher", "1")
|
|
77
|
+
.option("-u, --universe <universe>", "Universe name", "local")
|
|
78
|
+
.option("-a, --account <account>", "Name of the keystore to sign the transaction")
|
|
79
|
+
.option("-p, --password [password]", "Password to decrypt the keystore")
|
|
80
|
+
.option("--password-file <file>", "File containing the password to decrypt the keystore")
|
|
81
|
+
.action(async (files, options) => {
|
|
82
|
+
const materials = [];
|
|
83
|
+
// Process each file argument
|
|
84
|
+
for (const file of files) {
|
|
85
|
+
let [filePath, hasher_, contentType] = file.split(":");
|
|
86
|
+
const hasher = hasher_ ? Number(hasher_) : Number(options.hasher) || 1;
|
|
87
|
+
if (!contentType) {
|
|
88
|
+
if (options.contentType) {
|
|
89
|
+
contentType = options.contentType;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
contentType = guessContentType(filePath);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
materials.push({ filePath, hasher, contentType });
|
|
96
|
+
}
|
|
97
|
+
// Get universe configuration
|
|
98
|
+
const conf = getUniverseConfig(options);
|
|
99
|
+
if (!conf.observer.rpc) {
|
|
100
|
+
throw new Error("pre_rpc_url not configured in universe");
|
|
101
|
+
}
|
|
102
|
+
// Get account pair for signing
|
|
103
|
+
if (!options.account) {
|
|
104
|
+
throw new Error("Account must be specified with --account");
|
|
105
|
+
}
|
|
106
|
+
// const keystorePath = resolveKeystoreFile(options.account, options);
|
|
107
|
+
// const keystore = loadKeystore(keystorePath);
|
|
108
|
+
// const password = getPassword(options);
|
|
109
|
+
const accountPair = getSubstrateAccountPair(options);
|
|
110
|
+
// Connect to the Substrate node
|
|
111
|
+
console.log(`Connecting to ${conf.observer.rpc}...`);
|
|
112
|
+
const provider = new WsProvider(conf.observer.rpc);
|
|
113
|
+
const api = await ApiPromise.create({ provider });
|
|
114
|
+
await api.isReady;
|
|
115
|
+
console.log("Connected to Substrate node");
|
|
116
|
+
const txns = [];
|
|
117
|
+
// Submit transactions for each material
|
|
118
|
+
for (const { filePath, hasher, contentType } of materials) {
|
|
119
|
+
console.log(`Processing ${filePath}: content-type=${contentType}, hasher=${hasher}`);
|
|
120
|
+
const content = fs.readFileSync(filePath);
|
|
121
|
+
const contentRaw = api.createType("Raw", content, content.length);
|
|
122
|
+
const call = api.tx.matter.register(hasher, contentType, contentRaw);
|
|
123
|
+
console.log(`Submitting transaction for ${filePath}...`);
|
|
124
|
+
const txn = await submitTransaction(api, call, accountPair);
|
|
125
|
+
console.log(`Transaction submitted: ${txn.txHash}`);
|
|
126
|
+
txns.push({ txn, filePath });
|
|
127
|
+
}
|
|
128
|
+
// Wait for all transactions to be finalized
|
|
129
|
+
for (const { txn, filePath } of txns) {
|
|
130
|
+
console.log(`Waiting for ${filePath} to be finalized...`);
|
|
131
|
+
const receipt = await txn.receipt;
|
|
132
|
+
const event = findEvent(receipt.events, "MaterialAdded");
|
|
133
|
+
if (event) {
|
|
134
|
+
const hash = event.data[0].toString();
|
|
135
|
+
console.log(`${filePath} finalized in block ${receipt.blockHash}`);
|
|
136
|
+
console.log(` Matter hash: ${hash}`);
|
|
137
|
+
if (conf.observer.gateway) {
|
|
138
|
+
console.log(` Preimage: ${conf.observer.gateway}/m/${hash}`);
|
|
139
|
+
}
|
|
140
|
+
if (conf.observer.explorer) {
|
|
141
|
+
console.log(` Transaction: ${conf.observer.explorer}/extrinsic/${receipt.blockNumber}-${receipt.txIndex}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
console.log(`${filePath} finalized, but no MaterialAdded event found`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
await api.disconnect();
|
|
149
|
+
console.log("Disconnected from Substrate node");
|
|
150
|
+
});
|
|
151
|
+
return cmd;
|
|
152
|
+
}
|
package/dist/mint.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { defaultWriteFunctionOptions } from "./cmds.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getClientsEth, stringify } from "./utils.js";
|
|
4
4
|
import { getUniverseConfig } from "./config.js";
|
|
5
5
|
import { parseEventLogs, parseUnits } from "viem";
|
|
6
6
|
import { abi } from "./abi.js";
|
|
@@ -24,7 +24,7 @@ async function action() {
|
|
|
24
24
|
const conf = getUniverseConfig(opts);
|
|
25
25
|
const objectMinter = conf.contracts["ObjectMinter"];
|
|
26
26
|
const setRegistry = conf.contracts["SetRegistry"];
|
|
27
|
-
const { publicClient, walletClient } = await
|
|
27
|
+
const { publicClient, walletClient } = await getClientsEth(conf, opts);
|
|
28
28
|
const account = walletClient.account;
|
|
29
29
|
const [set, id] = args0[0].split(".");
|
|
30
30
|
const setContract = (await publicClient.readContract({
|
package/dist/program.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { generateCommands } from "./cmdgen.js";
|
|
3
|
+
import { RenamingCommand } from "./cmds.js";
|
|
4
|
+
import { version } from "./utils.js";
|
|
5
|
+
import { genWalletCommands } from "./wallet.js";
|
|
6
|
+
import { genMatterCommand } from "./matter.js";
|
|
7
|
+
function buildProgram() {
|
|
8
|
+
const subCmds = generateCommands();
|
|
9
|
+
const kindCmd = new RenamingCommand().name("kind").description("manage kinds").addCommands(subCmds.kind);
|
|
10
|
+
const setCmd = new RenamingCommand().name("set").description("manage sets").addCommands(subCmds.set);
|
|
11
|
+
const relationCmd = new RenamingCommand()
|
|
12
|
+
.name("relation")
|
|
13
|
+
.description("manage relations")
|
|
14
|
+
.addCommands(subCmds.relation);
|
|
15
|
+
const uniqueCmd = new RenamingCommand().name("unique").description("manage uniques").addCommands(subCmds.unique);
|
|
16
|
+
const valueCmd = new RenamingCommand().name("value").description("manage values").addCommands(subCmds.value);
|
|
17
|
+
const objectCmd = new RenamingCommand()
|
|
18
|
+
.name("object")
|
|
19
|
+
.description("create and interact with objects")
|
|
20
|
+
.addCommands(subCmds.object);
|
|
21
|
+
const mintPolicyCmd = new RenamingCommand()
|
|
22
|
+
.name("mintpolicy")
|
|
23
|
+
.description("manage mint policies")
|
|
24
|
+
.addCommands(subCmds.mintpolicy);
|
|
25
|
+
const program = new Command()
|
|
26
|
+
.name("every")
|
|
27
|
+
.description("CLI for interacting with Every Protocol")
|
|
28
|
+
.version(version())
|
|
29
|
+
.showHelpAfterError(true);
|
|
30
|
+
program.addCommand(kindCmd);
|
|
31
|
+
program.addCommand(setCmd);
|
|
32
|
+
program.addCommand(relationCmd);
|
|
33
|
+
program.addCommand(uniqueCmd);
|
|
34
|
+
program.addCommand(valueCmd);
|
|
35
|
+
program.addCommand(objectCmd);
|
|
36
|
+
program.addCommand(mintPolicyCmd);
|
|
37
|
+
program.addCommand(genWalletCommands());
|
|
38
|
+
program.addCommand(genMatterCommand());
|
|
39
|
+
return program;
|
|
40
|
+
}
|
|
41
|
+
export const program = buildProgram();
|
package/dist/relate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { defaultWriteFunctionOptions } from "./cmds.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getClientsEth, stringify } from "./utils.js";
|
|
4
4
|
import { getUniverseConfig } from "./config.js";
|
|
5
5
|
import { parseEventLogs } from "viem";
|
|
6
6
|
import { abi } from "./abi.js";
|
|
@@ -41,7 +41,7 @@ async function action(cmd, functionName) {
|
|
|
41
41
|
await sendTransaction(conf, opts, address, abi.relation, functionName, [tail, rel, head]);
|
|
42
42
|
}
|
|
43
43
|
async function sendTransaction(conf, opts, address, abi /* eslint-disable-line @typescript-eslint/no-explicit-any*/, functionName, args /* eslint-disable-line @typescript-eslint/no-explicit-any*/) {
|
|
44
|
-
const { publicClient, walletClient } = await
|
|
44
|
+
const { publicClient, walletClient } = await getClientsEth(conf, opts);
|
|
45
45
|
const { request } = await publicClient.simulateContract({
|
|
46
46
|
address,
|
|
47
47
|
abi,
|
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,10 @@ 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 Keyring from "@polkadot/keyring";
|
|
11
15
|
export const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
16
|
export function version() {
|
|
13
17
|
const pkgPath = path.resolve(__dirname, "../package.json");
|
|
@@ -42,33 +46,45 @@ export function stringify(o) {
|
|
|
42
46
|
const replacer = (_key, value) => (typeof value === "bigint" ? value.toString() : value);
|
|
43
47
|
return JSON5.stringify(o, replacer);
|
|
44
48
|
}
|
|
45
|
-
export async function
|
|
46
|
-
const transport = http(uniConf.
|
|
49
|
+
export async function getClientsEth(uniConf, opts) {
|
|
50
|
+
const transport = http(uniConf.rpc);
|
|
47
51
|
const publicClient = createPublicClient({ transport });
|
|
48
|
-
const privateKey = await
|
|
52
|
+
const privateKey = await readPrivateKeyEth(opts);
|
|
49
53
|
const account = privateKeyToAccount(privateKey);
|
|
50
54
|
const walletClient = createWalletClient({ account, transport });
|
|
51
55
|
return { publicClient, walletClient };
|
|
52
56
|
}
|
|
53
|
-
export async function
|
|
57
|
+
export async function readPrivateKeyEth(opts) {
|
|
54
58
|
if (opts.privateKey) {
|
|
55
59
|
return opts.privateKey.startsWith("0x") ? opts.privateKey : `0x${opts.privateKey}`;
|
|
56
60
|
}
|
|
57
61
|
else if (opts.account) {
|
|
58
|
-
const keystorePath =
|
|
59
|
-
const keystore =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
const keystorePath = resolveKeystoreFile(opts.account, opts);
|
|
63
|
+
const keystore = loadKeystore(keystorePath);
|
|
64
|
+
if (keystore.crypto || keystore.Crypto) {
|
|
65
|
+
// for Ethereum keystores
|
|
66
|
+
const password = getPassword(opts);
|
|
67
|
+
const wallet = await Wallet.fromEncryptedJson(JSON.stringify(keystore), password);
|
|
68
|
+
return wallet.privateKey;
|
|
69
|
+
}
|
|
70
|
+
else if (keystore.encoding || keystore.meta) {
|
|
71
|
+
// for Substrate keystores
|
|
72
|
+
if (keystore.meta?.isEthereum || keystore.meta?.type === "ethereum") {
|
|
73
|
+
const password = getPassword(opts);
|
|
74
|
+
const pair = decodeSubstratePair(keystore, password);
|
|
75
|
+
return bytesToHex(pair.secretKey);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
throw new Error("Not an Ethereum account");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// Not supported for now
|
|
83
|
+
throw new Error("Unknown keystore format");
|
|
84
|
+
}
|
|
69
85
|
}
|
|
70
86
|
else {
|
|
71
|
-
throw new Error(
|
|
87
|
+
throw new Error(`Neither account nor private key specified`);
|
|
72
88
|
}
|
|
73
89
|
}
|
|
74
90
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -88,3 +104,78 @@ export function checkArguments(raw, func) {
|
|
|
88
104
|
return arg;
|
|
89
105
|
});
|
|
90
106
|
}
|
|
107
|
+
export function resolveKeystoreDir(options) {
|
|
108
|
+
if (options.foundry) {
|
|
109
|
+
return path.join(os.homedir(), ".foundry", "keystores");
|
|
110
|
+
}
|
|
111
|
+
if (options.dir) {
|
|
112
|
+
return options.dir;
|
|
113
|
+
}
|
|
114
|
+
return path.join(os.homedir(), ".every", "keystores");
|
|
115
|
+
}
|
|
116
|
+
export function resolveKeystoreFile(name, options) {
|
|
117
|
+
const dir = resolveKeystoreDir(options);
|
|
118
|
+
return path.join(dir, name);
|
|
119
|
+
}
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
121
|
+
export function saveKeystore(json, name, options) {
|
|
122
|
+
const dir = resolveKeystoreDir(options);
|
|
123
|
+
if (!fs.existsSync(dir)) {
|
|
124
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
125
|
+
}
|
|
126
|
+
const file = path.join(dir, name);
|
|
127
|
+
if (fs.existsSync(file)) {
|
|
128
|
+
throw new Error(`File exists: ${file}`);
|
|
129
|
+
}
|
|
130
|
+
fs.writeFileSync(file, JSON.stringify(json));
|
|
131
|
+
console.log(`File saved: ${file}`);
|
|
132
|
+
}
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
134
|
+
export function loadKeystore(file) {
|
|
135
|
+
if (!fs.existsSync(file)) {
|
|
136
|
+
throw new Error(`Keystore file not found: ${file}`);
|
|
137
|
+
}
|
|
138
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
139
|
+
}
|
|
140
|
+
export function getPassword(opts) {
|
|
141
|
+
return opts.password
|
|
142
|
+
? opts.password
|
|
143
|
+
: opts.passwordFile
|
|
144
|
+
? fs.readFileSync(opts.passwordFile, "utf8").trim()
|
|
145
|
+
: promptSync({ sigint: true })("Password: ", { echo: "" });
|
|
146
|
+
}
|
|
147
|
+
export function getPasswordConfirm(opts) {
|
|
148
|
+
if (opts.password) {
|
|
149
|
+
return opts.password;
|
|
150
|
+
}
|
|
151
|
+
if (opts.passwordFile) {
|
|
152
|
+
return fs.readFileSync(opts.passwordFile, "utf8").trim();
|
|
153
|
+
}
|
|
154
|
+
const prompt = promptSync({ sigint: true });
|
|
155
|
+
const password = prompt("Password: ", { echo: "" });
|
|
156
|
+
const confirmation = prompt("Confirm: ", { echo: "" });
|
|
157
|
+
if (password !== confirmation) {
|
|
158
|
+
throw new Error(`Error: Passwords do not match`);
|
|
159
|
+
}
|
|
160
|
+
return password;
|
|
161
|
+
}
|
|
162
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
163
|
+
export function decodeSubstratePair(keystore, password) {
|
|
164
|
+
const encodedRaw = keystore.encoded;
|
|
165
|
+
let encodingType = keystore.encoding.type;
|
|
166
|
+
encodingType = !Array.isArray(encodingType) ? [encodingType] : encodingType;
|
|
167
|
+
const encoded = isHex(encodedRaw) ? hexToU8a(encodedRaw) : base64Decode(encodedRaw);
|
|
168
|
+
const decoded = decodePair(password, encoded, encodingType);
|
|
169
|
+
return decoded;
|
|
170
|
+
}
|
|
171
|
+
export function getSubstrateAccountPair(flags) {
|
|
172
|
+
const keyFile = resolveKeystoreFile(flags.account, flags);
|
|
173
|
+
const keyData = loadKeystore(keyFile);
|
|
174
|
+
const keyring = new Keyring();
|
|
175
|
+
const pair = keyring.createFromJson(keyData);
|
|
176
|
+
if (pair.isLocked) {
|
|
177
|
+
const password = getPassword(flags);
|
|
178
|
+
pair.unlock(password);
|
|
179
|
+
}
|
|
180
|
+
return pair;
|
|
181
|
+
}
|
package/dist/wallet.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
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 { decodeSubstratePair, getPassword, getPasswordConfirm, loadKeystore, resolveKeystoreDir, resolveKeystoreFile, saveKeystore, } from "./utils.js";
|
|
7
|
+
// Wallet commands
|
|
8
|
+
export function genWalletCommands() {
|
|
9
|
+
const walletCmd = new Command().name("wallet").description("manage wallets");
|
|
10
|
+
// List command
|
|
11
|
+
const listCmd = new Command()
|
|
12
|
+
.name("list")
|
|
13
|
+
.description("List all wallets")
|
|
14
|
+
.option("--foundry", "use foundry keystore directory (~/.foundry/keystores)")
|
|
15
|
+
.option("--dir <dir>", "specify a custom keystore directory")
|
|
16
|
+
.action(async (options) => {
|
|
17
|
+
const dir = resolveKeystoreDir(options);
|
|
18
|
+
const files = fs.readdirSync(dir);
|
|
19
|
+
files.forEach((file) => console.log(file));
|
|
20
|
+
});
|
|
21
|
+
// Generate command
|
|
22
|
+
const generateCmd = new Command()
|
|
23
|
+
.name("new")
|
|
24
|
+
.description("Generate a new wallet")
|
|
25
|
+
.option("-t, --type <type>", "key type (ed25519, sr25519, ethereum)", "sr25519")
|
|
26
|
+
.option("-p, --password <password>", "password to encrypt the keystore")
|
|
27
|
+
.option("-P, --password-file <file>", "password file")
|
|
28
|
+
.option("--dir <dir>", "specify keystore directory")
|
|
29
|
+
.option("--foundry", "use foundry keystore directory (~/.foundry/keystores)")
|
|
30
|
+
.argument("<name>", "name of the wallet")
|
|
31
|
+
.action(async (name, options) => {
|
|
32
|
+
const password = getPasswordConfirm(options);
|
|
33
|
+
await cryptoWaitReady();
|
|
34
|
+
const keyring = new Keyring();
|
|
35
|
+
const mnemonic = mnemonicGenerate();
|
|
36
|
+
const pair = keyring.addFromUri(mnemonic, { name }, options.type);
|
|
37
|
+
const json = pair.toJson(password);
|
|
38
|
+
saveKeystore(json, name, options);
|
|
39
|
+
});
|
|
40
|
+
// Import command
|
|
41
|
+
const importCmd = new Command()
|
|
42
|
+
.name("import")
|
|
43
|
+
.description("Import a wallet from a secrete URI")
|
|
44
|
+
.option("-t, --type <type>", "key type (sr25519, ed25519, ethereum)", "sr25519")
|
|
45
|
+
.option("-p, --password <password>", "password to encrypt the keystore")
|
|
46
|
+
.option("-P, --password-file <file>", "password file")
|
|
47
|
+
.option("--dir <dir>", "specify a custom keystore directory")
|
|
48
|
+
.option("--foundry", "use foundry keystore directory (~/.foundry/keystores)")
|
|
49
|
+
.argument("<name>", "name of the wallet")
|
|
50
|
+
.argument("<suri>", "secret URI")
|
|
51
|
+
.action(async (name, suri, options) => {
|
|
52
|
+
const password = getPasswordConfirm(options);
|
|
53
|
+
await cryptoWaitReady();
|
|
54
|
+
const keyring = new Keyring({ type: options.type });
|
|
55
|
+
const pair = keyring.addFromUri(suri);
|
|
56
|
+
const json = pair.toJson(password);
|
|
57
|
+
saveKeystore(json, name, options);
|
|
58
|
+
});
|
|
59
|
+
// Inspect command
|
|
60
|
+
const inspectCmd = new Command()
|
|
61
|
+
.name("inspect")
|
|
62
|
+
.description("Inspect a wallet")
|
|
63
|
+
.option("-t, --type <type>", "key type (sr25519, ed25519, ethereum)", "sr25519")
|
|
64
|
+
.option("-p, --password <password>", "password to decrypt the keystore")
|
|
65
|
+
.option("-P, --password-file <file>", "file containing the password")
|
|
66
|
+
.option("-x, --decrypt", "also decrypt the private key", false)
|
|
67
|
+
.option("--dir <dir>", "specify a custom keystore directory")
|
|
68
|
+
.option("--foundry", "use foundry keystore directory (~/.foundry/keystores)")
|
|
69
|
+
.argument("<name>", "name of the wallet")
|
|
70
|
+
.action(async (name, options) => {
|
|
71
|
+
const file = resolveKeystoreFile(name, options);
|
|
72
|
+
const keyData = loadKeystore(file);
|
|
73
|
+
await cryptoWaitReady();
|
|
74
|
+
const keyring = new Keyring({ type: options.type });
|
|
75
|
+
const account = keyring.addFromJson(keyData);
|
|
76
|
+
let password;
|
|
77
|
+
if (account.isLocked) {
|
|
78
|
+
password = getPassword(options);
|
|
79
|
+
account.unlock(password);
|
|
80
|
+
}
|
|
81
|
+
let decoded;
|
|
82
|
+
if (options.decrypt) {
|
|
83
|
+
decoded = decodeSubstratePair(keyData, password);
|
|
84
|
+
}
|
|
85
|
+
let keystoreDisplay = "~/.every/keystores";
|
|
86
|
+
if (options.foundry) {
|
|
87
|
+
keystoreDisplay = "~/.foundry/keystores";
|
|
88
|
+
}
|
|
89
|
+
else if (options.dir) {
|
|
90
|
+
keystoreDisplay = options.dir;
|
|
91
|
+
}
|
|
92
|
+
console.log(` Keystore: ${keystoreDisplay}`);
|
|
93
|
+
console.log(` Wallet: ${name}`);
|
|
94
|
+
console.log(` Type: ${account.type}`);
|
|
95
|
+
console.log(` Meta: ${JSON.stringify(account.meta)}`);
|
|
96
|
+
console.log(` Address: ${account.address}`);
|
|
97
|
+
console.log(`AddressRaw: ${bytesToHex(account.addressRaw)}`);
|
|
98
|
+
console.log(` PublicKey: ${bytesToHex(account.publicKey)}`);
|
|
99
|
+
if (decoded) {
|
|
100
|
+
console.log(`SecretKey: ${bytesToHex(decoded.secretKey)}`);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
walletCmd.addCommand(listCmd);
|
|
104
|
+
walletCmd.addCommand(generateCmd);
|
|
105
|
+
walletCmd.addCommand(importCmd);
|
|
106
|
+
walletCmd.addCommand(inspectCmd);
|
|
107
|
+
return walletCmd;
|
|
108
|
+
}
|
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.2",
|
|
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",
|