@zkproofport-app/sdk 0.2.1 → 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 +87 -25
- package/dist/ProofportSDK.d.ts +23 -0
- package/dist/constants.d.ts +32 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.esm.js +220 -40
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +224 -39
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +220 -40
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.ts +18 -2
- package/dist/verifier.d.ts +19 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -122,7 +122,23 @@ const relay = await sdk.createRelayRequest('coinbase_country_attestation', {
|
|
|
122
122
|
});
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
|
|
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:
|
|
@@ -294,6 +327,43 @@ if (result.status === 'completed') {
|
|
|
294
327
|
const verification = await sdk.verifyResponseOnChain(response);
|
|
295
328
|
```
|
|
296
329
|
|
|
330
|
+
### Step 7: Extract Scope and Nullifier
|
|
331
|
+
|
|
332
|
+
After verification, extract the scope and nullifier from the public inputs:
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
if (result.status === 'completed') {
|
|
336
|
+
// Extract scope — the keccak256 hash of the scope string you provided
|
|
337
|
+
const scope = sdk.extractScope(result.publicInputs, result.circuit);
|
|
338
|
+
|
|
339
|
+
// Extract nullifier — a unique, deterministic hash per user + scope
|
|
340
|
+
// Same user with the same scope always produces the same nullifier
|
|
341
|
+
const nullifier = sdk.extractNullifier(result.publicInputs, result.circuit);
|
|
342
|
+
|
|
343
|
+
console.log('Scope:', scope); // '0x7a6b70726f...'
|
|
344
|
+
console.log('Nullifier:', nullifier); // '0xabc123...'
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
The **nullifier** serves as a privacy-preserving user identifier:
|
|
349
|
+
- Deterministic: same user + same scope = same nullifier (enables duplicate detection)
|
|
350
|
+
- Privacy-preserving: the wallet address (Coinbase) or email (OIDC) is never revealed
|
|
351
|
+
- Scope-bound: different scopes produce different nullifiers for the same user
|
|
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
|
+
|
|
355
|
+
**Standalone utility functions** are also available for use outside the SDK class:
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
import {
|
|
359
|
+
extractScopeFromPublicInputs,
|
|
360
|
+
extractNullifierFromPublicInputs,
|
|
361
|
+
} from '@zkproofport-app/sdk';
|
|
362
|
+
|
|
363
|
+
const scope = extractScopeFromPublicInputs(publicInputs, 'coinbase_attestation');
|
|
364
|
+
const nullifier = extractNullifierFromPublicInputs(publicInputs, 'coinbase_attestation');
|
|
365
|
+
```
|
|
366
|
+
|
|
297
367
|
## Complete Example
|
|
298
368
|
|
|
299
369
|
End-to-end integration using the relay flow:
|
|
@@ -354,21 +424,7 @@ async function verifyUser() {
|
|
|
354
424
|
|
|
355
425
|
## Configuration
|
|
356
426
|
|
|
357
|
-
`ProofportSDK.create()` returns a fully configured SDK instance. No manual configuration is needed
|
|
358
|
-
|
|
359
|
-
For advanced scenarios (e.g., custom verifier deployments), pass a `ProofportConfig`:
|
|
360
|
-
|
|
361
|
-
```typescript
|
|
362
|
-
const sdk = new ProofportSDK({
|
|
363
|
-
relayUrl: 'https://relay.zkproofport.app',
|
|
364
|
-
verifiers: {
|
|
365
|
-
coinbase_attestation: {
|
|
366
|
-
verifierAddress: '0x...',
|
|
367
|
-
chainId: 8453,
|
|
368
|
-
},
|
|
369
|
-
},
|
|
370
|
-
});
|
|
371
|
-
```
|
|
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.
|
|
372
428
|
|
|
373
429
|
## Types Reference
|
|
374
430
|
|
|
@@ -380,6 +436,7 @@ import type {
|
|
|
380
436
|
ProofRequestStatus,
|
|
381
437
|
CoinbaseKycInputs,
|
|
382
438
|
CoinbaseCountryInputs,
|
|
439
|
+
OidcDomainInputs,
|
|
383
440
|
CircuitInputs,
|
|
384
441
|
ProofRequest,
|
|
385
442
|
ProofResponse,
|
|
@@ -390,27 +447,35 @@ import type {
|
|
|
390
447
|
WalletSigner,
|
|
391
448
|
RelayProofRequest,
|
|
392
449
|
RelayProofResult,
|
|
393
|
-
SDKEnvironment,
|
|
394
450
|
} from '@zkproofport-app/sdk';
|
|
395
451
|
```
|
|
396
452
|
|
|
397
453
|
| Type | Description |
|
|
398
454
|
|------|-------------|
|
|
399
|
-
| `CircuitType` | `'coinbase_attestation' \| 'coinbase_country_attestation'` |
|
|
455
|
+
| `CircuitType` | `'coinbase_attestation' \| 'coinbase_country_attestation' \| 'oidc_domain_attestation'` |
|
|
400
456
|
| `ProofRequestStatus` | `'pending' \| 'completed' \| 'error' \| 'cancelled'` |
|
|
401
457
|
| `CoinbaseKycInputs` | Inputs for `coinbase_attestation` (`{ scope, userAddress?, rawTransaction? }`) |
|
|
402
458
|
| `CoinbaseCountryInputs` | Inputs for `coinbase_country_attestation` (`{ scope, countryList, isIncluded, ... }`) |
|
|
403
|
-
| `
|
|
459
|
+
| `OidcDomainInputs` | Inputs for `oidc_domain_attestation` (`{ domain, scope }`) |
|
|
460
|
+
| `CircuitInputs` | Union: `CoinbaseKycInputs \| CoinbaseCountryInputs \| OidcDomainInputs` |
|
|
404
461
|
| `ProofRequest` | Proof request object with `requestId`, `circuit`, `inputs`, metadata, and expiry |
|
|
405
462
|
| `ProofResponse` | Proof response with `status`, `proof`, `publicInputs`, `verifierAddress`, `chainId` |
|
|
406
463
|
| `QRCodeOptions` | QR customization: `width`, `margin`, `darkColor`, `lightColor`, `errorCorrectionLevel` |
|
|
407
464
|
| `VerifierContract` | Verifier contract info: `{ address, chainId, abi }` |
|
|
408
|
-
| `ProofportConfig` | SDK configuration
|
|
465
|
+
| `ProofportConfig` | SDK configuration (internal use — `ProofportSDK.create()` handles defaults) |
|
|
409
466
|
| `ChallengeResponse` | Challenge from relay: `{ challenge, expiresAt }` |
|
|
410
467
|
| `WalletSigner` | Signer interface: `{ signMessage(msg), getAddress() }` |
|
|
411
468
|
| `RelayProofRequest` | Relay response: `{ requestId, deepLink, status, pollUrl }` |
|
|
412
469
|
| `RelayProofResult` | Relay result: `{ requestId, status, proof?, publicInputs?, circuit?, error? }` |
|
|
413
|
-
|
|
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
|
+
```
|
|
414
479
|
|
|
415
480
|
## Error Handling
|
|
416
481
|
|
|
@@ -432,10 +497,7 @@ try {
|
|
|
432
497
|
|
|
433
498
|
## Networks
|
|
434
499
|
|
|
435
|
-
|
|
436
|
-
|---------|----------|--------|
|
|
437
|
-
| Base Mainnet | 8453 | Production |
|
|
438
|
-
| Base Sepolia | 84532 | Testnet |
|
|
500
|
+
Proofs are verified on **Base** (Ethereum L2). The SDK handles network configuration automatically — no manual setup required.
|
|
439
501
|
|
|
440
502
|
## Development
|
|
441
503
|
|
package/dist/ProofportSDK.d.ts
CHANGED
|
@@ -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;
|
|
@@ -893,5 +894,27 @@ export declare class ProofportSDK {
|
|
|
893
894
|
* ```
|
|
894
895
|
*/
|
|
895
896
|
extractScope(publicInputs: string[], circuit: CircuitType): string | null;
|
|
897
|
+
/**
|
|
898
|
+
* Extracts the nullifier (bytes32) from proof public inputs.
|
|
899
|
+
*
|
|
900
|
+
* The nullifier is a unique, deterministic hash derived from the user's attestation
|
|
901
|
+
* and scope. It serves as a privacy-preserving user identifier — the same user
|
|
902
|
+
* with the same scope always produces the same nullifier, enabling duplicate
|
|
903
|
+
* detection without revealing the wallet address.
|
|
904
|
+
*
|
|
905
|
+
* @param publicInputs - Array of hex-encoded field elements from proof result
|
|
906
|
+
* @param circuit - Circuit type that produced the public inputs
|
|
907
|
+
* @returns Nullifier as hex string (bytes32), or null if publicInputs too short
|
|
908
|
+
*
|
|
909
|
+
* @example
|
|
910
|
+
* ```typescript
|
|
911
|
+
* const result = await sdk.waitForProof(relay.requestId);
|
|
912
|
+
* if (result.status === 'completed') {
|
|
913
|
+
* const nullifier = sdk.extractNullifier(result.publicInputs, result.circuit);
|
|
914
|
+
* console.log('Nullifier:', nullifier); // '0xabc123...'
|
|
915
|
+
* }
|
|
916
|
+
* ```
|
|
917
|
+
*/
|
|
918
|
+
extractNullifier(publicInputs: string[], circuit: CircuitType): string | null;
|
|
896
919
|
}
|
|
897
920
|
export default ProofportSDK;
|
package/dist/constants.d.ts
CHANGED
|
@@ -145,6 +145,8 @@ export declare const COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT: {
|
|
|
145
145
|
readonly MERKLE_ROOT_END: 63;
|
|
146
146
|
readonly SCOPE_START: 64;
|
|
147
147
|
readonly SCOPE_END: 95;
|
|
148
|
+
readonly NULLIFIER_START: 96;
|
|
149
|
+
readonly NULLIFIER_END: 127;
|
|
148
150
|
};
|
|
149
151
|
/**
|
|
150
152
|
* Coinbase Country Attestation circuit public input layout (byte offsets).
|
|
@@ -178,4 +180,34 @@ export declare const COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT: {
|
|
|
178
180
|
readonly IS_INCLUDED: 85;
|
|
179
181
|
readonly SCOPE_START: 86;
|
|
180
182
|
readonly SCOPE_END: 117;
|
|
183
|
+
readonly NULLIFIER_START: 118;
|
|
184
|
+
readonly NULLIFIER_END: 149;
|
|
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;
|
|
181
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
|
|
11
|
-
* const sdk = ProofportSDK.create(
|
|
10
|
+
* // Initialize SDK
|
|
11
|
+
* const sdk = ProofportSDK.create();
|
|
12
12
|
*
|
|
13
13
|
* // Set wallet signer
|
|
14
14
|
* sdk.setSigner(signer);
|
|
@@ -26,4 +26,6 @@
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
export { ProofportSDK, default } from './ProofportSDK';
|
|
29
|
-
export
|
|
29
|
+
export { extractScopeFromPublicInputs, extractNullifierFromPublicInputs, } from './verifier';
|
|
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.
|
|
@@ -135,6 +141,97 @@ const DEFAULT_REQUEST_EXPIRY_MS = 10 * 60 * 1000;
|
|
|
135
141
|
* ```
|
|
136
142
|
*/
|
|
137
143
|
const MAX_QR_DATA_SIZE = 2953; // Version 40 with L error correction
|
|
144
|
+
/**
|
|
145
|
+
* Coinbase Attestation circuit public input layout (byte offsets).
|
|
146
|
+
* Defines the byte positions of each field in the flattened public inputs array.
|
|
147
|
+
*
|
|
148
|
+
* Public inputs are packed as bytes32 values:
|
|
149
|
+
* - signal_hash: bytes 0-31
|
|
150
|
+
* - merkle_root: bytes 32-63
|
|
151
|
+
* - scope: bytes 64-95
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const publicInputs = response.publicInputs;
|
|
156
|
+
* const signalHash = publicInputs.slice(
|
|
157
|
+
* COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT.SIGNAL_HASH_START,
|
|
158
|
+
* COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT.SIGNAL_HASH_END + 1
|
|
159
|
+
* );
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
const COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT = {
|
|
163
|
+
SIGNAL_HASH_START: 0,
|
|
164
|
+
SIGNAL_HASH_END: 31,
|
|
165
|
+
MERKLE_ROOT_START: 32,
|
|
166
|
+
MERKLE_ROOT_END: 63,
|
|
167
|
+
SCOPE_START: 64,
|
|
168
|
+
SCOPE_END: 95,
|
|
169
|
+
NULLIFIER_START: 96,
|
|
170
|
+
NULLIFIER_END: 127,
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Coinbase Country Attestation circuit public input layout (byte offsets).
|
|
174
|
+
* Defines the byte positions of each field in the flattened public inputs array.
|
|
175
|
+
*
|
|
176
|
+
* Public inputs are packed as bytes32 values:
|
|
177
|
+
* - signal_hash: bytes 0-31
|
|
178
|
+
* - merkle_root: bytes 32-63
|
|
179
|
+
* - country_list: bytes 64-83 (20 bytes for 10 countries)
|
|
180
|
+
* - country_list_length: byte 84
|
|
181
|
+
* - is_included: byte 85
|
|
182
|
+
* - scope: bytes 86-117
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```typescript
|
|
186
|
+
* const publicInputs = response.publicInputs;
|
|
187
|
+
* const countryList = publicInputs.slice(
|
|
188
|
+
* COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT.COUNTRY_LIST_START,
|
|
189
|
+
* COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT.COUNTRY_LIST_END + 1
|
|
190
|
+
* );
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
const COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT = {
|
|
194
|
+
SIGNAL_HASH_START: 0,
|
|
195
|
+
SIGNAL_HASH_END: 31,
|
|
196
|
+
MERKLE_ROOT_START: 32,
|
|
197
|
+
MERKLE_ROOT_END: 63,
|
|
198
|
+
COUNTRY_LIST_START: 64,
|
|
199
|
+
COUNTRY_LIST_END: 83,
|
|
200
|
+
COUNTRY_LIST_LENGTH: 84,
|
|
201
|
+
IS_INCLUDED: 85,
|
|
202
|
+
SCOPE_START: 86,
|
|
203
|
+
SCOPE_END: 117,
|
|
204
|
+
NULLIFIER_START: 118,
|
|
205
|
+
NULLIFIER_END: 149,
|
|
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
|
+
};
|
|
138
235
|
|
|
139
236
|
/**
|
|
140
237
|
* Deep Link utilities for ZKProofport SDK
|
|
@@ -387,7 +484,7 @@ function validateProofRequest(request) {
|
|
|
387
484
|
if (!request.circuit) {
|
|
388
485
|
return { valid: false, error: 'Missing circuit type' };
|
|
389
486
|
}
|
|
390
|
-
if (!['coinbase_attestation', 'coinbase_country_attestation'].includes(request.circuit)) {
|
|
487
|
+
if (!['coinbase_attestation', 'coinbase_country_attestation', 'oidc_domain_attestation'].includes(request.circuit)) {
|
|
391
488
|
return { valid: false, error: `Invalid circuit type: ${request.circuit}` };
|
|
392
489
|
}
|
|
393
490
|
if (!request.callbackUrl) {
|
|
@@ -417,6 +514,15 @@ function validateProofRequest(request) {
|
|
|
417
514
|
return { valid: false, error: 'isIncluded is required and must be a boolean' };
|
|
418
515
|
}
|
|
419
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
|
+
}
|
|
420
526
|
// Check expiry
|
|
421
527
|
if (request.expiresAt && Date.now() > request.expiresAt) {
|
|
422
528
|
return { valid: false, error: 'Request has expired' };
|
|
@@ -3718,6 +3824,10 @@ function extractScopeFromPublicInputs(publicInputsHex, circuit) {
|
|
|
3718
3824
|
start = 86;
|
|
3719
3825
|
end = 117;
|
|
3720
3826
|
}
|
|
3827
|
+
else if (circuit === 'oidc_domain_attestation') {
|
|
3828
|
+
start = 356;
|
|
3829
|
+
end = 387;
|
|
3830
|
+
}
|
|
3721
3831
|
else {
|
|
3722
3832
|
start = 64;
|
|
3723
3833
|
end = 95;
|
|
@@ -3727,6 +3837,43 @@ function extractScopeFromPublicInputs(publicInputsHex, circuit) {
|
|
|
3727
3837
|
const scopeFields = publicInputsHex.slice(start, end + 1);
|
|
3728
3838
|
return reconstructBytes32FromFields(scopeFields);
|
|
3729
3839
|
}
|
|
3840
|
+
/**
|
|
3841
|
+
* Extracts the nullifier (bytes32) from public inputs based on circuit type.
|
|
3842
|
+
*
|
|
3843
|
+
* The nullifier is a unique, deterministic hash derived from the user's attestation
|
|
3844
|
+
* and scope. It serves as a privacy-preserving user identifier — the same user
|
|
3845
|
+
* with the same scope always produces the same nullifier, enabling duplicate
|
|
3846
|
+
* detection without revealing the wallet address.
|
|
3847
|
+
*
|
|
3848
|
+
* @param publicInputsHex - Array of hex-encoded field elements
|
|
3849
|
+
* @param circuit - Circuit type (defaults to coinbase_attestation)
|
|
3850
|
+
* @returns Nullifier as hex string (bytes32), or null if publicInputs too short
|
|
3851
|
+
*
|
|
3852
|
+
* @example
|
|
3853
|
+
* ```typescript
|
|
3854
|
+
* const nullifier = extractNullifierFromPublicInputs(publicInputs, 'coinbase_attestation');
|
|
3855
|
+
* console.log(nullifier); // '0xabc123...'
|
|
3856
|
+
* ```
|
|
3857
|
+
*/
|
|
3858
|
+
function extractNullifierFromPublicInputs(publicInputsHex, circuit) {
|
|
3859
|
+
let start, end;
|
|
3860
|
+
if (circuit === 'coinbase_country_attestation') {
|
|
3861
|
+
start = 118;
|
|
3862
|
+
end = 149;
|
|
3863
|
+
}
|
|
3864
|
+
else if (circuit === 'oidc_domain_attestation') {
|
|
3865
|
+
start = 388;
|
|
3866
|
+
end = 419;
|
|
3867
|
+
}
|
|
3868
|
+
else {
|
|
3869
|
+
start = 96;
|
|
3870
|
+
end = 127;
|
|
3871
|
+
}
|
|
3872
|
+
if (publicInputsHex.length <= end)
|
|
3873
|
+
return null;
|
|
3874
|
+
const nullifierFields = publicInputsHex.slice(start, end + 1);
|
|
3875
|
+
return reconstructBytes32FromFields(nullifierFields);
|
|
3876
|
+
}
|
|
3730
3877
|
/** @internal Reconstruct a bytes32 value from 32 individual field elements */
|
|
3731
3878
|
function reconstructBytes32FromFields(fields) {
|
|
3732
3879
|
if (fields.length !== 32) {
|
|
@@ -4609,53 +4756,26 @@ class ProofportSDK {
|
|
|
4609
4756
|
}
|
|
4610
4757
|
return await response.json();
|
|
4611
4758
|
}
|
|
4612
|
-
/**
|
|
4613
|
-
* Creates a proof request through the relay server.
|
|
4614
|
-
*
|
|
4615
|
-
* This is the recommended way to create proof requests. The relay server:
|
|
4616
|
-
* - Issues a server-side requestId (validated by the mobile app)
|
|
4617
|
-
* - Tracks request status in Redis
|
|
4618
|
-
* - Builds the deep link with relay callback URL
|
|
4619
|
-
* - Stores inputs hash for deep link integrity verification
|
|
4620
|
-
*
|
|
4621
|
-
* @param circuit - Circuit type identifier
|
|
4622
|
-
* @param inputs - Circuit-specific inputs
|
|
4623
|
-
* @param options - Request options (message, dappName, dappIcon, nonce)
|
|
4624
|
-
* @returns Promise resolving to RelayProofRequest with requestId, deepLink, pollUrl
|
|
4625
|
-
* @throws Error if signer not set or relay request fails
|
|
4626
|
-
*
|
|
4627
|
-
* @example
|
|
4628
|
-
* ```typescript
|
|
4629
|
-
* const sdk = ProofportSDK.create();
|
|
4630
|
-
* sdk.setSigner(signer);
|
|
4631
|
-
*
|
|
4632
|
-
* const relay = await sdk.createRelayRequest('coinbase_attestation', {
|
|
4633
|
-
* scope: 'myapp.com'
|
|
4634
|
-
* }, { dappName: 'My DApp' });
|
|
4635
|
-
*
|
|
4636
|
-
* // Generate QR code from relay deep link
|
|
4637
|
-
* const qr = await sdk.generateQRCode(relay.deepLink);
|
|
4638
|
-
*
|
|
4639
|
-
* // Wait for proof (WebSocket primary, polling fallback)
|
|
4640
|
-
* const result = await sdk.waitForProof(relay.requestId);
|
|
4641
|
-
* ```
|
|
4642
|
-
*/
|
|
4643
4759
|
async createRelayRequest(circuit, inputs, options = {}) {
|
|
4644
|
-
if (!this.signer) {
|
|
4645
|
-
throw new Error('Signer not set. Call setSigner() first.');
|
|
4646
|
-
}
|
|
4647
4760
|
if (!this.relayUrl) {
|
|
4648
4761
|
throw new Error('relayUrl is required. Set it in ProofportSDK config.');
|
|
4649
4762
|
}
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
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();
|
|
4653
4769
|
const body = {
|
|
4770
|
+
requestId,
|
|
4654
4771
|
circuitId: circuit,
|
|
4655
4772
|
inputs,
|
|
4656
4773
|
challenge,
|
|
4657
|
-
signature,
|
|
4658
4774
|
};
|
|
4775
|
+
// Sign challenge for circuits that require wallet signature
|
|
4776
|
+
if (needsSignature && this.signer) {
|
|
4777
|
+
body.signature = await this.signer.signMessage(challenge);
|
|
4778
|
+
}
|
|
4659
4779
|
if (options.message)
|
|
4660
4780
|
body.message = options.message;
|
|
4661
4781
|
if (options.dappName)
|
|
@@ -4907,7 +5027,67 @@ class ProofportSDK {
|
|
|
4907
5027
|
extractScope(publicInputs, circuit) {
|
|
4908
5028
|
return extractScopeFromPublicInputs(publicInputs, circuit);
|
|
4909
5029
|
}
|
|
5030
|
+
/**
|
|
5031
|
+
* Extracts the nullifier (bytes32) from proof public inputs.
|
|
5032
|
+
*
|
|
5033
|
+
* The nullifier is a unique, deterministic hash derived from the user's attestation
|
|
5034
|
+
* and scope. It serves as a privacy-preserving user identifier — the same user
|
|
5035
|
+
* with the same scope always produces the same nullifier, enabling duplicate
|
|
5036
|
+
* detection without revealing the wallet address.
|
|
5037
|
+
*
|
|
5038
|
+
* @param publicInputs - Array of hex-encoded field elements from proof result
|
|
5039
|
+
* @param circuit - Circuit type that produced the public inputs
|
|
5040
|
+
* @returns Nullifier as hex string (bytes32), or null if publicInputs too short
|
|
5041
|
+
*
|
|
5042
|
+
* @example
|
|
5043
|
+
* ```typescript
|
|
5044
|
+
* const result = await sdk.waitForProof(relay.requestId);
|
|
5045
|
+
* if (result.status === 'completed') {
|
|
5046
|
+
* const nullifier = sdk.extractNullifier(result.publicInputs, result.circuit);
|
|
5047
|
+
* console.log('Nullifier:', nullifier); // '0xabc123...'
|
|
5048
|
+
* }
|
|
5049
|
+
* ```
|
|
5050
|
+
*/
|
|
5051
|
+
extractNullifier(publicInputs, circuit) {
|
|
5052
|
+
return extractNullifierFromPublicInputs(publicInputs, circuit);
|
|
5053
|
+
}
|
|
4910
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
|
+
];
|
|
4911
5091
|
|
|
4912
|
-
export { ProofportSDK, ProofportSDK as default };
|
|
5092
|
+
export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, ProofportSDK, ProofportSDK as default, extractNullifierFromPublicInputs, extractScopeFromPublicInputs };
|
|
4913
5093
|
//# sourceMappingURL=index.esm.js.map
|