@everyprotocol/every-cli 0.1.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/.every.toml +13 -0
- package/README.md +1 -0
- package/abis/ElementRegistry.json +1 -0
- package/abis/IElementRegistry.json +1 -0
- package/abis/IKindRegistry.json +1 -0
- package/abis/IObjectMinter.json +1 -0
- package/abis/IObjectMinterAdmin.json +1 -0
- package/abis/IOmniRegistry.json +1 -0
- package/abis/ISet.json +1 -0
- package/abis/ISetRegistry.json +1 -0
- package/abis/ISetRegistryAdmin.json +1 -0
- package/abis/KindRegistry.json +1 -0
- package/abis/ObjectMinter.json +1 -0
- package/abis/OmniRegistry.json +1 -0
- package/abis/SetRegistry.json +1 -0
- package/dist/abi.js +106 -0
- package/dist/cmdgen.js +144 -0
- package/dist/cmds.js +132 -0
- package/dist/config.js +58 -0
- package/dist/index.js +43 -0
- package/dist/mint.js +64 -0
- package/dist/relate.js +104 -0
- package/dist/utils.js +90 -0
- package/package.json +47 -0
package/dist/cmdgen.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { createPublicClient, http } from "viem";
|
|
2
|
+
import { configureCommand } from "./cmds.js";
|
|
3
|
+
import JSON5 from "json5";
|
|
4
|
+
import { rstrip, excludes, includes, lstrip, startsWith, checkArguments } from "./utils.js";
|
|
5
|
+
import { replaceAbiParamAt, insertAbiParamAt, abi } from "./abi.js";
|
|
6
|
+
import { genMintCommand } from "./mint.js";
|
|
7
|
+
import { genRelateCommand, genUnrelateCommand } from "./relate.js";
|
|
8
|
+
export function generateCommands() {
|
|
9
|
+
const kind = abi.funcs.kindRegistry
|
|
10
|
+
.map(AbiToCommand({ contract: "KindRegistry", nonFuncs: abi.nonFuncs.kindRegistry, cmdName: lstrip("kind") }))
|
|
11
|
+
.sort(byPreferredOrder);
|
|
12
|
+
const set = [
|
|
13
|
+
...abi.funcs.setRegistryAdmin.map(AbiToCommand(setRegistryAdminCmdConfig)),
|
|
14
|
+
...abi.funcs.setRegistry
|
|
15
|
+
.filter(excludes(["setRegister", "setUpdate", "setTouch", "setUpgrade"]))
|
|
16
|
+
.map(AbiToCommand({ contract: "SetRegistry", nonFuncs: abi.nonFuncs.setRegistry, cmdName: lstrip("set") })),
|
|
17
|
+
].sort(byPreferredOrder);
|
|
18
|
+
const relation = abi.funcs.omniRegistry
|
|
19
|
+
.filter(startsWith("relation"))
|
|
20
|
+
.map(AbiToCommand({
|
|
21
|
+
contract: "OmniRegistry",
|
|
22
|
+
nonFuncs: abi.nonFuncs.omniRegistry,
|
|
23
|
+
cmdName: lstrip("relation"),
|
|
24
|
+
}))
|
|
25
|
+
.sort(byPreferredOrder);
|
|
26
|
+
const unique = abi.funcs.elemRegistry
|
|
27
|
+
.filter(startsWith("unique"))
|
|
28
|
+
.map(AbiToCommand({ contract: "ElementRegistry", nonFuncs: abi.nonFuncs.elemRegistry, cmdName: lstrip("unique") }))
|
|
29
|
+
.sort(byPreferredOrder);
|
|
30
|
+
const value = abi.funcs.elemRegistry
|
|
31
|
+
.filter(startsWith("value"))
|
|
32
|
+
.map(AbiToCommand({ contract: "ElementRegistry", nonFuncs: abi.nonFuncs.elemRegistry, cmdName: lstrip("value") }))
|
|
33
|
+
.sort(byPreferredOrder);
|
|
34
|
+
const object = [
|
|
35
|
+
genMintCommand(),
|
|
36
|
+
genRelateCommand(),
|
|
37
|
+
genUnrelateCommand(),
|
|
38
|
+
// write functions
|
|
39
|
+
...abi.funcs.setContract
|
|
40
|
+
.filter(includes("update,upgrade,touch,transfer".split(",")))
|
|
41
|
+
.map(AbiToCommand(setContractObjectCmdConfig)),
|
|
42
|
+
// read functions
|
|
43
|
+
...abi.funcs.setContract
|
|
44
|
+
.filter(excludes("update,upgrade,touch,transfer,uri,supportsInterface".split(",")))
|
|
45
|
+
.map(AbiToCommand(setContractObjectCmdConfig)),
|
|
46
|
+
...abi.funcs.setContract
|
|
47
|
+
.filter(includes("uri".split(",")))
|
|
48
|
+
.map(AbiToCommand({ ...setContractObjectCmdConfig, txnPrepare: objectUriTxnPrepare })),
|
|
49
|
+
].sort(byPreferredOrder);
|
|
50
|
+
const mintpolicy = [
|
|
51
|
+
...abi.funcs.objectMinterAdmin.map(AbiToCommand(objectMinterAdminCmdConfig)),
|
|
52
|
+
...abi.funcs.objectMinter
|
|
53
|
+
.filter(startsWith("mintPolicy"))
|
|
54
|
+
.filter(excludes("mintPolicyAdd,mintPolicyEnable,mintPolicyDisable".split(",")))
|
|
55
|
+
.map(AbiToCommand({ contract: "ObjectMinter", nonFuncs: abi.nonFuncs.objectMinter, cmdName: lstrip("mintPolicy") })),
|
|
56
|
+
].sort(byPreferredOrder);
|
|
57
|
+
return { kind, set, relation, unique, value, mintpolicy, object };
|
|
58
|
+
}
|
|
59
|
+
const setContractObjectCmdConfig = {
|
|
60
|
+
contract: "ISet",
|
|
61
|
+
nonFuncs: [],
|
|
62
|
+
cmdAbi: function (txnAbi) {
|
|
63
|
+
return replaceAbiParamAt(txnAbi, 0, {
|
|
64
|
+
name: "sid",
|
|
65
|
+
type: "string",
|
|
66
|
+
doc: "Scoped Object ID (in form of set.id, e.g., 17.1)",
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
txnPrepare: async function (ctx) {
|
|
71
|
+
const rawArgs = checkArguments(ctx.cmd.args, ctx.cmdAbi);
|
|
72
|
+
const [set, id] = rawArgs[0].split(".");
|
|
73
|
+
const args = [JSON5.parse(id), ...rawArgs.slice(1)];
|
|
74
|
+
const publicClient = createPublicClient({ transport: http(ctx.conf.rpcUrl) });
|
|
75
|
+
const address = (await publicClient.readContract({
|
|
76
|
+
address: ctx.conf.contracts["SetRegistry"],
|
|
77
|
+
abi: abi.setContract,
|
|
78
|
+
functionName: "setContract",
|
|
79
|
+
args: [set],
|
|
80
|
+
}));
|
|
81
|
+
return { address: address, tag: ctx.contract, args };
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
const objectUriTxnPrepare = async function (ctx) {
|
|
85
|
+
const rawArgs = checkArguments(ctx.cmd.args, ctx.cmdAbi);
|
|
86
|
+
// const [set, id] = rawArgs[0].split(".");
|
|
87
|
+
const set = rawArgs[0].split(".")[0];
|
|
88
|
+
const publicClient = createPublicClient({ transport: http(ctx.conf.rpcUrl) });
|
|
89
|
+
const address = (await publicClient.readContract({
|
|
90
|
+
address: ctx.conf.contracts["SetRegistry"],
|
|
91
|
+
abi: abi.setContract,
|
|
92
|
+
functionName: "setContract",
|
|
93
|
+
args: [set],
|
|
94
|
+
}));
|
|
95
|
+
return { address: address, tag: ctx.contract, args: [] };
|
|
96
|
+
};
|
|
97
|
+
const setRegistryAdminCmdConfig = {
|
|
98
|
+
contract: "ISetRegistryAdmin",
|
|
99
|
+
nonFuncs: abi.nonFuncs.setRegistry,
|
|
100
|
+
cmdName: rstrip("Set"),
|
|
101
|
+
cmdAbi: function (txnAbi) {
|
|
102
|
+
return insertAbiParamAt(txnAbi, 0, {
|
|
103
|
+
name: "contract",
|
|
104
|
+
type: "address",
|
|
105
|
+
doc: "address of the set contract",
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
109
|
+
txnPrepare: async function (ctx) {
|
|
110
|
+
const raw = checkArguments(ctx.cmd.args, ctx.cmdAbi);
|
|
111
|
+
const address = raw[0];
|
|
112
|
+
const args = raw.slice(1);
|
|
113
|
+
return { address, tag: ctx.contract, args };
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
const objectMinterAdminCmdConfig = {
|
|
117
|
+
contract: "IObjectMinterAdmin",
|
|
118
|
+
nonFuncs: abi.nonFuncs.objectMinter,
|
|
119
|
+
cmdName: rstrip("MintPolicy"),
|
|
120
|
+
cmdAbi: function (txnAbi) {
|
|
121
|
+
return insertAbiParamAt(txnAbi, 0, {
|
|
122
|
+
name: "contract",
|
|
123
|
+
type: "address",
|
|
124
|
+
doc: "address of the contract",
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
txnPrepare: async function (ctx) {
|
|
128
|
+
const raw = checkArguments(ctx.cmd.args, ctx.cmdAbi);
|
|
129
|
+
const address = raw[0];
|
|
130
|
+
const args = raw.slice(1);
|
|
131
|
+
return { address, tag: ctx.contract, args };
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
function AbiToCommand(conf) {
|
|
135
|
+
return (txnAbi) => configureCommand(txnAbi, conf);
|
|
136
|
+
}
|
|
137
|
+
function byPreferredOrder(a, b) {
|
|
138
|
+
const ORDER_MAP = new Map("mint,register,update,upgrade,touch,transfer,relate,unrelate,owner,descriptor,elements,revision,sota,snapshot,status,admint,contract,rule,uri"
|
|
139
|
+
.split(",")
|
|
140
|
+
.map((name, index) => [name, index]));
|
|
141
|
+
const aIndex = ORDER_MAP.get(a.name()) ?? Infinity;
|
|
142
|
+
const bIndex = ORDER_MAP.get(b.name()) ?? Infinity;
|
|
143
|
+
return aIndex - bIndex;
|
|
144
|
+
}
|
package/dist/cmds.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Argument, Command, Option } from "commander";
|
|
2
|
+
import { createPublicClient, http, parseEventLogs, stringify } from "viem";
|
|
3
|
+
import { getUniverseConfig } from "./config.js";
|
|
4
|
+
import { checkArguments, getClients } from "./utils.js";
|
|
5
|
+
export function defaultReadFunctionOptions() {
|
|
6
|
+
const options = [];
|
|
7
|
+
options.push(new Option("-u, --universe <universe>", "universe name").default("local"));
|
|
8
|
+
options.push(new Option("--dryrun", "dry run"));
|
|
9
|
+
return options;
|
|
10
|
+
}
|
|
11
|
+
export function defaultWriteFunctionOptions() {
|
|
12
|
+
const options = [];
|
|
13
|
+
options.push(new Option("-u, --universe <universe>", "universe name").default("local"));
|
|
14
|
+
options.push(new Option("-k, --private-key <key>", "private key to sign the transaction"));
|
|
15
|
+
options.push(new Option("-a, --account <account>", "name of the keystore to sign the transaction"));
|
|
16
|
+
options.push(new Option("-p, --password [password]", "password to decrypt the keystore"));
|
|
17
|
+
options.push(new Option("-f, --password-file <file>", "file containing the password to decrypt the keystore"));
|
|
18
|
+
options.push(new Option("--foundry", "use keystore from Foundry directory (~/.foundry/keystores)"));
|
|
19
|
+
options.push(new Option("--dryrun", "dry run"));
|
|
20
|
+
return options;
|
|
21
|
+
}
|
|
22
|
+
const defaultConfig = {
|
|
23
|
+
cmdAbi: (txnAbi) => txnAbi,
|
|
24
|
+
cmdName: (cmdAbi) => cmdAbi.name,
|
|
25
|
+
cmdDescription: (cmdAbi) => cmdAbi._metadata?.notice || cmdAbi.name,
|
|
26
|
+
cmdOptions: (cmdAbi) => {
|
|
27
|
+
const read = cmdAbi.stateMutability == "view" || cmdAbi.stateMutability == "pure";
|
|
28
|
+
return read ? defaultReadFunctionOptions() : defaultWriteFunctionOptions();
|
|
29
|
+
},
|
|
30
|
+
cmdArguments: (cmdAbi) => cmdAbi.inputs.map((input) => {
|
|
31
|
+
const argDesc = cmdAbi._metadata?.params?.[input.name] || `${input.type} parameter`;
|
|
32
|
+
return new Argument(`<${input.name}>`, argDesc);
|
|
33
|
+
}),
|
|
34
|
+
cmdAction: async function (ctx) {
|
|
35
|
+
const isRead = ctx.cmdAbi.stateMutability == "view" || ctx.cmdAbi.stateMutability == "pure";
|
|
36
|
+
const opts = ctx.cmd.opts();
|
|
37
|
+
const args0 = ctx.cmd.args;
|
|
38
|
+
console.log({ args0 });
|
|
39
|
+
const { address, tag, args } = await ctx.txnPrepare(ctx);
|
|
40
|
+
const abi = [ctx.txnAbi, ...ctx.nonFuncs];
|
|
41
|
+
const functionName = ctx.txnAbi.name;
|
|
42
|
+
if (isRead) {
|
|
43
|
+
const publicClient = createPublicClient({ transport: http(ctx.conf.rpcUrl) });
|
|
44
|
+
const result = await publicClient.readContract({ address, abi, functionName, args });
|
|
45
|
+
console.log(`Result:`, result);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const { publicClient, walletClient } = await getClients(ctx.conf, opts);
|
|
49
|
+
const account = walletClient.account;
|
|
50
|
+
console.log({
|
|
51
|
+
isRead,
|
|
52
|
+
address: `${address} (${tag})`,
|
|
53
|
+
account: account?.address,
|
|
54
|
+
signature: ctx.txnAbi._metadata.signature,
|
|
55
|
+
args,
|
|
56
|
+
});
|
|
57
|
+
const { request } = await publicClient.simulateContract({ address, abi, functionName, args, account });
|
|
58
|
+
const hash = await walletClient.writeContract(request);
|
|
59
|
+
console.log(`Transaction sent: ${hash}`);
|
|
60
|
+
console.log("Transaction mining...");
|
|
61
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
62
|
+
console.log("Transaction mined");
|
|
63
|
+
if (receipt.logs && receipt.logs.length > 0) {
|
|
64
|
+
const parsedLogs = parseEventLogs({ abi, logs: receipt.logs });
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
parsedLogs.forEach((log) => {
|
|
67
|
+
console.log(" - Event", log.eventName, stringify(log.args));
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
console.log({ isRead, address: `${address} (${tag})`, signature: ctx.txnAbi._metadata.signature, args });
|
|
72
|
+
return;
|
|
73
|
+
},
|
|
74
|
+
txnPrepare: function (ctx) {
|
|
75
|
+
const args = checkArguments(ctx.cmd.args, ctx.cmdAbi);
|
|
76
|
+
return { address: ctx.conf.contracts[ctx.contract], tag: ctx.contract, args };
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
export function configureCommand(txnAbi, config) {
|
|
80
|
+
const cmdAbi = (config.cmdAbi || defaultConfig.cmdAbi)(txnAbi);
|
|
81
|
+
const name = (config.cmdName || defaultConfig.cmdName)(cmdAbi);
|
|
82
|
+
const desc = (config.cmdDescription || defaultConfig.cmdDescription)(cmdAbi);
|
|
83
|
+
const opts = (config.cmdOptions || defaultConfig.cmdOptions)(cmdAbi);
|
|
84
|
+
const args = (config.cmdArguments || defaultConfig.cmdArguments)(cmdAbi);
|
|
85
|
+
const action = async function () {
|
|
86
|
+
const ctx = {
|
|
87
|
+
conf: getUniverseConfig(this.opts()),
|
|
88
|
+
contract: config.contract,
|
|
89
|
+
cmdAbi,
|
|
90
|
+
txnAbi,
|
|
91
|
+
nonFuncs: config.nonFuncs,
|
|
92
|
+
cmd: this,
|
|
93
|
+
txnPrepare: config.txnPrepare || defaultConfig.txnPrepare,
|
|
94
|
+
};
|
|
95
|
+
await (config.cmdAction || defaultConfig.cmdAction)(ctx);
|
|
96
|
+
};
|
|
97
|
+
const cmd = new Command();
|
|
98
|
+
cmd.name(name);
|
|
99
|
+
cmd.description(desc);
|
|
100
|
+
opts.forEach((opt) => cmd.addOption(opt));
|
|
101
|
+
args.forEach((arg) => cmd.addArgument(arg));
|
|
102
|
+
cmd.action(action);
|
|
103
|
+
return cmd;
|
|
104
|
+
}
|
|
105
|
+
export class RenamingCommand extends Command {
|
|
106
|
+
nameCounts = new Map();
|
|
107
|
+
addCommand(cmd, opts) {
|
|
108
|
+
const originalName = cmd.name();
|
|
109
|
+
const uniqueName = this.getUniqueName(originalName);
|
|
110
|
+
if (originalName !== uniqueName) {
|
|
111
|
+
cmd.name(uniqueName);
|
|
112
|
+
}
|
|
113
|
+
return super.addCommand(cmd, opts);
|
|
114
|
+
}
|
|
115
|
+
addCommands(cmds) {
|
|
116
|
+
cmds.forEach((cmd) => this.addCommand(cmd));
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
resetCounts(name) {
|
|
120
|
+
if (name) {
|
|
121
|
+
this.nameCounts.delete(name);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
this.nameCounts.clear();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
getUniqueName(baseName) {
|
|
128
|
+
const count = this.nameCounts.get(baseName) || 0;
|
|
129
|
+
this.nameCounts.set(baseName, count + 1);
|
|
130
|
+
return count === 0 ? baseName : `${baseName}${count + 1}`;
|
|
131
|
+
}
|
|
132
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import { parse as parseTOML } from "@iarna/toml";
|
|
5
|
+
import { __dirname } from "./utils.js";
|
|
6
|
+
// Cache
|
|
7
|
+
let CONFIG_CACHED = null;
|
|
8
|
+
export function getUniverseConfig(opts) {
|
|
9
|
+
const config = loadProtocolConfig();
|
|
10
|
+
const universeName = opts.universe || "local";
|
|
11
|
+
const universe = config.universes[universeName];
|
|
12
|
+
if (!universe) {
|
|
13
|
+
const available = Object.keys(config.universes).join(", ");
|
|
14
|
+
throw new Error(`Universe "${universeName}" not found. Available: ${available}`);
|
|
15
|
+
}
|
|
16
|
+
return universe;
|
|
17
|
+
}
|
|
18
|
+
function loadProtocolConfig() {
|
|
19
|
+
if (CONFIG_CACHED)
|
|
20
|
+
return CONFIG_CACHED;
|
|
21
|
+
const configPaths = new Set([
|
|
22
|
+
path.resolve(__dirname, "../.every.toml"),
|
|
23
|
+
path.resolve(os.homedir(), ".every.toml"),
|
|
24
|
+
path.resolve(process.cwd(), ".every.toml"),
|
|
25
|
+
]);
|
|
26
|
+
const mergedConfig = {
|
|
27
|
+
universes: {},
|
|
28
|
+
};
|
|
29
|
+
for (const configPath of configPaths) {
|
|
30
|
+
if (!fs.existsSync(configPath))
|
|
31
|
+
continue;
|
|
32
|
+
try {
|
|
33
|
+
const raw = fs.readFileSync(configPath, "utf8");
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
const parsed = parseTOML(raw);
|
|
36
|
+
if (parsed.universes) {
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
+
for (const [name, uni] of Object.entries(parsed.universes)) {
|
|
39
|
+
// console.log(name, uni);
|
|
40
|
+
mergedConfig.universes[name] = {
|
|
41
|
+
name: name,
|
|
42
|
+
rpcUrl: uni.rpc_url,
|
|
43
|
+
contracts: uni.contracts || {},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
console.log(`Loaded configuration from ${configPath}`);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.warn(`Failed to load ${configPath}:`, err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (Object.keys(mergedConfig.universes).length === 0) {
|
|
54
|
+
console.warn("No universe configurations found");
|
|
55
|
+
}
|
|
56
|
+
CONFIG_CACHED = mergedConfig;
|
|
57
|
+
return CONFIG_CACHED;
|
|
58
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { generateCommands } from "./cmdgen.js";
|
|
4
|
+
import { RenamingCommand } from "./cmds.js";
|
|
5
|
+
import { version } from "./utils.js";
|
|
6
|
+
async function main() {
|
|
7
|
+
const subCmds = generateCommands();
|
|
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
|
+
}
|
|
42
|
+
}
|
|
43
|
+
await main();
|
package/dist/mint.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { defaultWriteFunctionOptions } from "./cmds.js";
|
|
3
|
+
import { getClients, stringify } from "./utils.js";
|
|
4
|
+
import { getUniverseConfig } from "./config.js";
|
|
5
|
+
import { parseEventLogs, parseUnits } from "viem";
|
|
6
|
+
import { abi } from "./abi.js";
|
|
7
|
+
export function genMintCommand() {
|
|
8
|
+
const cmd = new Command()
|
|
9
|
+
.name("mint")
|
|
10
|
+
.description("Mint an object via ObjectMinter")
|
|
11
|
+
.option("--auth <data>", "authorization data, if needed for a permissioned mint", "0x")
|
|
12
|
+
.option("--policy <index>", "the index number of the mint policy", "0")
|
|
13
|
+
.option("--value <amount>", "the amount of ETH to send together", "0")
|
|
14
|
+
.argument("<sid>", "scoped object ID, in form of set.id (e.g., 17.1)")
|
|
15
|
+
.argument("<to>", "address of the recipient")
|
|
16
|
+
.argument("[data]", "additional mint data", "0x")
|
|
17
|
+
.action(action);
|
|
18
|
+
defaultWriteFunctionOptions().forEach((option) => cmd.addOption(option));
|
|
19
|
+
return cmd;
|
|
20
|
+
}
|
|
21
|
+
async function action() {
|
|
22
|
+
const opts = this.opts();
|
|
23
|
+
const args0 = this.args;
|
|
24
|
+
const conf = getUniverseConfig(opts);
|
|
25
|
+
const objectMinter = conf.contracts["ObjectMinter"];
|
|
26
|
+
const setRegistry = conf.contracts["SetRegistry"];
|
|
27
|
+
const { publicClient, walletClient } = await getClients(conf, opts);
|
|
28
|
+
const account = walletClient.account;
|
|
29
|
+
const [set, id] = args0[0].split(".");
|
|
30
|
+
const setContract = (await publicClient.readContract({
|
|
31
|
+
address: setRegistry,
|
|
32
|
+
abi: abi.setContract,
|
|
33
|
+
functionName: "setContract",
|
|
34
|
+
args: [BigInt(set)],
|
|
35
|
+
}));
|
|
36
|
+
const value = parseUnits(opts.value || "0", 18);
|
|
37
|
+
const { request } = await publicClient.simulateContract({
|
|
38
|
+
address: objectMinter,
|
|
39
|
+
abi: abi.mint,
|
|
40
|
+
functionName: "mint",
|
|
41
|
+
args: [
|
|
42
|
+
args0[1],
|
|
43
|
+
setContract,
|
|
44
|
+
BigInt(id),
|
|
45
|
+
(args0[2] || "0x"),
|
|
46
|
+
opts.auth || "0x",
|
|
47
|
+
Number(opts.policy || "0"),
|
|
48
|
+
],
|
|
49
|
+
account,
|
|
50
|
+
value,
|
|
51
|
+
});
|
|
52
|
+
const hash = await walletClient.writeContract(request);
|
|
53
|
+
console.log(`Transaction sent: ${hash}`);
|
|
54
|
+
console.log("Transaction mining...");
|
|
55
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
56
|
+
console.log("Transaction mined");
|
|
57
|
+
if (receipt.logs && receipt.logs.length > 0) {
|
|
58
|
+
const parsedLogs = parseEventLogs({ abi: abi.mint, logs: receipt.logs });
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
+
parsedLogs.forEach((log) => {
|
|
61
|
+
console.log(" - Event", log.eventName, stringify(log.args));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
package/dist/relate.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { defaultWriteFunctionOptions } from "./cmds.js";
|
|
3
|
+
import { getClients, stringify } from "./utils.js";
|
|
4
|
+
import { getUniverseConfig } from "./config.js";
|
|
5
|
+
import { parseEventLogs } from "viem";
|
|
6
|
+
import { abi } from "./abi.js";
|
|
7
|
+
export function genRelateCommand() {
|
|
8
|
+
const cmd = new Command()
|
|
9
|
+
.name("relate")
|
|
10
|
+
.description("Link a tail object to a head object through a relation")
|
|
11
|
+
.argument("<tail>", "tail node, in form of [[data.]grant.]set.id")
|
|
12
|
+
.argument("<rel>", "relation ID")
|
|
13
|
+
.argument("<head>", "head node in form of [grant.]set.id, ")
|
|
14
|
+
.action(async function () {
|
|
15
|
+
await action(this, "relate");
|
|
16
|
+
});
|
|
17
|
+
defaultWriteFunctionOptions().forEach((option) => cmd.addOption(option));
|
|
18
|
+
return cmd;
|
|
19
|
+
}
|
|
20
|
+
export function genUnrelateCommand() {
|
|
21
|
+
const cmd = new Command()
|
|
22
|
+
.name("unrelate")
|
|
23
|
+
.description("Unlinks a tail object from a head object")
|
|
24
|
+
.argument("<tail>", "tail node, in form of [[data.]grant.]set.id")
|
|
25
|
+
.argument("<rel>", "relation ID")
|
|
26
|
+
.argument("<head>", "head node in form of [grant.]set.id, ")
|
|
27
|
+
.action(async function () {
|
|
28
|
+
await action(this, "unrelate");
|
|
29
|
+
});
|
|
30
|
+
defaultWriteFunctionOptions().forEach((option) => cmd.addOption(option));
|
|
31
|
+
return cmd;
|
|
32
|
+
}
|
|
33
|
+
async function action(cmd, functionName) {
|
|
34
|
+
const opts = cmd.opts();
|
|
35
|
+
const args0 = cmd.args;
|
|
36
|
+
const conf = getUniverseConfig(opts);
|
|
37
|
+
const address = conf.contracts["OmniRegistry"];
|
|
38
|
+
const tail = parseNode4(args0[0]);
|
|
39
|
+
const rel = BigInt(args0[1]);
|
|
40
|
+
const head = parseNode3(args0[2]);
|
|
41
|
+
await sendTransaction(conf, opts, address, abi.relation, functionName, [tail, rel, head]);
|
|
42
|
+
}
|
|
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 getClients(conf, opts);
|
|
45
|
+
const { request } = await publicClient.simulateContract({
|
|
46
|
+
address,
|
|
47
|
+
abi,
|
|
48
|
+
functionName: "relate",
|
|
49
|
+
args,
|
|
50
|
+
account: walletClient.account,
|
|
51
|
+
});
|
|
52
|
+
const hash = await walletClient.writeContract(request);
|
|
53
|
+
console.log(`Transaction sent: ${hash}`);
|
|
54
|
+
console.log("Transaction mining...");
|
|
55
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
56
|
+
console.log("Transaction mined");
|
|
57
|
+
if (receipt.logs && receipt.logs.length > 0) {
|
|
58
|
+
const parsedLogs = parseEventLogs({ abi: abi.relation, logs: receipt.logs });
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
+
parsedLogs.forEach((log) => {
|
|
61
|
+
console.log(" - Event", log.eventName, stringify(log.args));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function parseNode4(arg) {
|
|
66
|
+
const parts = arg.split(".");
|
|
67
|
+
let [data, grant, set, id] = [0n, 0n, 0n, 0n];
|
|
68
|
+
if (parts.length == 2) {
|
|
69
|
+
set = BigInt(parts[0]);
|
|
70
|
+
id = BigInt(parts[1]);
|
|
71
|
+
}
|
|
72
|
+
else if (parts.length == 3) {
|
|
73
|
+
grant = BigInt(parts[0]);
|
|
74
|
+
set = BigInt(parts[1]);
|
|
75
|
+
id = BigInt(parts[2]);
|
|
76
|
+
}
|
|
77
|
+
else if (parts.length == 4) {
|
|
78
|
+
data = BigInt(parts[0]);
|
|
79
|
+
grant = BigInt(parts[1]);
|
|
80
|
+
set = BigInt(parts[2]);
|
|
81
|
+
id = BigInt(parts[3]);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw new Error("");
|
|
85
|
+
}
|
|
86
|
+
return (data << 192n) | (grant << 128n) | (set << 64n) | id;
|
|
87
|
+
}
|
|
88
|
+
function parseNode3(arg) {
|
|
89
|
+
const parts = arg.split(".");
|
|
90
|
+
let [grant, set, id] = [0n, 0n, 0n];
|
|
91
|
+
if (parts.length == 2) {
|
|
92
|
+
set = BigInt(parts[0]);
|
|
93
|
+
id = BigInt(parts[1]);
|
|
94
|
+
}
|
|
95
|
+
else if (parts.length == 3) {
|
|
96
|
+
grant = BigInt(parts[0]);
|
|
97
|
+
set = BigInt(parts[1]);
|
|
98
|
+
id = BigInt(parts[2]);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
throw new Error("");
|
|
102
|
+
}
|
|
103
|
+
return (grant << 128n) | (set << 64n) | id;
|
|
104
|
+
}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createPublicClient, createWalletClient, encodeAbiParameters, http } from "viem";
|
|
2
|
+
import { formatAbiParameter } from "abitype";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { Wallet } from "ethers";
|
|
6
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
7
|
+
import promptSync from "prompt-sync";
|
|
8
|
+
import os from "os";
|
|
9
|
+
import JSON5 from "json5";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
export const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
export function version() {
|
|
13
|
+
const pkgPath = path.resolve(__dirname, "../package.json");
|
|
14
|
+
return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
|
|
15
|
+
}
|
|
16
|
+
export function lstrip(prefix) {
|
|
17
|
+
return function (func) {
|
|
18
|
+
return func.name.startsWith(prefix) ? func.name.substring(prefix.length).toLowerCase() : func.name;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function rstrip(postfix) {
|
|
22
|
+
return function (func) {
|
|
23
|
+
return func.name.endsWith(postfix) ? func.name.slice(0, -postfix.length) : func.name;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function startsWith(prefix) {
|
|
27
|
+
return function (func) {
|
|
28
|
+
return func.name.startsWith(prefix);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function excludes(names) {
|
|
32
|
+
return function (func) {
|
|
33
|
+
return !names.includes(func.name);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function includes(names) {
|
|
37
|
+
return function (f) {
|
|
38
|
+
return names.includes(f.name);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function stringify(o) {
|
|
42
|
+
const replacer = (_key, value) => (typeof value === "bigint" ? value.toString() : value);
|
|
43
|
+
return JSON5.stringify(o, replacer);
|
|
44
|
+
}
|
|
45
|
+
export async function getClients(uniConf, opts) {
|
|
46
|
+
const transport = http(uniConf.rpcUrl);
|
|
47
|
+
const publicClient = createPublicClient({ transport });
|
|
48
|
+
const privateKey = await readPrivateKey(opts);
|
|
49
|
+
const account = privateKeyToAccount(privateKey);
|
|
50
|
+
const walletClient = createWalletClient({ account, transport });
|
|
51
|
+
return { publicClient, walletClient };
|
|
52
|
+
}
|
|
53
|
+
export async function readPrivateKey(opts) {
|
|
54
|
+
if (opts.privateKey) {
|
|
55
|
+
return opts.privateKey.startsWith("0x") ? opts.privateKey : `0x${opts.privateKey}`;
|
|
56
|
+
}
|
|
57
|
+
else if (opts.account) {
|
|
58
|
+
const keystorePath = path.join(os.homedir(), opts.foundry ? ".foundry" : ".every", "keystores", opts.account);
|
|
59
|
+
const keystore = JSON.parse(fs.readFileSync(keystorePath, "utf8"));
|
|
60
|
+
const password = opts.password
|
|
61
|
+
? opts.password
|
|
62
|
+
: opts.passwordFile
|
|
63
|
+
? fs.readFileSync(opts.passwordFile, "utf8").trim()
|
|
64
|
+
: keystore.crypto
|
|
65
|
+
? promptSync({ sigint: true })("Enter password to decrypt keystore: ", { echo: "" })
|
|
66
|
+
: undefined;
|
|
67
|
+
const wallet = await Wallet.fromEncryptedJson(JSON.stringify(keystore), password);
|
|
68
|
+
return wallet.privateKey;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
throw new Error(`--account or --private-key not specified`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
export function checkArguments(raw, func) {
|
|
76
|
+
return raw.map((rawArg, index) => {
|
|
77
|
+
const abiParam = func.inputs[index];
|
|
78
|
+
const pt = abiParam?.type;
|
|
79
|
+
const arg = pt === "address" || pt.startsWith("bytes") || pt === "string" ? rawArg : JSON5.parse(rawArg);
|
|
80
|
+
try {
|
|
81
|
+
encodeAbiParameters([abiParam], [arg]);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
if (e instanceof Error) {
|
|
85
|
+
throw new Error(`invalid param ${formatAbiParameter(abiParam)}\n${e.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return arg;
|
|
89
|
+
});
|
|
90
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@everyprotocol/every-cli",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist/",
|
|
7
|
+
"abis/",
|
|
8
|
+
".every.toml",
|
|
9
|
+
"LICENSE",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"bin": {
|
|
13
|
+
"every": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@eslint/js": "^9.26.0",
|
|
17
|
+
"@types/bun": "latest",
|
|
18
|
+
"@types/lodash-es": "^4.17.12",
|
|
19
|
+
"@types/prompt-sync": "^4.2.3",
|
|
20
|
+
"@typescript-eslint/eslint-plugin": "^8.32.1",
|
|
21
|
+
"@typescript-eslint/parser": "^8.32.1",
|
|
22
|
+
"eslint": "^9.26.0",
|
|
23
|
+
"globals": "^16.1.0",
|
|
24
|
+
"typescript-eslint": "^8.32.1"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"typescript": "^5.8.3"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@iarna/toml": "^2.2.5",
|
|
31
|
+
"commander": "^13.1.0",
|
|
32
|
+
"ethers": "^6.14.0",
|
|
33
|
+
"json5": "^2.2.3",
|
|
34
|
+
"lodash-es": "^4.17.21",
|
|
35
|
+
"prompt-sync": "^4.2.0",
|
|
36
|
+
"viem": "^2.29.1"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "npm run tsc -b",
|
|
40
|
+
"clean": "rm -rf dist",
|
|
41
|
+
"bb": "bun build src/*.ts --target=node --outdir dist",
|
|
42
|
+
"prepublishOnly": "npm run build"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
}
|
|
47
|
+
}
|