@zkproofport-app/sdk 0.5.0-beta.1 → 0.5.0-beta.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/dist/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
- import { parseProofForOnChain, verifyProofOnChain, getVerifierAddress, getVerifierChainId, extractScopeFromPublicInputs, extractNullifierFromPublicInputs, extractDomainFromPublicInputs } from '@zkproofport-app/core';
2
- export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, MDL_KR_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs } from '@zkproofport-app/core';
1
+ import { checkProofIssuer, parseProofForOnChain, verifyProofOnChain, getPinnedVerifier, extractScopeFromPublicInputs, extractNullifierFromPublicInputs, extractDomainFromPublicInputs } from '@zkproofport-app/core';
2
+ export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, MDL_KR_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_VERIFIERS, checkOidcPolicy, deriveOidcNonceBinding, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs, getPinnedVerifier, oidcAudienceHash } from '@zkproofport-app/core';
3
3
  import { isCircuitId, ALL_CIRCUIT_IDS, circuitActionBinding, CIRCUIT_VK_PATHS, CIRCUIT_NEEDS_WALLET_SIGNATURE } from '@zkproofport-app/core/circuits';
4
4
  export { ALL_CIRCUIT_IDS, CIRCUIT_ACTION_BINDING, CIRCUIT_IDS, CIRCUIT_SUPPORT_STATUS, CIRCUIT_VK_PATHS, EXPERIMENTAL_CIRCUIT_IDS, PLANNED_CIRCUIT_IDS, SUPPORTED_CIRCUIT_IDS, circuitActionBinding, getCircuitSupportStatus, isCircuitId, isSupportedCircuitId } from '@zkproofport-app/core/circuits';
5
5
  import { validateTypedAction } from '@zkproofport-app/core/typedAction';
@@ -23,6 +23,49 @@ const RELAY_URLS = {
23
23
  staging: 'https://stg-relay.zkproofport.app',
24
24
  local: 'http://localhost:4001',
25
25
  };
26
+ /**
27
+ * The chain each circuit is verified on, per environment. Complete: a circuit
28
+ * added to the SDK is a compile error here until somebody names its chain.
29
+ *
30
+ * Mirrors the mobile app's `CIRCUIT_NETWORKS` (proofport-app
31
+ * src/config/contracts.ts), because a proof is checked where the app said its
32
+ * verifier is: Base for the Coinbase and OIDC circuits, GIWA Sepolia for GIWA
33
+ * and Dojang (testnet-only), Base Sepolia for Korea Mobile ID (testnet-only).
34
+ * The ADDRESS on that chain always comes from core's pinned table, never from
35
+ * a proof response.
36
+ */
37
+ const VERIFICATION_CHAINS = {
38
+ production: {
39
+ coinbase_attestation: 8453,
40
+ coinbase_country_attestation: 8453,
41
+ oidc_domain_attestation: 8453,
42
+ giwa_attestation: 91342,
43
+ dojang_playground: 91342,
44
+ mdl_kr_ownership: 84532,
45
+ mdl_kr_age: 84532,
46
+ mdl_kr_region: 84532,
47
+ },
48
+ staging: {
49
+ coinbase_attestation: 84532,
50
+ coinbase_country_attestation: 84532,
51
+ oidc_domain_attestation: 84532,
52
+ giwa_attestation: 91342,
53
+ dojang_playground: 91342,
54
+ mdl_kr_ownership: 84532,
55
+ mdl_kr_age: 84532,
56
+ mdl_kr_region: 84532,
57
+ },
58
+ local: {
59
+ coinbase_attestation: 84532,
60
+ coinbase_country_attestation: 84532,
61
+ oidc_domain_attestation: 84532,
62
+ giwa_attestation: 91342,
63
+ dojang_playground: 91342,
64
+ mdl_kr_ownership: 84532,
65
+ mdl_kr_age: 84532,
66
+ mdl_kr_region: 84532,
67
+ },
68
+ };
26
69
  /**
27
70
  * Default deep link URL scheme for ZKProofport mobile app.
28
71
  * Used to construct deep link URLs that open the mobile app.
@@ -81,8 +124,10 @@ const CIRCUIT_METADATA = {
81
124
  oidc_domain_attestation: {
82
125
  name: 'OIDC Domain',
83
126
  description: 'Prove email domain affiliation via OIDC JWT',
84
- publicInputsCount: 148,
85
- publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier', 'provider'],
127
+ // v2: 148 v1 fields, then aud_hash (32), nonce_mode (1), nonce_binding
128
+ // (32), now (1). oidc-domain-attestation/src/main.nr.
129
+ publicInputsCount: 214,
130
+ publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier', 'provider', 'aud_hash', 'nonce_mode', 'nonce_binding', 'now'],
86
131
  },
87
132
  // Planned circuit — the identifier is reserved and the circuit exists, but it
88
133
  // is not officially supported yet. See CIRCUIT_SUPPORT_STATUS in ./circuits.
@@ -3963,6 +4008,12 @@ async function fetchVerificationKey(circuit, timeoutMs) {
3963
4008
  * Needs no setup from the caller: the verifier ships with this SDK and is
3964
4009
  * loaded on first use.
3965
4010
  *
4011
+ * For `oidc_domain_attestation` it first checks the token's audience, nonce and
4012
+ * time (core's `checkOidcPolicy`, against `options.request` when given) and the
4013
+ * proof's RSA key against the provider's published JWKS (core's
4014
+ * `checkOidcProviderKey`); any refusal, or a JWKS that cannot be fetched, is
4015
+ * `{ valid: false }`.
4016
+ *
3966
4017
  * @param circuit - Canonical circuit identifier
3967
4018
  * @param proof - Proof bytes as hex, WITHOUT public inputs (as the relay sends it)
3968
4019
  * @param publicInputs - Public inputs as hex strings, in circuit order
@@ -3981,6 +4032,18 @@ async function fetchVerificationKey(circuit, timeoutMs) {
3981
4032
  */
3982
4033
  async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
3983
4034
  const timeoutMs = options.timeoutMs ?? 30000;
4035
+ if (!isCircuitId(circuit)) {
4036
+ return { valid: false, error: `Unknown circuit '${circuit}'. Known: ${ALL_CIRCUIT_IDS.join(', ')}.` };
4037
+ }
4038
+ // Before Barretenberg, and for the same reason as on-chain: a proof can be
4039
+ // mathematically valid and still mean nothing, because some circuits take
4040
+ // their trust anchor as a public input the prover chooses. For OIDC that is
4041
+ // the RSA key -- see core's oidcProviderKeys. Checked here so a dApp that
4042
+ // verifies off-chain gets the same answer as one that verifies on-chain.
4043
+ const issuer = await checkProofIssuer(circuit, publicInputs, { request: options.request });
4044
+ if (!issuer.ok) {
4045
+ return { valid: false, error: issuer.error };
4046
+ }
3984
4047
  // A REQUIRED dependency, loaded lazily.
3985
4048
  //
3986
4049
  // It was briefly an optional peer dependency, on the reasoning that the WASM
@@ -4119,24 +4182,41 @@ class ProofportSDK {
4119
4182
  */
4120
4183
  constructor(config = {}) {
4121
4184
  this.pendingRequests = new Map();
4185
+ /**
4186
+ * requestId -> scope of the relay requests this instance created, so that
4187
+ * verifyResponse* can check an OIDC proof's request-bound nonce without the
4188
+ * dApp passing the request back in. Oldest entries go first past
4189
+ * {@link ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS}.
4190
+ */
4191
+ this.relayRequests = new Map();
4122
4192
  this.signer = null;
4123
4193
  this.socket = null;
4194
+ /**
4195
+ * Set by {@link ProofportSDK.create} from its environment preset. It decides
4196
+ * which chain a proof is verified on when the dApp configured no verifier
4197
+ * for the circuit; a custom-config SDK has none and must configure one.
4198
+ */
4199
+ this.environment = null;
4124
4200
  this.config = {
4125
4201
  scheme: config.scheme || DEFAULT_SCHEME,
4126
4202
  verifiers: config.verifiers || {},
4127
4203
  };
4128
4204
  this.relayUrl = config.relayUrl || '';
4205
+ if (config.environment !== undefined) {
4206
+ if (!Object.prototype.hasOwnProperty.call(VERIFICATION_CHAINS, config.environment)) {
4207
+ throw new Error(`Unknown environment '${config.environment}'. Known: ${Object.keys(VERIFICATION_CHAINS).join(', ')}.`);
4208
+ }
4209
+ this.environment = config.environment;
4210
+ }
4129
4211
  }
4130
4212
  // ============ Request Creation ============
4131
4213
  /**
4132
4214
  * @internal
4133
- * Creates a Coinbase KYC verification proof request.
4134
- *
4135
- * Generates a proof request for verifying Coinbase KYC status without revealing
4136
- * the actual attestation data. The proof confirms the user has passed Coinbase KYC
4137
- * while maintaining privacy through zero-knowledge proofs.
4215
+ * Creates a proof request for a circuit whose dapp-side input is a scope
4216
+ * (Coinbase KYC, OIDC domain, GIWA, Dojang), keeping that circuit's id.
4138
4217
  *
4139
- * @param inputs - Circuit inputs for Coinbase KYC
4218
+ * @param circuit - The circuit requested; it is the request's circuit
4219
+ * @param inputs - Circuit inputs; `scope` is required (and `domain` for OIDC)
4140
4220
  * @param inputs.scope - Application-specific scope (e.g., domain name)
4141
4221
  * @param options - Request configuration options
4142
4222
 
@@ -4151,7 +4231,7 @@ class ProofportSDK {
4151
4231
  *
4152
4232
  * @example
4153
4233
  * ```typescript
4154
- * const request = sdk.createCoinbaseKycRequest({
4234
+ * const request = sdk.createScopedRequest('coinbase_attestation', {
4155
4235
  * scope: 'myapp.com'
4156
4236
  * }, {
4157
4237
  * dappName: 'My DApp',
@@ -4161,13 +4241,34 @@ class ProofportSDK {
4161
4241
  * const deepLink = sdk.getDeepLinkUrl(request);
4162
4242
  * ```
4163
4243
  */
4164
- createCoinbaseKycRequest(inputs, options = {}) {
4165
- if (!inputs.scope) {
4166
- throw new Error('scope is required for coinbase_attestation circuit');
4244
+ createScopedRequest(circuit, inputs, options = {}) {
4245
+ const scope = inputs.scope;
4246
+ if (typeof scope !== 'string' || scope.trim() === '') {
4247
+ throw new Error(`scope is required for ${circuit} circuit`);
4248
+ }
4249
+ if (circuit === 'oidc_domain_attestation') {
4250
+ const domain = inputs.domain;
4251
+ if (typeof domain !== 'string' || domain.trim() === '') {
4252
+ throw new Error('domain is required for oidc_domain_attestation circuit');
4253
+ }
4254
+ }
4255
+ const action = inputs.action;
4256
+ const binding = circuitActionBinding(circuit);
4257
+ if (binding === 'none' && action !== undefined) {
4258
+ throw new Error(`${circuit} has no action inputs, so an action sent with it would not be proved.`);
4259
+ }
4260
+ if (binding === 'required' || (binding === 'optional' && action !== undefined)) {
4261
+ const actionError = validateTypedAction(action);
4262
+ if (actionError)
4263
+ throw new Error(`${actionError} (circuit: ${circuit})`);
4167
4264
  }
4168
4265
  const request = {
4169
4266
  requestId: generateRequestId(),
4170
- circuit: 'coinbase_attestation',
4267
+ // The circuit asked for, always. This used to be the literal
4268
+ // 'coinbase_attestation', and every circuit that was not country or mDL
4269
+ // was routed here -- a GIWA, Dojang or OIDC request silently became a
4270
+ // Coinbase KYC request.
4271
+ circuit,
4171
4272
  inputs,
4172
4273
  message: options.message,
4173
4274
  dappName: options.dappName,
@@ -4264,16 +4365,17 @@ class ProofportSDK {
4264
4365
  * ```
4265
4366
  */
4266
4367
  createProofRequest(circuit, inputs, options = {}) {
4267
- if (circuit === 'coinbase_country_attestation') {
4268
- return this.createCoinbaseCountryRequest(inputs, options);
4269
- }
4270
- else if (circuit === 'mdl_kr_ownership' ||
4271
- circuit === 'mdl_kr_age' ||
4272
- circuit === 'mdl_kr_region') {
4273
- return this.createMdlKrRequest(circuit, inputs, options);
4368
+ if (!isCircuitId(circuit)) {
4369
+ throw new Error(`Unknown circuit '${circuit}'. Known: ${ALL_CIRCUIT_IDS.join(', ')}.`);
4274
4370
  }
4275
- else {
4276
- return this.createCoinbaseKycRequest(inputs, options);
4371
+ const builder = ProofportSDK.REQUEST_BUILDERS[circuit];
4372
+ switch (builder) {
4373
+ case 'country':
4374
+ return this.createCoinbaseCountryRequest(inputs, options);
4375
+ case 'mdl':
4376
+ return this.createMdlKrRequest(circuit, inputs, options);
4377
+ case 'scoped':
4378
+ return this.createScopedRequest(circuit, inputs, options);
4277
4379
  }
4278
4380
  }
4279
4381
  /**
@@ -4648,10 +4750,66 @@ class ProofportSDK {
4648
4750
  * }
4649
4751
  * ```
4650
4752
  */
4651
- async verifyOnChain(circuit, proof, publicInputs, providerOrSigner) {
4753
+ async verifyOnChain(circuit, proof, publicInputs, providerOrSigner, options = {}) {
4652
4754
  const parsedProof = parseProofForOnChain(proof, publicInputs, publicInputs.length);
4653
- const customVerifier = this.config.verifiers[circuit];
4654
- return verifyProofOnChain(circuit, parsedProof, providerOrSigner, customVerifier);
4755
+ return verifyProofOnChain(circuit, parsedProof, providerOrSigner, this.verifierTarget(circuit), undefined, {
4756
+ request: options.request,
4757
+ });
4758
+ }
4759
+ /**
4760
+ * The request an `oidc_domain_attestation` response answers: the one the
4761
+ * caller passed, else the relay request this instance created under the
4762
+ * response's requestId. `{ error }` when there is none, or when the two
4763
+ * disagree -- an OIDC proof made with a request-bound nonce is only
4764
+ * meaningful against the request it was bound to, so a response this SDK
4765
+ * cannot place is refused rather than checked without it.
4766
+ */
4767
+ oidcRequestFor(response, explicit) {
4768
+ if (explicit !== undefined) {
4769
+ if (explicit === null || typeof explicit !== 'object' || explicit.requestId !== response.requestId) {
4770
+ return {
4771
+ error: `The request passed in (${JSON.stringify(explicit?.requestId)}) is not the one this response answers (${JSON.stringify(response.requestId)}).`,
4772
+ };
4773
+ }
4774
+ return { request: explicit };
4775
+ }
4776
+ const remembered = this.relayRequests.get(response.requestId);
4777
+ if (!remembered) {
4778
+ return {
4779
+ error: `No request ${JSON.stringify(response.requestId)} was created by this SDK instance, so the OIDC proof's ` +
4780
+ 'nonce cannot be checked against it. Pass { request: { scope, requestId } } -- the scope and relay ' +
4781
+ 'requestId of the request you created.',
4782
+ };
4783
+ }
4784
+ return { request: remembered };
4785
+ }
4786
+ /** @internal Remember a relay request's scope for verifyResponse*. */
4787
+ rememberRelayRequest(requestId, scope) {
4788
+ if (typeof requestId !== 'string' || typeof scope !== 'string')
4789
+ return;
4790
+ this.relayRequests.delete(requestId);
4791
+ this.relayRequests.set(requestId, { scope, requestId });
4792
+ while (this.relayRequests.size > ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS) {
4793
+ const oldest = this.relayRequests.keys().next().value;
4794
+ this.relayRequests.delete(oldest);
4795
+ }
4796
+ }
4797
+ /**
4798
+ * The verifier for a circuit: the one the dApp configured, otherwise the
4799
+ * pinned deployment on the chain this SDK's environment verifies that
4800
+ * circuit on. `undefined` when neither exists, which core reports as an
4801
+ * error. Never anything a proof response said.
4802
+ */
4803
+ verifierTarget(circuit) {
4804
+ const configured = this.config.verifiers[circuit];
4805
+ if (configured)
4806
+ return configured;
4807
+ if (this.environment === null)
4808
+ return undefined;
4809
+ const chains = VERIFICATION_CHAINS[this.environment];
4810
+ if (!Object.prototype.hasOwnProperty.call(chains, circuit))
4811
+ return undefined;
4812
+ return { chainId: chains[circuit] };
4655
4813
  }
4656
4814
  /**
4657
4815
  * Verifies a proof response on-chain using the deployed verifier contract.
@@ -4661,6 +4819,9 @@ class ProofportSDK {
4661
4819
  *
4662
4820
  * @param response - ProofResponse object from parseResponse()
4663
4821
  * @param providerOrSigner - ethers v6 Provider or Signer (defaults to base mainnet public RPC)
4822
+ * @param options.request - For `oidc_domain_attestation`: the `{ scope, requestId }`
4823
+ * the response answers. Not needed for a relay request this SDK instance
4824
+ * created; an OIDC response this instance cannot place is refused without it.
4664
4825
  *
4665
4826
  * @returns Promise resolving to verification result with valid boolean and optional error message
4666
4827
  *
@@ -4687,17 +4848,26 @@ class ProofportSDK {
4687
4848
  * });
4688
4849
  * ```
4689
4850
  */
4690
- async verifyResponseOnChain(response, providerOrSigner) {
4851
+ async verifyResponseOnChain(response, providerOrSigner, options = {}) {
4691
4852
  if (response.status !== 'completed' || !response.proof || !response.publicInputs) {
4692
4853
  return { valid: false, error: 'Invalid or incomplete response' };
4693
4854
  }
4855
+ let request;
4856
+ if (response.circuit === 'oidc_domain_attestation') {
4857
+ const found = this.oidcRequestFor(response, options.request);
4858
+ if ('error' in found)
4859
+ return { valid: false, error: found.error };
4860
+ request = found.request;
4861
+ }
4694
4862
  const parsedProof = parseProofForOnChain(response.proof, response.publicInputs, response.publicInputs.length);
4695
- const customVerifier = this.config.verifiers[response.circuit];
4696
- const responseVerifier = {
4863
+ // The response's verifierAddress and chainId are a claim to check, never
4864
+ // the contract to call: whoever wrote the response would otherwise pick
4865
+ // the judge. A claim that differs from the pinned or configured verifier
4866
+ // is refused.
4867
+ return verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, this.verifierTarget(response.circuit), {
4697
4868
  verifierAddress: response.verifierAddress,
4698
4869
  chainId: response.chainId,
4699
- };
4700
- return verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, customVerifier, responseVerifier);
4870
+ }, { request });
4701
4871
  }
4702
4872
  /**
4703
4873
  * Verifies a proof response off-chain, against the circuit's verification key.
@@ -4727,7 +4897,14 @@ class ProofportSDK {
4727
4897
  if (response.status !== 'completed' || !response.proof || !response.publicInputs) {
4728
4898
  return { valid: false, error: 'Invalid or incomplete response' };
4729
4899
  }
4730
- return verifyProofOffChain(response.circuit, response.proof, response.publicInputs, options);
4900
+ let request;
4901
+ if (response.circuit === 'oidc_domain_attestation') {
4902
+ const found = this.oidcRequestFor(response, options?.request);
4903
+ if ('error' in found)
4904
+ return { valid: false, error: found.error };
4905
+ request = found.request;
4906
+ }
4907
+ return verifyProofOffChain(response.circuit, response.proof, response.publicInputs, { ...options, request });
4731
4908
  }
4732
4909
  // ============ Utility Methods ============
4733
4910
  /**
@@ -4748,8 +4925,17 @@ class ProofportSDK {
4748
4925
  * ```
4749
4926
  */
4750
4927
  getVerifierAddress(circuit) {
4751
- const customVerifier = this.config.verifiers[circuit];
4752
- return getVerifierAddress(circuit, customVerifier);
4928
+ return this.requireVerifier(circuit).address;
4929
+ }
4930
+ /** The configured or pinned verifier, or an error saying why there is none. */
4931
+ requireVerifier(circuit) {
4932
+ const target = this.verifierTarget(circuit);
4933
+ if (!target) {
4934
+ throw new Error(`No verifier configured for circuit '${circuit}'. Configure one in the SDK's verifiers option, or create the SDK with ProofportSDK.create() to use the pinned deployment.`);
4935
+ }
4936
+ if ('address' in target)
4937
+ return { address: target.address, chainId: target.chainId };
4938
+ return { address: getPinnedVerifier(circuit, target.chainId).address, chainId: target.chainId };
4753
4939
  }
4754
4940
  /**
4755
4941
  * Gets the blockchain chain ID where the verifier contract is deployed.
@@ -4769,8 +4955,7 @@ class ProofportSDK {
4769
4955
  * ```
4770
4956
  */
4771
4957
  getVerifierChainId(circuit) {
4772
- const customVerifier = this.config.verifiers[circuit];
4773
- return getVerifierChainId(circuit, customVerifier);
4958
+ return this.requireVerifier(circuit).chainId;
4774
4959
  }
4775
4960
  /**
4776
4961
  * Gets metadata for a specific circuit type.
@@ -4917,17 +5102,20 @@ class ProofportSDK {
4917
5102
  */
4918
5103
  static create(envOrConfig) {
4919
5104
  if (typeof envOrConfig === 'undefined') {
4920
- return new ProofportSDK({ relayUrl: RELAY_URLS.production });
5105
+ return ProofportSDK.forEnvironment('production');
4921
5106
  }
4922
5107
  if (typeof envOrConfig === 'string') {
4923
- const relayUrl = RELAY_URLS[envOrConfig];
4924
- if (!relayUrl) {
4925
- throw new Error(`Unknown environment: ${envOrConfig}. Use 'production', 'staging', or 'local'.`);
4926
- }
4927
- return new ProofportSDK({ relayUrl });
5108
+ return ProofportSDK.forEnvironment(envOrConfig);
4928
5109
  }
4929
5110
  return new ProofportSDK(envOrConfig);
4930
5111
  }
5112
+ /** @internal An SDK on an environment preset's relay, verifying on that environment's chains. */
5113
+ static forEnvironment(env) {
5114
+ if (!Object.prototype.hasOwnProperty.call(RELAY_URLS, env)) {
5115
+ throw new Error(`Unknown environment: ${env}. Use 'production', 'staging', or 'local'.`);
5116
+ }
5117
+ return new ProofportSDK({ relayUrl: RELAY_URLS[env], environment: env });
5118
+ }
4931
5119
  /**
4932
5120
  * Detects if the code is running on a mobile device.
4933
5121
  *
@@ -5061,7 +5249,10 @@ class ProofportSDK {
5061
5249
  // calling createRelayRequest() for it with no signer got no local error and
5062
5250
  // a relay 401 two round trips later.
5063
5251
  static needsWalletSignature(circuit) {
5064
- return CIRCUIT_NEEDS_WALLET_SIGNATURE[circuit] === true;
5252
+ if (!isCircuitId(circuit)) {
5253
+ throw new Error(`Unknown circuit '${circuit}'. Expected: ${ALL_CIRCUIT_IDS.join(', ')}`);
5254
+ }
5255
+ return CIRCUIT_NEEDS_WALLET_SIGNATURE[circuit];
5065
5256
  }
5066
5257
  async createRelayRequest(circuit, inputs, options = {}) {
5067
5258
  if (!this.relayUrl) {
@@ -5152,7 +5343,9 @@ class ProofportSDK {
5152
5343
  const error = await response.json().catch(() => ({ error: `HTTP ${response.status}` }));
5153
5344
  throw new Error(error.error || `Relay request failed: HTTP ${response.status}`);
5154
5345
  }
5155
- return await response.json();
5346
+ const created = await response.json();
5347
+ this.rememberRelayRequest(created.requestId, scope);
5348
+ return created;
5156
5349
  }
5157
5350
  /**
5158
5351
  * Polls the relay for proof result status.
@@ -5435,6 +5628,23 @@ class ProofportSDK {
5435
5628
  return extractDomainFromPublicInputs(publicInputs, circuit);
5436
5629
  }
5437
5630
  }
5631
+ /** Bound on {@link relayRequests}; a dApp that verifies later can pass the request explicitly. */
5632
+ ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS = 1000;
5633
+ /**
5634
+ * How each circuit's request is built and checked. Keyed by circuit id and
5635
+ * exhaustive over it, so a new circuit is a compile error here instead of
5636
+ * falling through to another circuit's builder.
5637
+ */
5638
+ ProofportSDK.REQUEST_BUILDERS = {
5639
+ coinbase_attestation: 'scoped',
5640
+ coinbase_country_attestation: 'country',
5641
+ oidc_domain_attestation: 'scoped',
5642
+ giwa_attestation: 'scoped',
5643
+ dojang_playground: 'scoped',
5644
+ mdl_kr_ownership: 'mdl',
5645
+ mdl_kr_age: 'mdl',
5646
+ mdl_kr_region: 'mdl',
5647
+ };
5438
5648
 
5439
5649
  export { ProofportSDK, RELAY_TERMINAL_STATUSES, ProofportSDK as default, isTerminalRelayStatus, verifyProofOffChain };
5440
5650
  //# sourceMappingURL=index.esm.js.map