@zkproofport-app/sdk 0.2.12 → 0.2.13

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
@@ -143,6 +143,13 @@ const VERIFIER_ABI = [
143
143
  * Supported networks:
144
144
  * - 84532: Base Sepolia (testnet)
145
145
  * - 8453: Base Mainnet (production)
146
+ * - 91342: GIWA Sepolia (testnet) — where the GIWA account circuit verifies
147
+ *
148
+ * A chain missing from this map is not a quiet degradation: `getDefaultProvider`
149
+ * throws "No RPC endpoint configured for chain N". That is what a caller saw
150
+ * when verifying a GIWA proof on chain 91342 — the verifier contract had been
151
+ * deployed and the proof was valid, but the SDK had no way to reach the chain
152
+ * to ask. Deploying a verifier to a new network means adding it here too.
146
153
  *
147
154
  * @example
148
155
  * ```typescript
@@ -154,6 +161,7 @@ const VERIFIER_ABI = [
154
161
  const RPC_ENDPOINTS = {
155
162
  84532: 'https://sepolia.base.org', // Base Sepolia
156
163
  8453: 'https://mainnet.base.org', // Base Mainnet
164
+ 91342: 'https://sepolia-rpc.giwa.io', // GIWA Sepolia
157
165
  };
158
166
  /**
159
167
  * Default proof request expiration time in milliseconds.
@@ -1392,6 +1400,383 @@ function reconstructBytes32FromFields(fields) {
1392
1400
  return '0x' + bytes;
1393
1401
  }
1394
1402
 
1403
+ /**
1404
+ * Canonical ZKProofport circuit identifiers.
1405
+ *
1406
+ * This module is the single source of truth for circuit IDs. Every other layer
1407
+ * — the mobile app, the demo, the relay, server-side provers — reads its list
1408
+ * from here instead of keeping a private copy that silently drifts.
1409
+ *
1410
+ * It has **no imports and no dependencies**, so it can be pulled in on its own:
1411
+ *
1412
+ * ```typescript
1413
+ * import { CIRCUIT_IDS, isCircuitId } from '@zkproofport-app/sdk/circuits';
1414
+ * ```
1415
+ *
1416
+ * The same names are also re-exported from the package root
1417
+ * (`@zkproofport-app/sdk`) for callers that already import the SDK.
1418
+ *
1419
+ * ## Identifiers are canonical and case-sensitive
1420
+ *
1421
+ * Each ID is the circuit's `name` in its `Nargo.toml`, verbatim. The format is
1422
+ * lowercase with underscores. Hyphenated spellings (`coinbase-kyc`,
1423
+ * `mdl-kr-age`) are directory or UI route names in other repositories — they
1424
+ * are **not** circuit IDs, and passing one produces a nullifier mismatch or a
1425
+ * failed on-chain lookup rather than a clear error.
1426
+ */
1427
+ /**
1428
+ * Canonical circuit identifiers, keyed by a stable constant name.
1429
+ *
1430
+ * Prefer these constants over string literals so a rename is a compile error
1431
+ * rather than a runtime surprise.
1432
+ *
1433
+ * @example
1434
+ * ```typescript
1435
+ * import { CIRCUIT_IDS } from '@zkproofport-app/sdk/circuits';
1436
+ *
1437
+ * const relay = await sdk.createRelayRequest(CIRCUIT_IDS.COINBASE_ATTESTATION, {
1438
+ * scope: 'myapp.com',
1439
+ * });
1440
+ * ```
1441
+ */
1442
+ const CIRCUIT_IDS = Object.freeze({
1443
+ /** Coinbase KYC attestation. Officially supported. */
1444
+ COINBASE_ATTESTATION: 'coinbase_attestation',
1445
+ /** Coinbase country attestation (inclusion / exclusion). Officially supported. */
1446
+ COINBASE_COUNTRY_ATTESTATION: 'coinbase_country_attestation',
1447
+ /** OIDC email-domain attestation. Officially supported. */
1448
+ OIDC_DOMAIN_ATTESTATION: 'oidc_domain_attestation',
1449
+ /** GIWA attestation. Planned — not officially supported yet. */
1450
+ GIWA_ATTESTATION: 'giwa_attestation',
1451
+ /** Korea Mobile ID ownership. Planned — not officially supported yet. */
1452
+ MDL_KR_OWNERSHIP: 'mdl_kr_ownership',
1453
+ /** Korea Mobile ID age threshold. Planned — not officially supported yet. */
1454
+ MDL_KR_AGE: 'mdl_kr_age',
1455
+ /** Korea Mobile ID si/do region. Planned — not officially supported yet. */
1456
+ MDL_KR_REGION: 'mdl_kr_region',
1457
+ });
1458
+ /**
1459
+ * Support status for every circuit.
1460
+ *
1461
+ * The record is exhaustive over {@link CircuitId}: adding an identifier without
1462
+ * giving it a status is a compile error, so this list cannot drift from
1463
+ * {@link CIRCUIT_IDS}.
1464
+ *
1465
+ * @example
1466
+ * ```typescript
1467
+ * import { CIRCUIT_SUPPORT_STATUS } from '@zkproofport-app/sdk/circuits';
1468
+ *
1469
+ * CIRCUIT_SUPPORT_STATUS.coinbase_attestation; // 'supported'
1470
+ * CIRCUIT_SUPPORT_STATUS.mdl_kr_age; // 'planned'
1471
+ * ```
1472
+ */
1473
+ const CIRCUIT_SUPPORT_STATUS = Object.freeze({
1474
+ coinbase_attestation: 'supported',
1475
+ coinbase_country_attestation: 'supported',
1476
+ oidc_domain_attestation: 'supported',
1477
+ giwa_attestation: 'planned',
1478
+ mdl_kr_ownership: 'planned',
1479
+ mdl_kr_age: 'planned',
1480
+ mdl_kr_region: 'planned',
1481
+ });
1482
+ /**
1483
+ * Where each circuit's verification key lives inside the `zkproofport/circuits`
1484
+ * repository, relative to its root.
1485
+ *
1486
+ * Off-chain verification needs the verification key and nothing else — no
1487
+ * circuit bytecode, no witness. The key is public by nature: it is what anyone
1488
+ * checking a proof must already have, and the Solidity verifiers deployed
1489
+ * on-chain embed the same key.
1490
+ *
1491
+ * Exhaustive over {@link CircuitId} on purpose. A new circuit whose key path is
1492
+ * not listed here fails to compile rather than failing at verification time,
1493
+ * where the only symptom would be a proof that cannot be checked.
1494
+ */
1495
+ const CIRCUIT_VK_PATHS = Object.freeze({
1496
+ coinbase_attestation: 'coinbase-attestation/target/vk',
1497
+ coinbase_country_attestation: 'coinbase-country-attestation/target/vk',
1498
+ oidc_domain_attestation: 'oidc-domain-attestation/target/vk',
1499
+ giwa_attestation: 'giwa-attestation/target/vk',
1500
+ mdl_kr_ownership: 'mdl/kr-ownership/target/vk',
1501
+ mdl_kr_age: 'mdl/kr-age/target/vk',
1502
+ mdl_kr_region: 'mdl/kr-region/target/vk',
1503
+ });
1504
+ /**
1505
+ * Every canonical circuit identifier, in declaration order.
1506
+ *
1507
+ * Includes circuits that are still `planned`. Use {@link SUPPORTED_CIRCUIT_IDS}
1508
+ * when you only want the ones that are officially supported today.
1509
+ */
1510
+ const ALL_CIRCUIT_IDS = Object.freeze(Object.values(CIRCUIT_IDS));
1511
+ /**
1512
+ * Circuits that are officially supported today: the two Coinbase circuits and
1513
+ * OIDC domain attestation.
1514
+ *
1515
+ * Derived from {@link CIRCUIT_SUPPORT_STATUS}, so it cannot fall out of sync.
1516
+ */
1517
+ const SUPPORTED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'supported'));
1518
+ /**
1519
+ * Circuits whose identifiers are reserved but which are not officially
1520
+ * supported yet: GIWA attestation and the three Korea Mobile ID circuits.
1521
+ *
1522
+ * Derived from {@link CIRCUIT_SUPPORT_STATUS}, so it cannot fall out of sync.
1523
+ */
1524
+ const PLANNED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'planned'));
1525
+ /**
1526
+ * Narrows an unknown value to a canonical circuit identifier.
1527
+ *
1528
+ * Returns `false` for hyphenated route names such as `'coinbase-kyc'`, which
1529
+ * are not circuit IDs.
1530
+ *
1531
+ * @example
1532
+ * ```typescript
1533
+ * isCircuitId('coinbase_attestation'); // true
1534
+ * isCircuitId('coinbase-kyc'); // false
1535
+ * ```
1536
+ */
1537
+ function isCircuitId(value) {
1538
+ return typeof value === 'string' && ALL_CIRCUIT_IDS.includes(value);
1539
+ }
1540
+ /**
1541
+ * Narrows an unknown value to a circuit identifier that is officially supported
1542
+ * today. Planned circuits return `false`.
1543
+ *
1544
+ * @example
1545
+ * ```typescript
1546
+ * isSupportedCircuitId('oidc_domain_attestation'); // true
1547
+ * isSupportedCircuitId('mdl_kr_age'); // false — planned
1548
+ * ```
1549
+ */
1550
+ function isSupportedCircuitId(value) {
1551
+ return isCircuitId(value) && CIRCUIT_SUPPORT_STATUS[value] === 'supported';
1552
+ }
1553
+ /**
1554
+ * Returns the support status of a circuit.
1555
+ *
1556
+ * @param circuit - A canonical circuit identifier.
1557
+ * @throws Error when the value is not a canonical circuit identifier.
1558
+ *
1559
+ * @example
1560
+ * ```typescript
1561
+ * getCircuitSupportStatus('giwa_attestation'); // 'planned'
1562
+ * ```
1563
+ */
1564
+ function getCircuitSupportStatus(circuit) {
1565
+ if (!isCircuitId(circuit)) {
1566
+ throw new Error(`Unknown circuit '${String(circuit)}'. Expected one of: ${ALL_CIRCUIT_IDS.join(', ')}`);
1567
+ }
1568
+ return CIRCUIT_SUPPORT_STATUS[circuit];
1569
+ }
1570
+
1571
+ /**
1572
+ * Off-chain proof verification — checking a proof against its verification key,
1573
+ * with no chain, no RPC, and no gas.
1574
+ *
1575
+ * WHY THIS EXISTS. The SDK had only `verifyProofOnChain`, and every caller that
1576
+ * wanted an off-chain check called that instead and relabelled the result. The
1577
+ * demo page did exactly this from its first commit: pressing "Off-Chain Verify"
1578
+ * ran an on-chain verification and printed "Off-Chain Verification Passed!".
1579
+ * The answer was true and the label was a lie, which is the worst combination —
1580
+ * nothing looked broken. The mobile app has always verified off-chain properly
1581
+ * (through mopro's native Barretenberg); it was only the JavaScript side that
1582
+ * had no such thing.
1583
+ *
1584
+ * HOW IT WORKS. `bb` proofs are checked by `acirVerifyUltraKeccakHonk(proof,
1585
+ * vk)` — the keccak variant, matching how these proofs are produced. Two things
1586
+ * have to line up:
1587
+ *
1588
+ * The version. The verification key format is not stable across bb releases:
1589
+ * the pinned build emits a 1816-byte key where bb 0.87.0 and 1.2.0 emit 1760.
1590
+ * `@aztec/bb.js@1.0.0-nightly.20250723` is the WASM build of the same bb the
1591
+ * circuits were compiled with. A different one will reject valid proofs.
1592
+ *
1593
+ * The oracle hash. Proofs are generated with `--oracle_hash keccak` so the
1594
+ * Solidity verifiers can check them, so the poseidon-flavoured verify — which
1595
+ * is what `BarretenbergVerifier.verifyUltraHonkProof` uses — says false for a
1596
+ * perfectly good proof.
1597
+ *
1598
+ * THE PROOF ARRIVES IN TWO PIECES. The mobile app splits what Barretenberg
1599
+ * produced into `proof` and `publicInputs` before sending it, because the
1600
+ * Solidity verifier takes them separately. Verifying needs them joined back, in
1601
+ * that order, which is what `reconstructHonkProof` does.
1602
+ */
1603
+ /**
1604
+ * Where verification keys are published.
1605
+ *
1606
+ * Not configurable, deliberately: a caller who could point this somewhere else
1607
+ * could point it at a key of their choosing, and "the proof verified" would
1608
+ * then mean nothing. Same repository the mobile app downloads circuit data
1609
+ * from.
1610
+ */
1611
+ const CIRCUITS_VK_BASE = 'https://raw.githubusercontent.com/zkproofport/circuits/main';
1612
+ /** How long a fetched verification key is reused. Keys change only on a recompile. */
1613
+ const VK_CACHE_TTL_MS = 60 * 60 * 1000;
1614
+ const vkCache = new Map();
1615
+ /** @internal Hex string (with or without 0x) to bytes. */
1616
+ function hexToBytes(hex) {
1617
+ const clean = hex.startsWith('0x') || hex.startsWith('0X') ? hex.slice(2) : hex;
1618
+ if (clean.length === 0)
1619
+ return new Uint8Array(0);
1620
+ if (clean.length % 2 !== 0) {
1621
+ throw new Error(`Hex string has an odd length (${clean.length}); it cannot be bytes`);
1622
+ }
1623
+ if (!/^[0-9a-fA-F]*$/.test(clean)) {
1624
+ throw new Error('Hex string contains non-hex characters');
1625
+ }
1626
+ const out = new Uint8Array(clean.length / 2);
1627
+ for (let i = 0; i < out.length; i++) {
1628
+ out[i] = parseInt(clean.substr(i * 2, 2), 16);
1629
+ }
1630
+ return out;
1631
+ }
1632
+ /**
1633
+ * @internal Public inputs as the flat byte run Barretenberg expects: each one a
1634
+ * 32-byte big-endian field element, concatenated in order.
1635
+ *
1636
+ * The relay carries them as hex strings that are USUALLY already 32 bytes, but
1637
+ * a value that happens to have leading zeros can arrive short (`0x01`). Left-
1638
+ * padding here rather than trusting the sender is the difference between a
1639
+ * verification that works and one that fails on roughly one proof in a hundred,
1640
+ * for no reason the caller can see.
1641
+ */
1642
+ function packPublicInputs(publicInputs) {
1643
+ const out = new Uint8Array(publicInputs.length * 32);
1644
+ publicInputs.forEach((input, i) => {
1645
+ const bytes = hexToBytes(input);
1646
+ if (bytes.length > 32) {
1647
+ throw new Error(`Public input ${i} is ${bytes.length} bytes; a field element is at most 32`);
1648
+ }
1649
+ out.set(bytes, i * 32 + (32 - bytes.length));
1650
+ });
1651
+ return out;
1652
+ }
1653
+ /**
1654
+ * Fetch a circuit's verification key, with a small cache.
1655
+ *
1656
+ * @internal
1657
+ */
1658
+ async function fetchVerificationKey(circuit, timeoutMs) {
1659
+ const cached = vkCache.get(circuit);
1660
+ if (cached && Date.now() - cached.at < VK_CACHE_TTL_MS)
1661
+ return cached.vk;
1662
+ const vkPath = CIRCUIT_VK_PATHS[circuit];
1663
+ if (!vkPath) {
1664
+ throw new Error(`No verification key path for circuit '${circuit}'. ` +
1665
+ `Add it to CIRCUIT_VK_PATHS, or pass verificationKey directly.`);
1666
+ }
1667
+ const url = `${CIRCUITS_VK_BASE}/${vkPath}`;
1668
+ const controller = new AbortController();
1669
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
1670
+ try {
1671
+ const resp = await fetch(url, { signal: controller.signal });
1672
+ if (!resp.ok) {
1673
+ throw new Error(`Could not fetch the verification key (HTTP ${resp.status}) from ${url}`);
1674
+ }
1675
+ const vk = new Uint8Array(await resp.arrayBuffer());
1676
+ // A 404 page or an HTML redirect arrives as a successful response full of
1677
+ // text, and would reach Barretenberg as a "verification key" that fails for
1678
+ // reasons no one could guess from the message.
1679
+ if (vk.length < 512) {
1680
+ throw new Error(`The verification key at ${url} is ${vk.length} bytes, far too small to be one. ` +
1681
+ `The path is probably wrong and something else was served.`);
1682
+ }
1683
+ vkCache.set(circuit, { vk, at: Date.now() });
1684
+ return vk;
1685
+ }
1686
+ finally {
1687
+ clearTimeout(timer);
1688
+ }
1689
+ }
1690
+ /**
1691
+ * Verify a proof off-chain, against its verification key.
1692
+ *
1693
+ * Needs `@aztec/bb.js` at the pinned version. It is an optional peer dependency
1694
+ * — the WASM is large and most callers verify on-chain — so the failure to have
1695
+ * it installed says so plainly rather than surfacing as a module-not-found.
1696
+ *
1697
+ * @param circuit - Canonical circuit identifier
1698
+ * @param proof - Proof bytes as hex, WITHOUT public inputs (as the relay sends it)
1699
+ * @param publicInputs - Public inputs as hex strings, in circuit order
1700
+ * @param options - Optional verification key and timeout
1701
+ * @returns `{ valid }`, or `{ valid: false, error }` when the check could not run
1702
+ *
1703
+ * @example
1704
+ * ```typescript
1705
+ * const result = await verifyProofOffChain(
1706
+ * 'coinbase_attestation',
1707
+ * response.proof,
1708
+ * response.publicInputs
1709
+ * );
1710
+ * if (result.valid) console.log('Proof is valid, no chain involved');
1711
+ * ```
1712
+ */
1713
+ async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
1714
+ const timeoutMs = options.timeoutMs ?? 30000;
1715
+ let bb;
1716
+ try {
1717
+ bb = await import('@aztec/bb.js');
1718
+ }
1719
+ catch {
1720
+ return {
1721
+ valid: false,
1722
+ error: 'Off-chain verification needs @aztec/bb.js. Install the pinned version: ' +
1723
+ 'npm install @aztec/bb.js@1.0.0-nightly.20250723',
1724
+ };
1725
+ }
1726
+ let api;
1727
+ try {
1728
+ const proofBytes = hexToBytes(proof);
1729
+ if (proofBytes.length === 0) {
1730
+ return { valid: false, error: 'Proof is empty' };
1731
+ }
1732
+ const publicInputBytes = packPublicInputs(publicInputs);
1733
+ const vk = options.verificationKey ?? (await fetchVerificationKey(circuit, timeoutMs));
1734
+ // Public inputs first, then the proof — the order Barretenberg wrote them in
1735
+ // before the mobile app split them apart for the Solidity verifier.
1736
+ const full = bb.reconstructHonkProof(publicInputBytes, proofBytes);
1737
+ api = (await bb.Barretenberg.new({ threads: 1 }));
1738
+ // THE VERIFY MUST MATCH HOW THE PROOF WAS MADE, AND THERE ARE FOUR OF THEM.
1739
+ //
1740
+ // `scripts/build.sh` in the circuits repo proves with `--oracle_hash keccak`
1741
+ // and ZK on — so the only entry point that answers correctly is the one that
1742
+ // is BOTH. Measured against a real proof of the Korea Mobile ID age circuit,
1743
+ // freshly generated against its own key:
1744
+ //
1745
+ // acirVerifyUltraKeccakZkHonk true ← the right one
1746
+ // acirVerifyUltraKeccakHonk TRAPS with a bare "unreachable"
1747
+ // acirVerifyUltraZKHonk false (poseidon: wrong oracle hash)
1748
+ //
1749
+ // The middle row is why this took a while: picking keccak-without-ZK does
1750
+ // not return false, it kills the WebAssembly instance with a message that
1751
+ // names nothing. It looks identical to a malformed proof, to a mismatched
1752
+ // key, and to a missing SRS.
1753
+ //
1754
+ // Two more things that produce that same trap, both required:
1755
+ // initSRSForCircuitSize must be called before the first verify.
1756
+ // The key must be a RawBuffer — a plain Uint8Array is marshalled as a
1757
+ // length-prefixed vector, so its length is read as key material.
1758
+ // Both are what bb.js's own BarretenbergVerifier does before verifying.
1759
+ await api.initSRSForCircuitSize(0);
1760
+ const valid = await api.acirVerifyUltraKeccakZkHonk(full, new bb.RawBuffer(vk));
1761
+ return { valid };
1762
+ }
1763
+ catch (error) {
1764
+ return { valid: false, error: error instanceof Error ? error.message : String(error) };
1765
+ }
1766
+ finally {
1767
+ // The WASM instance holds worker threads; leaving them running keeps a Node
1768
+ // process alive after the check is done.
1769
+ if (api) {
1770
+ try {
1771
+ await api.destroy();
1772
+ }
1773
+ catch {
1774
+ /* nothing useful to do if teardown fails */
1775
+ }
1776
+ }
1777
+ }
1778
+ }
1779
+
1395
1780
  /**
1396
1781
  * Proofport SDK - Main class
1397
1782
  */
@@ -2038,6 +2423,38 @@ class ProofportSDK {
2038
2423
  };
2039
2424
  return verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, customVerifier, responseVerifier);
2040
2425
  }
2426
+ /**
2427
+ * Verifies a proof response off-chain, against the circuit's verification key.
2428
+ *
2429
+ * No chain, no RPC, no gas, and no verifier contract — so this works for a
2430
+ * circuit whose verifier has not been deployed anywhere, and it does not
2431
+ * depend on a network being reachable. What it does not give you is the
2432
+ * chain's own opinion: use {@link verifyResponseOnChain} when the answer has
2433
+ * to be one a contract will agree with.
2434
+ *
2435
+ * Requires the optional peer dependency:
2436
+ * `npm install @aztec/bb.js@1.0.0-nightly.20250723`. The version is exact —
2437
+ * verification keys are not portable across bb releases, so a different build
2438
+ * rejects valid proofs.
2439
+ *
2440
+ * @param response - Proof response from the relay
2441
+ * @param options - Optional verification key (skips the fetch) and timeout
2442
+ * @returns `{ valid }`, or `{ valid: false, error }` if the check could not run
2443
+ *
2444
+ * @example
2445
+ * ```typescript
2446
+ * const result = await sdk.verifyResponseOffChain(response);
2447
+ * if (result.valid) {
2448
+ * console.log('Valid, and nothing was asked of any chain');
2449
+ * }
2450
+ * ```
2451
+ */
2452
+ async verifyResponseOffChain(response, options) {
2453
+ if (response.status !== 'completed' || !response.proof || !response.publicInputs) {
2454
+ return { valid: false, error: 'Invalid or incomplete response' };
2455
+ }
2456
+ return verifyProofOffChain(response.circuit, response.proof, response.publicInputs, options);
2457
+ }
2041
2458
  // ============ Utility Methods ============
2042
2459
  /**
2043
2460
  * Gets the deployed verifier contract address for a circuit.
@@ -2711,155 +3128,10 @@ ProofportSDK.WALLET_SIGNATURE_CIRCUITS = [
2711
3128
  'coinbase_country_attestation',
2712
3129
  ];
2713
3130
 
2714
- /**
2715
- * Canonical ZKProofport circuit identifiers.
2716
- *
2717
- * This module is the single source of truth for circuit IDs. Every other layer
2718
- * — the mobile app, the demo, the relay, server-side provers — reads its list
2719
- * from here instead of keeping a private copy that silently drifts.
2720
- *
2721
- * It has **no imports and no dependencies**, so it can be pulled in on its own:
2722
- *
2723
- * ```typescript
2724
- * import { CIRCUIT_IDS, isCircuitId } from '@zkproofport-app/sdk/circuits';
2725
- * ```
2726
- *
2727
- * The same names are also re-exported from the package root
2728
- * (`@zkproofport-app/sdk`) for callers that already import the SDK.
2729
- *
2730
- * ## Identifiers are canonical and case-sensitive
2731
- *
2732
- * Each ID is the circuit's `name` in its `Nargo.toml`, verbatim. The format is
2733
- * lowercase with underscores. Hyphenated spellings (`coinbase-kyc`,
2734
- * `mdl-kr-age`) are directory or UI route names in other repositories — they
2735
- * are **not** circuit IDs, and passing one produces a nullifier mismatch or a
2736
- * failed on-chain lookup rather than a clear error.
2737
- */
2738
- /**
2739
- * Canonical circuit identifiers, keyed by a stable constant name.
2740
- *
2741
- * Prefer these constants over string literals so a rename is a compile error
2742
- * rather than a runtime surprise.
2743
- *
2744
- * @example
2745
- * ```typescript
2746
- * import { CIRCUIT_IDS } from '@zkproofport-app/sdk/circuits';
2747
- *
2748
- * const relay = await sdk.createRelayRequest(CIRCUIT_IDS.COINBASE_ATTESTATION, {
2749
- * scope: 'myapp.com',
2750
- * });
2751
- * ```
2752
- */
2753
- const CIRCUIT_IDS = Object.freeze({
2754
- /** Coinbase KYC attestation. Officially supported. */
2755
- COINBASE_ATTESTATION: 'coinbase_attestation',
2756
- /** Coinbase country attestation (inclusion / exclusion). Officially supported. */
2757
- COINBASE_COUNTRY_ATTESTATION: 'coinbase_country_attestation',
2758
- /** OIDC email-domain attestation. Officially supported. */
2759
- OIDC_DOMAIN_ATTESTATION: 'oidc_domain_attestation',
2760
- /** GIWA attestation. Planned — not officially supported yet. */
2761
- GIWA_ATTESTATION: 'giwa_attestation',
2762
- /** Korea Mobile ID ownership. Planned — not officially supported yet. */
2763
- MDL_KR_OWNERSHIP: 'mdl_kr_ownership',
2764
- /** Korea Mobile ID age threshold. Planned — not officially supported yet. */
2765
- MDL_KR_AGE: 'mdl_kr_age',
2766
- /** Korea Mobile ID si/do region. Planned — not officially supported yet. */
2767
- MDL_KR_REGION: 'mdl_kr_region',
2768
- });
2769
- /**
2770
- * Support status for every circuit.
2771
- *
2772
- * The record is exhaustive over {@link CircuitId}: adding an identifier without
2773
- * giving it a status is a compile error, so this list cannot drift from
2774
- * {@link CIRCUIT_IDS}.
2775
- *
2776
- * @example
2777
- * ```typescript
2778
- * import { CIRCUIT_SUPPORT_STATUS } from '@zkproofport-app/sdk/circuits';
2779
- *
2780
- * CIRCUIT_SUPPORT_STATUS.coinbase_attestation; // 'supported'
2781
- * CIRCUIT_SUPPORT_STATUS.mdl_kr_age; // 'planned'
2782
- * ```
2783
- */
2784
- const CIRCUIT_SUPPORT_STATUS = Object.freeze({
2785
- coinbase_attestation: 'supported',
2786
- coinbase_country_attestation: 'supported',
2787
- oidc_domain_attestation: 'supported',
2788
- giwa_attestation: 'planned',
2789
- mdl_kr_ownership: 'planned',
2790
- mdl_kr_age: 'planned',
2791
- mdl_kr_region: 'planned',
2792
- });
2793
- /**
2794
- * Every canonical circuit identifier, in declaration order.
2795
- *
2796
- * Includes circuits that are still `planned`. Use {@link SUPPORTED_CIRCUIT_IDS}
2797
- * when you only want the ones that are officially supported today.
2798
- */
2799
- const ALL_CIRCUIT_IDS = Object.freeze(Object.values(CIRCUIT_IDS));
2800
- /**
2801
- * Circuits that are officially supported today: the two Coinbase circuits and
2802
- * OIDC domain attestation.
2803
- *
2804
- * Derived from {@link CIRCUIT_SUPPORT_STATUS}, so it cannot fall out of sync.
2805
- */
2806
- const SUPPORTED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'supported'));
2807
- /**
2808
- * Circuits whose identifiers are reserved but which are not officially
2809
- * supported yet: GIWA attestation and the three Korea Mobile ID circuits.
2810
- *
2811
- * Derived from {@link CIRCUIT_SUPPORT_STATUS}, so it cannot fall out of sync.
2812
- */
2813
- const PLANNED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'planned'));
2814
- /**
2815
- * Narrows an unknown value to a canonical circuit identifier.
2816
- *
2817
- * Returns `false` for hyphenated route names such as `'coinbase-kyc'`, which
2818
- * are not circuit IDs.
2819
- *
2820
- * @example
2821
- * ```typescript
2822
- * isCircuitId('coinbase_attestation'); // true
2823
- * isCircuitId('coinbase-kyc'); // false
2824
- * ```
2825
- */
2826
- function isCircuitId(value) {
2827
- return typeof value === 'string' && ALL_CIRCUIT_IDS.includes(value);
2828
- }
2829
- /**
2830
- * Narrows an unknown value to a circuit identifier that is officially supported
2831
- * today. Planned circuits return `false`.
2832
- *
2833
- * @example
2834
- * ```typescript
2835
- * isSupportedCircuitId('oidc_domain_attestation'); // true
2836
- * isSupportedCircuitId('mdl_kr_age'); // false — planned
2837
- * ```
2838
- */
2839
- function isSupportedCircuitId(value) {
2840
- return isCircuitId(value) && CIRCUIT_SUPPORT_STATUS[value] === 'supported';
2841
- }
2842
- /**
2843
- * Returns the support status of a circuit.
2844
- *
2845
- * @param circuit - A canonical circuit identifier.
2846
- * @throws Error when the value is not a canonical circuit identifier.
2847
- *
2848
- * @example
2849
- * ```typescript
2850
- * getCircuitSupportStatus('giwa_attestation'); // 'planned'
2851
- * ```
2852
- */
2853
- function getCircuitSupportStatus(circuit) {
2854
- if (!isCircuitId(circuit)) {
2855
- throw new Error(`Unknown circuit '${String(circuit)}'. Expected one of: ${ALL_CIRCUIT_IDS.join(', ')}`);
2856
- }
2857
- return CIRCUIT_SUPPORT_STATUS[circuit];
2858
- }
2859
-
2860
3131
  exports.ALL_CIRCUIT_IDS = ALL_CIRCUIT_IDS;
2861
3132
  exports.CIRCUIT_IDS = CIRCUIT_IDS;
2862
3133
  exports.CIRCUIT_SUPPORT_STATUS = CIRCUIT_SUPPORT_STATUS;
3134
+ exports.CIRCUIT_VK_PATHS = CIRCUIT_VK_PATHS;
2863
3135
  exports.COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT = COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT;
2864
3136
  exports.COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT = COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT;
2865
3137
  exports.MDL_KR_PUBLIC_INPUT_LAYOUT = MDL_KR_PUBLIC_INPUT_LAYOUT;
@@ -2874,4 +3146,5 @@ exports.extractScopeFromPublicInputs = extractScopeFromPublicInputs;
2874
3146
  exports.getCircuitSupportStatus = getCircuitSupportStatus;
2875
3147
  exports.isCircuitId = isCircuitId;
2876
3148
  exports.isSupportedCircuitId = isSupportedCircuitId;
3149
+ exports.verifyProofOffChain = verifyProofOffChain;
2877
3150
  //# sourceMappingURL=index.js.map