@zkproofport-app/sdk 0.1.2-beta.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ZKProofport
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,469 @@
1
+ # @zkproofport-app/sdk
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@zkproofport-app/sdk)](https://www.npmjs.com/package/@zkproofport-app/sdk)
4
+ [![license](https://img.shields.io/npm/l/@zkproofport-app/sdk)](./LICENSE)
5
+
6
+ TypeScript SDK for requesting zero-knowledge proofs from the [ZKProofport](https://zkproofport.com) mobile app and verifying them on-chain.
7
+
8
+ > **Beta** — Currently deployed on **Base Sepolia (testnet)** only. APIs may change before the stable release.
9
+
10
+ ## How It Works
11
+
12
+ ```
13
+ ┌──────────────┐ ┌─────────┐ ┌──────────────┐ ┌──────────────────┐
14
+ │ Your Web App │────>│ SDK │────>│ Relay Server │────>│ ZKProofport App │
15
+ │ │ │ │ │ │ │ │
16
+ │ │ │ login + │ │ issues ID, │ │ - Connects wallet│
17
+ │ │ │ create │ │ tracks state │ │ - Fetches data │
18
+ │ │ │ request │ │ │ │ - Generates proof│
19
+ └──────┬───────┘ └─────────┘ └──────┬───────┘ └────────┬─────────┘
20
+ │ │ │
21
+ │ │<─────────────────────┘
22
+ │ ┌─────────────────────────────────┘ Proof result via
23
+ │ │ relay callback
24
+ │ v
25
+ │ ┌──────────────────────────────────────────────────┐
26
+ │ │ SDK receives result (WebSocket / polling) │
27
+ │ │ (proof, publicInputs, nullifier, status) │
28
+ │ └─────────────────────┬────────────────────────────┘
29
+ │ │
30
+ v v
31
+ ┌──────────────┐ ┌──────────────────┐ ┌───────────────────┐
32
+ │ Verify │────>│ On-chain verify │────>│ Access granted │
33
+ │ on-chain │ │ (Base Sepolia) │ │ or denied │
34
+ └──────────────┘ └──────────────────┘ └───────────────────┘
35
+ ```
36
+
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
39
+ 3. The SDK displays a QR code (desktop) or opens the deep link (mobile)
40
+ 4. The user opens the ZKProofport app, which generates the ZK proof
41
+ 5. The proof result flows back through the relay to your app via WebSocket (or polling)
42
+ 6. Your app verifies the proof on-chain
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ npm install @zkproofport-app/sdk@beta
48
+ ```
49
+
50
+ > Published under the `beta` dist-tag. Use `@beta` to install.
51
+
52
+ **Peer dependency (required for on-chain verification):**
53
+
54
+ ```bash
55
+ npm install ethers
56
+ ```
57
+
58
+ ## Quick Start
59
+
60
+ ```typescript
61
+ import { ProofportSDK } from '@zkproofport-app/sdk';
62
+
63
+ // 1. Initialize
64
+ const sdk = ProofportSDK.create();
65
+
66
+ // 2. Authenticate
67
+ await sdk.login({ clientId: 'your-client-id', apiKey: 'your-api-key' });
68
+
69
+ // 3. Create proof request via relay
70
+ const relay = await sdk.createRelayRequest('coinbase_attestation', {
71
+ scope: 'myapp.com',
72
+ });
73
+
74
+ // 4. Show QR code to user
75
+ const qrDataUrl = await sdk.generateQRCode(relay.deepLink);
76
+ document.getElementById('qr').src = qrDataUrl;
77
+
78
+ // 5. Wait for proof (WebSocket primary, HTTP polling fallback)
79
+ const result = await sdk.waitForProof(relay.requestId);
80
+
81
+ if (result.status === 'completed') {
82
+ // 6. Verify on-chain
83
+ const verification = await sdk.verifyOnChain(
84
+ result.circuit,
85
+ result.proof,
86
+ result.publicInputs
87
+ );
88
+ console.log('Valid:', verification.valid);
89
+ }
90
+ ```
91
+
92
+ ## Supported Circuits
93
+
94
+ ### `coinbase_attestation`
95
+
96
+ Proves that a user has completed Coinbase KYC identity verification without revealing any personal information.
97
+
98
+ | Field | Type | Required | Description |
99
+ |-------|------|----------|-------------|
100
+ | `scope` | `string` | Yes | Application-specific identifier (e.g., your domain). Generates a unique nullifier per app to prevent cross-app tracking. |
101
+
102
+ ```typescript
103
+ const relay = await sdk.createRelayRequest('coinbase_attestation', {
104
+ scope: 'myapp.com',
105
+ });
106
+ ```
107
+
108
+ ### `coinbase_country_attestation`
109
+
110
+ Proves a user's country based on Coinbase verification, supporting inclusion and exclusion checks, without revealing the actual country.
111
+
112
+ | Field | Type | Required | Description |
113
+ |-------|------|----------|-------------|
114
+ | `scope` | `string` | Yes | Application-specific identifier |
115
+ | `countryList` | `string[]` | Yes | ISO 3166-1 alpha-2 country codes (e.g., `['US', 'KR']`) |
116
+ | `isIncluded` | `boolean` | Yes | `true` = prove user IS from listed countries; `false` = prove user is NOT |
117
+
118
+ ```typescript
119
+ const relay = await sdk.createRelayRequest('coinbase_country_attestation', {
120
+ scope: 'myapp.com',
121
+ countryList: ['US', 'KR'],
122
+ isIncluded: true,
123
+ });
124
+ ```
125
+
126
+ > The ZKProofport mobile app handles wallet connection and attestation data retrieval automatically. You only provide the inputs above.
127
+
128
+ ## Integration Guide
129
+
130
+ ### Step 1: Initialize
131
+
132
+ ```typescript
133
+ import { ProofportSDK } from '@zkproofport-app/sdk';
134
+
135
+ const sdk = ProofportSDK.create();
136
+ ```
137
+
138
+ `ProofportSDK.create()` returns an SDK instance pre-configured with the relay server, verifier contracts, and nullifier registry. No manual configuration is needed.
139
+
140
+ ### Step 2: Authenticate
141
+
142
+ Authenticate with the relay server using your client credentials. The SDK stores the JWT token internally for subsequent requests.
143
+
144
+ ```typescript
145
+ await sdk.login({
146
+ clientId: process.env.CLIENT_ID,
147
+ apiKey: process.env.API_KEY,
148
+ });
149
+
150
+ // Check auth status at any time
151
+ sdk.isAuthenticated(); // true
152
+ sdk.getAuthToken(); // AuthToken object
153
+ sdk.logout(); // Clear stored token
154
+ ```
155
+
156
+ Get your `clientId` and `apiKey` from the [ZKProofport Dashboard](https://zkproofport.com).
157
+
158
+ ### Step 3: Create Request (via Relay)
159
+
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.
161
+
162
+ ```typescript
163
+ const relay = await sdk.createRelayRequest('coinbase_attestation', {
164
+ scope: 'myapp.com',
165
+ }, {
166
+ dappName: 'My DApp',
167
+ dappIcon: 'https://myapp.com/icon.png',
168
+ message: 'Verify your identity to continue',
169
+ nonce: 'unique-nonce-123', // Optional: replay prevention
170
+ });
171
+
172
+ // relay.requestId — Relay-issued UUID
173
+ // relay.deepLink — Deep link URL for the mobile app
174
+ // relay.status — 'pending'
175
+ // relay.pollUrl — Relative URL for HTTP polling
176
+ ```
177
+
178
+ ### Step 4: Display QR Code
179
+
180
+ Generate a QR code from the relay deep link for the user to scan with the ZKProofport mobile app:
181
+
182
+ ```typescript
183
+ const qrDataUrl = await sdk.generateQRCode(relay.deepLink, {
184
+ width: 400,
185
+ darkColor: '#1a1a1a',
186
+ margin: 4,
187
+ });
188
+ document.getElementById('qr').src = qrDataUrl;
189
+ ```
190
+
191
+ **Other QR formats:**
192
+
193
+ ```typescript
194
+ // SVG string
195
+ const svg = await sdk.generateQRCodeSVG(relay.deepLink);
196
+
197
+ // Render to canvas
198
+ await sdk.renderQRCodeToCanvas(canvasElement, relay.deepLink, { width: 400 });
199
+
200
+ // Check if data fits QR limits
201
+ const { size, withinLimit } = sdk.checkQRCodeSize(relay.deepLink);
202
+ ```
203
+
204
+ **Mobile:** On mobile browsers, you can redirect directly to the deep link instead of showing a QR code:
205
+
206
+ ```typescript
207
+ if (ProofportSDK.isMobile()) {
208
+ window.location.href = relay.deepLink;
209
+ }
210
+ ```
211
+
212
+ ### Step 5: Wait for Proof
213
+
214
+ **`waitForProof` (recommended)** — Uses WebSocket (Socket.IO) for instant delivery, with automatic HTTP polling fallback if `socket.io-client` is not installed or connection fails.
215
+
216
+ ```typescript
217
+ const result = await sdk.waitForProof(relay.requestId, {
218
+ timeoutMs: 300000, // 5 minutes (default)
219
+ onStatusChange: (update) => {
220
+ console.log('Status:', update.status);
221
+ },
222
+ });
223
+ ```
224
+
225
+ **Alternative: Subscribe to real-time updates directly:**
226
+
227
+ ```typescript
228
+ const unsubscribe = await sdk.subscribe(relay.requestId, {
229
+ onStatus: (data) => console.log('Status:', data.status),
230
+ onResult: (result) => {
231
+ if (result.status === 'completed') {
232
+ console.log('Proof received:', result.proof);
233
+ }
234
+ unsubscribe();
235
+ },
236
+ onError: (err) => console.error(err.error),
237
+ });
238
+ ```
239
+
240
+ **Alternative: HTTP polling only:**
241
+
242
+ ```typescript
243
+ // Single poll
244
+ const result = await sdk.pollResult(relay.requestId);
245
+
246
+ // Poll until terminal state
247
+ const result = await sdk.waitForResult(relay.requestId, {
248
+ intervalMs: 2000,
249
+ timeoutMs: 300000,
250
+ onStatusChange: (result) => console.log(result.status),
251
+ });
252
+ ```
253
+
254
+ ### Step 6: Verify On-Chain
255
+
256
+ Verify the proof cryptographically by calling the deployed Solidity verifier contract.
257
+
258
+ ```typescript
259
+ if (result.status === 'completed') {
260
+ const verification = await sdk.verifyOnChain(
261
+ result.circuit,
262
+ result.proof,
263
+ result.publicInputs
264
+ );
265
+
266
+ if (verification.valid) {
267
+ console.log('Proof verified on-chain!');
268
+ } else {
269
+ console.error('Verification failed:', verification.error);
270
+ }
271
+ }
272
+ ```
273
+
274
+ **Or verify from a `ProofResponse` object:**
275
+
276
+ ```typescript
277
+ const verification = await sdk.verifyResponseOnChain(response);
278
+ ```
279
+
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
+ ## Complete Example
305
+
306
+ End-to-end integration using the relay flow:
307
+
308
+ ```typescript
309
+ import { ProofportSDK } from '@zkproofport-app/sdk';
310
+
311
+ async function verifyUser() {
312
+ // Initialize and authenticate
313
+ const sdk = ProofportSDK.create();
314
+ await sdk.login({
315
+ clientId: process.env.CLIENT_ID,
316
+ apiKey: process.env.API_KEY,
317
+ });
318
+
319
+ // Create proof request via relay
320
+ const relay = await sdk.createRelayRequest('coinbase_attestation', {
321
+ scope: 'myapp.com',
322
+ }, {
323
+ dappName: 'My DApp',
324
+ message: 'Verify your identity',
325
+ });
326
+
327
+ // Display QR code
328
+ const qrDataUrl = await sdk.generateQRCode(relay.deepLink, { width: 400 });
329
+ document.getElementById('qr-image').src = qrDataUrl;
330
+ document.getElementById('status').textContent = 'Scan the QR code with ZKProofport';
331
+
332
+ // Wait for proof result
333
+ const result = await sdk.waitForProof(relay.requestId, {
334
+ onStatusChange: (update) => {
335
+ document.getElementById('status').textContent = `Status: ${update.status}`;
336
+ },
337
+ });
338
+
339
+ if (result.status === 'completed') {
340
+ // Verify on-chain
341
+ const verification = await sdk.verifyOnChain(
342
+ result.circuit,
343
+ result.proof,
344
+ result.publicInputs
345
+ );
346
+
347
+ if (verification.valid) {
348
+ document.getElementById('status').textContent = 'Identity verified!';
349
+ // Grant access to your application
350
+ }
351
+ } else {
352
+ document.getElementById('status').textContent = `Failed: ${result.error}`;
353
+ }
354
+
355
+ // Cleanup
356
+ sdk.disconnect();
357
+ }
358
+ ```
359
+
360
+ ## Advanced Usage
361
+
362
+ ### Nullifier Duplicate Detection
363
+
364
+ All nullifier operations are instance methods on the SDK:
365
+
366
+ ```typescript
367
+ const nullifier = sdk.extractNullifier(publicInputs, circuit);
368
+ const isDuplicate = await sdk.checkNullifier(nullifier);
369
+ const details = await sdk.getNullifierDetails(nullifier);
370
+ ```
371
+
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
+ ## Types Reference
379
+
380
+ All 15 exported types:
381
+
382
+ ```typescript
383
+ import type {
384
+ CircuitType,
385
+ ProofRequestStatus,
386
+ CoinbaseKycInputs,
387
+ CoinbaseCountryInputs,
388
+ CircuitInputs,
389
+ ProofRequest,
390
+ ProofResponse,
391
+ QRCodeOptions,
392
+ VerifierContract,
393
+ ProofportConfig,
394
+ AuthCredentials,
395
+ AuthToken,
396
+ RelayProofRequest,
397
+ RelayProofResult,
398
+ SDKEnvironment,
399
+ } from '@zkproofport-app/sdk';
400
+ ```
401
+
402
+ | Type | Description |
403
+ |------|-------------|
404
+ | `CircuitType` | `'coinbase_attestation' \| 'coinbase_country_attestation'` |
405
+ | `ProofRequestStatus` | `'pending' \| 'completed' \| 'error' \| 'cancelled'` |
406
+ | `CoinbaseKycInputs` | Inputs for `coinbase_attestation` (`{ scope, userAddress?, rawTransaction? }`) |
407
+ | `CoinbaseCountryInputs` | Inputs for `coinbase_country_attestation` (`{ scope, countryList, isIncluded, ... }`) |
408
+ | `CircuitInputs` | Union: `CoinbaseKycInputs \| CoinbaseCountryInputs` |
409
+ | `ProofRequest` | Proof request object with `requestId`, `circuit`, `inputs`, metadata, and expiry |
410
+ | `ProofResponse` | Proof response with `status`, `proof`, `publicInputs`, `nullifier`, `verifierAddress`, `chainId` |
411
+ | `QRCodeOptions` | QR customization: `width`, `margin`, `darkColor`, `lightColor`, `errorCorrectionLevel` |
412
+ | `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 }` |
416
+ | `RelayProofRequest` | Relay response: `{ requestId, deepLink, status, pollUrl }` |
417
+ | `RelayProofResult` | Relay result: `{ requestId, status, proof?, publicInputs?, nullifier?, circuit?, error? }` |
418
+ | `SDKEnvironment` | SDK environment preset |
419
+
420
+ ## Error Handling
421
+
422
+ All async SDK methods throw standard `Error` objects. Common error scenarios:
423
+
424
+ ```typescript
425
+ try {
426
+ await sdk.login({ clientId: 'bad-id', apiKey: 'bad-key' });
427
+ } catch (err) {
428
+ // "Authentication failed: HTTP 401"
429
+ }
430
+
431
+ try {
432
+ await sdk.createRelayRequest('coinbase_attestation', { scope: 'app.com' });
433
+ } catch (err) {
434
+ // "Not authenticated. Call login() first."
435
+ }
436
+
437
+ try {
438
+ await sdk.waitForProof(relay.requestId, { timeoutMs: 60000 });
439
+ } catch (err) {
440
+ // "Waiting for proof timed out after 60000ms"
441
+ }
442
+ ```
443
+
444
+ Relay request validation errors:
445
+
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
+ ```
457
+
458
+ ## Development
459
+
460
+ ```bash
461
+ npm install # Install dependencies
462
+ npm run build # Build SDK (output in dist/)
463
+ npm run dev # Watch mode
464
+ npm test # Run tests
465
+ ```
466
+
467
+ ## License
468
+
469
+ MIT