cilantro-react 0.1.4 → 0.1.6
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 +965 -518
- package/dist/index.d.mts +506 -554
- package/dist/index.d.ts +506 -554
- package/dist/index.js +2471 -2525
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2475 -2532
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,800 +1,1247 @@
|
|
|
1
1
|
# cilantro-react
|
|
2
2
|
|
|
3
|
-
React SDK/UI for [Cilantro Smart Wallet](https://www.npmjs.com/package/cilantro-sdk). Provides
|
|
3
|
+
React SDK/UI for [Cilantro Smart Wallet](https://www.npmjs.com/package/cilantro-sdk). Provides a single context provider, hooks, and UI components for authentication, wallets, signers, and transactions.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [cilantro-react](#cilantro-react)
|
|
8
|
+
- [Table of Contents](#table-of-contents)
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Polyfills (Browser)](#polyfills-browser)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [CilantroProvider](#cilantroprovider)
|
|
13
|
+
- [Props](#props)
|
|
14
|
+
- [Storage Adapters](#storage-adapters)
|
|
15
|
+
- [Hooks](#hooks)
|
|
16
|
+
- [useAuth](#useauth)
|
|
17
|
+
- [useWallet](#usewallet)
|
|
18
|
+
- [useSigners](#usesigners)
|
|
19
|
+
- [usePasskey](#usepasskey)
|
|
20
|
+
- [useExternalWallet](#useexternalwallet)
|
|
21
|
+
- [useSendTransaction](#usesendtransaction)
|
|
22
|
+
- [useCilantroConfig](#usecilantroconfig)
|
|
23
|
+
- [Components](#components)
|
|
24
|
+
- [Auth Components](#auth-components)
|
|
25
|
+
- [LoginForm](#loginform)
|
|
26
|
+
- [LogoutButton](#logoutbutton)
|
|
27
|
+
- [AuthGuard](#authguard)
|
|
28
|
+
- [Wallet Components](#wallet-components)
|
|
29
|
+
- [WalletSelector](#walletselector)
|
|
30
|
+
- [CreateWalletForm](#createwalletform)
|
|
31
|
+
- [WalletAddress](#walletaddress)
|
|
32
|
+
- [WalletBalance](#walletbalance)
|
|
33
|
+
- [ConnectWalletButton](#connectwalletbutton)
|
|
34
|
+
- [Signer Components](#signer-components)
|
|
35
|
+
- [SignerSelector](#signerselector)
|
|
36
|
+
- [SignerList](#signerlist)
|
|
37
|
+
- [CreateEmailSignerForm](#createemailsignerform)
|
|
38
|
+
- [CreatePhoneSignerForm](#createphonesignerform)
|
|
39
|
+
- [AddPasskeyButton](#addpasskeybutton)
|
|
40
|
+
- [Transaction Components](#transaction-components)
|
|
41
|
+
- [SendSOLForm](#sendsolform)
|
|
42
|
+
- [SendSPLForm](#sendsplform)
|
|
43
|
+
- [TransactionStatus](#transactionstatus)
|
|
44
|
+
- [TransactionHistory](#transactionhistory)
|
|
45
|
+
- [Layout Components](#layout-components)
|
|
46
|
+
- [CilantroConnect](#cilantroconnect)
|
|
47
|
+
- [LoadingOverlay](#loadingoverlay)
|
|
48
|
+
- [ErrorBoundary](#errorboundary)
|
|
49
|
+
- [Theming](#theming)
|
|
50
|
+
- [Built-in Theme (ThemeProvider)](#built-in-theme-themeprovider)
|
|
51
|
+
- [Import Theme CSS Directly](#import-theme-css-directly)
|
|
52
|
+
- [Advanced Usage](#advanced-usage)
|
|
53
|
+
- [Low-Level Signer Signing](#low-level-signer-signing)
|
|
54
|
+
- [Solana Connection Adapter](#solana-connection-adapter)
|
|
55
|
+
- [Utilities](#utilities)
|
|
56
|
+
- [Types](#types)
|
|
57
|
+
- [License](#license)
|
|
4
58
|
|
|
5
|
-
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
6
62
|
|
|
7
63
|
```bash
|
|
8
|
-
npm install cilantro-react cilantro-sdk
|
|
64
|
+
npm install cilantro-react cilantro-sdk
|
|
65
|
+
# or
|
|
66
|
+
yarn add cilantro-react cilantro-sdk
|
|
67
|
+
# or
|
|
68
|
+
pnpm add cilantro-react cilantro-sdk
|
|
9
69
|
```
|
|
10
70
|
|
|
11
|
-
**
|
|
71
|
+
**Requirements:**
|
|
12
72
|
|
|
13
|
-
|
|
73
|
+
- React 18+
|
|
74
|
+
- `cilantro-sdk` (peer dependency)
|
|
75
|
+
- [Tailwind CSS](https://tailwindcss.com) for component styling (components use Tailwind-compatible class names)
|
|
14
76
|
|
|
15
|
-
|
|
77
|
+
### Polyfills (Browser)
|
|
16
78
|
|
|
17
|
-
Import
|
|
79
|
+
Import polyfills at the very top of your entry file (before any other imports):
|
|
18
80
|
|
|
19
81
|
```ts
|
|
20
|
-
import 'cilantro-sdk/polyfills'
|
|
82
|
+
import 'cilantro-sdk/polyfills';
|
|
21
83
|
```
|
|
22
84
|
|
|
23
|
-
|
|
85
|
+
---
|
|
24
86
|
|
|
25
|
-
|
|
87
|
+
## Quick Start
|
|
26
88
|
|
|
27
89
|
```tsx
|
|
28
|
-
import
|
|
90
|
+
import 'cilantro-sdk/polyfills';
|
|
91
|
+
import { CilantroProvider, LoginForm, AuthGuard, useAuth, useWallet } from 'cilantro-react';
|
|
29
92
|
|
|
30
|
-
|
|
93
|
+
function App() {
|
|
31
94
|
return (
|
|
32
|
-
<CilantroProvider
|
|
33
|
-
{
|
|
95
|
+
<CilantroProvider
|
|
96
|
+
apiKey={import.meta.env.VITE_API_KEY}
|
|
97
|
+
baseURL="https://api.cilantro.gg"
|
|
98
|
+
>
|
|
99
|
+
<AuthGuard>
|
|
100
|
+
<Dashboard />
|
|
101
|
+
</AuthGuard>
|
|
34
102
|
</CilantroProvider>
|
|
35
|
-
)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function Dashboard() {
|
|
107
|
+
const { user, logout } = useAuth();
|
|
108
|
+
const { wallet, wallets } = useWallet();
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<div>
|
|
112
|
+
<p>Welcome, {user?.username}</p>
|
|
113
|
+
<p>Wallet: {wallet?.walletName}</p>
|
|
114
|
+
<button onClick={logout}>Logout</button>
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
36
117
|
}
|
|
37
118
|
```
|
|
38
119
|
|
|
39
|
-
|
|
120
|
+
---
|
|
40
121
|
|
|
41
|
-
|
|
42
|
-
|------|------|-------------|
|
|
43
|
-
| `platformApiKey` | `string` | **Required.** Your Cilantro platform API key. |
|
|
44
|
-
| `apiUrl` | `string` | Optional API base URL. |
|
|
45
|
-
| `jwtStorageKey` | `string` | localStorage key for JWT (default: `cilantro_jwt`). |
|
|
46
|
-
| `walletStorageKey` | `string` | localStorage key for selected wallet id (default: `cilantro_selected_wallet_id`). |
|
|
47
|
-
| `onLoginSuccess` | `() => void` | Callback after successful login. |
|
|
48
|
-
| `onLogout` | `() => void` | Callback after logout. |
|
|
49
|
-
| `onRegisterSuccess` | `() => void` | Callback after successful registration. |
|
|
122
|
+
## CilantroProvider
|
|
50
123
|
|
|
51
|
-
|
|
124
|
+
The root provider that initializes configuration, storage, authentication, and wallet state.
|
|
52
125
|
|
|
53
|
-
|
|
126
|
+
```tsx
|
|
127
|
+
import { CilantroProvider, createIndexedDBAdapter } from 'cilantro-react';
|
|
54
128
|
|
|
55
|
-
|
|
129
|
+
const storage = createIndexedDBAdapter();
|
|
56
130
|
|
|
57
|
-
|
|
131
|
+
<CilantroProvider
|
|
132
|
+
apiKey="your-platform-api-key"
|
|
133
|
+
baseURL="https://api.cilantro.gg"
|
|
134
|
+
storageAdapter={storage}
|
|
135
|
+
>
|
|
136
|
+
<App />
|
|
137
|
+
</CilantroProvider>
|
|
138
|
+
```
|
|
58
139
|
|
|
59
|
-
|
|
60
|
-
- **CilantroAuthProvider** – Auth only (token, user, login, register, logout). Use when you don’t need the full provider stack (e.g. auth-only pages).
|
|
61
|
-
- **WalletProvider** – Wallet list and selection. Depends on auth; usually used inside `CilantroProvider`.
|
|
62
|
-
- **ThemeProvider** – Optional: sets `data-theme="light"` or `"dark"` on a wrapper and injects default CSS variables for light/dark. Props: `theme` (`"light"` | `"dark"` | `"system"`), `defaultTheme`, `storageKey`, `className`, `injectStyles`. See [Themes](#built-in-theme-themeprovider).
|
|
140
|
+
### Props
|
|
63
141
|
|
|
64
|
-
|
|
142
|
+
| Prop | Type | Required | Default | Description |
|
|
143
|
+
|------|------|----------|---------|-------------|
|
|
144
|
+
| `apiKey` | `string` | No* | — | Platform API key for server-side operations |
|
|
145
|
+
| `baseURL` | `string` | No | `https://api.cilantro.gg` | API base URL |
|
|
146
|
+
| `storageAdapter` | `DeviceKeyStorage` | No | IndexedDB | Storage adapter for device keys |
|
|
147
|
+
| `children` | `ReactNode` | Yes | — | App content |
|
|
65
148
|
|
|
66
|
-
|
|
149
|
+
*Either `apiKey` or JWT (obtained via login) is required for authenticated requests.
|
|
67
150
|
|
|
68
|
-
|
|
151
|
+
### Storage Adapters
|
|
69
152
|
|
|
70
|
-
|
|
153
|
+
For email and phone signers, device keys must be stored. Choose an adapter:
|
|
71
154
|
|
|
72
|
-
|
|
155
|
+
| Adapter | Use Case |
|
|
156
|
+
|---------|----------|
|
|
157
|
+
| `createIndexedDBAdapter()` | **Production** – persistent, large capacity |
|
|
158
|
+
| `createLocalStorageAdapter()` | Development – simple, limited capacity |
|
|
159
|
+
| `createMemoryAdapter()` | Testing – not persisted |
|
|
73
160
|
|
|
74
|
-
|
|
161
|
+
```tsx
|
|
162
|
+
import { createIndexedDBAdapter, createLocalStorageAdapter, createMemoryAdapter } from 'cilantro-react';
|
|
75
163
|
|
|
76
|
-
|
|
164
|
+
// Production
|
|
165
|
+
const storage = createIndexedDBAdapter();
|
|
77
166
|
|
|
78
|
-
|
|
167
|
+
// Development
|
|
168
|
+
const storage = createLocalStorageAdapter();
|
|
79
169
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
| `user` | `User \| null` | User object: `{ username?, email?, userType? }`. |
|
|
84
|
-
| `isAuthenticated` | `boolean` | True when `token` is set. |
|
|
85
|
-
| `isLoading` | `boolean` | True while restoring session from storage. |
|
|
86
|
-
| `login` | `(usernameOrEmail: string, password: string) => Promise<void>` | Log in; throws on error. |
|
|
87
|
-
| `register` | `(username: string, email: string, password: string, isActive?: boolean) => Promise<void>` | Register and log in; throws on error. |
|
|
88
|
-
| `logout` | `() => void` | Clear token and user; calls `onLogout` if provided. |
|
|
170
|
+
// Testing
|
|
171
|
+
const storage = createMemoryAdapter();
|
|
172
|
+
```
|
|
89
173
|
|
|
90
174
|
---
|
|
91
175
|
|
|
92
|
-
|
|
176
|
+
## Hooks
|
|
93
177
|
|
|
94
|
-
|
|
178
|
+
### useAuth
|
|
95
179
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
| Property | Type | Description |
|
|
99
|
-
|----------|------|-------------|
|
|
100
|
-
| `wallets` | `WalletData[]` | List of wallets for the authenticated user. |
|
|
101
|
-
| `selectedWallet` | `WalletData \| null` | Currently selected wallet (persisted in localStorage). |
|
|
102
|
-
| `selectWallet` | `(walletId: string) => void` | Set the selected wallet by id. |
|
|
103
|
-
| `refreshWallets` | `() => Promise<void>` | Reload wallets from the API. |
|
|
104
|
-
| `isLoading` | `boolean` | True while loading wallets. |
|
|
180
|
+
Authentication state and actions.
|
|
105
181
|
|
|
106
|
-
|
|
182
|
+
```tsx
|
|
183
|
+
import { useAuth } from 'cilantro-react';
|
|
107
184
|
|
|
108
|
-
|
|
185
|
+
function MyComponent() {
|
|
186
|
+
const { user, jwt, isLoading, isAuthenticated, login, logout } = useAuth();
|
|
109
187
|
|
|
110
|
-
|
|
188
|
+
const handleLogin = async () => {
|
|
189
|
+
await login({ usernameOrEmail: 'user@example.com', password: 'password123' });
|
|
190
|
+
};
|
|
111
191
|
|
|
112
|
-
|
|
192
|
+
return (
|
|
193
|
+
<div>
|
|
194
|
+
{isLoading && <p>Loading...</p>}
|
|
195
|
+
{isAuthenticated ? (
|
|
196
|
+
<>
|
|
197
|
+
<p>Hello, {user?.username}</p>
|
|
198
|
+
<button onClick={logout}>Logout</button>
|
|
199
|
+
</>
|
|
200
|
+
) : (
|
|
201
|
+
<button onClick={handleLogin}>Login</button>
|
|
202
|
+
)}
|
|
203
|
+
</div>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
```
|
|
113
207
|
|
|
114
|
-
**Returns
|
|
208
|
+
**Returns: `UseAuthResult`**
|
|
115
209
|
|
|
116
210
|
| Property | Type | Description |
|
|
117
211
|
|----------|------|-------------|
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
|
|
122
|
-
|
|
212
|
+
| `user` | `User \| null` | Current user object (`{ username?, email?, userType? }`) |
|
|
213
|
+
| `jwt` | `string \| null` | JWT token (null when not authenticated) |
|
|
214
|
+
| `isLoading` | `boolean` | True while restoring session from storage |
|
|
215
|
+
| `isAuthenticated` | `boolean` | True when user has valid JWT |
|
|
216
|
+
| `login` | `(params: { usernameOrEmail: string; password: string }) => Promise<void>` | Log in; throws on error |
|
|
217
|
+
| `logout` | `() => void` | Clear session and token |
|
|
123
218
|
|
|
124
219
|
---
|
|
125
220
|
|
|
126
|
-
|
|
221
|
+
### useWallet
|
|
222
|
+
|
|
223
|
+
Wallet state and actions.
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
import { useWallet } from 'cilantro-react';
|
|
127
227
|
|
|
128
|
-
|
|
228
|
+
function WalletManager() {
|
|
229
|
+
const { wallet, wallets, createWallet, selectWallet, isLoading } = useWallet();
|
|
129
230
|
|
|
130
|
-
|
|
231
|
+
const handleCreate = async () => {
|
|
232
|
+
const result = await createWallet({ name: 'My New Wallet' });
|
|
233
|
+
console.log('Created:', result.data.id);
|
|
234
|
+
};
|
|
131
235
|
|
|
132
|
-
|
|
236
|
+
return (
|
|
237
|
+
<div>
|
|
238
|
+
{isLoading && <p>Loading wallets...</p>}
|
|
239
|
+
<p>Current wallet: {wallet?.walletName ?? 'None selected'}</p>
|
|
240
|
+
<ul>
|
|
241
|
+
{wallets.map((w) => (
|
|
242
|
+
<li key={w.id} onClick={() => selectWallet(w.id)}>
|
|
243
|
+
{w.walletName}
|
|
244
|
+
</li>
|
|
245
|
+
))}
|
|
246
|
+
</ul>
|
|
247
|
+
<button onClick={handleCreate}>Create Wallet</button>
|
|
248
|
+
</div>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Returns: `UseWalletResult`**
|
|
254
|
+
|
|
255
|
+
| Property | Type | Description |
|
|
256
|
+
|----------|------|-------------|
|
|
257
|
+
| `wallet` | `WalletData \| null` | Currently selected wallet |
|
|
258
|
+
| `wallets` | `WalletData[]` | All wallets for the authenticated user |
|
|
259
|
+
| `createWallet` | `(params: { name: string; userId?: string }) => Promise<WalletControllerCreateResult>` | Create a new wallet |
|
|
260
|
+
| `selectWallet` | `(walletId: string) => void` | Select a wallet by ID |
|
|
261
|
+
| `isLoading` | `boolean` | True while loading wallets |
|
|
133
262
|
|
|
134
263
|
---
|
|
135
264
|
|
|
136
|
-
|
|
265
|
+
### useSigners
|
|
266
|
+
|
|
267
|
+
Signers for a specific wallet.
|
|
137
268
|
|
|
138
|
-
|
|
269
|
+
```tsx
|
|
270
|
+
import { useSigners } from 'cilantro-react';
|
|
271
|
+
|
|
272
|
+
function SignerManager({ walletId }: { walletId: string }) {
|
|
273
|
+
const {
|
|
274
|
+
signers,
|
|
275
|
+
isLoading,
|
|
276
|
+
error,
|
|
277
|
+
refresh,
|
|
278
|
+
createEmailSigner,
|
|
279
|
+
createPhoneSigner,
|
|
280
|
+
revokeSigner,
|
|
281
|
+
} = useSigners(walletId);
|
|
282
|
+
|
|
283
|
+
const handleCreateEmail = async () => {
|
|
284
|
+
const signer = await createEmailSigner({ email: 'user@example.com' });
|
|
285
|
+
console.log('Created signer:', signer.signerId);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const handleCreatePhone = async () => {
|
|
289
|
+
const signer = await createPhoneSigner({ phone: '+1234567890' });
|
|
290
|
+
console.log('Created signer:', signer.signerId);
|
|
291
|
+
};
|
|
139
292
|
|
|
140
|
-
|
|
293
|
+
return (
|
|
294
|
+
<div>
|
|
295
|
+
{isLoading && <p>Loading signers...</p>}
|
|
296
|
+
{error && <p className="text-red-500">{error}</p>}
|
|
297
|
+
<ul>
|
|
298
|
+
{signers.map((s) => (
|
|
299
|
+
<li key={s.id}>
|
|
300
|
+
{s.email || s.phone || s.id} ({s.signerType})
|
|
301
|
+
<button onClick={() => revokeSigner(s.id)}>Revoke</button>
|
|
302
|
+
</li>
|
|
303
|
+
))}
|
|
304
|
+
</ul>
|
|
305
|
+
<button onClick={handleCreateEmail}>Add Email Signer</button>
|
|
306
|
+
<button onClick={handleCreatePhone}>Add Phone Signer</button>
|
|
307
|
+
<button onClick={refresh}>Refresh</button>
|
|
308
|
+
</div>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
```
|
|
141
312
|
|
|
142
|
-
|
|
143
|
-
|--------|------|-------------|
|
|
144
|
-
| `walletId` | `string \| null \| undefined` | Wallet ID to load signers for. If omitted, you can pass it from `useWallets().selectedWallet?.id`. |
|
|
313
|
+
**Parameters:**
|
|
145
314
|
|
|
146
|
-
|
|
315
|
+
- `walletId: string | null | { walletId?: string | null }` — Wallet ID to load signers for
|
|
316
|
+
|
|
317
|
+
**Returns: `UseSignersResult`**
|
|
147
318
|
|
|
148
319
|
| Property | Type | Description |
|
|
149
320
|
|----------|------|-------------|
|
|
150
|
-
| `signers` | `SignerData[]` | List of signers for the wallet
|
|
151
|
-
| `isLoading` | `boolean` | True while loading
|
|
152
|
-
| `error` | `string \| null` | Error message if load failed
|
|
153
|
-
| `refresh` | `() => Promise<void>` | Reload signers
|
|
154
|
-
|
|
155
|
-
|
|
321
|
+
| `signers` | `SignerData[]` | List of signers for the wallet |
|
|
322
|
+
| `isLoading` | `boolean` | True while loading |
|
|
323
|
+
| `error` | `string \| null` | Error message if load failed |
|
|
324
|
+
| `refresh` | `() => Promise<void>` | Reload signers |
|
|
325
|
+
| `createEmailSigner` | `(params: { email: string }) => Promise<SignerInfo>` | Create email signer |
|
|
326
|
+
| `createPhoneSigner` | `(params: { phone: string }) => Promise<SignerInfo>` | Create phone signer |
|
|
327
|
+
| `revokeSigner` | `(signerId: string) => Promise<void>` | Revoke a signer |
|
|
156
328
|
|
|
157
329
|
---
|
|
158
330
|
|
|
159
|
-
|
|
331
|
+
### usePasskey
|
|
160
332
|
|
|
161
|
-
|
|
333
|
+
Passkey (WebAuthn) registration and authentication.
|
|
162
334
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
| Option | Type | Description |
|
|
166
|
-
|--------|------|-------------|
|
|
167
|
-
| `walletId` | `string \| null \| undefined` | Override; when omitted, uses the selected wallet from context. |
|
|
335
|
+
```tsx
|
|
336
|
+
import { usePasskey } from 'cilantro-react';
|
|
168
337
|
|
|
169
|
-
|
|
338
|
+
function PasskeyManager({ walletId }: { walletId: string }) {
|
|
339
|
+
const { isSupported, register, authenticate, isLoading } = usePasskey(walletId);
|
|
170
340
|
|
|
171
|
-
|
|
341
|
+
if (!isSupported) {
|
|
342
|
+
return <p>Passkeys not supported in this browser.</p>;
|
|
343
|
+
}
|
|
172
344
|
|
|
173
|
-
|
|
345
|
+
const handleRegister = async () => {
|
|
346
|
+
const result = await register();
|
|
347
|
+
console.log('Passkey registered:', result.signerId);
|
|
348
|
+
};
|
|
174
349
|
|
|
175
|
-
|
|
350
|
+
const handleAuthenticate = async () => {
|
|
351
|
+
const result = await authenticate();
|
|
352
|
+
console.log('Authenticated with passkey');
|
|
353
|
+
};
|
|
176
354
|
|
|
177
|
-
|
|
355
|
+
return (
|
|
356
|
+
<div>
|
|
357
|
+
<button onClick={handleRegister} disabled={isLoading}>
|
|
358
|
+
{isLoading ? 'Registering...' : 'Register Passkey'}
|
|
359
|
+
</button>
|
|
360
|
+
<button onClick={handleAuthenticate} disabled={isLoading}>
|
|
361
|
+
{isLoading ? 'Authenticating...' : 'Authenticate'}
|
|
362
|
+
</button>
|
|
363
|
+
</div>
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
```
|
|
178
367
|
|
|
179
|
-
**
|
|
368
|
+
**Parameters:**
|
|
180
369
|
|
|
181
|
-
|
|
182
|
-
|--------|------|-------------|
|
|
183
|
-
| `walletId` | `string \| null \| undefined` | Override wallet ID; if not set, uses `useWallets().selectedWallet?.id`. |
|
|
184
|
-
| `signingMethod` | `'wallet-adapter' \| 'sdk-signer' \| null` | Current signing method (default: `'sdk-signer'`). When `'wallet-adapter'`, signer selection is ignored. |
|
|
370
|
+
- `walletId: string | undefined` — Wallet ID to register passkey for
|
|
185
371
|
|
|
186
|
-
**Returns
|
|
372
|
+
**Returns: `UsePasskeyResult`**
|
|
187
373
|
|
|
188
374
|
| Property | Type | Description |
|
|
189
375
|
|----------|------|-------------|
|
|
190
|
-
| `
|
|
191
|
-
| `
|
|
192
|
-
| `
|
|
193
|
-
| `
|
|
194
|
-
| `setSelectedSigner` | `(signer: SignerData \| null) => void` | Set selected signer. |
|
|
195
|
-
| `isLoadingSigners` | `boolean` | True while signers are loading. |
|
|
196
|
-
| `reset` | `() => void` | Clear `selectedWalletId` and `selectedSigner`. |
|
|
197
|
-
|
|
198
|
-
**SigningMethod:** `'wallet-adapter'` = use wallet adapter for signing; `'sdk-signer'` = use a Cilantro signer (email, phone, passkey, external, etc.).
|
|
376
|
+
| `isSupported` | `boolean` | True if WebAuthn is available |
|
|
377
|
+
| `register` | `() => Promise<{ signerId: string; isNew: boolean }>` | Register a new passkey |
|
|
378
|
+
| `authenticate` | `(signerId?: string) => Promise<{ credential: unknown }>` | Authenticate with passkey |
|
|
379
|
+
| `isLoading` | `boolean` | True during operation |
|
|
199
380
|
|
|
200
381
|
---
|
|
201
382
|
|
|
202
|
-
|
|
383
|
+
### useExternalWallet
|
|
384
|
+
|
|
385
|
+
Connect to external Solana wallets (Phantom, Solflare, etc.).
|
|
203
386
|
|
|
204
|
-
|
|
387
|
+
```tsx
|
|
388
|
+
import { useExternalWallet } from 'cilantro-react';
|
|
205
389
|
|
|
206
|
-
|
|
390
|
+
function ExternalWalletConnect() {
|
|
391
|
+
const { wallets, connectedWallet, connect, disconnect } = useExternalWallet();
|
|
207
392
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
393
|
+
return (
|
|
394
|
+
<div>
|
|
395
|
+
{connectedWallet ? (
|
|
396
|
+
<div>
|
|
397
|
+
<p>Connected: {connectedWallet.name}</p>
|
|
398
|
+
<p>Public Key: {connectedWallet.publicKey?.toBase58()}</p>
|
|
399
|
+
<button onClick={disconnect}>Disconnect</button>
|
|
400
|
+
</div>
|
|
401
|
+
) : (
|
|
402
|
+
<div>
|
|
403
|
+
{wallets.map((wallet) => (
|
|
404
|
+
<button key={wallet.name} onClick={() => connect(wallet)}>
|
|
405
|
+
Connect {wallet.name}
|
|
406
|
+
</button>
|
|
407
|
+
))}
|
|
408
|
+
</div>
|
|
409
|
+
)}
|
|
410
|
+
</div>
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
```
|
|
212
414
|
|
|
213
|
-
**Returns
|
|
415
|
+
**Returns: `UseExternalWalletResult`**
|
|
214
416
|
|
|
215
417
|
| Property | Type | Description |
|
|
216
418
|
|----------|------|-------------|
|
|
217
|
-
| `
|
|
218
|
-
| `
|
|
219
|
-
| `
|
|
220
|
-
| `
|
|
221
|
-
|
|
222
|
-
**Use when:** You need the list of delegated keys in custom UI or logic without using the DelegatedKeySelector component.
|
|
419
|
+
| `wallets` | `SolanaWallet[]` | Detected Solana wallets (Phantom, Solflare, etc.) |
|
|
420
|
+
| `connectedWallet` | `SolanaWallet \| null` | Currently connected wallet |
|
|
421
|
+
| `connect` | `(wallet: SolanaWallet) => Promise<void>` | Connect to a wallet |
|
|
422
|
+
| `disconnect` | `() => void` | Disconnect current wallet |
|
|
223
423
|
|
|
224
424
|
---
|
|
225
425
|
|
|
226
|
-
|
|
426
|
+
### useSendTransaction
|
|
227
427
|
|
|
228
|
-
|
|
428
|
+
Send SOL and SPL tokens.
|
|
229
429
|
|
|
230
|
-
|
|
430
|
+
```tsx
|
|
431
|
+
import { useSendTransaction } from 'cilantro-react';
|
|
432
|
+
|
|
433
|
+
function SendTransaction({ walletId, signerId }: { walletId: string; signerId: string }) {
|
|
434
|
+
const { sendSOL, sendSPL, isPending, error } = useSendTransaction(walletId, signerId, 'email');
|
|
435
|
+
|
|
436
|
+
const handleSendSOL = async () => {
|
|
437
|
+
const result = await sendSOL({
|
|
438
|
+
recipientAddress: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU',
|
|
439
|
+
amountLamports: 100000000, // 0.1 SOL
|
|
440
|
+
});
|
|
441
|
+
console.log('Transaction signature:', result.data.signature);
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
const handleSendSPL = async () => {
|
|
445
|
+
const result = await sendSPL({
|
|
446
|
+
mintAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
|
|
447
|
+
recipientAddress: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU',
|
|
448
|
+
amount: 1000000, // 1 USDC (6 decimals)
|
|
449
|
+
});
|
|
450
|
+
console.log('Transaction signature:', result.data.signature);
|
|
451
|
+
};
|
|
231
452
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
453
|
+
return (
|
|
454
|
+
<div>
|
|
455
|
+
{error && <p className="text-red-500">{error}</p>}
|
|
456
|
+
<button onClick={handleSendSOL} disabled={isPending}>
|
|
457
|
+
{isPending ? 'Sending...' : 'Send SOL'}
|
|
458
|
+
</button>
|
|
459
|
+
<button onClick={handleSendSPL} disabled={isPending}>
|
|
460
|
+
{isPending ? 'Sending...' : 'Send USDC'}
|
|
461
|
+
</button>
|
|
462
|
+
</div>
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**Parameters:**
|
|
237
468
|
|
|
238
|
-
|
|
469
|
+
- `walletId: string | undefined` — Wallet to send from
|
|
470
|
+
- `signerId?: string` — Signer ID (optional)
|
|
471
|
+
- `signerType?: 'email' | 'phone' | 'passkey' | 'external'` — Signer type (optional)
|
|
472
|
+
|
|
473
|
+
**Returns: `UseSendTransactionResult`**
|
|
239
474
|
|
|
240
475
|
| Property | Type | Description |
|
|
241
476
|
|----------|------|-------------|
|
|
242
|
-
| `
|
|
243
|
-
| `
|
|
244
|
-
| `
|
|
245
|
-
| `
|
|
246
|
-
| `canSignTransaction` | `boolean` | True when ready to sign/send a transaction (connection still required for send). |
|
|
247
|
-
|
|
248
|
-
**Use when:** You need to disable sign buttons or show "Select wallet" / "Select signer" prompts without reimplementing token/wallet/signer checks. For wallet-adapter, pass `walletAdapterConnected` from your adapter state.
|
|
477
|
+
| `sendSOL` | `(params: { recipientAddress: string; amountLamports: number }) => Promise<SubmitTransactionResult>` | Send SOL |
|
|
478
|
+
| `sendSPL` | `(params: { mintAddress: string; recipientAddress: string; amount: number }) => Promise<SubmitTransactionResult>` | Send SPL tokens |
|
|
479
|
+
| `isPending` | `boolean` | True while sending |
|
|
480
|
+
| `error` | `string \| null` | Error message if send failed |
|
|
249
481
|
|
|
250
482
|
---
|
|
251
483
|
|
|
252
|
-
|
|
484
|
+
### useCilantroConfig
|
|
253
485
|
|
|
254
|
-
|
|
486
|
+
Access SDK configuration and storage adapter.
|
|
255
487
|
|
|
256
|
-
|
|
488
|
+
```tsx
|
|
489
|
+
import { useCilantroConfig } from 'cilantro-react';
|
|
490
|
+
|
|
491
|
+
function ConfigDisplay() {
|
|
492
|
+
const { config, storage } = useCilantroConfig();
|
|
257
493
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
494
|
+
return (
|
|
495
|
+
<div>
|
|
496
|
+
<p>API Key: {config.apiKey ? '***' : 'Not set'}</p>
|
|
497
|
+
<p>Base URL: {config.baseURL}</p>
|
|
498
|
+
<p>Storage: {storage ? 'Configured' : 'Not configured'}</p>
|
|
499
|
+
</div>
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
```
|
|
266
503
|
|
|
267
|
-
**Returns
|
|
504
|
+
**Returns: `UseCilantroConfigResult`**
|
|
268
505
|
|
|
269
506
|
| Property | Type | Description |
|
|
270
507
|
|----------|------|-------------|
|
|
271
|
-
| `
|
|
272
|
-
| `
|
|
273
|
-
| `signResultState` | `ActionState` | `{ status, message?, detail? }` – idle, loading, success, or error. |
|
|
274
|
-
| `isSigning` | `boolean` | True while signing. |
|
|
275
|
-
| `handleSign` | `() => Promise<void>` | Sign the current message; updates `signResultState`. |
|
|
276
|
-
| `reset` | `() => void` | Reset message to default and clear result state. |
|
|
508
|
+
| `config` | `{ apiKey?: string; baseURL?: string; jwt?: string }` | Current configuration |
|
|
509
|
+
| `storage` | `DeviceKeyStorage \| null` | Storage adapter instance |
|
|
277
510
|
|
|
278
511
|
---
|
|
279
512
|
|
|
280
|
-
|
|
513
|
+
## Components
|
|
281
514
|
|
|
282
|
-
|
|
515
|
+
### Auth Components
|
|
283
516
|
|
|
284
|
-
|
|
517
|
+
#### LoginForm
|
|
285
518
|
|
|
286
|
-
|
|
287
|
-
|--------|------|-------------|
|
|
288
|
-
| `token` | `string \| null` | JWT (usually from `useCilantroAuth().token`). |
|
|
289
|
-
| `signingMethod` | `SigningMethod \| null` | `'sdk-signer'` or `'wallet-adapter'`. |
|
|
290
|
-
| `selectedSigner` | `SignerData \| null` | Signer when `signingMethod === 'sdk-signer'`. |
|
|
291
|
-
| `selectedWalletId` | `string` | Wallet ID when using SDK signer. |
|
|
292
|
-
| `walletAdapterSignTransaction` | `(transaction: Transaction) => Promise<Transaction>` | Optional; required for wallet-adapter signing. |
|
|
293
|
-
| `walletAdapterPublicKey` | `string \| null` | Optional; required for wallet-adapter send. |
|
|
294
|
-
| `connection` | `SignAndSendConnection \| null` | **Required for sign-and-send** when using wallet-adapter or passkey. Provide your Solana `Connection` (e.g. from `@solana/web3.js`). |
|
|
519
|
+
Username/email + password login form with loading and error states.
|
|
295
520
|
|
|
296
|
-
|
|
521
|
+
```tsx
|
|
522
|
+
import { LoginForm } from 'cilantro-react';
|
|
523
|
+
|
|
524
|
+
<LoginForm
|
|
525
|
+
onSuccess={() => console.log('Logged in!')}
|
|
526
|
+
onError={(err) => console.error(err.message)}
|
|
527
|
+
title="Welcome back"
|
|
528
|
+
description="Sign in to your account"
|
|
529
|
+
submitLabel="Sign in"
|
|
530
|
+
renderSwitchToRegister={() => <a href="/register">Create account</a>}
|
|
531
|
+
className="max-w-sm"
|
|
532
|
+
/>
|
|
533
|
+
```
|
|
297
534
|
|
|
298
|
-
|
|
299
|
-
|----------|------|-------------|
|
|
300
|
-
| `transactionResultState` | `ActionState` | Result of last sign/send (idle, loading, success, error). |
|
|
301
|
-
| `isSigningTransaction` | `boolean` | True while `signTransaction` is running. |
|
|
302
|
-
| `isSendingTransaction` | `boolean` | True while `signAndSendTransaction` is running. |
|
|
303
|
-
| `signTransaction` | `(transaction: Transaction) => Promise<void>` | Sign only (no send). Passkey signers cannot sign-only; use sign-and-send. |
|
|
304
|
-
| `signAndSendTransaction` | `(transaction: Transaction) => Promise<void>` | Sign and send. Requires `connection` for wallet-adapter and passkey. |
|
|
305
|
-
| `reset` | `() => void` | Set `transactionResultState` back to idle. |
|
|
535
|
+
**Props:**
|
|
306
536
|
|
|
307
|
-
|
|
537
|
+
| Prop | Type | Default | Description |
|
|
538
|
+
|------|------|---------|-------------|
|
|
539
|
+
| `onSuccess` | `() => void` | — | Called after successful login |
|
|
540
|
+
| `onError` | `(error: Error) => void` | — | Called on login error |
|
|
541
|
+
| `title` | `string` | `"Sign in"` | Form title |
|
|
542
|
+
| `description` | `string` | — | Form description |
|
|
543
|
+
| `submitLabel` | `string` | `"Sign in"` | Submit button label |
|
|
544
|
+
| `rememberMe` | `boolean` | — | Show remember me option |
|
|
545
|
+
| `renderSwitchToRegister` | `() => ReactNode` | — | Render link to register page |
|
|
546
|
+
| `className` | `string` | — | Root element class |
|
|
547
|
+
| `classNames` | `LoginFormClassNames` | — | Class names for inner elements |
|
|
308
548
|
|
|
309
549
|
---
|
|
310
550
|
|
|
311
|
-
|
|
551
|
+
#### LogoutButton
|
|
312
552
|
|
|
313
|
-
|
|
553
|
+
Logout button with optional confirmation dialog.
|
|
314
554
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
555
|
+
```tsx
|
|
556
|
+
import { LogoutButton } from 'cilantro-react';
|
|
557
|
+
|
|
558
|
+
<LogoutButton
|
|
559
|
+
onLogout={() => console.log('Logged out')}
|
|
560
|
+
confirmBeforeLogout={true}
|
|
561
|
+
>
|
|
562
|
+
Sign out
|
|
563
|
+
</LogoutButton>
|
|
564
|
+
```
|
|
323
565
|
|
|
324
|
-
|
|
566
|
+
**Props:**
|
|
325
567
|
|
|
326
|
-
|
|
|
327
|
-
|
|
328
|
-
|
|
|
329
|
-
|
|
|
330
|
-
|
|
|
331
|
-
|
|
|
332
|
-
| **DelegatedKeySelector** | Select delegated key for a wallet. | `walletId`, `value?`, `onChange?(keyId, key)`, `filterActive?` (default true), `placeholder`, `className`, `classNames?`. Headless: `children?` (keys, selectedKeyId, onSelect, isLoading, error, refresh). |
|
|
568
|
+
| Prop | Type | Default | Description |
|
|
569
|
+
|------|------|---------|-------------|
|
|
570
|
+
| `onLogout` | `() => void` | — | Called after logout |
|
|
571
|
+
| `confirmBeforeLogout` | `boolean` | `false` | Show confirmation dialog |
|
|
572
|
+
| `className` | `string` | — | Button class |
|
|
573
|
+
| `children` | `ReactNode` | `"Log out"` | Button content |
|
|
333
574
|
|
|
334
|
-
|
|
575
|
+
---
|
|
335
576
|
|
|
336
|
-
|
|
337
|
-
- **ThemeProvider** – See [Themes](#built-in-theme-themeprovider).
|
|
577
|
+
#### AuthGuard
|
|
338
578
|
|
|
339
|
-
|
|
579
|
+
Protect content behind authentication. Shows fallback (default: LoginForm) when not authenticated.
|
|
340
580
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
| **MessageSigningForm** | Message input + sign button; shows wallet/signer context. | `token?`, `selectedWalletId?`, `selectedSigner?`, `signingMethod?`, `walletAdapterSignMessage?`, `walletAdapterPublicKey?`, `showContext?`, `showCharCount?`, `className`, `classNames?`. Headless: `children?` (messageText, setMessageText, signResultState, isSigning, handleSign, reset). |
|
|
344
|
-
| **TransactionSigningForm** | Sign/send transaction; pass `connection` for sign-and-send. | `token?`, `selectedWalletId?`, `selectedSigner?`, `signingMethod?`, `walletAdapterSignTransaction?`, `walletAdapterPublicKey?`, **`connection?`** (required for wallet-adapter or passkey sign-and-send), `showContext?`, `className`, `classNames?`. Headless: `children?` (transactionResultState, isSigningTransaction, isSendingTransaction, signTransaction, signAndSendTransaction, reset). |
|
|
581
|
+
```tsx
|
|
582
|
+
import { AuthGuard, LoginForm } from 'cilantro-react';
|
|
345
583
|
|
|
346
|
-
|
|
584
|
+
// Basic usage - shows LoginForm when not authenticated
|
|
585
|
+
<AuthGuard>
|
|
586
|
+
<ProtectedContent />
|
|
587
|
+
</AuthGuard>
|
|
347
588
|
|
|
348
|
-
|
|
589
|
+
// Custom fallback
|
|
590
|
+
<AuthGuard fallback={<CustomLoginPage />}>
|
|
591
|
+
<ProtectedContent />
|
|
592
|
+
</AuthGuard>
|
|
349
593
|
|
|
350
|
-
|
|
594
|
+
// Redirect instead of showing fallback
|
|
595
|
+
<AuthGuard redirectTo="/login">
|
|
596
|
+
<ProtectedContent />
|
|
597
|
+
</AuthGuard>
|
|
351
598
|
|
|
352
|
-
|
|
599
|
+
// Hide content when not authenticated (no fallback)
|
|
600
|
+
<AuthGuard showFallback={false}>
|
|
601
|
+
<ProtectedContent />
|
|
602
|
+
</AuthGuard>
|
|
353
603
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
detail?: T;
|
|
359
|
-
};
|
|
604
|
+
// Show skeleton while loading
|
|
605
|
+
<AuthGuard useSkeleton={true}>
|
|
606
|
+
<ProtectedContent />
|
|
607
|
+
</AuthGuard>
|
|
360
608
|
```
|
|
361
609
|
|
|
362
|
-
|
|
363
|
-
- **loading** – Sign/send in progress.
|
|
364
|
-
- **success** – Done; `message` and optional `detail` (e.g. signature, explorer URL).
|
|
365
|
-
- **error** – Failed; `message` (and optional `detail`).
|
|
610
|
+
**Props:**
|
|
366
611
|
|
|
367
|
-
|
|
612
|
+
| Prop | Type | Default | Description |
|
|
613
|
+
|------|------|---------|-------------|
|
|
614
|
+
| `children` | `ReactNode` | — | Content to show when authenticated |
|
|
615
|
+
| `fallback` | `ReactNode` | `<LoginForm />` | Content when not authenticated |
|
|
616
|
+
| `redirectTo` | `string` | — | Redirect URL when not authenticated |
|
|
617
|
+
| `showFallback` | `boolean` | `true` | Whether to show fallback or null |
|
|
618
|
+
| `useSkeleton` | `boolean` | `false` | Show skeleton while loading auth state |
|
|
619
|
+
| `className` | `string` | — | Root element class |
|
|
620
|
+
| `classNames` | `AuthGuardClassNames` | — | Class names for inner elements |
|
|
368
621
|
|
|
369
|
-
|
|
622
|
+
---
|
|
370
623
|
|
|
371
|
-
|
|
372
|
-
interface User {
|
|
373
|
-
username?: string;
|
|
374
|
-
email?: string;
|
|
375
|
-
userType?: string;
|
|
376
|
-
}
|
|
377
|
-
```
|
|
624
|
+
### Wallet Components
|
|
378
625
|
|
|
379
|
-
####
|
|
626
|
+
#### WalletSelector
|
|
380
627
|
|
|
381
|
-
|
|
628
|
+
Dropdown to select from available wallets.
|
|
382
629
|
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
id: string;
|
|
386
|
-
walletId: string;
|
|
387
|
-
walletName?: string;
|
|
388
|
-
address?: string;
|
|
389
|
-
walletAddress?: string;
|
|
390
|
-
chain?: string;
|
|
391
|
-
active?: boolean;
|
|
392
|
-
[key: string]: unknown;
|
|
393
|
-
}
|
|
394
|
-
```
|
|
630
|
+
```tsx
|
|
631
|
+
import { WalletSelector } from 'cilantro-react';
|
|
395
632
|
|
|
396
|
-
|
|
633
|
+
// Controlled
|
|
634
|
+
const [walletId, setWalletId] = useState('');
|
|
635
|
+
<WalletSelector value={walletId} onChange={setWalletId} />
|
|
397
636
|
|
|
398
|
-
|
|
637
|
+
// With callback
|
|
638
|
+
<WalletSelector
|
|
639
|
+
onWalletChange={(id, wallet) => console.log('Selected:', wallet?.walletName)}
|
|
640
|
+
placeholder="Choose a wallet"
|
|
641
|
+
/>
|
|
642
|
+
|
|
643
|
+
// Headless (custom rendering)
|
|
644
|
+
<WalletSelector>
|
|
645
|
+
{({ wallets, selectedWallet, selectWallet, isLoading }) => (
|
|
646
|
+
<div>
|
|
647
|
+
{wallets.map((w) => (
|
|
648
|
+
<button key={w.id} onClick={() => selectWallet(w.id)}>
|
|
649
|
+
{w.walletName}
|
|
650
|
+
</button>
|
|
651
|
+
))}
|
|
652
|
+
</div>
|
|
653
|
+
)}
|
|
654
|
+
</WalletSelector>
|
|
655
|
+
```
|
|
399
656
|
|
|
400
|
-
|
|
657
|
+
**Props:**
|
|
658
|
+
|
|
659
|
+
| Prop | Type | Default | Description |
|
|
660
|
+
|------|------|---------|-------------|
|
|
661
|
+
| `value` | `string` | — | Controlled wallet ID |
|
|
662
|
+
| `onChange` | `(walletId: string) => void` | — | Called when wallet changes |
|
|
663
|
+
| `onWalletChange` | `(walletId: string, wallet: WalletData \| null) => void` | — | Called with wallet object |
|
|
664
|
+
| `placeholder` | `string` | `"Select a wallet"` | Placeholder text |
|
|
665
|
+
| `useSkeleton` | `boolean` | `true` | Show skeleton while loading |
|
|
666
|
+
| `className` | `string` | — | Root element class |
|
|
667
|
+
| `classNames` | `WalletSelectorClassNames` | — | Class names for inner elements |
|
|
668
|
+
| `children` | `(props) => ReactNode` | — | Headless render function |
|
|
669
|
+
| `renderTrigger` | `(props) => ReactNode` | — | Custom trigger render |
|
|
670
|
+
| `renderList` | `(props) => ReactNode` | — | Custom list render |
|
|
401
671
|
|
|
402
|
-
|
|
672
|
+
---
|
|
403
673
|
|
|
404
|
-
|
|
405
|
-
- `confirmTransaction(opts: { signature, blockhash, lastValidBlockHeight })` → `Promise<void>`
|
|
674
|
+
#### CreateWalletForm
|
|
406
675
|
|
|
407
|
-
|
|
676
|
+
Form to create a new wallet.
|
|
408
677
|
|
|
409
|
-
|
|
678
|
+
```tsx
|
|
679
|
+
import { CreateWalletForm } from 'cilantro-react';
|
|
410
680
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
-
|
|
415
|
-
|
|
416
|
-
|
|
681
|
+
<CreateWalletForm
|
|
682
|
+
onCreated={(result) => console.log('Created wallet:', result.data.id)}
|
|
683
|
+
onError={(err) => console.error(err.message)}
|
|
684
|
+
userId="optional-user-id"
|
|
685
|
+
/>
|
|
686
|
+
```
|
|
417
687
|
|
|
418
|
-
|
|
688
|
+
**Props:**
|
|
419
689
|
|
|
420
|
-
|
|
421
|
-
|
|
690
|
+
| Prop | Type | Default | Description |
|
|
691
|
+
|------|------|---------|-------------|
|
|
692
|
+
| `userId` | `string` | — | Optional user ID to associate wallet with |
|
|
693
|
+
| `onCreated` | `(wallet: WalletControllerCreateResult) => void` | — | Called after wallet is created |
|
|
694
|
+
| `onError` | `(error: Error) => void` | — | Called on error |
|
|
695
|
+
| `className` | `string` | — | Form class |
|
|
422
696
|
|
|
423
697
|
---
|
|
424
698
|
|
|
425
|
-
|
|
699
|
+
#### WalletAddress
|
|
426
700
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
One form that switches between sign-in and create-account:
|
|
701
|
+
Display a wallet address with copy functionality.
|
|
430
702
|
|
|
431
703
|
```tsx
|
|
432
|
-
import {
|
|
704
|
+
import { WalletAddress } from 'cilantro-react';
|
|
433
705
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
onError={(err) => console.error(err)}
|
|
440
|
-
loginTitle="Welcome back"
|
|
441
|
-
registerTitle="Create an account"
|
|
442
|
-
switchToRegisterText="Don't have an account? Create one"
|
|
443
|
-
switchToLoginText="Already have an account? Sign in"
|
|
444
|
-
/>
|
|
445
|
-
)
|
|
446
|
-
}
|
|
706
|
+
<WalletAddress
|
|
707
|
+
address="7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
|
|
708
|
+
short={true}
|
|
709
|
+
copyable={true}
|
|
710
|
+
/>
|
|
447
711
|
```
|
|
448
712
|
|
|
449
|
-
|
|
713
|
+
**Props:**
|
|
450
714
|
|
|
451
|
-
|
|
715
|
+
| Prop | Type | Default | Description |
|
|
716
|
+
|------|------|---------|-------------|
|
|
717
|
+
| `address` | `string` | — | Wallet address to display |
|
|
718
|
+
| `short` | `boolean` | `false` | Truncate address for display |
|
|
719
|
+
| `copyable` | `boolean` | `false` | Show copy button |
|
|
720
|
+
| `className` | `string` | — | Element class |
|
|
452
721
|
|
|
453
|
-
|
|
454
|
-
import { LoginForm, RegisterForm } from 'cilantro-react'
|
|
455
|
-
import { Link } from 'react-router-dom'
|
|
722
|
+
---
|
|
456
723
|
|
|
457
|
-
|
|
458
|
-
return (
|
|
459
|
-
<LoginForm
|
|
460
|
-
onSuccess={() => navigate('/dashboard')}
|
|
461
|
-
renderSwitchToRegister={() => (
|
|
462
|
-
<Link to="/register">Create account</Link>
|
|
463
|
-
)}
|
|
464
|
-
/>
|
|
465
|
-
)
|
|
466
|
-
}
|
|
724
|
+
#### WalletBalance
|
|
467
725
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
)}
|
|
475
|
-
/>
|
|
476
|
-
)
|
|
477
|
-
}
|
|
726
|
+
Display wallet SOL and token balances.
|
|
727
|
+
|
|
728
|
+
```tsx
|
|
729
|
+
import { WalletBalance } from 'cilantro-react';
|
|
730
|
+
|
|
731
|
+
<WalletBalance walletId={walletId} showTokens={true} />
|
|
478
732
|
```
|
|
479
733
|
|
|
480
|
-
|
|
734
|
+
**Props:**
|
|
481
735
|
|
|
482
|
-
|
|
736
|
+
| Prop | Type | Default | Description |
|
|
737
|
+
|------|------|---------|-------------|
|
|
738
|
+
| `walletId` | `string` | — | Wallet ID to show balance for |
|
|
739
|
+
| `showTokens` | `boolean` | `false` | Show SPL token balances |
|
|
740
|
+
| `className` | `string` | — | Element class |
|
|
483
741
|
|
|
484
|
-
|
|
485
|
-
import { AuthGuard, LoginForm } from 'cilantro-react'
|
|
742
|
+
---
|
|
486
743
|
|
|
487
|
-
|
|
488
|
-
return (
|
|
489
|
-
<AuthGuard fallback={<LoginForm />}>
|
|
490
|
-
<Dashboard />
|
|
491
|
-
</AuthGuard>
|
|
492
|
-
)
|
|
493
|
-
}
|
|
744
|
+
#### ConnectWalletButton
|
|
494
745
|
|
|
495
|
-
|
|
496
|
-
<AuthGuard>
|
|
497
|
-
<Dashboard />
|
|
498
|
-
</AuthGuard>
|
|
746
|
+
Connect to external Solana wallets (Phantom, Solflare).
|
|
499
747
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
748
|
+
```tsx
|
|
749
|
+
import { ConnectWalletButton } from 'cilantro-react';
|
|
750
|
+
|
|
751
|
+
<ConnectWalletButton
|
|
752
|
+
onConnect={(publicKey, wallet) => console.log('Connected:', publicKey)}
|
|
753
|
+
onDisconnect={() => console.log('Disconnected')}
|
|
754
|
+
>
|
|
755
|
+
Connect External Wallet
|
|
756
|
+
</ConnectWalletButton>
|
|
504
757
|
```
|
|
505
758
|
|
|
506
|
-
|
|
759
|
+
**Props:**
|
|
760
|
+
|
|
761
|
+
| Prop | Type | Default | Description |
|
|
762
|
+
|------|------|---------|-------------|
|
|
763
|
+
| `onConnect` | `(publicKey: string, wallet: SolanaWallet) => void` | — | Called when connected |
|
|
764
|
+
| `onDisconnect` | `() => void` | — | Called when disconnected |
|
|
765
|
+
| `className` | `string` | — | Button class |
|
|
766
|
+
| `children` | `ReactNode` | `"Connect Wallet"` | Button content |
|
|
767
|
+
|
|
768
|
+
---
|
|
769
|
+
|
|
770
|
+
### Signer Components
|
|
771
|
+
|
|
772
|
+
#### SignerSelector
|
|
507
773
|
|
|
508
|
-
|
|
774
|
+
Select a signer from the wallet's signers.
|
|
509
775
|
|
|
510
776
|
```tsx
|
|
511
|
-
import {
|
|
777
|
+
import { SignerSelector } from 'cilantro-react';
|
|
512
778
|
|
|
513
|
-
|
|
514
|
-
|
|
779
|
+
const [signerId, setSignerId] = useState('');
|
|
780
|
+
const [signerType, setSignerType] = useState<'email' | 'phone'>('email');
|
|
515
781
|
|
|
516
|
-
|
|
782
|
+
<SignerSelector
|
|
783
|
+
walletId={walletId}
|
|
784
|
+
value={signerId}
|
|
785
|
+
onChange={(id, type) => {
|
|
786
|
+
setSignerId(id);
|
|
787
|
+
setSignerType(type);
|
|
788
|
+
}}
|
|
789
|
+
/>
|
|
790
|
+
|
|
791
|
+
// Headless
|
|
792
|
+
<SignerSelector walletId={walletId} onChange={handleChange}>
|
|
793
|
+
{({ signers, selectedSigner, onSignerSelect, isLoading }) => (
|
|
517
794
|
<div>
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
trigger: 'border-2',
|
|
524
|
-
}}
|
|
525
|
-
/>
|
|
526
|
-
{selectedWallet && (
|
|
527
|
-
<p>Selected: {selectedWallet.walletName ?? selectedWallet.id}</p>
|
|
528
|
-
)}
|
|
795
|
+
{signers.map((s) => (
|
|
796
|
+
<button key={s.id} onClick={() => onSignerSelect(s)}>
|
|
797
|
+
{s.email || s.phone}
|
|
798
|
+
</button>
|
|
799
|
+
))}
|
|
529
800
|
</div>
|
|
530
|
-
)
|
|
531
|
-
|
|
801
|
+
)}
|
|
802
|
+
</SignerSelector>
|
|
532
803
|
```
|
|
533
804
|
|
|
534
|
-
|
|
805
|
+
**Props:**
|
|
806
|
+
|
|
807
|
+
| Prop | Type | Default | Description |
|
|
808
|
+
|------|------|---------|-------------|
|
|
809
|
+
| `walletId` | `string` | — | Wallet ID to load signers for |
|
|
810
|
+
| `value` | `string` | — | Selected signer ID |
|
|
811
|
+
| `onChange` | `(signerId: string, signerType: SignerType) => void` | — | Called when signer changes |
|
|
812
|
+
| `useSkeleton` | `boolean` | `true` | Show skeleton while loading |
|
|
813
|
+
| `className` | `string` | — | Root element class |
|
|
814
|
+
| `classNames` | `SignerSelectorClassNames` | — | Class names for inner elements |
|
|
815
|
+
| `children` | `(props) => ReactNode` | — | Headless render function |
|
|
816
|
+
| `renderList` | `(props) => ReactNode` | — | Custom list render |
|
|
817
|
+
|
|
818
|
+
---
|
|
819
|
+
|
|
820
|
+
#### SignerList
|
|
535
821
|
|
|
536
|
-
|
|
822
|
+
List signers with "Add signer" dialog (includes email, phone, passkey forms).
|
|
537
823
|
|
|
538
824
|
```tsx
|
|
539
|
-
import {
|
|
825
|
+
import { SignerList } from 'cilantro-react';
|
|
540
826
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
827
|
+
<SignerList
|
|
828
|
+
walletId={walletId}
|
|
829
|
+
onSignerAdded={() => console.log('Signer added')}
|
|
830
|
+
onRevoke={(signerId) => console.log('Revoked:', signerId)}
|
|
831
|
+
/>
|
|
544
832
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
)
|
|
555
|
-
}
|
|
833
|
+
// Headless
|
|
834
|
+
<SignerList walletId={walletId}>
|
|
835
|
+
{({ signers, isLoading, openAddSigner }) => (
|
|
836
|
+
<div>
|
|
837
|
+
{signers.map((s) => <div key={s.id}>{s.email}</div>)}
|
|
838
|
+
<button onClick={openAddSigner}>Add Signer</button>
|
|
839
|
+
</div>
|
|
840
|
+
)}
|
|
841
|
+
</SignerList>
|
|
556
842
|
```
|
|
557
843
|
|
|
558
|
-
|
|
844
|
+
**Props:**
|
|
559
845
|
|
|
560
|
-
|
|
846
|
+
| Prop | Type | Default | Description |
|
|
847
|
+
|------|------|---------|-------------|
|
|
848
|
+
| `walletId` | `string` | — | Wallet ID |
|
|
849
|
+
| `onSignerAdded` | `() => void` | — | Called after signer is added |
|
|
850
|
+
| `onRevoke` | `(signerId: string) => void` | — | Called when signer is revoked |
|
|
851
|
+
| `useSkeleton` | `boolean` | `true` | Show skeleton while loading |
|
|
852
|
+
| `className` | `string` | — | Root element class |
|
|
853
|
+
| `classNames` | `SignerListClassNames` | — | Class names for inner elements |
|
|
854
|
+
| `children` | `(props) => ReactNode` | — | Headless render function |
|
|
561
855
|
|
|
562
|
-
|
|
856
|
+
---
|
|
563
857
|
|
|
564
|
-
|
|
565
|
-
import { SignerList } from 'cilantro-react'
|
|
858
|
+
#### CreateEmailSignerForm
|
|
566
859
|
|
|
567
|
-
|
|
568
|
-
const walletId = 'your-wallet-id'
|
|
860
|
+
Form to create an email signer.
|
|
569
861
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
)
|
|
576
|
-
}
|
|
862
|
+
```tsx
|
|
863
|
+
import { CreateEmailSignerForm } from 'cilantro-react';
|
|
864
|
+
|
|
865
|
+
<CreateEmailSignerForm
|
|
866
|
+
walletId={walletId}
|
|
867
|
+
onCreated={(signer) => console.log('Created:', signer.signerId)}
|
|
868
|
+
onError={(err) => console.error(err.message)}
|
|
869
|
+
/>
|
|
577
870
|
```
|
|
578
871
|
|
|
579
|
-
|
|
872
|
+
**Props:**
|
|
873
|
+
|
|
874
|
+
| Prop | Type | Default | Description |
|
|
875
|
+
|------|------|---------|-------------|
|
|
876
|
+
| `walletId` | `string` | — | Wallet ID |
|
|
877
|
+
| `onCreated` | `(signer: SignerInfo) => void` | — | Called after signer is created |
|
|
878
|
+
| `onError` | `(error: Error) => void` | — | Called on error |
|
|
879
|
+
| `className` | `string` | — | Form class |
|
|
880
|
+
|
|
881
|
+
---
|
|
882
|
+
|
|
883
|
+
#### CreatePhoneSignerForm
|
|
884
|
+
|
|
885
|
+
Form to create a phone signer.
|
|
580
886
|
|
|
581
887
|
```tsx
|
|
582
|
-
import {
|
|
888
|
+
import { CreatePhoneSignerForm } from 'cilantro-react';
|
|
583
889
|
|
|
584
|
-
<
|
|
890
|
+
<CreatePhoneSignerForm
|
|
585
891
|
walletId={walletId}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
onSuccess={() => { refreshSigners(); setDialogOpen(false) }}
|
|
589
|
-
asDialog={true}
|
|
892
|
+
onCreated={(signer) => console.log('Created:', signer.signerId)}
|
|
893
|
+
onError={(err) => console.error(err.message)}
|
|
590
894
|
/>
|
|
591
895
|
```
|
|
592
896
|
|
|
593
|
-
|
|
897
|
+
**Props:**
|
|
594
898
|
|
|
595
|
-
|
|
899
|
+
| Prop | Type | Default | Description |
|
|
900
|
+
|------|------|---------|-------------|
|
|
901
|
+
| `walletId` | `string` | — | Wallet ID |
|
|
902
|
+
| `onCreated` | `(signer: SignerInfo) => void` | — | Called after signer is created |
|
|
903
|
+
| `onError` | `(error: Error) => void` | — | Called on error |
|
|
904
|
+
| `className` | `string` | — | Form class |
|
|
596
905
|
|
|
597
|
-
|
|
598
|
-
import { DelegatedKeySelector } from 'cilantro-react'
|
|
906
|
+
---
|
|
599
907
|
|
|
600
|
-
|
|
601
|
-
const [keyId, setKeyId] = useState<string>('')
|
|
908
|
+
#### AddPasskeyButton
|
|
602
909
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
)
|
|
612
|
-
|
|
910
|
+
Button to register a passkey signer. Only renders if WebAuthn is supported.
|
|
911
|
+
|
|
912
|
+
```tsx
|
|
913
|
+
import { AddPasskeyButton } from 'cilantro-react';
|
|
914
|
+
|
|
915
|
+
<AddPasskeyButton
|
|
916
|
+
walletId={walletId}
|
|
917
|
+
onRegistered={(signer) => console.log('Registered:', signer.signerId)}
|
|
918
|
+
onError={(err) => console.error(err.message)}
|
|
919
|
+
>
|
|
920
|
+
Add Passkey
|
|
921
|
+
</AddPasskeyButton>
|
|
613
922
|
```
|
|
614
923
|
|
|
615
|
-
|
|
924
|
+
**Props:**
|
|
925
|
+
|
|
926
|
+
| Prop | Type | Default | Description |
|
|
927
|
+
|------|------|---------|-------------|
|
|
928
|
+
| `walletId` | `string` | — | Wallet ID |
|
|
929
|
+
| `onRegistered` | `(signer: SignerInfo) => void` | — | Called after passkey is registered |
|
|
930
|
+
| `onError` | `(error: Error) => void` | — | Called on error |
|
|
931
|
+
| `className` | `string` | — | Container class |
|
|
932
|
+
| `children` | `ReactNode` | `"Add Passkey"` | Button content |
|
|
933
|
+
|
|
934
|
+
---
|
|
935
|
+
|
|
936
|
+
### Transaction Components
|
|
616
937
|
|
|
617
|
-
|
|
938
|
+
#### SendSOLForm
|
|
618
939
|
|
|
619
|
-
|
|
940
|
+
Form to send SOL.
|
|
620
941
|
|
|
621
942
|
```tsx
|
|
622
|
-
import {
|
|
943
|
+
import { SendSOLForm } from 'cilantro-react';
|
|
623
944
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
945
|
+
<SendSOLForm
|
|
946
|
+
walletId={walletId}
|
|
947
|
+
signerId={signerId}
|
|
948
|
+
signerType="email"
|
|
949
|
+
onSuccess={(result) => console.log('Sent! Signature:', result.data.signature)}
|
|
950
|
+
onError={(err) => console.error(err.message)}
|
|
951
|
+
/>
|
|
632
952
|
```
|
|
633
953
|
|
|
634
|
-
|
|
954
|
+
**Props:**
|
|
955
|
+
|
|
956
|
+
| Prop | Type | Default | Description |
|
|
957
|
+
|------|------|---------|-------------|
|
|
958
|
+
| `walletId` | `string` | — | Wallet to send from |
|
|
959
|
+
| `signerId` | `string` | — | Signer ID to authorize transaction |
|
|
960
|
+
| `signerType` | `'email' \| 'phone' \| 'passkey' \| 'external'` | — | Signer type |
|
|
961
|
+
| `onSuccess` | `(result: SubmitTransactionResult) => void` | — | Called after successful send |
|
|
962
|
+
| `onError` | `(error: Error) => void` | — | Called on error |
|
|
963
|
+
| `className` | `string` | — | Form class |
|
|
964
|
+
|
|
965
|
+
---
|
|
966
|
+
|
|
967
|
+
#### SendSPLForm
|
|
968
|
+
|
|
969
|
+
Form to send SPL tokens.
|
|
635
970
|
|
|
636
971
|
```tsx
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
)}
|
|
646
|
-
|
|
972
|
+
import { SendSPLForm } from 'cilantro-react';
|
|
973
|
+
|
|
974
|
+
<SendSPLForm
|
|
975
|
+
walletId={walletId}
|
|
976
|
+
signerId={signerId}
|
|
977
|
+
signerType="email"
|
|
978
|
+
mintAddress="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" // USDC
|
|
979
|
+
onSuccess={(result) => console.log('Sent! Signature:', result.data.signature)}
|
|
980
|
+
onError={(err) => console.error(err.message)}
|
|
981
|
+
/>
|
|
647
982
|
```
|
|
648
983
|
|
|
649
|
-
|
|
984
|
+
**Props:**
|
|
650
985
|
|
|
651
|
-
|
|
986
|
+
| Prop | Type | Default | Description |
|
|
987
|
+
|------|------|---------|-------------|
|
|
988
|
+
| `walletId` | `string` | — | Wallet to send from |
|
|
989
|
+
| `signerId` | `string` | — | Signer ID |
|
|
990
|
+
| `signerType` | `SignerType` | — | Signer type |
|
|
991
|
+
| `mintAddress` | `string` | — | Pre-fill token mint address |
|
|
992
|
+
| `onSuccess` | `(result: SubmitTransactionResult) => void` | — | Called after successful send |
|
|
993
|
+
| `onError` | `(error: Error) => void` | — | Called on error |
|
|
994
|
+
| `className` | `string` | — | Form class |
|
|
652
995
|
|
|
653
|
-
|
|
654
|
-
import { Connection } from '@solana/web3.js'
|
|
655
|
-
import { TransactionSigningForm } from 'cilantro-react'
|
|
996
|
+
---
|
|
656
997
|
|
|
657
|
-
|
|
658
|
-
const connection = new Connection('https://api.devnet.solana.com')
|
|
998
|
+
#### TransactionStatus
|
|
659
999
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
const tx = buildMyTransaction() // your code
|
|
670
|
-
signAndSendTransaction(tx)
|
|
671
|
-
}}
|
|
672
|
-
disabled={isSendingTransaction}
|
|
673
|
-
>
|
|
674
|
-
{isSendingTransaction ? 'Sending...' : 'Sign and send'}
|
|
675
|
-
</button>
|
|
676
|
-
{transactionResultState.status !== 'idle' && (
|
|
677
|
-
<p>{transactionResultState.message}</p>
|
|
678
|
-
)}
|
|
679
|
-
<button onClick={reset}>Reset status</button>
|
|
680
|
-
</div>
|
|
681
|
-
)}
|
|
682
|
-
</TransactionSigningForm>
|
|
683
|
-
)
|
|
684
|
-
}
|
|
1000
|
+
Display transaction status with explorer link.
|
|
1001
|
+
|
|
1002
|
+
```tsx
|
|
1003
|
+
import { TransactionStatus } from 'cilantro-react';
|
|
1004
|
+
|
|
1005
|
+
<TransactionStatus
|
|
1006
|
+
signature="5wHu1qwD7q9..."
|
|
1007
|
+
cluster="mainnet-beta"
|
|
1008
|
+
/>
|
|
685
1009
|
```
|
|
686
1010
|
|
|
687
|
-
|
|
1011
|
+
**Props:**
|
|
1012
|
+
|
|
1013
|
+
| Prop | Type | Default | Description |
|
|
1014
|
+
|------|------|---------|-------------|
|
|
1015
|
+
| `signature` | `string` | — | Transaction signature |
|
|
1016
|
+
| `cluster` | `'mainnet-beta' \| 'devnet' \| 'testnet'` | `'mainnet-beta'` | Solana cluster |
|
|
1017
|
+
| `className` | `string` | — | Element class |
|
|
1018
|
+
|
|
1019
|
+
---
|
|
1020
|
+
|
|
1021
|
+
#### TransactionHistory
|
|
688
1022
|
|
|
689
|
-
|
|
1023
|
+
Paginated list of recent transactions.
|
|
690
1024
|
|
|
691
|
-
|
|
1025
|
+
```tsx
|
|
1026
|
+
import { TransactionHistory } from 'cilantro-react';
|
|
692
1027
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
1028
|
+
<TransactionHistory walletId={walletId} pageSize={10} />
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
**Props:**
|
|
1032
|
+
|
|
1033
|
+
| Prop | Type | Default | Description |
|
|
1034
|
+
|------|------|---------|-------------|
|
|
1035
|
+
| `walletId` | `string` | — | Wallet ID |
|
|
1036
|
+
| `pageSize` | `number` | `10` | Transactions per page |
|
|
1037
|
+
| `className` | `string` | — | Element class |
|
|
699
1038
|
|
|
700
1039
|
---
|
|
701
1040
|
|
|
702
|
-
|
|
1041
|
+
### Layout Components
|
|
1042
|
+
|
|
1043
|
+
#### CilantroConnect
|
|
1044
|
+
|
|
1045
|
+
All-in-one connect component combining wallet, signers, and auth.
|
|
1046
|
+
|
|
1047
|
+
```tsx
|
|
1048
|
+
import { CilantroConnect } from 'cilantro-react';
|
|
1049
|
+
|
|
1050
|
+
<CilantroConnect
|
|
1051
|
+
onConnect={(wallet, signers) => console.log('Connected:', wallet, signers)}
|
|
1052
|
+
onDisconnect={() => console.log('Disconnected')}
|
|
1053
|
+
/>
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
**Props:**
|
|
703
1057
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
1058
|
+
| Prop | Type | Default | Description |
|
|
1059
|
+
|------|------|---------|-------------|
|
|
1060
|
+
| `onConnect` | `(wallet: WalletInfo, signers: SignerInfo[]) => void` | — | Called when connected |
|
|
1061
|
+
| `onDisconnect` | `() => void` | — | Called when disconnected |
|
|
1062
|
+
| `className` | `string` | — | Element class |
|
|
707
1063
|
|
|
708
1064
|
---
|
|
709
1065
|
|
|
710
|
-
|
|
1066
|
+
#### LoadingOverlay
|
|
1067
|
+
|
|
1068
|
+
Overlay with loading spinner for async operations.
|
|
1069
|
+
|
|
1070
|
+
```tsx
|
|
1071
|
+
import { LoadingOverlay } from 'cilantro-react';
|
|
1072
|
+
|
|
1073
|
+
<LoadingOverlay isLoading={isPending} message="Sending transaction...">
|
|
1074
|
+
<MyContent />
|
|
1075
|
+
</LoadingOverlay>
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
**Props:**
|
|
711
1079
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
1080
|
+
| Prop | Type | Default | Description |
|
|
1081
|
+
|------|------|---------|-------------|
|
|
1082
|
+
| `isLoading` | `boolean` | — | Show overlay when true |
|
|
1083
|
+
| `message` | `string` | — | Loading message |
|
|
1084
|
+
| `children` | `ReactNode` | — | Content to wrap |
|
|
1085
|
+
| `className` | `string` | — | Overlay class |
|
|
716
1086
|
|
|
717
1087
|
---
|
|
718
1088
|
|
|
719
|
-
|
|
1089
|
+
#### ErrorBoundary
|
|
720
1090
|
|
|
721
|
-
|
|
1091
|
+
Error boundary with retry functionality.
|
|
722
1092
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
1093
|
+
```tsx
|
|
1094
|
+
import { ErrorBoundary } from 'cilantro-react';
|
|
1095
|
+
|
|
1096
|
+
<ErrorBoundary
|
|
1097
|
+
fallback={<div>Something went wrong</div>}
|
|
1098
|
+
onRetry={() => window.location.reload()}
|
|
1099
|
+
>
|
|
1100
|
+
<App />
|
|
1101
|
+
</ErrorBoundary>
|
|
1102
|
+
```
|
|
728
1103
|
|
|
729
|
-
|
|
1104
|
+
**Props:**
|
|
730
1105
|
|
|
731
|
-
|
|
1106
|
+
| Prop | Type | Default | Description |
|
|
1107
|
+
|------|------|---------|-------------|
|
|
1108
|
+
| `fallback` | `ReactNode` | — | Content to show on error |
|
|
1109
|
+
| `onRetry` | `() => void` | — | Retry callback |
|
|
1110
|
+
| `children` | `ReactNode` | — | Content to wrap |
|
|
732
1111
|
|
|
733
1112
|
---
|
|
734
1113
|
|
|
735
1114
|
## Theming
|
|
736
1115
|
|
|
737
|
-
Components use Shadcn-style class names and CSS variables
|
|
1116
|
+
Components use Shadcn-style class names and CSS variables. Ensure your app has **Tailwind CSS** configured.
|
|
738
1117
|
|
|
739
|
-
### Built-in
|
|
740
|
-
|
|
741
|
-
You can opt into a built-in light/dark theme without bringing your own Shadcn setup. Wrap your app (or just the Cilantro UI) with `ThemeProvider`:
|
|
1118
|
+
### Built-in Theme (ThemeProvider)
|
|
742
1119
|
|
|
743
1120
|
```tsx
|
|
744
|
-
import { ThemeProvider } from 'cilantro-react'
|
|
745
|
-
|
|
746
|
-
<ThemeProvider
|
|
747
|
-
|
|
748
|
-
|
|
1121
|
+
import { ThemeProvider } from 'cilantro-react';
|
|
1122
|
+
|
|
1123
|
+
<ThemeProvider
|
|
1124
|
+
theme="system"
|
|
1125
|
+
defaultTheme="dark"
|
|
1126
|
+
injectStyles={true}
|
|
1127
|
+
className="min-h-screen"
|
|
1128
|
+
>
|
|
1129
|
+
<CilantroProvider apiKey="...">
|
|
1130
|
+
<App />
|
|
749
1131
|
</CilantroProvider>
|
|
750
1132
|
</ThemeProvider>
|
|
751
1133
|
```
|
|
752
1134
|
|
|
753
|
-
|
|
754
|
-
- **defaultTheme** – Initial theme when using `"system"` (default: `"dark"`).
|
|
755
|
-
- **storageKey** – Optional localStorage key to persist the resolved theme when using `"system"`.
|
|
756
|
-
- **injectStyles** – When `true` (default), the provider injects a minimal set of CSS variables (`--background`, `--foreground`, `--primary`, `--muted`, `--border`, `--input`, `--ring`, etc.) for light and `[data-theme="dark"]`. You can set `injectStyles={false}` and import the theme file yourself: `import 'cilantro-react/themes/default.css'`.
|
|
1135
|
+
**Props:**
|
|
757
1136
|
|
|
758
|
-
|
|
1137
|
+
| Prop | Type | Default | Description |
|
|
1138
|
+
|------|------|---------|-------------|
|
|
1139
|
+
| `theme` | `'light' \| 'dark' \| 'system'` | `'system'` | Theme mode |
|
|
1140
|
+
| `defaultTheme` | `'light' \| 'dark'` | `'dark'` | Default when using system |
|
|
1141
|
+
| `storageKey` | `string` | — | localStorage key for persistence |
|
|
1142
|
+
| `injectStyles` | `boolean` | `true` | Inject CSS variables |
|
|
1143
|
+
| `className` | `string` | — | Wrapper class |
|
|
759
1144
|
|
|
760
|
-
|
|
1145
|
+
### Import Theme CSS Directly
|
|
1146
|
+
|
|
1147
|
+
```ts
|
|
1148
|
+
import 'cilantro-react/themes/default.css';
|
|
1149
|
+
```
|
|
761
1150
|
|
|
762
1151
|
---
|
|
763
1152
|
|
|
764
|
-
##
|
|
1153
|
+
## Advanced Usage
|
|
1154
|
+
|
|
1155
|
+
### Low-Level Signer Signing
|
|
765
1156
|
|
|
766
|
-
|
|
1157
|
+
For advanced use cases without React hooks:
|
|
767
1158
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1159
|
+
```tsx
|
|
1160
|
+
import {
|
|
1161
|
+
signMessageWithSigner,
|
|
1162
|
+
signTransactionWithSigner,
|
|
1163
|
+
signAndSendTransactionWithSigner,
|
|
1164
|
+
getWalletData,
|
|
1165
|
+
getSignerPublicKey,
|
|
1166
|
+
SIGNER_TYPES,
|
|
1167
|
+
} from 'cilantro-react';
|
|
1168
|
+
|
|
1169
|
+
// Sign a message
|
|
1170
|
+
const result = await signMessageWithSigner(walletId, signer, 'Hello, Solana!');
|
|
1171
|
+
|
|
1172
|
+
// Sign and send a transaction
|
|
1173
|
+
const txResult = await signAndSendTransactionWithSigner(walletId, signer, transaction, connection);
|
|
1174
|
+
```
|
|
772
1175
|
|
|
773
|
-
|
|
1176
|
+
### Solana Connection Adapter
|
|
774
1177
|
|
|
775
|
-
|
|
1178
|
+
For `@solana/web3.js` connection compatibility:
|
|
1179
|
+
|
|
1180
|
+
```tsx
|
|
1181
|
+
import { Connection } from '@solana/web3.js';
|
|
1182
|
+
import { adaptSolanaConnection } from 'cilantro-react';
|
|
776
1183
|
|
|
777
|
-
|
|
1184
|
+
const connection = new Connection('https://api.mainnet-beta.solana.com');
|
|
1185
|
+
const adapted = adaptSolanaConnection(connection);
|
|
1186
|
+
```
|
|
778
1187
|
|
|
779
|
-
|
|
1188
|
+
### Utilities
|
|
780
1189
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1190
|
+
| Function | Description |
|
|
1191
|
+
|----------|-------------|
|
|
1192
|
+
| `extractErrorMessage(error)` | Normalize unknown errors to string |
|
|
1193
|
+
| `extractResponseData(response)` | Unwrap SDK response shapes |
|
|
1194
|
+
| `normalizeWallet(dto)` | Normalize wallet data from SDK |
|
|
1195
|
+
| `normalizeSigner(dto)` | Normalize signer data from SDK |
|
|
1196
|
+
| `isJwtExpired(jwt)` | Check if JWT is expired |
|
|
1197
|
+
| `isAuthError(error)` | Check if error is an auth error |
|
|
784
1198
|
|
|
785
1199
|
---
|
|
786
1200
|
|
|
787
|
-
##
|
|
1201
|
+
## Types
|
|
788
1202
|
|
|
789
|
-
|
|
1203
|
+
Key exported types:
|
|
790
1204
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
1205
|
+
```ts
|
|
1206
|
+
import type {
|
|
1207
|
+
// Config
|
|
1208
|
+
CilantroConfig,
|
|
1209
|
+
CilantroContextValue,
|
|
1210
|
+
DeviceKeyStorage,
|
|
1211
|
+
|
|
1212
|
+
// User/Auth
|
|
1213
|
+
User,
|
|
1214
|
+
UseAuthResult,
|
|
1215
|
+
|
|
1216
|
+
// Wallet
|
|
1217
|
+
WalletData,
|
|
1218
|
+
UseWalletResult,
|
|
1219
|
+
WalletControllerCreateResult,
|
|
1220
|
+
|
|
1221
|
+
// Signer
|
|
1222
|
+
SignerData,
|
|
1223
|
+
SignerInfo,
|
|
1224
|
+
SignerType,
|
|
1225
|
+
UseSignersResult,
|
|
1226
|
+
|
|
1227
|
+
// Transaction
|
|
1228
|
+
SubmitTransactionResult,
|
|
1229
|
+
UseSendTransactionResult,
|
|
1230
|
+
|
|
1231
|
+
// External Wallet
|
|
1232
|
+
SolanaWallet,
|
|
1233
|
+
UseExternalWalletResult,
|
|
1234
|
+
|
|
1235
|
+
// Passkey
|
|
1236
|
+
UsePasskeyResult,
|
|
1237
|
+
|
|
1238
|
+
// UI
|
|
1239
|
+
ActionState,
|
|
1240
|
+
} from 'cilantro-react';
|
|
1241
|
+
```
|
|
795
1242
|
|
|
796
1243
|
---
|
|
797
1244
|
|
|
798
|
-
##
|
|
1245
|
+
## License
|
|
799
1246
|
|
|
800
|
-
|
|
1247
|
+
MIT
|