@zkproofport-app/sdk 0.5.0-beta.0 → 0.5.0-beta.2

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.mjs 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_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, PINNED_VERIFIERS, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs, getPinnedVerifier } 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.
@@ -3963,6 +4006,10 @@ async function fetchVerificationKey(circuit, timeoutMs) {
3963
4006
  * Needs no setup from the caller: the verifier ships with this SDK and is
3964
4007
  * loaded on first use.
3965
4008
  *
4009
+ * For `oidc_domain_attestation` it first checks the proof's RSA key against the
4010
+ * provider's published JWKS (core's `checkOidcProviderKey`); a key the provider
4011
+ * does not publish, or a JWKS that cannot be fetched, is `{ valid: false }`.
4012
+ *
3966
4013
  * @param circuit - Canonical circuit identifier
3967
4014
  * @param proof - Proof bytes as hex, WITHOUT public inputs (as the relay sends it)
3968
4015
  * @param publicInputs - Public inputs as hex strings, in circuit order
@@ -3981,6 +4028,18 @@ async function fetchVerificationKey(circuit, timeoutMs) {
3981
4028
  */
3982
4029
  async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
3983
4030
  const timeoutMs = options.timeoutMs ?? 30000;
4031
+ if (!isCircuitId(circuit)) {
4032
+ return { valid: false, error: `Unknown circuit '${circuit}'. Known: ${ALL_CIRCUIT_IDS.join(', ')}.` };
4033
+ }
4034
+ // Before Barretenberg, and for the same reason as on-chain: a proof can be
4035
+ // mathematically valid and still mean nothing, because some circuits take
4036
+ // their trust anchor as a public input the prover chooses. For OIDC that is
4037
+ // the RSA key -- see core's oidcProviderKeys. Checked here so a dApp that
4038
+ // verifies off-chain gets the same answer as one that verifies on-chain.
4039
+ const issuer = await checkProofIssuer(circuit, publicInputs);
4040
+ if (!issuer.ok) {
4041
+ return { valid: false, error: issuer.error };
4042
+ }
3984
4043
  // A REQUIRED dependency, loaded lazily.
3985
4044
  //
3986
4045
  // It was briefly an optional peer dependency, on the reasoning that the WASM
@@ -4121,22 +4180,32 @@ class ProofportSDK {
4121
4180
  this.pendingRequests = new Map();
4122
4181
  this.signer = null;
4123
4182
  this.socket = null;
4183
+ /**
4184
+ * Set by {@link ProofportSDK.create} from its environment preset. It decides
4185
+ * which chain a proof is verified on when the dApp configured no verifier
4186
+ * for the circuit; a custom-config SDK has none and must configure one.
4187
+ */
4188
+ this.environment = null;
4124
4189
  this.config = {
4125
4190
  scheme: config.scheme || DEFAULT_SCHEME,
4126
4191
  verifiers: config.verifiers || {},
4127
4192
  };
4128
4193
  this.relayUrl = config.relayUrl || '';
4194
+ if (config.environment !== undefined) {
4195
+ if (!Object.prototype.hasOwnProperty.call(VERIFICATION_CHAINS, config.environment)) {
4196
+ throw new Error(`Unknown environment '${config.environment}'. Known: ${Object.keys(VERIFICATION_CHAINS).join(', ')}.`);
4197
+ }
4198
+ this.environment = config.environment;
4199
+ }
4129
4200
  }
4130
4201
  // ============ Request Creation ============
4131
4202
  /**
4132
4203
  * @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.
4204
+ * Creates a proof request for a circuit whose dapp-side input is a scope
4205
+ * (Coinbase KYC, OIDC domain, GIWA, Dojang), keeping that circuit's id.
4138
4206
  *
4139
- * @param inputs - Circuit inputs for Coinbase KYC
4207
+ * @param circuit - The circuit requested; it is the request's circuit
4208
+ * @param inputs - Circuit inputs; `scope` is required (and `domain` for OIDC)
4140
4209
  * @param inputs.scope - Application-specific scope (e.g., domain name)
4141
4210
  * @param options - Request configuration options
4142
4211
 
@@ -4151,7 +4220,7 @@ class ProofportSDK {
4151
4220
  *
4152
4221
  * @example
4153
4222
  * ```typescript
4154
- * const request = sdk.createCoinbaseKycRequest({
4223
+ * const request = sdk.createScopedRequest('coinbase_attestation', {
4155
4224
  * scope: 'myapp.com'
4156
4225
  * }, {
4157
4226
  * dappName: 'My DApp',
@@ -4161,13 +4230,34 @@ class ProofportSDK {
4161
4230
  * const deepLink = sdk.getDeepLinkUrl(request);
4162
4231
  * ```
4163
4232
  */
4164
- createCoinbaseKycRequest(inputs, options = {}) {
4165
- if (!inputs.scope) {
4166
- throw new Error('scope is required for coinbase_attestation circuit');
4233
+ createScopedRequest(circuit, inputs, options = {}) {
4234
+ const scope = inputs.scope;
4235
+ if (typeof scope !== 'string' || scope.trim() === '') {
4236
+ throw new Error(`scope is required for ${circuit} circuit`);
4237
+ }
4238
+ if (circuit === 'oidc_domain_attestation') {
4239
+ const domain = inputs.domain;
4240
+ if (typeof domain !== 'string' || domain.trim() === '') {
4241
+ throw new Error('domain is required for oidc_domain_attestation circuit');
4242
+ }
4243
+ }
4244
+ const action = inputs.action;
4245
+ const binding = circuitActionBinding(circuit);
4246
+ if (binding === 'none' && action !== undefined) {
4247
+ throw new Error(`${circuit} has no action inputs, so an action sent with it would not be proved.`);
4248
+ }
4249
+ if (binding === 'required' || (binding === 'optional' && action !== undefined)) {
4250
+ const actionError = validateTypedAction(action);
4251
+ if (actionError)
4252
+ throw new Error(`${actionError} (circuit: ${circuit})`);
4167
4253
  }
4168
4254
  const request = {
4169
4255
  requestId: generateRequestId(),
4170
- circuit: 'coinbase_attestation',
4256
+ // The circuit asked for, always. This used to be the literal
4257
+ // 'coinbase_attestation', and every circuit that was not country or mDL
4258
+ // was routed here -- a GIWA, Dojang or OIDC request silently became a
4259
+ // Coinbase KYC request.
4260
+ circuit,
4171
4261
  inputs,
4172
4262
  message: options.message,
4173
4263
  dappName: options.dappName,
@@ -4264,16 +4354,17 @@ class ProofportSDK {
4264
4354
  * ```
4265
4355
  */
4266
4356
  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);
4357
+ if (!isCircuitId(circuit)) {
4358
+ throw new Error(`Unknown circuit '${circuit}'. Known: ${ALL_CIRCUIT_IDS.join(', ')}.`);
4274
4359
  }
4275
- else {
4276
- return this.createCoinbaseKycRequest(inputs, options);
4360
+ const builder = ProofportSDK.REQUEST_BUILDERS[circuit];
4361
+ switch (builder) {
4362
+ case 'country':
4363
+ return this.createCoinbaseCountryRequest(inputs, options);
4364
+ case 'mdl':
4365
+ return this.createMdlKrRequest(circuit, inputs, options);
4366
+ case 'scoped':
4367
+ return this.createScopedRequest(circuit, inputs, options);
4277
4368
  }
4278
4369
  }
4279
4370
  /**
@@ -4650,8 +4741,24 @@ class ProofportSDK {
4650
4741
  */
4651
4742
  async verifyOnChain(circuit, proof, publicInputs, providerOrSigner) {
4652
4743
  const parsedProof = parseProofForOnChain(proof, publicInputs, publicInputs.length);
4653
- const customVerifier = this.config.verifiers[circuit];
4654
- return verifyProofOnChain(circuit, parsedProof, providerOrSigner, customVerifier);
4744
+ return verifyProofOnChain(circuit, parsedProof, providerOrSigner, this.verifierTarget(circuit));
4745
+ }
4746
+ /**
4747
+ * The verifier for a circuit: the one the dApp configured, otherwise the
4748
+ * pinned deployment on the chain this SDK's environment verifies that
4749
+ * circuit on. `undefined` when neither exists, which core reports as an
4750
+ * error. Never anything a proof response said.
4751
+ */
4752
+ verifierTarget(circuit) {
4753
+ const configured = this.config.verifiers[circuit];
4754
+ if (configured)
4755
+ return configured;
4756
+ if (this.environment === null)
4757
+ return undefined;
4758
+ const chains = VERIFICATION_CHAINS[this.environment];
4759
+ if (!Object.prototype.hasOwnProperty.call(chains, circuit))
4760
+ return undefined;
4761
+ return { chainId: chains[circuit] };
4655
4762
  }
4656
4763
  /**
4657
4764
  * Verifies a proof response on-chain using the deployed verifier contract.
@@ -4692,12 +4799,14 @@ class ProofportSDK {
4692
4799
  return { valid: false, error: 'Invalid or incomplete response' };
4693
4800
  }
4694
4801
  const parsedProof = parseProofForOnChain(response.proof, response.publicInputs, response.publicInputs.length);
4695
- const customVerifier = this.config.verifiers[response.circuit];
4696
- const responseVerifier = {
4802
+ // The response's verifierAddress and chainId are a claim to check, never
4803
+ // the contract to call: whoever wrote the response would otherwise pick
4804
+ // the judge. A claim that differs from the pinned or configured verifier
4805
+ // is refused.
4806
+ return verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, this.verifierTarget(response.circuit), {
4697
4807
  verifierAddress: response.verifierAddress,
4698
4808
  chainId: response.chainId,
4699
- };
4700
- return verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, customVerifier, responseVerifier);
4809
+ });
4701
4810
  }
4702
4811
  /**
4703
4812
  * Verifies a proof response off-chain, against the circuit's verification key.
@@ -4748,8 +4857,17 @@ class ProofportSDK {
4748
4857
  * ```
4749
4858
  */
4750
4859
  getVerifierAddress(circuit) {
4751
- const customVerifier = this.config.verifiers[circuit];
4752
- return getVerifierAddress(circuit, customVerifier);
4860
+ return this.requireVerifier(circuit).address;
4861
+ }
4862
+ /** The configured or pinned verifier, or an error saying why there is none. */
4863
+ requireVerifier(circuit) {
4864
+ const target = this.verifierTarget(circuit);
4865
+ if (!target) {
4866
+ 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.`);
4867
+ }
4868
+ if ('address' in target)
4869
+ return { address: target.address, chainId: target.chainId };
4870
+ return { address: getPinnedVerifier(circuit, target.chainId).address, chainId: target.chainId };
4753
4871
  }
4754
4872
  /**
4755
4873
  * Gets the blockchain chain ID where the verifier contract is deployed.
@@ -4769,8 +4887,7 @@ class ProofportSDK {
4769
4887
  * ```
4770
4888
  */
4771
4889
  getVerifierChainId(circuit) {
4772
- const customVerifier = this.config.verifiers[circuit];
4773
- return getVerifierChainId(circuit, customVerifier);
4890
+ return this.requireVerifier(circuit).chainId;
4774
4891
  }
4775
4892
  /**
4776
4893
  * Gets metadata for a specific circuit type.
@@ -4917,17 +5034,20 @@ class ProofportSDK {
4917
5034
  */
4918
5035
  static create(envOrConfig) {
4919
5036
  if (typeof envOrConfig === 'undefined') {
4920
- return new ProofportSDK({ relayUrl: RELAY_URLS.production });
5037
+ return ProofportSDK.forEnvironment('production');
4921
5038
  }
4922
5039
  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 });
5040
+ return ProofportSDK.forEnvironment(envOrConfig);
4928
5041
  }
4929
5042
  return new ProofportSDK(envOrConfig);
4930
5043
  }
5044
+ /** @internal An SDK on an environment preset's relay, verifying on that environment's chains. */
5045
+ static forEnvironment(env) {
5046
+ if (!Object.prototype.hasOwnProperty.call(RELAY_URLS, env)) {
5047
+ throw new Error(`Unknown environment: ${env}. Use 'production', 'staging', or 'local'.`);
5048
+ }
5049
+ return new ProofportSDK({ relayUrl: RELAY_URLS[env], environment: env });
5050
+ }
4931
5051
  /**
4932
5052
  * Detects if the code is running on a mobile device.
4933
5053
  *
@@ -5061,7 +5181,10 @@ class ProofportSDK {
5061
5181
  // calling createRelayRequest() for it with no signer got no local error and
5062
5182
  // a relay 401 two round trips later.
5063
5183
  static needsWalletSignature(circuit) {
5064
- return CIRCUIT_NEEDS_WALLET_SIGNATURE[circuit] === true;
5184
+ if (!isCircuitId(circuit)) {
5185
+ throw new Error(`Unknown circuit '${circuit}'. Expected: ${ALL_CIRCUIT_IDS.join(', ')}`);
5186
+ }
5187
+ return CIRCUIT_NEEDS_WALLET_SIGNATURE[circuit];
5065
5188
  }
5066
5189
  async createRelayRequest(circuit, inputs, options = {}) {
5067
5190
  if (!this.relayUrl) {
@@ -5435,6 +5558,21 @@ class ProofportSDK {
5435
5558
  return extractDomainFromPublicInputs(publicInputs, circuit);
5436
5559
  }
5437
5560
  }
5561
+ /**
5562
+ * How each circuit's request is built and checked. Keyed by circuit id and
5563
+ * exhaustive over it, so a new circuit is a compile error here instead of
5564
+ * falling through to another circuit's builder.
5565
+ */
5566
+ ProofportSDK.REQUEST_BUILDERS = {
5567
+ coinbase_attestation: 'scoped',
5568
+ coinbase_country_attestation: 'country',
5569
+ oidc_domain_attestation: 'scoped',
5570
+ giwa_attestation: 'scoped',
5571
+ dojang_playground: 'scoped',
5572
+ mdl_kr_ownership: 'mdl',
5573
+ mdl_kr_age: 'mdl',
5574
+ mdl_kr_region: 'mdl',
5575
+ };
5438
5576
 
5439
5577
  export { ProofportSDK, RELAY_TERMINAL_STATUSES, ProofportSDK as default, isTerminalRelayStatus, verifyProofOffChain };
5440
5578
  //# sourceMappingURL=index.mjs.map