@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.
Files changed (3) hide show
  1. package/README.md +3 -0
  2. package/dist/cli.js +948 -162
  3. 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 ALLOWED_FRONTMATTER_KEYS = BOUNTY_CHALLENGE_FRONTMATTER_KEYS.join(", ");
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/deployments.ts
148
- import { baseSepolia as baseSepolia2 } from "viem/chains";
149
- var DEPLOYMENTS = {
150
- [baseSepolia2.id]: {
151
- ELGORA_HUB_ADDRESS: "0x2f97b5f616495c2e923f39a46648eb783c053ad7",
152
- ELGORA_ESCROW_TOKEN_ADDRESS: "0x036cbd53842c5426634e7929541ec2318f3dcf7e",
153
- payoutSchemeAddress: "0x752d4305b8567b777d479dfa9847dc4f5ffb5750",
154
- ELGORA_SUBGRAPH_ENDPOINT: "https://api.goldsky.com/api/public/project_cmqz9ta0fx8bu01xt0om3fo75/subgraphs/elgora-elgorahub/testnet-production/gn",
155
- deployBlock: 46353511,
156
- deployTransaction: "0xed3d3c8af3c69f9f8d4ec47da7179fe4b89bd4a4cd8d8232dbbd3d3f3d38e74b",
157
- sourceCommit: "1377da5c38dc5c90d8608ffa3cb15ad6f8567a01"
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
- var DEFAULT_CHAIN_ID = baseSepolia2.id;
161
- function getDeployment(chainId) {
162
- return DEPLOYMENTS[chainId];
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 SAFE_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
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().regex(SAFE_NAME_RE);
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).refine(
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 crypto = getWebCrypto();
853
- const key = await crypto.subtle.importKey(
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 crypto.subtle.encrypt(
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 crypto = getWebCrypto();
875
- const key = await crypto.subtle.importKey(
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 crypto.subtle.decrypt(
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 { encodeFunctionData, getAbiItem as getAbiItem2 } from "viem";
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: boundedText(120),
3695
- message: boundedText(500)
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
- judgeable: z6.boolean(),
3699
- issues: z6.array(readinessIssueSchema).max(MAX_ORCHESTRATOR_ISSUES)
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 FRONTMATTER_DELIMITER = "---";
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 = `${FRONTMATTER_DELIMITER}
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
- ${FRONTMATTER_DELIMITER}
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
- ${FRONTMATTER_DELIMITER}
4646
+ ${FRONTMATTER_DELIMITER2}
3922
4647
  `,
3923
- FRONTMATTER_DELIMITER.length + 1
4648
+ FRONTMATTER_DELIMITER2.length + 1
3924
4649
  );
3925
- if (!markdown.startsWith(`${FRONTMATTER_DELIMITER}
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
- FRONTMATTER_DELIMITER.length + 1,
4658
+ FRONTMATTER_DELIMITER2.length + 1,
3934
4659
  closingDelimiter
3935
4660
  );
3936
4661
  const rawVerdict = {
3937
4662
  verdict_body: markdown.slice(
3938
- closingDelimiter + FRONTMATTER_DELIMITER.length + 2
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 block = await client.getBlock();
4013
- if (block.hash === null) {
4014
- throw new Error(
4015
- "The latest block has no hash yet \u2014 retry signing in a moment."
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 main(env = process.env, argv = process.argv.slice(2)) {
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
- parts.push(`issues: ${JSON.stringify(apiError.issues)}`);
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(" \u2014 ");
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 main2(env = process.env, argv = process.argv.slice(2)) {
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 main3(env = process.env, argv = process.argv.slice(2)) {
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 main4(env = process.env, argv = process.argv.slice(2)) {
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 readStdinText } from "stream/consumers";
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 main5(env = process.env, argv = process.argv.slice(2)) {
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 readStdinText(process.stdin) : await readFile(path5, "utf8")
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 readFile2 } from "fs/promises";
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 main6(env = process.env, argv = process.argv.slice(2)) {
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 readFile2(args.verdictBodyPath, "utf8");
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 main7(env = process.env, argv = process.argv.slice(2)) {
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 readFile3 } from "fs/promises";
6312
- import { text as readStdinText2 } from "stream/consumers";
7049
+ import { readFile as readFile4 } from "fs/promises";
7050
+ import { text as readStdinText3 } from "stream/consumers";
6313
7051
  import {
6314
- parseAbi,
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 = parseAbi([
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 main8(env = process.env, argv = process.argv.slice(2)) {
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 readBountyChallengeMarkdown(remainingArgv);
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 approveHash = await walletClient.writeContract({
6404
- address: config.escrowTokenAddress,
6405
- abi: usdcAbi,
6406
- functionName: "approve",
6407
- args: [config.hubAddress, escrowAmount]
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
- if (approveReceipt.status !== "success") {
6413
- throw new Error(`approve transaction reverted: ${approveHash}`);
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 readBountyChallengeMarkdown(argv) {
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 readStdinText2(process.stdin);
7249
+ return readStdinText3(process.stdin);
6488
7250
  }
6489
- return readFile3(path5, "utf8");
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 main9(env = process.env, argv = process.argv.slice(2)) {
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 main10(env = process.env, argv = process.argv.slice(2)) {
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 readFile4 } from "fs/promises";
7488
+ import { readFile as readFile5 } from "fs/promises";
6727
7489
  import { isAddress as isAddress6, isHex as isHex5, recoverMessageAddress } from "viem";
6728
- async function main11(_env = process.env, argv = process.argv.slice(2)) {
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 readFile4(preparedPath, "utf8"));
6736
- const response = parseResponse(await readFile4(responsePath, "utf8"));
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 readFile5 } from "fs/promises";
6819
- import { text as readStdinText3 } from "stream/consumers";
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 main12(_env = process.env, argv = process.argv.slice(2)) {
6822
- const markdown = await readBountyChallengeMarkdown2(argv);
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 readBountyChallengeMarkdown2(argv) {
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 readStdinText3(process.stdin);
7600
+ return readStdinText4(process.stdin);
6839
7601
  }
6840
- return readFile5(path5, "utf8");
7602
+ return readFile6(path5, "utf8");
6841
7603
  }
6842
7604
 
6843
7605
  // src/submit.ts
6844
- import { readdir, readFile as readFile6, stat } from "fs/promises";
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 main13(env = process.env, argv = process.argv.slice(2)) {
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 50 MiB encrypted submission limit. Reduce the final artifact package before submitting.`
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 > MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES) {
8032
+ if (plaintextByteLength > MAX_SOLVER_SUBMISSION_ARTIFACT_BYTES) {
7266
8033
  throw new Error(
7267
- `Artifact directory exceeds Elgora's 50 MiB encrypted submission limit before packaging. Reduce the final artifact package before submitting.`
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 readFile6(path3.join(artifactDir, entry.name));
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 main14(env = process.env, argv = process.argv.slice(2)) {
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": main4,
7460
- "guardian:open": main2,
7461
- "guardian:prove-roster-key": main5,
7462
- "guardian:verdict": main6,
7463
- "guardian:deliver-key": main3,
7464
- "poster:open-winning-submission": main7,
7465
- "poster:publish-fund": main8,
7466
- "roster:add-guardian": main9,
7467
- "roster:prepare-guardian-proof": main10,
7468
- "roster:verify-guardian-proof": main11,
7469
- "solver:submit": main13,
7470
- claim: main,
7471
- "spec-commitment": main12,
7472
- "verification-record": main14
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, publish them through\n Elgora's API, approve the escrow token, and create the bounty on\n ElgoraHub. Pass '-' to read the challenge from stdin.",
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 environment configuration",
8507
+ "invalid input",
7722
8508
  error.issues.map((issue) => ({
7723
8509
  path: issue.path.join("."),
7724
8510
  message: issue.message