@orbs-network/spot-react 0.0.46 → 0.0.48

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/.cursor/rules DELETED
@@ -1,831 +0,0 @@
1
- # @orbs-network/spot-react Integration Guide
2
-
3
- ## Overview
4
- `@orbs-network/spot-react` is a React component library for building Spot trading interfaces (TWAP, Limit, Stop-Loss, Take-Profit orders) on EVM chains.
5
-
6
- ## Installation
7
- ```bash
8
- npm install @orbs-network/spot-react
9
- # or
10
- pnpm add @orbs-network/spot-react
11
- ```
12
-
13
- ## Required Peer Dependencies
14
- - react ^18.0.0 || ^19.0.0
15
- - react-dom ^18.0.0 || ^19.0.0
16
-
17
- ## Core Setup Pattern
18
-
19
- ### 1. Wrap your trading UI with SpotProvider
20
-
21
- ```tsx
22
- import {
23
- SpotProvider,
24
- Module,
25
- Partners,
26
- Components,
27
- Token,
28
- useSrcTokenPanel,
29
- useDstTokenPanel,
30
- // ... other hooks
31
- } from "@orbs-network/spot-react";
32
-
33
- function TradingPage() {
34
- // Get wallet connection (wagmi example)
35
- const { address, chainId } = useAccount();
36
- const { data: walletClient } = useWalletClient();
37
-
38
- // Your token selection state
39
- const [srcToken, setSrcToken] = useState<Token>();
40
- const [dstToken, setDstToken] = useState<Token>();
41
-
42
- // Fetch balances (wei strings)
43
- const srcBalance = useBalance(srcToken?.address); // "1000000000000000000"
44
- const dstBalance = useBalance(dstToken?.address);
45
-
46
- // Fetch USD prices for 1 token
47
- const srcUsd1Token = useUSDPrice(srcToken?.address); // "1850.50"
48
- const dstUsd1Token = useUSDPrice(dstToken?.address);
49
-
50
- // Calculate market reference price (expected output for input amount)
51
- const marketReferencePrice = useMarketPrice(srcToken, dstToken);
52
-
53
- // Settings
54
- const slippage = 0.05; // 5%
55
-
56
- // Callbacks for order lifecycle events
57
- const callbacks = useOrderCallbacks();
58
-
59
- return (
60
- <SpotProvider
61
- partner={Partners.Quick} // Your partner ID
62
- chainId={chainId}
63
- module={Module.TWAP} // TWAP | LIMIT | STOP_LOSS | TAKE_PROFIT
64
- priceProtection={slippage}
65
- minChunkSizeUsd={5}
66
-
67
- // Token data
68
- srcToken={srcToken}
69
- dstToken={dstToken}
70
- srcBalance={srcBalance}
71
- dstBalance={dstBalance}
72
- srcUsd1Token={srcUsd1Token}
73
- dstUsd1Token={dstUsd1Token}
74
-
75
- // Market price - CRITICAL: This should be the expected output amount
76
- // for the typed input amount, not just a 1:1 ratio
77
- marketReferencePrice={marketReferencePrice}
78
-
79
- // Wallet
80
- account={address}
81
- provider={walletClient?.transport}
82
-
83
- // UI Components (required)
84
- components={{
85
- Button: YourButton,
86
- Tooltip: YourTooltip,
87
- TokenLogo: YourTokenLogo,
88
- Spinner: <YourSpinner />,
89
- }}
90
-
91
- // Optional
92
- callbacks={callbacks}
93
- refetchBalances={() => refetchBalances()}
94
- useToken={useTokenByAddress} // Hook to get token by address
95
- fees={0.25} // Fee percentage
96
- >
97
- <YourTradingUI />
98
- </SpotProvider>
99
- );
100
- }
101
- ```
102
-
103
- ### 2. Token Type Definition
104
- ```tsx
105
- type Token = {
106
- address: string; // Contract address
107
- symbol: string; // Token symbol (e.g., "ETH")
108
- decimals: number; // Token decimals (e.g., 18)
109
- logoUrl: string; // Logo image URL
110
- };
111
-
112
- // Example conversion from your Currency type:
113
- const parseToSpotToken = (currency?: Currency): Token | undefined => {
114
- if (!currency) return undefined;
115
- return {
116
- address: currency.address,
117
- decimals: currency.decimals,
118
- symbol: currency.symbol,
119
- logoUrl: currency.logoUrl,
120
- };
121
- };
122
- ```
123
-
124
- ### 3. Required UI Components
125
-
126
- ```tsx
127
- // Button Component
128
- type ButtonProps = {
129
- children: ReactNode;
130
- onClick: () => void;
131
- disabled?: boolean;
132
- loading?: boolean;
133
- style?: CSSProperties;
134
- allowClickWhileLoading?: boolean;
135
- };
136
-
137
- const SpotButton = (props: ButtonProps) => {
138
- return (
139
- <Button isLoading={props.loading} onClick={props.onClick} disabled={props.disabled}>
140
- {props.children}
141
- </Button>
142
- );
143
- };
144
-
145
- // Tooltip Component
146
- type TooltipProps = {
147
- children?: ReactNode;
148
- tooltipText?: string;
149
- };
150
-
151
- const SpotTooltip = (props: TooltipProps) => {
152
- if (!props.tooltipText) return null;
153
- return (
154
- <Tooltip>
155
- <TooltipTrigger>{props.children || <InfoIcon />}</TooltipTrigger>
156
- <TooltipContent>{props.tooltipText}</TooltipContent>
157
- </Tooltip>
158
- );
159
- };
160
-
161
- // Token Logo Component
162
- type TokenLogoProps = {
163
- token?: Token;
164
- size?: number;
165
- className?: string;
166
- };
167
-
168
- const SpotTokenLogo = ({ token }: TokenLogoProps) => {
169
- return <Avatar src={token?.logoUrl ?? ""} />;
170
- };
171
- ```
172
-
173
- ---
174
-
175
- ## Using the Hooks
176
-
177
- ### Token Panel Hooks
178
-
179
- #### `useSrcTokenPanel` / `useDstTokenPanel`
180
- Controls the source (sell) and destination (buy) token inputs.
181
-
182
- ```tsx
183
- const TokenPanel = ({ isSrcToken }: { isSrcToken: boolean }) => {
184
- const srcTokenPanel = useSrcTokenPanel();
185
- const dstTokenPanel = useDstTokenPanel();
186
- const panel = isSrcToken ? srcTokenPanel : dstTokenPanel;
187
-
188
- return (
189
- <div>
190
- <input
191
- value={panel.value}
192
- onChange={(e) => panel.onChange(e.target.value)}
193
- disabled={!isSrcToken} // Only src is editable
194
- />
195
- {panel.isLoading && <Spinner />}
196
- </div>
197
- );
198
- };
199
- ```
200
-
201
- #### `useTypedSrcAmount`
202
- Get the current typed source amount.
203
-
204
- ```tsx
205
- const Listener = () => {
206
- const { amount } = useTypedSrcAmount();
207
-
208
- useEffect(() => {
209
- // Sync with your app state
210
- setInputAmount(amount ?? "");
211
- }, [amount]);
212
-
213
- return null;
214
- };
215
- ```
216
-
217
- ### Order Configuration Hooks
218
-
219
- #### `useTradesPanel` (TWAP only)
220
- Configure number of trades for TWAP orders.
221
-
222
- ```tsx
223
- const TradesPanel = () => {
224
- const { totalTrades, onChange, label, tooltip, error } = useTradesPanel();
225
-
226
- return (
227
- <Card error={Boolean(error)}>
228
- <Label title={label} tooltip={tooltip} />
229
- <div className="flex items-center gap-2">
230
- <NumericInput
231
- value={totalTrades?.toString() ?? ""}
232
- onChange={(val) => onChange(Number(val))}
233
- />
234
- <span>Trades</span>
235
- </div>
236
- </Card>
237
- );
238
- };
239
- ```
240
-
241
- #### `useDurationPanel`
242
- Configure order expiration duration.
243
-
244
- ```tsx
245
- import { DEFAULT_DURATION_OPTIONS } from "@orbs-network/spot-react";
246
- // Options: [{ text: "Minutes", value: 60 }, { text: "Hours", value: 3600 }, { text: "Days", value: 86400 }]
247
-
248
- const DurationPanel = () => {
249
- const { duration, onInputChange, onUnitSelect, label, tooltip } = useDurationPanel();
250
-
251
- return (
252
- <Card>
253
- <Label title={label} tooltip={tooltip} />
254
- <div className="flex items-center gap-2">
255
- <NumericInput
256
- value={duration.value?.toString() ?? ""}
257
- onChange={(val) => onInputChange(val)}
258
- />
259
- <Select
260
- value={duration.unit}
261
- onChange={(unit) => onUnitSelect(unit)}
262
- options={DEFAULT_DURATION_OPTIONS}
263
- />
264
- </div>
265
- </Card>
266
- );
267
- };
268
- ```
269
-
270
- #### `useFillDelayPanel` (TWAP only)
271
- Configure delay between individual trades.
272
-
273
- ```tsx
274
- const FillDelayPanel = () => {
275
- const { fillDelay, onInputChange, onUnitSelect, label, tooltip } = useFillDelayPanel();
276
-
277
- return (
278
- <Card>
279
- <Label title={label} tooltip={tooltip} />
280
- <div className="flex items-center gap-2">
281
- <NumericInput
282
- value={fillDelay.value?.toString() ?? ""}
283
- onChange={(val) => onInputChange(val)}
284
- />
285
- <Select
286
- value={fillDelay.unit}
287
- onChange={(unit) => onUnitSelect(unit)}
288
- options={DEFAULT_DURATION_OPTIONS}
289
- />
290
- </div>
291
- </Card>
292
- );
293
- };
294
- ```
295
-
296
- ### Price Hooks
297
-
298
- #### `useLimitPricePanel`
299
- Configure limit price for orders.
300
-
301
- ```tsx
302
- const LimitPricePanel = () => {
303
- const {
304
- price, // Current price value
305
- onChange, // Update price
306
- percentage, // Percentage from market price
307
- onPercentageChange, // Update via percentage
308
- usd, // USD value
309
- isLimitPrice, // Is limit price enabled
310
- toggleLimitPrice, // Toggle on/off
311
- onReset, // Reset to market price
312
- isLoading,
313
- toToken, // Destination token
314
- label,
315
- tooltip,
316
- } = useLimitPricePanel();
317
-
318
- return (
319
- <div>
320
- <div className="flex items-center gap-2">
321
- {/* Toggle for non-LIMIT modules */}
322
- <Switch checked={isLimitPrice} onCheckedChange={toggleLimitPrice} />
323
- <Label title={label} tooltip={tooltip} />
324
- {isLimitPrice && <button onClick={onReset}>Set to default</button>}
325
- </div>
326
-
327
- {isLimitPrice && (
328
- <div className="flex gap-2">
329
- <div>
330
- <span>{toToken?.symbol}</span>
331
- <NumericInput value={price} onChange={onChange} isLoading={isLoading} />
332
- <span>${usd}</span>
333
- </div>
334
- <NumericInput
335
- value={percentage}
336
- onChange={onPercentageChange}
337
- suffix="%"
338
- allowNegative
339
- />
340
- </div>
341
- )}
342
- </div>
343
- );
344
- };
345
- ```
346
-
347
- #### `useTriggerPricePanel` (Stop-Loss / Take-Profit only)
348
- Configure trigger price for conditional orders.
349
-
350
- ```tsx
351
- const TriggerPricePanel = () => {
352
- const {
353
- price,
354
- onChange,
355
- percentage,
356
- onPercentageChange,
357
- onReset,
358
- toToken,
359
- usd,
360
- label,
361
- tooltip,
362
- } = useTriggerPricePanel();
363
-
364
- return (
365
- <div>
366
- <Label title={label} tooltip={tooltip} />
367
- <button onClick={onReset}>Set to default</button>
368
- <PriceInput
369
- symbol={toToken?.symbol}
370
- value={price}
371
- onChange={onChange}
372
- percentage={percentage}
373
- onPercentageChange={onPercentageChange}
374
- usd={usd}
375
- />
376
- </div>
377
- );
378
- };
379
- ```
380
-
381
- #### `useInvertTradePanel`
382
- Invert the price display direction.
383
-
384
- ```tsx
385
- const PricesHeader = () => {
386
- const { onInvert, isInverted, fromToken, isMarketPrice } = useInvertTradePanel();
387
-
388
- return (
389
- <div className="flex items-center justify-between">
390
- <span>
391
- {isInverted ? "Buy " : "Sell "}
392
- {fromToken?.symbol} {isMarketPrice ? "at best rate" : "at rate"}
393
- </span>
394
- {!isMarketPrice && (
395
- <button onClick={onInvert}>
396
- <ArrowLeftRightIcon />
397
- </button>
398
- )}
399
- </div>
400
- );
401
- };
402
- ```
403
-
404
- ### Validation
405
-
406
- #### `useInputErrors`
407
- Get current validation errors.
408
-
409
- ```tsx
410
- const InputsErrorPanel = () => {
411
- const error = useInputErrors();
412
- // Returns: { type: InputErrors, value: string | number, message: string } | null
413
-
414
- if (!error) return null;
415
-
416
- return (
417
- <div className="bg-destructive/50 p-2 rounded-md">
418
- <AlertTriangleIcon />
419
- <p>{error.message}</p>
420
- </div>
421
- );
422
- };
423
-
424
- // Available error types:
425
- enum InputErrors {
426
- EMPTY_LIMIT_PRICE,
427
- MAX_CHUNKS,
428
- MIN_CHUNKS,
429
- MIN_TRADE_SIZE,
430
- MAX_FILL_DELAY,
431
- MIN_FILL_DELAY,
432
- MAX_ORDER_DURATION,
433
- MIN_ORDER_DURATION,
434
- MISSING_LIMIT_PRICE,
435
- STOP_LOSS_TRIGGER_PRICE_GREATER_THAN_MARKET_PRICE,
436
- TRIGGER_LIMIT_PRICE_GREATER_THAN_TRIGGER_PRICE,
437
- TAKE_PROFIT_TRIGGER_PRICE_LESS_THAN_MARKET_PRICE,
438
- EMPTY_TRIGGER_PRICE,
439
- INSUFFICIENT_BALANCE,
440
- MAX_ORDER_SIZE,
441
- }
442
- ```
443
-
444
- ### Order Submission
445
-
446
- #### `useSubmitOrderPanel`
447
- Full control over order submission flow.
448
-
449
- ```tsx
450
- const SubmitSwap = () => {
451
- const {
452
- onSubmit, // Call to submit order
453
- onOpenModal, // Call when opening confirmation modal
454
- onCloseModal, // Call when closing modal
455
- isLoading, // Submission in progress
456
- isSuccess, // Order created successfully
457
- isFailed, // Order creation failed
458
- parsedError, // Error details { message, code }
459
- orderId, // Created order ID
460
- status, // SwapStatus enum
461
- reset, // Reset swap state
462
- } = useSubmitOrderPanel();
463
-
464
- const [isOpen, setIsOpen] = useState(false);
465
-
466
- const handleOpen = () => {
467
- setIsOpen(true);
468
- onOpenModal(); // Important: call this to prepare state
469
- };
470
-
471
- const handleClose = () => {
472
- setIsOpen(false);
473
- onCloseModal(); // Important: call this to cleanup
474
- };
475
-
476
- return (
477
- <Dialog open={isOpen} onOpenChange={handleClose}>
478
- <SubmitButton onClick={handleOpen} />
479
- <DialogContent>
480
- {parsedError ? (
481
- <ErrorView error={parsedError} onClose={handleClose} />
482
- ) : (
483
- <Components.SubmitOrderPanel
484
- reviewDetails={
485
- <Button onClick={onSubmit} isLoading={isLoading}>
486
- Create Order
487
- </Button>
488
- }
489
- />
490
- )}
491
- </DialogContent>
492
- </Dialog>
493
- );
494
- };
495
- ```
496
-
497
- #### `useSubmitOrderButton`
498
- Just the button state (simpler).
499
-
500
- ```tsx
501
- const SubmitButton = ({ onClick }: { onClick: () => void }) => {
502
- const { disabled, text, loading } = useSubmitOrderButton();
503
- // text: "Place Order" | "Enter Amount" | "Insufficient Funds" | "No Liquidity"
504
-
505
- return (
506
- <Button onClick={onClick} disabled={disabled} isLoading={loading}>
507
- {text}
508
- </Button>
509
- );
510
- };
511
- ```
512
-
513
- ### Order History
514
-
515
- #### `useOrderHistoryPanel`
516
- Display and filter order history.
517
-
518
- ```tsx
519
- import { Components, OrderStatus, SelectMeuItem } from "@orbs-network/spot-react";
520
-
521
- const OrdersPanel = () => {
522
- const {
523
- orders, // { all: Order[], open: Order[], completed: Order[], ... }
524
- selectedOrder, // Currently selected order details
525
- onSelectStatus, // Filter by status
526
- statuses, // Available status filters
527
- selectedStatus, // Current filter
528
- onHideSelectedOrder, // Go back to list
529
- } = useOrderHistoryPanel();
530
-
531
- const handleSelectStatus = (item: SelectMeuItem) => {
532
- onSelectStatus(item.value === "all" ? undefined : item.value as OrderStatus);
533
- };
534
-
535
- return (
536
- <div>
537
- <header>
538
- {selectedOrder && (
539
- <button onClick={onHideSelectedOrder}>
540
- <ArrowLeftIcon />
541
- </button>
542
- )}
543
- <h2>{selectedOrder?.title ?? `Orders (${orders.all?.length})`}</h2>
544
- </header>
545
-
546
- {!selectedOrder && (
547
- <Select
548
- value={selectedStatus}
549
- onChange={handleSelectStatus}
550
- options={statuses}
551
- />
552
- )}
553
-
554
- {/* Pre-built Orders component */}
555
- <Components.Orders />
556
- </div>
557
- );
558
- };
559
- ```
560
-
561
- ### Other Useful Hooks
562
-
563
- #### `useDisclaimerPanel`
564
- Show module-specific disclaimer.
565
-
566
- ```tsx
567
- const Disclaimer = () => {
568
- const message = useDisclaimerPanel();
569
- // Returns: { text: string, url: string } | null
570
-
571
- if (!message) return null;
572
-
573
- return (
574
- <div>
575
- <InfoIcon />
576
- <p>
577
- {message.text}{" "}
578
- <a href={message.url} target="_blank">Learn more</a>
579
- </p>
580
- </div>
581
- );
582
- };
583
- ```
584
-
585
- #### `useTranslations`
586
- Get translated strings.
587
-
588
- ```tsx
589
- const t = useTranslations();
590
-
591
- // Usage:
592
- t("placeOrder") // "Place Order"
593
- t("enterAmount") // "Enter Amount"
594
- t("insufficientFunds") // "Insufficient Funds"
595
- t("noLiquidity") // "No Liquidity"
596
- // ... many more keys
597
- ```
598
-
599
- #### `useFormatNumber`
600
- Format numbers with proper locale.
601
-
602
- ```tsx
603
- const { value: formatted } = useFormatNumber({ value: "1234.5678", decimalScale: 2 });
604
- // "1,234.57"
605
- ```
606
-
607
- #### `usePartnerChains`
608
- Get supported chains for current partner.
609
-
610
- ```tsx
611
- const chains = usePartnerChains();
612
- // [1, 137, 56, ...]
613
- ```
614
-
615
- #### `useAddresses`
616
- Get contract addresses for current config.
617
-
618
- ```tsx
619
- const addresses = useAddresses();
620
- // { twap: "0x...", exchange: "0x...", ... }
621
- ```
622
-
623
- ---
624
-
625
- ## Callbacks Reference
626
-
627
- Set up callbacks to handle order lifecycle events:
628
-
629
- ```tsx
630
- const useOrderCallbacks = () => {
631
- const toastRef = useRef<number>(null);
632
-
633
- return {
634
- // Wrap ETH -> WETH
635
- onWrapRequest: () => {
636
- toastRef.current = toast.loading("Wrapping ETH...");
637
- },
638
- onWrapSuccess: ({ txHash, explorerUrl, amount }) => {
639
- toast.success("Wrapped ETH", {
640
- id: toastRef.current,
641
- action: <a href={explorerUrl}>View</a>,
642
- });
643
- },
644
-
645
- // Token Approval
646
- onApproveRequest: () => {
647
- toast.loading("Approving token...");
648
- },
649
- onApproveSuccess: ({ txHash, explorerUrl, token, amount }) => {
650
- toast.success(`Approved ${token.symbol}`);
651
- },
652
-
653
- // Order Signing
654
- onSignOrderRequest: () => {
655
- toast.loading("Creating order...", { description: "Proceed in wallet" });
656
- },
657
- onSignOrderSuccess: (signature) => {
658
- // Signature received
659
- },
660
- onSignOrderError: (error) => {
661
- toast.error("Failed to sign order");
662
- },
663
-
664
- // Order Created
665
- onOrderCreated: (order) => {
666
- toast.success(`Order created: ${order.srcTokenSymbol} → ${order.dstTokenSymbol}`);
667
- },
668
-
669
- // Order Filled
670
- onOrderFilled: (order) => {
671
- toast.success("Order filled!");
672
- },
673
-
674
- // Order Progress
675
- onOrdersProgressUpdate: (orders) => {
676
- // Orders updated (useful for live progress)
677
- },
678
-
679
- // Errors
680
- onSubmitOrderFailed: ({ message, code }) => {
681
- toast.error(`Failed: ${code}`);
682
- },
683
- onSubmitOrderRejected: () => {
684
- toast.error("Order rejected");
685
- },
686
-
687
- // Cancel
688
- onCancelOrderRequest: (orders) => {
689
- toast.loading("Cancelling order...");
690
- },
691
- onCancelOrderSuccess: ({ orders, txHash, explorerUrl }) => {
692
- toast.success("Order cancelled");
693
- },
694
- onCancelOrderFailed: (error) => {
695
- toast.error("Cancel failed");
696
- },
697
-
698
- // Clipboard
699
- onCopy: () => {
700
- toast.success("Copied to clipboard");
701
- },
702
- };
703
- };
704
- ```
705
-
706
- ---
707
-
708
- ## Pre-built Components
709
-
710
- ```tsx
711
- import { Components } from "@orbs-network/spot-react";
712
-
713
- // Submit order panel with steps display (wrap → approve → sign)
714
- <Components.SubmitOrderPanel
715
- reviewDetails={
716
- <div>
717
- <Switch label="Accept Disclaimer" />
718
- <Button onClick={onSubmit}>Create Order</Button>
719
- </div>
720
- }
721
- />
722
-
723
- // Orders list with details view
724
- <Components.Orders />
725
- ```
726
-
727
- ---
728
-
729
- ## Market Reference Price
730
-
731
- **CRITICAL**: The `marketReferencePrice.value` should be the expected output amount (in wei) for the current input amount, NOT a 1:1 price ratio.
732
-
733
- ### Option 1: Use a DEX aggregator quote
734
- ```tsx
735
- const useMarketReferencePrice = () => {
736
- const { trade, isLoading } = useDexQuote(srcToken, dstToken, inputAmountWei);
737
-
738
- return {
739
- value: trade?.outAmount, // Expected output in wei
740
- isLoading,
741
- noLiquidity: !trade && !isLoading,
742
- };
743
- };
744
- ```
745
-
746
- ### Option 2: Calculate from USD prices (synthetic)
747
- ```tsx
748
- const useMarketReferencePrice = () => {
749
- const srcUSD = useUSDPrice(srcToken?.address); // Price for 1 token
750
- const dstUSD = useUSDPrice(dstToken?.address);
751
- const inputAmountUI = toAmountUI(inputAmountWei, srcToken?.decimals);
752
-
753
- const outputAmountWei = useMemo(() => {
754
- if (!srcUSD || !dstUSD || !inputAmountUI) return undefined;
755
- const outputUI = (srcUSD / dstUSD) * parseFloat(inputAmountUI);
756
- return toAmountWei(outputUI.toString(), dstToken?.decimals);
757
- }, [srcUSD, dstUSD, inputAmountUI]);
758
-
759
- return {
760
- value: outputAmountWei,
761
- isLoading: !srcUSD || !dstUSD,
762
- noLiquidity: false,
763
- };
764
- };
765
- ```
766
-
767
- ---
768
-
769
- ## Module-Specific UI
770
-
771
- ```tsx
772
- const ModuleInputs = ({ module }: { module: Module }) => {
773
- if (module === Module.TWAP) {
774
- return (
775
- <>
776
- <TradesPanel /> {/* Number of trades */}
777
- <FillDelayPanel /> {/* Delay between trades */}
778
- </>
779
- );
780
- }
781
-
782
- // LIMIT, STOP_LOSS, TAKE_PROFIT
783
- return <DurationPanel />; {/* Order expiration */}
784
- };
785
-
786
- const PricePanels = ({ module }: { module: Module }) => {
787
- return (
788
- <>
789
- {/* Show trigger price for conditional orders */}
790
- {(module === Module.STOP_LOSS || module === Module.TAKE_PROFIT) && (
791
- <TriggerPricePanel />
792
- )}
793
-
794
- {/* Limit price (optional for TWAP, required for LIMIT) */}
795
- <LimitPricePanel />
796
- </>
797
- );
798
- };
799
- ```
800
-
801
- ---
802
-
803
- ## Integration Checklist
804
-
805
- 1. [ ] Install package and peer dependencies
806
- 2. [ ] Create Button, Tooltip, TokenLogo, Spinner components
807
- 3. [ ] Set up wallet connection (wagmi recommended)
808
- 4. [ ] Implement token fetching/selection
809
- 5. [ ] Implement balance fetching (wei strings)
810
- 6. [ ] Implement USD price fetching (price for 1 token)
811
- 7. [ ] Implement market reference price (output amount for input)
812
- 8. [ ] Wrap trading UI with SpotProvider
813
- 9. [ ] Build token panels with `useSrcTokenPanel`/`useDstTokenPanel`
814
- 10. [ ] Build order config UI based on module type
815
- 11. [ ] Build price panels with `useLimitPricePanel`/`useTriggerPricePanel`
816
- 12. [ ] Add validation display with `useInputErrors`
817
- 13. [ ] Build submission flow with `useSubmitOrderPanel`
818
- 14. [ ] Build orders history with `useOrderHistoryPanel`
819
- 15. [ ] Set up callbacks for toast notifications
820
- 16. [ ] Import styles: `import "@orbs-network/spot-react/styles.css"`
821
-
822
- ---
823
-
824
- ## Styles
825
-
826
- Import the default stylesheet:
827
- ```tsx
828
- import "@orbs-network/spot-react/styles.css";
829
- ```
830
-
831
- Or customize with your own CSS targeting the component classes.