@provex/extension-react 1.0.16

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 ADDED
@@ -0,0 +1,258 @@
1
+ # @provex/extension-react
2
+
3
+ React hooks and context providers for integrating with the ZKTLS (Zero-Knowledge Transport Layer Security) browser extension.
4
+
5
+ ## Overview
6
+
7
+ This package provides React components and hooks that enable web applications to communicate with the ZKTLS browser extension for generating cryptographic proofs of payments. It handles the bridge between your React application and the browser extension, allowing users to prove they made payments through traditional payment providers without revealing sensitive account information.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @provex/extension-react
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ### 1. Wrap your app with the ZKTLS Provider
18
+
19
+ ```typescript
20
+ import { ZKTLSProvider } from '@provex/extension-react'
21
+
22
+ function App() {
23
+ return (
24
+ <ZKTLSProvider fallbackComponent={<div>Loading ZKTLS...</div>}>
25
+ <YourApp />
26
+ </ZKTLSProvider>
27
+ )
28
+ }
29
+ ```
30
+
31
+ ### 2. Use the ZKTLS context in your components
32
+
33
+ ```typescript
34
+ import { useZKTLSContext } from '@provex/extension-react'
35
+
36
+ function PaymentProofComponent() {
37
+ const zktls = useZKTLSContext()
38
+
39
+ const handleGenerateProof = async () => {
40
+ if (!zktls.isInitialized) {
41
+ console.log('ZKTLS extension not ready')
42
+ return
43
+ }
44
+
45
+ try {
46
+ // Request connection to extension
47
+ await zktls.requestConnection()
48
+
49
+ // Authenticate with payment provider
50
+ const metadata = await zktls.authenticate({
51
+ actionType: 'transfer_venmo',
52
+ platform: 'venmo'
53
+ })
54
+
55
+ // Generate cryptographic proof
56
+ const proofs = await zktls.generateProofSeries({
57
+ platform: 'venmo',
58
+ provider: 'venmo',
59
+ intentHash: '0x1234...',
60
+ originalIndex: 0
61
+ })
62
+
63
+ console.log('Proofs generated:', proofs)
64
+ } catch (error) {
65
+ console.error('Proof generation failed:', error)
66
+ }
67
+ }
68
+
69
+ return (
70
+ <button onClick={handleGenerateProof}>
71
+ Generate Payment Proof
72
+ </button>
73
+ )
74
+ }
75
+ ```
76
+
77
+ ## API Reference
78
+
79
+ ### `useZKTLS()` Hook
80
+
81
+ The main hook that provides all ZKTLS functionality. Returns an object with the following methods:
82
+
83
+ #### Connection Management
84
+ - `isInitialized: boolean` - Whether the ZKTLS extension is initialized
85
+ - `requestConnection(): Promise<boolean>` - Request connection to the extension
86
+ - `checkConnectionStatus(): Promise<Status>` - Check current connection status
87
+ - `getVersion(): Promise<string>` - Get extension version
88
+
89
+ #### Authentication & Proof Generation
90
+ - `authenticate(inputs: AuthenticationInputs): Promise<MetadataMessageResponse>` - Authenticate with a payment provider
91
+ - `generateProof(inputs: HexIntentGenerateProofInputs): Promise<GenerateProofResponse>` - Generate a single proof
92
+ - `generateProofSeries(inputs, mutations): Promise<NotaryRequest[]>` - Generate multiple proofs for complex verification
93
+ - `waitForProof(proofId: string): Promise<NotaryRequest>` - Wait for proof generation to complete
94
+
95
+ #### Proof Management
96
+ - `fetchProofById(proofId: string): Promise<NotaryRequest | null>` - Fetch a specific proof
97
+ - `fetchProofs(): Promise<FetchProofsResponse>` - Fetch all generated proofs
98
+ - `fetchGeneratedProofs(responses: GenerateProofResponse[]): Promise<NotaryRequest[]>` - Fetch multiple proofs by their responses
99
+
100
+ #### Extension Integration
101
+ - `openSidebar(route: string): Promise<void>` - Open the extension sidebar
102
+ - `onMetadataMessage(callback): () => void` - Subscribe to metadata messages
103
+
104
+ ### `ZKTLSProvider` Component
105
+
106
+ React context provider that makes ZKTLS functionality available to child components.
107
+
108
+ **Props:**
109
+ - `children: ReactNode` - Child components
110
+ - `fallbackComponent?: ReactNode` - Component to show while ZKTLS is initializing
111
+
112
+ ### `useZKTLSContext()` Hook
113
+
114
+ Hook to access the ZKTLS context within components. Must be used within a `ZKTLSProvider`.
115
+
116
+ **Returns:** The same object as `useZKTLS()`
117
+
118
+ **Throws:** Error if used outside of `ZKTLSProvider`
119
+
120
+ ## Supported Payment Providers
121
+
122
+ The extension supports proof generation for the following payment providers:
123
+
124
+ - **Venmo** - Single proof generation
125
+ - **Zelle** - Multiple proof generation (supports Chase, Bank of America, Citi sub-providers)
126
+ - **CashApp** - Single proof generation
127
+ - **Revolut** - Single proof generation
128
+ - **Wise** - Single proof generation
129
+ - **MercadoPago** - Single proof generation
130
+
131
+ ## Advanced Usage
132
+
133
+ ### Progress Tracking for Proof Series
134
+
135
+ ```typescript
136
+ const generateProofWithProgress = async () => {
137
+ await zktls.generateProofSeries(
138
+ {
139
+ platform: 'chase',
140
+ provider: 'zelle',
141
+ intentHash: '0x1234...',
142
+ originalIndex: 0
143
+ },
144
+ {
145
+ progress: ({ progress, step, id, total }) => {
146
+ console.log(`Step ${step + 1}/${total}: ${progress}`)
147
+ if (id) console.log(`Proof ID: ${id}`)
148
+ }
149
+ }
150
+ )
151
+ }
152
+ ```
153
+
154
+ ### Metadata Message Handling
155
+
156
+ ```typescript
157
+ useEffect(() => {
158
+ const unsubscribe = zktls.onMetadataMessage((data) => {
159
+ console.log('Payment metadata:', data.metadata)
160
+ console.log('Platform:', data.platform)
161
+ console.log('Request ID:', data.requestId)
162
+ })
163
+
164
+ return unsubscribe
165
+ }, [zktls])
166
+ ```
167
+
168
+ ### Finding Matching Payment Metadata
169
+
170
+ ```typescript
171
+ import { findMetadataMessage } from '@provex/extension-react'
172
+
173
+ const matchingMessage = findMetadataMessage({
174
+ metadata: paymentMetadata,
175
+ intent: userIntent,
176
+ token: tokenInfo
177
+ })
178
+ ```
179
+
180
+ ## Proof Encoding Utilities
181
+
182
+ The package includes utilities for encoding proofs for blockchain submission:
183
+
184
+ ```typescript
185
+ import {
186
+ encodeProofAsBytes,
187
+ encodeTwoProofs,
188
+ encodeProof
189
+ } from '@provex/extension-react'
190
+
191
+ // Encode single proof
192
+ const encodedProof = encodeProofAsBytes(reclaimProof)
193
+
194
+ // Encode multiple proofs (for Zelle/Chase)
195
+ const encodedProofs = encodeTwoProofs(proof1, proof2)
196
+
197
+ // High-level encoding based on provider
198
+ const encoded = encodeProof({
199
+ extensionProofs: notarizedProofs,
200
+ subProvider: 'chase',
201
+ provider: 'zelle'
202
+ })
203
+ ```
204
+
205
+ ## Error Handling
206
+
207
+ Always wrap ZKTLS calls in try-catch blocks:
208
+
209
+ ```typescript
210
+ const handleProofGeneration = async () => {
211
+ try {
212
+ const proofs = await zktls.generateProofSeries(inputs)
213
+ // Handle success
214
+ } catch (error) {
215
+ if (error.message.includes('failed')) {
216
+ // Handle proof generation failure
217
+ } else if (error.message.includes('cancelled')) {
218
+ // Handle user cancellation
219
+ } else {
220
+ // Handle other errors
221
+ }
222
+ }
223
+ }
224
+ ```
225
+
226
+ ## Types
227
+
228
+ The package exports TypeScript types for all major interfaces:
229
+
230
+ ```typescript
231
+ import type {
232
+ Status,
233
+ MetadataMessage,
234
+ MetadataMessageResponse,
235
+ GenerateProofInputs,
236
+ GenerateProofResponse,
237
+ AuthenticationInputs,
238
+ NotaryRequest,
239
+ ReclaimProof
240
+ } from '@provex/extension-react'
241
+ ```
242
+
243
+ ## Dependencies
244
+
245
+ - **React** ^18.2.0 (peer dependency)
246
+ - **@provex/utils** - Shared utilities package
247
+ - **viem** - Ethereum utilities for encoding
248
+
249
+ ## Browser Extension Requirement
250
+
251
+ This package requires users to have the ZKTLS browser extension installed. The extension handles all cryptographic operations and secure communication with payment providers.
252
+
253
+ ## Security Notes
254
+
255
+ - All sensitive operations are handled by the browser extension
256
+ - No private keys or sensitive data are exposed to the web application
257
+ - Zero-knowledge proofs ensure payment verification without revealing account details
258
+ - All proof generation happens locally in the user's browser
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@provex/extension-react",
3
+ "version": "1.0.16",
4
+ "main": "index.js",
5
+ "type": "module",
6
+ "access": "public",
7
+ "exports": {
8
+ ".": "./src/index.ts",
9
+ "./useZKTLS": "./src/useZKTLS.ts",
10
+ "./ZKTLSContext": "./src/ZKTLSContext.tsx"
11
+ },
12
+ "scripts": {
13
+ "build": "tsc"
14
+ },
15
+ "dependencies": {
16
+ "@provex/utils": "^1.0.16",
17
+ "react": "^18.2.0",
18
+ "tsx": "^4.20.3",
19
+ "typescript": "^5.9.2",
20
+ "viem": "^2.38.0"
21
+ },
22
+ "keywords": [],
23
+ "author": "",
24
+ "license": "ISC",
25
+ "description": "",
26
+ "devDependencies": {
27
+ "@types/react": "^19.2.2"
28
+ }
29
+ }
@@ -0,0 +1,28 @@
1
+ import { createContext, useContext, type ReactNode } from 'react'
2
+ import { useZKTLS } from './useZKTLS'
3
+
4
+ type ZKTLSContextType = ReturnType<typeof useZKTLS>
5
+
6
+ const ZKTLSContext = createContext<ZKTLSContextType | undefined>(undefined)
7
+
8
+ interface ZKTLSProviderProps {
9
+ children: ReactNode
10
+ }
11
+
12
+ export function ZKTLSProvider({ children }: ZKTLSProviderProps) {
13
+ const zktlsHook = useZKTLS()
14
+
15
+ return (
16
+ <ZKTLSContext.Provider value={zktlsHook}>
17
+ {children}
18
+ </ZKTLSContext.Provider>
19
+ )
20
+ }
21
+
22
+ export function useZKTLSContext() {
23
+ const context = useContext(ZKTLSContext)
24
+ if (context === undefined) {
25
+ throw new Error('useZKTLSContext must be used within a ZKTLSProvider')
26
+ }
27
+ return context
28
+ }
@@ -0,0 +1,9 @@
1
+ declare module "*.svg" {
2
+ const content: string;
3
+ export default content;
4
+ }
5
+
6
+ declare module "*.json" {
7
+ const content: any;
8
+ export default content;
9
+ }
@@ -0,0 +1,459 @@
1
+ import { useEffect, useMemo, useState } from "react"
2
+ import { encodeAbiParameters, encodePacked, parseAbiParameter, parseUnits, type Hex } from "viem"
3
+ import { interval, sleep } from "@provex/utils/time"
4
+ import type { IntentStruct } from "@provex/utils/intent"
5
+ import type { TokenInfo } from "@provex/utils/tokens"
6
+ import { providers, subProviderConfigs, subProviders, type ActionType, type PlatformKey, type ProviderKey, type SubProviderKey } from "@provex/utils/payment"
7
+ import { convertTokenInputToCurrency } from "@provex/utils/conversionRates"
8
+
9
+ declare global {
10
+ interface ZKTLS {
11
+ requestConnection(): Promise<boolean>
12
+ checkConnectionStatus(): Promise<Status>
13
+ getVersion(): Promise<string>
14
+ authenticate(inputs: AuthenticationInputs): Promise<void>
15
+ generateProof(inputs: GenerateProofInputs): Promise<GenerateProofResponse>
16
+ fetchProofById(proofId: string): Promise<ProofResponse>
17
+ fetchProofs(): Promise<FetchProofsResponse>
18
+ openSidebar(route: string): Promise<void>
19
+ onMetadataMessage(fn: (data: MetadataMessageResponse) => void): () => void
20
+ }
21
+ interface Window {
22
+ zktls: ZKTLS | undefined
23
+ }
24
+ }
25
+
26
+ /** the status of the zktls extension */
27
+ export type Status = 'connected' | 'disconnected' | 'pending'
28
+
29
+ /** the individual metadata message sent by the zktls extension */
30
+ export interface MetadataMessage {
31
+ amount: number
32
+ date: string
33
+ hidden: boolean
34
+ originalIndex: number
35
+ paymentId: number
36
+ recipient: string
37
+ }
38
+
39
+ /** the response from the zktls extension when a metadata message is received */
40
+ export interface MetadataMessageResponse {
41
+ expiresAt: number
42
+ platform: ProviderKey
43
+ requestId: string
44
+ metadata: MetadataMessage[]
45
+ }
46
+
47
+ /** the response from the zktls extension when fetching proofs */
48
+ export interface FetchProofsResponse {
49
+ notaryRequests: NotaryRequest[]
50
+ }
51
+
52
+ /** the inputs for the generateProof function */
53
+ export type GenerateProofInputsAbstracted = {
54
+ platform: PlatformKey
55
+ provider: ProviderKey
56
+ intentHash: Hex
57
+ originalIndex: number
58
+ }
59
+
60
+ /** the inputs for the generateProof function */
61
+ export type GenerateProofInputs = {
62
+ platform: ProviderKey
63
+ originalIndex: number
64
+ proofIndex?: number
65
+ intentHash: string
66
+ }
67
+
68
+ /**
69
+ * inputs for the generate proof function that requires the intent hash
70
+ * as hex to ensure it can be converted to a bigint string accurately
71
+ */
72
+ export type HexIntentGenerateProofInputs = Exclude<GenerateProofInputs, 'intentHash'> & { intentHash: Hex }
73
+
74
+ /** the response from the zktls extension when generating a proof */
75
+ export type GenerateProofResponse = {
76
+ proofId: string
77
+ platform: ProviderKey
78
+ }
79
+
80
+ /** the inputs for the authenticate function */
81
+ export type AuthenticationInputs = {
82
+ actionType: ActionType
83
+ platform: PlatformKey
84
+ }
85
+
86
+ /** the response from the zktls extension when fetching a proof */
87
+ export interface Proof {
88
+ claimInfo: ClaimInfo
89
+ signedClaim: SignedClaim
90
+ isAppclipProof: boolean
91
+ status: string
92
+ }
93
+
94
+ /** the response from the zktls extension when fetching a proof */
95
+ export interface NotarizedProof {
96
+ claim: {
97
+ context: string
98
+ epoch: number
99
+ identifier: Hex
100
+ owner: Hex
101
+ parameters: string
102
+ provider: string
103
+ timestampS: number
104
+ },
105
+ signatures: {
106
+ attestorAddress: Hex
107
+ claimSignature: Record<number, number>
108
+ resultSignature: Record<number, number>
109
+ },
110
+ }
111
+
112
+ /** the claim info for a proof */
113
+ export interface ClaimInfo {
114
+ provider: string
115
+ parameters: string
116
+ context: string
117
+ }
118
+
119
+ /** the complete claim data for a proof */
120
+ export interface CompleteClaimData {
121
+ identifier: Hex
122
+ owner: Hex
123
+ timestampS: number
124
+ epoch: number
125
+ }
126
+
127
+ /** the signed claim for a proof */
128
+ export interface SignedClaim {
129
+ claim: CompleteClaimData
130
+ signatures: Hex[]
131
+ }
132
+
133
+ /** the reclaim proof for a proof */
134
+ export type ReclaimProof = {
135
+ claimInfo: ClaimInfo
136
+ signedClaim: SignedClaim
137
+ isAppclipProof: boolean
138
+ }
139
+
140
+ /** the notary request for a proof */
141
+ export interface NotaryRequest {
142
+ metadata: (string | number)[]
143
+ actionType: ActionType
144
+ id: string
145
+ status: string
146
+ timestamp: number
147
+ proof: NotarizedProof
148
+ }
149
+
150
+ /** the response from the zktls extension when fetching a proof */
151
+ export interface ProofResponse {
152
+ notaryRequest: NotaryRequest
153
+ }
154
+
155
+ export const extensionUrl = 'https://chromewebstore.google.com/detail/ijpgccednehjpeclfcllnjjcmiohdjih'
156
+
157
+ /** convert a byte array to a hex string */
158
+ export const byteArrayToHexString = (byteArray: number[]): Hex => {
159
+ return `0x${byteArray
160
+ .map(byte => byte.toString(16).padStart(2, '0'))
161
+ .join('')}`
162
+ }
163
+
164
+ /** parse a proof object from the zktls extension */
165
+ export const parseExtensionProof = (proofObject: NotarizedProof) => {
166
+ return {
167
+ claimInfo: {
168
+ provider: proofObject.claim.provider,
169
+ parameters: proofObject.claim.parameters,
170
+ context: proofObject.claim.context
171
+ },
172
+ signedClaim: {
173
+ claim: {
174
+ identifier: proofObject.claim.identifier as Hex,
175
+ owner: proofObject.claim.owner as Hex,
176
+ timestampS: proofObject.claim.timestampS,
177
+ epoch: proofObject.claim.epoch
178
+ },
179
+ signatures: [byteArrayToHexString(Object.values(proofObject.signatures.claimSignature))]
180
+ },
181
+ isAppclipProof: false
182
+ } as ReclaimProof
183
+ }
184
+
185
+ /** the abi encoding for a proof */
186
+ export const PROOF_ENCODING = parseAbiParameter("((string provider, string parameters, string context) claimInfo, ((bytes32 identifier, address owner, uint32 timestampS, uint32 epoch) claim, bytes[] signatures) signedClaim, bool isAppclipProof)")
187
+
188
+ /** encode a single proof as a bytes array */
189
+ export function encodeProofAsBytes(proof: ReclaimProof): Hex {
190
+ return encodeAbiParameters([PROOF_ENCODING], [proof])
191
+ }
192
+
193
+ /** encode two proofs to be passed to the underlying sub-provider */
194
+ export const encodeTwoProofs = (proof1: ReclaimProof, proof2: ReclaimProof) => {
195
+ return encodeAbiParameters([PROOF_ENCODING, PROOF_ENCODING], [proof1, proof2])
196
+ }
197
+
198
+ /** a high level function to encode a proof for a given provider and sub-provider */
199
+ export const encodeProof = ({ extensionProofs, subProvider, provider }: {
200
+ extensionProofs: NotarizedProof[],
201
+ subProvider: PlatformKey | null,
202
+ provider: ProviderKey
203
+ }) => {
204
+ const reclaimProofs = extensionProofs.map((p) => parseExtensionProof(p))
205
+ if (provider === 'zelle') {
206
+ // move this to check the blockchain for the payment method
207
+ const paymentMethod = subProviderConfigs![provider]![subProvider as SubProviderKey]!.paymentMethod
208
+ const twoEncoded = reclaimProofs.length === 2
209
+ ? encodeTwoProofs(reclaimProofs[0], reclaimProofs[1])
210
+ : encodeProofAsBytes(reclaimProofs[0])
211
+ // select between various possible sub-providers
212
+ const withPaymentMethod = encodeProofWithPaymentMethod({
213
+ paymentMethod,
214
+ proof: twoEncoded,
215
+ })
216
+ return withPaymentMethod
217
+ }
218
+ const [reclaimProof] = reclaimProofs
219
+ return encodeProofAsBytes(reclaimProof)
220
+ }
221
+
222
+ /**
223
+ * wrap a proof with a payment method and proof
224
+ * this allows us to use a single verifier as an entry point for multiple underlying sub-providers
225
+ */
226
+ export const encodeProofWithPaymentMethod = ({
227
+ proof,
228
+ paymentMethod
229
+ }: {
230
+ proof: Hex,
231
+ paymentMethod: number
232
+ }): Hex => {
233
+ return encodePacked(['uint8', 'bytes'], [paymentMethod, proof])
234
+ }
235
+
236
+ /** the progress of a proof series */
237
+ export enum SeriesProgress {
238
+ Generating,
239
+ Polling,
240
+ Complete,
241
+ }
242
+
243
+ export type ProgressInfo = {
244
+ progress: SeriesProgress
245
+ total: number
246
+ step: number | null
247
+ id: string | null
248
+ }
249
+
250
+ /** the mutations for the generate proof series function */
251
+ export type GenerateProofSeriesMutations = {
252
+ progress?: ({ progress, step, id }: ProgressInfo) => void
253
+ setOwnerName?: (name: string) => void
254
+ }
255
+
256
+ /** the zktls context */
257
+ export const useZKTLS = () => {
258
+ const [isInitialized, setIsInitialized] = useState(false)
259
+ useEffect(() => {
260
+ const setup = () => {
261
+ setIsInitialized(true)
262
+ }
263
+ const eventName = 'zktls#initialized'
264
+ if (typeof window.zktls !== 'undefined') {
265
+ // api is available
266
+ setup()
267
+ } else {
268
+ window.addEventListener(eventName, setup)
269
+ return () => {
270
+ window.removeEventListener(eventName, setup)
271
+ }
272
+ }
273
+ }, [])
274
+ return useMemo(() => {
275
+ /** fetch a proof by its id */
276
+ const fetchProofById = async (proofId: string) => {
277
+ const proof = await window.zktls!.fetchProofById(proofId)
278
+ if (proof && proof.notaryRequest) return proof.notaryRequest
279
+ const proofs = await window.zktls!.fetchProofs()
280
+ const proofFromList = proofs.notaryRequests.find((p) => p.id === proofId)
281
+ if (proofFromList) return proofFromList
282
+ return null
283
+ }
284
+ /** wait for a proof to be generated */
285
+ const waitForProof = async (proofId: string, onCurrent?: (notaryRequest: NotaryRequest | null) => void) => {
286
+ let notaryRequest: NotaryRequest | null = null
287
+ do {
288
+ notaryRequest = await fetchProofById(proofId)
289
+ onCurrent?.(notaryRequest)
290
+ await sleep(1_000)
291
+ } while (!notaryRequest || !notaryRequest.status || notaryRequest.status === 'pending')
292
+ if (notaryRequest.status === 'failed' || notaryRequest.status === 'expired' || notaryRequest.status === 'cancelled' || notaryRequest.status === 'error') {
293
+ console.log(notaryRequest)
294
+ throw new Error(`Proof generation failed: ${notaryRequest.status}`)
295
+ }
296
+ return notaryRequest
297
+ }
298
+ /** generate a proof */
299
+ const generateProof = async (inputs: HexIntentGenerateProofInputs): Promise<GenerateProofResponse> => {
300
+ return await window.zktls!.generateProof({
301
+ proofIndex: 0,
302
+ ...inputs,
303
+ intentHash: BigInt(inputs.intentHash).toString(),
304
+ })
305
+ }
306
+ return {
307
+ /** check if the zktls extension is initialized */
308
+ isInitialized,
309
+ /** request a connection to the zktls extension */
310
+ requestConnection: async () => {
311
+ if (!window.zktls) return false
312
+ return window.zktls!.requestConnection()
313
+ },
314
+ /**
315
+ * check the connection status of the zktls extension
316
+ * @returns the connection status
317
+ */
318
+ checkConnectionStatus: async () => {
319
+ return await window.zktls!.checkConnectionStatus()
320
+ },
321
+ /** get the version of the zktls extension */
322
+ getVersion: async () => {
323
+ return await window.zktls?.getVersion()
324
+ },
325
+ /** authenticate with the zktls extension */
326
+ authenticate: async (inputs: AuthenticationInputs) => {
327
+ let resolve: (value: MetadataMessageResponse) => void = () => { }
328
+ const unsub = window.zktls!.onMetadataMessage((data) => {
329
+ console.log('metadata message', data, inputs)
330
+ if (`transfer_${data.platform}` === inputs.actionType) {
331
+ resolve(data)
332
+ }
333
+ })
334
+ const promise = new Promise<MetadataMessageResponse>((resolver) => {
335
+ resolve = resolver
336
+ })
337
+ await window.zktls!.authenticate(inputs)
338
+ return await promise.then((data) => {
339
+ unsub()
340
+ return data
341
+ })
342
+ },
343
+ /** generate a proof */
344
+ generateProof,
345
+ /** wait for a proof to be generated */
346
+ waitForProof,
347
+ /** generate a proof series */
348
+ generateProofSeries: async (
349
+ inputs: GenerateProofInputsAbstracted,
350
+ { progress: progressCallback }: GenerateProofSeriesMutations = {},
351
+ ): Promise<NotaryRequest[]> => {
352
+ const { platform, provider, intentHash, originalIndex } = inputs
353
+ const args = {
354
+ platform: provider,
355
+ originalIndex,
356
+ intentHash,
357
+ } as const
358
+ const expectedTotal = provider === providers.ZELLE && platform === subProviders.CHASE ? 2 : 1
359
+ progressCallback?.({ progress: SeriesProgress.Generating, step: 0, id: null, total: expectedTotal })
360
+ const genProofA = await generateProof({ ...args, proofIndex: 0 })
361
+ progressCallback?.({ progress: SeriesProgress.Polling, step: 0, id: genProofA.proofId, total: expectedTotal })
362
+ const proofA = await waitForProof(genProofA.proofId)
363
+ if (platform !== subProviders.CHASE || provider !== providers.ZELLE) {
364
+ progressCallback?.({ progress: SeriesProgress.Complete, step: null, id: null, total: expectedTotal })
365
+ return [proofA]
366
+ }
367
+ progressCallback?.({ progress: SeriesProgress.Generating, step: 1, id: null, total: expectedTotal })
368
+ const genProofB = await generateProof({ ...args, proofIndex: 1 })
369
+ progressCallback?.({ progress: SeriesProgress.Polling, step: 1, id: genProofB.proofId, total: expectedTotal })
370
+ const proofB = await waitForProof(genProofB.proofId)
371
+ progressCallback?.({ progress: SeriesProgress.Complete, step: null, id: null, total: expectedTotal })
372
+ return [proofA, proofB]
373
+ },
374
+ /** subscribe to metadata messages */
375
+ onMetadataMessage: (fn: (data: any) => void) => {
376
+ return window.zktls!.onMetadataMessage(fn)
377
+ },
378
+ /** fetch a proof by its id */
379
+ fetchProofById,
380
+ /** fetch generated proofs */
381
+ fetchGeneratedProofs: async (genProofResponses: GenerateProofResponse[]) => {
382
+ return await Promise.all(genProofResponses.map((r) => fetchProofById(r.proofId)))
383
+ },
384
+ /** fetch all proofs */
385
+ fetchProofs: async () => {
386
+ return await window.zktls!.fetchProofs()
387
+ },
388
+ /** open the zktls sidebar */
389
+ openSidebar: async (route: string) => {
390
+ return await window.zktls!.openSidebar(route)
391
+ },
392
+ }
393
+ }, [isInitialized])
394
+ }
395
+
396
+ /** find the metadata message that matches the intent to the best of our ability */
397
+ export const findMetadataMessage = ({ metadata, intent, token, recipient, centIsInt = false }: {
398
+ metadata: MetadataMessage[],
399
+ intent: IntentStruct,
400
+ token: TokenInfo,
401
+ recipient: string,
402
+ centIsInt?: boolean,
403
+ }): MetadataMessage | null => {
404
+ const currencyAmount = convertTokenInputToCurrency({
405
+ token,
406
+ amountOutInt: intent.amount,
407
+ rate: intent.conversionRate,
408
+ })
409
+ const convertDecimalToBigInt = (amount: number) => parseUnits(amount.toString(), token.decimals)
410
+ const amountFilter = (m: MetadataMessage) => {
411
+ let converted = convertDecimalToBigInt(m.amount)
412
+ if (centIsInt) {
413
+ converted = converted / 100n
414
+ }
415
+ return converted === currencyAmount || converted === (currencyAmount * -1n)
416
+ }
417
+ const timeFilter = (m: MetadataMessage, onlyRequireLessThan?: boolean) => {
418
+ const date = new Date(m.date)
419
+ let year, month, day = 0
420
+ if (m.date.length) {
421
+ year = +m.date.slice(0, 4)
422
+ month = +m.date.slice(4, 6)
423
+ day = +m.date.slice(6, 8)
424
+ } else {
425
+ year = date.getFullYear()
426
+ month = date.getMonth() + 1
427
+ day = date.getDate()
428
+ }
429
+ const timestamp = new Date(`${year}-${month}-${day}`)
430
+ const tsmp = Number(intent.timestamp) * 1_000
431
+ // intent happens first
432
+ const intentTimestamp0 = new Date(tsmp - (tsmp % interval.day))
433
+ const intentTimestamp1 = new Date(tsmp - (tsmp % interval.day) + interval.day)
434
+ // if the transaction timestamp is within 1 day of the intent timestamp, it's a match
435
+ const txTsmp = timestamp.getTime()
436
+ if (onlyRequireLessThan) {
437
+ return intentTimestamp0.getTime() <= txTsmp
438
+ }
439
+ return intentTimestamp0.getTime() === txTsmp || intentTimestamp1.getTime() === txTsmp
440
+ }
441
+ const filtered = metadata.filter((m) => !m.hidden && m.recipient === recipient)
442
+ const foundWithinDay = filtered.find((m) => amountFilter(m) && timeFilter(m))
443
+ if (foundWithinDay) {
444
+ console.log('found within day', foundWithinDay, intent)
445
+ return foundWithinDay
446
+ }
447
+ const foundWithBound = metadata.find((m) => amountFilter(m) && timeFilter(m, true))
448
+ if (foundWithBound) {
449
+ console.log('found with bound', foundWithBound, intent)
450
+ return foundWithBound
451
+ }
452
+ const foundByAmountOnly = metadata.find(amountFilter)
453
+ if (foundByAmountOnly) {
454
+ console.log('found by amount only', foundByAmountOnly, intent)
455
+ return foundByAmountOnly
456
+ }
457
+ console.log('no match', intent, metadata, convertDecimalToBigInt(metadata[0].amount), currencyAmount)
458
+ return null
459
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "useDefineForClassFields": true,
5
+ "module": "NodeNext",
6
+ "composite": true,
7
+ "allowJs": true,
8
+ "outDir": "./dist",
9
+ "strict": true,
10
+ "isolatedModules": true,
11
+ "skipLibCheck": true,
12
+ "moduleDetection": "force",
13
+ "esModuleInterop": true,
14
+ "resolveJsonModule": true,
15
+ "moduleResolution": "nodenext"
16
+ },
17
+ "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.json", "src/**/*.svg"]
18
+ }