@lifi/widget 4.1.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/AppDefault.js +14 -0
- package/dist/esm/AppDefault.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInput.js +2 -1
- package/dist/esm/components/AmountInput/AmountInput.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +4 -2
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.style.js +1 -1
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.style.js.map +1 -1
- package/dist/esm/components/AmountInput/PriceFormHelperText.js +4 -2
- package/dist/esm/components/AmountInput/PriceFormHelperText.js.map +1 -1
- package/dist/esm/components/AmountInputCard/AmountInputCard.style.js +98 -0
- package/dist/esm/components/AmountInputCard/AmountInputCard.style.js.map +1 -0
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js +25 -0
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js.map +1 -0
- package/dist/esm/components/AmountInputCard/BalanceDisplay.js +35 -0
- package/dist/esm/components/AmountInputCard/BalanceDisplay.js.map +1 -0
- package/dist/esm/components/AmountInputCard/BalanceDisplay.style.js +11 -0
- package/dist/esm/components/AmountInputCard/BalanceDisplay.style.js.map +1 -0
- package/dist/esm/components/AmountInputCard/FiatValueToggle.js +41 -0
- package/dist/esm/components/AmountInputCard/FiatValueToggle.js.map +1 -0
- package/dist/esm/components/AmountInputCard/PercentageChips.js +80 -0
- package/dist/esm/components/AmountInputCard/PercentageChips.js.map +1 -0
- package/dist/esm/components/AmountInputCard/PercentageChips.style.js +43 -0
- package/dist/esm/components/AmountInputCard/PercentageChips.style.js.map +1 -0
- package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js +137 -0
- package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js.map +1 -0
- package/dist/esm/components/AmountInputCard/SendAmountCard.js +102 -0
- package/dist/esm/components/AmountInputCard/SendAmountCard.js.map +1 -0
- package/dist/esm/components/Avatar/Avatar.js +12 -3
- package/dist/esm/components/Avatar/Avatar.js.map +1 -1
- package/dist/esm/components/Chains/VirtualizedChainList.js +2 -1
- package/dist/esm/components/Chains/VirtualizedChainList.js.map +1 -1
- package/dist/esm/components/Header/HeaderNavigationTabs.js +14 -1
- package/dist/esm/components/Header/HeaderNavigationTabs.js.map +1 -1
- package/dist/esm/components/IconCircle/IconCircle.style.js +1 -1
- package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.js +99 -0
- package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.js.map +1 -0
- package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.style.js +52 -0
- package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.style.js.map +1 -0
- package/dist/esm/components/LimitPriceCard/LimitPriceCard.js +115 -0
- package/dist/esm/components/LimitPriceCard/LimitPriceCard.js.map +1 -0
- package/dist/esm/components/LimitPriceCard/LimitPriceCard.style.js +89 -0
- package/dist/esm/components/LimitPriceCard/LimitPriceCard.style.js.map +1 -0
- package/dist/esm/components/LimitPriceCard/LimitPricePresets.js +91 -0
- package/dist/esm/components/LimitPriceCard/LimitPricePresets.js.map +1 -0
- package/dist/esm/components/RouteCard/RouteProviderCard.js +118 -0
- package/dist/esm/components/RouteCard/RouteProviderCard.js.map +1 -0
- package/dist/esm/components/Routes/LimitOrderRoutes.js +34 -0
- package/dist/esm/components/Routes/LimitOrderRoutes.js.map +1 -0
- package/dist/esm/components/Routes/Routes.js +2 -2
- package/dist/esm/components/Routes/Routes.js.map +1 -1
- package/dist/esm/components/SettingsPanel/SettingsPanel.js +51 -0
- package/dist/esm/components/SettingsPanel/SettingsPanel.js.map +1 -0
- package/dist/esm/components/SettingsPanel/SettingsPanel.style.js +33 -0
- package/dist/esm/components/SettingsPanel/SettingsPanel.style.js.map +1 -0
- package/dist/esm/components/SettingsPanel/utils.js +14 -0
- package/dist/esm/components/SettingsPanel/utils.js.map +1 -0
- package/dist/esm/components/SwapButton/SwapButton.js +50 -0
- package/dist/esm/components/SwapButton/SwapButton.js.map +1 -0
- package/dist/esm/components/SwapButton/SwapButton.style.js +24 -0
- package/dist/esm/components/SwapButton/SwapButton.style.js.map +1 -0
- package/dist/esm/components/TokenPillButton/TokenPillButton.js +52 -0
- package/dist/esm/components/TokenPillButton/TokenPillButton.js.map +1 -0
- package/dist/esm/components/TokenPillButton/TokenPillButton.style.js +39 -0
- package/dist/esm/components/TokenPillButton/TokenPillButton.style.js.map +1 -0
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/config/version.js.map +1 -1
- package/dist/esm/hooks/useChainSearch.js +2 -1
- package/dist/esm/hooks/useChainSearch.js.map +1 -1
- package/dist/esm/hooks/useLimitMarketRate.js +24 -0
- package/dist/esm/hooks/useLimitMarketRate.js.map +1 -0
- package/dist/esm/hooks/useLinkedLimitFields.js +49 -0
- package/dist/esm/hooks/useLinkedLimitFields.js.map +1 -0
- package/dist/esm/hooks/useRoutes.js +23 -6
- package/dist/esm/hooks/useRoutes.js.map +1 -1
- package/dist/esm/hooks/useTokens.js +16 -14
- package/dist/esm/hooks/useTokens.js.map +1 -1
- package/dist/esm/hooks/useWideVariant.js +5 -2
- package/dist/esm/hooks/useWideVariant.js.map +1 -1
- package/dist/esm/i18n/en.json +22 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/pages/MainPage/LimitPageContent.js +23 -0
- package/dist/esm/pages/MainPage/LimitPageContent.js.map +1 -0
- package/dist/esm/pages/MainPage/MainPage.js +19 -22
- package/dist/esm/pages/MainPage/MainPage.js.map +1 -1
- package/dist/esm/pages/MainPage/MainPageActions.js +28 -0
- package/dist/esm/pages/MainPage/MainPageActions.js.map +1 -0
- package/dist/esm/pages/MainPage/ReviewButton.js +9 -2
- package/dist/esm/pages/MainPage/ReviewButton.js.map +1 -1
- package/dist/esm/pages/RoutePriorityPage.js +48 -0
- package/dist/esm/pages/RoutePriorityPage.js.map +1 -0
- package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js +2 -2
- package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js.map +1 -1
- package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js +12 -27
- package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js.map +1 -1
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippagePage.js +133 -0
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippagePage.js.map +1 -0
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js +14 -72
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js.map +1 -1
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.style.js +26 -14
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.style.js.map +1 -1
- package/dist/esm/pages/SettingsPage/ThemeSettings.js +4 -0
- package/dist/esm/pages/SettingsPage/ThemeSettings.js.map +1 -1
- package/dist/esm/pages/TransactionPage/TransactionReview.js +4 -1
- package/dist/esm/pages/TransactionPage/TransactionReview.js.map +1 -1
- package/dist/esm/providers/I18nProvider/I18nProvider.js +1 -1
- package/dist/esm/providers/I18nProvider/enResource.d.ts +1 -1
- package/dist/esm/providers/I18nProvider/enResource.js +1 -1
- package/dist/esm/providers/I18nProvider/types.d.ts +2 -2
- package/dist/esm/providers/SDKClientProvider.js +25 -24
- package/dist/esm/providers/SDKClientProvider.js.map +1 -1
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js +22 -21
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
- package/dist/esm/stores/form/URLSearchParamsBuilder.js +14 -3
- package/dist/esm/stores/form/URLSearchParamsBuilder.js.map +1 -1
- package/dist/esm/stores/form/createFormStore.js +6 -2
- package/dist/esm/stores/form/createFormStore.js.map +1 -1
- package/dist/esm/stores/form/getDefaultValuesFromQueryString.js +3 -0
- package/dist/esm/stores/form/getDefaultValuesFromQueryString.js.map +1 -1
- package/dist/esm/stores/form/types.d.ts +9 -1
- package/dist/esm/stores/form/types.d.ts.map +1 -1
- package/dist/esm/stores/form/types.js.map +1 -1
- package/dist/esm/stores/form/useClearAmountFields.js +1 -0
- package/dist/esm/stores/form/useClearAmountFields.js.map +1 -1
- package/dist/esm/stores/navigationTabs/useNavigationTabsStore.js +4 -2
- package/dist/esm/stores/navigationTabs/useNavigationTabsStore.js.map +1 -1
- package/dist/esm/stores/navigationTabs/utils.js +38 -8
- package/dist/esm/stores/navigationTabs/utils.js.map +1 -1
- package/dist/esm/types/events.d.ts +10 -1
- package/dist/esm/types/events.d.ts.map +1 -1
- package/dist/esm/types/events.js +2 -0
- package/dist/esm/types/events.js.map +1 -1
- package/dist/esm/types/widget.d.ts +9 -0
- package/dist/esm/types/widget.d.ts.map +1 -1
- package/dist/esm/types/widget.js.map +1 -1
- package/dist/esm/utils/format.d.ts +1 -1
- package/dist/esm/utils/format.d.ts.map +1 -1
- package/dist/esm/utils/format.js +2 -2
- package/dist/esm/utils/format.js.map +1 -1
- package/dist/esm/utils/limitOrder.js +61 -0
- package/dist/esm/utils/limitOrder.js.map +1 -0
- package/dist/esm/utils/navigationRoutes.d.ts +2 -0
- package/dist/esm/utils/navigationRoutes.d.ts.map +1 -1
- package/dist/esm/utils/navigationRoutes.js +6 -0
- package/dist/esm/utils/navigationRoutes.js.map +1 -1
- package/package.json +11 -11
- package/src/AppDefault.tsx +16 -0
- package/src/components/AmountInput/AmountInputEndAdornment.style.ts +1 -1
- package/src/components/AmountInput/AmountInputEndAdornment.tsx +5 -2
- package/src/components/AmountInputCard/AmountInputCard.style.tsx +157 -0
- package/src/components/AmountInputCard/AmountInputCardPair.tsx +25 -0
- package/src/components/AmountInputCard/BalanceDisplay.style.tsx +9 -0
- package/src/components/AmountInputCard/BalanceDisplay.tsx +46 -0
- package/src/components/AmountInputCard/FiatValueToggle.tsx +55 -0
- package/src/components/AmountInputCard/PercentageChips.style.tsx +60 -0
- package/src/components/AmountInputCard/PercentageChips.tsx +100 -0
- package/src/components/AmountInputCard/ReceiveAmountCard.tsx +219 -0
- package/src/components/AmountInputCard/SendAmountCard.tsx +179 -0
- package/src/components/Avatar/Avatar.tsx +8 -3
- package/src/components/Header/HeaderNavigationTabs.tsx +19 -2
- package/src/components/LimitOrderSettings/LimitOrderSettings.style.tsx +69 -0
- package/src/components/LimitOrderSettings/LimitOrderSettings.tsx +124 -0
- package/src/components/LimitPriceCard/LimitPriceCard.style.tsx +103 -0
- package/src/components/LimitPriceCard/LimitPriceCard.tsx +153 -0
- package/src/components/LimitPriceCard/LimitPricePresets.tsx +139 -0
- package/src/components/RouteCard/RouteProviderCard.tsx +113 -0
- package/src/components/Routes/LimitOrderRoutes.tsx +64 -0
- package/src/components/Routes/Routes.tsx +3 -2
- package/src/components/SettingsPanel/SettingsPanel.style.tsx +41 -0
- package/src/components/SettingsPanel/SettingsPanel.tsx +73 -0
- package/src/components/SettingsPanel/utils.ts +27 -0
- package/src/components/SwapButton/SwapButton.style.tsx +28 -0
- package/src/components/SwapButton/SwapButton.tsx +71 -0
- package/src/components/TokenPillButton/TokenPillButton.style.tsx +50 -0
- package/src/components/TokenPillButton/TokenPillButton.tsx +81 -0
- package/src/config/version.ts +1 -1
- package/src/hooks/useLimitMarketRate.ts +39 -0
- package/src/hooks/useLinkedLimitFields.ts +74 -0
- package/src/hooks/useRoutes.ts +45 -4
- package/src/hooks/useWideVariant.ts +8 -2
- package/src/pages/MainPage/LimitPageContent.tsx +18 -0
- package/src/pages/MainPage/MainPage.tsx +43 -30
- package/src/pages/MainPage/MainPageActions.tsx +35 -0
- package/src/pages/MainPage/ReviewButton.tsx +13 -1
- package/src/pages/RoutePriorityPage.tsx +55 -0
- package/src/pages/SettingsPage/RoutePrioritySettings.tsx +14 -38
- package/src/pages/SettingsPage/SlippageSettings/SlippagePage.tsx +176 -0
- package/src/pages/SettingsPage/SlippageSettings/SlippageSettings.style.tsx +32 -25
- package/src/pages/SettingsPage/SlippageSettings/SlippageSettings.tsx +14 -128
- package/src/pages/SettingsPage/ThemeSettings.tsx +4 -0
- package/src/stores/form/URLSearchParamsBuilder.tsx +25 -3
- package/src/stores/form/createFormStore.ts +7 -3
- package/src/stores/form/getDefaultValuesFromQueryString.ts +11 -0
- package/src/stores/form/types.ts +14 -1
- package/src/stores/form/useClearAmountFields.ts +1 -0
- package/src/stores/navigationTabs/types.ts +6 -0
- package/src/stores/navigationTabs/useNavigationTabsStore.tsx +8 -3
- package/src/stores/navigationTabs/utils.ts +67 -5
- package/src/types/events.ts +12 -0
- package/src/types/widget.ts +9 -0
- package/src/utils/format.ts +6 -2
- package/src/utils/limitOrder.ts +88 -0
- package/src/utils/navigationRoutes.ts +6 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { BoxProps } from '@mui/material'
|
|
2
|
+
import { useNavigate } from '@tanstack/react-router'
|
|
3
|
+
import type { JSX } from 'react'
|
|
4
|
+
import { useTranslation } from 'react-i18next'
|
|
5
|
+
import { useSplitMode } from '../../stores/navigationTabs/useNavigationTabsStore.js'
|
|
6
|
+
import { useSettingsStore } from '../../stores/settings/SettingsStore.js'
|
|
7
|
+
import { useSettings } from '../../stores/settings/useSettings.js'
|
|
8
|
+
import { navigationRoutes } from '../../utils/navigationRoutes.js'
|
|
9
|
+
import { Card } from '../Card/Card.js'
|
|
10
|
+
import {
|
|
11
|
+
SettingsPanelButton,
|
|
12
|
+
SettingsPanelContainer,
|
|
13
|
+
SettingsPanelTitle,
|
|
14
|
+
SettingsPanelValue,
|
|
15
|
+
} from './SettingsPanel.style.js'
|
|
16
|
+
import {
|
|
17
|
+
bridgeSettingsPanelKeys,
|
|
18
|
+
type SettingsPanelKey,
|
|
19
|
+
swapSettingsPanelKeys,
|
|
20
|
+
} from './utils.js'
|
|
21
|
+
|
|
22
|
+
export const SettingsPanel: React.FC<BoxProps> = (props): JSX.Element => {
|
|
23
|
+
const { t } = useTranslation()
|
|
24
|
+
const navigate = useNavigate()
|
|
25
|
+
const splitMode = useSplitMode()
|
|
26
|
+
const { slippage, routePriority } = useSettings(['slippage', 'routePriority'])
|
|
27
|
+
const [enabledBridges, totalBridges, enabledExchanges, totalExchanges] =
|
|
28
|
+
useSettingsStore((state) => [
|
|
29
|
+
Object.values(state._enabledBridges).filter(Boolean).length,
|
|
30
|
+
Object.values(state._enabledBridges).length,
|
|
31
|
+
Object.values(state._enabledExchanges).filter(Boolean).length,
|
|
32
|
+
Object.values(state._enabledExchanges).length,
|
|
33
|
+
])
|
|
34
|
+
|
|
35
|
+
const rows =
|
|
36
|
+
splitMode === 'bridge' ? bridgeSettingsPanelKeys : swapSettingsPanelKeys
|
|
37
|
+
|
|
38
|
+
const labelByKey: Record<SettingsPanelKey, string> = {
|
|
39
|
+
routePriority: t('settings.routePriority'),
|
|
40
|
+
exchanges: t('settings.enabledExchanges'),
|
|
41
|
+
bridges: t('settings.enabledBridges'),
|
|
42
|
+
slippage: t('settings.slippage'),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const valueByKey: Record<SettingsPanelKey, string> = {
|
|
46
|
+
routePriority:
|
|
47
|
+
routePriority === 'FASTEST'
|
|
48
|
+
? t('settings.routePriorityOptions.fastest.title')
|
|
49
|
+
: t('settings.routePriorityOptions.cheapest.title'),
|
|
50
|
+
exchanges: `${enabledExchanges}/${totalExchanges}`,
|
|
51
|
+
bridges: `${enabledBridges}/${totalBridges}`,
|
|
52
|
+
slippage: slippage ? `${slippage}%` : t('button.auto'),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const handleClick = (key: SettingsPanelKey) => {
|
|
56
|
+
navigate({
|
|
57
|
+
to: `/${navigationRoutes.settings}/${navigationRoutes[key]}`,
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<SettingsPanelContainer {...props}>
|
|
63
|
+
{rows.map((key) => (
|
|
64
|
+
<Card key={key}>
|
|
65
|
+
<SettingsPanelButton onClick={() => handleClick(key)} disableRipple>
|
|
66
|
+
<SettingsPanelTitle>{labelByKey[key]}</SettingsPanelTitle>
|
|
67
|
+
<SettingsPanelValue>{valueByKey[key]}</SettingsPanelValue>
|
|
68
|
+
</SettingsPanelButton>
|
|
69
|
+
</Card>
|
|
70
|
+
))}
|
|
71
|
+
</SettingsPanelContainer>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { NavigationTabKey } from '../../types/widget.js'
|
|
2
|
+
|
|
3
|
+
export type SettingsPanelKey =
|
|
4
|
+
| 'routePriority'
|
|
5
|
+
| 'exchanges'
|
|
6
|
+
| 'bridges'
|
|
7
|
+
| 'slippage'
|
|
8
|
+
|
|
9
|
+
/** Navigation tabs that surface the settings panel on the main page. */
|
|
10
|
+
export const advancedNavigationTabKeys: NavigationTabKey[] = [
|
|
11
|
+
'swap-advanced',
|
|
12
|
+
'bridge-advanced',
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
// Keys are ordered to match the settings page layout.
|
|
16
|
+
// Swap is same-chain, so route priority and bridges are not relevant.
|
|
17
|
+
export const swapSettingsPanelKeys: SettingsPanelKey[] = [
|
|
18
|
+
'slippage',
|
|
19
|
+
'exchanges',
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
export const bridgeSettingsPanelKeys: SettingsPanelKey[] = [
|
|
23
|
+
'routePriority',
|
|
24
|
+
'slippage',
|
|
25
|
+
'bridges',
|
|
26
|
+
'exchanges',
|
|
27
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Box, styled } from '@mui/material'
|
|
2
|
+
import type React from 'react'
|
|
3
|
+
import { Card } from '../Card/Card.js'
|
|
4
|
+
|
|
5
|
+
// Sits just above MUI's appBar layer (1100) so the swap icon stays on top of the
|
|
6
|
+
// two overlapping amount cards it bridges.
|
|
7
|
+
const swapButtonZIndex = 1110
|
|
8
|
+
|
|
9
|
+
export const SwapIconCard: React.FC<React.ComponentProps<typeof Card>> = styled(
|
|
10
|
+
Card
|
|
11
|
+
)(({ theme }) => ({
|
|
12
|
+
height: 32,
|
|
13
|
+
width: 32,
|
|
14
|
+
fontSize: 16,
|
|
15
|
+
display: 'flex',
|
|
16
|
+
alignItems: 'center',
|
|
17
|
+
justifyContent: 'center',
|
|
18
|
+
borderRadius: theme.vars.shape.borderRadiusTertiary,
|
|
19
|
+
zIndex: swapButtonZIndex,
|
|
20
|
+
}))
|
|
21
|
+
|
|
22
|
+
export const SwapButtonContainer: React.FC<React.ComponentProps<typeof Box>> =
|
|
23
|
+
styled(Box)(({ theme }) => ({
|
|
24
|
+
display: 'flex',
|
|
25
|
+
justifyContent: 'center',
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
margin: theme.spacing(-1.5),
|
|
28
|
+
}))
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import ArrowDownward from '@mui/icons-material/ArrowDownward'
|
|
2
|
+
import type { BoxProps } from '@mui/material'
|
|
3
|
+
import type { JSX } from 'react'
|
|
4
|
+
import { useTranslation } from 'react-i18next'
|
|
5
|
+
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
6
|
+
import { useToAddressAutoPopulate } from '../../hooks/useToAddressAutoPopulate.js'
|
|
7
|
+
import { useToAddressReset } from '../../hooks/useToAddressReset.js'
|
|
8
|
+
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
|
|
9
|
+
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
10
|
+
import { SwapButtonContainer, SwapIconCard } from './SwapButton.style.js'
|
|
11
|
+
|
|
12
|
+
export const SwapButton: React.FC<BoxProps> = ({
|
|
13
|
+
sx,
|
|
14
|
+
...props
|
|
15
|
+
}): JSX.Element => {
|
|
16
|
+
const { t } = useTranslation()
|
|
17
|
+
const { mode } = useWidgetConfig()
|
|
18
|
+
const { setFieldValue, getFieldValues } = useFieldActions()
|
|
19
|
+
const { getChainById } = useAvailableChains()
|
|
20
|
+
const { tryResetToAddress } = useToAddressReset()
|
|
21
|
+
const autoPopulateToAddress = useToAddressAutoPopulate()
|
|
22
|
+
|
|
23
|
+
const handleClick = (): void => {
|
|
24
|
+
const [fromChainId, fromToken, toChainId, toToken, toAddress, toAmount] =
|
|
25
|
+
getFieldValues(
|
|
26
|
+
'fromChain',
|
|
27
|
+
'fromToken',
|
|
28
|
+
'toChain',
|
|
29
|
+
'toToken',
|
|
30
|
+
'toAddress',
|
|
31
|
+
'toAmount'
|
|
32
|
+
)
|
|
33
|
+
const newFromAmount =
|
|
34
|
+
mode === 'limit' && toAmount ? (toAmount as string) : ''
|
|
35
|
+
setFieldValue('fromAmount', newFromAmount, { isTouched: true })
|
|
36
|
+
setFieldValue('fromChain', toChainId, { isTouched: true })
|
|
37
|
+
setFieldValue('fromToken', toToken, { isTouched: true })
|
|
38
|
+
setFieldValue('toChain', fromChainId, { isTouched: true })
|
|
39
|
+
setFieldValue('toToken', fromToken, { isTouched: true })
|
|
40
|
+
|
|
41
|
+
const autoPopulatedToAddress = autoPopulateToAddress({
|
|
42
|
+
formType: 'from',
|
|
43
|
+
selectedToAddress: toAddress,
|
|
44
|
+
selectedChainId: toChainId,
|
|
45
|
+
selectedOppositeChainId: fromChainId,
|
|
46
|
+
selectedOppositeTokenAddress: fromToken,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
if (autoPopulatedToAddress) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const toChain = getChainById(fromChainId)
|
|
54
|
+
if (toChain) {
|
|
55
|
+
tryResetToAddress(toChain)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<SwapButtonContainer sx={sx} {...props}>
|
|
61
|
+
<SwapIconCard
|
|
62
|
+
component="button"
|
|
63
|
+
onClick={handleClick}
|
|
64
|
+
aria-label={t('button.swap')}
|
|
65
|
+
data-testid="widget-swap-tokens-button"
|
|
66
|
+
>
|
|
67
|
+
<ArrowDownward fontSize="inherit" />
|
|
68
|
+
</SwapIconCard>
|
|
69
|
+
</SwapButtonContainer>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Button, styled, type Theme, Typography } from '@mui/material'
|
|
2
|
+
import type React from 'react'
|
|
3
|
+
|
|
4
|
+
const pillLayout = (theme: Theme) => ({
|
|
5
|
+
height: 36,
|
|
6
|
+
borderRadius: theme.vars.shape.borderRadiusSecondary,
|
|
7
|
+
padding: theme.spacing(0.5, 1),
|
|
8
|
+
display: 'flex',
|
|
9
|
+
alignItems: 'center',
|
|
10
|
+
gap: theme.spacing(1),
|
|
11
|
+
whiteSpace: 'nowrap',
|
|
12
|
+
flexShrink: 0,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export const TokenPill: React.FC<React.ComponentProps<typeof Button>> = styled(
|
|
16
|
+
Button
|
|
17
|
+
)(({ theme }) => ({
|
|
18
|
+
...pillLayout(theme),
|
|
19
|
+
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.common.onBackground} 4%, transparent)`,
|
|
20
|
+
border: '1px solid transparent',
|
|
21
|
+
transition: theme.transitions.create(['background-color'], {
|
|
22
|
+
duration: theme.transitions.duration.short,
|
|
23
|
+
}),
|
|
24
|
+
'&:hover': {
|
|
25
|
+
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.common.onBackground} 8%, transparent)`,
|
|
26
|
+
},
|
|
27
|
+
}))
|
|
28
|
+
|
|
29
|
+
export const TokenSelectPill: React.FC<React.ComponentProps<typeof Button>> =
|
|
30
|
+
styled(TokenPill)({ minWidth: 0 })
|
|
31
|
+
|
|
32
|
+
export const TokenPillSymbol: React.FC<
|
|
33
|
+
React.ComponentProps<typeof Typography>
|
|
34
|
+
> = styled(Typography)(({ theme }) => ({
|
|
35
|
+
fontSize: 14,
|
|
36
|
+
fontWeight: 700,
|
|
37
|
+
lineHeight: '18px',
|
|
38
|
+
overflow: 'hidden',
|
|
39
|
+
textOverflow: 'ellipsis',
|
|
40
|
+
whiteSpace: 'nowrap',
|
|
41
|
+
color: theme.vars.palette.text.primary,
|
|
42
|
+
}))
|
|
43
|
+
|
|
44
|
+
export const TokenPillLabel: React.FC<React.ComponentProps<typeof Typography>> =
|
|
45
|
+
styled(Typography)(({ theme }) => ({
|
|
46
|
+
fontSize: 16,
|
|
47
|
+
fontWeight: 700,
|
|
48
|
+
lineHeight: '20px',
|
|
49
|
+
color: theme.vars.palette.text.secondary,
|
|
50
|
+
}))
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useNavigate } from '@tanstack/react-router'
|
|
2
|
+
import type { JSX } from 'react'
|
|
3
|
+
import { useTranslation } from 'react-i18next'
|
|
4
|
+
import { useChain } from '../../hooks/useChain.js'
|
|
5
|
+
import { useToken } from '../../hooks/useToken.js'
|
|
6
|
+
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
|
|
7
|
+
import type { FormTypeProps } from '../../stores/form/types.js'
|
|
8
|
+
import { FormKeyHelper } from '../../stores/form/types.js'
|
|
9
|
+
import { useFieldValues } from '../../stores/form/useFieldValues.js'
|
|
10
|
+
import type { DisabledUIConfig } from '../../types/widget.js'
|
|
11
|
+
import { navigationRoutes } from '../../utils/navigationRoutes.js'
|
|
12
|
+
import { AvatarBadgedDefault } from '../Avatar/Avatar.js'
|
|
13
|
+
import { TokenAvatar } from '../Avatar/TokenAvatar.js'
|
|
14
|
+
import {
|
|
15
|
+
TokenPill,
|
|
16
|
+
TokenPillLabel,
|
|
17
|
+
TokenPillSymbol,
|
|
18
|
+
TokenSelectPill,
|
|
19
|
+
} from './TokenPillButton.style.js'
|
|
20
|
+
|
|
21
|
+
export const TokenPillButton: React.FC<FormTypeProps> = ({
|
|
22
|
+
formType,
|
|
23
|
+
}): JSX.Element => {
|
|
24
|
+
const { t } = useTranslation()
|
|
25
|
+
const navigate = useNavigate()
|
|
26
|
+
const { disabledUI, mode } = useWidgetConfig()
|
|
27
|
+
|
|
28
|
+
const tokenKey = FormKeyHelper.getTokenKey(formType)
|
|
29
|
+
const [chainId, tokenAddress] = useFieldValues(
|
|
30
|
+
FormKeyHelper.getChainKey(formType),
|
|
31
|
+
tokenKey
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const { chain } = useChain(chainId)
|
|
35
|
+
const { token } = useToken(chainId, tokenAddress)
|
|
36
|
+
|
|
37
|
+
const isSelected = !!(chain && token)
|
|
38
|
+
|
|
39
|
+
const handleClick = (): void => {
|
|
40
|
+
navigate({
|
|
41
|
+
to:
|
|
42
|
+
formType === 'from'
|
|
43
|
+
? navigationRoutes.fromToken
|
|
44
|
+
: mode === 'refuel'
|
|
45
|
+
? navigationRoutes.toTokenNative
|
|
46
|
+
: navigationRoutes.toToken,
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const isDisabled = !!disabledUI?.[tokenKey as keyof DisabledUIConfig]
|
|
51
|
+
|
|
52
|
+
const testId = `widget-${formType}-token-button`
|
|
53
|
+
|
|
54
|
+
if (isSelected) {
|
|
55
|
+
return (
|
|
56
|
+
<TokenPill
|
|
57
|
+
onClick={isDisabled ? undefined : handleClick}
|
|
58
|
+
disabled={isDisabled}
|
|
59
|
+
data-testid={testId}
|
|
60
|
+
>
|
|
61
|
+
<TokenAvatar
|
|
62
|
+
token={token}
|
|
63
|
+
chain={chain}
|
|
64
|
+
tokenAvatarSize={24}
|
|
65
|
+
chainAvatarSize={12}
|
|
66
|
+
/>
|
|
67
|
+
<TokenPillSymbol>{token.symbol}</TokenPillSymbol>
|
|
68
|
+
</TokenPill>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<TokenSelectPill
|
|
74
|
+
onClick={isDisabled ? undefined : handleClick}
|
|
75
|
+
data-testid={testId}
|
|
76
|
+
>
|
|
77
|
+
<AvatarBadgedDefault chain={chain} avatarSize={24} badgeSize={12} />
|
|
78
|
+
<TokenPillLabel>{t('main.select')}</TokenPillLabel>
|
|
79
|
+
</TokenSelectPill>
|
|
80
|
+
)
|
|
81
|
+
}
|
package/src/config/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/widget'
|
|
2
|
-
export const version = '4.
|
|
2
|
+
export const version = '4.2.0'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FormKeyHelper } from '../stores/form/types.js'
|
|
2
|
+
import { useFieldValues } from '../stores/form/useFieldValues.js'
|
|
3
|
+
import { useToken } from './useToken.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Canonical limit-order market rate (receive token per send token), derived
|
|
7
|
+
* from the tokens' USD prices: 1 send token ≈ sendPrice / receivePrice receive
|
|
8
|
+
* tokens. This is the spot ratio — what the price card's "Market" reference and
|
|
9
|
+
* proximity indicator compare against. Returns `undefined` until both token
|
|
10
|
+
* prices are known.
|
|
11
|
+
*/
|
|
12
|
+
export const useLimitMarketRate = (): number | undefined => {
|
|
13
|
+
const [fromChainId, fromTokenAddress, toChainId, toTokenAddress] =
|
|
14
|
+
useFieldValues(
|
|
15
|
+
FormKeyHelper.getChainKey('from'),
|
|
16
|
+
FormKeyHelper.getTokenKey('from'),
|
|
17
|
+
FormKeyHelper.getChainKey('to'),
|
|
18
|
+
FormKeyHelper.getTokenKey('to')
|
|
19
|
+
)
|
|
20
|
+
const { token: fromToken } = useToken(fromChainId, fromTokenAddress)
|
|
21
|
+
const { token: toToken } = useToken(toChainId, toTokenAddress)
|
|
22
|
+
|
|
23
|
+
const fromPrice = Number(fromToken?.priceUSD)
|
|
24
|
+
const toPrice = Number(toToken?.priceUSD)
|
|
25
|
+
|
|
26
|
+
// A `priceUSD` of `"0"` (or missing) means the price is unknown — there's no
|
|
27
|
+
// meaningful spot ratio to derive, and dividing by a zero `toPrice` would be
|
|
28
|
+
// `Infinity`, so the falsy guard intentionally suppresses the rate here.
|
|
29
|
+
if (
|
|
30
|
+
!fromPrice ||
|
|
31
|
+
!toPrice ||
|
|
32
|
+
Number.isNaN(fromPrice) ||
|
|
33
|
+
Number.isNaN(toPrice)
|
|
34
|
+
) {
|
|
35
|
+
return undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return fromPrice / toPrice
|
|
39
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { FormKeyHelper } from '../stores/form/types.js'
|
|
2
|
+
import { useFieldActions } from '../stores/form/useFieldActions.js'
|
|
3
|
+
import { useFieldValues } from '../stores/form/useFieldValues.js'
|
|
4
|
+
import { deriveLimitPrice, deriveReceiveAmount } from '../utils/limitOrder.js'
|
|
5
|
+
import { useToken } from './useToken.js'
|
|
6
|
+
|
|
7
|
+
export interface LinkedLimitFields {
|
|
8
|
+
limitPrice: string
|
|
9
|
+
/** Set the send amount (explicit user edit); derives receiveAmount when a price is set. */
|
|
10
|
+
setSendAmount: (value: string) => void
|
|
11
|
+
/** Set the limit price (canonical: receive per send); derives receiveAmount when a send amount is set. */
|
|
12
|
+
setLimitPrice: (value: string) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Implements the linked-field derivation for limit orders:
|
|
17
|
+
* - `limitPrice = receiveAmount / sendAmount`
|
|
18
|
+
* - editing sendAmount recomputes receiveAmount, holding the current price
|
|
19
|
+
* - editing limitPrice (with a send amount set) recomputes receiveAmount
|
|
20
|
+
* - sendAmount never changes without an explicit user edit
|
|
21
|
+
*
|
|
22
|
+
* The receive amount (`toAmount`) is read-only in the UI — it is derived here
|
|
23
|
+
* from the send amount and limit price, then sent to the routes request; the
|
|
24
|
+
* receive card only displays the resulting quote.
|
|
25
|
+
*
|
|
26
|
+
* The limit price is NOT stored: it is purely a function of the `fromAmount`
|
|
27
|
+
* and `toAmount` form fields (canonical receive-per-send). The UI layer handles
|
|
28
|
+
* base/quote display inversion. Because it's derived, editing the send amount
|
|
29
|
+
* only has to preserve the pre-edit price.
|
|
30
|
+
*/
|
|
31
|
+
export const useLinkedLimitFields = (): LinkedLimitFields => {
|
|
32
|
+
const { setFieldValue } = useFieldActions()
|
|
33
|
+
const [toChainId, toTokenAddress, sendAmount, receiveAmount] = useFieldValues(
|
|
34
|
+
FormKeyHelper.getChainKey('to'),
|
|
35
|
+
FormKeyHelper.getTokenKey('to'),
|
|
36
|
+
FormKeyHelper.getAmountKey('from'),
|
|
37
|
+
FormKeyHelper.getAmountKey('to')
|
|
38
|
+
)
|
|
39
|
+
const { token: toToken } = useToken(toChainId, toTokenAddress)
|
|
40
|
+
|
|
41
|
+
const limitPrice = deriveLimitPrice(sendAmount, receiveAmount)
|
|
42
|
+
|
|
43
|
+
const setSendAmount = (value: string): void => {
|
|
44
|
+
// Hold the pre-edit price (derived from the current fields) so changing the
|
|
45
|
+
// send amount rescales the receive amount instead of moving the price.
|
|
46
|
+
setFieldValue('fromAmount', value, { isDirty: true, isTouched: true })
|
|
47
|
+
const nextReceive = deriveReceiveAmount(
|
|
48
|
+
value,
|
|
49
|
+
limitPrice,
|
|
50
|
+
toToken?.decimals
|
|
51
|
+
)
|
|
52
|
+
if (nextReceive) {
|
|
53
|
+
setFieldValue('toAmount', nextReceive, { isDirty: true })
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const setLimitPrice = (value: string): void => {
|
|
58
|
+
// Write unconditionally (including '') so the receive amount tracks the
|
|
59
|
+
// price both ways — clearing the price clears the derived receive amount
|
|
60
|
+
// instead of leaving a stale value that re-derives the old price.
|
|
61
|
+
const nextReceive = deriveReceiveAmount(
|
|
62
|
+
sendAmount,
|
|
63
|
+
value,
|
|
64
|
+
toToken?.decimals
|
|
65
|
+
)
|
|
66
|
+
setFieldValue('toAmount', nextReceive, { isDirty: true })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
limitPrice,
|
|
71
|
+
setSendAmount,
|
|
72
|
+
setLimitPrice,
|
|
73
|
+
}
|
|
74
|
+
}
|
package/src/hooks/useRoutes.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { useCallback, useMemo } from 'react'
|
|
|
18
18
|
import { useSDKClient } from '../providers/SDKClientProvider.js'
|
|
19
19
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
20
20
|
import { useFieldValues } from '../stores/form/useFieldValues.js'
|
|
21
|
+
import { useNavigationTabsStore } from '../stores/navigationTabs/useNavigationTabsStore.js'
|
|
21
22
|
import { useIntermediateRoutesStore } from '../stores/routes/useIntermediateRoutesStore.js'
|
|
22
23
|
import { useSetExecutableRoute } from '../stores/routes/useSetExecutableRoute.js'
|
|
23
24
|
import { defaultSlippage } from '../stores/settings/createSettingsStore.js'
|
|
@@ -70,6 +71,10 @@ export const useRoutes = ({
|
|
|
70
71
|
const queryClient = useQueryClient()
|
|
71
72
|
const emitter = useWidgetEvents()
|
|
72
73
|
const swapOnly = useSwapOnly()
|
|
74
|
+
const isPrivate = useNavigationTabsStore(
|
|
75
|
+
(state) => state.activeTab === 'private'
|
|
76
|
+
)
|
|
77
|
+
|
|
73
78
|
const {
|
|
74
79
|
disabledBridges,
|
|
75
80
|
disabledExchanges,
|
|
@@ -88,11 +93,12 @@ export const useRoutes = ({
|
|
|
88
93
|
'slippage',
|
|
89
94
|
])
|
|
90
95
|
const [fromTokenAmount] = useDebouncedWatch(500, 'fromAmount')
|
|
96
|
+
// Debounce toAmount like fromAmount to avoid a request per keystroke
|
|
97
|
+
const [toTokenAmount] = useDebouncedWatch(500, 'toAmount')
|
|
91
98
|
const [
|
|
92
99
|
fromChainId,
|
|
93
100
|
fromTokenAddress,
|
|
94
101
|
toAddress,
|
|
95
|
-
toTokenAmount,
|
|
96
102
|
toChainId,
|
|
97
103
|
toTokenAddress,
|
|
98
104
|
contractCalls,
|
|
@@ -100,11 +106,14 @@ export const useRoutes = ({
|
|
|
100
106
|
'fromChain',
|
|
101
107
|
'fromToken',
|
|
102
108
|
'toAddress',
|
|
103
|
-
'toAmount',
|
|
104
109
|
'toChain',
|
|
105
110
|
'toToken',
|
|
106
111
|
'contractCalls'
|
|
107
112
|
)
|
|
113
|
+
const [validUntilDuration, partiallyFillable] = useFieldValues(
|
|
114
|
+
'validUntil',
|
|
115
|
+
'partiallyFillable'
|
|
116
|
+
)
|
|
108
117
|
const { token: fromToken } = useToken(fromChainId, fromTokenAddress)
|
|
109
118
|
const { token: toToken } = useToken(toChainId, toTokenAddress)
|
|
110
119
|
const { chain: fromChain } = useChain(fromChainId)
|
|
@@ -117,7 +126,15 @@ export const useRoutes = ({
|
|
|
117
126
|
const { isBatchingSupported, isBatchingSupportedLoading } =
|
|
118
127
|
useIsBatchingSupported(fromChain, account.address)
|
|
119
128
|
|
|
120
|
-
|
|
129
|
+
// In limit mode toAmount is the user's limit target, derived from the sell
|
|
130
|
+
// amount and limit price. It's keyed and sent to the backend so changing the
|
|
131
|
+
// limit fetches a fresh quote. The receive card is read-only (it displays the
|
|
132
|
+
// quote and never writes toAmount back), so keying on it can't loop. Both a
|
|
133
|
+
// sell amount and a limit price (→ toAmount) are required before fetching.
|
|
134
|
+
const hasAmount =
|
|
135
|
+
mode === 'limit'
|
|
136
|
+
? Number(fromTokenAmount) > 0 && Number(toTokenAmount) > 0
|
|
137
|
+
: Number(fromTokenAmount) > 0 || Number(toTokenAmount) > 0
|
|
121
138
|
|
|
122
139
|
const contractCallQuoteEnabled: boolean =
|
|
123
140
|
mode === 'custom' ? Boolean(contractCalls && account.address) : true
|
|
@@ -169,6 +186,8 @@ export const useRoutes = ({
|
|
|
169
186
|
toChain?.id as number,
|
|
170
187
|
toToken?.address as string,
|
|
171
188
|
toTokenAmount,
|
|
189
|
+
validUntilDuration,
|
|
190
|
+
partiallyFillable,
|
|
172
191
|
contractCalls,
|
|
173
192
|
slippage,
|
|
174
193
|
swapOnly,
|
|
@@ -184,6 +203,7 @@ export const useRoutes = ({
|
|
|
184
203
|
feeConfig?.fee,
|
|
185
204
|
disableMessageSigning,
|
|
186
205
|
!!isBatchingSupported,
|
|
206
|
+
isPrivate,
|
|
187
207
|
observableRoute?.id,
|
|
188
208
|
] as const,
|
|
189
209
|
[
|
|
@@ -196,6 +216,8 @@ export const useRoutes = ({
|
|
|
196
216
|
toChain?.id,
|
|
197
217
|
toToken?.address,
|
|
198
218
|
toTokenAmount,
|
|
219
|
+
validUntilDuration,
|
|
220
|
+
partiallyFillable,
|
|
199
221
|
contractCalls,
|
|
200
222
|
slippage,
|
|
201
223
|
swapOnly,
|
|
@@ -212,6 +234,7 @@ export const useRoutes = ({
|
|
|
212
234
|
feeConfig?.fee,
|
|
213
235
|
disableMessageSigning,
|
|
214
236
|
isBatchingSupported,
|
|
237
|
+
isPrivate,
|
|
215
238
|
observableRoute?.id,
|
|
216
239
|
]
|
|
217
240
|
)
|
|
@@ -233,6 +256,8 @@ export const useRoutes = ({
|
|
|
233
256
|
toChainId,
|
|
234
257
|
toTokenAddress,
|
|
235
258
|
toTokenAmount,
|
|
259
|
+
validUntilDuration,
|
|
260
|
+
partiallyFillable,
|
|
236
261
|
contractCalls,
|
|
237
262
|
slippage = defaultSlippage,
|
|
238
263
|
swapOnly,
|
|
@@ -248,13 +273,16 @@ export const useRoutes = ({
|
|
|
248
273
|
configuredFee,
|
|
249
274
|
disableMessageSigning,
|
|
250
275
|
isBatchingSupported,
|
|
276
|
+
isPrivate,
|
|
251
277
|
// _observableRouteId must be the last element in the query key
|
|
252
278
|
_observableRouteId,
|
|
253
279
|
],
|
|
254
280
|
signal,
|
|
255
281
|
}) => {
|
|
256
282
|
const fromAmount = parseUnits(fromTokenAmount, fromToken!.decimals)
|
|
257
|
-
const toAmount =
|
|
283
|
+
const toAmount = toTokenAmount
|
|
284
|
+
? parseUnits(toTokenAmount, toToken!.decimals)
|
|
285
|
+
: undefined
|
|
258
286
|
const formattedSlippage = slippage
|
|
259
287
|
? Number.parseFloat(slippage) / 100
|
|
260
288
|
: defaultSlippage
|
|
@@ -360,6 +388,8 @@ export const useRoutes = ({
|
|
|
360
388
|
const shouldUseMainRoutes =
|
|
361
389
|
!observableRoute || !isObservableRelayerRoute
|
|
362
390
|
const shouldUseRelayerQuote =
|
|
391
|
+
// Relayer quotes don't support limit-order params
|
|
392
|
+
mode !== 'limit' &&
|
|
363
393
|
fromAddress &&
|
|
364
394
|
fromChain?.chainType === ChainType.EVM &&
|
|
365
395
|
fromChain.permit2 &&
|
|
@@ -370,6 +400,15 @@ export const useRoutes = ({
|
|
|
370
400
|
!isBatchingSupported &&
|
|
371
401
|
(!observableRoute || isObservableRelayerRoute)
|
|
372
402
|
|
|
403
|
+
const limitOrderRouteParams =
|
|
404
|
+
mode === 'limit'
|
|
405
|
+
? {
|
|
406
|
+
toAmount: toAmount?.toString(),
|
|
407
|
+
validUntil: Math.floor(Date.now() / 1000) + validUntilDuration,
|
|
408
|
+
partiallyFillable: partiallyFillable,
|
|
409
|
+
}
|
|
410
|
+
: undefined
|
|
411
|
+
|
|
373
412
|
const mainRoutesPromise = shouldUseMainRoutes
|
|
374
413
|
? getRoutes(
|
|
375
414
|
sdkClient,
|
|
@@ -385,6 +424,7 @@ export const useRoutes = ({
|
|
|
385
424
|
enabledRefuel && gasRecommendationFromAmount
|
|
386
425
|
? gasRecommendationFromAmount
|
|
387
426
|
: undefined,
|
|
427
|
+
...limitOrderRouteParams,
|
|
388
428
|
options: {
|
|
389
429
|
allowSwitchChain:
|
|
390
430
|
mode === 'refuel' ? false : allowSwitchChain,
|
|
@@ -410,6 +450,7 @@ export const useRoutes = ({
|
|
|
410
450
|
slippage: formattedSlippage,
|
|
411
451
|
fee: calculatedFee || configuredFee,
|
|
412
452
|
executionType: disableMessageSigning ? 'transaction' : 'all',
|
|
453
|
+
...(isPrivate && { private: true }),
|
|
413
454
|
},
|
|
414
455
|
},
|
|
415
456
|
{ signal }
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import type { Theme } from '@mui/material'
|
|
2
2
|
import { useMediaQuery } from '@mui/material'
|
|
3
3
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
4
|
+
import { useNavigationTabsStore } from '../stores/navigationTabs/useNavigationTabsStore.js'
|
|
5
|
+
import { getTabVariant } from '../stores/navigationTabs/utils.js'
|
|
4
6
|
|
|
5
7
|
const defaultExpandableWidth = 852
|
|
6
8
|
|
|
7
9
|
export const useWideVariant = (): boolean => {
|
|
8
|
-
const
|
|
10
|
+
const config = useWidgetConfig()
|
|
11
|
+
const activeTab = useNavigationTabsStore((store) => store.activeTab)
|
|
12
|
+
// The active tab can pin its own variant (e.g. the limit tab is compact),
|
|
13
|
+
// falling back to the widget config's variant when it doesn't.
|
|
14
|
+
const variant = getTabVariant(config, activeTab)
|
|
9
15
|
const expandableAllowed = useMediaQuery((theme: Theme) =>
|
|
10
16
|
theme.breakpoints.up(defaultExpandableWidth)
|
|
11
17
|
)
|
|
12
|
-
return variant === 'wide' && expandableAllowed && !showSingleRoute
|
|
18
|
+
return variant === 'wide' && expandableAllowed && !config.showSingleRoute
|
|
13
19
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AmountInputCardPair } from '../../components/AmountInputCard/AmountInputCardPair.js'
|
|
2
|
+
import { LimitOrderSettings } from '../../components/LimitOrderSettings/LimitOrderSettings.js'
|
|
3
|
+
import { PageContainer } from '../../components/PageContainer.js'
|
|
4
|
+
import { LimitOrderRoutes } from '../../components/Routes/LimitOrderRoutes.js'
|
|
5
|
+
import { MainPageActions } from './MainPageActions.js'
|
|
6
|
+
|
|
7
|
+
const marginSx = { marginBottom: 2 }
|
|
8
|
+
|
|
9
|
+
export const LimitPageContent: React.FC = () => {
|
|
10
|
+
return (
|
|
11
|
+
<PageContainer topGutters>
|
|
12
|
+
<AmountInputCardPair sx={marginSx} />
|
|
13
|
+
<LimitOrderSettings sx={marginSx} />
|
|
14
|
+
<LimitOrderRoutes sx={marginSx} />
|
|
15
|
+
<MainPageActions />
|
|
16
|
+
</PageContainer>
|
|
17
|
+
)
|
|
18
|
+
}
|