@zkproofport-app/sdk 0.3.0 → 0.3.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/README.md +93 -9
- package/dist/circuits.d.ts +39 -2
- package/dist/circuits.esm.js +47 -13
- package/dist/circuits.esm.js.map +1 -1
- package/dist/circuits.js +48 -12
- package/dist/circuits.js.map +1 -1
- package/dist/circuits.mjs +47 -13
- package/dist/circuits.mjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.esm.js +115 -35
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +116 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +115 -35
- package/dist/index.mjs.map +1 -1
- package/dist/typedAction.d.ts +5 -0
- package/dist/typedAction.esm.js +5 -0
- package/dist/typedAction.esm.js.map +1 -1
- package/dist/typedAction.js +5 -0
- package/dist/typedAction.js.map +1 -1
- package/dist/typedAction.mjs +5 -0
- package/dist/typedAction.mjs.map +1 -1
- package/dist/types.d.ts +24 -3
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -91,16 +91,22 @@ const CIRCUIT_METADATA = {
|
|
|
91
91
|
},
|
|
92
92
|
// Planned circuit — the identifier is reserved and the circuit exists, but it
|
|
93
93
|
// is not officially supported yet. See CIRCUIT_SUPPORT_STATUS in ./circuits.
|
|
94
|
-
// Forked from coinbase_attestation
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
94
|
+
// Forked from coinbase_attestation, then given the EIP-712 pair and a
|
|
95
|
+
// branch: with an action the wallet signs the typed data and signal_hash is
|
|
96
|
+
// zero; without one it signs signal_hash and the pair is zero. Read off the
|
|
97
|
+
// compiled ABI (6 x [u8; 32] = 192): signal_hash 0-31, domain_separator
|
|
98
|
+
// 32-63, action_hash 64-95, signer_list_merkle_root 96-127, scope 128-159,
|
|
99
|
+
// nullifier 160-191 -- the same order as arc_eligibility.
|
|
100
|
+
//
|
|
101
|
+
// Unlike Arc, signal_hash is NOT part of this circuit's nullifier. That one
|
|
102
|
+
// derives its nullifier from a public input nobody signs and nothing
|
|
103
|
+
// constrains, so one wallet can mint as many nullifiers as it likes; here
|
|
104
|
+
// the secret is the address plus a constant compiled into the circuit.
|
|
99
105
|
giwa_attestation: {
|
|
100
106
|
name: 'GIWA Attestation',
|
|
101
|
-
description: 'Prove GIWA identity verification (planned — not officially supported yet)',
|
|
102
|
-
publicInputsCount:
|
|
103
|
-
publicInputNames: ['signal_hash', 'signer_list_merkle_root', 'scope', 'nullifier'],
|
|
107
|
+
description: 'Prove GIWA identity verification, optionally authorizing one EIP-712 action (planned — not officially supported yet)',
|
|
108
|
+
publicInputsCount: 192,
|
|
109
|
+
publicInputNames: ['signal_hash', 'domain_separator', 'action_hash', 'signer_list_merkle_root', 'scope', 'nullifier'],
|
|
104
110
|
},
|
|
105
111
|
// Korea Mobile ID (mDL) circuits. Planned — not officially supported yet.
|
|
106
112
|
// Counts are byte-flattened field totals from the compiled circuit ABIs:
|
|
@@ -167,6 +173,11 @@ const RPC_ENDPOINTS = {
|
|
|
167
173
|
84532: 'https://sepolia.base.org', // Base Sepolia
|
|
168
174
|
8453: 'https://mainnet.base.org', // Base Mainnet
|
|
169
175
|
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
|
|
170
181
|
};
|
|
171
182
|
/**
|
|
172
183
|
* Default proof request expiration time in milliseconds.
|
|
@@ -374,7 +385,7 @@ const CIRCUIT_IDS = Object.freeze({
|
|
|
374
385
|
COINBASE_COUNTRY_ATTESTATION: 'coinbase_country_attestation',
|
|
375
386
|
/** OIDC email-domain attestation. Officially supported. */
|
|
376
387
|
OIDC_DOMAIN_ATTESTATION: 'oidc_domain_attestation',
|
|
377
|
-
/** Arc eligibility:
|
|
388
|
+
/** Arc eligibility: exact EIP-712 action authorization. Experimental on Arc Testnet. */
|
|
378
389
|
ARC_ELIGIBILITY: 'arc_eligibility',
|
|
379
390
|
/** GIWA attestation. Planned — not officially supported yet. */
|
|
380
391
|
GIWA_ATTESTATION: 'giwa_attestation',
|
|
@@ -405,7 +416,11 @@ const CIRCUIT_SUPPORT_STATUS = Object.freeze({
|
|
|
405
416
|
coinbase_country_attestation: 'supported',
|
|
406
417
|
oidc_domain_attestation: 'supported',
|
|
407
418
|
arc_eligibility: 'experimental',
|
|
408
|
-
|
|
419
|
+
// Experimental, not planned: the circuit is built, its verifier is
|
|
420
|
+
// deployed on GIWA Sepolia and the demo requests it. What keeps it out of
|
|
421
|
+
// 'supported' is that it is testnet-only, so the app offers it behind
|
|
422
|
+
// Developer Mode and reads its files from main rather than a release tag.
|
|
423
|
+
giwa_attestation: 'experimental',
|
|
409
424
|
mdl_kr_ownership: 'planned',
|
|
410
425
|
mdl_kr_age: 'planned',
|
|
411
426
|
mdl_kr_region: 'planned',
|
|
@@ -517,18 +532,44 @@ function getCircuitSupportStatus(circuit) {
|
|
|
517
532
|
}
|
|
518
533
|
return CIRCUIT_SUPPORT_STATUS[circuit];
|
|
519
534
|
}
|
|
535
|
+
const CIRCUIT_ACTION_BINDING = {
|
|
536
|
+
coinbase_attestation: 'none',
|
|
537
|
+
coinbase_country_attestation: 'none',
|
|
538
|
+
// The signature lives inside the identity token; no wallet, no action.
|
|
539
|
+
oidc_domain_attestation: 'none',
|
|
540
|
+
// Both branch in-circuit: with an action the wallet signs the typed data,
|
|
541
|
+
// without one it personal_signs the request's signal hash. Arc read
|
|
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',
|
|
546
|
+
giwa_attestation: 'optional',
|
|
547
|
+
mdl_kr_ownership: 'none',
|
|
548
|
+
mdl_kr_age: 'none',
|
|
549
|
+
mdl_kr_region: 'none',
|
|
550
|
+
};
|
|
551
|
+
/*
|
|
552
|
+
* The union is declared above rather than derived from the table's values.
|
|
553
|
+
*
|
|
554
|
+
* Deriving it narrowed the type to whatever the table happens to hold today --
|
|
555
|
+
* 'none' | 'optional' -- and TypeScript then called every `=== 'required'`
|
|
556
|
+
* check impossible. The publish typecheck caught it; `rollup` and `vitest` did
|
|
557
|
+
* not. 'required' is a state a circuit can legitimately be in (an EIP-712 path
|
|
558
|
+
* and nothing else), so the union says so and the table is checked against it.
|
|
559
|
+
*/
|
|
520
560
|
/**
|
|
521
|
-
*
|
|
561
|
+
* How this circuit treats an action, or an error naming the id that is not one.
|
|
522
562
|
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
* what. The relay declares its OWN copy on purpose and does not import this
|
|
526
|
-
* one: it is a security boundary, and an auth policy that a published package
|
|
527
|
-
* can change is not a policy. A test in the relay asserts the two agree.
|
|
528
|
-
*
|
|
529
|
-
* `satisfies` and not an annotation, so a circuit added above is a compile
|
|
530
|
-
* error here until somebody decides which it is.
|
|
563
|
+
* No default: a circuit nobody has classified must not inherit 'none', which
|
|
564
|
+
* would silently drop an action a dapp meant to bind.
|
|
531
565
|
*/
|
|
566
|
+
function circuitActionBinding(circuit) {
|
|
567
|
+
const binding = CIRCUIT_ACTION_BINDING[circuit];
|
|
568
|
+
if (!binding) {
|
|
569
|
+
throw new Error(`Unknown circuit '${circuit}'. Expected: ${ALL_CIRCUIT_IDS.join(', ')}`);
|
|
570
|
+
}
|
|
571
|
+
return binding;
|
|
572
|
+
}
|
|
532
573
|
const CIRCUIT_NEEDS_WALLET_SIGNATURE = {
|
|
533
574
|
coinbase_attestation: true,
|
|
534
575
|
coinbase_country_attestation: true,
|
|
@@ -541,13 +582,22 @@ const CIRCUIT_NEEDS_WALLET_SIGNATURE = {
|
|
|
541
582
|
mdl_kr_ownership: false,
|
|
542
583
|
mdl_kr_age: false,
|
|
543
584
|
mdl_kr_region: false,
|
|
544
|
-
|
|
585
|
+
// A GIWA-attested wallet signing an EIP-712 action, the same shape as Arc.
|
|
586
|
+
// This read `false` while the circuit had always verified a wallet signature
|
|
587
|
+
// -- it sat under the Web2 comment above and was never revisited. A request
|
|
588
|
+
// for it would have reached the relay with no signer.
|
|
589
|
+
giwa_attestation: true,
|
|
545
590
|
};
|
|
546
591
|
|
|
547
592
|
const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
548
593
|
/**
|
|
549
594
|
* The reason the value is not a usable action, or `null` when it is one.
|
|
550
595
|
*
|
|
596
|
+
* Structural validation only, not application authorization policy. Consumers
|
|
597
|
+
* (e.g. EligibilityGate) must independently enforce the expected chain, verifying
|
|
598
|
+
* contract, actor, action parameters, nonce and deadline as applicable.
|
|
599
|
+
* EIP-712 signs only fields declared in types, not extra message properties.
|
|
600
|
+
*
|
|
551
601
|
* A string rather than a boolean because every caller shows it to somebody —
|
|
552
602
|
* a dapp developer reading a validation error, or a person looking at the
|
|
553
603
|
* mobile app's demo screen.
|
|
@@ -1171,9 +1221,26 @@ function validateProofRequest(request) {
|
|
|
1171
1221
|
if (inputs.userAddress && !/^0x[a-fA-F0-9]{40}$/.test(inputs.userAddress)) {
|
|
1172
1222
|
return { valid: false, error: 'Invalid userAddress format' };
|
|
1173
1223
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1224
|
+
}
|
|
1225
|
+
// The action, decided by the circuit's own entry rather than by naming a
|
|
1226
|
+
// circuit here. This used to be part of the arc_eligibility branch above,
|
|
1227
|
+
// which is why a second action-capable circuit had nowhere to be checked.
|
|
1228
|
+
{
|
|
1229
|
+
const action = request.inputs?.action;
|
|
1230
|
+
const binding = circuitActionBinding(request.circuit);
|
|
1231
|
+
if (binding === 'none') {
|
|
1232
|
+
if (action !== undefined) {
|
|
1233
|
+
return {
|
|
1234
|
+
valid: false,
|
|
1235
|
+
error: `${request.circuit} has no action inputs, so an action sent with it would not be proved. Refusing rather than dropping it.`,
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
else if (binding === 'required' || action !== undefined) {
|
|
1240
|
+
const actionError = validateTypedAction(action);
|
|
1241
|
+
if (actionError) {
|
|
1242
|
+
return { valid: false, error: actionError };
|
|
1243
|
+
}
|
|
1177
1244
|
}
|
|
1178
1245
|
}
|
|
1179
1246
|
// Check expiry
|
|
@@ -4474,8 +4541,10 @@ function getVerifierChainId(circuit, customVerifier) {
|
|
|
4474
4541
|
const PUBLIC_INPUT_OFFSETS = Object.freeze({
|
|
4475
4542
|
// signal_hash, signer_list_merkle_root, scope, nullifier
|
|
4476
4543
|
coinbase_attestation: { scope: [64, 95], nullifier: [96, 127] },
|
|
4477
|
-
//
|
|
4478
|
-
|
|
4544
|
+
// No longer Coinbase's layout. The GIWA circuit gained the EIP-712 pair, so
|
|
4545
|
+
// it reads like Arc: signal_hash, domain_separator, action_hash, merkle
|
|
4546
|
+
// root, then scope and nullifier 64 bytes further along than Coinbase.
|
|
4547
|
+
giwa_attestation: { scope: [128, 159], nullifier: [160, 191] },
|
|
4479
4548
|
// country_list, country_list_length and is_included sit before scope.
|
|
4480
4549
|
coinbase_country_attestation: { scope: [86, 117], nullifier: [118, 149] },
|
|
4481
4550
|
// pubkey_modulus_limbs and the bounded domain sit before scope.
|
|
@@ -5884,15 +5953,26 @@ class ProofportSDK {
|
|
|
5884
5953
|
throw new Error(`${mdlError} (circuit: ${circuit})`);
|
|
5885
5954
|
}
|
|
5886
5955
|
}
|
|
5887
|
-
//
|
|
5888
|
-
//
|
|
5889
|
-
//
|
|
5890
|
-
//
|
|
5891
|
-
//
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5956
|
+
// The action, per the circuit's entry in CIRCUIT_ACTION_BINDING. Checked
|
|
5957
|
+
// here, before a challenge is burned, because the alternative is a refusal
|
|
5958
|
+
// from inside the mobile app -- a screen the dapp developer never sees,
|
|
5959
|
+
// reached after the person has already scanned a QR code.
|
|
5960
|
+
//
|
|
5961
|
+
// This named arc_eligibility directly until giwa_attestation became the
|
|
5962
|
+
// second circuit that can carry an action; the id belongs in the table,
|
|
5963
|
+
// not in the check.
|
|
5964
|
+
{
|
|
5965
|
+
const action = inputs.action;
|
|
5966
|
+
const binding = circuitActionBinding(circuit);
|
|
5967
|
+
if (binding === 'none' && action !== undefined) {
|
|
5968
|
+
throw new Error(`${circuit} has no action inputs, so an action sent with it would not be proved. ` +
|
|
5969
|
+
'Remove it, or use a circuit that binds one.');
|
|
5970
|
+
}
|
|
5971
|
+
if (binding === 'required' || (binding === 'optional' && action !== undefined)) {
|
|
5972
|
+
const actionError = validateTypedAction(action);
|
|
5973
|
+
if (actionError) {
|
|
5974
|
+
throw new Error(`${actionError} (circuit: ${circuit})`);
|
|
5975
|
+
}
|
|
5896
5976
|
}
|
|
5897
5977
|
}
|
|
5898
5978
|
// Return target: validated before we burn a challenge, so a typo surfaces
|
|
@@ -6220,5 +6300,5 @@ class ProofportSDK {
|
|
|
6220
6300
|
}
|
|
6221
6301
|
}
|
|
6222
6302
|
|
|
6223
|
-
export { ALL_CIRCUIT_IDS, CIRCUIT_IDS, CIRCUIT_SUPPORT_STATUS, CIRCUIT_VK_PATHS, COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, EXPERIMENTAL_CIRCUIT_IDS, MDL_KR_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, PLANNED_CIRCUIT_IDS, ProofportSDK, SUPPORTED_CIRCUIT_IDS, ProofportSDK as default, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs, getCircuitSupportStatus, isCircuitId, isSupportedCircuitId, parseTypedAction, validateTypedAction, verifyProofOffChain };
|
|
6303
|
+
export { ALL_CIRCUIT_IDS, CIRCUIT_ACTION_BINDING, CIRCUIT_IDS, CIRCUIT_SUPPORT_STATUS, CIRCUIT_VK_PATHS, COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, EXPERIMENTAL_CIRCUIT_IDS, MDL_KR_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, PLANNED_CIRCUIT_IDS, ProofportSDK, SUPPORTED_CIRCUIT_IDS, circuitActionBinding, ProofportSDK as default, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs, getCircuitSupportStatus, isCircuitId, isSupportedCircuitId, parseTypedAction, validateTypedAction, verifyProofOffChain };
|
|
6224
6304
|
//# sourceMappingURL=index.mjs.map
|