@liberfi.io/ui-trade 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/README.md +441 -5
- package/dist/index.d.mts +487 -3
- package/dist/index.d.ts +487 -3
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -7
package/README.md
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
# @liberfi.io/ui-trade
|
|
2
2
|
|
|
3
|
-
Trade hooks for the Liberfi React SDK. This package provides chain-agnostic swap logic that
|
|
3
|
+
Trade hooks and widgets for the Liberfi React SDK. This package provides chain-agnostic swap logic and ready-to-use trade form components that work across Solana, Ethereum, and BSC.
|
|
4
|
+
|
|
5
|
+
The package is organized in three layers:
|
|
6
|
+
|
|
7
|
+
- **Hooks** (`useSwap`, `useSwapRoutePolling`, `useTxConfirmation`) — pure-logic building blocks with no UI side-effects, designed for IoC.
|
|
8
|
+
- **Swap Widget** (`SwapWidget` / `useSwapScript` / `SwapUI` / `SwapPreviewModal`) — a full swap form with preview-before-confirm flow, following the Script/Widget/UI three-layer architecture, composable at any level.
|
|
9
|
+
- **Instant Trade** (`InstantTradeWidget` / `InstantTradeProvider` / `PresetFormWidget`) — a configurable quick-trade form with buy/sell tabs, preset management, customizable quick-amount buttons, and trade settings (slippage, priority fee, tip fee, anti-MEV).
|
|
4
10
|
|
|
5
11
|
## Design Philosophy
|
|
6
12
|
|
|
7
|
-
- **IoC (Inversion of Control)** — Side-effects (toast, analytics, navigation) are injected via
|
|
13
|
+
- **IoC (Inversion of Control)** — Side-effects (toast, analytics, navigation) are injected via callbacks. Hooks never hardcode any feedback mechanism.
|
|
14
|
+
- **Script/Widget/UI architecture** — The swap form follows the monorepo's three-layer pattern: `useSwapScript` (data + state), `SwapWidget` (orchestration), `SwapUI` (presentation). Consumers can use the widget directly or compose Script + custom UI.
|
|
8
15
|
- **Chain-agnostic** — Works across Solana (Jupiter) and EVM chains (KyberSwap). Chain-specific differences (dex selection, tx format) are handled by the underlying `@liberfi.io/client` and `WalletAdapter` abstractions.
|
|
9
|
-
- **
|
|
10
|
-
- **Layered architecture** — Builds on `@liberfi.io/react` (data layer)
|
|
16
|
+
- **UI via `@liberfi.io/ui`** — Uses the shared UI library (`Button`, `Input`, `Avatar`, `Modal`, etc.) for consistent styling across the monorepo. No direct `@heroui/react` dependency.
|
|
17
|
+
- **Layered architecture** — Builds on `@liberfi.io/react` (data layer), `@liberfi.io/wallet-connector` (signing layer), and `@liberfi.io/ui` (presentation layer) without duplicating their logic.
|
|
11
18
|
|
|
12
19
|
## Installation
|
|
13
20
|
|
|
@@ -20,6 +27,7 @@ Peer dependencies the consumer must provide:
|
|
|
20
27
|
- `react` (>=18)
|
|
21
28
|
- `react-dom` (>=18)
|
|
22
29
|
- `@liberfi.io/react` (provides `DexClientProvider` and `useDexClient`)
|
|
30
|
+
- `@liberfi.io/ui` (provides UI components: `Button`, `Input`, `Modal`, `Avatar`, etc.)
|
|
23
31
|
- `@liberfi.io/wallet-connector` (provides `WalletAdapter` type)
|
|
24
32
|
|
|
25
33
|
## API Reference
|
|
@@ -44,6 +52,27 @@ Orchestrates the full swap flow: **route -> sign -> send**.
|
|
|
44
52
|
| `swap` | `(input: SwapInput) => Promise<SwapResult>` | Executes the swap. Resolves with `SwapResult` on success, throws on failure. |
|
|
45
53
|
| `isSwapping` | `boolean` | Whether a swap is currently in progress. |
|
|
46
54
|
|
|
55
|
+
#### `useSwapRoutePolling(params, options?)`
|
|
56
|
+
|
|
57
|
+
Polls for swap route quotes at a configurable interval. Built on `useSwapRouteQuery` (TanStack Query).
|
|
58
|
+
|
|
59
|
+
**Parameters:**
|
|
60
|
+
|
|
61
|
+
| Name | Type | Description |
|
|
62
|
+
| ------------------ | ------------------------- | ---------------------------------------------------------------------- |
|
|
63
|
+
| `params` | `Partial<API.SwapParams>` | Route parameters. Polling starts when all required fields are present. |
|
|
64
|
+
| `options.interval` | `number` | Polling interval in ms. Defaults to 12000. |
|
|
65
|
+
| `options.paused` | `boolean` | Pause polling (e.g. during swap execution). |
|
|
66
|
+
| `options.onError` | `(error: Error) => void` | Called when a route fetch fails. |
|
|
67
|
+
|
|
68
|
+
**Returns:** `{ route, isRouting, error }`
|
|
69
|
+
|
|
70
|
+
| Name | Type | Description |
|
|
71
|
+
| ----------- | ---------------------------- | --------------------------------- |
|
|
72
|
+
| `route` | `API.SwapRoute \| undefined` | Current route quote. |
|
|
73
|
+
| `isRouting` | `boolean` | Whether a route is being fetched. |
|
|
74
|
+
| `error` | `Error \| null` | Latest route fetch error. |
|
|
75
|
+
|
|
47
76
|
#### `useTxConfirmation(options?: UseTxConfirmationOptions)`
|
|
48
77
|
|
|
49
78
|
Tracks transaction confirmation via backend SSE. Designed to compose with `useSwap`.
|
|
@@ -65,6 +94,126 @@ Tracks transaction confirmation via backend SSE. Designed to compose with `useSw
|
|
|
65
94
|
| `clearAll` | `() => void` | Remove all tracked transactions. |
|
|
66
95
|
| `transactions` | `Map<string, TrackedTx>` | All tracked transactions with their current status. |
|
|
67
96
|
|
|
97
|
+
### Components (Swap Widget)
|
|
98
|
+
|
|
99
|
+
The swap widget follows the **Script / Widget / UI** three-layer architecture.
|
|
100
|
+
|
|
101
|
+
#### `SwapWidget`
|
|
102
|
+
|
|
103
|
+
Plug-and-play swap form. Calls `useSwapScript` internally and renders `SwapUI` with `SwapPreviewModal`. Clicking the swap button opens a preview modal; confirming in the modal executes the swap.
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<SwapWidget
|
|
107
|
+
chain={Chain.SOLANA}
|
|
108
|
+
from="So11111111111111111111111111111111111111112"
|
|
109
|
+
to="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
|
|
110
|
+
onComplete={(result) => console.log(result)}
|
|
111
|
+
className="my-swap"
|
|
112
|
+
/>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Props (`SwapWidgetProps`):**
|
|
116
|
+
|
|
117
|
+
| Name | Type | Description |
|
|
118
|
+
| ------------- | --------------------------------------------------------- | ----------------------------- |
|
|
119
|
+
| `chain` | `Chain` | Target chain. |
|
|
120
|
+
| `from?` | `string` | Initial from-token address. |
|
|
121
|
+
| `to?` | `string` | Initial to-token address. |
|
|
122
|
+
| `onComplete?` | `(result: { success: boolean; txHash?: string }) => void` | Called when swap completes. |
|
|
123
|
+
| `className?` | `string` | External style customization. |
|
|
124
|
+
|
|
125
|
+
#### `useSwapScript(params: UseSwapScriptParams)`
|
|
126
|
+
|
|
127
|
+
Script layer hook. Encapsulates all data fetching, state management, and swap execution. No JSX, no UI imports. Use this to build a custom swap UI.
|
|
128
|
+
|
|
129
|
+
**Parameters (`UseSwapScriptParams`):**
|
|
130
|
+
|
|
131
|
+
| Name | Type | Description |
|
|
132
|
+
| ------------- | --------------------------------------------------------- | --------------------------- |
|
|
133
|
+
| `chain` | `Chain` | Target chain. |
|
|
134
|
+
| `from?` | `string` | Initial from-token address. |
|
|
135
|
+
| `to?` | `string` | Initial to-token address. |
|
|
136
|
+
| `onComplete?` | `(result: { success: boolean; txHash?: string }) => void` | Called when swap completes. |
|
|
137
|
+
|
|
138
|
+
**Returns (`UseSwapScriptResult`):**
|
|
139
|
+
|
|
140
|
+
| Name | Type | Description |
|
|
141
|
+
| --------------------- | ---------------------------------- | -------------------------------------------- |
|
|
142
|
+
| `fromTokenAddress` | `string` | Currently selected from-token address. |
|
|
143
|
+
| `toTokenAddress` | `string` | Currently selected to-token address. |
|
|
144
|
+
| `setFromTokenAddress` | `(addr: string) => void` | Update from-token address. |
|
|
145
|
+
| `setToTokenAddress` | `(addr: string) => void` | Update to-token address. |
|
|
146
|
+
| `fromToken` | `Token \| null` | From-token metadata. |
|
|
147
|
+
| `toToken` | `Token \| null` | To-token metadata. |
|
|
148
|
+
| `fromBalance` | `Portfolio \| null` | User's from-token balance. |
|
|
149
|
+
| `toBalance` | `Portfolio \| null` | User's to-token balance. |
|
|
150
|
+
| `amount` | `string \| undefined` | Human-readable amount. |
|
|
151
|
+
| `setAmount` | `(v: string \| undefined) => void` | Update amount. |
|
|
152
|
+
| `setHalfAmount` | `() => void` | Set amount to half of from-token balance. |
|
|
153
|
+
| `setMaxAmount` | `() => void` | Set amount to full from-token balance. |
|
|
154
|
+
| `amountInDecimals` | `string \| undefined` | Amount in smallest unit. |
|
|
155
|
+
| `amountInUsd` | `string \| undefined` | Amount in USD. |
|
|
156
|
+
| `outputAmount` | `string \| undefined` | Formatted output amount from route. |
|
|
157
|
+
| `outputAmountInUsd` | `string \| undefined` | Output amount in USD. |
|
|
158
|
+
| `route` | `API.SwapRoute \| undefined` | Current route quote. |
|
|
159
|
+
| `isRouting` | `boolean` | Whether a route is being fetched. |
|
|
160
|
+
| `routeError` | `Error \| null` | Route fetch error. |
|
|
161
|
+
| `swap` | `() => Promise<void>` | Execute the swap. |
|
|
162
|
+
| `isSwapping` | `boolean` | Whether a swap is in progress. |
|
|
163
|
+
| `txStatus` | `TxConfirmationStatus` | Confirmation status of the last transaction. |
|
|
164
|
+
| `isLoading` | `boolean` | Whether initial data is loading. |
|
|
165
|
+
|
|
166
|
+
#### `SwapUI`
|
|
167
|
+
|
|
168
|
+
Pure presentational component for the swap form. Renders the From/To token inputs with token selector buttons, balance display, Half/Max shortcuts, and a preview button. Styled with `@liberfi.io/ui` components and Tailwind CSS.
|
|
169
|
+
|
|
170
|
+
**Props (`SwapUIProps`):**
|
|
171
|
+
|
|
172
|
+
| Name | Type | Description |
|
|
173
|
+
| ------------------- | ---------------------------------- | --------------------------------- |
|
|
174
|
+
| `fromToken` | `Token \| null` | From-token metadata. |
|
|
175
|
+
| `toToken` | `Token \| null` | To-token metadata. |
|
|
176
|
+
| `fromBalance` | `Portfolio \| null` | User's from-token balance. |
|
|
177
|
+
| `toBalance` | `Portfolio \| null` | User's to-token balance. |
|
|
178
|
+
| `amount` | `string \| undefined` | Human-readable input amount. |
|
|
179
|
+
| `amountInUsd` | `string \| undefined` | Input amount in USD. |
|
|
180
|
+
| `onAmountChange` | `(v: string \| undefined) => void` | Amount change handler. |
|
|
181
|
+
| `onHalfAmount?` | `() => void` | Half balance shortcut handler. |
|
|
182
|
+
| `onMaxAmount?` | `() => void` | Max balance shortcut handler. |
|
|
183
|
+
| `outputAmount` | `string \| undefined` | Formatted output amount. |
|
|
184
|
+
| `outputAmountInUsd` | `string \| undefined` | Output amount in USD. |
|
|
185
|
+
| `onFromTokenSelect` | `(addr: string) => void` | From-token selection handler. |
|
|
186
|
+
| `onToTokenSelect` | `(addr: string) => void` | To-token selection handler. |
|
|
187
|
+
| `route` | `API.SwapRoute \| undefined` | Current swap route quote. |
|
|
188
|
+
| `isRouting` | `boolean` | Whether a route is being fetched. |
|
|
189
|
+
| `routeError` | `Error \| null` | Route error. |
|
|
190
|
+
| `onPreview` | `() => void` | Open preview modal handler. |
|
|
191
|
+
| `isSwapping` | `boolean` | Whether a swap is in progress. |
|
|
192
|
+
| `className?` | `string` | External style customization. |
|
|
193
|
+
|
|
194
|
+
#### `SwapPreviewModal`
|
|
195
|
+
|
|
196
|
+
Modal component that previews swap details before confirmation. Shows from/to token cards with amounts, an expandable route plan details section, and a confirm button.
|
|
197
|
+
|
|
198
|
+
**Props (`SwapPreviewModalProps`):**
|
|
199
|
+
|
|
200
|
+
| Name | Type | Description |
|
|
201
|
+
| ------------------- | ---------------------------- | --------------------------------- |
|
|
202
|
+
| `isOpen` | `boolean` | Whether the modal is open. |
|
|
203
|
+
| `onOpenChange` | `(isOpen: boolean) => void` | Modal open/close handler. |
|
|
204
|
+
| `fromToken` | `Token \| null` | From-token metadata. |
|
|
205
|
+
| `toToken` | `Token \| null` | To-token metadata. |
|
|
206
|
+
| `fromBalance` | `Portfolio \| null` | User's from-token balance. |
|
|
207
|
+
| `inputAmount` | `string \| undefined` | Input amount (human-readable). |
|
|
208
|
+
| `inputAmountInUsd` | `string \| undefined` | Input amount in USD. |
|
|
209
|
+
| `outputAmount` | `string \| undefined` | Output amount (human-readable). |
|
|
210
|
+
| `outputAmountInUsd` | `string \| undefined` | Output amount in USD. |
|
|
211
|
+
| `route` | `API.SwapRoute \| undefined` | Current swap route. |
|
|
212
|
+
| `isRouting` | `boolean` | Whether a route is being fetched. |
|
|
213
|
+
| `routeError` | `Error \| null` | Route error. |
|
|
214
|
+
| `onConfirm` | `() => void` | Confirm swap handler. |
|
|
215
|
+
| `isSwapping` | `boolean` | Whether a swap is in progress. |
|
|
216
|
+
|
|
68
217
|
### Types
|
|
69
218
|
|
|
70
219
|
#### `SwapInput`
|
|
@@ -128,6 +277,16 @@ interface UseTxConfirmationOptions {
|
|
|
128
277
|
}
|
|
129
278
|
```
|
|
130
279
|
|
|
280
|
+
#### `UseSwapRoutePollingOptions`
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
interface UseSwapRoutePollingOptions {
|
|
284
|
+
interval?: number; // default: 12000
|
|
285
|
+
paused?: boolean;
|
|
286
|
+
onError?: (error: Error) => void;
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
131
290
|
### Constants
|
|
132
291
|
|
|
133
292
|
#### `version`
|
|
@@ -138,7 +297,91 @@ const version: string; // e.g. "0.1.0"
|
|
|
138
297
|
|
|
139
298
|
## Usage Examples
|
|
140
299
|
|
|
141
|
-
###
|
|
300
|
+
### SwapWidget (Plug-and-Play)
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
import { Chain } from "@liberfi.io/types";
|
|
304
|
+
import { SwapWidget } from "@liberfi.io/ui-trade";
|
|
305
|
+
|
|
306
|
+
function TradePage() {
|
|
307
|
+
return (
|
|
308
|
+
<SwapWidget
|
|
309
|
+
chain={Chain.SOLANA}
|
|
310
|
+
from="So11111111111111111111111111111111111111112"
|
|
311
|
+
to="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
|
|
312
|
+
onComplete={(result) => {
|
|
313
|
+
if (result.success) {
|
|
314
|
+
toast.success(`Swap confirmed: ${result.txHash}`);
|
|
315
|
+
} else {
|
|
316
|
+
toast.error("Swap failed");
|
|
317
|
+
}
|
|
318
|
+
}}
|
|
319
|
+
/>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Custom UI with useSwapScript + SwapPreviewModal
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
import { Chain } from "@liberfi.io/types";
|
|
328
|
+
import { useDisclosure } from "@liberfi.io/ui";
|
|
329
|
+
import { useSwapScript, SwapUI, SwapPreviewModal } from "@liberfi.io/ui-trade";
|
|
330
|
+
|
|
331
|
+
function MyCustomSwapForm() {
|
|
332
|
+
const script = useSwapScript({
|
|
333
|
+
chain: Chain.SOLANA,
|
|
334
|
+
from: "So11111111111111111111111111111111111111112",
|
|
335
|
+
to: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
336
|
+
onComplete: (result) => console.log("Done:", result),
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const { isOpen, onOpen, onOpenChange } = useDisclosure();
|
|
340
|
+
|
|
341
|
+
return (
|
|
342
|
+
<>
|
|
343
|
+
<SwapUI
|
|
344
|
+
fromToken={script.fromToken}
|
|
345
|
+
toToken={script.toToken}
|
|
346
|
+
fromBalance={script.fromBalance}
|
|
347
|
+
toBalance={script.toBalance}
|
|
348
|
+
amount={script.amount}
|
|
349
|
+
amountInUsd={script.amountInUsd}
|
|
350
|
+
onAmountChange={script.setAmount}
|
|
351
|
+
onHalfAmount={script.setHalfAmount}
|
|
352
|
+
onMaxAmount={script.setMaxAmount}
|
|
353
|
+
outputAmount={script.outputAmount}
|
|
354
|
+
outputAmountInUsd={script.outputAmountInUsd}
|
|
355
|
+
onFromTokenSelect={script.setFromTokenAddress}
|
|
356
|
+
onToTokenSelect={script.setToTokenAddress}
|
|
357
|
+
route={script.route}
|
|
358
|
+
isRouting={script.isRouting}
|
|
359
|
+
routeError={script.routeError}
|
|
360
|
+
onPreview={onOpen}
|
|
361
|
+
isSwapping={script.isSwapping}
|
|
362
|
+
/>
|
|
363
|
+
<SwapPreviewModal
|
|
364
|
+
isOpen={isOpen}
|
|
365
|
+
onOpenChange={onOpenChange}
|
|
366
|
+
fromToken={script.fromToken}
|
|
367
|
+
toToken={script.toToken}
|
|
368
|
+
fromBalance={script.fromBalance}
|
|
369
|
+
inputAmount={script.amount}
|
|
370
|
+
inputAmountInUsd={script.amountInUsd}
|
|
371
|
+
outputAmount={script.outputAmount}
|
|
372
|
+
outputAmountInUsd={script.outputAmountInUsd}
|
|
373
|
+
route={script.route}
|
|
374
|
+
isRouting={script.isRouting}
|
|
375
|
+
routeError={script.routeError}
|
|
376
|
+
onConfirm={script.swap}
|
|
377
|
+
isSwapping={script.isSwapping}
|
|
378
|
+
/>
|
|
379
|
+
</>
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Swap with Confirmation Tracking (Low-Level Hooks)
|
|
142
385
|
|
|
143
386
|
```tsx
|
|
144
387
|
import { Chain } from "@liberfi.io/types";
|
|
@@ -236,8 +479,201 @@ function EvmSwapButton() {
|
|
|
236
479
|
}
|
|
237
480
|
```
|
|
238
481
|
|
|
482
|
+
### Components (Instant Trade)
|
|
483
|
+
|
|
484
|
+
The instant trade module provides a configurable quick-trade form with preset management and customizable quick-amount buttons.
|
|
485
|
+
|
|
486
|
+
#### `InstantTradeWidget`
|
|
487
|
+
|
|
488
|
+
Full instant-trade form with buy/sell tabs, amount input, quick buttons, preset management, and trade execution.
|
|
489
|
+
|
|
490
|
+
```tsx
|
|
491
|
+
<InstantTradeWidget
|
|
492
|
+
chain={Chain.SOLANA}
|
|
493
|
+
tokenAddress="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
|
|
494
|
+
onSwapSubmitted={(result) => console.log(result)}
|
|
495
|
+
onSwapError={(error, phase) => console.error(error)}
|
|
496
|
+
/>
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Props (`InstantTradeWidgetProps`):**
|
|
500
|
+
|
|
501
|
+
| Name | Type | Description |
|
|
502
|
+
| ------------------- | ------------------------------------------ | ----------------------------------------------------- |
|
|
503
|
+
| `chain` | `Chain` | Target chain. |
|
|
504
|
+
| `tokenAddress` | `string` | Address of the token being traded. |
|
|
505
|
+
| `onSwapSubmitted?` | `(result: SwapResult) => void` | Called when a swap is submitted. |
|
|
506
|
+
| `onSwapError?` | `(error: Error, phase: SwapPhase) => void` | Called on swap error. |
|
|
507
|
+
| `settings?` | `InstantTradeSettings` | Controlled settings (omit for localStorage). |
|
|
508
|
+
| `onSettingsChange?` | `(settings: InstantTradeSettings) => void` | Called when settings change. |
|
|
509
|
+
| `headerExtra?` | `ReactNode` | Slot for extra header content (e.g. wallet switcher). |
|
|
510
|
+
| `className?` | `string` | External style customization. |
|
|
511
|
+
|
|
512
|
+
#### `InstantTradeProvider`
|
|
513
|
+
|
|
514
|
+
Context provider for instant trade state. Use with `InstantTradeAmountInput` and `InstantTradeButton` for compact inline trading.
|
|
515
|
+
|
|
516
|
+
```tsx
|
|
517
|
+
<InstantTradeProvider chain={Chain.SOLANA} tokenAddress="...">
|
|
518
|
+
<InstantTradeAmountInput
|
|
519
|
+
amount={amount}
|
|
520
|
+
onAmountChange={setAmount}
|
|
521
|
+
preset={preset}
|
|
522
|
+
onPresetChange={setPreset}
|
|
523
|
+
/>
|
|
524
|
+
<InstantTradeButton />
|
|
525
|
+
</InstantTradeProvider>
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
#### `useInstantTradeScript(params)`
|
|
529
|
+
|
|
530
|
+
Script-layer hook for the instant trade form. Must be used inside an `InstantTradeProvider`. Encapsulates token queries, balance queries, form state, and swap execution.
|
|
531
|
+
|
|
532
|
+
#### `InstantTradeAmountInput`
|
|
533
|
+
|
|
534
|
+
Compact amount input with lightning icon, native token avatar, and 3 preset buttons (P1/P2/P3) with tooltips. Must be inside an `InstantTradeProvider`.
|
|
535
|
+
|
|
536
|
+
#### `InstantTradeButton`
|
|
537
|
+
|
|
538
|
+
Trade execution button that reads state from `InstantTradeProvider`. Handles wallet resolution, amount conversion, and swap execution.
|
|
539
|
+
|
|
540
|
+
#### `PresetFormWidget`
|
|
541
|
+
|
|
542
|
+
Standalone widget for editing trade preset values (slippage, priority fee, tip fee, auto fee, anti-MEV, custom RPC).
|
|
543
|
+
|
|
544
|
+
```tsx
|
|
545
|
+
<PresetFormWidget
|
|
546
|
+
chain={Chain.SOLANA}
|
|
547
|
+
value={presetValues}
|
|
548
|
+
onChange={setPresetValues}
|
|
549
|
+
/>
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
#### `PresetFormUI`
|
|
553
|
+
|
|
554
|
+
Pure presentational preset form. Accepts value/onChange props directly, with optional `nativeSymbol` and `nativeDecimals`.
|
|
555
|
+
|
|
556
|
+
### Instant Trade Types
|
|
557
|
+
|
|
558
|
+
#### `TradePresetValues`
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
interface TradePresetValues {
|
|
562
|
+
slippage: number | null;
|
|
563
|
+
priorityFee: number | null;
|
|
564
|
+
tipFee: number | null;
|
|
565
|
+
autoFee: boolean;
|
|
566
|
+
maxAutoFee: number | null;
|
|
567
|
+
antiMev: "off" | "reduced" | "secure";
|
|
568
|
+
customRPC: string | null;
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
#### `InstantTradeSettings`
|
|
573
|
+
|
|
574
|
+
```typescript
|
|
575
|
+
interface InstantTradeSettings {
|
|
576
|
+
buy: BuySettings;
|
|
577
|
+
sell: SellSettings;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
interface BuySettings {
|
|
581
|
+
customAmounts: (number | null)[];
|
|
582
|
+
presets: TradePresetValues[];
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
interface SellSettings {
|
|
586
|
+
customPercentages: (number | null)[];
|
|
587
|
+
presets: TradePresetValues[];
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
#### `DEFAULT_TRADE_PRESET`
|
|
592
|
+
|
|
593
|
+
Default preset values: slippage=20, priorityFee=0.001, tipFee=0.001, autoFee=false, maxAutoFee=0.1, antiMev="off".
|
|
594
|
+
|
|
595
|
+
#### `DEFAULT_INSTANT_TRADE_SETTINGS`
|
|
596
|
+
|
|
597
|
+
Default settings with 4 buy amounts (0.01, 0.1, 1, 10), 4 sell percentages (10, 25, 50, 100), and 3 default presets each.
|
|
598
|
+
|
|
599
|
+
## Usage Examples
|
|
600
|
+
|
|
601
|
+
### InstantTradeWidget (Full Trade Form)
|
|
602
|
+
|
|
603
|
+
```tsx
|
|
604
|
+
import { Chain } from "@liberfi.io/types";
|
|
605
|
+
import { InstantTradeWidget } from "@liberfi.io/ui-trade";
|
|
606
|
+
|
|
607
|
+
function TokenTradePage({ tokenAddress }: { tokenAddress: string }) {
|
|
608
|
+
return (
|
|
609
|
+
<InstantTradeWidget
|
|
610
|
+
chain={Chain.SOLANA}
|
|
611
|
+
tokenAddress={tokenAddress}
|
|
612
|
+
onSwapSubmitted={(result) => {
|
|
613
|
+
toast.success(`Transaction submitted: ${result.txHash}`);
|
|
614
|
+
}}
|
|
615
|
+
onSwapError={(error, phase) => {
|
|
616
|
+
toast.error(`Swap failed at ${phase}: ${error.message}`);
|
|
617
|
+
}}
|
|
618
|
+
/>
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### Compact Inline Trading
|
|
624
|
+
|
|
625
|
+
```tsx
|
|
626
|
+
import { Chain } from "@liberfi.io/types";
|
|
627
|
+
import {
|
|
628
|
+
InstantTradeProvider,
|
|
629
|
+
InstantTradeAmountInput,
|
|
630
|
+
InstantTradeButton,
|
|
631
|
+
} from "@liberfi.io/ui-trade";
|
|
632
|
+
|
|
633
|
+
function TokenHeader({ tokenAddress }: { tokenAddress: string }) {
|
|
634
|
+
return (
|
|
635
|
+
<InstantTradeProvider chain={Chain.SOLANA} tokenAddress={tokenAddress}>
|
|
636
|
+
<div className="flex items-center gap-2">
|
|
637
|
+
<InstantTradeAmountInput
|
|
638
|
+
amount={undefined}
|
|
639
|
+
onAmountChange={() => {}}
|
|
640
|
+
variant="bordered"
|
|
641
|
+
size="sm"
|
|
642
|
+
/>
|
|
643
|
+
<InstantTradeButton />
|
|
644
|
+
</div>
|
|
645
|
+
</InstantTradeProvider>
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
### Preset Settings Editor
|
|
651
|
+
|
|
652
|
+
```tsx
|
|
653
|
+
import { useState } from "react";
|
|
654
|
+
import { Chain } from "@liberfi.io/types";
|
|
655
|
+
import { PresetFormWidget, DEFAULT_TRADE_PRESET } from "@liberfi.io/ui-trade";
|
|
656
|
+
|
|
657
|
+
function TradeSettings() {
|
|
658
|
+
const [preset, setPreset] = useState(DEFAULT_TRADE_PRESET);
|
|
659
|
+
|
|
660
|
+
return (
|
|
661
|
+
<PresetFormWidget
|
|
662
|
+
chain={Chain.SOLANA}
|
|
663
|
+
value={preset}
|
|
664
|
+
onChange={setPreset}
|
|
665
|
+
/>
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
```
|
|
669
|
+
|
|
239
670
|
## Future Improvements
|
|
240
671
|
|
|
241
672
|
- Add `useSwapQuote` hook for fetching quotes without executing (read-only route info).
|
|
242
673
|
- Add ERC20 approval detection and `useTokenApproval` hook for EVM chains that don't support permit.
|
|
243
674
|
- Support `ExactOut` swap mode with output amount estimation.
|
|
675
|
+
- Add a skeleton loading component (`swap-skeleton.ui.tsx`) for the swap form.
|
|
676
|
+
- Support token-select callback props on `SwapWidget` for integrating with external token picker UI.
|
|
677
|
+
- Add limit order and advanced trade modes to `InstantTradeWidget`.
|
|
678
|
+
- Add token price conversion display in the instant trade form.
|
|
679
|
+
- Support custom RPC endpoint validation in `PresetFormWidget`.
|