Swap polish 02 (#93)

* rework swap UI

* Add v2/v3 route toggle UX

* improve button and progress styles

* put optional route in swap header

* Further refinements

* Alt swap layout

* clean up

* Tweak route ui

* merge main

* update swap header

* adjust currency select ui
This commit is contained in:
Callil Capuozzo 2021-05-03 16:32:40 -04:00 committed by GitHub
parent d7785942b1
commit 4c2cb5b0c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 422 additions and 384 deletions

@ -1,6 +1,6 @@
import React from 'react'
import styled from 'styled-components'
import { darken, lighten } from 'polished'
import { darken } from 'polished'
import { RowBetween } from '../Row'
import { ChevronDown, Check } from 'react-feather'
@ -32,6 +32,15 @@ const Base = styled(RebassButton)<{
z-index: 1;
&:disabled {
cursor: auto;
pointer-events: none;
}
will-change: transform;
transition: transform 450ms ease;
transform: perspective(1px) translateZ(0);
&:hover {
transform: scale(0.99);
}
> * {
@ -59,14 +68,14 @@ export const ButtonPrimary = styled(Base)`
}
&:disabled {
background-color: ${({ theme, altDisabledStyle, disabled }) =>
altDisabledStyle ? (disabled ? theme.bg3 : theme.primary1) : theme.bg3};
color: ${({ theme, altDisabledStyle, disabled }) =>
altDisabledStyle ? (disabled ? theme.text3 : 'white') : theme.text3};
altDisabledStyle ? (disabled ? theme.primary1 : theme.primary1) : theme.primary1};
color: white;
cursor: auto;
box-shadow: none;
border: 1px solid transparent;
outline: none;
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.5' : '1')};
opacity: 0.4;
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.5' : '0.4')};
}
`
@ -270,12 +279,14 @@ export const ButtonWhite = styled(Base)`
`
const ButtonConfirmedStyle = styled(Base)`
background-color: ${({ theme }) => lighten(0.5, theme.green1)};
color: ${({ theme }) => theme.green1};
border: 1px solid ${({ theme }) => theme.green1};
background-color: ${({ theme }) => theme.bg3};
color: ${({ theme }) => theme.text1};
/* border: 1px solid ${({ theme }) => theme.green1}; */
&:disabled {
opacity: 50%;
/* opacity: 50%; */
background-color: ${({ theme }) => theme.bg2};
color: ${({ theme }) => theme.text2};
cursor: auto;
}
`

@ -9,6 +9,7 @@ import { useCurrencyBalance } from '../../state/wallet/hooks'
import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { ButtonGray } from '../Button'
import { RowBetween, RowFixed } from '../Row'
import { TYPE } from '../../theme'
import { Input as NumericalInput } from '../NumericalInput'
@ -52,24 +53,24 @@ const Container = styled.div<{ hideInput: boolean }>`
}
`
const CurrencySelect = styled.button<{ selected: boolean; hideInput?: boolean }>`
const CurrencySelect = styled(ButtonGray)<{ selected: boolean; hideInput?: boolean }>`
align-items: center;
font-size: 20px;
font-size: 24px;
font-weight: 500;
background-color: ${({ selected, theme }) => (selected ? theme.bg2 : theme.primary1)};
color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
border-radius: 12px;
/* box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')}; */
border-radius: 16px;
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
outline: none;
cursor: pointer;
user-select: none;
border: none;
height: ${({ hideInput }) => (hideInput ? '2.8rem' : '2.4rem')};
height: ${({ hideInput }) => (hideInput ? '2.4rem' : '2.4rem')};
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
padding: 0 8px;
justify-content: space-between;
margin-right: 12px;
:focus,
:hover {
background-color: ${({ selected, theme }) => (selected ? theme.bg3 : darken(0.05, theme.primary1))};
@ -79,7 +80,7 @@ const CurrencySelect = styled.button<{ selected: boolean; hideInput?: boolean }>
const InputRow = styled.div<{ selected: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
padding: ${({ selected }) => (selected ? '.75rem 0.75rem .75rem 1rem' : '.75rem 0.75rem .75rem 1rem')};
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 0.75rem 1rem')};
`
const LabelRow = styled.div`
@ -88,13 +89,17 @@ const LabelRow = styled.div`
color: ${({ theme }) => theme.text1};
font-size: 0.75rem;
line-height: 1rem;
padding: 0rem 1rem 0.75rem 1rem;
padding: 0 1rem 1rem;
span:hover {
cursor: pointer;
color: ${({ theme }) => darken(0.2, theme.text2)};
}
`
const FiatRow = styled(LabelRow)`
justify-content: flex-end;
`
const Aligner = styled.span`
display: flex;
align-items: center;
@ -102,7 +107,7 @@ const Aligner = styled.span`
`
const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
margin: 0 0.25rem 0 0.5rem;
margin: 0 0.25rem 0 0.35rem;
height: 35%;
path {
@ -113,27 +118,27 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
const StyledTokenName = styled.span<{ active?: boolean }>`
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
font-size: ${({ active }) => (active ? '20px' : '18px')};
font-size: ${({ active }) => (active ? '18px' : '18px')};
`
const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
background-color: ${({ theme }) => theme.primary5};
border: 1px solid ${({ theme }) => theme.primary5};
border-radius: 0.5rem;
background-color: transparent;
border: none;
border-radius: 12px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
/* margin-left: 0.5rem; */
padding: 0;
color: ${({ theme }) => theme.primary1};
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
margin-left: 0.25rem;
:hover {
border: 1px solid ${({ theme }) => theme.primary1};
/* border: 1px solid ${({ theme }) => theme.primary1}; */
}
:focus {
border: 1px solid ${({ theme }) => theme.primary1};
/* border: 1px solid ${({ theme }) => theme.primary1}; */
outline: none;
}
@ -166,7 +171,6 @@ export default function CurrencyInputPanel({
onUserInput,
onMax,
showMaxButton,
label = 'Input',
onCurrencySelect,
currency,
otherCurrency,
@ -210,19 +214,6 @@ export default function CurrencyInputPanel({
</FixedContainer>
)}
<Container hideInput={hideInput}>
{!hideInput && (
<LabelRow style={{ padding: ' 1rem 1rem 0rem 1rem' }}>
<RowBetween>
<TYPE.body color={theme.text3} fontWeight={500} fontSize={14}>
{label}
</TYPE.body>
<TYPE.label>
{fiatValueOfTypedAmount ? '~' : ''}${fiatValueOfTypedAmount?.toSignificant(4) ?? '-'}
</TYPE.label>
</RowBetween>
</LabelRow>
)}
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
<CurrencySelect
selected={!!currency}
@ -261,24 +252,27 @@ export default function CurrencyInputPanel({
</Aligner>
</CurrencySelect>
{!hideInput && (
<NumericalInput
className="token-amount-input"
value={value}
onUserInput={(val) => {
onUserInput(val)
}}
/>
<>
{' '}
<NumericalInput
className="token-amount-input"
value={value}
onUserInput={(val) => {
onUserInput(val)
}}
/>
</>
)}
</InputRow>
{!hideInput && !hideBalance && (
<LabelRow>
<FiatRow>
<RowBetween>
{account && (
<RowFixed>
<RowFixed style={{ height: '17px' }}>
<TYPE.body
onClick={onMax}
color={theme.text3}
fontWeight={500}
color={theme.text2}
fontWeight={400}
fontSize={14}
style={{ display: 'inline', cursor: 'pointer' }}
>
@ -287,17 +281,26 @@ export default function CurrencyInputPanel({
selectedCurrencyBalance?.toSignificant(4) +
' ' +
currency.symbol
: ' '}
: '-'}
</TYPE.body>
{showMaxButton && selectedCurrencyBalance ? (
<StyledBalanceMax disabled={!showMaxButton} onClick={onMax}>
(Max)
</StyledBalanceMax>
) : (
<StyledBalanceMax disabled={!showMaxButton} onClick={onMax}>
{''}
</StyledBalanceMax>
)}
</RowFixed>
)}
{showMaxButton && (
<StyledBalanceMax disabled={!showMaxButton} onClick={onMax}>
Max
</StyledBalanceMax>
)}
<TYPE.body fontSize={14} color={fiatValueOfTypedAmount ? theme.text2 : theme.text4}>
{fiatValueOfTypedAmount ? '~' : ''}$
{fiatValueOfTypedAmount ? Number(fiatValueOfTypedAmount?.toSignificant(6)).toLocaleString('en') : '-'}
</TYPE.body>
</RowBetween>
</LabelRow>
</FiatRow>
)}
</Container>
{onCurrencySelect && (

@ -1,4 +1,4 @@
import { ChainId, TokenAmount } from '@uniswap/sdk-core'
import { ChainId } from '@uniswap/sdk-core'
import React, { useState } from 'react'
import { Text } from 'rebass'
import { NavLink } from 'react-router-dom'
@ -9,11 +9,11 @@ import styled from 'styled-components'
import Logo from '../../assets/svg/logo.svg'
import LogoDark from '../../assets/svg/logo_white.svg'
import { useActiveWeb3React } from '../../hooks'
import { useDarkModeManager } from '../../state/user/hooks'
import { useETHBalances, useAggregateUniBalance } from '../../state/wallet/hooks'
import { useETHBalances } from '../../state/wallet/hooks'
import { CardNoise } from '../earn/styled'
import { CountUp } from 'use-count-up'
import { TYPE, ExternalLink } from '../../theme'
import { YellowCard } from '../Card'
@ -28,7 +28,6 @@ import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
import { Dots } from '../swap/styleds'
import Modal from '../Modal'
import UniBalanceContent from './UniBalanceContent'
import usePrevious from '../../hooks/usePrevious'
const HeaderFrame = styled.div`
display: grid;
@ -40,10 +39,10 @@ const HeaderFrame = styled.div`
width: 100%;
top: 0;
position: relative;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
/* border-bottom: 1px solid rgba(0, 0, 0, 0.1); */
padding: 0.5rem 1rem;
z-index: 21;
background-color: ${({ theme }) => theme.bg0};
background-color: ${({ theme }) => theme.bg1};
${({ theme }) => theme.mediaWidth.upToMedium`
display: flex;
@ -314,14 +313,9 @@ export default function Header() {
const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined)
const aggregateBalance: TokenAmount | undefined = useAggregateUniBalance()
const [showUniBalanceModal, setShowUniBalanceModal] = useState(false)
const showClaimPopup = useShowClaimPopup()
const countUpValue = aggregateBalance?.toFixed(0) ?? '0'
const countUpValuePrevious = usePrevious(countUpValue) ?? '0'
return (
<HeaderFrame>
<ClaimModal />
@ -376,33 +370,7 @@ export default function Header() {
<CardNoise />
</UNIWrapper>
)}
{/* I want to put this in the overflow menu now */}
{!availableClaim && aggregateBalance && (
<UNIWrapper onClick={() => setShowUniBalanceModal(true)}>
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
{account && (
<HideSmall>
<TYPE.white
style={{
paddingRight: '.4rem',
}}
>
<CountUp
key={countUpValue}
isCounting
start={parseFloat(countUpValuePrevious)}
end={parseFloat(countUpValue)}
thousandsSeparator={','}
duration={1}
/>
</TYPE.white>
</HideSmall>
)}
UNI
</UNIAmount>
<CardNoise />
</UNIWrapper>
)}
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
{account && userEthBalance ? (
<BalanceText style={{ flexShrink: 0 }} pl="0.75rem" pr="0.5rem" fontWeight={500}>

@ -21,7 +21,7 @@ export const Popup = styled.div`
display: inline-block;
width: 100%;
padding: 1em;
background-color: ${({ theme }) => theme.bg1};
background-color: ${({ theme }) => theme.bg0};
position: relative;
border-radius: 10px;
padding: 20px;

@ -33,7 +33,7 @@ const MobilePopupInner = styled.div`
const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>`
position: fixed;
top: ${({ extraPadding }) => (extraPadding ? '108px' : '88px')};
top: ${({ extraPadding }) => (extraPadding ? '72px' : '88px')};
right: 1rem;
max-width: 355px !important;
width: 100%;

@ -2,26 +2,26 @@ import React, { useContext } from 'react'
import styled from 'styled-components'
import { AutoColumn } from '../Column'
import { ThemeContext } from 'styled-components'
import { ArrowDown } from 'react-feather'
import { TYPE } from '../../theme'
const Wrapper = styled(AutoColumn)`
margin-left: 8px;
margin-right: 8px;
height: 100%;
`
const Grouping = styled(AutoColumn)`
width: fit-content;
padding: 4px;
background-color: ${({ theme }) => theme.bg2};
/* background-color: ${({ theme }) => theme.bg2}; */
border-radius: 16px;
`
const Circle = styled.div<{ confirmed?: boolean; disabled?: boolean }>`
width: 100%;
height: 100%;
width: 48px;
height: 48px;
background-color: ${({ theme, confirmed, disabled }) =>
disabled ? theme.bg3 : confirmed ? theme.green1 : theme.primary1};
border-radius: 12px;
border-radius: 50%;
color: ${({ theme, disabled }) => (disabled ? theme.text3 : theme.text1)};
display: flex;
align-items: center;
@ -37,12 +37,6 @@ const CircleRow = styled.div`
align-items: center;
`
const StyledArrowDown = styled(ArrowDown)`
margin: 0.5rem;
min-height: 14px;
/* color: ${({ theme }) => theme.text1}; */
`
interface ProgressCirclesProps {
steps: boolean[]
disabled?: boolean
@ -68,13 +62,13 @@ export default function ProgressCircles({ steps, disabled = false, ...rest }: Pr
return (
<CircleRow key={i}>
<Circle confirmed={step} disabled={disabled || (!steps[i - 1] && i !== 0)}>
{step ? '✓' : i + 1}
{step ? '✓' : i + 1 + '.'}
</Circle>
<StyledArrowDown size="16" color={theme.text3} />
<TYPE.main color={theme.text4}>|</TYPE.main>
</CircleRow>
)
})}
<Circle disabled={disabled || !steps[steps.length - 1]}>{steps.length + 1}</Circle>
<Circle disabled={disabled || !steps[steps.length - 1]}>{steps.length + 1 + '.'}</Circle>
</Grouping>
</Wrapper>
)

@ -21,8 +21,6 @@ import QuestionHelper from '../QuestionHelper'
import { RowBetween, RowFixed } from '../Row'
import Toggle from '../Toggle'
import TransactionSettings from '../TransactionSettings'
import { Moon, Sun } from 'react-feather'
import { useDarkModeManager } from '../../state/user/hooks'
const StyledMenuIcon = styled(Settings)`
height: 20px;
@ -86,6 +84,7 @@ const StyledMenu = styled.div`
const MenuFlyout = styled.span`
min-width: 20.125rem;
background-color: ${({ theme }) => theme.bg2};
border: 1px solid ${({ theme }) => theme.bg3};
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
0px 24px 32px rgba(0, 0, 0, 0.01);
border-radius: 12px;
@ -93,7 +92,7 @@ const MenuFlyout = styled.span`
flex-direction: column;
font-size: 1rem;
position: absolute;
top: 3rem;
top: 2rem;
right: 0rem;
z-index: 100;
@ -134,8 +133,6 @@ export default function SettingsTab() {
// show confirmation view before turning on
const [showConfirmation, setShowConfirmation] = useState(false)
const [darkMode, toggleDarkMode] = useDarkModeManager()
useOnClickOutside(node, open ? toggle : undefined)
return (
@ -245,10 +242,6 @@ export default function SettingsTab() {
}}
/>
</RowBetween>
{/* WIP */}
<StyledMenuButton onClick={() => toggleDarkMode()}>
{darkMode ? <Moon color={theme.text2} size={20} /> : <Sun size={20} />}
</StyledMenuButton>
</AutoColumn>
</MenuFlyout>
)}

@ -3,9 +3,9 @@ import styled from 'styled-components'
import Popover, { PopoverProps } from '../Popover'
const TooltipContainer = styled.div`
width: 228px;
width: 256px;
padding: 0.6rem 1rem;
line-height: 150%;
/* line-height: 150%; */
font-weight: 400;
`
@ -13,10 +13,18 @@ interface TooltipProps extends Omit<PopoverProps, 'content'> {
text: string
}
interface TooltipContentProps extends Omit<PopoverProps, 'content'> {
content: React.ReactNode
}
export default function Tooltip({ text, ...rest }: TooltipProps) {
return <Popover content={<TooltipContainer>{text}</TooltipContainer>} {...rest} />
}
export function TooltipContent({ content, ...rest }: TooltipContentProps) {
return <Popover content={<TooltipContainer>{content}</TooltipContainer>} {...rest} />
}
export function MouseoverTooltip({ children, ...rest }: Omit<TooltipProps, 'show'>) {
const [show, setShow] = useState(false)
const open = useCallback(() => setShow(true), [setShow])
@ -29,3 +37,16 @@ export function MouseoverTooltip({ children, ...rest }: Omit<TooltipProps, 'show
</Tooltip>
)
}
export function MouseoverTooltipContent({ content, children, ...rest }: Omit<TooltipContentProps, 'show'>) {
const [show, setShow] = useState(false)
const open = useCallback(() => setShow(true), [setShow])
const close = useCallback(() => setShow(false), [setShow])
return (
<TooltipContent {...rest} show={show} content={content}>
<div onMouseEnter={open} onMouseLeave={close}>
{children}
</div>
</TooltipContent>
)
}

@ -20,7 +20,7 @@ const Wrapper = styled.div`
padding: 1rem;
`
const Section = styled(AutoColumn)<{ inline?: boolean }>`
padding: ${({ inline }) => (inline ? '0' : '24px')};
padding: ${({ inline }) => (inline ? '0' : '0')};
`
const BottomSection = styled(Section)`

@ -1,14 +1,10 @@
import { TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import React, { useContext } from 'react'
import { ThemeContext } from 'styled-components'
import { Field } from '../../state/swap/actions'
import { useUserSlippageTolerance } from '../../state/user/hooks'
import { TYPE } from '../../theme'
import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown } from '../../utils/prices'
import { computeTradePriceBreakdown } from '../../utils/prices'
import { AutoColumn } from '../Column'
import QuestionHelper from '../QuestionHelper'
import { RowBetween, RowFixed } from '../Row'
import FormattedPriceImpact from './FormattedPriceImpact'
import SwapRoute from './SwapRoute'
@ -16,74 +12,28 @@ import SwapRoute from './SwapRoute'
function TradeSummary({ trade }: { trade: V2Trade | V3Trade }) {
const theme = useContext(ThemeContext)
const { priceImpactWithoutFee, realizedLPFee } = computeTradePriceBreakdown(trade)
const isExactIn = trade.tradeType === TradeType.EXACT_INPUT
const [allowedSlippage] = useUserSlippageTolerance()
const slippageAdjustedAmounts = computeSlippageAdjustedAmounts(trade, allowedSlippage)
const price = trade.executionPrice
return (
<>
<AutoColumn style={{ padding: '8px 16px' }} gap="8px">
<AutoColumn gap={'8px'}>
<RowBetween>
<RowFixed>
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
Price
</TYPE.black>
</RowFixed>
<TYPE.black color={theme.text1} fontSize={14}>
{'1 ' +
price?.quoteCurrency?.symbol +
' = ' +
price?.invert()?.toSignificant(6) +
' ' +
price?.baseCurrency?.symbol}
</TYPE.black>
</RowBetween>
<RowBetween>
<RowFixed>
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
Inverted Price
</TYPE.black>
</RowFixed>
<TYPE.black color={theme.text1} fontSize={14}>
{'1 ' + price?.baseCurrency?.symbol + ' = ' + price?.toSignificant(6) + ' ' + price?.quoteCurrency?.symbol}
</TYPE.black>
</RowBetween>
<RowBetween>
<RowFixed>
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
{isExactIn ? 'Minimum received' : 'Maximum sold'}
</TYPE.black>
<QuestionHelper text="Your transaction will revert if there is a large, unfavorable price movement before it is confirmed." />
</RowFixed>
<RowFixed>
<TYPE.black color={theme.text1} fontSize={14}>
{isExactIn
? `${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(4)} ${trade.outputAmount.currency.symbol}` ??
'-'
: `${slippageAdjustedAmounts[Field.INPUT]?.toSignificant(4)} ${trade.inputAmount.currency.symbol}` ??
'-'}
</TYPE.black>
</RowFixed>
</RowBetween>
<RowBetween>
<RowFixed>
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
<TYPE.black fontSize={12} fontWeight={400} color={theme.text2}>
Price Impact
</TYPE.black>
<QuestionHelper text="The difference between the market price and estimated price due to trade size." />
{/* <QuestionHelper text="The difference between the market price and estimated price due to trade size." /> */}
</RowFixed>
<FormattedPriceImpact priceImpact={priceImpactWithoutFee} />
</RowBetween>
<RowBetween>
<RowFixed>
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
<TYPE.black fontSize={12} fontWeight={400} color={theme.text2}>
Liquidity Provider Fee
</TYPE.black>
<QuestionHelper text="A portion of each trade goes to liquidity providers as a protocol incentive." />
{/* <QuestionHelper text="A portion of each trade goes to liquidity providers as a protocol incentive." /> */}
</RowFixed>
<TYPE.black fontSize={14} color={theme.text1}>
<TYPE.black fontSize={12} color={theme.text1}>
{realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${trade.inputAmount.currency.symbol}` : '-'}
</TYPE.black>
</RowBetween>
@ -116,7 +66,7 @@ export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) {
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
Route
</TYPE.black>
<QuestionHelper text="Routing through these tokens resulted in the best price for your trade." />
{/* <QuestionHelper text="Routing through these tokens resulted in the best price for your trade." /> */}
</span>
<SwapRoute trade={trade} />
</RowBetween>

@ -1,28 +1,14 @@
import { stringify } from 'qs'
import React, { useContext, useMemo } from 'react'
import React, { useMemo } from 'react'
import { useLocation } from 'react-router'
import { Text } from 'rebass'
import { ThemeContext } from 'styled-components'
import { Link } from 'react-router-dom'
import useParsedQueryString from '../../hooks/useParsedQueryString'
import useToggledVersion, { DEFAULT_VERSION, Version } from '../../hooks/useToggledVersion'
import { TYPE } from '../../theme'
import { ButtonPrimary } from '../Button'
import { StyledInternalLink } from '../../theme'
import { YellowCard } from '../Card'
import { AutoColumn } from '../Column'
function VersionLinkContainer({ children }: { children: React.ReactNode }) {
const theme = useContext(ThemeContext)
return (
<YellowCard style={{ marginTop: '4px', padding: '0.5rem 0.5rem' }}>
<AutoColumn gap="sm" justify="center" style={{ alignItems: 'center', textAlign: 'center' }}>
<Text lineHeight="145.23%;" fontSize={14} fontWeight={400} color={theme.text1}>
{children}
</Text>
</AutoColumn>
</YellowCard>
)
}
import { Zap } from 'react-feather'
export default function BetterTradeLink({
version,
@ -45,12 +31,24 @@ export default function BetterTradeLink({
}, [location, search, version])
return (
<VersionLinkContainer>
{otherTradeNonexistent ? 'This trade can be executed on ' : 'There is a better price for this trade on '}
<StyledInternalLink to={linkDestination}>
<b>Uniswap {version.toUpperCase()} </b>
</StyledInternalLink>
</VersionLinkContainer>
<ButtonPrimary
as={Link}
to={linkDestination}
style={{
width: 'fit-content',
padding: '.2rem .5rem',
wordBreak: 'keep-all',
height: '24px',
marginLeft: '.25rem',
}}
>
<Zap size={12} style={{ marginRight: '0.25rem' }} />
<TYPE.small style={{ lineHeight: '120%' }} fontSize={12}>
{otherTradeNonexistent
? `No liquidity! Click to trade with ${version.toUpperCase()}`
: `Get a better price on ${version.toUpperCase()}`}
</TYPE.small>
</ButtonPrimary>
)
}
@ -70,11 +68,12 @@ export function DefaultVersionLink() {
}, [location, search])
return (
<VersionLinkContainer>
Showing {version.toUpperCase()} price.{' '}
<StyledInternalLink to={linkDestination}>
<b>Switch to Uniswap {DEFAULT_VERSION.toUpperCase()} </b>
</StyledInternalLink>
</VersionLinkContainer>
<ButtonPrimary
as={Link}
to={linkDestination}
style={{ width: 'fit-content', marginTop: '4px', padding: '0.5rem 0.5rem' }}
>
Showing {version.toUpperCase()} price. <b>Switch to Uniswap {DEFAULT_VERSION.toUpperCase()} </b>
</ButtonPrimary>
)
}

@ -81,10 +81,9 @@ export default function ConfirmSwapModal({
trade={trade}
disabledConfirm={showAcceptChanges}
swapErrorMessage={swapErrorMessage}
allowedSlippage={allowedSlippage}
/>
) : null
}, [allowedSlippage, onConfirm, showAcceptChanges, swapErrorMessage, trade])
}, [onConfirm, showAcceptChanges, swapErrorMessage, trade])
// text to show while loading
const pendingText = `Swapping ${trade?.maximumAmountIn(allowedSlippage)?.toSignificant(6)} ${

@ -9,7 +9,7 @@ import { ErrorText, ErrorPill } from './styleds'
*/
export default function FormattedPriceImpact({ priceImpact }: { priceImpact?: Percent }) {
return (
<ErrorText fontWeight={500} fontSize={14} severity={warningSeverity(priceImpact)}>
<ErrorText fontWeight={500} fontSize={12} severity={warningSeverity(priceImpact)}>
{priceImpact
? priceImpact.lessThan(ONE_BIPS)
? `-${priceImpact.toFixed(2)}%`
@ -21,7 +21,7 @@ export default function FormattedPriceImpact({ priceImpact }: { priceImpact?: Pe
export function SmallFormattedPriceImpact({ priceImpact }: { priceImpact?: Percent }) {
return (
<ErrorPill fontWeight={500} fontSize={14} severity={warningSeverity(priceImpact)}>
<ErrorPill fontWeight={500} fontSize={12} severity={warningSeverity(priceImpact)}>
{priceImpact
? priceImpact.lessThan(ONE_BIPS)
? `(-${priceImpact.toFixed(2)}%)`

@ -1,51 +1,28 @@
import React from 'react'
import styled from 'styled-components'
import { Version } from '../../hooks/useToggledVersion'
import Settings from '../Settings'
import { RowBetween, RowFixed } from '../Row'
import { TYPE } from '../../theme'
// import { Info } from 'react-feather'
const StyledSwapHeader = styled.div`
padding: 1rem 1.25rem 0.5rem 1.25rem;
width: 100%;
color: ${({ theme }) => theme.text2};
`
//
// const InfoLink = styled(ExternalLink)`
// width: 100%;
// text-align: center;
// font-size: 14px;
// height: 20px;
// margin-right: 8px;
// color: ${({ theme }) => theme.text1};
// `
interface SwapHeaderProps {
toggledVersion: Version
}
export default function SwapHeader({ toggledVersion }: SwapHeaderProps) {
export default function SwapHeader() {
return (
<StyledSwapHeader>
<RowBetween>
<TYPE.black fontWeight={500} fontSize={16} style={{ opacity: '0.6' }}>
Swap ({toggledVersion})
</TYPE.black>
<RowFixed>
{/* Send icon appears here when expert mode is toggled on */}
{/* <Send style={{ marginRight: '16px' }} size="20" onClick={() => onChangeRecipient('')} /> */}
{/* This info icon should open info.uniswap.org with the pair */}
{/*{trade && (*/}
{/* <InfoLink*/}
{/* href={'https://info.uniswap.org/pair/' + trade.route.pairs[0].liquidityToken.address}*/}
{/* target="_blank"*/}
{/* >*/}
{/* <Info size="20" style={{ opacity: '0.6' }} />*/}
{/* </InfoLink>*/}
{/*)}*/}
<TYPE.black fontWeight={500} fontSize={16} style={{ marginRight: '8px' }}>
Swap{' '}
</TYPE.black>
</RowFixed>
<RowFixed>
{/* <TradeInfo disabled={!trade} trade={trade} /> */}
{/* <div style={{ width: '8px' }}></div> */}
<Settings />
</RowFixed>
</RowBetween>

@ -1,61 +1,30 @@
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { Percent } from '@uniswap/sdk-core'
import React, { useContext, useMemo, useState } from 'react'
import { Repeat } from 'react-feather'
import React, { useMemo } from 'react'
import { Text } from 'rebass'
import { ThemeContext } from 'styled-components'
import { computeTradePriceBreakdown, formatExecutionPrice, warningSeverity } from '../../utils/prices'
import { computeTradePriceBreakdown, warningSeverity } from '../../utils/prices'
import { ButtonError } from '../Button'
import { AutoColumn } from '../Column'
import { AutoRow, RowBetween } from '../Row'
import { StyledBalanceMaxMini, SwapCallbackError } from './styleds'
import {} from '../Column'
import { AutoRow } from '../Row'
import { SwapCallbackError } from './styleds'
export default function SwapModalFooter({
trade,
onConfirm,
allowedSlippage,
swapErrorMessage,
disabledConfirm,
}: {
trade: V2Trade | V3Trade
allowedSlippage: Percent
onConfirm: () => void
swapErrorMessage: string | undefined
disabledConfirm: boolean
}) {
const [showInverted, setShowInverted] = useState<boolean>(false)
const theme = useContext(ThemeContext)
const { priceImpactWithoutFee } = useMemo(() => computeTradePriceBreakdown(trade), [trade])
const severity = warningSeverity(priceImpactWithoutFee)
return (
<>
<AutoColumn gap="0px">
<RowBetween align="center">
<Text fontWeight={400} fontSize={14} color={theme.text2}>
Price
</Text>
<Text
fontWeight={500}
fontSize={14}
color={theme.text1}
style={{
justifyContent: 'center',
alignItems: 'center',
display: 'flex',
textAlign: 'right',
paddingLeft: '10px',
}}
>
{formatExecutionPrice(trade, showInverted, allowedSlippage)}
<StyledBalanceMaxMini onClick={() => setShowInverted(!showInverted)}>
<Repeat size={14} />
</StyledBalanceMaxMini>
</Text>
</RowBetween>
</AutoColumn>
<AutoRow>
<ButtonError
onClick={onConfirm}

@ -1,10 +1,10 @@
import { Percent, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import React, { useContext } from 'react'
import React, { useContext, useState } from 'react'
import { ArrowDown, AlertTriangle } from 'react-feather'
import { Text } from 'rebass'
import { ThemeContext } from 'styled-components'
import styled, { ThemeContext } from 'styled-components'
import { TYPE } from '../../theme'
import { ButtonPrimary } from '../Button'
import { isAddress, shortenAddress } from '../../utils'
@ -13,6 +13,30 @@ import CurrencyLogo from '../CurrencyLogo'
import { RowBetween, RowFixed } from '../Row'
import { TruncatedText, SwapShowAcceptChanges } from './styleds'
import { AdvancedSwapDetails } from './AdvancedSwapDetails'
import { LightCard } from '../Card'
import { DarkGreyCard } from '../Card'
import TradePrice from '../swap/TradePrice'
export const ArrowWrapper = styled.div`
padding: 4px;
border-radius: 12px;
height: 32px;
width: 32px;
position: relative;
margin-top: -18px;
margin-bottom: -18px;
left: calc(50% - 16px);
/* transform: rotate(90deg); */
display: flex;
justify-content: center;
align-items: center;
background-color: ${({ theme }) => theme.bg1};
/* border: 4px solid ${({ theme }) => theme.bg0}; */
z-index: 2;
`
export default function SwapModalHeader({
trade,
allowedSlippage,
@ -31,41 +55,82 @@ export default function SwapModalHeader({
const theme = useContext(ThemeContext)
const [showInverted, setShowInverted] = useState<boolean>(false)
return (
<AutoColumn gap={'md'} style={{ marginTop: '20px' }}>
<RowBetween align="flex-end">
<RowFixed gap={'0px'}>
<CurrencyLogo currency={trade.inputAmount.currency} size={'24px'} style={{ marginRight: '12px' }} />
<TruncatedText
fontSize={24}
fontWeight={500}
color={showAcceptChanges && trade.tradeType === TradeType.EXACT_OUTPUT ? theme.primary1 : ''}
>
{maximumAmountIn.toSignificant(6)}
</TruncatedText>
</RowFixed>
<RowFixed gap={'0px'}>
<Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
{trade.inputAmount.currency.symbol}
</Text>
</RowFixed>
</RowBetween>
<RowFixed>
<ArrowDown size="16" color={theme.text2} style={{ marginLeft: '4px', minWidth: '16px' }} />
</RowFixed>
<RowBetween align="flex-end">
<RowFixed gap={'0px'}>
<CurrencyLogo currency={trade.outputAmount.currency} size={'24px'} style={{ marginRight: '12px' }} />
<TruncatedText fontSize={24} fontWeight={500}>
{minimumAmountOut.toSignificant(6)}
</TruncatedText>
</RowFixed>
<RowFixed gap={'0px'}>
<Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
{trade.outputAmount.currency.symbol}
</Text>
</RowFixed>
<AutoColumn gap={'4px'} style={{ marginTop: '1rem' }}>
<DarkGreyCard padding="0.75rem 1rem">
<AutoColumn gap={'8px'}>
<RowBetween>
<TYPE.body color={theme.text3} fontWeight={500} fontSize={14}>
{'From'}
</TYPE.body>
<TYPE.body fontSize={14} color={theme.text3}>
{'$-'}
</TYPE.body>
</RowBetween>
<RowBetween align="center">
<RowFixed gap={'0px'}>
<CurrencyLogo currency={trade.inputAmount.currency} size={'20px'} style={{ marginRight: '12px' }} />
<Text fontSize={20} fontWeight={500}>
{trade.inputAmount.currency.symbol}
</Text>
</RowFixed>
<RowFixed gap={'0px'}>
<TruncatedText
fontSize={24}
fontWeight={500}
color={showAcceptChanges && trade.tradeType === TradeType.EXACT_OUTPUT ? theme.primary1 : ''}
>
{maximumAmountIn.toSignificant(6)}
</TruncatedText>
</RowFixed>
</RowBetween>
</AutoColumn>
</DarkGreyCard>
<ArrowWrapper>
<ArrowDown size="16" color={theme.text2} />
</ArrowWrapper>
<DarkGreyCard padding="0.75rem 1rem" style={{ marginBottom: '0.25rem' }}>
<AutoColumn gap={'8px'}>
<RowBetween>
<TYPE.body color={theme.text3} fontWeight={500} fontSize={14}>
{'To'}
</TYPE.body>
<TYPE.body fontSize={14} color={theme.text3}>
{'$-'}
</TYPE.body>
</RowBetween>
<RowBetween align="flex-end">
<RowFixed gap={'0px'}>
<CurrencyLogo currency={trade.outputAmount.currency} size={'20px'} style={{ marginRight: '12px' }} />
<Text fontSize={20} fontWeight={500}>
{trade.outputAmount.currency.symbol}
</Text>
</RowFixed>
<RowFixed gap={'0px'}>
<TruncatedText fontSize={24} fontWeight={500}>
{minimumAmountOut.toSignificant(6)}
</TruncatedText>
</RowFixed>
</RowBetween>
</AutoColumn>
</DarkGreyCard>
<RowBetween style={{ marginTop: '0.25rem', padding: '0 1rem' }}>
<TYPE.body color={theme.text2} fontWeight={500} fontSize={14}>
{'Price:'}
</TYPE.body>
<TradePrice
price={trade.worstExecutionPrice(allowedSlippage)}
showInverted={showInverted}
setShowInverted={setShowInverted}
/>
</RowBetween>
<LightCard style={{ padding: '.75rem', marginTop: '0.5rem' }}>
<AdvancedSwapDetails trade={trade} />
</LightCard>
{showAcceptChanges ? (
<SwapShowAcceptChanges justify="flex-start" gap={'0px'}>
<RowBetween>
@ -82,9 +147,10 @@ export default function SwapModalHeader({
</RowBetween>
</SwapShowAcceptChanges>
) : null}
<AutoColumn justify="flex-start" gap="sm" style={{ padding: '12px 0 0 0px' }}>
{/* <AutoColumn justify="flex-start" gap="sm" style={{ padding: '.75rem 1rem' }}>
{trade.tradeType === TradeType.EXACT_INPUT ? (
<TYPE.italic textAlign="left" style={{ width: '100%' }}>
<TYPE.italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
{`Output is estimated. You will receive at least `}
<b>
{minimumAmountOut.toSignificant(6)} {trade.outputAmount.currency.symbol}
@ -92,7 +158,7 @@ export default function SwapModalHeader({
{' or the transaction will revert.'}
</TYPE.italic>
) : (
<TYPE.italic textAlign="left" style={{ width: '100%' }}>
<TYPE.italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
{`Input is estimated. You will sell at most `}
<b>
{maximumAmountIn.toSignificant(6)} {trade.inputAmount.currency.symbol}
@ -100,7 +166,7 @@ export default function SwapModalHeader({
{' or the transaction will revert.'}
</TYPE.italic>
)}
</AutoColumn>
</AutoColumn> */}
{recipient !== null ? (
<AutoColumn justify="flex-start" gap="sm" style={{ padding: '12px 0 0 0px' }}>
<TYPE.main>

@ -4,8 +4,6 @@ import { useContext } from 'react'
import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components'
import { StyledBalanceMaxMini } from './styleds'
import Switch from '../../assets/svg/switch.svg'
interface TradePriceProps {
price: Price
@ -13,11 +11,19 @@ interface TradePriceProps {
setShowInverted: (showInverted: boolean) => void
}
const StyledPriceContainer = styled.div`
justify-content: flex-end;
const StyledPriceContainer = styled.button`
justify-content: center;
align-items: center;
display: flex;
width: 100%;
width: fit-content;
padding: 0;
font-size: 0.875rem;
font-weight: 400;
background-color: transparent;
border: none;
margin-left: 1rem;
height: 24px;
cursor: pointer;
`
export default function TradePrice({ price, showInverted, setShowInverted }: TradePriceProps) {
@ -25,7 +31,7 @@ export default function TradePrice({ price, showInverted, setShowInverted }: Tra
let formattedPrice: string
try {
formattedPrice = showInverted ? price.toSignificant(6) : price.invert()?.toSignificant(6)
formattedPrice = showInverted ? price.toSignificant(4) : price.invert()?.toSignificant(4)
} catch (error) {
formattedPrice = '0'
}
@ -35,14 +41,11 @@ export default function TradePrice({ price, showInverted, setShowInverted }: Tra
const flipPrice = useCallback(() => setShowInverted(!showInverted), [setShowInverted, showInverted])
return (
<StyledPriceContainer>
<StyledPriceContainer onClick={flipPrice}>
<div style={{ alignItems: 'center', display: 'flex', width: 'fit-content' }}>
<Text fontWeight={500} fontSize={14} color={theme.text2}>
<Text fontWeight={500} fontSize={14} color={theme.text1}>
{'1 ' + labelInverted + ' = ' + formattedPrice ?? '-'} {label}
</Text>
<StyledBalanceMaxMini style={{ marginLeft: '0.5rem' }} onClick={flipPrice}>
<img width={'16px'} src={Switch} alt="logo" />
</StyledBalanceMaxMini>
</div>
</StyledPriceContainer>
)

@ -1,5 +1,7 @@
import { transparentize } from 'polished'
import React from 'react'
import { Link } from 'react-router-dom'
import { AlertTriangle } from 'react-feather'
import styled, { css } from 'styled-components'
import { Text } from 'rebass'
@ -84,10 +86,11 @@ export const StyledBalanceMaxMini = styled.button`
background-color: ${({ theme }) => theme.bg1};
border: none;
border-radius: 8px;
padding: 0.25rem 0.35rem;
padding: 0;
font-size: 0.875rem;
font-weight: 400;
opacity: 0.6;
margin-right: 0.5rem;
cursor: pointer;
color: ${({ theme }) => theme.text1};
display: flex;
@ -106,8 +109,9 @@ export const StyledBalanceMaxMini = styled.button`
export const TruncatedText = styled(Text)`
text-overflow: ellipsis;
width: 220px;
max-width: 220px;
overflow: hidden;
text-align: right;
`
// styles
@ -184,3 +188,14 @@ export const Separator = styled.div`
height: 1px;
background-color: ${({ theme }) => theme.bg2};
`
export const V2TradeAlertWrapper = styled(Link)`
background-color: ${({ theme }) => theme.bg2};
display: flex;
align-items: center;
border-radius: 12px;
height: 22px;
margin-right: 0.5rem;
padding: 0 0.25rem 0 0.5rem;
text-decoration: none !important;
`

@ -1,28 +1,28 @@
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { AdvancedSwapDetails } from 'components/swap/AdvancedSwapDetails'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import { MouseoverTooltip, MouseoverTooltipContent } from 'components/Tooltip'
import JSBI from 'jsbi'
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'
import { ArrowDown, Repeat, Unlock } from 'react-feather'
import { ArrowDown, HelpCircle, CheckCircle, Info, X } from 'react-feather'
import ReactGA from 'react-ga'
import { RouteComponentProps } from 'react-router-dom'
import { Text } from 'rebass'
import { ThemeContext } from 'styled-components'
import AddressInputPanel from '../../components/AddressInputPanel'
import { ButtonConfirmed, ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button'
import { ButtonConfirmed, ButtonError, ButtonLight, ButtonPrimary, ButtonGray } from '../../components/Button'
import { GreyCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column'
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import CurrencyLogo from '../../components/CurrencyLogo'
import Loader from '../../components/Loader'
import ProgressSteps from '../../components/ProgressSteps'
import { AutoRow } from '../../components/Row'
import BetterTradeLink from '../../components/swap/BetterTradeLink'
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
import confirmPriceImpactWithoutFee from '../../components/swap/confirmPriceImpactWithoutFee'
import ConfirmSwapModal from '../../components/swap/ConfirmSwapModal'
import { ArrowWrapper, BottomGrouping, Dots, SwapCallbackError, Wrapper } from '../../components/swap/styleds'
import SwapHeader from '../../components/swap/SwapHeader'
import TradePrice from '../../components/swap/TradePrice'
import TokenWarningModal from '../../components/TokenWarningModal'
import { useActiveWeb3React } from '../../hooks'
@ -46,11 +46,15 @@ import {
import { useExpertModeManager, useUserSingleHopOnly, useUserSlippageTolerance } from '../../state/user/hooks'
import { LinkStyledButton, TYPE } from '../../theme'
import { getTradeVersion } from '../../utils/getTradeVersion'
import { isTradeBetter } from '../../utils/isTradeBetter'
import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { computeTradePriceBreakdown, warningSeverity } from '../../utils/prices'
import AppBody from '../AppBody'
import { Link } from 'react-router-dom'
import { isTradeBetter } from '../../utils/isTradeBetter'
import BetterTradeLink from '../../components/swap/BetterTradeLink'
import SwapHeader from '../../components/swap/SwapHeader'
export default function Swap({ history }: RouteComponentProps) {
const loadedUrlParams = useDefaultsFromURLSearch()
@ -321,7 +325,7 @@ export default function Swap({ history }: RouteComponentProps) {
onDismiss={handleDismissTokenWarning}
/>
<AppBody>
<SwapHeader toggledVersion={toggledVersion} />
<SwapHeader />
<Wrapper id="swap-page">
<ConfirmSwapModal
isOpen={showConfirm}
@ -353,14 +357,13 @@ export default function Swap({ history }: RouteComponentProps) {
id="swap-currency-input"
/>
<ArrowWrapper clickable>
<Repeat
<ArrowDown
size="16"
onClick={() => {
setApprovalSubmitted(false) // reset 2 step UI for approvals
onSwitchTokens()
}}
color={currencies[Field.INPUT] && currencies[Field.OUTPUT] ? theme.text1 : theme.text3}
style={{ transform: 'rotate(90deg)' }}
/>
</ArrowWrapper>
<CurrencyInputPanel
@ -368,7 +371,7 @@ export default function Swap({ history }: RouteComponentProps) {
onUserInput={handleTypeOutput}
label={independentField === Field.INPUT && !showWrap ? 'To (at least)' : 'To'}
showMaxButton={false}
hideBalance={true}
hideBalance={false}
showFiatValue
currency={currencies[Field.OUTPUT]}
onCurrencySelect={handleOutputSelect}
@ -391,20 +394,84 @@ export default function Swap({ history }: RouteComponentProps) {
<AddressInputPanel id="recipient" value={recipient} onChange={onChangeRecipient} />
</>
) : null}
{trade ? (
<TradePrice
price={trade.worstExecutionPrice(allowedSlippage)}
showInverted={showInverted}
setShowInverted={setShowInverted}
/>
) : null}
{[V3TradeState.VALID, V3TradeState.SYNCING, V3TradeState.NO_ROUTE_FOUND].includes(v3TradeState) ? (
toggledVersion === Version.v3 && isTradeBetter(v3Trade, v2Trade) ? (
<BetterTradeLink version={Version.v2} otherTradeNonexistent={!v3Trade} />
) : toggledVersion === Version.v2 && isTradeBetter(v2Trade, v3Trade) ? (
<BetterTradeLink version={Version.v3} otherTradeNonexistent={!v2Trade} />
) : null
) : null}
<RowBetween style={{ justifyContent: !trade ? 'center' : 'space-between' }}>
<RowFixed>
{[V3TradeState.VALID, V3TradeState.SYNCING, V3TradeState.NO_ROUTE_FOUND].includes(v3TradeState) &&
(toggledVersion === Version.v3 && isTradeBetter(v3Trade, v2Trade) ? (
<BetterTradeLink version={Version.v2} otherTradeNonexistent={!v3Trade} />
) : toggledVersion === Version.v2 && isTradeBetter(v2Trade, v3Trade) ? (
<BetterTradeLink version={Version.v3} otherTradeNonexistent={!v2Trade} />
) : (
toggledVersion === Version.v2 && (
<ButtonGray
width="fit-content"
padding="0.1rem 0.5rem 0.1rem 0.35rem"
as={Link}
to="/swap"
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: '24px',
opacity: 0.8,
lineHeight: '120%',
marginLeft: '0.25rem',
}}
>
<X color={theme.text3} size={12} /> &nbsp;
<TYPE.main style={{ lineHeight: '120%' }} fontSize={12}>
Routed via V2
</TYPE.main>
</ButtonGray>
)
))}
{toggledVersion === Version.v3 && trade && isTradeBetter(v2Trade, v3Trade) && (
<ButtonGray
width="fit-content"
padding="0.1rem 0.5rem"
disabled
style={{
display: 'flex',
justifyContent: 'space-between',
height: '24px',
opacity: 0.4,
marginLeft: '0.25rem',
}}
>
<TYPE.black fontSize={12}>V3</TYPE.black>
</ButtonGray>
)}
</RowFixed>
<RowFixed>
{trade ? (
<TradePrice
price={trade.worstExecutionPrice(allowedSlippage)}
showInverted={showInverted}
setShowInverted={setShowInverted}
/>
) : (
<TYPE.main></TYPE.main>
)}
{trade && (
<MouseoverTooltipContent content={<AdvancedSwapDetails trade={trade} />}>
<Info
size={16}
style={{
display: 'flex',
justifyContent: 'space-between',
height: '24px',
opacity: 0.4,
margin: '0 .75rem 0 .5rem',
}}
color={theme.text1}
/>
</MouseoverTooltipContent>
)}
</RowFixed>
</RowBetween>
<BottomGrouping>
{swapIsUnsupported ? (
<ButtonPrimary disabled={true}>
@ -447,19 +514,29 @@ export default function Swap({ history }: RouteComponentProps) {
<span style={{ display: 'flex', alignItems: 'center' }}>
<CurrencyLogo
currency={currencies[Field.INPUT]}
size={'16px'}
size={'20px'}
style={{ marginRight: '8px' }}
/>
{/* we need to shorten this string on mobile */}
{'Allow Uniswap to spend your ' + currencies[Field.INPUT]?.symbol}
{approvalState === ApprovalState.APPROVED || signatureState === UseERC20PermitState.SIGNED
? currencies[Field.INPUT]?.symbol + ' unlocked for trading.'
: 'Allow Uniswap to spend your ' + currencies[Field.INPUT]?.symbol}
</span>
{approvalState === ApprovalState.PENDING ? (
<Loader stroke="white" />
) : (approvalSubmitted && approvalState === ApprovalState.APPROVED) ||
signatureState === UseERC20PermitState.SIGNED ? (
<Unlock size="16" stroke="white" />
<CheckCircle size="20" color={theme.green1} />
) : (
<Unlock size="16" stroke="white" />
<MouseoverTooltip
text={
'You cannot swap until you give Uniswap permission to spend your ' +
currencies[Field.INPUT]?.symbol +
'. You only have to do this once per token and only when you are selling a token for the first time.'
}
>
<HelpCircle size="20" color={'white'} />
</MouseoverTooltip>
)}
</AutoRow>
</ButtonConfirmed>
@ -491,13 +568,6 @@ export default function Swap({ history }: RouteComponentProps) {
</Text>
</ButtonError>
</AutoColumn>
{showApproveFlow && (
<ProgressSteps
steps={[
approvalState === ApprovalState.APPROVED || signatureState === UseERC20PermitState.SIGNED,
]}
/>
)}
</AutoRow>
) : (
<ButtonError

@ -28,7 +28,7 @@ export default createReducer(initialState, (builder) =>
.addCase(setOpenModal, (state, action) => {
state.openModal = action.payload
})
.addCase(addPopup, (state, { payload: { content, key, removeAfterMs = 15000 } }) => {
.addCase(addPopup, (state, { payload: { content, key, removeAfterMs = 25000 } }) => {
state.popupList = (key ? state.popupList.filter((popup) => popup.key !== key) : state.popupList).concat([
{
key: key || nanoid(),

@ -47,9 +47,9 @@ export function colors(darkMode: boolean): Colors {
text5: darkMode ? '#2C2F36' : '#EDEEF2',
// backgrounds / greys
bg0: darkMode ? '#191B1F' : '#FFFFFF',
bg1: darkMode ? '#212429' : '#F7F8FA',
bg2: darkMode ? '#2C2F36' : '#EDEEF2',
bg0: darkMode ? '#191B1F' : '#F7F8FA',
bg1: darkMode ? '#212429' : '#EDEEF2',
bg2: darkMode ? '#2C2F36' : '#F0F0F0',
bg3: darkMode ? '#40444F' : '#CED0D9',
bg4: darkMode ? '#565A69' : '#888D9B',
bg5: darkMode ? '#6C7284' : '#888D9B',
@ -221,7 +221,7 @@ html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
font-feature-settings: 'ss01' on, 'ss02' on, 'cv01' on, 'cv03' on;
font-feature-settings: 'ss01' on, 'cv01' on, 'cv03' on;
}
`