@zkproofport-app/sdk 0.1.3-beta.1 → 0.2.1

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
@@ -5,17 +5,15 @@
5
5
 
6
6
  TypeScript SDK for requesting zero-knowledge proofs from the [ZKProofport](https://zkproofport.com) mobile app and verifying them on-chain.
7
7
 
8
- > **Beta** — Currently deployed on **Base Sepolia (testnet)** only. APIs may change before the stable release.
9
-
10
8
  ## How It Works
11
9
 
12
10
  ```
13
11
  ┌──────────────┐ ┌─────────┐ ┌──────────────┐ ┌──────────────────┐
14
12
  │ Your Web App │────>│ SDK │────>│ Relay Server │────>│ ZKProofport App │
15
13
  │ │ │ │ │ │ │ │
16
- │ │ │ login + │ issues ID, │ │ - Connects wallet│
17
- │ │ │ create │ tracks state │ │ - Fetches data │
18
- │ │ │ request │ │ │ - Generates proof│
14
+ │ │ │ setSigner │ issues ID, │ │ - Connects wallet│
15
+ │ │ │ + create │ tracks state │ │ - Fetches data │
16
+ │ │ │ request │ │ │ - Generates proof│
19
17
  └──────┬───────┘ └─────────┘ └──────┬───────┘ └────────┬─────────┘
20
18
  │ │ │
21
19
  │ │<─────────────────────┘
@@ -24,18 +22,18 @@ TypeScript SDK for requesting zero-knowledge proofs from the [ZKProofport](https
24
22
  │ v
25
23
  │ ┌──────────────────────────────────────────────────┐
26
24
  │ │ SDK receives result (WebSocket / polling) │
27
- │ │ (proof, publicInputs, nullifier, status)
25
+ │ │ (proof, publicInputs, status)
28
26
  │ └─────────────────────┬────────────────────────────┘
29
27
  │ │
30
28
  v v
31
29
  ┌──────────────┐ ┌──────────────────┐ ┌───────────────────┐
32
30
  │ Verify │────>│ On-chain verify │────>│ Access granted │
33
- │ on-chain │ │ (Base Sepolia) │ │ or denied │
31
+ │ on-chain │ │ (Base Mainnet) │ │ or denied │
34
32
  └──────────────┘ └──────────────────┘ └───────────────────┘
35
33
  ```
36
34
 
37
- 1. Your app authenticates with the relay and creates a proof request via the SDK
38
- 2. The relay issues a tracked request ID and returns a deep link
35
+ 1. Your app sets a wallet signer and creates a proof request via the SDK
36
+ 2. The SDK authenticates with the relay using challenge-signature (EIP-191) and gets a tracked request ID
39
37
  3. The SDK displays a QR code (desktop) or opens the deep link (mobile)
40
38
  4. The user opens the ZKProofport app, which generates the ZK proof
41
39
  5. The proof result flows back through the relay to your app via WebSocket (or polling)
@@ -44,11 +42,9 @@ TypeScript SDK for requesting zero-knowledge proofs from the [ZKProofport](https
44
42
  ## Installation
45
43
 
46
44
  ```bash
47
- npm install @zkproofport-app/sdk@beta
45
+ npm install @zkproofport-app/sdk
48
46
  ```
49
47
 
50
- > Published under the `beta` dist-tag. Use `@beta` to install.
51
-
52
48
  **Peer dependency (required for on-chain verification):**
53
49
 
54
50
  ```bash
@@ -59,12 +55,15 @@ npm install ethers
59
55
 
60
56
  ```typescript
61
57
  import { ProofportSDK } from '@zkproofport-app/sdk';
58
+ import { BrowserProvider } from 'ethers';
62
59
 
63
60
  // 1. Initialize
64
61
  const sdk = ProofportSDK.create();
65
62
 
66
- // 2. Authenticate
67
- await sdk.login({ clientId: 'your-client-id', apiKey: 'your-api-key' });
63
+ // 2. Set wallet signer (ethers v6 Signer)
64
+ const provider = new BrowserProvider(window.ethereum);
65
+ const signer = await provider.getSigner();
66
+ sdk.setSigner(signer);
68
67
 
69
68
  // 3. Create proof request via relay
70
69
  const relay = await sdk.createRelayRequest('coinbase_attestation', {
@@ -97,7 +96,7 @@ Proves that a user has completed Coinbase KYC identity verification without reve
97
96
 
98
97
  | Field | Type | Required | Description |
99
98
  |-------|------|----------|-------------|
100
- | `scope` | `string` | Yes | Application-specific identifier (e.g., your domain). Generates a unique nullifier per app to prevent cross-app tracking. |
99
+ | `scope` | `string` | Yes | Application-specific identifier (e.g., your domain). Ensures proof uniqueness per app. |
101
100
 
102
101
  ```typescript
103
102
  const relay = await sdk.createRelayRequest('coinbase_attestation', {
@@ -135,29 +134,47 @@ import { ProofportSDK } from '@zkproofport-app/sdk';
135
134
  const sdk = ProofportSDK.create();
136
135
  ```
137
136
 
138
- `ProofportSDK.create()` returns an SDK instance pre-configured with the relay server, verifier contracts, and nullifier registry. No manual configuration is needed.
137
+ `ProofportSDK.create()` returns an SDK instance pre-configured with the relay server and verifier contracts. No configuration needed.
139
138
 
140
- ### Step 2: Authenticate
139
+ ### Step 2: Set Wallet Signer
141
140
 
142
- Authenticate with the relay server using your client credentials. The SDK stores the JWT token internally for subsequent requests.
141
+ The SDK uses challenge-signature authentication (EIP-191). Set a wallet signer that can sign messages:
143
142
 
144
143
  ```typescript
145
- await sdk.login({
146
- clientId: process.env.CLIENT_ID,
147
- apiKey: process.env.API_KEY,
148
- });
144
+ import { BrowserProvider } from 'ethers';
145
+
146
+ const provider = new BrowserProvider(window.ethereum);
147
+ const signer = await provider.getSigner();
148
+ sdk.setSigner(signer);
149
+ ```
150
+
151
+ The `WalletSigner` interface requires two methods:
149
152
 
150
- // Check auth status at any time
151
- sdk.isAuthenticated(); // true
152
- sdk.getAuthToken(); // AuthToken object
153
- sdk.logout(); // Clear stored token
153
+ ```typescript
154
+ interface WalletSigner {
155
+ signMessage(message: string): Promise<string>;
156
+ getAddress(): Promise<string>;
157
+ }
154
158
  ```
155
159
 
156
- Get your `clientId` and `apiKey` from the [ZKProofport Dashboard](https://zkproofport.com).
160
+ Any ethers v5/v6 `Signer` is compatible.
161
+
162
+ #### About challenge-signature
163
+
164
+ 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.
165
+
166
+ For server-side or headless environments, using an ephemeral random wallet is fine. A persistent wallet (fixed private key) is **not recommended** as it adds unnecessary key management overhead with no functional benefit.
167
+
168
+ ```typescript
169
+ import { Wallet } from 'ethers';
170
+
171
+ // Server-side: ephemeral wallet per request
172
+ sdk.setSigner(Wallet.createRandom());
173
+ ```
157
174
 
158
175
  ### Step 3: Create Request (via Relay)
159
176
 
160
- `createRelayRequest` is the **recommended** method. The relay server issues a tracked request ID, manages credits, and builds the deep link with the relay callback URL.
177
+ `createRelayRequest` authenticates with the relay (challenge-signature), creates a tracked proof request, and returns a deep link.
161
178
 
162
179
  ```typescript
163
180
  const relay = await sdk.createRelayRequest('coinbase_attestation', {
@@ -201,7 +218,7 @@ await sdk.renderQRCodeToCanvas(canvasElement, relay.deepLink, { width: 400 });
201
218
  const { size, withinLimit } = sdk.checkQRCodeSize(relay.deepLink);
202
219
  ```
203
220
 
204
- **Mobile:** On mobile browsers, you can redirect directly to the deep link instead of showing a QR code:
221
+ **Mobile:** On mobile browsers, redirect directly to the deep link instead of showing a QR code:
205
222
 
206
223
  ```typescript
207
224
  if (ProofportSDK.isMobile()) {
@@ -277,44 +294,22 @@ if (result.status === 'completed') {
277
294
  const verification = await sdk.verifyResponseOnChain(response);
278
295
  ```
279
296
 
280
- ### Step 7: Check Nullifier (Optional)
281
-
282
- Nullifiers prevent the same user from submitting duplicate proofs for the same scope. The SDK is pre-configured with the nullifier registry contract.
283
-
284
- ```typescript
285
- // Extract nullifier from proof result
286
- const nullifier = sdk.extractNullifier(result.publicInputs, result.circuit);
287
- const scope = sdk.extractScope(result.publicInputs, result.circuit);
288
-
289
- // Check if already used
290
- const isDuplicate = await sdk.checkNullifier(nullifier);
291
- if (isDuplicate) {
292
- console.log('This user has already submitted a proof for this scope');
293
- }
294
-
295
- // Get registration details
296
- const info = await sdk.getNullifierDetails(nullifier);
297
- if (info) {
298
- console.log('Registered at:', new Date(info.registeredAt * 1000));
299
- console.log('Circuit:', info.circuitId);
300
- console.log('Scope:', info.scope);
301
- }
302
- ```
303
-
304
297
  ## Complete Example
305
298
 
306
299
  End-to-end integration using the relay flow:
307
300
 
308
301
  ```typescript
309
302
  import { ProofportSDK } from '@zkproofport-app/sdk';
303
+ import { BrowserProvider } from 'ethers';
310
304
 
311
305
  async function verifyUser() {
312
- // Initialize and authenticate
306
+ // Initialize
313
307
  const sdk = ProofportSDK.create();
314
- await sdk.login({
315
- clientId: process.env.CLIENT_ID,
316
- apiKey: process.env.API_KEY,
317
- });
308
+
309
+ // Set wallet signer
310
+ const provider = new BrowserProvider(window.ethereum);
311
+ const signer = await provider.getSigner();
312
+ sdk.setSigner(signer);
318
313
 
319
314
  // Create proof request via relay
320
315
  const relay = await sdk.createRelayRequest('coinbase_attestation', {
@@ -357,24 +352,24 @@ async function verifyUser() {
357
352
  }
358
353
  ```
359
354
 
360
- ## Advanced Usage
355
+ ## Configuration
361
356
 
362
- ### Nullifier Duplicate Detection
357
+ `ProofportSDK.create()` returns a fully configured SDK instance. No manual configuration is needed for standard usage.
363
358
 
364
- All nullifier operations are instance methods on the SDK:
359
+ For advanced scenarios (e.g., custom verifier deployments), pass a `ProofportConfig`:
365
360
 
366
361
  ```typescript
367
- const nullifier = sdk.extractNullifier(publicInputs, circuit);
368
- const isDuplicate = await sdk.checkNullifier(nullifier);
369
- const details = await sdk.getNullifierDetails(nullifier);
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
+ });
370
371
  ```
371
372
 
372
- ## Configuration
373
-
374
- `ProofportSDK.create()` returns a fully configured SDK instance. No manual configuration is needed for standard usage.
375
-
376
- For advanced scenarios (e.g., custom verifier deployments), see the `ProofportConfig` type exported by the SDK.
377
-
378
373
  ## Types Reference
379
374
 
380
375
  All 15 exported types:
@@ -391,8 +386,8 @@ import type {
391
386
  QRCodeOptions,
392
387
  VerifierContract,
393
388
  ProofportConfig,
394
- AuthCredentials,
395
- AuthToken,
389
+ ChallengeResponse,
390
+ WalletSigner,
396
391
  RelayProofRequest,
397
392
  RelayProofResult,
398
393
  SDKEnvironment,
@@ -407,31 +402,25 @@ import type {
407
402
  | `CoinbaseCountryInputs` | Inputs for `coinbase_country_attestation` (`{ scope, countryList, isIncluded, ... }`) |
408
403
  | `CircuitInputs` | Union: `CoinbaseKycInputs \| CoinbaseCountryInputs` |
409
404
  | `ProofRequest` | Proof request object with `requestId`, `circuit`, `inputs`, metadata, and expiry |
410
- | `ProofResponse` | Proof response with `status`, `proof`, `publicInputs`, `nullifier`, `verifierAddress`, `chainId` |
405
+ | `ProofResponse` | Proof response with `status`, `proof`, `publicInputs`, `verifierAddress`, `chainId` |
411
406
  | `QRCodeOptions` | QR customization: `width`, `margin`, `darkColor`, `lightColor`, `errorCorrectionLevel` |
412
407
  | `VerifierContract` | Verifier contract info: `{ address, chainId, abi }` |
413
- | `ProofportConfig` | SDK configuration (for advanced usage only) |
414
- | `AuthCredentials` | Login credentials: `{ clientId, apiKey }` |
415
- | `AuthToken` | JWT token: `{ token, clientId, dappId, tier, expiresIn, expiresAt }` |
408
+ | `ProofportConfig` | SDK configuration: `{ scheme?, relayUrl?, verifiers? }` |
409
+ | `ChallengeResponse` | Challenge from relay: `{ challenge, expiresAt }` |
410
+ | `WalletSigner` | Signer interface: `{ signMessage(msg), getAddress() }` |
416
411
  | `RelayProofRequest` | Relay response: `{ requestId, deepLink, status, pollUrl }` |
417
- | `RelayProofResult` | Relay result: `{ requestId, status, proof?, publicInputs?, nullifier?, circuit?, error? }` |
418
- | `SDKEnvironment` | SDK environment preset |
412
+ | `RelayProofResult` | Relay result: `{ requestId, status, proof?, publicInputs?, circuit?, error? }` |
413
+ | `SDKEnvironment` | Environment preset (not needed for normal usage) |
419
414
 
420
415
  ## Error Handling
421
416
 
422
- All async SDK methods throw standard `Error` objects. Common error scenarios:
417
+ All async SDK methods throw standard `Error` objects:
423
418
 
424
419
  ```typescript
425
- try {
426
- await sdk.login({ clientId: 'bad-id', apiKey: 'bad-key' });
427
- } catch (err) {
428
- // "Authentication failed: HTTP 401"
429
- }
430
-
431
420
  try {
432
421
  await sdk.createRelayRequest('coinbase_attestation', { scope: 'app.com' });
433
422
  } catch (err) {
434
- // "Not authenticated. Call login() first."
423
+ // "Signer not set. Call setSigner() first."
435
424
  }
436
425
 
437
426
  try {
@@ -441,19 +430,12 @@ try {
441
430
  }
442
431
  ```
443
432
 
444
- Relay request validation errors:
433
+ ## Networks
445
434
 
446
- ```typescript
447
- try {
448
- await sdk.createRelayRequest('coinbase_country_attestation', {
449
- scope: 'app.com',
450
- countryList: [],
451
- isIncluded: true,
452
- });
453
- } catch (err) {
454
- // Relay or input validation error
455
- }
456
- ```
435
+ | Network | Chain ID | Status |
436
+ |---------|----------|--------|
437
+ | Base Mainnet | 8453 | Production |
438
+ | Base Sepolia | 84532 | Testnet |
457
439
 
458
440
  ## Development
459
441
 
@@ -42,7 +42,6 @@ export declare class ProofportSDK {
42
42
  private pendingRequests;
43
43
  private signer;
44
44
  private relayUrl;
45
- private nullifierRegistry?;
46
45
  private socket;
47
46
  /**
48
47
  * Creates a new ProofportSDK instance.
@@ -874,27 +873,6 @@ export declare class ProofportSDK {
874
873
  * Disconnects the Socket.IO connection if active.
875
874
  */
876
875
  disconnect(): void;
877
- /**
878
- * Extracts the nullifier from proof public inputs.
879
- *
880
- * The nullifier is a bytes32 value derived from the user's address and scope,
881
- * used to prevent duplicate proof submissions. Each user+scope combination
882
- * produces a unique nullifier.
883
- *
884
- * @param publicInputs - Array of public input hex strings from proof response
885
- * @param circuit - Circuit type to determine field positions
886
- * @returns Nullifier as hex string (0x...), or null if inputs are insufficient
887
- *
888
- * @example
889
- * ```typescript
890
- * const result = await sdk.waitForProof(relay.requestId);
891
- * if (result.status === 'completed') {
892
- * const nullifier = sdk.extractNullifier(result.publicInputs, result.circuit);
893
- * console.log('Nullifier:', nullifier);
894
- * }
895
- * ```
896
- */
897
- extractNullifier(publicInputs: string[], circuit: CircuitType): string | null;
898
876
  /**
899
877
  * Extracts the scope from proof public inputs.
900
878
  *
@@ -915,57 +893,5 @@ export declare class ProofportSDK {
915
893
  * ```
916
894
  */
917
895
  extractScope(publicInputs: string[], circuit: CircuitType): string | null;
918
- /**
919
- * Checks if a nullifier is already registered on-chain.
920
- *
921
- * Queries the ZKProofportNullifierRegistry contract to determine if the
922
- * nullifier has been used before. Used to prevent duplicate proof submissions.
923
- *
924
- * Requires `nullifierRegistry` in SDK config.
925
- *
926
- * @param nullifier - Nullifier hex string from extractNullifier()
927
- * @param provider - Optional ethers provider (defaults to public RPC for configured chain)
928
- * @returns True if nullifier is already registered
929
- * @throws Error if nullifierRegistry is not configured
930
- *
931
- * @example
932
- * ```typescript
933
- * const sdk = ProofportSDK.create({
934
- * relayUrl: 'https://relay.zkproofport.app',
935
- * nullifierRegistry: { address: '0x...', chainId: 8453 }
936
- * });
937
- *
938
- * const nullifier = sdk.extractNullifier(publicInputs, circuit);
939
- * const isDuplicate = await sdk.checkNullifier(nullifier);
940
- * ```
941
- */
942
- checkNullifier(nullifier: string, provider?: any): Promise<boolean>;
943
- /**
944
- * Gets detailed information about a registered nullifier from on-chain registry.
945
- *
946
- * Retrieves the registration timestamp, scope, and circuit ID for a nullifier.
947
- * Returns null if the nullifier is not registered.
948
- *
949
- * Requires `nullifierRegistry` in SDK config.
950
- *
951
- * @param nullifier - Nullifier hex string from extractNullifier()
952
- * @param provider - Optional ethers provider (defaults to public RPC for configured chain)
953
- * @returns Nullifier info or null if not registered
954
- * @throws Error if nullifierRegistry is not configured
955
- *
956
- * @example
957
- * ```typescript
958
- * const info = await sdk.getNullifierDetails(nullifier);
959
- * if (info) {
960
- * console.log('Registered at:', new Date(info.registeredAt * 1000));
961
- * console.log('Circuit:', info.circuitId);
962
- * }
963
- * ```
964
- */
965
- getNullifierDetails(nullifier: string, provider?: any): Promise<{
966
- registeredAt: number;
967
- scope: string;
968
- circuitId: string;
969
- } | null>;
970
896
  }
971
897
  export default ProofportSDK;
@@ -128,7 +128,6 @@ export declare const MAX_QR_DATA_SIZE = 2953;
128
128
  * - signal_hash: bytes 0-31
129
129
  * - merkle_root: bytes 32-63
130
130
  * - scope: bytes 64-95
131
- * - nullifier: bytes 96-127
132
131
  *
133
132
  * @example
134
133
  * ```typescript
@@ -146,8 +145,6 @@ export declare const COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT: {
146
145
  readonly MERKLE_ROOT_END: 63;
147
146
  readonly SCOPE_START: 64;
148
147
  readonly SCOPE_END: 95;
149
- readonly NULLIFIER_START: 96;
150
- readonly NULLIFIER_END: 127;
151
148
  };
152
149
  /**
153
150
  * Coinbase Country Attestation circuit public input layout (byte offsets).
@@ -160,7 +157,6 @@ export declare const COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT: {
160
157
  * - country_list_length: byte 84
161
158
  * - is_included: byte 85
162
159
  * - scope: bytes 86-117
163
- * - nullifier: bytes 118-149
164
160
  *
165
161
  * @example
166
162
  * ```typescript
@@ -182,42 +178,4 @@ export declare const COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT: {
182
178
  readonly IS_INCLUDED: 85;
183
179
  readonly SCOPE_START: 86;
184
180
  readonly SCOPE_END: 117;
185
- readonly NULLIFIER_START: 118;
186
- readonly NULLIFIER_END: 149;
187
181
  };
188
- /**
189
- * V1 NullifierRegistry contract ABI (DEPRECATED).
190
- *
191
- * This is the legacy nullifier registry interface.
192
- * Use ZKPROOFPORT_NULLIFIER_REGISTRY_ABI for new integrations.
193
- *
194
- * @deprecated Use ZKPROOFPORT_NULLIFIER_REGISTRY_ABI instead. This is the V1 NullifierRegistry ABI.
195
- */
196
- export declare const NULLIFIER_REGISTRY_ABI: string[];
197
- /**
198
- * ZKProofportNullifierRegistry contract ABI (V2).
199
- *
200
- * This is the current nullifier registry interface with relayer-only registration.
201
- * Public view functions allow checking nullifier status and verifying proofs without registration.
202
- *
203
- * Key functions:
204
- * - `isNullifierRegistered`: Check if a nullifier has been used
205
- * - `getNullifierInfo`: Get registration details for a nullifier
206
- * - `verifyOnly`: Verify a proof without registering the nullifier
207
- *
208
- * Note: Registration functions (verifyAndRegister) are relayer-only and not exposed in this ABI.
209
- *
210
- * @example
211
- * ```typescript
212
- * import { Contract } from 'ethers';
213
- *
214
- * const registry = new Contract(
215
- * registryAddress,
216
- * ZKPROOFPORT_NULLIFIER_REGISTRY_ABI,
217
- * provider
218
- * );
219
- *
220
- * const isUsed = await registry.isNullifierRegistered(nullifier);
221
- * ```
222
- */
223
- export declare const ZKPROOFPORT_NULLIFIER_REGISTRY_ABI: string[];