noah-avalanche-sdk 0.1.2

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.
Files changed (103) hide show
  1. package/README.md +892 -0
  2. package/dist/core/APIClient.d.ts +71 -0
  3. package/dist/core/APIClient.d.ts.map +1 -0
  4. package/dist/core/APIClient.js +92 -0
  5. package/dist/core/APIClient.js.map +1 -0
  6. package/dist/core/ContractClient.d.ts +38 -0
  7. package/dist/core/ContractClient.d.ts.map +1 -0
  8. package/dist/core/ContractClient.js +209 -0
  9. package/dist/core/ContractClient.js.map +1 -0
  10. package/dist/core/NoahSDK.d.ts +43 -0
  11. package/dist/core/NoahSDK.d.ts.map +1 -0
  12. package/dist/core/NoahSDK.js +93 -0
  13. package/dist/core/NoahSDK.js.map +1 -0
  14. package/dist/core/WalletAdapter.d.ts +188 -0
  15. package/dist/core/WalletAdapter.d.ts.map +1 -0
  16. package/dist/core/WalletAdapter.js +425 -0
  17. package/dist/core/WalletAdapter.js.map +1 -0
  18. package/dist/hooks/index.d.ts +18 -0
  19. package/dist/hooks/index.d.ts.map +1 -0
  20. package/dist/hooks/index.js +15 -0
  21. package/dist/hooks/index.js.map +1 -0
  22. package/dist/hooks/useCredentials.d.ts +136 -0
  23. package/dist/hooks/useCredentials.d.ts.map +1 -0
  24. package/dist/hooks/useCredentials.js +217 -0
  25. package/dist/hooks/useCredentials.js.map +1 -0
  26. package/dist/hooks/useProtocol.d.ts +117 -0
  27. package/dist/hooks/useProtocol.d.ts.map +1 -0
  28. package/dist/hooks/useProtocol.js +165 -0
  29. package/dist/hooks/useProtocol.js.map +1 -0
  30. package/dist/hooks/useUser.d.ts +159 -0
  31. package/dist/hooks/useUser.d.ts.map +1 -0
  32. package/dist/hooks/useUser.js +188 -0
  33. package/dist/hooks/useUser.js.map +1 -0
  34. package/dist/index.d.ts +28 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +26 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/issuer/IssuerClient.d.ts +98 -0
  39. package/dist/issuer/IssuerClient.d.ts.map +1 -0
  40. package/dist/issuer/IssuerClient.js +159 -0
  41. package/dist/issuer/IssuerClient.js.map +1 -0
  42. package/dist/protocol/ProtocolClient.d.ts +124 -0
  43. package/dist/protocol/ProtocolClient.d.ts.map +1 -0
  44. package/dist/protocol/ProtocolClient.js +265 -0
  45. package/dist/protocol/ProtocolClient.js.map +1 -0
  46. package/dist/protocol/RequirementsManager.d.ts +9 -0
  47. package/dist/protocol/RequirementsManager.d.ts.map +1 -0
  48. package/dist/protocol/RequirementsManager.js +9 -0
  49. package/dist/protocol/RequirementsManager.js.map +1 -0
  50. package/dist/user/ProofGenerator.d.ts +49 -0
  51. package/dist/user/ProofGenerator.d.ts.map +1 -0
  52. package/dist/user/ProofGenerator.js +80 -0
  53. package/dist/user/ProofGenerator.js.map +1 -0
  54. package/dist/user/UserClient.d.ts +191 -0
  55. package/dist/user/UserClient.d.ts.map +1 -0
  56. package/dist/user/UserClient.js +338 -0
  57. package/dist/user/UserClient.js.map +1 -0
  58. package/dist/utils/credentials.d.ts +47 -0
  59. package/dist/utils/credentials.d.ts.map +1 -0
  60. package/dist/utils/credentials.js +99 -0
  61. package/dist/utils/credentials.js.map +1 -0
  62. package/dist/utils/identity.d.ts +22 -0
  63. package/dist/utils/identity.d.ts.map +1 -0
  64. package/dist/utils/identity.js +35 -0
  65. package/dist/utils/identity.js.map +1 -0
  66. package/dist/utils/jurisdiction.d.ts +21 -0
  67. package/dist/utils/jurisdiction.d.ts.map +1 -0
  68. package/dist/utils/jurisdiction.js +64 -0
  69. package/dist/utils/jurisdiction.js.map +1 -0
  70. package/dist/utils/mrz.d.ts +16 -0
  71. package/dist/utils/mrz.d.ts.map +1 -0
  72. package/dist/utils/mrz.js +91 -0
  73. package/dist/utils/mrz.js.map +1 -0
  74. package/dist/utils/ocr.d.ts +31 -0
  75. package/dist/utils/ocr.d.ts.map +1 -0
  76. package/dist/utils/ocr.js +69 -0
  77. package/dist/utils/ocr.js.map +1 -0
  78. package/dist/utils/types.d.ts +122 -0
  79. package/dist/utils/types.d.ts.map +1 -0
  80. package/dist/utils/types.js +2 -0
  81. package/dist/utils/types.js.map +1 -0
  82. package/package.json +53 -0
  83. package/src/core/APIClient.ts +165 -0
  84. package/src/core/ContractClient.ts +266 -0
  85. package/src/core/NoahSDK.ts +123 -0
  86. package/src/core/WalletAdapter.ts +546 -0
  87. package/src/hooks/index.ts +31 -0
  88. package/src/hooks/types.d.ts +18 -0
  89. package/src/hooks/useCredentials.ts +359 -0
  90. package/src/hooks/useProtocol.ts +284 -0
  91. package/src/hooks/useUser.ts +331 -0
  92. package/src/index.ts +80 -0
  93. package/src/issuer/IssuerClient.ts +209 -0
  94. package/src/protocol/ProtocolClient.ts +330 -0
  95. package/src/protocol/RequirementsManager.ts +16 -0
  96. package/src/user/ProofGenerator.ts +113 -0
  97. package/src/user/UserClient.ts +440 -0
  98. package/src/utils/credentials.ts +122 -0
  99. package/src/utils/identity.ts +46 -0
  100. package/src/utils/jurisdiction.ts +83 -0
  101. package/src/utils/mrz.ts +113 -0
  102. package/src/utils/ocr.ts +84 -0
  103. package/src/utils/types.ts +144 -0
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "noah-avalanche-sdk",
3
+ "version": "0.1.2",
4
+ "description": "Securely verify your identity using Zero-Knowledge Proofs.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "src"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsc --watch",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "keywords": [
18
+ "defi",
19
+ "kyc",
20
+ "zk-proof",
21
+ "privacy",
22
+ "ethereum",
23
+ "blockchain"
24
+ ],
25
+ "author": "NOAH Protocol",
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "axios": "^1.6.2",
29
+ "ethers": "^6.0.0",
30
+ "tesseract.js": "^5.1.1"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^20.0.0",
34
+ "typescript": "^5.0.0"
35
+ },
36
+ "peerDependencies": {
37
+ "@tanstack/react-query": "^5.0.0",
38
+ "ethers": "^6.0.0",
39
+ "react": "^18.0.0 || ^19.0.0",
40
+ "react-dom": "^18.0.0 || ^19.0.0"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "react": {
44
+ "optional": true
45
+ },
46
+ "react-dom": {
47
+ "optional": true
48
+ },
49
+ "@tanstack/react-query": {
50
+ "optional": true
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,165 @@
1
+ import axios, { type AxiosInstance } from 'axios';
2
+ import type { Proof, Requirements, TransactionResult } from '../utils/types.js';
3
+
4
+ /**
5
+ * APIClient configuration
6
+ */
7
+ export interface APIClientConfig {
8
+ baseURL?: string;
9
+ timeout?: number;
10
+ authToken?: string;
11
+ }
12
+
13
+ /**
14
+ * Proof generation data
15
+ */
16
+ export interface ProofGenerationData {
17
+ credential: {
18
+ age: number;
19
+ jurisdiction: string | number;
20
+ accredited: number; // 0 or 1
21
+ credentialHash: string;
22
+ userAddress?: string;
23
+ };
24
+ requirements: Requirements;
25
+ }
26
+
27
+ /**
28
+ * Proof generation result
29
+ */
30
+ export interface ProofGenerationResult {
31
+ proof: Proof;
32
+ publicSignals: string[]; // 13 elements
33
+ credentialHash: string;
34
+ success: boolean;
35
+ error?: string;
36
+ }
37
+
38
+ /**
39
+ * Access status information
40
+ */
41
+ export interface AccessStatus {
42
+ hasAccess: boolean;
43
+ protocolAddress: string;
44
+ userAddress: string;
45
+ credentialHash?: string;
46
+ }
47
+
48
+ /**
49
+ * Credential status information
50
+ */
51
+ export interface CredentialStatus {
52
+ isValid: boolean;
53
+ credentialHash: string;
54
+ isRevoked: boolean;
55
+ issuer?: string;
56
+ }
57
+
58
+ /**
59
+ * APIClient - Handles backend API interactions
60
+ */
61
+ export class APIClient {
62
+ private client: AxiosInstance;
63
+ private authToken: string | null = null;
64
+
65
+ constructor(config: APIClientConfig = {}) {
66
+ this.client = axios.create({
67
+ baseURL: config.baseURL || 'http://localhost:3000/api/v1',
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ },
71
+ timeout: config.timeout || 30000,
72
+ });
73
+
74
+ if (config.authToken) {
75
+ this.setAuthToken(config.authToken);
76
+ }
77
+
78
+ this.client.interceptors.response.use(
79
+ (response) => response.data as any,
80
+ (error) => {
81
+ const errorMessage = error.response?.data?.error?.message || error.message || 'An error occurred';
82
+ return Promise.reject(new Error(errorMessage));
83
+ }
84
+ );
85
+ }
86
+
87
+ setAuthToken(token: string): void {
88
+ this.authToken = token;
89
+ }
90
+
91
+ async generateProof(proofData: ProofGenerationData): Promise<ProofGenerationResult> {
92
+ try {
93
+ const response = await this.client.post('/proof/generate', proofData) as any;
94
+ return {
95
+ proof: response.proof,
96
+ publicSignals: response.publicSignals || response.publicInputs || [],
97
+ credentialHash: response.credentialHash || proofData.credential.credentialHash,
98
+ success: response.success !== false,
99
+ };
100
+ } catch (error) {
101
+ throw new Error(`Failed to generate proof: ${error}`);
102
+ }
103
+ }
104
+
105
+ async generateAgeProof(data: {
106
+ mrzData: any;
107
+ minAge: number;
108
+ recipientAddress: string;
109
+ }): Promise<ProofGenerationResult> {
110
+ return this.generateProof({
111
+ credential: {
112
+ age: 0,
113
+ jurisdiction: "",
114
+ accredited: 0,
115
+ credentialHash: "",
116
+ userAddress: data.recipientAddress
117
+ },
118
+ requirements: {
119
+ minAge: data.minAge,
120
+ allowedJurisdictions: [],
121
+ requireAccredited: false
122
+ }
123
+ });
124
+ }
125
+
126
+ async getProtocolRequirements(protocolAddress: string): Promise<Requirements> {
127
+ const response = await this.client.get(`/user/protocol/${protocolAddress}/requirements`) as any;
128
+ return {
129
+ minAge: Number(response.minAge),
130
+ allowedJurisdictions: response.allowedJurisdictions || [],
131
+ requireAccredited: response.requireAccredited || false,
132
+ isSet: response.isSet !== false,
133
+ };
134
+ }
135
+
136
+ async checkAccess(protocolAddress: string, userAddress: string): Promise<AccessStatus> {
137
+ const response = await this.client.get(`/user/access/${protocolAddress}/${userAddress}`) as any;
138
+ return {
139
+ hasAccess: response.hasAccess || false,
140
+ protocolAddress,
141
+ userAddress,
142
+ credentialHash: response.credentialHash,
143
+ };
144
+ }
145
+
146
+ async registerCredential(credentialHash: string, userAddress: string): Promise<TransactionResult> {
147
+ const response = await this.client.post('/issuer/credential/register', { credentialHash, userAddress }) as any;
148
+ return { transactionHash: response.transactionHash, receipt: response.receipt || null };
149
+ }
150
+
151
+ async revokeCredential(credentialHash: string): Promise<TransactionResult> {
152
+ const response = await this.client.post('/issuer/credential/revoke', { credentialHash }) as any;
153
+ return { transactionHash: response.transactionHash, receipt: response.receipt || null };
154
+ }
155
+
156
+ async checkCredential(credentialHash: string): Promise<CredentialStatus> {
157
+ const response = await this.client.get(`/issuer/credential/check/${credentialHash}`) as any;
158
+ return {
159
+ isValid: response.isValid || false,
160
+ credentialHash,
161
+ isRevoked: response.isRevoked || false,
162
+ issuer: response.issuer,
163
+ };
164
+ }
165
+ }
@@ -0,0 +1,266 @@
1
+ import {
2
+ ethers,
3
+ BrowserProvider,
4
+ JsonRpcProvider,
5
+ type Provider,
6
+ type Signer,
7
+ type Contract,
8
+ type ContractTransactionResponse,
9
+ type Eip1193Provider
10
+ } from 'ethers';
11
+ import type {
12
+ ContractAddresses,
13
+ Requirements,
14
+ IssuerInfo,
15
+ Proof,
16
+ ZKProof,
17
+ TransactionResult,
18
+ ContractClientConfig,
19
+ EventCallback
20
+ } from '../utils/types.js';
21
+
22
+ /**
23
+ * Contract ABIs (synchronized with production contracts)
24
+ */
25
+ const CREDENTIAL_REGISTRY_ABI = [
26
+ 'function isCredentialValid(bytes32 credentialHash) view returns (bool)',
27
+ 'function credentials(bytes32) view returns (bool)',
28
+ 'function revokedCredentials(bytes32) view returns (bool)',
29
+ 'function trustedIssuers(address) view returns (bool)',
30
+ 'function issuerNames(address) view returns (string)',
31
+ 'function credentialIssuers(bytes32) view returns (address)',
32
+ 'function nullifierOwners(bytes32) view returns (address)',
33
+ 'function userToCredential(address) view returns (bytes32)',
34
+ 'function registerCredential(bytes32 credentialHash, address user)',
35
+ 'function registerNullifier(bytes32 nullifier, bytes32 credentialHash, address user)',
36
+ 'function revokeCredential(bytes32 credentialHash)',
37
+ 'function hasRole(bytes32 role, address account) view returns (bool)',
38
+ 'event CredentialIssued(address indexed user, bytes32 indexed credentialHash, address indexed issuer, uint256 timestamp)',
39
+ 'event CredentialRevoked(bytes32 indexed credentialHash, address indexed issuer, uint256 timestamp)',
40
+ 'event NullifierRegistered(bytes32 indexed nullifier, bytes32 indexed credentialHash, address indexed user)',
41
+ ] as const;
42
+
43
+ const PROTOCOL_ACCESS_CONTROL_ABI = [
44
+ 'function hasAccess(address protocol, address user) view returns (bool)',
45
+ 'function checkAccess(address user) view returns (bool)',
46
+ 'function protocolRequirements(address) view returns (uint256 minAge, bool requireAccredited, bool isSet)',
47
+ 'function userCredentials(address protocol, address user) view returns (bytes32)',
48
+ 'function setRequirements(uint256 minAge, uint256[] memory allowedJurisdictions, bool requireAccredited)',
49
+ 'function verifyAndGrantAccess(uint[2] a, uint[2][2] b, uint[2] c, uint[28] publicSignals, bytes32 credentialHash, address user)',
50
+ 'event AccessGranted(address indexed user, address indexed protocol, bytes32 indexed credentialHash, uint256 timestamp)',
51
+ 'event AccessRevoked(address indexed user, address indexed protocol, uint256 timestamp)',
52
+ 'event RequirementsSet(address indexed protocol, uint256 minAge, uint256[] allowedJurisdictions, bool requireAccredited)',
53
+ ] as const;
54
+
55
+ /**
56
+ * Contract Client Service
57
+ * Handles direct smart contract interactions for read and write operations
58
+ */
59
+ export class ContractClient {
60
+ private provider: Provider | null = null;
61
+ private credentialRegistry: Contract | null = null;
62
+ private protocolAccessControl: Contract | null = null;
63
+ private contractAddresses: ContractAddresses;
64
+ private rpcUrl: string;
65
+
66
+ /**
67
+ * Create a new ContractClient instance
68
+ * @param config - Configuration options including provider, contract addresses, and RPC URL
69
+ */
70
+ constructor(config?: ContractClientConfig) {
71
+ this.contractAddresses = config?.contractAddresses || {
72
+ CredentialRegistry: '0x8E4B7e3f9F3C55DA50aB587168f1d8A011FC8e95',
73
+ ZKVerifier: '0x48392A1bE10b5687c06be11152675215dff14512',
74
+ ProtocolAccessControl: '0xb92f19431617F5B34bFDCb06E3a80533939DD71b',
75
+ };
76
+
77
+ this.rpcUrl = config?.rpcUrl || 'https://avax-fuji.g.alchemy.com/v2/gu3D3rKyivv6bhmb3UbyUSYxThLz7C_c';
78
+
79
+ if (config?.provider) {
80
+ this.initialize(config.provider);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Initialize provider and contracts with robust support for various provider types
86
+ * @param inputProvider - EIP-1193 provider (window.ethereum), Ethers Provider, or custom
87
+ */
88
+ initialize(inputProvider?: any): void {
89
+ if (!inputProvider) {
90
+ this.provider = new JsonRpcProvider(this.rpcUrl);
91
+ } else if (inputProvider.request) {
92
+ // It's an EIP-1193 provider (MetaMask, etc.)
93
+ this.provider = new BrowserProvider(inputProvider as Eip1193Provider);
94
+ } else {
95
+ // Assume it's already an ethers-compatible provider
96
+ this.provider = inputProvider as Provider;
97
+ }
98
+
99
+ this.credentialRegistry = new ethers.Contract(
100
+ this.contractAddresses.CredentialRegistry,
101
+ CREDENTIAL_REGISTRY_ABI,
102
+ this.provider
103
+ );
104
+ this.protocolAccessControl = new ethers.Contract(
105
+ this.contractAddresses.ProtocolAccessControl,
106
+ PROTOCOL_ACCESS_CONTROL_ABI,
107
+ this.provider
108
+ );
109
+ }
110
+
111
+ /**
112
+ * Pre-flight validation for proof inputs
113
+ */
114
+ validateProofInput(publicSignals: (string | number)[]): void {
115
+ if (!publicSignals || publicSignals.length < 28) {
116
+ throw new Error(`Invalid public signals length: expected 28, got ${publicSignals?.length || 0}`);
117
+ }
118
+ // Check isValid bit
119
+ if (publicSignals[25] != "1" && publicSignals[25] != 1) {
120
+ throw new Error("ZK Proof internal validation failed (isValid signal is not 1)");
121
+ }
122
+ }
123
+
124
+ async isCredentialValid(credentialHash: string): Promise<boolean> {
125
+ if (!this.credentialRegistry) this.initialize();
126
+ try {
127
+ return await this.credentialRegistry!.isCredentialValid(credentialHash);
128
+ } catch (error) {
129
+ throw new Error(`Failed to check credential validity: ${error}`);
130
+ }
131
+ }
132
+
133
+ async isNullifierUsed(nullifier: string): Promise<boolean> {
134
+ if (!this.credentialRegistry) this.initialize();
135
+ try {
136
+ const owner = await this.credentialRegistry!.nullifierOwners(nullifier);
137
+ return owner !== ethers.ZeroAddress;
138
+ } catch (error) {
139
+ throw new Error(`Failed to check nullifier status: ${error}`);
140
+ }
141
+ }
142
+
143
+ async getCredentialByUser(userAddress: string): Promise<string> {
144
+ if (!this.credentialRegistry) this.initialize();
145
+ try {
146
+ return await this.credentialRegistry!.userToCredential(userAddress);
147
+ } catch (error) {
148
+ throw new Error(`Failed to get credential by user: ${error}`);
149
+ }
150
+ }
151
+
152
+ async hasAccess(protocolAddress: string, userAddress: string): Promise<boolean> {
153
+ if (!this.protocolAccessControl) this.initialize();
154
+ try {
155
+ return await this.protocolAccessControl!.hasAccess(protocolAddress, userAddress);
156
+ } catch (error) {
157
+ throw new Error(`Failed to check access: ${error}`);
158
+ }
159
+ }
160
+
161
+ async getRequirements(protocolAddress: string): Promise<Requirements> {
162
+ if (!this.protocolAccessControl) this.initialize();
163
+ try {
164
+ const [minAge, requireAccredited, isSet] =
165
+ await this.protocolAccessControl!.protocolRequirements(protocolAddress);
166
+
167
+ return {
168
+ minAge: Number(minAge),
169
+ allowedJurisdictions: [],
170
+ requireAccredited,
171
+ isSet,
172
+ };
173
+ } catch (error) {
174
+ throw new Error(`Failed to get protocol requirements: ${error}`);
175
+ }
176
+ }
177
+
178
+ async getUserCredential(protocolAddress: string, userAddress: string): Promise<string> {
179
+ if (!this.protocolAccessControl) this.initialize();
180
+ try {
181
+ return await this.protocolAccessControl!.userCredentials(protocolAddress, userAddress);
182
+ } catch (error) {
183
+ throw new Error(`Failed to get user credential: ${error}`);
184
+ }
185
+ }
186
+
187
+ async registerCredential(
188
+ signer: Signer,
189
+ credentialHash: string,
190
+ userAddress: string
191
+ ): Promise<TransactionResult> {
192
+ if (!signer) throw new Error('Signer is required');
193
+ const contract = new ethers.Contract(this.contractAddresses.CredentialRegistry, CREDENTIAL_REGISTRY_ABI, signer);
194
+ try {
195
+ const tx = await contract.registerCredential(credentialHash, userAddress) as ContractTransactionResponse;
196
+ const receipt = await tx.wait();
197
+ return { transactionHash: tx.hash, receipt };
198
+ } catch (error) {
199
+ throw new Error(`Failed to register credential: ${error}`);
200
+ }
201
+ }
202
+
203
+ async revokeCredential(
204
+ signer: Signer,
205
+ credentialHash: string
206
+ ): Promise<TransactionResult> {
207
+ if (!signer) throw new Error('Signer is required');
208
+ const contract = new ethers.Contract(this.contractAddresses.CredentialRegistry, CREDENTIAL_REGISTRY_ABI, signer);
209
+ try {
210
+ const tx = await contract.revokeCredential(credentialHash) as ContractTransactionResponse;
211
+ const receipt = await tx.wait();
212
+ return { transactionHash: tx.hash, receipt };
213
+ } catch (error) {
214
+ throw new Error(`Failed to revoke credential: ${error}`);
215
+ }
216
+ }
217
+
218
+ async verifyAndGrantAccess(
219
+ signer: Signer,
220
+ proof: Proof | ZKProof,
221
+ publicSignals: (string | number)[],
222
+ credentialHash: string,
223
+ userAddress: string
224
+ ): Promise<TransactionResult> {
225
+ if (!signer) throw new Error('Signer is required to verify and grant access');
226
+
227
+ // Pre-flight check
228
+ this.validateProofInput(publicSignals);
229
+
230
+ const accessControl = new ethers.Contract(
231
+ this.contractAddresses.ProtocolAccessControl,
232
+ PROTOCOL_ACCESS_CONTROL_ABI,
233
+ signer
234
+ );
235
+
236
+ const a: [bigint, bigint] = [BigInt(proof.a[0]), BigInt(proof.a[1])];
237
+ const b: [[bigint, bigint], [bigint, bigint]] = [
238
+ [BigInt(proof.b[0][0]), BigInt(proof.b[0][1])],
239
+ [BigInt(proof.b[1][0]), BigInt(proof.b[1][1])]
240
+ ];
241
+ const c: [bigint, bigint] = [BigInt(proof.c[0]), BigInt(proof.c[1])];
242
+
243
+ const publicSignalsArray = publicSignals.slice(0, 28).map(s => BigInt(s));
244
+
245
+ try {
246
+ const tx = await accessControl.verifyAndGrantAccess(
247
+ a, b, c,
248
+ publicSignalsArray,
249
+ credentialHash,
250
+ userAddress
251
+ ) as ContractTransactionResponse;
252
+
253
+ const receipt = await tx.wait();
254
+ if (!receipt) throw new Error('Transaction failed: No receipt returned');
255
+
256
+ return {
257
+ transactionHash: tx.hash,
258
+ receipt,
259
+ };
260
+ } catch (error) {
261
+ throw new Error(`Contract verification failed: ${error}`);
262
+ }
263
+ }
264
+
265
+ getProvider(): Provider | null { return this.provider; }
266
+ }
@@ -0,0 +1,123 @@
1
+ import { ContractClient } from './ContractClient.js';
2
+ import { APIClient, type APIClientConfig } from './APIClient.js';
3
+ import { OCRExtractor } from '../utils/ocr.js';
4
+ import { parseTD3, type MRZData } from '../utils/mrz.js';
5
+ import type {
6
+ ContractClientConfig,
7
+ Proof,
8
+ TransactionResult,
9
+ Requirements
10
+ } from '../utils/types.js';
11
+ import type { Signer } from 'ethers';
12
+
13
+ export class NoahError extends Error {
14
+ constructor(public message: string, public code: string) {
15
+ super(message);
16
+ this.name = 'NoahError';
17
+ }
18
+ }
19
+
20
+ export class NoahValidationError extends NoahError {
21
+ constructor(message: string) {
22
+ super(message, 'VALIDATION_ERROR');
23
+ }
24
+ }
25
+
26
+ export class NoahProverError extends NoahError {
27
+ constructor(message: string) {
28
+ super(message, 'PROVER_ERROR');
29
+ }
30
+ }
31
+
32
+ /**
33
+ * NoahSDK - Main entry point for the Noah Protocol
34
+ */
35
+ export class NoahSDK {
36
+ public contracts: ContractClient;
37
+ public api: APIClient;
38
+ private ocrExtractor: OCRExtractor;
39
+
40
+ constructor(config?: ContractClientConfig & APIClientConfig) {
41
+ this.contracts = new ContractClient(config);
42
+ this.api = new APIClient(config);
43
+ this.ocrExtractor = new OCRExtractor();
44
+ }
45
+
46
+ /**
47
+ * Extract identity data from a document image
48
+ * @param image - File or URL of the passport image
49
+ */
50
+ public async extractPassportData(image: File | string | Blob): Promise<MRZData> {
51
+ const { mrzLines } = await this.ocrExtractor.extractMRZ(image);
52
+
53
+ if (mrzLines.length < 2) {
54
+ throw new NoahValidationError('Could not detect MRZ lines in the image. Please ensure the bottom part of the passport is clearly visible.');
55
+ }
56
+
57
+ // We assume the last two lines are the TD3 MRZ lines
58
+ const line1 = mrzLines[mrzLines.length - 2];
59
+ const line2 = mrzLines[mrzLines.length - 1];
60
+
61
+ try {
62
+ return parseTD3(line1, line2);
63
+ } catch (error) {
64
+ throw new NoahValidationError(`Failed to parse MRZ: ${error instanceof Error ? error.message : 'Unknown error'}`);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Initialize the SDK with a provider
70
+ */
71
+ public init(provider: any): void {
72
+ this.contracts.initialize(provider);
73
+ }
74
+
75
+ /**
76
+ * High-level method to prove age and grant access in one go
77
+ */
78
+ public async proveAndGrant(
79
+ signer: Signer,
80
+ protocolAddress: string,
81
+ mrzData: any,
82
+ targetAge: number
83
+ ): Promise<TransactionResult> {
84
+ try {
85
+ // 1. Pre-flight check requirements
86
+ const requirements = await this.contracts.getRequirements(protocolAddress);
87
+ if (requirements.minAge > targetAge) {
88
+ throw new NoahValidationError(`Protocol requires age ${requirements.minAge}, but target age is ${targetAge}`);
89
+ }
90
+
91
+ // 2. Generate Proof via API/Prover
92
+ const userAddress = await signer.getAddress();
93
+ const proofResult = await this.api.generateAgeProof({
94
+ mrzData,
95
+ minAge: targetAge,
96
+ recipientAddress: userAddress
97
+ });
98
+
99
+ if (!proofResult.success) {
100
+ throw new NoahProverError(proofResult.error || 'Unknown prover error');
101
+ }
102
+
103
+ // 3. Submit to Chain
104
+ return await this.contracts.verifyAndGrantAccess(
105
+ signer,
106
+ proofResult.proof,
107
+ proofResult.publicSignals,
108
+ proofResult.credentialHash,
109
+ userAddress
110
+ );
111
+ } catch (error) {
112
+ if (error instanceof NoahError) throw error;
113
+ throw new NoahError(error instanceof Error ? error.message : 'Unknown error', 'INTERNAL_ERROR');
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Get protocol requirements via contract client
119
+ */
120
+ public async getProtocolRequirements(protocolAddress: string): Promise<Requirements> {
121
+ return this.contracts.getRequirements(protocolAddress);
122
+ }
123
+ }