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.
- package/README.md +118 -0
- package/dist/components/Common/ErrorBoundary.d.ts +23 -0
- package/dist/components/Common/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/Common/ErrorBoundary.js +93 -0
- package/dist/components/Common/ErrorBoundary.jsx +103 -0
- package/dist/components/Common/ErrorMessage.d.ts +8 -0
- package/dist/components/Common/ErrorMessage.d.ts.map +1 -0
- package/dist/components/Common/ErrorMessage.js +36 -0
- package/dist/components/Common/ErrorMessage.jsx +40 -0
- package/dist/components/Common/Loading.d.ts +8 -0
- package/dist/components/Common/Loading.d.ts.map +1 -0
- package/dist/components/Common/Loading.js +41 -0
- package/dist/components/Common/Loading.jsx +44 -0
- package/dist/components/Common/LoadingIndicator.d.ts +17 -0
- package/dist/components/Common/LoadingIndicator.d.ts.map +1 -0
- package/dist/components/Common/LoadingIndicator.js +95 -0
- package/dist/components/Common/LoadingIndicator.jsx +108 -0
- package/dist/components/Common/ProgramStatus.d.ts +3 -0
- package/dist/components/Common/ProgramStatus.d.ts.map +1 -0
- package/dist/components/Common/ProgramStatus.js +26 -0
- package/dist/components/Common/ProgramStatus.jsx +27 -0
- package/dist/components/Common/Skeleton.d.ts +39 -0
- package/dist/components/Common/Skeleton.d.ts.map +1 -0
- package/dist/components/Common/Skeleton.js +53 -0
- package/dist/components/Common/Skeleton.jsx +67 -0
- package/dist/components/Common/SkeletonScreen.d.ts +18 -0
- package/dist/components/Common/SkeletonScreen.d.ts.map +1 -0
- package/dist/components/Common/SkeletonScreen.js +98 -0
- package/dist/components/Common/SkeletonScreen.jsx +108 -0
- package/dist/components/Common/index.d.ts +11 -0
- package/dist/components/Common/index.d.ts.map +1 -0
- package/dist/components/Common/index.js +10 -0
- package/dist/components/Wallet/TransactionStatus.d.ts +11 -0
- package/dist/components/Wallet/TransactionStatus.d.ts.map +1 -0
- package/dist/components/Wallet/TransactionStatus.js +97 -0
- package/dist/components/Wallet/TransactionStatus.jsx +106 -0
- package/dist/components/Wallet/WalletBalance.d.ts +4 -0
- package/dist/components/Wallet/WalletBalance.d.ts.map +1 -0
- package/dist/components/Wallet/WalletBalance.js +82 -0
- package/dist/components/Wallet/WalletBalance.jsx +86 -0
- package/dist/components/Wallet/WalletButton.d.ts +3 -0
- package/dist/components/Wallet/WalletButton.d.ts.map +1 -0
- package/dist/components/Wallet/WalletButton.js +51 -0
- package/dist/components/Wallet/WalletButton.jsx +53 -0
- package/dist/components/Wallet/WalletConnectionModal.d.ts +8 -0
- package/dist/components/Wallet/WalletConnectionModal.d.ts.map +1 -0
- package/dist/components/Wallet/WalletConnectionModal.js +150 -0
- package/dist/components/Wallet/WalletConnectionModal.jsx +170 -0
- package/dist/components/Wallet/WalletProvider.d.ts +9 -0
- package/dist/components/Wallet/WalletProvider.d.ts.map +1 -0
- package/dist/components/Wallet/WalletProvider.js +70 -0
- package/dist/components/Wallet/WalletProvider.jsx +75 -0
- package/dist/components/Wallet/index.d.ts +9 -0
- package/dist/components/Wallet/index.d.ts.map +1 -0
- package/dist/components/Wallet/index.js +8 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +6 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +9 -0
- package/dist/hooks/useCache.d.ts +16 -0
- package/dist/hooks/useCache.d.ts.map +1 -0
- package/dist/hooks/useCache.js +67 -0
- package/dist/hooks/usePolling.d.ts +16 -0
- package/dist/hooks/usePolling.d.ts.map +1 -0
- package/dist/hooks/usePolling.js +79 -0
- package/dist/hooks/useProgram.d.ts +14 -0
- package/dist/hooks/useProgram.d.ts.map +1 -0
- package/dist/hooks/useProgram.js +88 -0
- package/dist/hooks/useTokenBalance.d.ts +16 -0
- package/dist/hooks/useTokenBalance.d.ts.map +1 -0
- package/dist/hooks/useTokenBalance.js +100 -0
- package/dist/hooks/useVaults.d.ts +23 -0
- package/dist/hooks/useVaults.d.ts.map +1 -0
- package/dist/hooks/useVaults.js +98 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +6 -0
- package/dist/services/reconciliationService.d.ts +76 -0
- package/dist/services/reconciliationService.d.ts.map +1 -0
- package/dist/services/reconciliationService.js +216 -0
- package/dist/services/syncService.d.ts +51 -0
- package/dist/services/syncService.d.ts.map +1 -0
- package/dist/services/syncService.js +218 -0
- package/dist/types/index.d.ts +201 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/cacheManager.d.ts +73 -0
- package/dist/utils/cacheManager.d.ts.map +1 -0
- package/dist/utils/cacheManager.js +232 -0
- package/dist/utils/errorHandler.d.ts +76 -0
- package/dist/utils/errorHandler.d.ts.map +1 -0
- package/dist/utils/errorHandler.js +267 -0
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/performanceMonitor.d.ts +75 -0
- package/dist/utils/performanceMonitor.d.ts.map +1 -0
- package/dist/utils/performanceMonitor.js +197 -0
- package/dist/utils/rpcRetry.d.ts +12 -0
- package/dist/utils/rpcRetry.d.ts.map +1 -0
- package/dist/utils/rpcRetry.js +47 -0
- package/dist/utils/supabase.d.ts +198 -0
- package/dist/utils/supabase.d.ts.map +1 -0
- package/dist/utils/supabase.js +50 -0
- package/dist/utils/toastService.d.ts +52 -0
- package/dist/utils/toastService.d.ts.map +1 -0
- package/dist/utils/toastService.js +139 -0
- package/dist/utils/tokenUtils.d.ts +33 -0
- package/dist/utils/tokenUtils.d.ts.map +1 -0
- package/dist/utils/tokenUtils.js +66 -0
- package/dist/utils/validation.d.ts +35 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +83 -0
- package/package.json +45 -0
- package/src/components/Common/ErrorBoundary.tsx +135 -0
- package/src/components/Common/ErrorMessage.tsx +52 -0
- package/src/components/Common/Loading.tsx +56 -0
- package/src/components/Common/LoadingIndicator.tsx +143 -0
- package/src/components/Common/ProgramStatus.tsx +37 -0
- package/src/components/Common/Skeleton.tsx +83 -0
- package/src/components/Common/SkeletonScreen.tsx +166 -0
- package/src/components/Common/index.ts +10 -0
- package/src/components/Wallet/TransactionStatus.tsx +138 -0
- package/src/components/Wallet/WalletBalance.tsx +94 -0
- package/src/components/Wallet/WalletButton.tsx +65 -0
- package/src/components/Wallet/WalletConnectionModal.tsx +193 -0
- package/src/components/Wallet/WalletProvider.tsx +104 -0
- package/src/components/Wallet/index.ts +8 -0
- package/src/components/index.ts +6 -0
- package/src/hooks/index.ts +10 -0
- package/src/hooks/useCache.ts +87 -0
- package/src/hooks/usePolling.ts +98 -0
- package/src/hooks/useProgram.ts +93 -0
- package/src/hooks/useTokenBalance.ts +113 -0
- package/src/hooks/useVaults.ts +122 -0
- package/src/index.ts +23 -0
- package/src/services/index.ts +6 -0
- package/src/services/reconciliationService.ts +246 -0
- package/src/services/syncService.ts +238 -0
- package/src/types/index.ts +233 -0
- package/src/utils/cacheManager.ts +286 -0
- package/src/utils/errorHandler.ts +336 -0
- package/src/utils/index.ts +12 -0
- package/src/utils/performanceMonitor.ts +222 -0
- package/src/utils/rpcRetry.ts +55 -0
- package/src/utils/supabase.ts +253 -0
- package/src/utils/toastService.ts +166 -0
- package/src/utils/tokenUtils.ts +75 -0
- 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
|
+
`;
|