keynesol-shared 1.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.
Files changed (154) hide show
  1. package/README.md +118 -0
  2. package/dist/components/Common/ErrorBoundary.d.ts +23 -0
  3. package/dist/components/Common/ErrorBoundary.d.ts.map +1 -0
  4. package/dist/components/Common/ErrorBoundary.js +93 -0
  5. package/dist/components/Common/ErrorBoundary.jsx +103 -0
  6. package/dist/components/Common/ErrorMessage.d.ts +8 -0
  7. package/dist/components/Common/ErrorMessage.d.ts.map +1 -0
  8. package/dist/components/Common/ErrorMessage.js +36 -0
  9. package/dist/components/Common/ErrorMessage.jsx +40 -0
  10. package/dist/components/Common/Loading.d.ts +8 -0
  11. package/dist/components/Common/Loading.d.ts.map +1 -0
  12. package/dist/components/Common/Loading.js +41 -0
  13. package/dist/components/Common/Loading.jsx +44 -0
  14. package/dist/components/Common/LoadingIndicator.d.ts +17 -0
  15. package/dist/components/Common/LoadingIndicator.d.ts.map +1 -0
  16. package/dist/components/Common/LoadingIndicator.js +95 -0
  17. package/dist/components/Common/LoadingIndicator.jsx +108 -0
  18. package/dist/components/Common/ProgramStatus.d.ts +3 -0
  19. package/dist/components/Common/ProgramStatus.d.ts.map +1 -0
  20. package/dist/components/Common/ProgramStatus.js +26 -0
  21. package/dist/components/Common/ProgramStatus.jsx +27 -0
  22. package/dist/components/Common/Skeleton.d.ts +39 -0
  23. package/dist/components/Common/Skeleton.d.ts.map +1 -0
  24. package/dist/components/Common/Skeleton.js +53 -0
  25. package/dist/components/Common/Skeleton.jsx +67 -0
  26. package/dist/components/Common/SkeletonScreen.d.ts +18 -0
  27. package/dist/components/Common/SkeletonScreen.d.ts.map +1 -0
  28. package/dist/components/Common/SkeletonScreen.js +98 -0
  29. package/dist/components/Common/SkeletonScreen.jsx +108 -0
  30. package/dist/components/Common/index.d.ts +11 -0
  31. package/dist/components/Common/index.d.ts.map +1 -0
  32. package/dist/components/Common/index.js +10 -0
  33. package/dist/components/Wallet/TransactionStatus.d.ts +11 -0
  34. package/dist/components/Wallet/TransactionStatus.d.ts.map +1 -0
  35. package/dist/components/Wallet/TransactionStatus.js +97 -0
  36. package/dist/components/Wallet/TransactionStatus.jsx +106 -0
  37. package/dist/components/Wallet/WalletBalance.d.ts +4 -0
  38. package/dist/components/Wallet/WalletBalance.d.ts.map +1 -0
  39. package/dist/components/Wallet/WalletBalance.js +82 -0
  40. package/dist/components/Wallet/WalletBalance.jsx +86 -0
  41. package/dist/components/Wallet/WalletButton.d.ts +3 -0
  42. package/dist/components/Wallet/WalletButton.d.ts.map +1 -0
  43. package/dist/components/Wallet/WalletButton.js +51 -0
  44. package/dist/components/Wallet/WalletButton.jsx +53 -0
  45. package/dist/components/Wallet/WalletConnectionModal.d.ts +8 -0
  46. package/dist/components/Wallet/WalletConnectionModal.d.ts.map +1 -0
  47. package/dist/components/Wallet/WalletConnectionModal.js +150 -0
  48. package/dist/components/Wallet/WalletConnectionModal.jsx +170 -0
  49. package/dist/components/Wallet/WalletProvider.d.ts +9 -0
  50. package/dist/components/Wallet/WalletProvider.d.ts.map +1 -0
  51. package/dist/components/Wallet/WalletProvider.js +70 -0
  52. package/dist/components/Wallet/WalletProvider.jsx +75 -0
  53. package/dist/components/Wallet/index.d.ts +9 -0
  54. package/dist/components/Wallet/index.d.ts.map +1 -0
  55. package/dist/components/Wallet/index.js +8 -0
  56. package/dist/components/index.d.ts +7 -0
  57. package/dist/components/index.d.ts.map +1 -0
  58. package/dist/components/index.js +6 -0
  59. package/dist/hooks/index.d.ts +10 -0
  60. package/dist/hooks/index.d.ts.map +1 -0
  61. package/dist/hooks/index.js +9 -0
  62. package/dist/hooks/useCache.d.ts +16 -0
  63. package/dist/hooks/useCache.d.ts.map +1 -0
  64. package/dist/hooks/useCache.js +67 -0
  65. package/dist/hooks/usePolling.d.ts +16 -0
  66. package/dist/hooks/usePolling.d.ts.map +1 -0
  67. package/dist/hooks/usePolling.js +79 -0
  68. package/dist/hooks/useProgram.d.ts +14 -0
  69. package/dist/hooks/useProgram.d.ts.map +1 -0
  70. package/dist/hooks/useProgram.js +88 -0
  71. package/dist/hooks/useTokenBalance.d.ts +16 -0
  72. package/dist/hooks/useTokenBalance.d.ts.map +1 -0
  73. package/dist/hooks/useTokenBalance.js +100 -0
  74. package/dist/hooks/useVaults.d.ts +23 -0
  75. package/dist/hooks/useVaults.d.ts.map +1 -0
  76. package/dist/hooks/useVaults.js +98 -0
  77. package/dist/index.d.ts +12 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +17 -0
  80. package/dist/services/index.d.ts +7 -0
  81. package/dist/services/index.d.ts.map +1 -0
  82. package/dist/services/index.js +6 -0
  83. package/dist/services/reconciliationService.d.ts +76 -0
  84. package/dist/services/reconciliationService.d.ts.map +1 -0
  85. package/dist/services/reconciliationService.js +216 -0
  86. package/dist/services/syncService.d.ts +51 -0
  87. package/dist/services/syncService.d.ts.map +1 -0
  88. package/dist/services/syncService.js +218 -0
  89. package/dist/types/index.d.ts +201 -0
  90. package/dist/types/index.d.ts.map +1 -0
  91. package/dist/types/index.js +1 -0
  92. package/dist/utils/cacheManager.d.ts +73 -0
  93. package/dist/utils/cacheManager.d.ts.map +1 -0
  94. package/dist/utils/cacheManager.js +232 -0
  95. package/dist/utils/errorHandler.d.ts +76 -0
  96. package/dist/utils/errorHandler.d.ts.map +1 -0
  97. package/dist/utils/errorHandler.js +267 -0
  98. package/dist/utils/index.d.ts +12 -0
  99. package/dist/utils/index.d.ts.map +1 -0
  100. package/dist/utils/index.js +11 -0
  101. package/dist/utils/performanceMonitor.d.ts +75 -0
  102. package/dist/utils/performanceMonitor.d.ts.map +1 -0
  103. package/dist/utils/performanceMonitor.js +197 -0
  104. package/dist/utils/rpcRetry.d.ts +12 -0
  105. package/dist/utils/rpcRetry.d.ts.map +1 -0
  106. package/dist/utils/rpcRetry.js +47 -0
  107. package/dist/utils/supabase.d.ts +198 -0
  108. package/dist/utils/supabase.d.ts.map +1 -0
  109. package/dist/utils/supabase.js +50 -0
  110. package/dist/utils/toastService.d.ts +52 -0
  111. package/dist/utils/toastService.d.ts.map +1 -0
  112. package/dist/utils/toastService.js +139 -0
  113. package/dist/utils/tokenUtils.d.ts +33 -0
  114. package/dist/utils/tokenUtils.d.ts.map +1 -0
  115. package/dist/utils/tokenUtils.js +66 -0
  116. package/dist/utils/validation.d.ts +35 -0
  117. package/dist/utils/validation.d.ts.map +1 -0
  118. package/dist/utils/validation.js +83 -0
  119. package/package.json +45 -0
  120. package/src/components/Common/ErrorBoundary.tsx +135 -0
  121. package/src/components/Common/ErrorMessage.tsx +52 -0
  122. package/src/components/Common/Loading.tsx +56 -0
  123. package/src/components/Common/LoadingIndicator.tsx +143 -0
  124. package/src/components/Common/ProgramStatus.tsx +37 -0
  125. package/src/components/Common/Skeleton.tsx +83 -0
  126. package/src/components/Common/SkeletonScreen.tsx +166 -0
  127. package/src/components/Common/index.ts +10 -0
  128. package/src/components/Wallet/TransactionStatus.tsx +138 -0
  129. package/src/components/Wallet/WalletBalance.tsx +94 -0
  130. package/src/components/Wallet/WalletButton.tsx +65 -0
  131. package/src/components/Wallet/WalletConnectionModal.tsx +193 -0
  132. package/src/components/Wallet/WalletProvider.tsx +104 -0
  133. package/src/components/Wallet/index.ts +8 -0
  134. package/src/components/index.ts +6 -0
  135. package/src/hooks/index.ts +10 -0
  136. package/src/hooks/useCache.ts +87 -0
  137. package/src/hooks/usePolling.ts +98 -0
  138. package/src/hooks/useProgram.ts +93 -0
  139. package/src/hooks/useTokenBalance.ts +113 -0
  140. package/src/hooks/useVaults.ts +122 -0
  141. package/src/index.ts +23 -0
  142. package/src/services/index.ts +6 -0
  143. package/src/services/reconciliationService.ts +246 -0
  144. package/src/services/syncService.ts +238 -0
  145. package/src/types/index.ts +233 -0
  146. package/src/utils/cacheManager.ts +286 -0
  147. package/src/utils/errorHandler.ts +336 -0
  148. package/src/utils/index.ts +12 -0
  149. package/src/utils/performanceMonitor.ts +222 -0
  150. package/src/utils/rpcRetry.ts +55 -0
  151. package/src/utils/supabase.ts +253 -0
  152. package/src/utils/toastService.ts +166 -0
  153. package/src/utils/tokenUtils.ts +75 -0
  154. package/src/utils/validation.ts +107 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Toast Notification Service
3
+ * Centralized toast notification system using react-hot-toast
4
+ * Requirements: 4.2
5
+ */
6
+ import toast from 'react-hot-toast';
7
+ import { errorHandler } from './errorHandler';
8
+ class ToastService {
9
+ constructor() {
10
+ this.defaultDuration = 5000;
11
+ this.defaultPosition = 'top-right';
12
+ }
13
+ /**
14
+ * Show success toast
15
+ */
16
+ success(message, options) {
17
+ return toast.success(message, {
18
+ duration: (options === null || options === void 0 ? void 0 : options.duration) || this.defaultDuration,
19
+ position: (options === null || options === void 0 ? void 0 : options.position) || this.defaultPosition,
20
+ icon: options === null || options === void 0 ? void 0 : options.icon,
21
+ style: {
22
+ background: 'var(--color-surface)',
23
+ color: 'var(--color-text)',
24
+ border: '1px solid var(--color-success)',
25
+ borderRadius: 'var(--border-radius-md)',
26
+ },
27
+ });
28
+ }
29
+ /**
30
+ * Show error toast
31
+ */
32
+ error(message, options) {
33
+ return toast.error(message, {
34
+ duration: (options === null || options === void 0 ? void 0 : options.duration) || this.defaultDuration * 1.5, // Errors stay longer
35
+ position: (options === null || options === void 0 ? void 0 : options.position) || this.defaultPosition,
36
+ icon: options === null || options === void 0 ? void 0 : options.icon,
37
+ style: {
38
+ background: 'var(--color-surface)',
39
+ color: 'var(--color-text)',
40
+ border: '1px solid var(--color-error)',
41
+ borderRadius: 'var(--border-radius-md)',
42
+ },
43
+ });
44
+ }
45
+ /**
46
+ * Show error toast with error handler integration
47
+ */
48
+ errorWithHandler(error, context, options) {
49
+ const userMessage = errorHandler.getErrorMessage(error);
50
+ const suggestion = errorHandler.getRecoverySuggestion(error);
51
+ errorHandler.logError(error, context);
52
+ const fullMessage = suggestion
53
+ ? `${userMessage}\n${suggestion}`
54
+ : userMessage;
55
+ return this.error(fullMessage, options);
56
+ }
57
+ /**
58
+ * Show warning toast
59
+ */
60
+ warning(message, options) {
61
+ return toast(message, {
62
+ duration: (options === null || options === void 0 ? void 0 : options.duration) || this.defaultDuration,
63
+ position: (options === null || options === void 0 ? void 0 : options.position) || this.defaultPosition,
64
+ icon: ((options === null || options === void 0 ? void 0 : options.icon) || '⚠️'),
65
+ style: {
66
+ background: 'var(--color-surface)',
67
+ color: 'var(--color-text)',
68
+ border: '1px solid var(--color-warning)',
69
+ borderRadius: 'var(--border-radius-md)',
70
+ },
71
+ });
72
+ }
73
+ /**
74
+ * Show info toast
75
+ */
76
+ info(message, options) {
77
+ return toast(message, {
78
+ duration: (options === null || options === void 0 ? void 0 : options.duration) || this.defaultDuration,
79
+ position: (options === null || options === void 0 ? void 0 : options.position) || this.defaultPosition,
80
+ icon: ((options === null || options === void 0 ? void 0 : options.icon) || 'ℹ️'),
81
+ style: {
82
+ background: 'var(--color-surface)',
83
+ color: 'var(--color-text)',
84
+ border: '1px solid var(--color-primary)',
85
+ borderRadius: 'var(--border-radius-md)',
86
+ },
87
+ });
88
+ }
89
+ /**
90
+ * Show loading toast
91
+ */
92
+ loading(message, options) {
93
+ return toast.loading(message, {
94
+ position: (options === null || options === void 0 ? void 0 : options.position) || this.defaultPosition,
95
+ style: {
96
+ background: 'var(--color-surface)',
97
+ color: 'var(--color-text)',
98
+ border: '1px solid var(--color-border)',
99
+ borderRadius: 'var(--border-radius-md)',
100
+ },
101
+ });
102
+ }
103
+ /**
104
+ * Show promise toast (loading -> success/error)
105
+ */
106
+ promise(promise, messages, options) {
107
+ return toast.promise(promise, {
108
+ loading: messages.loading,
109
+ success: messages.success,
110
+ error: (err) => {
111
+ const userMessage = errorHandler.getErrorMessage(err);
112
+ return messages.error || userMessage;
113
+ },
114
+ }, {
115
+ position: (options === null || options === void 0 ? void 0 : options.position) || this.defaultPosition,
116
+ style: {
117
+ background: 'var(--color-surface)',
118
+ color: 'var(--color-text)',
119
+ borderRadius: 'var(--border-radius-md)',
120
+ },
121
+ });
122
+ }
123
+ /**
124
+ * Dismiss toast
125
+ */
126
+ dismiss(toastId) {
127
+ toast.dismiss(toastId);
128
+ }
129
+ /**
130
+ * Dismiss all toasts
131
+ */
132
+ dismissAll() {
133
+ toast.dismiss();
134
+ }
135
+ }
136
+ // Export singleton instance
137
+ export const toastService = new ToastService();
138
+ // Re-export for convenience
139
+ export { toastService as toast };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Token Utilities
3
+ * Helper functions for handling SOL and SPL tokens
4
+ */
5
+ import { PublicKey } from '@solana/web3.js';
6
+ export declare const SOL_MINT: PublicKey;
7
+ export declare const USDC_DEVNET_MINT: PublicKey;
8
+ export declare const USDC_MAINNET_MINT: PublicKey;
9
+ /**
10
+ * Check if a token mint is native SOL
11
+ */
12
+ export declare function isNativeSOL(tokenMint: PublicKey | string): boolean;
13
+ /**
14
+ * Check if a token mint is USDC
15
+ */
16
+ export declare function isUSDC(tokenMint: PublicKey | string): boolean;
17
+ /**
18
+ * Get token name from mint
19
+ */
20
+ export declare function getTokenName(tokenMint: PublicKey | string): string;
21
+ /**
22
+ * Get token decimals
23
+ */
24
+ export declare function getTokenDecimals(tokenMint: PublicKey | string): number;
25
+ /**
26
+ * Convert amount to lamports based on token type
27
+ */
28
+ export declare function toLamports(amount: number, tokenMint: PublicKey | string): number;
29
+ /**
30
+ * Convert lamports to amount based on token type
31
+ */
32
+ export declare function fromLamports(lamports: number, tokenMint: PublicKey | string): number;
33
+ //# sourceMappingURL=tokenUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenUtils.d.ts","sourceRoot":"","sources":["../../src/utils/tokenUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAmC,MAAM,iBAAiB,CAAC;AAI7E,eAAO,MAAM,QAAQ,WAA0B,CAAC;AAGhD,eAAO,MAAM,gBAAgB,WAAgE,CAAC;AAG9F,eAAO,MAAM,iBAAiB,WAAgE,CAAC;AAE/F;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAKlE;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAG7D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAQlE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAQtE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAGhF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAGpF"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Token Utilities
3
+ * Helper functions for handling SOL and SPL tokens
4
+ */
5
+ import { PublicKey, SystemProgram } from '@solana/web3.js';
6
+ import { NATIVE_MINT } from '@solana/spl-token';
7
+ // SOL native mint (System Program)
8
+ export const SOL_MINT = SystemProgram.programId;
9
+ // USDC devnet mint
10
+ export const USDC_DEVNET_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
11
+ // USDC mainnet mint
12
+ export const USDC_MAINNET_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
13
+ /**
14
+ * Check if a token mint is native SOL
15
+ */
16
+ export function isNativeSOL(tokenMint) {
17
+ const mint = typeof tokenMint === 'string' ? new PublicKey(tokenMint) : tokenMint;
18
+ // Native SOL uses SystemProgram
19
+ // We can also check against NATIVE_MINT (wrapped SOL)
20
+ return mint.equals(SOL_MINT) || mint.equals(NATIVE_MINT);
21
+ }
22
+ /**
23
+ * Check if a token mint is USDC
24
+ */
25
+ export function isUSDC(tokenMint) {
26
+ const mint = typeof tokenMint === 'string' ? new PublicKey(tokenMint) : tokenMint;
27
+ return mint.equals(USDC_DEVNET_MINT) || mint.equals(USDC_MAINNET_MINT);
28
+ }
29
+ /**
30
+ * Get token name from mint
31
+ */
32
+ export function getTokenName(tokenMint) {
33
+ if (isNativeSOL(tokenMint)) {
34
+ return 'SOL';
35
+ }
36
+ if (isUSDC(tokenMint)) {
37
+ return 'USDC';
38
+ }
39
+ return 'Token';
40
+ }
41
+ /**
42
+ * Get token decimals
43
+ */
44
+ export function getTokenDecimals(tokenMint) {
45
+ if (isNativeSOL(tokenMint)) {
46
+ return 9; // SOL has 9 decimals
47
+ }
48
+ if (isUSDC(tokenMint)) {
49
+ return 6; // USDC has 6 decimals
50
+ }
51
+ return 9; // Default to 9
52
+ }
53
+ /**
54
+ * Convert amount to lamports based on token type
55
+ */
56
+ export function toLamports(amount, tokenMint) {
57
+ const decimals = getTokenDecimals(tokenMint);
58
+ return Math.floor(amount * Math.pow(10, decimals));
59
+ }
60
+ /**
61
+ * Convert lamports to amount based on token type
62
+ */
63
+ export function fromLamports(lamports, tokenMint) {
64
+ const decimals = getTokenDecimals(tokenMint);
65
+ return lamports / Math.pow(10, decimals);
66
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Validation Utilities
3
+ * Centralized validation functions for user inputs
4
+ */
5
+ import { PublicKey } from '@solana/web3.js';
6
+ export interface ValidationResult {
7
+ valid: boolean;
8
+ error?: string;
9
+ number?: number;
10
+ }
11
+ /**
12
+ * Validate and parse numeric input
13
+ */
14
+ export declare function validateNumericInput(input: string): ValidationResult;
15
+ /**
16
+ * Validate stake amount
17
+ */
18
+ export declare function validateStakeAmount(amount: number, balance: number, minStake?: number): ValidationResult;
19
+ /**
20
+ * Validate unstake amount
21
+ */
22
+ export declare function validateUnstakeAmount(amount: number, stakedAmount: number): ValidationResult;
23
+ /**
24
+ * Validate PublicKey
25
+ */
26
+ export declare function validatePublicKey(address: string | PublicKey): ValidationResult;
27
+ /**
28
+ * Validate vault ID
29
+ */
30
+ export declare function validateVaultId(vaultId: number): ValidationResult;
31
+ /**
32
+ * Validate percentage (0-100)
33
+ */
34
+ export declare function validatePercentage(value: number): ValidationResult;
35
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAqBpE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,gBAAgB,CAc3G;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,gBAAgB,CAU5F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAQ/E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAMjE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAMlE"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Validation Utilities
3
+ * Centralized validation functions for user inputs
4
+ */
5
+ import { PublicKey } from '@solana/web3.js';
6
+ /**
7
+ * Validate and parse numeric input
8
+ */
9
+ export function validateNumericInput(input) {
10
+ if (!input || input.trim() === '') {
11
+ return { valid: false, error: 'Please enter an amount' };
12
+ }
13
+ const trimmed = input.trim();
14
+ const number = parseFloat(trimmed);
15
+ if (isNaN(number)) {
16
+ return { valid: false, error: 'Please enter a valid number' };
17
+ }
18
+ if (number <= 0) {
19
+ return { valid: false, error: 'Amount must be greater than 0' };
20
+ }
21
+ if (!isFinite(number)) {
22
+ return { valid: false, error: 'Amount must be a finite number' };
23
+ }
24
+ return { valid: true, number };
25
+ }
26
+ /**
27
+ * Validate stake amount
28
+ */
29
+ export function validateStakeAmount(amount, balance, minStake = 0) {
30
+ if (isNaN(amount) || amount <= 0) {
31
+ return { valid: false, error: 'Amount must be greater than 0' };
32
+ }
33
+ if (amount < minStake) {
34
+ return { valid: false, error: `Minimum stake amount is ${minStake}` };
35
+ }
36
+ if (amount > balance) {
37
+ return { valid: false, error: 'Insufficient balance' };
38
+ }
39
+ return { valid: true };
40
+ }
41
+ /**
42
+ * Validate unstake amount
43
+ */
44
+ export function validateUnstakeAmount(amount, stakedAmount) {
45
+ if (isNaN(amount) || amount <= 0) {
46
+ return { valid: false, error: 'Amount must be greater than 0' };
47
+ }
48
+ if (amount > stakedAmount) {
49
+ return { valid: false, error: 'Insufficient staked balance' };
50
+ }
51
+ return { valid: true };
52
+ }
53
+ /**
54
+ * Validate PublicKey
55
+ */
56
+ export function validatePublicKey(address) {
57
+ try {
58
+ const pubkey = typeof address === 'string' ? new PublicKey(address) : address;
59
+ // PublicKey constructor will throw if invalid
60
+ return { valid: true };
61
+ }
62
+ catch (error) {
63
+ return { valid: false, error: 'Invalid public key address' };
64
+ }
65
+ }
66
+ /**
67
+ * Validate vault ID
68
+ */
69
+ export function validateVaultId(vaultId) {
70
+ if (!Number.isInteger(vaultId) || vaultId <= 0) {
71
+ return { valid: false, error: 'Invalid vault ID' };
72
+ }
73
+ return { valid: true };
74
+ }
75
+ /**
76
+ * Validate percentage (0-100)
77
+ */
78
+ export function validatePercentage(value) {
79
+ if (isNaN(value) || value < 0 || value > 100) {
80
+ return { valid: false, error: 'Percentage must be between 0 and 100' };
81
+ }
82
+ return { valid: true };
83
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "keynesol-shared",
3
+ "version": "1.0.0",
4
+ "description": "Shared code package for Keynesol Web3 Prediction Platform",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "src"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepublishOnly": "npm run build",
14
+ "type-check": "tsc --noEmit"
15
+ },
16
+ "keywords": [
17
+ "solana",
18
+ "web3",
19
+ "staking",
20
+ "shared"
21
+ ],
22
+ "author": "",
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "@coral-xyz/anchor": "^0.31.1",
26
+ "@solana/spl-token": "^0.3.9",
27
+ "@solana/wallet-adapter-base": "^0.9.23",
28
+ "@solana/wallet-adapter-react": "^0.15.35",
29
+ "@solana/wallet-adapter-react-ui": "^0.9.35",
30
+ "@solana/wallet-adapter-wallets": "^0.19.32",
31
+ "@solana/web3.js": "^1.87.6",
32
+ "@supabase/supabase-js": "^2.90.1",
33
+ "react-hot-toast": "^2.4.1"
34
+ },
35
+ "devDependencies": {
36
+ "@types/react": "^18.2.45",
37
+ "@types/react-dom": "^18.2.18",
38
+ "typescript": "^5.3.3"
39
+ },
40
+ "peerDependencies": {
41
+ "react": "^18.2.0",
42
+ "react-dom": "^18.2.0",
43
+ "styled-components": "^6.1.6"
44
+ }
45
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * ErrorBoundary Component
3
+ * Requirements: 11.1 - Global error handling with React error boundaries
4
+ */
5
+ import React, { Component, ErrorInfo, ReactNode } from 'react';
6
+ import styled from 'styled-components';
7
+ import { ErrorMessage } from './ErrorMessage';
8
+
9
+ interface Props {
10
+ children: ReactNode;
11
+ fallback?: ReactNode;
12
+ }
13
+
14
+ interface State {
15
+ hasError: boolean;
16
+ error: Error | null;
17
+ errorInfo: ErrorInfo | null;
18
+ }
19
+
20
+ const Container = styled.div`
21
+ padding: var(--spacing-xl, 1.5rem);
22
+ display: flex;
23
+ flex-direction: column;
24
+ align-items: center;
25
+ justify-content: center;
26
+ min-height: 400px;
27
+ `;
28
+
29
+ const ErrorTitle = styled.h2`
30
+ font-size: 1.5rem;
31
+ font-weight: 700;
32
+ color: var(--color-error, #dc3545);
33
+ margin-bottom: var(--spacing-md, 1rem);
34
+ `;
35
+
36
+ const ErrorDetails = styled.details`
37
+ margin-top: var(--spacing-lg, 1.5rem);
38
+ width: 100%;
39
+ max-width: 800px;
40
+ `;
41
+
42
+ const ErrorSummary = styled.summary`
43
+ cursor: pointer;
44
+ color: var(--color-text-secondary, #6b7280);
45
+ font-size: 0.875rem;
46
+ margin-bottom: var(--spacing-md, 1rem);
47
+
48
+ &:hover {
49
+ color: var(--color-text, #1a1a1a);
50
+ }
51
+ `;
52
+
53
+ const ErrorCode = styled.pre`
54
+ background: var(--color-background, #ffffff);
55
+ border: 1px solid var(--color-border, #e5e7eb);
56
+ border-radius: var(--border-radius-md, 0.375rem);
57
+ padding: var(--spacing-md, 1rem);
58
+ overflow-x: auto;
59
+ font-size: 0.75rem;
60
+ color: var(--color-text-secondary, #6b7280);
61
+ max-height: 300px;
62
+ overflow-y: auto;
63
+ `;
64
+
65
+ export class ErrorBoundary extends Component<Props, State> {
66
+ constructor(props: Props) {
67
+ super(props);
68
+ this.state = {
69
+ hasError: false,
70
+ error: null,
71
+ errorInfo: null,
72
+ };
73
+ }
74
+
75
+ static getDerivedStateFromError(error: Error): State {
76
+ return {
77
+ hasError: true,
78
+ error,
79
+ errorInfo: null,
80
+ };
81
+ }
82
+
83
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
84
+ // Log error to console and monitoring service
85
+ console.error('ErrorBoundary caught an error:', error, errorInfo);
86
+
87
+ // TODO: Send to error monitoring service (e.g., Sentry)
88
+ // logErrorToService(error, errorInfo);
89
+
90
+ this.setState({
91
+ error,
92
+ errorInfo,
93
+ });
94
+ }
95
+
96
+ handleReset = (): void => {
97
+ this.setState({
98
+ hasError: false,
99
+ error: null,
100
+ errorInfo: null,
101
+ });
102
+ };
103
+
104
+ render(): ReactNode {
105
+ if (this.state.hasError) {
106
+ if (this.props.fallback) {
107
+ return this.props.fallback;
108
+ }
109
+
110
+ const isDevelopment = typeof process !== 'undefined' && process.env?.NODE_ENV === 'development';
111
+
112
+ return (
113
+ <Container>
114
+ <ErrorTitle>Something went wrong</ErrorTitle>
115
+ <ErrorMessage
116
+ message={this.state.error?.message || 'An unexpected error occurred'}
117
+ onRetry={this.handleReset}
118
+ />
119
+
120
+ {isDevelopment && this.state.errorInfo && (
121
+ <ErrorDetails>
122
+ <ErrorSummary>Error Details (Development Only)</ErrorSummary>
123
+ <ErrorCode>
124
+ {this.state.error?.toString()}
125
+ {this.state.errorInfo.componentStack}
126
+ </ErrorCode>
127
+ </ErrorDetails>
128
+ )}
129
+ </Container>
130
+ );
131
+ }
132
+
133
+ return this.props.children;
134
+ }
135
+ }
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ interface ErrorMessageProps {
5
+ message: string;
6
+ onRetry?: () => void;
7
+ }
8
+
9
+ export const ErrorMessage: React.FC<ErrorMessageProps> = ({ message, onRetry }) => {
10
+ return (
11
+ <Container>
12
+ <Icon>⚠️</Icon>
13
+ <Message>{message}</Message>
14
+ {onRetry && <RetryButton onClick={onRetry}>Try Again</RetryButton>}
15
+ </Container>
16
+ );
17
+ };
18
+
19
+ const Container = styled.div`
20
+ background-color: rgba(239, 68, 68, 0.1);
21
+ border: 1px solid var(--color-error, #dc3545);
22
+ border-radius: var(--border-radius-lg, 0.5rem);
23
+ padding: var(--spacing-xl, 1.5rem);
24
+ text-align: center;
25
+ `;
26
+
27
+ const Icon = styled.div`
28
+ font-size: 3rem;
29
+ margin-bottom: var(--spacing-md, 1rem);
30
+ `;
31
+
32
+ const Message = styled.p`
33
+ color: var(--color-error, #dc3545);
34
+ font-size: 1rem;
35
+ margin-bottom: var(--spacing-md, 1rem);
36
+ `;
37
+
38
+ const RetryButton = styled.button`
39
+ background-color: var(--color-error, #dc3545);
40
+ color: white;
41
+ padding: var(--spacing-sm, 0.5rem) var(--spacing-lg, 1.5rem);
42
+ border-radius: var(--border-radius-md, 0.375rem);
43
+ font-weight: 600;
44
+ transition: all 0.3s ease;
45
+ border: none;
46
+ cursor: pointer;
47
+
48
+ &:hover {
49
+ transform: translateY(-2px);
50
+ box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
51
+ }
52
+ `;
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+ import styled, { keyframes } from 'styled-components';
3
+
4
+ interface LoadingProps {
5
+ size?: 'small' | 'medium' | 'large';
6
+ text?: string;
7
+ }
8
+
9
+ export const Loading: React.FC<LoadingProps> = ({ size = 'medium', text }) => {
10
+ return (
11
+ <Container>
12
+ <Spinner size={size} />
13
+ {text && <LoadingText>{text}</LoadingText>}
14
+ </Container>
15
+ );
16
+ };
17
+
18
+ const spin = keyframes`
19
+ to { transform: rotate(360deg); }
20
+ `;
21
+
22
+ const Container = styled.div`
23
+ display: flex;
24
+ flex-direction: column;
25
+ align-items: center;
26
+ justify-content: center;
27
+ gap: var(--spacing-md, 1rem);
28
+ padding: var(--spacing-xl, 1.5rem);
29
+ `;
30
+
31
+ const Spinner = styled.div<{ size: string }>`
32
+ width: ${props => {
33
+ switch (props.size) {
34
+ case 'small': return '24px';
35
+ case 'large': return '64px';
36
+ default: return '48px';
37
+ }
38
+ }};
39
+ height: ${props => {
40
+ switch (props.size) {
41
+ case 'small': return '24px';
42
+ case 'large': return '64px';
43
+ default: return '48px';
44
+ }
45
+ }};
46
+ border: 4px solid var(--color-border, #e5e7eb);
47
+ border-top-color: var(--color-primary, #6a8102);
48
+ border-radius: 50%;
49
+ animation: ${spin} 1s linear infinite;
50
+ `;
51
+
52
+ const LoadingText = styled.p`
53
+ color: var(--color-text-secondary, #6b7280);
54
+ font-size: 1rem;
55
+ margin: 0;
56
+ `;