@elgora/cli 7.2.3 → 7.4.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/README.md +3 -0
- package/dist/cli.js +933 -162
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,10 @@ import path4 from "path";
|
|
|
5
5
|
import { config as loadDotenv } from "dotenv";
|
|
6
6
|
import { ZodError as ZodError2 } from "zod";
|
|
7
7
|
|
|
8
|
+
// src/check-page.ts
|
|
9
|
+
import { readFile } from "fs/promises";
|
|
10
|
+
import { text as readStdinText } from "stream/consumers";
|
|
11
|
+
|
|
8
12
|
// ../../packages/core/src/argv-flags.ts
|
|
9
13
|
function extractFlagValue(argv, flag) {
|
|
10
14
|
const remaining = [];
|
|
@@ -63,10 +67,13 @@ function safeDecodeUtf8(bytes) {
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
// ../../packages/core/src/constants.ts
|
|
70
|
+
var BOUNTY_CHALLENGE_PROFILE = "elgora_markdown_bounty_challenge_v0";
|
|
71
|
+
var WINNER_TAKE_ALL_PAYOUT_POLICY = "winner_take_all";
|
|
66
72
|
var SUBMISSION_ENCRYPTION_TYPE = "guardian_timelock_hpke_aes_256_gcm_v0";
|
|
67
73
|
var ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
68
74
|
|
|
69
75
|
// ../../packages/core/src/bounty-challenge-markdown.ts
|
|
76
|
+
var FRONTMATTER_DELIMITER = "---";
|
|
70
77
|
var MAX_BOUNTY_CHALLENGE_MARKDOWN_CHARACTERS = 1e6;
|
|
71
78
|
var BOUNTY_CHALLENGE_FRONTMATTER_KEYS = [
|
|
72
79
|
"profile",
|
|
@@ -74,15 +81,307 @@ var BOUNTY_CHALLENGE_FRONTMATTER_KEYS = [
|
|
|
74
81
|
"submission_deadline",
|
|
75
82
|
"payout_policy"
|
|
76
83
|
];
|
|
77
|
-
var
|
|
84
|
+
var BOUNTY_CHALLENGE_OPTIONAL_FRONTMATTER_KEYS = [
|
|
85
|
+
"constraints"
|
|
86
|
+
];
|
|
87
|
+
var ALLOWED_FRONTMATTER_KEYS = [
|
|
88
|
+
...BOUNTY_CHALLENGE_FRONTMATTER_KEYS,
|
|
89
|
+
...BOUNTY_CHALLENGE_OPTIONAL_FRONTMATTER_KEYS
|
|
90
|
+
].join(", ");
|
|
78
91
|
var ALLOWED_FRONTMATTER_KEYS_MESSAGE = `Use only the v0 template frontmatter keys: ${ALLOWED_FRONTMATTER_KEYS}`;
|
|
92
|
+
var DEPLOYMENT_OWNED_FRONTMATTER_KEYS = /* @__PURE__ */ new Set([
|
|
93
|
+
"chain_id",
|
|
94
|
+
"token_address",
|
|
95
|
+
"hub_address",
|
|
96
|
+
"guardian_recipients_commitment",
|
|
97
|
+
"guardian_settlement_signer",
|
|
98
|
+
"treasury_recipient",
|
|
99
|
+
"treasury_fee_bps",
|
|
100
|
+
"guardian_fee_recipient",
|
|
101
|
+
"guardian_fee_bps",
|
|
102
|
+
"timelock_reveal_delay_seconds",
|
|
103
|
+
"timelock_network",
|
|
104
|
+
"timelock_chain_hash",
|
|
105
|
+
"storage_provider",
|
|
106
|
+
"artifact_storage_bucket"
|
|
107
|
+
]);
|
|
108
|
+
var BountyChallengeSpecValidationError = class extends Error {
|
|
109
|
+
issues;
|
|
110
|
+
constructor(issues) {
|
|
111
|
+
super("Invalid BountyChallengeSpec Markdown");
|
|
112
|
+
this.name = "BountyChallengeSpecValidationError";
|
|
113
|
+
this.issues = issues;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
79
116
|
var markdownSchema = z.string().min(1, "Markdown body must not be empty").max(
|
|
80
117
|
MAX_BOUNTY_CHALLENGE_MARKDOWN_CHARACTERS,
|
|
81
118
|
`Markdown body must be at most ${MAX_BOUNTY_CHALLENGE_MARKDOWN_CHARACTERS} characters`
|
|
82
119
|
);
|
|
120
|
+
function parseBountyChallengeMarkdown(input) {
|
|
121
|
+
const markdown = parseMarkdownInput(input);
|
|
122
|
+
const lines = normalizedLines(markdown);
|
|
123
|
+
const issues = [];
|
|
124
|
+
const { frontmatterLines, bodyLines } = splitFrontmatter(lines, issues);
|
|
125
|
+
const { scalarLines, constraints } = splitConstraintsBlock(
|
|
126
|
+
frontmatterLines,
|
|
127
|
+
issues
|
|
128
|
+
);
|
|
129
|
+
const publicationTerms = parsePublicationTermsFrontmatter(
|
|
130
|
+
scalarLines,
|
|
131
|
+
issues
|
|
132
|
+
);
|
|
133
|
+
validateChallengeBody(bodyLines, issues);
|
|
134
|
+
if (issues.length > 0 || publicationTerms === null) {
|
|
135
|
+
throw new BountyChallengeSpecValidationError(issues);
|
|
136
|
+
}
|
|
137
|
+
return { markdown, publicationTerms, constraints };
|
|
138
|
+
}
|
|
83
139
|
function deriveBountyChallengeContentCommitment(content) {
|
|
84
140
|
return keccak256(toBytes(content));
|
|
85
141
|
}
|
|
142
|
+
function parseMarkdownInput(input) {
|
|
143
|
+
const parsed = markdownSchema.safeParse(input);
|
|
144
|
+
if (!parsed.success) {
|
|
145
|
+
throw new BountyChallengeSpecValidationError(
|
|
146
|
+
parsed.error.issues.map((issue) => ({
|
|
147
|
+
path: issue.path.join("."),
|
|
148
|
+
message: issue.message
|
|
149
|
+
}))
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return parsed.data;
|
|
153
|
+
}
|
|
154
|
+
function splitFrontmatter(lines, issues) {
|
|
155
|
+
if (lines[0] !== FRONTMATTER_DELIMITER) {
|
|
156
|
+
issues.push({
|
|
157
|
+
path: "frontmatter",
|
|
158
|
+
message: "Document must start with v0 frontmatter delimited by ---"
|
|
159
|
+
});
|
|
160
|
+
return { frontmatterLines: [], bodyLines: lines };
|
|
161
|
+
}
|
|
162
|
+
const closingIndex = lines.findIndex(
|
|
163
|
+
(line, index) => index > 0 && line === FRONTMATTER_DELIMITER
|
|
164
|
+
);
|
|
165
|
+
if (closingIndex === -1) {
|
|
166
|
+
issues.push({
|
|
167
|
+
path: "frontmatter",
|
|
168
|
+
message: "Frontmatter must close with --- before the H1 title"
|
|
169
|
+
});
|
|
170
|
+
return { frontmatterLines: lines.slice(1), bodyLines: [] };
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
frontmatterLines: lines.slice(1, closingIndex),
|
|
174
|
+
bodyLines: lines.slice(closingIndex + 1)
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function splitConstraintsBlock(lines, issues) {
|
|
178
|
+
const scalarLines = [];
|
|
179
|
+
const constraints = [];
|
|
180
|
+
let seenConstraintsBlock = false;
|
|
181
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
182
|
+
const line = lines[index] ?? "";
|
|
183
|
+
if (line.trim() !== "constraints:") {
|
|
184
|
+
scalarLines.push({ text: line, index });
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (seenConstraintsBlock) {
|
|
188
|
+
issues.push({
|
|
189
|
+
path: "frontmatter.constraints",
|
|
190
|
+
message: 'Frontmatter key "constraints" must appear exactly once'
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
seenConstraintsBlock = true;
|
|
194
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
195
|
+
while (index + 1 < lines.length) {
|
|
196
|
+
const entry = lines[index + 1] ?? "";
|
|
197
|
+
if (entry.trim().length === 0) break;
|
|
198
|
+
if (!/^\s+\S/.test(entry)) break;
|
|
199
|
+
index += 1;
|
|
200
|
+
const match = entry.trim().match(/^([a-z][a-z0-9_]*):\s*(.+)$/);
|
|
201
|
+
const key = match?.[1];
|
|
202
|
+
const rawValue = match?.[2];
|
|
203
|
+
if (key === void 0 || rawValue === void 0) {
|
|
204
|
+
issues.push({
|
|
205
|
+
path: `frontmatter.constraints.line_${index + 2}`,
|
|
206
|
+
message: "Constraint entries must use snake_case_key: value"
|
|
207
|
+
});
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (seenKeys.has(key)) {
|
|
211
|
+
issues.push({
|
|
212
|
+
path: `frontmatter.constraints.${key}`,
|
|
213
|
+
message: `Constraint "${key}" must appear exactly once`
|
|
214
|
+
});
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
seenKeys.add(key);
|
|
218
|
+
const value = normalizeFrontmatterScalar(
|
|
219
|
+
rawValue,
|
|
220
|
+
`frontmatter.constraints.${key}`,
|
|
221
|
+
issues
|
|
222
|
+
);
|
|
223
|
+
if (value !== null) {
|
|
224
|
+
constraints.push({ key, value, line: index + 2 });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (constraints.length === 0 && seenKeys.size === 0) {
|
|
228
|
+
issues.push({
|
|
229
|
+
path: "frontmatter.constraints",
|
|
230
|
+
message: "Remove the empty constraints block, or add at least one indented constraint entry"
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return { scalarLines, constraints };
|
|
235
|
+
}
|
|
236
|
+
function parsePublicationTermsFrontmatter(lines, issues) {
|
|
237
|
+
const values = /* @__PURE__ */ new Map();
|
|
238
|
+
let foundUnsupportedKey = false;
|
|
239
|
+
for (const { text: line, index } of lines) {
|
|
240
|
+
if (line.trim().length === 0) continue;
|
|
241
|
+
const match = line.match(/^([a-z][a-z0-9_]*):\s*(.*)$/);
|
|
242
|
+
if (!match) {
|
|
243
|
+
issues.push({
|
|
244
|
+
path: `frontmatter.line_${index + 2}`,
|
|
245
|
+
message: "Frontmatter entries must use snake_case_key: value"
|
|
246
|
+
});
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const rawKey = match[1];
|
|
250
|
+
const rawValue = match[2];
|
|
251
|
+
if (rawKey === void 0 || rawValue === void 0) {
|
|
252
|
+
issues.push({
|
|
253
|
+
path: `frontmatter.line_${index + 2}`,
|
|
254
|
+
message: "Frontmatter entries must use snake_case_key: value"
|
|
255
|
+
});
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (!isBountyChallengeFrontmatterKey(rawKey)) {
|
|
259
|
+
if (DEPLOYMENT_OWNED_FRONTMATTER_KEYS.has(rawKey)) {
|
|
260
|
+
if (!foundUnsupportedKey) {
|
|
261
|
+
issues.push({
|
|
262
|
+
path: "frontmatter",
|
|
263
|
+
message: ALLOWED_FRONTMATTER_KEYS_MESSAGE
|
|
264
|
+
});
|
|
265
|
+
foundUnsupportedKey = true;
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
issues.push({
|
|
269
|
+
path: `frontmatter.${rawKey}`,
|
|
270
|
+
message: `Remove frontmatter key "${rawKey}"; use only the v0 template frontmatter keys: ${ALLOWED_FRONTMATTER_KEYS}`
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (values.has(rawKey)) {
|
|
276
|
+
issues.push({
|
|
277
|
+
path: `frontmatter.${rawKey}`,
|
|
278
|
+
message: `Frontmatter key "${rawKey}" must appear exactly once`
|
|
279
|
+
});
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
const value = normalizeFrontmatterScalar(
|
|
283
|
+
rawValue,
|
|
284
|
+
`frontmatter.${rawKey}`,
|
|
285
|
+
issues
|
|
286
|
+
);
|
|
287
|
+
if (value !== null) values.set(rawKey, value);
|
|
288
|
+
}
|
|
289
|
+
for (const key of BOUNTY_CHALLENGE_FRONTMATTER_KEYS) {
|
|
290
|
+
if (!values.has(key)) {
|
|
291
|
+
issues.push({
|
|
292
|
+
path: `frontmatter.${key}`,
|
|
293
|
+
message: `Add required frontmatter key "${key}"`
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const profile = values.get("profile");
|
|
298
|
+
if (profile !== void 0 && profile !== BOUNTY_CHALLENGE_PROFILE) {
|
|
299
|
+
issues.push({
|
|
300
|
+
path: "frontmatter.profile",
|
|
301
|
+
message: `Must equal ${BOUNTY_CHALLENGE_PROFILE}`
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
const payoutPolicy = values.get("payout_policy");
|
|
305
|
+
if (payoutPolicy !== void 0 && payoutPolicy !== WINNER_TAKE_ALL_PAYOUT_POLICY) {
|
|
306
|
+
issues.push({
|
|
307
|
+
path: "frontmatter.payout_policy",
|
|
308
|
+
message: `Must equal ${WINNER_TAKE_ALL_PAYOUT_POLICY}`
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
const submissionDeadline = parsePositiveSafeIntegerFrontmatter(
|
|
312
|
+
values.get("submission_deadline"),
|
|
313
|
+
"frontmatter.submission_deadline",
|
|
314
|
+
issues
|
|
315
|
+
);
|
|
316
|
+
const escrowAmount = parsePositiveIntegerStringFrontmatter(
|
|
317
|
+
values.get("escrow_amount"),
|
|
318
|
+
"frontmatter.escrow_amount",
|
|
319
|
+
issues
|
|
320
|
+
);
|
|
321
|
+
if (profile !== BOUNTY_CHALLENGE_PROFILE || payoutPolicy !== WINNER_TAKE_ALL_PAYOUT_POLICY || escrowAmount === null || submissionDeadline === null) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
profile,
|
|
326
|
+
escrowAmount,
|
|
327
|
+
submissionDeadline,
|
|
328
|
+
payoutPolicy
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function normalizeFrontmatterScalar(rawValue, path5, issues) {
|
|
332
|
+
const value = rawValue.trim();
|
|
333
|
+
if (value.length === 0) {
|
|
334
|
+
issues.push({ path: path5, message: "Frontmatter value must not be empty" });
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
const quote = value[0];
|
|
338
|
+
if (quote !== '"' && quote !== "'") return value;
|
|
339
|
+
if (value.length < 2 || value.at(-1) !== quote) {
|
|
340
|
+
issues.push({
|
|
341
|
+
path: path5,
|
|
342
|
+
message: "Quoted frontmatter values must close with the same quote"
|
|
343
|
+
});
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
return value.slice(1, -1);
|
|
347
|
+
}
|
|
348
|
+
function parsePositiveSafeIntegerFrontmatter(value, path5, issues) {
|
|
349
|
+
if (value === void 0) return null;
|
|
350
|
+
if (!isPositiveInteger(value)) {
|
|
351
|
+
issues.push({ path: path5, message: "Must be a positive integer" });
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
const parsed = Number(value);
|
|
355
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
356
|
+
issues.push({ path: path5, message: "Must be a positive safe integer" });
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
return parsed;
|
|
360
|
+
}
|
|
361
|
+
function parsePositiveIntegerStringFrontmatter(value, path5, issues) {
|
|
362
|
+
if (value === void 0) return null;
|
|
363
|
+
if (!isPositiveInteger(value)) {
|
|
364
|
+
issues.push({ path: path5, message: "Must be positive token smallest units" });
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
return value;
|
|
368
|
+
}
|
|
369
|
+
function validateChallengeBody(lines, issues) {
|
|
370
|
+
if (!lines.some((line) => line.trim().length > 0)) {
|
|
371
|
+
issues.push({
|
|
372
|
+
path: "body",
|
|
373
|
+
message: "Add the Markdown challenge after frontmatter"
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function normalizedLines(markdown) {
|
|
378
|
+
return markdown.split("\n").map((line) => line.replace(/\r$/, ""));
|
|
379
|
+
}
|
|
380
|
+
function isBountyChallengeFrontmatterKey(value) {
|
|
381
|
+
return BOUNTY_CHALLENGE_FRONTMATTER_KEYS.includes(
|
|
382
|
+
value
|
|
383
|
+
);
|
|
384
|
+
}
|
|
86
385
|
|
|
87
386
|
// ../../packages/core/src/bounty-publication-response.ts
|
|
88
387
|
import { isHex as isHex2 } from "viem";
|
|
@@ -144,22 +443,42 @@ function resolveSupportedChain(chainId) {
|
|
|
144
443
|
}
|
|
145
444
|
var FOUNDRY_CHAIN_ID = foundry.id;
|
|
146
445
|
|
|
147
|
-
// ../../packages/core/src/
|
|
148
|
-
import {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
446
|
+
// ../../packages/core/src/submission-encryption.ts
|
|
447
|
+
import { Aes256Gcm, CipherSuite, HkdfSha256 } from "@hpke/core";
|
|
448
|
+
import { DhkemX25519HkdfSha256 } from "@hpke/dhkem-x25519";
|
|
449
|
+
import { concatBytes, keccak256 as keccak2562, toBytes as toBytes2 } from "viem";
|
|
450
|
+
import { z as z3 } from "zod";
|
|
451
|
+
|
|
452
|
+
// ../../packages/core/src/base64.ts
|
|
453
|
+
var BASE64_ENCODE_CHUNK_BYTES = 3 * 16384;
|
|
454
|
+
var FROM_CHAR_CODE_MAX_ARGUMENTS = 4096;
|
|
455
|
+
function bytesToBase64Url(bytes) {
|
|
456
|
+
const chunks = [];
|
|
457
|
+
for (let offset = 0; offset < bytes.length; offset += BASE64_ENCODE_CHUNK_BYTES) {
|
|
458
|
+
const chunk = bytes.subarray(offset, offset + BASE64_ENCODE_CHUNK_BYTES);
|
|
459
|
+
let binary = "";
|
|
460
|
+
for (let index = 0; index < chunk.length; index += FROM_CHAR_CODE_MAX_ARGUMENTS) {
|
|
461
|
+
binary += String.fromCharCode(
|
|
462
|
+
...chunk.subarray(index, index + FROM_CHAR_CODE_MAX_ARGUMENTS)
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
chunks.push(
|
|
466
|
+
btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "")
|
|
467
|
+
);
|
|
158
468
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
function
|
|
162
|
-
|
|
469
|
+
return chunks.join("");
|
|
470
|
+
}
|
|
471
|
+
function base64UrlToBytes(value) {
|
|
472
|
+
const padded = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
|
|
473
|
+
return base64ToBytes(padded);
|
|
474
|
+
}
|
|
475
|
+
function base64ToBytes(value) {
|
|
476
|
+
const binary = atob(value);
|
|
477
|
+
const bytes = new Uint8Array(binary.length);
|
|
478
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
479
|
+
bytes[index] = binary.charCodeAt(index);
|
|
480
|
+
}
|
|
481
|
+
return bytes;
|
|
163
482
|
}
|
|
164
483
|
|
|
165
484
|
// ../../packages/core/src/json.ts
|
|
@@ -193,33 +512,6 @@ function canonicalizeJson(value) {
|
|
|
193
512
|
throw new Error("Unsupported JSON value");
|
|
194
513
|
}
|
|
195
514
|
|
|
196
|
-
// ../../packages/core/src/submission-encryption.ts
|
|
197
|
-
import { Aes256Gcm, CipherSuite, HkdfSha256 } from "@hpke/core";
|
|
198
|
-
import { DhkemX25519HkdfSha256 } from "@hpke/dhkem-x25519";
|
|
199
|
-
import { concatBytes, keccak256 as keccak2562, toBytes as toBytes2 } from "viem";
|
|
200
|
-
import { z as z3 } from "zod";
|
|
201
|
-
|
|
202
|
-
// ../../packages/core/src/base64.ts
|
|
203
|
-
function bytesToBase64Url(bytes) {
|
|
204
|
-
let binary = "";
|
|
205
|
-
for (const byte of bytes) {
|
|
206
|
-
binary += String.fromCharCode(byte);
|
|
207
|
-
}
|
|
208
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
209
|
-
}
|
|
210
|
-
function base64UrlToBytes(value) {
|
|
211
|
-
const padded = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
|
|
212
|
-
return base64ToBytes(padded);
|
|
213
|
-
}
|
|
214
|
-
function base64ToBytes(value) {
|
|
215
|
-
const binary = atob(value);
|
|
216
|
-
const bytes = new Uint8Array(binary.length);
|
|
217
|
-
for (let index = 0; index < binary.length; index += 1) {
|
|
218
|
-
bytes[index] = binary.charCodeAt(index);
|
|
219
|
-
}
|
|
220
|
-
return bytes;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
515
|
// ../../packages/core/src/submission-encryption.ts
|
|
224
516
|
var PRIVATE_ARTIFACT_ENVELOPE_KIND = "elgora_private_artifact_envelope_v0";
|
|
225
517
|
var PRIVATE_ARTIFACT_PUBLIC_MANIFEST_KIND = "elgora_private_artifact_public_manifest_v0";
|
|
@@ -228,13 +520,17 @@ var PRIVATE_ARTIFACT_WRAPPED_KEY_BAG_KIND = "elgora_private_artifact_wrapped_key
|
|
|
228
520
|
var PRIVATE_ARTIFACT_REVEAL_POLICY_KIND = "drand_timelock_reveal_policy_v0";
|
|
229
521
|
var PRIVATE_ARTIFACT_HPKE_SUITE = "hpke-dhkem-x25519-hkdf-sha256+hkdf-sha256+aes-256-gcm@1";
|
|
230
522
|
var MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES = 50 * 1024 * 1024;
|
|
523
|
+
var MAX_SOLVER_SUBMISSION_FILES = 500;
|
|
524
|
+
var MAX_SOLVER_SUBMISSION_NAME_CHARACTERS = 255;
|
|
525
|
+
var MAX_SOLVER_SUBMISSION_ARTIFACT_BYTES = 37 * 1024 * 1024;
|
|
526
|
+
var MAX_SOLVER_SUBMISSION_EXTRACTED_BYTES = 250 * 1e3 * 1e3;
|
|
231
527
|
var PRIVATE_ARTIFACT_HPKE_INFO_PREFIX = "elgora/private-artifact-envelope/hpke-aes-key/v0";
|
|
232
528
|
var AES_256_KEY_BYTES = 32;
|
|
233
529
|
var AES_GCM_IV_BYTES = 12;
|
|
234
530
|
var PRIVATE_ARTIFACT_NONCE_BYTES = 16;
|
|
235
531
|
var AES_GCM_TAG_BITS = 128;
|
|
236
532
|
var BASE64URL_RE = /^[A-Za-z0-9_-]+$/;
|
|
237
|
-
var
|
|
533
|
+
var SOLVER_SUBMISSION_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
238
534
|
var MEDIA_TYPE_RE = /^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/;
|
|
239
535
|
var KEY_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
240
536
|
var base64UrlSchema = z3.string().regex(BASE64URL_RE);
|
|
@@ -242,7 +538,10 @@ var lowerAddressSchema = z3.string().refine(isLowercaseHexAddress);
|
|
|
242
538
|
var bytes32Schema2 = z3.string().refine(isLowercaseBytes32);
|
|
243
539
|
var sha256HexSchema = z3.string().refine(isSha256Hex);
|
|
244
540
|
var keyIdSchema = z3.string().min(1).max(128).regex(KEY_ID_RE);
|
|
245
|
-
var artifactNameSchema = z3.string().
|
|
541
|
+
var artifactNameSchema = z3.string().max(
|
|
542
|
+
MAX_SOLVER_SUBMISSION_NAME_CHARACTERS,
|
|
543
|
+
`Artifact filename exceeds ${MAX_SOLVER_SUBMISSION_NAME_CHARACTERS} ASCII characters (including extension). Solvers may shorten it only if the published challenge permits, before the deadline.`
|
|
544
|
+
).regex(SOLVER_SUBMISSION_NAME_RE);
|
|
246
545
|
var mediaTypeSchema = z3.string().regex(MEDIA_TYPE_RE);
|
|
247
546
|
var bountyIdSchema = z3.string().refine(isBountyId);
|
|
248
547
|
var chainIdSchema = z3.number().int().positive().lte(Number.MAX_SAFE_INTEGER);
|
|
@@ -299,7 +598,10 @@ var encryptedBundleArtifactSchema = artifactManifestEntrySchema.extend({
|
|
|
299
598
|
var privateArtifactEncryptedBundleSchema = z3.strictObject({
|
|
300
599
|
kind: z3.literal(PRIVATE_ARTIFACT_ENCRYPTED_BUNDLE_KIND),
|
|
301
600
|
context: privateArtifactContextSchema,
|
|
302
|
-
artifacts: z3.array(encryptedBundleArtifactSchema).min(1).
|
|
601
|
+
artifacts: z3.array(encryptedBundleArtifactSchema).min(1).max(
|
|
602
|
+
MAX_SOLVER_SUBMISSION_FILES,
|
|
603
|
+
`Submission exceeds the maximum of ${MAX_SOLVER_SUBMISSION_FILES} files.`
|
|
604
|
+
).refine(
|
|
303
605
|
(artifacts) => new Set(artifacts.map((artifact) => artifact.name.toLowerCase())).size === artifacts.length,
|
|
304
606
|
"artifact names must be unique case-insensitively"
|
|
305
607
|
)
|
|
@@ -849,8 +1151,8 @@ async function openAesKeyForRecipient(input) {
|
|
|
849
1151
|
});
|
|
850
1152
|
}
|
|
851
1153
|
async function encryptAes256Gcm(input) {
|
|
852
|
-
const
|
|
853
|
-
const key = await
|
|
1154
|
+
const crypto2 = getWebCrypto();
|
|
1155
|
+
const key = await crypto2.subtle.importKey(
|
|
854
1156
|
"raw",
|
|
855
1157
|
toArrayBufferView(input.rawAesKey),
|
|
856
1158
|
"AES-GCM",
|
|
@@ -858,7 +1160,7 @@ async function encryptAes256Gcm(input) {
|
|
|
858
1160
|
["encrypt"]
|
|
859
1161
|
);
|
|
860
1162
|
return toUint8Array(
|
|
861
|
-
await
|
|
1163
|
+
await crypto2.subtle.encrypt(
|
|
862
1164
|
{
|
|
863
1165
|
name: "AES-GCM",
|
|
864
1166
|
iv: toArrayBufferView(input.iv),
|
|
@@ -871,8 +1173,8 @@ async function encryptAes256Gcm(input) {
|
|
|
871
1173
|
);
|
|
872
1174
|
}
|
|
873
1175
|
async function decryptAes256Gcm(input) {
|
|
874
|
-
const
|
|
875
|
-
const key = await
|
|
1176
|
+
const crypto2 = getWebCrypto();
|
|
1177
|
+
const key = await crypto2.subtle.importKey(
|
|
876
1178
|
"raw",
|
|
877
1179
|
toArrayBufferView(input.rawAesKey),
|
|
878
1180
|
"AES-GCM",
|
|
@@ -881,7 +1183,7 @@ async function decryptAes256Gcm(input) {
|
|
|
881
1183
|
);
|
|
882
1184
|
try {
|
|
883
1185
|
return toUint8Array(
|
|
884
|
-
await
|
|
1186
|
+
await crypto2.subtle.decrypt(
|
|
885
1187
|
{
|
|
886
1188
|
name: "AES-GCM",
|
|
887
1189
|
iv: toArrayBufferView(input.iv),
|
|
@@ -959,6 +1261,277 @@ function compareCanonicalStrings(left, right) {
|
|
|
959
1261
|
return 0;
|
|
960
1262
|
}
|
|
961
1263
|
|
|
1264
|
+
// ../../packages/core/src/protocol-profile.ts
|
|
1265
|
+
var PROTOCOL_PROFILE_V0 = Object.freeze({
|
|
1266
|
+
profile: BOUNTY_CHALLENGE_PROFILE,
|
|
1267
|
+
submission: Object.freeze({
|
|
1268
|
+
maxCiphertextBytes: MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES,
|
|
1269
|
+
maxExtractedBytes: MAX_SOLVER_SUBMISSION_EXTRACTED_BYTES,
|
|
1270
|
+
maxFiles: MAX_SOLVER_SUBMISSION_FILES,
|
|
1271
|
+
filename: Object.freeze({
|
|
1272
|
+
maxCharacters: MAX_SOLVER_SUBMISSION_NAME_CHARACTERS,
|
|
1273
|
+
firstCharacter: "letter_or_digit",
|
|
1274
|
+
allowedCharacters: "letters_digits_dot_underscore_hyphen",
|
|
1275
|
+
caseInsensitivelyUnique: true
|
|
1276
|
+
}),
|
|
1277
|
+
layout: Object.freeze({
|
|
1278
|
+
flat: true,
|
|
1279
|
+
regularFilesOnly: true,
|
|
1280
|
+
allowsSubdirectories: false,
|
|
1281
|
+
allowsSymlinks: false,
|
|
1282
|
+
allowsEmpty: false,
|
|
1283
|
+
allowsArchivedDirectoryEntries: false,
|
|
1284
|
+
allowsArchivedPathTraversal: false
|
|
1285
|
+
}),
|
|
1286
|
+
externalReferences: false
|
|
1287
|
+
}),
|
|
1288
|
+
challenge: Object.freeze({
|
|
1289
|
+
maxMarkdownCharacters: MAX_BOUNTY_CHALLENGE_MARKDOWN_CHARACTERS,
|
|
1290
|
+
requiredFrontmatterKeys: Object.freeze([
|
|
1291
|
+
...BOUNTY_CHALLENGE_FRONTMATTER_KEYS
|
|
1292
|
+
]),
|
|
1293
|
+
optionalFrontmatterKeys: Object.freeze([
|
|
1294
|
+
...BOUNTY_CHALLENGE_OPTIONAL_FRONTMATTER_KEYS
|
|
1295
|
+
]),
|
|
1296
|
+
payoutPolicies: Object.freeze([WINNER_TAKE_ALL_PAYOUT_POLICY])
|
|
1297
|
+
}),
|
|
1298
|
+
encryption: Object.freeze({
|
|
1299
|
+
type: SUBMISSION_ENCRYPTION_TYPE
|
|
1300
|
+
}),
|
|
1301
|
+
tightenable: Object.freeze({
|
|
1302
|
+
max_extracted_bytes: Object.freeze({
|
|
1303
|
+
comparison: "at_most",
|
|
1304
|
+
profileValue: MAX_SOLVER_SUBMISSION_EXTRACTED_BYTES
|
|
1305
|
+
}),
|
|
1306
|
+
max_files: Object.freeze({
|
|
1307
|
+
comparison: "at_most",
|
|
1308
|
+
profileValue: MAX_SOLVER_SUBMISSION_FILES
|
|
1309
|
+
}),
|
|
1310
|
+
allowed_extensions: Object.freeze({ comparison: "subset" }),
|
|
1311
|
+
required_files: Object.freeze({ comparison: "filename_list" })
|
|
1312
|
+
})
|
|
1313
|
+
});
|
|
1314
|
+
var PROTOCOL_PROFILES = /* @__PURE__ */ new Map([
|
|
1315
|
+
[PROTOCOL_PROFILE_V0.profile, PROTOCOL_PROFILE_V0]
|
|
1316
|
+
]);
|
|
1317
|
+
var CURRENT_PROTOCOL_PROFILE = PROTOCOL_PROFILE_V0;
|
|
1318
|
+
var PROTOCOL_PROFILE_VERSIONS = Object.freeze([
|
|
1319
|
+
...PROTOCOL_PROFILES.keys()
|
|
1320
|
+
]);
|
|
1321
|
+
function getProtocolProfile(version) {
|
|
1322
|
+
return PROTOCOL_PROFILES.get(version) ?? null;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// ../../packages/core/src/challenge-conformance.ts
|
|
1326
|
+
function checkChallengeConformance(input, profile = CURRENT_PROTOCOL_PROFILE) {
|
|
1327
|
+
const issues = [];
|
|
1328
|
+
const parsed = typeof input === "string" ? readFrontmatter(input, issues) : input;
|
|
1329
|
+
const pinned = parsed === null ? null : resolveProfile(parsed.publicationTerms.profile, profile, issues);
|
|
1330
|
+
if (parsed !== null && pinned !== null) {
|
|
1331
|
+
checkTightenings(parsed.constraints, pinned, issues);
|
|
1332
|
+
}
|
|
1333
|
+
return { issues, profile: pinned };
|
|
1334
|
+
}
|
|
1335
|
+
function readFrontmatter(markdown, issues) {
|
|
1336
|
+
try {
|
|
1337
|
+
return parseBountyChallengeMarkdown(markdown);
|
|
1338
|
+
} catch (error) {
|
|
1339
|
+
if (!(error instanceof BountyChallengeSpecValidationError)) throw error;
|
|
1340
|
+
for (const issue of error.issues) {
|
|
1341
|
+
issues.push({
|
|
1342
|
+
code: "frontmatter_invalid",
|
|
1343
|
+
anchor: issue.path,
|
|
1344
|
+
path: issue.path,
|
|
1345
|
+
message: issue.message
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
return null;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
function resolveProfile(declared, fallback, issues) {
|
|
1352
|
+
const pinned = getProtocolProfile(declared) ?? (declared === fallback.profile ? fallback : null);
|
|
1353
|
+
if (pinned === null) {
|
|
1354
|
+
issues.push({
|
|
1355
|
+
code: "profile_unknown",
|
|
1356
|
+
anchor: "frontmatter.profile",
|
|
1357
|
+
path: "frontmatter.profile",
|
|
1358
|
+
message: `Unknown protocol profile "${declared}". A bounty can only pin a published profile version.`
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
return pinned;
|
|
1362
|
+
}
|
|
1363
|
+
function checkTightenings(constraints, profile, issues) {
|
|
1364
|
+
for (const constraint of constraints) {
|
|
1365
|
+
const anchor = `frontmatter.constraints.${constraint.key}`;
|
|
1366
|
+
const rule = profile.tightenable[constraint.key];
|
|
1367
|
+
if (rule === void 0) {
|
|
1368
|
+
issues.push({
|
|
1369
|
+
code: "tightening_unknown_key",
|
|
1370
|
+
anchor,
|
|
1371
|
+
path: anchor,
|
|
1372
|
+
message: `"${constraint.key}" is not a constraint a challenge may narrow. A Poster sets bounty parameters; protocol invariants and agent procedure are not among them. Tightenable: ${Object.keys(profile.tightenable).join(", ")}.`
|
|
1373
|
+
});
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
if (rule.comparison === "at_most") {
|
|
1377
|
+
checkNumericTightening(constraint, rule, anchor, issues);
|
|
1378
|
+
continue;
|
|
1379
|
+
}
|
|
1380
|
+
checkListTightening(constraint, rule, profile, anchor, issues);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
function checkNumericTightening(constraint, rule, anchor, issues) {
|
|
1384
|
+
const ceiling = rule.profileValue;
|
|
1385
|
+
if (ceiling === void 0) return;
|
|
1386
|
+
if (!/^\d+$/.test(constraint.value)) {
|
|
1387
|
+
issues.push({
|
|
1388
|
+
code: "tightening_malformed",
|
|
1389
|
+
anchor,
|
|
1390
|
+
path: anchor,
|
|
1391
|
+
message: `"${constraint.key}" must be a positive integer, written in full without units or separators.`
|
|
1392
|
+
});
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
const value = Number(constraint.value);
|
|
1396
|
+
if (value <= 0 || !Number.isSafeInteger(value)) {
|
|
1397
|
+
issues.push({
|
|
1398
|
+
code: "tightening_malformed",
|
|
1399
|
+
anchor,
|
|
1400
|
+
path: anchor,
|
|
1401
|
+
message: `"${constraint.key}" must be a positive integer below ${ceiling}.`
|
|
1402
|
+
});
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
if (value >= ceiling) {
|
|
1406
|
+
issues.push({
|
|
1407
|
+
code: "tightening_exceeds_profile",
|
|
1408
|
+
anchor,
|
|
1409
|
+
path: anchor,
|
|
1410
|
+
message: `"${constraint.key}" is ${value}, which does not narrow the protocol value of ${ceiling}. A challenge may lower a limit and may never raise or restate one.`
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
function checkListTightening(constraint, rule, profile, anchor, issues) {
|
|
1415
|
+
const members = parseInlineList(constraint.value);
|
|
1416
|
+
if (members === null || members.length === 0) {
|
|
1417
|
+
issues.push({
|
|
1418
|
+
code: "tightening_malformed",
|
|
1419
|
+
anchor,
|
|
1420
|
+
path: anchor,
|
|
1421
|
+
message: `"${constraint.key}" must be a non-empty inline list, written as [a, b].`
|
|
1422
|
+
});
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
if (rule.comparison === "subset") {
|
|
1426
|
+
const malformed2 = members.filter(
|
|
1427
|
+
(member) => !/^\.[A-Za-z0-9][A-Za-z0-9.]*$/.test(member)
|
|
1428
|
+
);
|
|
1429
|
+
if (malformed2.length > 0) {
|
|
1430
|
+
issues.push({
|
|
1431
|
+
code: "tightening_malformed",
|
|
1432
|
+
anchor,
|
|
1433
|
+
path: anchor,
|
|
1434
|
+
message: `"${constraint.key}" entries must be filename extensions including the dot: ${malformed2.join(", ")}.`
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1439
|
+
const { filename, maxFiles } = profile.submission;
|
|
1440
|
+
const malformed = members.filter(
|
|
1441
|
+
(member) => member.length > filename.maxCharacters || !SOLVER_SUBMISSION_NAME_RE.test(member)
|
|
1442
|
+
);
|
|
1443
|
+
if (malformed.length > 0) {
|
|
1444
|
+
issues.push({
|
|
1445
|
+
code: "tightening_malformed",
|
|
1446
|
+
anchor,
|
|
1447
|
+
path: anchor,
|
|
1448
|
+
message: `"${constraint.key}" names files that do not satisfy the profile's filename rule: ${malformed.join(", ")}.`
|
|
1449
|
+
});
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
if (filename.caseInsensitivelyUnique) {
|
|
1453
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1454
|
+
const collisions = /* @__PURE__ */ new Set();
|
|
1455
|
+
for (const member of members) {
|
|
1456
|
+
const folded = member.toLowerCase();
|
|
1457
|
+
if (seen.has(folded)) collisions.add(folded);
|
|
1458
|
+
seen.add(folded);
|
|
1459
|
+
}
|
|
1460
|
+
if (collisions.size > 0) {
|
|
1461
|
+
issues.push({
|
|
1462
|
+
code: "tightening_malformed",
|
|
1463
|
+
anchor,
|
|
1464
|
+
path: anchor,
|
|
1465
|
+
message: `"${constraint.key}" names files that differ only in case: ${[...collisions].join(", ")}. A Submission's filenames are unique case-insensitively, so no Solver could satisfy this set.`
|
|
1466
|
+
});
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if (members.length > maxFiles) {
|
|
1471
|
+
issues.push({
|
|
1472
|
+
code: "tightening_exceeds_profile",
|
|
1473
|
+
anchor,
|
|
1474
|
+
path: anchor,
|
|
1475
|
+
message: `"${constraint.key}" requires ${members.length} files, above the profile's ceiling of ${maxFiles}.`
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
function parseInlineList(value) {
|
|
1480
|
+
if (!value.startsWith("[") || !value.endsWith("]")) return null;
|
|
1481
|
+
return value.slice(1, -1).split(",").map((member) => member.trim().replace(/^["']|["']$/g, "")).filter((member) => member.length > 0);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
// ../../packages/core/src/deployments.ts
|
|
1485
|
+
import { baseSepolia as baseSepolia2 } from "viem/chains";
|
|
1486
|
+
var DEPLOYMENTS = {
|
|
1487
|
+
[baseSepolia2.id]: {
|
|
1488
|
+
ELGORA_HUB_ADDRESS: "0x2f97b5f616495c2e923f39a46648eb783c053ad7",
|
|
1489
|
+
ELGORA_ESCROW_TOKEN_ADDRESS: "0x036cbd53842c5426634e7929541ec2318f3dcf7e",
|
|
1490
|
+
payoutSchemeAddress: "0x752d4305b8567b777d479dfa9847dc4f5ffb5750",
|
|
1491
|
+
ELGORA_SUBGRAPH_ENDPOINT: "https://api.goldsky.com/api/public/project_cmtu7wslg04s501ovc2je3beh/subgraphs/elgora-elgorahub/testnet-production/gn",
|
|
1492
|
+
deployBlock: 46353511,
|
|
1493
|
+
deployTransaction: "0xed3d3c8af3c69f9f8d4ec47da7179fe4b89bd4a4cd8d8232dbbd3d3f3d38e74b",
|
|
1494
|
+
sourceCommit: "1377da5c38dc5c90d8608ffa3cb15ad6f8567a01"
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
var DEFAULT_CHAIN_ID = baseSepolia2.id;
|
|
1498
|
+
function getDeployment(chainId) {
|
|
1499
|
+
return DEPLOYMENTS[chainId];
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
// src/check-page.ts
|
|
1503
|
+
var CHECK_PAGE_USAGE = "Usage: elgora-cli check-page <bounty_challenge_md_path>\n or: elgora-cli check-page -";
|
|
1504
|
+
async function main(_env = process.env, argv = process.argv.slice(2)) {
|
|
1505
|
+
const markdown = await readBountyChallengeMarkdown(argv);
|
|
1506
|
+
const { issues, profile } = checkChallengeConformance(markdown);
|
|
1507
|
+
console.log(
|
|
1508
|
+
JSON.stringify({
|
|
1509
|
+
event: issues.length === 0 ? "check_page.structure_conforming" : "check_page.issues",
|
|
1510
|
+
profile: profile?.profile ?? null,
|
|
1511
|
+
// Named so a caller cannot read a clean result as "this page is safe".
|
|
1512
|
+
checked: "structure",
|
|
1513
|
+
issues: issues.map((issue) => ({
|
|
1514
|
+
code: issue.code,
|
|
1515
|
+
anchor: issue.anchor,
|
|
1516
|
+
message: issue.message
|
|
1517
|
+
}))
|
|
1518
|
+
})
|
|
1519
|
+
);
|
|
1520
|
+
if (issues.length > 0) {
|
|
1521
|
+
process.exitCode = 1;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
async function readBountyChallengeMarkdown(argv) {
|
|
1525
|
+
const path5 = argv[0];
|
|
1526
|
+
if (!path5) {
|
|
1527
|
+
throw new Error(CHECK_PAGE_USAGE);
|
|
1528
|
+
}
|
|
1529
|
+
if (path5 === "-") {
|
|
1530
|
+
return readStdinText(process.stdin);
|
|
1531
|
+
}
|
|
1532
|
+
return readFile(path5, "utf8");
|
|
1533
|
+
}
|
|
1534
|
+
|
|
962
1535
|
// ../../packages/core/src/api/artifact-storage.ts
|
|
963
1536
|
var ArtifactTooLargeError = class extends Error {
|
|
964
1537
|
code = "artifact_too_large";
|
|
@@ -3481,12 +4054,24 @@ async function classifyBeaconFetch(operation) {
|
|
|
3481
4054
|
}
|
|
3482
4055
|
|
|
3483
4056
|
// ../../packages/core/src/api/elgorahub.ts
|
|
3484
|
-
import {
|
|
4057
|
+
import {
|
|
4058
|
+
BaseError,
|
|
4059
|
+
bytesToHex,
|
|
4060
|
+
ContractFunctionRevertedError,
|
|
4061
|
+
ContractFunctionZeroDataError,
|
|
4062
|
+
domainSeparator,
|
|
4063
|
+
encodeFunctionData,
|
|
4064
|
+
getAbiItem as getAbiItem2,
|
|
4065
|
+
parseAbi,
|
|
4066
|
+
parseSignature,
|
|
4067
|
+
recoverTypedDataAddress
|
|
4068
|
+
} from "viem";
|
|
3485
4069
|
var ELGORA_HUB_ERRORS = ELGORA_HUB_ABI.filter(
|
|
3486
4070
|
(item) => item.type === "error"
|
|
3487
4071
|
);
|
|
3488
4072
|
var ELGORA_HUB_CREATE_BOUNTY_ABI = [
|
|
3489
4073
|
getAbiItem2({ abi: ELGORA_HUB_ABI, name: "createBounty" }),
|
|
4074
|
+
getAbiItem2({ abi: ELGORA_HUB_ABI, name: "createBountyWithAuthorization" }),
|
|
3490
4075
|
...ELGORA_HUB_ERRORS
|
|
3491
4076
|
];
|
|
3492
4077
|
var ELGORA_HUB_SUBMIT_ABI = [
|
|
@@ -3546,6 +4131,117 @@ var ELGORA_HUB_ADD_GUARDIAN_ABI = [
|
|
|
3546
4131
|
getAbiItem2({ abi: ELGORA_HUB_ABI, name: "addGuardian" }),
|
|
3547
4132
|
...ELGORA_HUB_ERRORS
|
|
3548
4133
|
];
|
|
4134
|
+
var RECEIVE_AUTHORIZATION_TYPES = {
|
|
4135
|
+
ReceiveWithAuthorization: [
|
|
4136
|
+
{ name: "from", type: "address" },
|
|
4137
|
+
{ name: "to", type: "address" },
|
|
4138
|
+
{ name: "value", type: "uint256" },
|
|
4139
|
+
{ name: "validAfter", type: "uint256" },
|
|
4140
|
+
{ name: "validBefore", type: "uint256" },
|
|
4141
|
+
{ name: "nonce", type: "bytes32" }
|
|
4142
|
+
]
|
|
4143
|
+
};
|
|
4144
|
+
var USDC_DOMAIN_ABI = parseAbi([
|
|
4145
|
+
"function name() view returns (string)",
|
|
4146
|
+
"function version() view returns (string)",
|
|
4147
|
+
"function DOMAIN_SEPARATOR() view returns (bytes32)"
|
|
4148
|
+
]);
|
|
4149
|
+
async function prepareAuthorizedCreateBountyTransaction(input) {
|
|
4150
|
+
const { transaction, publicClient, poster, tokenAddress } = input;
|
|
4151
|
+
if (await publicClient.getChainId() !== transaction.chain_id) {
|
|
4152
|
+
throw new Error("Funding RPC is on the wrong chain.");
|
|
4153
|
+
}
|
|
4154
|
+
const code = await publicClient.getCode({ address: poster });
|
|
4155
|
+
if (code && code !== "0x") return null;
|
|
4156
|
+
let name;
|
|
4157
|
+
let version;
|
|
4158
|
+
let tokenDomain;
|
|
4159
|
+
try {
|
|
4160
|
+
[name, version, tokenDomain] = await Promise.all([
|
|
4161
|
+
publicClient.readContract({
|
|
4162
|
+
address: tokenAddress,
|
|
4163
|
+
abi: USDC_DOMAIN_ABI,
|
|
4164
|
+
functionName: "name"
|
|
4165
|
+
}),
|
|
4166
|
+
publicClient.readContract({
|
|
4167
|
+
address: tokenAddress,
|
|
4168
|
+
abi: USDC_DOMAIN_ABI,
|
|
4169
|
+
functionName: "version"
|
|
4170
|
+
}),
|
|
4171
|
+
publicClient.readContract({
|
|
4172
|
+
address: tokenAddress,
|
|
4173
|
+
abi: USDC_DOMAIN_ABI,
|
|
4174
|
+
functionName: "DOMAIN_SEPARATOR"
|
|
4175
|
+
})
|
|
4176
|
+
]);
|
|
4177
|
+
} catch (error) {
|
|
4178
|
+
if (error instanceof BaseError && error.walk(
|
|
4179
|
+
(cause) => cause instanceof ContractFunctionRevertedError || cause instanceof ContractFunctionZeroDataError
|
|
4180
|
+
))
|
|
4181
|
+
return null;
|
|
4182
|
+
throw error;
|
|
4183
|
+
}
|
|
4184
|
+
const block = await publicClient.getBlock();
|
|
4185
|
+
const authorization = {
|
|
4186
|
+
domain: {
|
|
4187
|
+
name,
|
|
4188
|
+
version,
|
|
4189
|
+
chainId: transaction.chain_id,
|
|
4190
|
+
verifyingContract: tokenAddress
|
|
4191
|
+
},
|
|
4192
|
+
types: RECEIVE_AUTHORIZATION_TYPES,
|
|
4193
|
+
primaryType: "ReceiveWithAuthorization",
|
|
4194
|
+
message: {
|
|
4195
|
+
from: poster,
|
|
4196
|
+
to: transaction.to,
|
|
4197
|
+
value: BigInt(transaction.args.escrow_amount),
|
|
4198
|
+
validAfter: 0n,
|
|
4199
|
+
validBefore: block.timestamp + 600n,
|
|
4200
|
+
nonce: bytesToHex(crypto.getRandomValues(new Uint8Array(32)))
|
|
4201
|
+
}
|
|
4202
|
+
};
|
|
4203
|
+
if (domainSeparator({ domain: authorization.domain }).toLowerCase() !== tokenDomain.toLowerCase()) {
|
|
4204
|
+
throw new Error("USDC authorization domain does not match the token.");
|
|
4205
|
+
}
|
|
4206
|
+
let signature;
|
|
4207
|
+
try {
|
|
4208
|
+
signature = await input.sign(authorization);
|
|
4209
|
+
} catch (error) {
|
|
4210
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4211
|
+
let current = error;
|
|
4212
|
+
let unsupported = false;
|
|
4213
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
4214
|
+
seen.add(current);
|
|
4215
|
+
const cause = current;
|
|
4216
|
+
if (cause.code === 4001 || cause.name === "UserRejectedRequestError")
|
|
4217
|
+
throw error;
|
|
4218
|
+
unsupported ||= cause.code === 4200 || cause.code === -32601 || cause.code === -32004;
|
|
4219
|
+
current = cause.cause;
|
|
4220
|
+
}
|
|
4221
|
+
if (unsupported) return null;
|
|
4222
|
+
throw error;
|
|
4223
|
+
}
|
|
4224
|
+
if ((await recoverTypedDataAddress({ ...authorization, signature })).toLowerCase() !== poster.toLowerCase()) {
|
|
4225
|
+
throw new Error("USDC authorization was not signed by the funding Poster.");
|
|
4226
|
+
}
|
|
4227
|
+
const { r, s, yParity } = parseSignature(signature);
|
|
4228
|
+
const { validAfter, validBefore, nonce } = authorization.message;
|
|
4229
|
+
return {
|
|
4230
|
+
chain_id: transaction.chain_id,
|
|
4231
|
+
to: transaction.to,
|
|
4232
|
+
value: "0",
|
|
4233
|
+
data: encodeFunctionData({
|
|
4234
|
+
abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
|
|
4235
|
+
functionName: "createBountyWithAuthorization",
|
|
4236
|
+
args: [
|
|
4237
|
+
transaction.args.spec_commitment,
|
|
4238
|
+
authorization.message.value,
|
|
4239
|
+
BigInt(transaction.args.submission_deadline),
|
|
4240
|
+
{ validAfter, validBefore, nonce, v: yParity + 27, r, s }
|
|
4241
|
+
]
|
|
4242
|
+
})
|
|
4243
|
+
};
|
|
4244
|
+
}
|
|
3549
4245
|
function encodeAddGuardianTransaction(input) {
|
|
3550
4246
|
return {
|
|
3551
4247
|
chain_id: input.chainId,
|
|
@@ -3699,15 +4395,34 @@ var ELGORA_HUB_FEE_POLICY_ABI = [
|
|
|
3699
4395
|
// ../../packages/core/src/api/orchestrator-readiness.ts
|
|
3700
4396
|
import { z as z6 } from "zod";
|
|
3701
4397
|
var MAX_ORCHESTRATOR_ISSUES = 20;
|
|
3702
|
-
var boundedText = (maxLength) => z6.string().trim().min(1).max(maxLength);
|
|
3703
4398
|
var readinessIssueSchema = z6.object({
|
|
3704
|
-
path:
|
|
3705
|
-
message:
|
|
4399
|
+
path: z6.string().trim().min(1).max(120),
|
|
4400
|
+
message: z6.string().trim().min(1).max(1500),
|
|
4401
|
+
code: z6.enum([
|
|
4402
|
+
"acceptance_undecidable",
|
|
4403
|
+
"winner_unresolved",
|
|
4404
|
+
"source_unidentified",
|
|
4405
|
+
"domain_underspecified",
|
|
4406
|
+
"platform_conflict",
|
|
4407
|
+
"operative_instruction",
|
|
4408
|
+
"placeholder_unreplaced",
|
|
4409
|
+
"unsafe_content"
|
|
4410
|
+
]),
|
|
4411
|
+
anchor: z6.string().trim().min(1).max(120),
|
|
4412
|
+
hazard: z6.boolean()
|
|
3706
4413
|
}).strict();
|
|
3707
4414
|
var readinessResponseSchema = z6.object({
|
|
3708
|
-
|
|
3709
|
-
|
|
4415
|
+
issues: z6.array(readinessIssueSchema).max(MAX_ORCHESTRATOR_ISSUES),
|
|
4416
|
+
judgeable: z6.boolean()
|
|
3710
4417
|
}).strict();
|
|
4418
|
+
var READINESS_OUTPUT_JSON_SCHEMA = z6.toJSONSchema(readinessResponseSchema, {
|
|
4419
|
+
override: ({ jsonSchema }) => {
|
|
4420
|
+
delete jsonSchema.minLength;
|
|
4421
|
+
delete jsonSchema.maxLength;
|
|
4422
|
+
delete jsonSchema.maxItems;
|
|
4423
|
+
}
|
|
4424
|
+
});
|
|
4425
|
+
delete READINESS_OUTPUT_JSON_SCHEMA.$schema;
|
|
3711
4426
|
var providerMessageSchema = z6.object({
|
|
3712
4427
|
id: z6.string().optional(),
|
|
3713
4428
|
choices: z6.array(
|
|
@@ -3875,7 +4590,7 @@ var deliveryKeyConfigResponseSchema = z9.strictObject({
|
|
|
3875
4590
|
import { keccak256 as keccak2564, toBytes as toBytes3, zeroAddress as zeroAddress2 } from "viem";
|
|
3876
4591
|
import { z as z10 } from "zod";
|
|
3877
4592
|
var WRITTEN_VERDICT_FORMAT_VERSION = 0;
|
|
3878
|
-
var
|
|
4593
|
+
var FRONTMATTER_DELIMITER2 = "---";
|
|
3879
4594
|
var decimalSchema = z10.string().regex(/^[1-9][0-9]*$/, "Must be a positive integer").transform(Number).refine(Number.isSafeInteger, "Must be a safe integer");
|
|
3880
4595
|
var bountyIdSchema2 = z10.string().regex(/^(0|[1-9][0-9]*)$/, "Must be a non-negative integer");
|
|
3881
4596
|
var addressSchema2 = z10.string().refine(isHexAddress, "Must be a hex-encoded address").refine((value) => value === value.toLowerCase(), "Must use lowercase hex").transform((value) => value);
|
|
@@ -3907,7 +4622,7 @@ var writtenVerdictSchema = z10.discriminatedUnion("outcome", [
|
|
|
3907
4622
|
}).strict()
|
|
3908
4623
|
]);
|
|
3909
4624
|
function buildWrittenVerdictMarkdown(input) {
|
|
3910
|
-
const common = `${
|
|
4625
|
+
const common = `${FRONTMATTER_DELIMITER2}
|
|
3911
4626
|
format_version: ${WRITTEN_VERDICT_FORMAT_VERSION}
|
|
3912
4627
|
chain_id: ${input.config.chainId}
|
|
3913
4628
|
hub_address: ${input.config.hubAddress.toLowerCase()}
|
|
@@ -3917,7 +4632,7 @@ outcome: ${input.outcome}`;
|
|
|
3917
4632
|
awarded_submission_commitment: ${input.awardedSubmissionCommitment.toLowerCase()}` : `winner: ${zeroAddress2}`;
|
|
3918
4633
|
const markdown = `${common}
|
|
3919
4634
|
${position}
|
|
3920
|
-
${
|
|
4635
|
+
${FRONTMATTER_DELIMITER2}
|
|
3921
4636
|
${input.verdictBody}`;
|
|
3922
4637
|
parseWrittenVerdictMarkdown(markdown);
|
|
3923
4638
|
return markdown;
|
|
@@ -3928,11 +4643,11 @@ function deriveReportCommitment(markdown) {
|
|
|
3928
4643
|
function parseWrittenVerdictMarkdown(markdown) {
|
|
3929
4644
|
const closingDelimiter = markdown.indexOf(
|
|
3930
4645
|
`
|
|
3931
|
-
${
|
|
4646
|
+
${FRONTMATTER_DELIMITER2}
|
|
3932
4647
|
`,
|
|
3933
|
-
|
|
4648
|
+
FRONTMATTER_DELIMITER2.length + 1
|
|
3934
4649
|
);
|
|
3935
|
-
if (!markdown.startsWith(`${
|
|
4650
|
+
if (!markdown.startsWith(`${FRONTMATTER_DELIMITER2}
|
|
3936
4651
|
`) || closingDelimiter < 0) {
|
|
3937
4652
|
throw invalidWrittenVerdict(
|
|
3938
4653
|
"frontmatter",
|
|
@@ -3940,12 +4655,12 @@ ${FRONTMATTER_DELIMITER}
|
|
|
3940
4655
|
);
|
|
3941
4656
|
}
|
|
3942
4657
|
const frontmatter = markdown.slice(
|
|
3943
|
-
|
|
4658
|
+
FRONTMATTER_DELIMITER2.length + 1,
|
|
3944
4659
|
closingDelimiter
|
|
3945
4660
|
);
|
|
3946
4661
|
const rawVerdict = {
|
|
3947
4662
|
verdict_body: markdown.slice(
|
|
3948
|
-
closingDelimiter +
|
|
4663
|
+
closingDelimiter + FRONTMATTER_DELIMITER2.length + 2
|
|
3949
4664
|
)
|
|
3950
4665
|
};
|
|
3951
4666
|
for (const [index, line] of frontmatter.split("\n").entries()) {
|
|
@@ -4018,14 +4733,12 @@ function assertChainId(actual, expected) {
|
|
|
4018
4733
|
throw new Error(`RPC returned chain_id ${actual}, expected ${expected}`);
|
|
4019
4734
|
}
|
|
4020
4735
|
}
|
|
4736
|
+
var SIGNING_BLOCK_OFFSET = 3n;
|
|
4021
4737
|
async function fetchSignableBlock(client) {
|
|
4022
|
-
const
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
);
|
|
4027
|
-
}
|
|
4028
|
-
return { number: block.number, hash: block.hash };
|
|
4738
|
+
const head = await client.getBlockNumber();
|
|
4739
|
+
const anchorNumber = head > SIGNING_BLOCK_OFFSET ? head - SIGNING_BLOCK_OFFSET : 0n;
|
|
4740
|
+
const anchor = await client.getBlock({ blockNumber: anchorNumber });
|
|
4741
|
+
return { number: anchor.number, hash: anchor.hash };
|
|
4029
4742
|
}
|
|
4030
4743
|
async function assertHubDeployment(client, chainId, hubAddress) {
|
|
4031
4744
|
const [actualChainId, hubCode] = await Promise.all([
|
|
@@ -4068,7 +4781,7 @@ function withDeploymentDefaults(env) {
|
|
|
4068
4781
|
|
|
4069
4782
|
// src/claim.ts
|
|
4070
4783
|
var CLAIMANT_ADDRESS_FLAG = "--claimant-address";
|
|
4071
|
-
async function
|
|
4784
|
+
async function main2(env = process.env, argv = process.argv.slice(2)) {
|
|
4072
4785
|
const args = readClaimArgs(argv);
|
|
4073
4786
|
const config = loadClaimConfig(env, args.claimantAddress);
|
|
4074
4787
|
const account = config.claimantPrivateKey ? privateKeyToAccount(config.claimantPrivateKey) : null;
|
|
@@ -4470,12 +5183,16 @@ function formatApiError(body) {
|
|
|
4470
5183
|
const apiError = body.error;
|
|
4471
5184
|
const parts = [String(apiError.message)];
|
|
4472
5185
|
if (Array.isArray(apiError.issues) && apiError.issues.length > 0) {
|
|
4473
|
-
|
|
5186
|
+
for (const issue of apiError.issues) {
|
|
5187
|
+
if (!issue || typeof issue !== "object" || typeof issue.message !== "string")
|
|
5188
|
+
continue;
|
|
5189
|
+
parts.push(`${issue.path || "request"}: ${issue.message}`);
|
|
5190
|
+
}
|
|
4474
5191
|
}
|
|
4475
5192
|
if (apiError.next_action !== void 0 && apiError.next_action !== null) {
|
|
4476
5193
|
parts.push(`next action: ${String(apiError.next_action)}`);
|
|
4477
5194
|
}
|
|
4478
|
-
return parts.join("
|
|
5195
|
+
return parts.join("\n");
|
|
4479
5196
|
}
|
|
4480
5197
|
return JSON.stringify(body);
|
|
4481
5198
|
}
|
|
@@ -4579,7 +5296,7 @@ import {
|
|
|
4579
5296
|
// ../../packages/core/src/auth/signature.ts
|
|
4580
5297
|
import {
|
|
4581
5298
|
isAddressEqual,
|
|
4582
|
-
recoverTypedDataAddress
|
|
5299
|
+
recoverTypedDataAddress as recoverTypedDataAddress2
|
|
4583
5300
|
} from "viem";
|
|
4584
5301
|
|
|
4585
5302
|
// ../../packages/core/src/auth/request-approval-header.ts
|
|
@@ -4700,7 +5417,7 @@ import { mkdir, writeFile } from "fs/promises";
|
|
|
4700
5417
|
import path from "path";
|
|
4701
5418
|
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
4702
5419
|
var DEFAULT_OUTPUT_ROOT = "./guardian-open";
|
|
4703
|
-
async function
|
|
5420
|
+
async function main3(env = process.env, argv = process.argv.slice(2)) {
|
|
4704
5421
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
|
|
4705
5422
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
4706
5423
|
const config = loadClientDeploymentConfig(
|
|
@@ -4791,7 +5508,11 @@ async function main2(env = process.env, argv = process.argv.slice(2)) {
|
|
|
4791
5508
|
bounty_id: args.bountyId,
|
|
4792
5509
|
solver: args.solver,
|
|
4793
5510
|
submission_commitment: args.submissionCommitment,
|
|
4794
|
-
artifact_count: opened.artifacts.length
|
|
5511
|
+
artifact_count: opened.artifacts.length,
|
|
5512
|
+
// The budget the Guardian has to vet these artifacts against before
|
|
5513
|
+
// extracting any archive among them. Reported here so it comes from the
|
|
5514
|
+
// running client rather than a possibly older copy of the skill.
|
|
5515
|
+
max_extracted_bytes: MAX_SOLVER_SUBMISSION_EXTRACTED_BYTES
|
|
4795
5516
|
})
|
|
4796
5517
|
);
|
|
4797
5518
|
}
|
|
@@ -4857,7 +5578,7 @@ function readGuardianPrivateKeys(env) {
|
|
|
4857
5578
|
|
|
4858
5579
|
// src/guardian-deliver-key.ts
|
|
4859
5580
|
var USAGE = "Usage: elgora-cli guardian:deliver-key <bounty_id> <solver> <submission_commitment>";
|
|
4860
|
-
async function
|
|
5581
|
+
async function main4(env = process.env, argv = process.argv.slice(2)) {
|
|
4861
5582
|
const { apiBaseUrl, argv: positionalArgs } = extractApiBaseUrlFlag(argv);
|
|
4862
5583
|
const { bountyId, solver, submissionCommitment } = readArgs(positionalArgs);
|
|
4863
5584
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
@@ -4999,6 +5720,7 @@ var VERDICT_FIELDS = `
|
|
|
4999
5720
|
awardedSubmissionCommitment
|
|
5000
5721
|
reportCommitment
|
|
5001
5722
|
payoutData
|
|
5723
|
+
committedTxHash
|
|
5002
5724
|
committedAt
|
|
5003
5725
|
updatedAt
|
|
5004
5726
|
`;
|
|
@@ -5458,6 +6180,7 @@ function mapVerdict(row) {
|
|
|
5458
6180
|
),
|
|
5459
6181
|
reportCommitment: asHex(record.reportCommitment, "reportCommitment"),
|
|
5460
6182
|
payoutData: asHex(record.payoutData, "payoutData"),
|
|
6183
|
+
committedTxHash: asHex(record.committedTxHash, "committedTxHash"),
|
|
5461
6184
|
committedAt: asString(record.committedAt, "committedAt"),
|
|
5462
6185
|
updatedAt: asString(record.updatedAt, "updatedAt")
|
|
5463
6186
|
};
|
|
@@ -5686,7 +6409,7 @@ function asNullableOutcome(value, field) {
|
|
|
5686
6409
|
|
|
5687
6410
|
// src/guardian-judgeable.ts
|
|
5688
6411
|
import { zeroAddress as zeroAddress3 } from "viem";
|
|
5689
|
-
async function
|
|
6412
|
+
async function main5(env = process.env, argv = process.argv.slice(2)) {
|
|
5690
6413
|
const guardian = readGuardianAddress(argv);
|
|
5691
6414
|
const config = readConfig(withDeploymentDefaults(env));
|
|
5692
6415
|
const snapshot = await createElgoraSubgraphClient({
|
|
@@ -5780,10 +6503,10 @@ function requiredUrl(env, name) {
|
|
|
5780
6503
|
}
|
|
5781
6504
|
|
|
5782
6505
|
// src/guardian-prove-roster-key.ts
|
|
5783
|
-
import { readFile } from "fs/promises";
|
|
5784
|
-
import { text as
|
|
6506
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
6507
|
+
import { text as readStdinText2 } from "stream/consumers";
|
|
5785
6508
|
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
5786
|
-
async function
|
|
6509
|
+
async function main6(env = process.env, argv = process.argv.slice(2)) {
|
|
5787
6510
|
const [path5] = argv;
|
|
5788
6511
|
if (!path5) {
|
|
5789
6512
|
throw new Error(
|
|
@@ -5791,7 +6514,7 @@ async function main5(env = process.env, argv = process.argv.slice(2)) {
|
|
|
5791
6514
|
);
|
|
5792
6515
|
}
|
|
5793
6516
|
const challenge = parseChallenge(
|
|
5794
|
-
path5 === "-" ? await
|
|
6517
|
+
path5 === "-" ? await readStdinText2(process.stdin) : await readFile2(path5, "utf8")
|
|
5795
6518
|
);
|
|
5796
6519
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
5797
6520
|
const { chainId, hubAddress } = loadPublicDeploymentConfig(envWithDefaults);
|
|
@@ -5858,7 +6581,7 @@ function parseChallenge(text) {
|
|
|
5858
6581
|
}
|
|
5859
6582
|
|
|
5860
6583
|
// src/guardian-verdict.ts
|
|
5861
|
-
import { readFile as
|
|
6584
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
5862
6585
|
import { zeroAddress as zeroAddress4 } from "viem";
|
|
5863
6586
|
import { privateKeyToAccount as privateKeyToAccount6 } from "viem/accounts";
|
|
5864
6587
|
var OUTCOME = {
|
|
@@ -5895,7 +6618,7 @@ function parseVerdictArgs(argv) {
|
|
|
5895
6618
|
}
|
|
5896
6619
|
throw usageError();
|
|
5897
6620
|
}
|
|
5898
|
-
async function
|
|
6621
|
+
async function main7(env = process.env, argv = process.argv.slice(2)) {
|
|
5899
6622
|
const { apiBaseUrl: apiBaseUrlOverride, argv: positionalArgs } = extractApiBaseUrlFlag(argv);
|
|
5900
6623
|
const args = parseVerdictArgs(positionalArgs);
|
|
5901
6624
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
@@ -5915,7 +6638,7 @@ async function main6(env = process.env, argv = process.argv.slice(2)) {
|
|
|
5915
6638
|
const publicClient = createChainPublicClient(config.chainId, rpcUrl);
|
|
5916
6639
|
const walletClient = createChainWalletClient(config.chainId, rpcUrl, account);
|
|
5917
6640
|
await assertHubDeployment(publicClient, config.chainId, config.hubAddress);
|
|
5918
|
-
const verdictBody = await
|
|
6641
|
+
const verdictBody = await readFile3(args.verdictBodyPath, "utf8");
|
|
5919
6642
|
const markdown = args.outcome === "awarded" ? buildWrittenVerdictMarkdown({
|
|
5920
6643
|
config,
|
|
5921
6644
|
bountyId: args.bountyId,
|
|
@@ -6184,7 +6907,7 @@ import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
|
6184
6907
|
import path2 from "path";
|
|
6185
6908
|
import { privateKeyToAccount as privateKeyToAccount7 } from "viem/accounts";
|
|
6186
6909
|
var DEFAULT_OUTPUT_ROOT2 = "./winning-submission";
|
|
6187
|
-
async function
|
|
6910
|
+
async function main8(env = process.env, argv = process.argv.slice(2)) {
|
|
6188
6911
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
|
|
6189
6912
|
const args = readPosterOpenWinningSubmissionArgs(remainingArgv);
|
|
6190
6913
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
@@ -6323,25 +7046,26 @@ function required3(env, name) {
|
|
|
6323
7046
|
}
|
|
6324
7047
|
|
|
6325
7048
|
// src/publish-fund.ts
|
|
6326
|
-
import { readFile as
|
|
6327
|
-
import { text as
|
|
7049
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
7050
|
+
import { text as readStdinText3 } from "stream/consumers";
|
|
6328
7051
|
import {
|
|
6329
|
-
|
|
7052
|
+
decodeFunctionData,
|
|
7053
|
+
parseAbi as parseAbi2,
|
|
6330
7054
|
parseEventLogs
|
|
6331
7055
|
} from "viem";
|
|
6332
7056
|
import { privateKeyToAccount as privateKeyToAccount8 } from "viem/accounts";
|
|
6333
7057
|
var PUBLISH_FUND_USAGE = "Usage: elgora-cli poster:publish-fund <bounty_challenge_md_path>\n or: elgora-cli poster:publish-fund -";
|
|
6334
|
-
var usdcAbi =
|
|
7058
|
+
var usdcAbi = parseAbi2([
|
|
6335
7059
|
"function approve(address spender,uint256 value) returns (bool)",
|
|
6336
7060
|
"function balanceOf(address account) view returns (uint256)"
|
|
6337
7061
|
]);
|
|
6338
|
-
async function
|
|
7062
|
+
async function main9(env = process.env, argv = process.argv.slice(2)) {
|
|
6339
7063
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
|
|
6340
7064
|
const config = loadPublishFundConfig(
|
|
6341
7065
|
withDeploymentDefaults(env),
|
|
6342
7066
|
apiBaseUrlOverride
|
|
6343
7067
|
);
|
|
6344
|
-
const bountyChallengeMarkdown = await
|
|
7068
|
+
const bountyChallengeMarkdown = await readBountyChallengeMarkdown2(remainingArgv);
|
|
6345
7069
|
const account = privateKeyToAccount8(config.posterPrivateKey);
|
|
6346
7070
|
const publicClient = createChainPublicClient(config.chainId, config.rpcUrl);
|
|
6347
7071
|
const walletClient = createChainWalletClient(
|
|
@@ -6415,35 +7139,58 @@ async function main8(env = process.env, argv = process.argv.slice(2)) {
|
|
|
6415
7139
|
`Poster has insufficient escrow token balance on chain ${config.chainId}: required=${escrowAmount.toString()} available=${usdcBalance.toString()}`
|
|
6416
7140
|
);
|
|
6417
7141
|
}
|
|
6418
|
-
const
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
const approveReceipt = await publicClient.waitForTransactionReceipt({
|
|
6425
|
-
hash: approveHash
|
|
7142
|
+
const authorized = await prepareAuthorizedCreateBountyTransaction({
|
|
7143
|
+
transaction: prepared.create_bounty_transaction,
|
|
7144
|
+
tokenAddress: config.escrowTokenAddress,
|
|
7145
|
+
poster: account.address,
|
|
7146
|
+
publicClient,
|
|
7147
|
+
sign: (typedData) => account.signTypedData(typedData)
|
|
6426
7148
|
});
|
|
6427
|
-
|
|
6428
|
-
|
|
7149
|
+
let createBountyHash;
|
|
7150
|
+
if (authorized) {
|
|
7151
|
+
const call = decodeFunctionData({
|
|
7152
|
+
abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
|
|
7153
|
+
data: authorized.data
|
|
7154
|
+
});
|
|
7155
|
+
if (call.functionName !== "createBountyWithAuthorization") {
|
|
7156
|
+
throw new Error("Expected an authorization funding call.");
|
|
7157
|
+
}
|
|
7158
|
+
createBountyHash = await walletClient.writeContract({
|
|
7159
|
+
address: authorized.to,
|
|
7160
|
+
abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
|
|
7161
|
+
...call
|
|
7162
|
+
});
|
|
7163
|
+
} else {
|
|
7164
|
+
const approveHash = await walletClient.writeContract({
|
|
7165
|
+
address: config.escrowTokenAddress,
|
|
7166
|
+
abi: usdcAbi,
|
|
7167
|
+
functionName: "approve",
|
|
7168
|
+
args: [config.hubAddress, escrowAmount]
|
|
7169
|
+
});
|
|
7170
|
+
const approveReceipt = await publicClient.waitForTransactionReceipt({
|
|
7171
|
+
hash: approveHash
|
|
7172
|
+
});
|
|
7173
|
+
if (approveReceipt.status !== "success") {
|
|
7174
|
+
throw new Error(`approve transaction reverted: ${approveHash}`);
|
|
7175
|
+
}
|
|
7176
|
+
console.log(
|
|
7177
|
+
JSON.stringify({
|
|
7178
|
+
event: "publish_fund.usdc_approved",
|
|
7179
|
+
tx_hash: approveHash,
|
|
7180
|
+
block_number: approveReceipt.blockNumber.toString()
|
|
7181
|
+
})
|
|
7182
|
+
);
|
|
7183
|
+
createBountyHash = await walletClient.writeContract({
|
|
7184
|
+
address: config.hubAddress,
|
|
7185
|
+
abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
|
|
7186
|
+
functionName: "createBounty",
|
|
7187
|
+
args: [
|
|
7188
|
+
localSpecCommitment,
|
|
7189
|
+
escrowAmount,
|
|
7190
|
+
BigInt(prepared.create_bounty_transaction.args.submission_deadline)
|
|
7191
|
+
]
|
|
7192
|
+
});
|
|
6429
7193
|
}
|
|
6430
|
-
console.log(
|
|
6431
|
-
JSON.stringify({
|
|
6432
|
-
event: "publish_fund.usdc_approved",
|
|
6433
|
-
tx_hash: approveHash,
|
|
6434
|
-
block_number: approveReceipt.blockNumber.toString()
|
|
6435
|
-
})
|
|
6436
|
-
);
|
|
6437
|
-
const createBountyHash = await walletClient.writeContract({
|
|
6438
|
-
address: config.hubAddress,
|
|
6439
|
-
abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
|
|
6440
|
-
functionName: "createBounty",
|
|
6441
|
-
args: [
|
|
6442
|
-
localSpecCommitment,
|
|
6443
|
-
escrowAmount,
|
|
6444
|
-
BigInt(prepared.create_bounty_transaction.args.submission_deadline)
|
|
6445
|
-
]
|
|
6446
|
-
});
|
|
6447
7194
|
const createBountyReceipt = await publicClient.waitForTransactionReceipt({
|
|
6448
7195
|
hash: createBountyHash
|
|
6449
7196
|
});
|
|
@@ -6493,15 +7240,15 @@ async function requestPreparePublication(input) {
|
|
|
6493
7240
|
);
|
|
6494
7241
|
return bountyPublicationResponseSchema.parse(body);
|
|
6495
7242
|
}
|
|
6496
|
-
async function
|
|
7243
|
+
async function readBountyChallengeMarkdown2(argv) {
|
|
6497
7244
|
const path5 = argv[0];
|
|
6498
7245
|
if (!path5) {
|
|
6499
7246
|
throw new Error(PUBLISH_FUND_USAGE);
|
|
6500
7247
|
}
|
|
6501
7248
|
if (path5 === "-") {
|
|
6502
|
-
return
|
|
7249
|
+
return readStdinText3(process.stdin);
|
|
6503
7250
|
}
|
|
6504
|
-
return
|
|
7251
|
+
return readFile4(path5, "utf8");
|
|
6505
7252
|
}
|
|
6506
7253
|
function loadPublishFundConfig(env, apiBaseUrlOverride) {
|
|
6507
7254
|
const clientConfig = loadClientDeploymentConfig(env, apiBaseUrlOverride);
|
|
@@ -6534,7 +7281,7 @@ import { isAddress as isAddress4 } from "viem";
|
|
|
6534
7281
|
import { privateKeyToAccount as privateKeyToAccount9 } from "viem/accounts";
|
|
6535
7282
|
var ROSTER_ADMIN_ADDRESS_FLAG = "--roster-admin-address";
|
|
6536
7283
|
var ENCRYPTION_PUBLIC_KEY_RE = /^[A-Za-z0-9_-]{43}$/;
|
|
6537
|
-
async function
|
|
7284
|
+
async function main10(env = process.env, argv = process.argv.slice(2)) {
|
|
6538
7285
|
const args = parseRosterAddGuardianArgs(argv);
|
|
6539
7286
|
const config = loadRosterAddGuardianConfig(env, args.rosterAdminAddress);
|
|
6540
7287
|
const account = config.rosterAdminPrivateKey ? privateKeyToAccount9(config.rosterAdminPrivateKey) : null;
|
|
@@ -6691,7 +7438,7 @@ function usageError2() {
|
|
|
6691
7438
|
// src/roster-prepare-guardian-proof.ts
|
|
6692
7439
|
import { isAddress as isAddress5 } from "viem";
|
|
6693
7440
|
var ENCRYPTION_PUBLIC_KEY_RE2 = /^[A-Za-z0-9_-]{43}$/;
|
|
6694
|
-
async function
|
|
7441
|
+
async function main11(env = process.env, argv = process.argv.slice(2)) {
|
|
6695
7442
|
const args = parseArgs(argv);
|
|
6696
7443
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
6697
7444
|
const { chainId, hubAddress } = loadPublicDeploymentConfig(envWithDefaults);
|
|
@@ -6738,17 +7485,17 @@ function usageError3() {
|
|
|
6738
7485
|
}
|
|
6739
7486
|
|
|
6740
7487
|
// src/roster-verify-guardian-proof.ts
|
|
6741
|
-
import { readFile as
|
|
7488
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
6742
7489
|
import { isAddress as isAddress6, isHex as isHex5, recoverMessageAddress } from "viem";
|
|
6743
|
-
async function
|
|
7490
|
+
async function main12(_env = process.env, argv = process.argv.slice(2)) {
|
|
6744
7491
|
const [preparedPath, responsePath] = argv;
|
|
6745
7492
|
if (!preparedPath || !responsePath) {
|
|
6746
7493
|
throw new Error(
|
|
6747
7494
|
"Usage: elgora-cli roster:verify-guardian-proof <prepared_json_path> <response_json_path>"
|
|
6748
7495
|
);
|
|
6749
7496
|
}
|
|
6750
|
-
const prepared = parsePrepared(await
|
|
6751
|
-
const response = parseResponse(await
|
|
7497
|
+
const prepared = parsePrepared(await readFile5(preparedPath, "utf8"));
|
|
7498
|
+
const response = parseResponse(await readFile5(responsePath, "utf8"));
|
|
6752
7499
|
const fixture = base64UrlToBytes(prepared.fixture);
|
|
6753
7500
|
const signedPayload = buildGuardianRosterKeyProofSignedPayload(
|
|
6754
7501
|
prepared.message,
|
|
@@ -6830,11 +7577,11 @@ function get(value, key) {
|
|
|
6830
7577
|
}
|
|
6831
7578
|
|
|
6832
7579
|
// src/spec-commitment.ts
|
|
6833
|
-
import { readFile as
|
|
6834
|
-
import { text as
|
|
7580
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
7581
|
+
import { text as readStdinText4 } from "stream/consumers";
|
|
6835
7582
|
var SPEC_COMMITMENT_USAGE = "Usage: elgora-cli spec-commitment <bounty_challenge_md_path>\n or: elgora-cli spec-commitment -";
|
|
6836
|
-
async function
|
|
6837
|
-
const markdown = await
|
|
7583
|
+
async function main13(_env = process.env, argv = process.argv.slice(2)) {
|
|
7584
|
+
const markdown = await readBountyChallengeMarkdown3(argv);
|
|
6838
7585
|
const specCommitment = deriveBountyChallengeContentCommitment(markdown);
|
|
6839
7586
|
console.log(
|
|
6840
7587
|
JSON.stringify({
|
|
@@ -6844,19 +7591,19 @@ async function main12(_env = process.env, argv = process.argv.slice(2)) {
|
|
|
6844
7591
|
})
|
|
6845
7592
|
);
|
|
6846
7593
|
}
|
|
6847
|
-
async function
|
|
7594
|
+
async function readBountyChallengeMarkdown3(argv) {
|
|
6848
7595
|
const path5 = argv[0];
|
|
6849
7596
|
if (!path5) {
|
|
6850
7597
|
throw new Error(SPEC_COMMITMENT_USAGE);
|
|
6851
7598
|
}
|
|
6852
7599
|
if (path5 === "-") {
|
|
6853
|
-
return
|
|
7600
|
+
return readStdinText4(process.stdin);
|
|
6854
7601
|
}
|
|
6855
|
-
return
|
|
7602
|
+
return readFile6(path5, "utf8");
|
|
6856
7603
|
}
|
|
6857
7604
|
|
|
6858
7605
|
// src/submit.ts
|
|
6859
|
-
import { readdir, readFile as
|
|
7606
|
+
import { readdir, readFile as readFile7, stat } from "fs/promises";
|
|
6860
7607
|
import path3 from "path";
|
|
6861
7608
|
import { createInterface } from "readline";
|
|
6862
7609
|
import { isAddress as isAddress7 } from "viem";
|
|
@@ -6910,7 +7657,7 @@ var MEDIA_TYPE_BY_EXTENSION = {
|
|
|
6910
7657
|
diff: "text/x-diff"
|
|
6911
7658
|
};
|
|
6912
7659
|
var DEFAULT_MEDIA_TYPE = "application/octet-stream";
|
|
6913
|
-
async function
|
|
7660
|
+
async function main14(env = process.env, argv = process.argv.slice(2)) {
|
|
6914
7661
|
const { solverAddress, argv: argvWithoutSolverAddress } = extractSolverAddressFlag(argv);
|
|
6915
7662
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argvWithoutSolverAddress);
|
|
6916
7663
|
const config = loadSubmitConfig(env, apiBaseUrlOverride, solverAddress);
|
|
@@ -6987,7 +7734,7 @@ async function main13(env = process.env, argv = process.argv.slice(2)) {
|
|
|
6987
7734
|
});
|
|
6988
7735
|
if (ciphertext.byteLength > MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES) {
|
|
6989
7736
|
throw new Error(
|
|
6990
|
-
`Encrypted submission is ${ciphertext.byteLength} bytes and exceeds Elgora's
|
|
7737
|
+
`Encrypted submission is ${ciphertext.byteLength} bytes and exceeds Elgora's ${formatMebibytes(MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES)} encrypted submission limit. Reduce the final artifact package before submitting.`
|
|
6991
7738
|
);
|
|
6992
7739
|
}
|
|
6993
7740
|
const prepared = await (async () => {
|
|
@@ -7274,18 +8021,23 @@ async function readArtifactDirectory(artifactDir) {
|
|
|
7274
8021
|
if (entries.length === 0) {
|
|
7275
8022
|
throw new Error(`No files found in artifact directory: ${artifactDir}`);
|
|
7276
8023
|
}
|
|
8024
|
+
if (entries.length > MAX_SOLVER_SUBMISSION_FILES) {
|
|
8025
|
+
throw new Error(
|
|
8026
|
+
`Artifact directory holds ${entries.length} files and exceeds Elgora's maximum of ${MAX_SOLVER_SUBMISSION_FILES} files per Submission. Combine them into fewer files, if the challenge allows it, before submitting.`
|
|
8027
|
+
);
|
|
8028
|
+
}
|
|
7277
8029
|
let plaintextByteLength = 0;
|
|
7278
8030
|
for (const entry of entries) {
|
|
7279
8031
|
plaintextByteLength += (await stat(path3.join(artifactDir, entry.name))).size;
|
|
7280
|
-
if (plaintextByteLength >
|
|
8032
|
+
if (plaintextByteLength > MAX_SOLVER_SUBMISSION_ARTIFACT_BYTES) {
|
|
7281
8033
|
throw new Error(
|
|
7282
|
-
`Artifact directory exceeds Elgora's
|
|
8034
|
+
`Artifact directory exceeds Elgora's ${formatMebibytes(MAX_SOLVER_SUBMISSION_ARTIFACT_BYTES)} of files per Submission, which is what fits the ${formatMebibytes(MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES)} encrypted submission limit once packaged. Reduce the final artifact package before submitting.`
|
|
7283
8035
|
);
|
|
7284
8036
|
}
|
|
7285
8037
|
}
|
|
7286
8038
|
return Promise.all(
|
|
7287
8039
|
entries.map(async (entry) => {
|
|
7288
|
-
const bytes = await
|
|
8040
|
+
const bytes = await readFile7(path3.join(artifactDir, entry.name));
|
|
7289
8041
|
return {
|
|
7290
8042
|
name: entry.name,
|
|
7291
8043
|
media_type: guessMediaType(entry.name),
|
|
@@ -7294,6 +8046,9 @@ async function readArtifactDirectory(artifactDir) {
|
|
|
7294
8046
|
})
|
|
7295
8047
|
);
|
|
7296
8048
|
}
|
|
8049
|
+
function formatMebibytes(byteLength) {
|
|
8050
|
+
return `${byteLength / 1024 / 1024} MiB`;
|
|
8051
|
+
}
|
|
7297
8052
|
function guessMediaType(fileName) {
|
|
7298
8053
|
const extension = path3.extname(fileName).slice(1).toLowerCase();
|
|
7299
8054
|
return MEDIA_TYPE_BY_EXTENSION[extension] ?? DEFAULT_MEDIA_TYPE;
|
|
@@ -7395,7 +8150,7 @@ function readTimelockRevealDelaySeconds(env) {
|
|
|
7395
8150
|
|
|
7396
8151
|
// src/verification-record.ts
|
|
7397
8152
|
var VERIFICATION_RECORD_USAGE = "Usage: elgora-cli verification-record <bounty_id>";
|
|
7398
|
-
async function
|
|
8153
|
+
async function main15(env = process.env, argv = process.argv.slice(2)) {
|
|
7399
8154
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
|
|
7400
8155
|
const args = readVerificationRecordArgs(remainingArgv);
|
|
7401
8156
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
@@ -7460,6 +8215,9 @@ function usageError4() {
|
|
|
7460
8215
|
return new Error(VERIFICATION_RECORD_USAGE);
|
|
7461
8216
|
}
|
|
7462
8217
|
|
|
8218
|
+
// src/version.ts
|
|
8219
|
+
var CLI_VERSION = true ? "7.4.0" : "0.0.0-dev";
|
|
8220
|
+
|
|
7463
8221
|
// src/cli.ts
|
|
7464
8222
|
var dotenvPath = path4.resolve(process.env.DOTENV_CONFIG_PATH ?? ".env");
|
|
7465
8223
|
var dotenvResult = loadDotenv({
|
|
@@ -7471,20 +8229,21 @@ process.stderr.write(
|
|
|
7471
8229
|
`
|
|
7472
8230
|
);
|
|
7473
8231
|
var COMMANDS = {
|
|
7474
|
-
"guardian:judgeable":
|
|
7475
|
-
"guardian:open":
|
|
7476
|
-
"guardian:prove-roster-key":
|
|
7477
|
-
"guardian:verdict":
|
|
7478
|
-
"guardian:deliver-key":
|
|
7479
|
-
"poster:open-winning-submission":
|
|
7480
|
-
"poster:publish-fund":
|
|
7481
|
-
"roster:add-guardian":
|
|
7482
|
-
"roster:prepare-guardian-proof":
|
|
7483
|
-
"roster:verify-guardian-proof":
|
|
7484
|
-
"solver:submit":
|
|
7485
|
-
claim:
|
|
7486
|
-
"
|
|
7487
|
-
"
|
|
8232
|
+
"guardian:judgeable": main5,
|
|
8233
|
+
"guardian:open": main3,
|
|
8234
|
+
"guardian:prove-roster-key": main6,
|
|
8235
|
+
"guardian:verdict": main7,
|
|
8236
|
+
"guardian:deliver-key": main4,
|
|
8237
|
+
"poster:open-winning-submission": main8,
|
|
8238
|
+
"poster:publish-fund": main9,
|
|
8239
|
+
"roster:add-guardian": main10,
|
|
8240
|
+
"roster:prepare-guardian-proof": main11,
|
|
8241
|
+
"roster:verify-guardian-proof": main12,
|
|
8242
|
+
"solver:submit": main14,
|
|
8243
|
+
claim: main2,
|
|
8244
|
+
"check-page": main,
|
|
8245
|
+
"spec-commitment": main13,
|
|
8246
|
+
"verification-record": main15
|
|
7488
8247
|
};
|
|
7489
8248
|
var COMMAND_HELP = {
|
|
7490
8249
|
"guardian:judgeable": {
|
|
@@ -7556,7 +8315,7 @@ var COMMAND_HELP = {
|
|
|
7556
8315
|
"poster:publish-fund": {
|
|
7557
8316
|
usage: "elgora-cli poster:publish-fund <bounty_challenge_md_path|->",
|
|
7558
8317
|
title: "Publish an approved challenge and fund its bounty.",
|
|
7559
|
-
description: "Read the exact approved bounty_challenge.md bytes
|
|
8318
|
+
description: "Read the exact approved bounty_challenge.md bytes and publish them through\n Elgora's API. Eligible wallets sign a USDC authorization, then send\n one funding transaction and pay gas. No new allowance is required;\n this authorization path does not revoke existing allowances.\n Unsupported accounts use approval then creation. Pass '-' to read stdin.",
|
|
7560
8319
|
required: ["ELGORA_POSTER_PRIVATE_KEY"],
|
|
7561
8320
|
defaulted: [
|
|
7562
8321
|
"ELGORA_CHAIN_ID",
|
|
@@ -7621,6 +8380,12 @@ var COMMAND_HELP = {
|
|
|
7621
8380
|
required: [],
|
|
7622
8381
|
defaulted: ["ELGORA_CHAIN_ID", "ELGORA_HUB_ADDRESS", "ELGORA_RPC_URL"]
|
|
7623
8382
|
},
|
|
8383
|
+
"check-page": {
|
|
8384
|
+
usage: "elgora-cli check-page <bounty_challenge_md_path|->",
|
|
8385
|
+
title: "Check a challenge page's structure against Elgora's rules.",
|
|
8386
|
+
description: "Run the same structural module the API runs before its readiness\n review: frontmatter, a known profile, and every declared tightening.\n Reports each issue with a stable code and the field that fixes it,\n and exits non-zero when any is found. A Guardian runs this against a\n bounty's committed bytes before judging, because a page can reach\n chain without passing publication. It checks structure only: whether\n the page tries to instruct you, or carries unsafe content, is a\n reading you make yourself. Pass '-' to read from stdin. Makes no\n network call and needs no configuration or secret.",
|
|
8387
|
+
required: []
|
|
8388
|
+
},
|
|
7624
8389
|
"spec-commitment": {
|
|
7625
8390
|
usage: "elgora-cli spec-commitment <bounty_challenge_md_path|->",
|
|
7626
8391
|
title: "Reproduce a challenge's spec_commitment offline.",
|
|
@@ -7648,6 +8413,11 @@ async function run(rawArgv) {
|
|
|
7648
8413
|
printUsage();
|
|
7649
8414
|
return;
|
|
7650
8415
|
}
|
|
8416
|
+
if (subcommand === "--version" || subcommand === "-v" || subcommand === "version") {
|
|
8417
|
+
process.stdout.write(`${CLI_VERSION}
|
|
8418
|
+
`);
|
|
8419
|
+
return;
|
|
8420
|
+
}
|
|
7651
8421
|
if (subcommand === "help") {
|
|
7652
8422
|
printCommandHelpOrUsage(rest[0]);
|
|
7653
8423
|
return;
|
|
@@ -7680,7 +8450,8 @@ function printUsage() {
|
|
|
7680
8450
|
`Every command accepts ${NETWORK_FLAG} <name|chain id> to select the`,
|
|
7681
8451
|
`deployment for that invocation. Supported: ${NETWORK_NAMES_HINT}.`,
|
|
7682
8452
|
"",
|
|
7683
|
-
"Run `elgora-cli help <command>` for exact arguments and configuration."
|
|
8453
|
+
"Run `elgora-cli help <command>` for exact arguments and configuration.",
|
|
8454
|
+
"Run `elgora-cli --version` to print this release's version."
|
|
7684
8455
|
].join("\n")}
|
|
7685
8456
|
`
|
|
7686
8457
|
);
|
|
@@ -7733,7 +8504,7 @@ run(process.argv.slice(2)).catch((error) => {
|
|
|
7733
8504
|
function formatCliError(error) {
|
|
7734
8505
|
if (error instanceof ZodError2) {
|
|
7735
8506
|
return renderError(
|
|
7736
|
-
"invalid
|
|
8507
|
+
"invalid input",
|
|
7737
8508
|
error.issues.map((issue) => ({
|
|
7738
8509
|
path: issue.path.join("."),
|
|
7739
8510
|
message: issue.message
|