@everyprotocol/every-cli 0.1.6 → 0.1.8

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/dist/cmdgen.js CHANGED
@@ -10,7 +10,7 @@ const getReadAction = (config, funName, abiFunc, abi) => async function readActi
10
10
  const opts = this.opts();
11
11
  const conf = FromOpts.getUniverseConfig(opts);
12
12
  const address = await config.getContract(conf, this.processedArgs, abiFunc);
13
- const args = (config.getFuncArgs ?? CommandGenDefaults.getFuncArgs)(this.processedArgs, abiFunc);
13
+ const args = (config.getFuncArgs ?? CommandGenDefaults.getFuncArgs)(this.processedArgs, abiFunc, opts);
14
14
  const console = new Logger(opts);
15
15
  const publicClient = createPublicClient({ transport: http(conf.rpc) });
16
16
  const result = await publicClient.readContract({ address, abi, functionName: funName, args });
@@ -20,7 +20,7 @@ const getReadAction = (config, funName, abiFunc, abi) => async function readActi
20
20
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
21
  const getWriteAction = (config, funcName, abiFunc, abi) => async function writeAction() {
22
22
  const opts = this.opts();
23
- const args = (config.getFuncArgs ?? CommandGenDefaults.getFuncArgs)(this.args, abiFunc);
23
+ const args = (config.getFuncArgs ?? CommandGenDefaults.getFuncArgs)(this.args, abiFunc, opts);
24
24
  const { publicClient, walletClient, conf } = await FromOpts.toWriteEthereum(opts);
25
25
  const address = config.getContract(conf, this.args, abiFunc);
26
26
  const account = walletClient.account;
@@ -36,7 +36,8 @@ export const getCommandGen = (config) => function genCmd(cmdName) {
36
36
  const abiFuncDoc = abiFuncs[0];
37
37
  const description = abiFuncDoc._metadata?.notice || "";
38
38
  const isRead = abiFuncDoc.stateMutability == "view" || abiFuncDoc.stateMutability == "pure";
39
- const options = isRead ? [universe, ...outputOptions] : [...writeOptions, ...outputOptions];
39
+ // const options = isRead ? [universe, ...outputOptions] : [...writeOptions, ...outputOptions];
40
+ const options = (config.getCmdOpts ?? CommandGenDefaults.getCmdOpts)(abiFuncDoc);
40
41
  const args = (config.getCmdArgs ?? CommandGenDefaults.getCmdArgs)(abiFuncDoc);
41
42
  const abiContract = [...abiFuncs, ...abiNonFuncs];
42
43
  const action = isRead
@@ -48,7 +49,12 @@ export function makeFuncName(cmdName, prefix) {
48
49
  return `${prefix}${cmdName[0].toUpperCase()}${cmdName.slice(1)}`;
49
50
  }
50
51
  export const CommandGenDefaults = {
51
- getFuncArgs: (args, abiFunc) => args.map((arg, i) => coerceValue(arg, abiFunc.inputs[i])),
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
53
+ getFuncArgs: (args, abiFunc, opts) => args.map((arg, i) => coerceValue(arg, abiFunc.inputs[i])),
54
+ getCmdOpts: (abiFunc) => {
55
+ const isRead = abiFunc.stateMutability == "view" || abiFunc.stateMutability == "pure";
56
+ return isRead ? [universe, ...outputOptions] : [...writeOptions, ...outputOptions];
57
+ },
52
58
  getCmdArgs: (abiFunc) => abiFunc.inputs.map((input) => {
53
59
  const desc = abiFunc._metadata?.params?.[input.name] || `${input.type} parameter`;
54
60
  return new Argument(`<${input.name}>`, desc);
package/dist/cmds/kind.js CHANGED
@@ -1,14 +1,132 @@
1
- import { Command } from "commander";
1
+ import { Argument, Command, Option } from "commander";
2
2
  import { abi } from "../abi.js";
3
3
  import { getCommandGen, makeFuncName } from "../cmdgen.js";
4
- const cmdGenConfig = {
4
+ import { toHex } from "viem";
5
+ import { coerceValue, readKindElements, toElementType, toRelationId } from "../utils.js";
6
+ import { outputOptions, writeOptions } from "../commander-patch.js";
7
+ import { submitSimulation } from "../ethereum.js";
8
+ import { Logger } from "../logger.js";
9
+ import { FromOpts } from "../from-opts.js";
10
+ const genKindCmd = getCommandGen({
5
11
  getFuncName: (cmdName) => makeFuncName(cmdName, `kind`),
6
12
  getAbiFuncs: (funcName) => abi.funcs.kindRegistry.filter((i) => i.name == funcName),
7
13
  // eslint-disable-next-line
8
14
  getAbiNonFuncs: (funcName) => abi.nonFuncs.kindRegistry,
9
15
  // eslint-disable-next-line
10
16
  getContract: (conf, args, abiFunc) => conf.contracts.KindRegistry,
11
- };
12
- const cmdGen = getCommandGen(cmdGenConfig);
13
- const subCmds = "register,upgrade,touch,transfer,owner,descriptor,snapshot".split(",");
14
- export const kindCmd = new Command("kind").description("manage kinds").addCommands(subCmds.map(cmdGen));
17
+ });
18
+ const registerCmd = genRegisterCmd();
19
+ const updateCmd = genUpdateCmd();
20
+ const otherCmds = "upgrade,touch,transfer,owner,descriptor,snapshot".split(",").map(genKindCmd);
21
+ export const kindCmd = new Command("kind")
22
+ .description("manage kinds")
23
+ .addCommand(registerCmd)
24
+ .addCommand(updateCmd)
25
+ .addCommands(otherCmds);
26
+ function genRegisterCmd() {
27
+ const funcName = "kindRegister";
28
+ const abiFuncs = abi.funcs.kindRegistry.filter((i) => i.name == funcName);
29
+ const abiNonFuncs = abi.nonFuncs.kindRegistry;
30
+ const code = new Argument("<code>", "Matter hash of the kind code");
31
+ const data = new Argument("<data>", "Matter hash of the kind data");
32
+ const etys = new Option("--elements <ety...>", "Element types");
33
+ const rels = new Option("--relations <rel...>", "IDs of relations supported");
34
+ const options = [etys, rels, ...writeOptions, ...outputOptions];
35
+ const args = [code, data];
36
+ function resolveElementTypes(args) {
37
+ return args.length == 0
38
+ ? []
39
+ : args.length == 1 && /\.wasm$/i.test(args[0])
40
+ ? readKindElements(args[0])
41
+ : args.map(toElementType);
42
+ }
43
+ function getFuncArgs(cmd) {
44
+ const opts = cmd.opts();
45
+ const etys = resolveElementTypes(opts.elements ?? []);
46
+ const rels = (opts.relations ?? []).map(toRelationId);
47
+ const args = cmd.args.map((arg, i) => coerceValue(arg, abiFuncs[0].inputs[i]));
48
+ args.push(etys, rels);
49
+ return args;
50
+ }
51
+ async function getCmdAction(cmd) {
52
+ const opts = cmd.opts();
53
+ const args = getFuncArgs(cmd);
54
+ const { publicClient, walletClient, conf } = await FromOpts.toWriteEthereum(opts);
55
+ const address = conf.contracts.KindRegistry;
56
+ const account = walletClient.account;
57
+ const simulation = {
58
+ address,
59
+ abi: [...abiFuncs, ...abiNonFuncs],
60
+ functionName: funcName,
61
+ args,
62
+ account,
63
+ };
64
+ await submitSimulation(simulation, publicClient, walletClient, new Logger(opts));
65
+ }
66
+ return new Command("register")
67
+ .description("Register a new kind")
68
+ .addOptions(options)
69
+ .addArguments(args)
70
+ .action(getCmdAction);
71
+ }
72
+ function genUpdateCmd() {
73
+ const funcName = "kindUpdate";
74
+ const abiFuncs = abi.funcs.kindRegistry.filter((i) => i.name == funcName);
75
+ const abiNonFuncs = abi.nonFuncs.kindRegistry;
76
+ const code = new Option("--code <code>", "Matter hash of the kind code");
77
+ const data = new Option("--data <data>", "Matter hash of the kind data");
78
+ const rels = new Option("--relations [rel...]", "IDs of relations supported");
79
+ const id = new Argument(`<id>`, "Kind id");
80
+ const options = [code, data, rels, ...writeOptions, ...outputOptions];
81
+ const args = [id];
82
+ function getFuncArgs(cmd) {
83
+ const opts = cmd.opts();
84
+ const args = [cmd.args[0]];
85
+ const ZERO32 = toHex(0, { size: 32 });
86
+ let sig;
87
+ if (opts.relations) {
88
+ if (opts.code || opts.data) {
89
+ sig = "kindUpdate(uint64,bytes32,bytes32,uint64[])";
90
+ args.push(opts.code ?? ZERO32);
91
+ args.push(opts.data ?? ZERO32);
92
+ args.push(typeof opts.relations == "boolean" ? [] : opts.relations);
93
+ }
94
+ else {
95
+ sig = "kindUpdate(uint64,uint64[])";
96
+ args.push(typeof opts.relations == "boolean" ? [] : opts.relations);
97
+ }
98
+ }
99
+ else {
100
+ if (opts.code || opts.data) {
101
+ sig = "kindUpdate(uint64,bytes32,bytes32)";
102
+ args.push(opts.code ?? ZERO32);
103
+ args.push(opts.data ?? ZERO32);
104
+ }
105
+ else {
106
+ throw new Error("");
107
+ }
108
+ }
109
+ const abiFunc = abiFuncs.filter((f) => f._metadata.signature == sig)[0];
110
+ return [args, abiFunc];
111
+ }
112
+ async function getCmdAction(cmd) {
113
+ const opts = cmd.opts();
114
+ const [args, abiFunc] = getFuncArgs(cmd);
115
+ const { publicClient, walletClient, conf } = await FromOpts.toWriteEthereum(opts);
116
+ const address = conf.contracts.KindRegistry;
117
+ const account = walletClient.account;
118
+ const simulation = {
119
+ address,
120
+ abi: [abiFunc, ...abiNonFuncs],
121
+ functionName: "kindUpdate",
122
+ args,
123
+ account,
124
+ };
125
+ await submitSimulation(simulation, publicClient, walletClient, new Logger(opts));
126
+ }
127
+ return new Command("update")
128
+ .description("Update an existing kind")
129
+ .addOptions(options)
130
+ .addArguments(args)
131
+ .action(getCmdAction);
132
+ }
@@ -229,13 +229,24 @@ async function compileEnumMatter(file) {
229
229
  throw new Error(`Invalid column element type: ${h}`);
230
230
  }
231
231
  };
232
+ function resolvePath(v) {
233
+ if (path.isAbsolute(v))
234
+ return v;
235
+ const dir = path.dirname(file);
236
+ if (path.isAbsolute(file)) {
237
+ return path.resolve(dir, v);
238
+ }
239
+ else {
240
+ return path.normalize(path.join(dir, v));
241
+ }
242
+ }
232
243
  const mapValues = ({ header, value }) => {
233
244
  const v = value.trim();
234
245
  if (v.startsWith("0x")) {
235
246
  return fromHex(pad(v, { size: 32, dir: "left" }), "bytes");
236
247
  }
237
248
  else if (header == "IMAGE" || header == "JSON") {
238
- const { hash, mime, form, path } = computeHashMemo(v);
249
+ const { hash, mime, form, path } = computeHashMemo(resolvePath(v));
239
250
  if (!deps.has(path)) {
240
251
  deps.set(path, { hash, mime, form, path });
241
252
  }
@@ -261,7 +272,7 @@ async function compileEnumMatter(file) {
261
272
  .on("end", () => {
262
273
  const auxTypes = Object.keys(aux ?? {});
263
274
  const auxValues = Object.values(aux ?? {});
264
- const enumHeader = buildEnumHeader(auxTypes, colTypes, rows);
275
+ const enumHeader = buildEnumHeader(auxTypes ?? [], colTypes ?? [], rows);
265
276
  resolve(Buffer.concat([enumHeader, ...auxValues, ...content]));
266
277
  })
267
278
  .on("error", (e /* eslint-disable-line */) => {
@@ -318,22 +329,24 @@ function computeHashFile(file) {
318
329
  * [16..31] = col_types [u8; 16]
319
330
  */
320
331
  function buildEnumHeader(auxTypes, colTypes, rows = 0) {
321
- if (colTypes.length === 0 && auxTypes.length === 0) {
332
+ const colTypesLen = colTypes.length;
333
+ const auxTypesLen = auxTypes.length;
334
+ if (colTypesLen + auxTypesLen == 0) {
322
335
  throw new Error("Empty header list");
323
336
  }
324
- if (auxTypes.length > 8) {
325
- throw new Error(`Too many aux types (max 8): ${auxTypes.length}`);
337
+ if (auxTypesLen > 8) {
338
+ throw new Error(`Too many aux types (max 8): ${auxTypesLen}`);
326
339
  }
327
- if (colTypes.length > 16) {
328
- throw new Error(`Too many column types (max 16): ${colTypes.length}`);
340
+ if (colTypesLen > 16) {
341
+ throw new Error(`Too many column types (max 16): ${colTypesLen}`);
329
342
  }
330
343
  if (rows < 0 || rows > 0xffff) {
331
344
  throw new Error(`Rows out of range: ${rows}`);
332
345
  }
333
346
  const buf = new Uint8Array(32);
334
347
  buf.set([0x45, 0x4e, 0x55, 0x4d], 0); // magic "ENUM"
335
- buf[4] = (0x01 << 4) | (auxTypes.length & 0x0f); // version(hi4), aux(lo4)
336
- buf[5] = colTypes.length; // cols
348
+ buf[4] = (0x01 << 4) | (auxTypesLen & 0x0f); // version(hi4), aux(lo4)
349
+ buf[5] = colTypesLen; // cols
337
350
  buf[6] = rows & 0xff; // rows.lo
338
351
  buf[7] = (rows >>> 8) & 0xff; // rows.hi
339
352
  auxTypes.forEach((t, i) => (buf[8 + i] = formByName(t))); // aux types
package/dist/cmds/pick.js CHANGED
@@ -3,14 +3,22 @@ import path from "node:path";
3
3
  import jp from "jsonpath";
4
4
  import * as _ from "lodash-es";
5
5
  import { Command } from "commander";
6
+ import { loadMergedConfig } from "../config.js";
6
7
  const DEFAULT_DIR = "./register";
8
+ const DEFAULT_UNIVERSE = "anvil";
7
9
  const STATIC_RULES = {
10
+ // config
11
+ "universe.id": { file: "config://universe.json", root: "$", field: "id" },
12
+ "universe.sreg": { file: "config://universe.json", root: "$", field: "contracts.SetRegistry" },
13
+ "universe.oreg": { file: "config://universe.json", root: "$", field: "contracts.OmniRegistry" },
14
+ "universe.kreg": { file: "config://universe.json", root: "$", field: "contracts.KindRegistry" },
15
+ "universe.ereg": { file: "config://universe.json", root: "$", field: "contracts.ElementRegistry" },
16
+ "universe.minter": { file: "config://universe.json", root: "$", field: "contracts.ObjectMinter" },
17
+ "universe.rpc": { file: "config://universe.json", root: "$", field: "rpc" },
18
+ "universe.observer": { file: "config://universe.json", root: "$", field: "observer" },
8
19
  // contract
9
- "deploy.addr": {
10
- file: "deploy.json",
11
- root: "$",
12
- field: "deployedTo",
13
- },
20
+ "deploy.addr": { file: "deploy.json", root: "$", field: "deployedTo" },
21
+ "contract.addr": { file: "contract.json", root: "$", field: "deployedTo" },
14
22
  // kind
15
23
  "kind.id": { file: "kind.json", root: "$.events[?(@.name=='KindRegistered')].data", field: "id" },
16
24
  "kind.rev": { file: "kind.json", root: "$.events[?(@.name=='KindRegistered')].data.desc", field: "rev" },
@@ -99,13 +107,24 @@ export const pickCmd = new Command("pick")
99
107
  .description("Pick values from outputs")
100
108
  .argument("<keys...>", "Keys to resolve")
101
109
  .option("--from <dir>", "Output directory", DEFAULT_DIR)
110
+ .option("-u, --universe <name>", "Universe name", DEFAULT_UNIVERSE)
102
111
  .action(function (keys) {
103
- const { from } = this.opts();
104
- const dir = from || DEFAULT_DIR;
112
+ const { from, universe } = this.opts();
105
113
  const loadJsonCache = _.memoize((rel) => {
106
- const full = path.join(dir, rel);
114
+ if (rel == "config://universe.json") {
115
+ return loadUniverseConfig(universe);
116
+ }
117
+ const full = path.join(from, rel);
107
118
  return loadJson(full);
108
119
  });
109
120
  const outputs = keys.map((key) => pickOnce(loadJsonCache, key));
110
121
  console.log(outputs.join(" "));
111
122
  });
123
+ function loadUniverseConfig(universe) {
124
+ const config = loadMergedConfig();
125
+ const uniConf = config.universes?.[universe];
126
+ if (!uniConf) {
127
+ throw new Error(`config for universe ${universe} not found`);
128
+ }
129
+ return uniConf;
130
+ }
@@ -7,11 +7,10 @@ export const privateKey = new Option("-k, --private-key <key>", "Private key to
7
7
  export const foundry = new Option("-f, --foundry", "Use foundry keystores (~/.foundry/keystores)");
8
8
  export const universe = new Option("-u, --universe <universe>", "Universe name").default("anvil");
9
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");
10
+ export const json = new Option("-j, --json [file]", "Output result as JSON to stdout or file");
11
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
12
  export const keystoreOptions = [account, password, passwordFile, foundry];
14
- export const outputOptions = [json, quiet, noQuiet];
13
+ export const outputOptions = [json, quiet];
15
14
  export const writeOptions = [universe, account, password, passwordFile, foundry];
16
15
  Command.prototype.addCommands = function (commands) {
17
16
  commands.forEach((cmd) => this.addCommand(cmd));
@@ -29,7 +28,7 @@ Command.prototype.addKeystoreOptions = function () {
29
28
  return this.addOptions([account, password, passwordFile, foundry]);
30
29
  };
31
30
  Command.prototype.addOutputOptions = function () {
32
- return this.addOptions([json, quiet, noQuiet]);
31
+ return this.addOptions([json, quiet]);
33
32
  };
34
33
  Command.prototype.addWriteOptions = function () {
35
34
  return this.addKeystoreOptions().addOption(universe);
package/dist/logger.js CHANGED
@@ -5,7 +5,7 @@ export class Logger {
5
5
  quiet;
6
6
  json;
7
7
  constructor(opts = {}) {
8
- this.quiet = opts.quiet ?? !!opts.json;
8
+ this.quiet = opts.quiet ?? false;
9
9
  this.json = opts.json ?? false;
10
10
  }
11
11
  log(...args) {
package/dist/utils.js CHANGED
@@ -167,3 +167,26 @@ export function loadBinary(file) {
167
167
  const buf = fs.readFileSync(file);
168
168
  return buf;
169
169
  }
170
+ export function readKindElements(file) {
171
+ const buf = fs.readFileSync(file);
172
+ const mod = new WebAssembly.Module(buf); // sync
173
+ const [section] = WebAssembly.Module.customSections(mod, "kindelms");
174
+ if (!section)
175
+ throw new Error(`Section "kindelms" not found in ${file}`);
176
+ return Array.from(new Uint8Array(section));
177
+ }
178
+ export function toElementType(v) {
179
+ const n = typeof v === "number" ? v : v.startsWith("0x") ? Number.parseInt(v.slice(2), 16) : Number.parseInt(v, 10);
180
+ if (!Number.isInteger(n) || n < 1 || n > 255) {
181
+ throw new Error(`Invalid element type: ${v} (must be 1–255)`);
182
+ }
183
+ return n;
184
+ }
185
+ export function toRelationId(v) {
186
+ const n = typeof v === "bigint" ? v : BigInt(v);
187
+ const U48_MAX = (1n << 48n) - 1n;
188
+ if (n < 1n || n > U48_MAX) {
189
+ throw new Error(`Invalid relation id: ${String(v)} (must be between 1 and ${U48_MAX})`);
190
+ }
191
+ return n;
192
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@everyprotocol/every-cli",
3
3
  "type": "module",
4
- "version": "0.1.6",
4
+ "version": "0.1.8",
5
5
  "files": [
6
6
  "dist/",
7
7
  "abis/",