@rialo/frost 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +778 -0
- package/dist/index.cjs +1025 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +504 -0
- package/dist/index.d.ts +504 -0
- package/dist/index.js +998 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
# Rialo Frost 🧊
|
|
2
|
+
|
|
3
|
+
**The wallet integration toolkit for Rialo dApps** — Connect wallets, sign transactions, and build Web3 apps in minutes.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm add @rialo/frost
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 30-Second Setup
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
// 1. Create config (do this once, outside components)
|
|
13
|
+
import { createConfig, getDefaultRialoClientConfig } from "@rialo/frost";
|
|
14
|
+
|
|
15
|
+
export const config = createConfig({
|
|
16
|
+
clientConfig: getDefaultRialoClientConfig("devnet"),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// 2. Wrap your app
|
|
20
|
+
import { FrostProvider, ConnectButton } from "@rialo/frost";
|
|
21
|
+
|
|
22
|
+
function App() {
|
|
23
|
+
return (
|
|
24
|
+
<FrostProvider config={config}>
|
|
25
|
+
<ConnectButton />
|
|
26
|
+
</FrostProvider>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
That's it. You now have a working wallet connection button.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Table of Contents
|
|
36
|
+
|
|
37
|
+
- [New to Web3?](#new-to-web3)
|
|
38
|
+
- [Installation](#installation)
|
|
39
|
+
- [Step-by-Step Guide](#step-by-step-guide)
|
|
40
|
+
- [Hooks Reference](#hooks-reference)
|
|
41
|
+
- [Components](#components)
|
|
42
|
+
- [Error Handling](#error-handling)
|
|
43
|
+
- [Advanced Usage](#advanced-usage)
|
|
44
|
+
- [Troubleshooting](#troubleshooting)
|
|
45
|
+
- [CSS Custom Properties](#css-custom-properties)
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## New to Web3?
|
|
50
|
+
|
|
51
|
+
<details>
|
|
52
|
+
<summary><strong>Click here if you're new to blockchain development</strong></summary>
|
|
53
|
+
|
|
54
|
+
### What You Need to Know
|
|
55
|
+
|
|
56
|
+
**Wallet** = A browser extension (like MetaMask, but for Rialo) that stores the user's private keys and lets them approve transactions.
|
|
57
|
+
|
|
58
|
+
**Account/Address** = A public identifier (like `7xKXtg2CW87d97...`) that represents a user on the blockchain. One wallet can have multiple accounts.
|
|
59
|
+
|
|
60
|
+
**Signing** = When a user cryptographically approves something using their wallet. This proves they own the account without exposing their private key.
|
|
61
|
+
|
|
62
|
+
**Transaction** = An operation that changes state on the blockchain (sending tokens, interacting with smart contracts, etc.).
|
|
63
|
+
|
|
64
|
+
### How Frost Fits In
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
68
|
+
│ Your App │ ───► │ Frost │ ───► │ Wallet │
|
|
69
|
+
│ │ │ │ │ Extension │
|
|
70
|
+
│ - UI │ │ - Discovers │ │ │
|
|
71
|
+
│ - Logic │ │ wallets │ │ - Stores │
|
|
72
|
+
│ - Hooks │ │ - Manages │ │ keys │
|
|
73
|
+
│ │ │ state │ │ - Signs │
|
|
74
|
+
└──────────────┘ └──────────────┘ └──────────────┘
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Frost handles all the complex wallet communication so you can focus on building your app.
|
|
78
|
+
|
|
79
|
+
</details>
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
### Requirements
|
|
86
|
+
|
|
87
|
+
- Node.js 18+
|
|
88
|
+
- React 18 or 19
|
|
89
|
+
- A Rialo wallet extension (for testing)
|
|
90
|
+
|
|
91
|
+
### Install
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# pnpm (recommended)
|
|
95
|
+
pnpm add @rialo/frost
|
|
96
|
+
|
|
97
|
+
# npm
|
|
98
|
+
npm install @rialo/frost
|
|
99
|
+
|
|
100
|
+
# yarn
|
|
101
|
+
yarn add @rialo/frost
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### For Non-React Apps
|
|
105
|
+
|
|
106
|
+
Use the framework-agnostic core package:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pnpm add @rialo/frost-core
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Step-by-Step Guide
|
|
115
|
+
|
|
116
|
+
### Step 1: Create Your Config
|
|
117
|
+
|
|
118
|
+
Create a file called `frost.config.ts` in your `src` folder:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// src/frost.config.ts
|
|
122
|
+
import { createConfig, getDefaultRialoClientConfig } from "@rialo/frost";
|
|
123
|
+
|
|
124
|
+
export const frostConfig = createConfig({
|
|
125
|
+
clientConfig: getDefaultRialoClientConfig("devnet"), // Use "mainnet" for production
|
|
126
|
+
autoConnect: true, // Reconnect automatically on page refresh
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
> ⚠️ **Important**: Create the config outside of React components. This prevents it from being recreated on every render.
|
|
131
|
+
|
|
132
|
+
### Step 2: Add the Provider
|
|
133
|
+
|
|
134
|
+
Wrap your app with `FrostProvider`:
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
// src/App.tsx
|
|
138
|
+
import { FrostProvider } from "@rialo/frost";
|
|
139
|
+
import { frostConfig } from "./frost.config";
|
|
140
|
+
|
|
141
|
+
function App() {
|
|
142
|
+
return (
|
|
143
|
+
<FrostProvider config={frostConfig}>
|
|
144
|
+
<YourApp />
|
|
145
|
+
</FrostProvider>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Step 3: Add a Connect Button
|
|
151
|
+
|
|
152
|
+
**Option A: Use the built-in component**
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { ConnectButton } from "@rialo/frost";
|
|
156
|
+
|
|
157
|
+
function Header() {
|
|
158
|
+
return (
|
|
159
|
+
<nav>
|
|
160
|
+
<h1>My dApp</h1>
|
|
161
|
+
<ConnectButton />
|
|
162
|
+
</nav>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Option B: Build your own**
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import {
|
|
171
|
+
useWallets,
|
|
172
|
+
useConnectWallet,
|
|
173
|
+
useDisconnectWallet,
|
|
174
|
+
useIsConnected,
|
|
175
|
+
useActiveAccount,
|
|
176
|
+
} from "@rialo/frost";
|
|
177
|
+
|
|
178
|
+
function CustomConnectButton() {
|
|
179
|
+
const wallets = useWallets();
|
|
180
|
+
const { mutate: connect, isPending } = useConnectWallet();
|
|
181
|
+
const { mutate: disconnect } = useDisconnectWallet();
|
|
182
|
+
const isConnected = useIsConnected();
|
|
183
|
+
const account = useActiveAccount();
|
|
184
|
+
|
|
185
|
+
if (isConnected && account) {
|
|
186
|
+
return (
|
|
187
|
+
<div>
|
|
188
|
+
<span>{account.address.slice(0, 6)}...{account.address.slice(-4)}</span>
|
|
189
|
+
<button onClick={() => disconnect()}>Disconnect</button>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<div>
|
|
196
|
+
{wallets.map((wallet) => (
|
|
197
|
+
<button
|
|
198
|
+
key={wallet.name}
|
|
199
|
+
onClick={() => connect({ walletName: wallet.name })}
|
|
200
|
+
disabled={isPending}
|
|
201
|
+
>
|
|
202
|
+
{wallet.icon && <img src={wallet.icon} alt="" width={20} />}
|
|
203
|
+
Connect {wallet.name}
|
|
204
|
+
</button>
|
|
205
|
+
))}
|
|
206
|
+
</div>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Step 4: Display Balance
|
|
212
|
+
|
|
213
|
+
```tsx
|
|
214
|
+
import { useNativeBalance, useIsConnected } from "@rialo/frost";
|
|
215
|
+
|
|
216
|
+
function Balance() {
|
|
217
|
+
const isConnected = useIsConnected();
|
|
218
|
+
const { formatted, isLoading, refetch } = useNativeBalance();
|
|
219
|
+
|
|
220
|
+
if (!isConnected) return null;
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<div>
|
|
224
|
+
{isLoading ? "Loading..." : `${formatted} RIA`}
|
|
225
|
+
<button onClick={() => refetch()}>↻</button>
|
|
226
|
+
</div>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Step 5: Sign Messages
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import { useSignMessage, useIsConnected } from "@rialo/frost";
|
|
235
|
+
|
|
236
|
+
function SignDemo() {
|
|
237
|
+
const isConnected = useIsConnected();
|
|
238
|
+
const { mutate: signMessage, isPending } = useSignMessage({
|
|
239
|
+
onSuccess: (result) => {
|
|
240
|
+
console.log("Signature:", result.signature);
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (!isConnected) return <p>Connect wallet first</p>;
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<button
|
|
248
|
+
onClick={() => signMessage({ message: "Hello from my dApp!" })}
|
|
249
|
+
disabled={isPending}
|
|
250
|
+
>
|
|
251
|
+
{isPending ? "Signing..." : "Sign Message"}
|
|
252
|
+
</button>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Step 6: Send Transactions
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
import { useSendTransaction } from "@rialo/frost";
|
|
261
|
+
|
|
262
|
+
function SendTx({ transaction }: { transaction: Uint8Array }) {
|
|
263
|
+
const { mutate: send, isPending, isSuccess, data } = useSendTransaction({
|
|
264
|
+
onSuccess: (result) => {
|
|
265
|
+
console.log("Transaction sent:", result.signature);
|
|
266
|
+
},
|
|
267
|
+
onError: (error) => {
|
|
268
|
+
console.error("Failed:", error.message);
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<div>
|
|
274
|
+
<button onClick={() => send({ transaction })} disabled={isPending}>
|
|
275
|
+
{isPending ? "Sending..." : "Send Transaction"}
|
|
276
|
+
</button>
|
|
277
|
+
{isSuccess && <p>✓ Signature: {data.signature}</p>}
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Hooks Reference
|
|
286
|
+
|
|
287
|
+
### Connection
|
|
288
|
+
|
|
289
|
+
| Hook | Returns | Description |
|
|
290
|
+
|------|---------|-------------|
|
|
291
|
+
| `useWallets()` | `WalletEntity[]` | All discovered wallets |
|
|
292
|
+
| `useWalletsReady()` | `boolean` | True when wallet discovery is complete |
|
|
293
|
+
| `useConnectWallet()` | Mutation | Connect to a wallet |
|
|
294
|
+
| `useDisconnectWallet()` | Mutation | Disconnect current wallet |
|
|
295
|
+
| `useConnectionStatus()` | `ConnectionStatus` | `"disconnected"` \| `"connecting"` \| `"connected"` \| `"reconnecting"` |
|
|
296
|
+
| `useIsConnected()` | `boolean` | True if connected |
|
|
297
|
+
|
|
298
|
+
### Account & Wallet
|
|
299
|
+
|
|
300
|
+
| Hook | Returns | Description |
|
|
301
|
+
|------|---------|-------------|
|
|
302
|
+
| `useActiveWallet()` | `WalletEntity \| null` | Currently connected wallet |
|
|
303
|
+
| `useActiveAccount()` | `AccountEntity \| null` | Currently active account |
|
|
304
|
+
| `useAccounts()` | `AccountEntity[]` | All accounts from connected wallet |
|
|
305
|
+
|
|
306
|
+
### Chain & Client
|
|
307
|
+
|
|
308
|
+
| Hook | Returns | Description |
|
|
309
|
+
|------|---------|-------------|
|
|
310
|
+
| `useChainId()` | `string` | Current chain ID (e.g., `"rialo:devnet"`) |
|
|
311
|
+
| `useSwitchChain()` | Mutation | Switch to a different network |
|
|
312
|
+
| `useClient()` | `RialoClient` | Direct RPC client access |
|
|
313
|
+
|
|
314
|
+
### Balance
|
|
315
|
+
|
|
316
|
+
| Hook | Returns | Description |
|
|
317
|
+
|------|---------|-------------|
|
|
318
|
+
| `useNativeBalance()` | `{ formatted, balance, isLoading, refetch }` | Native token balance |
|
|
319
|
+
|
|
320
|
+
### Signing & Transactions
|
|
321
|
+
|
|
322
|
+
| Hook | Description |
|
|
323
|
+
|------|-------------|
|
|
324
|
+
| `useSignMessage()` | Sign arbitrary messages |
|
|
325
|
+
| `useSignIn()` | Sign-In with Wallet (SIWS authentication) |
|
|
326
|
+
| `useSignTransaction()` | Sign transaction without sending |
|
|
327
|
+
| `useSendTransaction()` | Sign and send via wallet |
|
|
328
|
+
| `useSignAndSendTransaction()` | Sign via wallet, send via Frost's RPC |
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Hooks in Detail
|
|
333
|
+
|
|
334
|
+
### useConnectWallet
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
const {
|
|
338
|
+
mutate: connect, // Call to trigger connection
|
|
339
|
+
isPending, // True while connecting
|
|
340
|
+
isSuccess, // True after successful connection
|
|
341
|
+
isError, // True if connection failed
|
|
342
|
+
error, // Error object if failed
|
|
343
|
+
data, // ConnectResult if successful
|
|
344
|
+
} = useConnectWallet({
|
|
345
|
+
onSuccess: (result) => {
|
|
346
|
+
console.log("Connected:", result.walletName, result.accountAddress);
|
|
347
|
+
},
|
|
348
|
+
onError: (error) => {
|
|
349
|
+
console.error("Failed:", error.message);
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// Usage
|
|
354
|
+
connect({ walletName: "Rialo" });
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### useSignMessage
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
const { mutate: signMessage, isPending } = useSignMessage({
|
|
361
|
+
onSuccess: (result) => {
|
|
362
|
+
// result.signature: Uint8Array
|
|
363
|
+
// result.signedMessage: Uint8Array
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// Sign a string
|
|
368
|
+
signMessage({ message: "Hello World" });
|
|
369
|
+
|
|
370
|
+
// Sign raw bytes
|
|
371
|
+
signMessage({ message: new Uint8Array([1, 2, 3]) });
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### useSignIn
|
|
375
|
+
|
|
376
|
+
SIWS (Sign-In with Wallet) for authentication:
|
|
377
|
+
|
|
378
|
+
```tsx
|
|
379
|
+
const { mutate: signIn } = useSignIn({
|
|
380
|
+
onSuccess: async (result) => {
|
|
381
|
+
// Send to your backend for verification
|
|
382
|
+
await fetch("/api/auth", {
|
|
383
|
+
method: "POST",
|
|
384
|
+
body: JSON.stringify({
|
|
385
|
+
signature: Array.from(result.signature),
|
|
386
|
+
message: Array.from(result.signedMessage),
|
|
387
|
+
address: result.account.address,
|
|
388
|
+
}),
|
|
389
|
+
});
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
signIn({
|
|
394
|
+
domain: window.location.host,
|
|
395
|
+
statement: "Sign in to MyApp",
|
|
396
|
+
nonce: crypto.randomUUID(),
|
|
397
|
+
expirationTime: new Date(Date.now() + 3600000).toISOString(),
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### useNativeBalance
|
|
402
|
+
|
|
403
|
+
```tsx
|
|
404
|
+
const {
|
|
405
|
+
balance, // bigint | undefined - Raw balance in lamports
|
|
406
|
+
formatted, // string | undefined - Human readable (e.g., "1.5")
|
|
407
|
+
isLoading, // boolean
|
|
408
|
+
isFetching, // boolean - True during refetch
|
|
409
|
+
isError, // boolean
|
|
410
|
+
error, // Error | null
|
|
411
|
+
refetch, // () => void
|
|
412
|
+
} = useNativeBalance({
|
|
413
|
+
address: "optional-address", // Defaults to active account
|
|
414
|
+
decimals: 9, // Defaults to 9
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### useSwitchChain
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
import { getDefaultRialoClientConfig } from "@rialo/frost";
|
|
422
|
+
|
|
423
|
+
const { mutate: switchChain } = useSwitchChain();
|
|
424
|
+
|
|
425
|
+
// Switch to mainnet
|
|
426
|
+
switchChain(getDefaultRialoClientConfig("mainnet"));
|
|
427
|
+
|
|
428
|
+
// Switch to testnet
|
|
429
|
+
switchChain(getDefaultRialoClientConfig("testnet"));
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Components
|
|
435
|
+
|
|
436
|
+
### ConnectButton
|
|
437
|
+
|
|
438
|
+
A complete, styled connect button with dropdown:
|
|
439
|
+
|
|
440
|
+
```tsx
|
|
441
|
+
<ConnectButton
|
|
442
|
+
label="Connect Wallet" // Button text when disconnected
|
|
443
|
+
connectingLabel="Connecting..." // Text while connecting
|
|
444
|
+
connectedLabel={(addr, wallet) => // Text when connected
|
|
445
|
+
`${addr.slice(0, 6)}...`
|
|
446
|
+
}
|
|
447
|
+
showDropdown={true} // Show dropdown when connected
|
|
448
|
+
onConnect={(wallet, address) => {}} // Connection callback
|
|
449
|
+
onDisconnect={() => {}} // Disconnect callback
|
|
450
|
+
onError={(error) => {}} // Error callback
|
|
451
|
+
/>
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### WalletModal
|
|
455
|
+
|
|
456
|
+
A modal for wallet selection:
|
|
457
|
+
|
|
458
|
+
```tsx
|
|
459
|
+
const [open, setOpen] = useState(false);
|
|
460
|
+
|
|
461
|
+
<WalletModal
|
|
462
|
+
open={open}
|
|
463
|
+
onClose={() => setOpen(false)}
|
|
464
|
+
onConnect={(walletName, address) => {
|
|
465
|
+
console.log(`Connected to ${walletName}`);
|
|
466
|
+
setOpen(false);
|
|
467
|
+
}}
|
|
468
|
+
onError={(error, walletName) => {
|
|
469
|
+
console.error(`${walletName} failed:`, error);
|
|
470
|
+
}}
|
|
471
|
+
title="Select a Wallet"
|
|
472
|
+
/>
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Error Handling
|
|
478
|
+
|
|
479
|
+
Frost provides typed errors for precise handling:
|
|
480
|
+
|
|
481
|
+
```tsx
|
|
482
|
+
import { isFrostError, type FrostError } from "@rialo/frost";
|
|
483
|
+
|
|
484
|
+
function handleError(error: Error) {
|
|
485
|
+
if (!isFrostError(error)) {
|
|
486
|
+
console.error("Unknown error:", error);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
switch (error.code) {
|
|
491
|
+
case "WALLET_NOT_FOUND":
|
|
492
|
+
alert(`Install ${error.walletName} to continue`);
|
|
493
|
+
break;
|
|
494
|
+
case "WALLET_DISCONNECTED":
|
|
495
|
+
alert("Please connect your wallet");
|
|
496
|
+
break;
|
|
497
|
+
case "UNSUPPORTED_CHAIN":
|
|
498
|
+
alert("Switch to a supported network");
|
|
499
|
+
break;
|
|
500
|
+
case "UNSUPPORTED_FEATURE":
|
|
501
|
+
alert("Your wallet doesn't support this feature");
|
|
502
|
+
break;
|
|
503
|
+
case "CONNECTION_FAILED":
|
|
504
|
+
alert("Connection rejected or failed");
|
|
505
|
+
break;
|
|
506
|
+
case "SESSION_EXPIRED":
|
|
507
|
+
alert("Session expired, please reconnect");
|
|
508
|
+
break;
|
|
509
|
+
case "TRANSACTION_FAILED":
|
|
510
|
+
alert(`Transaction failed: ${error.reason}`);
|
|
511
|
+
break;
|
|
512
|
+
case "WALLET_ERROR":
|
|
513
|
+
alert(error.message);
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Error Types
|
|
520
|
+
|
|
521
|
+
| Code | Class | When |
|
|
522
|
+
|------|-------|------|
|
|
523
|
+
| `WALLET_NOT_FOUND` | `WalletNotFoundError` | Wallet extension not installed |
|
|
524
|
+
| `WALLET_DISCONNECTED` | `WalletDisconnectedError` | No wallet connected |
|
|
525
|
+
| `UNSUPPORTED_CHAIN` | `UnsupportedChainError` | Wallet doesn't support current chain |
|
|
526
|
+
| `UNSUPPORTED_FEATURE` | `UnsupportedFeatureError` | Missing wallet capability |
|
|
527
|
+
| `CONNECTION_FAILED` | `ConnectionFailedError` | User rejected or wallet error |
|
|
528
|
+
| `SESSION_EXPIRED` | `SessionExpiredError` | Saved session too old |
|
|
529
|
+
| `TRANSACTION_FAILED` | `TransactionFailedError` | Transaction confirmed but execution failed on-chain |
|
|
530
|
+
| `WALLET_ERROR` | `WalletError` | Generic wallet error |
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## Advanced Usage
|
|
535
|
+
|
|
536
|
+
### Custom RPC URL
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
import { createConfig } from "@rialo/frost";
|
|
540
|
+
|
|
541
|
+
const config = createConfig({
|
|
542
|
+
clientConfig: {
|
|
543
|
+
chain: {
|
|
544
|
+
id: "rialo:mainnet",
|
|
545
|
+
name: "Mainnet",
|
|
546
|
+
rpcUrl: "https://my-custom-rpc.example.com",
|
|
547
|
+
},
|
|
548
|
+
transport: { timeout: 30000 },
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### Disable Session Persistence
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
const config = createConfig({
|
|
557
|
+
clientConfig: getDefaultRialoClientConfig("devnet"),
|
|
558
|
+
storage: null, // Won't remember connected wallet
|
|
559
|
+
});
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### Share TanStack Query Client
|
|
563
|
+
|
|
564
|
+
If your app already uses TanStack Query:
|
|
565
|
+
|
|
566
|
+
```tsx
|
|
567
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
568
|
+
|
|
569
|
+
const queryClient = new QueryClient();
|
|
570
|
+
|
|
571
|
+
function App() {
|
|
572
|
+
return (
|
|
573
|
+
<QueryClientProvider client={queryClient}>
|
|
574
|
+
<FrostProvider config={frostConfig} queryClient={queryClient}>
|
|
575
|
+
<YourApp />
|
|
576
|
+
</FrostProvider>
|
|
577
|
+
</QueryClientProvider>
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Direct Config Access
|
|
583
|
+
|
|
584
|
+
```tsx
|
|
585
|
+
import { useFrostConfig } from "@rialo/frost";
|
|
586
|
+
|
|
587
|
+
function Advanced() {
|
|
588
|
+
const config = useFrostConfig();
|
|
589
|
+
|
|
590
|
+
// Access RPC client
|
|
591
|
+
const client = config.client;
|
|
592
|
+
|
|
593
|
+
// Get chain ID
|
|
594
|
+
const chainId = config.getChainId();
|
|
595
|
+
|
|
596
|
+
// Switch chain imperatively
|
|
597
|
+
config.switchChain(getDefaultRialoClientConfig("mainnet"));
|
|
598
|
+
}
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### Non-React Usage
|
|
602
|
+
|
|
603
|
+
```typescript
|
|
604
|
+
import {
|
|
605
|
+
createConfig,
|
|
606
|
+
getDefaultRialoClientConfig,
|
|
607
|
+
connect,
|
|
608
|
+
disconnect,
|
|
609
|
+
signTransaction,
|
|
610
|
+
WalletRegistry,
|
|
611
|
+
WalletEventBridge,
|
|
612
|
+
initializeConfig,
|
|
613
|
+
} from "@rialo/frost-core";
|
|
614
|
+
|
|
615
|
+
// Setup
|
|
616
|
+
const config = createConfig({
|
|
617
|
+
clientConfig: getDefaultRialoClientConfig("devnet"),
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
const registry = new WalletRegistry(config);
|
|
621
|
+
const bridge = new WalletEventBridge(config);
|
|
622
|
+
initializeConfig(config, registry, bridge);
|
|
623
|
+
|
|
624
|
+
// Connect
|
|
625
|
+
const { accountAddress } = await connect(config, { walletName: "Rialo" });
|
|
626
|
+
|
|
627
|
+
// Sign
|
|
628
|
+
const { signedTransaction } = await signTransaction(config, { transaction });
|
|
629
|
+
|
|
630
|
+
// Disconnect
|
|
631
|
+
await disconnect(config);
|
|
632
|
+
|
|
633
|
+
// Cleanup
|
|
634
|
+
config.destroy();
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## Troubleshooting
|
|
640
|
+
|
|
641
|
+
### "No wallets found"
|
|
642
|
+
|
|
643
|
+
1. Install a Rialo wallet extension
|
|
644
|
+
2. Make sure it's enabled for your site
|
|
645
|
+
3. Refresh the page
|
|
646
|
+
|
|
647
|
+
### Connection keeps failing
|
|
648
|
+
|
|
649
|
+
1. Unlock your wallet
|
|
650
|
+
2. Check if the wallet supports your network (devnet/mainnet)
|
|
651
|
+
3. Try disabling and re-enabling the extension
|
|
652
|
+
4. Clear localStorage and refresh
|
|
653
|
+
|
|
654
|
+
### Hooks not updating
|
|
655
|
+
|
|
656
|
+
**Wrong:** Creating config inside component
|
|
657
|
+
|
|
658
|
+
```tsx
|
|
659
|
+
function App() {
|
|
660
|
+
const config = createConfig({ ... }); // ❌ Recreated every render
|
|
661
|
+
return <FrostProvider config={config}>...</FrostProvider>;
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Right:** Creating config outside component
|
|
666
|
+
|
|
667
|
+
```tsx
|
|
668
|
+
const config = createConfig({ ... }); // ✅ Created once
|
|
669
|
+
|
|
670
|
+
function App() {
|
|
671
|
+
return <FrostProvider config={config}>...</FrostProvider>;
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### "Session expired" on page load
|
|
676
|
+
|
|
677
|
+
This is expected behavior. The default session TTL is 7 days. Users just need to reconnect.
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
## Configuration Options
|
|
682
|
+
|
|
683
|
+
```typescript
|
|
684
|
+
interface CreateConfigOptions {
|
|
685
|
+
/** RPC client configuration (required) */
|
|
686
|
+
clientConfig: RialoClientConfig;
|
|
687
|
+
|
|
688
|
+
/** Auto-reconnect on page load (default: true) */
|
|
689
|
+
autoConnect?: boolean;
|
|
690
|
+
|
|
691
|
+
/** Session TTL in ms (default: 7 days) */
|
|
692
|
+
sessionTTL?: number;
|
|
693
|
+
|
|
694
|
+
/** Storage key prefix (default: "rialo-frost") */
|
|
695
|
+
storageKey?: string;
|
|
696
|
+
|
|
697
|
+
/** Custom storage (default: localStorage, null to disable) */
|
|
698
|
+
storage?: Storage | null;
|
|
699
|
+
}
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
---
|
|
703
|
+
|
|
704
|
+
## Networks
|
|
705
|
+
|
|
706
|
+
| Network | Chain ID | Use Case |
|
|
707
|
+
|---------|----------|----------|
|
|
708
|
+
| Devnet | `rialo:devnet` | Development (free test tokens) |
|
|
709
|
+
| Testnet | `rialo:testnet` | Pre-production testing |
|
|
710
|
+
| Mainnet | `rialo:mainnet` | Production |
|
|
711
|
+
|
|
712
|
+
---
|
|
713
|
+
|
|
714
|
+
## CSS Custom Properties
|
|
715
|
+
|
|
716
|
+
Frost components support theming via CSS custom properties. Set these variables to customize the appearance:
|
|
717
|
+
|
|
718
|
+
### Colors
|
|
719
|
+
|
|
720
|
+
| Variable | Default | Description |
|
|
721
|
+
|----------|---------|-------------|
|
|
722
|
+
| `--frost-primary` | `#3b82f6` | Primary brand color (buttons, accents) |
|
|
723
|
+
| `--frost-text-color` | `#111827` | Main text color |
|
|
724
|
+
| `--frost-text-secondary` | `#6b7280` | Secondary/muted text |
|
|
725
|
+
| `--frost-border-color` | `#e5e7eb` | Border and divider color |
|
|
726
|
+
| `--frost-danger` | `#ef4444` | Destructive action color (disconnect) |
|
|
727
|
+
|
|
728
|
+
### Backgrounds
|
|
729
|
+
|
|
730
|
+
| Variable | Default | Description |
|
|
731
|
+
|----------|---------|-------------|
|
|
732
|
+
| `--frost-button-text` | `#ffffff` | Button text color |
|
|
733
|
+
| `--frost-button-connected-bg` | `#f3f4f6` | Connected button background |
|
|
734
|
+
| `--frost-button-connected-text` | `#111827` | Connected button text |
|
|
735
|
+
| `--frost-dropdown-bg` | `#ffffff` | Dropdown menu background |
|
|
736
|
+
| `--frost-hover-bg` | `#f3f4f6` | Hover state background |
|
|
737
|
+
| `--frost-modal-bg` | `#ffffff` | Modal dialog background |
|
|
738
|
+
| `--frost-overlay-bg` | `rgba(0, 0, 0, 0.5)` | Modal overlay background |
|
|
739
|
+
| `--frost-icon-bg` | `#f3f4f6` | Wallet icon background |
|
|
740
|
+
|
|
741
|
+
### Layout
|
|
742
|
+
|
|
743
|
+
| Variable | Default | Description |
|
|
744
|
+
|----------|---------|-------------|
|
|
745
|
+
| `--frost-border-radius` | `8px` | Default border radius |
|
|
746
|
+
| `--frost-border-radius-sm` | `6px` | Small border radius |
|
|
747
|
+
| `--frost-modal-max-width` | `400px` | Maximum modal width |
|
|
748
|
+
| `--frost-shadow` | `0 4px 16px rgba(0, 0, 0, 0.12)` | Box shadow |
|
|
749
|
+
| `--frost-z-index` | `9999` | Z-index for overlays |
|
|
750
|
+
|
|
751
|
+
### Example: Dark Theme
|
|
752
|
+
|
|
753
|
+
```css
|
|
754
|
+
:root {
|
|
755
|
+
--frost-primary: #60a5fa;
|
|
756
|
+
--frost-text-color: #f9fafb;
|
|
757
|
+
--frost-text-secondary: #9ca3af;
|
|
758
|
+
--frost-border-color: #374151;
|
|
759
|
+
--frost-button-text: #ffffff;
|
|
760
|
+
--frost-button-connected-bg: #1f2937;
|
|
761
|
+
--frost-button-connected-text: #f9fafb;
|
|
762
|
+
--frost-dropdown-bg: #1f2937;
|
|
763
|
+
--frost-hover-bg: #374151;
|
|
764
|
+
--frost-modal-bg: #111827;
|
|
765
|
+
--frost-overlay-bg: rgba(0, 0, 0, 0.7);
|
|
766
|
+
--frost-icon-bg: #374151;
|
|
767
|
+
}
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Example: Custom Brand Colors
|
|
771
|
+
|
|
772
|
+
```css
|
|
773
|
+
:root {
|
|
774
|
+
--frost-primary: #8b5cf6; /* Purple primary */
|
|
775
|
+
--frost-border-radius: 12px; /* More rounded */
|
|
776
|
+
--frost-shadow: 0 8px 32px rgba(139, 92, 246, 0.2); /* Purple tinted shadow */
|
|
777
|
+
}
|
|
778
|
+
```
|