@zkproofport-app/sdk 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -79,16 +79,6 @@ const CIRCUIT_METADATA = {
79
79
  publicInputsCount: 148,
80
80
  publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier', 'provider'],
81
81
  },
82
- arc_eligibility: {
83
- name: 'Arc Eligibility',
84
- description: 'Prove eligibility and authorize one EIP-712 action, for an Arc gate',
85
- // 6 x `pub [u8; 32]` in arc-eligibility/src/main.nr, flattened to bytes.
86
- // Read off the compiled ABI rather than counted: signal_hash 0-31,
87
- // domain_separator 32-63, action_hash 64-95, signer_list_merkle_root
88
- // 96-127, scope 128-159, nullifier 160-191.
89
- publicInputsCount: 192,
90
- publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'signer_list_merkle_root'],
91
- },
92
82
  // Planned circuit — the identifier is reserved and the circuit exists, but it
93
83
  // is not officially supported yet. See CIRCUIT_SUPPORT_STATUS in ./circuits.
94
84
  // Forked from coinbase_attestation, then given the EIP-712 pair and a
@@ -96,12 +86,12 @@ const CIRCUIT_METADATA = {
96
86
  // zero; without one it signs signal_hash and the pair is zero. Read off the
97
87
  // compiled ABI (6 x [u8; 32] = 192): signal_hash 0-31, domain_separator
98
88
  // 32-63, action_hash 64-95, signer_list_merkle_root 96-127, scope 128-159,
99
- // nullifier 160-191 -- the same order as arc_eligibility.
89
+ // nullifier 160-191.
100
90
  //
101
- // Unlike Arc, signal_hash is NOT part of this circuit's nullifier. That one
102
- // derives its nullifier from a public input nobody signs and nothing
103
- // constrains, so one wallet can mint as many nullifiers as it likes; here
104
- // the secret is the address plus a constant compiled into the circuit.
91
+ // signal_hash is NOT part of this circuit's nullifier: a nullifier derived
92
+ // from a public input nobody signs and nothing constrains would let one
93
+ // wallet mint as many nullifiers as it likes; here the secret is the address
94
+ // plus a constant compiled into the circuit.
105
95
  giwa_attestation: {
106
96
  name: 'GIWA Attestation',
107
97
  description: 'Prove GIWA identity verification, optionally authorizing one EIP-712 action (planned — not officially supported yet)',
@@ -173,11 +163,6 @@ const RPC_ENDPOINTS = {
173
163
  84532: 'https://sepolia.base.org', // Base Sepolia
174
164
  8453: 'https://mainnet.base.org', // Base Mainnet
175
165
  91342: 'https://sepolia-rpc.giwa.io', // GIWA Sepolia
176
- // Arc Testnet. The arc_eligibility verifier is deployed here and nowhere
177
- // else, and this line was missing on 2026-09-12 — the circuit shipped, a
178
- // phone produced a valid proof, and on-chain verification answered "No RPC
179
- // endpoint configured for chain 5042002", which reads like the proof's fault.
180
- 5042002: 'https://rpc.testnet.arc.io', // Arc Testnet
181
166
  };
182
167
  /**
183
168
  * Default proof request expiration time in milliseconds.
@@ -385,8 +370,6 @@ const CIRCUIT_IDS = Object.freeze({
385
370
  COINBASE_COUNTRY_ATTESTATION: 'coinbase_country_attestation',
386
371
  /** OIDC email-domain attestation. Officially supported. */
387
372
  OIDC_DOMAIN_ATTESTATION: 'oidc_domain_attestation',
388
- /** Arc eligibility: exact EIP-712 action authorization. Experimental on Arc Testnet. */
389
- ARC_ELIGIBILITY: 'arc_eligibility',
390
373
  /** GIWA attestation. Planned — not officially supported yet. */
391
374
  GIWA_ATTESTATION: 'giwa_attestation',
392
375
  /** Korea Mobile ID ownership. Planned — not officially supported yet. */
@@ -415,7 +398,6 @@ const CIRCUIT_SUPPORT_STATUS = Object.freeze({
415
398
  coinbase_attestation: 'supported',
416
399
  coinbase_country_attestation: 'supported',
417
400
  oidc_domain_attestation: 'supported',
418
- arc_eligibility: 'experimental',
419
401
  // Experimental, not planned: the circuit is built, its verifier is
420
402
  // deployed on GIWA Sepolia and the demo requests it. What keeps it out of
421
403
  // 'supported' is that it is testnet-only, so the app offers it behind
@@ -449,7 +431,6 @@ const CIRCUIT_VK_PATHS = Object.freeze({
449
431
  coinbase_attestation: 'coinbase-attestation/target/vk/vk',
450
432
  coinbase_country_attestation: 'coinbase-country-attestation/target/vk/vk',
451
433
  oidc_domain_attestation: 'oidc-domain-attestation/target/vk/vk',
452
- arc_eligibility: 'arc-eligibility/target/vk/vk',
453
434
  giwa_attestation: 'giwa-attestation/target/vk/vk',
454
435
  mdl_kr_ownership: 'mdl/kr-ownership/target/vk/vk',
455
436
  mdl_kr_age: 'mdl/kr-age/target/vk/vk',
@@ -478,12 +459,11 @@ const SUPPORTED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCU
478
459
  /**
479
460
  * Circuits you can prove today, on a testnet, with everything subject to change.
480
461
  *
481
- * `arc_eligibility` is the reason this status exists. Calling it `planned`
482
- * would be wrong — the circuit compiles, the verifier is deployed and a proof
483
- * verifies against it — and calling it `supported` would be worse, because the
484
- * only deployment is Arc Testnet, Circle has not published a mainnet chain id,
485
- * and the public-input layout is still open. Neither existing word was true, so
486
- * the honest move was a third word rather than picking the closer lie.
462
+ * `giwa_attestation` is in this state. Calling it `planned` would be wrong —
463
+ * the circuit compiles, the verifier is deployed and a proof verifies against
464
+ * it — and calling it `supported` would be worse, because the only deployment
465
+ * is a testnet (GIWA Sepolia). Neither existing word was true, so the honest
466
+ * move was a third word rather than picking the closer lie.
487
467
  */
488
468
  const EXPERIMENTAL_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter(id => CIRCUIT_SUPPORT_STATUS[id] === 'experimental'));
489
469
  const PLANNED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'planned'));
@@ -537,12 +517,8 @@ const CIRCUIT_ACTION_BINDING = {
537
517
  coinbase_country_attestation: 'none',
538
518
  // The signature lives inside the identity token; no wallet, no action.
539
519
  oidc_domain_attestation: 'none',
540
- // Both branch in-circuit: with an action the wallet signs the typed data,
541
- // without one it personal_signs the request's signal hash. Arc read
542
- // 'required' until 2026-09-22 because its circuit had no second path -- a
543
- // request without an action built a vector 64 bytes short and died inside
544
- // the prover. The circuit gained the branch; the table follows it.
545
- arc_eligibility: 'optional',
520
+ // Branches in-circuit: with an action the wallet signs the typed data,
521
+ // without one it personal_signs the request's signal hash.
546
522
  giwa_attestation: 'optional',
547
523
  mdl_kr_ownership: 'none',
548
524
  mdl_kr_age: 'none',
@@ -573,16 +549,13 @@ function circuitActionBinding(circuit) {
573
549
  const CIRCUIT_NEEDS_WALLET_SIGNATURE = {
574
550
  coinbase_attestation: true,
575
551
  coinbase_country_attestation: true,
576
- // Same Coinbase-attested wallet as above; what changes is that it signs an
577
- // EIP-712 action rather than signal_hash.
578
- arc_eligibility: true,
579
552
  // The signature is inside the OIDC identity token, checked by the prover.
580
553
  oidc_domain_attestation: false,
581
554
  // Web2 (OmniOne CX) flows with no wallet binding at all.
582
555
  mdl_kr_ownership: false,
583
556
  mdl_kr_age: false,
584
557
  mdl_kr_region: false,
585
- // A GIWA-attested wallet signing an EIP-712 action, the same shape as Arc.
558
+ // A GIWA-attested wallet signing an EIP-712 action (or its signal hash).
586
559
  // This read `false` while the circuit had always verified a wallet signature
587
560
  // -- it sat under the Web2 comment above and was never revisited. A request
588
561
  // for it would have reached the relay with no signer.
@@ -604,7 +577,7 @@ const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
604
577
  */
605
578
  function validateTypedAction(value) {
606
579
  if (value === undefined || value === null) {
607
- return 'action is required for arc_eligibility. The circuit proves that a wallet authorised ONE EIP-712 action; there is nothing to prove without it.';
580
+ return 'action is required. The circuit proves that a wallet authorised ONE EIP-712 action; there is nothing to prove without it.';
608
581
  }
609
582
  if (typeof value !== 'object' || Array.isArray(value)) {
610
583
  return 'action must be an object.';
@@ -1160,8 +1133,7 @@ function validateProofRequest(request) {
1160
1133
  return { valid: false, error: 'Missing circuit type' };
1161
1134
  }
1162
1135
  // The list is the SDK's own, not a copy. The copy that used to sit here
1163
- // named six circuits and had missed two — `giwa_attestation` and
1164
- // `arc_eligibility` — so a link for either was refused as an invalid type
1136
+ // had missed circuits, so a link for one was refused as an invalid type
1165
1137
  // while every other layer accepted it.
1166
1138
  if (!isCircuitId(request.circuit)) {
1167
1139
  return {
@@ -1216,15 +1188,8 @@ function validateProofRequest(request) {
1216
1188
  return { valid: false, error: mdlError };
1217
1189
  }
1218
1190
  }
1219
- else if (request.circuit === 'arc_eligibility') {
1220
- const inputs = request.inputs;
1221
- if (inputs.userAddress && !/^0x[a-fA-F0-9]{40}$/.test(inputs.userAddress)) {
1222
- return { valid: false, error: 'Invalid userAddress format' };
1223
- }
1224
- }
1225
1191
  // The action, decided by the circuit's own entry rather than by naming a
1226
- // circuit here. This used to be part of the arc_eligibility branch above,
1227
- // which is why a second action-capable circuit had nowhere to be checked.
1192
+ // circuit here, so every action-capable circuit is checked the same way.
1228
1193
  {
1229
1194
  const action = request.inputs?.action;
1230
1195
  const binding = circuitActionBinding(request.circuit);
@@ -4383,7 +4348,7 @@ function getVerifierContract(providerOrSigner, verifier) {
4383
4348
  function getDefaultProvider(chainId) {
4384
4349
  const rpcUrl = RPC_ENDPOINTS[chainId];
4385
4350
  if (!rpcUrl) {
4386
- throw new Error(`No RPC endpoint configured for chain ${chainId}`);
4351
+ throw new Error(`No RPC endpoint configured for chain ${chainId}. Known chains: ${Object.keys(RPC_ENDPOINTS).join(', ')}`);
4387
4352
  }
4388
4353
  return createJsonRpcProvider(rpcUrl);
4389
4354
  }
@@ -4532,7 +4497,7 @@ function getVerifierChainId(circuit, customVerifier) {
4532
4497
  *
4533
4498
  * It replaced an `if / else if / else` chain whose final `else` handed every
4534
4499
  * unrecognised circuit — and every call that named no circuit at all —
4535
- * Coinbase's offsets. For `arc_eligibility` that is not a near miss: its
4500
+ * Coinbase's offsets. For `giwa_attestation` that is not a near miss: its
4536
4501
  * public inputs are signal_hash, domain_separator, action_hash, merkle root,
4537
4502
  * scope, nullifier, so the nullifier slot under Coinbase's offsets holds the
4538
4503
  * MERKLE ROOT, a value identical for every user. Duplicate detection built on
@@ -4542,15 +4507,13 @@ const PUBLIC_INPUT_OFFSETS = Object.freeze({
4542
4507
  // signal_hash, signer_list_merkle_root, scope, nullifier
4543
4508
  coinbase_attestation: { scope: [64, 95], nullifier: [96, 127] },
4544
4509
  // No longer Coinbase's layout. The GIWA circuit gained the EIP-712 pair, so
4545
- // it reads like Arc: signal_hash, domain_separator, action_hash, merkle
4546
- // root, then scope and nullifier 64 bytes further along than Coinbase.
4510
+ // it reads: signal_hash, domain_separator, action_hash, merkle root, then
4511
+ // scope and nullifier 64 bytes further along than Coinbase.
4547
4512
  giwa_attestation: { scope: [128, 159], nullifier: [160, 191] },
4548
4513
  // country_list, country_list_length and is_included sit before scope.
4549
4514
  coinbase_country_attestation: { scope: [86, 117], nullifier: [118, 149] },
4550
4515
  // pubkey_modulus_limbs and the bounded domain sit before scope.
4551
4516
  oidc_domain_attestation: { scope: [83, 114], nullifier: [115, 146] },
4552
- // signal_hash, domain_separator, action_hash, merkle root, then the pair.
4553
- arc_eligibility: { scope: [128, 159], nullifier: [160, 191] },
4554
4517
  // The Korea mDL circuits open with the pair; signal_hash is commented out.
4555
4518
  mdl_kr_ownership: { scope: [0, 31], nullifier: [32, 63] },
4556
4519
  mdl_kr_age: { scope: [0, 31], nullifier: [32, 63] },
@@ -5931,9 +5894,9 @@ class ProofportSDK {
5931
5894
  * ```
5932
5895
  */
5933
5896
  // Which circuits need a wallet signature, from the one record in
5934
- // src/circuits.ts. The typed-out pair that used to sit here had missed
5935
- // arc_eligibility, so a dapp calling createRelayRequest() for it with no
5936
- // signer got no local error and a relay 401 two round trips later.
5897
+ // src/circuits.ts. A typed-out list here once missed a circuit, so a dapp
5898
+ // calling createRelayRequest() for it with no signer got no local error and
5899
+ // a relay 401 two round trips later.
5937
5900
  static needsWalletSignature(circuit) {
5938
5901
  return CIRCUIT_NEEDS_WALLET_SIGNATURE[circuit] === true;
5939
5902
  }
@@ -5958,9 +5921,7 @@ class ProofportSDK {
5958
5921
  // from inside the mobile app -- a screen the dapp developer never sees,
5959
5922
  // reached after the person has already scanned a QR code.
5960
5923
  //
5961
- // This named arc_eligibility directly until giwa_attestation became the
5962
- // second circuit that can carry an action; the id belongs in the table,
5963
- // not in the check.
5924
+ // The circuit id belongs in the table, not in the check.
5964
5925
  {
5965
5926
  const action = inputs.action;
5966
5927
  const binding = circuitActionBinding(circuit);