@zkproofport-app/sdk 0.5.0-beta.2 → 0.5.0-beta.4
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 +34 -122
- package/dist/ProofportSDK.d.ts +33 -20
- package/dist/constants.d.ts +2 -11
- package/dist/deeplink.d.ts +17 -18
- package/dist/index.d.ts +8 -6
- package/dist/index.esm.js +139 -204
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +206 -202
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +139 -204
- package/dist/index.mjs.map +1 -1
- package/dist/offChainVerifier.d.ts +20 -4
- package/dist/relayStatus.d.ts +11 -0
- package/dist/types.d.ts +11 -63
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -32,7 +32,7 @@ const RELAY_URLS = {
|
|
|
32
32
|
* Mirrors the mobile app's `CIRCUIT_NETWORKS` (proofport-app
|
|
33
33
|
* src/config/contracts.ts), because a proof is checked where the app said its
|
|
34
34
|
* verifier is: Base for the Coinbase and OIDC circuits, GIWA Sepolia for GIWA
|
|
35
|
-
* and Dojang (testnet-only)
|
|
35
|
+
* and Dojang (testnet-only).
|
|
36
36
|
* The ADDRESS on that chain always comes from core's pinned table, never from
|
|
37
37
|
* a proof response.
|
|
38
38
|
*/
|
|
@@ -43,9 +43,6 @@ const VERIFICATION_CHAINS = {
|
|
|
43
43
|
oidc_domain_attestation: 8453,
|
|
44
44
|
giwa_attestation: 91342,
|
|
45
45
|
dojang_playground: 91342,
|
|
46
|
-
mdl_kr_ownership: 84532,
|
|
47
|
-
mdl_kr_age: 84532,
|
|
48
|
-
mdl_kr_region: 84532,
|
|
49
46
|
},
|
|
50
47
|
staging: {
|
|
51
48
|
coinbase_attestation: 84532,
|
|
@@ -53,9 +50,6 @@ const VERIFICATION_CHAINS = {
|
|
|
53
50
|
oidc_domain_attestation: 84532,
|
|
54
51
|
giwa_attestation: 91342,
|
|
55
52
|
dojang_playground: 91342,
|
|
56
|
-
mdl_kr_ownership: 84532,
|
|
57
|
-
mdl_kr_age: 84532,
|
|
58
|
-
mdl_kr_region: 84532,
|
|
59
53
|
},
|
|
60
54
|
local: {
|
|
61
55
|
coinbase_attestation: 84532,
|
|
@@ -63,9 +57,6 @@ const VERIFICATION_CHAINS = {
|
|
|
63
57
|
oidc_domain_attestation: 84532,
|
|
64
58
|
giwa_attestation: 91342,
|
|
65
59
|
dojang_playground: 91342,
|
|
66
|
-
mdl_kr_ownership: 84532,
|
|
67
|
-
mdl_kr_age: 84532,
|
|
68
|
-
mdl_kr_region: 84532,
|
|
69
60
|
},
|
|
70
61
|
};
|
|
71
62
|
/**
|
|
@@ -126,8 +117,10 @@ const CIRCUIT_METADATA = {
|
|
|
126
117
|
oidc_domain_attestation: {
|
|
127
118
|
name: 'OIDC Domain',
|
|
128
119
|
description: 'Prove email domain affiliation via OIDC JWT',
|
|
129
|
-
|
|
130
|
-
|
|
120
|
+
// v2: 148 v1 fields, then aud_hash (32), nonce_mode (1), nonce_binding
|
|
121
|
+
// (32), now (1). oidc-domain-attestation/src/main.nr.
|
|
122
|
+
publicInputsCount: 214,
|
|
123
|
+
publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier', 'provider', 'aud_hash', 'nonce_mode', 'nonce_binding', 'now'],
|
|
131
124
|
},
|
|
132
125
|
// Planned circuit — the identifier is reserved and the circuit exists, but it
|
|
133
126
|
// is not officially supported yet. See CIRCUIT_SUPPORT_STATUS in ./circuits.
|
|
@@ -160,27 +153,6 @@ const CIRCUIT_METADATA = {
|
|
|
160
153
|
publicInputsCount: 160,
|
|
161
154
|
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'scope', 'nullifier'],
|
|
162
155
|
},
|
|
163
|
-
// Korea Mobile ID (mDL) circuits. Planned — not officially supported yet.
|
|
164
|
-
// Counts are byte-flattened field totals from the compiled circuit ABIs:
|
|
165
|
-
// [u8; 32] arrays contribute 32 inputs each.
|
|
166
|
-
mdl_kr_ownership: {
|
|
167
|
-
name: 'Korea Mobile ID — Ownership',
|
|
168
|
-
description: "Prove valid Korean mobile driver's license ownership with selective disclosure",
|
|
169
|
-
publicInputsCount: 97,
|
|
170
|
-
publicInputNames: ['scope', 'nullifier_value', 'disclose_flags', 'owner_commit'],
|
|
171
|
-
},
|
|
172
|
-
mdl_kr_age: {
|
|
173
|
-
name: 'Korea Mobile ID — Age',
|
|
174
|
-
description: "Prove minimum age from Korean mobile driver's license without revealing birth date",
|
|
175
|
-
publicInputsCount: 66,
|
|
176
|
-
publicInputNames: ['scope', 'nullifier_value', 'age_threshold', 'current_year'],
|
|
177
|
-
},
|
|
178
|
-
mdl_kr_region: {
|
|
179
|
-
name: 'Korea Mobile ID — Region',
|
|
180
|
-
description: "Prove si/do residency from Korean mobile driver's license without revealing address",
|
|
181
|
-
publicInputsCount: 96,
|
|
182
|
-
publicInputNames: ['scope', 'nullifier_value', 'region_code'],
|
|
183
|
-
},
|
|
184
156
|
};
|
|
185
157
|
/**
|
|
186
158
|
* Default proof request expiration time in milliseconds.
|
|
@@ -424,6 +396,12 @@ function parseProofResponseUrl(url) {
|
|
|
424
396
|
if (timestamp) {
|
|
425
397
|
response.timestamp = parseInt(timestamp, 10);
|
|
426
398
|
}
|
|
399
|
+
// Kept as sent, even when unknown: verification refuses it by name,
|
|
400
|
+
// which says more than a callback that fails to parse.
|
|
401
|
+
const signerVariant = urlObj.searchParams.get('signerVariant');
|
|
402
|
+
if (signerVariant !== null) {
|
|
403
|
+
response.signerVariant = signerVariant;
|
|
404
|
+
}
|
|
427
405
|
}
|
|
428
406
|
else if (status === 'error') {
|
|
429
407
|
response.error = urlObj.searchParams.get('error') || undefined;
|
|
@@ -455,94 +433,6 @@ function parseProofResponseUrl(url) {
|
|
|
455
433
|
function isProofportDeepLink(url, scheme = DEFAULT_SCHEME) {
|
|
456
434
|
return url.toLowerCase().startsWith(`${scheme.toLowerCase()}://`);
|
|
457
435
|
}
|
|
458
|
-
/**
|
|
459
|
-
* @internal
|
|
460
|
-
* Validates a proof request for completeness and correctness.
|
|
461
|
-
*
|
|
462
|
-
* Performs comprehensive validation including:
|
|
463
|
-
* - Required fields (requestId, circuit, callbackUrl)
|
|
464
|
-
* - Circuit type validity (must be a supported circuit)
|
|
465
|
-
* - Circuit-specific input validation (e.g., userAddress format, countryList structure)
|
|
466
|
-
* - Expiration check (if expiresAt is provided)
|
|
467
|
-
*
|
|
468
|
-
* Note: userAddress is optional for both circuits - the mobile app will prompt
|
|
469
|
-
* for wallet connection if not provided.
|
|
470
|
-
*
|
|
471
|
-
* @param request - Proof request to validate
|
|
472
|
-
* @returns Validation result with `valid` flag and optional `error` message
|
|
473
|
-
*/
|
|
474
|
-
/**
|
|
475
|
-
* Validates a free-text mDL input (scope / targetRegion).
|
|
476
|
-
* Rejects empty or whitespace-only values, control characters (which break
|
|
477
|
-
* deep-link query encoding and downstream logging), and values over 256 chars
|
|
478
|
-
* (server-side length cap mirror). UTF-8 text such as Korean region names is
|
|
479
|
-
* allowed.
|
|
480
|
-
*
|
|
481
|
-
* @returns Error message string, or null when valid.
|
|
482
|
-
*/
|
|
483
|
-
function validateMdlTextInput(value, fieldName) {
|
|
484
|
-
if (typeof value !== 'string' || value.trim() === '') {
|
|
485
|
-
return `${fieldName} is required and must be a non-empty string`;
|
|
486
|
-
}
|
|
487
|
-
if (value.length > 256) {
|
|
488
|
-
return `${fieldName} must be 256 characters or fewer`;
|
|
489
|
-
}
|
|
490
|
-
// eslint-disable-next-line no-control-regex
|
|
491
|
-
if (/[\u0000-\u001F\u007F]/.test(value)) {
|
|
492
|
-
return `${fieldName} must not contain control characters`;
|
|
493
|
-
}
|
|
494
|
-
return null;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Validates dapp-supplied inputs for the Korea Mobile ID (mDL) circuits.
|
|
498
|
-
* Shared by deep-link request validation and relay request creation so the
|
|
499
|
-
* same guards apply on every request path.
|
|
500
|
-
*
|
|
501
|
-
* Rules:
|
|
502
|
-
* - `scope` — required non-empty string, ≤256 chars, no control characters
|
|
503
|
-
* - `mdl_kr_ownership.discloseFlags` — optional integer 0–15 (0x0F bitmask)
|
|
504
|
-
* - `mdl_kr_age.ageThreshold` — required integer 1–150
|
|
505
|
-
* - `mdl_kr_region.targetRegion` — required non-empty string (Korean si/do),
|
|
506
|
-
* ≤256 chars, no control characters
|
|
507
|
-
*
|
|
508
|
-
* @param circuit - One of the three mDL circuit identifiers
|
|
509
|
-
* @param inputs - Dapp-supplied circuit inputs
|
|
510
|
-
* @returns Error message string, or null when the inputs are valid
|
|
511
|
-
*/
|
|
512
|
-
function validateMdlInputs(circuit, inputs) {
|
|
513
|
-
const scopeError = validateMdlTextInput(inputs.scope, 'scope');
|
|
514
|
-
if (scopeError) {
|
|
515
|
-
return scopeError;
|
|
516
|
-
}
|
|
517
|
-
if (circuit === 'mdl_kr_ownership') {
|
|
518
|
-
const { discloseFlags } = inputs;
|
|
519
|
-
if (discloseFlags !== undefined) {
|
|
520
|
-
if (typeof discloseFlags !== 'number' ||
|
|
521
|
-
!Number.isInteger(discloseFlags) ||
|
|
522
|
-
discloseFlags < 0 ||
|
|
523
|
-
discloseFlags > 0x0f) {
|
|
524
|
-
return 'discloseFlags must be an integer between 0 and 15';
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
else if (circuit === 'mdl_kr_age') {
|
|
529
|
-
const { ageThreshold } = inputs;
|
|
530
|
-
if (typeof ageThreshold !== 'number' ||
|
|
531
|
-
!Number.isInteger(ageThreshold) ||
|
|
532
|
-
ageThreshold < 1 ||
|
|
533
|
-
ageThreshold > 150) {
|
|
534
|
-
return 'ageThreshold is required and must be an integer between 1 and 150';
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
const { targetRegion } = inputs;
|
|
539
|
-
const regionError = validateMdlTextInput(targetRegion, 'targetRegion');
|
|
540
|
-
if (regionError) {
|
|
541
|
-
return regionError;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
546
436
|
/**
|
|
547
437
|
* Longest accepted `returnScheme` value.
|
|
548
438
|
* Kept in sync with proofport-relay `src/returnScheme.ts`.
|
|
@@ -702,6 +592,22 @@ function detectReturnScheme(userAgent) {
|
|
|
702
592
|
}
|
|
703
593
|
return undefined;
|
|
704
594
|
}
|
|
595
|
+
/**
|
|
596
|
+
* @internal
|
|
597
|
+
* Validates a proof request for completeness and correctness.
|
|
598
|
+
*
|
|
599
|
+
* Performs comprehensive validation including:
|
|
600
|
+
* - Required fields (requestId, circuit, callbackUrl)
|
|
601
|
+
* - Circuit type validity (must be a supported circuit)
|
|
602
|
+
* - Circuit-specific input validation (e.g., userAddress format, countryList structure)
|
|
603
|
+
* - Expiration check (if expiresAt is provided)
|
|
604
|
+
*
|
|
605
|
+
* Note: userAddress is optional for both circuits - the mobile app will prompt
|
|
606
|
+
* for wallet connection if not provided.
|
|
607
|
+
*
|
|
608
|
+
* @param request - Proof request to validate
|
|
609
|
+
* @returns Validation result with `valid` flag and optional `error` message
|
|
610
|
+
*/
|
|
705
611
|
function validateProofRequest(request) {
|
|
706
612
|
if (!request.requestId) {
|
|
707
613
|
return { valid: false, error: 'Missing requestId' };
|
|
@@ -757,14 +663,6 @@ function validateProofRequest(request) {
|
|
|
757
663
|
return { valid: false, error: 'provider must be a non-empty string when specified' };
|
|
758
664
|
}
|
|
759
665
|
}
|
|
760
|
-
else if (request.circuit === 'mdl_kr_ownership' ||
|
|
761
|
-
request.circuit === 'mdl_kr_age' ||
|
|
762
|
-
request.circuit === 'mdl_kr_region') {
|
|
763
|
-
const mdlError = validateMdlInputs(request.circuit, request.inputs);
|
|
764
|
-
if (mdlError) {
|
|
765
|
-
return { valid: false, error: mdlError };
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
666
|
// The action, decided by the circuit's own entry rather than by naming a
|
|
769
667
|
// circuit here, so every action-capable circuit is checked the same way.
|
|
770
668
|
{
|
|
@@ -1001,7 +899,7 @@ function estimateQRDataSize(requestOrUrl, scheme = DEFAULT_SCHEME) {
|
|
|
1001
899
|
* The version. `@aztec/bb.js` must be the WASM build of the same bb the
|
|
1002
900
|
* circuits are compiled with -- `BB_VERSION` in circuits/toolchain.env,
|
|
1003
901
|
* 4.2.0-aztecnr-rc.2 since the beta.19 toolchain. Versions are not
|
|
1004
|
-
* interchangeable: on
|
|
902
|
+
* interchangeable: on one of our circuits the 2025 nightly wrote
|
|
1005
903
|
* a 16256-byte proof where bb 1.2.0 wrote 16224 for the identical circuit and
|
|
1006
904
|
* witness, with the SAME verification key, so the key cannot warn you. A
|
|
1007
905
|
* mismatch shows up only as verification failing.
|
|
@@ -1103,15 +1001,12 @@ function normalisePublicInputs(publicInputs) {
|
|
|
1103
1001
|
*
|
|
1104
1002
|
* @internal
|
|
1105
1003
|
*/
|
|
1106
|
-
async function fetchVerificationKey(circuit, timeoutMs) {
|
|
1107
|
-
const
|
|
1004
|
+
async function fetchVerificationKey(circuit, variant, timeoutMs) {
|
|
1005
|
+
const cacheKey = `${circuit}/${variant}`;
|
|
1006
|
+
const cached = vkCache.get(cacheKey);
|
|
1108
1007
|
if (cached && Date.now() - cached.at < VK_CACHE_TTL_MS)
|
|
1109
1008
|
return cached.vk;
|
|
1110
|
-
const vkPath = circuits.
|
|
1111
|
-
if (!vkPath) {
|
|
1112
|
-
throw new Error(`No verification key path for circuit '${circuit}'. ` +
|
|
1113
|
-
`Add it to CIRCUIT_VK_PATHS, or pass verificationKey directly.`);
|
|
1114
|
-
}
|
|
1009
|
+
const vkPath = circuits.getCircuitPackage(circuit, variant).vkPath;
|
|
1115
1010
|
const url = `${CIRCUITS_VK_BASE}/${vkPath}`;
|
|
1116
1011
|
const controller = new AbortController();
|
|
1117
1012
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -1128,7 +1023,7 @@ async function fetchVerificationKey(circuit, timeoutMs) {
|
|
|
1128
1023
|
throw new Error(`The verification key at ${url} is ${vk.length} bytes, far too small to be one. ` +
|
|
1129
1024
|
`The path is probably wrong and something else was served.`);
|
|
1130
1025
|
}
|
|
1131
|
-
vkCache.set(
|
|
1026
|
+
vkCache.set(cacheKey, { vk, at: Date.now() });
|
|
1132
1027
|
return vk;
|
|
1133
1028
|
}
|
|
1134
1029
|
finally {
|
|
@@ -1141,9 +1036,11 @@ async function fetchVerificationKey(circuit, timeoutMs) {
|
|
|
1141
1036
|
* Needs no setup from the caller: the verifier ships with this SDK and is
|
|
1142
1037
|
* loaded on first use.
|
|
1143
1038
|
*
|
|
1144
|
-
* For `oidc_domain_attestation` it first checks the
|
|
1145
|
-
*
|
|
1146
|
-
*
|
|
1039
|
+
* For `oidc_domain_attestation` it first checks the token's audience, nonce and
|
|
1040
|
+
* time (core's `checkOidcPolicy`, against `options.request` when given) and the
|
|
1041
|
+
* proof's RSA key against the provider's published JWKS (core's
|
|
1042
|
+
* `checkOidcProviderKey`); any refusal, or a JWKS that cannot be fetched, is
|
|
1043
|
+
* `{ valid: false }`.
|
|
1147
1044
|
*
|
|
1148
1045
|
* @param circuit - Canonical circuit identifier
|
|
1149
1046
|
* @param proof - Proof bytes as hex, WITHOUT public inputs (as the relay sends it)
|
|
@@ -1166,12 +1063,16 @@ async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
|
|
|
1166
1063
|
if (!circuits.isCircuitId(circuit)) {
|
|
1167
1064
|
return { valid: false, error: `Unknown circuit '${circuit}'. Known: ${circuits.ALL_CIRCUIT_IDS.join(', ')}.` };
|
|
1168
1065
|
}
|
|
1066
|
+
const signer = circuits.resolveSignerVariant(circuit, options.signerVariant, proof);
|
|
1067
|
+
if ('error' in signer) {
|
|
1068
|
+
return { valid: false, error: signer.error };
|
|
1069
|
+
}
|
|
1169
1070
|
// Before Barretenberg, and for the same reason as on-chain: a proof can be
|
|
1170
1071
|
// mathematically valid and still mean nothing, because some circuits take
|
|
1171
1072
|
// their trust anchor as a public input the prover chooses. For OIDC that is
|
|
1172
1073
|
// the RSA key -- see core's oidcProviderKeys. Checked here so a dApp that
|
|
1173
1074
|
// verifies off-chain gets the same answer as one that verifies on-chain.
|
|
1174
|
-
const issuer = await core.checkProofIssuer(circuit, publicInputs);
|
|
1075
|
+
const issuer = await core.checkProofIssuer(circuit, publicInputs, { request: options.request });
|
|
1175
1076
|
if (!issuer.ok) {
|
|
1176
1077
|
return { valid: false, error: issuer.error };
|
|
1177
1078
|
}
|
|
@@ -1212,7 +1113,7 @@ async function verifyProofOffChain(circuit, proof, publicInputs, options = {}) {
|
|
|
1212
1113
|
// relay can deliver a short value (`0x01`), and the check below must see
|
|
1213
1114
|
// exactly what the circuit committed to.
|
|
1214
1115
|
const fields = normalisePublicInputs(publicInputs);
|
|
1215
|
-
const vk = options.verificationKey ?? (await fetchVerificationKey(circuit, timeoutMs));
|
|
1116
|
+
const vk = options.verificationKey ?? (await fetchVerificationKey(circuit, signer.variant, timeoutMs));
|
|
1216
1117
|
const barretenberg = await bb.Barretenberg.new({ threads: 1 });
|
|
1217
1118
|
api = barretenberg;
|
|
1218
1119
|
const verifier = new bb.UltraHonkVerifierBackend(barretenberg);
|
|
@@ -1246,6 +1147,17 @@ const RELAY_TERMINAL_STATUSES = ['completed', 'error', 'failed', 'cancelled', 'e
|
|
|
1246
1147
|
function isTerminalRelayStatus(status) {
|
|
1247
1148
|
return RELAY_TERMINAL_STATUSES.includes(status);
|
|
1248
1149
|
}
|
|
1150
|
+
/**
|
|
1151
|
+
* How long `waitForProof()` / `waitForResult()` wait when the caller passes no
|
|
1152
|
+
* `timeoutMs`: 600 000 ms (10 min).
|
|
1153
|
+
*
|
|
1154
|
+
* The relay answers `expired` itself at 540 s, so with this default a dApp
|
|
1155
|
+
* always gets a terminal status to branch on rather than its own timeout. A
|
|
1156
|
+
* heavy proof on an older phone takes a few minutes after the user confirms;
|
|
1157
|
+
* the earlier 300 000 ms default could fire while such a proof was still being
|
|
1158
|
+
* made.
|
|
1159
|
+
*/
|
|
1160
|
+
const DEFAULT_RELAY_WAIT_MS = 600000;
|
|
1249
1161
|
|
|
1250
1162
|
/**
|
|
1251
1163
|
* Proofport SDK - Main class
|
|
@@ -1313,6 +1225,13 @@ class ProofportSDK {
|
|
|
1313
1225
|
*/
|
|
1314
1226
|
constructor(config = {}) {
|
|
1315
1227
|
this.pendingRequests = new Map();
|
|
1228
|
+
/**
|
|
1229
|
+
* requestId -> scope of the relay requests this instance created, so that
|
|
1230
|
+
* verifyResponse* can check an OIDC proof's request-bound nonce without the
|
|
1231
|
+
* dApp passing the request back in. Oldest entries go first past
|
|
1232
|
+
* {@link ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS}.
|
|
1233
|
+
*/
|
|
1234
|
+
this.relayRequests = new Map();
|
|
1316
1235
|
this.signer = null;
|
|
1317
1236
|
this.socket = null;
|
|
1318
1237
|
/**
|
|
@@ -1389,7 +1308,7 @@ class ProofportSDK {
|
|
|
1389
1308
|
const request = {
|
|
1390
1309
|
requestId: generateRequestId(),
|
|
1391
1310
|
// The circuit asked for, always. This used to be the literal
|
|
1392
|
-
// 'coinbase_attestation', and every circuit that was not country
|
|
1311
|
+
// 'coinbase_attestation', and every circuit that was not country
|
|
1393
1312
|
// was routed here -- a GIWA, Dojang or OIDC request silently became a
|
|
1394
1313
|
// Coinbase KYC request.
|
|
1395
1314
|
circuit,
|
|
@@ -1496,45 +1415,10 @@ class ProofportSDK {
|
|
|
1496
1415
|
switch (builder) {
|
|
1497
1416
|
case 'country':
|
|
1498
1417
|
return this.createCoinbaseCountryRequest(inputs, options);
|
|
1499
|
-
case 'mdl':
|
|
1500
|
-
return this.createMdlKrRequest(circuit, inputs, options);
|
|
1501
1418
|
case 'scoped':
|
|
1502
1419
|
return this.createScopedRequest(circuit, inputs, options);
|
|
1503
1420
|
}
|
|
1504
1421
|
}
|
|
1505
|
-
/**
|
|
1506
|
-
* @internal
|
|
1507
|
-
* Creates a Korea Mobile ID (mDL) proof request for any of the three
|
|
1508
|
-
* mDL predicate circuits (ownership / age / region).
|
|
1509
|
-
*
|
|
1510
|
-
* The mobile app collects the license data on-device; the dapp only
|
|
1511
|
-
* supplies the predicate parameters validated here. No wallet signature
|
|
1512
|
-
* is required for mDL circuits.
|
|
1513
|
-
*
|
|
1514
|
-
* @param circuit - 'mdl_kr_ownership' | 'mdl_kr_age' | 'mdl_kr_region'
|
|
1515
|
-
* @param inputs - Predicate inputs (see MdlKr*Inputs types)
|
|
1516
|
-
* @param options - Request configuration options
|
|
1517
|
-
*
|
|
1518
|
-
* @throws Error if the predicate inputs fail validation
|
|
1519
|
-
*/
|
|
1520
|
-
createMdlKrRequest(circuit, inputs, options = {}) {
|
|
1521
|
-
const mdlError = validateMdlInputs(circuit, inputs);
|
|
1522
|
-
if (mdlError) {
|
|
1523
|
-
throw new Error(`${mdlError} (circuit: ${circuit})`);
|
|
1524
|
-
}
|
|
1525
|
-
const request = {
|
|
1526
|
-
requestId: generateRequestId(),
|
|
1527
|
-
circuit,
|
|
1528
|
-
inputs,
|
|
1529
|
-
message: options.message,
|
|
1530
|
-
dappName: options.dappName,
|
|
1531
|
-
dappIcon: options.dappIcon,
|
|
1532
|
-
createdAt: Date.now(),
|
|
1533
|
-
expiresAt: Date.now() + (options.expiresInMs || DEFAULT_REQUEST_EXPIRY_MS),
|
|
1534
|
-
};
|
|
1535
|
-
this.pendingRequests.set(request.requestId, request);
|
|
1536
|
-
return request;
|
|
1537
|
-
}
|
|
1538
1422
|
// ============ Deep Link Generation ============
|
|
1539
1423
|
/**
|
|
1540
1424
|
* @internal
|
|
@@ -1874,9 +1758,50 @@ class ProofportSDK {
|
|
|
1874
1758
|
* }
|
|
1875
1759
|
* ```
|
|
1876
1760
|
*/
|
|
1877
|
-
async verifyOnChain(circuit, proof, publicInputs, providerOrSigner) {
|
|
1761
|
+
async verifyOnChain(circuit, proof, publicInputs, providerOrSigner, options = {}) {
|
|
1878
1762
|
const parsedProof = core.parseProofForOnChain(proof, publicInputs, publicInputs.length);
|
|
1879
|
-
return core.verifyProofOnChain(circuit, parsedProof, providerOrSigner, this.verifierTarget(circuit)
|
|
1763
|
+
return core.verifyProofOnChain(circuit, parsedProof, providerOrSigner, this.verifierTarget(circuit), undefined, {
|
|
1764
|
+
request: options.request,
|
|
1765
|
+
signerVariant: options.signerVariant,
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* The request an `oidc_domain_attestation` response answers: the one the
|
|
1770
|
+
* caller passed, else the relay request this instance created under the
|
|
1771
|
+
* response's requestId. `{ error }` when there is none, or when the two
|
|
1772
|
+
* disagree -- an OIDC proof made with a request-bound nonce is only
|
|
1773
|
+
* meaningful against the request it was bound to, so a response this SDK
|
|
1774
|
+
* cannot place is refused rather than checked without it.
|
|
1775
|
+
*/
|
|
1776
|
+
oidcRequestFor(response, explicit) {
|
|
1777
|
+
if (explicit !== undefined) {
|
|
1778
|
+
if (explicit === null || typeof explicit !== 'object' || explicit.requestId !== response.requestId) {
|
|
1779
|
+
return {
|
|
1780
|
+
error: `The request passed in (${JSON.stringify(explicit?.requestId)}) is not the one this response answers (${JSON.stringify(response.requestId)}).`,
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
return { request: explicit };
|
|
1784
|
+
}
|
|
1785
|
+
const remembered = this.relayRequests.get(response.requestId);
|
|
1786
|
+
if (!remembered) {
|
|
1787
|
+
return {
|
|
1788
|
+
error: `No request ${JSON.stringify(response.requestId)} was created by this SDK instance, so the OIDC proof's ` +
|
|
1789
|
+
'nonce cannot be checked against it. Pass { request: { scope, requestId } } -- the scope and relay ' +
|
|
1790
|
+
'requestId of the request you created.',
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
return { request: remembered };
|
|
1794
|
+
}
|
|
1795
|
+
/** @internal Remember a relay request's scope for verifyResponse*. */
|
|
1796
|
+
rememberRelayRequest(requestId, scope) {
|
|
1797
|
+
if (typeof requestId !== 'string' || typeof scope !== 'string')
|
|
1798
|
+
return;
|
|
1799
|
+
this.relayRequests.delete(requestId);
|
|
1800
|
+
this.relayRequests.set(requestId, { scope, requestId });
|
|
1801
|
+
while (this.relayRequests.size > ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS) {
|
|
1802
|
+
const oldest = this.relayRequests.keys().next().value;
|
|
1803
|
+
this.relayRequests.delete(oldest);
|
|
1804
|
+
}
|
|
1880
1805
|
}
|
|
1881
1806
|
/**
|
|
1882
1807
|
* The verifier for a circuit: the one the dApp configured, otherwise the
|
|
@@ -1903,6 +1828,9 @@ class ProofportSDK {
|
|
|
1903
1828
|
*
|
|
1904
1829
|
* @param response - ProofResponse object from parseResponse()
|
|
1905
1830
|
* @param providerOrSigner - ethers v6 Provider or Signer (defaults to base mainnet public RPC)
|
|
1831
|
+
* @param options.request - For `oidc_domain_attestation`: the `{ scope, requestId }`
|
|
1832
|
+
* the response answers. Not needed for a relay request this SDK instance
|
|
1833
|
+
* created; an OIDC response this instance cannot place is refused without it.
|
|
1906
1834
|
*
|
|
1907
1835
|
* @returns Promise resolving to verification result with valid boolean and optional error message
|
|
1908
1836
|
*
|
|
@@ -1929,10 +1857,17 @@ class ProofportSDK {
|
|
|
1929
1857
|
* });
|
|
1930
1858
|
* ```
|
|
1931
1859
|
*/
|
|
1932
|
-
async verifyResponseOnChain(response, providerOrSigner) {
|
|
1860
|
+
async verifyResponseOnChain(response, providerOrSigner, options = {}) {
|
|
1933
1861
|
if (response.status !== 'completed' || !response.proof || !response.publicInputs) {
|
|
1934
1862
|
return { valid: false, error: 'Invalid or incomplete response' };
|
|
1935
1863
|
}
|
|
1864
|
+
let request;
|
|
1865
|
+
if (response.circuit === 'oidc_domain_attestation') {
|
|
1866
|
+
const found = this.oidcRequestFor(response, options.request);
|
|
1867
|
+
if ('error' in found)
|
|
1868
|
+
return { valid: false, error: found.error };
|
|
1869
|
+
request = found.request;
|
|
1870
|
+
}
|
|
1936
1871
|
const parsedProof = core.parseProofForOnChain(response.proof, response.publicInputs, response.publicInputs.length);
|
|
1937
1872
|
// The response's verifierAddress and chainId are a claim to check, never
|
|
1938
1873
|
// the contract to call: whoever wrote the response would otherwise pick
|
|
@@ -1941,7 +1876,7 @@ class ProofportSDK {
|
|
|
1941
1876
|
return core.verifyProofOnChain(response.circuit, parsedProof, providerOrSigner, this.verifierTarget(response.circuit), {
|
|
1942
1877
|
verifierAddress: response.verifierAddress,
|
|
1943
1878
|
chainId: response.chainId,
|
|
1944
|
-
});
|
|
1879
|
+
}, { request, signerVariant: response.signerVariant });
|
|
1945
1880
|
}
|
|
1946
1881
|
/**
|
|
1947
1882
|
* Verifies a proof response off-chain, against the circuit's verification key.
|
|
@@ -1971,7 +1906,14 @@ class ProofportSDK {
|
|
|
1971
1906
|
if (response.status !== 'completed' || !response.proof || !response.publicInputs) {
|
|
1972
1907
|
return { valid: false, error: 'Invalid or incomplete response' };
|
|
1973
1908
|
}
|
|
1974
|
-
|
|
1909
|
+
let request;
|
|
1910
|
+
if (response.circuit === 'oidc_domain_attestation') {
|
|
1911
|
+
const found = this.oidcRequestFor(response, options?.request);
|
|
1912
|
+
if ('error' in found)
|
|
1913
|
+
return { valid: false, error: found.error };
|
|
1914
|
+
request = found.request;
|
|
1915
|
+
}
|
|
1916
|
+
return verifyProofOffChain(response.circuit, response.proof, response.publicInputs, { ...options, request, signerVariant: response.signerVariant });
|
|
1975
1917
|
}
|
|
1976
1918
|
// ============ Utility Methods ============
|
|
1977
1919
|
/**
|
|
@@ -2329,14 +2271,6 @@ class ProofportSDK {
|
|
|
2329
2271
|
if (needsSignature && !this.signer) {
|
|
2330
2272
|
throw new Error('Signer not set. Call setSigner() first. Wallet signature is required for this circuit.');
|
|
2331
2273
|
}
|
|
2332
|
-
// mDL circuits: validate predicate inputs client-side before hitting the
|
|
2333
|
-
// relay, so a bad ageThreshold / targetRegion fails fast with a clear error.
|
|
2334
|
-
if (circuit === 'mdl_kr_ownership' || circuit === 'mdl_kr_age' || circuit === 'mdl_kr_region') {
|
|
2335
|
-
const mdlError = validateMdlInputs(circuit, inputs);
|
|
2336
|
-
if (mdlError) {
|
|
2337
|
-
throw new Error(`${mdlError} (circuit: ${circuit})`);
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
2274
|
// The action, per the circuit's entry in CIRCUIT_ACTION_BINDING. Checked
|
|
2341
2275
|
// here, before a challenge is burned, because the alternative is a refusal
|
|
2342
2276
|
// from inside the mobile app -- a screen the dapp developer never sees,
|
|
@@ -2410,7 +2344,9 @@ class ProofportSDK {
|
|
|
2410
2344
|
const error = await response.json().catch(() => ({ error: `HTTP ${response.status}` }));
|
|
2411
2345
|
throw new Error(error.error || `Relay request failed: HTTP ${response.status}`);
|
|
2412
2346
|
}
|
|
2413
|
-
|
|
2347
|
+
const created = await response.json();
|
|
2348
|
+
this.rememberRelayRequest(created.requestId, scope);
|
|
2349
|
+
return created;
|
|
2414
2350
|
}
|
|
2415
2351
|
/**
|
|
2416
2352
|
* Polls the relay for proof result status.
|
|
@@ -2450,14 +2386,14 @@ class ProofportSDK {
|
|
|
2450
2386
|
* @param requestId - The relay-issued request ID
|
|
2451
2387
|
* @param options - Polling options
|
|
2452
2388
|
* @param options.intervalMs - Polling interval in milliseconds (default: 2000)
|
|
2453
|
-
* @param options.timeoutMs - Maximum polling time in milliseconds (default:
|
|
2389
|
+
* @param options.timeoutMs - Maximum polling time in milliseconds (default: 600000 = 10 min, `DEFAULT_RELAY_WAIT_MS`)
|
|
2454
2390
|
* @param options.onStatusChange - Callback when status changes
|
|
2455
2391
|
* @returns Promise resolving to final RelayProofResult
|
|
2456
2392
|
* @throws Error if timeout or relay error
|
|
2457
2393
|
*/
|
|
2458
2394
|
async waitForResult(requestId, options = {}) {
|
|
2459
2395
|
const interval = options.intervalMs || 2000;
|
|
2460
|
-
const timeout = options.timeoutMs ||
|
|
2396
|
+
const timeout = options.timeoutMs || DEFAULT_RELAY_WAIT_MS;
|
|
2461
2397
|
const startTime = Date.now();
|
|
2462
2398
|
let lastStatus = '';
|
|
2463
2399
|
while (Date.now() - startTime < timeout) {
|
|
@@ -2569,12 +2505,12 @@ class ProofportSDK {
|
|
|
2569
2505
|
*
|
|
2570
2506
|
* @param requestId - The relay-issued request ID
|
|
2571
2507
|
* @param options - Configuration options
|
|
2572
|
-
* @param options.timeoutMs - Maximum wait time in ms (default:
|
|
2508
|
+
* @param options.timeoutMs - Maximum wait time in ms (default: 600000 = 10 min, `DEFAULT_RELAY_WAIT_MS`)
|
|
2573
2509
|
* @param options.onStatusChange - Callback for status updates
|
|
2574
2510
|
* @returns Promise resolving to final RelayProofResult
|
|
2575
2511
|
*/
|
|
2576
2512
|
async waitForProof(requestId, options = {}) {
|
|
2577
|
-
const timeout = options.timeoutMs ||
|
|
2513
|
+
const timeout = options.timeoutMs || DEFAULT_RELAY_WAIT_MS;
|
|
2578
2514
|
// Try Socket.IO first
|
|
2579
2515
|
if (this.relayUrl) {
|
|
2580
2516
|
try {
|
|
@@ -2693,6 +2629,8 @@ class ProofportSDK {
|
|
|
2693
2629
|
return core.extractDomainFromPublicInputs(publicInputs, circuit);
|
|
2694
2630
|
}
|
|
2695
2631
|
}
|
|
2632
|
+
/** Bound on {@link relayRequests}; a dApp that verifies later can pass the request explicitly. */
|
|
2633
|
+
ProofportSDK.MAX_REMEMBERED_RELAY_REQUESTS = 1000;
|
|
2696
2634
|
/**
|
|
2697
2635
|
* How each circuit's request is built and checked. Keyed by circuit id and
|
|
2698
2636
|
* exhaustive over it, so a new circuit is a compile error here instead of
|
|
@@ -2704,9 +2642,6 @@ ProofportSDK.REQUEST_BUILDERS = {
|
|
|
2704
2642
|
oidc_domain_attestation: 'scoped',
|
|
2705
2643
|
giwa_attestation: 'scoped',
|
|
2706
2644
|
dojang_playground: 'scoped',
|
|
2707
|
-
mdl_kr_ownership: 'mdl',
|
|
2708
|
-
mdl_kr_age: 'mdl',
|
|
2709
|
-
mdl_kr_region: 'mdl',
|
|
2710
2645
|
};
|
|
2711
2646
|
|
|
2712
2647
|
Object.defineProperty(exports, "COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT", {
|
|
@@ -2717,18 +2652,50 @@ Object.defineProperty(exports, "COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT", {
|
|
|
2717
2652
|
enumerable: true,
|
|
2718
2653
|
get: function () { return core.COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT; }
|
|
2719
2654
|
});
|
|
2720
|
-
Object.defineProperty(exports, "
|
|
2655
|
+
Object.defineProperty(exports, "OIDC_ALLOWED_AUDIENCES", {
|
|
2721
2656
|
enumerable: true,
|
|
2722
|
-
get: function () { return core.
|
|
2657
|
+
get: function () { return core.OIDC_ALLOWED_AUDIENCES; }
|
|
2723
2658
|
});
|
|
2724
2659
|
Object.defineProperty(exports, "OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT", {
|
|
2725
2660
|
enumerable: true,
|
|
2726
2661
|
get: function () { return core.OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT; }
|
|
2727
2662
|
});
|
|
2663
|
+
Object.defineProperty(exports, "OIDC_NONCE_DOMAIN", {
|
|
2664
|
+
enumerable: true,
|
|
2665
|
+
get: function () { return core.OIDC_NONCE_DOMAIN; }
|
|
2666
|
+
});
|
|
2667
|
+
Object.defineProperty(exports, "OIDC_NOW_MAX_AGE_SECONDS", {
|
|
2668
|
+
enumerable: true,
|
|
2669
|
+
get: function () { return core.OIDC_NOW_MAX_AGE_SECONDS; }
|
|
2670
|
+
});
|
|
2671
|
+
Object.defineProperty(exports, "OIDC_NOW_MAX_FUTURE_SECONDS", {
|
|
2672
|
+
enumerable: true,
|
|
2673
|
+
get: function () { return core.OIDC_NOW_MAX_FUTURE_SECONDS; }
|
|
2674
|
+
});
|
|
2675
|
+
Object.defineProperty(exports, "OIDC_V1_PUBLIC_INPUT_COUNT", {
|
|
2676
|
+
enumerable: true,
|
|
2677
|
+
get: function () { return core.OIDC_V1_PUBLIC_INPUT_COUNT; }
|
|
2678
|
+
});
|
|
2679
|
+
Object.defineProperty(exports, "OIDC_V2_PUBLIC_INPUT_COUNT", {
|
|
2680
|
+
enumerable: true,
|
|
2681
|
+
get: function () { return core.OIDC_V2_PUBLIC_INPUT_COUNT; }
|
|
2682
|
+
});
|
|
2683
|
+
Object.defineProperty(exports, "PINNED_VARIANT_VERIFIERS", {
|
|
2684
|
+
enumerable: true,
|
|
2685
|
+
get: function () { return core.PINNED_VARIANT_VERIFIERS; }
|
|
2686
|
+
});
|
|
2728
2687
|
Object.defineProperty(exports, "PINNED_VERIFIERS", {
|
|
2729
2688
|
enumerable: true,
|
|
2730
2689
|
get: function () { return core.PINNED_VERIFIERS; }
|
|
2731
2690
|
});
|
|
2691
|
+
Object.defineProperty(exports, "checkOidcPolicy", {
|
|
2692
|
+
enumerable: true,
|
|
2693
|
+
get: function () { return core.checkOidcPolicy; }
|
|
2694
|
+
});
|
|
2695
|
+
Object.defineProperty(exports, "deriveOidcNonceBinding", {
|
|
2696
|
+
enumerable: true,
|
|
2697
|
+
get: function () { return core.deriveOidcNonceBinding; }
|
|
2698
|
+
});
|
|
2732
2699
|
Object.defineProperty(exports, "extractDomainFromPublicInputs", {
|
|
2733
2700
|
enumerable: true,
|
|
2734
2701
|
get: function () { return core.extractDomainFromPublicInputs; }
|
|
@@ -2741,10 +2708,18 @@ Object.defineProperty(exports, "extractScopeFromPublicInputs", {
|
|
|
2741
2708
|
enumerable: true,
|
|
2742
2709
|
get: function () { return core.extractScopeFromPublicInputs; }
|
|
2743
2710
|
});
|
|
2711
|
+
Object.defineProperty(exports, "getPinnedVariantVerifier", {
|
|
2712
|
+
enumerable: true,
|
|
2713
|
+
get: function () { return core.getPinnedVariantVerifier; }
|
|
2714
|
+
});
|
|
2744
2715
|
Object.defineProperty(exports, "getPinnedVerifier", {
|
|
2745
2716
|
enumerable: true,
|
|
2746
2717
|
get: function () { return core.getPinnedVerifier; }
|
|
2747
2718
|
});
|
|
2719
|
+
Object.defineProperty(exports, "oidcAudienceHash", {
|
|
2720
|
+
enumerable: true,
|
|
2721
|
+
get: function () { return core.oidcAudienceHash; }
|
|
2722
|
+
});
|
|
2748
2723
|
Object.defineProperty(exports, "ALL_CIRCUIT_IDS", {
|
|
2749
2724
|
enumerable: true,
|
|
2750
2725
|
get: function () { return circuits.ALL_CIRCUIT_IDS; }
|
|
@@ -2757,6 +2732,10 @@ Object.defineProperty(exports, "CIRCUIT_IDS", {
|
|
|
2757
2732
|
enumerable: true,
|
|
2758
2733
|
get: function () { return circuits.CIRCUIT_IDS; }
|
|
2759
2734
|
});
|
|
2735
|
+
Object.defineProperty(exports, "CIRCUIT_PACKAGES", {
|
|
2736
|
+
enumerable: true,
|
|
2737
|
+
get: function () { return circuits.CIRCUIT_PACKAGES; }
|
|
2738
|
+
});
|
|
2760
2739
|
Object.defineProperty(exports, "CIRCUIT_SUPPORT_STATUS", {
|
|
2761
2740
|
enumerable: true,
|
|
2762
2741
|
get: function () { return circuits.CIRCUIT_SUPPORT_STATUS; }
|
|
@@ -2773,6 +2752,10 @@ Object.defineProperty(exports, "PLANNED_CIRCUIT_IDS", {
|
|
|
2773
2752
|
enumerable: true,
|
|
2774
2753
|
get: function () { return circuits.PLANNED_CIRCUIT_IDS; }
|
|
2775
2754
|
});
|
|
2755
|
+
Object.defineProperty(exports, "SIGNER_VARIANTS", {
|
|
2756
|
+
enumerable: true,
|
|
2757
|
+
get: function () { return circuits.SIGNER_VARIANTS; }
|
|
2758
|
+
});
|
|
2776
2759
|
Object.defineProperty(exports, "SUPPORTED_CIRCUIT_IDS", {
|
|
2777
2760
|
enumerable: true,
|
|
2778
2761
|
get: function () { return circuits.SUPPORTED_CIRCUIT_IDS; }
|
|
@@ -2781,6 +2764,14 @@ Object.defineProperty(exports, "circuitActionBinding", {
|
|
|
2781
2764
|
enumerable: true,
|
|
2782
2765
|
get: function () { return circuits.circuitActionBinding; }
|
|
2783
2766
|
});
|
|
2767
|
+
Object.defineProperty(exports, "evmProofByteLength", {
|
|
2768
|
+
enumerable: true,
|
|
2769
|
+
get: function () { return circuits.evmProofByteLength; }
|
|
2770
|
+
});
|
|
2771
|
+
Object.defineProperty(exports, "getCircuitPackage", {
|
|
2772
|
+
enumerable: true,
|
|
2773
|
+
get: function () { return circuits.getCircuitPackage; }
|
|
2774
|
+
});
|
|
2784
2775
|
Object.defineProperty(exports, "getCircuitSupportStatus", {
|
|
2785
2776
|
enumerable: true,
|
|
2786
2777
|
get: function () { return circuits.getCircuitSupportStatus; }
|
|
@@ -2789,10 +2780,22 @@ Object.defineProperty(exports, "isCircuitId", {
|
|
|
2789
2780
|
enumerable: true,
|
|
2790
2781
|
get: function () { return circuits.isCircuitId; }
|
|
2791
2782
|
});
|
|
2783
|
+
Object.defineProperty(exports, "isSignerVariant", {
|
|
2784
|
+
enumerable: true,
|
|
2785
|
+
get: function () { return circuits.isSignerVariant; }
|
|
2786
|
+
});
|
|
2792
2787
|
Object.defineProperty(exports, "isSupportedCircuitId", {
|
|
2793
2788
|
enumerable: true,
|
|
2794
2789
|
get: function () { return circuits.isSupportedCircuitId; }
|
|
2795
2790
|
});
|
|
2791
|
+
Object.defineProperty(exports, "resolveSignerVariant", {
|
|
2792
|
+
enumerable: true,
|
|
2793
|
+
get: function () { return circuits.resolveSignerVariant; }
|
|
2794
|
+
});
|
|
2795
|
+
Object.defineProperty(exports, "signerVariantsOf", {
|
|
2796
|
+
enumerable: true,
|
|
2797
|
+
get: function () { return circuits.signerVariantsOf; }
|
|
2798
|
+
});
|
|
2796
2799
|
Object.defineProperty(exports, "parseTypedAction", {
|
|
2797
2800
|
enumerable: true,
|
|
2798
2801
|
get: function () { return typedAction.parseTypedAction; }
|
|
@@ -2801,6 +2804,7 @@ Object.defineProperty(exports, "validateTypedAction", {
|
|
|
2801
2804
|
enumerable: true,
|
|
2802
2805
|
get: function () { return typedAction.validateTypedAction; }
|
|
2803
2806
|
});
|
|
2807
|
+
exports.DEFAULT_RELAY_WAIT_MS = DEFAULT_RELAY_WAIT_MS;
|
|
2804
2808
|
exports.ProofportSDK = ProofportSDK;
|
|
2805
2809
|
exports.RELAY_TERMINAL_STATUSES = RELAY_TERMINAL_STATUSES;
|
|
2806
2810
|
exports.default = ProofportSDK;
|