@mantle-rwa/react 0.1.1 → 0.1.3

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 (73) hide show
  1. package/dist/cjs/components/ConnectWalletPrompt.js +13 -0
  2. package/dist/cjs/components/ConnectWalletPrompt.js.map +1 -0
  3. package/dist/cjs/components/ErrorDisplay.js +42 -0
  4. package/dist/cjs/components/ErrorDisplay.js.map +1 -0
  5. package/dist/cjs/components/InvestorDashboard.js +156 -0
  6. package/dist/cjs/components/InvestorDashboard.js.map +1 -0
  7. package/dist/cjs/components/KYCFlow.js +146 -0
  8. package/dist/cjs/components/KYCFlow.js.map +1 -0
  9. package/dist/cjs/components/LoadingSpinner.js +18 -0
  10. package/dist/cjs/components/LoadingSpinner.js.map +1 -0
  11. package/dist/cjs/components/TokenMintForm.js +163 -0
  12. package/dist/cjs/components/TokenMintForm.js.map +1 -0
  13. package/dist/cjs/components/YieldCalculator.js +97 -0
  14. package/dist/cjs/components/YieldCalculator.js.map +1 -0
  15. package/dist/cjs/hooks/useRWA.js +87 -40
  16. package/dist/cjs/hooks/useRWA.js.map +1 -1
  17. package/dist/cjs/index.js +11 -1
  18. package/dist/cjs/index.js.map +1 -1
  19. package/dist/cjs/types/index.js +2 -2
  20. package/dist/cjs/types/index.js.map +1 -1
  21. package/dist/esm/components/ConnectWalletPrompt.js +10 -0
  22. package/dist/esm/components/ConnectWalletPrompt.js.map +1 -0
  23. package/dist/esm/components/ErrorDisplay.js +38 -0
  24. package/dist/esm/components/ErrorDisplay.js.map +1 -0
  25. package/dist/esm/components/InvestorDashboard.js +153 -0
  26. package/dist/esm/components/InvestorDashboard.js.map +1 -0
  27. package/dist/esm/components/KYCFlow.js +143 -0
  28. package/dist/esm/components/KYCFlow.js.map +1 -0
  29. package/dist/esm/components/LoadingSpinner.js +15 -0
  30. package/dist/esm/components/LoadingSpinner.js.map +1 -0
  31. package/dist/esm/components/TokenMintForm.js +158 -0
  32. package/dist/esm/components/TokenMintForm.js.map +1 -0
  33. package/dist/esm/components/YieldCalculator.js +94 -0
  34. package/dist/esm/components/YieldCalculator.js.map +1 -0
  35. package/dist/esm/hooks/useRWA.js +86 -39
  36. package/dist/esm/hooks/useRWA.js.map +1 -1
  37. package/dist/esm/index.js +4 -0
  38. package/dist/esm/index.js.map +1 -1
  39. package/dist/esm/types/index.js +3 -3
  40. package/dist/esm/types/index.js.map +1 -1
  41. package/dist/styles.css +3 -1
  42. package/dist/types/components/ConnectWalletPrompt.d.ts +15 -0
  43. package/dist/types/components/ConnectWalletPrompt.d.ts.map +1 -0
  44. package/dist/types/components/ErrorDisplay.d.ts +16 -0
  45. package/dist/types/components/ErrorDisplay.d.ts.map +1 -0
  46. package/dist/types/components/InvestorDashboard.d.ts +7 -0
  47. package/dist/types/components/InvestorDashboard.d.ts.map +1 -0
  48. package/dist/types/components/KYCFlow.d.ts +7 -0
  49. package/dist/types/components/KYCFlow.d.ts.map +1 -0
  50. package/dist/types/components/LoadingSpinner.d.ts +10 -0
  51. package/dist/types/components/LoadingSpinner.d.ts.map +1 -0
  52. package/dist/types/components/TokenMintForm.d.ts +15 -0
  53. package/dist/types/components/TokenMintForm.d.ts.map +1 -0
  54. package/dist/types/components/YieldCalculator.d.ts +7 -0
  55. package/dist/types/components/YieldCalculator.d.ts.map +1 -0
  56. package/dist/types/hooks/useRWA.d.ts +7 -19
  57. package/dist/types/hooks/useRWA.d.ts.map +1 -1
  58. package/dist/types/index.d.ts +5 -1
  59. package/dist/types/index.d.ts.map +1 -1
  60. package/dist/types/types/index.d.ts +113 -131
  61. package/dist/types/types/index.d.ts.map +1 -1
  62. package/package.json +5 -3
  63. package/src/components/ConnectWalletPrompt.tsx +47 -0
  64. package/src/components/ErrorDisplay.tsx +90 -0
  65. package/src/components/InvestorDashboard.tsx +315 -0
  66. package/src/components/KYCFlow.tsx +267 -0
  67. package/src/components/LoadingSpinner.tsx +33 -0
  68. package/src/components/TokenMintForm.tsx +291 -0
  69. package/src/components/YieldCalculator.tsx +250 -0
  70. package/src/hooks/useRWA.ts +110 -0
  71. package/src/index.ts +4 -0
  72. package/src/styles/index.css +68 -14
  73. package/src/types/index.ts +200 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantle-rwa/react",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "React components for RWA tokenization on Mantle Network",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,7 +20,7 @@
20
20
  "src"
21
21
  ],
22
22
  "dependencies": {
23
- "@mantle-rwa/sdk": "^0.1.0"
23
+ "@mantle-rwa/sdk": "^0.1.3"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@testing-library/jest-dom": "^6.2.0",
@@ -35,6 +35,7 @@
35
35
  "eslint": "^8.56.0",
36
36
  "eslint-plugin-react": "^7.33.2",
37
37
  "eslint-plugin-react-hooks": "^4.6.0",
38
+ "ethers": "^6.11.0",
38
39
  "fast-check": "^3.15.0",
39
40
  "jsdom": "^24.0.0",
40
41
  "postcss": "^8.4.33",
@@ -46,7 +47,8 @@
46
47
  "react": "^18.0.0",
47
48
  "react-dom": "^18.0.0",
48
49
  "wagmi": "^2.0.0",
49
- "viem": "^2.0.0"
50
+ "viem": "^2.0.0",
51
+ "ethers": "^6.0.0"
50
52
  },
51
53
  "engines": {
52
54
  "node": ">=18.0.0"
@@ -0,0 +1,47 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * ConnectWalletPrompt - Prompt for wallet connection
5
+ */
6
+
7
+ interface ConnectWalletPromptProps {
8
+ /** Message to display */
9
+ message?: string;
10
+ /** Custom CSS class */
11
+ className?: string;
12
+ }
13
+
14
+ /**
15
+ * ConnectWalletPrompt component
16
+ */
17
+ export function ConnectWalletPrompt({
18
+ message = 'Please connect your wallet to continue.',
19
+ className = '',
20
+ }: ConnectWalletPromptProps): JSX.Element {
21
+ return (
22
+ <div className={`rwa-connect-wallet-prompt ${className}`}>
23
+ <div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg border border-yellow-200 dark:border-yellow-800">
24
+ <div className="flex items-center">
25
+ <svg
26
+ className="w-5 h-5 text-yellow-600 dark:text-yellow-400 flex-shrink-0"
27
+ fill="none"
28
+ stroke="currentColor"
29
+ viewBox="0 0 24 24"
30
+ >
31
+ <path
32
+ strokeLinecap="round"
33
+ strokeLinejoin="round"
34
+ strokeWidth={2}
35
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
36
+ />
37
+ </svg>
38
+ <p className="ml-3 text-sm text-yellow-700 dark:text-yellow-300">
39
+ {message}
40
+ </p>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ );
45
+ }
46
+
47
+ export default ConnectWalletPrompt;
@@ -0,0 +1,90 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * ErrorDisplay - Shared error display component
5
+ *
6
+ * Provides consistent error UI across all components with retry functionality.
7
+ */
8
+
9
+ import type { ErrorDisplayProps } from '../types';
10
+
11
+ /**
12
+ * Format error message for display
13
+ */
14
+ export function formatErrorMessage(error: Error): string {
15
+ const message = error.message;
16
+
17
+ // Handle common contract errors
18
+ if (message.includes('user rejected')) {
19
+ return 'Transaction was rejected by the user.';
20
+ }
21
+ if (message.includes('insufficient funds')) {
22
+ return 'Insufficient funds for this transaction.';
23
+ }
24
+ if (message.includes('nonce')) {
25
+ return 'Transaction nonce error. Please try again.';
26
+ }
27
+ if (message.includes('gas')) {
28
+ return 'Gas estimation failed. The transaction may fail.';
29
+ }
30
+ if (message.includes('network')) {
31
+ return 'Network error. Please check your connection.';
32
+ }
33
+ if (message.includes('timeout')) {
34
+ return 'Request timed out. Please try again.';
35
+ }
36
+
37
+ // Return original message if no special handling
38
+ return message;
39
+ }
40
+
41
+ /**
42
+ * ErrorDisplay component for consistent error UI
43
+ */
44
+ export function ErrorDisplay({
45
+ error,
46
+ onRetry,
47
+ className = '',
48
+ }: ErrorDisplayProps): JSX.Element {
49
+ const formattedMessage = formatErrorMessage(error);
50
+
51
+ return (
52
+ <div className={`rwa-error-display ${className}`}>
53
+ <div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:border-red-800">
54
+ <div className="flex items-start">
55
+ <svg
56
+ className="w-5 h-5 text-red-600 dark:text-red-400 mt-0.5 flex-shrink-0"
57
+ fill="none"
58
+ stroke="currentColor"
59
+ viewBox="0 0 24 24"
60
+ >
61
+ <path
62
+ strokeLinecap="round"
63
+ strokeLinejoin="round"
64
+ strokeWidth={2}
65
+ d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
66
+ />
67
+ </svg>
68
+ <div className="ml-3 flex-1">
69
+ <h3 className="text-sm font-medium text-red-800 dark:text-red-200">
70
+ Error
71
+ </h3>
72
+ <p className="mt-1 text-sm text-red-700 dark:text-red-300">
73
+ {formattedMessage}
74
+ </p>
75
+ {onRetry && (
76
+ <button
77
+ onClick={onRetry}
78
+ className="mt-3 text-sm font-medium text-red-600 dark:text-red-400 hover:text-red-500 dark:hover:text-red-300 underline"
79
+ >
80
+ Try again
81
+ </button>
82
+ )}
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ );
88
+ }
89
+
90
+ export default ErrorDisplay;
@@ -0,0 +1,315 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * InvestorDashboard - Dashboard displaying investor portfolio information
5
+ *
6
+ * Shows token balance, KYC status, accreditation tier, and pending yield claims.
7
+ */
8
+
9
+ import { useState, useEffect, useCallback } from 'react';
10
+ import { useAccount } from 'wagmi';
11
+ import { formatUnits } from 'viem';
12
+ import { useRWA } from '../hooks/useRWA';
13
+ import type { InvestorDashboardProps } from '../types';
14
+ import { AccreditationTier, type TokenInfo, type InvestorData, type PendingClaim } from '@mantle-rwa/sdk';
15
+
16
+ /**
17
+ * Get display name for accreditation tier
18
+ */
19
+ function getTierName(tier: AccreditationTier): string {
20
+ switch (tier) {
21
+ case AccreditationTier.None:
22
+ return 'None';
23
+ case AccreditationTier.Retail:
24
+ return 'Retail';
25
+ case AccreditationTier.Accredited:
26
+ return 'Accredited';
27
+ case AccreditationTier.Institutional:
28
+ return 'Institutional';
29
+ default:
30
+ return 'Unknown';
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Format token amount for display
36
+ */
37
+ function formatTokenAmount(amount: bigint, decimals: number, symbol: string): string {
38
+ const formatted = formatUnits(amount, decimals);
39
+ const num = parseFloat(formatted);
40
+ return `${num.toLocaleString(undefined, { maximumFractionDigits: 4 })} ${symbol}`;
41
+ }
42
+
43
+ /**
44
+ * InvestorDashboard component
45
+ */
46
+ export function InvestorDashboard({
47
+ tokenAddress,
48
+ kycRegistryAddress,
49
+ yieldDistributorAddress,
50
+ investorAddress,
51
+ onClaimSuccess,
52
+ onError,
53
+ className = '',
54
+ }: InvestorDashboardProps): JSX.Element {
55
+ const { client, isInitialized, hasSigner } = useRWA();
56
+ const { address: connectedAddress } = useAccount();
57
+
58
+ const targetAddress = investorAddress || connectedAddress;
59
+
60
+ const [tokenInfo, setTokenInfo] = useState<TokenInfo | null>(null);
61
+ const [balance, setBalance] = useState<bigint | null>(null);
62
+ const [kycInfo, setKycInfo] = useState<InvestorData | null>(null);
63
+ const [pendingClaims, setPendingClaims] = useState<PendingClaim[]>([]);
64
+ const [totalClaimable, setTotalClaimable] = useState<bigint>(0n);
65
+ const [isLoading, setIsLoading] = useState(true);
66
+ const [error, setError] = useState<Error | null>(null);
67
+ const [isClaiming, setIsClaiming] = useState(false);
68
+
69
+ // Fetch all dashboard data
70
+ const fetchData = useCallback(async () => {
71
+ if (!client || !isInitialized || !targetAddress) {
72
+ setIsLoading(false);
73
+ return;
74
+ }
75
+
76
+ setIsLoading(true);
77
+ setError(null);
78
+
79
+ try {
80
+ // Fetch token info and balance
81
+ const token = client.token.connect(tokenAddress);
82
+ const [info, userBalance] = await Promise.all([
83
+ token.getInfo(),
84
+ token.balanceOf(targetAddress),
85
+ ]);
86
+ setTokenInfo(info);
87
+ setBalance(userBalance);
88
+
89
+ // Fetch KYC info
90
+ const registry = client.kyc.connect(kycRegistryAddress);
91
+ const investorData = await registry.getInvestorInfo(targetAddress);
92
+ setKycInfo(investorData);
93
+
94
+ // Fetch pending claims
95
+ const distributor = client.yield.connect(yieldDistributorAddress);
96
+ const claims = await distributor.getPendingClaims(targetAddress);
97
+ setPendingClaims(claims);
98
+
99
+ // Calculate total claimable
100
+ const total = claims.reduce((sum, claim) => sum + claim.amount, 0n);
101
+ setTotalClaimable(total);
102
+ } catch (err) {
103
+ const errorObj = err instanceof Error ? err : new Error('Failed to fetch dashboard data');
104
+ setError(errorObj);
105
+ onError?.(errorObj);
106
+ } finally {
107
+ setIsLoading(false);
108
+ }
109
+ }, [client, isInitialized, targetAddress, tokenAddress, kycRegistryAddress, yieldDistributorAddress, onError]);
110
+
111
+ // Fetch data on mount and when dependencies change
112
+ useEffect(() => {
113
+ fetchData();
114
+ }, [fetchData]);
115
+
116
+ // Handle claim single distribution
117
+ const handleClaim = useCallback(async (distributionId: number) => {
118
+ if (!client || !hasSigner) return;
119
+
120
+ setIsClaiming(true);
121
+ setError(null);
122
+
123
+ try {
124
+ const distributor = client.yield.connect(yieldDistributorAddress);
125
+ const result = await distributor.claim(distributionId);
126
+ onClaimSuccess?.(result);
127
+ await fetchData(); // Refresh data
128
+ } catch (err) {
129
+ const errorObj = err instanceof Error ? err : new Error('Failed to claim yield');
130
+ setError(errorObj);
131
+ onError?.(errorObj);
132
+ } finally {
133
+ setIsClaiming(false);
134
+ }
135
+ }, [client, hasSigner, yieldDistributorAddress, onClaimSuccess, onError, fetchData]);
136
+
137
+ // Handle claim all
138
+ const handleClaimAll = useCallback(async () => {
139
+ if (!client || !hasSigner || pendingClaims.length === 0) return;
140
+
141
+ setIsClaiming(true);
142
+ setError(null);
143
+
144
+ try {
145
+ const distributor = client.yield.connect(yieldDistributorAddress);
146
+ const distributionIds = pendingClaims.map(c => c.distributionId);
147
+ const result = await distributor.claimMultiple(distributionIds);
148
+ onClaimSuccess?.(result);
149
+ await fetchData(); // Refresh data
150
+ } catch (err) {
151
+ const errorObj = err instanceof Error ? err : new Error('Failed to claim yields');
152
+ setError(errorObj);
153
+ onError?.(errorObj);
154
+ } finally {
155
+ setIsClaiming(false);
156
+ }
157
+ }, [client, hasSigner, pendingClaims, yieldDistributorAddress, onClaimSuccess, onError, fetchData]);
158
+
159
+ // Render loading state
160
+ if (isLoading) {
161
+ return (
162
+ <div className={`rwa-investor-dashboard rwa-investor-dashboard--loading ${className}`}>
163
+ <div className="flex items-center justify-center p-8">
164
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" />
165
+ <span className="ml-3 text-gray-600 dark:text-gray-300">Loading dashboard...</span>
166
+ </div>
167
+ </div>
168
+ );
169
+ }
170
+
171
+ // Render no wallet state
172
+ if (!targetAddress) {
173
+ return (
174
+ <div className={`rwa-investor-dashboard rwa-investor-dashboard--no-wallet ${className}`}>
175
+ <div className="p-6 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg border border-yellow-200 dark:border-yellow-800">
176
+ <p className="text-yellow-700 dark:text-yellow-300">
177
+ Please connect your wallet to view your dashboard.
178
+ </p>
179
+ </div>
180
+ </div>
181
+ );
182
+ }
183
+
184
+ // Render error state
185
+ if (error && !tokenInfo) {
186
+ return (
187
+ <div className={`rwa-investor-dashboard rwa-investor-dashboard--error ${className}`}>
188
+ <div className="p-6 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:border-red-800">
189
+ <h3 className="text-lg font-semibold text-red-800 dark:text-red-200">Error</h3>
190
+ <p className="mt-2 text-red-700 dark:text-red-300">{error.message}</p>
191
+ <button
192
+ onClick={fetchData}
193
+ className="mt-4 px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-md transition-colors"
194
+ >
195
+ Retry
196
+ </button>
197
+ </div>
198
+ </div>
199
+ );
200
+ }
201
+
202
+ return (
203
+ <div className={`rwa-investor-dashboard ${className}`}>
204
+ <div className="space-y-6">
205
+ {/* Token Balance Card */}
206
+ <div className="p-6 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm">
207
+ <h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200">Token Balance</h3>
208
+ {tokenInfo && balance !== null && (
209
+ <div className="mt-4">
210
+ <p className="text-3xl font-bold text-gray-900 dark:text-white">
211
+ {formatTokenAmount(balance, Number(tokenInfo.decimals), tokenInfo.symbol)}
212
+ </p>
213
+ <p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
214
+ {tokenInfo.name}
215
+ </p>
216
+ </div>
217
+ )}
218
+ </div>
219
+
220
+ {/* KYC Status Card */}
221
+ <div className="p-6 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm">
222
+ <h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200">KYC Status</h3>
223
+ {kycInfo && (
224
+ <div className="mt-4 space-y-3">
225
+ <div className="flex items-center">
226
+ <span className="text-gray-600 dark:text-gray-400 w-32">Status:</span>
227
+ <span className={`font-medium ${kycInfo.verified ? 'text-green-600 dark:text-green-400' : 'text-yellow-600 dark:text-yellow-400'}`}>
228
+ {kycInfo.verified ? 'Verified' : 'Not Verified'}
229
+ </span>
230
+ </div>
231
+ <div className="flex items-center">
232
+ <span className="text-gray-600 dark:text-gray-400 w-32">Tier:</span>
233
+ <span className="font-medium text-gray-900 dark:text-white">
234
+ {getTierName(kycInfo.tier)}
235
+ </span>
236
+ </div>
237
+ {kycInfo.expiry && kycInfo.verified && (
238
+ <div className="flex items-center">
239
+ <span className="text-gray-600 dark:text-gray-400 w-32">Valid Until:</span>
240
+ <span className="font-medium text-gray-900 dark:text-white">
241
+ {kycInfo.expiry.toLocaleDateString()}
242
+ </span>
243
+ </div>
244
+ )}
245
+ </div>
246
+ )}
247
+ </div>
248
+
249
+ {/* Pending Claims Card */}
250
+ <div className="p-6 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm">
251
+ <div className="flex items-center justify-between">
252
+ <h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200">Pending Yields</h3>
253
+ {pendingClaims.length > 0 && hasSigner && (
254
+ <button
255
+ onClick={handleClaimAll}
256
+ disabled={isClaiming}
257
+ className="px-4 py-2 bg-green-600 hover:bg-green-700 disabled:bg-gray-400 text-white rounded-md transition-colors text-sm"
258
+ >
259
+ {isClaiming ? 'Claiming...' : 'Claim All'}
260
+ </button>
261
+ )}
262
+ </div>
263
+
264
+ {pendingClaims.length === 0 ? (
265
+ <p className="mt-4 text-gray-500 dark:text-gray-400">No pending yields to claim.</p>
266
+ ) : (
267
+ <div className="mt-4 space-y-3">
268
+ <p className="text-sm text-gray-600 dark:text-gray-400">
269
+ Total Claimable: <span className="font-semibold">{formatUnits(totalClaimable, 18)} tokens</span>
270
+ </p>
271
+ <div className="divide-y divide-gray-200 dark:divide-gray-700">
272
+ {pendingClaims.map((claim) => (
273
+ <div key={claim.distributionId} className="py-3 flex items-center justify-between">
274
+ <div>
275
+ <p className="font-medium text-gray-900 dark:text-white">
276
+ Distribution #{claim.distributionId}
277
+ </p>
278
+ <p className="text-sm text-gray-500 dark:text-gray-400">
279
+ Amount: {formatUnits(claim.amount, 18)} • Deadline: {claim.deadline.toLocaleDateString()}
280
+ </p>
281
+ </div>
282
+ {hasSigner && (
283
+ <button
284
+ onClick={() => handleClaim(claim.distributionId)}
285
+ disabled={isClaiming}
286
+ className="px-3 py-1 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded text-sm transition-colors"
287
+ >
288
+ Claim
289
+ </button>
290
+ )}
291
+ </div>
292
+ ))}
293
+ </div>
294
+ </div>
295
+ )}
296
+
297
+ {!hasSigner && pendingClaims.length > 0 && (
298
+ <p className="mt-4 text-sm text-yellow-600 dark:text-yellow-400">
299
+ Connect your wallet to claim yields.
300
+ </p>
301
+ )}
302
+ </div>
303
+
304
+ {/* Error display */}
305
+ {error && (
306
+ <div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:border-red-800">
307
+ <p className="text-red-700 dark:text-red-300">{error.message}</p>
308
+ </div>
309
+ )}
310
+ </div>
311
+ </div>
312
+ );
313
+ }
314
+
315
+ export default InvestorDashboard;