@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.
- package/dist/cjs/components/ConnectWalletPrompt.js +13 -0
- package/dist/cjs/components/ConnectWalletPrompt.js.map +1 -0
- package/dist/cjs/components/ErrorDisplay.js +42 -0
- package/dist/cjs/components/ErrorDisplay.js.map +1 -0
- package/dist/cjs/components/InvestorDashboard.js +156 -0
- package/dist/cjs/components/InvestorDashboard.js.map +1 -0
- package/dist/cjs/components/KYCFlow.js +146 -0
- package/dist/cjs/components/KYCFlow.js.map +1 -0
- package/dist/cjs/components/LoadingSpinner.js +18 -0
- package/dist/cjs/components/LoadingSpinner.js.map +1 -0
- package/dist/cjs/components/TokenMintForm.js +163 -0
- package/dist/cjs/components/TokenMintForm.js.map +1 -0
- package/dist/cjs/components/YieldCalculator.js +97 -0
- package/dist/cjs/components/YieldCalculator.js.map +1 -0
- package/dist/cjs/hooks/useRWA.js +87 -40
- package/dist/cjs/hooks/useRWA.js.map +1 -1
- package/dist/cjs/index.js +11 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/index.js +2 -2
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/esm/components/ConnectWalletPrompt.js +10 -0
- package/dist/esm/components/ConnectWalletPrompt.js.map +1 -0
- package/dist/esm/components/ErrorDisplay.js +38 -0
- package/dist/esm/components/ErrorDisplay.js.map +1 -0
- package/dist/esm/components/InvestorDashboard.js +153 -0
- package/dist/esm/components/InvestorDashboard.js.map +1 -0
- package/dist/esm/components/KYCFlow.js +143 -0
- package/dist/esm/components/KYCFlow.js.map +1 -0
- package/dist/esm/components/LoadingSpinner.js +15 -0
- package/dist/esm/components/LoadingSpinner.js.map +1 -0
- package/dist/esm/components/TokenMintForm.js +158 -0
- package/dist/esm/components/TokenMintForm.js.map +1 -0
- package/dist/esm/components/YieldCalculator.js +94 -0
- package/dist/esm/components/YieldCalculator.js.map +1 -0
- package/dist/esm/hooks/useRWA.js +86 -39
- package/dist/esm/hooks/useRWA.js.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/index.js +3 -3
- package/dist/esm/types/index.js.map +1 -1
- package/dist/styles.css +3 -1
- package/dist/types/components/ConnectWalletPrompt.d.ts +15 -0
- package/dist/types/components/ConnectWalletPrompt.d.ts.map +1 -0
- package/dist/types/components/ErrorDisplay.d.ts +16 -0
- package/dist/types/components/ErrorDisplay.d.ts.map +1 -0
- package/dist/types/components/InvestorDashboard.d.ts +7 -0
- package/dist/types/components/InvestorDashboard.d.ts.map +1 -0
- package/dist/types/components/KYCFlow.d.ts +7 -0
- package/dist/types/components/KYCFlow.d.ts.map +1 -0
- package/dist/types/components/LoadingSpinner.d.ts +10 -0
- package/dist/types/components/LoadingSpinner.d.ts.map +1 -0
- package/dist/types/components/TokenMintForm.d.ts +15 -0
- package/dist/types/components/TokenMintForm.d.ts.map +1 -0
- package/dist/types/components/YieldCalculator.d.ts +7 -0
- package/dist/types/components/YieldCalculator.d.ts.map +1 -0
- package/dist/types/hooks/useRWA.d.ts +7 -19
- package/dist/types/hooks/useRWA.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +113 -131
- package/dist/types/types/index.d.ts.map +1 -1
- package/package.json +5 -3
- package/src/components/ConnectWalletPrompt.tsx +47 -0
- package/src/components/ErrorDisplay.tsx +90 -0
- package/src/components/InvestorDashboard.tsx +315 -0
- package/src/components/KYCFlow.tsx +267 -0
- package/src/components/LoadingSpinner.tsx +33 -0
- package/src/components/TokenMintForm.tsx +291 -0
- package/src/components/YieldCalculator.tsx +250 -0
- package/src/hooks/useRWA.ts +110 -0
- package/src/index.ts +4 -0
- package/src/styles/index.css +68 -14
- package/src/types/index.ts +200 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* YieldCalculator - Calculator for previewing yield distributions
|
|
5
|
+
*
|
|
6
|
+
* Shows how yields will be allocated to token holders before creating a distribution.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
10
|
+
import { formatUnits } from 'viem';
|
|
11
|
+
import { useRWA } from '../hooks/useRWA';
|
|
12
|
+
import type { YieldCalculatorProps } from '../types';
|
|
13
|
+
import type { DistributionPreview, HolderDistribution } from '@mantle-rwa/sdk';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Format address for display
|
|
17
|
+
*/
|
|
18
|
+
function formatAddress(address: string): string {
|
|
19
|
+
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Format token amount for display
|
|
24
|
+
*/
|
|
25
|
+
function formatAmount(amount: bigint, decimals: number = 18): string {
|
|
26
|
+
const formatted = formatUnits(amount, decimals);
|
|
27
|
+
const num = parseFloat(formatted);
|
|
28
|
+
return num.toLocaleString(undefined, { maximumFractionDigits: 4 });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* YieldCalculator component
|
|
33
|
+
*/
|
|
34
|
+
export function YieldCalculator({
|
|
35
|
+
tokenAddress,
|
|
36
|
+
yieldDistributorAddress: _yieldDistributorAddress,
|
|
37
|
+
holderAddresses,
|
|
38
|
+
onCalculate,
|
|
39
|
+
onError,
|
|
40
|
+
className = '',
|
|
41
|
+
}: YieldCalculatorProps): JSX.Element {
|
|
42
|
+
const { client, isInitialized } = useRWA();
|
|
43
|
+
|
|
44
|
+
const [amount, setAmount] = useState<string>('');
|
|
45
|
+
const [preview, setPreview] = useState<DistributionPreview | null>(null);
|
|
46
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
47
|
+
const [error, setError] = useState<Error | null>(null);
|
|
48
|
+
|
|
49
|
+
// Debounce timer ref
|
|
50
|
+
const debounceRef = useRef<NodeJS.Timeout | null>(null);
|
|
51
|
+
|
|
52
|
+
// Calculate preview
|
|
53
|
+
const calculatePreview = useCallback(async (distributionAmount: string) => {
|
|
54
|
+
if (!client || !isInitialized || !distributionAmount || parseFloat(distributionAmount) <= 0) {
|
|
55
|
+
setPreview(null);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
setIsLoading(true);
|
|
60
|
+
setError(null);
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const result = await client.yield.previewDistribution(
|
|
64
|
+
tokenAddress,
|
|
65
|
+
distributionAmount,
|
|
66
|
+
undefined, // snapshotId
|
|
67
|
+
holderAddresses
|
|
68
|
+
);
|
|
69
|
+
setPreview(result);
|
|
70
|
+
onCalculate?.(result);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
const errorObj = err instanceof Error ? err : new Error('Failed to calculate preview');
|
|
73
|
+
setError(errorObj);
|
|
74
|
+
onError?.(errorObj);
|
|
75
|
+
setPreview(null);
|
|
76
|
+
} finally {
|
|
77
|
+
setIsLoading(false);
|
|
78
|
+
}
|
|
79
|
+
}, [client, isInitialized, tokenAddress, holderAddresses, onCalculate, onError]);
|
|
80
|
+
|
|
81
|
+
// Handle amount change with debounce
|
|
82
|
+
const handleAmountChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
83
|
+
const value = e.target.value;
|
|
84
|
+
setAmount(value);
|
|
85
|
+
|
|
86
|
+
// Clear previous debounce
|
|
87
|
+
if (debounceRef.current) {
|
|
88
|
+
clearTimeout(debounceRef.current);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Debounce the calculation
|
|
92
|
+
debounceRef.current = setTimeout(() => {
|
|
93
|
+
calculatePreview(value);
|
|
94
|
+
}, 500);
|
|
95
|
+
}, [calculatePreview]);
|
|
96
|
+
|
|
97
|
+
// Cleanup debounce on unmount
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
return () => {
|
|
100
|
+
if (debounceRef.current) {
|
|
101
|
+
clearTimeout(debounceRef.current);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}, []);
|
|
105
|
+
|
|
106
|
+
// Recalculate when holder addresses change
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (amount && parseFloat(amount) > 0) {
|
|
109
|
+
calculatePreview(amount);
|
|
110
|
+
}
|
|
111
|
+
}, [holderAddresses]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
112
|
+
|
|
113
|
+
// Handle manual calculate button
|
|
114
|
+
const handleCalculate = useCallback(() => {
|
|
115
|
+
calculatePreview(amount);
|
|
116
|
+
}, [amount, calculatePreview]);
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<div className={`rwa-yield-calculator ${className}`}>
|
|
120
|
+
<div className="space-y-6">
|
|
121
|
+
{/* Amount Input */}
|
|
122
|
+
<div>
|
|
123
|
+
<label htmlFor="distribution-amount" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
124
|
+
Total Distribution Amount
|
|
125
|
+
</label>
|
|
126
|
+
<div className="mt-1 flex rounded-md shadow-sm">
|
|
127
|
+
<input
|
|
128
|
+
type="text"
|
|
129
|
+
id="distribution-amount"
|
|
130
|
+
value={amount}
|
|
131
|
+
onChange={handleAmountChange}
|
|
132
|
+
placeholder="Enter amount to distribute"
|
|
133
|
+
className="flex-1 block w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:text-white"
|
|
134
|
+
/>
|
|
135
|
+
<button
|
|
136
|
+
type="button"
|
|
137
|
+
onClick={handleCalculate}
|
|
138
|
+
disabled={isLoading || !amount}
|
|
139
|
+
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-r-md transition-colors"
|
|
140
|
+
>
|
|
141
|
+
{isLoading ? 'Calculating...' : 'Calculate'}
|
|
142
|
+
</button>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
{/* Loading State */}
|
|
147
|
+
{isLoading && (
|
|
148
|
+
<div className="flex items-center justify-center p-4">
|
|
149
|
+
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600" />
|
|
150
|
+
<span className="ml-2 text-gray-600 dark:text-gray-300">Calculating distribution...</span>
|
|
151
|
+
</div>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
{/* Error State */}
|
|
155
|
+
{error && (
|
|
156
|
+
<div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:border-red-800">
|
|
157
|
+
<p className="text-red-700 dark:text-red-300">{error.message}</p>
|
|
158
|
+
<button
|
|
159
|
+
onClick={handleCalculate}
|
|
160
|
+
className="mt-2 text-sm text-red-600 dark:text-red-400 underline hover:no-underline"
|
|
161
|
+
>
|
|
162
|
+
Retry
|
|
163
|
+
</button>
|
|
164
|
+
</div>
|
|
165
|
+
)}
|
|
166
|
+
|
|
167
|
+
{/* Preview Results */}
|
|
168
|
+
{preview && !isLoading && (
|
|
169
|
+
<div className="space-y-4">
|
|
170
|
+
{/* Summary */}
|
|
171
|
+
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
|
|
172
|
+
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300">Distribution Summary</h4>
|
|
173
|
+
<div className="mt-2 grid grid-cols-2 gap-4">
|
|
174
|
+
<div>
|
|
175
|
+
<p className="text-xs text-gray-500 dark:text-gray-400">Total Holders</p>
|
|
176
|
+
<p className="text-lg font-semibold text-gray-900 dark:text-white">{preview.totalHolders}</p>
|
|
177
|
+
</div>
|
|
178
|
+
<div>
|
|
179
|
+
<p className="text-xs text-gray-500 dark:text-gray-400">Total Supply</p>
|
|
180
|
+
<p className="text-lg font-semibold text-gray-900 dark:text-white">
|
|
181
|
+
{formatAmount(preview.totalSupplyAtSnapshot)}
|
|
182
|
+
</p>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
{/* Holder Distributions Table */}
|
|
188
|
+
{preview.distributions.length > 0 && (
|
|
189
|
+
<div className="overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700">
|
|
190
|
+
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
191
|
+
<thead className="bg-gray-50 dark:bg-gray-800">
|
|
192
|
+
<tr>
|
|
193
|
+
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
194
|
+
Address
|
|
195
|
+
</th>
|
|
196
|
+
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
197
|
+
Balance
|
|
198
|
+
</th>
|
|
199
|
+
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
200
|
+
Yield Amount
|
|
201
|
+
</th>
|
|
202
|
+
<th className="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
203
|
+
Share
|
|
204
|
+
</th>
|
|
205
|
+
</tr>
|
|
206
|
+
</thead>
|
|
207
|
+
<tbody className="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
|
|
208
|
+
{preview.distributions.map((dist: HolderDistribution) => (
|
|
209
|
+
<tr key={dist.address}>
|
|
210
|
+
<td className="px-4 py-3 whitespace-nowrap text-sm font-mono text-gray-900 dark:text-white">
|
|
211
|
+
{formatAddress(dist.address)}
|
|
212
|
+
</td>
|
|
213
|
+
<td className="px-4 py-3 whitespace-nowrap text-sm text-right text-gray-600 dark:text-gray-300">
|
|
214
|
+
{formatAmount(dist.balance)}
|
|
215
|
+
</td>
|
|
216
|
+
<td className="px-4 py-3 whitespace-nowrap text-sm text-right font-medium text-green-600 dark:text-green-400">
|
|
217
|
+
{formatAmount(dist.yieldAmount)}
|
|
218
|
+
</td>
|
|
219
|
+
<td className="px-4 py-3 whitespace-nowrap text-sm text-right text-gray-600 dark:text-gray-300">
|
|
220
|
+
{dist.percentage.toFixed(2)}%
|
|
221
|
+
</td>
|
|
222
|
+
</tr>
|
|
223
|
+
))}
|
|
224
|
+
</tbody>
|
|
225
|
+
</table>
|
|
226
|
+
</div>
|
|
227
|
+
)}
|
|
228
|
+
|
|
229
|
+
{preview.distributions.length === 0 && (
|
|
230
|
+
<div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg border border-yellow-200 dark:border-yellow-800">
|
|
231
|
+
<p className="text-yellow-700 dark:text-yellow-300">
|
|
232
|
+
No holder addresses provided or no holders have balances.
|
|
233
|
+
</p>
|
|
234
|
+
</div>
|
|
235
|
+
)}
|
|
236
|
+
</div>
|
|
237
|
+
)}
|
|
238
|
+
|
|
239
|
+
{/* Empty State */}
|
|
240
|
+
{!preview && !isLoading && !error && (
|
|
241
|
+
<div className="p-6 text-center text-gray-500 dark:text-gray-400">
|
|
242
|
+
<p>Enter a distribution amount to preview how yields will be allocated.</p>
|
|
243
|
+
</div>
|
|
244
|
+
)}
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export default YieldCalculator;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* useRWA - Core hook for accessing the RWA SDK client
|
|
5
|
+
*
|
|
6
|
+
* Provides access to the @mantle-rwa/sdk RWAClient instance with
|
|
7
|
+
* automatic wagmi wallet integration.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { useRWA } from '@mantle-rwa/react';
|
|
12
|
+
*
|
|
13
|
+
* function MyComponent() {
|
|
14
|
+
* const { client, isInitialized, hasSigner } = useRWA();
|
|
15
|
+
*
|
|
16
|
+
* if (!isInitialized) return <div>Loading...</div>;
|
|
17
|
+
*
|
|
18
|
+
* // Use client.token, client.kyc, client.yield, client.compliance
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
24
|
+
import { useAccount, useWalletClient } from 'wagmi';
|
|
25
|
+
import { BrowserProvider } from 'ethers';
|
|
26
|
+
import { RWAClient } from '@mantle-rwa/sdk';
|
|
27
|
+
import type { UseRWAConfig, UseRWAReturn, ContractAddresses } from '../types';
|
|
28
|
+
|
|
29
|
+
// Default contract addresses (can be overridden via config)
|
|
30
|
+
const DEFAULT_CONTRACTS: ContractAddresses = {
|
|
31
|
+
token: undefined,
|
|
32
|
+
kycRegistry: undefined,
|
|
33
|
+
yieldDistributor: undefined,
|
|
34
|
+
assetVault: undefined,
|
|
35
|
+
factory: undefined,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Hook for initializing and managing the RWA SDK client
|
|
40
|
+
*
|
|
41
|
+
* @param config - Optional configuration for network and contract addresses
|
|
42
|
+
* @returns UseRWAReturn object with client, state, and utilities
|
|
43
|
+
*/
|
|
44
|
+
export function useRWA(config?: UseRWAConfig): UseRWAReturn {
|
|
45
|
+
const { isConnected } = useAccount();
|
|
46
|
+
const { data: walletClient } = useWalletClient();
|
|
47
|
+
|
|
48
|
+
const [client, setClient] = useState<RWAClient | null>(null);
|
|
49
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
50
|
+
const [error, setError] = useState<Error | null>(null);
|
|
51
|
+
|
|
52
|
+
const network = config?.network ?? 'mantle-sepolia';
|
|
53
|
+
|
|
54
|
+
// Initialize or reinitialize the client
|
|
55
|
+
const initializeClient = useCallback(async () => {
|
|
56
|
+
setIsLoading(true);
|
|
57
|
+
setError(null);
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
let rwaClient: RWAClient;
|
|
61
|
+
|
|
62
|
+
if (walletClient && isConnected) {
|
|
63
|
+
// Create client with wallet signer for write operations
|
|
64
|
+
const provider = new BrowserProvider(walletClient.transport);
|
|
65
|
+
const signer = await provider.getSigner();
|
|
66
|
+
|
|
67
|
+
rwaClient = new RWAClient({
|
|
68
|
+
network,
|
|
69
|
+
signer,
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
// Create read-only client (no signer)
|
|
73
|
+
rwaClient = new RWAClient({
|
|
74
|
+
network,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
setClient(rwaClient);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to initialize RWA client';
|
|
81
|
+
setError(new Error(errorMessage));
|
|
82
|
+
setClient(null);
|
|
83
|
+
} finally {
|
|
84
|
+
setIsLoading(false);
|
|
85
|
+
}
|
|
86
|
+
}, [walletClient, isConnected, network]);
|
|
87
|
+
|
|
88
|
+
// Initialize client on mount and when wallet changes
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
initializeClient();
|
|
91
|
+
}, [initializeClient]);
|
|
92
|
+
|
|
93
|
+
// Memoized contract addresses
|
|
94
|
+
const contracts = useMemo<ContractAddresses>(() => ({
|
|
95
|
+
...DEFAULT_CONTRACTS,
|
|
96
|
+
...config?.contracts,
|
|
97
|
+
}), [config?.contracts]);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
client,
|
|
101
|
+
isInitialized: client !== null && !isLoading,
|
|
102
|
+
isLoading,
|
|
103
|
+
error,
|
|
104
|
+
hasSigner: client?.hasSigner ?? false,
|
|
105
|
+
contracts,
|
|
106
|
+
reinitialize: initializeClient,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default useRWA;
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
export { KYCFlow } from './components/KYCFlow';
|
|
8
8
|
export { InvestorDashboard } from './components/InvestorDashboard';
|
|
9
9
|
export { TokenMintForm } from './components/TokenMintForm';
|
|
10
|
+
export { isValidAddress, isValidAmount } from './components/TokenMintForm';
|
|
10
11
|
export { YieldCalculator } from './components/YieldCalculator';
|
|
12
|
+
export { ErrorDisplay, formatErrorMessage } from './components/ErrorDisplay';
|
|
13
|
+
export { LoadingSpinner } from './components/LoadingSpinner';
|
|
14
|
+
export { ConnectWalletPrompt } from './components/ConnectWalletPrompt';
|
|
11
15
|
|
|
12
16
|
// Hooks
|
|
13
17
|
export { useRWA } from './hooks/useRWA';
|
package/src/styles/index.css
CHANGED
|
@@ -1,28 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mantle-rwa/react - Default Styles
|
|
3
|
+
*
|
|
4
|
+
* Tailwind CSS styles for RWA React components.
|
|
5
|
+
* Import this file in your application to use default styling.
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
@tailwind base;
|
|
2
9
|
@tailwind components;
|
|
3
10
|
@tailwind utilities;
|
|
4
11
|
|
|
5
|
-
/*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
/* Base component styles */
|
|
13
|
+
@layer components {
|
|
14
|
+
|
|
15
|
+
/* KYC Flow */
|
|
16
|
+
.rwa-kyc-flow {
|
|
17
|
+
@apply w-full;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Investor Dashboard */
|
|
21
|
+
.rwa-investor-dashboard {
|
|
22
|
+
@apply w-full;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Token Mint Form */
|
|
26
|
+
.rwa-token-mint-form {
|
|
27
|
+
@apply w-full max-w-md;
|
|
28
|
+
}
|
|
9
29
|
|
|
10
|
-
|
|
11
|
-
|
|
30
|
+
/* Yield Calculator */
|
|
31
|
+
.rwa-yield-calculator {
|
|
32
|
+
@apply w-full;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Error Display */
|
|
36
|
+
.rwa-error-display {
|
|
37
|
+
@apply w-full;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Loading Spinner */
|
|
41
|
+
.rwa-loading-spinner {
|
|
42
|
+
@apply inline-flex items-center justify-center;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Connect Wallet Prompt */
|
|
46
|
+
.rwa-connect-wallet-prompt {
|
|
47
|
+
@apply w-full;
|
|
48
|
+
}
|
|
12
49
|
}
|
|
13
50
|
|
|
14
|
-
|
|
15
|
-
|
|
51
|
+
/* Dark mode support */
|
|
52
|
+
@layer utilities {
|
|
53
|
+
|
|
54
|
+
.dark .rwa-kyc-flow,
|
|
55
|
+
.dark .rwa-investor-dashboard,
|
|
56
|
+
.dark .rwa-token-mint-form,
|
|
57
|
+
.dark .rwa-yield-calculator {
|
|
58
|
+
@apply text-gray-100;
|
|
59
|
+
}
|
|
16
60
|
}
|
|
17
61
|
|
|
18
|
-
|
|
19
|
-
|
|
62
|
+
/* Custom animations */
|
|
63
|
+
@keyframes rwa-spin {
|
|
64
|
+
to {
|
|
65
|
+
transform: rotate(360deg);
|
|
66
|
+
}
|
|
20
67
|
}
|
|
21
68
|
|
|
22
|
-
.rwa-
|
|
23
|
-
|
|
69
|
+
.rwa-animate-spin {
|
|
70
|
+
animation: rwa-spin 1s linear infinite;
|
|
24
71
|
}
|
|
25
72
|
|
|
26
|
-
|
|
27
|
-
|
|
73
|
+
/* Focus styles for accessibility */
|
|
74
|
+
@layer utilities {
|
|
75
|
+
.rwa-focus-ring {
|
|
76
|
+
@apply focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.dark .rwa-focus-ring {
|
|
80
|
+
@apply focus:ring-offset-gray-900;
|
|
81
|
+
}
|
|
28
82
|
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for @mantle-rwa/react
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Re-export relevant types from SDK
|
|
6
|
+
export {
|
|
7
|
+
AccreditationTier,
|
|
8
|
+
type TokenInfo,
|
|
9
|
+
type InvestorData,
|
|
10
|
+
type TransactionResult,
|
|
11
|
+
type Distribution,
|
|
12
|
+
type DistributionPreview,
|
|
13
|
+
type HolderDistribution,
|
|
14
|
+
type PendingClaim,
|
|
15
|
+
type VerificationSession,
|
|
16
|
+
type NetworkConfig,
|
|
17
|
+
} from '@mantle-rwa/sdk';
|
|
18
|
+
|
|
19
|
+
import type { RWAClient, TransactionResult, AccreditationTier, DistributionPreview } from '@mantle-rwa/sdk';
|
|
20
|
+
|
|
21
|
+
/*//////////////////////////////////////////////////////////////
|
|
22
|
+
CONTRACT ADDRESSES
|
|
23
|
+
//////////////////////////////////////////////////////////////*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Contract addresses configuration
|
|
27
|
+
*/
|
|
28
|
+
export interface ContractAddresses {
|
|
29
|
+
/** RWA token contract address */
|
|
30
|
+
token?: string;
|
|
31
|
+
/** KYC registry contract address */
|
|
32
|
+
kycRegistry?: string;
|
|
33
|
+
/** Yield distributor contract address */
|
|
34
|
+
yieldDistributor?: string;
|
|
35
|
+
/** Asset vault contract address */
|
|
36
|
+
assetVault?: string;
|
|
37
|
+
/** RWA factory contract address */
|
|
38
|
+
factory?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/*//////////////////////////////////////////////////////////////
|
|
42
|
+
HOOK TYPES
|
|
43
|
+
//////////////////////////////////////////////////////////////*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Configuration for useRWA hook
|
|
47
|
+
*/
|
|
48
|
+
export interface UseRWAConfig {
|
|
49
|
+
/** Network to connect to (default: 'mantle-sepolia') */
|
|
50
|
+
network?: 'mantle' | 'mantle-sepolia';
|
|
51
|
+
/** Contract addresses configuration */
|
|
52
|
+
contracts?: ContractAddresses;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Return type for useRWA hook
|
|
57
|
+
*/
|
|
58
|
+
export interface UseRWAReturn {
|
|
59
|
+
/** The RWAClient instance (null if not initialized) */
|
|
60
|
+
client: RWAClient | null;
|
|
61
|
+
/** Whether the client is initialized and ready */
|
|
62
|
+
isInitialized: boolean;
|
|
63
|
+
/** Whether the client is currently initializing */
|
|
64
|
+
isLoading: boolean;
|
|
65
|
+
/** Error that occurred during initialization */
|
|
66
|
+
error: Error | null;
|
|
67
|
+
/** Whether a signer is available (wallet connected) */
|
|
68
|
+
hasSigner: boolean;
|
|
69
|
+
/** Contract addresses */
|
|
70
|
+
contracts: ContractAddresses;
|
|
71
|
+
/** Reinitialize the client */
|
|
72
|
+
reinitialize: () => Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*//////////////////////////////////////////////////////////////
|
|
76
|
+
COMPONENT PROPS
|
|
77
|
+
//////////////////////////////////////////////////////////////*/
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Verification status for KYC flow
|
|
81
|
+
*/
|
|
82
|
+
export type VerificationStatus = 'idle' | 'pending' | 'in_progress' | 'completed' | 'failed';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Props for KYCFlow component
|
|
86
|
+
*/
|
|
87
|
+
export interface KYCFlowProps {
|
|
88
|
+
/** KYC registry contract address */
|
|
89
|
+
registryAddress: string;
|
|
90
|
+
/** Investor address to verify (defaults to connected wallet) */
|
|
91
|
+
investorAddress?: string;
|
|
92
|
+
/** Callback when verification status changes */
|
|
93
|
+
onStatusChange?: (status: VerificationStatus) => void;
|
|
94
|
+
/** Callback when verification completes successfully */
|
|
95
|
+
onComplete?: (tier: AccreditationTier) => void;
|
|
96
|
+
/** Callback when an error occurs */
|
|
97
|
+
onError?: (error: Error) => void;
|
|
98
|
+
/** Custom CSS class */
|
|
99
|
+
className?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Props for InvestorDashboard component
|
|
104
|
+
*/
|
|
105
|
+
export interface InvestorDashboardProps {
|
|
106
|
+
/** RWA token contract address */
|
|
107
|
+
tokenAddress: string;
|
|
108
|
+
/** KYC registry contract address */
|
|
109
|
+
kycRegistryAddress: string;
|
|
110
|
+
/** Yield distributor contract address */
|
|
111
|
+
yieldDistributorAddress: string;
|
|
112
|
+
/** Investor address (defaults to connected wallet) */
|
|
113
|
+
investorAddress?: string;
|
|
114
|
+
/** Callback when yield claim succeeds */
|
|
115
|
+
onClaimSuccess?: (result: TransactionResult) => void;
|
|
116
|
+
/** Callback when an error occurs */
|
|
117
|
+
onError?: (error: Error) => void;
|
|
118
|
+
/** Custom CSS class */
|
|
119
|
+
className?: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Props for TokenMintForm component
|
|
124
|
+
*/
|
|
125
|
+
export interface TokenMintFormProps {
|
|
126
|
+
/** RWA token contract address */
|
|
127
|
+
tokenAddress: string;
|
|
128
|
+
/** KYC registry contract address */
|
|
129
|
+
kycRegistryAddress: string;
|
|
130
|
+
/** Callback when mint succeeds */
|
|
131
|
+
onSuccess?: (result: TransactionResult) => void;
|
|
132
|
+
/** Callback when an error occurs */
|
|
133
|
+
onError?: (error: Error) => void;
|
|
134
|
+
/** Custom CSS class */
|
|
135
|
+
className?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Props for YieldCalculator component
|
|
140
|
+
*/
|
|
141
|
+
export interface YieldCalculatorProps {
|
|
142
|
+
/** RWA token contract address */
|
|
143
|
+
tokenAddress: string;
|
|
144
|
+
/** Yield distributor contract address */
|
|
145
|
+
yieldDistributorAddress: string;
|
|
146
|
+
/** Optional list of holder addresses to include in preview */
|
|
147
|
+
holderAddresses?: string[];
|
|
148
|
+
/** Callback when calculation completes */
|
|
149
|
+
onCalculate?: (preview: DistributionPreview) => void;
|
|
150
|
+
/** Callback when an error occurs */
|
|
151
|
+
onError?: (error: Error) => void;
|
|
152
|
+
/** Custom CSS class */
|
|
153
|
+
className?: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/*//////////////////////////////////////////////////////////////
|
|
157
|
+
UTILITY TYPES
|
|
158
|
+
//////////////////////////////////////////////////////////////*/
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Common loading state interface
|
|
162
|
+
*/
|
|
163
|
+
export interface LoadingState {
|
|
164
|
+
/** Whether data is loading */
|
|
165
|
+
isLoading: boolean;
|
|
166
|
+
/** Error that occurred */
|
|
167
|
+
error: Error | null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Transaction state interface
|
|
172
|
+
*/
|
|
173
|
+
export interface TransactionState extends LoadingState {
|
|
174
|
+
/** Whether a transaction is pending */
|
|
175
|
+
isPending: boolean;
|
|
176
|
+
/** Transaction hash if available */
|
|
177
|
+
txHash: string | null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Props for ErrorDisplay component
|
|
182
|
+
*/
|
|
183
|
+
export interface ErrorDisplayProps {
|
|
184
|
+
/** Error to display */
|
|
185
|
+
error: Error;
|
|
186
|
+
/** Callback for retry action */
|
|
187
|
+
onRetry?: () => void;
|
|
188
|
+
/** Custom CSS class */
|
|
189
|
+
className?: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Props for LoadingSpinner component
|
|
194
|
+
*/
|
|
195
|
+
export interface LoadingSpinnerProps {
|
|
196
|
+
/** Size of the spinner */
|
|
197
|
+
size?: 'sm' | 'md' | 'lg';
|
|
198
|
+
/** Custom CSS class */
|
|
199
|
+
className?: string;
|
|
200
|
+
}
|