@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.js
CHANGED
|
@@ -84,16 +84,6 @@ const CIRCUIT_METADATA = {
|
|
|
84
84
|
publicInputsCount: 148,
|
|
85
85
|
publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier', 'provider'],
|
|
86
86
|
},
|
|
87
|
-
arc_eligibility: {
|
|
88
|
-
name: 'Arc Eligibility',
|
|
89
|
-
description: 'Prove eligibility and authorize one EIP-712 action, for an Arc gate',
|
|
90
|
-
// 6 x `pub [u8; 32]` in arc-eligibility/src/main.nr, flattened to bytes.
|
|
91
|
-
// Read off the compiled ABI rather than counted: signal_hash 0-31,
|
|
92
|
-
// domain_separator 32-63, action_hash 64-95, signer_list_merkle_root
|
|
93
|
-
// 96-127, scope 128-159, nullifier 160-191.
|
|
94
|
-
publicInputsCount: 192,
|
|
95
|
-
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'signer_list_merkle_root'],
|
|
96
|
-
},
|
|
97
87
|
// Planned circuit — the identifier is reserved and the circuit exists, but it
|
|
98
88
|
// is not officially supported yet. See CIRCUIT_SUPPORT_STATUS in ./circuits.
|
|
99
89
|
// Forked from coinbase_attestation, then given the EIP-712 pair and a
|
|
@@ -101,18 +91,30 @@ const CIRCUIT_METADATA = {
|
|
|
101
91
|
// zero; without one it signs signal_hash and the pair is zero. Read off the
|
|
102
92
|
// compiled ABI (6 x [u8; 32] = 192): signal_hash 0-31, domain_separator
|
|
103
93
|
// 32-63, action_hash 64-95, signer_list_merkle_root 96-127, scope 128-159,
|
|
104
|
-
// nullifier 160-191
|
|
94
|
+
// nullifier 160-191.
|
|
105
95
|
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
96
|
+
// signal_hash is NOT part of this circuit's nullifier: a nullifier derived
|
|
97
|
+
// from a public input nobody signs and nothing constrains would let one
|
|
98
|
+
// wallet mint as many nullifiers as it likes; here the secret is the address
|
|
99
|
+
// plus a constant compiled into the circuit.
|
|
110
100
|
giwa_attestation: {
|
|
111
101
|
name: 'GIWA Attestation',
|
|
112
102
|
description: 'Prove GIWA identity verification, optionally authorizing one EIP-712 action (planned — not officially supported yet)',
|
|
113
103
|
publicInputsCount: 192,
|
|
114
104
|
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'signer_list_merkle_root', 'scope', 'nullifier'],
|
|
115
105
|
},
|
|
106
|
+
// Experimental, GIWA Sepolia only. The wallet signs the request (an EIP-712
|
|
107
|
+
// action, or signal_hash when none is given -- the same branch as GIWA), and
|
|
108
|
+
// the same key must have signed a payAndIssueEAS() call to the Playground
|
|
109
|
+
// faucet. No issuer list, so no merkle root: 5 x [u8; 32] = 160, read off the
|
|
110
|
+
// compiled ABI -- signal_hash 0-31, domain_separator 32-63, action_hash
|
|
111
|
+
// 64-95, scope 96-127, nullifier 128-159.
|
|
112
|
+
dojang_playground: {
|
|
113
|
+
name: 'Dojang Playground',
|
|
114
|
+
description: 'Prove this wallet issued itself a Dojang through the GIWA Playground, optionally authorizing one EIP-712 action',
|
|
115
|
+
publicInputsCount: 160,
|
|
116
|
+
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'scope', 'nullifier'],
|
|
117
|
+
},
|
|
116
118
|
// Korea Mobile ID (mDL) circuits. Planned — not officially supported yet.
|
|
117
119
|
// Counts are byte-flattened field totals from the compiled circuit ABIs:
|
|
118
120
|
// [u8; 32] arrays contribute 32 inputs each.
|
|
@@ -178,11 +180,6 @@ const RPC_ENDPOINTS = {
|
|
|
178
180
|
84532: 'https://sepolia.base.org', // Base Sepolia
|
|
179
181
|
8453: 'https://mainnet.base.org', // Base Mainnet
|
|
180
182
|
91342: 'https://sepolia-rpc.giwa.io', // GIWA Sepolia
|
|
181
|
-
// Arc Testnet. The arc_eligibility verifier is deployed here and nowhere
|
|
182
|
-
// else, and this line was missing on 2026-09-12 — the circuit shipped, a
|
|
183
|
-
// phone produced a valid proof, and on-chain verification answered "No RPC
|
|
184
|
-
// endpoint configured for chain 5042002", which reads like the proof's fault.
|
|
185
|
-
5042002: 'https://rpc.testnet.arc.io', // Arc Testnet
|
|
186
183
|
};
|
|
187
184
|
/**
|
|
188
185
|
* Default proof request expiration time in milliseconds.
|
|
@@ -390,10 +387,10 @@ const CIRCUIT_IDS = Object.freeze({
|
|
|
390
387
|
COINBASE_COUNTRY_ATTESTATION: 'coinbase_country_attestation',
|
|
391
388
|
/** OIDC email-domain attestation. Officially supported. */
|
|
392
389
|
OIDC_DOMAIN_ATTESTATION: 'oidc_domain_attestation',
|
|
393
|
-
/** Arc eligibility: exact EIP-712 action authorization. Experimental on Arc Testnet. */
|
|
394
|
-
ARC_ELIGIBILITY: 'arc_eligibility',
|
|
395
390
|
/** GIWA attestation. Planned — not officially supported yet. */
|
|
396
391
|
GIWA_ATTESTATION: 'giwa_attestation',
|
|
392
|
+
/** Dojang Playground on GIWA Sepolia. Experimental — testnet only. */
|
|
393
|
+
DOJANG_PLAYGROUND: 'dojang_playground',
|
|
397
394
|
/** Korea Mobile ID ownership. Planned — not officially supported yet. */
|
|
398
395
|
MDL_KR_OWNERSHIP: 'mdl_kr_ownership',
|
|
399
396
|
/** Korea Mobile ID age threshold. Planned — not officially supported yet. */
|
|
@@ -420,12 +417,14 @@ const CIRCUIT_SUPPORT_STATUS = Object.freeze({
|
|
|
420
417
|
coinbase_attestation: 'supported',
|
|
421
418
|
coinbase_country_attestation: 'supported',
|
|
422
419
|
oidc_domain_attestation: 'supported',
|
|
423
|
-
arc_eligibility: 'experimental',
|
|
424
420
|
// Experimental, not planned: the circuit is built, its verifier is
|
|
425
421
|
// deployed on GIWA Sepolia and the demo requests it. What keeps it out of
|
|
426
422
|
// 'supported' is that it is testnet-only, so the app offers it behind
|
|
427
423
|
// Developer Mode and reads its files from main rather than a release tag.
|
|
428
424
|
giwa_attestation: 'experimental',
|
|
425
|
+
// Same footing as GIWA: built, verifier deployed on GIWA Sepolia, a real
|
|
426
|
+
// proof verifies there. Testnet only.
|
|
427
|
+
dojang_playground: 'experimental',
|
|
429
428
|
mdl_kr_ownership: 'planned',
|
|
430
429
|
mdl_kr_age: 'planned',
|
|
431
430
|
mdl_kr_region: 'planned',
|
|
@@ -454,8 +453,8 @@ const CIRCUIT_VK_PATHS = Object.freeze({
|
|
|
454
453
|
coinbase_attestation: 'coinbase-attestation/target/vk/vk',
|
|
455
454
|
coinbase_country_attestation: 'coinbase-country-attestation/target/vk/vk',
|
|
456
455
|
oidc_domain_attestation: 'oidc-domain-attestation/target/vk/vk',
|
|
457
|
-
arc_eligibility: 'arc-eligibility/target/vk/vk',
|
|
458
456
|
giwa_attestation: 'giwa-attestation/target/vk/vk',
|
|
457
|
+
dojang_playground: 'dojang-playground/target/vk/vk',
|
|
459
458
|
mdl_kr_ownership: 'mdl/kr-ownership/target/vk/vk',
|
|
460
459
|
mdl_kr_age: 'mdl/kr-age/target/vk/vk',
|
|
461
460
|
mdl_kr_region: 'mdl/kr-region/target/vk/vk',
|
|
@@ -483,12 +482,11 @@ const SUPPORTED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCU
|
|
|
483
482
|
/**
|
|
484
483
|
* Circuits you can prove today, on a testnet, with everything subject to change.
|
|
485
484
|
*
|
|
486
|
-
* `
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
* the honest move was a third word rather than picking the closer lie.
|
|
485
|
+
* `giwa_attestation` is in this state. Calling it `planned` would be wrong —
|
|
486
|
+
* the circuit compiles, the verifier is deployed and a proof verifies against
|
|
487
|
+
* it — and calling it `supported` would be worse, because the only deployment
|
|
488
|
+
* is a testnet (GIWA Sepolia). Neither existing word was true, so the honest
|
|
489
|
+
* move was a third word rather than picking the closer lie.
|
|
492
490
|
*/
|
|
493
491
|
const EXPERIMENTAL_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter(id => CIRCUIT_SUPPORT_STATUS[id] === 'experimental'));
|
|
494
492
|
const PLANNED_CIRCUIT_IDS = Object.freeze(ALL_CIRCUIT_IDS.filter((id) => CIRCUIT_SUPPORT_STATUS[id] === 'planned'));
|
|
@@ -542,13 +540,11 @@ const CIRCUIT_ACTION_BINDING = {
|
|
|
542
540
|
coinbase_country_attestation: 'none',
|
|
543
541
|
// The signature lives inside the identity token; no wallet, no action.
|
|
544
542
|
oidc_domain_attestation: 'none',
|
|
545
|
-
//
|
|
546
|
-
// without one it personal_signs the request's signal hash.
|
|
547
|
-
// 'required' until 2026-09-22 because its circuit had no second path -- a
|
|
548
|
-
// request without an action built a vector 64 bytes short and died inside
|
|
549
|
-
// the prover. The circuit gained the branch; the table follows it.
|
|
550
|
-
arc_eligibility: 'optional',
|
|
543
|
+
// Branches in-circuit: with an action the wallet signs the typed data,
|
|
544
|
+
// without one it personal_signs the request's signal hash.
|
|
551
545
|
giwa_attestation: 'optional',
|
|
546
|
+
// Same branch as GIWA; see dojang-playground/src/main.nr.
|
|
547
|
+
dojang_playground: 'optional',
|
|
552
548
|
mdl_kr_ownership: 'none',
|
|
553
549
|
mdl_kr_age: 'none',
|
|
554
550
|
mdl_kr_region: 'none',
|
|
@@ -578,20 +574,20 @@ function circuitActionBinding(circuit) {
|
|
|
578
574
|
const CIRCUIT_NEEDS_WALLET_SIGNATURE = {
|
|
579
575
|
coinbase_attestation: true,
|
|
580
576
|
coinbase_country_attestation: true,
|
|
581
|
-
// Same Coinbase-attested wallet as above; what changes is that it signs an
|
|
582
|
-
// EIP-712 action rather than signal_hash.
|
|
583
|
-
arc_eligibility: true,
|
|
584
577
|
// The signature is inside the OIDC identity token, checked by the prover.
|
|
585
578
|
oidc_domain_attestation: false,
|
|
586
579
|
// Web2 (OmniOne CX) flows with no wallet binding at all.
|
|
587
580
|
mdl_kr_ownership: false,
|
|
588
581
|
mdl_kr_age: false,
|
|
589
582
|
mdl_kr_region: false,
|
|
590
|
-
// A GIWA-attested wallet signing an EIP-712 action
|
|
583
|
+
// A GIWA-attested wallet signing an EIP-712 action (or its signal hash).
|
|
591
584
|
// This read `false` while the circuit had always verified a wallet signature
|
|
592
585
|
// -- it sat under the Web2 comment above and was never revisited. A request
|
|
593
586
|
// for it would have reached the relay with no signer.
|
|
594
587
|
giwa_attestation: true,
|
|
588
|
+
// The wallet signs the request, and the same key must have signed its
|
|
589
|
+
// Playground faucet transaction.
|
|
590
|
+
dojang_playground: true,
|
|
595
591
|
};
|
|
596
592
|
|
|
597
593
|
const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
@@ -609,7 +605,7 @@ const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
|
609
605
|
*/
|
|
610
606
|
function validateTypedAction(value) {
|
|
611
607
|
if (value === undefined || value === null) {
|
|
612
|
-
return 'action is required
|
|
608
|
+
return 'action is required. The circuit proves that a wallet authorised ONE EIP-712 action; there is nothing to prove without it.';
|
|
613
609
|
}
|
|
614
610
|
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
615
611
|
return 'action must be an object.';
|
|
@@ -1165,8 +1161,7 @@ function validateProofRequest(request) {
|
|
|
1165
1161
|
return { valid: false, error: 'Missing circuit type' };
|
|
1166
1162
|
}
|
|
1167
1163
|
// The list is the SDK's own, not a copy. The copy that used to sit here
|
|
1168
|
-
//
|
|
1169
|
-
// `arc_eligibility` — so a link for either was refused as an invalid type
|
|
1164
|
+
// had missed circuits, so a link for one was refused as an invalid type
|
|
1170
1165
|
// while every other layer accepted it.
|
|
1171
1166
|
if (!isCircuitId(request.circuit)) {
|
|
1172
1167
|
return {
|
|
@@ -1221,15 +1216,8 @@ function validateProofRequest(request) {
|
|
|
1221
1216
|
return { valid: false, error: mdlError };
|
|
1222
1217
|
}
|
|
1223
1218
|
}
|
|
1224
|
-
else if (request.circuit === 'arc_eligibility') {
|
|
1225
|
-
const inputs = request.inputs;
|
|
1226
|
-
if (inputs.userAddress && !/^0x[a-fA-F0-9]{40}$/.test(inputs.userAddress)) {
|
|
1227
|
-
return { valid: false, error: 'Invalid userAddress format' };
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
1219
|
// The action, decided by the circuit's own entry rather than by naming a
|
|
1231
|
-
// circuit here
|
|
1232
|
-
// which is why a second action-capable circuit had nowhere to be checked.
|
|
1220
|
+
// circuit here, so every action-capable circuit is checked the same way.
|
|
1233
1221
|
{
|
|
1234
1222
|
const action = request.inputs?.action;
|
|
1235
1223
|
const binding = circuitActionBinding(request.circuit);
|
|
@@ -1521,7 +1509,7 @@ function getVerifierContract(providerOrSigner, verifier) {
|
|
|
1521
1509
|
function getDefaultProvider(chainId) {
|
|
1522
1510
|
const rpcUrl = RPC_ENDPOINTS[chainId];
|
|
1523
1511
|
if (!rpcUrl) {
|
|
1524
|
-
throw new Error(`No RPC endpoint configured for chain ${chainId}`);
|
|
1512
|
+
throw new Error(`No RPC endpoint configured for chain ${chainId}. Known chains: ${Object.keys(RPC_ENDPOINTS).join(', ')}`);
|
|
1525
1513
|
}
|
|
1526
1514
|
return createJsonRpcProvider(rpcUrl);
|
|
1527
1515
|
}
|
|
@@ -1670,7 +1658,7 @@ function getVerifierChainId(circuit, customVerifier) {
|
|
|
1670
1658
|
*
|
|
1671
1659
|
* It replaced an `if / else if / else` chain whose final `else` handed every
|
|
1672
1660
|
* unrecognised circuit — and every call that named no circuit at all —
|
|
1673
|
-
* Coinbase's offsets. For `
|
|
1661
|
+
* Coinbase's offsets. For `giwa_attestation` that is not a near miss: its
|
|
1674
1662
|
* public inputs are signal_hash, domain_separator, action_hash, merkle root,
|
|
1675
1663
|
* scope, nullifier, so the nullifier slot under Coinbase's offsets holds the
|
|
1676
1664
|
* MERKLE ROOT, a value identical for every user. Duplicate detection built on
|
|
@@ -1680,15 +1668,16 @@ const PUBLIC_INPUT_OFFSETS = Object.freeze({
|
|
|
1680
1668
|
// signal_hash, signer_list_merkle_root, scope, nullifier
|
|
1681
1669
|
coinbase_attestation: { scope: [64, 95], nullifier: [96, 127] },
|
|
1682
1670
|
// No longer Coinbase's layout. The GIWA circuit gained the EIP-712 pair, so
|
|
1683
|
-
// it reads
|
|
1684
|
-
//
|
|
1671
|
+
// it reads: signal_hash, domain_separator, action_hash, merkle root, then
|
|
1672
|
+
// scope and nullifier 64 bytes further along than Coinbase.
|
|
1685
1673
|
giwa_attestation: { scope: [128, 159], nullifier: [160, 191] },
|
|
1674
|
+
// signal_hash, domain_separator, action_hash, scope, nullifier: GIWA's
|
|
1675
|
+
// order without the merkle root, since there is no issuer list.
|
|
1676
|
+
dojang_playground: { scope: [96, 127], nullifier: [128, 159] },
|
|
1686
1677
|
// country_list, country_list_length and is_included sit before scope.
|
|
1687
1678
|
coinbase_country_attestation: { scope: [86, 117], nullifier: [118, 149] },
|
|
1688
1679
|
// pubkey_modulus_limbs and the bounded domain sit before scope.
|
|
1689
1680
|
oidc_domain_attestation: { scope: [83, 114], nullifier: [115, 146] },
|
|
1690
|
-
// signal_hash, domain_separator, action_hash, merkle root, then the pair.
|
|
1691
|
-
arc_eligibility: { scope: [128, 159], nullifier: [160, 191] },
|
|
1692
1681
|
// The Korea mDL circuits open with the pair; signal_hash is commented out.
|
|
1693
1682
|
mdl_kr_ownership: { scope: [0, 31], nullifier: [32, 63] },
|
|
1694
1683
|
mdl_kr_age: { scope: [0, 31], nullifier: [32, 63] },
|
|
@@ -3069,9 +3058,9 @@ class ProofportSDK {
|
|
|
3069
3058
|
* ```
|
|
3070
3059
|
*/
|
|
3071
3060
|
// Which circuits need a wallet signature, from the one record in
|
|
3072
|
-
// src/circuits.ts.
|
|
3073
|
-
//
|
|
3074
|
-
//
|
|
3061
|
+
// src/circuits.ts. A typed-out list here once missed a circuit, so a dapp
|
|
3062
|
+
// calling createRelayRequest() for it with no signer got no local error and
|
|
3063
|
+
// a relay 401 two round trips later.
|
|
3075
3064
|
static needsWalletSignature(circuit) {
|
|
3076
3065
|
return CIRCUIT_NEEDS_WALLET_SIGNATURE[circuit] === true;
|
|
3077
3066
|
}
|
|
@@ -3096,9 +3085,7 @@ class ProofportSDK {
|
|
|
3096
3085
|
// from inside the mobile app -- a screen the dapp developer never sees,
|
|
3097
3086
|
// reached after the person has already scanned a QR code.
|
|
3098
3087
|
//
|
|
3099
|
-
//
|
|
3100
|
-
// second circuit that can carry an action; the id belongs in the table,
|
|
3101
|
-
// not in the check.
|
|
3088
|
+
// The circuit id belongs in the table, not in the check.
|
|
3102
3089
|
{
|
|
3103
3090
|
const action = inputs.action;
|
|
3104
3091
|
const binding = circuitActionBinding(circuit);
|