Skip to content

Commit bafd3f3

Browse files
authored
feat(v1-support): Enable executing swaps on v1 or v2 (#883)
* swap-v1 * toggle the version switch based on the search query parameter * rework some of the query parameter stuff in send/swap * hide the url when they click it * allow switching back to v2 via the toggle * represent the v1 trade in the UI if they toggle it on * show trade link in both directions (5% threshold for v1 link) * input amounts should reflect v1/v2 * perform the approve on v1 exchange for v1 trades * get swap on v1 working * move some code around to reduce duplication * fix ts error * correct input allowance * fix exact token to token on v1 * fix pending approvals to be specific to the spender * google analytics for swap version * disable the version switch on pages other than swap and send
1 parent 29db0a5 commit bafd3f3

24 files changed

+656
-383
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { stringify } from 'qs'
2+
import React, { useCallback, useMemo } from 'react'
3+
import { Link, useLocation } from 'react-router-dom'
4+
import styled from 'styled-components'
5+
import useParsedQueryString from '../../hooks/useParsedQueryString'
6+
import useToggledVersion, { Version } from '../../hooks/useToggledVersion'
7+
8+
const VersionLabel = styled.span<{ enabled: boolean }>`
9+
padding: ${({ enabled }) => (enabled ? '0.15rem 0.5rem 0.16rem 0.45rem' : '0.15rem 0.5rem 0.16rem 0.35rem')};
10+
border-radius: 14px;
11+
background: ${({ theme, enabled }) => (enabled ? theme.primary1 : 'none')};
12+
color: ${({ theme, enabled }) => (enabled ? theme.white : theme.primary1)};
13+
font-size: 0.825rem;
14+
font-weight: 400;
15+
:hover {
16+
user-select: ${({ enabled }) => (enabled ? 'none' : 'initial')};
17+
background: ${({ theme, enabled }) => (enabled ? theme.primary1 : 'none')};
18+
color: ${({ theme, enabled }) => (enabled ? theme.white : theme.primary3)};
19+
}
20+
`
21+
const VersionToggle = styled(Link)<{ enabled: boolean }>`
22+
border-radius: 16px;
23+
opacity: ${({ enabled }) => (enabled ? 1 : 0.5)};
24+
cursor: ${({ enabled }) => (enabled ? 'pointer' : 'default')};
25+
background: ${({ theme }) => theme.primary5};
26+
border: 1px solid ${({ theme }) => theme.primary4};
27+
color: ${({ theme }) => theme.primary1};
28+
display: flex;
29+
width: fit-content;
30+
text-decoration: none;
31+
:hover {
32+
text-decoration: none;
33+
}
34+
`
35+
36+
export function VersionSwitch() {
37+
const version = useToggledVersion()
38+
const location = useLocation()
39+
const query = useParsedQueryString()
40+
const versionSwitchAvailable = location.pathname === '/swap' || location.pathname === '/send'
41+
42+
const toggleDest = useMemo(() => {
43+
return versionSwitchAvailable
44+
? {
45+
...location,
46+
search: `?${stringify({ ...query, use: version === Version.v1 ? undefined : Version.v1 })}`
47+
}
48+
: location
49+
}, [location, query, version, versionSwitchAvailable])
50+
51+
const handleClick = useCallback(
52+
e => {
53+
if (!versionSwitchAvailable) e.preventDefault()
54+
},
55+
[versionSwitchAvailable]
56+
)
57+
58+
return (
59+
<VersionToggle enabled={versionSwitchAvailable} to={toggleDest} onClick={handleClick}>
60+
<VersionLabel enabled={version === Version.v2 || !versionSwitchAvailable}>V2</VersionLabel>
61+
<VersionLabel enabled={version === Version.v1 && versionSwitchAvailable}>V1</VersionLabel>
62+
</VersionToggle>
63+
)
64+
}

src/components/Header/index.tsx

Lines changed: 24 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
1+
import { ChainId, WETH } from '@uniswap/sdk'
12
import React from 'react'
3+
import { isMobile } from 'react-device-detect'
24
import { Link as HistoryLink } from 'react-router-dom'
5+
import { Text } from 'rebass'
36

47
import styled from 'styled-components'
5-
import { useTokenBalanceTreatingWETHasETH } from '../../state/wallet/hooks'
6-
7-
import Row from '../Row'
8-
import Menu from '../Menu'
9-
import Web3Status from '../Web3Status'
10-
11-
import { ExternalLink, StyledInternalLink } from '../../theme'
12-
import { Text } from 'rebass'
13-
import { WETH, ChainId } from '@uniswap/sdk'
14-
import { isMobile } from 'react-device-detect'
15-
import { YellowCard } from '../Card'
16-
import { useActiveWeb3React } from '../../hooks'
17-
import { useDarkModeManager } from '../../state/user/hooks'
188

199
import Logo from '../../assets/svg/logo.svg'
20-
import Wordmark from '../../assets/svg/wordmark.svg'
2110
import LogoDark from '../../assets/svg/logo_white.svg'
11+
import Wordmark from '../../assets/svg/wordmark.svg'
2212
import WordmarkDark from '../../assets/svg/wordmark_white.svg'
13+
import { useActiveWeb3React } from '../../hooks'
14+
import { useDarkModeManager } from '../../state/user/hooks'
15+
import { useTokenBalanceTreatingWETHasETH } from '../../state/wallet/hooks'
16+
17+
import { ExternalLink, StyledInternalLink } from '../../theme'
18+
import { YellowCard } from '../Card'
2319
import { AutoColumn } from '../Column'
24-
import { RowBetween } from '../Row'
20+
import Menu from '../Menu'
21+
22+
import Row, { RowBetween } from '../Row'
23+
import Web3Status from '../Web3Status'
24+
import { VersionSwitch } from './VersionSwitch'
2525

2626
const HeaderFrame = styled.div`
2727
display: flex;
@@ -122,33 +122,13 @@ const MigrateBanner = styled(AutoColumn)`
122122
`};
123123
`
124124

125-
const VersionLabel = styled.span<{ isV2?: boolean }>`
126-
padding: ${({ isV2 }) => (isV2 ? '0.15rem 0.5rem 0.16rem 0.45rem' : '0.15rem 0.5rem 0.16rem 0.35rem')};
127-
border-radius: 14px;
128-
background: ${({ theme, isV2 }) => (isV2 ? theme.primary1 : 'none')};
129-
color: ${({ theme, isV2 }) => (isV2 ? theme.white : theme.primary1)};
130-
font-size: 0.825rem;
131-
font-weight: 400;
132-
:hover {
133-
user-select: ${({ isV2 }) => (isV2 ? 'none' : 'initial')};
134-
background: ${({ theme, isV2 }) => (isV2 ? theme.primary1 : 'none')};
135-
color: ${({ theme, isV2 }) => (isV2 ? theme.white : theme.primary3)};
136-
}
137-
`
138-
139-
const VersionToggle = styled.a`
140-
border-radius: 16px;
141-
background: ${({ theme }) => theme.primary5};
142-
border: 1px solid ${({ theme }) => theme.primary4};
143-
color: ${({ theme }) => theme.primary1};
144-
display: flex;
145-
width: fit-content;
146-
cursor: pointer;
147-
text-decoration: none;
148-
:hover {
149-
text-decoration: none;
150-
}
151-
`
125+
const NETWORK_LABELS: { [chainId in ChainId]: string | null } = {
126+
[ChainId.MAINNET]: null,
127+
[ChainId.RINKEBY]: 'Rinkeby',
128+
[ChainId.ROPSTEN]: 'Ropsten',
129+
[ChainId.GÖRLI]: 'Görli',
130+
[ChainId.KOVAN]: 'Kovan'
131+
}
152132

153133
export default function Header() {
154134
const { account, chainId } = useActiveWeb3React()
@@ -187,21 +167,11 @@ export default function Header() {
187167
</TitleText>
188168
)}
189169
</Title>
190-
<TestnetWrapper style={{ pointerEvents: 'auto' }}>
191-
{!isMobile && (
192-
<VersionToggle target="_self" href="https://v1.uniswap.exchange">
193-
<VersionLabel isV2={true}>V2</VersionLabel>
194-
<VersionLabel isV2={false}>V1</VersionLabel>
195-
</VersionToggle>
196-
)}
197-
</TestnetWrapper>
170+
<TestnetWrapper style={{ pointerEvents: 'auto' }}>{!isMobile && <VersionSwitch />}</TestnetWrapper>
198171
</HeaderElement>
199172
<HeaderElement>
200173
<TestnetWrapper>
201-
{!isMobile && chainId === ChainId.ROPSTEN && <NetworkCard>Ropsten</NetworkCard>}
202-
{!isMobile && chainId === ChainId.RINKEBY && <NetworkCard>Rinkeby</NetworkCard>}
203-
{!isMobile && chainId === ChainId.GÖRLI && <NetworkCard>Görli</NetworkCard>}
204-
{!isMobile && chainId === ChainId.KOVAN && <NetworkCard>Kovan</NetworkCard>}
174+
{!isMobile && NETWORK_LABELS[chainId] && <NetworkCard>{NETWORK_LABELS[chainId]}</NetworkCard>}
205175
</TestnetWrapper>
206176
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
207177
{account && userEthBalance ? (

src/components/TxnPopup/index.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import React, { useCallback, useState } from 'react'
1+
import React, { useCallback, useContext, useState } from 'react'
22
import { AlertCircle, CheckCircle } from 'react-feather'
33

4-
import styled from 'styled-components'
4+
import styled, { ThemeContext } from 'styled-components'
55

66
import { useActiveWeb3React } from '../../hooks'
77
import useInterval from '../../hooks/useInterval'
@@ -51,13 +51,16 @@ export default function TxnPopup({
5151
isRunning ? delay : null
5252
)
5353

54+
const handleMouseEnter = useCallback(() => setIsRunning(false), [])
55+
const handleMouseLeave = useCallback(() => setIsRunning(true), [])
56+
57+
const theme = useContext(ThemeContext)
58+
5459
return (
55-
<AutoRow onMouseEnter={() => setIsRunning(false)} onMouseLeave={() => setIsRunning(true)}>
56-
{success ? (
57-
<CheckCircle color={'#27AE60'} size={24} style={{ paddingRight: '24px' }} />
58-
) : (
59-
<AlertCircle color={'#FF6871'} size={24} style={{ paddingRight: '24px' }} />
60-
)}
60+
<AutoRow onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
61+
<div style={{ paddingRight: 16 }}>
62+
{success ? <CheckCircle color={theme.green1} size={24} /> : <AlertCircle color={theme.red1} size={24} />}
63+
</div>
6164
<AutoColumn gap="8px">
6265
<TYPE.body fontWeight={500}>
6366
{summary ? summary : 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { stringify } from 'qs'
2+
import React, { useContext, useMemo } from 'react'
3+
import { useLocation } from 'react-router'
4+
import { Text } from 'rebass'
5+
import { ThemeContext } from 'styled-components'
6+
import useParsedQueryString from '../../hooks/useParsedQueryString'
7+
import { Version } from '../../hooks/useToggledVersion'
8+
9+
import { StyledInternalLink } from '../../theme'
10+
import { YellowCard } from '../Card'
11+
import { AutoColumn } from '../Column'
12+
13+
export default function BetterTradeLink({ version }: { version: Version }) {
14+
const theme = useContext(ThemeContext)
15+
const location = useLocation()
16+
const search = useParsedQueryString()
17+
18+
const linkDestination = useMemo(() => {
19+
return {
20+
...location,
21+
search: `?${stringify({
22+
...search,
23+
use: version
24+
})}`
25+
}
26+
}, [location, search, version])
27+
28+
return (
29+
<YellowCard style={{ marginTop: '12px', padding: '8px 4px' }}>
30+
<AutoColumn gap="sm" justify="center" style={{ alignItems: 'center', textAlign: 'center' }}>
31+
<Text lineHeight="145.23%;" fontSize={14} fontWeight={400} color={theme.text1}>
32+
There is a better price for this trade on{' '}
33+
<StyledInternalLink to={linkDestination}>
34+
<b>Uniswap {version.toUpperCase()}</b>
35+
</StyledInternalLink>
36+
</Text>
37+
</AutoColumn>
38+
</YellowCard>
39+
)
40+
}

src/components/swap/V1TradeLink.tsx

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/constants/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,4 @@ export const PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN: Percent = new Percent(JSBI.Bi
159159

160160
// used to ensure the user doesn't send so much ETH so they end up with <.01
161161
export const MIN_ETH: JSBI = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(16)) // .01 ETH
162-
export const V1_TRADE_LINK_THRESHOLD = new Percent(JSBI.BigInt(75), JSBI.BigInt(10000))
162+
export const BETTER_TRADE_LINK_THRESHOLD = new Percent(JSBI.BigInt(75), JSBI.BigInt(10000))

0 commit comments

Comments
 (0)