@kryptos_connect/mobile-sdk 0.0.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.
package/dist/index.js ADDED
@@ -0,0 +1,3834 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ KryptosConnectButton: () => KryptosConnectButton,
34
+ KryptosConnectModal: () => KryptosConnectModal,
35
+ KryptosConnectProvider: () => KryptosConnectProvider,
36
+ useKryptosConnect: () => useKryptosConnect
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+
40
+ // src/KryptosConnectButton.tsx
41
+ var import_react31 = __toESM(require("react"));
42
+ var import_react_native17 = require("react-native");
43
+
44
+ // src/assets/LogoIcon.tsx
45
+ var import_react = __toESM(require("react"));
46
+ var import_react_native_svg = __toESM(require("react-native-svg"));
47
+ var LogoIcon = ({ size = 36 }) => {
48
+ return /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.default, { width: size, height: size, viewBox: "0 0 36 36", fill: "none" }, /* @__PURE__ */ import_react.default.createElement(
49
+ import_react_native_svg.Path,
50
+ {
51
+ d: "M0 4.11429C0 1.84203 1.84203 0 4.11429 0H31.8857C34.158 0 36 1.84203 36 4.11429V31.8857C36 34.158 34.158 36 31.8857 36H4.11429C1.84203 36 0 34.158 0 31.8857V4.11429Z",
52
+ fill: "#00C693"
53
+ }
54
+ ), /* @__PURE__ */ import_react.default.createElement(
55
+ import_react_native_svg.Path,
56
+ {
57
+ d: "M12.3916 28.2857H8.43388C8.03646 28.2857 7.71429 27.9886 7.71429 27.6221V22.92C7.71429 22.744 7.7901 22.5752 7.92505 22.4508L9.66229 20.8487C9.79724 20.7243 9.98027 20.6544 10.1711 20.6544H12.3916C12.789 20.6544 13.1112 20.9515 13.1112 21.318V27.6221C13.1112 27.9886 12.789 28.2857 12.3916 28.2857Z",
58
+ fill: "white"
59
+ }
60
+ ), /* @__PURE__ */ import_react.default.createElement(
61
+ import_react_native_svg.Path,
62
+ {
63
+ d: "M27.5647 28.2857H22.0443C21.8535 28.2857 21.6704 28.2158 21.5355 28.0914L13.9798 21.1236C13.6988 20.8645 13.6988 20.4443 13.9798 20.1851L15.7788 18.5262C15.9137 18.4017 16.0968 18.3318 16.2876 18.3318H18.21C18.4009 18.3318 18.5839 18.4017 18.7189 18.5262L28.0735 27.1529C28.5268 27.5709 28.2058 28.2857 27.5647 28.2857Z",
64
+ fill: "white"
65
+ }
66
+ ), /* @__PURE__ */ import_react.default.createElement(
67
+ import_react_native_svg.Path,
68
+ {
69
+ d: "M27.5647 7.71429H22.0443C21.8535 7.71429 21.6704 7.7842 21.5355 7.90865L13.9798 14.8764C13.6988 15.1355 13.6988 15.5557 13.9798 15.8149L15.7788 17.4738C15.9137 17.5983 16.0968 17.6682 16.2876 17.6682H18.21C18.4009 17.6682 18.5839 17.5983 18.7189 17.4738L28.0735 8.84711C28.5268 8.42907 28.2058 7.71429 27.5647 7.71429Z",
70
+ fill: "white"
71
+ }
72
+ ), /* @__PURE__ */ import_react.default.createElement(
73
+ import_react_native_svg.Path,
74
+ {
75
+ d: "M12.3916 7.71429H8.43388C8.03646 7.71429 7.71429 8.01139 7.71429 8.37788V13.08C7.71429 13.256 7.7901 13.4248 7.92505 13.5492L9.66229 15.1513C9.79724 15.2757 9.98027 15.3456 10.1711 15.3456H12.3916C12.789 15.3456 13.1112 15.0485 13.1112 14.682V8.37788C13.1112 8.01139 12.789 7.71429 12.3916 7.71429Z",
76
+ fill: "white"
77
+ }
78
+ ));
79
+ };
80
+
81
+ // src/contexts/KryptosContext.tsx
82
+ var import_react2 = __toESM(require("react"));
83
+ var KryptosContext = import_react2.default.createContext(
84
+ void 0
85
+ );
86
+ var KryptosConnectProvider = ({ children, config }) => {
87
+ const [isInitialized, setIsInitialized] = import_react2.default.useState(false);
88
+ const [linkToken, setLinkToken] = import_react2.default.useState("");
89
+ const [user, setUser] = import_react2.default.useState(null);
90
+ const [email, setEmail] = import_react2.default.useState("");
91
+ const [userConsent, setUserConsent] = import_react2.default.useState(
92
+ null
93
+ );
94
+ return /* @__PURE__ */ import_react2.default.createElement(
95
+ KryptosContext.Provider,
96
+ {
97
+ value: {
98
+ ...config,
99
+ isInitialized,
100
+ setIsInitialized,
101
+ linkToken,
102
+ setLinkToken,
103
+ user,
104
+ setUser,
105
+ email,
106
+ setEmail,
107
+ userConsent,
108
+ setUserConsent
109
+ }
110
+ },
111
+ children
112
+ );
113
+ };
114
+ var useKryptosConnect = () => {
115
+ const ctx = import_react2.default.useContext(KryptosContext);
116
+ if (!ctx)
117
+ throw new Error(
118
+ "useKryptosConnect must be used inside <KryptosConnectProvider>"
119
+ );
120
+ return ctx;
121
+ };
122
+
123
+ // src/hooks/useTheme.ts
124
+ var import_react3 = __toESM(require("react"));
125
+
126
+ // src/theme/index.ts
127
+ var lightTheme = {
128
+ colors: {
129
+ primary: "#00C693",
130
+ primaryDark: "#00A67A",
131
+ background: "#FFFFFF",
132
+ surface: "#F8F9FA",
133
+ surfaceSecondary: "#F1F3F5",
134
+ text: "#1A1A1A",
135
+ textSecondary: "#6B7280",
136
+ textTertiary: "#9CA3AF",
137
+ border: "#E5E7EB",
138
+ borderLight: "#F3F4F6",
139
+ error: "#EF4444",
140
+ errorLight: "#FEE2E2",
141
+ success: "#10B981",
142
+ successLight: "#D1FAE5",
143
+ warning: "#F59E0B",
144
+ warningLight: "#FEF3C7",
145
+ overlay: "rgba(0, 0, 0, 0.5)",
146
+ white: "#FFFFFF",
147
+ black: "#000000"
148
+ },
149
+ spacing: {
150
+ xs: 4,
151
+ sm: 8,
152
+ md: 12,
153
+ lg: 16,
154
+ xl: 20,
155
+ xxl: 24,
156
+ xxxl: 32
157
+ },
158
+ borderRadius: {
159
+ xs: 4,
160
+ sm: 8,
161
+ md: 12,
162
+ lg: 16,
163
+ xl: 20,
164
+ full: 9999
165
+ },
166
+ fontSize: {
167
+ xs: 10,
168
+ sm: 12,
169
+ md: 14,
170
+ lg: 16,
171
+ xl: 18,
172
+ xxl: 20,
173
+ xxxl: 24,
174
+ display: 32
175
+ },
176
+ fontWeight: {
177
+ regular: "400",
178
+ medium: "500",
179
+ semibold: "600",
180
+ bold: "700"
181
+ },
182
+ shadow: {
183
+ sm: {
184
+ shadowColor: "#000",
185
+ shadowOffset: { width: 0, height: 1 },
186
+ shadowOpacity: 0.05,
187
+ shadowRadius: 2,
188
+ elevation: 1
189
+ },
190
+ md: {
191
+ shadowColor: "#000",
192
+ shadowOffset: { width: 0, height: 2 },
193
+ shadowOpacity: 0.1,
194
+ shadowRadius: 4,
195
+ elevation: 3
196
+ },
197
+ lg: {
198
+ shadowColor: "#000",
199
+ shadowOffset: { width: 0, height: 4 },
200
+ shadowOpacity: 0.15,
201
+ shadowRadius: 8,
202
+ elevation: 5
203
+ }
204
+ }
205
+ };
206
+ var darkTheme = {
207
+ colors: {
208
+ primary: "#00C693",
209
+ primaryDark: "#00A67A",
210
+ background: "#0F0F0F",
211
+ surface: "#1A1A1A",
212
+ surfaceSecondary: "#252525",
213
+ text: "#FFFFFF",
214
+ textSecondary: "#A1A1A1",
215
+ textTertiary: "#6B6B6B",
216
+ border: "#2D2D2D",
217
+ borderLight: "#1F1F1F",
218
+ error: "#EF4444",
219
+ errorLight: "#7F1D1D",
220
+ success: "#10B981",
221
+ successLight: "#065F46",
222
+ warning: "#F59E0B",
223
+ warningLight: "#78350F",
224
+ overlay: "rgba(0, 0, 0, 0.7)",
225
+ white: "#FFFFFF",
226
+ black: "#000000"
227
+ },
228
+ spacing: lightTheme.spacing,
229
+ borderRadius: lightTheme.borderRadius,
230
+ fontSize: lightTheme.fontSize,
231
+ fontWeight: lightTheme.fontWeight,
232
+ shadow: {
233
+ sm: {
234
+ shadowColor: "#000",
235
+ shadowOffset: { width: 0, height: 1 },
236
+ shadowOpacity: 0.2,
237
+ shadowRadius: 2,
238
+ elevation: 1
239
+ },
240
+ md: {
241
+ shadowColor: "#000",
242
+ shadowOffset: { width: 0, height: 2 },
243
+ shadowOpacity: 0.3,
244
+ shadowRadius: 4,
245
+ elevation: 3
246
+ },
247
+ lg: {
248
+ shadowColor: "#000",
249
+ shadowOffset: { width: 0, height: 4 },
250
+ shadowOpacity: 0.4,
251
+ shadowRadius: 8,
252
+ elevation: 5
253
+ }
254
+ }
255
+ };
256
+ var getTheme = (isDark) => {
257
+ return isDark ? darkTheme : lightTheme;
258
+ };
259
+
260
+ // src/hooks/useTheme.ts
261
+ var useTheme = () => {
262
+ const { theme } = useKryptosConnect();
263
+ const currentTheme = import_react3.default.useMemo(() => {
264
+ return getTheme(theme === "dark");
265
+ }, [theme]);
266
+ return currentTheme;
267
+ };
268
+
269
+ // src/molecules/Auth.tsx
270
+ var import_react13 = __toESM(require("react"));
271
+ var import_react_native6 = require("react-native");
272
+
273
+ // src/assets/LinkIcon.tsx
274
+ var import_react4 = __toESM(require("react"));
275
+ var import_react_native_svg2 = __toESM(require("react-native-svg"));
276
+ var LinkIcon = ({
277
+ size = 20,
278
+ color = "#00C693"
279
+ }) => {
280
+ return /* @__PURE__ */ import_react4.default.createElement(import_react_native_svg2.default, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ import_react4.default.createElement(
281
+ import_react_native_svg2.Path,
282
+ {
283
+ d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",
284
+ stroke: color,
285
+ strokeWidth: 2,
286
+ strokeLinecap: "round",
287
+ strokeLinejoin: "round"
288
+ }
289
+ ), /* @__PURE__ */ import_react4.default.createElement(
290
+ import_react_native_svg2.Path,
291
+ {
292
+ d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",
293
+ stroke: color,
294
+ strokeWidth: 2,
295
+ strokeLinecap: "round",
296
+ strokeLinejoin: "round"
297
+ }
298
+ ));
299
+ };
300
+
301
+ // src/assets/ShieldIcon.tsx
302
+ var import_react5 = __toESM(require("react"));
303
+ var import_react_native_svg3 = __toESM(require("react-native-svg"));
304
+ var ShieldIcon = ({
305
+ size = 20,
306
+ color = "#00C693"
307
+ }) => {
308
+ return /* @__PURE__ */ import_react5.default.createElement(import_react_native_svg3.default, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ import_react5.default.createElement(
309
+ import_react_native_svg3.Path,
310
+ {
311
+ d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",
312
+ stroke: color,
313
+ strokeWidth: 2,
314
+ strokeLinecap: "round",
315
+ strokeLinejoin: "round"
316
+ }
317
+ ), /* @__PURE__ */ import_react5.default.createElement(
318
+ import_react_native_svg3.Path,
319
+ {
320
+ d: "m9 12 2 2 4-4",
321
+ stroke: color,
322
+ strokeWidth: 2,
323
+ strokeLinecap: "round",
324
+ strokeLinejoin: "round"
325
+ }
326
+ ));
327
+ };
328
+
329
+ // src/components/Alert.tsx
330
+ var import_react6 = __toESM(require("react"));
331
+ var import_react_native = require("react-native");
332
+ var Alert = ({
333
+ variant = "default",
334
+ children,
335
+ style
336
+ }) => {
337
+ const theme = useTheme();
338
+ const getVariantStyles = () => {
339
+ switch (variant) {
340
+ case "destructive":
341
+ return {
342
+ backgroundColor: theme.colors.errorLight,
343
+ borderColor: theme.colors.error
344
+ };
345
+ default:
346
+ return {
347
+ backgroundColor: theme.colors.surface,
348
+ borderColor: theme.colors.border
349
+ };
350
+ }
351
+ };
352
+ return /* @__PURE__ */ import_react6.default.createElement(
353
+ import_react_native.View,
354
+ {
355
+ style: [
356
+ styles.alert,
357
+ {
358
+ borderRadius: theme.borderRadius.md,
359
+ padding: theme.spacing.md
360
+ },
361
+ getVariantStyles(),
362
+ style
363
+ ]
364
+ },
365
+ children
366
+ );
367
+ };
368
+ var AlertDescription = ({
369
+ children,
370
+ style
371
+ }) => {
372
+ const theme = useTheme();
373
+ return /* @__PURE__ */ import_react6.default.createElement(
374
+ import_react_native.Text,
375
+ {
376
+ style: [
377
+ styles.description,
378
+ {
379
+ color: theme.colors.text,
380
+ fontSize: theme.fontSize.md
381
+ },
382
+ style
383
+ ]
384
+ },
385
+ children
386
+ );
387
+ };
388
+ var styles = import_react_native.StyleSheet.create({
389
+ alert: {
390
+ borderWidth: 1,
391
+ marginVertical: 12
392
+ // theme.spacing.md - consistent alert spacing
393
+ },
394
+ title: {
395
+ fontWeight: "600",
396
+ marginBottom: 4
397
+ // theme.spacing.xs
398
+ },
399
+ description: {
400
+ lineHeight: 18,
401
+ textAlign: "center"
402
+ }
403
+ });
404
+
405
+ // src/components/Button.tsx
406
+ var import_react7 = __toESM(require("react"));
407
+ var import_react_native2 = require("react-native");
408
+ var Button = ({
409
+ variant = "primary",
410
+ size = "md",
411
+ children,
412
+ onPress,
413
+ disabled = false,
414
+ loading = false,
415
+ style,
416
+ textStyle
417
+ }) => {
418
+ const theme = useTheme();
419
+ const getVariantStyles = () => {
420
+ switch (variant) {
421
+ case "primary":
422
+ return {
423
+ backgroundColor: disabled ? theme.colors.textTertiary : theme.colors.primary,
424
+ borderWidth: 0
425
+ };
426
+ case "secondary":
427
+ return {
428
+ backgroundColor: disabled ? theme.colors.surfaceSecondary : theme.colors.surface,
429
+ borderWidth: 1,
430
+ borderColor: theme.colors.border
431
+ };
432
+ case "outline":
433
+ return {
434
+ backgroundColor: "transparent",
435
+ borderWidth: 1,
436
+ borderColor: disabled ? theme.colors.textTertiary : theme.colors.primary
437
+ };
438
+ case "ghost":
439
+ return {
440
+ backgroundColor: "transparent",
441
+ borderWidth: 0
442
+ };
443
+ case "success":
444
+ return {
445
+ backgroundColor: disabled ? theme.colors.textTertiary : theme.colors.success,
446
+ borderWidth: 0
447
+ };
448
+ case "error":
449
+ return {
450
+ backgroundColor: disabled ? theme.colors.textTertiary : theme.colors.error,
451
+ borderWidth: 0
452
+ };
453
+ default:
454
+ return {};
455
+ }
456
+ };
457
+ const getTextColor = () => {
458
+ if (disabled) {
459
+ return variant === "outline" || variant === "ghost" ? theme.colors.textTertiary : theme.colors.white;
460
+ }
461
+ switch (variant) {
462
+ case "primary":
463
+ case "success":
464
+ case "error":
465
+ return theme.colors.white;
466
+ case "secondary":
467
+ return theme.colors.text;
468
+ case "outline":
469
+ return theme.colors.primary;
470
+ case "ghost":
471
+ return theme.colors.text;
472
+ default:
473
+ return theme.colors.white;
474
+ }
475
+ };
476
+ const getSizeStyles = () => {
477
+ switch (size) {
478
+ case "sm":
479
+ return {
480
+ button: {
481
+ paddingVertical: theme.spacing.sm,
482
+ paddingHorizontal: theme.spacing.md,
483
+ borderRadius: theme.borderRadius.sm
484
+ },
485
+ text: {
486
+ fontSize: theme.fontSize.sm
487
+ }
488
+ };
489
+ case "md":
490
+ return {
491
+ button: {
492
+ paddingVertical: theme.spacing.md,
493
+ paddingHorizontal: theme.spacing.lg,
494
+ borderRadius: theme.borderRadius.md
495
+ },
496
+ text: {
497
+ fontSize: theme.fontSize.md
498
+ }
499
+ };
500
+ case "lg":
501
+ return {
502
+ button: {
503
+ paddingVertical: theme.spacing.lg,
504
+ paddingHorizontal: theme.spacing.xl,
505
+ borderRadius: theme.borderRadius.md
506
+ },
507
+ text: {
508
+ fontSize: theme.fontSize.lg
509
+ }
510
+ };
511
+ default:
512
+ return {
513
+ button: {},
514
+ text: {}
515
+ };
516
+ }
517
+ };
518
+ const sizeStyles = getSizeStyles();
519
+ return /* @__PURE__ */ import_react7.default.createElement(
520
+ import_react_native2.TouchableOpacity,
521
+ {
522
+ onPress,
523
+ disabled: disabled || loading,
524
+ activeOpacity: 0.7,
525
+ style: [
526
+ styles2.button,
527
+ getVariantStyles(),
528
+ sizeStyles.button,
529
+ disabled && styles2.disabled,
530
+ style
531
+ ]
532
+ },
533
+ loading ? /* @__PURE__ */ import_react7.default.createElement(import_react_native2.ActivityIndicator, { size: "small", color: getTextColor() }) : typeof children === "string" ? /* @__PURE__ */ import_react7.default.createElement(
534
+ import_react_native2.Text,
535
+ {
536
+ style: [
537
+ styles2.text,
538
+ { color: getTextColor() },
539
+ sizeStyles.text,
540
+ textStyle
541
+ ]
542
+ },
543
+ children
544
+ ) : /* @__PURE__ */ import_react7.default.createElement(import_react_native2.View, { style: styles2.contentContainer }, children)
545
+ );
546
+ };
547
+ var styles2 = import_react_native2.StyleSheet.create({
548
+ button: {
549
+ flexDirection: "row",
550
+ alignItems: "center",
551
+ justifyContent: "center",
552
+ minHeight: 44
553
+ },
554
+ text: {
555
+ fontWeight: "600",
556
+ textAlign: "center"
557
+ },
558
+ disabled: {
559
+ opacity: 0.6
560
+ },
561
+ contentContainer: {
562
+ flexDirection: "row",
563
+ alignItems: "center",
564
+ justifyContent: "center"
565
+ }
566
+ });
567
+
568
+ // src/components/Input.tsx
569
+ var import_react8 = __toESM(require("react"));
570
+ var import_react_native3 = require("react-native");
571
+ var Input = ({
572
+ label,
573
+ error,
574
+ helperText,
575
+ status = "default",
576
+ containerStyle,
577
+ inputStyle,
578
+ labelStyle,
579
+ ...props
580
+ }) => {
581
+ const theme = useTheme();
582
+ const inputStatus = error ? "error" : status;
583
+ const getBorderColor = () => {
584
+ switch (inputStatus) {
585
+ case "error":
586
+ return theme.colors.error;
587
+ case "success":
588
+ return theme.colors.success;
589
+ default:
590
+ return theme.colors.border;
591
+ }
592
+ };
593
+ return /* @__PURE__ */ import_react8.default.createElement(import_react_native3.View, { style: [styles3.wrapper, containerStyle] }, label && /* @__PURE__ */ import_react8.default.createElement(
594
+ import_react_native3.Text,
595
+ {
596
+ style: [
597
+ styles3.label,
598
+ { color: theme.colors.text, fontSize: theme.fontSize.sm },
599
+ labelStyle
600
+ ]
601
+ },
602
+ label
603
+ ), /* @__PURE__ */ import_react8.default.createElement(
604
+ import_react_native3.TextInput,
605
+ {
606
+ placeholderTextColor: theme.colors.textTertiary,
607
+ style: [
608
+ styles3.input,
609
+ {
610
+ backgroundColor: theme.colors.surface,
611
+ borderColor: getBorderColor(),
612
+ color: theme.colors.text,
613
+ fontSize: theme.fontSize.md,
614
+ borderRadius: theme.borderRadius.md,
615
+ paddingHorizontal: theme.spacing.lg,
616
+ paddingVertical: theme.spacing.md
617
+ },
618
+ inputStyle
619
+ ],
620
+ ...props
621
+ }
622
+ ), error && /* @__PURE__ */ import_react8.default.createElement(
623
+ import_react_native3.Text,
624
+ {
625
+ style: [
626
+ styles3.error,
627
+ { color: theme.colors.error, fontSize: theme.fontSize.sm }
628
+ ]
629
+ },
630
+ error
631
+ ), helperText && !error && /* @__PURE__ */ import_react8.default.createElement(
632
+ import_react_native3.Text,
633
+ {
634
+ style: [
635
+ styles3.helper,
636
+ {
637
+ color: theme.colors.textSecondary,
638
+ fontSize: theme.fontSize.sm
639
+ }
640
+ ]
641
+ },
642
+ helperText
643
+ ));
644
+ };
645
+ var styles3 = import_react_native3.StyleSheet.create({
646
+ wrapper: {
647
+ marginBottom: 16
648
+ // theme.spacing.lg - consistent form spacing
649
+ },
650
+ label: {
651
+ fontWeight: "500",
652
+ marginBottom: 8
653
+ // theme.spacing.sm
654
+ },
655
+ input: {
656
+ borderWidth: 1,
657
+ minHeight: 48
658
+ },
659
+ error: {
660
+ marginTop: 4
661
+ // theme.spacing.xs
662
+ },
663
+ helper: {
664
+ marginTop: 4
665
+ // theme.spacing.xs
666
+ }
667
+ });
668
+
669
+ // src/components/Modal.tsx
670
+ var import_react10 = __toESM(require("react"));
671
+ var import_react_native4 = require("react-native");
672
+
673
+ // src/assets/CloseIcon.tsx
674
+ var import_react9 = __toESM(require("react"));
675
+ var import_react_native_svg4 = __toESM(require("react-native-svg"));
676
+ var CloseIcon = ({
677
+ size = 20,
678
+ color = "#000"
679
+ }) => {
680
+ return /* @__PURE__ */ import_react9.default.createElement(import_react_native_svg4.default, { width: size, height: size, viewBox: "0 0 20 20", fill: "none" }, /* @__PURE__ */ import_react9.default.createElement(
681
+ import_react_native_svg4.Path,
682
+ {
683
+ d: "M15 5L5 15M5 5L15 15",
684
+ stroke: color,
685
+ strokeWidth: 2,
686
+ strokeLinecap: "round",
687
+ strokeLinejoin: "round"
688
+ }
689
+ ));
690
+ };
691
+
692
+ // src/components/Modal.tsx
693
+ var { height: SCREEN_HEIGHT } = import_react_native4.Dimensions.get("window");
694
+ var Modal = ({
695
+ isOpen,
696
+ onClose,
697
+ children,
698
+ size = "md",
699
+ closeOnOverlayClick = true,
700
+ disableClose = true,
701
+ style
702
+ }) => {
703
+ const theme = useTheme();
704
+ const getSizeStyles = () => {
705
+ switch (size) {
706
+ case "xs":
707
+ return { maxHeight: SCREEN_HEIGHT * 0.35 };
708
+ case "sm":
709
+ return { maxHeight: SCREEN_HEIGHT * 0.45 };
710
+ case "md":
711
+ return { maxHeight: SCREEN_HEIGHT * 0.55 };
712
+ case "lg":
713
+ return { maxHeight: SCREEN_HEIGHT * 0.65 };
714
+ case "xl":
715
+ return { maxHeight: SCREEN_HEIGHT * 0.75 };
716
+ case "full":
717
+ return { maxHeight: SCREEN_HEIGHT * 0.85 };
718
+ default:
719
+ return { maxHeight: SCREEN_HEIGHT * 0.6 };
720
+ }
721
+ };
722
+ const handleOverlayPress = () => {
723
+ if (!disableClose && closeOnOverlayClick) onClose();
724
+ };
725
+ return /* @__PURE__ */ import_react10.default.createElement(
726
+ import_react_native4.KeyboardAvoidingView,
727
+ {
728
+ behavior: import_react_native4.Platform.OS === "ios" ? "padding" : "height",
729
+ style: styles4.keyboardView
730
+ },
731
+ /* @__PURE__ */ import_react10.default.createElement(
732
+ import_react_native4.Modal,
733
+ {
734
+ visible: isOpen,
735
+ transparent: true,
736
+ animationType: "none",
737
+ statusBarTranslucent: true,
738
+ onRequestClose: disableClose ? void 0 : onClose
739
+ },
740
+ /* @__PURE__ */ import_react10.default.createElement(
741
+ import_react_native4.TouchableOpacity,
742
+ {
743
+ activeOpacity: 1,
744
+ style: [styles4.overlay, { backgroundColor: theme.colors.overlay }],
745
+ onPress: handleOverlayPress
746
+ },
747
+ /* @__PURE__ */ import_react10.default.createElement(
748
+ import_react_native4.View,
749
+ {
750
+ style: [
751
+ styles4.container,
752
+ {
753
+ backgroundColor: theme.colors.background,
754
+ borderTopLeftRadius: theme.borderRadius.xl,
755
+ borderTopRightRadius: theme.borderRadius.xl,
756
+ ...theme.shadow.lg,
757
+ paddingBottom: theme.spacing.xl
758
+ // 20
759
+ },
760
+ getSizeStyles(),
761
+ style
762
+ ],
763
+ onStartShouldSetResponder: () => true
764
+ },
765
+ children
766
+ )
767
+ )
768
+ )
769
+ );
770
+ };
771
+ var ModalHeader = ({
772
+ children,
773
+ onClose,
774
+ showCloseButton = true,
775
+ style
776
+ }) => {
777
+ const theme = useTheme();
778
+ return /* @__PURE__ */ import_react10.default.createElement(
779
+ import_react_native4.View,
780
+ {
781
+ style: [
782
+ styles4.header,
783
+ {
784
+ borderBottomColor: theme.colors.border,
785
+ paddingHorizontal: theme.spacing.lg,
786
+ paddingVertical: theme.spacing.md
787
+ },
788
+ style
789
+ ]
790
+ },
791
+ /* @__PURE__ */ import_react10.default.createElement(import_react_native4.View, { style: styles4.headerContent }, typeof children === "string" ? /* @__PURE__ */ import_react10.default.createElement(
792
+ import_react_native4.Text,
793
+ {
794
+ style: [
795
+ styles4.title,
796
+ { color: theme.colors.text, fontSize: theme.fontSize.lg }
797
+ ]
798
+ },
799
+ children
800
+ ) : children),
801
+ showCloseButton && onClose && /* @__PURE__ */ import_react10.default.createElement(
802
+ import_react_native4.TouchableOpacity,
803
+ {
804
+ onPress: onClose,
805
+ hitSlop: { top: 10, bottom: 10, left: 10, right: 10 },
806
+ style: [
807
+ styles4.closeButton,
808
+ { backgroundColor: theme.colors.surface }
809
+ ]
810
+ },
811
+ /* @__PURE__ */ import_react10.default.createElement(CloseIcon, { color: theme.colors.text, size: 20 })
812
+ )
813
+ );
814
+ };
815
+ var ModalBody = ({
816
+ children,
817
+ style,
818
+ scrollable = true
819
+ }) => {
820
+ const theme = useTheme();
821
+ if (scrollable) {
822
+ return /* @__PURE__ */ import_react10.default.createElement(
823
+ import_react_native4.ScrollView,
824
+ {
825
+ style: styles4.bodyScroll,
826
+ contentContainerStyle: [
827
+ styles4.bodyContent,
828
+ { padding: theme.spacing.lg },
829
+ style
830
+ ],
831
+ showsVerticalScrollIndicator: false,
832
+ keyboardShouldPersistTaps: "handled"
833
+ },
834
+ children
835
+ );
836
+ }
837
+ return /* @__PURE__ */ import_react10.default.createElement(import_react_native4.View, { style: [styles4.body, { padding: theme.spacing.lg }, style] }, children);
838
+ };
839
+ var ModalFooter = ({
840
+ children,
841
+ style
842
+ }) => {
843
+ const theme = useTheme();
844
+ return /* @__PURE__ */ import_react10.default.createElement(
845
+ import_react_native4.View,
846
+ {
847
+ style: [
848
+ styles4.footer,
849
+ {
850
+ borderTopColor: theme.colors.border,
851
+ paddingHorizontal: theme.spacing.lg,
852
+ paddingVertical: theme.spacing.md
853
+ },
854
+ style
855
+ ]
856
+ },
857
+ children
858
+ );
859
+ };
860
+ var styles4 = import_react_native4.StyleSheet.create({
861
+ keyboardView: {
862
+ flex: 1
863
+ },
864
+ overlay: {
865
+ flex: 1,
866
+ justifyContent: "flex-end",
867
+ alignItems: "center"
868
+ // 🔥 ensures modal respects height
869
+ },
870
+ container: {
871
+ width: "100%",
872
+ overflow: "hidden",
873
+ alignSelf: "center",
874
+ flexDirection: "column",
875
+ maxWidth: "100%",
876
+ minHeight: 0,
877
+ flexShrink: 1,
878
+ flexGrow: 1
879
+ },
880
+ header: {
881
+ flexDirection: "row",
882
+ alignItems: "center",
883
+ justifyContent: "space-between",
884
+ borderBottomWidth: 1
885
+ },
886
+ headerContent: {
887
+ flex: 1,
888
+ flexDirection: "row",
889
+ alignItems: "center"
890
+ },
891
+ title: {
892
+ fontWeight: "600"
893
+ },
894
+ closeButton: {
895
+ width: 32,
896
+ height: 32,
897
+ borderRadius: 16,
898
+ alignItems: "center",
899
+ justifyContent: "center",
900
+ marginLeft: 12
901
+ },
902
+ bodyScroll: {
903
+ flexShrink: 1
904
+ },
905
+ bodyContent: {
906
+ flexGrow: 1,
907
+ paddingBottom: 20
908
+ },
909
+ body: {
910
+ flex: 1
911
+ },
912
+ footer: {
913
+ borderTopWidth: 1
914
+ }
915
+ });
916
+
917
+ // src/services/api.ts
918
+ var import_axios = __toESM(require("axios"));
919
+
920
+ // src/config/index.ts
921
+ var BASE_URL = "https://connect-dev.kryptos.io/connect/";
922
+
923
+ // src/services/api.ts
924
+ var api = import_axios.default.create({
925
+ baseURL: BASE_URL,
926
+ headers: {
927
+ "Content-Type": "application/json"
928
+ }
929
+ });
930
+ var SCOPES = "openid offline_access profile email holdings:read transactions:read defi-portfolio:read nft-portfolio:read ledger:read tax:read integrations:read holdings:write transactions:write defi-portfolio:write nft-portfolio:write ledger:write tax:write integrations:write";
931
+ async function sendEmailOtp(linkToken, email, clientId) {
932
+ const res = await api.post(
933
+ "/v1/sendEmailOTP",
934
+ {
935
+ email,
936
+ purpose: "login",
937
+ clientId
938
+ },
939
+ {
940
+ headers: {
941
+ "X-Link-Token": linkToken
942
+ }
943
+ }
944
+ );
945
+ return res.data;
946
+ }
947
+ async function loginWithOtp(linkToken, email, code, clientId) {
948
+ const res = await api.post(
949
+ "/v1/loginUserUsingOTP",
950
+ {
951
+ email,
952
+ code,
953
+ clientId,
954
+ purpose: "login"
955
+ },
956
+ {
957
+ headers: {
958
+ "X-Link-Token": linkToken
959
+ }
960
+ }
961
+ );
962
+ return res.data;
963
+ }
964
+ async function createAnonymousUser(linkToken, clientId) {
965
+ const res = await api.post(
966
+ "/v1/anonymousAccountCreation",
967
+ { clientId },
968
+ {
969
+ headers: {
970
+ "X-Link-Token": linkToken
971
+ }
972
+ }
973
+ );
974
+ return res.data;
975
+ }
976
+ async function addUserIntegration(linkToken, integration) {
977
+ const res = await api.post(
978
+ "/v1/addUserIntegration",
979
+ { providers: [...integration] },
980
+ {
981
+ headers: {
982
+ "X-Link-Token": linkToken
983
+ }
984
+ }
985
+ );
986
+ return res.data;
987
+ }
988
+ async function giveUserConsent(linkToken) {
989
+ const res = await api.post(
990
+ "/v1/consent",
991
+ {
992
+ granted_scopes: SCOPES,
993
+ user_consent: true
994
+ },
995
+ {
996
+ headers: {
997
+ "X-Link-Token": linkToken
998
+ }
999
+ }
1000
+ );
1001
+ return res.data;
1002
+ }
1003
+ async function testCredentials(linkToken, data) {
1004
+ const res = await api.post("/v1/api/testCredentials", data, {
1005
+ headers: {
1006
+ "X-Link-Token": linkToken
1007
+ }
1008
+ });
1009
+ return res.data;
1010
+ }
1011
+ async function getSupportedProviders(linkToken) {
1012
+ const res = await api.get("/v1/integrations", {
1013
+ headers: {
1014
+ "X-Link-Token": linkToken
1015
+ }
1016
+ });
1017
+ return res.data;
1018
+ }
1019
+ async function getUserIntegrations(linkToken) {
1020
+ const res = await api.get("/v1/userIntegrations", {
1021
+ headers: {
1022
+ "X-Link-Token": linkToken
1023
+ }
1024
+ });
1025
+ return res.data;
1026
+ }
1027
+ async function getUserUsedChains(linkToken, address) {
1028
+ const res = await api.get("/v1/api/getUserUsedChainV2", {
1029
+ headers: {
1030
+ "X-Link-Token": linkToken
1031
+ },
1032
+ params: {
1033
+ id: address
1034
+ }
1035
+ });
1036
+ return res.data;
1037
+ }
1038
+
1039
+ // src/molecules/ConnectLogo.tsx
1040
+ var import_react12 = __toESM(require("react"));
1041
+ var import_react_native5 = require("react-native");
1042
+
1043
+ // src/assets/UnplugIcon.tsx
1044
+ var import_react11 = __toESM(require("react"));
1045
+ var import_react_native_svg5 = __toESM(require("react-native-svg"));
1046
+ var UnplugIcon = ({
1047
+ size = 24,
1048
+ color = "#6B7280"
1049
+ }) => {
1050
+ return /* @__PURE__ */ import_react11.default.createElement(import_react_native_svg5.default, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ import_react11.default.createElement(
1051
+ import_react_native_svg5.Path,
1052
+ {
1053
+ d: "m19 5 3-3",
1054
+ stroke: color,
1055
+ strokeWidth: 2,
1056
+ strokeLinecap: "round",
1057
+ strokeLinejoin: "round"
1058
+ }
1059
+ ), /* @__PURE__ */ import_react11.default.createElement(
1060
+ import_react_native_svg5.Path,
1061
+ {
1062
+ d: "m2 22 3-3",
1063
+ stroke: color,
1064
+ strokeWidth: 2,
1065
+ strokeLinecap: "round",
1066
+ strokeLinejoin: "round"
1067
+ }
1068
+ ), /* @__PURE__ */ import_react11.default.createElement(
1069
+ import_react_native_svg5.Path,
1070
+ {
1071
+ d: "M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z",
1072
+ stroke: color,
1073
+ strokeWidth: 2,
1074
+ strokeLinecap: "round",
1075
+ strokeLinejoin: "round"
1076
+ }
1077
+ ), /* @__PURE__ */ import_react11.default.createElement(
1078
+ import_react_native_svg5.Path,
1079
+ {
1080
+ d: "m18 12-6-6 2.3-2.3a2.4 2.4 0 0 1 3.4 0l2.6 2.6a2.4 2.4 0 0 1 0 3.4Z",
1081
+ stroke: color,
1082
+ strokeWidth: 2,
1083
+ strokeLinecap: "round",
1084
+ strokeLinejoin: "round"
1085
+ }
1086
+ ), /* @__PURE__ */ import_react11.default.createElement(
1087
+ import_react_native_svg5.Line,
1088
+ {
1089
+ x1: 7.5,
1090
+ y1: 13.5,
1091
+ x2: 10.5,
1092
+ y2: 10.5,
1093
+ stroke: color,
1094
+ strokeWidth: 2,
1095
+ strokeLinecap: "round"
1096
+ }
1097
+ ));
1098
+ };
1099
+
1100
+ // src/molecules/ConnectLogo.tsx
1101
+ var KryptosLogo = () => {
1102
+ const theme = useTheme();
1103
+ return /* @__PURE__ */ import_react12.default.createElement(
1104
+ import_react_native5.View,
1105
+ {
1106
+ style: [styles5.logoContainer, { backgroundColor: theme.colors.surface }]
1107
+ },
1108
+ /* @__PURE__ */ import_react12.default.createElement(LogoIcon, { size: 36 })
1109
+ );
1110
+ };
1111
+ var ConnectLogo = () => {
1112
+ const { appName, appLogo } = useKryptosConnect();
1113
+ const theme = useTheme();
1114
+ const isValidUrl = (str) => {
1115
+ try {
1116
+ new URL(str);
1117
+ return true;
1118
+ } catch {
1119
+ return false;
1120
+ }
1121
+ };
1122
+ const renderLogo = () => {
1123
+ if ((0, import_react12.isValidElement)(appLogo)) {
1124
+ return appLogo;
1125
+ } else if (typeof appLogo === "string" && isValidUrl(appLogo)) {
1126
+ return /* @__PURE__ */ import_react12.default.createElement(
1127
+ import_react_native5.Image,
1128
+ {
1129
+ source: { uri: appLogo },
1130
+ style: styles5.appLogoImage,
1131
+ resizeMode: "contain"
1132
+ }
1133
+ );
1134
+ } else if (typeof appLogo === "number" || typeof appLogo === "object" && appLogo !== null) {
1135
+ return /* @__PURE__ */ import_react12.default.createElement(
1136
+ import_react_native5.Image,
1137
+ {
1138
+ source: appLogo,
1139
+ style: styles5.appLogoImage,
1140
+ resizeMode: "contain"
1141
+ }
1142
+ );
1143
+ } else if (appName) {
1144
+ return /* @__PURE__ */ import_react12.default.createElement(import_react_native5.Text, { style: [styles5.appLogoText, { color: theme.colors.text }] }, appName.charAt(0).toUpperCase());
1145
+ }
1146
+ return /* @__PURE__ */ import_react12.default.createElement(import_react_native5.Text, { style: [styles5.appLogoText, { color: theme.colors.text }] }, "?");
1147
+ };
1148
+ return /* @__PURE__ */ import_react12.default.createElement(import_react_native5.View, { style: styles5.container }, /* @__PURE__ */ import_react12.default.createElement(KryptosLogo, null), /* @__PURE__ */ import_react12.default.createElement(import_react_native5.View, { style: styles5.iconContainer }, /* @__PURE__ */ import_react12.default.createElement(UnplugIcon, { size: 24, color: theme.colors.textSecondary })), /* @__PURE__ */ import_react12.default.createElement(
1149
+ import_react_native5.View,
1150
+ {
1151
+ style: [
1152
+ styles5.logoContainer,
1153
+ { backgroundColor: theme.colors.surface }
1154
+ ]
1155
+ },
1156
+ renderLogo()
1157
+ ));
1158
+ };
1159
+ var styles5 = import_react_native5.StyleSheet.create({
1160
+ container: {
1161
+ flexDirection: "row",
1162
+ alignItems: "center",
1163
+ justifyContent: "center",
1164
+ marginVertical: 24,
1165
+ // theme.spacing.xxl
1166
+ gap: 12
1167
+ // theme.spacing.md
1168
+ },
1169
+ logoContainer: {
1170
+ width: 56,
1171
+ height: 56,
1172
+ borderRadius: 12,
1173
+ // theme.borderRadius.md
1174
+ alignItems: "center",
1175
+ justifyContent: "center",
1176
+ overflow: "hidden"
1177
+ },
1178
+ iconContainer: {
1179
+ paddingHorizontal: 8
1180
+ // theme.spacing.sm
1181
+ },
1182
+ appLogoImage: {
1183
+ width: 32,
1184
+ height: 32
1185
+ },
1186
+ appLogoText: {
1187
+ fontSize: 24,
1188
+ // theme.fontSize.xxxl
1189
+ fontWeight: "700"
1190
+ }
1191
+ });
1192
+
1193
+ // src/molecules/Auth.tsx
1194
+ var Auth = ({
1195
+ open,
1196
+ onEmailSuccess,
1197
+ onGuestSuccess,
1198
+ onClose
1199
+ }) => {
1200
+ const { appName, linkToken, clientId, setUser, setEmail } = useKryptosConnect();
1201
+ const theme = useTheme();
1202
+ const [isLoading, setIsLoading] = import_react13.default.useState(false);
1203
+ const [errorMessage, setErrorMessage] = import_react13.default.useState("");
1204
+ const [emailValue, setEmailValue] = import_react13.default.useState("");
1205
+ const [emailError, setEmailError] = import_react13.default.useState("");
1206
+ const [loadingType, setLoadingType] = import_react13.default.useState(null);
1207
+ const validateEmail = (email) => {
1208
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1209
+ if (!email) {
1210
+ setEmailError("Email is required");
1211
+ return false;
1212
+ }
1213
+ if (!emailRegex.test(email)) {
1214
+ setEmailError("Invalid email address");
1215
+ return false;
1216
+ }
1217
+ setEmailError("");
1218
+ return true;
1219
+ };
1220
+ const handleClose = () => {
1221
+ onClose();
1222
+ setEmailValue("");
1223
+ setEmailError("");
1224
+ setErrorMessage("");
1225
+ };
1226
+ const handleEmailSubmit = async () => {
1227
+ if (!validateEmail(emailValue)) return;
1228
+ try {
1229
+ setIsLoading(true);
1230
+ setLoadingType("email");
1231
+ setErrorMessage("");
1232
+ await sendEmailOtp(linkToken, emailValue, clientId);
1233
+ setEmail(emailValue);
1234
+ setEmailError("");
1235
+ onEmailSuccess();
1236
+ } catch (error) {
1237
+ const err = error;
1238
+ setErrorMessage(
1239
+ err?.response?.data?.message || "Failed to send email OTP"
1240
+ );
1241
+ } finally {
1242
+ setIsLoading(false);
1243
+ setLoadingType(null);
1244
+ }
1245
+ };
1246
+ const handleContinueAsGuest = async () => {
1247
+ try {
1248
+ setIsLoading(true);
1249
+ setLoadingType("guest");
1250
+ setErrorMessage("");
1251
+ const res = await createAnonymousUser(linkToken, clientId);
1252
+ setUser(res);
1253
+ setEmailError("");
1254
+ onGuestSuccess();
1255
+ } catch (error) {
1256
+ const err = error;
1257
+ console.error(error);
1258
+ setErrorMessage(
1259
+ err?.response?.data?.message || "Failed to continue as guest"
1260
+ );
1261
+ } finally {
1262
+ setIsLoading(false);
1263
+ setLoadingType(null);
1264
+ }
1265
+ };
1266
+ const infoSections = [
1267
+ {
1268
+ icon: /* @__PURE__ */ import_react13.default.createElement(LinkIcon, { size: 20, color: theme.colors.primary }),
1269
+ title: "Simple and secure",
1270
+ text: "Connect your Web3 accounts with Kryptos in just a few clicks"
1271
+ },
1272
+ {
1273
+ icon: /* @__PURE__ */ import_react13.default.createElement(ShieldIcon, { size: 20, color: theme.colors.primary }),
1274
+ title: "Control what you share",
1275
+ text: "We never share your data without your permission"
1276
+ }
1277
+ ];
1278
+ return /* @__PURE__ */ import_react13.default.createElement(Modal, { isOpen: open, onClose: handleClose, size: "full" }, /* @__PURE__ */ import_react13.default.createElement(ModalHeader, { onClose: handleClose }, ""), /* @__PURE__ */ import_react13.default.createElement(ModalBody, null, /* @__PURE__ */ import_react13.default.createElement(import_react_native6.View, { style: styles6.container }, /* @__PURE__ */ import_react13.default.createElement(import_react_native6.Text, { style: [styles6.title, { color: theme.colors.text }] }, "Connect ", appName, " to your Kryptos account"), /* @__PURE__ */ import_react13.default.createElement(ConnectLogo, null), infoSections.map((section, index) => /* @__PURE__ */ import_react13.default.createElement(import_react_native6.View, { key: `info-${index}`, style: styles6.infoSection }, /* @__PURE__ */ import_react13.default.createElement(import_react_native6.View, { style: styles6.infoIcon }, section.icon), /* @__PURE__ */ import_react13.default.createElement(import_react_native6.View, { style: styles6.infoContent }, /* @__PURE__ */ import_react13.default.createElement(import_react_native6.Text, { style: [styles6.infoTitle, { color: theme.colors.text }] }, section.title), /* @__PURE__ */ import_react13.default.createElement(
1279
+ import_react_native6.Text,
1280
+ {
1281
+ style: [
1282
+ styles6.infoDescription,
1283
+ { color: theme.colors.textSecondary }
1284
+ ]
1285
+ },
1286
+ section.text
1287
+ )))), errorMessage ? /* @__PURE__ */ import_react13.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ import_react13.default.createElement(AlertDescription, null, errorMessage)) : null, /* @__PURE__ */ import_react13.default.createElement(
1288
+ Input,
1289
+ {
1290
+ placeholder: "Enter email address",
1291
+ value: emailValue,
1292
+ onChangeText: (text) => {
1293
+ setEmailValue(text);
1294
+ if (emailError) validateEmail(text);
1295
+ },
1296
+ error: emailError,
1297
+ keyboardType: "email-address",
1298
+ autoCapitalize: "none",
1299
+ autoCorrect: false
1300
+ }
1301
+ ), /* @__PURE__ */ import_react13.default.createElement(Alert, { variant: "default" }, /* @__PURE__ */ import_react13.default.createElement(AlertDescription, null, "Sign in or create your Kryptos account with your email for quicker access next time.")), /* @__PURE__ */ import_react13.default.createElement(
1302
+ Button,
1303
+ {
1304
+ variant: "outline",
1305
+ size: "lg",
1306
+ onPress: handleEmailSubmit,
1307
+ loading: loadingType === "email",
1308
+ disabled: isLoading,
1309
+ style: styles6.button
1310
+ },
1311
+ "Continue"
1312
+ ), /* @__PURE__ */ import_react13.default.createElement(import_react_native6.Text, { style: [styles6.footer, { color: theme.colors.textSecondary }] }, "By continuing, you agree to Kryptos", " ", /* @__PURE__ */ import_react13.default.createElement(
1313
+ import_react_native6.Text,
1314
+ {
1315
+ style: {
1316
+ color: theme.colors.primary,
1317
+ textDecorationLine: "underline"
1318
+ }
1319
+ },
1320
+ "Privacy Policy"
1321
+ )), /* @__PURE__ */ import_react13.default.createElement(
1322
+ Button,
1323
+ {
1324
+ variant: "ghost",
1325
+ size: "lg",
1326
+ onPress: handleContinueAsGuest,
1327
+ loading: loadingType === "guest",
1328
+ disabled: isLoading,
1329
+ style: styles6.button
1330
+ },
1331
+ "Continue as guest"
1332
+ ))));
1333
+ };
1334
+ var styles6 = import_react_native6.StyleSheet.create({
1335
+ container: {
1336
+ flex: 1
1337
+ },
1338
+ title: {
1339
+ fontSize: 18,
1340
+ // theme.fontSize.xl
1341
+ fontWeight: "600",
1342
+ textAlign: "center",
1343
+ marginBottom: 16
1344
+ // theme.spacing.lg - consistent section spacing
1345
+ },
1346
+ infoSection: {
1347
+ flexDirection: "row",
1348
+ marginBottom: 16,
1349
+ // theme.spacing.lg
1350
+ alignItems: "flex-start"
1351
+ },
1352
+ infoIcon: {
1353
+ width: 32,
1354
+ height: 32,
1355
+ borderRadius: 16,
1356
+ // theme.borderRadius.lg
1357
+ alignItems: "center",
1358
+ justifyContent: "center",
1359
+ marginRight: 12
1360
+ // theme.spacing.md
1361
+ },
1362
+ infoContent: {
1363
+ flex: 1
1364
+ },
1365
+ infoTitle: {
1366
+ fontSize: 14,
1367
+ // theme.fontSize.md
1368
+ fontWeight: "600",
1369
+ marginBottom: 4
1370
+ // theme.spacing.xs
1371
+ },
1372
+ infoDescription: {
1373
+ fontSize: 13,
1374
+ lineHeight: 18
1375
+ },
1376
+ button: {
1377
+ width: "100%",
1378
+ marginTop: 16
1379
+ // theme.spacing.lg - consistent button spacing
1380
+ },
1381
+ footer: {
1382
+ fontSize: 12,
1383
+ // theme.fontSize.sm
1384
+ textAlign: "center",
1385
+ marginTop: 16
1386
+ // theme.spacing.lg
1387
+ }
1388
+ });
1389
+
1390
+ // src/components/OTP.tsx
1391
+ var import_react14 = __toESM(require("react"));
1392
+ var import_react_native7 = require("react-native");
1393
+ var OTP = ({
1394
+ length = 6,
1395
+ value = "",
1396
+ onChange,
1397
+ onComplete,
1398
+ error,
1399
+ label,
1400
+ disabled = false,
1401
+ containerStyle,
1402
+ inputStyle,
1403
+ setErrorMessage
1404
+ }) => {
1405
+ const theme = useTheme();
1406
+ const AUTO_SUBMIT_DELAY = 500;
1407
+ const [otp, setOtp] = import_react14.default.useState(
1408
+ value.split("").concat(Array(length).fill("")).slice(0, length)
1409
+ );
1410
+ const inputRefs = import_react14.default.useRef([]);
1411
+ import_react14.default.useEffect(() => {
1412
+ const isComplete = otp.every((digit) => digit !== "");
1413
+ let timer;
1414
+ if (isComplete && onComplete) {
1415
+ timer = setTimeout(() => {
1416
+ onComplete(otp.join(""));
1417
+ }, AUTO_SUBMIT_DELAY);
1418
+ }
1419
+ return () => {
1420
+ if (timer) clearTimeout(timer);
1421
+ };
1422
+ }, [otp, onComplete]);
1423
+ import_react14.default.useEffect(() => {
1424
+ setTimeout(() => {
1425
+ inputRefs.current[0]?.focus();
1426
+ }, 100);
1427
+ }, []);
1428
+ const handleChange = import_react14.default.useCallback(
1429
+ (index, val) => {
1430
+ if (disabled) return;
1431
+ setErrorMessage("");
1432
+ const numericValue = val.replace(/[^0-9]/g, "");
1433
+ const newValue = numericValue.slice(-1);
1434
+ if (val && !numericValue) {
1435
+ return;
1436
+ }
1437
+ const newOtp = [...otp];
1438
+ newOtp[index] = newValue;
1439
+ setOtp(newOtp);
1440
+ const otpString = newOtp.join("");
1441
+ onChange?.(otpString);
1442
+ if (newValue && index < length - 1) {
1443
+ inputRefs.current[index + 1]?.focus();
1444
+ }
1445
+ if (otpString.length === length && !otpString.includes("")) {
1446
+ onComplete?.(otpString);
1447
+ }
1448
+ },
1449
+ [otp, length, onChange, onComplete, disabled]
1450
+ );
1451
+ const handleKeyPress = import_react14.default.useCallback(
1452
+ (index, e) => {
1453
+ if (disabled) return;
1454
+ if (e.nativeEvent.key === "Backspace") {
1455
+ if (!otp[index] && index > 0) {
1456
+ inputRefs.current[index - 1]?.focus();
1457
+ } else {
1458
+ const newOtp = [...otp];
1459
+ newOtp[index] = "";
1460
+ setOtp(newOtp);
1461
+ onChange?.(newOtp.join(""));
1462
+ }
1463
+ }
1464
+ },
1465
+ [otp, onChange, disabled]
1466
+ );
1467
+ const getBorderColor = (index) => {
1468
+ if (error) return theme.colors.error;
1469
+ if (otp[index]) return theme.colors.success;
1470
+ return theme.colors.border;
1471
+ };
1472
+ return /* @__PURE__ */ import_react14.default.createElement(import_react_native7.View, { style: [styles7.wrapper, containerStyle] }, label && /* @__PURE__ */ import_react14.default.createElement(
1473
+ import_react_native7.Text,
1474
+ {
1475
+ style: [
1476
+ styles7.label,
1477
+ { color: theme.colors.text, fontSize: theme.fontSize.sm }
1478
+ ]
1479
+ },
1480
+ label
1481
+ ), /* @__PURE__ */ import_react14.default.createElement(import_react_native7.View, { style: styles7.container }, Array.from({ length }, (_, index) => /* @__PURE__ */ import_react14.default.createElement(
1482
+ import_react_native7.TextInput,
1483
+ {
1484
+ key: index,
1485
+ ref: (el) => inputRefs.current[index] = el,
1486
+ style: [
1487
+ styles7.input,
1488
+ {
1489
+ backgroundColor: theme.colors.surface,
1490
+ borderColor: getBorderColor(index),
1491
+ color: theme.colors.text,
1492
+ fontSize: theme.fontSize.xxl,
1493
+ borderRadius: theme.borderRadius.md
1494
+ },
1495
+ inputStyle
1496
+ ],
1497
+ keyboardType: "numeric",
1498
+ maxLength: 1,
1499
+ value: otp[index] || "",
1500
+ onChangeText: (val) => handleChange(index, val),
1501
+ onKeyPress: (e) => handleKeyPress(index, e),
1502
+ editable: !disabled,
1503
+ selectTextOnFocus: true,
1504
+ caretHidden: true
1505
+ }
1506
+ ))), error && /* @__PURE__ */ import_react14.default.createElement(
1507
+ import_react_native7.Text,
1508
+ {
1509
+ style: [
1510
+ styles7.error,
1511
+ { color: theme.colors.error, fontSize: theme.fontSize.sm }
1512
+ ]
1513
+ },
1514
+ error
1515
+ ));
1516
+ };
1517
+ var styles7 = import_react_native7.StyleSheet.create({
1518
+ wrapper: {
1519
+ marginBottom: 16
1520
+ // theme.spacing.lg
1521
+ },
1522
+ label: {
1523
+ fontWeight: "500",
1524
+ marginBottom: 12,
1525
+ // theme.spacing.md - consistent label spacing
1526
+ textAlign: "center"
1527
+ },
1528
+ container: {
1529
+ flexDirection: "row",
1530
+ justifyContent: "center",
1531
+ gap: 8
1532
+ // theme.spacing.sm
1533
+ },
1534
+ input: {
1535
+ width: 48,
1536
+ height: 56,
1537
+ borderWidth: 1,
1538
+ textAlign: "center",
1539
+ fontWeight: "600"
1540
+ },
1541
+ error: {
1542
+ marginTop: 12,
1543
+ // theme.spacing.md - consistent error spacing
1544
+ textAlign: "center"
1545
+ }
1546
+ });
1547
+
1548
+ // src/molecules/Init.tsx
1549
+ var import_react15 = __toESM(require("react"));
1550
+ var import_react_native8 = require("react-native");
1551
+ var Init = ({
1552
+ open,
1553
+ onSuccess,
1554
+ onClose,
1555
+ generateLinkToken
1556
+ }) => {
1557
+ const { setIsInitialized, isInitialized, setLinkToken } = useKryptosConnect();
1558
+ const theme = useTheme();
1559
+ const [isFetching, setIsFetching] = import_react15.default.useState(false);
1560
+ const [error, setError] = import_react15.default.useState(null);
1561
+ const fetchLinkToken = import_react15.default.useCallback(async () => {
1562
+ if (!open) return;
1563
+ setIsFetching(true);
1564
+ setError(null);
1565
+ try {
1566
+ const linkToken = await generateLinkToken();
1567
+ if (!linkToken) {
1568
+ setIsInitialized(false);
1569
+ setError("Failed to fetch link token. Please try again.");
1570
+ return;
1571
+ }
1572
+ setLinkToken(linkToken);
1573
+ setIsInitialized(true);
1574
+ onSuccess();
1575
+ } catch (err) {
1576
+ console.error("Failed to fetch link token:", err);
1577
+ setIsInitialized(false);
1578
+ setError("Failed to fetch link token. Please try again.");
1579
+ } finally {
1580
+ setIsFetching(false);
1581
+ }
1582
+ }, [generateLinkToken, open, setIsInitialized, setLinkToken, onSuccess]);
1583
+ import_react15.default.useEffect(() => {
1584
+ fetchLinkToken();
1585
+ }, [fetchLinkToken]);
1586
+ return /* @__PURE__ */ import_react15.default.createElement(Modal, { isOpen: open, onClose, size: "xs" }, /* @__PURE__ */ import_react15.default.createElement(ModalHeader, { onClose }, "Kryptos Connect"), /* @__PURE__ */ import_react15.default.createElement(ModalBody, null, /* @__PURE__ */ import_react15.default.createElement(import_react_native8.View, { style: styles8.container }, isFetching && /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(
1587
+ import_react_native8.ActivityIndicator,
1588
+ {
1589
+ size: "large",
1590
+ color: theme.colors.primary,
1591
+ style: styles8.spinner
1592
+ }
1593
+ ), /* @__PURE__ */ import_react15.default.createElement(import_react_native8.Text, { style: [styles8.message, { color: theme.colors.text }] }, isInitialized ? "Fetching link token..." : "Initializing...")), !isFetching && error && /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ import_react15.default.createElement(AlertDescription, null, error)), /* @__PURE__ */ import_react15.default.createElement(
1594
+ Button,
1595
+ {
1596
+ variant: "primary",
1597
+ size: "lg",
1598
+ onPress: fetchLinkToken,
1599
+ style: styles8.retryButton
1600
+ },
1601
+ "Retry"
1602
+ )))));
1603
+ };
1604
+ var styles8 = import_react_native8.StyleSheet.create({
1605
+ container: {
1606
+ flex: 1,
1607
+ alignItems: "center",
1608
+ justifyContent: "center",
1609
+ paddingVertical: 16
1610
+ // theme.spacing.lg
1611
+ },
1612
+ spinner: {
1613
+ marginBottom: 16
1614
+ // theme.spacing.lg
1615
+ },
1616
+ message: {
1617
+ fontSize: 16,
1618
+ // theme.fontSize.lg
1619
+ fontWeight: "500"
1620
+ },
1621
+ errorText: {
1622
+ textAlign: "center"
1623
+ },
1624
+ retryButton: {
1625
+ marginTop: 12,
1626
+ width: "100%"
1627
+ }
1628
+ });
1629
+
1630
+ // src/molecules/Integration.tsx
1631
+ var import_react27 = __toESM(require("react"));
1632
+ var import_react_native13 = require("react-native");
1633
+
1634
+ // src/assets/ArrowLeftIcon.tsx
1635
+ var import_react16 = __toESM(require("react"));
1636
+ var import_react_native_svg6 = __toESM(require("react-native-svg"));
1637
+ var ArrowLeftIcon = ({
1638
+ size = 20,
1639
+ color = "#000"
1640
+ }) => {
1641
+ return /* @__PURE__ */ import_react16.default.createElement(import_react_native_svg6.default, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ import_react16.default.createElement(
1642
+ import_react_native_svg6.Path,
1643
+ {
1644
+ d: "M19 12H5M12 19l-7-7 7-7",
1645
+ stroke: color,
1646
+ strokeWidth: 2,
1647
+ strokeLinecap: "round",
1648
+ strokeLinejoin: "round"
1649
+ }
1650
+ ));
1651
+ };
1652
+
1653
+ // src/assets/CheckCircleIcon.tsx
1654
+ var import_react17 = __toESM(require("react"));
1655
+ var import_react_native_svg7 = __toESM(require("react-native-svg"));
1656
+ var CheckCircleIcon = ({
1657
+ size = 20,
1658
+ color = "#10B981"
1659
+ }) => {
1660
+ return /* @__PURE__ */ import_react17.default.createElement(import_react_native_svg7.default, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ import_react17.default.createElement(
1661
+ import_react_native_svg7.Circle,
1662
+ {
1663
+ cx: 12,
1664
+ cy: 12,
1665
+ r: 10,
1666
+ stroke: color,
1667
+ strokeWidth: 2
1668
+ }
1669
+ ), /* @__PURE__ */ import_react17.default.createElement(
1670
+ import_react_native_svg7.Path,
1671
+ {
1672
+ d: "m9 12 2 2 4-4",
1673
+ stroke: color,
1674
+ strokeWidth: 2,
1675
+ strokeLinecap: "round",
1676
+ strokeLinejoin: "round"
1677
+ }
1678
+ ));
1679
+ };
1680
+
1681
+ // src/assets/LoaderIcon.tsx
1682
+ var import_react18 = __toESM(require("react"));
1683
+ var import_react_native9 = require("react-native");
1684
+ var import_react_native_svg8 = __toESM(require("react-native-svg"));
1685
+ var AnimatedSvg = import_react_native9.Animated.createAnimatedComponent(import_react_native_svg8.default);
1686
+ var LoaderIcon = ({
1687
+ size = 20,
1688
+ color = "#00C693"
1689
+ }) => {
1690
+ const rotateAnim = import_react18.default.useRef(new import_react_native9.Animated.Value(0)).current;
1691
+ import_react18.default.useEffect(() => {
1692
+ import_react_native9.Animated.loop(
1693
+ import_react_native9.Animated.timing(rotateAnim, {
1694
+ toValue: 1,
1695
+ duration: 1e3,
1696
+ easing: import_react_native9.Easing.linear,
1697
+ useNativeDriver: true
1698
+ })
1699
+ ).start();
1700
+ }, [rotateAnim]);
1701
+ const spin = rotateAnim.interpolate({
1702
+ inputRange: [0, 1],
1703
+ outputRange: ["0deg", "360deg"]
1704
+ });
1705
+ return /* @__PURE__ */ import_react18.default.createElement(
1706
+ AnimatedSvg,
1707
+ {
1708
+ width: size,
1709
+ height: size,
1710
+ viewBox: "0 0 24 24",
1711
+ fill: "none",
1712
+ style: { transform: [{ rotate: spin }] }
1713
+ },
1714
+ /* @__PURE__ */ import_react18.default.createElement(
1715
+ import_react_native_svg8.Path,
1716
+ {
1717
+ d: "M21 12a9 9 0 1 1-6.219-8.56",
1718
+ stroke: color,
1719
+ strokeWidth: 2,
1720
+ strokeLinecap: "round",
1721
+ strokeLinejoin: "round"
1722
+ }
1723
+ )
1724
+ );
1725
+ };
1726
+
1727
+ // src/assets/SuccessIcon.tsx
1728
+ var import_react19 = __toESM(require("react"));
1729
+ var import_react_native_svg9 = __toESM(require("react-native-svg"));
1730
+ var SuccessIcon = ({ size = 64 }) => {
1731
+ return /* @__PURE__ */ import_react19.default.createElement(import_react_native_svg9.default, { width: size, height: size, viewBox: "0 0 64 64", fill: "none" }, /* @__PURE__ */ import_react19.default.createElement(
1732
+ import_react_native_svg9.Circle,
1733
+ {
1734
+ cx: 32,
1735
+ cy: 32,
1736
+ r: 30,
1737
+ fill: "#00C693",
1738
+ opacity: 0.1
1739
+ }
1740
+ ), /* @__PURE__ */ import_react19.default.createElement(
1741
+ import_react_native_svg9.Circle,
1742
+ {
1743
+ cx: 32,
1744
+ cy: 32,
1745
+ r: 24,
1746
+ fill: "#00C693"
1747
+ }
1748
+ ), /* @__PURE__ */ import_react19.default.createElement(
1749
+ import_react_native_svg9.Path,
1750
+ {
1751
+ d: "M24 32l6 6 12-12",
1752
+ stroke: "white",
1753
+ strokeWidth: 3,
1754
+ strokeLinecap: "round",
1755
+ strokeLinejoin: "round"
1756
+ }
1757
+ ));
1758
+ };
1759
+
1760
+ // src/assets/ErrorIcon.tsx
1761
+ var import_react20 = __toESM(require("react"));
1762
+ var import_react_native_svg10 = __toESM(require("react-native-svg"));
1763
+ var ErrorIcon = ({ size = 64 }) => {
1764
+ return /* @__PURE__ */ import_react20.default.createElement(import_react_native_svg10.default, { width: size, height: size, viewBox: "0 0 64 64", fill: "none" }, /* @__PURE__ */ import_react20.default.createElement(
1765
+ import_react_native_svg10.Circle,
1766
+ {
1767
+ cx: 32,
1768
+ cy: 32,
1769
+ r: 30,
1770
+ fill: "#EF4444",
1771
+ opacity: 0.1
1772
+ }
1773
+ ), /* @__PURE__ */ import_react20.default.createElement(
1774
+ import_react_native_svg10.Circle,
1775
+ {
1776
+ cx: 32,
1777
+ cy: 32,
1778
+ r: 24,
1779
+ fill: "#EF4444"
1780
+ }
1781
+ ), /* @__PURE__ */ import_react20.default.createElement(
1782
+ import_react_native_svg10.Path,
1783
+ {
1784
+ d: "M24 24l16 16M40 24l-16 16",
1785
+ stroke: "white",
1786
+ strokeWidth: 3,
1787
+ strokeLinecap: "round",
1788
+ strokeLinejoin: "round"
1789
+ }
1790
+ ));
1791
+ };
1792
+
1793
+ // src/assets/SearchIcon.tsx
1794
+ var import_react21 = __toESM(require("react"));
1795
+ var import_react_native_svg11 = __toESM(require("react-native-svg"));
1796
+
1797
+ // src/assets/PlusIcon.tsx
1798
+ var import_react22 = __toESM(require("react"));
1799
+ var import_react_native_svg12 = __toESM(require("react-native-svg"));
1800
+ var PlusIcon = ({
1801
+ size = 14,
1802
+ color = "#6B7280"
1803
+ }) => {
1804
+ return /* @__PURE__ */ import_react22.default.createElement(import_react_native_svg12.default, { width: size, height: size, viewBox: "0 0 14 14", fill: "none" }, /* @__PURE__ */ import_react22.default.createElement(
1805
+ import_react_native_svg12.Path,
1806
+ {
1807
+ d: "M7 3.5v7M3.5 7h7",
1808
+ stroke: color,
1809
+ strokeWidth: 2,
1810
+ strokeLinecap: "round"
1811
+ }
1812
+ ));
1813
+ };
1814
+
1815
+ // src/wallet-connect/index.tsx
1816
+ var import_appkit_react_native3 = require("@reown/appkit-react-native");
1817
+ var import_react24 = __toESM(require("react"));
1818
+ var import_react_native10 = require("react-native");
1819
+
1820
+ // src/utils/uuid.ts
1821
+ function generateUUID() {
1822
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
1823
+ const r = Math.random() * 16 | 0;
1824
+ const v = c === "x" ? r : r & 3 | 8;
1825
+ return v.toString(16);
1826
+ });
1827
+ }
1828
+
1829
+ // src/wallet-connect/wallet-connect.tsx
1830
+ var import_react23 = __toESM(require("react"));
1831
+ var import_appkit_react_native2 = require("@reown/appkit-react-native");
1832
+
1833
+ // src/wallet-connect/AppKitConfig.ts
1834
+ var import_react_native_compat = require("@walletconnect/react-native-compat");
1835
+ var import_async_storage = __toESM(require("@react-native-async-storage/async-storage"));
1836
+ var import_appkit_react_native = require("@reown/appkit-react-native");
1837
+
1838
+ // node_modules/@walletconnect/safe-json/dist/esm/index.js
1839
+ var JSONStringify = (data) => JSON.stringify(data, (_, value) => typeof value === "bigint" ? value.toString() + "n" : value);
1840
+ var JSONParse = (json) => {
1841
+ const numbersBiggerThanMaxInt = /([\[:])?(\d{17,}|(?:[9](?:[1-9]07199254740991|0[1-9]7199254740991|00[8-9]199254740991|007[2-9]99254740991|007199[3-9]54740991|0071992[6-9]4740991|00719925[5-9]740991|007199254[8-9]40991|0071992547[5-9]0991|00719925474[1-9]991|00719925474099[2-9])))([,\}\]])/g;
1842
+ const serializedData = json.replace(numbersBiggerThanMaxInt, '$1"$2n"$3');
1843
+ return JSON.parse(serializedData, (_, value) => {
1844
+ const isCustomFormatBigInt = typeof value === "string" && value.match(/^\d+n$/);
1845
+ if (isCustomFormatBigInt)
1846
+ return BigInt(value.substring(0, value.length - 1));
1847
+ return value;
1848
+ });
1849
+ };
1850
+ function safeJsonParse(value) {
1851
+ if (typeof value !== "string") {
1852
+ throw new Error(`Cannot safe json parse value of type ${typeof value}`);
1853
+ }
1854
+ try {
1855
+ return JSONParse(value);
1856
+ } catch (_a) {
1857
+ return value;
1858
+ }
1859
+ }
1860
+ function safeJsonStringify(value) {
1861
+ return typeof value === "string" ? value : JSONStringify(value) || "";
1862
+ }
1863
+
1864
+ // src/wallet-connect/AppKitConfig.ts
1865
+ var import_appkit_ethers_react_native = require("@reown/appkit-ethers-react-native");
1866
+ var import_chains = require("viem/chains");
1867
+ var ethersAdapter = new import_appkit_ethers_react_native.EthersAdapter();
1868
+ var storage = {
1869
+ getKeys: async () => {
1870
+ return await import_async_storage.default.getAllKeys();
1871
+ },
1872
+ getEntries: async () => {
1873
+ const keys = await import_async_storage.default.getAllKeys();
1874
+ return Promise.all(
1875
+ keys.map(async (key) => {
1876
+ const value = await import_async_storage.default.getItem(key);
1877
+ return [key, safeJsonParse(value || "")];
1878
+ })
1879
+ );
1880
+ },
1881
+ setItem: async (key, value) => {
1882
+ await import_async_storage.default.setItem(key, safeJsonStringify(value));
1883
+ },
1884
+ getItem: async (key) => {
1885
+ const raw = await import_async_storage.default.getItem(key);
1886
+ if (!raw) return void 0;
1887
+ return safeJsonParse(raw);
1888
+ },
1889
+ removeItem: async (key) => {
1890
+ await import_async_storage.default.removeItem(key);
1891
+ }
1892
+ };
1893
+ var createAppKitInstance = (projectId) => {
1894
+ if (!projectId) {
1895
+ throw new Error("walletConnectProjectId is required to initialize AppKit");
1896
+ }
1897
+ return (0, import_appkit_react_native.createAppKit)({
1898
+ projectId,
1899
+ networks: [
1900
+ import_chains.mainnet,
1901
+ import_chains.arbitrum,
1902
+ import_chains.avalanche,
1903
+ import_chains.bsc,
1904
+ import_chains.fantom,
1905
+ import_chains.gnosis,
1906
+ import_chains.optimism,
1907
+ import_chains.polygon,
1908
+ import_chains.base
1909
+ ],
1910
+ adapters: [ethersAdapter],
1911
+ features: {
1912
+ swaps: false,
1913
+ socials: false,
1914
+ onramp: false
1915
+ },
1916
+ metadata: {
1917
+ name: "Kryptos Connect",
1918
+ description: "Kryptos Connect",
1919
+ url: "https://kryptos.com",
1920
+ icons: ["https://kryptos.com/icon.png"]
1921
+ },
1922
+ storage
1923
+ });
1924
+ };
1925
+
1926
+ // src/wallet-connect/wallet-connect.tsx
1927
+ var WalletConnectWrapper = ({ children }) => {
1928
+ const { walletConnectProjectId } = useKryptosConnect();
1929
+ const appKit = import_react23.default.useMemo(() => {
1930
+ if (!walletConnectProjectId) {
1931
+ console.warn(
1932
+ "walletConnectProjectId is missing in KryptosConnectProvider config"
1933
+ );
1934
+ return null;
1935
+ }
1936
+ return createAppKitInstance(walletConnectProjectId);
1937
+ }, [walletConnectProjectId]);
1938
+ if (!appKit) {
1939
+ return /* @__PURE__ */ import_react23.default.createElement(import_react23.default.Fragment, null, children);
1940
+ }
1941
+ return /* @__PURE__ */ import_react23.default.createElement(import_appkit_react_native2.AppKitProvider, { instance: appKit }, /* @__PURE__ */ import_react23.default.createElement(import_appkit_react_native2.AppKit, null), children);
1942
+ };
1943
+ var wallet_connect_default = WalletConnectWrapper;
1944
+
1945
+ // src/wallet-connect/index.tsx
1946
+ var WalletConnectComponent = ({
1947
+ integration,
1948
+ onClose,
1949
+ onAddHandle,
1950
+ handleClose,
1951
+ modalOpen,
1952
+ setAddIntegrationMode
1953
+ }) => {
1954
+ const { walletConnectProjectId } = useKryptosConnect();
1955
+ const theme = useTheme();
1956
+ if (!walletConnectProjectId) {
1957
+ return /* @__PURE__ */ import_react24.default.createElement(Modal, { isOpen: modalOpen, onClose: handleClose, size: "full" }, /* @__PURE__ */ import_react24.default.createElement(ModalHeader, { onClose: handleClose }, /* @__PURE__ */ import_react24.default.createElement(import_react_native10.View, { style: styles9.headerContent }, /* @__PURE__ */ import_react24.default.createElement(
1958
+ import_react_native10.TouchableOpacity,
1959
+ {
1960
+ onPress: () => {
1961
+ setAddIntegrationMode(null);
1962
+ },
1963
+ style: styles9.backButton
1964
+ },
1965
+ /* @__PURE__ */ import_react24.default.createElement(ArrowLeftIcon, { size: 20, color: theme.colors.text })
1966
+ ), /* @__PURE__ */ import_react24.default.createElement(import_react_native10.Text, { style: [styles9.headerTitle, { color: theme.colors.text }] }, "Integration"))), /* @__PURE__ */ import_react24.default.createElement(ModalBody, { scrollable: false, style: styles9.contentContainer }, /* @__PURE__ */ import_react24.default.createElement(import_react_native10.View, { style: styles9.emptyState }, /* @__PURE__ */ import_react24.default.createElement(
1967
+ import_react_native10.Text,
1968
+ {
1969
+ style: [styles9.emptyStateTitle, { color: theme.colors.text }]
1970
+ },
1971
+ "WalletConnect is not configured"
1972
+ ), /* @__PURE__ */ import_react24.default.createElement(
1973
+ import_react_native10.Text,
1974
+ {
1975
+ style: [
1976
+ styles9.infoText,
1977
+ { color: theme.colors.textSecondary, textAlign: "center" }
1978
+ ]
1979
+ },
1980
+ "Please add a walletConnectProjectId to KryptosConnectProvider to enable wallet connections."
1981
+ ), /* @__PURE__ */ import_react24.default.createElement(
1982
+ Button,
1983
+ {
1984
+ variant: "outline",
1985
+ size: "sm",
1986
+ onPress: () => setAddIntegrationMode(null),
1987
+ style: styles9.emptyStateButton
1988
+ },
1989
+ "Go back"
1990
+ ))));
1991
+ }
1992
+ return /* @__PURE__ */ import_react24.default.createElement(wallet_connect_default, null, /* @__PURE__ */ import_react24.default.createElement(
1993
+ ConnectButton,
1994
+ {
1995
+ integration,
1996
+ onAddHandle,
1997
+ onClose,
1998
+ handleClose,
1999
+ modalOpen,
2000
+ setAddIntegrationMode
2001
+ }
2002
+ ));
2003
+ };
2004
+ function ConnectButton({
2005
+ integration,
2006
+ onAddHandle,
2007
+ handleClose,
2008
+ modalOpen,
2009
+ setAddIntegrationMode
2010
+ }) {
2011
+ const theme = useTheme();
2012
+ const { open, disconnect } = (0, import_appkit_react_native3.useAppKit)();
2013
+ const { address, isConnected, chainId } = (0, import_appkit_react_native3.useAccount)();
2014
+ const { linkToken, user, clientId } = useKryptosConnect();
2015
+ const [selectedChains, setSelectedChains] = (0, import_react24.useState)(/* @__PURE__ */ new Set());
2016
+ const [errorMessage, setErrorMessage] = (0, import_react24.useState)("");
2017
+ const [chainErrors, setChainErrors] = (0, import_react24.useState)({});
2018
+ const [isLoading, setIsLoading] = (0, import_react24.useState)(false);
2019
+ const userUsedChains = integration?.walletSupportedChains || [];
2020
+ const validateForm = () => {
2021
+ if (!address) {
2022
+ setErrorMessage("Please connect a wallet");
2023
+ return false;
2024
+ }
2025
+ if (selectedChains.size === 0) {
2026
+ setErrorMessage("Select at least one chain");
2027
+ return false;
2028
+ }
2029
+ return true;
2030
+ };
2031
+ const onSubmitWalletConnect = async () => {
2032
+ if (!validateForm()) return;
2033
+ try {
2034
+ setIsLoading(true);
2035
+ setErrorMessage("");
2036
+ setChainErrors({});
2037
+ const chainsToProcess = userUsedChains.filter(
2038
+ (c) => selectedChains.has(c.id)
2039
+ );
2040
+ const integrationsToAdd = [];
2041
+ const errors = {};
2042
+ const walletTestsPayload = chainsToProcess.map((chain) => {
2043
+ const walletId = generateUUID();
2044
+ const displaySuffix = address ? address?.length > 8 ? `${address.slice(0, 4)}...${address.slice(-4)}` : address : "";
2045
+ const alias = `${integration.id} - ${chain.id} (${displaySuffix})`;
2046
+ return {
2047
+ chain,
2048
+ walletId,
2049
+ alias,
2050
+ credential: {
2051
+ source: integration.id,
2052
+ credential: {
2053
+ address,
2054
+ userId: user?.user?.uid || "0",
2055
+ projectId: integration.projectId,
2056
+ apiKey: "0",
2057
+ secret: "0",
2058
+ privateKey: "0",
2059
+ alias,
2060
+ walletId,
2061
+ exchange: integration.id
2062
+ }
2063
+ }
2064
+ };
2065
+ });
2066
+ const results = await Promise.allSettled(
2067
+ walletTestsPayload.map(
2068
+ (data) => testCredentials(linkToken, data.credential)
2069
+ )
2070
+ );
2071
+ results.forEach((result, index) => {
2072
+ const { chain, walletId, alias } = walletTestsPayload[index];
2073
+ if (result.status === "fulfilled") {
2074
+ const data = {
2075
+ alias,
2076
+ exchange: integration.id.toLowerCase(),
2077
+ id: integration.id,
2078
+ public_name: integration.public_name,
2079
+ sync_time: (/* @__PURE__ */ new Date()).getTime(),
2080
+ fetchAll: true,
2081
+ logo: integration.logo || null,
2082
+ startTime: null,
2083
+ endTime: null,
2084
+ uid: user?.user?.uid || "",
2085
+ walletId,
2086
+ clientMetadata: {
2087
+ clientId,
2088
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2089
+ },
2090
+ metadata: {
2091
+ environment: "sandbox"
2092
+ },
2093
+ addedOn: (/* @__PURE__ */ new Date()).getTime(),
2094
+ default_chain: chain.name,
2095
+ default_chain_logo: chain.logo || null,
2096
+ type: integration.type,
2097
+ isNftSupported: integration.isEvmWallet || integration.nftSupport || false,
2098
+ chainId: chain.chainId || chain.id,
2099
+ address
2100
+ };
2101
+ integrationsToAdd.push(data);
2102
+ } else {
2103
+ errors[chain.id] = "Wallet verification failed";
2104
+ }
2105
+ });
2106
+ if (integrationsToAdd.length > 0) {
2107
+ onAddHandle(integrationsToAdd);
2108
+ setChainErrors({});
2109
+ setErrorMessage("");
2110
+ } else {
2111
+ setErrorMessage("No integrations could be added. Please try again.");
2112
+ }
2113
+ } catch (error) {
2114
+ const err = error;
2115
+ console.error(error);
2116
+ setErrorMessage(
2117
+ err?.response?.data?.message || "Failed to add integration"
2118
+ );
2119
+ } finally {
2120
+ setIsLoading(false);
2121
+ }
2122
+ };
2123
+ const toggleChainSelection = (chainId2) => {
2124
+ const newSelected = new Set(selectedChains);
2125
+ if (newSelected.has(chainId2)) {
2126
+ newSelected.delete(chainId2);
2127
+ } else {
2128
+ newSelected.add(chainId2);
2129
+ }
2130
+ setSelectedChains(newSelected);
2131
+ if (chainErrors[chainId2]) {
2132
+ const newErrors = { ...chainErrors };
2133
+ delete newErrors[chainId2];
2134
+ setChainErrors(newErrors);
2135
+ }
2136
+ };
2137
+ return /* @__PURE__ */ import_react24.default.createElement(Modal, { isOpen: modalOpen, onClose: handleClose, size: "full" }, /* @__PURE__ */ import_react24.default.createElement(ModalHeader, { onClose: handleClose }, /* @__PURE__ */ import_react24.default.createElement(import_react_native10.View, { style: styles9.headerContent }, /* @__PURE__ */ import_react24.default.createElement(
2138
+ import_react_native10.TouchableOpacity,
2139
+ {
2140
+ onPress: () => {
2141
+ setAddIntegrationMode(null);
2142
+ },
2143
+ style: styles9.backButton
2144
+ },
2145
+ /* @__PURE__ */ import_react24.default.createElement(ArrowLeftIcon, { size: 20, color: theme.colors.text })
2146
+ ), /* @__PURE__ */ import_react24.default.createElement(import_react_native10.Text, { style: [styles9.headerTitle, { color: theme.colors.text }] }, "Integration"))), /* @__PURE__ */ import_react24.default.createElement(ModalBody, { scrollable: false, style: styles9.contentContainer }, !isConnected ? /* @__PURE__ */ import_react24.default.createElement(import_react_native10.View, null, /* @__PURE__ */ import_react24.default.createElement(import_react_native10.Text, { style: [styles9.infoText, { color: theme.colors.text }] }, "Connect your wallet to continue"), /* @__PURE__ */ import_react24.default.createElement(
2147
+ Button,
2148
+ {
2149
+ variant: "primary",
2150
+ size: "sm",
2151
+ onPress: () => open({ view: "Connect" })
2152
+ },
2153
+ "Connect Wallet"
2154
+ )) : /* @__PURE__ */ import_react24.default.createElement(import_react_native10.View, null, /* @__PURE__ */ import_react24.default.createElement(import_react_native10.Text, { style: [styles9.connectedTitle, { color: theme.colors.text }] }, "Wallet Connected"), /* @__PURE__ */ import_react24.default.createElement(import_react_native10.Text, { style: [styles9.connectedText, { color: theme.colors.text }] }, "Address: ", address), /* @__PURE__ */ import_react24.default.createElement(import_react_native10.Text, { style: [styles9.connectedText, { color: theme.colors.text }] }, "Chain: ", chainId), /* @__PURE__ */ import_react24.default.createElement(Button, { variant: "ghost", size: "sm", onPress: () => disconnect() }, "Disconnect Wallet"), userUsedChains.length > 0 && address && /* @__PURE__ */ import_react24.default.createElement(import_react_native10.View, { style: styles9.chainSelection }, /* @__PURE__ */ import_react24.default.createElement(import_react_native10.Text, { style: [styles9.chainTitle, { color: theme.colors.text }] }, "Select Chains to Add:"), /* @__PURE__ */ import_react24.default.createElement(import_react_native10.ScrollView, { contentContainerStyle: styles9.scrollViewContent }, /* @__PURE__ */ import_react24.default.createElement(import_react_native10.View, { style: styles9.chainChips }, userUsedChains.map((chain) => {
2155
+ const isSelected = selectedChains.has(chain.id);
2156
+ const hasError = chainErrors[chain.id];
2157
+ return /* @__PURE__ */ import_react24.default.createElement(
2158
+ import_react_native10.TouchableOpacity,
2159
+ {
2160
+ onPress: () => toggleChainSelection(chain.id),
2161
+ style: styles9.chainButton,
2162
+ key: chain.id
2163
+ },
2164
+ /* @__PURE__ */ import_react24.default.createElement(
2165
+ import_react_native10.View,
2166
+ {
2167
+ style: [
2168
+ styles9.chainChip,
2169
+ {
2170
+ backgroundColor: hasError ? theme.colors.errorLight : isSelected ? theme.colors.primary + "20" : theme.colors.surface,
2171
+ borderColor: hasError ? theme.colors.error : isSelected ? theme.colors.primary : theme.colors.border
2172
+ }
2173
+ ]
2174
+ },
2175
+ /* @__PURE__ */ import_react24.default.createElement(
2176
+ import_react_native10.Text,
2177
+ {
2178
+ style: [
2179
+ styles9.chainName,
2180
+ {
2181
+ color: hasError ? theme.colors.error : isSelected ? theme.colors.primary : theme.colors.text
2182
+ }
2183
+ ]
2184
+ },
2185
+ chain.id
2186
+ ),
2187
+ isSelected ? /* @__PURE__ */ import_react24.default.createElement(
2188
+ CloseIcon,
2189
+ {
2190
+ size: 12,
2191
+ color: hasError ? theme.colors.error : theme.colors.primary
2192
+ }
2193
+ ) : /* @__PURE__ */ import_react24.default.createElement(
2194
+ PlusIcon,
2195
+ {
2196
+ size: 12,
2197
+ color: theme.colors.textSecondary
2198
+ }
2199
+ )
2200
+ )
2201
+ );
2202
+ }))), Object.keys(chainErrors).length > 0 && /* @__PURE__ */ import_react24.default.createElement(import_react_native10.View, { style: styles9.chainErrorsContainer }, /* @__PURE__ */ import_react24.default.createElement(
2203
+ import_react_native10.Text,
2204
+ {
2205
+ style: [
2206
+ styles9.chainErrorsTitle,
2207
+ { color: theme.colors.error }
2208
+ ]
2209
+ },
2210
+ "Errors:"
2211
+ ), Object.entries(chainErrors).map(([chainId2, error]) => {
2212
+ const chain = userUsedChains.find(
2213
+ (c) => c.id === chainId2
2214
+ );
2215
+ return /* @__PURE__ */ import_react24.default.createElement(
2216
+ import_react_native10.Text,
2217
+ {
2218
+ key: chainId2,
2219
+ style: [
2220
+ styles9.chainErrorItem,
2221
+ { color: theme.colors.error }
2222
+ ]
2223
+ },
2224
+ "\u2022 ",
2225
+ chain?.name,
2226
+ ": ",
2227
+ error
2228
+ );
2229
+ }))), errorMessage ? /* @__PURE__ */ import_react24.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ import_react24.default.createElement(AlertDescription, null, errorMessage)) : null)), userUsedChains.length > 0 && address && /* @__PURE__ */ import_react24.default.createElement(ModalFooter, null, /* @__PURE__ */ import_react24.default.createElement(
2230
+ Button,
2231
+ {
2232
+ variant: "outline",
2233
+ size: "lg",
2234
+ onPress: onSubmitWalletConnect,
2235
+ loading: isLoading,
2236
+ disabled: isLoading || !!address && userUsedChains.length > 0 && selectedChains.size === 0,
2237
+ style: styles9.button
2238
+ },
2239
+ selectedChains.size > 0 ? `Add ${selectedChains.size} Chain${selectedChains.size > 1 ? "s" : ""}` : "Add Integration"
2240
+ )));
2241
+ }
2242
+ var styles9 = import_react_native10.StyleSheet.create({
2243
+ connectedTitle: { fontSize: 18, fontWeight: "600", marginBottom: 4 },
2244
+ connectedText: { fontSize: 14, marginBottom: 4 },
2245
+ infoText: {
2246
+ fontSize: 16,
2247
+ fontWeight: "600",
2248
+ marginBottom: 8,
2249
+ textAlign: "center"
2250
+ },
2251
+ scrollViewContent: {
2252
+ paddingBottom: 100,
2253
+ flexGrow: 1
2254
+ },
2255
+ headerContent: {
2256
+ flexDirection: "row",
2257
+ alignItems: "center"
2258
+ },
2259
+ backButton: {
2260
+ padding: 4,
2261
+ // theme.spacing.xs
2262
+ marginRight: 8
2263
+ // theme.spacing.sm
2264
+ },
2265
+ headerTitle: {
2266
+ fontSize: 16,
2267
+ // theme.fontSize.lg
2268
+ fontWeight: "600"
2269
+ },
2270
+ contentContainer: {
2271
+ padding: 20,
2272
+ // theme.spacing.xl
2273
+ paddingBottom: 40,
2274
+ width: "100%",
2275
+ overflow: "hidden",
2276
+ alignSelf: "center",
2277
+ flexDirection: "column",
2278
+ flex: 1
2279
+ },
2280
+ chainSelection: {
2281
+ marginBottom: 16
2282
+ // theme.spacing.lg
2283
+ },
2284
+ chainTitle: {
2285
+ fontSize: 14,
2286
+ // theme.fontSize.md
2287
+ fontWeight: "500",
2288
+ marginBottom: 12
2289
+ // theme.spacing.md - consistent label spacing
2290
+ },
2291
+ chainChips: {
2292
+ flexDirection: "row",
2293
+ flexWrap: "wrap",
2294
+ gap: 6
2295
+ // theme.spacing.sm
2296
+ },
2297
+ chainChip: {
2298
+ flexDirection: "row",
2299
+ alignItems: "center",
2300
+ paddingHorizontal: 8,
2301
+ // theme.spacing.sm
2302
+ paddingVertical: 5,
2303
+ // theme.spacing.xs
2304
+ borderRadius: 12,
2305
+ // theme.borderRadius.md
2306
+ borderWidth: 1
2307
+ },
2308
+ chainName: {
2309
+ fontSize: 12,
2310
+ fontWeight: "500",
2311
+ marginRight: 6
2312
+ // theme.spacing.xs
2313
+ },
2314
+ chainButton: {
2315
+ padding: 2
2316
+ // theme.spacing.xs
2317
+ },
2318
+ chainErrorsContainer: {
2319
+ marginTop: 12
2320
+ // theme.spacing.md - consistent spacing
2321
+ },
2322
+ chainErrorsTitle: {
2323
+ fontSize: 13,
2324
+ fontWeight: "500",
2325
+ marginBottom: 4
2326
+ // theme.spacing.xs
2327
+ },
2328
+ chainErrorItem: {
2329
+ fontSize: 12,
2330
+ // theme.fontSize.sm
2331
+ marginBottom: 4
2332
+ // theme.spacing.xs
2333
+ },
2334
+ button: {
2335
+ width: "100%",
2336
+ marginTop: 16
2337
+ // theme.spacing.lg - consistent button spacing
2338
+ },
2339
+ emptyState: {
2340
+ flex: 1,
2341
+ alignItems: "center",
2342
+ justifyContent: "center",
2343
+ gap: 12
2344
+ },
2345
+ emptyStateTitle: {
2346
+ fontSize: 18,
2347
+ fontWeight: "600"
2348
+ },
2349
+ emptyStateButton: {
2350
+ marginTop: 8
2351
+ }
2352
+ });
2353
+
2354
+ // src/molecules/IntegrationForm.tsx
2355
+ var import_react25 = __toESM(require("react"));
2356
+ var import_react_native11 = require("react-native");
2357
+ var IntegrationForm = ({
2358
+ metadata,
2359
+ onAddHandle,
2360
+ open,
2361
+ setAddIntegrationMode,
2362
+ handleClose
2363
+ }) => {
2364
+ const { clientId, linkToken, user } = useKryptosConnect();
2365
+ const theme = useTheme();
2366
+ const [isLoading, setIsLoading] = import_react25.default.useState(false);
2367
+ const [userUsedChains, setUserUsedChains] = import_react25.default.useState([]);
2368
+ const [selectedChains, setSelectedChains] = import_react25.default.useState(
2369
+ /* @__PURE__ */ new Set()
2370
+ );
2371
+ const [chainErrors, setChainErrors] = import_react25.default.useState(
2372
+ {}
2373
+ );
2374
+ const [errorMessage, setErrorMessage] = import_react25.default.useState("");
2375
+ const [formValues, setFormValues] = import_react25.default.useState({
2376
+ address: "",
2377
+ account_name: "",
2378
+ api_key: "",
2379
+ secret_key: "",
2380
+ password: ""
2381
+ });
2382
+ const [formErrors, setFormErrors] = import_react25.default.useState({});
2383
+ import_react25.default.useEffect(() => {
2384
+ const fetchUserUsedChains = async () => {
2385
+ if (linkToken && formValues.address && formValues.address.trim()) {
2386
+ try {
2387
+ const res = await getUserUsedChains(
2388
+ linkToken,
2389
+ formValues.address.trim()
2390
+ );
2391
+ if (res && Array.isArray(res)) {
2392
+ setUserUsedChains(res);
2393
+ setSelectedChains(new Set(res.map((chain) => chain.id)));
2394
+ }
2395
+ } catch (error) {
2396
+ console.error("Failed to fetch user chains:", error);
2397
+ setUserUsedChains([]);
2398
+ setSelectedChains(/* @__PURE__ */ new Set());
2399
+ }
2400
+ } else {
2401
+ setUserUsedChains([]);
2402
+ setSelectedChains(/* @__PURE__ */ new Set());
2403
+ }
2404
+ };
2405
+ fetchUserUsedChains();
2406
+ }, [linkToken, formValues.address]);
2407
+ const toggleChainSelection = (chainId) => {
2408
+ const newSelected = new Set(selectedChains);
2409
+ if (newSelected.has(chainId)) {
2410
+ newSelected.delete(chainId);
2411
+ } else {
2412
+ newSelected.add(chainId);
2413
+ }
2414
+ setSelectedChains(newSelected);
2415
+ if (chainErrors[chainId]) {
2416
+ const newErrors = { ...chainErrors };
2417
+ delete newErrors[chainId];
2418
+ setChainErrors(newErrors);
2419
+ }
2420
+ };
2421
+ const validateForm = () => {
2422
+ const errors = {};
2423
+ if (metadata.address && !formValues.address.trim()) {
2424
+ errors.address = "Address is required";
2425
+ }
2426
+ if (metadata.account_name && !formValues.account_name.trim()) {
2427
+ errors.account_name = "Account name is required";
2428
+ }
2429
+ if (metadata.api_key && !formValues.api_key.trim()) {
2430
+ errors.api_key = "API key is required";
2431
+ }
2432
+ if (metadata.secret_key && !formValues.secret_key.trim()) {
2433
+ errors.secret_key = "Secret key is required";
2434
+ }
2435
+ if (metadata.password && !formValues.password.trim()) {
2436
+ errors.password = "Password is required";
2437
+ }
2438
+ setFormErrors(errors);
2439
+ return Object.keys(errors).length === 0;
2440
+ };
2441
+ const handleSubmit = async () => {
2442
+ if (!validateForm()) return;
2443
+ try {
2444
+ setIsLoading(true);
2445
+ setChainErrors({});
2446
+ setErrorMessage("");
2447
+ const primaryField = formValues.address || formValues.account_name || formValues.api_key || "";
2448
+ const chainsToProcess = userUsedChains.length > 0 && formValues.address && selectedChains.size > 0 ? userUsedChains.filter((chain) => selectedChains.has(chain.id)) : [];
2449
+ const integrationsToAdd = [];
2450
+ const errors = {};
2451
+ if (chainsToProcess.length > 0) {
2452
+ const credentialTestsData = chainsToProcess.map((chain) => {
2453
+ const walletId = generateUUID();
2454
+ const displaySuffix = primaryField.length >= 8 ? `${primaryField.slice(0, 4)}...${primaryField.slice(-4)}` : primaryField;
2455
+ const alias = `${metadata.id} - ${chain.name} (${displaySuffix})`;
2456
+ const credential = {
2457
+ source: metadata.id,
2458
+ credential: {
2459
+ apiKey: formValues.api_key?.trim() || "0",
2460
+ secret: formValues.secret_key?.trim() || "0",
2461
+ accountName: formValues.account_name?.trim() || "0",
2462
+ address: formValues.address?.trim() || "0",
2463
+ password: formValues.password?.trim() || "0",
2464
+ userId: user?.user?.uid || "0",
2465
+ projectId: metadata?.projectId || "0",
2466
+ privateKey: "0",
2467
+ alias,
2468
+ walletId,
2469
+ exchange: metadata.id
2470
+ }
2471
+ };
2472
+ return { chain, walletId, alias, credential };
2473
+ });
2474
+ const results = await Promise.allSettled(
2475
+ credentialTestsData.map(
2476
+ (testData) => testCredentials(linkToken, { ...testData.credential })
2477
+ )
2478
+ );
2479
+ results.forEach((result, index) => {
2480
+ const { chain, walletId, alias } = credentialTestsData[index];
2481
+ if (result.status === "fulfilled" && result.value?.value?.status) {
2482
+ const data = {
2483
+ alias,
2484
+ exchange: metadata.id.toLowerCase(),
2485
+ id: metadata.id,
2486
+ public_name: metadata.public_name,
2487
+ sync_time: (/* @__PURE__ */ new Date()).getTime(),
2488
+ fetchAll: true,
2489
+ logo: metadata.logo || null,
2490
+ startTime: null,
2491
+ endTime: null,
2492
+ uid: user?.user?.uid || "",
2493
+ walletId,
2494
+ clientMetadata: {
2495
+ clientId,
2496
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2497
+ },
2498
+ metadata: {
2499
+ environment: "sandbox"
2500
+ },
2501
+ addedOn: (/* @__PURE__ */ new Date()).getTime(),
2502
+ default_chain: chain.name,
2503
+ default_chain_logo: chain.logo || null,
2504
+ type: metadata.type,
2505
+ isNftSupported: metadata.isEvmWallet || metadata.nftSupport || false,
2506
+ chainId: chain.chainId || chain.id,
2507
+ address: formValues.address
2508
+ };
2509
+ if (formValues.account_name)
2510
+ data.accountName = formValues.account_name;
2511
+ if (formValues.api_key) data.apiKey = formValues.api_key;
2512
+ if (formValues.secret_key) data.secret = formValues.secret_key;
2513
+ if (formValues.password) data.password = formValues.password;
2514
+ integrationsToAdd.push(data);
2515
+ } else {
2516
+ if (result.status === "rejected") {
2517
+ const reason = result.reason;
2518
+ errors[chain.id] = reason?.response?.data?.message || "Failed to process chain";
2519
+ } else if (result.status === "fulfilled") {
2520
+ errors[chain.id] = result.value?.value?.message || "Failed to verify chain";
2521
+ }
2522
+ }
2523
+ });
2524
+ setChainErrors(errors);
2525
+ if (Object.keys(errors).length > 0) {
2526
+ setErrorMessage(
2527
+ `Cannot add integrations. ${Object.keys(errors).length} chain${Object.keys(errors).length > 1 ? "s" : ""} failed verification.`
2528
+ );
2529
+ return;
2530
+ }
2531
+ } else {
2532
+ const displaySuffix = primaryField.length >= 8 ? `${primaryField.slice(0, 4)}...${primaryField.slice(-4)}` : primaryField;
2533
+ const alias = `${metadata.id} (${displaySuffix})`;
2534
+ const walletId = generateUUID();
2535
+ const credential = {
2536
+ source: metadata.id,
2537
+ credential: {
2538
+ apiKey: formValues.api_key?.trim() || "0",
2539
+ secret: formValues.secret_key?.trim() || "0",
2540
+ accountName: formValues.account_name?.trim() || "0",
2541
+ address: formValues.address?.trim() || "0",
2542
+ password: formValues.password?.trim() || "0",
2543
+ userId: user?.user?.uid || "0",
2544
+ projectId: metadata?.projectId || "0",
2545
+ privateKey: "0",
2546
+ alias,
2547
+ walletId,
2548
+ exchange: metadata.id
2549
+ }
2550
+ };
2551
+ const testResult = await testCredentials(linkToken, { ...credential });
2552
+ if (!testResult?.value?.status) {
2553
+ setErrorMessage(
2554
+ testResult?.value?.message || "Credentials are invalid"
2555
+ );
2556
+ return;
2557
+ }
2558
+ const data = {
2559
+ alias,
2560
+ exchange: metadata.id.toLowerCase(),
2561
+ id: metadata.id,
2562
+ public_name: metadata.public_name,
2563
+ sync_time: (/* @__PURE__ */ new Date()).getTime(),
2564
+ fetchAll: true,
2565
+ logo: metadata.logo || null,
2566
+ startTime: null,
2567
+ endTime: null,
2568
+ uid: user?.user?.uid || "",
2569
+ walletId,
2570
+ clientMetadata: {
2571
+ clientId,
2572
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2573
+ },
2574
+ metadata: {
2575
+ environment: "sandbox"
2576
+ },
2577
+ addedOn: (/* @__PURE__ */ new Date()).getTime(),
2578
+ default_chain: metadata.id,
2579
+ default_chain_logo: null,
2580
+ type: metadata.type,
2581
+ isNftSupported: metadata.isEvmWallet || metadata.nftSupport || false
2582
+ };
2583
+ if (metadata.community_id) {
2584
+ data.chainId = Number(metadata.community_id);
2585
+ }
2586
+ if (formValues.address) data.address = formValues.address;
2587
+ if (formValues.account_name) data.accountName = formValues.account_name;
2588
+ if (formValues.api_key) data.apiKey = formValues.api_key;
2589
+ if (formValues.secret_key) data.secret = formValues.secret_key;
2590
+ if (formValues.password) data.password = formValues.password;
2591
+ integrationsToAdd.push(data);
2592
+ }
2593
+ if (integrationsToAdd.length > 0) {
2594
+ onAddHandle(integrationsToAdd);
2595
+ setFormErrors({});
2596
+ setFormValues({
2597
+ address: "",
2598
+ account_name: "",
2599
+ api_key: "",
2600
+ secret_key: "",
2601
+ password: ""
2602
+ });
2603
+ setErrorMessage("");
2604
+ } else {
2605
+ setErrorMessage("No integrations could be added. Please try again.");
2606
+ }
2607
+ } catch (error) {
2608
+ const err = error;
2609
+ console.error(error);
2610
+ setErrorMessage(
2611
+ err?.response?.data?.message || "Failed to add integration"
2612
+ );
2613
+ } finally {
2614
+ setIsLoading(false);
2615
+ }
2616
+ };
2617
+ const hasNoFields = !metadata.password && !metadata.secret_key && !metadata.api_key && !metadata.address && !metadata.account_name;
2618
+ const shouldShowFormFields = metadata.password || metadata.secret_key || metadata.api_key || metadata.address || metadata.account_name;
2619
+ const renderLogo = () => metadata.logo ? /* @__PURE__ */ import_react25.default.createElement(
2620
+ import_react_native11.Image,
2621
+ {
2622
+ source: { uri: metadata.logo },
2623
+ style: styles10.logo,
2624
+ resizeMode: "contain"
2625
+ }
2626
+ ) : /* @__PURE__ */ import_react25.default.createElement(
2627
+ import_react_native11.View,
2628
+ {
2629
+ style: [
2630
+ styles10.logoPlaceholder,
2631
+ { backgroundColor: theme.colors.surface }
2632
+ ]
2633
+ },
2634
+ /* @__PURE__ */ import_react25.default.createElement(import_react_native11.Text, { style: { color: theme.colors.text } }, metadata.name?.charAt(0) || "?")
2635
+ );
2636
+ const renderInput = (key, props) => /* @__PURE__ */ import_react25.default.createElement(
2637
+ Input,
2638
+ {
2639
+ placeholder: props.placeholder,
2640
+ value: formValues[key],
2641
+ onChangeText: (text) => setFormValues((prev) => ({ ...prev, [key]: text })),
2642
+ error: formErrors[key],
2643
+ autoCapitalize: props.autoCapitalize,
2644
+ autoCorrect: props.autoCorrect,
2645
+ secureTextEntry: props.secureTextEntry
2646
+ }
2647
+ );
2648
+ const renderErrorAlert = () => errorMessage ? /* @__PURE__ */ import_react25.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ import_react25.default.createElement(AlertDescription, null, errorMessage)) : null;
2649
+ const renderChainSelection = () => userUsedChains.length > 0 && formValues.address && /* @__PURE__ */ import_react25.default.createElement(import_react_native11.View, { style: styles10.chainSelection }, /* @__PURE__ */ import_react25.default.createElement(import_react_native11.Text, { style: [styles10.chainTitle, { color: theme.colors.text }] }, "Select Chains to Add:"), /* @__PURE__ */ import_react25.default.createElement(import_react_native11.ScrollView, { contentContainerStyle: styles10.scrollViewContent }, /* @__PURE__ */ import_react25.default.createElement(import_react_native11.View, { style: styles10.chainChips }, userUsedChains.map((chain) => {
2650
+ const isSelected = selectedChains.has(chain.id);
2651
+ const hasError = chainErrors[chain.id];
2652
+ return /* @__PURE__ */ import_react25.default.createElement(
2653
+ import_react_native11.TouchableOpacity,
2654
+ {
2655
+ onPress: () => toggleChainSelection(chain.id),
2656
+ style: styles10.chainButton,
2657
+ key: chain.id
2658
+ },
2659
+ /* @__PURE__ */ import_react25.default.createElement(
2660
+ import_react_native11.View,
2661
+ {
2662
+ style: [
2663
+ styles10.chainChip,
2664
+ {
2665
+ backgroundColor: hasError ? theme.colors.errorLight : isSelected ? theme.colors.primary + "20" : theme.colors.surface,
2666
+ borderColor: hasError ? theme.colors.error : isSelected ? theme.colors.primary : theme.colors.border
2667
+ }
2668
+ ]
2669
+ },
2670
+ /* @__PURE__ */ import_react25.default.createElement(
2671
+ import_react_native11.Text,
2672
+ {
2673
+ style: [
2674
+ styles10.chainName,
2675
+ {
2676
+ color: hasError ? theme.colors.error : isSelected ? theme.colors.primary : theme.colors.text
2677
+ }
2678
+ ]
2679
+ },
2680
+ chain.name
2681
+ ),
2682
+ isSelected ? /* @__PURE__ */ import_react25.default.createElement(
2683
+ CloseIcon,
2684
+ {
2685
+ size: 12,
2686
+ color: hasError ? theme.colors.error : theme.colors.primary
2687
+ }
2688
+ ) : /* @__PURE__ */ import_react25.default.createElement(PlusIcon, { size: 12, color: theme.colors.textSecondary })
2689
+ )
2690
+ );
2691
+ }))), Object.keys(chainErrors).length > 0 && /* @__PURE__ */ import_react25.default.createElement(import_react_native11.View, { style: styles10.chainErrorsContainer }, /* @__PURE__ */ import_react25.default.createElement(
2692
+ import_react_native11.Text,
2693
+ {
2694
+ style: [styles10.chainErrorsTitle, { color: theme.colors.error }]
2695
+ },
2696
+ "Errors:"
2697
+ ), Object.entries(chainErrors).map(([chainId, error]) => {
2698
+ const chain = userUsedChains.find((c) => c.id === chainId);
2699
+ return /* @__PURE__ */ import_react25.default.createElement(
2700
+ import_react_native11.Text,
2701
+ {
2702
+ key: chainId,
2703
+ style: [styles10.chainErrorItem, { color: theme.colors.error }]
2704
+ },
2705
+ "\u2022 ",
2706
+ chain?.name,
2707
+ ": ",
2708
+ error
2709
+ );
2710
+ })));
2711
+ const renderFormBlock = () => /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, /* @__PURE__ */ import_react25.default.createElement(import_react_native11.View, { style: styles10.header }, renderLogo(), /* @__PURE__ */ import_react25.default.createElement(import_react_native11.Text, { style: [styles10.name, { color: theme.colors.text }] }, metadata.name)), renderErrorAlert(), shouldShowFormFields && /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, metadata.address && /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, renderInput("address", {
2712
+ placeholder: "Enter address",
2713
+ autoCapitalize: "none",
2714
+ autoCorrect: false
2715
+ }), renderChainSelection()), metadata.account_name && renderInput("account_name", { placeholder: "Enter account name" }), metadata.api_key && renderInput("api_key", {
2716
+ placeholder: "Enter API key",
2717
+ autoCapitalize: "none",
2718
+ autoCorrect: false
2719
+ }), metadata.secret_key && renderInput("secret_key", {
2720
+ placeholder: "Enter API secret",
2721
+ autoCapitalize: "none",
2722
+ autoCorrect: false,
2723
+ secureTextEntry: true
2724
+ }), metadata.password && renderInput("password", {
2725
+ placeholder: "Enter Password",
2726
+ secureTextEntry: true
2727
+ })), hasNoFields && !metadata?.isWalletConnectSupported && /* @__PURE__ */ import_react25.default.createElement(Alert, { variant: "default", style: { marginTop: 12 } }, /* @__PURE__ */ import_react25.default.createElement(AlertDescription, null, "This integration is not supported here yet \u2014 try using it through our Kryptos Platform.")));
2728
+ const addIntegrationLabel = formValues.address && userUsedChains.length > 0 && selectedChains.size > 0 ? `Add ${selectedChains.size} Chain${selectedChains.size !== 1 ? "s" : ""}` : "Add Integration";
2729
+ return /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, !metadata?.isWalletConnectSupported ? /* @__PURE__ */ import_react25.default.createElement(Modal, { isOpen: open, onClose: handleClose, size: "full" }, /* @__PURE__ */ import_react25.default.createElement(ModalHeader, { onClose: handleClose }, /* @__PURE__ */ import_react25.default.createElement(import_react_native11.View, { style: styles10.headerContent }, /* @__PURE__ */ import_react25.default.createElement(
2730
+ import_react_native11.TouchableOpacity,
2731
+ {
2732
+ onPress: () => setAddIntegrationMode(null),
2733
+ style: styles10.backButton
2734
+ },
2735
+ /* @__PURE__ */ import_react25.default.createElement(ArrowLeftIcon, { size: 20, color: theme.colors.text })
2736
+ ), /* @__PURE__ */ import_react25.default.createElement(import_react_native11.Text, { style: [styles10.headerTitle, { color: theme.colors.text }] }, "Integration"))), /* @__PURE__ */ import_react25.default.createElement(ModalBody, { scrollable: false, style: styles10.contentContainer }, renderFormBlock()), !hasNoFields && /* @__PURE__ */ import_react25.default.createElement(ModalFooter, null, /* @__PURE__ */ import_react25.default.createElement(
2737
+ Button,
2738
+ {
2739
+ variant: "outline",
2740
+ size: "lg",
2741
+ onPress: handleSubmit,
2742
+ loading: isLoading,
2743
+ disabled: isLoading || !!formValues.address && userUsedChains.length > 0 && selectedChains.size === 0,
2744
+ style: styles10.button
2745
+ },
2746
+ addIntegrationLabel
2747
+ ))) : /* @__PURE__ */ import_react25.default.createElement(
2748
+ WalletConnectComponent,
2749
+ {
2750
+ integration: metadata,
2751
+ onClose: handleClose,
2752
+ onAddHandle,
2753
+ modalOpen: open,
2754
+ setAddIntegrationMode,
2755
+ handleClose
2756
+ }
2757
+ ));
2758
+ };
2759
+ var styles10 = import_react_native11.StyleSheet.create({
2760
+ scrollViewContent: {
2761
+ flexGrow: 1,
2762
+ paddingBottom: 100
2763
+ },
2764
+ headerContent: {
2765
+ flexDirection: "row",
2766
+ alignItems: "center"
2767
+ },
2768
+ backButton: {
2769
+ padding: 4,
2770
+ // theme.spacing.xs
2771
+ marginRight: 8
2772
+ // theme.spacing.sm
2773
+ },
2774
+ headerTitle: {
2775
+ fontSize: 16,
2776
+ // theme.fontSize.lg
2777
+ fontWeight: "600"
2778
+ },
2779
+ container: {
2780
+ flex: 1
2781
+ },
2782
+ contentContainer: {
2783
+ padding: 20,
2784
+ // theme.spacing.xl
2785
+ paddingBottom: 40,
2786
+ width: "100%",
2787
+ overflow: "hidden",
2788
+ alignSelf: "center",
2789
+ flexDirection: "column",
2790
+ flex: 1
2791
+ },
2792
+ header: {
2793
+ flexDirection: "row",
2794
+ justifyContent: "center",
2795
+ alignItems: "center",
2796
+ marginBottom: 16
2797
+ // theme.spacing.lg
2798
+ },
2799
+ logo: {
2800
+ width: 32,
2801
+ height: 32,
2802
+ borderRadius: 8
2803
+ // theme.borderRadius.sm
2804
+ },
2805
+ logoPlaceholder: {
2806
+ width: 32,
2807
+ height: 32,
2808
+ borderRadius: 8,
2809
+ // theme.borderRadius.sm
2810
+ alignItems: "center",
2811
+ justifyContent: "center"
2812
+ },
2813
+ name: {
2814
+ fontSize: 16,
2815
+ // theme.fontSize.lg
2816
+ fontWeight: "600",
2817
+ marginLeft: 12,
2818
+ // theme.spacing.md
2819
+ textTransform: "capitalize"
2820
+ },
2821
+ chainSelection: {
2822
+ marginBottom: 16
2823
+ // theme.spacing.lg
2824
+ },
2825
+ chainTitle: {
2826
+ fontSize: 14,
2827
+ // theme.fontSize.md
2828
+ fontWeight: "500",
2829
+ marginBottom: 12
2830
+ // theme.spacing.md - consistent label spacing
2831
+ },
2832
+ chainChips: {
2833
+ flexDirection: "row",
2834
+ flexWrap: "wrap",
2835
+ gap: 6
2836
+ // theme.spacing.sm
2837
+ },
2838
+ chainChip: {
2839
+ flexDirection: "row",
2840
+ alignItems: "center",
2841
+ paddingHorizontal: 8,
2842
+ // theme.spacing.sm
2843
+ paddingVertical: 5,
2844
+ // theme.spacing.xs
2845
+ borderRadius: 12,
2846
+ // theme.borderRadius.md
2847
+ borderWidth: 1
2848
+ },
2849
+ chainName: {
2850
+ fontSize: 12,
2851
+ fontWeight: "500",
2852
+ marginRight: 6
2853
+ // theme.spacing.xs
2854
+ },
2855
+ chainButton: {
2856
+ padding: 2
2857
+ // theme.spacing.xs
2858
+ },
2859
+ chainErrorsContainer: {
2860
+ marginTop: 12
2861
+ // theme.spacing.md - consistent spacing
2862
+ },
2863
+ chainErrorsTitle: {
2864
+ fontSize: 13,
2865
+ fontWeight: "500",
2866
+ marginBottom: 4
2867
+ // theme.spacing.xs
2868
+ },
2869
+ chainErrorItem: {
2870
+ fontSize: 12,
2871
+ // theme.fontSize.sm
2872
+ marginBottom: 4
2873
+ // theme.spacing.xs
2874
+ },
2875
+ button: {
2876
+ width: "100%",
2877
+ marginTop: 16
2878
+ // theme.spacing.lg - consistent button spacing
2879
+ }
2880
+ });
2881
+
2882
+ // src/components/SkeletonItem.tsx
2883
+ var import_react26 = __toESM(require("react"));
2884
+ var import_react_native12 = require("react-native");
2885
+ var SkeletonItem = () => {
2886
+ const opacity = (0, import_react26.useRef)(new import_react_native12.Animated.Value(0.3)).current;
2887
+ (0, import_react26.useEffect)(() => {
2888
+ import_react_native12.Animated.loop(
2889
+ import_react_native12.Animated.sequence([
2890
+ import_react_native12.Animated.timing(opacity, {
2891
+ toValue: 1,
2892
+ duration: 600,
2893
+ useNativeDriver: true
2894
+ }),
2895
+ import_react_native12.Animated.timing(opacity, {
2896
+ toValue: 0.3,
2897
+ duration: 600,
2898
+ useNativeDriver: true
2899
+ })
2900
+ ])
2901
+ ).start();
2902
+ }, []);
2903
+ return /* @__PURE__ */ import_react26.default.createElement(import_react_native12.Animated.View, { style: [styles11.row, { opacity }] }, /* @__PURE__ */ import_react26.default.createElement(import_react_native12.View, { style: styles11.iconCircle }), /* @__PURE__ */ import_react26.default.createElement(import_react_native12.View, { style: styles11.textBlock }, /* @__PURE__ */ import_react26.default.createElement(import_react_native12.View, { style: styles11.lineLong }), /* @__PURE__ */ import_react26.default.createElement(import_react_native12.View, { style: styles11.lineShort })));
2904
+ };
2905
+ var styles11 = import_react_native12.StyleSheet.create({
2906
+ row: {
2907
+ flexDirection: "row",
2908
+ alignItems: "center",
2909
+ paddingVertical: 16
2910
+ },
2911
+ iconCircle: {
2912
+ width: 45,
2913
+ height: 45,
2914
+ borderRadius: 22.5,
2915
+ backgroundColor: "#E5E5E5"
2916
+ },
2917
+ textBlock: {
2918
+ marginLeft: 12,
2919
+ flex: 1
2920
+ },
2921
+ lineShort: {
2922
+ width: "50%",
2923
+ height: 14,
2924
+ borderRadius: 6,
2925
+ backgroundColor: "#E5E5E5"
2926
+ },
2927
+ lineLong: {
2928
+ marginBottom: 6,
2929
+ width: "100%",
2930
+ height: 14,
2931
+ borderRadius: 6,
2932
+ backgroundColor: "#E5E5E5"
2933
+ }
2934
+ });
2935
+ var SkeletonItem_default = SkeletonItem;
2936
+
2937
+ // src/molecules/Integration.tsx
2938
+ var Integration = ({
2939
+ open,
2940
+ onSuccess,
2941
+ onClose
2942
+ }) => {
2943
+ const { appName, linkToken } = useKryptosConnect();
2944
+ const theme = useTheme();
2945
+ const [addIntegrationMode, setAddIntegrationMode] = import_react27.default.useState(null);
2946
+ const [query, setQuery] = import_react27.default.useState("");
2947
+ const [supportedProviders, setSupportedProviders] = import_react27.default.useState([]);
2948
+ const [addedIntegrations, setAddedIntegrations] = import_react27.default.useState([]);
2949
+ const [existingIntegrations, setExistingIntegrations] = import_react27.default.useState([]);
2950
+ const [isLoading, setIsLoading] = import_react27.default.useState(false);
2951
+ const [errorMessage, setErrorMessage] = import_react27.default.useState("");
2952
+ const handleClose = () => {
2953
+ onClose();
2954
+ };
2955
+ const fetchExistingIntegrations = async () => {
2956
+ try {
2957
+ const res = await getUserIntegrations(linkToken);
2958
+ setExistingIntegrations(res?.integrations || []);
2959
+ } catch (error) {
2960
+ const err = error;
2961
+ setErrorMessage(
2962
+ err?.response?.data?.message || "Failed to fetch existing integrations"
2963
+ );
2964
+ console.error(error);
2965
+ }
2966
+ };
2967
+ const fetchSupportedProviders = async () => {
2968
+ try {
2969
+ setIsLoading(true);
2970
+ const res = await getSupportedProviders(linkToken);
2971
+ setSupportedProviders(res?.providers || []);
2972
+ } catch (error) {
2973
+ const err = error;
2974
+ setErrorMessage(
2975
+ err?.response?.data?.message || "Failed to fetch supported providers"
2976
+ );
2977
+ console.error(error);
2978
+ } finally {
2979
+ setIsLoading(false);
2980
+ }
2981
+ };
2982
+ import_react27.default.useEffect(() => {
2983
+ if (linkToken) {
2984
+ fetchSupportedProviders();
2985
+ fetchExistingIntegrations();
2986
+ }
2987
+ }, [linkToken]);
2988
+ const isIntegrationAdded = import_react27.default.useCallback(
2989
+ (publicName) => {
2990
+ const integrations = [...addedIntegrations, ...existingIntegrations];
2991
+ return integrations.some(
2992
+ (integration) => integration.public_name === publicName
2993
+ );
2994
+ },
2995
+ [addedIntegrations, existingIntegrations]
2996
+ );
2997
+ const getIntegrationCount = import_react27.default.useCallback(
2998
+ (publicName) => {
2999
+ const integrations = [...addedIntegrations, ...existingIntegrations];
3000
+ return integrations.filter(
3001
+ (integration) => integration.public_name === publicName
3002
+ ).length;
3003
+ },
3004
+ [addedIntegrations, existingIntegrations]
3005
+ );
3006
+ const filteredResults = import_react27.default.useMemo(() => {
3007
+ if (query) {
3008
+ const lowerQuery = query.toLowerCase();
3009
+ return supportedProviders.filter(
3010
+ (provider) => provider.name?.toLowerCase().includes(lowerQuery) || provider.public_name?.toLowerCase().includes(lowerQuery) || provider.id?.toLowerCase().includes(lowerQuery)
3011
+ );
3012
+ }
3013
+ return [...supportedProviders].sort((a, b) => {
3014
+ const countA = getIntegrationCount(a.public_name);
3015
+ const countB = getIntegrationCount(b.public_name);
3016
+ if (countB !== countA) {
3017
+ return countB - countA;
3018
+ }
3019
+ return a.name.localeCompare(b.name);
3020
+ });
3021
+ }, [query, supportedProviders, getIntegrationCount]);
3022
+ const handleAddIntegration = async () => {
3023
+ try {
3024
+ setIsLoading(true);
3025
+ const integrations = [...addedIntegrations, ...existingIntegrations];
3026
+ if (integrations.length === 0) {
3027
+ setErrorMessage("Please add at least one integration");
3028
+ } else if (addedIntegrations.length === 0) {
3029
+ onSuccess();
3030
+ } else {
3031
+ await addUserIntegration(linkToken, addedIntegrations);
3032
+ onSuccess();
3033
+ }
3034
+ } catch (error) {
3035
+ const err = error;
3036
+ console.error(error);
3037
+ setErrorMessage(
3038
+ err?.response?.data?.message || "Failed to add integrations"
3039
+ );
3040
+ } finally {
3041
+ setIsLoading(false);
3042
+ }
3043
+ };
3044
+ const renderProviderItem = ({ item }) => /* @__PURE__ */ import_react27.default.createElement(
3045
+ import_react_native13.TouchableOpacity,
3046
+ {
3047
+ style: [
3048
+ styles12.providerItem,
3049
+ {
3050
+ backgroundColor: theme.colors.surface,
3051
+ borderColor: theme.colors.border
3052
+ }
3053
+ ],
3054
+ onPress: () => setAddIntegrationMode(item),
3055
+ activeOpacity: 0.7
3056
+ },
3057
+ /* @__PURE__ */ import_react27.default.createElement(import_react_native13.View, { style: styles12.providerInfo }, item?.logo ? /* @__PURE__ */ import_react27.default.createElement(
3058
+ import_react_native13.Image,
3059
+ {
3060
+ source: { uri: item?.logo },
3061
+ style: styles12.providerLogo,
3062
+ resizeMode: "contain"
3063
+ }
3064
+ ) : /* @__PURE__ */ import_react27.default.createElement(
3065
+ import_react_native13.View,
3066
+ {
3067
+ style: [
3068
+ styles12.providerLogoPlaceholder,
3069
+ { backgroundColor: theme.colors.surfaceSecondary }
3070
+ ]
3071
+ },
3072
+ /* @__PURE__ */ import_react27.default.createElement(import_react_native13.Text, { style: { color: theme.colors.text } }, item?.name?.charAt(0) || "?")
3073
+ ), /* @__PURE__ */ import_react27.default.createElement(import_react_native13.Text, { style: [styles12.providerName, { color: theme.colors.text }] }, item?.name + "\u200B")),
3074
+ isIntegrationAdded(item?.public_name) && /* @__PURE__ */ import_react27.default.createElement(import_react_native13.View, { style: styles12.providerStatus }, /* @__PURE__ */ import_react27.default.createElement(CheckCircleIcon, { size: 18, color: theme.colors.success }), /* @__PURE__ */ import_react27.default.createElement(
3075
+ import_react_native13.Text,
3076
+ {
3077
+ style: [
3078
+ styles12.providerCount,
3079
+ { color: theme.colors.textSecondary }
3080
+ ]
3081
+ },
3082
+ getIntegrationCount(item?.public_name)
3083
+ ))
3084
+ );
3085
+ const renderSkeletonItem = () => /* @__PURE__ */ import_react27.default.createElement(SkeletonItem_default, null);
3086
+ return /* @__PURE__ */ import_react27.default.createElement(import_react27.default.Fragment, null, !addIntegrationMode ? /* @__PURE__ */ import_react27.default.createElement(Modal, { isOpen: open, onClose: handleClose, size: "full" }, /* @__PURE__ */ import_react27.default.createElement(ModalHeader, { onClose: handleClose }, /* @__PURE__ */ import_react27.default.createElement(import_react_native13.View, { style: styles12.headerContent }, addIntegrationMode && /* @__PURE__ */ import_react27.default.createElement(
3087
+ import_react_native13.TouchableOpacity,
3088
+ {
3089
+ onPress: () => setAddIntegrationMode(null),
3090
+ style: styles12.backButton
3091
+ },
3092
+ /* @__PURE__ */ import_react27.default.createElement(ArrowLeftIcon, { size: 20, color: theme.colors.text })
3093
+ ), /* @__PURE__ */ import_react27.default.createElement(import_react_native13.Text, { style: [styles12.headerTitle, { color: theme.colors.text }] }, "Integration"))), /* @__PURE__ */ import_react27.default.createElement(ModalBody, { scrollable: false, style: styles12.noPadding }, /* @__PURE__ */ import_react27.default.createElement(import_react_native13.View, { style: styles12.container }, /* @__PURE__ */ import_react27.default.createElement(import_react_native13.View, { style: styles12.headerSection }, /* @__PURE__ */ import_react27.default.createElement(ConnectLogo, null), /* @__PURE__ */ import_react27.default.createElement(import_react_native13.Text, { style: [styles12.title, { color: theme.colors.text }] }, "Connect ", appName, " to your Kryptos account")), /* @__PURE__ */ import_react27.default.createElement(
3094
+ import_react_native13.FlatList,
3095
+ {
3096
+ data: isLoading ? Array.from({ length: 8 }, (_, i) => ({
3097
+ id: `skeleton-${i}`,
3098
+ name: "",
3099
+ public_name: "",
3100
+ type: ""
3101
+ })) : filteredResults,
3102
+ keyExtractor: (item, index) => isLoading ? item.id : `provider-${item.id}-${index}`,
3103
+ renderItem: isLoading ? renderSkeletonItem : renderProviderItem,
3104
+ style: styles12.list,
3105
+ contentContainerStyle: [
3106
+ styles12.listContent,
3107
+ { paddingHorizontal: theme.spacing.xl }
3108
+ ],
3109
+ showsVerticalScrollIndicator: false,
3110
+ ListHeaderComponent: /* @__PURE__ */ import_react27.default.createElement(
3111
+ import_react_native13.View,
3112
+ {
3113
+ style: {
3114
+ paddingVertical: theme.spacing.sm + 2,
3115
+ backgroundColor: theme.colors.background,
3116
+ zIndex: 10
3117
+ }
3118
+ },
3119
+ /* @__PURE__ */ import_react27.default.createElement(
3120
+ Input,
3121
+ {
3122
+ value: query,
3123
+ onChangeText: setQuery,
3124
+ placeholder: "Search Integrations...",
3125
+ containerStyle: styles12.searchInput
3126
+ }
3127
+ )
3128
+ ),
3129
+ stickyHeaderIndices: [0],
3130
+ ListEmptyComponent: /* @__PURE__ */ import_react27.default.createElement(import_react_native13.View, { style: styles12.emptyContainer }, !isLoading && /* @__PURE__ */ import_react27.default.createElement(
3131
+ import_react_native13.Text,
3132
+ {
3133
+ style: [
3134
+ styles12.emptyText,
3135
+ { color: theme.colors.textSecondary }
3136
+ ]
3137
+ },
3138
+ query ? "No search results found" : "No supported integrations found"
3139
+ ))
3140
+ }
3141
+ ), errorMessage && /* @__PURE__ */ import_react27.default.createElement(import_react_native13.View, { style: styles12.errorContainer }, /* @__PURE__ */ import_react27.default.createElement(Alert, { variant: "destructive", style: styles12.errorAlert }, /* @__PURE__ */ import_react27.default.createElement(AlertDescription, null, errorMessage))))), /* @__PURE__ */ import_react27.default.createElement(ModalFooter, null, /* @__PURE__ */ import_react27.default.createElement(
3142
+ Button,
3143
+ {
3144
+ variant: "outline",
3145
+ size: "lg",
3146
+ onPress: handleAddIntegration,
3147
+ loading: isLoading,
3148
+ disabled: isLoading,
3149
+ style: styles12.continueButton
3150
+ },
3151
+ "Continue"
3152
+ ))) : /* @__PURE__ */ import_react27.default.createElement(
3153
+ IntegrationForm,
3154
+ {
3155
+ metadata: addIntegrationMode,
3156
+ onAddHandle: (integrations) => {
3157
+ setAddedIntegrations((prev) => [...prev, ...integrations]);
3158
+ setAddIntegrationMode(null);
3159
+ },
3160
+ open: !!addIntegrationMode,
3161
+ setAddIntegrationMode,
3162
+ handleClose
3163
+ }
3164
+ ));
3165
+ };
3166
+ var styles12 = import_react_native13.StyleSheet.create({
3167
+ headerContent: {
3168
+ flexDirection: "row",
3169
+ alignItems: "center"
3170
+ },
3171
+ backButton: {
3172
+ padding: 4,
3173
+ // theme.spacing.xs
3174
+ marginRight: 8
3175
+ // theme.spacing.sm
3176
+ },
3177
+ headerTitle: {
3178
+ fontSize: 16,
3179
+ // theme.fontSize.lg
3180
+ fontWeight: "600"
3181
+ },
3182
+ noPadding: {
3183
+ padding: 0
3184
+ },
3185
+ container: {
3186
+ flex: 1
3187
+ },
3188
+ headerSection: {
3189
+ paddingHorizontal: 20,
3190
+ // theme.spacing.xl
3191
+ paddingTop: 10
3192
+ // theme.spacing.xl
3193
+ },
3194
+ title: {
3195
+ fontSize: 16,
3196
+ // theme.fontSize.lg
3197
+ fontWeight: "600",
3198
+ textAlign: "center",
3199
+ marginBottom: 10
3200
+ // theme.spacing.lg
3201
+ },
3202
+ searchInput: {},
3203
+ list: {
3204
+ flex: 1,
3205
+ minHeight: 200
3206
+ },
3207
+ listContent: {
3208
+ paddingBottom: 12
3209
+ // theme.spacing.md - consistent padding
3210
+ },
3211
+ providerItem: {
3212
+ flexDirection: "row",
3213
+ alignItems: "center",
3214
+ justifyContent: "space-between",
3215
+ padding: 12,
3216
+ // theme.spacing.md
3217
+ borderRadius: 12,
3218
+ // theme.borderRadius.md
3219
+ borderWidth: 1,
3220
+ marginBottom: 12
3221
+ // theme.spacing.md - consistent list item spacing
3222
+ },
3223
+ providerInfo: {
3224
+ flexDirection: "row",
3225
+ alignItems: "center",
3226
+ flex: 1
3227
+ },
3228
+ providerLogo: {
3229
+ width: 32,
3230
+ height: 32,
3231
+ borderRadius: 8
3232
+ // theme.borderRadius.sm
3233
+ },
3234
+ providerLogoPlaceholder: {
3235
+ width: 32,
3236
+ height: 32,
3237
+ borderRadius: 8,
3238
+ // theme.borderRadius.sm
3239
+ alignItems: "center",
3240
+ justifyContent: "center"
3241
+ },
3242
+ providerName: {
3243
+ fontSize: 14,
3244
+ // theme.fontSize.md
3245
+ fontWeight: "500",
3246
+ marginLeft: 12,
3247
+ // theme.spacing.md
3248
+ textTransform: "capitalize"
3249
+ },
3250
+ providerStatus: {
3251
+ flexDirection: "row",
3252
+ alignItems: "center"
3253
+ },
3254
+ providerCount: {
3255
+ fontSize: 14,
3256
+ // theme.fontSize.md
3257
+ marginLeft: 4
3258
+ // theme.spacing.xs
3259
+ },
3260
+ emptyContainer: {
3261
+ flex: 1,
3262
+ alignItems: "center",
3263
+ justifyContent: "center",
3264
+ paddingVertical: 40
3265
+ // theme.spacing.xxxl + sm
3266
+ },
3267
+ emptyText: {
3268
+ fontSize: 14
3269
+ // theme.fontSize.md
3270
+ },
3271
+ continueButton: {
3272
+ width: "100%"
3273
+ },
3274
+ errorContainer: {
3275
+ paddingHorizontal: 20
3276
+ // theme.spacing.xl
3277
+ },
3278
+ errorAlert: {
3279
+ marginTop: 16
3280
+ // theme.spacing.lg - consistent alert spacing
3281
+ }
3282
+ });
3283
+
3284
+ // src/molecules/OTPVerify.tsx
3285
+ var import_react28 = __toESM(require("react"));
3286
+ var import_react_native14 = require("react-native");
3287
+ var OTPVerify = ({
3288
+ open,
3289
+ onSuccess,
3290
+ onClose
3291
+ }) => {
3292
+ const theme = useTheme();
3293
+ const [otp, setOtp] = import_react28.default.useState("");
3294
+ const { linkToken, clientId, email, setUser } = useKryptosConnect();
3295
+ const [isLoading, setIsLoading] = import_react28.default.useState(false);
3296
+ const [isResending, setIsResending] = import_react28.default.useState(false);
3297
+ const [resendCooldown, setResendCooldown] = import_react28.default.useState(0);
3298
+ const [errorMessage, setErrorMessage] = import_react28.default.useState("");
3299
+ const [successMessage, setSuccessMessage] = import_react28.default.useState("");
3300
+ const handleSubmit = async () => {
3301
+ if (otp.length !== 6) return;
3302
+ setIsLoading(true);
3303
+ setErrorMessage("");
3304
+ try {
3305
+ const res = await loginWithOtp(linkToken, email, otp, clientId);
3306
+ setUser(res);
3307
+ onSuccess();
3308
+ } catch (error) {
3309
+ const err = error;
3310
+ setErrorMessage(
3311
+ err?.response?.data?.message || "Failed to login with OTP"
3312
+ );
3313
+ console.error(error);
3314
+ } finally {
3315
+ setIsLoading(false);
3316
+ }
3317
+ };
3318
+ const handleOtpComplete = (value) => {
3319
+ setOtp(value);
3320
+ };
3321
+ const handleResendOtp = async () => {
3322
+ if (resendCooldown > 0 || isResending) return;
3323
+ try {
3324
+ setIsResending(true);
3325
+ setErrorMessage("");
3326
+ setSuccessMessage("");
3327
+ await sendEmailOtp(linkToken, email, clientId);
3328
+ setSuccessMessage("OTP sent successfully!");
3329
+ setResendCooldown(60);
3330
+ setOtp("");
3331
+ setTimeout(() => {
3332
+ setSuccessMessage("");
3333
+ }, 3e3);
3334
+ } catch (error) {
3335
+ const err = error;
3336
+ setErrorMessage(
3337
+ err?.response?.data?.message || "Failed to resend OTP. Please try again."
3338
+ );
3339
+ console.error(error);
3340
+ } finally {
3341
+ setIsResending(false);
3342
+ }
3343
+ };
3344
+ const handleClose = () => {
3345
+ onClose();
3346
+ setErrorMessage("");
3347
+ setSuccessMessage("");
3348
+ setOtp("");
3349
+ };
3350
+ import_react28.default.useEffect(() => {
3351
+ if (resendCooldown > 0) {
3352
+ const timer = setTimeout(() => {
3353
+ setResendCooldown(resendCooldown - 1);
3354
+ }, 1e3);
3355
+ return () => clearTimeout(timer);
3356
+ }
3357
+ return void 0;
3358
+ }, [resendCooldown]);
3359
+ return /* @__PURE__ */ import_react28.default.createElement(Modal, { isOpen: open, onClose: handleClose, size: "lg" }, /* @__PURE__ */ import_react28.default.createElement(ModalHeader, { onClose: handleClose }, /* @__PURE__ */ import_react28.default.createElement(import_react_native14.View, { style: styles13.headerContent }, /* @__PURE__ */ import_react28.default.createElement(import_react_native14.Text, { style: [styles13.headerTitle, { color: theme.colors.text }] }, "OTP Verification"))), /* @__PURE__ */ import_react28.default.createElement(ModalBody, null, /* @__PURE__ */ import_react28.default.createElement(import_react_native14.View, { style: styles13.container }, /* @__PURE__ */ import_react28.default.createElement(ConnectLogo, null), /* @__PURE__ */ import_react28.default.createElement(import_react_native14.View, { style: styles13.emailInfo }, /* @__PURE__ */ import_react28.default.createElement(
3360
+ import_react_native14.Text,
3361
+ {
3362
+ style: [styles13.infoText, { color: theme.colors.textSecondary }]
3363
+ },
3364
+ "We have sent a verification code to"
3365
+ ), /* @__PURE__ */ import_react28.default.createElement(import_react_native14.Text, { style: [styles13.emailText, { color: theme.colors.text }] }, email)), /* @__PURE__ */ import_react28.default.createElement(OTP, { onComplete: handleOtpComplete, length: 6, setErrorMessage }), errorMessage ? /* @__PURE__ */ import_react28.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ import_react28.default.createElement(AlertDescription, null, errorMessage)) : null, successMessage ? /* @__PURE__ */ import_react28.default.createElement(Alert, { variant: "default" }, /* @__PURE__ */ import_react28.default.createElement(AlertDescription, null, successMessage)) : null, /* @__PURE__ */ import_react28.default.createElement(
3366
+ Button,
3367
+ {
3368
+ variant: "outline",
3369
+ size: "lg",
3370
+ onPress: handleSubmit,
3371
+ loading: isLoading,
3372
+ disabled: otp.length !== 6 || isLoading,
3373
+ style: styles13.button
3374
+ },
3375
+ "Continue"
3376
+ ), /* @__PURE__ */ import_react28.default.createElement(
3377
+ import_react_native14.TouchableOpacity,
3378
+ {
3379
+ onPress: handleResendOtp,
3380
+ disabled: resendCooldown > 0 || isResending,
3381
+ style: styles13.resendContainer
3382
+ },
3383
+ isResending ? /* @__PURE__ */ import_react28.default.createElement(import_react_native14.View, { style: styles13.resendLoading }, /* @__PURE__ */ import_react28.default.createElement(LoaderIcon, { size: 16, color: theme.colors.primary }), /* @__PURE__ */ import_react28.default.createElement(
3384
+ import_react_native14.Text,
3385
+ {
3386
+ style: [styles13.resendText, { color: theme.colors.primary }]
3387
+ },
3388
+ " ",
3389
+ "Sending..."
3390
+ )) : resendCooldown > 0 ? /* @__PURE__ */ import_react28.default.createElement(
3391
+ import_react_native14.Text,
3392
+ {
3393
+ style: [
3394
+ styles13.resendText,
3395
+ { color: theme.colors.textSecondary }
3396
+ ]
3397
+ },
3398
+ "Resend OTP in ",
3399
+ resendCooldown,
3400
+ "s"
3401
+ ) : /* @__PURE__ */ import_react28.default.createElement(
3402
+ import_react_native14.Text,
3403
+ {
3404
+ style: [styles13.resendText, { color: theme.colors.primary }]
3405
+ },
3406
+ "Resend OTP"
3407
+ )
3408
+ ))));
3409
+ };
3410
+ var styles13 = import_react_native14.StyleSheet.create({
3411
+ headerContent: {
3412
+ flexDirection: "row",
3413
+ alignItems: "center"
3414
+ },
3415
+ backButton: {
3416
+ padding: 4,
3417
+ // theme.spacing.xs
3418
+ marginRight: 8
3419
+ // theme.spacing.sm
3420
+ },
3421
+ headerTitle: {
3422
+ fontSize: 16,
3423
+ // theme.fontSize.lg
3424
+ fontWeight: "600"
3425
+ },
3426
+ container: {
3427
+ flex: 1,
3428
+ alignItems: "center"
3429
+ },
3430
+ emailInfo: {
3431
+ alignItems: "center",
3432
+ marginBottom: 24
3433
+ // theme.spacing.xxl
3434
+ },
3435
+ infoText: {
3436
+ fontSize: 14,
3437
+ // theme.fontSize.md
3438
+ marginBottom: 4
3439
+ // theme.spacing.xs
3440
+ },
3441
+ emailText: {
3442
+ fontSize: 16,
3443
+ // theme.fontSize.lg
3444
+ fontWeight: "600"
3445
+ },
3446
+ button: {
3447
+ width: "100%",
3448
+ marginTop: 16
3449
+ // theme.spacing.lg
3450
+ },
3451
+ resendContainer: {
3452
+ marginTop: 16,
3453
+ // theme.spacing.lg - consistent spacing
3454
+ padding: 8
3455
+ // theme.spacing.sm
3456
+ },
3457
+ resendLoading: {
3458
+ flexDirection: "row",
3459
+ alignItems: "center"
3460
+ },
3461
+ resendText: {
3462
+ fontSize: 14,
3463
+ // theme.fontSize.md
3464
+ fontWeight: "500"
3465
+ }
3466
+ });
3467
+
3468
+ // src/molecules/Permissions.tsx
3469
+ var import_react29 = __toESM(require("react"));
3470
+ var import_react_native15 = require("react-native");
3471
+ var Permissions = ({
3472
+ open,
3473
+ onClose,
3474
+ onSuccess
3475
+ }) => {
3476
+ const { appName, linkToken, setUserConsent } = useKryptosConnect();
3477
+ const theme = useTheme();
3478
+ const [isLoading, setIsLoading] = import_react29.default.useState(false);
3479
+ const [errorMessage, setErrorMessage] = import_react29.default.useState("");
3480
+ const handleConsentClick = async () => {
3481
+ try {
3482
+ setIsLoading(true);
3483
+ setErrorMessage("");
3484
+ const res = await giveUserConsent(linkToken);
3485
+ setUserConsent(res);
3486
+ onSuccess();
3487
+ } catch (error) {
3488
+ const err = error;
3489
+ setErrorMessage(
3490
+ err?.response?.data?.message || "Failed to give consent. Please try again."
3491
+ );
3492
+ console.error(error);
3493
+ } finally {
3494
+ setIsLoading(false);
3495
+ }
3496
+ };
3497
+ const permissionItems = [
3498
+ "View your crypto holdings, wallet balances, and portfolio data",
3499
+ "Access your transaction history and trading activity",
3500
+ "Keep this data updated and accessible when you're offline"
3501
+ ];
3502
+ return /* @__PURE__ */ import_react29.default.createElement(Modal, { isOpen: open, onClose, size: "xl" }, /* @__PURE__ */ import_react29.default.createElement(ModalHeader, { onClose }, "Permissions"), /* @__PURE__ */ import_react29.default.createElement(ModalBody, null, /* @__PURE__ */ import_react29.default.createElement(import_react_native15.View, { style: styles14.container }, /* @__PURE__ */ import_react29.default.createElement(ConnectLogo, null), errorMessage ? /* @__PURE__ */ import_react29.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ import_react29.default.createElement(AlertDescription, null, errorMessage)) : null, /* @__PURE__ */ import_react29.default.createElement(import_react_native15.View, { style: styles14.permissionsList }, /* @__PURE__ */ import_react29.default.createElement(
3503
+ import_react_native15.Text,
3504
+ {
3505
+ style: [styles14.subtitle, { color: theme.colors.textSecondary }]
3506
+ },
3507
+ "Allow ",
3508
+ appName,
3509
+ " to:"
3510
+ ), permissionItems.map((item, index) => /* @__PURE__ */ import_react29.default.createElement(import_react_native15.View, { key: `permission-${index}`, style: styles14.permissionItem }, /* @__PURE__ */ import_react29.default.createElement(import_react_native15.Text, { style: [styles14.bullet, { color: theme.colors.primary }] }, index + 1, "."), /* @__PURE__ */ import_react29.default.createElement(
3511
+ import_react_native15.Text,
3512
+ {
3513
+ style: [styles14.permissionText, { color: theme.colors.text }]
3514
+ },
3515
+ item
3516
+ )))), /* @__PURE__ */ import_react29.default.createElement(
3517
+ import_react_native15.View,
3518
+ {
3519
+ style: [
3520
+ styles14.infoBox,
3521
+ {
3522
+ backgroundColor: theme.colors.surface,
3523
+ borderColor: theme.colors.border
3524
+ }
3525
+ ]
3526
+ },
3527
+ /* @__PURE__ */ import_react29.default.createElement(
3528
+ import_react_native15.Text,
3529
+ {
3530
+ style: [styles14.infoText, { color: theme.colors.textSecondary }]
3531
+ },
3532
+ "By selecting",
3533
+ " ",
3534
+ /* @__PURE__ */ import_react29.default.createElement(import_react_native15.Text, { style: { fontWeight: "600", color: theme.colors.text } }, "'Allow'"),
3535
+ ", you agree to share this information and keep it updated."
3536
+ )
3537
+ ))), /* @__PURE__ */ import_react29.default.createElement(ModalFooter, null, /* @__PURE__ */ import_react29.default.createElement(
3538
+ Button,
3539
+ {
3540
+ variant: "outline",
3541
+ size: "lg",
3542
+ onPress: handleConsentClick,
3543
+ loading: isLoading,
3544
+ disabled: isLoading,
3545
+ style: styles14.button
3546
+ },
3547
+ "Allow"
3548
+ )));
3549
+ };
3550
+ var styles14 = import_react_native15.StyleSheet.create({
3551
+ container: {
3552
+ flex: 1
3553
+ },
3554
+ permissionsList: {
3555
+ marginBottom: 24
3556
+ // theme.spacing.xxl
3557
+ },
3558
+ subtitle: {
3559
+ fontSize: 14,
3560
+ // theme.fontSize.md
3561
+ fontWeight: "500",
3562
+ marginBottom: 16
3563
+ // theme.spacing.lg
3564
+ },
3565
+ permissionItem: {
3566
+ flexDirection: "row",
3567
+ marginBottom: 12,
3568
+ // theme.spacing.md
3569
+ paddingLeft: 4
3570
+ // theme.spacing.xs
3571
+ },
3572
+ bullet: {
3573
+ fontSize: 14,
3574
+ // theme.fontSize.md
3575
+ fontWeight: "600",
3576
+ marginRight: 8,
3577
+ // theme.spacing.sm
3578
+ width: 20
3579
+ // theme.spacing.xl
3580
+ },
3581
+ permissionText: {
3582
+ fontSize: 14,
3583
+ // theme.fontSize.md
3584
+ flex: 1,
3585
+ lineHeight: 20
3586
+ // theme.spacing.xl
3587
+ },
3588
+ infoBox: {
3589
+ padding: 16,
3590
+ // theme.spacing.lg
3591
+ borderRadius: 12,
3592
+ // theme.borderRadius.md
3593
+ borderWidth: 1,
3594
+ marginBottom: 24
3595
+ // theme.spacing.xxl
3596
+ },
3597
+ infoText: {
3598
+ fontSize: 13,
3599
+ lineHeight: 18,
3600
+ textAlign: "center"
3601
+ },
3602
+ button: {
3603
+ width: "100%"
3604
+ }
3605
+ });
3606
+
3607
+ // src/molecules/StatusModal.tsx
3608
+ var import_react30 = __toESM(require("react"));
3609
+ var import_react_native16 = require("react-native");
3610
+ var StatusModal = ({
3611
+ open,
3612
+ onClose,
3613
+ status,
3614
+ onSuccess,
3615
+ onError
3616
+ }) => {
3617
+ const theme = useTheme();
3618
+ const handleClose = () => {
3619
+ if (status === "success") {
3620
+ onSuccess();
3621
+ } else {
3622
+ onError();
3623
+ }
3624
+ onClose();
3625
+ };
3626
+ return /* @__PURE__ */ import_react30.default.createElement(Modal, { isOpen: open, onClose: handleClose, size: "xs" }, /* @__PURE__ */ import_react30.default.createElement(ModalBody, null, /* @__PURE__ */ import_react30.default.createElement(import_react_native16.View, { style: styles15.container }, /* @__PURE__ */ import_react30.default.createElement(import_react_native16.View, { style: styles15.iconContainer }, status === "success" ? /* @__PURE__ */ import_react30.default.createElement(SuccessIcon, { size: 80 }) : /* @__PURE__ */ import_react30.default.createElement(ErrorIcon, { size: 80 })), /* @__PURE__ */ import_react30.default.createElement(import_react_native16.Text, { style: [styles15.message, { color: theme.colors.text }] }, status === "success" ? "Connection successful" : "Connection failed"), /* @__PURE__ */ import_react30.default.createElement(
3627
+ Button,
3628
+ {
3629
+ variant: "outline",
3630
+ size: "lg",
3631
+ onPress: status === "success" ? onSuccess : onError,
3632
+ style: styles15.button
3633
+ },
3634
+ status === "success" ? "Continue" : "Try again later"
3635
+ ))));
3636
+ };
3637
+ var styles15 = import_react_native16.StyleSheet.create({
3638
+ container: {
3639
+ flex: 1,
3640
+ alignItems: "center",
3641
+ paddingVertical: 24
3642
+ // theme.spacing.xxl
3643
+ },
3644
+ iconContainer: {
3645
+ marginBottom: 24
3646
+ // theme.spacing.xxl
3647
+ },
3648
+ message: {
3649
+ fontSize: 18,
3650
+ // theme.fontSize.xl
3651
+ fontWeight: "600",
3652
+ marginBottom: 32,
3653
+ // theme.spacing.xxxl
3654
+ textAlign: "center"
3655
+ },
3656
+ button: {
3657
+ width: "100%"
3658
+ }
3659
+ });
3660
+
3661
+ // src/KryptosConnectButton.tsx
3662
+ var KryptosConnectButton = ({
3663
+ children,
3664
+ onSuccess,
3665
+ onError,
3666
+ generateLinkToken,
3667
+ style,
3668
+ textStyle
3669
+ }) => {
3670
+ const { theme: themeName } = useKryptosConnect();
3671
+ const [open, setOpen] = import_react31.default.useState(false);
3672
+ const theme = useTheme();
3673
+ return /* @__PURE__ */ import_react31.default.createElement(import_react31.default.Fragment, null, children ? /* @__PURE__ */ import_react31.default.createElement(import_react_native17.TouchableOpacity, { onPress: () => setOpen(true), style }, children) : /* @__PURE__ */ import_react31.default.createElement(
3674
+ import_react_native17.TouchableOpacity,
3675
+ {
3676
+ onPress: () => setOpen(true),
3677
+ style: [
3678
+ styles16.defaultButton,
3679
+ themeName === "light" ? {
3680
+ backgroundColor: theme.colors.white,
3681
+ borderRadius: theme.borderRadius.md,
3682
+ borderWidth: 1,
3683
+ borderColor: theme.colors.primary
3684
+ } : {
3685
+ backgroundColor: theme.colors.black,
3686
+ borderRadius: theme.borderRadius.md
3687
+ },
3688
+ style
3689
+ ],
3690
+ activeOpacity: 0.8
3691
+ },
3692
+ /* @__PURE__ */ import_react31.default.createElement(
3693
+ import_react_native17.Text,
3694
+ {
3695
+ style: [
3696
+ styles16.buttonText,
3697
+ {
3698
+ color: themeName === "light" ? theme.colors.primary : theme.colors.white,
3699
+ fontSize: theme.fontSize.lg
3700
+ },
3701
+ textStyle
3702
+ ]
3703
+ },
3704
+ "Connect with",
3705
+ " "
3706
+ ),
3707
+ /* @__PURE__ */ import_react31.default.createElement(LogoIcon, { size: 24 })
3708
+ ), /* @__PURE__ */ import_react31.default.createElement(
3709
+ KryptosConnectModal,
3710
+ {
3711
+ open,
3712
+ setOpen,
3713
+ onSuccess,
3714
+ onError,
3715
+ generateLinkToken
3716
+ }
3717
+ ));
3718
+ };
3719
+ var KryptosConnectModal = ({
3720
+ open,
3721
+ setOpen,
3722
+ onSuccess,
3723
+ onError,
3724
+ generateLinkToken
3725
+ }) => {
3726
+ const {
3727
+ setIsInitialized,
3728
+ userConsent,
3729
+ setUserConsent,
3730
+ setUser,
3731
+ setEmail,
3732
+ setLinkToken
3733
+ } = useKryptosConnect();
3734
+ const [step, setStep] = import_react31.default.useState("INIT" /* INIT */);
3735
+ const handleClose = () => {
3736
+ setOpen(false);
3737
+ setIsInitialized(false);
3738
+ setStep("INIT" /* INIT */);
3739
+ setUserConsent(null);
3740
+ setUser(null);
3741
+ setEmail("");
3742
+ setLinkToken("");
3743
+ };
3744
+ const handleSuccess = () => {
3745
+ onSuccess?.(userConsent);
3746
+ handleClose();
3747
+ };
3748
+ const handleError = () => {
3749
+ onError?.(userConsent);
3750
+ handleClose();
3751
+ };
3752
+ const handleAbort = () => {
3753
+ onError?.(new Error("User closed the modal"));
3754
+ handleClose();
3755
+ };
3756
+ if (!open) return null;
3757
+ return /* @__PURE__ */ import_react31.default.createElement(import_react_native17.View, { style: styles16.container }, step === "INIT" /* INIT */ && /* @__PURE__ */ import_react31.default.createElement(
3758
+ Init,
3759
+ {
3760
+ open,
3761
+ generateLinkToken,
3762
+ onSuccess: () => {
3763
+ setStep("AUTH" /* AUTH */);
3764
+ },
3765
+ onClose: handleAbort
3766
+ }
3767
+ ), step === "AUTH" /* AUTH */ && /* @__PURE__ */ import_react31.default.createElement(
3768
+ Auth,
3769
+ {
3770
+ open,
3771
+ onEmailSuccess: () => setStep("OTP" /* OTP */),
3772
+ onGuestSuccess: () => setStep("INTEGRATION" /* INTEGRATION */),
3773
+ onClose: handleAbort
3774
+ }
3775
+ ), step === "OTP" /* OTP */ && /* @__PURE__ */ import_react31.default.createElement(
3776
+ OTPVerify,
3777
+ {
3778
+ open,
3779
+ onSuccess: () => setStep("INTEGRATION" /* INTEGRATION */),
3780
+ onClose: handleAbort
3781
+ }
3782
+ ), step === "INTEGRATION" /* INTEGRATION */ && /* @__PURE__ */ import_react31.default.createElement(
3783
+ Integration,
3784
+ {
3785
+ open,
3786
+ onSuccess: () => setStep("PERMISSIONS" /* PERMISSIONS */),
3787
+ onClose: handleAbort
3788
+ }
3789
+ ), step === "PERMISSIONS" /* PERMISSIONS */ && /* @__PURE__ */ import_react31.default.createElement(
3790
+ Permissions,
3791
+ {
3792
+ open,
3793
+ onClose: handleAbort,
3794
+ onSuccess: () => setStep("STATUS" /* STATUS */)
3795
+ }
3796
+ ), step === "STATUS" /* STATUS */ && /* @__PURE__ */ import_react31.default.createElement(
3797
+ StatusModal,
3798
+ {
3799
+ open,
3800
+ onClose: handleAbort,
3801
+ onSuccess: handleSuccess,
3802
+ onError: handleError,
3803
+ status: userConsent?.public_token ? "success" : "error"
3804
+ }
3805
+ ));
3806
+ };
3807
+ var styles16 = import_react_native17.StyleSheet.create({
3808
+ defaultButton: {
3809
+ flexDirection: "row",
3810
+ alignItems: "center",
3811
+ justifyContent: "center",
3812
+ paddingVertical: 14,
3813
+ paddingHorizontal: 20,
3814
+ minHeight: 48,
3815
+ gap: 8
3816
+ },
3817
+ buttonText: {
3818
+ fontWeight: "600"
3819
+ },
3820
+ container: {
3821
+ position: "absolute",
3822
+ top: 0,
3823
+ left: 0,
3824
+ right: 0,
3825
+ bottom: 0
3826
+ }
3827
+ });
3828
+ // Annotate the CommonJS export names for ESM import in node:
3829
+ 0 && (module.exports = {
3830
+ KryptosConnectButton,
3831
+ KryptosConnectModal,
3832
+ KryptosConnectProvider,
3833
+ useKryptosConnect
3834
+ });