@liberfi.io/ui-trade 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 CHANGED
@@ -6,7 +6,8 @@ The package is organized in three layers:
6
6
 
7
7
  - **Hooks** (`useSwap`, `useSwapRoutePolling`, `useTxConfirmation`) — pure-logic building blocks with no UI side-effects, designed for IoC.
8
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).
9
+ - **Instant Trade** (`InstantTradeWidget` / `InstantTradeProvider` / `MultiChainPresetFormWidget` / `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).
10
+ - **State atoms** (`presetAtomFamily` / `instantTradeAmountAtomFamily`) — Jotai-based persistent state for trade presets and amount/preset selection, storage-backend agnostic via `atomWithStorage`.
10
11
 
11
12
  ## Design Philosophy
12
13
 
@@ -26,8 +27,10 @@ Peer dependencies the consumer must provide:
26
27
 
27
28
  - `react` (>=18)
28
29
  - `react-dom` (>=18)
30
+ - `jotai` (>=2.15.1) — used for persistent preset state via `atomWithStorage`
29
31
  - `@liberfi.io/react` (provides `DexClientProvider` and `useDexClient`)
30
32
  - `@liberfi.io/ui` (provides UI components: `Button`, `Input`, `Modal`, `Avatar`, etc.)
33
+ - `@liberfi.io/ui-chain-select` (provides `ChainSelectMobileUI` for `MultiChainPresetFormWidget`)
31
34
  - `@liberfi.io/wallet-connector` (provides `WalletAdapter` type)
32
35
 
33
36
  ## API Reference
@@ -511,47 +514,185 @@ Full instant-trade form with buy/sell tabs, amount input, quick buttons, preset
511
514
 
512
515
  #### `InstantTradeProvider`
513
516
 
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
- ```
517
+ Context provider for instant trade state. Use with `InstantTradeButton` for compact inline trading.
527
518
 
528
519
  #### `useInstantTradeScript(params)`
529
520
 
530
521
  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
522
 
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`.
523
+ #### `AmountPresetInputUI`
524
+
525
+ Pure presentational compact amount input with token icon, lightning icon, and 3 preset buttons (P1/P2/P3) with tooltips. No context dependency all data is received via props.
526
+
527
+ **Props (`AmountPresetInputUIProps`):**
528
+
529
+ | Name | Type | Description |
530
+ | ----------------- | -------------------------------- | ------------------------------------------------------------------- |
531
+ | `token` | `PredefinedToken` | Payment token (provides symbol, decimals, and icon). |
532
+ | `chain` | `Chain` | Target chain — used by preset tooltips to show chain-specific info. |
533
+ | `amount?` | `number` | Current amount value. |
534
+ | `onAmountChange` | `(amount?: number) => void` | Called when the amount changes. |
535
+ | `preset?` | `number` | Currently selected preset index (0–2). Defaults to 0. |
536
+ | `onPresetChange?` | `(preset: number) => void` | Called when the user selects a different preset. |
537
+ | `onPresetClick?` | `(preset: number) => void` | Called when the user clicks the already-selected preset. |
538
+ | `presetValues?` | `TradePresetValues[]` | Preset configurations for tooltip display. Falls back to defaults. |
539
+ | `size?` | `"sm" \| "lg"` | Controls overall component size. Defaults to `"sm"`. |
540
+ | `variant?` | `"default" \| "bordered"` | Visual variant. |
541
+ | `radius?` | `"full" \| "lg" \| "md" \| "sm"` | Border radius. |
542
+ | `fullWidth?` | `boolean` | Whether the input takes full width. |
543
+ | `className?` | `string` | External style customization. |
544
+
545
+ #### `AmountPresetInputWidget`
546
+
547
+ Atom-backed widget that wraps `AmountPresetInputUI` with `atomWithStorage` persistence. Amount and preset selection are persisted per `id + chain + token.address` so different payment tokens and chains have separate saved values.
548
+
549
+ **Props (`AmountPresetInputWidgetProps`):**
550
+
551
+ | Name | Type | Description |
552
+ | ------------------- | -------------------------------- | ---------------------------------------------------------------------------- |
553
+ | `id` | `string` | Business identifier for storage key (e.g. `"token-detail"`). |
554
+ | `chain` | `Chain` | Target chain. |
555
+ | `token` | `PredefinedToken` | Payment token (provides symbol, decimals, address for storage key). |
556
+ | `storageKeyPrefix?` | `string` | Storage key prefix. Must match `PresetFormWidget`. Defaults to `"liberfi."`. |
557
+ | `onAmountChange?` | `(amount?: number) => void` | Notification callback (does not control state). |
558
+ | `onPresetChange?` | `(preset: number) => void` | Notification callback (does not control state). |
559
+ | `onPresetClick?` | `(preset: number) => void` | Called when the user clicks the already-selected preset. |
560
+ | `size?` | `"sm" \| "lg"` | Controls overall component size. Defaults to `"sm"`. |
561
+ | `variant?` | `"default" \| "bordered"` | Visual variant. |
562
+ | `radius?` | `"full" \| "lg" \| "md" \| "sm"` | Border radius. |
563
+ | `fullWidth?` | `boolean` | Whether the input takes full width. |
564
+ | `className?` | `string` | External style customization. |
535
565
 
536
566
  #### `InstantTradeButton`
537
567
 
538
568
  Trade execution button that reads state from `InstantTradeProvider`. Handles wallet resolution, amount conversion, and swap execution.
539
569
 
570
+ #### `MultiChainPresetFormWidget`
571
+
572
+ Self-contained multi-chain preset editor. Combines chain switching (`ChainSelectMobileUI`), preset index tabs (P1/P2/P3), and a persisted `PresetFormWidget`.
573
+
574
+ ```tsx
575
+ <MultiChainPresetFormWidget
576
+ chains={[Chain.SOLANA, Chain.ETHEREUM, Chain.BINANCE]}
577
+ onChange={(chain, idx, value) => console.log(chain, idx, value)}
578
+ />
579
+ ```
580
+
581
+ **Props (`MultiChainPresetFormWidgetProps`):**
582
+
583
+ | Name | Type | Description |
584
+ | ------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------- |
585
+ | `chains` | `Chain[]` | Available chains to switch between. |
586
+ | `defaultChain?` | `Chain` | Initial chain. Defaults to first item in `chains`. |
587
+ | `storageKeyPrefix?` | `string` | Storage key prefix. Defaults to `"liberfi."`. |
588
+ | `onChange?` | `(chain: Chain, presetIndex: number, value: TradePresetValues) => void` | Notification callback with chain and preset context. |
589
+ | `className?` | `string` | External style customization. |
590
+
540
591
  #### `PresetFormWidget`
541
592
 
542
- Standalone widget for editing trade preset values (slippage, priority fee, tip fee, auto fee, anti-MEV, custom RPC).
593
+ Atom-backed widget for editing a single trade preset (slippage, priority fee, tip fee, auto fee, anti-MEV, custom RPC). State is persisted via `atomWithStorage` (keyed by prefix + chain + preset index).
594
+
595
+ For a pure presentational form without persistence, use `PresetFormUI` directly.
543
596
 
544
597
  ```tsx
545
598
  <PresetFormWidget
546
599
  chain={Chain.SOLANA}
547
- value={presetValues}
548
- onChange={setPresetValues}
600
+ presetIndex={0}
601
+ storageKeyPrefix="myapp."
602
+ onChange={(next) => console.log("Preset changed:", next)}
549
603
  />
550
604
  ```
551
605
 
606
+ **Props (`PresetFormWidgetProps`):**
607
+
608
+ | Name | Type | Description |
609
+ | ------------------- | ------------------------------------ | ---------------------------------------------------- |
610
+ | `chain` | `Chain` | Target chain — determines default values and fields. |
611
+ | `presetIndex?` | `number` | Preset index (0, 1, or 2). Defaults to 0. |
612
+ | `storageKeyPrefix?` | `string` | Storage key prefix. Defaults to `"liberfi."`. |
613
+ | `onChange?` | `(value: TradePresetValues) => void` | Notification callback (does not control state). |
614
+ | `className?` | `string` | External style customization. |
615
+
552
616
  #### `PresetFormUI`
553
617
 
554
- Pure presentational preset form. Accepts value/onChange props directly, with optional `nativeSymbol` and `nativeDecimals`.
618
+ Pure presentational preset form. Accepts value/onChange props directly. No persistence the caller owns the state.
619
+
620
+ #### `useInstantTradeAmount(params)`
621
+
622
+ Read-only hook for the persisted instant-trade amount and preset index. Reads from the same `atomWithStorage` atom family that `AmountPresetInputWidget` writes to.
623
+
624
+ **Parameters (`UseInstantTradeAmountParams`):**
625
+
626
+ | Name | Type | Description |
627
+ | ------------------- | -------- | ----------------------------------------------------------------------------- |
628
+ | `id` | `string` | Business identifier (must match the widget's `id`). |
629
+ | `chain` | `Chain` | Target chain. |
630
+ | `tokenAddress` | `string` | Payment token address. |
631
+ | `storageKeyPrefix?` | `string` | Storage key prefix. Must match the widget's prefix. Defaults to `"liberfi."`. |
632
+
633
+ **Returns:** `AmountPresetState`
634
+
635
+ | Name | Type | Description |
636
+ | -------- | --------------------- | ----------------------- |
637
+ | `amount` | `number \| undefined` | Persisted amount value. |
638
+ | `preset` | `number` | Persisted preset index. |
639
+
640
+ ### State Atoms
641
+
642
+ #### `presetAtomFamily`
643
+
644
+ Atom family for trade preset values, persisted via `atomWithStorage`. Each atom is keyed by `"{chain}:{index}"` (use `presetKey` to build the key). Default values are chain-specific.
645
+
646
+ ```typescript
647
+ import { presetAtomFamily, presetKey } from "@liberfi.io/ui-trade";
648
+
649
+ const atom = presetAtomFamily(presetKey(Chain.SOLANA, "buy", 0));
650
+ ```
651
+
652
+ #### `presetKey(chain, direction, index, prefix?)`
653
+
654
+ Constructs an atomFamily key string from chain, direction, preset index and optional prefix.
655
+
656
+ | Name | Type | Description |
657
+ | ----------- | ----------------- | --------------------------------------------- |
658
+ | `chain` | `Chain` | Target chain. |
659
+ | `direction` | `"buy" \| "sell"` | Trade direction. |
660
+ | `index` | `number` | Preset index (0, 1, or 2). |
661
+ | `prefix?` | `string` | Storage key prefix. Defaults to `"liberfi."`. |
662
+
663
+ **Returns:** `string` — key for use with `presetAtomFamily`.
664
+
665
+ #### `instantTradeAmountAtomFamily`
666
+
667
+ Atom family for instant-trade amount and preset index, persisted via `atomWithStorage`. Each atom is keyed by a string built with `instantTradeAmountKey`. The storage key includes `id`, `chain`, and `tokenAddress` so the same token on different chains has separate persisted values.
668
+
669
+ ```typescript
670
+ import {
671
+ instantTradeAmountAtomFamily,
672
+ instantTradeAmountKey,
673
+ } from "@liberfi.io/ui-trade";
674
+
675
+ const atom = instantTradeAmountAtomFamily(
676
+ instantTradeAmountKey(
677
+ "token-detail",
678
+ Chain.SOLANA,
679
+ "11111111111111111111111111111111",
680
+ ),
681
+ );
682
+ ```
683
+
684
+ #### `instantTradeAmountKey(id, chain, tokenAddress, prefix?)`
685
+
686
+ Constructs an atomFamily key string for instant-trade amount + preset state.
687
+
688
+ | Name | Type | Description |
689
+ | -------------- | -------- | --------------------------------------------- |
690
+ | `id` | `string` | Business identifier. |
691
+ | `chain` | `Chain` | Target chain. |
692
+ | `tokenAddress` | `string` | Payment token address. |
693
+ | `prefix?` | `string` | Storage key prefix. Defaults to `"liberfi."`. |
694
+
695
+ **Returns:** `string` — key for use with `instantTradeAmountAtomFamily`.
555
696
 
556
697
  ### Instant Trade Types
557
698
 
@@ -588,7 +729,7 @@ interface SellSettings {
588
729
  }
589
730
  ```
590
731
 
591
- #### `DEFAULT_TRADE_PRESET`
732
+ #### `DEFAULT_SOL_TRADE_PRESET`
592
733
 
593
734
  Default preset values: slippage=20, priorityFee=0.001, tipFee=0.001, autoFee=false, maxAutoFee=0.1, antiMev="off".
594
735
 
@@ -620,53 +761,100 @@ function TokenTradePage({ tokenAddress }: { tokenAddress: string }) {
620
761
  }
621
762
  ```
622
763
 
623
- ### Compact Inline Trading
764
+ ### AmountPresetInputWidget (Persisted Quick-Buy)
624
765
 
625
766
  ```tsx
626
767
  import { Chain } from "@liberfi.io/types";
627
- import {
628
- InstantTradeProvider,
629
- InstantTradeAmountInput,
630
- InstantTradeButton,
631
- } from "@liberfi.io/ui-trade";
768
+ import { AmountPresetInputWidget } from "@liberfi.io/ui-trade";
769
+ import { SOLANA_TOKENS } from "@liberfi.io/utils";
632
770
 
633
- function TokenHeader({ tokenAddress }: { tokenAddress: string }) {
771
+ function TokenHeader() {
634
772
  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>
773
+ <AmountPresetInputWidget
774
+ id="token-detail"
775
+ chain={Chain.SOLANA}
776
+ token={SOLANA_TOKENS.native}
777
+ variant="bordered"
778
+ size="sm"
779
+ onPresetClick={(p) => console.log(`Open settings for preset ${p}`)}
780
+ />
646
781
  );
647
782
  }
648
783
  ```
649
784
 
650
- ### Preset Settings Editor
785
+ ### AmountPresetInputUI (Controlled)
651
786
 
652
787
  ```tsx
653
788
  import { useState } from "react";
654
789
  import { Chain } from "@liberfi.io/types";
655
- import { PresetFormWidget, DEFAULT_TRADE_PRESET } from "@liberfi.io/ui-trade";
790
+ import { AmountPresetInputUI } from "@liberfi.io/ui-trade";
791
+ import { SOLANA_TOKENS } from "@liberfi.io/utils";
656
792
 
657
- function TradeSettings() {
658
- const [preset, setPreset] = useState(DEFAULT_TRADE_PRESET);
793
+ function CustomAmountInput() {
794
+ const [amount, setAmount] = useState<number | undefined>();
795
+ const [preset, setPreset] = useState(0);
659
796
 
660
797
  return (
661
- <PresetFormWidget
798
+ <AmountPresetInputUI
799
+ token={SOLANA_TOKENS.native}
662
800
  chain={Chain.SOLANA}
663
- value={preset}
664
- onChange={setPreset}
801
+ amount={amount}
802
+ onAmountChange={setAmount}
803
+ preset={preset}
804
+ onPresetChange={setPreset}
805
+ variant="bordered"
806
+ size="lg"
807
+ />
808
+ );
809
+ }
810
+ ```
811
+
812
+ ### Reading Persisted Amount/Preset
813
+
814
+ ```tsx
815
+ import { Chain } from "@liberfi.io/types";
816
+ import { useInstantTradeAmount } from "@liberfi.io/ui-trade";
817
+
818
+ function useTokenDetailAmount(tokenAddress: string) {
819
+ const { amount, preset } = useInstantTradeAmount({
820
+ id: "token-detail",
821
+ chain: Chain.SOLANA,
822
+ tokenAddress,
823
+ });
824
+ return { amount, preset };
825
+ }
826
+ ```
827
+
828
+ ### Multi-Chain Preset Settings Editor
829
+
830
+ ```tsx
831
+ import { Chain } from "@liberfi.io/types";
832
+ import { MultiChainPresetFormWidget } from "@liberfi.io/ui-trade";
833
+
834
+ function TradeSettings() {
835
+ return (
836
+ <MultiChainPresetFormWidget
837
+ chains={[Chain.SOLANA, Chain.ETHEREUM, Chain.BINANCE]}
838
+ storageKeyPrefix="myapp."
839
+ onChange={(chain, idx, value) =>
840
+ console.log(`chain=${chain}, preset=${idx}`, value)
841
+ }
665
842
  />
666
843
  );
667
844
  }
668
845
  ```
669
846
 
847
+ ### Reading Preset Values from Atoms
848
+
849
+ ```tsx
850
+ import { Chain } from "@liberfi.io/types";
851
+ import { usePresetValues } from "@liberfi.io/ui-trade";
852
+
853
+ function useCurrentPreset(chain: Chain, index: number) {
854
+ return usePresetValues({ chain, direction: "buy", presetIndex: index });
855
+ }
856
+ ```
857
+
670
858
  ## Future Improvements
671
859
 
672
860
  - Add `useSwapQuote` hook for fetching quotes without executing (read-only route info).