@lifi/widget 4.1.0 → 4.3.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.
Files changed (210) hide show
  1. package/dist/esm/AppDefault.js +14 -0
  2. package/dist/esm/AppDefault.js.map +1 -1
  3. package/dist/esm/components/AmountInput/AmountInput.js +2 -1
  4. package/dist/esm/components/AmountInput/AmountInput.js.map +1 -1
  5. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +4 -2
  6. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  7. package/dist/esm/components/AmountInput/AmountInputEndAdornment.style.js +1 -1
  8. package/dist/esm/components/AmountInput/AmountInputEndAdornment.style.js.map +1 -1
  9. package/dist/esm/components/AmountInput/PriceFormHelperText.js +4 -2
  10. package/dist/esm/components/AmountInput/PriceFormHelperText.js.map +1 -1
  11. package/dist/esm/components/AmountInputCard/AmountInputCard.style.js +98 -0
  12. package/dist/esm/components/AmountInputCard/AmountInputCard.style.js.map +1 -0
  13. package/dist/esm/components/AmountInputCard/AmountInputCardPair.js +25 -0
  14. package/dist/esm/components/AmountInputCard/AmountInputCardPair.js.map +1 -0
  15. package/dist/esm/components/AmountInputCard/BalanceDisplay.js +35 -0
  16. package/dist/esm/components/AmountInputCard/BalanceDisplay.js.map +1 -0
  17. package/dist/esm/components/AmountInputCard/BalanceDisplay.style.js +11 -0
  18. package/dist/esm/components/AmountInputCard/BalanceDisplay.style.js.map +1 -0
  19. package/dist/esm/components/AmountInputCard/FiatValueToggle.js +41 -0
  20. package/dist/esm/components/AmountInputCard/FiatValueToggle.js.map +1 -0
  21. package/dist/esm/components/AmountInputCard/PercentageChips.js +80 -0
  22. package/dist/esm/components/AmountInputCard/PercentageChips.js.map +1 -0
  23. package/dist/esm/components/AmountInputCard/PercentageChips.style.js +43 -0
  24. package/dist/esm/components/AmountInputCard/PercentageChips.style.js.map +1 -0
  25. package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js +137 -0
  26. package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js.map +1 -0
  27. package/dist/esm/components/AmountInputCard/SendAmountCard.js +102 -0
  28. package/dist/esm/components/AmountInputCard/SendAmountCard.js.map +1 -0
  29. package/dist/esm/components/Avatar/Avatar.js +12 -3
  30. package/dist/esm/components/Avatar/Avatar.js.map +1 -1
  31. package/dist/esm/components/Chains/VirtualizedChainList.js +2 -1
  32. package/dist/esm/components/Chains/VirtualizedChainList.js.map +1 -1
  33. package/dist/esm/components/Header/HeaderNavigationTabs.js +14 -1
  34. package/dist/esm/components/Header/HeaderNavigationTabs.js.map +1 -1
  35. package/dist/esm/components/IconCircle/IconCircle.style.js +1 -1
  36. package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.js +99 -0
  37. package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.js.map +1 -0
  38. package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.style.js +52 -0
  39. package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.style.js.map +1 -0
  40. package/dist/esm/components/LimitPriceCard/LimitPriceCard.js +115 -0
  41. package/dist/esm/components/LimitPriceCard/LimitPriceCard.js.map +1 -0
  42. package/dist/esm/components/LimitPriceCard/LimitPriceCard.style.js +89 -0
  43. package/dist/esm/components/LimitPriceCard/LimitPriceCard.style.js.map +1 -0
  44. package/dist/esm/components/LimitPriceCard/LimitPricePresets.js +91 -0
  45. package/dist/esm/components/LimitPriceCard/LimitPricePresets.js.map +1 -0
  46. package/dist/esm/components/RouteCard/RouteProviderCard.js +118 -0
  47. package/dist/esm/components/RouteCard/RouteProviderCard.js.map +1 -0
  48. package/dist/esm/components/Routes/LimitOrderRoutes.js +34 -0
  49. package/dist/esm/components/Routes/LimitOrderRoutes.js.map +1 -0
  50. package/dist/esm/components/Routes/Routes.js +2 -2
  51. package/dist/esm/components/Routes/Routes.js.map +1 -1
  52. package/dist/esm/components/SettingsPanel/SettingsPanel.js +51 -0
  53. package/dist/esm/components/SettingsPanel/SettingsPanel.js.map +1 -0
  54. package/dist/esm/components/SettingsPanel/SettingsPanel.style.js +33 -0
  55. package/dist/esm/components/SettingsPanel/SettingsPanel.style.js.map +1 -0
  56. package/dist/esm/components/SettingsPanel/utils.js +14 -0
  57. package/dist/esm/components/SettingsPanel/utils.js.map +1 -0
  58. package/dist/esm/components/SwapButton/SwapButton.js +50 -0
  59. package/dist/esm/components/SwapButton/SwapButton.js.map +1 -0
  60. package/dist/esm/components/SwapButton/SwapButton.style.js +24 -0
  61. package/dist/esm/components/SwapButton/SwapButton.style.js.map +1 -0
  62. package/dist/esm/components/TokenPillButton/TokenPillButton.js +52 -0
  63. package/dist/esm/components/TokenPillButton/TokenPillButton.js.map +1 -0
  64. package/dist/esm/components/TokenPillButton/TokenPillButton.style.js +39 -0
  65. package/dist/esm/components/TokenPillButton/TokenPillButton.style.js.map +1 -0
  66. package/dist/esm/config/version.d.ts +1 -1
  67. package/dist/esm/config/version.js +1 -1
  68. package/dist/esm/config/version.js.map +1 -1
  69. package/dist/esm/hooks/useChainSearch.js +2 -1
  70. package/dist/esm/hooks/useChainSearch.js.map +1 -1
  71. package/dist/esm/hooks/useFilteredByTokenBalances.js +4 -1
  72. package/dist/esm/hooks/useFilteredByTokenBalances.js.map +1 -1
  73. package/dist/esm/hooks/useLimitMarketRate.js +24 -0
  74. package/dist/esm/hooks/useLimitMarketRate.js.map +1 -0
  75. package/dist/esm/hooks/useLinkedLimitFields.js +49 -0
  76. package/dist/esm/hooks/useLinkedLimitFields.js.map +1 -0
  77. package/dist/esm/hooks/useRoutes.js +23 -6
  78. package/dist/esm/hooks/useRoutes.js.map +1 -1
  79. package/dist/esm/hooks/useTokens.js +16 -14
  80. package/dist/esm/hooks/useTokens.js.map +1 -1
  81. package/dist/esm/hooks/useWideVariant.js +5 -2
  82. package/dist/esm/hooks/useWideVariant.js.map +1 -1
  83. package/dist/esm/i18n/en.json +22 -0
  84. package/dist/esm/index.d.ts +2 -2
  85. package/dist/esm/pages/MainPage/LimitPageContent.js +23 -0
  86. package/dist/esm/pages/MainPage/LimitPageContent.js.map +1 -0
  87. package/dist/esm/pages/MainPage/MainPage.js +19 -22
  88. package/dist/esm/pages/MainPage/MainPage.js.map +1 -1
  89. package/dist/esm/pages/MainPage/MainPageActions.js +28 -0
  90. package/dist/esm/pages/MainPage/MainPageActions.js.map +1 -0
  91. package/dist/esm/pages/MainPage/ReviewButton.js +9 -2
  92. package/dist/esm/pages/MainPage/ReviewButton.js.map +1 -1
  93. package/dist/esm/pages/RoutePriorityPage.js +48 -0
  94. package/dist/esm/pages/RoutePriorityPage.js.map +1 -0
  95. package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js +2 -2
  96. package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js.map +1 -1
  97. package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js +12 -27
  98. package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js.map +1 -1
  99. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippagePage.js +133 -0
  100. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippagePage.js.map +1 -0
  101. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js +14 -72
  102. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js.map +1 -1
  103. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.style.js +26 -14
  104. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.style.js.map +1 -1
  105. package/dist/esm/pages/SettingsPage/ThemeSettings.js +4 -0
  106. package/dist/esm/pages/SettingsPage/ThemeSettings.js.map +1 -1
  107. package/dist/esm/pages/TransactionPage/TransactionReview.js +4 -1
  108. package/dist/esm/pages/TransactionPage/TransactionReview.js.map +1 -1
  109. package/dist/esm/providers/I18nProvider/I18nProvider.js +1 -1
  110. package/dist/esm/providers/I18nProvider/enResource.d.ts +1 -1
  111. package/dist/esm/providers/I18nProvider/enResource.js +1 -1
  112. package/dist/esm/providers/I18nProvider/types.d.ts +2 -2
  113. package/dist/esm/providers/SDKClientProvider.js +25 -24
  114. package/dist/esm/providers/SDKClientProvider.js.map +1 -1
  115. package/dist/esm/providers/WidgetProvider/WidgetProvider.js +22 -21
  116. package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
  117. package/dist/esm/stores/form/URLSearchParamsBuilder.js +14 -3
  118. package/dist/esm/stores/form/URLSearchParamsBuilder.js.map +1 -1
  119. package/dist/esm/stores/form/createFormStore.js +6 -2
  120. package/dist/esm/stores/form/createFormStore.js.map +1 -1
  121. package/dist/esm/stores/form/getDefaultValuesFromQueryString.js +3 -0
  122. package/dist/esm/stores/form/getDefaultValuesFromQueryString.js.map +1 -1
  123. package/dist/esm/stores/form/types.d.ts +9 -1
  124. package/dist/esm/stores/form/types.d.ts.map +1 -1
  125. package/dist/esm/stores/form/types.js.map +1 -1
  126. package/dist/esm/stores/form/useClearAmountFields.js +1 -0
  127. package/dist/esm/stores/form/useClearAmountFields.js.map +1 -1
  128. package/dist/esm/stores/navigationTabs/useNavigationTabsStore.js +4 -2
  129. package/dist/esm/stores/navigationTabs/useNavigationTabsStore.js.map +1 -1
  130. package/dist/esm/stores/navigationTabs/utils.js +38 -8
  131. package/dist/esm/stores/navigationTabs/utils.js.map +1 -1
  132. package/dist/esm/types/events.d.ts +10 -1
  133. package/dist/esm/types/events.d.ts.map +1 -1
  134. package/dist/esm/types/events.js +2 -0
  135. package/dist/esm/types/events.js.map +1 -1
  136. package/dist/esm/types/widget.d.ts +10 -0
  137. package/dist/esm/types/widget.d.ts.map +1 -1
  138. package/dist/esm/types/widget.js.map +1 -1
  139. package/dist/esm/utils/format.d.ts +1 -1
  140. package/dist/esm/utils/format.d.ts.map +1 -1
  141. package/dist/esm/utils/format.js +2 -2
  142. package/dist/esm/utils/format.js.map +1 -1
  143. package/dist/esm/utils/limitOrder.js +61 -0
  144. package/dist/esm/utils/limitOrder.js.map +1 -0
  145. package/dist/esm/utils/navigationRoutes.d.ts +2 -0
  146. package/dist/esm/utils/navigationRoutes.d.ts.map +1 -1
  147. package/dist/esm/utils/navigationRoutes.js +6 -0
  148. package/dist/esm/utils/navigationRoutes.js.map +1 -1
  149. package/dist/esm/utils/token.js +38 -3
  150. package/dist/esm/utils/token.js.map +1 -1
  151. package/package.json +11 -11
  152. package/src/AppDefault.tsx +16 -0
  153. package/src/components/AmountInput/AmountInputEndAdornment.style.ts +1 -1
  154. package/src/components/AmountInput/AmountInputEndAdornment.tsx +5 -2
  155. package/src/components/AmountInputCard/AmountInputCard.style.tsx +157 -0
  156. package/src/components/AmountInputCard/AmountInputCardPair.tsx +25 -0
  157. package/src/components/AmountInputCard/BalanceDisplay.style.tsx +9 -0
  158. package/src/components/AmountInputCard/BalanceDisplay.tsx +46 -0
  159. package/src/components/AmountInputCard/FiatValueToggle.tsx +55 -0
  160. package/src/components/AmountInputCard/PercentageChips.style.tsx +60 -0
  161. package/src/components/AmountInputCard/PercentageChips.tsx +100 -0
  162. package/src/components/AmountInputCard/ReceiveAmountCard.tsx +219 -0
  163. package/src/components/AmountInputCard/SendAmountCard.tsx +179 -0
  164. package/src/components/Avatar/Avatar.tsx +8 -3
  165. package/src/components/Header/HeaderNavigationTabs.tsx +19 -2
  166. package/src/components/LimitOrderSettings/LimitOrderSettings.style.tsx +69 -0
  167. package/src/components/LimitOrderSettings/LimitOrderSettings.tsx +124 -0
  168. package/src/components/LimitPriceCard/LimitPriceCard.style.tsx +103 -0
  169. package/src/components/LimitPriceCard/LimitPriceCard.tsx +153 -0
  170. package/src/components/LimitPriceCard/LimitPricePresets.tsx +139 -0
  171. package/src/components/RouteCard/RouteProviderCard.tsx +113 -0
  172. package/src/components/Routes/LimitOrderRoutes.tsx +64 -0
  173. package/src/components/Routes/Routes.tsx +3 -2
  174. package/src/components/SettingsPanel/SettingsPanel.style.tsx +41 -0
  175. package/src/components/SettingsPanel/SettingsPanel.tsx +73 -0
  176. package/src/components/SettingsPanel/utils.ts +27 -0
  177. package/src/components/SwapButton/SwapButton.style.tsx +28 -0
  178. package/src/components/SwapButton/SwapButton.tsx +71 -0
  179. package/src/components/TokenPillButton/TokenPillButton.style.tsx +50 -0
  180. package/src/components/TokenPillButton/TokenPillButton.tsx +81 -0
  181. package/src/config/version.ts +1 -1
  182. package/src/hooks/useFilteredByTokenBalances.ts +7 -2
  183. package/src/hooks/useLimitMarketRate.ts +39 -0
  184. package/src/hooks/useLinkedLimitFields.ts +74 -0
  185. package/src/hooks/useRoutes.ts +45 -4
  186. package/src/hooks/useWideVariant.ts +8 -2
  187. package/src/pages/MainPage/LimitPageContent.tsx +18 -0
  188. package/src/pages/MainPage/MainPage.tsx +43 -30
  189. package/src/pages/MainPage/MainPageActions.tsx +35 -0
  190. package/src/pages/MainPage/ReviewButton.tsx +13 -1
  191. package/src/pages/RoutePriorityPage.tsx +55 -0
  192. package/src/pages/SettingsPage/RoutePrioritySettings.tsx +14 -38
  193. package/src/pages/SettingsPage/SlippageSettings/SlippagePage.tsx +176 -0
  194. package/src/pages/SettingsPage/SlippageSettings/SlippageSettings.style.tsx +32 -25
  195. package/src/pages/SettingsPage/SlippageSettings/SlippageSettings.tsx +14 -128
  196. package/src/pages/SettingsPage/ThemeSettings.tsx +4 -0
  197. package/src/stores/form/URLSearchParamsBuilder.tsx +25 -3
  198. package/src/stores/form/createFormStore.ts +7 -3
  199. package/src/stores/form/getDefaultValuesFromQueryString.ts +11 -0
  200. package/src/stores/form/types.ts +14 -1
  201. package/src/stores/form/useClearAmountFields.ts +1 -0
  202. package/src/stores/navigationTabs/types.ts +6 -0
  203. package/src/stores/navigationTabs/useNavigationTabsStore.tsx +8 -3
  204. package/src/stores/navigationTabs/utils.ts +67 -5
  205. package/src/types/events.ts +12 -0
  206. package/src/types/widget.ts +10 -0
  207. package/src/utils/format.ts +6 -2
  208. package/src/utils/limitOrder.ts +88 -0
  209. package/src/utils/navigationRoutes.ts +6 -0
  210. package/src/utils/token.ts +71 -20
@@ -1,7 +1,9 @@
1
1
  import type {
2
+ DefaultUI,
2
3
  InternalNavigationTabKey,
3
4
  ModeOptions,
4
5
  NavigationTabKey,
6
+ RequiredUIConfig,
5
7
  SplitMode,
6
8
  SplitNavigationTabKey,
7
9
  WidgetConfig,
@@ -13,26 +15,56 @@ import type { NavigationTab } from './types.js'
13
15
 
14
16
  /** Configured navigation tabs by key. Labels are resolved from the key by a hook. */
15
17
  const navigationTabsByKey: Record<InternalNavigationTabKey, NavigationTab> = {
16
- default: { key: 'default', variant: 'wide', mode: 'default' },
18
+ default: {
19
+ key: 'default',
20
+ variant: 'wide',
21
+ mode: 'default',
22
+ defaultUI: {
23
+ layout: 'cards',
24
+ },
25
+ },
17
26
  private: {
18
27
  key: 'private',
19
28
  variant: 'compact',
20
- mode: 'split',
21
- modeOptions: { split: 'swap' },
29
+ mode: 'default',
30
+ defaultUI: {
31
+ layout: 'cards',
32
+ },
33
+ requiredUI: { toAddress: true },
34
+ },
35
+ refuel: {
36
+ key: 'refuel',
37
+ variant: 'wide',
38
+ mode: 'refuel',
39
+ defaultUI: {
40
+ layout: 'cards',
41
+ },
42
+ },
43
+ limit: {
44
+ key: 'limit',
45
+ variant: 'compact',
46
+ mode: 'limit',
47
+ defaultUI: {
48
+ layout: 'cards',
49
+ },
22
50
  },
23
- refuel: { key: 'refuel', variant: 'wide', mode: 'refuel' },
24
- limit: { key: 'limit', variant: 'compact', mode: 'limit' },
25
51
  'swap-advanced': {
26
52
  key: 'swap-advanced',
27
53
  variant: 'wide',
28
54
  mode: 'split',
29
55
  modeOptions: { split: 'swap' },
56
+ defaultUI: {
57
+ layout: 'cards',
58
+ },
30
59
  },
31
60
  'bridge-advanced': {
32
61
  key: 'bridge-advanced',
33
62
  variant: 'wide',
34
63
  mode: 'split',
35
64
  modeOptions: { split: 'bridge' },
65
+ defaultUI: {
66
+ layout: 'cards',
67
+ },
36
68
  },
37
69
  }
38
70
 
@@ -132,3 +164,33 @@ export const getTabModeOptions = (
132
164
  const tab = key ? tabByKey[key] : undefined
133
165
  return tab?.modeOptions ?? config.modeOptions
134
166
  }
167
+
168
+ /**
169
+ * Default UI for a tab, layered over the widget config's `defaultUI` so the
170
+ * tab overrides only the fields it sets and inherits the rest.
171
+ */
172
+ export const getTabDefaultUI = (
173
+ config: WidgetConfig,
174
+ key?: NavigationTabKey
175
+ ): DefaultUI | undefined => {
176
+ const tab = key ? tabByKey[key] : undefined
177
+ if (!tab?.defaultUI) {
178
+ return config.defaultUI
179
+ }
180
+ return { ...config.defaultUI, ...tab.defaultUI }
181
+ }
182
+
183
+ /**
184
+ * Required UI for a tab, layered over the widget config's `requiredUI` so the
185
+ * tab overrides only the fields it sets and inherits the rest.
186
+ */
187
+ export const getTabRequiredUI = (
188
+ config: WidgetConfig,
189
+ key?: NavigationTabKey
190
+ ): RequiredUIConfig | undefined => {
191
+ const tab = key ? tabByKey[key] : undefined
192
+ if (!tab?.requiredUI) {
193
+ return config.requiredUI
194
+ }
195
+ return { ...config.requiredUI, ...tab.requiredUI }
196
+ }
@@ -3,14 +3,17 @@ import type { DefaultValues } from '../stores/form/types.js'
3
3
  import type { SettingsProps } from '../stores/settings/types.js'
4
4
  import type { NavigationRouteType } from '../utils/navigationRoutes.js'
5
5
  import type { TokenAmount } from './token.js'
6
+ import type { Appearance, NavigationTabKey } from './widget.js'
6
7
 
7
8
  export enum WidgetEvent {
9
+ AppearanceChanged = 'appearanceChanged',
8
10
  AvailableRoutes = 'availableRoutes',
9
11
  ChainPinned = 'chainPinned',
10
12
  ContactSupport = 'contactSupport',
11
13
  DestinationChainTokenSelected = 'destinationChainTokenSelected',
12
14
  FormFieldChanged = 'formFieldChanged',
13
15
  LowAddressActivityConfirmed = 'lowAddressActivityConfirmed',
16
+ NavigationTabChanged = 'navigationTabChanged',
14
17
  PageEntered = 'pageEntered',
15
18
  RouteExecutionCompleted = 'routeExecutionCompleted',
16
19
  RouteExecutionFailed = 'routeExecutionFailed',
@@ -26,12 +29,14 @@ export enum WidgetEvent {
26
29
  }
27
30
 
28
31
  export type WidgetEvents = {
32
+ appearanceChanged: (data: Appearance) => void
29
33
  availableRoutes: (data: Route[]) => void
30
34
  chainPinned: (data: ChainPinned) => void
31
35
  contactSupport: (data: ContactSupport) => void
32
36
  destinationChainTokenSelected: (data: ChainTokenSelected) => void
33
37
  formFieldChanged: (data: FormFieldChanged) => void
34
38
  lowAddressActivityConfirmed: (data: LowAddressActivityConfirmed) => void
39
+ navigationTabChanged: (data: NavigationTabChanged) => void
35
40
  pageEntered: (data: NavigationRouteType) => void
36
41
  routeExecutionCompleted: (data: Route) => void
37
42
  routeExecutionFailed: (data: RouteExecutionUpdate) => void
@@ -112,3 +117,10 @@ export type LowAddressActivityConfirmed = {
112
117
  address: string
113
118
  chainId: number
114
119
  }
120
+
121
+ export type NavigationTabChanged = {
122
+ /** Key of the now-active tab. */
123
+ tab: NavigationTabKey
124
+ /** Key of the previously active tab, or `undefined` on first render. */
125
+ previousTab?: NavigationTabKey
126
+ }
@@ -173,6 +173,15 @@ export interface RequiredUIConfig {
173
173
  export type DefaultUI = {
174
174
  transactionDetailsExpanded?: boolean
175
175
  navigationHeaderTitleNoWrap?: boolean
176
+ /**
177
+ * Layout for the main form.
178
+ * - `'default'`: the chain/token selectors plus single amount input.
179
+ * - `'cards'`: the redesigned card-based layout — stacked Send/Receive amount
180
+ * cards (`AmountInputCardPair`) with inline token pill selectors and a swap
181
+ * button.
182
+ * @default 'default'
183
+ */
184
+ layout?: 'default' | 'cards'
176
185
  }
177
186
 
178
187
  export interface WidgetWalletConfig {
@@ -293,6 +302,7 @@ export type WidgetTokens = {
293
302
  featured?: StaticToken[]
294
303
  include?: Token[]
295
304
  popular?: StaticToken[]
305
+ verified?: BaseToken[]
296
306
  } & AllowDenyItems<BaseToken>
297
307
 
298
308
  export type WidgetLanguages = {
@@ -175,7 +175,11 @@ const units = [
175
175
  ['second', 1],
176
176
  ] as const
177
177
 
178
- export function formatDuration(seconds: number, locale: string): string {
178
+ export function formatDuration(
179
+ seconds: number,
180
+ locale: string,
181
+ unitDisplay: Intl.NumberFormatOptions['unitDisplay'] = 'narrow'
182
+ ): string {
179
183
  const match = units.find(([, v]) => seconds >= v)
180
184
  const amount = match ? Math.floor(seconds / match[1]) : 0
181
185
  const unit = match?.[0] ?? 'second'
@@ -183,7 +187,7 @@ export function formatDuration(seconds: number, locale: string): string {
183
187
  return amount.toLocaleString(locale, {
184
188
  style: 'unit',
185
189
  unit,
186
- unitDisplay: 'narrow',
190
+ unitDisplay,
187
191
  })
188
192
  }
189
193
 
@@ -0,0 +1,88 @@
1
+ import { formatInputAmount } from './format.js'
2
+
3
+ /** Default order validity: 1 day, expressed as a duration in seconds. */
4
+ export const DEFAULT_VALID_UNTIL_SECONDS: number = 24 * 60 * 60
5
+
6
+ /**
7
+ * High-precision formatter for derived limit-order values. Mirrors the
8
+ * approach used by `priceToTokenAmount` — these are display-level derivations;
9
+ * the route request re-parses amounts via `parseUnits`.
10
+ */
11
+ const preciseFormatter = new Intl.NumberFormat('en', {
12
+ notation: 'standard',
13
+ roundingPriority: 'morePrecision',
14
+ maximumSignificantDigits: 20,
15
+ maximumFractionDigits: 20,
16
+ useGrouping: false,
17
+ })
18
+
19
+ function parsePositive(value?: string): number | undefined {
20
+ if (!value) {
21
+ return undefined
22
+ }
23
+ const parsed = Number.parseFloat(value)
24
+ if (Number.isNaN(parsed) || parsed <= 0) {
25
+ return undefined
26
+ }
27
+ return parsed
28
+ }
29
+
30
+ /**
31
+ * Derive the receive amount: `receiveAmount = sendAmount × limitPrice`.
32
+ * Returns an empty string when either input is missing or invalid.
33
+ */
34
+ export function deriveReceiveAmount(
35
+ sendAmount?: string,
36
+ limitPrice?: string,
37
+ toDecimals?: number
38
+ ): string {
39
+ const send = parsePositive(sendAmount)
40
+ const price = parsePositive(limitPrice)
41
+ if (send === undefined || price === undefined) {
42
+ return ''
43
+ }
44
+ return formatInputAmount(
45
+ preciseFormatter.format(send * price),
46
+ toDecimals ?? null
47
+ )
48
+ }
49
+
50
+ /**
51
+ * Derive the limit price: `limitPrice = receiveAmount ÷ sendAmount`.
52
+ * Returns an empty string when either input is missing or invalid.
53
+ */
54
+ export function deriveLimitPrice(
55
+ sendAmount?: string,
56
+ receiveAmount?: string
57
+ ): string {
58
+ const send = parsePositive(sendAmount)
59
+ const receive = parsePositive(receiveAmount)
60
+ if (send === undefined || receive === undefined) {
61
+ return ''
62
+ }
63
+ return preciseFormatter.format(receive / send)
64
+ }
65
+
66
+ /** Invert a price for display flipping: `1 ÷ price`. */
67
+ export function invertPrice(price?: string): string {
68
+ const parsed = parsePositive(price)
69
+ if (parsed === undefined) {
70
+ return ''
71
+ }
72
+ return preciseFormatter.format(1 / parsed)
73
+ }
74
+
75
+ /**
76
+ * Apply a percentage offset to a price, e.g. `percent = 2` → `price × 1.02`.
77
+ * Used by the quick-set buttons (+2%, +5%).
78
+ */
79
+ export function applyPriceOffset(
80
+ price: string | undefined,
81
+ percent: number
82
+ ): string {
83
+ const parsed = parsePositive(price)
84
+ if (parsed === undefined) {
85
+ return ''
86
+ }
87
+ return preciseFormatter.format(parsed * (1 + percent / 100))
88
+ }
@@ -8,6 +8,8 @@ export const navigationRoutes = {
8
8
  languages: 'languages',
9
9
  routes: 'routes',
10
10
  settings: 'settings',
11
+ routePriority: 'route-priority',
12
+ slippage: 'slippage',
11
13
  toChain: 'to-chain',
12
14
  toToken: 'to-token',
13
15
  toTokenNative: 'to-token-native',
@@ -30,6 +32,8 @@ export const stickyHeaderRoutes: string[] = [
30
32
  navigationRoutes.home,
31
33
  navigationRoutes.routes,
32
34
  navigationRoutes.settings,
35
+ navigationRoutes.routePriority,
36
+ navigationRoutes.slippage,
33
37
  navigationRoutes.toChain,
34
38
  navigationRoutes.toTokenNative,
35
39
  navigationRoutes.transactionDetails,
@@ -50,6 +54,8 @@ export const backButtonRoutes: string[] = [
50
54
  navigationRoutes.fromToken,
51
55
  navigationRoutes.routes,
52
56
  navigationRoutes.settings,
57
+ navigationRoutes.routePriority,
58
+ navigationRoutes.slippage,
53
59
  navigationRoutes.toChain,
54
60
  navigationRoutes.toToken,
55
61
  navigationRoutes.toTokenNative,
@@ -1,15 +1,32 @@
1
- import type {
2
- BaseToken,
3
- RouteExtended,
4
- Token,
5
- TokenAmount,
6
- TokenExtended,
7
- } from '@lifi/sdk'
1
+ import type { BaseToken, RouteExtended, Token, TokenAmount } from '@lifi/sdk'
8
2
  import type { FormType } from '../stores/form/types.js'
9
- import type { TokensByChain } from '../types/token.js'
3
+ import type { TokensByChain, TokenWithVerified } from '../types/token.js'
10
4
  import type { WidgetChains, WidgetTokens } from '../types/widget.js'
11
5
  import { getConfigItemSets, isFormItemAllowed } from './item.js'
12
6
 
7
+ /**
8
+ * Builds per-chain sets of lowercase token addresses from the
9
+ * `tokens.verified` config allowlist.
10
+ */
11
+ export const getVerifiedTokensSets = (
12
+ configTokens?: WidgetTokens
13
+ ): Map<number, Set<string>> | undefined => {
14
+ if (!configTokens?.verified?.length) {
15
+ return undefined
16
+ }
17
+ const sets = new Map<number, Set<string>>()
18
+ for (const token of configTokens.verified) {
19
+ const chainId = Number(token.chainId)
20
+ let addresses = sets.get(chainId)
21
+ if (!addresses) {
22
+ addresses = new Set()
23
+ sets.set(chainId, addresses)
24
+ }
25
+ addresses.add(token.address.toLowerCase())
26
+ }
27
+ return sets
28
+ }
29
+
13
30
  /**
14
31
  * Merges verified tokens with search tokens.
15
32
  * Verified tokens take priority - search tokens are only added if they don't already exist.
@@ -74,11 +91,11 @@ export const updateTokenInCache = (
74
91
  }
75
92
 
76
93
  export const filterAllowedTokens = (
77
- dataTokens: { [chainId: number]: TokenExtended[] } | undefined,
94
+ dataTokens: TokensByChain | undefined,
78
95
  configTokens?: WidgetTokens,
79
96
  chainsConfig?: WidgetChains,
80
97
  formType?: FormType
81
- ): { [chainId: number]: TokenExtended[] } | undefined => {
98
+ ): TokensByChain | undefined => {
82
99
  if (!dataTokens) {
83
100
  return
84
101
  }
@@ -103,12 +120,16 @@ export const filterAllowedTokens = (
103
120
  )
104
121
  : allChainIds
105
122
 
106
- const allowedTokensByChain: { [chainId: number]: TokenExtended[] } = {}
123
+ const verifiedTokensSets = getVerifiedTokensSets(configTokens)
124
+
125
+ const allowedTokensByChain: TokensByChain = {}
107
126
  for (const chainId of allowedChainIds) {
108
- const chainTokens = [
109
- ...dataTokens[chainId],
110
- ...includedTokens.filter((t) => Number(t.chainId) === chainId),
111
- ]
127
+ const chainIncludedTokens = includedTokens.filter(
128
+ (t) => Number(t.chainId) === chainId
129
+ )
130
+ const includedAddresses = new Set(
131
+ chainIncludedTokens.map((t) => t.address.toLowerCase())
132
+ )
112
133
 
113
134
  const allowedAddresses = getConfigItemSets(
114
135
  configTokens,
@@ -121,11 +142,41 @@ export const filterAllowedTokens = (
121
142
  formType
122
143
  )
123
144
 
124
- const filtered = chainTokens.filter((token) =>
125
- isFormItemAllowed(token, allowedAddresses, formType, (t) =>
126
- t.address.toLowerCase()
127
- )
128
- )
145
+ const verifiedAddresses = verifiedTokensSets?.get(chainId)
146
+
147
+ const chainTokens: TokenWithVerified[] = [
148
+ ...(dataTokens[chainId] ?? []),
149
+ ...chainIncludedTokens,
150
+ ]
151
+
152
+ const filtered: TokenWithVerified[] = []
153
+ const seenAddresses = new Set<string>()
154
+ for (const token of chainTokens) {
155
+ const address = token.address.toLowerCase()
156
+ // Data tokens come first and win over include duplicates,
157
+ // keeping their extended data
158
+ if (seenAddresses.has(address)) {
159
+ continue
160
+ }
161
+ seenAddresses.add(address)
162
+ if (
163
+ !isFormItemAllowed(token, allowedAddresses, formType, (t) =>
164
+ t.address.toLowerCase()
165
+ )
166
+ ) {
167
+ continue
168
+ }
169
+ // Include and verified-allowlist tokens are explicitly set by the
170
+ // integrator, mark them as verified
171
+ if (
172
+ !token.verified &&
173
+ (includedAddresses.has(address) || verifiedAddresses?.has(address))
174
+ ) {
175
+ filtered.push({ ...token, verified: true })
176
+ } else {
177
+ filtered.push(token)
178
+ }
179
+ }
129
180
 
130
181
  allowedTokensByChain[chainId] = filtered
131
182
  }