@zkproofport-app/sdk 0.5.0-beta.7 → 0.5.0-beta.9

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
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@zkproofport-app/sdk)](https://www.npmjs.com/package/@zkproofport-app/sdk)
4
4
  [![license](https://img.shields.io/npm/l/@zkproofport-app/sdk)](./LICENSE)
5
5
 
6
- TypeScript SDK for requesting zero-knowledge proofs from the [ZKProofport](https://zkproofport.com) mobile app and verifying them on-chain. Three circuits are officially supported today — Coinbase KYC, Coinbase country attestation, and OIDC email-domain attestation (Google, Microsoft 365). Two more prove today against a testnet deployment and are marked experimental: GIWA attestation and Dojang Playground. [Circuit Identifiers](#circuit-identifiers) has the full list and what each status means.
6
+ TypeScript SDK for requesting zero-knowledge proofs from the [ZKProofport](https://zkproofport.com) mobile app and verifying them on-chain. Four circuits are officially supported today — Coinbase KYC, Coinbase country attestation, OIDC email-domain attestation (Google, Microsoft 365), and Human Passport score. Two more prove today against a testnet deployment and are marked experimental: GIWA attestation and Dojang Playground. [Circuit Identifiers](#circuit-identifiers) has the full list and what each status means.
7
7
 
8
8
  ## How It Works
9
9
 
@@ -125,6 +125,7 @@ import { CIRCUIT_IDS, CIRCUIT_SUPPORT_STATUS } from '@zkproofport-app/sdk/circui
125
125
  | `CIRCUIT_IDS.COINBASE_ATTESTATION` | `coinbase_attestation` | **Supported** |
126
126
  | `CIRCUIT_IDS.COINBASE_COUNTRY_ATTESTATION` | `coinbase_country_attestation` | **Supported** |
127
127
  | `CIRCUIT_IDS.OIDC_DOMAIN_ATTESTATION` | `oidc_domain_attestation` | **Supported** |
128
+ | `CIRCUIT_IDS.HUMAN_PASSPORT_SCORE` | `human_passport_score` | **Supported** |
128
129
  | `CIRCUIT_IDS.GIWA_ATTESTATION` | `giwa_attestation` | _Experimental_ |
129
130
  | `CIRCUIT_IDS.DOJANG_PLAYGROUND` | `dojang_playground` | _Experimental_ |
130
131
 
@@ -268,6 +269,38 @@ const relay = await sdk.createRelayRequest('oidc_domain_attestation', {
268
269
 
269
270
  > When `provider` is set, the mobile app verifies the user's account is managed by the specified workspace provider (e.g., Google Workspace `hd` claim, Microsoft 365 `tid` claim). Without `provider`, only the email domain is verified.
270
271
 
272
+ ### `human_passport_score`
273
+
274
+ Proves the wallet holds an unexpired [Human Passport](https://passport.human.tech) score at or above a threshold, without revealing the wallet or the exact score. The score is the one Human Passport attests on Base (scoreV2); the user creates it in Human Passport and records it on Base first. Requires a wallet signer, and can authorize one EIP-712 action like Coinbase KYC.
275
+
276
+ | Field | Type | Required | Description |
277
+ |-------|------|----------|-------------|
278
+ | `scope` | `string` | Yes | Application-specific identifier |
279
+ | `minScore` | `number` | Yes | Threshold in Passport points (`20` = 20.0; above 0, at most four decimals) |
280
+ | `action` | `TypedAction` | No | An EIP-712 action the wallet signs instead of the request's signal hash |
281
+
282
+ ```typescript
283
+ const relay = await sdk.createRelayRequest('human_passport_score', {
284
+ scope: 'myapp.com',
285
+ minScore: 20,
286
+ });
287
+ ```
288
+
289
+ Its nullifier is `keccak(keccak(wallet ++ keccak("human_passport_score")) ++ scope)`.
290
+
291
+ The threshold is a public input the prover chooses, so `verifyResponseOnChain`
292
+ and `verifyResponseOffChain` compare it with the request this SDK instance
293
+ created and refuse any other. A response the instance cannot place is refused;
294
+ pass the threshold explicitly in that case:
295
+
296
+ ```typescript
297
+ await sdk.verifyResponseOnChain(response, provider, { minScore: 20 });
298
+ ```
299
+
300
+ Every verify path also requires Human Passport's issuer root, its Base scorer
301
+ (`335`) and a proof time within 15 minutes behind and 5 minutes ahead of your
302
+ clock (`checkHumanPassportProof`).
303
+
271
304
  ## Experimental Circuits
272
305
 
273
306
  ### `giwa_attestation`
@@ -57,6 +57,12 @@ export declare class ProofportSDK {
57
57
  * them. Bounded like {@link relayRequests}.
58
58
  */
59
59
  private countryRequests;
60
+ /**
61
+ * requestId -> the score threshold of the Human Passport relay requests this
62
+ * instance created, so verifyResponse* can hold the proof's min_score to it.
63
+ * Bounded like {@link relayRequests}.
64
+ */
65
+ private passportRequests;
60
66
  private signer;
61
67
  private relayUrl;
62
68
  private socket;
@@ -501,6 +507,7 @@ export declare class ProofportSDK {
501
507
  request?: OidcRequestContext;
502
508
  signerVariant?: string;
503
509
  countryCondition?: CountryCondition;
510
+ minScore?: number;
504
511
  }): Promise<{
505
512
  valid: boolean;
506
513
  error?: string;
@@ -525,6 +532,17 @@ export declare class ProofportSDK {
525
532
  * place is refused rather than accepted for whatever list it carries.
526
533
  */
527
534
  private countryConditionFor;
535
+ /**
536
+ * The score threshold a `human_passport_score` response must carry: the one
537
+ * the caller passed, else the relay request this instance created under the
538
+ * response's requestId, else the deep-link request still pending under it.
539
+ * `{ error }` when there is none: the proof's threshold is the prover's
540
+ * choice, so a response this SDK cannot place is refused rather than
541
+ * accepted for whatever threshold it carries.
542
+ */
543
+ private passportMinScoreFor;
544
+ /** @internal Remember a Human Passport relay request's threshold for verifyResponse*. */
545
+ private rememberPassportRequest;
528
546
  /** @internal Remember a Coinbase Country relay request's list and direction for verifyResponse*. */
529
547
  private rememberCountryRequest;
530
548
  /**
@@ -578,6 +596,7 @@ export declare class ProofportSDK {
578
596
  verifyResponseOnChain(response: ProofResponse, providerOrSigner?: any, options?: {
579
597
  request?: OidcRequestContext;
580
598
  countryCondition?: CountryCondition;
599
+ minScore?: number;
581
600
  }): Promise<{
582
601
  valid: boolean;
583
602
  error?: string;
@@ -31,6 +31,7 @@ export declare const VERIFICATION_CHAINS: {
31
31
  readonly oidc_domain_attestation: 8453;
32
32
  readonly giwa_attestation: 91342;
33
33
  readonly dojang_playground: 91342;
34
+ readonly human_passport_score: 8453;
34
35
  };
35
36
  readonly staging: {
36
37
  readonly coinbase_attestation: 84532;
@@ -38,6 +39,7 @@ export declare const VERIFICATION_CHAINS: {
38
39
  readonly oidc_domain_attestation: 84532;
39
40
  readonly giwa_attestation: 91342;
40
41
  readonly dojang_playground: 91342;
42
+ readonly human_passport_score: 84532;
41
43
  };
42
44
  readonly local: {
43
45
  readonly coinbase_attestation: 84532;
@@ -45,6 +47,7 @@ export declare const VERIFICATION_CHAINS: {
45
47
  readonly oidc_domain_attestation: 84532;
46
48
  readonly giwa_attestation: 91342;
47
49
  readonly dojang_playground: 91342;
50
+ readonly human_passport_score: 84532;
48
51
  };
49
52
  };
50
53
  /**
@@ -123,4 +126,4 @@ export declare const DEFAULT_REQUEST_EXPIRY_MS: number;
123
126
  * ```
124
127
  */
125
128
  export declare const MAX_QR_DATA_SIZE = 2953;
126
- export { VERIFIER_ABI, RPC_ENDPOINTS, COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_COUNT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, } from '@zkproofport-app/core';
129
+ export { VERIFIER_ABI, RPC_ENDPOINTS, COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_COUNT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, HUMAN_PASSPORT_SCORE_PUBLIC_INPUT_LAYOUT, HUMAN_PASSPORT_SCORE_PUBLIC_INPUT_COUNT, } from '@zkproofport-app/core';
package/dist/index.d.ts CHANGED
@@ -27,17 +27,17 @@
27
27
  */
28
28
  export { ProofportSDK, default } from './ProofportSDK';
29
29
  export { extractScopeFromPublicInputs, extractNullifierFromPublicInputs, extractDomainFromPublicInputs, } from './verifier';
30
- export { CIRCUIT_IDS, CIRCUIT_SUPPORT_STATUS, ALL_CIRCUIT_IDS, SUPPORTED_CIRCUIT_IDS, EXPERIMENTAL_CIRCUIT_IDS, PLANNED_CIRCUIT_IDS, isCircuitId, isSupportedCircuitId, getCircuitSupportStatus, CIRCUIT_VK_PATHS, CIRCUIT_PACKAGES, SIGNER_VARIANTS, isSignerVariant, signerVariantsOf, getCircuitPackage, resolveSignerVariant, evmProofByteLength, } from './circuits';
30
+ export { CIRCUIT_IDS, CIRCUIT_SUPPORT_STATUS, ALL_CIRCUIT_IDS, SUPPORTED_CIRCUIT_IDS, EXPERIMENTAL_CIRCUIT_IDS, PLANNED_CIRCUIT_IDS, isCircuitId, isSupportedCircuitId, getCircuitSupportStatus, CIRCUIT_VK_PATHS, CIRCUIT_PACKAGES, SIGNER_VARIANTS, isSignerVariant, signerVariantsOf, getCircuitPackage, resolveSignerVariant, HUMAN_PASSPORT_ISSUER, HUMAN_PASSPORT_ISSUER_ROOT, HUMAN_PASSPORT_SCORER_ID, HUMAN_PASSPORT_SCORE_SCHEMA, humanPassportScoreUnits, evmProofByteLength, } from './circuits';
31
31
  export { verifyProofOffChain } from './offChainVerifier';
32
32
  export type { OffChainVerifyResult, OffChainVerifyOptions, } from './offChainVerifier';
33
33
  export { PINNED_VERIFIERS, PINNED_VARIANT_VERIFIERS, getPinnedVerifier, getPinnedVariantVerifier } from './verifier';
34
34
  export type { PinnedVerifier } from './verifier';
35
35
  export { checkOidcPolicy, deriveOidcNonceBinding, oidcAudienceHash, OIDC_ALLOWED_AUDIENCES, OIDC_NONCE_DOMAIN, OIDC_NOW_MAX_AGE_SECONDS, OIDC_NOW_MAX_FUTURE_SECONDS, OIDC_V1_PUBLIC_INPUT_COUNT, OIDC_V2_PUBLIC_INPUT_COUNT, } from './verifier';
36
36
  export type { OidcRequestContext, OidcAudience, OidcNonceMode } from './verifier';
37
- export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_COUNT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, } from './constants';
37
+ export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_COUNT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, HUMAN_PASSPORT_SCORE_PUBLIC_INPUT_LAYOUT, HUMAN_PASSPORT_SCORE_PUBLIC_INPUT_COUNT, } from './constants';
38
38
  export type { CircuitId, CircuitSupportStatus, SignerVariant, CircuitPackage } from './circuits';
39
39
  export { CIRCUIT_ACTION_BINDING, circuitActionBinding, } from './circuits';
40
40
  export type { CircuitActionBinding } from './circuits';
41
- export type { CircuitType, ProofRequestStatus, CoinbaseKycInputs, ActionBoundInputs, GiwaAttestationInputs, DojangPlaygroundInputs, TypedAction, CoinbaseCountryInputs, OidcDomainInputs, CircuitInputs, ProofRequest, ProofResponse, QRCodeOptions, VerifierContract, ProofportConfig, ChallengeResponse, WalletSigner, RelayProofRequest, RelayProofResult, RelayProofStatus, RelayTerminalStatus, SDKEnvironment, } from './types';
41
+ export type { CircuitType, ProofRequestStatus, CoinbaseKycInputs, ActionBoundInputs, GiwaAttestationInputs, DojangPlaygroundInputs, TypedAction, CoinbaseCountryInputs, HumanPassportScoreInputs, OidcDomainInputs, CircuitInputs, ProofRequest, ProofResponse, QRCodeOptions, VerifierContract, ProofportConfig, ChallengeResponse, WalletSigner, RelayProofRequest, RelayProofResult, RelayProofStatus, RelayTerminalStatus, SDKEnvironment, } from './types';
42
42
  export { RELAY_TERMINAL_STATUSES, isTerminalRelayStatus, DEFAULT_RELAY_WAIT_MS } from './relayStatus';
43
43
  export { validateTypedAction, parseTypedAction } from './typedAction';
package/dist/index.esm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { countryListError, checkProofIssuer, parseProofForOnChain, verifyProofOnChain, getPinnedVerifier, extractScopeFromPublicInputs, extractNullifierFromPublicInputs, extractDomainFromPublicInputs } from '@zkproofport-app/core';
2
- export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_COUNT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, OIDC_ALLOWED_AUDIENCES, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, OIDC_NONCE_DOMAIN, OIDC_NOW_MAX_AGE_SECONDS, OIDC_NOW_MAX_FUTURE_SECONDS, OIDC_V1_PUBLIC_INPUT_COUNT, OIDC_V2_PUBLIC_INPUT_COUNT, PINNED_VARIANT_VERIFIERS, PINNED_VERIFIERS, checkOidcPolicy, deriveOidcNonceBinding, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs, getPinnedVariantVerifier, getPinnedVerifier, oidcAudienceHash } from '@zkproofport-app/core';
3
- import { isCircuitId, ALL_CIRCUIT_IDS, circuitActionBinding, resolveSignerVariant, getCircuitPackage, CIRCUIT_NEEDS_WALLET_SIGNATURE } from '@zkproofport-app/core/circuits';
4
- export { ALL_CIRCUIT_IDS, CIRCUIT_ACTION_BINDING, CIRCUIT_IDS, CIRCUIT_PACKAGES, CIRCUIT_SUPPORT_STATUS, CIRCUIT_VK_PATHS, EXPERIMENTAL_CIRCUIT_IDS, PLANNED_CIRCUIT_IDS, SIGNER_VARIANTS, SUPPORTED_CIRCUIT_IDS, circuitActionBinding, evmProofByteLength, getCircuitPackage, getCircuitSupportStatus, isCircuitId, isSignerVariant, isSupportedCircuitId, resolveSignerVariant, signerVariantsOf } from '@zkproofport-app/core/circuits';
2
+ export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_COUNT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, HUMAN_PASSPORT_SCORE_PUBLIC_INPUT_COUNT, HUMAN_PASSPORT_SCORE_PUBLIC_INPUT_LAYOUT, OIDC_ALLOWED_AUDIENCES, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, OIDC_NONCE_DOMAIN, OIDC_NOW_MAX_AGE_SECONDS, OIDC_NOW_MAX_FUTURE_SECONDS, OIDC_V1_PUBLIC_INPUT_COUNT, OIDC_V2_PUBLIC_INPUT_COUNT, PINNED_VARIANT_VERIFIERS, PINNED_VERIFIERS, checkOidcPolicy, deriveOidcNonceBinding, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs, getPinnedVariantVerifier, getPinnedVerifier, oidcAudienceHash } from '@zkproofport-app/core';
3
+ import { isCircuitId, ALL_CIRCUIT_IDS, humanPassportScoreUnits, circuitActionBinding, resolveSignerVariant, getCircuitPackage, CIRCUIT_NEEDS_WALLET_SIGNATURE } from '@zkproofport-app/core/circuits';
4
+ export { ALL_CIRCUIT_IDS, CIRCUIT_ACTION_BINDING, CIRCUIT_IDS, CIRCUIT_PACKAGES, CIRCUIT_SUPPORT_STATUS, CIRCUIT_VK_PATHS, EXPERIMENTAL_CIRCUIT_IDS, HUMAN_PASSPORT_ISSUER, HUMAN_PASSPORT_ISSUER_ROOT, HUMAN_PASSPORT_SCORER_ID, HUMAN_PASSPORT_SCORE_SCHEMA, PLANNED_CIRCUIT_IDS, SIGNER_VARIANTS, SUPPORTED_CIRCUIT_IDS, circuitActionBinding, evmProofByteLength, getCircuitPackage, getCircuitSupportStatus, humanPassportScoreUnits, isCircuitId, isSignerVariant, isSupportedCircuitId, resolveSignerVariant, signerVariantsOf } from '@zkproofport-app/core/circuits';
5
5
  import { validateTypedAction } from '@zkproofport-app/core/typedAction';
6
6
  export { parseTypedAction, validateTypedAction } from '@zkproofport-app/core/typedAction';
7
7
 
@@ -41,6 +41,7 @@ const VERIFICATION_CHAINS = {
41
41
  oidc_domain_attestation: 8453,
42
42
  giwa_attestation: 91342,
43
43
  dojang_playground: 91342,
44
+ human_passport_score: 8453,
44
45
  },
45
46
  staging: {
46
47
  coinbase_attestation: 84532,
@@ -48,6 +49,7 @@ const VERIFICATION_CHAINS = {
48
49
  oidc_domain_attestation: 84532,
49
50
  giwa_attestation: 91342,
50
51
  dojang_playground: 91342,
52
+ human_passport_score: 84532,
51
53
  },
52
54
  local: {
53
55
  coinbase_attestation: 84532,
@@ -55,6 +57,7 @@ const VERIFICATION_CHAINS = {
55
57
  oidc_domain_attestation: 84532,
56
58
  giwa_attestation: 91342,
57
59
  dojang_playground: 91342,
60
+ human_passport_score: 84532,
58
61
  },
59
62
  };
60
63
  /**
@@ -156,6 +159,14 @@ const CIRCUIT_METADATA = {
156
159
  publicInputsCount: 160,
157
160
  publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'scope', 'nullifier'],
158
161
  },
162
+ // human-passport-score/src/main.nr: GIWA's six `pub [u8; 32]`, then
163
+ // min_score (u32, score x 10^4), scorer_id and now (u64) = 195 fields.
164
+ human_passport_score: {
165
+ name: 'Human Passport',
166
+ description: 'Prove a Human Passport score at or above a threshold, optionally authorizing one EIP-712 action',
167
+ publicInputsCount: 195,
168
+ publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'signer_list_merkle_root', 'scope', 'nullifier', 'min_score', 'scorer_id', 'now'],
169
+ },
159
170
  };
160
171
  /**
161
172
  * Default proof request expiration time in milliseconds.
@@ -652,6 +663,21 @@ function validateProofRequest(request) {
652
663
  return { valid: false, error: 'isIncluded is required and must be a boolean' };
653
664
  }
654
665
  }
666
+ else if (request.circuit === 'human_passport_score') {
667
+ const inputs = request.inputs;
668
+ if (inputs.userAddress && !/^0x[a-fA-F0-9]{40}$/.test(inputs.userAddress)) {
669
+ return { valid: false, error: 'Invalid userAddress format' };
670
+ }
671
+ if (!inputs.scope || typeof inputs.scope !== 'string' || inputs.scope.trim() === '') {
672
+ return { valid: false, error: 'scope is required and must be a non-empty string' };
673
+ }
674
+ try {
675
+ humanPassportScoreUnits(inputs.minScore);
676
+ }
677
+ catch (error) {
678
+ return { valid: false, error: error instanceof Error ? error.message : String(error) };
679
+ }
680
+ }
655
681
  else if (request.circuit === 'oidc_domain_attestation') {
656
682
  const inputs = request.inputs;
657
683
  if (!inputs.domain || typeof inputs.domain !== 'string' || inputs.domain.trim() === '') {
@@ -3940,7 +3966,7 @@ async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
3940
3966
  // their trust anchor as a public input the prover chooses. For OIDC that is
3941
3967
  // the RSA key -- see core's oidcProviderKeys. Checked here so a dApp that
3942
3968
  // verifies off-chain gets the same answer as one that verifies on-chain.
3943
- const issuer = await checkProofIssuer(circuit, publicInputs, { request: options.request, countryCondition: options.countryCondition });
3969
+ const issuer = await checkProofIssuer(circuit, publicInputs, { request: options.request, countryCondition: options.countryCondition, minScore: options.minScore });
3944
3970
  if (!issuer.ok) {
3945
3971
  return { valid: false, error: issuer.error };
3946
3972
  }
@@ -4106,6 +4132,12 @@ class ProofportSDK {
4106
4132
  * them. Bounded like {@link relayRequests}.
4107
4133
  */
4108
4134
  this.countryRequests = new Map();
4135
+ /**
4136
+ * requestId -> the score threshold of the Human Passport relay requests this
4137
+ * instance created, so verifyResponse* can hold the proof's min_score to it.
4138
+ * Bounded like {@link relayRequests}.
4139
+ */
4140
+ this.passportRequests = new Map();
4109
4141
  this.signer = null;
4110
4142
  this.socket = null;
4111
4143
  /**
@@ -4298,6 +4330,16 @@ class ProofportSDK {
4298
4330
  switch (builder) {
4299
4331
  case 'country':
4300
4332
  return this.createCoinbaseCountryRequest(inputs, options);
4333
+ case 'passport': {
4334
+ const minScore = inputs.minScore;
4335
+ try {
4336
+ humanPassportScoreUnits(minScore);
4337
+ }
4338
+ catch (error) {
4339
+ throw new Error(`${error instanceof Error ? error.message : String(error)} (circuit: ${circuit})`);
4340
+ }
4341
+ return this.createScopedRequest(circuit, inputs, options);
4342
+ }
4301
4343
  case 'scoped':
4302
4344
  return this.createScopedRequest(circuit, inputs, options);
4303
4345
  }
@@ -4647,6 +4689,7 @@ class ProofportSDK {
4647
4689
  request: options.request,
4648
4690
  signerVariant: options.signerVariant,
4649
4691
  countryCondition: options.countryCondition,
4692
+ minScore: options.minScore,
4650
4693
  });
4651
4694
  }
4652
4695
  /**
@@ -4712,6 +4755,42 @@ class ProofportSDK {
4712
4755
  'isIncluded } } -- the list and direction of the request you created.',
4713
4756
  };
4714
4757
  }
4758
+ /**
4759
+ * The score threshold a `human_passport_score` response must carry: the one
4760
+ * the caller passed, else the relay request this instance created under the
4761
+ * response's requestId, else the deep-link request still pending under it.
4762
+ * `{ error }` when there is none: the proof's threshold is the prover's
4763
+ * choice, so a response this SDK cannot place is refused rather than
4764
+ * accepted for whatever threshold it carries.
4765
+ */
4766
+ passportMinScoreFor(response, explicit) {
4767
+ if (explicit !== undefined)
4768
+ return { minScore: explicit };
4769
+ const remembered = this.passportRequests.get(response.requestId);
4770
+ if (remembered !== undefined)
4771
+ return { minScore: remembered };
4772
+ const pending = this.pendingRequests.get(response.requestId);
4773
+ const inputs = pending?.circuit === 'human_passport_score' ? pending.inputs : undefined;
4774
+ if (inputs && typeof inputs.minScore === 'number')
4775
+ return { minScore: inputs.minScore };
4776
+ return {
4777
+ error: `No Human Passport request ${JSON.stringify(response.requestId)} was created by this SDK instance, so the ` +
4778
+ "proof's score threshold cannot be checked against what was asked. Pass { minScore } -- the threshold of " +
4779
+ 'the request you created.',
4780
+ };
4781
+ }
4782
+ /** @internal Remember a Human Passport relay request's threshold for verifyResponse*. */
4783
+ rememberPassportRequest(requestId, inputs) {
4784
+ const minScore = inputs?.minScore;
4785
+ if (typeof requestId !== 'string' || typeof minScore !== 'number')
4786
+ return;
4787
+ this.passportRequests.delete(requestId);
4788
+ this.passportRequests.set(requestId, minScore);
4789
+ while (this.passportRequests.size > ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS) {
4790
+ const oldest = this.passportRequests.keys().next().value;
4791
+ this.passportRequests.delete(oldest);
4792
+ }
4793
+ }
4715
4794
  /** @internal Remember a Coinbase Country relay request's list and direction for verifyResponse*. */
4716
4795
  rememberCountryRequest(requestId, inputs) {
4717
4796
  const i = inputs;
@@ -4800,6 +4879,13 @@ class ProofportSDK {
4800
4879
  return { valid: false, error: found.error };
4801
4880
  countryCondition = found.condition;
4802
4881
  }
4882
+ let minScore;
4883
+ if (response.circuit === 'human_passport_score') {
4884
+ const found = this.passportMinScoreFor(response, options.minScore);
4885
+ if ('error' in found)
4886
+ return { valid: false, error: found.error };
4887
+ minScore = found.minScore;
4888
+ }
4803
4889
  const parsedProof = parseProofForOnChain(response.proof, response.publicInputs, response.publicInputs.length);
4804
4890
  // The response's verifierAddress and chainId are a claim to check, never
4805
4891
  // the contract to call: whoever wrote the response would otherwise pick
@@ -4808,7 +4894,7 @@ class ProofportSDK {
4808
4894
  return verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, this.verifierTarget(response.circuit), {
4809
4895
  verifierAddress: response.verifierAddress,
4810
4896
  chainId: response.chainId,
4811
- }, { request, signerVariant: response.signerVariant, countryCondition });
4897
+ }, { request, signerVariant: response.signerVariant, countryCondition, minScore });
4812
4898
  }
4813
4899
  /**
4814
4900
  * Verifies a proof response off-chain, against the circuit's verification key.
@@ -4852,7 +4938,14 @@ class ProofportSDK {
4852
4938
  return { valid: false, error: found.error };
4853
4939
  countryCondition = found.condition;
4854
4940
  }
4855
- return verifyProofOffChain(response.circuit, response.proof, response.publicInputs, { ...options, request, countryCondition, signerVariant: response.signerVariant });
4941
+ let minScore;
4942
+ if (response.circuit === 'human_passport_score') {
4943
+ const found = this.passportMinScoreFor(response, options?.minScore);
4944
+ if ('error' in found)
4945
+ return { valid: false, error: found.error };
4946
+ minScore = found.minScore;
4947
+ }
4948
+ return verifyProofOffChain(response.circuit, response.proof, response.publicInputs, { ...options, request, countryCondition, minScore, signerVariant: response.signerVariant });
4856
4949
  }
4857
4950
  // ============ Utility Methods ============
4858
4951
  /**
@@ -5230,6 +5323,15 @@ class ProofportSDK {
5230
5323
  }
5231
5324
  }
5232
5325
  }
5326
+ // A Human Passport threshold the circuit cannot carry is refused here too.
5327
+ if (circuit === 'human_passport_score') {
5328
+ try {
5329
+ humanPassportScoreUnits(inputs.minScore);
5330
+ }
5331
+ catch (error) {
5332
+ throw new Error(`${error instanceof Error ? error.message : String(error)} (circuit: ${circuit})`);
5333
+ }
5334
+ }
5233
5335
  // A Coinbase Country list the circuit would refuse is refused here, before a
5234
5336
  // challenge is burned, as createProofRequest does.
5235
5337
  if (circuit === 'coinbase_country_attestation') {
@@ -5297,6 +5399,8 @@ class ProofportSDK {
5297
5399
  this.rememberRelayRequest(created.requestId, scope);
5298
5400
  if (circuit === 'coinbase_country_attestation')
5299
5401
  this.rememberCountryRequest(created.requestId, inputs);
5402
+ if (circuit === 'human_passport_score')
5403
+ this.rememberPassportRequest(created.requestId, inputs);
5300
5404
  return created;
5301
5405
  }
5302
5406
  /**
@@ -5593,6 +5697,8 @@ ProofportSDK.REQUEST_BUILDERS = {
5593
5697
  oidc_domain_attestation: 'scoped',
5594
5698
  giwa_attestation: 'scoped',
5595
5699
  dojang_playground: 'scoped',
5700
+ // Scoped, plus the score threshold the proof must carry.
5701
+ human_passport_score: 'passport',
5596
5702
  };
5597
5703
 
5598
5704
  export { DEFAULT_RELAY_WAIT_MS, ProofportSDK, RELAY_TERMINAL_STATUSES, ProofportSDK as default, isTerminalRelayStatus, verifyProofOffChain };