@everyprotocol/every-cli 0.1.4 → 0.1.6
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 +4 -4
- package/dist/cmdgen.js +2 -2
- package/dist/cmds/matter.js +375 -25
- package/dist/cmds/object.js +2 -2
- package/dist/cmds/pick.js +111 -0
- package/dist/index.js +1 -1
- package/dist/parsers.js +11 -0
- package/dist/program.js +3 -1
- package/dist/utils.js +1 -16
- package/package.json +4 -1
package/.every.toml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[universes.anvil]
|
|
2
2
|
id = 31337
|
|
3
|
-
rpc = "http://
|
|
4
|
-
explorer = "http://127.0.0.1:8545"
|
|
3
|
+
rpc = "http://localhost:8545"
|
|
5
4
|
observer = "dev"
|
|
5
|
+
explorer = "http://localhost"
|
|
6
6
|
|
|
7
7
|
[universes.anvil.contracts]
|
|
8
8
|
SetRegistry = "0x854C35Fd2b65fE9fcE71dddE91De8c3e1A7Dc8Ae"
|
|
@@ -13,5 +13,5 @@ ObjectMinter = "0x12CaBC370b316F247126F3Fab529Ee25e03aE226"
|
|
|
13
13
|
|
|
14
14
|
[observers.dev]
|
|
15
15
|
rpc = "ws://localhost:9944"
|
|
16
|
-
gateway = "http://
|
|
17
|
-
explorer = "
|
|
16
|
+
gateway = "http://every.lo"
|
|
17
|
+
explorer = "https://portal.every.fun/?rpc=ws://localhost:9944#/explorer"
|
package/dist/cmdgen.js
CHANGED
|
@@ -9,8 +9,8 @@ import { coerceValue } from "./utils.js";
|
|
|
9
9
|
const getReadAction = (config, funName, abiFunc, abi) => async function readAction() {
|
|
10
10
|
const opts = this.opts();
|
|
11
11
|
const conf = FromOpts.getUniverseConfig(opts);
|
|
12
|
-
const address = await config.getContract(conf, this.
|
|
13
|
-
const args = (config.getFuncArgs ?? CommandGenDefaults.getFuncArgs)(this.
|
|
12
|
+
const address = await config.getContract(conf, this.processedArgs, abiFunc);
|
|
13
|
+
const args = (config.getFuncArgs ?? CommandGenDefaults.getFuncArgs)(this.processedArgs, abiFunc);
|
|
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 });
|
package/dist/cmds/matter.js
CHANGED
|
@@ -3,31 +3,38 @@ import "@polkadot/api-augment/substrate";
|
|
|
3
3
|
import * as fs from "fs";
|
|
4
4
|
import * as JSON11 from "json11";
|
|
5
5
|
import columify from "columnify";
|
|
6
|
-
import {
|
|
6
|
+
import { loadBinary, loadJson } from "../utils.js";
|
|
7
7
|
import { submitTransaction } from "../substrate.js";
|
|
8
8
|
import { network } from "../commander-patch.js";
|
|
9
9
|
import { Logger } from "../logger.js";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import csv from "csv-parser";
|
|
12
|
+
import { fromHex, pad, sha256 } from "viem";
|
|
13
|
+
import { memoize } from "lodash-es";
|
|
10
14
|
const matterRegisterCmd = new Command("register")
|
|
11
|
-
.description("Register
|
|
15
|
+
.description("Register matters")
|
|
12
16
|
.argument("<files...>", "Paths of matter blob files")
|
|
13
|
-
.option("--mime <string>", "Matter mime")
|
|
14
|
-
.option("--form <number>", "Matter form", "1")
|
|
15
17
|
.addOption(network)
|
|
16
18
|
.addKeystoreOptions()
|
|
17
19
|
.addOutputOptions()
|
|
18
20
|
.subWriteAction(async function (api, pair, files) {
|
|
19
21
|
const opts = this.opts();
|
|
20
22
|
const console = new Logger(opts);
|
|
21
|
-
const
|
|
23
|
+
const deduper = new Set();
|
|
24
|
+
const inputs = [];
|
|
22
25
|
for (const file of files) {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
const spec = parseFileSpec(file);
|
|
27
|
+
const mi = await toRegisterInput(spec);
|
|
28
|
+
mi.forEach((input) => {
|
|
29
|
+
if (!deduper.has(input.path)) {
|
|
30
|
+
inputs.push(input);
|
|
31
|
+
deduper.add(input.path);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
27
34
|
}
|
|
28
35
|
const txns = [];
|
|
29
|
-
for (const {
|
|
30
|
-
const content = fs.readFileSync(filePath);
|
|
36
|
+
for (const { blob, form, mime, path: filePath } of inputs) {
|
|
37
|
+
const content = blob || fs.readFileSync(filePath);
|
|
31
38
|
console.log(`Register matter: form=${form} mime=${mime} blob=${content.length}B ${filePath}`);
|
|
32
39
|
const contentRaw = api.createType("Raw", content, content.length);
|
|
33
40
|
const call = api.tx.every.matterRegister(form, mime, contentRaw);
|
|
@@ -44,7 +51,9 @@ const matterRegisterCmd = new Command("register")
|
|
|
44
51
|
const header = await api.rpc.chain.getHeader(r.blockHash);
|
|
45
52
|
console.log(`Transaction confirmed: ${txn.txHash} ${filePath}`);
|
|
46
53
|
console.log(`Confirmed in: block ${header.number}, hash ${header.hash}`);
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
55
|
const events = r.events.map((e) => [e.event.method, JSON11.stringify(e.event.data.toJSON())]);
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
57
|
const receipt = r.events.map((e) => ({ event: e.event.method, data: e.event.data.toJSON() }));
|
|
49
58
|
console.log(columify(events, { showHeaders: false }));
|
|
50
59
|
result.push({
|
|
@@ -55,28 +64,369 @@ const matterRegisterCmd = new Command("register")
|
|
|
55
64
|
}
|
|
56
65
|
console.result(result);
|
|
57
66
|
});
|
|
67
|
+
const matterCompileCmd = new Command("compile")
|
|
68
|
+
.description("Compile matters from human-readable to binary")
|
|
69
|
+
.argument("<files...>", "Paths to matter files")
|
|
70
|
+
.option("-o, --out <dir>", "Output directory")
|
|
71
|
+
.addOutputOptions()
|
|
72
|
+
.action(async function (files) {
|
|
73
|
+
const opts = this.opts();
|
|
74
|
+
const console = new Logger(opts);
|
|
75
|
+
const specs = files.map((f) => parseFileSpec(f));
|
|
76
|
+
if (specs.find((spec) => spec.type != "raw")) {
|
|
77
|
+
throw new Error("invalid input, only .enum.csv or .perm.csv files supported");
|
|
78
|
+
}
|
|
79
|
+
for (const spec of specs) {
|
|
80
|
+
const { binFile, depsFile, outDir, compile } = getOutFiles(spec.path, opts.out);
|
|
81
|
+
const { blob, deps } = await compile(spec.path);
|
|
82
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
83
|
+
fs.writeFileSync(binFile, blob, "binary");
|
|
84
|
+
console.log(`blob saved to ${binFile}`);
|
|
85
|
+
fs.writeFileSync(depsFile, JSON.stringify(deps));
|
|
86
|
+
console.log(`deps saved to ${depsFile}`);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
58
89
|
const matterHashCmd = new Command("hash")
|
|
59
|
-
.description("
|
|
90
|
+
.description("Compute matter hashes")
|
|
60
91
|
.argument("<files...>", "Paths of matter blob files")
|
|
61
|
-
.option("--mime <string>", "Matter mime")
|
|
62
|
-
.option("--form <number>", "Matter form", "1")
|
|
63
92
|
.addOutputOptions()
|
|
64
93
|
.action(async function (files) {
|
|
65
94
|
const opts = this.opts();
|
|
66
95
|
const console = new Logger(opts);
|
|
67
|
-
const
|
|
96
|
+
const seen = new Set();
|
|
97
|
+
const outputs = [];
|
|
68
98
|
for (const file of files) {
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
99
|
+
const spec = parseFileSpec(file);
|
|
100
|
+
const inputs = await toRegisterInput(spec);
|
|
101
|
+
inputs.forEach((input) => {
|
|
102
|
+
if (!seen.has(input.path)) {
|
|
103
|
+
seen.add(input.path);
|
|
104
|
+
const { form, mime, path } = input;
|
|
105
|
+
const blob = input.blob || loadBinary(path);
|
|
106
|
+
const hash = computeHash(form, mime, blob);
|
|
107
|
+
outputs.push({ hash, form, mime, path });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
console.log(columify(outputs));
|
|
112
|
+
console.result(outputs);
|
|
113
|
+
});
|
|
114
|
+
export const matterPickCmd = new Command("pick")
|
|
115
|
+
.description("Pick matter hashes from a hash output file")
|
|
116
|
+
.argument("<files...>", "Paths of the matter files")
|
|
117
|
+
.option("--from <file>", "Path to the hash output file", "register/hashes.json")
|
|
118
|
+
.action(function (files) {
|
|
119
|
+
const opts = this.opts();
|
|
120
|
+
const list = loadJson(opts.from);
|
|
121
|
+
if (!Array.isArray(list))
|
|
122
|
+
throw new Error(`Expected an array in ${opts.from}`);
|
|
123
|
+
const hashes = list;
|
|
124
|
+
const result = files.map((file) => {
|
|
125
|
+
const rec = hashes.find((r) => r.path === file);
|
|
126
|
+
if (!rec)
|
|
127
|
+
throw new Error(`No hash found for: ${file}`);
|
|
128
|
+
return rec.hash;
|
|
129
|
+
});
|
|
130
|
+
console.log(result.join(" "));
|
|
78
131
|
});
|
|
79
132
|
export const matterCmd = new Command("matter")
|
|
80
133
|
.description("matter utilities")
|
|
81
134
|
.addCommand(matterRegisterCmd)
|
|
82
|
-
.addCommand(
|
|
135
|
+
.addCommand(matterCompileCmd)
|
|
136
|
+
.addCommand(matterHashCmd)
|
|
137
|
+
.addCommand(matterPickCmd);
|
|
138
|
+
function parseFileSpec(file) {
|
|
139
|
+
if (file.startsWith("@")) {
|
|
140
|
+
const p = file.slice(1).trim();
|
|
141
|
+
if (!p)
|
|
142
|
+
throw new Error("Invalid manifest file spec (empty path)");
|
|
143
|
+
return { path: p, type: "manifest" };
|
|
144
|
+
}
|
|
145
|
+
const parts = file.split(":");
|
|
146
|
+
if (parts.length < 1 || parts.length > 3) {
|
|
147
|
+
throw new Error(`Invalid matter spec: ${file}`);
|
|
148
|
+
}
|
|
149
|
+
const pathPart = parts[0];
|
|
150
|
+
let form;
|
|
151
|
+
let mime;
|
|
152
|
+
parts.slice(1).forEach((part) => {
|
|
153
|
+
if (part.startsWith("form=")) {
|
|
154
|
+
form = Number(part.slice(5));
|
|
155
|
+
}
|
|
156
|
+
else if (part.startsWith("mime=")) {
|
|
157
|
+
mime = part.slice(5);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
throw new Error(`Invalid extra info: ${part}`);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
const type = pathPart.endsWith(".enum.csv") || pathPart.endsWith(".perm.csv") ? "raw" : "blob";
|
|
164
|
+
return { path: pathPart, type, form, mime };
|
|
165
|
+
}
|
|
166
|
+
async function toRegisterInput(spec) {
|
|
167
|
+
if (spec.type == "blob") {
|
|
168
|
+
if (spec.form && spec.mime)
|
|
169
|
+
return [{ path: spec.path, form: spec.form, mime: spec.mime }];
|
|
170
|
+
const g = inferMatterMeta(spec.path);
|
|
171
|
+
return [{ path: spec.path, form: spec.form ?? g.form, mime: spec.mime ?? g.mime }];
|
|
172
|
+
}
|
|
173
|
+
else if (spec.type == "manifest") {
|
|
174
|
+
return loadJson(spec.path).map((item) => {
|
|
175
|
+
const { path, form, mime } = item;
|
|
176
|
+
return { path, form, mime };
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
else if (spec.type == "raw") {
|
|
180
|
+
if (spec.path.endsWith(".enum.csv")) {
|
|
181
|
+
const { blob, deps } = await compileEnumMatter(spec.path);
|
|
182
|
+
const path = spec.path.slice(0, -9) + ".enum.bin";
|
|
183
|
+
const { form, mime } = inferMatterMeta(path);
|
|
184
|
+
return [...deps, { blob, form, mime, path }];
|
|
185
|
+
}
|
|
186
|
+
else if (spec.path.endsWith(".perm.csv")) {
|
|
187
|
+
const { blob, deps } = await compileEnumMatter(spec.path);
|
|
188
|
+
const path = spec.path.slice(0, -9) + ".perm.bin";
|
|
189
|
+
const { form, mime } = inferMatterMeta(path);
|
|
190
|
+
return [...deps, { blob, form, mime, path }];
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
throw new Error("unknown raw matter file extension");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
throw new Error("unknown matter file type");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function getOutFiles(file, outDir) {
|
|
201
|
+
const dir = outDir ? path.resolve(outDir) : path.dirname(path.resolve(file));
|
|
202
|
+
if (file.endsWith(".enum.csv")) {
|
|
203
|
+
const baseName = path.basename(file).slice(0, -9);
|
|
204
|
+
const binFile = path.join(dir, `${baseName}.enum.bin`);
|
|
205
|
+
const depsFile = path.join(dir, `${baseName}.enum.deps`);
|
|
206
|
+
return { binFile, depsFile, outDir: dir, compile: compileEnumMatter };
|
|
207
|
+
}
|
|
208
|
+
else if (file.endsWith(".perm.csv")) {
|
|
209
|
+
const baseName = path.basename(file).slice(0, -9);
|
|
210
|
+
const binFile = path.join(dir, `${baseName}.perm.bin`);
|
|
211
|
+
const depsFile = path.join(dir, `${baseName}.perm.deps`);
|
|
212
|
+
return { binFile, depsFile, outDir: dir, compile: compilePermMatter };
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
throw new Error("Unsupported raw matter file extension");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function compileEnumMatter(file) {
|
|
219
|
+
const computeHashMemo = memoize(computeHashFile);
|
|
220
|
+
const deps = new Map();
|
|
221
|
+
let rows = 0;
|
|
222
|
+
let colTypes;
|
|
223
|
+
const mapHeaders = ({ header }) => {
|
|
224
|
+
const h = header.trim().toUpperCase();
|
|
225
|
+
if (h in CELL_FORMS) {
|
|
226
|
+
return h;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
throw new Error(`Invalid column element type: ${h}`);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
const mapValues = ({ header, value }) => {
|
|
233
|
+
const v = value.trim();
|
|
234
|
+
if (v.startsWith("0x")) {
|
|
235
|
+
return fromHex(pad(v, { size: 32, dir: "left" }), "bytes");
|
|
236
|
+
}
|
|
237
|
+
else if (header == "IMAGE" || header == "JSON") {
|
|
238
|
+
const { hash, mime, form, path } = computeHashMemo(v);
|
|
239
|
+
if (!deps.has(path)) {
|
|
240
|
+
deps.set(path, { hash, mime, form, path });
|
|
241
|
+
}
|
|
242
|
+
return fromHex(hash, "bytes");
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
throw new Error("expect hex strings for cols other than IMAGE, JSON");
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const aux = parseAuxData(file, { mapValues, mapHeaders });
|
|
249
|
+
const parser = csv({ skipComments: true, strict: true, mapHeaders, mapValues });
|
|
250
|
+
const blob = await new Promise((resolve, reject) => {
|
|
251
|
+
const content = [];
|
|
252
|
+
fs.createReadStream(file)
|
|
253
|
+
.pipe(parser)
|
|
254
|
+
.on("headers", (headers) => {
|
|
255
|
+
colTypes = headers;
|
|
256
|
+
})
|
|
257
|
+
.on("data", (data) => {
|
|
258
|
+
Object.values(data).forEach((value) => content.push(value));
|
|
259
|
+
rows += 1;
|
|
260
|
+
})
|
|
261
|
+
.on("end", () => {
|
|
262
|
+
const auxTypes = Object.keys(aux ?? {});
|
|
263
|
+
const auxValues = Object.values(aux ?? {});
|
|
264
|
+
const enumHeader = buildEnumHeader(auxTypes, colTypes, rows);
|
|
265
|
+
resolve(Buffer.concat([enumHeader, ...auxValues, ...content]));
|
|
266
|
+
})
|
|
267
|
+
.on("error", (e /* eslint-disable-line */) => {
|
|
268
|
+
reject(e);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
return { blob, deps: Array.from(deps.values()) };
|
|
272
|
+
}
|
|
273
|
+
async function compilePermMatter(file) {
|
|
274
|
+
void file;
|
|
275
|
+
throw new Error("unimplemented");
|
|
276
|
+
}
|
|
277
|
+
function computeHash(form, mime, blob) {
|
|
278
|
+
if (form < 0 || form > 255) {
|
|
279
|
+
throw new Error("form must be uint8 (0..255)");
|
|
280
|
+
}
|
|
281
|
+
const mimeUTF8 = new TextEncoder().encode(mime);
|
|
282
|
+
if (mimeUTF8.length <= 0 || mimeUTF8.length > 31) {
|
|
283
|
+
throw new Error("form must be uint8 (0..255)");
|
|
284
|
+
}
|
|
285
|
+
const msg = new Uint8Array(32 + blob.length);
|
|
286
|
+
msg[0] = form & 0xff;
|
|
287
|
+
msg.set(pad(mimeUTF8, { size: 32, dir: "right" }), 1);
|
|
288
|
+
msg.set(blob, 32);
|
|
289
|
+
// SHA256(form:1 || mime:31 || blob:var)
|
|
290
|
+
return sha256(msg);
|
|
291
|
+
}
|
|
292
|
+
function computeHashFile(file) {
|
|
293
|
+
const spec = parseFileSpec(file);
|
|
294
|
+
if (spec.type != "blob") {
|
|
295
|
+
throw new Error("not blob type");
|
|
296
|
+
}
|
|
297
|
+
let form, mime;
|
|
298
|
+
if (spec.form && spec.mime) {
|
|
299
|
+
form = spec.form;
|
|
300
|
+
mime = spec.mime;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
const g = inferMatterMeta(spec.path);
|
|
304
|
+
form = spec.form ?? g.form;
|
|
305
|
+
mime = spec.mime ?? g.mime;
|
|
306
|
+
}
|
|
307
|
+
const hash = computeHash(form, mime, loadBinary(spec.path));
|
|
308
|
+
return { path: spec.path, form, mime, hash };
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Build a 32-byte EnumMatter header.
|
|
312
|
+
* Layout:
|
|
313
|
+
* [0..3] = "ENUM"
|
|
314
|
+
* [4] = ver, aux (u8.hi4, u8.lo4)
|
|
315
|
+
* [5] = cols (u8)
|
|
316
|
+
* [6..7] = rows (u16 LE)
|
|
317
|
+
* [8..15] = aux_types [u8; 8]
|
|
318
|
+
* [16..31] = col_types [u8; 16]
|
|
319
|
+
*/
|
|
320
|
+
function buildEnumHeader(auxTypes, colTypes, rows = 0) {
|
|
321
|
+
if (colTypes.length === 0 && auxTypes.length === 0) {
|
|
322
|
+
throw new Error("Empty header list");
|
|
323
|
+
}
|
|
324
|
+
if (auxTypes.length > 8) {
|
|
325
|
+
throw new Error(`Too many aux types (max 8): ${auxTypes.length}`);
|
|
326
|
+
}
|
|
327
|
+
if (colTypes.length > 16) {
|
|
328
|
+
throw new Error(`Too many column types (max 16): ${colTypes.length}`);
|
|
329
|
+
}
|
|
330
|
+
if (rows < 0 || rows > 0xffff) {
|
|
331
|
+
throw new Error(`Rows out of range: ${rows}`);
|
|
332
|
+
}
|
|
333
|
+
const buf = new Uint8Array(32);
|
|
334
|
+
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
|
|
337
|
+
buf[6] = rows & 0xff; // rows.lo
|
|
338
|
+
buf[7] = (rows >>> 8) & 0xff; // rows.hi
|
|
339
|
+
auxTypes.forEach((t, i) => (buf[8 + i] = formByName(t))); // aux types
|
|
340
|
+
colTypes.forEach((t, i) => (buf[16 + i] = formByName(t))); // col types
|
|
341
|
+
return buf;
|
|
342
|
+
}
|
|
343
|
+
const MATTER_FORMS = {
|
|
344
|
+
// Simple
|
|
345
|
+
JSON: 0x01,
|
|
346
|
+
IMAGE: 0x02,
|
|
347
|
+
// Code
|
|
348
|
+
WASM: 0xc0,
|
|
349
|
+
// Data
|
|
350
|
+
ENUM: 0xd0,
|
|
351
|
+
PERM: 0xd1,
|
|
352
|
+
// Info
|
|
353
|
+
INFO: 0xff,
|
|
354
|
+
};
|
|
355
|
+
const CELL_FORMS = {
|
|
356
|
+
JSON: 0x01,
|
|
357
|
+
IMAGE: 0x02,
|
|
358
|
+
INFO: 0xff,
|
|
359
|
+
};
|
|
360
|
+
function formByName(name) {
|
|
361
|
+
const value = MATTER_FORMS[name];
|
|
362
|
+
if (value === undefined) {
|
|
363
|
+
throw new Error(`Invalid form name: "${name}"`);
|
|
364
|
+
}
|
|
365
|
+
return value;
|
|
366
|
+
}
|
|
367
|
+
function inferMatterMeta(filePath) {
|
|
368
|
+
const ext = path.extname(filePath);
|
|
369
|
+
switch (ext) {
|
|
370
|
+
case ".wasm":
|
|
371
|
+
return { mime: "application/wasm", form: MATTER_FORMS.WASM };
|
|
372
|
+
case ".json":
|
|
373
|
+
return { mime: "application/json", form: MATTER_FORMS.JSON };
|
|
374
|
+
case ".jpg":
|
|
375
|
+
return { mime: "image/jpeg", form: MATTER_FORMS.IMAGE };
|
|
376
|
+
case ".jpeg":
|
|
377
|
+
return { mime: "image/jpeg", form: MATTER_FORMS.IMAGE };
|
|
378
|
+
case ".png":
|
|
379
|
+
return { mime: "image/png", form: MATTER_FORMS.IMAGE };
|
|
380
|
+
case ".bin":
|
|
381
|
+
if (filePath.endsWith(".enum.bin")) {
|
|
382
|
+
return { mime: "application/vnd.every.enum", form: MATTER_FORMS.ENUM };
|
|
383
|
+
}
|
|
384
|
+
else if (filePath.endsWith(".perm.bin")) {
|
|
385
|
+
return { mime: "application/vnd.every.perm", form: MATTER_FORMS.PERM };
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
throw new Error("unknown matter file extension");
|
|
389
|
+
}
|
|
390
|
+
default:
|
|
391
|
+
throw new Error("unknown matter file extension");
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function parseAuxData(filePath, options) {
|
|
395
|
+
const lines = readPrologue(filePath)
|
|
396
|
+
.filter((l) => /^\s*#\s*@aux\s+/i.test(l))
|
|
397
|
+
.map((l) => l.replace(/^\s*#\s*@aux\s+/i, "").trim());
|
|
398
|
+
const mapHeaders = options.mapHeaders ?? (({ header }) => header);
|
|
399
|
+
const mapValues = options.mapValues ?? (({ value }) => value);
|
|
400
|
+
if (lines.length == 0) {
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
else if (lines.length == 2) {
|
|
404
|
+
const headers = lines[0].split(",").map((header, index) => mapHeaders({ header, index }));
|
|
405
|
+
const parts = lines[1].split(",");
|
|
406
|
+
if (parts.length != headers.length) {
|
|
407
|
+
throw new Error("aux header and values mismatch");
|
|
408
|
+
}
|
|
409
|
+
const values = parts.map((value, index) => mapValues({ header: headers[index], index, value }));
|
|
410
|
+
return Object.fromEntries(headers.map((h, i) => [h, values[i]]));
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
throw new Error("invalid aux data");
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
function readPrologue(filePath, maxBytes = 2048) {
|
|
417
|
+
const fd = fs.openSync(filePath, "r");
|
|
418
|
+
const buf = Buffer.alloc(maxBytes);
|
|
419
|
+
const bytesRead = fs.readSync(fd, buf, 0, maxBytes, 0);
|
|
420
|
+
fs.closeSync(fd);
|
|
421
|
+
const lines = buf.subarray(0, bytesRead).toString("utf8").split(/\r?\n/);
|
|
422
|
+
const prologue = [];
|
|
423
|
+
for (const line of lines) {
|
|
424
|
+
if (line.trim().startsWith("#")) {
|
|
425
|
+
prologue.push(line);
|
|
426
|
+
}
|
|
427
|
+
else if (line.trim().length > 0) {
|
|
428
|
+
break; // stop at the first non-comment, non-empty line
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return prologue;
|
|
432
|
+
}
|
package/dist/cmds/object.js
CHANGED
|
@@ -4,7 +4,7 @@ import { parseAbiItem, erc1155Abi, erc721Abi } from "viem";
|
|
|
4
4
|
import { abi } from "../abi.js";
|
|
5
5
|
import { submitSimulation } from "../ethereum.js";
|
|
6
6
|
import { Logger } from "../logger.js";
|
|
7
|
-
import { parseBigInt, parseNode3, parseNode4 } from "../parsers.js";
|
|
7
|
+
import { parseBigInt, parseNode3, parseNode4, parseSID } from "../parsers.js";
|
|
8
8
|
import { CommandGenDefaults, getCommandGen } from "../cmdgen.js";
|
|
9
9
|
import { FromOpts } from "../from-opts.js";
|
|
10
10
|
import { coerceValue } from "../utils.js";
|
|
@@ -67,7 +67,7 @@ const cmdGenConfig = {
|
|
|
67
67
|
return abiFunc.name == "uri" ? args.slice(1) : [args[0].id, ...args.slice(1)];
|
|
68
68
|
},
|
|
69
69
|
getCmdArgs: function (abiFunc) {
|
|
70
|
-
const sid = new Argument(`<sid>`, "sid of the object");
|
|
70
|
+
const sid = new Argument(`<sid>`, "sid of the object").argParser(parseSID);
|
|
71
71
|
const args0 = CommandGenDefaults.getCmdArgs(abiFunc);
|
|
72
72
|
return abiFunc.name == "uri" ? [sid] : [sid, ...args0.slice(1)];
|
|
73
73
|
},
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import jp from "jsonpath";
|
|
4
|
+
import * as _ from "lodash-es";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
const DEFAULT_DIR = "./register";
|
|
7
|
+
const STATIC_RULES = {
|
|
8
|
+
// contract
|
|
9
|
+
"deploy.addr": {
|
|
10
|
+
file: "deploy.json",
|
|
11
|
+
root: "$",
|
|
12
|
+
field: "deployedTo",
|
|
13
|
+
},
|
|
14
|
+
// kind
|
|
15
|
+
"kind.id": { file: "kind.json", root: "$.events[?(@.name=='KindRegistered')].data", field: "id" },
|
|
16
|
+
"kind.rev": { file: "kind.json", root: "$.events[?(@.name=='KindRegistered')].data.desc", field: "rev" },
|
|
17
|
+
"kind.krev": { file: "kind.json", root: "$.events[?(@.name=='KindRegistered')].data.desc", field: "kindRev" },
|
|
18
|
+
"kind.srev": { file: "kind.json", root: "$.events[?(@.name=='KindRegistered')].data.desc", field: "setRev" },
|
|
19
|
+
// set
|
|
20
|
+
"set.id": { file: "set.json", root: "$.events[?(@.name=='SetRegistered')].data", field: "id" },
|
|
21
|
+
"set.rev": { file: "set.json", root: "$.events[?(@.name=='SetRegistered')].data.desc", field: "rev" },
|
|
22
|
+
"set.krev": { file: "set.json", root: "$.events[?(@.name=='SetRegistered')].data.desc", field: "kindRev" },
|
|
23
|
+
"set.srev": { file: "set.json", root: "$.events[?(@.name=='SetRegistered')].data.desc", field: "setRev" },
|
|
24
|
+
// matter: first entry’s MatterRegistered by default
|
|
25
|
+
"matter.hash": { file: "matter.json", root: "$[0].events[?(@.event=='MatterRegistered')].data", field: "1" },
|
|
26
|
+
"matter.who": { file: "matter.json", root: "$[0].events[?(@.event=='MatterRegistered')].data", field: "0" },
|
|
27
|
+
};
|
|
28
|
+
function dynamicRule(key) {
|
|
29
|
+
const esc = (s) => s.replace(/'/g, "\\'");
|
|
30
|
+
// hash=<path> → hashes.json: find by .path and return .hash
|
|
31
|
+
{
|
|
32
|
+
const m = key.match(/^hash=(.+)$/);
|
|
33
|
+
if (m) {
|
|
34
|
+
const filePath = esc(m[1]);
|
|
35
|
+
return {
|
|
36
|
+
file: "hashes.json",
|
|
37
|
+
root: `$[?(@.path=='${filePath}')]`,
|
|
38
|
+
field: "hash",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// matter.hash=<path> → matter.json: find entry by .file, then MatterRegistered.data[1]
|
|
43
|
+
{
|
|
44
|
+
const m = key.match(/^matter\.hash=(.+)$/);
|
|
45
|
+
if (m) {
|
|
46
|
+
const filePath = esc(m[1]);
|
|
47
|
+
return {
|
|
48
|
+
file: "matter.json",
|
|
49
|
+
root: `$[?(@.file=='${filePath}')].events[?(@.event=='MatterRegistered')].data`,
|
|
50
|
+
field: "1",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// matter.who=<path> → matter.json: find entry by .file, then MatterRegistered.data[0]
|
|
55
|
+
{
|
|
56
|
+
const m = key.match(/^matter\.who=(.+)$/);
|
|
57
|
+
if (m) {
|
|
58
|
+
const filePath = esc(m[1]);
|
|
59
|
+
return {
|
|
60
|
+
file: "matter.json",
|
|
61
|
+
root: `$[?(@.file=='${filePath}')].events[?(@.event=='MatterRegistered')].data`,
|
|
62
|
+
field: "0",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
function resolveRule(key) {
|
|
69
|
+
let rule = dynamicRule(key);
|
|
70
|
+
if (rule)
|
|
71
|
+
return rule;
|
|
72
|
+
rule = STATIC_RULES[key];
|
|
73
|
+
if (!rule)
|
|
74
|
+
throw new Error(`Unknown key: ${key}`);
|
|
75
|
+
return rule;
|
|
76
|
+
}
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
+
function pickOnce(getData, key) {
|
|
79
|
+
const rule = resolveRule(key);
|
|
80
|
+
const data = getData(rule.file);
|
|
81
|
+
const roots = jp.query(data, rule.root);
|
|
82
|
+
if (roots.length === 0) {
|
|
83
|
+
throw new Error(`Root not found for key "${key}": ${rule.root} in ${rule.file}`);
|
|
84
|
+
}
|
|
85
|
+
// Pick the first matched root
|
|
86
|
+
const obj = roots[0];
|
|
87
|
+
const value = rule.field ? _.get(obj, rule.field) : obj;
|
|
88
|
+
if (value === undefined) {
|
|
89
|
+
throw new Error(`Field not found for key "${key}": ${rule.field} in ${rule.file}`);
|
|
90
|
+
}
|
|
91
|
+
return String(value);
|
|
92
|
+
}
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
+
function loadJson(fullPath) {
|
|
95
|
+
const raw = fs.readFileSync(fullPath, "utf8");
|
|
96
|
+
return JSON.parse(raw);
|
|
97
|
+
}
|
|
98
|
+
export const pickCmd = new Command("pick")
|
|
99
|
+
.description("Pick values from outputs")
|
|
100
|
+
.argument("<keys...>", "Keys to resolve")
|
|
101
|
+
.option("--from <dir>", "Output directory", DEFAULT_DIR)
|
|
102
|
+
.action(function (keys) {
|
|
103
|
+
const { from } = this.opts();
|
|
104
|
+
const dir = from || DEFAULT_DIR;
|
|
105
|
+
const loadJsonCache = _.memoize((rel) => {
|
|
106
|
+
const full = path.join(dir, rel);
|
|
107
|
+
return loadJson(full);
|
|
108
|
+
});
|
|
109
|
+
const outputs = keys.map((key) => pickOnce(loadJsonCache, key));
|
|
110
|
+
console.log(outputs.join(" "));
|
|
111
|
+
});
|
package/dist/index.js
CHANGED
package/dist/parsers.js
CHANGED
|
@@ -9,6 +9,17 @@ export function parseBigInt(arg) {
|
|
|
9
9
|
throw new InvalidArgumentError("invalid bigint");
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
export function parseSID(arg) {
|
|
13
|
+
try {
|
|
14
|
+
const [s, i] = arg.split(".");
|
|
15
|
+
const set = BigInt(s);
|
|
16
|
+
const id = BigInt(i);
|
|
17
|
+
return { set, id };
|
|
18
|
+
}
|
|
19
|
+
catch (e /* eslint-disable-line */) {
|
|
20
|
+
throw new InvalidArgumentError(`invalid SID: ${arg}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
12
23
|
export function parseAccountId(address) {
|
|
13
24
|
try {
|
|
14
25
|
return u8aFixLength(decodeAddress(address), 256);
|
package/dist/program.js
CHANGED
|
@@ -12,6 +12,7 @@ import { setCmd } from "./cmds/set.js";
|
|
|
12
12
|
import { objectCmd } from "./cmds/object.js";
|
|
13
13
|
import { minterCmd } from "./cmds/minter.js";
|
|
14
14
|
import { universeCmd } from "./cmds/universe.js";
|
|
15
|
+
import { pickCmd } from "./cmds/pick.js";
|
|
15
16
|
export const program = new Command("every")
|
|
16
17
|
.description("CLI for interacting with Every Protocol")
|
|
17
18
|
.version(version())
|
|
@@ -27,4 +28,5 @@ export const program = new Command("every")
|
|
|
27
28
|
.addCommand(minterCmd)
|
|
28
29
|
.addCommand(balanceCmd)
|
|
29
30
|
.addCommand(walletCmd)
|
|
30
|
-
.addCommand(configCmd)
|
|
31
|
+
.addCommand(configCmd)
|
|
32
|
+
.addCommand(pickCmd, { hidden: true });
|
package/dist/utils.js
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from "url";
|
|
|
4
4
|
import { isHex, hexToU8a } from "@polkadot/util";
|
|
5
5
|
import { base64Decode } from "@polkadot/util-crypto/base64";
|
|
6
6
|
import { decodePair } from "@polkadot/keyring/pair/decode";
|
|
7
|
-
import { getAddress, isAddress
|
|
7
|
+
import { getAddress, isAddress } from "viem";
|
|
8
8
|
import { FromOpts } from "./from-opts.js";
|
|
9
9
|
export const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
export function version() {
|
|
@@ -167,18 +167,3 @@ export function loadBinary(file) {
|
|
|
167
167
|
const buf = fs.readFileSync(file);
|
|
168
168
|
return buf;
|
|
169
169
|
}
|
|
170
|
-
export function computeMatterHash(form, mime, blob) {
|
|
171
|
-
if (!Number.isInteger(form) || form < 0 || form > 255) {
|
|
172
|
-
throw new Error("form must be uint8 (0..255)");
|
|
173
|
-
}
|
|
174
|
-
const mimeUTF8 = new TextEncoder().encode(mime);
|
|
175
|
-
if (mimeUTF8.length <= 0 || mimeUTF8.length > 31) {
|
|
176
|
-
throw new Error("form must be uint8 (0..255)");
|
|
177
|
-
}
|
|
178
|
-
const msg = new Uint8Array(1 + 31 + blob.length);
|
|
179
|
-
msg[0] = form & 0xff;
|
|
180
|
-
msg.set(pad(mimeUTF8, { size: 32, dir: "right" }), 1);
|
|
181
|
-
msg.set(blob, 32);
|
|
182
|
-
// SHA256(form:1 || mime:31 || blob:var)
|
|
183
|
-
return sha256(msg);
|
|
184
|
-
}
|
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.6",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/",
|
|
7
7
|
"abis/",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"@eslint/js": "^9.26.0",
|
|
17
17
|
"@types/bun": "latest",
|
|
18
18
|
"@types/columnify": "^1.5.4",
|
|
19
|
+
"@types/jsonpath": "^0.2.4",
|
|
19
20
|
"@types/lodash-es": "^4.17.12",
|
|
20
21
|
"@types/prompt-sync": "^4.2.3",
|
|
21
22
|
"@typescript-eslint/eslint-plugin": "^8.32.1",
|
|
@@ -35,9 +36,11 @@
|
|
|
35
36
|
"@polkadot/util-crypto": "^13.5.2",
|
|
36
37
|
"columnify": "^1.6.0",
|
|
37
38
|
"commander": "^13.1.0",
|
|
39
|
+
"csv-parser": "^3.2.0",
|
|
38
40
|
"ethers": "^6.14.0",
|
|
39
41
|
"json11": "^2.0.2",
|
|
40
42
|
"json5": "^2.2.3",
|
|
43
|
+
"jsonpath": "^1.1.1",
|
|
41
44
|
"lodash-es": "^4.17.21",
|
|
42
45
|
"prompt-sync": "^4.2.0",
|
|
43
46
|
"viem": "^2.29.1"
|