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
@@ -0,0 +1,331 @@
1
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
2
+ import { useMemo, useState, useEffect } from 'react';
3
+ import type { Signer } from 'ethers';
4
+ import { UserClient, type Credential as UserCredential } from '../user/UserClient';
5
+ import type { Requirements, TransactionResult, ZKProof } from '../utils/types';
6
+
7
+ /**
8
+ * Configuration options for useUser hook
9
+ */
10
+ export interface UseUserOptions {
11
+ /** User's wallet address */
12
+ userAddress?: string;
13
+ /** Protocol address to check requirements/access for */
14
+ protocolAddress?: string;
15
+ /** Credential hash to check validity for */
16
+ credentialHash?: string;
17
+ /** Whether to enable automatic refetching */
18
+ enabled?: boolean;
19
+ /** React Query configuration options */
20
+ queryOptions?: {
21
+ refetchInterval?: number;
22
+ staleTime?: number;
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Credential structure (re-exported from UserClient for convenience)
28
+ */
29
+ export type Credential = UserCredential;
30
+
31
+ /**
32
+ * Return type for useUser hook
33
+ */
34
+ export interface UseUserReturn {
35
+ // User client instance
36
+ user: UserClient | null;
37
+
38
+ // Read operations
39
+ protocolRequirements: Requirements | undefined;
40
+ isLoadingRequirements: boolean;
41
+ requirementsError: Error | null;
42
+ refetchRequirements: () => void;
43
+
44
+ // Credential validation
45
+ isCredentialValid: boolean | undefined;
46
+ isLoadingCredential: boolean;
47
+ credentialError: Error | null;
48
+ refetchCredential: () => void;
49
+
50
+ // Write operations (mutations)
51
+ checkCredentialValidity: {
52
+ mutate: (credentialHash: string) => void;
53
+ mutateAsync: (credentialHash: string) => Promise<boolean>;
54
+ isLoading: boolean;
55
+ error: Error | null;
56
+ reset: () => void;
57
+ };
58
+
59
+ generateProof: {
60
+ mutate: (params: {
61
+ credential: Credential;
62
+ requirements: Requirements & { protocolAddress: string };
63
+ }) => void;
64
+ mutateAsync: (params: {
65
+ credential: Credential;
66
+ requirements: Requirements & { protocolAddress: string };
67
+ }) => Promise<{
68
+ proof: ZKProof;
69
+ publicSignals: string[];
70
+ nullifier: string;
71
+ packedFlags: number;
72
+ credentialHash: string;
73
+ success: boolean;
74
+ }>;
75
+ isLoading: boolean;
76
+ error: Error | null;
77
+ reset: () => void;
78
+ };
79
+
80
+ verifyAndGrantAccess: {
81
+ mutate: (params: {
82
+ proof: ZKProof;
83
+ publicSignals: string[];
84
+ nullifier: string;
85
+ packedFlags: number;
86
+ credentialHash: string;
87
+ protocolAddress?: string;
88
+ userAddress?: string;
89
+ }) => void;
90
+ mutateAsync: (params: {
91
+ proof: ZKProof;
92
+ publicSignals: string[];
93
+ nullifier: string;
94
+ packedFlags: number;
95
+ credentialHash: string;
96
+ protocolAddress?: string;
97
+ userAddress?: string;
98
+ }) => Promise<TransactionResult>;
99
+ isLoading: boolean;
100
+ error: Error | null;
101
+ reset: () => void;
102
+ };
103
+ }
104
+
105
+ /**
106
+ * React hook for user operations
107
+ *
108
+ * Provides easy access to user-related functionality including:
109
+ * - Getting protocol requirements
110
+ * - Checking credential validity
111
+ * - Generating ZK proofs
112
+ * - Verifying proofs and granting access to protocols
113
+ *
114
+ * @param signer - Ethers signer instance (from wallet)
115
+ * @param options - Configuration options
116
+ * @returns Hook return object with user operations
117
+ *
118
+ * @example
119
+ * ```tsx
120
+ * import { useUser } from '@noah-protocol/sdk/hooks';
121
+ * import { useSigner, useAccount } from 'wagmi';
122
+ *
123
+ * function UserDashboard() {
124
+ * const { data: signer } = useSigner();
125
+ * const { address } = useAccount();
126
+ * const {
127
+ * protocolRequirements,
128
+ * generateProof,
129
+ * verifyAndGrantAccess,
130
+ * hasAccess
131
+ * } = useUser(signer, {
132
+ * userAddress: address,
133
+ * protocolAddress: '0x...',
134
+ * apiBaseUrl: 'https://api.noah.xyz'
135
+ * });
136
+ *
137
+ * const handleGenerateProof = async () => {
138
+ * const result = await generateProof.mutateAsync({
139
+ * credential: {
140
+ * actualAge: 25,
141
+ * actualJurisdiction: 'US',
142
+ * actualAccredited: true,
143
+ * credentialHash: '0x...'
144
+ * },
145
+ * requirements: protocolRequirements!
146
+ * });
147
+ *
148
+ * await verifyAndGrantAccess.mutateAsync({
149
+ * ...result,
150
+ * credentialHash: '0x...',
151
+ * protocolAddress: '0x...'
152
+ * });
153
+ * };
154
+ *
155
+ * return (
156
+ * <div>
157
+ * {hasAccess ? (
158
+ * <p>You have access!</p>
159
+ * ) : (
160
+ * <button onClick={handleGenerateProof}>
161
+ * Request Access
162
+ * </button>
163
+ * )}
164
+ * </div>
165
+ * );
166
+ * }
167
+ * ```
168
+ */
169
+ export function useUser(
170
+ signer: Signer | null | undefined,
171
+ options: UseUserOptions = {}
172
+ ): UseUserReturn {
173
+ const {
174
+ userAddress,
175
+ protocolAddress,
176
+ credentialHash,
177
+ enabled = true,
178
+ queryOptions = {},
179
+ } = options;
180
+
181
+ const queryClient = useQueryClient();
182
+
183
+ // Create user client instance
184
+ const user = useMemo(() => {
185
+ if (!signer) return null;
186
+ try {
187
+ return new UserClient(signer);
188
+ } catch (error) {
189
+ console.warn('UserClient not available:', error);
190
+ return null;
191
+ }
192
+ }, [signer]);
193
+
194
+ // Note: userAddress is used directly in queries below
195
+
196
+ // Query: Get protocol requirements
197
+ const {
198
+ data: protocolRequirements,
199
+ isLoading: isLoadingRequirements,
200
+ error: requirementsError,
201
+ refetch: refetchRequirements,
202
+ } = useQuery({
203
+ queryKey: ['user', 'protocol-requirements', protocolAddress],
204
+ queryFn: async () => {
205
+ if (!user || !protocolAddress) {
206
+ throw new Error('User client or protocol address not available');
207
+ }
208
+ return user.getProtocolRequirements(protocolAddress);
209
+ },
210
+ enabled: enabled && !!user && !!protocolAddress,
211
+ ...queryOptions,
212
+ });
213
+
214
+ // Note: To check user access, use the useProtocol hook instead
215
+ // This is because access checking is a protocol-level operation
216
+
217
+ // Query: Check credential validity
218
+ const {
219
+ data: isCredentialValid,
220
+ isLoading: isLoadingCredential,
221
+ error: credentialError,
222
+ refetch: refetchCredential,
223
+ } = useQuery({
224
+ queryKey: ['user', 'credential-validity', credentialHash],
225
+ queryFn: async () => {
226
+ if (!user || !credentialHash) {
227
+ throw new Error('User client or credential hash not available');
228
+ }
229
+ return user.checkCredentialValidity(credentialHash);
230
+ },
231
+ enabled: enabled && !!user && !!credentialHash,
232
+ ...queryOptions,
233
+ });
234
+
235
+ // Mutation: Check credential validity
236
+ const checkCredentialValidityMutation = useMutation({
237
+ mutationFn: async (credentialHash: string) => {
238
+ if (!user) {
239
+ throw new Error('User client not available');
240
+ }
241
+ return user.checkCredentialValidity(credentialHash);
242
+ },
243
+ });
244
+
245
+ // Mutation: Generate proof
246
+ const generateProofMutation = useMutation({
247
+ mutationFn: async (params: {
248
+ credential: Credential;
249
+ requirements: Requirements & { protocolAddress: string };
250
+ }) => {
251
+ if (!user) {
252
+ throw new Error('User client not available');
253
+ }
254
+ return user.generateProof(params.credential, params.requirements);
255
+ },
256
+ });
257
+
258
+ // Mutation: Verify and grant access
259
+ const verifyAndGrantAccessMutation = useMutation({
260
+ mutationFn: async (params: {
261
+ proof: ZKProof;
262
+ publicSignals: string[];
263
+ nullifier: string;
264
+ packedFlags: number;
265
+ credentialHash: string;
266
+ protocolAddress?: string;
267
+ userAddress?: string;
268
+ }) => {
269
+ if (!user) {
270
+ throw new Error('User client not available');
271
+ }
272
+ // UserClient.verifyAndGrantAccess expects a ProofResult object
273
+ return user.verifyAndGrantAccess(
274
+ {
275
+ proof: params.proof,
276
+ publicSignals: params.publicSignals,
277
+ nullifier: params.nullifier,
278
+ packedFlags: params.packedFlags,
279
+ credentialHash: params.credentialHash,
280
+ success: true,
281
+ },
282
+ params.protocolAddress,
283
+ params.userAddress
284
+ );
285
+ },
286
+ onSuccess: () => {
287
+ // Invalidate and refetch access status
288
+ queryClient.invalidateQueries({
289
+ queryKey: ['user', 'access'],
290
+ });
291
+ },
292
+ });
293
+
294
+ return {
295
+ user,
296
+ protocolRequirements,
297
+ isLoadingRequirements,
298
+ requirementsError: requirementsError as Error | null,
299
+ refetchRequirements: () => {
300
+ refetchRequirements();
301
+ },
302
+ isCredentialValid,
303
+ isLoadingCredential,
304
+ credentialError: credentialError as Error | null,
305
+ refetchCredential: () => {
306
+ refetchCredential();
307
+ },
308
+ checkCredentialValidity: {
309
+ mutate: checkCredentialValidityMutation.mutate,
310
+ mutateAsync: checkCredentialValidityMutation.mutateAsync,
311
+ isLoading: checkCredentialValidityMutation.isPending,
312
+ error: checkCredentialValidityMutation.error as Error | null,
313
+ reset: checkCredentialValidityMutation.reset,
314
+ },
315
+ generateProof: {
316
+ mutate: generateProofMutation.mutate,
317
+ mutateAsync: generateProofMutation.mutateAsync,
318
+ isLoading: generateProofMutation.isPending,
319
+ error: generateProofMutation.error as Error | null,
320
+ reset: generateProofMutation.reset,
321
+ },
322
+ verifyAndGrantAccess: {
323
+ mutate: verifyAndGrantAccessMutation.mutate,
324
+ mutateAsync: verifyAndGrantAccessMutation.mutateAsync,
325
+ isLoading: verifyAndGrantAccessMutation.isPending,
326
+ error: verifyAndGrantAccessMutation.error as Error | null,
327
+ reset: verifyAndGrantAccessMutation.reset,
328
+ },
329
+ };
330
+ }
331
+
package/src/index.ts ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * NOAH Protocol SDK
3
+ *
4
+ * TypeScript/JavaScript SDK for integrating NOAH's privacy-preserving KYC functionality
5
+ * into DeFi protocols and applications.
6
+ */
7
+
8
+ // Core exports
9
+ export { NoahSDK, NoahError, NoahValidationError, NoahProverError } from './core/NoahSDK.js';
10
+ export { ContractClient } from './core/ContractClient.js';
11
+ export { APIClient } from './core/APIClient.js';
12
+ export { WalletAdapter } from './core/WalletAdapter.js';
13
+ export type { WalletAdapterConfig, WalletState, WalletType } from './core/WalletAdapter.js';
14
+
15
+ // Protocol exports
16
+ export { ProtocolClient } from './protocol/ProtocolClient.js';
17
+ export { RequirementsManager } from './protocol/RequirementsManager.js';
18
+
19
+ // User exports
20
+ export { UserClient } from './user/UserClient.js';
21
+ export { ProofGenerator } from './user/ProofGenerator.js';
22
+ export type { Credential, ProofResult, UserClientConfig } from './user/UserClient.js';
23
+
24
+ // Issuer exports
25
+ export { IssuerClient } from './issuer/IssuerClient.js';
26
+
27
+ // Utilities
28
+ export {
29
+ jurisdictionStringToHash,
30
+ jurisdictionStringsToHashes,
31
+ parseJurisdictions,
32
+ } from './utils/jurisdiction.js';
33
+
34
+ export {
35
+ generateCredentialHash,
36
+ isValidCredentialHash,
37
+ isValidAddress,
38
+ toChecksumAddress,
39
+ } from './utils/credentials.js';
40
+
41
+ export {
42
+ parseTD3,
43
+ validateCheckDigit,
44
+ } from './utils/mrz.js';
45
+ export type { MRZData } from './utils/mrz.js';
46
+
47
+ export { OCRExtractor } from './utils/ocr.js';
48
+ export type { OCROutput } from './utils/ocr.js';
49
+
50
+ export { IdentityManager } from './utils/identity.js';
51
+ export type { IdentityProfile } from './utils/identity.js';
52
+
53
+ // Type exports
54
+ export type {
55
+ ContractAddresses,
56
+ Requirements,
57
+ IssuerInfo,
58
+ Proof,
59
+ ZKProof,
60
+ TransactionResult,
61
+ ContractClientConfig,
62
+ ProtocolClientConfig,
63
+ SetRequirementsParams,
64
+ VerifyUserAccessParams,
65
+ CredentialIssuedEvent,
66
+ AccessGrantedEvent,
67
+ EventCallback,
68
+ ContractEventPayload,
69
+ CredentialData,
70
+ CredentialHashResult,
71
+ } from './utils/types.js';
72
+
73
+ // API Client types
74
+ export type {
75
+ APIClientConfig,
76
+ ProofGenerationData,
77
+ ProofGenerationResult,
78
+ AccessStatus,
79
+ CredentialStatus,
80
+ } from './core/APIClient.js';
@@ -0,0 +1,209 @@
1
+ import type { Signer } from 'ethers';
2
+ import type { TransactionResult, ContractAddresses } from '../utils/types';
3
+ import { ContractClient } from '../core/ContractClient';
4
+ import { APIClient } from '../core/APIClient';
5
+
6
+ /**
7
+ * IssuerClient configuration options
8
+ */
9
+ export interface IssuerClientConfig {
10
+ apiBaseUrl?: string;
11
+ contractAddresses?: Partial<ContractAddresses>;
12
+ rpcUrl?: string;
13
+ }
14
+
15
+ /**
16
+ * Credential status information
17
+ */
18
+ export interface CredentialStatus {
19
+ isValid: boolean;
20
+ credentialHash: string;
21
+ isRevoked: boolean;
22
+ issuer?: string;
23
+ }
24
+
25
+ /**
26
+ * IssuerClient - High-level API for credential issuers
27
+ *
28
+ * Provides a simple interface for issuers to:
29
+ * - Register credentials on-chain
30
+ * - Revoke credentials
31
+ * - Check credential status
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * import { IssuerClient } from '@noah-protocol/sdk';
36
+ * import { ethers } from 'ethers';
37
+ *
38
+ * const provider = new ethers.BrowserProvider(window.ethereum);
39
+ * const signer = await provider.getSigner();
40
+ * const issuer = new IssuerClient(signer, { apiBaseUrl: 'https://api.noah.xyz' });
41
+ *
42
+ * // Register credential
43
+ * await issuer.registerCredential(credentialHash, userAddress);
44
+ *
45
+ * // Revoke credential
46
+ * await issuer.revokeCredential(credentialHash);
47
+ * ```
48
+ */
49
+ export class IssuerClient {
50
+ private signer: Signer;
51
+ private contractClient: ContractClient;
52
+ private apiClient: APIClient;
53
+
54
+ /**
55
+ * Create a new IssuerClient instance
56
+ * @param signer - Ethers.js signer from issuer's wallet
57
+ * @param config - Optional configuration
58
+ */
59
+ constructor(signer: Signer, config: IssuerClientConfig = {}) {
60
+ if (!signer) {
61
+ throw new Error('Signer is required');
62
+ }
63
+
64
+ this.signer = signer;
65
+
66
+ // Initialize ContractClient
67
+ this.contractClient = new ContractClient({
68
+ provider: signer.provider || undefined,
69
+ contractAddresses: config.contractAddresses as ContractAddresses | undefined,
70
+ rpcUrl: config.rpcUrl,
71
+ });
72
+
73
+ // Initialize APIClient
74
+ this.apiClient = new APIClient({
75
+ baseURL: config.apiBaseUrl,
76
+ });
77
+ }
78
+
79
+ /**
80
+ * Register a credential on-chain
81
+ *
82
+ * @param credentialHash - The credential hash to register (bytes32)
83
+ * @param userAddress - The user's wallet address
84
+ * @param useAPI - If true, use backend API (gasless). If false, use direct contract call.
85
+ * @returns Promise resolving to transaction result
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * const tx = await issuer.registerCredential('0x1234...', '0x5678...');
90
+ * console.log('Transaction hash:', tx.transactionHash);
91
+ * ```
92
+ */
93
+ async registerCredential(
94
+ credentialHash: string,
95
+ userAddress: string,
96
+ useAPI: boolean = false
97
+ ): Promise<TransactionResult> {
98
+ if (!credentialHash) {
99
+ throw new Error('Credential hash is required');
100
+ }
101
+
102
+ if (!userAddress) {
103
+ throw new Error('User address is required');
104
+ }
105
+
106
+ try {
107
+ if (useAPI) {
108
+ // Use backend API for gasless transactions
109
+ return await this.apiClient.registerCredential(credentialHash, userAddress);
110
+ } else {
111
+ // Use direct contract call
112
+ return await this.contractClient.registerCredential(
113
+ this.signer,
114
+ credentialHash,
115
+ userAddress
116
+ );
117
+ }
118
+ } catch (error: any) {
119
+ throw new Error(
120
+ `Failed to register credential: ${error.message || 'Unknown error'}`
121
+ );
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Revoke a credential
127
+ *
128
+ * @param credentialHash - The credential hash to revoke (bytes32)
129
+ * @param useAPI - If true, use backend API (gasless). If false, use direct contract call.
130
+ * @returns Promise resolving to transaction result
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * const tx = await issuer.revokeCredential('0x1234...');
135
+ * console.log('Transaction hash:', tx.transactionHash);
136
+ * ```
137
+ */
138
+ async revokeCredential(
139
+ credentialHash: string,
140
+ useAPI: boolean = false
141
+ ): Promise<TransactionResult> {
142
+ if (!credentialHash) {
143
+ throw new Error('Credential hash is required');
144
+ }
145
+
146
+ try {
147
+ if (useAPI) {
148
+ // Use backend API for gasless transactions
149
+ return await this.apiClient.revokeCredential(credentialHash);
150
+ } else {
151
+ // Use direct contract call
152
+ return await this.contractClient.revokeCredential(
153
+ this.signer,
154
+ credentialHash
155
+ );
156
+ }
157
+ } catch (error: any) {
158
+ throw new Error(
159
+ `Failed to revoke credential: ${error.message || 'Unknown error'}`
160
+ );
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Check credential status
166
+ *
167
+ * @param credentialHash - The credential hash to check (bytes32)
168
+ * @returns Promise resolving to credential status information
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * const status = await issuer.checkCredential('0x1234...');
173
+ * console.log('Is valid:', status.isValid);
174
+ * console.log('Is revoked:', status.isRevoked);
175
+ * ```
176
+ */
177
+ async checkCredential(credentialHash: string): Promise<CredentialStatus> {
178
+ if (!credentialHash) {
179
+ throw new Error('Credential hash is required');
180
+ }
181
+
182
+ try {
183
+ // Check on-chain validity
184
+ const isValid = await this.contractClient.isCredentialValid(credentialHash);
185
+
186
+ // Get additional info from API if available
187
+ try {
188
+ const apiStatus = await this.apiClient.checkCredential(credentialHash);
189
+ return {
190
+ isValid: apiStatus.isValid && isValid,
191
+ credentialHash,
192
+ isRevoked: apiStatus.isRevoked,
193
+ issuer: apiStatus.issuer,
194
+ };
195
+ } catch {
196
+ // If API fails, return on-chain status
197
+ return {
198
+ isValid,
199
+ credentialHash,
200
+ isRevoked: !isValid,
201
+ };
202
+ }
203
+ } catch (error: any) {
204
+ throw new Error(
205
+ `Failed to check credential: ${error.message || 'Unknown error'}`
206
+ );
207
+ }
208
+ }
209
+ }