@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.js CHANGED
@@ -84,16 +84,6 @@ const CIRCUIT_METADATA = {
84
84
  publicInputsCount: 148,
85
85
  publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier', 'provider'],
86
86
  },
87
- arc_eligibility: {
88
- name: 'Arc Eligibility',
89
- description: 'Prove eligibility and authorize one EIP-712 action, for an Arc gate',
90
- // 6 x `pub [u8; 32]` in arc-eligibility/src/main.nr, flattened to bytes.
91
- // Read off the compiled ABI rather than counted: signal_hash 0-31,
92
- // domain_separator 32-63, action_hash 64-95, signer_list_merkle_root
93
- // 96-127, scope 128-159, nullifier 160-191.
94
- publicInputsCount: 192,
95
- publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'signer_list_merkle_root'],
96
- },
97
87
  // Planned circuit — the identifier is reserved and the circuit exists, but it
98
88
  // is not officially supported yet. See CIRCUIT_SUPPORT_STATUS in ./circuits.
99
89
  // Forked from coinbase_attestation, then given the EIP-712 pair and a
@@ -101,12 +91,12 @@ const CIRCUIT_METADATA = {
101
91
  // zero; without one it signs signal_hash and the pair is zero. Read off the
102
92
  // compiled ABI (6 x [u8; 32] = 192): signal_hash 0-31, domain_separator
103
93
  // 32-63, action_hash 64-95, signer_list_merkle_root 96-127, scope 128-159,
104
- // nullifier 160-191 -- the same order as arc_eligibility.
94
+ // nullifier 160-191.
105
95
  //
106
- // Unlike Arc, signal_hash is NOT part of this circuit's nullifier. That one
107
- // derives its nullifier from a public input nobody signs and nothing
108
- // constrains, so one wallet can mint as many nullifiers as it likes; here
109
- // the secret is the address plus a constant compiled into the circuit.
96
+ // signal_hash is NOT part of this circuit's nullifier: a nullifier derived
97
+ // from a public input nobody signs and nothing constrains would let one
98
+ // wallet mint as many nullifiers as it likes; here the secret is the address
99
+ // plus a constant compiled into the circuit.
110
100
  giwa_attestation: {
111
101
  name: 'GIWA Attestation',
112
102
  description: 'Prove GIWA identity verification, optionally authorizing one EIP-712 action (planned — not officially supported yet)',
@@ -178,11 +168,6 @@ const RPC_ENDPOINTS = {
178
168
  84532: 'https://sepolia.base.org', // Base Sepolia
179
169
  8453: 'https://mainnet.base.org', // Base Mainnet
180
170
  91342: 'https://sepolia-rpc.giwa.io', // GIWA Sepolia
181
- // Arc Testnet. The arc_eligibility verifier is deployed here and nowhere
182
- // else, and this line was missing on 2026-09-12 — the circuit shipped, a
183
- // phone produced a valid proof, and on-chain verification answered "No RPC
184
- // endpoint configured for chain 5042002", which reads like the proof's fault.
185
- 5042002: 'https://rpc.testnet.arc.io', // Arc Testnet
186
171
  };
187
172
  /**
188
173
  * Default proof request expiration time in milliseconds.
@@ -390,8 +375,6 @@ const CIRCUIT_IDS = Object.freeze({
390
375
  COINBASE_COUNTRY_ATTESTATION: 'coinbase_country_attestation',
391
376
  /** OIDC email-domain attestation. Officially supported. */
392
377
  OIDC_DOMAIN_ATTESTATION: 'oidc_domain_attestation',
393
- /** Arc eligibility: exact EIP-712 action authorization. Experimental on Arc Testnet. */
394
- ARC_ELIGIBILITY: 'arc_eligibility',
395
378
  /** GIWA attestation. Planned — not officially supported yet. */
396
379
  GIWA_ATTESTATION: 'giwa_attestation',
397
380
  /** Korea Mobile ID ownership. Planned — not officially supported yet. */
@@ -420,7 +403,6 @@ const CIRCUIT_SUPPORT_STATUS = Object.freeze({
420
403
  coinbase_attestation: 'supported',
421
404
  coinbase_country_attestation: 'supported',
422
405
  oidc_domain_attestation: 'supported',
423
- arc_eligibility: 'experimental',
424
406
  // Experimental, not planned: the circuit is built, its verifier is
425
407
  // deployed on GIWA Sepolia and the demo requests it. What keeps it out of
426
408
  // 'supported' is that it is testnet-only, so the app offers it behind
@@ -454,7 +436,6 @@ const CIRCUIT_VK_PATHS = Object.freeze({
454
436
  coinbase_attestation: 'coinbase-attestation/target/vk/vk',
455
437
  coinbase_country_attestation: 'coinbase-country-attestation/target/vk/vk',
456
438
  oidc_domain_attestation: 'oidc-domain-attestation/target/vk/vk',
457
- arc_eligibility: 'arc-eligibility/target/vk/vk',
458
439
  giwa_attestation: 'giwa-attestation/target/vk/vk',
459
440
  mdl_kr_ownership: 'mdl/kr-ownership/target/vk/vk',
460
441
  mdl_kr_age: 'mdl/kr-age/target/vk/vk',
@@ -483,12 +464,11 @@ const SUPPORTED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCU
483
464
  /**
484
465
  * Circuits you can prove today, on a testnet, with everything subject to change.
485
466
  *
486
- * `arc_eligibility` is the reason this status exists. Calling it `planned`
487
- * would be wrong — the circuit compiles, the verifier is deployed and a proof
488
- * verifies against it — and calling it `supported` would be worse, because the
489
- * only deployment is Arc Testnet, Circle has not published a mainnet chain id,
490
- * and the public-input layout is still open. Neither existing word was true, so
491
- * the honest move was a third word rather than picking the closer lie.
467
+ * `giwa_attestation` is in this state. Calling it `planned` would be wrong —
468
+ * the circuit compiles, the verifier is deployed and a proof verifies against
469
+ * it — and calling it `supported` would be worse, because the only deployment
470
+ * is a testnet (GIWA Sepolia). Neither existing word was true, so the honest
471
+ * move was a third word rather than picking the closer lie.
492
472
  */
493
473
  const EXPERIMENTAL_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter(id => CIRCUIT_SUPPORT_STATUS[id] === 'experimental'));
494
474
  const PLANNED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'planned'));
@@ -542,12 +522,8 @@ const CIRCUIT_ACTION_BINDING = {
542
522
  coinbase_country_attestation: 'none',
543
523
  // The signature lives inside the identity token; no wallet, no action.
544
524
  oidc_domain_attestation: 'none',
545
- // Both branch in-circuit: with an action the wallet signs the typed data,
546
- // without one it personal_signs the request's signal hash. Arc read
547
- // 'required' until 2026-09-22 because its circuit had no second path -- a
548
- // request without an action built a vector 64 bytes short and died inside
549
- // the prover. The circuit gained the branch; the table follows it.
550
- arc_eligibility: 'optional',
525
+ // Branches in-circuit: with an action the wallet signs the typed data,
526
+ // without one it personal_signs the request's signal hash.
551
527
  giwa_attestation: 'optional',
552
528
  mdl_kr_ownership: 'none',
553
529
  mdl_kr_age: 'none',
@@ -578,16 +554,13 @@ function circuitActionBinding(circuit) {
578
554
  const CIRCUIT_NEEDS_WALLET_SIGNATURE = {
579
555
  coinbase_attestation: true,
580
556
  coinbase_country_attestation: true,
581
- // Same Coinbase-attested wallet as above; what changes is that it signs an
582
- // EIP-712 action rather than signal_hash.
583
- arc_eligibility: true,
584
557
  // The signature is inside the OIDC identity token, checked by the prover.
585
558
  oidc_domain_attestation: false,
586
559
  // Web2 (OmniOne CX) flows with no wallet binding at all.
587
560
  mdl_kr_ownership: false,
588
561
  mdl_kr_age: false,
589
562
  mdl_kr_region: false,
590
- // A GIWA-attested wallet signing an EIP-712 action, the same shape as Arc.
563
+ // A GIWA-attested wallet signing an EIP-712 action (or its signal hash).
591
564
  // This read `false` while the circuit had always verified a wallet signature
592
565
  // -- it sat under the Web2 comment above and was never revisited. A request
593
566
  // for it would have reached the relay with no signer.
@@ -609,7 +582,7 @@ const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
609
582
  */
610
583
  function validateTypedAction(value) {
611
584
  if (value === undefined || value === null) {
612
- 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.';
585
+ return 'action is required. The circuit proves that a wallet authorised ONE EIP-712 action; there is nothing to prove without it.';
613
586
  }
614
587
  if (typeof value !== 'object' || Array.isArray(value)) {
615
588
  return 'action must be an object.';
@@ -1165,8 +1138,7 @@ function validateProofRequest(request) {
1165
1138
  return { valid: false, error: 'Missing circuit type' };
1166
1139
  }
1167
1140
  // The list is the SDK's own, not a copy. The copy that used to sit here
1168
- // named six circuits and had missed two — `giwa_attestation` and
1169
- // `arc_eligibility` — so a link for either was refused as an invalid type
1141
+ // had missed circuits, so a link for one was refused as an invalid type
1170
1142
  // while every other layer accepted it.
1171
1143
  if (!isCircuitId(request.circuit)) {
1172
1144
  return {
@@ -1221,15 +1193,8 @@ function validateProofRequest(request) {
1221
1193
  return { valid: false, error: mdlError };
1222
1194
  }
1223
1195
  }
1224
- else if (request.circuit === 'arc_eligibility') {
1225
- const inputs = request.inputs;
1226
- if (inputs.userAddress && !/^0x[a-fA-F0-9]{40}$/.test(inputs.userAddress)) {
1227
- return { valid: false, error: 'Invalid userAddress format' };
1228
- }
1229
- }
1230
1196
  // The action, decided by the circuit's own entry rather than by naming a
1231
- // circuit here. This used to be part of the arc_eligibility branch above,
1232
- // which is why a second action-capable circuit had nowhere to be checked.
1197
+ // circuit here, so every action-capable circuit is checked the same way.
1233
1198
  {
1234
1199
  const action = request.inputs?.action;
1235
1200
  const binding = circuitActionBinding(request.circuit);
@@ -1521,7 +1486,7 @@ function getVerifierContract(providerOrSigner, verifier) {
1521
1486
  function getDefaultProvider(chainId) {
1522
1487
  const rpcUrl = RPC_ENDPOINTS[chainId];
1523
1488
  if (!rpcUrl) {
1524
- throw new Error(`No RPC endpoint configured for chain ${chainId}`);
1489
+ throw new Error(`No RPC endpoint configured for chain ${chainId}. Known chains: ${Object.keys(RPC_ENDPOINTS).join(', ')}`);
1525
1490
  }
1526
1491
  return createJsonRpcProvider(rpcUrl);
1527
1492
  }
@@ -1670,7 +1635,7 @@ function getVerifierChainId(circuit, customVerifier) {
1670
1635
  *
1671
1636
  * It replaced an `if / else if / else` chain whose final `else` handed every
1672
1637
  * unrecognised circuit — and every call that named no circuit at all —
1673
- * Coinbase's offsets. For `arc_eligibility` that is not a near miss: its
1638
+ * Coinbase's offsets. For `giwa_attestation` that is not a near miss: its
1674
1639
  * public inputs are signal_hash, domain_separator, action_hash, merkle root,
1675
1640
  * scope, nullifier, so the nullifier slot under Coinbase's offsets holds the
1676
1641
  * MERKLE ROOT, a value identical for every user. Duplicate detection built on
@@ -1680,15 +1645,13 @@ const PUBLIC_INPUT_OFFSETS = Object.freeze({
1680
1645
  // signal_hash, signer_list_merkle_root, scope, nullifier
1681
1646
  coinbase_attestation: { scope: [64, 95], nullifier: [96, 127] },
1682
1647
  // No longer Coinbase's layout. The GIWA circuit gained the EIP-712 pair, so
1683
- // it reads like Arc: signal_hash, domain_separator, action_hash, merkle
1684
- // root, then scope and nullifier 64 bytes further along than Coinbase.
1648
+ // it reads: signal_hash, domain_separator, action_hash, merkle root, then
1649
+ // scope and nullifier 64 bytes further along than Coinbase.
1685
1650
  giwa_attestation: { scope: [128, 159], nullifier: [160, 191] },
1686
1651
  // country_list, country_list_length and is_included sit before scope.
1687
1652
  coinbase_country_attestation: { scope: [86, 117], nullifier: [118, 149] },
1688
1653
  // pubkey_modulus_limbs and the bounded domain sit before scope.
1689
1654
  oidc_domain_attestation: { scope: [83, 114], nullifier: [115, 146] },
1690
- // signal_hash, domain_separator, action_hash, merkle root, then the pair.
1691
- arc_eligibility: { scope: [128, 159], nullifier: [160, 191] },
1692
1655
  // The Korea mDL circuits open with the pair; signal_hash is commented out.
1693
1656
  mdl_kr_ownership: { scope: [0, 31], nullifier: [32, 63] },
1694
1657
  mdl_kr_age: { scope: [0, 31], nullifier: [32, 63] },
@@ -3069,9 +3032,9 @@ class ProofportSDK {
3069
3032
  * ```
3070
3033
  */
3071
3034
  // Which circuits need a wallet signature, from the one record in
3072
- // src/circuits.ts. The typed-out pair that used to sit here had missed
3073
- // arc_eligibility, so a dapp calling createRelayRequest() for it with no
3074
- // signer got no local error and a relay 401 two round trips later.
3035
+ // src/circuits.ts. A typed-out list here once missed a circuit, so a dapp
3036
+ // calling createRelayRequest() for it with no signer got no local error and
3037
+ // a relay 401 two round trips later.
3075
3038
  static needsWalletSignature(circuit) {
3076
3039
  return CIRCUIT_NEEDS_WALLET_SIGNATURE[circuit] === true;
3077
3040
  }
@@ -3096,9 +3059,7 @@ class ProofportSDK {
3096
3059
  // from inside the mobile app -- a screen the dapp developer never sees,
3097
3060
  // reached after the person has already scanned a QR code.
3098
3061
  //
3099
- // This named arc_eligibility directly until giwa_attestation became the
3100
- // second circuit that can carry an action; the id belongs in the table,
3101
- // not in the check.
3062
+ // The circuit id belongs in the table, not in the check.
3102
3063
  {
3103
3064
  const action = inputs.action;
3104
3065
  const binding = circuitActionBinding(circuit);