From 27d682987c14d2dc803ad1c882d6d466e25b270a Mon Sep 17 00:00:00 2001 From: fairlight <31534717+fairlighteth@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:08:51 +0100 Subject: [PATCH] WIP > feat: twap form styling WIP (#2785) * feat: twap form styling WIP * Fix code style issues with Prettier * feat: twap form styling WIP * feat: twap form styling WIP * Fix code style issues with Prettier * feat: twap form styling WIP * feat(twap): refactor and wire price protection (#2797) * refactor: `ReactNode` already has `string` inside it * feat: new optional params to ExecutionPrice * refactor: make TradeWidgetField and TradeNumberInput more generic * feat: calculate limit price and pass it down to price protection row * feat: pass isInverted state onto price row * feat: calculate buyAmount outside of twapOrderAtom * feat: use buyAmount from its own atom twapOrder is null when the account isn't set * feat: show `0` rather than `-` when there's no limit price * chore: fix merge * Fix code style issues with Prettier * Update src/modules/twap/containers/TwapFormWidget/index.tsx Co-authored-by: Leandro * feat: twap form styling WIP (#2796) * feat: twap form styling WIP * feat: twap form styling WIP * feat: twap form styling WIP * feat: twap form styling WIP * feat: twap form styling WIP * feat: twap form styling WIP * feat: twap form styling WIP * feat: twap form styling WIP * Fix code style issues with Prettier * feat: update styles * feat: update styles * feat: update styles * Fix code style issues with Prettier * feat: styling updates * feat: styling updates * feat: styling updates * feat: styling updates * Fix code style issues with Prettier * feat: styling updates * feat: styling updates * feat: fix custom date style * feat: safar text color fix * feat: fix styles * feat: fix styles * feat: fix styles --------- Co-authored-by: Lint Action * feat: styles update * feat: styles update --------- Co-authored-by: Lint Action Co-authored-by: Leandro Co-authored-by: Alexandr Kazachenko --- .../CurrencyInputPanel/CurrencyInputPanel.tsx | 4 +- src/common/pure/CurrencyInputPanel/styled.tsx | 21 ++- src/common/pure/ExecutionPrice/index.tsx | 8 +- src/common/pure/InlineBanner/banners.tsx | 36 ++--- src/common/pure/InlineBanner/index.cosmos.tsx | 18 +-- src/common/pure/InlineBanner/index.tsx | 51 +++++-- src/common/pure/Modal/index.tsx | 2 +- src/common/pure/RateInfo/index.tsx | 3 +- src/common/pure/TokenAmount/index.tsx | 2 +- src/legacy/assets/cow-swap/protection.svg | 1 + src/legacy/components/SwapWarnings/index.tsx | 2 +- .../containers/AccountDetails/SurplusCard.tsx | 116 +++++++++++++++- .../containers/AdvancedOrdersWidget/index.tsx | 2 +- .../containers/LimitOrdersWarnings/index.tsx | 10 +- .../ordersTable/pure/ReceiptModal/index.tsx | 8 +- .../TradeBasicConfirmDetails/styled.ts | 20 ++- .../trade/pure/ConfirmDetailsItem/index.tsx | 2 +- .../trade/pure/ConfirmDetailsItem/styled.ts | 49 ++++++- .../trade/pure/PriceUpdatedBanner/index.tsx | 4 +- .../trade/pure/TradeNumberInput/index.tsx | 31 +++-- .../trade/pure/TradeNumberInput/styled.tsx | 7 +- src/modules/trade/pure/TradeSelect/index.tsx | 17 ++- .../trade/pure/TradeWidgetField/index.tsx | 7 +- .../trade/pure/TradeWidgetField/styled.tsx | 128 ++++++++++++++++-- .../TwapConfirmModal/TwapConfirmDetails.tsx | 22 ++- .../warnings/FallbackHandlerWarning.tsx | 60 +++++--- .../warnings/SmallPartTimeWarning.tsx | 9 +- .../warnings/SmallPartVolumeWarning.tsx | 11 +- .../warnings/UnsupportedWalletWarning.tsx | 18 +-- .../twap/containers/TwapFormWidget/index.tsx | 52 ++++--- .../twap/containers/TwapFormWidget/styled.tsx | 10 +- .../containers/TwapFormWidget/tooltips.tsx | 34 ++++- src/modules/twap/pure/AmountParts/styled.tsx | 4 +- .../pure/CustomDeadlineSelector/styled.tsx | 1 + src/modules/twap/state/twapOrderAtom.ts | 27 +++- 35 files changed, 634 insertions(+), 163 deletions(-) create mode 100644 src/legacy/assets/cow-swap/protection.svg diff --git a/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx b/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx index 741b881e8d..5b5ad65205 100644 --- a/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx +++ b/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx @@ -126,7 +126,7 @@ export function CurrencyInputPanel(props: CurrencyInputPanelProps) { ) return ( - <> + {topLabel && {topLabel}} @@ -184,6 +184,6 @@ export function CurrencyInputPanel(props: CurrencyInputPanelProps) { showCurrencyAmount={true} disableNonToken={disableNonToken} /> - + ) } diff --git a/src/common/pure/CurrencyInputPanel/styled.tsx b/src/common/pure/CurrencyInputPanel/styled.tsx index 2acb6671e2..188dc4e55e 100644 --- a/src/common/pure/CurrencyInputPanel/styled.tsx +++ b/src/common/pure/CurrencyInputPanel/styled.tsx @@ -6,6 +6,12 @@ import Input from 'legacy/components/NumericalInput' import { TokenAmount } from 'common/pure/TokenAmount' +export const OuterWrapper = styled.div` + max-width: 100%; + display: flex; + flex-flow: column wrap; +` + export const Wrapper = styled.div<{ withReceiveAmountInfo: boolean; disabled: boolean }>` display: flex; flex-flow: row wrap; @@ -17,6 +23,7 @@ export const Wrapper = styled.div<{ withReceiveAmountInfo: boolean; disabled: bo border-radius: ${({ withReceiveAmountInfo }) => (withReceiveAmountInfo ? '16px 16px 0 0' : '16px')}; min-height: 106px; pointer-events: ${({ disabled }) => (disabled ? 'none' : '')}; + max-width: 100%; ${({ theme }) => theme.mediaWidth.upToSmall` padding: 16px 12px; @@ -67,14 +74,22 @@ export const NumericalInput = styled(Input)<{ $loading: boolean }>` font-weight: 500; color: ${({ theme }) => theme.text1}; - ${({ theme }) => theme.mediaWidth.upToSmall` - font-size: 26px; - `} + &[disabled] { + -webkit-text-fill-color: ${({ theme }) => theme.text1}; // safari fix + } + + &[disabled]::selection { + background: transparent; + } &::placeholder { color: ${({ theme }) => transparentize(0.3, theme.text1)}; } + ${({ theme }) => theme.mediaWidth.upToSmall` + font-size: 26px; + `} + ${loadingOpacityMixin} ` diff --git a/src/common/pure/ExecutionPrice/index.tsx b/src/common/pure/ExecutionPrice/index.tsx index 82e66cda2f..d083c5cddf 100644 --- a/src/common/pure/ExecutionPrice/index.tsx +++ b/src/common/pure/ExecutionPrice/index.tsx @@ -10,7 +10,9 @@ export interface ExecutionPriceProps { executionPrice: Price isInverted: boolean showBaseCurrency?: boolean + hideSeparator?: boolean separatorSymbol?: string + hideFiat?: boolean } export function ExecutionPrice({ @@ -18,8 +20,10 @@ export function ExecutionPrice({ isInverted, showBaseCurrency, separatorSymbol = '≈', + hideSeparator, + hideFiat, }: ExecutionPriceProps) { - const executionPriceFiat = useExecutionPriceFiat(executionPrice, isInverted) + const executionPriceFiat = useExecutionPriceFiat(hideFiat ? null : executionPrice, isInverted) const quoteCurrency = isInverted ? executionPrice?.baseCurrency : executionPrice?.quoteCurrency const baseCurrency = isInverted ? executionPrice?.quoteCurrency : executionPrice?.baseCurrency const oneBaseCurrency = tryParseCurrencyAmount('1', baseCurrency) @@ -27,7 +31,7 @@ export function ExecutionPrice({ return ( {showBaseCurrency && } - {` ${separatorSymbol} `} + {!hideSeparator && ` ${separatorSymbol} `} {executionPriceFiat && ( diff --git a/src/common/pure/InlineBanner/banners.tsx b/src/common/pure/InlineBanner/banners.tsx index 0097e78d95..12c13da83c 100644 --- a/src/common/pure/InlineBanner/banners.tsx +++ b/src/common/pure/InlineBanner/banners.tsx @@ -11,10 +11,11 @@ import { InlineBanner } from './index' export function BundleTxApprovalBanner() { return ( - <> - Token approval: For your convenience, token approval and order placement will be bundled into a - single transaction, streamlining your experience! - + Token approval bundling +

+ For your convenience, token approval and order placement will be bundled into a single transaction, streamlining + your experience! +

) } @@ -27,11 +28,12 @@ export type BundleTxWrapBannerProps = { export function BundleTxWrapBanner({ nativeCurrencySymbol, wrappedCurrencySymbol }: BundleTxWrapBannerProps) { return ( - <> - Token wrapping: For your convenience, CoW Swap will bundle all the necessary actions for this - trade into a single transaction. This includes the {nativeCurrencySymbol} wrapping and, if needed,{' '} + Token wrapping bundling +

+ For your convenience, CoW Swap will bundle all the necessary actions for this trade into a single transaction. + This includes the {nativeCurrencySymbol} wrapping and, if needed, {wrappedCurrencySymbol} approval. Even if the trade fails, your wrapping and approval will be done! - +

) } @@ -48,13 +50,14 @@ export function BundleTxSafeWcBanner({ nativeCurrencySymbol, supportsWrapping }: return ( - <> + Use Safe web app +

Use the Safe web app for streamlined trading: {supportsWrappingText}token approval and orders bundled in one go! Only available in the{' '} CoW Swap Safe App↗ - +

) } @@ -67,17 +70,18 @@ export type SmallVolumeWarningBannerProps = { export function SmallVolumeWarningBanner({ feePercentage, feeAmount }: SmallVolumeWarningBannerProps) { return ( - <> - Small orders are unlikely to be executed. For this order, network fees would be{' '} + Small orders are unlikely to be executed +

+ For this order, network fees would be{' '} {feePercentage?.toFixed(2)}% ( ) {' '} of your sell amount! Therefore, your order is unlikely to execute. - {/*
*/} - {/* TODO: add link to somewhere */} - {/*Learn more ↗*/} - +

+ {/*
*/} + {/* TODO: add link to somewhere */} + {/*Learn more ↗*/}
) } diff --git a/src/common/pure/InlineBanner/index.cosmos.tsx b/src/common/pure/InlineBanner/index.cosmos.tsx index dda1795d9c..f1ea3565a3 100644 --- a/src/common/pure/InlineBanner/index.cosmos.tsx +++ b/src/common/pure/InlineBanner/index.cosmos.tsx @@ -20,10 +20,11 @@ const Fixtures = { information: ( - <> - Token approval: For your convenience, token approval and order placement will be bundled into - a single transaction, streamlining your experience! - + Token approval bundling +

+ For your convenience, token approval and order placement will be bundled into a single transaction, + streamlining your experience! +

), @@ -44,10 +45,11 @@ const Fixtures = { smallVolumeWarning: ( - <> - Small orders are unlikely to be executed. For this order, network fees would be 1.00% (2500 COW) of - your sell amount! Therefore, your order is unlikely to execute. - + Small orders are unlikely to be executed +

+ For this order, network fees would be 1.00% (2500 COW) of your sell amount! Therefore, your order is + unlikely to execute. +

), diff --git a/src/common/pure/InlineBanner/index.tsx b/src/common/pure/InlineBanner/index.tsx index 90f5b9268e..b7e6b74fb8 100644 --- a/src/common/pure/InlineBanner/index.tsx +++ b/src/common/pure/InlineBanner/index.tsx @@ -38,18 +38,28 @@ const BANNER_CONFIG: Record = { const Wrapper = styled.span<{ color: string }>` display: flex; align-items: center; + justify-content: center; background: ${({ theme, color }) => (theme.darkMode ? transparentize(0.9, color) : transparentize(0.85, color))}; - color: ${({ theme, color }) => (theme.darkMode ? lighten(0.2, color) : darken(0.15, color))}; - gap: 10px; - border-radius: 10px; + color: ${({ theme, color }) => (theme.darkMode ? lighten(0.2, color) : darken(0.2, color))}; + gap: 24px 10px; + border-radius: 16px; margin: auto; - padding: 16px 12px; + padding: 16px; font-size: 14px; font-weight: 400; line-height: 1.2; width: 100%; - > svg { + > span { + display: flex; + justify-content: center; + align-items: center; + flex-flow: column wrap; + gap: 16px; + width: 100%; + } + + > span > svg { --size: 32px; display: block; min-width: var(--size); @@ -60,9 +70,32 @@ const Wrapper = styled.span<{ color: string }>` stroke: none !important; } - > svg > path { + > span > svg > path { fill: ${({ color }) => color}; } + + > span { + display: flex; + flex-flow: row wrap; + + ${({ theme }) => theme.mediaWidth.upToSmall` + flex-flow: column wrap; + gap: 16px; + `}; + } + + > span > strong { + display: flex; + align-items: center; + } + + > span > p { + line-height: 1.4; + margin: auto; + padding: 0; + width: 100%; + text-align: center; + } ` export type InlineBannerProps = { @@ -79,8 +112,10 @@ export function InlineBanner({ children, className, hideIcon, type = 'alert' }: return ( - {!hideIcon && } - {children} + + {!hideIcon && } + {children} + ) } diff --git a/src/common/pure/Modal/index.tsx b/src/common/pure/Modal/index.tsx index 72ab1d0aca..f0bcca4550 100644 --- a/src/common/pure/Modal/index.tsx +++ b/src/common/pure/Modal/index.tsx @@ -92,7 +92,7 @@ export const CowModal = styled(Modal)<{ > [data-reach-dialog-content] { color: ${({ theme }) => theme.text1}; width: 100%; - max-width: ${({ maxWidth = 470 }) => `${maxWidth}px`}; + max-width: ${({ maxWidth = 500 }) => `${maxWidth}px`}; border: ${({ border = 'inherit' }) => `${border}`}; z-index: 100; padding: ${({ padding = '0px' }) => `${padding}`}; diff --git a/src/common/pure/RateInfo/index.tsx b/src/common/pure/RateInfo/index.tsx index 437f63076b..9f84c1d116 100644 --- a/src/common/pure/RateInfo/index.tsx +++ b/src/common/pure/RateInfo/index.tsx @@ -28,7 +28,7 @@ export interface RateInfoParams { export interface RateInfoProps { className?: string - label?: string + label?: React.ReactNode stylized?: boolean noLabel?: boolean prependSymbol?: boolean @@ -105,6 +105,7 @@ export const RateWrapper = styled.button` letter-spacing: -0.1px; text-align: right; font-weight: 500; + width: 100%; ` export const FiatRate = styled.span` diff --git a/src/common/pure/TokenAmount/index.tsx b/src/common/pure/TokenAmount/index.tsx index 5fbcdb2bf0..db7516436c 100644 --- a/src/common/pure/TokenAmount/index.tsx +++ b/src/common/pure/TokenAmount/index.tsx @@ -14,7 +14,7 @@ export const Wrapper = styled.span<{ highlight: boolean; lowVolumeWarning?: bool background: ${({ highlight }) => (highlight ? 'rgba(196,18,255,0.4)' : '')}; color: ${({ lowVolumeWarning, theme }) => lowVolumeWarning ? darken(theme.darkMode ? 0 : 0.15, theme.alert) : 'inherit'}; - word-break: word-break; + word-break: break-word; ` export const SymbolElement = styled.span<{ opacitySymbol?: boolean }>` diff --git a/src/legacy/assets/cow-swap/protection.svg b/src/legacy/assets/cow-swap/protection.svg new file mode 100644 index 0000000000..9e7141cb0b --- /dev/null +++ b/src/legacy/assets/cow-swap/protection.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/legacy/components/SwapWarnings/index.tsx b/src/legacy/components/SwapWarnings/index.tsx index c825223599..557df61a87 100644 --- a/src/legacy/components/SwapWarnings/index.tsx +++ b/src/legacy/components/SwapWarnings/index.tsx @@ -57,7 +57,7 @@ const WarningContainer = styled(AuxInformationContainer).attrs((props) => ({ width: ${({ width = '100%' }) => width}; border-radius: 16px; border: 0; - margin: ${({ margin = '0 auto 12px auto' }) => margin}; + margin: ${({ margin = '0 auto' }) => margin}; position: relative; z-index: 1; diff --git a/src/modules/account/containers/AccountDetails/SurplusCard.tsx b/src/modules/account/containers/AccountDetails/SurplusCard.tsx index f23fe453a4..4b04cc805d 100644 --- a/src/modules/account/containers/AccountDetails/SurplusCard.tsx +++ b/src/modules/account/containers/AccountDetails/SurplusCard.tsx @@ -1,4 +1,7 @@ -import QuestionHelper from 'legacy/components/QuestionHelper' +import { transparentize } from 'polished' +import styled from 'styled-components/macro' + +import QuestionHelper, { QuestionWrapper } from 'legacy/components/QuestionHelper' import { useHigherUSDValue } from 'legacy/hooks/useStablecoinPrice' import { ExternalLink } from 'legacy/theme' @@ -6,15 +9,120 @@ import { FiatAmount } from 'common/pure/FiatAmount' import { TokenAmount } from 'common/pure/TokenAmount' import { useTotalSurplus } from 'common/state/totalSurplusState' -import { InfoCard, SurplusCardWrapper } from './styled' +import { InfoCard } from './styled' export function SurplusCard() { const { surplusAmount, isLoading } = useTotalSurplus() const surplusUsdAmount = useHigherUSDValue(surplusAmount) + const Wrapper = styled.div` + margin: 12px auto 24px; + width: 100%; + display: grid; + align-items: center; + justify-content: center; + grid-template-columns: 1fr; + gap: 24px; + box-sizing: border-box; + padding: 0; + + ${({ theme }) => theme.mediaWidth.upToSmall` + display: flex; + flex-flow: column wrap; + padding: 0; + `} + + ${InfoCard} { + display: flex; + flex-flow: column wrap; + align-items: center; + justify-content: center; + gap: 0; + background: ${({ theme }) => theme.gradient2}; + border-radius: 16px; + padding: 20px 26px 26px; + min-height: 190px; + width: 100%; + max-width: 100%; + margin: 0; + } + + ${InfoCard} > div { + display: flex; + flex-flow: column wrap; + align-items: center; + justify-content: center; + + &:first-child { + margin: 20px auto 0; + } + + &:last-child { + margin: auto 0 0; + } + } + + ${InfoCard} > div > span { + display: flex; + flex-flow: column wrap; + align-items: center; + justify-content: center; + } + + ${InfoCard} > div > span > i, + ${InfoCard} > div > a, + ${InfoCard} > div > span > p { + display: flex; + font-size: 13px; + font-style: normal; + font-weight: 500; + line-height: 1.1; + width: 100%; + text-align: center; + justify-content: center; + align-items: center; + color: ${({ theme }) => transparentize(0.3, theme.text1)}; + } + + ${InfoCard} > div > span > p { + color: ${({ theme }) => theme.text1}; + } + + ${InfoCard} > div > span > b { + font-size: 28px; + font-weight: bold; + color: ${({ theme }) => theme.success}; + width: 100%; + text-align: center; + margin: 12px auto 0; + word-break: break-all; + } + + ${InfoCard} > div > a { + margin: 20px auto 0; + } + + ${InfoCard} > div > small { + font-size: 15px; + font-weight: 500; + line-height: 1.1; + color: ${({ theme }) => transparentize(0.5, theme.text1)}; + margin: 3px auto 0; + } + + ${QuestionWrapper} { + opacity: 0.5; + transition: opacity 0.2s ease-in-out; + + &:hover { + opacity: 1; + } + } + ` + return ( - +
@@ -43,6 +151,6 @@ export function SurplusCard() {
-
+ ) } diff --git a/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx b/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx index 0076da2485..5d8a1670d8 100644 --- a/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx +++ b/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx @@ -100,7 +100,7 @@ export function AdvancedOrdersWidget({ children }: { children: JSX.Element }) { const params = { recipient, - compactView: false, + compactView: true, showRecipient: false, isTradePriceUpdating, priceImpact, diff --git a/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx b/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx index 8900a57bbb..675fc276f0 100644 --- a/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx +++ b/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx @@ -44,6 +44,12 @@ export interface LimitOrdersWarningsProps { className?: string } +const Wrapper = styled.div` + display: flex; + flex-flow: column wrap; + gap: 10px; +` + const StyledNoImpactWarning = styled(NoImpactWarning)` margin: 10px auto 0; ` @@ -123,7 +129,7 @@ export function LimitOrdersWarnings(props: LimitOrdersWarningsProps) { ) return isVisible ? ( -
+ {showZeroApprovalWarning && } {showPriceImpactWarning && ( } {showApprovalBundlingBanner && } {showSafeWcBundlingBanner && } -
+ ) : null } diff --git a/src/modules/ordersTable/pure/ReceiptModal/index.tsx b/src/modules/ordersTable/pure/ReceiptModal/index.tsx index d2ff6563f7..6a212033da 100644 --- a/src/modules/ordersTable/pure/ReceiptModal/index.tsx +++ b/src/modules/ordersTable/pure/ReceiptModal/index.tsx @@ -104,9 +104,11 @@ export function ReceiptModal({ {twapOrder && ( - {isTwapPartOrder - ? `Part of a ${twapOrder.order.n}-part TWAP order split` - : `TWAP order split into ${twapOrder.order.n} parts`} +

+ {isTwapPartOrder + ? `Part of a ${twapOrder.order.n}-part TWAP order split` + : `TWAP order split into ${twapOrder.order.n} parts`} +

)} diff --git a/src/modules/trade/containers/TradeBasicConfirmDetails/styled.ts b/src/modules/trade/containers/TradeBasicConfirmDetails/styled.ts index ceba27b052..985b8d76b1 100644 --- a/src/modules/trade/containers/TradeBasicConfirmDetails/styled.ts +++ b/src/modules/trade/containers/TradeBasicConfirmDetails/styled.ts @@ -3,13 +3,27 @@ import styled from 'styled-components/macro' import { RateInfo } from 'common/pure/RateInfo' export const Wrapper = styled.div` - padding: 1rem 0; + padding: 10px; + font-size: 13px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + display: flex; + flex-flow: column wrap; + align-items: flex-start; + `} ` export const StyledRateInfo = styled(RateInfo)` - font-size: 13px; - font-weight: 500; margin: 0 auto; + font-size: 13px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + display: flex; + flex-flow: column wrap; + gap: 2px; + align-items: flex-start; + margin: 0 0 10px; + `} ` export const ItemWithArrow = styled.div` diff --git a/src/modules/trade/pure/ConfirmDetailsItem/index.tsx b/src/modules/trade/pure/ConfirmDetailsItem/index.tsx index 470acb2d1d..303404f0c6 100644 --- a/src/modules/trade/pure/ConfirmDetailsItem/index.tsx +++ b/src/modules/trade/pure/ConfirmDetailsItem/index.tsx @@ -22,7 +22,7 @@ export function ConfirmDetailsItem(props: ConfirmDetailsItemProps) { return ( - {withArrow && } + {withArrow && } {label ? ( <> diff --git a/src/modules/trade/pure/ConfirmDetailsItem/styled.ts b/src/modules/trade/pure/ConfirmDetailsItem/styled.ts index 4de79e3556..caa261faef 100644 --- a/src/modules/trade/pure/ConfirmDetailsItem/styled.ts +++ b/src/modules/trade/pure/ConfirmDetailsItem/styled.ts @@ -6,9 +6,25 @@ export const Wrapper = styled.div` display: flex; align-items: center; + ${({ theme }) => theme.mediaWidth.upToSmall` + width: 100%; + flex-flow: row nowrap; + align-items: flex-start; + margin: 0 0 10px; + `} + > svg:first-child { - margin-right: 5px; + margin: 0 4px 0 0; color: ${({ theme }) => theme.text1}; + opacity: 0.5; + } + + ${StyledRowBetween} { + ${({ theme }) => theme.mediaWidth.upToSmall` + flex-flow: column wrap; + gap: 2px; + align-items: flex-start; + `} } ` @@ -16,8 +32,37 @@ export const Row = styled(StyledRowBetween)` display: flex; align-items: center; justify-content: space-between; + + ${({ theme }) => theme.mediaWidth.upToSmall` + flex-flow: column wrap; + gap: 2px; + align-items: flex-start; + `} ` export const Content = styled.div` - margin-left: auto; + display: flex; + flex-flow: row wrap; + align-items: center; + justify-content: flex-end; + margin: 0 0 0 auto; + font-weight: 500; + font-size: 13px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + margin: 0; + `} + + > span { + display: block; + text-align: right; + word-break: break-all; + } + + i { + font-style: normal; + opacity: 0.7; + word-break: break-all; + text-align: right; + } ` diff --git a/src/modules/trade/pure/PriceUpdatedBanner/index.tsx b/src/modules/trade/pure/PriceUpdatedBanner/index.tsx index d6d0bd4259..b40fd3b356 100644 --- a/src/modules/trade/pure/PriceUpdatedBanner/index.tsx +++ b/src/modules/trade/pure/PriceUpdatedBanner/index.tsx @@ -12,11 +12,12 @@ const Wrapper = styled.div` justify-content: space-between; font-size: 16px; font-weight: 600; + width: 100%; ` const StyledBanner = styled(InlineBanner)` > span { - width: 100%; + flex-flow: row nowrap; } ` @@ -31,6 +32,7 @@ const AcceptButton = styled.button` outline: none; padding: 10px 20px; border-radius: 8px; + margin: 0 0 0 auto; &:hover { background-color: ${({ theme }) => transparentize(0.2, theme.bg2)}; diff --git a/src/modules/trade/pure/TradeNumberInput/index.tsx b/src/modules/trade/pure/TradeNumberInput/index.tsx index 4f7bc2751f..4c7a091cb8 100644 --- a/src/modules/trade/pure/TradeNumberInput/index.tsx +++ b/src/modules/trade/pure/TradeNumberInput/index.tsx @@ -12,10 +12,20 @@ export interface TradeNumberInputProps extends TradeWidgetFieldProps { min?: number max?: number placeholder?: string + prefixComponent?: React.ReactElement } export function TradeNumberInput(props: TradeNumberInputProps) { - const { value, suffix, onUserInput, placeholder = '0', decimalsPlaces = 0, min, max = 100_000 } = props + const { + value, + suffix, + onUserInput, + placeholder = '0', + decimalsPlaces = 0, + min, + max = 100_000, + prefixComponent, + } = props const [displayedValue, setDisplayedValue] = useState(value === null ? '' : value.toString()) @@ -55,15 +65,18 @@ export function TradeNumberInput(props: TradeNumberInputProps) { }, []) return ( - + <> - validateInput(e.target.value)} - onUserInput={(value) => setDisplayedValue(value)} - /> - {suffix && {suffix}} + {prefixComponent} + + validateInput(e.target.value)} + onUserInput={(value) => setDisplayedValue(value)} + /> + {suffix && {suffix}} + ) diff --git a/src/modules/trade/pure/TradeNumberInput/styled.tsx b/src/modules/trade/pure/TradeNumberInput/styled.tsx index 06a0356e12..25737443d1 100644 --- a/src/modules/trade/pure/TradeNumberInput/styled.tsx +++ b/src/modules/trade/pure/TradeNumberInput/styled.tsx @@ -3,7 +3,9 @@ import styled from 'styled-components/macro' import Input from 'legacy/components/NumericalInput' export const Suffix = styled.span` - margin-left: 5px; + margin: 0 0 0 3px; + opacity: 0.7; + font-weight: 600; ` export const NumericalInput = styled(Input)<{ color?: string }>` color: ${({ theme, color }) => { @@ -15,9 +17,8 @@ export const NumericalInput = styled(Input)<{ color?: string }>` align-items: center; background: none; flex: 1; - width: 100%; + width: auto; height: 100%; - max-width: 70px; font-size: 22px; font-weight: 500; diff --git a/src/modules/trade/pure/TradeSelect/index.tsx b/src/modules/trade/pure/TradeSelect/index.tsx index 456768ff18..ae2eb60d7c 100644 --- a/src/modules/trade/pure/TradeSelect/index.tsx +++ b/src/modules/trade/pure/TradeSelect/index.tsx @@ -1,4 +1,5 @@ import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button' +import { transparentize } from 'polished' import { ChevronDown } from 'react-feather' import styled from 'styled-components/macro' @@ -17,13 +18,17 @@ const StyledMenuList = styled(MenuList)` background: ${({ theme }) => theme.bg1}; box-shadow: ${({ theme }) => theme.boxShadow2}; margin: 15px 0 0 0; - padding: 10px 15px; + padding: 16px; border-radius: 20px; outline: none; list-style: none; position: relative; z-index: 2; min-width: 120px; + display: flex; + flex-flow: column wrap; + gap: 10px; + transition: background 0.1s ease-in-out; ` const StyledMenuButton = styled(MenuButton)` @@ -45,7 +50,15 @@ const StyledMenuButton = styled(MenuButton)` const StyledMenuItem = styled(MenuItem)` cursor: pointer; font-size: 14px; - margin-bottom: 10px; + color: ${({ theme }) => transparentize(0.2, theme.text1)}; + + &:hover { + color: ${({ theme }) => theme.text1}; + } + + &:not(:last-child) { + margin: 0 0 10px; + } ` export function TradeSelect(props: TradeSelectProps) { diff --git a/src/modules/trade/pure/TradeWidgetField/index.tsx b/src/modules/trade/pure/TradeWidgetField/index.tsx index 6885e8da06..4705d10da8 100644 --- a/src/modules/trade/pure/TradeWidgetField/index.tsx +++ b/src/modules/trade/pure/TradeWidgetField/index.tsx @@ -5,7 +5,7 @@ import { Trans } from '@lingui/macro' import QuestionHelper from 'legacy/components/QuestionHelper' import { renderTooltip } from 'legacy/components/Tooltip' -import { TradeWidgetFieldBox, TradeWidgetFieldLabel, Content, ErrorText } from './styled' +import { Content, ErrorText, TradeWidgetFieldBox, TradeWidgetFieldLabel } from './styled' export type TradeWidgetFieldError = { type: 'error' | 'warning'; text: string | null } | null @@ -15,14 +15,15 @@ export interface TradeWidgetFieldProps { tooltip?: React.ReactNode | ((params: any) => React.ReactNode) error?: TradeWidgetFieldError className?: string + hasPrefix?: boolean } export function TradeWidgetField(props: TradeWidgetFieldProps) { - const { className, children, label, tooltip, error } = props + const { className, children, label, tooltip, error, hasPrefix } = props const tooltipElement = renderTooltip(tooltip, props) return ( - + {label} {tooltip && } diff --git a/src/modules/trade/pure/TradeWidgetField/styled.tsx b/src/modules/trade/pure/TradeWidgetField/styled.tsx index 40943d2d19..b82a620b93 100644 --- a/src/modules/trade/pure/TradeWidgetField/styled.tsx +++ b/src/modules/trade/pure/TradeWidgetField/styled.tsx @@ -1,19 +1,9 @@ import { transparentize } from 'polished' -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' -export const TradeWidgetFieldBox = styled.div` - background: ${({ theme }) => theme.grey1}; - border-radius: 16px; - min-height: 58px; - font-size: 18px; - padding: 16px; - display: flex; - justify-content: space-between; - align-items: center; - flex-flow: row wrap; - flex: 1; - gap: 3px; -` +import { QuestionWrapper } from 'legacy/components/QuestionHelper' + +import { NumericalInput } from '../TradeNumberInput/styled' export const TradeWidgetFieldLabel = styled.span` color: ${({ theme }) => transparentize(0.3, theme.text1)}; @@ -21,12 +11,25 @@ export const TradeWidgetFieldLabel = styled.span` align-items: center; font-size: 13px; font-weight: 500; + padding: 0; + flex: 0 1 auto; + flex: 1; + + ${QuestionWrapper} { + opacity: 0.5; + transition: opacity 0.2s ease-in-out; + + &:hover { + opacity: 1; + } + } ` export const Content = styled.div` display: flex; align-items: center; font-weight: 500; + position: relative; ` export const ErrorText = styled.div<{ type?: 'error' | 'warning' }>` @@ -38,3 +41,100 @@ export const ErrorText = styled.div<{ type?: 'error' | 'warning' }>` font-size: 12px; margin-top: 5px; ` + +export const TradeWidgetFieldBox = styled.div<{ hasPrefix?: boolean }>` + --minHeight: 45px; + background: ${({ theme, hasPrefix }) => (hasPrefix ? 'transparent' : theme.grey1)}; + border: 1px solid ${({ theme, hasPrefix }) => (hasPrefix ? theme.grey1 : 'transparent')}; + border-radius: 16px; + min-height: var(--minHeight); + font-size: 18px; + padding: 10px 16px; + padding: ${({ hasPrefix }) => (hasPrefix ? '0' : '10px 16px')}; + display: flex; + justify-content: space-between; + align-items: center; + flex-flow: row wrap; + flex: 1; + gap: 3px; + width: 100%; + + ${({ theme }) => theme.mediaWidth.upToSmall` + gap: 0; + `}; + +${({ hasPrefix }) => + hasPrefix && + css` + display: grid; + grid-template-columns: max-content auto; + + ${({ theme }) => theme.mediaWidth.upToSmall` + display: flex; + flex-flow: column wrap; + `}; + `} + + ${TradeWidgetFieldLabel} { + padding: ${({ hasPrefix }) => (hasPrefix ? '10px 16px' : 'initial')}; + } + + ${Content} { + padding: 0; + flex: 0 1 auto; + + ${NumericalInput} { + max-width: 200px; + }; + + ${({ hasPrefix }) => + hasPrefix && + css` + flex: 1 1 auto; + justify-content: center; + display: flex; + padding: 0; + height: var(--minHeight); + + ${({ theme }) => theme.mediaWidth.upToSmall` + border-top: 1px solid ${theme.grey1}; + width: 100%; + `}; + `} + + > em { + font-style: normal; + flex: 1 1 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: flex-end; + padding: 0 12px 0 0; + } + + > span { + background: ${({ theme, hasPrefix }) => (hasPrefix ? theme.grey1 : 'transparent')}; + + ${({ hasPrefix }) => + hasPrefix && + css` + margin: auto; + height: 100%; + display: flex; + padding: 0 10px 0 0; + flex: 0 1 auto; + border-radius: 0 15px 15px 0; + align-items: center; + + ${({ theme }) => theme.mediaWidth.upToSmall` + border-radius: 0 0 15px 0; + width: 136px; + `}; + + > ${NumericalInput} { + font-size: 18px; + width: 62px; + } + `} + } +` diff --git a/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx b/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx index 6198b69b13..c2b888d614 100644 --- a/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx +++ b/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx @@ -1,11 +1,27 @@ import React from 'react' +import styled from 'styled-components/macro' + import { ConfirmDetailsItem } from 'modules/trade/pure/ConfirmDetailsItem' import { ReviewOrderModalAmountRow } from 'modules/trade/pure/ReviewOrderModalAmountRow' import { PartsState } from '../../state/partsStateAtom' import { deadlinePartsDisplay } from '../../utils/deadlinePartsDisplay' +const Wrapper = styled.div` + padding: 10px; + font-size: 13px; + + > b { + display: block; + margin: 0 0 3px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + margin: 0 0 10px; + `} + } +` + export type TwapConfirmDetailsProps = { startTime: number | undefined partDuration: number | undefined @@ -24,8 +40,8 @@ export const TwapConfirmDetails = React.memo(function TwapConfirmDetails(props: const totalDurationDisplay = totalDuration ? deadlinePartsDisplay(totalDuration, true) : '' return ( -
- TWAP order split in {numberOfPartsValue} equal parts + + TWAP order split in {numberOfPartsValue} equal parts {/* Sell amount per part */} {totalDurationDisplay} -
+
) }) diff --git a/src/modules/twap/containers/TwapFormWarnings/warnings/FallbackHandlerWarning.tsx b/src/modules/twap/containers/TwapFormWarnings/warnings/FallbackHandlerWarning.tsx index 943e9a09aa..b8a056fc37 100644 --- a/src/modules/twap/containers/TwapFormWarnings/warnings/FallbackHandlerWarning.tsx +++ b/src/modules/twap/containers/TwapFormWarnings/warnings/FallbackHandlerWarning.tsx @@ -2,29 +2,51 @@ import styled from 'styled-components/macro' import { InlineBanner } from 'common/pure/InlineBanner' +const Wrapper = styled.div` + display: flex; + flex-flow: column wrap; + align-items: center; + justify-content: center; +` + const CheckboxWrapper = styled.div` + display: flex; + align-items: center; + justify-content: center; + width: 100%; font-weight: bold; text-align: left; - margin-left: 25px; ` const WarningCheckboxWrapper = styled(CheckboxWrapper)` - width: 90%; - margin: -50px auto 12px; - position: relative; - top: 70px; + width: 100%; + margin: 0 auto; + border: 1px solid; + border-radius: 16px; + padding: 20px; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; ` const WarningCheckbox = styled.label` display: flex; - gap: 5px; + gap: 10px; cursor: pointer; -` + align-items: center; + justify-content: center; + width: 100%; -const InlineBannerWithCheckbox = styled(InlineBanner)` - padding-bottom: 80px; + > input { + --size: 21px; + width: var(--size); + height: var(--size); + } ` +const InlineBannerWithCheckbox = styled(InlineBanner)`` + interface FallbackHandlerWarningProps { isFallbackHandlerSetupAccepted: boolean toggleFallbackHandlerSetupFlag(isChecked: boolean): void @@ -41,34 +63,32 @@ export function FallbackHandlerWarning({ checked={isFallbackHandlerSetupAccepted} onChange={(event) => toggleFallbackHandlerSetupFlag(event.currentTarget.checked)} /> - - Modify Safe's fallback handler -
when placing order -
+ Modify Safe's fallback handler when placing order ) if (isFallbackHandlerSetupAccepted) { return ( -
+ {fallbackHandlerCheckbox} -
+ ) } else { return ( -
+ Unsupported Safe detected -
- Connected Safe lacks required fallback handler. Switch to a compatible Safe or modify fallback handler for - TWAP orders when placing your order.{' '} +

+ Connected Safe lacks required fallback handler. Switch to a compatible Safe or modify fallback handler for + TWAP orders when placing your order. +

{/*Learn more*/} {/*TODO: set a proper link*/} {fallbackHandlerCheckbox}
-
+ ) } } diff --git a/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartTimeWarning.tsx b/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartTimeWarning.tsx index 3196bee934..9f7d177e55 100644 --- a/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartTimeWarning.tsx +++ b/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartTimeWarning.tsx @@ -8,10 +8,11 @@ export function SmallPartTimeWarning() { return ( - <> - Minimum part time: A minimum of {time} between parts is required. Decrease the - number of parts or increase the total duration. - + Insufficient time between parts +

+ A minimum of {time} between parts is required. Decrease the number of parts or increase the + total duration. +

) } diff --git a/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartVolumeWarning.tsx b/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartVolumeWarning.tsx index 8c9b900233..1d6483ef9f 100644 --- a/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartVolumeWarning.tsx +++ b/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartVolumeWarning.tsx @@ -14,13 +14,14 @@ export function SmallPartVolumeWarning({ chainId }: SmallPartVolumeWarningBanner return ( - <> - Minimum sell size: The sell amount per part of your TWAP order should be at least{' '} - + Minimum sell size +

+ The sell amount per part of your TWAP order should be at least{' '} + $ - + . Decrease the number of parts or increase the total sell amount. - +

) } diff --git a/src/modules/twap/containers/TwapFormWarnings/warnings/UnsupportedWalletWarning.tsx b/src/modules/twap/containers/TwapFormWarnings/warnings/UnsupportedWalletWarning.tsx index 3723d46d9b..854e2d8d38 100644 --- a/src/modules/twap/containers/TwapFormWarnings/warnings/UnsupportedWalletWarning.tsx +++ b/src/modules/twap/containers/TwapFormWarnings/warnings/UnsupportedWalletWarning.tsx @@ -6,12 +6,14 @@ export function UnsupportedWalletWarning({ isSafeViaWc }: { isSafeViaWc: boolean if (isSafeViaWc) { return ( - <> - Use the Safe web app for advanced trading. Only available in the{' '} + Use Safe web app +

+ Use the Safe web app for advanced trading.
+ Only available in the{' '} CoW Swap Safe App↗ - +

) } @@ -19,11 +21,11 @@ export function UnsupportedWalletWarning({ isSafeViaWc }: { isSafeViaWc: boolean return ( Unsupported wallet detected -
- TWAP orders currently require a Safe with a special fallback handler.
- Have one? Switch to it! {/*Need setup? Click here.*/}{' '} -
- Future updates may extend wallet support! +

+ TWAP orders currently require a Safe with a special fallback handler. Have one? Switch to it!{' '} + {/*Need setup? Click here.*/}Future updates may + extend wallet support! +

{/*TODO: set a proper link*/}
) diff --git a/src/modules/twap/containers/TwapFormWidget/index.tsx b/src/modules/twap/containers/TwapFormWidget/index.tsx index 3685317f48..f88f17ce7b 100644 --- a/src/modules/twap/containers/TwapFormWidget/index.tsx +++ b/src/modules/twap/containers/TwapFormWidget/index.tsx @@ -1,6 +1,6 @@ import { useAtomValue } from 'jotai' import { useUpdateAtom } from 'jotai/utils' -import { useEffect } from 'react' +import { useEffect, useState } from 'react' import { openAdvancedOrdersTabAnalytics, @@ -20,7 +20,9 @@ import { TwapFormState } from 'modules/twap/pure/PrimaryActionButton/getTwapForm import { QuoteObserverUpdater } from 'modules/twap/updaters/QuoteObserverUpdater' import { useIsSafeApp, useWalletInfo } from 'modules/wallet' +import { usePrice } from 'common/hooks/usePrice' import { useRateInfoParams } from 'common/hooks/useRateInfoParams' +import { ExecutionPrice } from 'common/pure/ExecutionPrice' import * as styledEl from './styled' import { AMOUNT_PARTS_LABELS, LABELS_TOOLTIPS } from './tooltips' @@ -35,7 +37,7 @@ import { useTwapFormState } from '../../hooks/useTwapFormState' import { AmountParts } from '../../pure/AmountParts' import { DeadlineSelector } from '../../pure/DeadlineSelector' import { partsStateAtom } from '../../state/partsStateAtom' -import { twapTimeIntervalAtom } from '../../state/twapOrderAtom' +import { twapSlippageAdjustedBuyAmount, twapTimeIntervalAtom } from '../../state/twapOrderAtom' import { twapOrderSlippageAtom, twapOrdersSettingsAtom, @@ -57,6 +59,7 @@ export function TwapFormWidget() { const { numberOfPartsValue, slippageValue, deadline, customDeadline, isCustomDeadline } = useAtomValue(twapOrdersSettingsAtom) + const buyAmount = useAtomValue(twapSlippageAdjustedBuyAmount) const { inputCurrencyAmount, outputCurrencyAmount } = useAdvancedOrdersDerivedState() const { inputCurrencyAmount: rawInputCurrencyAmount } = useAdvancedOrdersRawState() @@ -78,6 +81,8 @@ export function TwapFormWidget() { const rateInfoParams = useRateInfoParams(inputCurrencyAmount, outputCurrencyAmount) + const limitPrice = usePrice(inputCurrencyAmount, buyAmount) + const deadlineState = { deadline, customDeadline, @@ -110,6 +115,9 @@ export function TwapFormWidget() { } }, [account, isFallbackHandlerRequired, isFallbackHandlerCompatible, localFormValidation, verification]) + const isInvertedState = useState(false) + const [isInverted] = isInvertedState + return ( <> @@ -123,10 +131,32 @@ export function TwapFormWidget() { {!isWrapOrUnwrap && ( - + )} - + updateSettingsState({ slippageValue: value })} + decimalsPlaces={2} + placeholder={DEFAULT_TWAP_SLIPPAGE.toFixed(1)} + max={50} + label={LABELS_TOOLTIPS.slippage.label} + tooltip={renderTooltip(LABELS_TOOLTIPS.slippage.tooltip)} + prefixComponent={ + + {limitPrice ? ( + + ) : ( + '0' + )} + + } + suffix="%" + /> - updateSettingsState({ slippageValue: value })} - decimalsPlaces={2} - placeholder={DEFAULT_TWAP_SLIPPAGE.toFixed(0)} - max={50} - label={LABELS_TOOLTIPS.slippage.label} - tooltip={renderTooltip(LABELS_TOOLTIPS.slippage.tooltip)} - suffix="%" - /> - - + + theme.mediaWidth.upToSmall` flex-direction: column; @@ -31,3 +32,10 @@ export const StyledRateInfo = styled(RateInfo)` display: grid; grid-template-columns: max-content auto; ` + +export const StyledPriceProtection = styled.div` + display: flex; + flex-flow: row wrap; + align-items: center; + width: 100%; +` diff --git a/src/modules/twap/containers/TwapFormWidget/tooltips.tsx b/src/modules/twap/containers/TwapFormWidget/tooltips.tsx index 2c5f79dd73..2b56f49a5a 100644 --- a/src/modules/twap/containers/TwapFormWidget/tooltips.tsx +++ b/src/modules/twap/containers/TwapFormWidget/tooltips.tsx @@ -1,7 +1,24 @@ +import SVG from 'react-inlinesvg' +import styled from 'styled-components/macro' + +import ShieldImage from 'legacy/assets/cow-swap/protection.svg' + import { deadlinePartsDisplay } from 'modules/twap/utils/deadlinePartsDisplay' +const IconImage = styled.div` + display: flex; + align-items: center; + justify-content: center; + + > svg { + opacity: 0.5; + fill: ${({ theme }) => theme.text1}; + margin: 0 3px 0 0; + } +` + export interface LabelTooltip { - label: string + label: React.ReactNode tooltip?: React.ReactNode | ((params: any) => React.ReactNode) } @@ -42,14 +59,17 @@ export const LABELS_TOOLTIPS: LabelTooltipItems = { ), }, slippage: { - label: 'Slippage', + label: ( + <> + + + {' '} + Price protection + + ), tooltip: ( <> - This slippage will apply to each part of your order. Since a TWAP order executes over a longer period of time, - your slippage should take into account possible price fluctuations over that time. -
-
- If your slippage is too low, you risk some parts of your order failing to execute. + Your TWAP order won't execute and is protected if the market price dips more than your set slippage tolerance. ), }, diff --git a/src/modules/twap/pure/AmountParts/styled.tsx b/src/modules/twap/pure/AmountParts/styled.tsx index 4c18b6d69a..e753097f32 100644 --- a/src/modules/twap/pure/AmountParts/styled.tsx +++ b/src/modules/twap/pure/AmountParts/styled.tsx @@ -36,13 +36,15 @@ export const Label = styled(TradeWidgetFieldLabel)` ` export const Amount = styled(TokenAmount)` + display: flex; + flex-flow: row wrap; font-size: 18px; font-weight: 500; padding: 2px 0; width: 100%; overflow: hidden; text-overflow: ellipsis; - display: flex; + gap: 5px; > div:first-child { margin-right: 5px; diff --git a/src/modules/twap/pure/CustomDeadlineSelector/styled.tsx b/src/modules/twap/pure/CustomDeadlineSelector/styled.tsx index 35e187cf4a..cbb2963b65 100644 --- a/src/modules/twap/pure/CustomDeadlineSelector/styled.tsx +++ b/src/modules/twap/pure/CustomDeadlineSelector/styled.tsx @@ -40,6 +40,7 @@ export const ModalFooter = styled.div` export const ModalContent = styled.div` display: flex; + flex-flow: row wrap; grid-gap: 6px; ` diff --git a/src/modules/twap/state/twapOrderAtom.ts b/src/modules/twap/state/twapOrderAtom.ts index 0d117d8bd6..3158b0d4d6 100644 --- a/src/modules/twap/state/twapOrderAtom.ts +++ b/src/modules/twap/state/twapOrderAtom.ts @@ -19,22 +19,35 @@ export const twapTimeIntervalAtom = atom((get) => { return seconds / numberOfPartsValue }) +/** + * Get slippage adjusted buyAmount for TWAP orders + * + * Calculated independently as we don't need the user to be connected to know how much they would receive + */ +export const twapSlippageAdjustedBuyAmount = atom | null>((get) => { + const { outputCurrencyAmount } = get(advancedOrdersDerivedStateAtom) + + if (!outputCurrencyAmount) return null + + const slippage = get(twapOrderSlippageAtom) + + const slippageAmount = outputCurrencyAmount.multiply(slippage) + return outputCurrencyAmount.subtract(slippageAmount) as CurrencyAmount +}) + export const twapOrderAtom = atom((get) => { const appDataInfo = get(appDataInfoAtom) const { account } = get(walletInfoAtom) const { numberOfPartsValue } = get(twapOrdersSettingsAtom) const timeInterval = get(twapTimeIntervalAtom) - const { inputCurrencyAmount, outputCurrencyAmount, recipient } = get(advancedOrdersDerivedStateAtom) - const slippage = get(twapOrderSlippageAtom) + const { inputCurrencyAmount, recipient } = get(advancedOrdersDerivedStateAtom) + const buyAmount = get(twapSlippageAdjustedBuyAmount) - if (!inputCurrencyAmount || !outputCurrencyAmount || !account) return null - - const slippageAmount = outputCurrencyAmount.multiply(slippage) - const buyAmountWithSlippage = outputCurrencyAmount.subtract(slippageAmount) + if (!inputCurrencyAmount || !buyAmount || !account) return null return { sellAmount: inputCurrencyAmount as CurrencyAmount, - buyAmount: buyAmountWithSlippage as CurrencyAmount, + buyAmount, receiver: recipient || account, // TODO: check case with ENS name numOfParts: numberOfPartsValue, startTime: 0, // Will be set to a block timestamp value from CurrentBlockTimestampFactory