@zkproofport-app/sdk 0.2.2 → 0.2.3

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 CHANGED
@@ -122,7 +122,23 @@ const relay = await sdk.createRelayRequest('coinbase_country_attestation', {
122
122
  });
123
123
  ```
124
124
 
125
- > The ZKProofport mobile app handles wallet connection and attestation data retrieval automatically. You only provide the inputs above.
125
+ ### `oidc_domain_attestation`
126
+
127
+ Prove email domain affiliation via Google Sign-In. The mobile app handles authentication and proof generation entirely on-device — the user's email is never revealed.
128
+
129
+ | Field | Type | Required | Description |
130
+ |-------|------|----------|-------------|
131
+ | `domain` | `string` | Yes | Target email domain to prove (e.g., `'google.com'`, `'company.com'`) |
132
+ | `scope` | `string` | Yes | dApp scope identifier for proof uniqueness |
133
+
134
+ ```typescript
135
+ const relay = await sdk.createRelayRequest('oidc_domain_attestation', {
136
+ domain: 'company.com',
137
+ scope: 'myapp.com',
138
+ });
139
+ ```
140
+
141
+ > The mobile app prompts Google Sign-In, generates the ZK proof locally, and returns the result via relay. The `domain` is a public input — verifiers can confirm which domain was proven.
126
142
 
127
143
  ## Integration Guide
128
144
 
@@ -159,6 +175,8 @@ interface WalletSigner {
159
175
 
160
176
  Any ethers v5/v6 `Signer` is compatible.
161
177
 
178
+ > **OIDC Domain note:** Wallet signer is not required for OIDC Domain proofs. See Step 3 for OIDC-specific usage.
179
+
162
180
  #### About challenge-signature
163
181
 
164
182
  The challenge-signature mechanism was developed **for relay nonce replay prevention**. Each challenge is one-time use and consumed immediately. The signer's recovered address is recorded as `clientId` in relay server logs, which helps the relay operator track requests.
@@ -192,6 +210,21 @@ const relay = await sdk.createRelayRequest('coinbase_attestation', {
192
210
  // relay.pollUrl — Relative URL for HTTP polling
193
211
  ```
194
212
 
213
+ **OIDC Domain Attestation:**
214
+
215
+ ```typescript
216
+ const relay = await sdk.createRelayRequest('oidc_domain_attestation', {
217
+ domain: 'company.com',
218
+ scope: 'myapp.com',
219
+ }, {
220
+ dappName: 'My DApp',
221
+ dappIcon: 'https://myapp.com/icon.png',
222
+ message: 'Verify your email domain',
223
+ });
224
+ ```
225
+
226
+ The mobile app will prompt the user to sign in with Google. The circuit proves the user's email ends with `@company.com` without revealing the full email address. The `domain` field is a **public input** — verifiers can confirm which domain was proven.
227
+
195
228
  ### Step 4: Display QR Code
196
229
 
197
230
  Generate a QR code from the relay deep link for the user to scan with the ZKProofport mobile app:
@@ -314,9 +347,11 @@ if (result.status === 'completed') {
314
347
 
315
348
  The **nullifier** serves as a privacy-preserving user identifier:
316
349
  - Deterministic: same user + same scope = same nullifier (enables duplicate detection)
317
- - Privacy-preserving: the wallet address is never revealed
350
+ - Privacy-preserving: the wallet address (Coinbase) or email (OIDC) is never revealed
318
351
  - Scope-bound: different scopes produce different nullifiers for the same user
319
352
 
353
+ > **OIDC Domain:** The nullifier is a hash of the user's email and scope. The same email + scope always produces the same nullifier, enabling Sybil resistance without revealing the email address.
354
+
320
355
  **Standalone utility functions** are also available for use outside the SDK class:
321
356
 
322
357
  ```typescript
@@ -389,21 +424,7 @@ async function verifyUser() {
389
424
 
390
425
  ## Configuration
391
426
 
392
- `ProofportSDK.create()` returns a fully configured SDK instance. No manual configuration is needed for standard usage.
393
-
394
- For advanced scenarios (e.g., custom verifier deployments), pass a `ProofportConfig`:
395
-
396
- ```typescript
397
- const sdk = new ProofportSDK({
398
- relayUrl: 'https://relay.zkproofport.app',
399
- verifiers: {
400
- coinbase_attestation: {
401
- verifierAddress: '0x...',
402
- chainId: 8453,
403
- },
404
- },
405
- });
406
- ```
427
+ `ProofportSDK.create()` returns a fully configured SDK instance. No manual configuration is needed relay URLs, verifier contracts, and chain settings are all built-in.
407
428
 
408
429
  ## Types Reference
409
430
 
@@ -415,6 +436,7 @@ import type {
415
436
  ProofRequestStatus,
416
437
  CoinbaseKycInputs,
417
438
  CoinbaseCountryInputs,
439
+ OidcDomainInputs,
418
440
  CircuitInputs,
419
441
  ProofRequest,
420
442
  ProofResponse,
@@ -425,27 +447,35 @@ import type {
425
447
  WalletSigner,
426
448
  RelayProofRequest,
427
449
  RelayProofResult,
428
- SDKEnvironment,
429
450
  } from '@zkproofport-app/sdk';
430
451
  ```
431
452
 
432
453
  | Type | Description |
433
454
  |------|-------------|
434
- | `CircuitType` | `'coinbase_attestation' \| 'coinbase_country_attestation'` |
455
+ | `CircuitType` | `'coinbase_attestation' \| 'coinbase_country_attestation' \| 'oidc_domain_attestation'` |
435
456
  | `ProofRequestStatus` | `'pending' \| 'completed' \| 'error' \| 'cancelled'` |
436
457
  | `CoinbaseKycInputs` | Inputs for `coinbase_attestation` (`{ scope, userAddress?, rawTransaction? }`) |
437
458
  | `CoinbaseCountryInputs` | Inputs for `coinbase_country_attestation` (`{ scope, countryList, isIncluded, ... }`) |
438
- | `CircuitInputs` | Union: `CoinbaseKycInputs \| CoinbaseCountryInputs` |
459
+ | `OidcDomainInputs` | Inputs for `oidc_domain_attestation` (`{ domain, scope }`) |
460
+ | `CircuitInputs` | Union: `CoinbaseKycInputs \| CoinbaseCountryInputs \| OidcDomainInputs` |
439
461
  | `ProofRequest` | Proof request object with `requestId`, `circuit`, `inputs`, metadata, and expiry |
440
462
  | `ProofResponse` | Proof response with `status`, `proof`, `publicInputs`, `verifierAddress`, `chainId` |
441
463
  | `QRCodeOptions` | QR customization: `width`, `margin`, `darkColor`, `lightColor`, `errorCorrectionLevel` |
442
464
  | `VerifierContract` | Verifier contract info: `{ address, chainId, abi }` |
443
- | `ProofportConfig` | SDK configuration: `{ scheme?, relayUrl?, verifiers? }` |
465
+ | `ProofportConfig` | SDK configuration (internal use `ProofportSDK.create()` handles defaults) |
444
466
  | `ChallengeResponse` | Challenge from relay: `{ challenge, expiresAt }` |
445
467
  | `WalletSigner` | Signer interface: `{ signMessage(msg), getAddress() }` |
446
468
  | `RelayProofRequest` | Relay response: `{ requestId, deepLink, status, pollUrl }` |
447
469
  | `RelayProofResult` | Relay result: `{ requestId, status, proof?, publicInputs?, circuit?, error? }` |
448
- | `SDKEnvironment` | Environment preset (not needed for normal usage) |
470
+
471
+ The `OidcDomainInputs` interface:
472
+
473
+ ```typescript
474
+ interface OidcDomainInputs {
475
+ domain: string; // Target email domain (e.g., 'google.com')
476
+ scope: string; // dApp scope identifier
477
+ }
478
+ ```
449
479
 
450
480
  ## Error Handling
451
481
 
@@ -467,10 +497,7 @@ try {
467
497
 
468
498
  ## Networks
469
499
 
470
- | Network | Chain ID | Status |
471
- |---------|----------|--------|
472
- | Base Mainnet | 8453 | Production |
473
- | Base Sepolia | 84532 | Testnet |
500
+ Proofs are verified on **Base** (Ethereum L2). The SDK handles network configuration automatically — no manual setup required.
474
501
 
475
502
  ## Development
476
503
 
@@ -763,6 +763,7 @@ export declare class ProofportSDK {
763
763
  * const result = await sdk.waitForProof(relay.requestId);
764
764
  * ```
765
765
  */
766
+ private static readonly WALLET_SIGNATURE_CIRCUITS;
766
767
  createRelayRequest(circuit: CircuitType, inputs: CircuitInputs, options?: {
767
768
  message?: string;
768
769
  dappName?: string;
@@ -183,3 +183,31 @@ export declare const COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT: {
183
183
  readonly NULLIFIER_START: 118;
184
184
  readonly NULLIFIER_END: 149;
185
185
  };
186
+ /**
187
+ * OIDC Domain Attestation circuit public input layout (byte offsets).
188
+ * Defines the byte positions of each field in the flattened public inputs array.
189
+ *
190
+ * Public inputs are packed as individual field elements (one byte per element):
191
+ * - pubkey_modulus_limbs: 18 x u128 = 18 x 16 bytes = 288 bytes → fields 0–287
192
+ * - domain (BoundedVec<u8, 64>): 4-byte length (u32) + 64-byte storage = 68 fields → fields 288–355
193
+ * - scope: 32 bytes → fields 356–387
194
+ * - nullifier: 32 bytes → fields 388–419
195
+ *
196
+ * @example
197
+ * ```typescript
198
+ * const scope = publicInputs.slice(
199
+ * OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT.SCOPE_START,
200
+ * OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT.SCOPE_END + 1
201
+ * );
202
+ * ```
203
+ */
204
+ export declare const OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT: {
205
+ readonly PUBKEY_MODULUS_START: 0;
206
+ readonly PUBKEY_MODULUS_END: 287;
207
+ readonly DOMAIN_START: 288;
208
+ readonly DOMAIN_END: 355;
209
+ readonly SCOPE_START: 356;
210
+ readonly SCOPE_END: 387;
211
+ readonly NULLIFIER_START: 388;
212
+ readonly NULLIFIER_END: 419;
213
+ };
package/dist/index.d.ts CHANGED
@@ -7,8 +7,8 @@
7
7
  * ```typescript
8
8
  * import { ProofportSDK } from '@zkproofport-app/sdk';
9
9
  *
10
- * // Initialize with environment preset
11
- * const sdk = ProofportSDK.create('production');
10
+ * // Initialize SDK
11
+ * const sdk = ProofportSDK.create();
12
12
  *
13
13
  * // Set wallet signer
14
14
  * sdk.setSigner(signer);
@@ -27,5 +27,5 @@
27
27
  */
28
28
  export { ProofportSDK, default } from './ProofportSDK';
29
29
  export { extractScopeFromPublicInputs, extractNullifierFromPublicInputs, } from './verifier';
30
- export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, } from './constants';
31
- export type { CircuitType, ProofRequestStatus, CoinbaseKycInputs, CoinbaseCountryInputs, CircuitInputs, ProofRequest, ProofResponse, QRCodeOptions, VerifierContract, ProofportConfig, ChallengeResponse, WalletSigner, RelayProofRequest, RelayProofResult, SDKEnvironment, } from './types';
30
+ export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, } from './constants';
31
+ export type { CircuitType, ProofRequestStatus, CoinbaseKycInputs, CoinbaseCountryInputs, OidcDomainInputs, CircuitInputs, ProofRequest, ProofResponse, QRCodeOptions, VerifierContract, ProofportConfig, ChallengeResponse, WalletSigner, RelayProofRequest, RelayProofResult, SDKEnvironment, } from './types';
package/dist/index.esm.js CHANGED
@@ -67,6 +67,12 @@ const CIRCUIT_METADATA = {
67
67
  publicInputsCount: 14,
68
68
  publicInputNames: ['signal_hash', 'signer_list_merkle_root', 'country_list', 'country_list_length', 'is_included'],
69
69
  },
70
+ oidc_domain_attestation: {
71
+ name: 'OIDC Domain',
72
+ description: 'Prove email domain affiliation via OIDC JWT',
73
+ publicInputsCount: 420,
74
+ publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier'],
75
+ },
70
76
  };
71
77
  /**
72
78
  * Standard verifier contract ABI shared across all Barretenberg-generated verifiers.
@@ -198,6 +204,34 @@ const COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT = {
198
204
  NULLIFIER_START: 118,
199
205
  NULLIFIER_END: 149,
200
206
  };
207
+ /**
208
+ * OIDC Domain Attestation circuit public input layout (byte offsets).
209
+ * Defines the byte positions of each field in the flattened public inputs array.
210
+ *
211
+ * Public inputs are packed as individual field elements (one byte per element):
212
+ * - pubkey_modulus_limbs: 18 x u128 = 18 x 16 bytes = 288 bytes → fields 0–287
213
+ * - domain (BoundedVec<u8, 64>): 4-byte length (u32) + 64-byte storage = 68 fields → fields 288–355
214
+ * - scope: 32 bytes → fields 356–387
215
+ * - nullifier: 32 bytes → fields 388–419
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * const scope = publicInputs.slice(
220
+ * OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT.SCOPE_START,
221
+ * OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT.SCOPE_END + 1
222
+ * );
223
+ * ```
224
+ */
225
+ const OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT = {
226
+ PUBKEY_MODULUS_START: 0,
227
+ PUBKEY_MODULUS_END: 287,
228
+ DOMAIN_START: 288,
229
+ DOMAIN_END: 355,
230
+ SCOPE_START: 356,
231
+ SCOPE_END: 387,
232
+ NULLIFIER_START: 388,
233
+ NULLIFIER_END: 419,
234
+ };
201
235
 
202
236
  /**
203
237
  * Deep Link utilities for ZKProofport SDK
@@ -450,7 +484,7 @@ function validateProofRequest(request) {
450
484
  if (!request.circuit) {
451
485
  return { valid: false, error: 'Missing circuit type' };
452
486
  }
453
- if (!['coinbase_attestation', 'coinbase_country_attestation'].includes(request.circuit)) {
487
+ if (!['coinbase_attestation', 'coinbase_country_attestation', 'oidc_domain_attestation'].includes(request.circuit)) {
454
488
  return { valid: false, error: `Invalid circuit type: ${request.circuit}` };
455
489
  }
456
490
  if (!request.callbackUrl) {
@@ -480,6 +514,15 @@ function validateProofRequest(request) {
480
514
  return { valid: false, error: 'isIncluded is required and must be a boolean' };
481
515
  }
482
516
  }
517
+ else if (request.circuit === 'oidc_domain_attestation') {
518
+ const inputs = request.inputs;
519
+ if (!inputs.domain || typeof inputs.domain !== 'string' || inputs.domain.trim() === '') {
520
+ return { valid: false, error: 'domain is required and must be a non-empty string' };
521
+ }
522
+ if (!inputs.scope || typeof inputs.scope !== 'string' || inputs.scope.trim() === '') {
523
+ return { valid: false, error: 'scope is required and must be a non-empty string' };
524
+ }
525
+ }
483
526
  // Check expiry
484
527
  if (request.expiresAt && Date.now() > request.expiresAt) {
485
528
  return { valid: false, error: 'Request has expired' };
@@ -3781,6 +3824,10 @@ function extractScopeFromPublicInputs(publicInputsHex, circuit) {
3781
3824
  start = 86;
3782
3825
  end = 117;
3783
3826
  }
3827
+ else if (circuit === 'oidc_domain_attestation') {
3828
+ start = 356;
3829
+ end = 387;
3830
+ }
3784
3831
  else {
3785
3832
  start = 64;
3786
3833
  end = 95;
@@ -3814,6 +3861,10 @@ function extractNullifierFromPublicInputs(publicInputsHex, circuit) {
3814
3861
  start = 118;
3815
3862
  end = 149;
3816
3863
  }
3864
+ else if (circuit === 'oidc_domain_attestation') {
3865
+ start = 388;
3866
+ end = 419;
3867
+ }
3817
3868
  else {
3818
3869
  start = 96;
3819
3870
  end = 127;
@@ -4705,53 +4756,26 @@ class ProofportSDK {
4705
4756
  }
4706
4757
  return await response.json();
4707
4758
  }
4708
- /**
4709
- * Creates a proof request through the relay server.
4710
- *
4711
- * This is the recommended way to create proof requests. The relay server:
4712
- * - Issues a server-side requestId (validated by the mobile app)
4713
- * - Tracks request status in Redis
4714
- * - Builds the deep link with relay callback URL
4715
- * - Stores inputs hash for deep link integrity verification
4716
- *
4717
- * @param circuit - Circuit type identifier
4718
- * @param inputs - Circuit-specific inputs
4719
- * @param options - Request options (message, dappName, dappIcon, nonce)
4720
- * @returns Promise resolving to RelayProofRequest with requestId, deepLink, pollUrl
4721
- * @throws Error if signer not set or relay request fails
4722
- *
4723
- * @example
4724
- * ```typescript
4725
- * const sdk = ProofportSDK.create();
4726
- * sdk.setSigner(signer);
4727
- *
4728
- * const relay = await sdk.createRelayRequest('coinbase_attestation', {
4729
- * scope: 'myapp.com'
4730
- * }, { dappName: 'My DApp' });
4731
- *
4732
- * // Generate QR code from relay deep link
4733
- * const qr = await sdk.generateQRCode(relay.deepLink);
4734
- *
4735
- * // Wait for proof (WebSocket primary, polling fallback)
4736
- * const result = await sdk.waitForProof(relay.requestId);
4737
- * ```
4738
- */
4739
4759
  async createRelayRequest(circuit, inputs, options = {}) {
4740
- if (!this.signer) {
4741
- throw new Error('Signer not set. Call setSigner() first.');
4742
- }
4743
4760
  if (!this.relayUrl) {
4744
4761
  throw new Error('relayUrl is required. Set it in ProofportSDK config.');
4745
4762
  }
4746
- // Get challenge from relay and sign it
4747
- const { challenge } = await this.getChallenge();
4748
- const signature = await this.signer.signMessage(challenge);
4763
+ const needsSignature = ProofportSDK.WALLET_SIGNATURE_CIRCUITS.includes(circuit);
4764
+ if (needsSignature && !this.signer) {
4765
+ throw new Error('Signer not set. Call setSigner() first. Wallet signature is required for this circuit.');
4766
+ }
4767
+ // Get challenge + requestId from relay
4768
+ const { requestId, challenge } = await this.getChallenge();
4749
4769
  const body = {
4770
+ requestId,
4750
4771
  circuitId: circuit,
4751
4772
  inputs,
4752
4773
  challenge,
4753
- signature,
4754
4774
  };
4775
+ // Sign challenge for circuits that require wallet signature
4776
+ if (needsSignature && this.signer) {
4777
+ body.signature = await this.signer.signMessage(challenge);
4778
+ }
4755
4779
  if (options.message)
4756
4780
  body.message = options.message;
4757
4781
  if (options.dappName)
@@ -5028,6 +5052,42 @@ class ProofportSDK {
5028
5052
  return extractNullifierFromPublicInputs(publicInputs, circuit);
5029
5053
  }
5030
5054
  }
5055
+ /**
5056
+ * Creates a proof request through the relay server.
5057
+ *
5058
+ * This is the recommended way to create proof requests. The relay server:
5059
+ * - Issues a server-side requestId (validated by the mobile app)
5060
+ * - Tracks request status in Redis
5061
+ * - Builds the deep link with relay callback URL
5062
+ * - Stores inputs hash for deep link integrity verification
5063
+ *
5064
+ * @param circuit - Circuit type identifier
5065
+ * @param inputs - Circuit-specific inputs
5066
+ * @param options - Request options (message, dappName, dappIcon, nonce)
5067
+ * @returns Promise resolving to RelayProofRequest with requestId, deepLink, pollUrl
5068
+ * @throws Error if signer not set or relay request fails
5069
+ *
5070
+ * @example
5071
+ * ```typescript
5072
+ * const sdk = ProofportSDK.create();
5073
+ * sdk.setSigner(signer);
5074
+ *
5075
+ * const relay = await sdk.createRelayRequest('coinbase_attestation', {
5076
+ * scope: 'myapp.com'
5077
+ * }, { dappName: 'My DApp' });
5078
+ *
5079
+ * // Generate QR code from relay deep link
5080
+ * const qr = await sdk.generateQRCode(relay.deepLink);
5081
+ *
5082
+ * // Wait for proof (WebSocket primary, polling fallback)
5083
+ * const result = await sdk.waitForProof(relay.requestId);
5084
+ * ```
5085
+ */
5086
+ // Circuits that require wallet signature (used as circuit input)
5087
+ ProofportSDK.WALLET_SIGNATURE_CIRCUITS = [
5088
+ 'coinbase_attestation',
5089
+ 'coinbase_country_attestation',
5090
+ ];
5031
5091
 
5032
- export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, ProofportSDK, ProofportSDK as default, extractNullifierFromPublicInputs, extractScopeFromPublicInputs };
5092
+ export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, ProofportSDK, ProofportSDK as default, extractNullifierFromPublicInputs, extractScopeFromPublicInputs };
5033
5093
  //# sourceMappingURL=index.esm.js.map