@zkproofport-ai/sdk 0.4.0 → 0.5.0
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 +26 -13
- package/dist/action.js +1 -1
- package/dist/action.js.map +1 -1
- package/dist/circuits.d.ts +25 -102
- package/dist/circuits.d.ts.map +1 -1
- package/dist/circuits.js +18 -110
- package/dist/circuits.js.map +1 -1
- package/dist/extract.d.ts +12 -5
- package/dist/extract.d.ts.map +1 -1
- package/dist/extract.js +12 -5
- package/dist/extract.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/oidc-inputs.d.ts +53 -6
- package/dist/oidc-inputs.d.ts.map +1 -1
- package/dist/oidc-inputs.js +126 -29
- package/dist/oidc-inputs.js.map +1 -1
- package/dist/types.d.ts +10 -20
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/verify.d.ts +30 -6
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +63 -16
- package/dist/verify.js.map +1 -1
- package/package.json +2 -1
- package/dist/typedAction.d.ts +0 -3
- package/dist/typedAction.d.ts.map +0 -1
- package/dist/typedAction.js +0 -50
- package/dist/typedAction.js.map +0 -1
package/dist/verify.js
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
import { ethers } from 'ethers';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { RPC_ENDPOINTS, verifyProofOnChain } from '@zkproofport-app/core';
|
|
3
|
+
import { isCanonicalCircuitId } from './circuits.js';
|
|
4
|
+
/**
|
|
5
|
+
* Public RPC per chain this SDK verifies on: core's endpoints plus the two
|
|
6
|
+
* Ethereum chains the AI server reports OIDC/Coinbase proofs on (core 0.2.0
|
|
7
|
+
* lists only Base, Base Sepolia and GIWA Sepolia).
|
|
8
|
+
*
|
|
9
|
+
* Never the `rpcUrl` a prove response carries. A response is written by the
|
|
10
|
+
* server whose proof is being checked; an RPC it names can answer `true` to
|
|
11
|
+
* any call as easily as a verifier address it names can. Checked 2026-10-27:
|
|
12
|
+
* both publicnode URLs answer `eth_chainId` with their chain and return code
|
|
13
|
+
* for the pinned verifiers (11155111 0x7fc2…4a8a, 1 0xf3d5…9d0c).
|
|
14
|
+
*/
|
|
15
|
+
export const VERIFY_RPC_URLS = Object.freeze({
|
|
16
|
+
...RPC_ENDPOINTS,
|
|
17
|
+
1: 'https://ethereum-rpc.publicnode.com',
|
|
18
|
+
11155111: 'https://ethereum-sepolia-rpc.publicnode.com',
|
|
19
|
+
});
|
|
20
|
+
/** The RPC to verify on `chainId`. Throws naming the chain when there is none. */
|
|
21
|
+
export function verifyRpcUrl(chainId) {
|
|
22
|
+
const url = VERIFY_RPC_URLS[chainId];
|
|
23
|
+
if (!url) {
|
|
24
|
+
throw new Error(`No RPC to verify on chain ${chainId}. Known chains: ${Object.keys(VERIFY_RPC_URLS).join(', ')}.`);
|
|
25
|
+
}
|
|
26
|
+
return url;
|
|
27
|
+
}
|
|
5
28
|
/**
|
|
6
29
|
* Split concatenated public inputs hex into array of bytes32 values.
|
|
7
30
|
*/
|
|
@@ -14,28 +37,52 @@ function splitPublicInputs(publicInputsHex) {
|
|
|
14
37
|
return chunks;
|
|
15
38
|
}
|
|
16
39
|
/**
|
|
17
|
-
* Verify a
|
|
40
|
+
* Verify a proof on-chain against ZKProofport's pinned verifier.
|
|
41
|
+
*
|
|
42
|
+
* What the response says about its verifier is a claim, never an instruction:
|
|
43
|
+
* - the contract is core's pinned deployment of `circuit` on `claim.chainId`
|
|
44
|
+
* (`PINNED_VERIFIERS`); a chain with no deployment of that circuit is refused;
|
|
45
|
+
* - `claim.verifierAddress` is compared with it, and a different address is
|
|
46
|
+
* refused rather than called;
|
|
47
|
+
* - the RPC is {@link VERIFY_RPC_URLS}, not the response's `rpcUrl`.
|
|
48
|
+
* Core also runs the circuit's issuer check before the contract call: the OIDC
|
|
49
|
+
* provider key against the live Google/Microsoft JWKS, and the signer-list root
|
|
50
|
+
* for Coinbase, Country and GIWA. Any failure is `{ valid: false, error }`.
|
|
18
51
|
*
|
|
19
|
-
* @param
|
|
52
|
+
* @param circuit - Canonical circuit id the proof was generated for
|
|
53
|
+
* @param claim - `verification` from the ProveResponse
|
|
20
54
|
* @param proof - Raw proof hex
|
|
21
55
|
* @param publicInputs - Concatenated public inputs hex
|
|
22
56
|
*/
|
|
23
|
-
export async function verifyOnChain(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
57
|
+
export async function verifyOnChain(circuit, claim, proof, publicInputs) {
|
|
58
|
+
if (!isCanonicalCircuitId(circuit)) {
|
|
59
|
+
return { valid: false, error: `Unknown circuit '${circuit}'; cannot verify it on-chain.` };
|
|
60
|
+
}
|
|
61
|
+
const chainId = claim.chainId;
|
|
62
|
+
if (typeof chainId !== 'number' || !Number.isSafeInteger(chainId) || chainId <= 0) {
|
|
63
|
+
return { valid: false, error: `The response names chain '${String(chainId)}', which is not a chain id.` };
|
|
64
|
+
}
|
|
65
|
+
if (typeof claim.verifierAddress !== 'string') {
|
|
66
|
+
return { valid: false, error: 'The response names no verifier address; refusing to guess one.' };
|
|
67
|
+
}
|
|
68
|
+
let rpcUrl;
|
|
27
69
|
try {
|
|
28
|
-
|
|
29
|
-
return { valid: result === true };
|
|
70
|
+
rpcUrl = verifyRpcUrl(chainId);
|
|
30
71
|
}
|
|
31
72
|
catch (error) {
|
|
32
|
-
|
|
33
|
-
|
|
73
|
+
return { valid: false, error: error instanceof Error ? error.message : String(error) };
|
|
74
|
+
}
|
|
75
|
+
const provider = new ethers.JsonRpcProvider(rpcUrl, chainId, { staticNetwork: true });
|
|
76
|
+
const publicInputsArray = splitPublicInputs(publicInputs);
|
|
77
|
+
const result = await verifyProofOnChain(circuit, { proofHex: proof, publicInputsHex: publicInputsArray, numPublicInputs: publicInputsArray.length }, provider, { chainId }, { verifierAddress: claim.verifierAddress, chainId });
|
|
78
|
+
if (result.error !== undefined) {
|
|
79
|
+
return { valid: false, error: `Verification failed: ${result.error}` };
|
|
34
80
|
}
|
|
81
|
+
return { valid: result.valid === true };
|
|
35
82
|
}
|
|
36
83
|
/**
|
|
37
|
-
* Verify a proof
|
|
38
|
-
*
|
|
84
|
+
* Verify a proof from a ProveResponse against the pinned verifier for its
|
|
85
|
+
* circuit and chain. See {@link verifyOnChain} for what is and is not trusted.
|
|
39
86
|
*
|
|
40
87
|
* @param result - The full ProveResponse from generateProof()
|
|
41
88
|
* @returns VerifyResult
|
|
@@ -51,6 +98,6 @@ export async function verifyProof(result) {
|
|
|
51
98
|
if (!result.verification) {
|
|
52
99
|
return { valid: false, error: 'No verification info in response (verifier not deployed on this network)' };
|
|
53
100
|
}
|
|
54
|
-
return verifyOnChain(result.verification, result.proof, result.publicInputs);
|
|
101
|
+
return verifyOnChain(result.circuit, result.verification, result.proof, result.publicInputs);
|
|
55
102
|
}
|
|
56
103
|
//# sourceMappingURL=verify.js.map
|
package/dist/verify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zkproofport-ai/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "TypeScript SDK for ZKProofport Coinbase KYC, country, OIDC and GIWA attestation proofs. Optional EIP-712 action binding for GIWA; x402 USDC payments.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sdk",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@x402/core": "^2.25.0",
|
|
39
39
|
"@x402/evm": "^2.25.0",
|
|
40
|
+
"@zkproofport-app/core": "0.3.0",
|
|
40
41
|
"ethers": "^6.13.0",
|
|
41
42
|
"viem": "^2.45.2"
|
|
42
43
|
},
|
package/dist/typedAction.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"typedAction.d.ts","sourceRoot":"","sources":["../src/typedAction.ts"],"names":[],"mappings":"AAsBA,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAkDjE"}
|
package/dist/typedAction.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
2
|
-
/** The reason the value is not a usable action, or `null` when it is one. */
|
|
3
|
-
export function validateTypedAction(value) {
|
|
4
|
-
if (value === undefined || value === null) {
|
|
5
|
-
return 'action is required. The circuit proves that a wallet authorised ONE EIP-712 action; there is nothing to prove without it.';
|
|
6
|
-
}
|
|
7
|
-
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
8
|
-
return 'action must be an object.';
|
|
9
|
-
}
|
|
10
|
-
const a = value;
|
|
11
|
-
const d = a.domain;
|
|
12
|
-
if (!d || typeof d !== 'object') {
|
|
13
|
-
return 'action.domain is required.';
|
|
14
|
-
}
|
|
15
|
-
for (const k of ['name', 'version', 'verifyingContract']) {
|
|
16
|
-
if (typeof d[k] !== 'string' || !d[k]) {
|
|
17
|
-
return `action.domain.${k} must be a non-empty string.`;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
if (typeof d.chainId !== 'number' || !Number.isInteger(d.chainId)) {
|
|
21
|
-
return 'action.domain.chainId must be an integer.';
|
|
22
|
-
}
|
|
23
|
-
if (!ADDRESS.test(d.verifyingContract)) {
|
|
24
|
-
return 'action.domain.verifyingContract must be a 20-byte hex address.';
|
|
25
|
-
}
|
|
26
|
-
if (!a.types || typeof a.types !== 'object' || Array.isArray(a.types)) {
|
|
27
|
-
return 'action.types is required.';
|
|
28
|
-
}
|
|
29
|
-
if (typeof a.primaryType !== 'string' || !a.primaryType) {
|
|
30
|
-
return 'action.primaryType is required.';
|
|
31
|
-
}
|
|
32
|
-
if (!(a.primaryType in a.types)) {
|
|
33
|
-
return `action.primaryType '${a.primaryType}' is not one of the structs in action.types.`;
|
|
34
|
-
}
|
|
35
|
-
if (!a.message || typeof a.message !== 'object' || Array.isArray(a.message)) {
|
|
36
|
-
return 'action.message is required.';
|
|
37
|
-
}
|
|
38
|
-
const fields = a.types[a.primaryType];
|
|
39
|
-
if (!Array.isArray(fields) || fields.length === 0) {
|
|
40
|
-
return `action.types.${a.primaryType} declares no fields.`;
|
|
41
|
-
}
|
|
42
|
-
const missing = fields
|
|
43
|
-
.map(f => f?.name)
|
|
44
|
-
.filter(n => typeof n === 'string' && !(n in a.message));
|
|
45
|
-
if (missing.length) {
|
|
46
|
-
return `action.message is missing: ${missing.join(', ')}`;
|
|
47
|
-
}
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
//# sourceMappingURL=typedAction.js.map
|
package/dist/typedAction.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"typedAction.js","sourceRoot":"","sources":["../src/typedAction.ts"],"names":[],"mappings":"AAoBA,MAAM,OAAO,GAAG,qBAAqB,CAAC;AAEtC,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,2HAA2H,CAAC;IACrI,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,OAAO,2BAA2B,CAAC;IACrC,CAAC;IACD,MAAM,CAAC,GAAG,KAA6B,CAAC;IAExC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,4BAA4B,CAAC;IACtC,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,mBAAmB,CAAU,EAAE,CAAC;QAClE,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,OAAO,iBAAiB,CAAC,8BAA8B,CAAC;QAC1D,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAClE,OAAO,2CAA2C,CAAC;IACrD,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACvC,OAAO,gEAAgE,CAAC;IAC1E,CAAC;IAED,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACtE,OAAO,2BAA2B,CAAC;IACrC,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACxD,OAAO,iCAAiC,CAAC;IAC3C,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,uBAAuB,CAAC,CAAC,WAAW,8CAA8C,CAAC;IAC5F,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5E,OAAO,6BAA6B,CAAC;IACvC,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,gBAAgB,CAAC,CAAC,WAAW,sBAAsB,CAAC;IAC7D,CAAC;IACD,MAAM,OAAO,GAAG,MAAM;SACnB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC;SACjB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,IAAK,CAAC,CAAC,OAAkB,CAAC,CAAC,CAAC;IACvE,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,8BAA8B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC5D,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|