@verifiedonchain-protocol/sdk 0.1.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.
@@ -0,0 +1,563 @@
1
+ import Human, { FaceResult } from '@vladmandic/human';
2
+ import { FaceLandmarker } from '@mediapipe/tasks-vision';
3
+ import React from 'react';
4
+
5
+ interface RawFacialData {
6
+ faceEmbedding: number[];
7
+ faceMesh: number[][];
8
+ faceLandmarks: number[][];
9
+ faceRotation: {
10
+ angle: {
11
+ yaw: number;
12
+ pitch: number;
13
+ roll: number;
14
+ };
15
+ matrix: number[];
16
+ } | null;
17
+ faceBox: {
18
+ x: number;
19
+ y: number;
20
+ width: number;
21
+ height: number;
22
+ } | null;
23
+ emotion?: Array<{
24
+ emotion: string;
25
+ score: number;
26
+ }>;
27
+ timestamp: number;
28
+ }
29
+ interface LivenessData {
30
+ blinkCount: number;
31
+ blinkTimestamps: number[];
32
+ emotionTransitions: Array<{
33
+ from: string;
34
+ to: string;
35
+ timestamp: number;
36
+ duration: number;
37
+ }>;
38
+ headMovement: {
39
+ yawRange: [number, number];
40
+ pitchRange: [number, number];
41
+ rollRange: [number, number];
42
+ };
43
+ verificationDuration: number;
44
+ stepCompletionTimes: {
45
+ happy: number;
46
+ eyeBlink: number;
47
+ };
48
+ }
49
+ interface VerificationResult {
50
+ rawFacialData: RawFacialData;
51
+ livenessData: LivenessData;
52
+ isComplete: boolean;
53
+ message: string;
54
+ }
55
+ interface AleoEmbeddingChunks {
56
+ embedding_chunk1: number[][];
57
+ embedding_chunk2: number[][];
58
+ embedding_chunk3: number[][];
59
+ embedding_chunk4: number[][];
60
+ }
61
+ interface AleoCircuitInputs extends AleoEmbeddingChunks {
62
+ liveness_combined: number;
63
+ }
64
+ /**
65
+ * Clean biometric data payload
66
+ */
67
+ interface BiometricSimHashPayload {
68
+ simhash_full: string;
69
+ liveness_combined: number;
70
+ }
71
+ interface AleoCircuitInputsComplete extends AleoCircuitInputs {
72
+ salt: string;
73
+ }
74
+ interface DataQuality {
75
+ isValid: boolean;
76
+ score: number;
77
+ issues: string[];
78
+ }
79
+ /**
80
+ * Average multiple embeddings to reduce noise (Robust outlier rejection)
81
+ * We calculate the median of all embeddings to find a stable center point,
82
+ * then filter out frames that are too far from this center, and average the rest.
83
+ */
84
+ declare function averageEmbeddings(embeddings: number[][]): number[];
85
+ /**
86
+ * Extract raw facial data from Human.js face detection result
87
+ * Improved with better error handling and data validation
88
+ */
89
+ declare function extractRawFacialData(face: FaceResult): RawFacialData;
90
+ /**
91
+ * Normalize face embedding vector to unit length (L2 normalization)
92
+ */
93
+ declare function normalizeEmbedding(embedding: number[]): number[];
94
+ /**
95
+ * Create liveness data from verification session
96
+ */
97
+ declare function createLivenessData(blinkCount: number, blinkTimestamps: number[], emotionTransitions: LivenessData['emotionTransitions'], headMovement: LivenessData['headMovement'], verificationDuration: number, stepCompletionTimes: LivenessData['stepCompletionTimes']): LivenessData;
98
+ declare class EmbeddingQuantizer {
99
+ private DEFAULT_MIN;
100
+ private DEFAULT_MAX;
101
+ private readonly EPSILON;
102
+ constructor(calibrationParams?: {
103
+ min?: number;
104
+ max?: number;
105
+ });
106
+ /**
107
+ * Quantize embedding to u8 format for Aleo circuit
108
+ */
109
+ quantizeEmbedding(embedding: number[] | Float32Array, targetRows?: number, targetCols?: number): number[][];
110
+ /**
111
+ * Normalize to [0, 1] range with improved outlier handling
112
+ */
113
+ private normalizeToRange;
114
+ /**
115
+ * Strategic sampling with information-preserving bias
116
+ */
117
+ private reshapeEmbedding;
118
+ /**
119
+ * Improved sampling with better distribution
120
+ */
121
+ private sampleAndReshape;
122
+ /**
123
+ * Pad with mean value to maintain distribution
124
+ */
125
+ private padAndReshape;
126
+ /**
127
+ * Process embedding for all 4 Aleo circuit chunks
128
+ */
129
+ processForAleo(embedding: number[] | Float32Array): AleoEmbeddingChunks;
130
+ /**
131
+ * Create chunk with circular offset
132
+ */
133
+ private createChunkWithOffset;
134
+ /**
135
+ * Validate quantized embedding meets Aleo requirements
136
+ */
137
+ validateForAleo(chunks: AleoEmbeddingChunks): void;
138
+ /**
139
+ * Calibrate parameters from sample embeddings
140
+ */
141
+ calibrateFromSamples(samples: (number[] | Float32Array)[]): {
142
+ min: number;
143
+ max: number;
144
+ };
145
+ }
146
+ /**
147
+ * Validate facial data quality with detailed scoring
148
+ */
149
+ declare function validateDataQuality(data: RawFacialData): DataQuality;
150
+ /**
151
+ * Process facial data for Aleo circuit with complete validation
152
+ */
153
+ declare function processFacialDataForAleo(face: FaceResult, livenessData: LivenessData, quantizer?: EmbeddingQuantizer): AleoCircuitInputs;
154
+ interface AleoSimHashInputs {
155
+ simhash_full: string;
156
+ liveness_combined: string;
157
+ salt: string;
158
+ }
159
+ interface AleoSimHashInputs {
160
+ simhash_full: string;
161
+ liveness_combined: string;
162
+ salt: string;
163
+ }
164
+ /**
165
+ * Process facial data using SimHash (Simplified for local testing)
166
+ */
167
+ declare function generateBiometricSimHash(face: FaceResult, livenessData: LivenessData, averagedEmbedding?: number[]): BiometricSimHashPayload;
168
+ declare function generateRandomSalt(): string;
169
+ declare function createCalibratedQuantizer(samples: (number[] | Float32Array)[]): EmbeddingQuantizer;
170
+ /**
171
+ * Calculate Hamming Distance between two hex-encoded SimHashes.
172
+ * Delegates to the canonical byte-level implementation in simHash.ts
173
+ * to guarantee consistency with the on-chain comparison logic.
174
+ */
175
+ declare function calculateHammingDistance(hash1: string, hash2: string): number;
176
+
177
+ /**
178
+ * Face occlusion checks from a video frame + MediaPipe Face Landmarker points.
179
+ *
180
+ * Glasses Detection Overhaul (v2):
181
+ * Six independent signals are computed per eye. A bilateral threshold model
182
+ * (≥3 signals on both eyes, or ≥4 total + ≥1 bridge signal) fires the
183
+ * "glasses detected" flag. An internal per-signal EMA smooths results across
184
+ * frames, so a single noisy frame cannot cause a false positive.
185
+ *
186
+ * Public API is unchanged — callers of checkFaceOcclusionRaw,
187
+ * FaceOcclusionTracker, and checkFaceOcclusion need no modifications.
188
+ */
189
+ interface NormalizedLandmark {
190
+ x: number;
191
+ y: number;
192
+ z?: number;
193
+ }
194
+ interface FaceOcclusionResult {
195
+ ok: boolean;
196
+ message?: string;
197
+ }
198
+ interface OcclusionCheckOptions {
199
+ yawDegrees?: number;
200
+ duringLivenessFlash?: boolean;
201
+ }
202
+ interface RgbSample {
203
+ r: number;
204
+ g: number;
205
+ b: number;
206
+ luma: number;
207
+ }
208
+ /**
209
+ * Stateful glasses detector with per-signal EMA temporal smoothing.
210
+ * Create one instance and call `update()` on every frame.
211
+ */
212
+ declare class GlassesDetector {
213
+ private emaLeft;
214
+ private emaRight;
215
+ private emaBridge;
216
+ private consecutivePositive;
217
+ private consecutiveNegative;
218
+ private _detected;
219
+ reset(): void;
220
+ get detected(): boolean;
221
+ /**
222
+ * Feed a new frame into the detector.
223
+ * @returns true if glasses are currently detected (after smoothing).
224
+ */
225
+ update(skin: RgbSample, ctx: CanvasRenderingContext2D, landmarks: NormalizedLandmark[], imgW: number, imgH: number, options?: OcclusionCheckOptions): boolean;
226
+ }
227
+ /**
228
+ * Stateful face mask detector with per-signal EMA temporal smoothing.
229
+ * Create one instance; call update() every frame.
230
+ */
231
+ declare class MaskDetector {
232
+ private ema;
233
+ private consecutivePositive;
234
+ private consecutiveNegative;
235
+ private _detected;
236
+ reset(): void;
237
+ get detected(): boolean;
238
+ update(skin: RgbSample, ctx: CanvasRenderingContext2D, landmarks: NormalizedLandmark[], imgW: number, imgH: number): boolean;
239
+ }
240
+ /**
241
+ * Tracks occlusion across frames with pre-arm debounce and session-violation flag.
242
+ */
243
+ declare class FaceOcclusionTracker {
244
+ private armed;
245
+ private blocked;
246
+ private obstructionFrames;
247
+ private clearFrames;
248
+ private sessionViolation;
249
+ private lastMessage;
250
+ private readonly preArmBlockFrames;
251
+ private readonly violationAfterFrames;
252
+ private readonly releaseFrames;
253
+ reset(): void;
254
+ arm(): void;
255
+ hasSessionViolation(): boolean;
256
+ apply(raw: FaceOcclusionResult): FaceOcclusionResult;
257
+ }
258
+ declare function checkFaceOcclusionRaw(landmarks: NormalizedLandmark[], ctx: CanvasRenderingContext2D, width: number, height: number, options?: OcclusionCheckOptions): FaceOcclusionResult;
259
+ /** @deprecated Use tracker + checkFaceOcclusionRaw */
260
+ declare function checkFaceOcclusion(landmarks: NormalizedLandmark[], ctx: CanvasRenderingContext2D, width: number, height: number): FaceOcclusionResult;
261
+
262
+ /**
263
+ * Generates deterministic random hyperplanes
264
+ * These chop the high-dimensional space into 256 regions
265
+ * @param dimensions Input embedding dimensions
266
+ * @param count Number of hyperplanes (bits in output hash)
267
+ */
268
+ declare function generateHyperplanes(dimensions: number, count: number): number[][];
269
+ /**
270
+ * Computes SimHash for a given embedding
271
+ * @param embedding The face embedding vector
272
+ * @param hyperplanes Pre-computed hyperplanes
273
+ * @returns Hex string representation of the hash
274
+ */
275
+ declare function computeSimHash(embedding: number[], hyperplanes: number[][]): string;
276
+ /**
277
+ * Similarity from Hamming distance: 1 - (distance / totalBits).
278
+ * Same hash => 1, max distance (256) => 0.
279
+ */
280
+ declare function similarityFromDistance(distance: number, totalBits?: number): number;
281
+ /**
282
+ * Computes Hamming Distance between two hex strings
283
+ */
284
+ declare function hammingDistance(hex1: string, hex2: string): number;
285
+ declare function getHyperplanes(dimensions?: number, count?: number): number[][];
286
+
287
+ type FlashColor = 'red' | 'green' | 'blue' | 'none';
288
+ interface LivenessResult {
289
+ passed: boolean;
290
+ score: number;
291
+ message?: string;
292
+ }
293
+ declare class LivenessEngine {
294
+ private videoElement;
295
+ private canvas;
296
+ private ctx;
297
+ private flashOverlay;
298
+ private colorHistory;
299
+ constructor();
300
+ setVideoElement(video: HTMLVideoElement): void;
301
+ setFlashOverlay(overlay: HTMLElement): void;
302
+ /**
303
+ * Run the active light reflection sequence
304
+ * Flashes colors and measures the face bounding box reflection
305
+ */
306
+ runLivenessSequence(boundingBox: {
307
+ x: number;
308
+ y: number;
309
+ width: number;
310
+ height: number;
311
+ }, assertFaceClear?: () => {
312
+ aligned: boolean;
313
+ message?: string;
314
+ }): Promise<LivenessResult>;
315
+ private flashAndMeasure;
316
+ private analyzeResults;
317
+ }
318
+
319
+ /**
320
+ * Capture quality checks for face verification:
321
+ * - Lighting (luminance) so we have enough face data
322
+ * - Face size so the face has enough resolution
323
+ */
324
+ declare const CAPTURE_QUALITY: {
325
+ /** Min average luminance in face region (0-255). Below = too dark */
326
+ readonly LUMINANCE_MIN: 80;
327
+ /** Max average luminance. Above = overexposed / washed out */
328
+ readonly LUMINANCE_MAX: 220;
329
+ /** Min face bounding box width/height in pixels */
330
+ readonly FACE_MIN_SIZE: 100;
331
+ /** Ideal min size for best results */
332
+ readonly FACE_IDEAL_SIZE: 140;
333
+ };
334
+ type FaceBox = {
335
+ x: number;
336
+ y: number;
337
+ width: number;
338
+ height: number;
339
+ };
340
+ /**
341
+ * Get face bounding box from Human.js face result (handles multiple formats).
342
+ */
343
+ declare function getFaceBox(face: {
344
+ box?: unknown;
345
+ }): FaceBox | null;
346
+ /**
347
+ * Compute average luminance (0-255) in a region of the canvas.
348
+ * Uses luminance formula: 0.299*R + 0.587*G + 0.114*B.
349
+ * Samples every 4th pixel for speed.
350
+ */
351
+ declare function getRegionLuminance(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number): number;
352
+ /**
353
+ * Get luminance in the face region. Canvas must already have the current video frame drawn.
354
+ * Face box can be in pixel or normalized (0-1) coordinates; pass canvas width/height to scale.
355
+ */
356
+ declare function getFaceLuminance(ctx: CanvasRenderingContext2D, face: {
357
+ box?: unknown;
358
+ }, canvasWidth: number, canvasHeight: number): number;
359
+ interface CaptureQualityResult {
360
+ luminance: number;
361
+ luminanceOk: boolean;
362
+ faceWidth: number;
363
+ faceHeight: number;
364
+ faceSizeOk: boolean;
365
+ /** True if lighting and face size are both OK */
366
+ ready: boolean;
367
+ message: string;
368
+ }
369
+ /**
370
+ * Evaluate capture quality from luminance and face box size.
371
+ */
372
+ declare function evaluateCaptureQuality(luminance: number, faceWidth: number, faceHeight: number): CaptureQualityResult;
373
+
374
+ declare const HUMAN_CONFIG: {
375
+ modelBasePath: string;
376
+ backend: "webgpu";
377
+ async: boolean;
378
+ cacheSensitivity: number;
379
+ skipFrame: boolean;
380
+ warmup: "none";
381
+ videoOptimized: boolean;
382
+ deallocate: boolean;
383
+ face: {
384
+ enabled: boolean;
385
+ detector: {
386
+ enabled: boolean;
387
+ rotation: boolean;
388
+ return: boolean;
389
+ minConfidence: number;
390
+ maxFaces: number;
391
+ modelPath: string;
392
+ };
393
+ mesh: {
394
+ enabled: boolean;
395
+ return: boolean;
396
+ };
397
+ landmarks: {
398
+ enabled: boolean;
399
+ return: boolean;
400
+ };
401
+ emotion: {
402
+ enabled: boolean;
403
+ return: boolean;
404
+ modelPath: string;
405
+ minConfidence: number;
406
+ };
407
+ description: {
408
+ enabled: boolean;
409
+ return: boolean;
410
+ modelPath: string;
411
+ };
412
+ iris: {
413
+ enabled: boolean;
414
+ };
415
+ antispoof: {
416
+ enabled: boolean;
417
+ };
418
+ liveness: {
419
+ enabled: boolean;
420
+ };
421
+ };
422
+ filter: {
423
+ enabled: boolean;
424
+ };
425
+ body: {
426
+ enabled: boolean;
427
+ };
428
+ hand: {
429
+ enabled: boolean;
430
+ };
431
+ object: {
432
+ enabled: boolean;
433
+ };
434
+ gesture: {
435
+ enabled: boolean;
436
+ return: boolean;
437
+ minConfidence: number;
438
+ };
439
+ debug: boolean;
440
+ profile: boolean;
441
+ };
442
+ declare const VERIFICATION_CONSTANTS: {
443
+ HAPPY_SUSTAIN_DURATION: number;
444
+ BLINK_REQUIRED_COUNT: number;
445
+ BLINK_COOLDOWN: number;
446
+ PROCESS_INTERVAL: number;
447
+ FPS_UPDATE_INTERVAL: number;
448
+ DEBUG_LOG_INTERVAL: number;
449
+ PROCESS_WIDTH: number;
450
+ PROCESS_HEIGHT: number;
451
+ SKIP_FRAMES: number;
452
+ };
453
+ declare const BACKEND_PRIORITY: readonly ["webgpu", "webgl", "wasm"];
454
+
455
+ type ChainType = 'solana' | 'evm';
456
+ interface VerificationPayload {
457
+ simhashFull: string;
458
+ livenessCombined: number;
459
+ walletAddress: string;
460
+ chain: ChainType;
461
+ }
462
+ interface RelayerConfig {
463
+ baseUrl: string;
464
+ network?: 'mainnet' | 'testnet';
465
+ /**
466
+ * The application ID assigned to your project.
467
+ * Required for production usage to bypass origin restrictions.
468
+ */
469
+ appId?: string;
470
+ }
471
+ interface RelayerResponse {
472
+ success: boolean;
473
+ txHash?: string;
474
+ data?: any;
475
+ }
476
+ declare class VerifiedRelayerClient {
477
+ private baseUrl;
478
+ private network;
479
+ private appId?;
480
+ constructor(config: RelayerConfig);
481
+ /**
482
+ * Submits the face verification payload to the backend relayer.
483
+ * The backend handles gas abstraction and execution for both EVM and Solana users.
484
+ */
485
+ submitVerification(payload: VerificationPayload): Promise<RelayerResponse>;
486
+ }
487
+
488
+ interface VisionModels {
489
+ faceLandmarker: FaceLandmarker | null;
490
+ human: Human | null;
491
+ isInitialized: boolean;
492
+ error: string | null;
493
+ }
494
+ declare const useVisionModels: () => VisionModels;
495
+
496
+ declare function useCameraStream(isReady: boolean): {
497
+ stream: MediaStream | null;
498
+ error: string | null;
499
+ startCamera: () => Promise<void>;
500
+ };
501
+
502
+ interface FaceZKProps {
503
+ onBiometricData?: (payload: {
504
+ simhash_full: string;
505
+ }) => void;
506
+ onVerificationComplete?: (result: any) => void;
507
+ onContinue?: () => void;
508
+ className?: string;
509
+ }
510
+
511
+ declare const FaceZKInner: React.FC<FaceZKProps>;
512
+
513
+ interface ProofOfHumanityProps {
514
+ onNextStep: () => void;
515
+ onBiometricData?: (data: {
516
+ simhash_full: string;
517
+ }) => void;
518
+ onVerificationComplete?: (result: VerificationResult) => void;
519
+ className?: string;
520
+ containerClassName?: string;
521
+ buttonClassName?: string;
522
+ }
523
+ declare const ProofOfHumanity: React.FC<ProofOfHumanityProps>;
524
+
525
+ interface VerifiedOnchainFlowProps {
526
+ relayerUrl?: string;
527
+ requiredSocials?: Array<'farcaster' | 'lens' | 'x' | 'tiktok' | 'instagram' | 'youtube' | string>;
528
+ requiredWallets?: Array<'phantom' | 'backpack' | 'metamask' | 'rainbow' | string>;
529
+ requireBiometrics?: boolean;
530
+ onIdentityVerified: (data: VerifiedIdentityData) => void;
531
+ className?: string;
532
+ environment?: 'mainnet' | 'testnet';
533
+ appId?: string;
534
+ }
535
+ interface VerifiedIdentityData {
536
+ walletAddress: string;
537
+ chain: 'evm' | 'solana';
538
+ socialProfile?: any;
539
+ socialPlatform?: string;
540
+ humanityCode?: string;
541
+ simHash?: string;
542
+ }
543
+ declare const VerifiedOnchainFlow: React.FC<VerifiedOnchainFlowProps>;
544
+
545
+ interface WalletSelectionProps {
546
+ onWalletConnect: (walletSlug: string) => void;
547
+ className?: string;
548
+ requiredWallets?: Array<'phantom' | 'backpack' | 'metamask' | 'rainbow' | string>;
549
+ }
550
+ declare const WalletSelection: React.FC<WalletSelectionProps>;
551
+
552
+ interface SocialConnectionProps {
553
+ onSocialConnect: (platform: string, profileData?: any) => void;
554
+ onNextStep: () => void;
555
+ userAddress?: string;
556
+ availableSocials: string[];
557
+ className?: string;
558
+ backendUrl?: string;
559
+ environment?: 'mainnet' | 'testnet';
560
+ }
561
+ declare const SocialConnection: React.FC<SocialConnectionProps>;
562
+
563
+ export { type AleoCircuitInputs, type AleoCircuitInputsComplete, type AleoEmbeddingChunks, type AleoSimHashInputs, BACKEND_PRIORITY, type BiometricSimHashPayload, CAPTURE_QUALITY, type CaptureQualityResult, type ChainType, type DataQuality, EmbeddingQuantizer, type FaceBox, type FaceOcclusionResult, FaceOcclusionTracker, FaceZKInner as FaceZK, type FlashColor, GlassesDetector, HUMAN_CONFIG, type LivenessData, LivenessEngine, type LivenessResult, MaskDetector, type NormalizedLandmark, type OcclusionCheckOptions, ProofOfHumanity, type RawFacialData, type RelayerConfig, type RelayerResponse, SocialConnection, VERIFICATION_CONSTANTS, type VerificationPayload, type VerificationResult, type VerifiedIdentityData, VerifiedOnchainFlow, type VerifiedOnchainFlowProps, VerifiedRelayerClient, type VisionModels, WalletSelection, averageEmbeddings, calculateHammingDistance, checkFaceOcclusion, checkFaceOcclusionRaw, computeSimHash, createCalibratedQuantizer, createLivenessData, evaluateCaptureQuality, extractRawFacialData, generateBiometricSimHash, generateHyperplanes, generateRandomSalt, getFaceBox, getFaceLuminance, getHyperplanes, getRegionLuminance, hammingDistance, normalizeEmbedding, processFacialDataForAleo, similarityFromDistance, useCameraStream, useVisionModels, validateDataQuality };