@elgora/cli 7.5.1 → 9.0.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 +22 -9
- package/dist/cli.js +692 -317
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -384,36 +384,107 @@ function isBountyChallengeFrontmatterKey(value) {
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
// ../../packages/core/src/bounty-publication-response.ts
|
|
387
|
-
import { isHex as
|
|
387
|
+
import { isHex as isHex3 } from "viem";
|
|
388
|
+
import { z as z3 } from "zod";
|
|
389
|
+
|
|
390
|
+
// ../../packages/core/src/auth/readiness-signoff.ts
|
|
391
|
+
import {
|
|
392
|
+
isAddressEqual,
|
|
393
|
+
isHex as isHex2,
|
|
394
|
+
recoverTypedDataAddress,
|
|
395
|
+
size as size2
|
|
396
|
+
} from "viem";
|
|
388
397
|
import { z as z2 } from "zod";
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
var
|
|
392
|
-
var
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
398
|
+
|
|
399
|
+
// ../../packages/core/src/auth/eip712.ts
|
|
400
|
+
var API_REQUEST_APPROVAL_PRIMARY_TYPE = "ApiRequestApproval";
|
|
401
|
+
var API_REQUEST_APPROVAL_TYPES = {
|
|
402
|
+
ApiRequestApproval: [
|
|
403
|
+
{ name: "endpointId", type: "string" },
|
|
404
|
+
{ name: "audience", type: "string" },
|
|
405
|
+
{ name: "bodyHash", type: "bytes32" },
|
|
406
|
+
{ name: "query", type: "string" },
|
|
407
|
+
{ name: "blockNumber", type: "uint256" },
|
|
408
|
+
{ name: "blockHash", type: "bytes32" }
|
|
409
|
+
]
|
|
410
|
+
};
|
|
411
|
+
function buildElgoraEip712Domain(params) {
|
|
412
|
+
return {
|
|
413
|
+
name: "Elgora",
|
|
414
|
+
version: "1",
|
|
415
|
+
chainId: params.chainId,
|
|
416
|
+
verifyingContract: params.hubAddress
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function buildApiRequestApprovalTypedData(params) {
|
|
420
|
+
return {
|
|
421
|
+
domain: buildElgoraEip712Domain({
|
|
422
|
+
chainId: params.chainId,
|
|
423
|
+
hubAddress: params.hubAddress
|
|
424
|
+
}),
|
|
425
|
+
types: API_REQUEST_APPROVAL_TYPES,
|
|
426
|
+
primaryType: API_REQUEST_APPROVAL_PRIMARY_TYPE,
|
|
427
|
+
message: params.message
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function endpointIdFor(input) {
|
|
431
|
+
return `${input.method.toUpperCase()} ${input.path}`;
|
|
432
|
+
}
|
|
433
|
+
function audienceFor(host) {
|
|
434
|
+
return host.toLowerCase();
|
|
435
|
+
}
|
|
436
|
+
function queryFor(query) {
|
|
437
|
+
return new URLSearchParams(query).toString();
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ../../packages/core/src/auth/readiness-signoff.ts
|
|
441
|
+
var READINESS_APPROVAL_VALIDITY_SECONDS = 7 * 24 * 60 * 60;
|
|
442
|
+
var bytes32Schema = z2.string().refine(isHexBytes32).transform((value) => value);
|
|
443
|
+
var timestampSchema = z2.number().int().nonnegative().max(9999999999);
|
|
444
|
+
var readinessSignoffMessageSchema = z2.object({
|
|
445
|
+
audience: z2.string().min(1).max(512),
|
|
446
|
+
spec_commitment: bytes32Schema,
|
|
447
|
+
reviewer_hash: bytes32Schema,
|
|
448
|
+
model: z2.string().min(1).max(256),
|
|
449
|
+
issued_at: timestampSchema,
|
|
450
|
+
expires_at: timestampSchema
|
|
451
|
+
}).strict();
|
|
452
|
+
var signatureSchema = z2.string().refine((value) => isHex2(value) && size2(value) === 65).transform((value) => value);
|
|
453
|
+
var readinessSignoffSchema = readinessSignoffMessageSchema.extend({ signature: signatureSchema }).refine((signoff) => signoff.expires_at > signoff.issued_at, {
|
|
454
|
+
path: ["expires_at"],
|
|
455
|
+
message: "Must expire after issuance"
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// ../../packages/core/src/bounty-publication-response.ts
|
|
459
|
+
var addressSchema = z3.string().refine(isHexAddress, "Must be a 20-byte EVM address").transform((value) => value);
|
|
460
|
+
var bytes32Schema2 = z3.string().refine(isHexBytes32, "Must be a bytes32 hex string").transform((value) => value);
|
|
461
|
+
var hexBytesSchema = z3.string().refine(isHex3, "Must be hex-encoded calldata").transform((value) => value);
|
|
462
|
+
var bountyChallengeContentRecordSchema = z3.object({
|
|
463
|
+
content_kind: z3.literal("bounty_challenge_spec"),
|
|
464
|
+
spec_commitment: bytes32Schema2,
|
|
465
|
+
byte_length: z3.number().int().positive(),
|
|
466
|
+
token_address: addressSchema,
|
|
467
|
+
created_at: z3.string().datetime({ offset: true })
|
|
468
|
+
}).strict();
|
|
469
|
+
var bountyPublicationResponseSchema = z3.object({
|
|
470
|
+
content_record: bountyChallengeContentRecordSchema,
|
|
471
|
+
create_bounty_transaction: z3.object({
|
|
472
|
+
chain_id: z3.number().int().positive(),
|
|
402
473
|
to: addressSchema,
|
|
403
|
-
value:
|
|
474
|
+
value: z3.literal("0"),
|
|
404
475
|
data: hexBytesSchema,
|
|
405
|
-
function_name:
|
|
406
|
-
args:
|
|
407
|
-
spec_commitment:
|
|
408
|
-
escrow_amount:
|
|
409
|
-
submission_deadline:
|
|
476
|
+
function_name: z3.literal("create_bounty"),
|
|
477
|
+
args: z3.object({
|
|
478
|
+
spec_commitment: bytes32Schema2,
|
|
479
|
+
escrow_amount: z3.string().refine(isPositiveInteger),
|
|
480
|
+
submission_deadline: z3.number().int().positive()
|
|
410
481
|
}).strict()
|
|
411
482
|
}).strict(),
|
|
412
|
-
protocol_fee_policy:
|
|
483
|
+
protocol_fee_policy: z3.object({
|
|
413
484
|
treasury_recipient: addressSchema,
|
|
414
|
-
treasury_fee_bps:
|
|
485
|
+
treasury_fee_bps: z3.number().int().min(0).max(1e4),
|
|
415
486
|
guardian_fee_recipient: addressSchema,
|
|
416
|
-
guardian_fee_bps:
|
|
487
|
+
guardian_fee_bps: z3.number().int().min(0).max(1e4)
|
|
417
488
|
}).strict()
|
|
418
489
|
}).strict().superRefine((response, context) => {
|
|
419
490
|
if (response.content_record.spec_commitment !== response.create_bounty_transaction.args.spec_commitment) {
|
|
@@ -424,6 +495,28 @@ var bountyPublicationResponseSchema = z2.object({
|
|
|
424
495
|
});
|
|
425
496
|
}
|
|
426
497
|
});
|
|
498
|
+
var priorIssueSchema = z3.object({ anchor: z3.string().min(1), code: z3.string().min(1) }).strict();
|
|
499
|
+
var bountyReviewResponseSchema = z3.object({
|
|
500
|
+
content_record: bountyChallengeContentRecordSchema,
|
|
501
|
+
readiness: z3.object({
|
|
502
|
+
decision: z3.literal("ready"),
|
|
503
|
+
signoff: readinessSignoffSchema,
|
|
504
|
+
prior_issues_resolved: z3.array(priorIssueSchema),
|
|
505
|
+
prior_issues_open: z3.array(priorIssueSchema)
|
|
506
|
+
}).strict()
|
|
507
|
+
}).strict().superRefine((response, context) => {
|
|
508
|
+
if (response.readiness.signoff.spec_commitment.toLowerCase() !== response.content_record.spec_commitment.toLowerCase()) {
|
|
509
|
+
context.addIssue({
|
|
510
|
+
code: "custom",
|
|
511
|
+
path: ["readiness", "signoff", "spec_commitment"],
|
|
512
|
+
message: "Must match content_record.spec_commitment"
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
var readinessSignerResponseSchema = z3.object({
|
|
517
|
+
signer_address: addressSchema,
|
|
518
|
+
audience: z3.string().min(1)
|
|
519
|
+
}).strict();
|
|
427
520
|
|
|
428
521
|
// ../../packages/core/src/chain-registry.ts
|
|
429
522
|
import { base, baseSepolia, foundry } from "viem/chains";
|
|
@@ -447,7 +540,7 @@ var FOUNDRY_CHAIN_ID = foundry.id;
|
|
|
447
540
|
import { Aes256Gcm, CipherSuite, HkdfSha256 } from "@hpke/core";
|
|
448
541
|
import { DhkemX25519HkdfSha256 } from "@hpke/dhkem-x25519";
|
|
449
542
|
import { concatBytes, keccak256 as keccak2562, toBytes as toBytes2 } from "viem";
|
|
450
|
-
import { z as
|
|
543
|
+
import { z as z4 } from "zod";
|
|
451
544
|
|
|
452
545
|
// ../../packages/core/src/base64.ts
|
|
453
546
|
var BASE64_ENCODE_CHUNK_BYTES = 3 * 16384;
|
|
@@ -533,42 +626,42 @@ var BASE64URL_RE = /^[A-Za-z0-9_-]+$/;
|
|
|
533
626
|
var SOLVER_SUBMISSION_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
534
627
|
var MEDIA_TYPE_RE = /^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/;
|
|
535
628
|
var KEY_ID_RE = /^[A-Za-z0-9][ -~]*$/;
|
|
536
|
-
var base64UrlSchema =
|
|
537
|
-
var lowerAddressSchema =
|
|
538
|
-
var
|
|
539
|
-
var sha256HexSchema =
|
|
540
|
-
var keyIdSchema =
|
|
541
|
-
var artifactNameSchema =
|
|
629
|
+
var base64UrlSchema = z4.string().regex(BASE64URL_RE);
|
|
630
|
+
var lowerAddressSchema = z4.string().refine(isLowercaseHexAddress);
|
|
631
|
+
var bytes32Schema3 = z4.string().refine(isLowercaseBytes32);
|
|
632
|
+
var sha256HexSchema = z4.string().refine(isSha256Hex);
|
|
633
|
+
var keyIdSchema = z4.string().min(1).max(128).regex(KEY_ID_RE);
|
|
634
|
+
var artifactNameSchema = z4.string().max(
|
|
542
635
|
MAX_SOLVER_SUBMISSION_NAME_CHARACTERS,
|
|
543
636
|
`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
637
|
).regex(SOLVER_SUBMISSION_NAME_RE);
|
|
545
|
-
var mediaTypeSchema =
|
|
546
|
-
var bountyIdSchema =
|
|
547
|
-
var chainIdSchema =
|
|
548
|
-
var privateArtifactContextSchema =
|
|
549
|
-
type:
|
|
638
|
+
var mediaTypeSchema = z4.string().regex(MEDIA_TYPE_RE);
|
|
639
|
+
var bountyIdSchema = z4.string().refine(isBountyId);
|
|
640
|
+
var chainIdSchema = z4.number().int().positive().lte(Number.MAX_SAFE_INTEGER);
|
|
641
|
+
var privateArtifactContextSchema = z4.strictObject({
|
|
642
|
+
type: z4.literal("solver_submission"),
|
|
550
643
|
chain_id: chainIdSchema,
|
|
551
644
|
hub_address: lowerAddressSchema,
|
|
552
645
|
bounty_id: bountyIdSchema,
|
|
553
646
|
actor_address: lowerAddressSchema
|
|
554
647
|
});
|
|
555
|
-
var guardianRecipientSchema =
|
|
648
|
+
var guardianRecipientSchema = z4.strictObject({
|
|
556
649
|
key_id: keyIdSchema,
|
|
557
|
-
suite:
|
|
650
|
+
suite: z4.literal(PRIVATE_ARTIFACT_HPKE_SUITE),
|
|
558
651
|
public_key: base64UrlSchema.length(
|
|
559
652
|
43,
|
|
560
653
|
"public_key must encode a 32-byte X25519 public key"
|
|
561
654
|
)
|
|
562
655
|
});
|
|
563
|
-
var guardianRecipientPrivateKeySchema =
|
|
656
|
+
var guardianRecipientPrivateKeySchema = z4.strictObject({
|
|
564
657
|
key_id: keyIdSchema,
|
|
565
|
-
suite:
|
|
658
|
+
suite: z4.literal(PRIVATE_ARTIFACT_HPKE_SUITE),
|
|
566
659
|
private_key: base64UrlSchema.length(
|
|
567
660
|
43,
|
|
568
661
|
"private_key must encode a 32-byte X25519 private key"
|
|
569
662
|
)
|
|
570
663
|
});
|
|
571
|
-
var guardianRecipientsSchema =
|
|
664
|
+
var guardianRecipientsSchema = z4.array(guardianRecipientSchema).min(1).superRefine((recipients, ctx) => {
|
|
572
665
|
const seen = /* @__PURE__ */ new Set();
|
|
573
666
|
for (const recipient of recipients) {
|
|
574
667
|
if (seen.has(recipient.key_id)) {
|
|
@@ -581,24 +674,24 @@ var guardianRecipientsSchema = z3.array(guardianRecipientSchema).min(1).superRef
|
|
|
581
674
|
seen.add(recipient.key_id);
|
|
582
675
|
}
|
|
583
676
|
});
|
|
584
|
-
var guardianRecipientPrivateKeysSchema =
|
|
585
|
-
var artifactManifestEntrySchema =
|
|
677
|
+
var guardianRecipientPrivateKeysSchema = z4.array(guardianRecipientPrivateKeySchema).min(1);
|
|
678
|
+
var artifactManifestEntrySchema = z4.strictObject({
|
|
586
679
|
name: artifactNameSchema,
|
|
587
680
|
media_type: mediaTypeSchema,
|
|
588
|
-
size_bytes:
|
|
681
|
+
size_bytes: z4.number().int().nonnegative().lte(Number.MAX_SAFE_INTEGER),
|
|
589
682
|
sha256: sha256HexSchema
|
|
590
683
|
});
|
|
591
|
-
var privateArtifactPublicManifestSchema =
|
|
592
|
-
kind:
|
|
684
|
+
var privateArtifactPublicManifestSchema = z4.strictObject({
|
|
685
|
+
kind: z4.literal(PRIVATE_ARTIFACT_PUBLIC_MANIFEST_KIND),
|
|
593
686
|
context: privateArtifactContextSchema
|
|
594
687
|
});
|
|
595
688
|
var encryptedBundleArtifactSchema = artifactManifestEntrySchema.extend({
|
|
596
689
|
bytes: base64UrlSchema
|
|
597
690
|
});
|
|
598
|
-
var privateArtifactEncryptedBundleSchema =
|
|
599
|
-
kind:
|
|
691
|
+
var privateArtifactEncryptedBundleSchema = z4.strictObject({
|
|
692
|
+
kind: z4.literal(PRIVATE_ARTIFACT_ENCRYPTED_BUNDLE_KIND),
|
|
600
693
|
context: privateArtifactContextSchema,
|
|
601
|
-
artifacts:
|
|
694
|
+
artifacts: z4.array(encryptedBundleArtifactSchema).min(1).max(
|
|
602
695
|
MAX_SOLVER_SUBMISSION_FILES,
|
|
603
696
|
`Submission exceeds the maximum of ${MAX_SOLVER_SUBMISSION_FILES} files.`
|
|
604
697
|
).refine(
|
|
@@ -606,33 +699,33 @@ var privateArtifactEncryptedBundleSchema = z3.strictObject({
|
|
|
606
699
|
"artifact names must be unique case-insensitively"
|
|
607
700
|
)
|
|
608
701
|
});
|
|
609
|
-
var privateArtifactRevealPolicySchema =
|
|
610
|
-
kind:
|
|
611
|
-
network:
|
|
612
|
-
chain_hash:
|
|
613
|
-
round:
|
|
614
|
-
not_before:
|
|
702
|
+
var privateArtifactRevealPolicySchema = z4.strictObject({
|
|
703
|
+
kind: z4.literal(PRIVATE_ARTIFACT_REVEAL_POLICY_KIND),
|
|
704
|
+
network: z4.string().min(1),
|
|
705
|
+
chain_hash: z4.string().min(1),
|
|
706
|
+
round: z4.string().refine(isPositiveInteger),
|
|
707
|
+
not_before: z4.number().int().nonnegative().lte(Number.MAX_SAFE_INTEGER)
|
|
615
708
|
});
|
|
616
|
-
var wrappedRecipientKeySchema =
|
|
709
|
+
var wrappedRecipientKeySchema = z4.strictObject({
|
|
617
710
|
key_id: keyIdSchema,
|
|
618
|
-
suite:
|
|
711
|
+
suite: z4.literal(PRIVATE_ARTIFACT_HPKE_SUITE),
|
|
619
712
|
enc: base64UrlSchema,
|
|
620
713
|
ciphertext: base64UrlSchema
|
|
621
714
|
});
|
|
622
|
-
var wrappedKeyBagSchema =
|
|
623
|
-
kind:
|
|
624
|
-
keys:
|
|
715
|
+
var wrappedKeyBagSchema = z4.strictObject({
|
|
716
|
+
kind: z4.literal(PRIVATE_ARTIFACT_WRAPPED_KEY_BAG_KIND),
|
|
717
|
+
keys: z4.array(wrappedRecipientKeySchema).min(1)
|
|
625
718
|
});
|
|
626
|
-
var privateArtifactEnvelopeSchema =
|
|
627
|
-
kind:
|
|
628
|
-
alg:
|
|
719
|
+
var privateArtifactEnvelopeSchema = z4.strictObject({
|
|
720
|
+
kind: z4.literal(PRIVATE_ARTIFACT_ENVELOPE_KIND),
|
|
721
|
+
alg: z4.literal(SUBMISSION_ENCRYPTION_TYPE),
|
|
629
722
|
context: privateArtifactContextSchema,
|
|
630
723
|
public_manifest: privateArtifactPublicManifestSchema,
|
|
631
724
|
public_manifest_sha256: sha256HexSchema,
|
|
632
725
|
encrypted_bundle_sha256: sha256HexSchema,
|
|
633
726
|
ciphertext_sha256: sha256HexSchema,
|
|
634
727
|
guardian_recipients: guardianRecipientsSchema,
|
|
635
|
-
guardian_recipients_commitment:
|
|
728
|
+
guardian_recipients_commitment: bytes32Schema3,
|
|
636
729
|
reveal_policy: privateArtifactRevealPolicySchema,
|
|
637
730
|
envelope_nonce: base64UrlSchema.length(
|
|
638
731
|
22,
|
|
@@ -640,7 +733,7 @@ var privateArtifactEnvelopeSchema = z3.strictObject({
|
|
|
640
733
|
),
|
|
641
734
|
iv: base64UrlSchema.length(16, "iv must encode 12 bytes"),
|
|
642
735
|
wrapped_key_bag: base64UrlSchema,
|
|
643
|
-
ciphertext_locator:
|
|
736
|
+
ciphertext_locator: z4.string().min(1)
|
|
644
737
|
});
|
|
645
738
|
function parsePrivateArtifactEnvelope(input) {
|
|
646
739
|
return privateArtifactEnvelopeSchema.parse(input);
|
|
@@ -698,11 +791,11 @@ var x25519PrivateKeySchema = base64UrlSchema.length(
|
|
|
698
791
|
43,
|
|
699
792
|
"must encode a 32-byte X25519 private key"
|
|
700
793
|
);
|
|
701
|
-
var winningSolutionDeliveryContextSchema =
|
|
794
|
+
var winningSolutionDeliveryContextSchema = z4.strictObject({
|
|
702
795
|
chain_id: chainIdSchema,
|
|
703
796
|
hub_address: lowerAddressSchema,
|
|
704
797
|
bounty_id: bountyIdSchema,
|
|
705
|
-
submission_commitment:
|
|
798
|
+
submission_commitment: bytes32Schema3
|
|
706
799
|
});
|
|
707
800
|
async function generateX25519KeyPair(input) {
|
|
708
801
|
const suite = createHpkeSuite();
|
|
@@ -779,7 +872,7 @@ function buildWinningSolutionDeliveryInfo(contextCommitment) {
|
|
|
779
872
|
var GUARDIAN_ROSTER_KEY_PROOF_INFO = "elgora/guardian-roster-key-proof/hpke-fixture/v0";
|
|
780
873
|
var GUARDIAN_ROSTER_KEY_PROOF_MESSAGE_TYPE = "elgora_guardian_roster_key_proof_v0";
|
|
781
874
|
var GUARDIAN_ROSTER_KEY_PROOF_FIXTURE_BYTES = 32;
|
|
782
|
-
var guardianRosterKeyProofContextSchema =
|
|
875
|
+
var guardianRosterKeyProofContextSchema = z4.strictObject({
|
|
783
876
|
chain_id: chainIdSchema,
|
|
784
877
|
hub_address: lowerAddressSchema,
|
|
785
878
|
proposed_account: lowerAddressSchema,
|
|
@@ -1107,7 +1200,7 @@ async function wrapAesKeyForRecipients(input) {
|
|
|
1107
1200
|
}
|
|
1108
1201
|
async function openAesKeyForRecipient(input) {
|
|
1109
1202
|
const suite = createHpkeSuite();
|
|
1110
|
-
const wrappedKeys =
|
|
1203
|
+
const wrappedKeys = z4.array(wrappedRecipientKeySchema).parse(input.wrappedKeys);
|
|
1111
1204
|
const privateKeys = guardianRecipientPrivateKeysSchema.parse(
|
|
1112
1205
|
input.privateKeys
|
|
1113
1206
|
);
|
|
@@ -1352,8 +1445,8 @@ function readFrontmatter(markdown, issues) {
|
|
|
1352
1445
|
return null;
|
|
1353
1446
|
}
|
|
1354
1447
|
}
|
|
1355
|
-
function resolveProfile(declared,
|
|
1356
|
-
const pinned = getProtocolProfile(declared) ?? (declared ===
|
|
1448
|
+
function resolveProfile(declared, fallback2, issues) {
|
|
1449
|
+
const pinned = getProtocolProfile(declared) ?? (declared === fallback2.profile ? fallback2 : null);
|
|
1357
1450
|
if (pinned === null) {
|
|
1358
1451
|
issues.push({
|
|
1359
1452
|
code: "profile_unknown",
|
|
@@ -1486,19 +1579,30 @@ function parseInlineList(value) {
|
|
|
1486
1579
|
}
|
|
1487
1580
|
|
|
1488
1581
|
// ../../packages/core/src/deployments.ts
|
|
1489
|
-
import { baseSepolia as baseSepolia2 } from "viem/chains";
|
|
1582
|
+
import { base as base2, baseSepolia as baseSepolia2 } from "viem/chains";
|
|
1490
1583
|
var DEPLOYMENTS = {
|
|
1584
|
+
[base2.id]: {
|
|
1585
|
+
ELGORA_HUB_ADDRESS: "0x8f80d1183cd983b01b0c9ac6777cc732ec9800de",
|
|
1586
|
+
ELGORA_ESCROW_TOKEN_ADDRESS: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
|
|
1587
|
+
payoutSchemeAddress: "0x36d304993cb1575a2f9b8a0388b18aa5ec6d7a61",
|
|
1588
|
+
ELGORA_SUBGRAPH_ENDPOINT: "https://api.goldsky.com/api/public/project_cmtu7wslg04s501ovc2je3beh/subgraphs/elgora-elgorahub-base/production/gn",
|
|
1589
|
+
ELGORA_API_BASE_URL: "https://elgora.ai",
|
|
1590
|
+
deployBlock: 51752389,
|
|
1591
|
+
deployTransaction: "0xd62f407b770c8fca82a9704b30ed0605d9e3ed2b56278dcca152958063f7f469",
|
|
1592
|
+
sourceCommit: "8d40270d617a63dd5a7a35045db0d9ea3ddd9093"
|
|
1593
|
+
},
|
|
1491
1594
|
[baseSepolia2.id]: {
|
|
1492
1595
|
ELGORA_HUB_ADDRESS: "0x2f97b5f616495c2e923f39a46648eb783c053ad7",
|
|
1493
1596
|
ELGORA_ESCROW_TOKEN_ADDRESS: "0x036cbd53842c5426634e7929541ec2318f3dcf7e",
|
|
1494
1597
|
payoutSchemeAddress: "0x752d4305b8567b777d479dfa9847dc4f5ffb5750",
|
|
1495
1598
|
ELGORA_SUBGRAPH_ENDPOINT: "https://api.goldsky.com/api/public/project_cmtu7wslg04s501ovc2je3beh/subgraphs/elgora-elgorahub/testnet-production/gn",
|
|
1599
|
+
ELGORA_API_BASE_URL: "https://staging.elgora.ai",
|
|
1496
1600
|
deployBlock: 46353511,
|
|
1497
1601
|
deployTransaction: "0xed3d3c8af3c69f9f8d4ec47da7179fe4b89bd4a4cd8d8232dbbd3d3f3d38e74b",
|
|
1498
1602
|
sourceCommit: "1377da5c38dc5c90d8608ffa3cb15ad6f8567a01"
|
|
1499
1603
|
}
|
|
1500
1604
|
};
|
|
1501
|
-
var DEFAULT_CHAIN_ID =
|
|
1605
|
+
var DEFAULT_CHAIN_ID = base2.id;
|
|
1502
1606
|
function getDeployment(chainId) {
|
|
1503
1607
|
return DEPLOYMENTS[chainId];
|
|
1504
1608
|
}
|
|
@@ -3807,7 +3911,7 @@ function settlementAmounts(escrowAmount, treasuryFeeBps, guardianFeeBps) {
|
|
|
3807
3911
|
import { zeroAddress } from "viem";
|
|
3808
3912
|
|
|
3809
3913
|
// ../../packages/core/src/api/bounty-preparation.ts
|
|
3810
|
-
import { ZodError, z as
|
|
3914
|
+
import { ZodError, z as z5 } from "zod";
|
|
3811
3915
|
|
|
3812
3916
|
// ../../packages/core/src/submission-timelock.ts
|
|
3813
3917
|
import {
|
|
@@ -4068,7 +4172,7 @@ import {
|
|
|
4068
4172
|
getAbiItem as getAbiItem2,
|
|
4069
4173
|
parseAbi,
|
|
4070
4174
|
parseSignature,
|
|
4071
|
-
recoverTypedDataAddress
|
|
4175
|
+
recoverTypedDataAddress as recoverTypedDataAddress2
|
|
4072
4176
|
} from "viem";
|
|
4073
4177
|
var ELGORA_HUB_ERRORS = ELGORA_HUB_ABI.filter(
|
|
4074
4178
|
(item) => item.type === "error"
|
|
@@ -4225,7 +4329,7 @@ async function prepareAuthorizedCreateBountyTransaction(input) {
|
|
|
4225
4329
|
if (unsupported) return null;
|
|
4226
4330
|
throw error;
|
|
4227
4331
|
}
|
|
4228
|
-
if ((await
|
|
4332
|
+
if ((await recoverTypedDataAddress2({ ...authorization, signature })).toLowerCase() !== poster.toLowerCase()) {
|
|
4229
4333
|
throw new Error("USDC authorization was not signed by the funding Poster.");
|
|
4230
4334
|
}
|
|
4231
4335
|
const { r, s, yParity } = parseSignature(signature);
|
|
@@ -4301,14 +4405,14 @@ var BoundaryValidationError = class extends Error {
|
|
|
4301
4405
|
this.issues = issues;
|
|
4302
4406
|
}
|
|
4303
4407
|
};
|
|
4304
|
-
var artifactUploadUrlRequestSchema =
|
|
4305
|
-
ciphertext_sha256:
|
|
4306
|
-
ciphertext_byte_length:
|
|
4408
|
+
var artifactUploadUrlRequestSchema = z5.object({
|
|
4409
|
+
ciphertext_sha256: z5.string().refine(isSha256Hex),
|
|
4410
|
+
ciphertext_byte_length: z5.number().int().positive().max(MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES)
|
|
4307
4411
|
}).strict();
|
|
4308
4412
|
|
|
4309
4413
|
// ../../packages/core/src/api/config.ts
|
|
4310
4414
|
import { createHash } from "crypto";
|
|
4311
|
-
import { z as
|
|
4415
|
+
import { z as z6 } from "zod";
|
|
4312
4416
|
function withDeploymentEnvDefaults(env) {
|
|
4313
4417
|
const merged = { ...env };
|
|
4314
4418
|
const setIfBlank = (key, value) => {
|
|
@@ -4329,11 +4433,11 @@ function withDeploymentEnvDefaults(env) {
|
|
|
4329
4433
|
setIfBlank("ELGORA_SUBGRAPH_ENDPOINT", deployment?.ELGORA_SUBGRAPH_ENDPOINT);
|
|
4330
4434
|
return merged;
|
|
4331
4435
|
}
|
|
4332
|
-
var chainIdField =
|
|
4436
|
+
var chainIdField = z6.coerce.number().int().positive().refine((chainId) => SUPPORTED_CHAINS[chainId] !== void 0, {
|
|
4333
4437
|
message: `ELGORA_CHAIN_ID must be a supported chain: ${Object.keys(SUPPORTED_CHAINS).join(", ")}`
|
|
4334
4438
|
});
|
|
4335
|
-
var hubAddressField =
|
|
4336
|
-
var publicDeploymentEnvSchema =
|
|
4439
|
+
var hubAddressField = z6.string().refine(isHexAddress, "ELGORA_HUB_ADDRESS must be a 20-byte EVM address").transform((value) => value.toLowerCase());
|
|
4440
|
+
var publicDeploymentEnvSchema = z6.object({
|
|
4337
4441
|
ELGORA_CHAIN_ID: chainIdField,
|
|
4338
4442
|
ELGORA_HUB_ADDRESS: hubAddressField
|
|
4339
4443
|
});
|
|
@@ -4349,40 +4453,50 @@ function loadPublicDeploymentConfig(env = process.env) {
|
|
|
4349
4453
|
var envShape = {
|
|
4350
4454
|
ELGORA_CHAIN_ID: chainIdField,
|
|
4351
4455
|
ELGORA_HUB_ADDRESS: hubAddressField,
|
|
4352
|
-
ELGORA_ESCROW_TOKEN_ADDRESS:
|
|
4456
|
+
ELGORA_ESCROW_TOKEN_ADDRESS: z6.string().refine(
|
|
4353
4457
|
isHexAddress,
|
|
4354
4458
|
"ELGORA_ESCROW_TOKEN_ADDRESS must be a 20-byte EVM address"
|
|
4355
4459
|
).transform((value) => value.toLowerCase()),
|
|
4356
|
-
ELGORA_TIMELOCK_REVEAL_DELAY_SECONDS:
|
|
4357
|
-
ELGORA_RPC_URL:
|
|
4460
|
+
ELGORA_TIMELOCK_REVEAL_DELAY_SECONDS: z6.coerce.number().int().nonnegative().default(DEFAULT_TIMELOCK_REVEAL_DELAY_SECONDS),
|
|
4461
|
+
ELGORA_RPC_URL: z6.string().min(1, "ELGORA_RPC_URL is required"),
|
|
4358
4462
|
/**
|
|
4359
4463
|
* Only consulted for a chain with no recorded deployment — a local anvil
|
|
4360
4464
|
* stack, whose addresses change on every deploy. A recorded deployment's
|
|
4361
4465
|
* scheme always wins, because settlement validation compares against it.
|
|
4362
4466
|
*/
|
|
4363
|
-
ELGORA_PAYOUT_SCHEME_ADDRESS:
|
|
4467
|
+
ELGORA_PAYOUT_SCHEME_ADDRESS: z6.string().refine(
|
|
4364
4468
|
isHexAddress,
|
|
4365
4469
|
"ELGORA_PAYOUT_SCHEME_ADDRESS must be a 20-byte EVM address"
|
|
4366
4470
|
).transform((value) => value.toLowerCase()).optional(),
|
|
4367
|
-
ELGORA_DATABASE_URL:
|
|
4368
|
-
ELGORA_SUBGRAPH_ENDPOINT:
|
|
4369
|
-
ELGORA_ARTIFACT_STORAGE_ENDPOINT:
|
|
4370
|
-
ELGORA_ARTIFACT_STORAGE_REGION:
|
|
4371
|
-
ELGORA_ARTIFACT_STORAGE_BUCKET:
|
|
4372
|
-
ELGORA_ARTIFACT_STORAGE_ACCESS_KEY_ID:
|
|
4373
|
-
ELGORA_ARTIFACT_STORAGE_SECRET_ACCESS_KEY:
|
|
4374
|
-
ELGORA_DELIVERY_PUBLIC_KEY:
|
|
4375
|
-
ELGORA_DELIVERY_PRIVATE_KEY:
|
|
4471
|
+
ELGORA_DATABASE_URL: z6.string().min(1, "ELGORA_DATABASE_URL is required"),
|
|
4472
|
+
ELGORA_SUBGRAPH_ENDPOINT: z6.string().min(1, "ELGORA_SUBGRAPH_ENDPOINT is required"),
|
|
4473
|
+
ELGORA_ARTIFACT_STORAGE_ENDPOINT: z6.string().min(1, "ELGORA_ARTIFACT_STORAGE_ENDPOINT is required"),
|
|
4474
|
+
ELGORA_ARTIFACT_STORAGE_REGION: z6.string().min(1, "ELGORA_ARTIFACT_STORAGE_REGION is required"),
|
|
4475
|
+
ELGORA_ARTIFACT_STORAGE_BUCKET: z6.string().min(1, "ELGORA_ARTIFACT_STORAGE_BUCKET is required"),
|
|
4476
|
+
ELGORA_ARTIFACT_STORAGE_ACCESS_KEY_ID: z6.string().min(1, "ELGORA_ARTIFACT_STORAGE_ACCESS_KEY_ID is required"),
|
|
4477
|
+
ELGORA_ARTIFACT_STORAGE_SECRET_ACCESS_KEY: z6.string().min(1, "ELGORA_ARTIFACT_STORAGE_SECRET_ACCESS_KEY is required"),
|
|
4478
|
+
ELGORA_DELIVERY_PUBLIC_KEY: z6.string().min(1, "ELGORA_DELIVERY_PUBLIC_KEY is required"),
|
|
4479
|
+
ELGORA_DELIVERY_PRIVATE_KEY: z6.string().min(1, "ELGORA_DELIVERY_PRIVATE_KEY is required")
|
|
4376
4480
|
};
|
|
4481
|
+
var readinessAudienceSchema = z6.string().trim().toLowerCase().min(1).max(512).refine((host) => {
|
|
4482
|
+
try {
|
|
4483
|
+
const url = new URL(`https://${host}`);
|
|
4484
|
+
return url.host === host && url.pathname === "/" && !url.username && !url.password && !url.search && !url.hash;
|
|
4485
|
+
} catch {
|
|
4486
|
+
return false;
|
|
4487
|
+
}
|
|
4488
|
+
}, "Must be a canonical hostname with optional port, without a URL path");
|
|
4377
4489
|
var publicationEnvShape = {
|
|
4378
4490
|
ELGORA_CHAIN_ID: envShape.ELGORA_CHAIN_ID,
|
|
4379
4491
|
ELGORA_HUB_ADDRESS: envShape.ELGORA_HUB_ADDRESS,
|
|
4380
4492
|
ELGORA_ESCROW_TOKEN_ADDRESS: envShape.ELGORA_ESCROW_TOKEN_ADDRESS,
|
|
4381
4493
|
ELGORA_DATABASE_URL: envShape.ELGORA_DATABASE_URL,
|
|
4382
|
-
ELGORA_SUBGRAPH_ENDPOINT: envShape.ELGORA_SUBGRAPH_ENDPOINT
|
|
4494
|
+
ELGORA_SUBGRAPH_ENDPOINT: envShape.ELGORA_SUBGRAPH_ENDPOINT,
|
|
4495
|
+
ELGORA_PUBLICATION_REVIEW_ENABLED: z6.string().trim().toLowerCase().pipe(z6.enum(["true", "false"])).default("true").transform((value) => value === "true"),
|
|
4496
|
+
ELGORA_READINESS_TIMEOUT_MS: z6.coerce.number().int().min(1).max(12e4).default(12e4)
|
|
4383
4497
|
};
|
|
4384
|
-
var envSchema =
|
|
4385
|
-
var bountyPublicationEnvSchema =
|
|
4498
|
+
var envSchema = z6.object(envShape);
|
|
4499
|
+
var bountyPublicationEnvSchema = z6.object(publicationEnvShape);
|
|
4386
4500
|
|
|
4387
4501
|
// ../../packages/core/src/api/elgora-hub-error-catalog.ts
|
|
4388
4502
|
import { decodeErrorResult } from "viem";
|
|
@@ -4396,55 +4510,12 @@ var ELGORA_HUB_FEE_POLICY_ABI = [
|
|
|
4396
4510
|
getAbiItem3({ abi: ELGORA_HUB_ABI, name: "guardianFeeBps" })
|
|
4397
4511
|
];
|
|
4398
4512
|
|
|
4399
|
-
// ../../packages/core/src/api/orchestrator-readiness.ts
|
|
4400
|
-
import { z as z6 } from "zod";
|
|
4401
|
-
var MAX_ORCHESTRATOR_ISSUES = 20;
|
|
4402
|
-
var readinessIssueSchema = z6.object({
|
|
4403
|
-
path: z6.string().trim().min(1).max(120),
|
|
4404
|
-
message: z6.string().trim().min(1).max(1500),
|
|
4405
|
-
code: z6.enum([
|
|
4406
|
-
"acceptance_undecidable",
|
|
4407
|
-
"winner_unresolved",
|
|
4408
|
-
"source_unidentified",
|
|
4409
|
-
"domain_underspecified",
|
|
4410
|
-
"platform_conflict",
|
|
4411
|
-
"operative_instruction",
|
|
4412
|
-
"placeholder_unreplaced",
|
|
4413
|
-
"unsafe_content"
|
|
4414
|
-
]),
|
|
4415
|
-
anchor: z6.string().trim().min(1).max(120),
|
|
4416
|
-
hazard: z6.boolean()
|
|
4417
|
-
}).strict();
|
|
4418
|
-
var readinessResponseSchema = z6.object({
|
|
4419
|
-
issues: z6.array(readinessIssueSchema).max(MAX_ORCHESTRATOR_ISSUES),
|
|
4420
|
-
judgeable: z6.boolean()
|
|
4421
|
-
}).strict();
|
|
4422
|
-
var READINESS_OUTPUT_JSON_SCHEMA = z6.toJSONSchema(readinessResponseSchema, {
|
|
4423
|
-
override: ({ jsonSchema }) => {
|
|
4424
|
-
delete jsonSchema.minLength;
|
|
4425
|
-
delete jsonSchema.maxLength;
|
|
4426
|
-
delete jsonSchema.maxItems;
|
|
4427
|
-
}
|
|
4428
|
-
});
|
|
4429
|
-
delete READINESS_OUTPUT_JSON_SCHEMA.$schema;
|
|
4430
|
-
var providerMessageSchema = z6.object({
|
|
4431
|
-
id: z6.string().optional(),
|
|
4432
|
-
choices: z6.array(
|
|
4433
|
-
z6.object({
|
|
4434
|
-
message: z6.object({
|
|
4435
|
-
content: z6.string().nullable().optional()
|
|
4436
|
-
}).passthrough(),
|
|
4437
|
-
finish_reason: z6.string().nullable().optional()
|
|
4438
|
-
}).passthrough()
|
|
4439
|
-
).min(1)
|
|
4440
|
-
}).passthrough();
|
|
4441
|
-
|
|
4442
4513
|
// ../../packages/core/src/api/solver-submission-responses.ts
|
|
4443
|
-
import { isHex as
|
|
4514
|
+
import { isHex as isHex4 } from "viem";
|
|
4444
4515
|
import { z as z7 } from "zod";
|
|
4445
4516
|
var hexAddressSchema = z7.string().refine(isHexAddress, "Must be a hex-encoded address");
|
|
4446
4517
|
var hexBytes32Schema = z7.string().refine(isHexBytes32, "Must be 32-byte hex");
|
|
4447
|
-
var hexDataSchema = z7.string().refine((value) =>
|
|
4518
|
+
var hexDataSchema = z7.string().refine((value) => isHex4(value), "Must be hex-encoded calldata");
|
|
4448
4519
|
var artifactUploadUrlResponseSchema = z7.object({
|
|
4449
4520
|
locator: z7.string().min(1),
|
|
4450
4521
|
upload_url: z7.string().min(1),
|
|
@@ -4598,7 +4669,7 @@ var FRONTMATTER_DELIMITER2 = "---";
|
|
|
4598
4669
|
var decimalSchema = z10.string().regex(/^[1-9][0-9]*$/, "Must be a positive integer").transform(Number).refine(Number.isSafeInteger, "Must be a safe integer");
|
|
4599
4670
|
var bountyIdSchema2 = z10.string().regex(/^(0|[1-9][0-9]*)$/, "Must be a non-negative integer");
|
|
4600
4671
|
var addressSchema2 = z10.string().refine(isHexAddress, "Must be a hex-encoded address").refine((value) => value === value.toLowerCase(), "Must use lowercase hex").transform((value) => value);
|
|
4601
|
-
var
|
|
4672
|
+
var bytes32Schema4 = z10.string().refine(isHexBytes32, "Must be 32-byte hex").refine((value) => value === value.toLowerCase(), "Must use lowercase hex").transform((value) => value);
|
|
4602
4673
|
var commonFields = {
|
|
4603
4674
|
format_version: z10.literal(WRITTEN_VERDICT_FORMAT_VERSION.toString()).transform(() => WRITTEN_VERDICT_FORMAT_VERSION),
|
|
4604
4675
|
chain_id: decimalSchema,
|
|
@@ -4614,7 +4685,7 @@ var writtenVerdictSchema = z10.discriminatedUnion("outcome", [
|
|
|
4614
4685
|
(value) => value !== zeroAddress2,
|
|
4615
4686
|
"Must not be the zero address"
|
|
4616
4687
|
),
|
|
4617
|
-
awarded_submission_commitment:
|
|
4688
|
+
awarded_submission_commitment: bytes32Schema4.refine(
|
|
4618
4689
|
(value) => value !== ZERO_BYTES32,
|
|
4619
4690
|
"Must not be zero bytes"
|
|
4620
4691
|
)
|
|
@@ -4716,20 +4787,52 @@ function invalidWrittenVerdict(path5, message) {
|
|
|
4716
4787
|
import {
|
|
4717
4788
|
createPublicClient,
|
|
4718
4789
|
createWalletClient,
|
|
4790
|
+
fallback,
|
|
4719
4791
|
http
|
|
4720
4792
|
} from "viem";
|
|
4793
|
+
|
|
4794
|
+
// ../../packages/core/src/auth/freshness.ts
|
|
4795
|
+
var API_REQUEST_APPROVAL_ANCHOR_LAG_SECONDS = 6;
|
|
4796
|
+
var FALLBACK_BLOCK_TIME_MS = 2e3;
|
|
4797
|
+
function blockTimeMsFor(chainId) {
|
|
4798
|
+
const chain = chainId === void 0 ? void 0 : SUPPORTED_CHAINS[chainId];
|
|
4799
|
+
return chain?.blockTime ?? FALLBACK_BLOCK_TIME_MS;
|
|
4800
|
+
}
|
|
4801
|
+
function blocksForSeconds(seconds, chainId) {
|
|
4802
|
+
const blocks = Math.ceil(seconds * 1e3 / blockTimeMsFor(chainId));
|
|
4803
|
+
return BigInt(Math.max(1, blocks));
|
|
4804
|
+
}
|
|
4805
|
+
function apiRequestApprovalAnchorLagBlocks(chainId) {
|
|
4806
|
+
return blocksForSeconds(API_REQUEST_APPROVAL_ANCHOR_LAG_SECONDS, chainId);
|
|
4807
|
+
}
|
|
4808
|
+
|
|
4809
|
+
// ../../packages/core/src/chain-client.ts
|
|
4721
4810
|
var RPC_RETRY_COUNT = 3;
|
|
4811
|
+
function createChainTransport(chainId, rpcUrl) {
|
|
4812
|
+
const publicRpcUrl = resolveSupportedChain(chainId).rpcUrls.default.http[0];
|
|
4813
|
+
const configuredRpcUrl = rpcUrl?.trim() || publicRpcUrl;
|
|
4814
|
+
if (!publicRpcUrl || configuredRpcUrl === publicRpcUrl) {
|
|
4815
|
+
return http(configuredRpcUrl, { retryCount: RPC_RETRY_COUNT });
|
|
4816
|
+
}
|
|
4817
|
+
return fallback(
|
|
4818
|
+
[
|
|
4819
|
+
http(configuredRpcUrl, { retryCount: 0 }),
|
|
4820
|
+
http(publicRpcUrl, { retryCount: 0 })
|
|
4821
|
+
],
|
|
4822
|
+
{ retryCount: RPC_RETRY_COUNT }
|
|
4823
|
+
);
|
|
4824
|
+
}
|
|
4722
4825
|
function createChainPublicClient(chainId, rpcUrl) {
|
|
4723
4826
|
return createPublicClient({
|
|
4724
4827
|
chain: resolveSupportedChain(chainId),
|
|
4725
|
-
transport:
|
|
4828
|
+
transport: createChainTransport(chainId, rpcUrl)
|
|
4726
4829
|
});
|
|
4727
4830
|
}
|
|
4728
4831
|
function createChainWalletClient(chainId, rpcUrl, account) {
|
|
4729
4832
|
return createWalletClient({
|
|
4730
4833
|
account,
|
|
4731
4834
|
chain: resolveSupportedChain(chainId),
|
|
4732
|
-
transport:
|
|
4835
|
+
transport: createChainTransport(chainId, rpcUrl)
|
|
4733
4836
|
});
|
|
4734
4837
|
}
|
|
4735
4838
|
function assertChainId(actual, expected) {
|
|
@@ -4737,10 +4840,10 @@ function assertChainId(actual, expected) {
|
|
|
4737
4840
|
throw new Error(`RPC returned chain_id ${actual}, expected ${expected}`);
|
|
4738
4841
|
}
|
|
4739
4842
|
}
|
|
4740
|
-
var SIGNING_BLOCK_OFFSET = 3n;
|
|
4741
4843
|
async function fetchSignableBlock(client) {
|
|
4844
|
+
const offset = apiRequestApprovalAnchorLagBlocks(client.chain?.id);
|
|
4742
4845
|
const head = await client.getBlockNumber();
|
|
4743
|
-
const anchorNumber = head >
|
|
4846
|
+
const anchorNumber = head > offset ? head - offset : 0n;
|
|
4744
4847
|
const anchor = await client.getBlock({ blockNumber: anchorNumber });
|
|
4745
4848
|
return { number: anchor.number, hash: anchor.hash };
|
|
4746
4849
|
}
|
|
@@ -5032,7 +5135,6 @@ function loadClaimConfig(env, claimantAddress) {
|
|
|
5032
5135
|
}
|
|
5033
5136
|
|
|
5034
5137
|
// ../../packages/core/src/api-client-config.ts
|
|
5035
|
-
var PRODUCTION_API_BASE_URL = "https://elgora.vercel.app";
|
|
5036
5138
|
var LOCAL_DEV_API_BASE_URL = "http://127.0.0.1:3000";
|
|
5037
5139
|
var API_BASE_URL_FLAG = "--api-base-url";
|
|
5038
5140
|
function loadApiClientConfig(env, apiBaseUrlOverride) {
|
|
@@ -5060,11 +5162,20 @@ function resolveApiBaseUrl(env, override) {
|
|
|
5060
5162
|
return value;
|
|
5061
5163
|
}
|
|
5062
5164
|
function defaultApiBaseUrl(env) {
|
|
5063
|
-
|
|
5165
|
+
if (env.NODE_ENV === "development") return LOCAL_DEV_API_BASE_URL;
|
|
5166
|
+
const chainId = Number(env.ELGORA_CHAIN_ID?.trim() || DEFAULT_CHAIN_ID);
|
|
5167
|
+
const recorded = getDeployment(chainId)?.ELGORA_API_BASE_URL;
|
|
5168
|
+
if (!recorded) {
|
|
5169
|
+
throw new Error(
|
|
5170
|
+
`No Elgora API is recorded for chain ${chainId}; set ELGORA_API_BASE_URL or pass --api-base-url`
|
|
5171
|
+
);
|
|
5172
|
+
}
|
|
5173
|
+
return recorded;
|
|
5064
5174
|
}
|
|
5065
5175
|
|
|
5066
5176
|
// ../../packages/core/src/api-client.ts
|
|
5067
5177
|
var ApiRequestError = class extends Error {
|
|
5178
|
+
issues;
|
|
5068
5179
|
status;
|
|
5069
5180
|
code;
|
|
5070
5181
|
retryAfterMs;
|
|
@@ -5073,15 +5184,16 @@ var ApiRequestError = class extends Error {
|
|
|
5073
5184
|
`Request to ${input.url} failed with ${input.status}: ${formatApiError(input.body)}`
|
|
5074
5185
|
);
|
|
5075
5186
|
this.name = "ApiRequestError";
|
|
5187
|
+
this.issues = readApiErrorIssues(input.body);
|
|
5076
5188
|
this.status = input.status;
|
|
5077
5189
|
this.code = readApiErrorCode(input.body);
|
|
5078
5190
|
this.retryAfterMs = input.retryAfterMs;
|
|
5079
5191
|
}
|
|
5080
5192
|
};
|
|
5081
|
-
async function postText(url, body, contentType, authorization) {
|
|
5193
|
+
async function postText(url, body, contentType, authorization, headers = {}) {
|
|
5082
5194
|
const response = await fetch(url, {
|
|
5083
5195
|
method: "POST",
|
|
5084
|
-
headers: { "content-type": contentType, authorization },
|
|
5196
|
+
headers: { ...headers, "content-type": contentType, authorization },
|
|
5085
5197
|
body
|
|
5086
5198
|
});
|
|
5087
5199
|
return parseJsonResponse(url, response);
|
|
@@ -5186,12 +5298,11 @@ function formatApiError(body) {
|
|
|
5186
5298
|
if (body && typeof body === "object" && "error" in body && body.error && typeof body.error === "object" && "message" in body.error) {
|
|
5187
5299
|
const apiError = body.error;
|
|
5188
5300
|
const parts = [String(apiError.message)];
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
}
|
|
5301
|
+
for (const issue of readApiErrorIssues(body)) {
|
|
5302
|
+
let line = `${issue.anchor || issue.path || "request"}: ${issue.message}`;
|
|
5303
|
+
if (issue.code) line += ` [${issue.code}]`;
|
|
5304
|
+
if (issue.hazard !== void 0) line += ` (hazard: ${issue.hazard})`;
|
|
5305
|
+
parts.push(line);
|
|
5195
5306
|
}
|
|
5196
5307
|
if (apiError.next_action !== void 0 && apiError.next_action !== null) {
|
|
5197
5308
|
parts.push(`next action: ${String(apiError.next_action)}`);
|
|
@@ -5214,6 +5325,13 @@ async function assertResponseOk(url, response) {
|
|
|
5214
5325
|
retryAfterMs: readRetryAfterMs(response)
|
|
5215
5326
|
});
|
|
5216
5327
|
}
|
|
5328
|
+
function readApiErrorIssues(body) {
|
|
5329
|
+
if (!body || typeof body !== "object" || !("error" in body) || !body.error || typeof body.error !== "object" || !("issues" in body.error) || !Array.isArray(body.error.issues))
|
|
5330
|
+
return [];
|
|
5331
|
+
return body.error.issues.filter(
|
|
5332
|
+
(issue) => issue && typeof issue === "object" && typeof issue.path === "string" && typeof issue.message === "string" && (issue.code === void 0 || typeof issue.code === "string") && (issue.anchor === void 0 || typeof issue.anchor === "string") && (issue.hazard === void 0 || typeof issue.hazard === "boolean")
|
|
5333
|
+
);
|
|
5334
|
+
}
|
|
5217
5335
|
function readApiErrorCode(body) {
|
|
5218
5336
|
if (!body || typeof body !== "object" || !("error" in body) || !body.error || typeof body.error !== "object" || !("code" in body.error) || typeof body.error.code !== "string") {
|
|
5219
5337
|
return void 0;
|
|
@@ -5235,7 +5353,7 @@ function toArrayBuffer(bytes) {
|
|
|
5235
5353
|
import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
5236
5354
|
|
|
5237
5355
|
// ../../packages/core/src/auth/authorization-header.ts
|
|
5238
|
-
import { isAddress as isAddress3, isHex as
|
|
5356
|
+
import { isAddress as isAddress3, isHex as isHex5 } from "viem";
|
|
5239
5357
|
var ELGORA_APPROVAL_AUTH_SCHEME = "Elgora-Approval";
|
|
5240
5358
|
var FIELD_SEPARATOR = "~";
|
|
5241
5359
|
function encodeApiRequestApprovalHeader(fields) {
|
|
@@ -5248,59 +5366,27 @@ function encodeApiRequestApprovalHeader(fields) {
|
|
|
5248
5366
|
return `${ELGORA_APPROVAL_AUTH_SCHEME} ${btoa(payload)}`;
|
|
5249
5367
|
}
|
|
5250
5368
|
|
|
5251
|
-
// ../../packages/core/src/auth/
|
|
5252
|
-
var
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
{ name: "query", type: "string" },
|
|
5259
|
-
{ name: "blockNumber", type: "uint256" },
|
|
5260
|
-
{ name: "blockHash", type: "bytes32" }
|
|
5261
|
-
]
|
|
5262
|
-
};
|
|
5263
|
-
function buildElgoraEip712Domain(params) {
|
|
5264
|
-
return {
|
|
5265
|
-
name: "Elgora",
|
|
5266
|
-
version: "1",
|
|
5267
|
-
chainId: params.chainId,
|
|
5268
|
-
verifyingContract: params.hubAddress
|
|
5269
|
-
};
|
|
5270
|
-
}
|
|
5271
|
-
function buildApiRequestApprovalTypedData(params) {
|
|
5272
|
-
return {
|
|
5273
|
-
domain: buildElgoraEip712Domain({
|
|
5274
|
-
chainId: params.chainId,
|
|
5275
|
-
hubAddress: params.hubAddress
|
|
5276
|
-
}),
|
|
5277
|
-
types: API_REQUEST_APPROVAL_TYPES,
|
|
5278
|
-
primaryType: API_REQUEST_APPROVAL_PRIMARY_TYPE,
|
|
5279
|
-
message: params.message
|
|
5280
|
-
};
|
|
5281
|
-
}
|
|
5282
|
-
function endpointIdFor(input) {
|
|
5283
|
-
return `${input.method.toUpperCase()} ${input.path}`;
|
|
5284
|
-
}
|
|
5285
|
-
function audienceFor(host) {
|
|
5286
|
-
return host.toLowerCase();
|
|
5287
|
-
}
|
|
5288
|
-
function queryFor(query) {
|
|
5289
|
-
return new URLSearchParams(query).toString();
|
|
5369
|
+
// ../../packages/core/src/auth/readiness-signoff-header.ts
|
|
5370
|
+
var ELGORA_READINESS_SIGNOFF_HEADER = "Elgora-Readiness-Signoff";
|
|
5371
|
+
function encodeReadinessSignoffHeader(signoff) {
|
|
5372
|
+
const bytes = new TextEncoder().encode(
|
|
5373
|
+
JSON.stringify(readinessSignoffSchema.parse(signoff))
|
|
5374
|
+
);
|
|
5375
|
+
return btoa(String.fromCharCode(...bytes));
|
|
5290
5376
|
}
|
|
5291
5377
|
|
|
5292
5378
|
// ../../packages/core/src/auth/request-approval.ts
|
|
5293
5379
|
import {
|
|
5294
5380
|
hashTypedData,
|
|
5295
|
-
isAddressEqual as
|
|
5381
|
+
isAddressEqual as isAddressEqual3,
|
|
5296
5382
|
keccak256 as keccak2565,
|
|
5297
5383
|
toBytes as toBytes4
|
|
5298
5384
|
} from "viem";
|
|
5299
5385
|
|
|
5300
5386
|
// ../../packages/core/src/auth/signature.ts
|
|
5301
5387
|
import {
|
|
5302
|
-
isAddressEqual,
|
|
5303
|
-
recoverTypedDataAddress as
|
|
5388
|
+
isAddressEqual as isAddressEqual2,
|
|
5389
|
+
recoverTypedDataAddress as recoverTypedDataAddress3
|
|
5304
5390
|
} from "viem";
|
|
5305
5391
|
|
|
5306
5392
|
// ../../packages/core/src/auth/request-approval-header.ts
|
|
@@ -5581,6 +5667,7 @@ function readGuardianPrivateKeys(env) {
|
|
|
5581
5667
|
}
|
|
5582
5668
|
|
|
5583
5669
|
// src/guardian-deliver-key.ts
|
|
5670
|
+
var PENDING_FINALITY_EXIT_CODE = 75;
|
|
5584
5671
|
var USAGE = "Usage: elgora-cli guardian:deliver-key <bounty_id> <solver> <submission_commitment>";
|
|
5585
5672
|
async function main4(env = process.env, argv = process.argv.slice(2)) {
|
|
5586
5673
|
const { apiBaseUrl, argv: positionalArgs } = extractApiBaseUrlFlag(argv);
|
|
@@ -5595,6 +5682,33 @@ async function main4(env = process.env, argv = process.argv.slice(2)) {
|
|
|
5595
5682
|
"ELGORA_GUARDIAN_ACCOUNT_PRIVATE_KEY must be a 32-byte hex private key"
|
|
5596
5683
|
);
|
|
5597
5684
|
}
|
|
5685
|
+
const guardianPrivateKeys = readGuardianPrivateKeys(env);
|
|
5686
|
+
const publicClient = createChainPublicClient(config.chainId, rpcUrl);
|
|
5687
|
+
const finality = await readSettlementFinality(
|
|
5688
|
+
publicClient,
|
|
5689
|
+
config.hubAddress,
|
|
5690
|
+
bountyId
|
|
5691
|
+
);
|
|
5692
|
+
if (finality.latestStatus === "no_valid_submission" || finality.latestStatus === "timed_out") {
|
|
5693
|
+
throw new Error(
|
|
5694
|
+
`Bounty ${bountyId.toString()} settled ${finality.latestStatus}; there is no winning key to deliver`
|
|
5695
|
+
);
|
|
5696
|
+
}
|
|
5697
|
+
if (finality.pending) {
|
|
5698
|
+
console.log(
|
|
5699
|
+
JSON.stringify({
|
|
5700
|
+
event: "guardian_deliver_key.pending_finality",
|
|
5701
|
+
bounty_id: bountyId.toString(),
|
|
5702
|
+
solver,
|
|
5703
|
+
submission_commitment: submissionCommitment,
|
|
5704
|
+
latest_status: finality.latestStatus,
|
|
5705
|
+
finalized_status: finality.finalizedStatus,
|
|
5706
|
+
message: "Settlement is not behind the chain's finalized tag yet; nothing was delivered. Retry in 10-20 minutes."
|
|
5707
|
+
})
|
|
5708
|
+
);
|
|
5709
|
+
process.exitCode = PENDING_FINALITY_EXIT_CODE;
|
|
5710
|
+
return;
|
|
5711
|
+
}
|
|
5598
5712
|
const account = privateKeyToAccount4(guardianAccountPrivateKey);
|
|
5599
5713
|
const wrapped = await wrapWinningSolutionKey({
|
|
5600
5714
|
config,
|
|
@@ -5605,8 +5719,8 @@ async function main4(env = process.env, argv = process.argv.slice(2)) {
|
|
|
5605
5719
|
privateKey: guardianAccountPrivateKey,
|
|
5606
5720
|
address: account.address
|
|
5607
5721
|
},
|
|
5608
|
-
publicClient
|
|
5609
|
-
guardianPrivateKeys
|
|
5722
|
+
publicClient,
|
|
5723
|
+
guardianPrivateKeys
|
|
5610
5724
|
});
|
|
5611
5725
|
console.log(
|
|
5612
5726
|
JSON.stringify({
|
|
@@ -5618,6 +5732,30 @@ async function main4(env = process.env, argv = process.argv.slice(2)) {
|
|
|
5618
5732
|
})
|
|
5619
5733
|
);
|
|
5620
5734
|
}
|
|
5735
|
+
async function readSettlementFinality(publicClient, hubAddress, bountyId) {
|
|
5736
|
+
const finalizedBlock = await publicClient.getBlock({
|
|
5737
|
+
blockTag: "finalized"
|
|
5738
|
+
});
|
|
5739
|
+
const readStatus = async (blockNumber) => {
|
|
5740
|
+
const [, state] = await publicClient.readContract({
|
|
5741
|
+
address: hubAddress,
|
|
5742
|
+
abi: ELGORA_HUB_BOUNTY_READ_ABI,
|
|
5743
|
+
functionName: "getBounty",
|
|
5744
|
+
args: [bountyId],
|
|
5745
|
+
...blockNumber === void 0 ? {} : { blockNumber }
|
|
5746
|
+
});
|
|
5747
|
+
return decodeBountyStatus(state.status);
|
|
5748
|
+
};
|
|
5749
|
+
const [latestStatus, finalizedStatus] = await Promise.all([
|
|
5750
|
+
readStatus(),
|
|
5751
|
+
readStatus(finalizedBlock.number)
|
|
5752
|
+
]);
|
|
5753
|
+
return {
|
|
5754
|
+
pending: latestStatus === "awarded" && finalizedStatus === "open",
|
|
5755
|
+
latestStatus,
|
|
5756
|
+
finalizedStatus
|
|
5757
|
+
};
|
|
5758
|
+
}
|
|
5621
5759
|
function readArgs(argv) {
|
|
5622
5760
|
const [bountyId, solver, submissionCommitment] = argv;
|
|
5623
5761
|
if (!bountyId || !isNumeric(bountyId) || !solver || !isHexAddress(solver) || !submissionCommitment || !isHexBytes32(submissionCommitment)) {
|
|
@@ -5754,6 +5892,13 @@ var BOUNTY_DETAIL_QUERY = `
|
|
|
5754
5892
|
reportCommitments
|
|
5755
5893
|
awardedPayoutData
|
|
5756
5894
|
timedOutRefundAmount
|
|
5895
|
+
settledLogIndex
|
|
5896
|
+
claimsQueued(first: 1, orderBy: queuedLogIndex, orderDirection: desc) {
|
|
5897
|
+
recipient
|
|
5898
|
+
amount
|
|
5899
|
+
queuedTxHash
|
|
5900
|
+
queuedLogIndex
|
|
5901
|
+
}
|
|
5757
5902
|
guardianRoster {
|
|
5758
5903
|
${GUARDIAN_ROSTER_SNAPSHOT_FIELDS}
|
|
5759
5904
|
}
|
|
@@ -5902,9 +6047,10 @@ function createElgoraSubgraphClient(options) {
|
|
|
5902
6047
|
);
|
|
5903
6048
|
raw = await response.text();
|
|
5904
6049
|
} catch (cause) {
|
|
6050
|
+
const code = transportErrorCode(cause);
|
|
5905
6051
|
throw new SubgraphReadError(
|
|
5906
6052
|
"transport_failed",
|
|
5907
|
-
|
|
6053
|
+
`Subgraph request did not complete${code ? ` (${code})` : ""}`,
|
|
5908
6054
|
{ cause }
|
|
5909
6055
|
);
|
|
5910
6056
|
}
|
|
@@ -6057,6 +6203,17 @@ function createElgoraSubgraphClient(options) {
|
|
|
6057
6203
|
}
|
|
6058
6204
|
};
|
|
6059
6205
|
}
|
|
6206
|
+
function transportErrorCode(error, depth = 0) {
|
|
6207
|
+
if (depth > 4 || typeof error !== "object" || error === null)
|
|
6208
|
+
return void 0;
|
|
6209
|
+
const { code, cause, errors } = error;
|
|
6210
|
+
if (typeof code === "string" && /^[A-Z][A-Z0-9_]*$/.test(code)) return code;
|
|
6211
|
+
for (const inner of [cause, ...Array.isArray(errors) ? errors : []]) {
|
|
6212
|
+
const found = transportErrorCode(inner, depth + 1);
|
|
6213
|
+
if (found) return found;
|
|
6214
|
+
}
|
|
6215
|
+
return void 0;
|
|
6216
|
+
}
|
|
6060
6217
|
var defaultFetch = (endpoint, body) => fetch(endpoint, {
|
|
6061
6218
|
method: "POST",
|
|
6062
6219
|
headers: { "content-type": "application/json" },
|
|
@@ -6131,8 +6288,17 @@ function mapAnalyticsGuardianRoster(row) {
|
|
|
6131
6288
|
}
|
|
6132
6289
|
function mapBountyDetail(row) {
|
|
6133
6290
|
const record = readRecord(row);
|
|
6291
|
+
const allocation = record.claimsQueued == null ? null : asArray(record.claimsQueued, "claimsQueued")[0];
|
|
6292
|
+
let guardianFeeAmount = null;
|
|
6293
|
+
if ((record.status === "Awarded" || record.status === "NoValidSubmission") && allocation && record.settledTxHash && record.settledLogIndex != null) {
|
|
6294
|
+
const claim = readRecord(allocation);
|
|
6295
|
+
if (asHex(claim.recipient, "recipient") === asHex(record.guardianFeeRecipient, "guardianFeeRecipient") && asHex(claim.queuedTxHash, "queuedTxHash") === asHex(record.settledTxHash, "settledTxHash") && asNumber(claim.queuedLogIndex, "queuedLogIndex") + 1 === asNumber(record.settledLogIndex, "settledLogIndex")) {
|
|
6296
|
+
guardianFeeAmount = asString(claim.amount, "amount");
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6134
6299
|
return {
|
|
6135
6300
|
...mapBounty(record),
|
|
6301
|
+
guardianFeeAmount,
|
|
6136
6302
|
guardianRosterAdmin: asHex(
|
|
6137
6303
|
record.guardianRosterAdmin,
|
|
6138
6304
|
"guardianRosterAdmin"
|
|
@@ -6586,7 +6752,10 @@ function parseChallenge(text) {
|
|
|
6586
6752
|
|
|
6587
6753
|
// src/guardian-verdict.ts
|
|
6588
6754
|
import { readFile as readFile3 } from "fs/promises";
|
|
6589
|
-
import {
|
|
6755
|
+
import {
|
|
6756
|
+
BaseError as BaseError2,
|
|
6757
|
+
zeroAddress as zeroAddress4
|
|
6758
|
+
} from "viem";
|
|
6590
6759
|
import { privateKeyToAccount as privateKeyToAccount6 } from "viem/accounts";
|
|
6591
6760
|
var OUTCOME = {
|
|
6592
6761
|
no_valid_submission: 0,
|
|
@@ -6797,20 +6966,66 @@ async function main7(env = process.env, argv = process.argv.slice(2)) {
|
|
|
6797
6966
|
functionName: "commitVerdict",
|
|
6798
6967
|
args: contractArgs
|
|
6799
6968
|
});
|
|
6969
|
+
const replacements = [];
|
|
6800
6970
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
6801
|
-
hash: transactionHash
|
|
6971
|
+
hash: transactionHash,
|
|
6972
|
+
onReplaced: (replacement) => replacements.push(replacement)
|
|
6802
6973
|
});
|
|
6974
|
+
if (receipt.transactionHash !== transactionHash && replacements.at(-1)?.reason !== "repriced") {
|
|
6975
|
+
throw new Error(
|
|
6976
|
+
`commitVerdict transaction ${transactionHash} was ${replacements.at(-1)?.reason ?? "replaced"} by ${receipt.transactionHash}; this Verdict was not recorded`
|
|
6977
|
+
);
|
|
6978
|
+
}
|
|
6803
6979
|
if (receipt.status !== "success") {
|
|
6804
|
-
throw new Error(
|
|
6980
|
+
throw new Error(
|
|
6981
|
+
`commitVerdict transaction reverted: ${receipt.transactionHash}`
|
|
6982
|
+
);
|
|
6805
6983
|
}
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6984
|
+
let readbackIssue;
|
|
6985
|
+
try {
|
|
6986
|
+
const recorded = await publicClient.readContract({
|
|
6987
|
+
address: config.hubAddress,
|
|
6988
|
+
abi: ELGORA_HUB_VERDICT_WRITE_ABI,
|
|
6989
|
+
functionName: "verdicts",
|
|
6990
|
+
args: [args.bountyId, account.address],
|
|
6991
|
+
blockNumber: receipt.blockNumber
|
|
6992
|
+
});
|
|
6993
|
+
if (!recorded[0] || recorded[1] !== contractArgs[1] || recorded[2].toLowerCase() !== contractArgs[2].toLowerCase() || recorded[3].toLowerCase() !== contractArgs[3].toLowerCase() || recorded[4].toLowerCase() !== reportCommitment || recorded[5].toLowerCase() !== "0x") {
|
|
6994
|
+
readbackIssue = {
|
|
6995
|
+
observed: {
|
|
6996
|
+
committed: recorded[0],
|
|
6997
|
+
outcome: recorded[1],
|
|
6998
|
+
winner: recorded[2],
|
|
6999
|
+
awarded_submission_commitment: recorded[3],
|
|
7000
|
+
report_commitment: recorded[4],
|
|
7001
|
+
payout_data: recorded[5]
|
|
7002
|
+
}
|
|
7003
|
+
};
|
|
7004
|
+
}
|
|
7005
|
+
} catch (error) {
|
|
7006
|
+
readbackIssue = {
|
|
7007
|
+
error: error instanceof BaseError2 ? error.walk().name : error instanceof Error ? error.name : "UnknownError"
|
|
7008
|
+
};
|
|
7009
|
+
}
|
|
7010
|
+
if (readbackIssue) {
|
|
7011
|
+
console.warn(
|
|
7012
|
+
JSON.stringify({
|
|
7013
|
+
event: "guardian_verdict.readback_unconfirmed",
|
|
7014
|
+
bounty_id: args.bountyId.toString(),
|
|
7015
|
+
guardian: account.address.toLowerCase(),
|
|
7016
|
+
tx_hash: receipt.transactionHash,
|
|
7017
|
+
block_number: receipt.blockNumber.toString(),
|
|
7018
|
+
expected: {
|
|
7019
|
+
committed: true,
|
|
7020
|
+
outcome: contractArgs[1],
|
|
7021
|
+
winner: contractArgs[2],
|
|
7022
|
+
awarded_submission_commitment: contractArgs[3],
|
|
7023
|
+
report_commitment: reportCommitment,
|
|
7024
|
+
payout_data: "0x"
|
|
7025
|
+
},
|
|
7026
|
+
...readbackIssue
|
|
7027
|
+
})
|
|
7028
|
+
);
|
|
6814
7029
|
}
|
|
6815
7030
|
console.log(
|
|
6816
7031
|
JSON.stringify({
|
|
@@ -6823,7 +7038,7 @@ async function main7(env = process.env, argv = process.argv.slice(2)) {
|
|
|
6823
7038
|
awarded_submission_commitment: args.awardedSubmissionCommitment
|
|
6824
7039
|
} : {},
|
|
6825
7040
|
report_commitment: reportCommitment,
|
|
6826
|
-
tx_hash: transactionHash,
|
|
7041
|
+
tx_hash: receipt.transactionHash,
|
|
6827
7042
|
block_number: receipt.blockNumber.toString()
|
|
6828
7043
|
})
|
|
6829
7044
|
);
|
|
@@ -6874,14 +7089,40 @@ function usageError() {
|
|
|
6874
7089
|
}
|
|
6875
7090
|
|
|
6876
7091
|
// src/network-flag.ts
|
|
6877
|
-
import { base as
|
|
7092
|
+
import { base as base3, baseSepolia as baseSepolia3, foundry as foundry3 } from "viem/chains";
|
|
6878
7093
|
var NETWORK_FLAG = "--network";
|
|
6879
|
-
var
|
|
6880
|
-
base:
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
7094
|
+
var NETWORKS = [
|
|
7095
|
+
{ name: "base", chainId: base3.id, label: "Base mainnet" },
|
|
7096
|
+
{
|
|
7097
|
+
name: "base-sepolia",
|
|
7098
|
+
chainId: baseSepolia3.id,
|
|
7099
|
+
label: "Base Sepolia testnet, Elgora's staging deployment"
|
|
7100
|
+
},
|
|
7101
|
+
{ name: "local", chainId: foundry3.id, label: "a local Anvil stack" }
|
|
7102
|
+
];
|
|
7103
|
+
var NETWORK_CHAIN_IDS = Object.fromEntries(
|
|
7104
|
+
NETWORKS.map(({ name, chainId }) => [name, chainId])
|
|
7105
|
+
);
|
|
7106
|
+
var STAGING_NETWORK = NETWORKS.find(
|
|
7107
|
+
({ chainId }) => chainId === baseSepolia3.id
|
|
7108
|
+
);
|
|
7109
|
+
var NETWORK_NAMES_HINT = NETWORKS.map(
|
|
7110
|
+
({ name, chainId }) => `${name} (${chainId})`
|
|
7111
|
+
).join(", ");
|
|
7112
|
+
function describeChain(chainId) {
|
|
7113
|
+
const network = NETWORKS.find((candidate) => candidate.chainId === chainId);
|
|
7114
|
+
return network ? `${network.name} (${network.label})` : `chain ${chainId}`;
|
|
7115
|
+
}
|
|
7116
|
+
var STAGING_HINT = `add ${NETWORK_FLAG} ${STAGING_NETWORK.name} or set ELGORA_CHAIN_ID=${STAGING_NETWORK.chainId}`;
|
|
7117
|
+
var DEFAULT_NETWORK_HELP_LINES = [
|
|
7118
|
+
`Default: ${describeChain(DEFAULT_CHAIN_ID)}.`,
|
|
7119
|
+
`Staging: ${describeChain(STAGING_NETWORK.chainId)};`,
|
|
7120
|
+
` ${STAGING_HINT}.`
|
|
7121
|
+
];
|
|
7122
|
+
function defaultNetworkNotice(env) {
|
|
7123
|
+
if (env.ELGORA_CHAIN_ID?.trim()) return void 0;
|
|
7124
|
+
return `elgora-cli: using the default network, ${describeChain(DEFAULT_CHAIN_ID)}. For staging, ${STAGING_HINT}.`;
|
|
7125
|
+
}
|
|
6885
7126
|
function applyNetworkFlag(env, argv) {
|
|
6886
7127
|
const present = argv.some(
|
|
6887
7128
|
(arg) => arg === NETWORK_FLAG || arg.startsWith(`${NETWORK_FLAG}=`)
|
|
@@ -6907,7 +7148,7 @@ function resolveChainId(selector) {
|
|
|
6907
7148
|
}
|
|
6908
7149
|
|
|
6909
7150
|
// src/version.ts
|
|
6910
|
-
var CLI_VERSION = true ? "
|
|
7151
|
+
var CLI_VERSION = true ? "9.0.0" : "0.0.0-dev";
|
|
6911
7152
|
var CLI_NODE_ENGINES = true ? ">=24 <25" : ">=24 <25";
|
|
6912
7153
|
|
|
6913
7154
|
// src/node-engines.ts
|
|
@@ -7114,26 +7355,99 @@ function required3(env, name) {
|
|
|
7114
7355
|
}
|
|
7115
7356
|
|
|
7116
7357
|
// src/publish-fund.ts
|
|
7117
|
-
import { readFile as
|
|
7118
|
-
import { text as readStdinText3 } from "stream/consumers";
|
|
7358
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
7119
7359
|
import {
|
|
7120
7360
|
decodeFunctionData,
|
|
7121
7361
|
parseAbi as parseAbi2,
|
|
7122
7362
|
parseEventLogs
|
|
7123
7363
|
} from "viem";
|
|
7124
7364
|
import { privateKeyToAccount as privateKeyToAccount8 } from "viem/accounts";
|
|
7125
|
-
|
|
7365
|
+
|
|
7366
|
+
// src/poster-request.ts
|
|
7367
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
7368
|
+
import { text as readStdinText3 } from "stream/consumers";
|
|
7369
|
+
function loadPosterConfig(env, apiBaseUrlOverride) {
|
|
7370
|
+
const defaults = withDeploymentDefaults(env);
|
|
7371
|
+
const clientConfig = loadClientDeploymentConfig(defaults, apiBaseUrlOverride);
|
|
7372
|
+
const rpcUrl = defaults.ELGORA_RPC_URL?.trim();
|
|
7373
|
+
if (!rpcUrl) throw new Error("ELGORA_RPC_URL is required");
|
|
7374
|
+
const privateKey = env.ELGORA_POSTER_PRIVATE_KEY?.trim();
|
|
7375
|
+
if (!privateKey || !isHexBytes32(privateKey)) {
|
|
7376
|
+
throw new Error(
|
|
7377
|
+
"ELGORA_POSTER_PRIVATE_KEY must be a 32-byte hex private key"
|
|
7378
|
+
);
|
|
7379
|
+
}
|
|
7380
|
+
return {
|
|
7381
|
+
...clientConfig,
|
|
7382
|
+
rpcUrl,
|
|
7383
|
+
posterPrivateKey: privateKey
|
|
7384
|
+
};
|
|
7385
|
+
}
|
|
7386
|
+
function takeFlag(argv, flag, usage) {
|
|
7387
|
+
const uses = argv.filter(
|
|
7388
|
+
(arg) => arg === flag || arg.startsWith(`${flag}=`)
|
|
7389
|
+
).length;
|
|
7390
|
+
const extracted = extractFlagValue(argv, flag);
|
|
7391
|
+
if (uses > 1 || uses === 1 && (!extracted.value || extracted.value.startsWith("--")))
|
|
7392
|
+
throw new Error(usage);
|
|
7393
|
+
return extracted;
|
|
7394
|
+
}
|
|
7395
|
+
async function readBountyChallengeMarkdown2(path5) {
|
|
7396
|
+
return path5 === "-" ? readStdinText3(process.stdin) : readFile4(path5, "utf8");
|
|
7397
|
+
}
|
|
7398
|
+
async function signPosterRequest(input) {
|
|
7399
|
+
return buildAuthorizationHeader({
|
|
7400
|
+
requestUrl: input.requestUrl.toString(),
|
|
7401
|
+
method: "POST",
|
|
7402
|
+
body: input.markdown,
|
|
7403
|
+
chainId: input.config.chainId,
|
|
7404
|
+
hubAddress: input.config.hubAddress,
|
|
7405
|
+
address: input.account.address,
|
|
7406
|
+
block: await fetchSignableBlock(input.publicClient),
|
|
7407
|
+
sign: (typedData) => input.account.signTypedData(typedData)
|
|
7408
|
+
});
|
|
7409
|
+
}
|
|
7410
|
+
|
|
7411
|
+
// src/publish-fund.ts
|
|
7412
|
+
var PUBLISH_FUND_USAGE = "Usage: elgora-cli poster:publish-fund <bounty_challenge_md_path|-> --signoff <file>";
|
|
7126
7413
|
var usdcAbi = parseAbi2([
|
|
7127
7414
|
"function approve(address spender,uint256 value) returns (bool)",
|
|
7128
7415
|
"function balanceOf(address account) view returns (uint256)"
|
|
7129
7416
|
]);
|
|
7130
7417
|
async function main9(env = process.env, argv = process.argv.slice(2)) {
|
|
7131
|
-
const { apiBaseUrl: apiBaseUrlOverride, argv:
|
|
7132
|
-
const
|
|
7133
|
-
|
|
7134
|
-
|
|
7418
|
+
const { apiBaseUrl: apiBaseUrlOverride, argv: afterApiBaseUrl } = extractApiBaseUrlFlag(argv);
|
|
7419
|
+
const { value: signoffPath, argv: remainingArgv } = takeFlag(
|
|
7420
|
+
afterApiBaseUrl,
|
|
7421
|
+
"--signoff",
|
|
7422
|
+
PUBLISH_FUND_USAGE
|
|
7135
7423
|
);
|
|
7136
|
-
|
|
7424
|
+
if (!signoffPath || remainingArgv.length !== 1 || !remainingArgv[0])
|
|
7425
|
+
throw new Error(PUBLISH_FUND_USAGE);
|
|
7426
|
+
const config = loadPosterConfig(env, apiBaseUrlOverride);
|
|
7427
|
+
const escrowTokenAddress = withDeploymentDefaults(env).ELGORA_ESCROW_TOKEN_ADDRESS?.trim();
|
|
7428
|
+
if (!escrowTokenAddress || !isHexAddress(escrowTokenAddress)) {
|
|
7429
|
+
throw new Error(
|
|
7430
|
+
"ELGORA_ESCROW_TOKEN_ADDRESS must be a 20-byte EVM address"
|
|
7431
|
+
);
|
|
7432
|
+
}
|
|
7433
|
+
const tokenAddress = escrowTokenAddress.toLowerCase();
|
|
7434
|
+
const bountyChallengeMarkdown = await readBountyChallengeMarkdown2(
|
|
7435
|
+
remainingArgv[0]
|
|
7436
|
+
);
|
|
7437
|
+
const localSpecCommitment = deriveBountyChallengeContentCommitment(
|
|
7438
|
+
bountyChallengeMarkdown
|
|
7439
|
+
);
|
|
7440
|
+
const signoff = readinessSignoffSchema.parse(
|
|
7441
|
+
JSON.parse(await readFile5(signoffPath, "utf8"))
|
|
7442
|
+
);
|
|
7443
|
+
if (signoff.spec_commitment.toLowerCase() !== localSpecCommitment) {
|
|
7444
|
+
throw new Error(
|
|
7445
|
+
"Readiness sign-off does not match the exact bounty_challenge.md bytes. Run poster:review again."
|
|
7446
|
+
);
|
|
7447
|
+
}
|
|
7448
|
+
if (signoff.expires_at <= Math.floor(Date.now() / 1e3)) {
|
|
7449
|
+
throw new Error("Readiness sign-off has expired. Run poster:review again.");
|
|
7450
|
+
}
|
|
7137
7451
|
const account = privateKeyToAccount8(config.posterPrivateKey);
|
|
7138
7452
|
const publicClient = createChainPublicClient(config.chainId, config.rpcUrl);
|
|
7139
7453
|
const walletClient = createChainWalletClient(
|
|
@@ -7147,31 +7461,25 @@ async function main9(env = process.env, argv = process.argv.slice(2)) {
|
|
|
7147
7461
|
config.apiBaseUrl
|
|
7148
7462
|
);
|
|
7149
7463
|
requestUrl.searchParams.set("poster_address", account.address);
|
|
7150
|
-
const
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
hubAddress: config.hubAddress,
|
|
7157
|
-
address: account.address,
|
|
7158
|
-
block,
|
|
7159
|
-
sign: (typedData) => account.signTypedData(typedData)
|
|
7464
|
+
const authorization = await signPosterRequest({
|
|
7465
|
+
requestUrl,
|
|
7466
|
+
markdown: bountyChallengeMarkdown,
|
|
7467
|
+
config,
|
|
7468
|
+
account,
|
|
7469
|
+
publicClient
|
|
7160
7470
|
});
|
|
7161
7471
|
const prepared = await requestPreparePublication({
|
|
7162
7472
|
requestUrl: requestUrl.toString(),
|
|
7163
7473
|
markdown: bountyChallengeMarkdown,
|
|
7164
|
-
authorization
|
|
7474
|
+
authorization,
|
|
7475
|
+
signoff: encodeReadinessSignoffHeader(signoff)
|
|
7165
7476
|
});
|
|
7166
|
-
const localSpecCommitment = deriveBountyChallengeContentCommitment(
|
|
7167
|
-
bountyChallengeMarkdown
|
|
7168
|
-
);
|
|
7169
7477
|
if (prepared.content_record.spec_commitment !== localSpecCommitment) {
|
|
7170
7478
|
throw new Error(
|
|
7171
7479
|
"Elgora API returned a spec_commitment that does not match the locally submitted bounty_challenge.md"
|
|
7172
7480
|
);
|
|
7173
7481
|
}
|
|
7174
|
-
if (prepared.create_bounty_transaction.chain_id !== config.chainId || prepared.create_bounty_transaction.to !== config.hubAddress || prepared.content_record.token_address !==
|
|
7482
|
+
if (prepared.create_bounty_transaction.chain_id !== config.chainId || prepared.create_bounty_transaction.to !== config.hubAddress || prepared.content_record.token_address !== tokenAddress) {
|
|
7175
7483
|
throw new Error(
|
|
7176
7484
|
"Elgora API returned a create_bounty_transaction that does not match the locally configured deployment (chain id, hub address, or escrow token)"
|
|
7177
7485
|
);
|
|
@@ -7193,7 +7501,7 @@ async function main9(env = process.env, argv = process.argv.slice(2)) {
|
|
|
7193
7501
|
const [ethBalance, usdcBalance] = await Promise.all([
|
|
7194
7502
|
publicClient.getBalance({ address: account.address }),
|
|
7195
7503
|
publicClient.readContract({
|
|
7196
|
-
address:
|
|
7504
|
+
address: tokenAddress,
|
|
7197
7505
|
abi: usdcAbi,
|
|
7198
7506
|
functionName: "balanceOf",
|
|
7199
7507
|
args: [account.address]
|
|
@@ -7209,7 +7517,7 @@ async function main9(env = process.env, argv = process.argv.slice(2)) {
|
|
|
7209
7517
|
}
|
|
7210
7518
|
const authorized = await prepareAuthorizedCreateBountyTransaction({
|
|
7211
7519
|
transaction: prepared.create_bounty_transaction,
|
|
7212
|
-
tokenAddress
|
|
7520
|
+
tokenAddress,
|
|
7213
7521
|
poster: account.address,
|
|
7214
7522
|
publicClient,
|
|
7215
7523
|
sign: (typedData) => account.signTypedData(typedData)
|
|
@@ -7230,7 +7538,7 @@ async function main9(env = process.env, argv = process.argv.slice(2)) {
|
|
|
7230
7538
|
});
|
|
7231
7539
|
} else {
|
|
7232
7540
|
const approveHash = await walletClient.writeContract({
|
|
7233
|
-
address:
|
|
7541
|
+
address: tokenAddress,
|
|
7234
7542
|
abi: usdcAbi,
|
|
7235
7543
|
functionName: "approve",
|
|
7236
7544
|
args: [config.hubAddress, escrowAmount]
|
|
@@ -7304,60 +7612,106 @@ async function requestPreparePublication(input) {
|
|
|
7304
7612
|
new URL(input.requestUrl),
|
|
7305
7613
|
input.markdown,
|
|
7306
7614
|
"text/markdown",
|
|
7307
|
-
input.authorization
|
|
7615
|
+
input.authorization,
|
|
7616
|
+
{ [ELGORA_READINESS_SIGNOFF_HEADER]: input.signoff }
|
|
7308
7617
|
);
|
|
7309
7618
|
return bountyPublicationResponseSchema.parse(body);
|
|
7310
7619
|
}
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
}
|
|
7316
|
-
if (path5 === "-") {
|
|
7317
|
-
return readStdinText3(process.stdin);
|
|
7318
|
-
}
|
|
7319
|
-
return readFile4(path5, "utf8");
|
|
7620
|
+
|
|
7621
|
+
// src/required-env.ts
|
|
7622
|
+
function missingRequiredEnv(required4, env) {
|
|
7623
|
+
return required4.filter((envName) => !env[envName]?.trim());
|
|
7320
7624
|
}
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7625
|
+
|
|
7626
|
+
// src/review.ts
|
|
7627
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
7628
|
+
import { resolve } from "path";
|
|
7629
|
+
import { privateKeyToAccount as privateKeyToAccount9 } from "viem/accounts";
|
|
7630
|
+
var USAGE2 = "Usage: elgora-cli poster:review <path|-> [--previous <commitment>] [--signoff-out <file>]";
|
|
7631
|
+
async function main10(env = process.env, argv = process.argv.slice(2)) {
|
|
7632
|
+
const { apiBaseUrl, argv: args } = extractApiBaseUrlFlag(argv);
|
|
7633
|
+
const { value: previous, argv: afterPrevious } = takeFlag(
|
|
7634
|
+
args,
|
|
7635
|
+
"--previous",
|
|
7636
|
+
"--previous must be one bytes32 page commitment"
|
|
7637
|
+
);
|
|
7638
|
+
if (previous && !isHexBytes32(previous))
|
|
7639
|
+
throw new Error("--previous must be one bytes32 page commitment");
|
|
7640
|
+
const { value: signoffOut, argv: paths } = takeFlag(
|
|
7641
|
+
afterPrevious,
|
|
7642
|
+
"--signoff-out",
|
|
7643
|
+
USAGE2
|
|
7644
|
+
);
|
|
7645
|
+
if (paths.length !== 1 || !paths[0] || paths[0].startsWith("--"))
|
|
7646
|
+
throw new Error(USAGE2);
|
|
7647
|
+
if (signoffOut && paths[0] !== "-" && resolve(signoffOut) === resolve(paths[0]))
|
|
7648
|
+
throw new Error("--signoff-out must not overwrite the bounty draft");
|
|
7649
|
+
const config = loadPosterConfig(env, apiBaseUrl);
|
|
7650
|
+
const markdown = await readBountyChallengeMarkdown2(paths[0]);
|
|
7651
|
+
const commitment = deriveBountyChallengeContentCommitment(markdown);
|
|
7652
|
+
const account = privateKeyToAccount9(config.posterPrivateKey);
|
|
7653
|
+
const publicClient = createChainPublicClient(config.chainId, config.rpcUrl);
|
|
7654
|
+
await assertHubDeployment(publicClient, config.chainId, config.hubAddress);
|
|
7655
|
+
const requestUrl = new URL(
|
|
7656
|
+
"/api/bounty-challenge-specs/review",
|
|
7657
|
+
config.apiBaseUrl
|
|
7658
|
+
);
|
|
7659
|
+
requestUrl.searchParams.set("poster_address", account.address);
|
|
7660
|
+
if (previous)
|
|
7661
|
+
requestUrl.searchParams.set("previous_spec_commitment", previous);
|
|
7662
|
+
const authorization = await signPosterRequest({
|
|
7663
|
+
requestUrl,
|
|
7664
|
+
markdown,
|
|
7665
|
+
config,
|
|
7666
|
+
account,
|
|
7667
|
+
publicClient
|
|
7668
|
+
});
|
|
7669
|
+
let response;
|
|
7670
|
+
try {
|
|
7671
|
+
response = bountyReviewResponseSchema.parse(
|
|
7672
|
+
await postText(requestUrl, markdown, "text/markdown", authorization)
|
|
7331
7673
|
);
|
|
7674
|
+
} catch (error) {
|
|
7675
|
+
if (error instanceof ApiRequestError && error.code === "bounty_challenge_not_ready") {
|
|
7676
|
+
console.log(
|
|
7677
|
+
JSON.stringify({ event: "review.not_ready", issues: error.issues })
|
|
7678
|
+
);
|
|
7679
|
+
process.exitCode = 2;
|
|
7680
|
+
return;
|
|
7681
|
+
}
|
|
7682
|
+
throw error;
|
|
7332
7683
|
}
|
|
7333
|
-
|
|
7334
|
-
if (!escrowTokenAddress || !isHexAddress(escrowTokenAddress)) {
|
|
7684
|
+
if (response.content_record.spec_commitment !== commitment) {
|
|
7335
7685
|
throw new Error(
|
|
7336
|
-
"
|
|
7686
|
+
"Elgora API returned a review for different bounty_challenge.md bytes"
|
|
7337
7687
|
);
|
|
7338
7688
|
}
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7689
|
+
if (signoffOut)
|
|
7690
|
+
await writeFile3(
|
|
7691
|
+
signoffOut,
|
|
7692
|
+
`${JSON.stringify(response.readiness.signoff, null, 2)}
|
|
7693
|
+
`
|
|
7694
|
+
);
|
|
7695
|
+
console.log(
|
|
7696
|
+
JSON.stringify({
|
|
7697
|
+
event: "review.ready",
|
|
7698
|
+
spec_commitment: commitment,
|
|
7699
|
+
signoff: response.readiness.signoff,
|
|
7700
|
+
prior_issues_resolved: response.readiness.prior_issues_resolved,
|
|
7701
|
+
prior_issues_open: response.readiness.prior_issues_open
|
|
7702
|
+
})
|
|
7703
|
+
);
|
|
7350
7704
|
}
|
|
7351
7705
|
|
|
7352
7706
|
// src/roster-add-guardian.ts
|
|
7353
7707
|
import { isAddress as isAddress4 } from "viem";
|
|
7354
|
-
import { privateKeyToAccount as
|
|
7708
|
+
import { privateKeyToAccount as privateKeyToAccount10 } from "viem/accounts";
|
|
7355
7709
|
var ROSTER_ADMIN_ADDRESS_FLAG = "--roster-admin-address";
|
|
7356
7710
|
var ENCRYPTION_PUBLIC_KEY_RE = /^[A-Za-z0-9_-]{43}$/;
|
|
7357
|
-
async function
|
|
7711
|
+
async function main11(env = process.env, argv = process.argv.slice(2)) {
|
|
7358
7712
|
const args = parseRosterAddGuardianArgs(argv);
|
|
7359
7713
|
const config = loadRosterAddGuardianConfig(env, args.rosterAdminAddress);
|
|
7360
|
-
const account = config.rosterAdminPrivateKey ?
|
|
7714
|
+
const account = config.rosterAdminPrivateKey ? privateKeyToAccount10(config.rosterAdminPrivateKey) : null;
|
|
7361
7715
|
const rosterAdmin = account?.address ?? args.rosterAdminAddress;
|
|
7362
7716
|
if (!rosterAdmin) throw new Error("Roster admin address is required");
|
|
7363
7717
|
const normalizedRosterAdmin = rosterAdmin.toLowerCase();
|
|
@@ -7525,7 +7879,7 @@ function usageError2() {
|
|
|
7525
7879
|
// src/roster-prepare-guardian-proof.ts
|
|
7526
7880
|
import { isAddress as isAddress5 } from "viem";
|
|
7527
7881
|
var ENCRYPTION_PUBLIC_KEY_RE2 = /^[A-Za-z0-9_-]{43}$/;
|
|
7528
|
-
async function
|
|
7882
|
+
async function main12(env = process.env, argv = process.argv.slice(2)) {
|
|
7529
7883
|
const args = parseArgs(argv);
|
|
7530
7884
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
7531
7885
|
const { chainId, hubAddress } = loadPublicDeploymentConfig(envWithDefaults);
|
|
@@ -7572,17 +7926,17 @@ function usageError3() {
|
|
|
7572
7926
|
}
|
|
7573
7927
|
|
|
7574
7928
|
// src/roster-verify-guardian-proof.ts
|
|
7575
|
-
import { readFile as
|
|
7576
|
-
import { isAddress as isAddress6, isHex as
|
|
7577
|
-
async function
|
|
7929
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
7930
|
+
import { isAddress as isAddress6, isHex as isHex6, recoverMessageAddress } from "viem";
|
|
7931
|
+
async function main13(_env = process.env, argv = process.argv.slice(2)) {
|
|
7578
7932
|
const [preparedPath, responsePath] = argv;
|
|
7579
7933
|
if (!preparedPath || !responsePath) {
|
|
7580
7934
|
throw new Error(
|
|
7581
7935
|
"Usage: elgora-cli roster:verify-guardian-proof <prepared_json_path> <response_json_path>"
|
|
7582
7936
|
);
|
|
7583
7937
|
}
|
|
7584
|
-
const prepared = parsePrepared(await
|
|
7585
|
-
const response = parseResponse(await
|
|
7938
|
+
const prepared = parsePrepared(await readFile6(preparedPath, "utf8"));
|
|
7939
|
+
const response = parseResponse(await readFile6(responsePath, "utf8"));
|
|
7586
7940
|
const fixture = base64UrlToBytes(prepared.fixture);
|
|
7587
7941
|
const signedPayload = buildGuardianRosterKeyProofSignedPayload(
|
|
7588
7942
|
prepared.message,
|
|
@@ -7646,7 +8000,7 @@ function parseResponse(text) {
|
|
|
7646
8000
|
}
|
|
7647
8001
|
const guardianAccount = get(parsed, "guardian_account");
|
|
7648
8002
|
const signature = get(parsed, "signature");
|
|
7649
|
-
if (typeof guardianAccount !== "string" || !isAddress6(guardianAccount) || typeof signature !== "string" || !
|
|
8003
|
+
if (typeof guardianAccount !== "string" || !isAddress6(guardianAccount) || typeof signature !== "string" || !isHex6(signature) || signature.length !== 132) {
|
|
7650
8004
|
throw new Error(
|
|
7651
8005
|
"Guardian response JSON must be the unmodified stdout of guardian:prove-roster-key"
|
|
7652
8006
|
);
|
|
@@ -7664,10 +8018,10 @@ function get(value, key) {
|
|
|
7664
8018
|
}
|
|
7665
8019
|
|
|
7666
8020
|
// src/spec-commitment.ts
|
|
7667
|
-
import { readFile as
|
|
8021
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
7668
8022
|
import { text as readStdinText4 } from "stream/consumers";
|
|
7669
8023
|
var SPEC_COMMITMENT_USAGE = "Usage: elgora-cli spec-commitment <bounty_challenge_md_path>\n or: elgora-cli spec-commitment -";
|
|
7670
|
-
async function
|
|
8024
|
+
async function main14(_env = process.env, argv = process.argv.slice(2)) {
|
|
7671
8025
|
const markdown = await readBountyChallengeMarkdown3(argv);
|
|
7672
8026
|
const specCommitment = deriveBountyChallengeContentCommitment(markdown);
|
|
7673
8027
|
console.log(
|
|
@@ -7686,23 +8040,23 @@ async function readBountyChallengeMarkdown3(argv) {
|
|
|
7686
8040
|
if (path5 === "-") {
|
|
7687
8041
|
return readStdinText4(process.stdin);
|
|
7688
8042
|
}
|
|
7689
|
-
return
|
|
8043
|
+
return readFile7(path5, "utf8");
|
|
7690
8044
|
}
|
|
7691
8045
|
|
|
7692
8046
|
// src/submit.ts
|
|
7693
|
-
import { readdir, readFile as
|
|
8047
|
+
import { readdir, readFile as readFile8, stat } from "fs/promises";
|
|
7694
8048
|
import path3 from "path";
|
|
7695
8049
|
import { createInterface } from "readline";
|
|
7696
8050
|
import { isAddress as isAddress7 } from "viem";
|
|
7697
|
-
import { privateKeyToAccount as
|
|
8051
|
+
import { privateKeyToAccount as privateKeyToAccount11 } from "viem/accounts";
|
|
7698
8052
|
|
|
7699
8053
|
// src/external-api-approval-signer.ts
|
|
7700
|
-
import { isHex as
|
|
8054
|
+
import { isHex as isHex7 } from "viem";
|
|
7701
8055
|
function createExternalApiApprovalSigner(input) {
|
|
7702
8056
|
return async (typedData) => {
|
|
7703
8057
|
input.writePrompt(input.renderPrompt(typedData));
|
|
7704
8058
|
const signature = (await input.readSignature())?.trim();
|
|
7705
|
-
if (!signature || signature === "0x" || !
|
|
8059
|
+
if (!signature || signature === "0x" || !isHex7(signature)) {
|
|
7706
8060
|
throw new Error(
|
|
7707
8061
|
"External signer must return one non-empty 0x-prefixed hex signature line on stdin"
|
|
7708
8062
|
);
|
|
@@ -7744,13 +8098,13 @@ var MEDIA_TYPE_BY_EXTENSION = {
|
|
|
7744
8098
|
diff: "text/x-diff"
|
|
7745
8099
|
};
|
|
7746
8100
|
var DEFAULT_MEDIA_TYPE = "application/octet-stream";
|
|
7747
|
-
async function
|
|
8101
|
+
async function main15(env = process.env, argv = process.argv.slice(2)) {
|
|
7748
8102
|
const { solverAddress, argv: argvWithoutSolverAddress } = extractSolverAddressFlag(argv);
|
|
7749
8103
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argvWithoutSolverAddress);
|
|
7750
8104
|
const config = loadSubmitConfig(env, apiBaseUrlOverride, solverAddress);
|
|
7751
8105
|
const args = readSubmitArgs(remainingArgv);
|
|
7752
8106
|
const bountyIdText = args.bountyId.toString();
|
|
7753
|
-
const account = config.solverPrivateKey ?
|
|
8107
|
+
const account = config.solverPrivateKey ? privateKeyToAccount11(config.solverPrivateKey) : null;
|
|
7754
8108
|
const solverAddressFromConfig = account?.address ?? config.solverAddress;
|
|
7755
8109
|
if (!solverAddressFromConfig) throw new Error("Solver address is required");
|
|
7756
8110
|
const solver = solverAddressFromConfig.toLowerCase();
|
|
@@ -8142,7 +8496,7 @@ async function readArtifactDirectory(artifactDir) {
|
|
|
8142
8496
|
}
|
|
8143
8497
|
return Promise.all(
|
|
8144
8498
|
entries.map(async (entry) => {
|
|
8145
|
-
const bytes = await
|
|
8499
|
+
const bytes = await readFile8(path3.join(artifactDir, entry.name));
|
|
8146
8500
|
return {
|
|
8147
8501
|
name: entry.name,
|
|
8148
8502
|
media_type: guessMediaType(entry.name),
|
|
@@ -8211,7 +8565,7 @@ async function requestPrepareSubmission(input) {
|
|
|
8211
8565
|
})
|
|
8212
8566
|
);
|
|
8213
8567
|
}
|
|
8214
|
-
await new Promise((
|
|
8568
|
+
await new Promise((resolve2) => setTimeout(resolve2, retryDelay));
|
|
8215
8569
|
}
|
|
8216
8570
|
}
|
|
8217
8571
|
}
|
|
@@ -8255,7 +8609,7 @@ function readTimelockRevealDelaySeconds(env) {
|
|
|
8255
8609
|
|
|
8256
8610
|
// src/verification-record.ts
|
|
8257
8611
|
var VERIFICATION_RECORD_USAGE = "Usage: elgora-cli verification-record <bounty_id>";
|
|
8258
|
-
async function
|
|
8612
|
+
async function main16(env = process.env, argv = process.argv.slice(2)) {
|
|
8259
8613
|
const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
|
|
8260
8614
|
const args = readVerificationRecordArgs(remainingArgv);
|
|
8261
8615
|
const envWithDefaults = withDeploymentDefaults(env);
|
|
@@ -8340,15 +8694,16 @@ var COMMANDS = {
|
|
|
8340
8694
|
"guardian:verdict": main7,
|
|
8341
8695
|
"guardian:deliver-key": main4,
|
|
8342
8696
|
"poster:open-winning-submission": main8,
|
|
8697
|
+
"poster:review": main10,
|
|
8343
8698
|
"poster:publish-fund": main9,
|
|
8344
|
-
"roster:add-guardian":
|
|
8345
|
-
"roster:prepare-guardian-proof":
|
|
8346
|
-
"roster:verify-guardian-proof":
|
|
8347
|
-
"solver:submit":
|
|
8699
|
+
"roster:add-guardian": main11,
|
|
8700
|
+
"roster:prepare-guardian-proof": main12,
|
|
8701
|
+
"roster:verify-guardian-proof": main13,
|
|
8702
|
+
"solver:submit": main15,
|
|
8348
8703
|
claim: main2,
|
|
8349
8704
|
"check-page": main,
|
|
8350
|
-
"spec-commitment":
|
|
8351
|
-
"verification-record":
|
|
8705
|
+
"spec-commitment": main14,
|
|
8706
|
+
"verification-record": main16
|
|
8352
8707
|
};
|
|
8353
8708
|
var COMMAND_HELP = {
|
|
8354
8709
|
"guardian:judgeable": {
|
|
@@ -8404,7 +8759,7 @@ var COMMAND_HELP = {
|
|
|
8404
8759
|
"guardian:deliver-key": {
|
|
8405
8760
|
usage: "elgora-cli guardian:deliver-key <bounty_id> <solver> <submission_commitment>",
|
|
8406
8761
|
title: "Deliver the finalized winner's content key for Poster retrieval.",
|
|
8407
|
-
description: "Required after a bounty finalizes `awarded`: wrap the winning\n Submission's key so the funding Poster can retrieve the work. Read the\n winning Solver and Submission from ElgoraHub's finalized state (the\n subgraph), not from a local Verdict, and pass exactly that pair. The\n API rechecks against ElgoraHub and accepts only the current active or\n finalized winner; written Verdicts and local state authorize nothing.\n Idempotent \u2014 safe to re-run and safe if another Guardian already did it.",
|
|
8762
|
+
description: "Required after a bounty finalizes `awarded`: wrap the winning\n Submission's key so the funding Poster can retrieve the work. Read the\n winning Solver and Submission from ElgoraHub's finalized state (the\n subgraph), not from a local Verdict, and pass exactly that pair. The\n API rechecks against ElgoraHub and accepts only the current active or\n finalized winner; written Verdicts and local state authorize nothing.\n Idempotent \u2014 safe to re-run and safe if another Guardian already did it.\n Until the settlement block is behind the chain's finalized tag (about\n 10-20 minutes after settlement) it delivers nothing, prints\n guardian_deliver_key.pending_finality, and exits 75; retry later.",
|
|
8408
8763
|
required: [
|
|
8409
8764
|
"ELGORA_GUARDIAN_ACCOUNT_PRIVATE_KEY",
|
|
8410
8765
|
"ELGORA_GUARDIAN_PRIVATE_KEYS_JSON"
|
|
@@ -8417,10 +8772,23 @@ var COMMAND_HELP = {
|
|
|
8417
8772
|
],
|
|
8418
8773
|
apiBaseUrlFlag: true
|
|
8419
8774
|
},
|
|
8775
|
+
"poster:review": {
|
|
8776
|
+
usage: "elgora-cli poster:review <path|-> [--previous <commitment>] [--signoff-out <file>]",
|
|
8777
|
+
title: "Review a bounty page and save its readiness certificate.",
|
|
8778
|
+
description: "Send the exact Markdown for a signed readiness review. Ready prints\n review.ready; --signoff-out saves the certificate for publication.\n Not-ready prints all issues and exits 2; other failures exit 1.\n --previous sends the previous page commitment with the signed request.",
|
|
8779
|
+
required: ["ELGORA_POSTER_PRIVATE_KEY"],
|
|
8780
|
+
defaulted: [
|
|
8781
|
+
"ELGORA_CHAIN_ID",
|
|
8782
|
+
"ELGORA_HUB_ADDRESS",
|
|
8783
|
+
"ELGORA_RPC_URL",
|
|
8784
|
+
"ELGORA_API_BASE_URL"
|
|
8785
|
+
],
|
|
8786
|
+
apiBaseUrlFlag: true
|
|
8787
|
+
},
|
|
8420
8788
|
"poster:publish-fund": {
|
|
8421
|
-
usage: "elgora-cli poster:publish-fund <bounty_challenge_md_path|->",
|
|
8789
|
+
usage: "elgora-cli poster:publish-fund <bounty_challenge_md_path|-> --signoff <file>",
|
|
8422
8790
|
title: "Publish an approved challenge and fund its bounty.",
|
|
8423
|
-
description: "Read the exact approved bounty_challenge.md bytes and
|
|
8791
|
+
description: "Read the exact approved bounty_challenge.md bytes and its saved certificate.\n Publishing checks the certificate and never runs another AI review.\n 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.",
|
|
8424
8792
|
required: ["ELGORA_POSTER_PRIVATE_KEY"],
|
|
8425
8793
|
defaulted: [
|
|
8426
8794
|
"ELGORA_CHAIN_ID",
|
|
@@ -8546,6 +8914,11 @@ async function run(rawArgv) {
|
|
|
8546
8914
|
process.exitCode = 1;
|
|
8547
8915
|
return;
|
|
8548
8916
|
}
|
|
8917
|
+
const networkNotice = COMMAND_HELP[subcommand]?.defaulted?.includes(
|
|
8918
|
+
"ELGORA_CHAIN_ID"
|
|
8919
|
+
) ? defaultNetworkNotice(env) : void 0;
|
|
8920
|
+
if (networkNotice) process.stderr.write(`${networkNotice}
|
|
8921
|
+
`);
|
|
8549
8922
|
const missing = missingRequiredEnv(
|
|
8550
8923
|
COMMAND_HELP[subcommand]?.required ?? [],
|
|
8551
8924
|
env
|
|
@@ -8580,6 +8953,7 @@ function printUsage() {
|
|
|
8580
8953
|
"",
|
|
8581
8954
|
`Every command accepts ${NETWORK_FLAG} <name|chain id> to select the`,
|
|
8582
8955
|
`deployment for that invocation. Supported: ${NETWORK_NAMES_HINT}.`,
|
|
8956
|
+
...DEFAULT_NETWORK_HELP_LINES,
|
|
8583
8957
|
"",
|
|
8584
8958
|
"Run `elgora-cli help <command>` for exact arguments and configuration.",
|
|
8585
8959
|
"Run `elgora-cli --version` to print this release's version."
|
|
@@ -8615,7 +8989,8 @@ function printCommandHelpOrUsage(name) {
|
|
|
8615
8989
|
"",
|
|
8616
8990
|
`Accepts ${NETWORK_FLAG} <name|chain id>; it sets ELGORA_CHAIN_ID for`,
|
|
8617
8991
|
"this invocation, winning over the environment.",
|
|
8618
|
-
`Supported: ${NETWORK_NAMES_HINT}
|
|
8992
|
+
`Supported: ${NETWORK_NAMES_HINT}.`,
|
|
8993
|
+
...DEFAULT_NETWORK_HELP_LINES
|
|
8619
8994
|
);
|
|
8620
8995
|
}
|
|
8621
8996
|
if (help.apiBaseUrlFlag) {
|