@zkproofport-app/sdk 0.3.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -83
- package/dist/circuits.d.ts +10 -11
- package/dist/circuits.esm.js +19 -20
- package/dist/circuits.esm.js.map +1 -1
- package/dist/circuits.js +19 -20
- package/dist/circuits.js.map +1 -1
- package/dist/circuits.mjs +19 -20
- package/dist/circuits.mjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +50 -63
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +50 -63
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +50 -63
- package/dist/index.mjs.map +1 -1
- package/dist/typedAction.d.ts +1 -1
- package/dist/typedAction.esm.js +1 -1
- package/dist/typedAction.esm.js.map +1 -1
- package/dist/typedAction.js +1 -1
- package/dist/typedAction.js.map +1 -1
- package/dist/typedAction.mjs +1 -1
- package/dist/typedAction.mjs.map +1 -1
- package/dist/types.d.ts +10 -21
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -79,16 +79,6 @@ const CIRCUIT_METADATA = {
|
|
|
79
79
|
publicInputsCount: 148,
|
|
80
80
|
publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier', 'provider'],
|
|
81
81
|
},
|
|
82
|
-
arc_eligibility: {
|
|
83
|
-
name: 'Arc Eligibility',
|
|
84
|
-
description: 'Prove eligibility and authorize one EIP-712 action, for an Arc gate',
|
|
85
|
-
// 6 x `pub [u8; 32]` in arc-eligibility/src/main.nr, flattened to bytes.
|
|
86
|
-
// Read off the compiled ABI rather than counted: signal_hash 0-31,
|
|
87
|
-
// domain_separator 32-63, action_hash 64-95, signer_list_merkle_root
|
|
88
|
-
// 96-127, scope 128-159, nullifier 160-191.
|
|
89
|
-
publicInputsCount: 192,
|
|
90
|
-
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'signer_list_merkle_root'],
|
|
91
|
-
},
|
|
92
82
|
// Planned circuit — the identifier is reserved and the circuit exists, but it
|
|
93
83
|
// is not officially supported yet. See CIRCUIT_SUPPORT_STATUS in ./circuits.
|
|
94
84
|
// Forked from coinbase_attestation, then given the EIP-712 pair and a
|
|
@@ -96,18 +86,30 @@ const CIRCUIT_METADATA = {
|
|
|
96
86
|
// zero; without one it signs signal_hash and the pair is zero. Read off the
|
|
97
87
|
// compiled ABI (6 x [u8; 32] = 192): signal_hash 0-31, domain_separator
|
|
98
88
|
// 32-63, action_hash 64-95, signer_list_merkle_root 96-127, scope 128-159,
|
|
99
|
-
// nullifier 160-191
|
|
89
|
+
// nullifier 160-191.
|
|
100
90
|
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
91
|
+
// signal_hash is NOT part of this circuit's nullifier: a nullifier derived
|
|
92
|
+
// from a public input nobody signs and nothing constrains would let one
|
|
93
|
+
// wallet mint as many nullifiers as it likes; here the secret is the address
|
|
94
|
+
// plus a constant compiled into the circuit.
|
|
105
95
|
giwa_attestation: {
|
|
106
96
|
name: 'GIWA Attestation',
|
|
107
97
|
description: 'Prove GIWA identity verification, optionally authorizing one EIP-712 action (planned — not officially supported yet)',
|
|
108
98
|
publicInputsCount: 192,
|
|
109
99
|
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'signer_list_merkle_root', 'scope', 'nullifier'],
|
|
110
100
|
},
|
|
101
|
+
// Experimental, GIWA Sepolia only. The wallet signs the request (an EIP-712
|
|
102
|
+
// action, or signal_hash when none is given -- the same branch as GIWA), and
|
|
103
|
+
// the same key must have signed a payAndIssueEAS() call to the Playground
|
|
104
|
+
// faucet. No issuer list, so no merkle root: 5 x [u8; 32] = 160, read off the
|
|
105
|
+
// compiled ABI -- signal_hash 0-31, domain_separator 32-63, action_hash
|
|
106
|
+
// 64-95, scope 96-127, nullifier 128-159.
|
|
107
|
+
dojang_playground: {
|
|
108
|
+
name: 'Dojang Playground',
|
|
109
|
+
description: 'Prove this wallet issued itself a Dojang through the GIWA Playground, optionally authorizing one EIP-712 action',
|
|
110
|
+
publicInputsCount: 160,
|
|
111
|
+
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'scope', 'nullifier'],
|
|
112
|
+
},
|
|
111
113
|
// Korea Mobile ID (mDL) circuits. Planned — not officially supported yet.
|
|
112
114
|
// Counts are byte-flattened field totals from the compiled circuit ABIs:
|
|
113
115
|
// [u8; 32] arrays contribute 32 inputs each.
|
|
@@ -173,11 +175,6 @@ const RPC_ENDPOINTS = {
|
|
|
173
175
|
84532: 'https://sepolia.base.org', // Base Sepolia
|
|
174
176
|
8453: 'https://mainnet.base.org', // Base Mainnet
|
|
175
177
|
91342: 'https://sepolia-rpc.giwa.io', // GIWA Sepolia
|
|
176
|
-
// Arc Testnet. The arc_eligibility verifier is deployed here and nowhere
|
|
177
|
-
// else, and this line was missing on 2026-09-12 — the circuit shipped, a
|
|
178
|
-
// phone produced a valid proof, and on-chain verification answered "No RPC
|
|
179
|
-
// endpoint configured for chain 5042002", which reads like the proof's fault.
|
|
180
|
-
5042002: 'https://rpc.testnet.arc.io', // Arc Testnet
|
|
181
178
|
};
|
|
182
179
|
/**
|
|
183
180
|
* Default proof request expiration time in milliseconds.
|
|
@@ -385,10 +382,10 @@ const CIRCUIT_IDS = Object.freeze({
|
|
|
385
382
|
COINBASE_COUNTRY_ATTESTATION: 'coinbase_country_attestation',
|
|
386
383
|
/** OIDC email-domain attestation. Officially supported. */
|
|
387
384
|
OIDC_DOMAIN_ATTESTATION: 'oidc_domain_attestation',
|
|
388
|
-
/** Arc eligibility: exact EIP-712 action authorization. Experimental on Arc Testnet. */
|
|
389
|
-
ARC_ELIGIBILITY: 'arc_eligibility',
|
|
390
385
|
/** GIWA attestation. Planned — not officially supported yet. */
|
|
391
386
|
GIWA_ATTESTATION: 'giwa_attestation',
|
|
387
|
+
/** Dojang Playground on GIWA Sepolia. Experimental — testnet only. */
|
|
388
|
+
DOJANG_PLAYGROUND: 'dojang_playground',
|
|
392
389
|
/** Korea Mobile ID ownership. Planned — not officially supported yet. */
|
|
393
390
|
MDL_KR_OWNERSHIP: 'mdl_kr_ownership',
|
|
394
391
|
/** Korea Mobile ID age threshold. Planned — not officially supported yet. */
|
|
@@ -415,12 +412,14 @@ const CIRCUIT_SUPPORT_STATUS = Object.freeze({
|
|
|
415
412
|
coinbase_attestation: 'supported',
|
|
416
413
|
coinbase_country_attestation: 'supported',
|
|
417
414
|
oidc_domain_attestation: 'supported',
|
|
418
|
-
arc_eligibility: 'experimental',
|
|
419
415
|
// Experimental, not planned: the circuit is built, its verifier is
|
|
420
416
|
// deployed on GIWA Sepolia and the demo requests it. What keeps it out of
|
|
421
417
|
// 'supported' is that it is testnet-only, so the app offers it behind
|
|
422
418
|
// Developer Mode and reads its files from main rather than a release tag.
|
|
423
419
|
giwa_attestation: 'experimental',
|
|
420
|
+
// Same footing as GIWA: built, verifier deployed on GIWA Sepolia, a real
|
|
421
|
+
// proof verifies there. Testnet only.
|
|
422
|
+
dojang_playground: 'experimental',
|
|
424
423
|
mdl_kr_ownership: 'planned',
|
|
425
424
|
mdl_kr_age: 'planned',
|
|
426
425
|
mdl_kr_region: 'planned',
|
|
@@ -449,8 +448,8 @@ const CIRCUIT_VK_PATHS = Object.freeze({
|
|
|
449
448
|
coinbase_attestation: 'coinbase-attestation/target/vk/vk',
|
|
450
449
|
coinbase_country_attestation: 'coinbase-country-attestation/target/vk/vk',
|
|
451
450
|
oidc_domain_attestation: 'oidc-domain-attestation/target/vk/vk',
|
|
452
|
-
arc_eligibility: 'arc-eligibility/target/vk/vk',
|
|
453
451
|
giwa_attestation: 'giwa-attestation/target/vk/vk',
|
|
452
|
+
dojang_playground: 'dojang-playground/target/vk/vk',
|
|
454
453
|
mdl_kr_ownership: 'mdl/kr-ownership/target/vk/vk',
|
|
455
454
|
mdl_kr_age: 'mdl/kr-age/target/vk/vk',
|
|
456
455
|
mdl_kr_region: 'mdl/kr-region/target/vk/vk',
|
|
@@ -478,12 +477,11 @@ const SUPPORTED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCU
|
|
|
478
477
|
/**
|
|
479
478
|
* Circuits you can prove today, on a testnet, with everything subject to change.
|
|
480
479
|
*
|
|
481
|
-
* `
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
* the honest move was a third word rather than picking the closer lie.
|
|
480
|
+
* `giwa_attestation` is in this state. Calling it `planned` would be wrong —
|
|
481
|
+
* the circuit compiles, the verifier is deployed and a proof verifies against
|
|
482
|
+
* it — and calling it `supported` would be worse, because the only deployment
|
|
483
|
+
* is a testnet (GIWA Sepolia). Neither existing word was true, so the honest
|
|
484
|
+
* move was a third word rather than picking the closer lie.
|
|
487
485
|
*/
|
|
488
486
|
const EXPERIMENTAL_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter(id => CIRCUIT_SUPPORT_STATUS[id] === 'experimental'));
|
|
489
487
|
const PLANNED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'planned'));
|
|
@@ -537,13 +535,11 @@ const CIRCUIT_ACTION_BINDING = {
|
|
|
537
535
|
coinbase_country_attestation: 'none',
|
|
538
536
|
// The signature lives inside the identity token; no wallet, no action.
|
|
539
537
|
oidc_domain_attestation: 'none',
|
|
540
|
-
//
|
|
541
|
-
// without one it personal_signs the request's signal hash.
|
|
542
|
-
// 'required' until 2026-09-22 because its circuit had no second path -- a
|
|
543
|
-
// request without an action built a vector 64 bytes short and died inside
|
|
544
|
-
// the prover. The circuit gained the branch; the table follows it.
|
|
545
|
-
arc_eligibility: 'optional',
|
|
538
|
+
// Branches in-circuit: with an action the wallet signs the typed data,
|
|
539
|
+
// without one it personal_signs the request's signal hash.
|
|
546
540
|
giwa_attestation: 'optional',
|
|
541
|
+
// Same branch as GIWA; see dojang-playground/src/main.nr.
|
|
542
|
+
dojang_playground: 'optional',
|
|
547
543
|
mdl_kr_ownership: 'none',
|
|
548
544
|
mdl_kr_age: 'none',
|
|
549
545
|
mdl_kr_region: 'none',
|
|
@@ -573,20 +569,20 @@ function circuitActionBinding(circuit) {
|
|
|
573
569
|
const CIRCUIT_NEEDS_WALLET_SIGNATURE = {
|
|
574
570
|
coinbase_attestation: true,
|
|
575
571
|
coinbase_country_attestation: true,
|
|
576
|
-
// Same Coinbase-attested wallet as above; what changes is that it signs an
|
|
577
|
-
// EIP-712 action rather than signal_hash.
|
|
578
|
-
arc_eligibility: true,
|
|
579
572
|
// The signature is inside the OIDC identity token, checked by the prover.
|
|
580
573
|
oidc_domain_attestation: false,
|
|
581
574
|
// Web2 (OmniOne CX) flows with no wallet binding at all.
|
|
582
575
|
mdl_kr_ownership: false,
|
|
583
576
|
mdl_kr_age: false,
|
|
584
577
|
mdl_kr_region: false,
|
|
585
|
-
// A GIWA-attested wallet signing an EIP-712 action
|
|
578
|
+
// A GIWA-attested wallet signing an EIP-712 action (or its signal hash).
|
|
586
579
|
// This read `false` while the circuit had always verified a wallet signature
|
|
587
580
|
// -- it sat under the Web2 comment above and was never revisited. A request
|
|
588
581
|
// for it would have reached the relay with no signer.
|
|
589
582
|
giwa_attestation: true,
|
|
583
|
+
// The wallet signs the request, and the same key must have signed its
|
|
584
|
+
// Playground faucet transaction.
|
|
585
|
+
dojang_playground: true,
|
|
590
586
|
};
|
|
591
587
|
|
|
592
588
|
const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
@@ -604,7 +600,7 @@ const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
|
604
600
|
*/
|
|
605
601
|
function validateTypedAction(value) {
|
|
606
602
|
if (value === undefined || value === null) {
|
|
607
|
-
return 'action is required
|
|
603
|
+
return 'action is required. The circuit proves that a wallet authorised ONE EIP-712 action; there is nothing to prove without it.';
|
|
608
604
|
}
|
|
609
605
|
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
610
606
|
return 'action must be an object.';
|
|
@@ -1160,8 +1156,7 @@ function validateProofRequest(request) {
|
|
|
1160
1156
|
return { valid: false, error: 'Missing circuit type' };
|
|
1161
1157
|
}
|
|
1162
1158
|
// The list is the SDK's own, not a copy. The copy that used to sit here
|
|
1163
|
-
//
|
|
1164
|
-
// `arc_eligibility` — so a link for either was refused as an invalid type
|
|
1159
|
+
// had missed circuits, so a link for one was refused as an invalid type
|
|
1165
1160
|
// while every other layer accepted it.
|
|
1166
1161
|
if (!isCircuitId(request.circuit)) {
|
|
1167
1162
|
return {
|
|
@@ -1216,15 +1211,8 @@ function validateProofRequest(request) {
|
|
|
1216
1211
|
return { valid: false, error: mdlError };
|
|
1217
1212
|
}
|
|
1218
1213
|
}
|
|
1219
|
-
else if (request.circuit === 'arc_eligibility') {
|
|
1220
|
-
const inputs = request.inputs;
|
|
1221
|
-
if (inputs.userAddress && !/^0x[a-fA-F0-9]{40}$/.test(inputs.userAddress)) {
|
|
1222
|
-
return { valid: false, error: 'Invalid userAddress format' };
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
1214
|
// The action, decided by the circuit's own entry rather than by naming a
|
|
1226
|
-
// circuit here
|
|
1227
|
-
// which is why a second action-capable circuit had nowhere to be checked.
|
|
1215
|
+
// circuit here, so every action-capable circuit is checked the same way.
|
|
1228
1216
|
{
|
|
1229
1217
|
const action = request.inputs?.action;
|
|
1230
1218
|
const binding = circuitActionBinding(request.circuit);
|
|
@@ -4383,7 +4371,7 @@ function getVerifierContract(providerOrSigner, verifier) {
|
|
|
4383
4371
|
function getDefaultProvider(chainId) {
|
|
4384
4372
|
const rpcUrl = RPC_ENDPOINTS[chainId];
|
|
4385
4373
|
if (!rpcUrl) {
|
|
4386
|
-
throw new Error(`No RPC endpoint configured for chain ${chainId}`);
|
|
4374
|
+
throw new Error(`No RPC endpoint configured for chain ${chainId}. Known chains: ${Object.keys(RPC_ENDPOINTS).join(', ')}`);
|
|
4387
4375
|
}
|
|
4388
4376
|
return createJsonRpcProvider(rpcUrl);
|
|
4389
4377
|
}
|
|
@@ -4532,7 +4520,7 @@ function getVerifierChainId(circuit, customVerifier) {
|
|
|
4532
4520
|
*
|
|
4533
4521
|
* It replaced an `if / else if / else` chain whose final `else` handed every
|
|
4534
4522
|
* unrecognised circuit — and every call that named no circuit at all —
|
|
4535
|
-
* Coinbase's offsets. For `
|
|
4523
|
+
* Coinbase's offsets. For `giwa_attestation` that is not a near miss: its
|
|
4536
4524
|
* public inputs are signal_hash, domain_separator, action_hash, merkle root,
|
|
4537
4525
|
* scope, nullifier, so the nullifier slot under Coinbase's offsets holds the
|
|
4538
4526
|
* MERKLE ROOT, a value identical for every user. Duplicate detection built on
|
|
@@ -4542,15 +4530,16 @@ const PUBLIC_INPUT_OFFSETS = Object.freeze({
|
|
|
4542
4530
|
// signal_hash, signer_list_merkle_root, scope, nullifier
|
|
4543
4531
|
coinbase_attestation: { scope: [64, 95], nullifier: [96, 127] },
|
|
4544
4532
|
// No longer Coinbase's layout. The GIWA circuit gained the EIP-712 pair, so
|
|
4545
|
-
// it reads
|
|
4546
|
-
//
|
|
4533
|
+
// it reads: signal_hash, domain_separator, action_hash, merkle root, then
|
|
4534
|
+
// scope and nullifier 64 bytes further along than Coinbase.
|
|
4547
4535
|
giwa_attestation: { scope: [128, 159], nullifier: [160, 191] },
|
|
4536
|
+
// signal_hash, domain_separator, action_hash, scope, nullifier: GIWA's
|
|
4537
|
+
// order without the merkle root, since there is no issuer list.
|
|
4538
|
+
dojang_playground: { scope: [96, 127], nullifier: [128, 159] },
|
|
4548
4539
|
// country_list, country_list_length and is_included sit before scope.
|
|
4549
4540
|
coinbase_country_attestation: { scope: [86, 117], nullifier: [118, 149] },
|
|
4550
4541
|
// pubkey_modulus_limbs and the bounded domain sit before scope.
|
|
4551
4542
|
oidc_domain_attestation: { scope: [83, 114], nullifier: [115, 146] },
|
|
4552
|
-
// signal_hash, domain_separator, action_hash, merkle root, then the pair.
|
|
4553
|
-
arc_eligibility: { scope: [128, 159], nullifier: [160, 191] },
|
|
4554
4543
|
// The Korea mDL circuits open with the pair; signal_hash is commented out.
|
|
4555
4544
|
mdl_kr_ownership: { scope: [0, 31], nullifier: [32, 63] },
|
|
4556
4545
|
mdl_kr_age: { scope: [0, 31], nullifier: [32, 63] },
|
|
@@ -5931,9 +5920,9 @@ class ProofportSDK {
|
|
|
5931
5920
|
* ```
|
|
5932
5921
|
*/
|
|
5933
5922
|
// Which circuits need a wallet signature, from the one record in
|
|
5934
|
-
// src/circuits.ts.
|
|
5935
|
-
//
|
|
5936
|
-
//
|
|
5923
|
+
// src/circuits.ts. A typed-out list here once missed a circuit, so a dapp
|
|
5924
|
+
// calling createRelayRequest() for it with no signer got no local error and
|
|
5925
|
+
// a relay 401 two round trips later.
|
|
5937
5926
|
static needsWalletSignature(circuit) {
|
|
5938
5927
|
return CIRCUIT_NEEDS_WALLET_SIGNATURE[circuit] === true;
|
|
5939
5928
|
}
|
|
@@ -5958,9 +5947,7 @@ class ProofportSDK {
|
|
|
5958
5947
|
// from inside the mobile app -- a screen the dapp developer never sees,
|
|
5959
5948
|
// reached after the person has already scanned a QR code.
|
|
5960
5949
|
//
|
|
5961
|
-
//
|
|
5962
|
-
// second circuit that can carry an action; the id belongs in the table,
|
|
5963
|
-
// not in the check.
|
|
5950
|
+
// The circuit id belongs in the table, not in the check.
|
|
5964
5951
|
{
|
|
5965
5952
|
const action = inputs.action;
|
|
5966
5953
|
const binding = circuitActionBinding(circuit);
|