solana-age-verify-sdk 2.0.0-beta.10
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 +193 -0
- package/dist/adapters/blazeface.d.ts +15 -0
- package/dist/adapters/blazeface.js +258 -0
- package/dist/adapters/mediapipe.d.ts +7 -0
- package/dist/adapters/mediapipe.js +55 -0
- package/dist/adapters/onnx.d.ts +10 -0
- package/dist/adapters/onnx.js +171 -0
- package/dist/camera.d.ts +15 -0
- package/dist/camera.js +76 -0
- package/dist/embedding/descriptor.d.ts +22 -0
- package/dist/embedding/descriptor.js +134 -0
- package/dist/hashing/facehash.d.ts +3 -0
- package/dist/hashing/facehash.js +27 -0
- package/dist/hashing/webcrypto.d.ts +1 -0
- package/dist/hashing/webcrypto.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/liveness/challenges.d.ts +3 -0
- package/dist/liveness/challenges.js +34 -0
- package/dist/liveness/scorer.d.ts +1 -0
- package/dist/liveness/scorer.js +3 -0
- package/dist/liveness/texture.d.ts +72 -0
- package/dist/liveness/texture.js +266 -0
- package/dist/security.d.ts +14 -0
- package/dist/security.js +73 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.js +9 -0
- package/dist/ui/spinner.d.ts +5 -0
- package/dist/ui/spinner.js +36 -0
- package/dist/verify.d.ts +4 -0
- package/dist/verify.js +970 -0
- package/dist/worker/frame.d.ts +5 -0
- package/dist/worker/frame.js +1 -0
- package/dist/worker/infer.d.ts +4 -0
- package/dist/worker/infer.js +22 -0
- package/dist/worker/worker.d.ts +0 -0
- package/dist/worker/worker.js +61 -0
- package/package.json +50 -0
- package/public/models/age_gender.onnx +1446 -0
- package/public/models/age_gender_model-weights_manifest.json +62 -0
- package/public/models/age_gender_model.shard1 +1447 -0
- package/public/models/face_landmark_68_model-weights_manifest.json +60 -0
- package/public/models/face_landmark_68_model.shard1 +1447 -0
- package/public/models/face_recognition_model-weights_manifest.json +128 -0
- package/public/models/face_recognition_model.shard1 +1447 -0
- package/public/models/face_recognition_model.shard2 +1447 -0
- package/public/models/ort-wasm-simd-threaded.asyncify.wasm +0 -0
- package/public/models/ort-wasm-simd-threaded.jsep.wasm +0 -0
- package/public/models/ort-wasm-simd-threaded.wasm +0 -0
- package/public/models/tiny_face_detector_model-weights_manifest.json +30 -0
- package/public/models/tiny_face_detector_model.shard1 +1447 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Texture Analysis for Passive Liveness Detection
|
|
3
|
+
*
|
|
4
|
+
* This module analyzes the surface characteristics of a face to distinguish between:
|
|
5
|
+
* - Real human faces (3D, natural skin texture, pores, wrinkles)
|
|
6
|
+
* - Spoofing attempts (2D photos, screens, masks)
|
|
7
|
+
*
|
|
8
|
+
* Techniques used:
|
|
9
|
+
* 1. Local Binary Patterns (LBP) - Detects local texture patterns
|
|
10
|
+
* 2. Frequency Analysis - Identifies print/screen artifacts
|
|
11
|
+
* 3. Moiré Pattern Detection - Detects screen recapture
|
|
12
|
+
* 4. Reflectance Analysis - Analyzes light interaction with surface
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Analyzes texture from a face region to detect liveness
|
|
16
|
+
*/
|
|
17
|
+
export class TextureAnalyzer {
|
|
18
|
+
constructor() {
|
|
19
|
+
Object.defineProperty(this, "canvas", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: void 0
|
|
24
|
+
});
|
|
25
|
+
Object.defineProperty(this, "ctx", {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true,
|
|
29
|
+
value: void 0
|
|
30
|
+
});
|
|
31
|
+
// Use OffscreenCanvas in worker, HTMLCanvasElement in main thread
|
|
32
|
+
if (typeof OffscreenCanvas !== 'undefined') {
|
|
33
|
+
this.canvas = new OffscreenCanvas(256, 256);
|
|
34
|
+
this.ctx = this.canvas.getContext('2d');
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.canvas = document.createElement('canvas');
|
|
38
|
+
this.canvas.width = 256;
|
|
39
|
+
this.canvas.height = 256;
|
|
40
|
+
this.ctx = this.canvas.getContext('2d');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Main analysis function
|
|
45
|
+
*/
|
|
46
|
+
async analyze(frame, faceRegion) {
|
|
47
|
+
// Extract face region or use entire frame
|
|
48
|
+
const imageData = this.extractRegion(frame, faceRegion);
|
|
49
|
+
// Run parallel analyses
|
|
50
|
+
const [lbpScore, freqScore, moireDetected, reflectance] = await Promise.all([
|
|
51
|
+
this.analyzeLBP(imageData),
|
|
52
|
+
this.analyzeFrequency(imageData),
|
|
53
|
+
this.detectMoire(imageData),
|
|
54
|
+
this.analyzeReflectance(imageData)
|
|
55
|
+
]);
|
|
56
|
+
// Combine scores
|
|
57
|
+
const skinComplexity = lbpScore;
|
|
58
|
+
const frequencyScore = freqScore;
|
|
59
|
+
// Decision logic: Real if complexity is high AND no moiré AND natural reflectance
|
|
60
|
+
const complexityThreshold = 0.3;
|
|
61
|
+
const frequencyThreshold = 0.35;
|
|
62
|
+
const isReal = skinComplexity > complexityThreshold &&
|
|
63
|
+
frequencyScore > frequencyThreshold &&
|
|
64
|
+
!moireDetected &&
|
|
65
|
+
reflectance !== 'artificial';
|
|
66
|
+
// Confidence based on how far from thresholds
|
|
67
|
+
const complexityDist = Math.abs(skinComplexity - complexityThreshold);
|
|
68
|
+
const freqDist = Math.abs(frequencyScore - frequencyThreshold);
|
|
69
|
+
const confidence = Math.min(1.0, (complexityDist + freqDist) / 2 + 0.5);
|
|
70
|
+
return {
|
|
71
|
+
isReal,
|
|
72
|
+
confidence,
|
|
73
|
+
features: {
|
|
74
|
+
skinComplexity,
|
|
75
|
+
moireDetected,
|
|
76
|
+
frequencyScore,
|
|
77
|
+
reflectancePattern: reflectance
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Extract region of interest from frame
|
|
83
|
+
*/
|
|
84
|
+
extractRegion(frame, region) {
|
|
85
|
+
// Draw to canvas
|
|
86
|
+
if (frame instanceof ImageData) {
|
|
87
|
+
this.ctx.putImageData(frame, 0, 0);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
this.ctx.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);
|
|
91
|
+
}
|
|
92
|
+
// Extract region if specified
|
|
93
|
+
if (region) {
|
|
94
|
+
const { x, y, width, height } = region;
|
|
95
|
+
return this.ctx.getImageData(x, y, width, height);
|
|
96
|
+
}
|
|
97
|
+
return this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Local Binary Patterns (LBP) Analysis
|
|
101
|
+
* Detects micro-texture patterns in skin
|
|
102
|
+
*/
|
|
103
|
+
async analyzeLBP(imageData) {
|
|
104
|
+
const { data, width, height } = imageData;
|
|
105
|
+
const grayscale = this.toGrayscale(data, width, height);
|
|
106
|
+
// LBP histogram (256 bins for uniform LBP)
|
|
107
|
+
const histogram = new Array(256).fill(0);
|
|
108
|
+
// Calculate LBP for each pixel (excluding borders)
|
|
109
|
+
for (let y = 1; y < height - 1; y++) {
|
|
110
|
+
for (let x = 1; x < width - 1; x++) {
|
|
111
|
+
const center = grayscale[y * width + x];
|
|
112
|
+
let lbpValue = 0;
|
|
113
|
+
// 8 neighbors in clockwise order
|
|
114
|
+
const neighbors = [
|
|
115
|
+
grayscale[(y - 1) * width + (x - 1)], // top-left
|
|
116
|
+
grayscale[(y - 1) * width + x], // top
|
|
117
|
+
grayscale[(y - 1) * width + (x + 1)], // top-right
|
|
118
|
+
grayscale[y * width + (x + 1)], // right
|
|
119
|
+
grayscale[(y + 1) * width + (x + 1)], // bottom-right
|
|
120
|
+
grayscale[(y + 1) * width + x], // bottom
|
|
121
|
+
grayscale[(y + 1) * width + (x - 1)], // bottom-left
|
|
122
|
+
grayscale[y * width + (x - 1)] // left
|
|
123
|
+
];
|
|
124
|
+
// Build LBP code
|
|
125
|
+
for (let i = 0; i < 8; i++) {
|
|
126
|
+
if (neighbors[i] >= center) {
|
|
127
|
+
lbpValue |= (1 << i);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
histogram[lbpValue]++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Normalize histogram
|
|
134
|
+
const totalPixels = (width - 2) * (height - 2);
|
|
135
|
+
const normalizedHist = histogram.map(v => v / totalPixels);
|
|
136
|
+
// Calculate entropy (measure of texture complexity)
|
|
137
|
+
let entropy = 0;
|
|
138
|
+
for (const p of normalizedHist) {
|
|
139
|
+
if (p > 0) {
|
|
140
|
+
entropy -= p * Math.log2(p);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Normalize entropy to 0-1 range (max entropy for 256 bins is 8)
|
|
144
|
+
const normalizedEntropy = entropy / 8;
|
|
145
|
+
// Real skin has moderate-to-high entropy (complex texture)
|
|
146
|
+
// Photos/screens have lower entropy (uniform patterns)
|
|
147
|
+
return normalizedEntropy;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Frequency Domain Analysis
|
|
151
|
+
* Detects print patterns and screen grids
|
|
152
|
+
*/
|
|
153
|
+
async analyzeFrequency(imageData) {
|
|
154
|
+
const { data, width, height } = imageData;
|
|
155
|
+
const grayscale = this.toGrayscale(data, width, height);
|
|
156
|
+
// Simple frequency analysis using gradient magnitude
|
|
157
|
+
let totalGradient = 0;
|
|
158
|
+
let highFreqCount = 0;
|
|
159
|
+
for (let y = 1; y < height - 1; y++) {
|
|
160
|
+
for (let x = 1; x < width - 1; x++) {
|
|
161
|
+
// Sobel gradients
|
|
162
|
+
const gx = -grayscale[(y - 1) * width + (x - 1)] +
|
|
163
|
+
grayscale[(y - 1) * width + (x + 1)] +
|
|
164
|
+
-2 * grayscale[y * width + (x - 1)] +
|
|
165
|
+
2 * grayscale[y * width + (x + 1)] +
|
|
166
|
+
-grayscale[(y + 1) * width + (x - 1)] +
|
|
167
|
+
grayscale[(y + 1) * width + (x + 1)];
|
|
168
|
+
const gy = -grayscale[(y - 1) * width + (x - 1)] +
|
|
169
|
+
-2 * grayscale[(y - 1) * width + x] +
|
|
170
|
+
-grayscale[(y - 1) * width + (x + 1)] +
|
|
171
|
+
grayscale[(y + 1) * width + (x - 1)] +
|
|
172
|
+
2 * grayscale[(y + 1) * width + x] +
|
|
173
|
+
grayscale[(y + 1) * width + (x + 1)];
|
|
174
|
+
const magnitude = Math.sqrt(gx * gx + gy * gy);
|
|
175
|
+
totalGradient += magnitude;
|
|
176
|
+
// Count high-frequency components (sharp edges)
|
|
177
|
+
if (magnitude > 50) {
|
|
178
|
+
highFreqCount++;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const avgGradient = totalGradient / ((width - 2) * (height - 2));
|
|
183
|
+
const highFreqRatio = highFreqCount / ((width - 2) * (height - 2));
|
|
184
|
+
// Real skin has moderate gradients with natural variation
|
|
185
|
+
// Printed images have either too uniform or too sharp edges
|
|
186
|
+
// Score based on "naturalness" of gradient distribution
|
|
187
|
+
const naturalGradientRange = avgGradient > 5 && avgGradient < 30;
|
|
188
|
+
const naturalFreqRatio = highFreqRatio > 0.01 && highFreqRatio < 0.15;
|
|
189
|
+
return (naturalGradientRange && naturalFreqRatio) ? 0.7 : 0.3;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Moiré Pattern Detection
|
|
193
|
+
* Detects interference patterns from screen recapture
|
|
194
|
+
*/
|
|
195
|
+
async detectMoire(imageData) {
|
|
196
|
+
const { data, width, height } = imageData;
|
|
197
|
+
// Look for periodic patterns that indicate moiré
|
|
198
|
+
// Simple approach: check for regular oscillations in intensity
|
|
199
|
+
let periodicityScore = 0;
|
|
200
|
+
const sampleRows = 10;
|
|
201
|
+
for (let i = 0; i < sampleRows; i++) {
|
|
202
|
+
const y = Math.floor((height / sampleRows) * i);
|
|
203
|
+
const rowData = [];
|
|
204
|
+
for (let x = 0; x < width; x++) {
|
|
205
|
+
const idx = (y * width + x) * 4;
|
|
206
|
+
const gray = (data[idx] + data[idx + 1] + data[idx + 2]) / 3;
|
|
207
|
+
rowData.push(gray);
|
|
208
|
+
}
|
|
209
|
+
// Count zero-crossings (sign changes in derivative)
|
|
210
|
+
let crossings = 0;
|
|
211
|
+
for (let x = 1; x < rowData.length - 1; x++) {
|
|
212
|
+
const diff1 = rowData[x] - rowData[x - 1];
|
|
213
|
+
const diff2 = rowData[x + 1] - rowData[x];
|
|
214
|
+
if (diff1 * diff2 < 0)
|
|
215
|
+
crossings++;
|
|
216
|
+
}
|
|
217
|
+
// High crossing count indicates periodic pattern
|
|
218
|
+
const crossingRatio = crossings / width;
|
|
219
|
+
if (crossingRatio > 0.3)
|
|
220
|
+
periodicityScore++;
|
|
221
|
+
}
|
|
222
|
+
// Moiré detected if multiple rows show high periodicity
|
|
223
|
+
return periodicityScore > sampleRows * 0.5;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Reflectance Pattern Analysis
|
|
227
|
+
* Analyzes how light interacts with the surface
|
|
228
|
+
*/
|
|
229
|
+
async analyzeReflectance(imageData) {
|
|
230
|
+
const { data } = imageData;
|
|
231
|
+
// Analyze brightness distribution
|
|
232
|
+
const brightnesses = [];
|
|
233
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
234
|
+
const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3;
|
|
235
|
+
brightnesses.push(brightness);
|
|
236
|
+
}
|
|
237
|
+
// Calculate statistics
|
|
238
|
+
const mean = brightnesses.reduce((a, b) => a + b, 0) / brightnesses.length;
|
|
239
|
+
const variance = brightnesses.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / brightnesses.length;
|
|
240
|
+
const stdDev = Math.sqrt(variance);
|
|
241
|
+
// Real skin has moderate variance (natural shadows and highlights)
|
|
242
|
+
// Photos/screens tend to have either too uniform or too high contrast
|
|
243
|
+
const coefficientOfVariation = stdDev / (mean + 1); // +1 to avoid division by zero
|
|
244
|
+
if (coefficientOfVariation > 0.15 && coefficientOfVariation < 0.5) {
|
|
245
|
+
return 'natural';
|
|
246
|
+
}
|
|
247
|
+
else if (coefficientOfVariation < 0.1 || coefficientOfVariation > 0.7) {
|
|
248
|
+
return 'artificial';
|
|
249
|
+
}
|
|
250
|
+
return 'unknown';
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Convert RGBA to grayscale
|
|
254
|
+
*/
|
|
255
|
+
toGrayscale(data, width, height) {
|
|
256
|
+
const grayscale = new Uint8Array(width * height);
|
|
257
|
+
for (let i = 0; i < grayscale.length; i++) {
|
|
258
|
+
const idx = i * 4;
|
|
259
|
+
// Luminance formula
|
|
260
|
+
grayscale[i] = Math.floor(0.299 * data[idx] +
|
|
261
|
+
0.587 * data[idx + 1] +
|
|
262
|
+
0.114 * data[idx + 2]);
|
|
263
|
+
}
|
|
264
|
+
return grayscale;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
/**
|
|
3
|
+
* Gets the platform's public key from the obfuscated store.
|
|
4
|
+
* This is the address that receives the protocol fee and signs the verification record.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getPlatformPublicKey(): PublicKey;
|
|
7
|
+
/**
|
|
8
|
+
* Gets the protocol fee in SOL.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getProtocolFee(override?: number): number;
|
|
11
|
+
/**
|
|
12
|
+
* Security wrapper to ensure the transaction destination is correct.
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateTransactionDestination(destination: PublicKey): boolean;
|
package/dist/security.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
/**
|
|
3
|
+
* CORE SECURITY CONFIGURATION
|
|
4
|
+
* This file contains the platform's public configuration.
|
|
5
|
+
* It is designed to be minified and obfuscated during the build process
|
|
6
|
+
* to ensure immutability and prevent easy tampering in the distributed SDK.
|
|
7
|
+
*/
|
|
8
|
+
// Obfuscation placeholder removed as it's currently unused to avoid build errors.
|
|
9
|
+
// Fallback fee (0.001 SOL)
|
|
10
|
+
const _F_B = 0.001;
|
|
11
|
+
let _cachedPlatformPubKey = null;
|
|
12
|
+
/**
|
|
13
|
+
* Gets the platform's public key from the obfuscated store.
|
|
14
|
+
* This is the address that receives the protocol fee and signs the verification record.
|
|
15
|
+
*/
|
|
16
|
+
export function getPlatformPublicKey() {
|
|
17
|
+
if (_cachedPlatformPubKey)
|
|
18
|
+
return _cachedPlatformPubKey;
|
|
19
|
+
// In a production build, this would be swapped or populated from env
|
|
20
|
+
// For now, we use the VITE environment variable if available (Vite/Vercel)
|
|
21
|
+
// We check VITE_TREASURY_ADDRESS first as per configuration, then fallback to legacy VITE_PLATFORM_PUBLIC_KEY
|
|
22
|
+
const envKey = import.meta.env?.VITE_TREASURY_ADDRESS || import.meta.env?.VITE_PLATFORM_PUBLIC_KEY;
|
|
23
|
+
if (envKey) {
|
|
24
|
+
_cachedPlatformPubKey = new PublicKey(envKey);
|
|
25
|
+
return _cachedPlatformPubKey;
|
|
26
|
+
}
|
|
27
|
+
// Default/Fallback logic
|
|
28
|
+
// We use a base58 encoded version that is "minified" in the code.
|
|
29
|
+
// The string here is obfuscated to avoid plain text search and easy replacement.
|
|
30
|
+
const _ob = "OUJLV3dwUG9WSHVIVXNqbzltdmRRNlBaWG5uc0FFd0NzVlRCMTJOVER0aGo=";
|
|
31
|
+
const _dec = (str) => {
|
|
32
|
+
try {
|
|
33
|
+
// Browser-safe atob
|
|
34
|
+
if (typeof window !== 'undefined' && window.atob) {
|
|
35
|
+
return window.atob(str);
|
|
36
|
+
}
|
|
37
|
+
// If window.atob is missing (unlikely in modern browser), final hard fallback
|
|
38
|
+
return "9BKWwpPoVHuHUsjo9mvdQ6PZXnnsAEwCsVTB12NTDthj";
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
// Final fallback to avoid crash, but return something that doesn't reveal the key in plain text
|
|
42
|
+
return "9BKWwpPoVHuHUsjo9mvdQ6PZXnnsAEwCsVTB12NTDthj";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const _t = _dec(_ob);
|
|
46
|
+
try {
|
|
47
|
+
_cachedPlatformPubKey = new PublicKey(_t);
|
|
48
|
+
// Ensure the object itself cannot be modified
|
|
49
|
+
Object.freeze(_cachedPlatformPubKey);
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
console.error("CRITICAL: Failed to construct Platform PublicKey from fallback. This indicates a build-time corruption.");
|
|
53
|
+
_cachedPlatformPubKey = new PublicKey("11111111111111111111111111111111");
|
|
54
|
+
Object.freeze(_cachedPlatformPubKey);
|
|
55
|
+
}
|
|
56
|
+
return _cachedPlatformPubKey;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Gets the protocol fee in SOL.
|
|
60
|
+
*/
|
|
61
|
+
export function getProtocolFee(override) {
|
|
62
|
+
if (override !== undefined)
|
|
63
|
+
return override;
|
|
64
|
+
const envFee = import.meta.env?.VITE_PROTOCOL_FEE_SOL;
|
|
65
|
+
return envFee ? parseFloat(envFee) : _F_B;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Security wrapper to ensure the transaction destination is correct.
|
|
69
|
+
*/
|
|
70
|
+
export function validateTransactionDestination(destination) {
|
|
71
|
+
const platformKey = getPlatformPublicKey();
|
|
72
|
+
return destination.equals(platformKey);
|
|
73
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export interface VerifyHost18PlusOptions {
|
|
2
|
+
walletPubkeyBase58: string;
|
|
3
|
+
videoElement?: HTMLVideoElement;
|
|
4
|
+
uiMountEl?: HTMLElement;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
config?: Partial<VerifyConfig>;
|
|
7
|
+
onChallenge?: (challenge: string) => void;
|
|
8
|
+
modelPath?: string;
|
|
9
|
+
workerFactory?: () => Worker;
|
|
10
|
+
connection?: any;
|
|
11
|
+
wallet?: {
|
|
12
|
+
publicKey: any;
|
|
13
|
+
signTransaction: (tx: any) => Promise<any>;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface VerifyConfig {
|
|
17
|
+
challenges: string[];
|
|
18
|
+
minLivenessScore: number;
|
|
19
|
+
minAgeConfidence: number;
|
|
20
|
+
minAgeEstimate: number;
|
|
21
|
+
timeoutMs: number;
|
|
22
|
+
maxRetries: number;
|
|
23
|
+
cooldownMinutes: number;
|
|
24
|
+
protocolFeeSol?: number;
|
|
25
|
+
}
|
|
26
|
+
export declare const DEFAULT_CONFIG: VerifyConfig;
|
|
27
|
+
export interface ChallengeResult {
|
|
28
|
+
type: string;
|
|
29
|
+
passed: boolean;
|
|
30
|
+
score: number;
|
|
31
|
+
}
|
|
32
|
+
export interface VerifyResult {
|
|
33
|
+
over18: boolean;
|
|
34
|
+
facehash: string;
|
|
35
|
+
description: string;
|
|
36
|
+
verifiedAt: string;
|
|
37
|
+
protocolFeePaid?: boolean;
|
|
38
|
+
protocolFeeTxId?: string;
|
|
39
|
+
evidence: {
|
|
40
|
+
ageEstimate: number;
|
|
41
|
+
ageConfidence: number;
|
|
42
|
+
livenessScore: number;
|
|
43
|
+
textureScore?: number;
|
|
44
|
+
textureFeatures?: TextureFeatures;
|
|
45
|
+
ageMethod?: string;
|
|
46
|
+
challenges: ChallengeResult[];
|
|
47
|
+
modelVersions: Record<string, string>;
|
|
48
|
+
saltHex: string;
|
|
49
|
+
sessionNonceHex: string;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export interface FrameData {
|
|
53
|
+
data: Uint8ClampedArray;
|
|
54
|
+
width: number;
|
|
55
|
+
height: number;
|
|
56
|
+
timestamp: number;
|
|
57
|
+
}
|
|
58
|
+
export interface WorkerResponse {
|
|
59
|
+
type: 'RESULT' | 'ERROR' | 'LOADED';
|
|
60
|
+
payload?: any;
|
|
61
|
+
error?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface WorkerRequest {
|
|
64
|
+
type: 'PROCESS_FRAME' | 'LOAD_MODELS';
|
|
65
|
+
payload?: any;
|
|
66
|
+
}
|
|
67
|
+
export interface TextureFeatures {
|
|
68
|
+
skinComplexity: number;
|
|
69
|
+
moireDetected: boolean;
|
|
70
|
+
frequencyScore: number;
|
|
71
|
+
reflectancePattern: 'natural' | 'artificial' | 'unknown';
|
|
72
|
+
}
|
|
73
|
+
export interface DetectionResult {
|
|
74
|
+
faceFound: boolean;
|
|
75
|
+
landmarks?: number[];
|
|
76
|
+
ageEstimate?: number;
|
|
77
|
+
embedding?: number[];
|
|
78
|
+
confidence?: number;
|
|
79
|
+
textureScore?: number;
|
|
80
|
+
textureFeatures?: TextureFeatures;
|
|
81
|
+
ageConfidence?: number;
|
|
82
|
+
ageMethod?: 'onnx' | 'geometric' | 'unknown';
|
|
83
|
+
}
|
|
84
|
+
export interface FaceModelAdapter {
|
|
85
|
+
load(basePath?: string): Promise<void>;
|
|
86
|
+
detect(frame: ImageData | HTMLCanvasElement | OffscreenCanvas): Promise<DetectionResult>;
|
|
87
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a premium gradient spinner HTML string for use in SDK UI overlays
|
|
3
|
+
* Matches Solana brand colors (purple to cyan gradient)
|
|
4
|
+
*/
|
|
5
|
+
export function createSpinnerHTML() {
|
|
6
|
+
return `
|
|
7
|
+
<div style="display: flex; align-items: center; justify-content: center; padding: 20px;">
|
|
8
|
+
<div style="
|
|
9
|
+
width: 64px;
|
|
10
|
+
height: 64px;
|
|
11
|
+
border: 4px solid transparent;
|
|
12
|
+
border-radius: 50%;
|
|
13
|
+
background: linear-gradient(#0f172a, #0f172a) padding-box, linear-gradient(135deg, #a78bfa 0%, #60a5fa 50%, #14b8a6 100%) border-box;
|
|
14
|
+
animation: spin 0.8s linear infinite;
|
|
15
|
+
position: relative;
|
|
16
|
+
" aria-label="Loading">
|
|
17
|
+
<div style="
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 50%;
|
|
20
|
+
left: 50%;
|
|
21
|
+
transform: translate(-50%, -50%);
|
|
22
|
+
width: 48px;
|
|
23
|
+
height: 48px;
|
|
24
|
+
border-radius: 50%;
|
|
25
|
+
background: #0f172a;
|
|
26
|
+
"></div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
<style>
|
|
30
|
+
@keyframes spin {
|
|
31
|
+
from { transform: rotate(0deg); }
|
|
32
|
+
to { transform: rotate(360deg); }
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
35
|
+
`;
|
|
36
|
+
}
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { VerifyHost18PlusOptions, VerifyResult } from './types';
|
|
2
|
+
export declare function verifyHost18Plus(options: VerifyHost18PlusOptions): Promise<VerifyResult>;
|
|
3
|
+
export declare function createVerificationUI(): HTMLElement;
|
|
4
|
+
export declare function setExecutionBackend(backend: 'tfjs' | 'onnx'): void;
|