@elgora/cli 7.2.2 → 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 +948 -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";
|
|
@@ -3215,6 +3788,16 @@ function decodeBountyStatus(status) {
|
|
|
3215
3788
|
}
|
|
3216
3789
|
return decoded;
|
|
3217
3790
|
}
|
|
3791
|
+
var BPS_DENOMINATOR = 10000n;
|
|
3792
|
+
function settlementAmounts(escrowAmount, treasuryFeeBps, guardianFeeBps) {
|
|
3793
|
+
const treasuryAmount = escrowAmount * BigInt(treasuryFeeBps) / BPS_DENOMINATOR;
|
|
3794
|
+
const guardianAmount = escrowAmount * BigInt(guardianFeeBps) / BPS_DENOMINATOR;
|
|
3795
|
+
return {
|
|
3796
|
+
treasuryAmount,
|
|
3797
|
+
guardianAmount,
|
|
3798
|
+
winnerPool: escrowAmount - treasuryAmount - guardianAmount
|
|
3799
|
+
};
|
|
3800
|
+
}
|
|
3218
3801
|
|
|
3219
3802
|
// ../../packages/core/src/api/bounty-detail.ts
|
|
3220
3803
|
import { zeroAddress } from "viem";
|
|
@@ -3471,12 +4054,24 @@ async function classifyBeaconFetch(operation) {
|
|
|
3471
4054
|
}
|
|
3472
4055
|
|
|
3473
4056
|
// ../../packages/core/src/api/elgorahub.ts
|
|
3474
|
-
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";
|
|
3475
4069
|
var ELGORA_HUB_ERRORS = ELGORA_HUB_ABI.filter(
|
|
3476
4070
|
(item) => item.type === "error"
|
|
3477
4071
|
);
|
|
3478
4072
|
var ELGORA_HUB_CREATE_BOUNTY_ABI = [
|
|
3479
4073
|
getAbiItem2({ abi: ELGORA_HUB_ABI, name: "createBounty" }),
|
|
4074
|
+
getAbiItem2({ abi: ELGORA_HUB_ABI, name: "createBountyWithAuthorization" }),
|
|
3480
4075
|
...ELGORA_HUB_ERRORS
|
|
3481
4076
|
];
|
|
3482
4077
|
var ELGORA_HUB_SUBMIT_ABI = [
|
|
@@ -3536,6 +4131,117 @@ var ELGORA_HUB_ADD_GUARDIAN_ABI = [
|
|
|
3536
4131
|
getAbiItem2({ abi: ELGORA_HUB_ABI, name: "addGuardian" }),
|
|
3537
4132
|
...ELGORA_HUB_ERRORS
|
|
3538
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
|
+
}
|
|
3539
4245
|
function encodeAddGuardianTransaction(input) {
|
|
3540
4246
|
return {
|
|
3541
4247
|
chain_id: input.chainId,
|
|
@@ -3689,15 +4395,34 @@ var ELGORA_HUB_FEE_POLICY_ABI = [
|
|
|
3689
4395
|
// ../../packages/core/src/api/orchestrator-readiness.ts
|
|
3690
4396
|
import { z as z6 } from "zod";
|
|
3691
4397
|
var MAX_ORCHESTRATOR_ISSUES = 20;
|
|
3692
|
-
var boundedText = (maxLength) => z6.string().trim().min(1).max(maxLength);
|
|
3693
4398
|
var readinessIssueSchema = z6.object({
|
|
3694
|
-
path:
|
|
3695
|
-
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()
|
|
3696
4413
|
}).strict();
|
|
3697
4414
|
var readinessResponseSchema = z6.object({
|
|
3698
|
-
|
|
3699
|
-
|
|
4415
|
+
issues: z6.array(readinessIssueSchema).max(MAX_ORCHESTRATOR_ISSUES),
|
|
4416
|
+
judgeable: z6.boolean()
|
|
3700
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;
|
|
3701
4426
|
var providerMessageSchema = z6.object({
|
|
3702
4427
|
id: z6.string().optional(),
|
|
3703
4428
|
choices: z6.array(
|
|
@@ -3865,7 +4590,7 @@ var deliveryKeyConfigResponseSchema = z9.strictObject({
|
|
|
3865
4590
|
import { keccak256 as keccak2564, toBytes as toBytes3, zeroAddress as zeroAddress2 } from "viem";
|
|
3866
4591
|
import { z as z10 } from "zod";
|
|
3867
4592
|
var WRITTEN_VERDICT_FORMAT_VERSION = 0;
|
|
3868
|
-
var
|
|
4593
|
+
var FRONTMATTER_DELIMITER2 = "---";
|
|
3869
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");
|
|
3870
4595
|
var bountyIdSchema2 = z10.string().regex(/^(0|[1-9][0-9]*)$/, "Must be a non-negative integer");
|
|
3871
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);
|
|
@@ -3897,7 +4622,7 @@ var writtenVerdictSchema = z10.discriminatedUnion("outcome", [
|
|
|
3897
4622
|
}).strict()
|
|
3898
4623
|
]);
|
|
3899
4624
|
function buildWrittenVerdictMarkdown(input) {
|
|
3900
|
-
const common = `${
|
|
4625
|
+
const common = `${FRONTMATTER_DELIMITER2}
|
|
3901
4626
|
format_version: ${WRITTEN_VERDICT_FORMAT_VERSION}
|
|
3902
4627
|
chain_id: ${input.config.chainId}
|
|
3903
4628
|
hub_address: ${input.config.hubAddress.toLowerCase()}
|
|
@@ -3907,7 +4632,7 @@ outcome: ${input.outcome}`;
|
|
|
3907
4632
|
awarded_submission_commitment: ${input.awardedSubmissionCommitment.toLowerCase()}` : `winner: ${zeroAddress2}`;
|
|
3908
4633
|
const markdown = `${common}
|
|
3909
4634
|
${position}
|
|
3910
|
-
${
|
|
4635
|
+
${FRONTMATTER_DELIMITER2}
|
|
3911
4636
|
${input.verdictBody}`;
|
|
3912
4637
|
parseWrittenVerdictMarkdown(markdown);
|
|
3913
4638
|
return markdown;
|
|
@@ -3918,11 +4643,11 @@ function deriveReportCommitment(markdown) {
|
|
|
3918
4643
|
function parseWrittenVerdictMarkdown(markdown) {
|
|
3919
4644
|
const closingDelimiter = markdown.indexOf(
|
|
3920
4645
|
`
|
|
3921
|
-
${
|
|
4646
|
+
${FRONTMATTER_DELIMITER2}
|
|
3922
4647
|
`,
|
|
3923
|
-
|
|
4648
|
+
FRONTMATTER_DELIMITER2.length + 1
|
|
3924
4649
|
);
|
|
3925
|
-
if (!markdown.startsWith(`${
|
|
4650
|
+
if (!markdown.startsWith(`${FRONTMATTER_DELIMITER2}
|
|
3926
4651
|
`) || closingDelimiter < 0) {
|
|
3927
4652
|
throw invalidWrittenVerdict(
|
|
3928
4653
|
"frontmatter",
|
|
@@ -3930,12 +4655,12 @@ ${FRONTMATTER_DELIMITER}
|
|
|
3930
4655
|
);
|
|
3931
4656
|
}
|
|
3932
4657
|
const frontmatter = markdown.slice(
|
|
3933
|
-
|
|
4658
|
+
FRONTMATTER_DELIMITER2.length + 1,
|
|
3934
4659
|
closingDelimiter
|
|
3935
4660
|
);
|
|
3936
4661
|
const rawVerdict = {
|
|
3937
4662
|
verdict_body: markdown.slice(
|
|
3938
|
-
closingDelimiter +
|
|
4663
|
+
closingDelimiter + FRONTMATTER_DELIMITER2.length + 2
|
|
3939
4664
|
)
|
|
3940
4665
|
};
|
|
3941
4666
|
for (const [index, line] of frontmatter.split("\n").entries()) {
|
|
@@ -4008,14 +4733,12 @@ function assertChainId(actual, expected) {
|
|
|
4008
4733
|
throw new Error(`RPC returned chain_id ${actual}, expected ${expected}`);
|
|
4009
4734
|
}
|
|
4010
4735
|
}
|
|
4736
|
+
var SIGNING_BLOCK_OFFSET = 3n;
|
|
4011
4737
|
async function fetchSignableBlock(client) {
|
|
4012
|
-
const
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
);
|
|
4017
|
-
}
|
|
4018
|
-
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 };
|
|
4019
4742
|
}
|
|
4020
4743
|
async function assertHubDeployment(client, chainId, hubAddress) {
|
|
4021
4744
|
const [actualChainId, hubCode] = await Promise.all([
|
|
@@ -4058,7 +4781,7 @@ function withDeploymentDefaults(env) {
|
|
|
4058
4781
|
|
|
4059
4782
|
// src/claim.ts
|
|
4060
4783
|
var CLAIMANT_ADDRESS_FLAG = "--claimant-address";
|
|
4061
|
-
async function
|
|
4784
|
+
async function main2(env = process.env, argv = process.argv.slice(2)) {
|
|
4062
4785
|
const args = readClaimArgs(argv);
|
|
4063
4786
|
const config = loadClaimConfig(env, args.claimantAddress);
|
|
4064
4787
|
const account = config.claimantPrivateKey ? privateKeyToAccount(config.claimantPrivateKey) : null;
|
|
@@ -4101,6 +4824,11 @@ async function main(env = process.env, argv = process.argv.slice(2)) {
|
|
|
4101
4824
|
`Award for bounty_id ${args.bountyId.toString()} is already claimed or partially claimed`
|
|
4102
4825
|
);
|
|
4103
4826
|
}
|
|
4827
|
+
claimableAmount = settlementAmounts(
|
|
4828
|
+
parameters.escrowAmount,
|
|
4829
|
+
parameters.treasuryFeeBps,
|
|
4830
|
+
parameters.guardianFeeBps
|
|
4831
|
+
).winnerPool;
|
|
4104
4832
|
functionName = "claimAward";
|
|
4105
4833
|
} else if (status === "no_valid_submission" || status === "timed_out") {
|
|
4106
4834
|
if (parameters.poster.toLowerCase() !== normalizedClaimant) {
|
|
@@ -4455,12 +5183,16 @@ function formatApiError(body) {
|
|
|
4455
5183
|
const apiError = body.error;
|
|
4456
5184
|
const parts = [String(apiError.message)];
|
|
4457
5185
|
if (Array.isArray(apiError.issues) && apiError.issues.length > 0) {
|
|
4458
|
-
|
|
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
|
+
}
|
|
4459
5191
|
}
|
|
4460
5192
|
if (apiError.next_action !== void 0 && apiError.next_action !== null) {
|
|
4461
5193
|
parts.push(`next action: ${String(apiError.next_action)}`);
|
|
4462
5194
|
}
|
|
4463
|
-
return parts.join("
|
|
5195
|
+
return parts.join("\n");
|
|
4464
5196
|
}
|
|
4465
5197
|
return JSON.stringify(body);
|
|
4466
5198
|
}
|
|
@@ -4564,7 +5296,7 @@ import {
|
|
|
4564
5296
|
// ../../packages/core/src/auth/signature.ts
|
|
4565
5297
|
import {
|
|
4566
5298
|
isAddressEqual,
|
|
4567
|
-
recoverTypedDataAddress
|
|
5299
|
+
recoverTypedDataAddress as recoverTypedDataAddress2
|
|
4568
5300
|
} from "viem";
|
|
4569
5301
|
|
|
4570
5302
|
// ../../packages/core/src/auth/request-approval-header.ts
|
|
@@ -4685,7 +5417,7 @@ import { mkdir, writeFile } from "fs/promises";
|
|
|
4685
5417
|
import path from "path";
|
|
4686
5418
|
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
4687
5419
|
var DEFAULT_OUTPUT_ROOT = "./guardian-open";
|
|
4688
|
-
async function
|
|
5420
|
+
async function main3(env = process.env, argv = process.argv.slice(2)) {
|
|
4689
5421
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
|
|
4690
5422
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
4691
5423
|
const config = loadClientDeploymentConfig(
|
|
@@ -4776,7 +5508,11 @@ async function main2(env = process.env, argv = process.argv.slice(2)) {
|
|
|
4776
5508
|
bounty_id: args.bountyId,
|
|
4777
5509
|
solver: args.solver,
|
|
4778
5510
|
submission_commitment: args.submissionCommitment,
|
|
4779
|
-
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
|
|
4780
5516
|
})
|
|
4781
5517
|
);
|
|
4782
5518
|
}
|
|
@@ -4842,7 +5578,7 @@ function readGuardianPrivateKeys(env) {
|
|
|
4842
5578
|
|
|
4843
5579
|
// src/guardian-deliver-key.ts
|
|
4844
5580
|
var USAGE = "Usage: elgora-cli guardian:deliver-key <bounty_id> <solver> <submission_commitment>";
|
|
4845
|
-
async function
|
|
5581
|
+
async function main4(env = process.env, argv = process.argv.slice(2)) {
|
|
4846
5582
|
const { apiBaseUrl, argv: positionalArgs } = extractApiBaseUrlFlag(argv);
|
|
4847
5583
|
const { bountyId, solver, submissionCommitment } = readArgs(positionalArgs);
|
|
4848
5584
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
@@ -4984,6 +5720,7 @@ var VERDICT_FIELDS = `
|
|
|
4984
5720
|
awardedSubmissionCommitment
|
|
4985
5721
|
reportCommitment
|
|
4986
5722
|
payoutData
|
|
5723
|
+
committedTxHash
|
|
4987
5724
|
committedAt
|
|
4988
5725
|
updatedAt
|
|
4989
5726
|
`;
|
|
@@ -5443,6 +6180,7 @@ function mapVerdict(row) {
|
|
|
5443
6180
|
),
|
|
5444
6181
|
reportCommitment: asHex(record.reportCommitment, "reportCommitment"),
|
|
5445
6182
|
payoutData: asHex(record.payoutData, "payoutData"),
|
|
6183
|
+
committedTxHash: asHex(record.committedTxHash, "committedTxHash"),
|
|
5446
6184
|
committedAt: asString(record.committedAt, "committedAt"),
|
|
5447
6185
|
updatedAt: asString(record.updatedAt, "updatedAt")
|
|
5448
6186
|
};
|
|
@@ -5671,7 +6409,7 @@ function asNullableOutcome(value, field) {
|
|
|
5671
6409
|
|
|
5672
6410
|
// src/guardian-judgeable.ts
|
|
5673
6411
|
import { zeroAddress as zeroAddress3 } from "viem";
|
|
5674
|
-
async function
|
|
6412
|
+
async function main5(env = process.env, argv = process.argv.slice(2)) {
|
|
5675
6413
|
const guardian = readGuardianAddress(argv);
|
|
5676
6414
|
const config = readConfig(withDeploymentDefaults(env));
|
|
5677
6415
|
const snapshot = await createElgoraSubgraphClient({
|
|
@@ -5765,10 +6503,10 @@ function requiredUrl(env, name) {
|
|
|
5765
6503
|
}
|
|
5766
6504
|
|
|
5767
6505
|
// src/guardian-prove-roster-key.ts
|
|
5768
|
-
import { readFile } from "fs/promises";
|
|
5769
|
-
import { text as
|
|
6506
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
6507
|
+
import { text as readStdinText2 } from "stream/consumers";
|
|
5770
6508
|
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
5771
|
-
async function
|
|
6509
|
+
async function main6(env = process.env, argv = process.argv.slice(2)) {
|
|
5772
6510
|
const [path5] = argv;
|
|
5773
6511
|
if (!path5) {
|
|
5774
6512
|
throw new Error(
|
|
@@ -5776,7 +6514,7 @@ async function main5(env = process.env, argv = process.argv.slice(2)) {
|
|
|
5776
6514
|
);
|
|
5777
6515
|
}
|
|
5778
6516
|
const challenge = parseChallenge(
|
|
5779
|
-
path5 === "-" ? await
|
|
6517
|
+
path5 === "-" ? await readStdinText2(process.stdin) : await readFile2(path5, "utf8")
|
|
5780
6518
|
);
|
|
5781
6519
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
5782
6520
|
const { chainId, hubAddress } = loadPublicDeploymentConfig(envWithDefaults);
|
|
@@ -5843,7 +6581,7 @@ function parseChallenge(text) {
|
|
|
5843
6581
|
}
|
|
5844
6582
|
|
|
5845
6583
|
// src/guardian-verdict.ts
|
|
5846
|
-
import { readFile as
|
|
6584
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
5847
6585
|
import { zeroAddress as zeroAddress4 } from "viem";
|
|
5848
6586
|
import { privateKeyToAccount as privateKeyToAccount6 } from "viem/accounts";
|
|
5849
6587
|
var OUTCOME = {
|
|
@@ -5880,7 +6618,7 @@ function parseVerdictArgs(argv) {
|
|
|
5880
6618
|
}
|
|
5881
6619
|
throw usageError();
|
|
5882
6620
|
}
|
|
5883
|
-
async function
|
|
6621
|
+
async function main7(env = process.env, argv = process.argv.slice(2)) {
|
|
5884
6622
|
const { apiBaseUrl: apiBaseUrlOverride, argv: positionalArgs } = extractApiBaseUrlFlag(argv);
|
|
5885
6623
|
const args = parseVerdictArgs(positionalArgs);
|
|
5886
6624
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
@@ -5900,7 +6638,7 @@ async function main6(env = process.env, argv = process.argv.slice(2)) {
|
|
|
5900
6638
|
const publicClient = createChainPublicClient(config.chainId, rpcUrl);
|
|
5901
6639
|
const walletClient = createChainWalletClient(config.chainId, rpcUrl, account);
|
|
5902
6640
|
await assertHubDeployment(publicClient, config.chainId, config.hubAddress);
|
|
5903
|
-
const verdictBody = await
|
|
6641
|
+
const verdictBody = await readFile3(args.verdictBodyPath, "utf8");
|
|
5904
6642
|
const markdown = args.outcome === "awarded" ? buildWrittenVerdictMarkdown({
|
|
5905
6643
|
config,
|
|
5906
6644
|
bountyId: args.bountyId,
|
|
@@ -6169,7 +6907,7 @@ import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
|
6169
6907
|
import path2 from "path";
|
|
6170
6908
|
import { privateKeyToAccount as privateKeyToAccount7 } from "viem/accounts";
|
|
6171
6909
|
var DEFAULT_OUTPUT_ROOT2 = "./winning-submission";
|
|
6172
|
-
async function
|
|
6910
|
+
async function main8(env = process.env, argv = process.argv.slice(2)) {
|
|
6173
6911
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
|
|
6174
6912
|
const args = readPosterOpenWinningSubmissionArgs(remainingArgv);
|
|
6175
6913
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
@@ -6308,25 +7046,26 @@ function required3(env, name) {
|
|
|
6308
7046
|
}
|
|
6309
7047
|
|
|
6310
7048
|
// src/publish-fund.ts
|
|
6311
|
-
import { readFile as
|
|
6312
|
-
import { text as
|
|
7049
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
7050
|
+
import { text as readStdinText3 } from "stream/consumers";
|
|
6313
7051
|
import {
|
|
6314
|
-
|
|
7052
|
+
decodeFunctionData,
|
|
7053
|
+
parseAbi as parseAbi2,
|
|
6315
7054
|
parseEventLogs
|
|
6316
7055
|
} from "viem";
|
|
6317
7056
|
import { privateKeyToAccount as privateKeyToAccount8 } from "viem/accounts";
|
|
6318
7057
|
var PUBLISH_FUND_USAGE = "Usage: elgora-cli poster:publish-fund <bounty_challenge_md_path>\n or: elgora-cli poster:publish-fund -";
|
|
6319
|
-
var usdcAbi =
|
|
7058
|
+
var usdcAbi = parseAbi2([
|
|
6320
7059
|
"function approve(address spender,uint256 value) returns (bool)",
|
|
6321
7060
|
"function balanceOf(address account) view returns (uint256)"
|
|
6322
7061
|
]);
|
|
6323
|
-
async function
|
|
7062
|
+
async function main9(env = process.env, argv = process.argv.slice(2)) {
|
|
6324
7063
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
|
|
6325
7064
|
const config = loadPublishFundConfig(
|
|
6326
7065
|
withDeploymentDefaults(env),
|
|
6327
7066
|
apiBaseUrlOverride
|
|
6328
7067
|
);
|
|
6329
|
-
const bountyChallengeMarkdown = await
|
|
7068
|
+
const bountyChallengeMarkdown = await readBountyChallengeMarkdown2(remainingArgv);
|
|
6330
7069
|
const account = privateKeyToAccount8(config.posterPrivateKey);
|
|
6331
7070
|
const publicClient = createChainPublicClient(config.chainId, config.rpcUrl);
|
|
6332
7071
|
const walletClient = createChainWalletClient(
|
|
@@ -6400,35 +7139,58 @@ async function main8(env = process.env, argv = process.argv.slice(2)) {
|
|
|
6400
7139
|
`Poster has insufficient escrow token balance on chain ${config.chainId}: required=${escrowAmount.toString()} available=${usdcBalance.toString()}`
|
|
6401
7140
|
);
|
|
6402
7141
|
}
|
|
6403
|
-
const
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
const approveReceipt = await publicClient.waitForTransactionReceipt({
|
|
6410
|
-
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)
|
|
6411
7148
|
});
|
|
6412
|
-
|
|
6413
|
-
|
|
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
|
+
});
|
|
6414
7193
|
}
|
|
6415
|
-
console.log(
|
|
6416
|
-
JSON.stringify({
|
|
6417
|
-
event: "publish_fund.usdc_approved",
|
|
6418
|
-
tx_hash: approveHash,
|
|
6419
|
-
block_number: approveReceipt.blockNumber.toString()
|
|
6420
|
-
})
|
|
6421
|
-
);
|
|
6422
|
-
const createBountyHash = await walletClient.writeContract({
|
|
6423
|
-
address: config.hubAddress,
|
|
6424
|
-
abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
|
|
6425
|
-
functionName: "createBounty",
|
|
6426
|
-
args: [
|
|
6427
|
-
localSpecCommitment,
|
|
6428
|
-
escrowAmount,
|
|
6429
|
-
BigInt(prepared.create_bounty_transaction.args.submission_deadline)
|
|
6430
|
-
]
|
|
6431
|
-
});
|
|
6432
7194
|
const createBountyReceipt = await publicClient.waitForTransactionReceipt({
|
|
6433
7195
|
hash: createBountyHash
|
|
6434
7196
|
});
|
|
@@ -6478,15 +7240,15 @@ async function requestPreparePublication(input) {
|
|
|
6478
7240
|
);
|
|
6479
7241
|
return bountyPublicationResponseSchema.parse(body);
|
|
6480
7242
|
}
|
|
6481
|
-
async function
|
|
7243
|
+
async function readBountyChallengeMarkdown2(argv) {
|
|
6482
7244
|
const path5 = argv[0];
|
|
6483
7245
|
if (!path5) {
|
|
6484
7246
|
throw new Error(PUBLISH_FUND_USAGE);
|
|
6485
7247
|
}
|
|
6486
7248
|
if (path5 === "-") {
|
|
6487
|
-
return
|
|
7249
|
+
return readStdinText3(process.stdin);
|
|
6488
7250
|
}
|
|
6489
|
-
return
|
|
7251
|
+
return readFile4(path5, "utf8");
|
|
6490
7252
|
}
|
|
6491
7253
|
function loadPublishFundConfig(env, apiBaseUrlOverride) {
|
|
6492
7254
|
const clientConfig = loadClientDeploymentConfig(env, apiBaseUrlOverride);
|
|
@@ -6519,7 +7281,7 @@ import { isAddress as isAddress4 } from "viem";
|
|
|
6519
7281
|
import { privateKeyToAccount as privateKeyToAccount9 } from "viem/accounts";
|
|
6520
7282
|
var ROSTER_ADMIN_ADDRESS_FLAG = "--roster-admin-address";
|
|
6521
7283
|
var ENCRYPTION_PUBLIC_KEY_RE = /^[A-Za-z0-9_-]{43}$/;
|
|
6522
|
-
async function
|
|
7284
|
+
async function main10(env = process.env, argv = process.argv.slice(2)) {
|
|
6523
7285
|
const args = parseRosterAddGuardianArgs(argv);
|
|
6524
7286
|
const config = loadRosterAddGuardianConfig(env, args.rosterAdminAddress);
|
|
6525
7287
|
const account = config.rosterAdminPrivateKey ? privateKeyToAccount9(config.rosterAdminPrivateKey) : null;
|
|
@@ -6676,7 +7438,7 @@ function usageError2() {
|
|
|
6676
7438
|
// src/roster-prepare-guardian-proof.ts
|
|
6677
7439
|
import { isAddress as isAddress5 } from "viem";
|
|
6678
7440
|
var ENCRYPTION_PUBLIC_KEY_RE2 = /^[A-Za-z0-9_-]{43}$/;
|
|
6679
|
-
async function
|
|
7441
|
+
async function main11(env = process.env, argv = process.argv.slice(2)) {
|
|
6680
7442
|
const args = parseArgs(argv);
|
|
6681
7443
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
6682
7444
|
const { chainId, hubAddress } = loadPublicDeploymentConfig(envWithDefaults);
|
|
@@ -6723,17 +7485,17 @@ function usageError3() {
|
|
|
6723
7485
|
}
|
|
6724
7486
|
|
|
6725
7487
|
// src/roster-verify-guardian-proof.ts
|
|
6726
|
-
import { readFile as
|
|
7488
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
6727
7489
|
import { isAddress as isAddress6, isHex as isHex5, recoverMessageAddress } from "viem";
|
|
6728
|
-
async function
|
|
7490
|
+
async function main12(_env = process.env, argv = process.argv.slice(2)) {
|
|
6729
7491
|
const [preparedPath, responsePath] = argv;
|
|
6730
7492
|
if (!preparedPath || !responsePath) {
|
|
6731
7493
|
throw new Error(
|
|
6732
7494
|
"Usage: elgora-cli roster:verify-guardian-proof <prepared_json_path> <response_json_path>"
|
|
6733
7495
|
);
|
|
6734
7496
|
}
|
|
6735
|
-
const prepared = parsePrepared(await
|
|
6736
|
-
const response = parseResponse(await
|
|
7497
|
+
const prepared = parsePrepared(await readFile5(preparedPath, "utf8"));
|
|
7498
|
+
const response = parseResponse(await readFile5(responsePath, "utf8"));
|
|
6737
7499
|
const fixture = base64UrlToBytes(prepared.fixture);
|
|
6738
7500
|
const signedPayload = buildGuardianRosterKeyProofSignedPayload(
|
|
6739
7501
|
prepared.message,
|
|
@@ -6815,11 +7577,11 @@ function get(value, key) {
|
|
|
6815
7577
|
}
|
|
6816
7578
|
|
|
6817
7579
|
// src/spec-commitment.ts
|
|
6818
|
-
import { readFile as
|
|
6819
|
-
import { text as
|
|
7580
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
7581
|
+
import { text as readStdinText4 } from "stream/consumers";
|
|
6820
7582
|
var SPEC_COMMITMENT_USAGE = "Usage: elgora-cli spec-commitment <bounty_challenge_md_path>\n or: elgora-cli spec-commitment -";
|
|
6821
|
-
async function
|
|
6822
|
-
const markdown = await
|
|
7583
|
+
async function main13(_env = process.env, argv = process.argv.slice(2)) {
|
|
7584
|
+
const markdown = await readBountyChallengeMarkdown3(argv);
|
|
6823
7585
|
const specCommitment = deriveBountyChallengeContentCommitment(markdown);
|
|
6824
7586
|
console.log(
|
|
6825
7587
|
JSON.stringify({
|
|
@@ -6829,19 +7591,19 @@ async function main12(_env = process.env, argv = process.argv.slice(2)) {
|
|
|
6829
7591
|
})
|
|
6830
7592
|
);
|
|
6831
7593
|
}
|
|
6832
|
-
async function
|
|
7594
|
+
async function readBountyChallengeMarkdown3(argv) {
|
|
6833
7595
|
const path5 = argv[0];
|
|
6834
7596
|
if (!path5) {
|
|
6835
7597
|
throw new Error(SPEC_COMMITMENT_USAGE);
|
|
6836
7598
|
}
|
|
6837
7599
|
if (path5 === "-") {
|
|
6838
|
-
return
|
|
7600
|
+
return readStdinText4(process.stdin);
|
|
6839
7601
|
}
|
|
6840
|
-
return
|
|
7602
|
+
return readFile6(path5, "utf8");
|
|
6841
7603
|
}
|
|
6842
7604
|
|
|
6843
7605
|
// src/submit.ts
|
|
6844
|
-
import { readdir, readFile as
|
|
7606
|
+
import { readdir, readFile as readFile7, stat } from "fs/promises";
|
|
6845
7607
|
import path3 from "path";
|
|
6846
7608
|
import { createInterface } from "readline";
|
|
6847
7609
|
import { isAddress as isAddress7 } from "viem";
|
|
@@ -6895,7 +7657,7 @@ var MEDIA_TYPE_BY_EXTENSION = {
|
|
|
6895
7657
|
diff: "text/x-diff"
|
|
6896
7658
|
};
|
|
6897
7659
|
var DEFAULT_MEDIA_TYPE = "application/octet-stream";
|
|
6898
|
-
async function
|
|
7660
|
+
async function main14(env = process.env, argv = process.argv.slice(2)) {
|
|
6899
7661
|
const { solverAddress, argv: argvWithoutSolverAddress } = extractSolverAddressFlag(argv);
|
|
6900
7662
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argvWithoutSolverAddress);
|
|
6901
7663
|
const config = loadSubmitConfig(env, apiBaseUrlOverride, solverAddress);
|
|
@@ -6972,7 +7734,7 @@ async function main13(env = process.env, argv = process.argv.slice(2)) {
|
|
|
6972
7734
|
});
|
|
6973
7735
|
if (ciphertext.byteLength > MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES) {
|
|
6974
7736
|
throw new Error(
|
|
6975
|
-
`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.`
|
|
6976
7738
|
);
|
|
6977
7739
|
}
|
|
6978
7740
|
const prepared = await (async () => {
|
|
@@ -7259,18 +8021,23 @@ async function readArtifactDirectory(artifactDir) {
|
|
|
7259
8021
|
if (entries.length === 0) {
|
|
7260
8022
|
throw new Error(`No files found in artifact directory: ${artifactDir}`);
|
|
7261
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
|
+
}
|
|
7262
8029
|
let plaintextByteLength = 0;
|
|
7263
8030
|
for (const entry of entries) {
|
|
7264
8031
|
plaintextByteLength += (await stat(path3.join(artifactDir, entry.name))).size;
|
|
7265
|
-
if (plaintextByteLength >
|
|
8032
|
+
if (plaintextByteLength > MAX_SOLVER_SUBMISSION_ARTIFACT_BYTES) {
|
|
7266
8033
|
throw new Error(
|
|
7267
|
-
`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.`
|
|
7268
8035
|
);
|
|
7269
8036
|
}
|
|
7270
8037
|
}
|
|
7271
8038
|
return Promise.all(
|
|
7272
8039
|
entries.map(async (entry) => {
|
|
7273
|
-
const bytes = await
|
|
8040
|
+
const bytes = await readFile7(path3.join(artifactDir, entry.name));
|
|
7274
8041
|
return {
|
|
7275
8042
|
name: entry.name,
|
|
7276
8043
|
media_type: guessMediaType(entry.name),
|
|
@@ -7279,6 +8046,9 @@ async function readArtifactDirectory(artifactDir) {
|
|
|
7279
8046
|
})
|
|
7280
8047
|
);
|
|
7281
8048
|
}
|
|
8049
|
+
function formatMebibytes(byteLength) {
|
|
8050
|
+
return `${byteLength / 1024 / 1024} MiB`;
|
|
8051
|
+
}
|
|
7282
8052
|
function guessMediaType(fileName) {
|
|
7283
8053
|
const extension = path3.extname(fileName).slice(1).toLowerCase();
|
|
7284
8054
|
return MEDIA_TYPE_BY_EXTENSION[extension] ?? DEFAULT_MEDIA_TYPE;
|
|
@@ -7380,7 +8150,7 @@ function readTimelockRevealDelaySeconds(env) {
|
|
|
7380
8150
|
|
|
7381
8151
|
// src/verification-record.ts
|
|
7382
8152
|
var VERIFICATION_RECORD_USAGE = "Usage: elgora-cli verification-record <bounty_id>";
|
|
7383
|
-
async function
|
|
8153
|
+
async function main15(env = process.env, argv = process.argv.slice(2)) {
|
|
7384
8154
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
|
|
7385
8155
|
const args = readVerificationRecordArgs(remainingArgv);
|
|
7386
8156
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
@@ -7445,6 +8215,9 @@ function usageError4() {
|
|
|
7445
8215
|
return new Error(VERIFICATION_RECORD_USAGE);
|
|
7446
8216
|
}
|
|
7447
8217
|
|
|
8218
|
+
// src/version.ts
|
|
8219
|
+
var CLI_VERSION = true ? "7.4.0" : "0.0.0-dev";
|
|
8220
|
+
|
|
7448
8221
|
// src/cli.ts
|
|
7449
8222
|
var dotenvPath = path4.resolve(process.env.DOTENV_CONFIG_PATH ?? ".env");
|
|
7450
8223
|
var dotenvResult = loadDotenv({
|
|
@@ -7456,20 +8229,21 @@ process.stderr.write(
|
|
|
7456
8229
|
`
|
|
7457
8230
|
);
|
|
7458
8231
|
var COMMANDS = {
|
|
7459
|
-
"guardian:judgeable":
|
|
7460
|
-
"guardian:open":
|
|
7461
|
-
"guardian:prove-roster-key":
|
|
7462
|
-
"guardian:verdict":
|
|
7463
|
-
"guardian:deliver-key":
|
|
7464
|
-
"poster:open-winning-submission":
|
|
7465
|
-
"poster:publish-fund":
|
|
7466
|
-
"roster:add-guardian":
|
|
7467
|
-
"roster:prepare-guardian-proof":
|
|
7468
|
-
"roster:verify-guardian-proof":
|
|
7469
|
-
"solver:submit":
|
|
7470
|
-
claim:
|
|
7471
|
-
"
|
|
7472
|
-
"
|
|
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
|
|
7473
8247
|
};
|
|
7474
8248
|
var COMMAND_HELP = {
|
|
7475
8249
|
"guardian:judgeable": {
|
|
@@ -7541,7 +8315,7 @@ var COMMAND_HELP = {
|
|
|
7541
8315
|
"poster:publish-fund": {
|
|
7542
8316
|
usage: "elgora-cli poster:publish-fund <bounty_challenge_md_path|->",
|
|
7543
8317
|
title: "Publish an approved challenge and fund its bounty.",
|
|
7544
|
-
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.",
|
|
7545
8319
|
required: ["ELGORA_POSTER_PRIVATE_KEY"],
|
|
7546
8320
|
defaulted: [
|
|
7547
8321
|
"ELGORA_CHAIN_ID",
|
|
@@ -7606,6 +8380,12 @@ var COMMAND_HELP = {
|
|
|
7606
8380
|
required: [],
|
|
7607
8381
|
defaulted: ["ELGORA_CHAIN_ID", "ELGORA_HUB_ADDRESS", "ELGORA_RPC_URL"]
|
|
7608
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
|
+
},
|
|
7609
8389
|
"spec-commitment": {
|
|
7610
8390
|
usage: "elgora-cli spec-commitment <bounty_challenge_md_path|->",
|
|
7611
8391
|
title: "Reproduce a challenge's spec_commitment offline.",
|
|
@@ -7633,6 +8413,11 @@ async function run(rawArgv) {
|
|
|
7633
8413
|
printUsage();
|
|
7634
8414
|
return;
|
|
7635
8415
|
}
|
|
8416
|
+
if (subcommand === "--version" || subcommand === "-v" || subcommand === "version") {
|
|
8417
|
+
process.stdout.write(`${CLI_VERSION}
|
|
8418
|
+
`);
|
|
8419
|
+
return;
|
|
8420
|
+
}
|
|
7636
8421
|
if (subcommand === "help") {
|
|
7637
8422
|
printCommandHelpOrUsage(rest[0]);
|
|
7638
8423
|
return;
|
|
@@ -7665,7 +8450,8 @@ function printUsage() {
|
|
|
7665
8450
|
`Every command accepts ${NETWORK_FLAG} <name|chain id> to select the`,
|
|
7666
8451
|
`deployment for that invocation. Supported: ${NETWORK_NAMES_HINT}.`,
|
|
7667
8452
|
"",
|
|
7668
|
-
"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."
|
|
7669
8455
|
].join("\n")}
|
|
7670
8456
|
`
|
|
7671
8457
|
);
|
|
@@ -7718,7 +8504,7 @@ run(process.argv.slice(2)).catch((error) => {
|
|
|
7718
8504
|
function formatCliError(error) {
|
|
7719
8505
|
if (error instanceof ZodError2) {
|
|
7720
8506
|
return renderError(
|
|
7721
|
-
"invalid
|
|
8507
|
+
"invalid input",
|
|
7722
8508
|
error.issues.map((issue) => ({
|
|
7723
8509
|
path: issue.path.join("."),
|
|
7724
8510
|
message: issue.message
|