Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37a4e2f6e3 | ||
|
|
19a3b12ca8 | ||
|
|
22c1ddf393 | ||
|
|
b44ae1a267 | ||
|
|
418dcf0cb2 | ||
|
|
58a508c9d6 | ||
|
|
3198129af2 | ||
|
|
89d484d882 | ||
|
|
fa4688d96c | ||
|
|
7ee761a59e | ||
|
|
78e95f6073 | ||
|
|
c67e57505a | ||
|
|
30f7385db7 | ||
|
|
c0f58ae810 | ||
|
|
54dd5476ca | ||
|
|
57786335df | ||
|
|
948e01a196 | ||
|
|
abf127c596 | ||
|
|
4d3f870b93 | ||
|
|
452f2dc3c0 | ||
|
|
b6bd59f2b1 | ||
|
|
0190b5a408 | ||
|
|
d6030dcd45 | ||
|
|
f0e2a491dc | ||
|
|
021aab6547 | ||
|
|
81af31eec1 | ||
|
|
d3898cf900 | ||
|
|
b8f61d5f90 |
12
package.json
12
package.json
@@ -20,7 +20,7 @@
|
||||
"@storybook/react": "^6.1.17",
|
||||
"@storybook/theming": "^6.1.17",
|
||||
"@styled-system/css": "^5.1.5",
|
||||
"@typechain/ethers-v5": "^6.0.5",
|
||||
"@typechain/ethers-v5": "^7.0.0",
|
||||
"@types/jest": "^25.2.1",
|
||||
"@types/lodash.flatmap": "^4.5.6",
|
||||
"@types/luxon": "^1.24.4",
|
||||
@@ -36,6 +36,7 @@
|
||||
"@types/rebass": "^4.0.7",
|
||||
"@types/styled-components": "^5.1.0",
|
||||
"@types/testing-library__cypress": "^5.0.5",
|
||||
"@types/ua-parser-js": "^0.7.35",
|
||||
"@types/wcag-contrast": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
||||
"@typescript-eslint/parser": "^4.1.0",
|
||||
@@ -49,7 +50,7 @@
|
||||
"@uniswap/v2-sdk": "^1.0.9",
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "1.0.0",
|
||||
"@uniswap/v3-sdk": "^1.0.3",
|
||||
"@uniswap/v3-sdk": "^1.0.8",
|
||||
"@web3-react/core": "^6.0.9",
|
||||
"@web3-react/fortmatic-connector": "^6.0.9",
|
||||
"@web3-react/injected-connector": "^6.0.7",
|
||||
@@ -103,8 +104,9 @@
|
||||
"start-server-and-test": "^1.11.0",
|
||||
"styled-components": "^4.2.0",
|
||||
"styled-system": "^5.1.5",
|
||||
"typechain": "^4.0.3",
|
||||
"typechain": "^5.0.0",
|
||||
"typescript": "^4.2.3",
|
||||
"ua-parser-js": "^0.7.28",
|
||||
"use-count-up": "^2.2.5",
|
||||
"wcag-contrast": "^3.0.0",
|
||||
"workbox-core": "^6.1.0",
|
||||
@@ -115,8 +117,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"compile-contract-types": "yarn compile-external-abi-types && yarn compile-v3-contract-types",
|
||||
"compile-external-abi-types": "npx typechain --target ethers-v5 --outDir src/abis/types './src/abis/**/*.json'",
|
||||
"compile-v3-contract-types": "npx typechain --target ethers-v5 --outDir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
|
||||
"compile-external-abi-types": "npx typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
|
||||
"compile-v3-contract-types": "npx typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
|
||||
"build": "yarn compile-contract-types && react-scripts build",
|
||||
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'",
|
||||
"postinstall": "yarn compile-contract-types",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB |
BIN
src/assets/images/sandtexture.webp
Normal file
BIN
src/assets/images/sandtexture.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 235 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 2.2 MiB |
@@ -76,7 +76,6 @@ const AccountGroupingRow = styled.div`
|
||||
`
|
||||
|
||||
const AccountSection = styled.div`
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
padding: 0rem 1rem;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`padding: 0rem 1rem 1.5rem 1rem;`};
|
||||
`
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function RangeBadge({
|
||||
<BadgeWrapper>
|
||||
{removed ? (
|
||||
<MouseoverTooltip text={`Your position has 0 liquidity, and is not earning fees.`}>
|
||||
<Badge variant={BadgeVariant.WARNING_OUTLINE}>
|
||||
<Badge variant={BadgeVariant.DEFAULT}>
|
||||
<AlertCircle width={14} height={14} />
|
||||
|
||||
<BadgeText>{t('Inactive')}</BadgeText>
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useMemo } from 'react'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { TYPE } from '../../theme'
|
||||
import { warningSeverity } from '../../utils/prices'
|
||||
import HoverInlineText from 'components/HoverInlineText'
|
||||
|
||||
export function FiatValue({
|
||||
fiatValue,
|
||||
@@ -23,7 +24,8 @@ export function FiatValue({
|
||||
|
||||
return (
|
||||
<TYPE.body fontSize={14} color={fiatValue ? theme.text2 : theme.text4}>
|
||||
{fiatValue ? '~' : ''}${fiatValue ? Number(fiatValue?.toSignificant(6)).toLocaleString('en') : '-'}
|
||||
{fiatValue ? '~' : ''}$
|
||||
<HoverInlineText text={fiatValue ? Number(fiatValue?.toSignificant(6)).toLocaleString('en') : '-'} />{' '}
|
||||
{priceImpact ? (
|
||||
<span style={{ color: priceImpactColor }}> ({priceImpact.multiply(-100).toSignificant(3)}%)</span>
|
||||
) : null}
|
||||
|
||||
144
src/components/ErrorBoundary/index.tsx
Normal file
144
src/components/ErrorBoundary/index.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
import React, { ErrorInfo } from 'react'
|
||||
import { ExternalLink, ThemedBackground, TYPE } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import styled from 'styled-components'
|
||||
import ReactGA from 'react-ga'
|
||||
import { getUserAgent } from '../../utils/getUserAgent'
|
||||
|
||||
const FallbackWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
`
|
||||
|
||||
const BodyWrapper = styled.div<{ margin?: string }>`
|
||||
position: relative;
|
||||
margin-top: 1rem;
|
||||
max-width: 60%;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const CodeBlockWrapper = styled.div`
|
||||
background: ${({ theme }) => theme.bg0};
|
||||
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: 24px;
|
||||
padding: 18px 24px;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
`
|
||||
|
||||
const LinkWrapper = styled.div`
|
||||
color: ${({ theme }) => theme.blue1};
|
||||
padding: 6px 24px;
|
||||
`
|
||||
|
||||
const SomethingWentWrongWrapper = styled.div`
|
||||
padding: 6px 24px;
|
||||
`
|
||||
|
||||
type ErrorBoundaryState = {
|
||||
error: Error | null
|
||||
}
|
||||
|
||||
export default class ErrorBoundary extends React.Component<unknown, ErrorBoundaryState> {
|
||||
constructor(props: unknown) {
|
||||
super(props)
|
||||
this.state = { error: null }
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||
return { error }
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
ReactGA.exception({
|
||||
...error,
|
||||
...errorInfo,
|
||||
fatal: true,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { error } = this.state
|
||||
if (error !== null) {
|
||||
const encodedBody = encodeURIComponent(issueBody(error))
|
||||
return (
|
||||
<FallbackWrapper>
|
||||
<ThemedBackground />
|
||||
<BodyWrapper>
|
||||
<AutoColumn gap={'md'}>
|
||||
<SomethingWentWrongWrapper>
|
||||
<TYPE.label fontSize={24} fontWeight={600}>
|
||||
Something went wrong
|
||||
</TYPE.label>
|
||||
</SomethingWentWrongWrapper>
|
||||
<CodeBlockWrapper>
|
||||
<code>
|
||||
<TYPE.main fontSize={10}>{error.stack}</TYPE.main>
|
||||
</code>
|
||||
</CodeBlockWrapper>
|
||||
<LinkWrapper>
|
||||
<ExternalLink
|
||||
id={`create-github-issue-link`}
|
||||
href={`https://github.com/Uniswap/uniswap-interface/issues/new?assignees=&labels=bug&body=${encodedBody}&title=Crash report`}
|
||||
target="_blank"
|
||||
>
|
||||
<TYPE.link fontSize={16}>
|
||||
Create an issue on GitHub
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
</AutoColumn>
|
||||
</BodyWrapper>
|
||||
</FallbackWrapper>
|
||||
)
|
||||
}
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
function issueBody(error: Error): string {
|
||||
if (!error) throw new Error('no error to report')
|
||||
const deviceData = getUserAgent()
|
||||
return `**Bug Description**
|
||||
|
||||
App crashed
|
||||
|
||||
**Steps to Reproduce**
|
||||
|
||||
1. Go to ...
|
||||
2. Click on ...
|
||||
...
|
||||
|
||||
${
|
||||
error.name &&
|
||||
`**Error**
|
||||
|
||||
\`\`\`
|
||||
${error.name}${error.message && `: ${error.message}`}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
${
|
||||
error.stack &&
|
||||
`**Stacktrace**
|
||||
|
||||
\`\`\`
|
||||
${error.stack}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
${
|
||||
deviceData &&
|
||||
`**Device data**
|
||||
|
||||
\`\`\`json5
|
||||
${JSON.stringify(deviceData, null, 2)}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
`
|
||||
}
|
||||
@@ -90,7 +90,7 @@ const HeaderElement = styled.div`
|
||||
}
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
flex-direction: row-reverse;
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
`};
|
||||
`
|
||||
@@ -116,6 +116,9 @@ const HeaderLinks = styled(Row)`
|
||||
grid-auto-flow: column;
|
||||
grid-gap: 10px;
|
||||
overflow: auto;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
justify-self: flex-end;
|
||||
`};
|
||||
`
|
||||
|
||||
const AccountElement = styled.div<{ active: boolean }>`
|
||||
|
||||
63
src/components/HoverInlineText/index.tsx
Normal file
63
src/components/HoverInlineText/index.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import React, { useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const TextWrapper = styled.span<{ margin: boolean; link: boolean; fontSize?: string; adjustSize?: boolean }>`
|
||||
position: relative;
|
||||
margin-left: ${({ margin }) => margin && '4px'};
|
||||
color: ${({ theme, link }) => (link ? theme.blue1 : theme.text1)};
|
||||
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
font-size: ${({ adjustSize }) => adjustSize && '12px'};
|
||||
}
|
||||
`
|
||||
|
||||
const HoverInlineText = ({
|
||||
text,
|
||||
maxCharacters = 20,
|
||||
margin = false,
|
||||
adjustSize = false,
|
||||
fontSize,
|
||||
link,
|
||||
...rest
|
||||
}: {
|
||||
text: string
|
||||
maxCharacters?: number
|
||||
margin?: boolean
|
||||
adjustSize?: boolean
|
||||
fontSize?: string
|
||||
link?: boolean
|
||||
}) => {
|
||||
const [showHover, setShowHover] = useState(false)
|
||||
|
||||
if (!text) {
|
||||
return <span></span>
|
||||
}
|
||||
|
||||
if (text.length > maxCharacters) {
|
||||
return (
|
||||
<Tooltip text={text} show={showHover}>
|
||||
<TextWrapper
|
||||
onMouseEnter={() => setShowHover(true)}
|
||||
onMouseLeave={() => setShowHover(false)}
|
||||
margin={margin}
|
||||
adjustSize={adjustSize}
|
||||
link={!!link}
|
||||
fontSize={fontSize}
|
||||
{...rest}
|
||||
>
|
||||
{' ' + text.slice(0, maxCharacters - 1) + '...'}
|
||||
</TextWrapper>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<TextWrapper margin={margin} adjustSize={adjustSize} link={!!link} fontSize={fontSize} {...rest}>
|
||||
{text}
|
||||
</TextWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default HoverInlineText
|
||||
@@ -139,7 +139,7 @@ const StepCounter = ({
|
||||
}}
|
||||
/>
|
||||
<InputTitle fontSize={12} textAlign="center">
|
||||
{tokenB + ' / ' + tokenA}
|
||||
{tokenB + ' per ' + tokenA}
|
||||
</InputTitle>
|
||||
</AutoColumn>
|
||||
{!locked ? (
|
||||
|
||||
@@ -3,10 +3,11 @@ import styled from 'styled-components'
|
||||
import { darken } from 'polished'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { NavLink, Link as HistoryLink } from 'react-router-dom'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
|
||||
import { ArrowLeft } from 'react-feather'
|
||||
import { RowBetween } from '../Row'
|
||||
import Settings from '../Settings'
|
||||
import SettingsTab from '../Settings'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { AppDispatch } from 'state'
|
||||
import { resetMintState } from 'state/mint/actions'
|
||||
@@ -80,7 +81,6 @@ export function FindPoolTabs({ origin }: { origin: string }) {
|
||||
<StyledArrowLeft />
|
||||
</HistoryLink>
|
||||
<ActiveText>Import Pool</ActiveText>
|
||||
<Settings />
|
||||
</RowBetween>
|
||||
</Tabs>
|
||||
)
|
||||
@@ -90,10 +90,12 @@ export function AddRemoveTabs({
|
||||
adding,
|
||||
creating,
|
||||
positionID,
|
||||
defaultSlippage,
|
||||
}: {
|
||||
adding: boolean
|
||||
creating: boolean
|
||||
positionID?: string | undefined
|
||||
defaultSlippage: Percent
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
|
||||
@@ -118,7 +120,7 @@ export function AddRemoveTabs({
|
||||
<TYPE.mediumHeader fontWeight={500} fontSize={20}>
|
||||
{creating ? 'Create a pair' : adding ? 'Add Liquidity' : 'Remove Liquidity'}
|
||||
</TYPE.mediumHeader>
|
||||
<Settings />
|
||||
<SettingsTab placeholderSlippage={defaultSlippage} />
|
||||
</RowBetween>
|
||||
</Tabs>
|
||||
)
|
||||
|
||||
@@ -198,28 +198,23 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
|
||||
<CardNoise />
|
||||
<AutoColumn gap="12px">
|
||||
<FixedHeightRow>
|
||||
<AutoRow gap="8px">
|
||||
<AutoRow gap="8px" style={{ marginLeft: '8px' }}>
|
||||
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{!currency0 || !currency1 ? <Dots>Loading</Dots> : `${currency0.symbol}/${currency1.symbol}`}
|
||||
</Text>
|
||||
</AutoRow>
|
||||
<RowFixed gap="8px">
|
||||
<ButtonEmpty
|
||||
padding="6px 8px"
|
||||
borderRadius="12px"
|
||||
width="fit-content"
|
||||
onClick={() => setShowMore(!showMore)}
|
||||
>
|
||||
<RowFixed gap="8px" style={{ marginRight: '4px' }}>
|
||||
<ButtonEmpty padding="6px 8px" borderRadius="12px" width="100%" onClick={() => setShowMore(!showMore)}>
|
||||
{showMore ? (
|
||||
<>
|
||||
Manage
|
||||
<ChevronUp size="20" style={{ marginLeft: '10px' }} />
|
||||
<ChevronUp size="20" style={{ marginLeft: '8px', height: '20px', minWidth: '20px' }} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Manage
|
||||
<ChevronDown size="20" style={{ marginLeft: '10px' }} />
|
||||
<ChevronDown size="20" style={{ marginLeft: '8px', height: '20px', minWidth: '20px' }} />
|
||||
</>
|
||||
)}
|
||||
</ButtonEmpty>
|
||||
@@ -303,12 +298,21 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
|
||||
</ButtonSecondary>
|
||||
{userDefaultPoolBalance && JSBI.greaterThan(userDefaultPoolBalance.raw, BIG_INT_ZERO) && (
|
||||
<RowBetween marginTop="10px">
|
||||
<ButtonPrimary
|
||||
padding="8px"
|
||||
borderRadius="8px"
|
||||
as={Link}
|
||||
to={`/migrate/v2/${pair.liquidityToken.address}`}
|
||||
width="32%"
|
||||
>
|
||||
Migrate
|
||||
</ButtonPrimary>
|
||||
<ButtonPrimary
|
||||
padding="8px"
|
||||
borderRadius="8px"
|
||||
as={Link}
|
||||
to={`/add/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
|
||||
width="48%"
|
||||
width="32%"
|
||||
>
|
||||
Add
|
||||
</ButtonPrimary>
|
||||
@@ -316,7 +320,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
|
||||
padding="8px"
|
||||
borderRadius="8px"
|
||||
as={Link}
|
||||
width="48%"
|
||||
width="32%"
|
||||
to={`/remove/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
|
||||
>
|
||||
Remove
|
||||
|
||||
@@ -15,6 +15,7 @@ import { unwrappedToken } from 'utils/wrappedCurrency'
|
||||
import { DAI, USDC, USDT, WBTC } from '../../constants'
|
||||
import RangeBadge from 'components/Badge/RangeBadge'
|
||||
import { RowFixed } from 'components/Row'
|
||||
import HoverInlineText from 'components/HoverInlineText'
|
||||
|
||||
const Row = styled(Link)`
|
||||
align-items: center;
|
||||
@@ -202,8 +203,8 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
|
||||
// prices
|
||||
let { priceLower, priceUpper, base, quote } = getPriceOrderingFromPositionForUI(position)
|
||||
const inverted = token1 ? base?.equals(token1) : undefined
|
||||
const currencyQuote = inverted ? currency0 : currency1
|
||||
const currencyBase = inverted ? currency1 : currency0
|
||||
const currencyQuote = inverted ? currency1 : currency0
|
||||
const currencyBase = inverted ? currency0 : currency1
|
||||
|
||||
// check if price is within range
|
||||
const outOfRange: boolean = pool ? pool.tickCurrent < tickLower || pool.tickCurrent >= tickUpper : false
|
||||
@@ -244,8 +245,10 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
|
||||
>
|
||||
<RangeText>
|
||||
<ExtentsText>Min: </ExtentsText>
|
||||
{formatPrice(priceLower, 4)} {manuallyInverted ? currencyQuote?.symbol : currencyBase?.symbol} {' / '}{' '}
|
||||
{manuallyInverted ? currencyBase?.symbol : currencyQuote?.symbol}
|
||||
{formatPrice(priceLower, 5)}{' '}
|
||||
<HoverInlineText text={manuallyInverted ? currencyQuote?.symbol ?? '' : currencyBase?.symbol ?? ''} />{' '}
|
||||
{' per '}{' '}
|
||||
<HoverInlineText text={manuallyInverted ? currencyBase?.symbol ?? '' : currencyQuote?.symbol ?? ''} />
|
||||
</RangeText>{' '}
|
||||
<HideSmall>
|
||||
<DoubleArrow>⟷</DoubleArrow>{' '}
|
||||
@@ -255,8 +258,13 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
|
||||
</SmallOnly>
|
||||
<RangeText>
|
||||
<ExtentsText>Max:</ExtentsText>
|
||||
{formatPrice(priceUpper, 4)} {manuallyInverted ? currencyQuote?.symbol : currencyBase?.symbol} {' / '}{' '}
|
||||
{manuallyInverted ? currencyBase?.symbol : currencyQuote?.symbol}
|
||||
{formatPrice(priceUpper, 5)}{' '}
|
||||
<HoverInlineText text={manuallyInverted ? currencyQuote?.symbol ?? '' : currencyBase?.symbol ?? ''} />{' '}
|
||||
{' per '}{' '}
|
||||
<HoverInlineText
|
||||
maxCharacters={10}
|
||||
text={manuallyInverted ? currencyBase?.symbol ?? '' : currencyQuote?.symbol ?? ''}
|
||||
/>
|
||||
</RangeText>{' '}
|
||||
</RangeLineItem>
|
||||
</>
|
||||
|
||||
@@ -135,7 +135,7 @@ export const PositionPreview = ({
|
||||
<TYPE.main
|
||||
textAlign="center"
|
||||
fontSize="12px"
|
||||
>{` ${quoteCurrency.symbol}/${baseCurrency.symbol}`}</TYPE.main>
|
||||
>{` ${quoteCurrency.symbol} per ${baseCurrency.symbol}`}</TYPE.main>
|
||||
<TYPE.small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
|
||||
Your position will be 100% composed of {quoteCurrency?.symbol} at this price
|
||||
</TYPE.small>
|
||||
@@ -145,11 +145,11 @@ export const PositionPreview = ({
|
||||
<LightCard padding="12px ">
|
||||
<AutoColumn gap="4px" justify="center">
|
||||
<TYPE.main fontSize="12px">Current price</TYPE.main>
|
||||
<TYPE.mediumHeader>{`${price.toSignificant(6)} `}</TYPE.mediumHeader>
|
||||
<TYPE.mediumHeader>{`${price.toSignificant(5)} `}</TYPE.mediumHeader>
|
||||
<TYPE.main
|
||||
textAlign="center"
|
||||
fontSize="12px"
|
||||
>{` ${quoteCurrency.symbol}/${baseCurrency.symbol}`}</TYPE.main>
|
||||
>{` ${quoteCurrency.symbol} per ${baseCurrency.symbol}`}</TYPE.main>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
</AutoColumn>
|
||||
|
||||
@@ -26,10 +26,10 @@ export default function RateToggle({
|
||||
<div style={{ width: 'fit-content', display: 'flex', alignItems: 'center' }}>
|
||||
<ToggleWrapper width="fit-content">
|
||||
<ToggleElement isActive={isSorted} fontSize="12px" onClick={handleRateToggle}>
|
||||
{isSorted ? currencyB.symbol + ' / ' + currencyA.symbol : currencyA.symbol + ' / ' + currencyB.symbol}{' '}
|
||||
{isSorted ? currencyA.symbol + ' price ' : currencyB.symbol + ' price '}
|
||||
</ToggleElement>
|
||||
<ToggleElement isActive={!isSorted} fontSize="12px" onClick={handleRateToggle}>
|
||||
{isSorted ? currencyA.symbol + ' / ' + currencyB.symbol : currencyB.symbol + ' / ' + currencyA.symbol}
|
||||
{isSorted ? currencyB.symbol + ' price ' : currencyA.symbol + ' price '}
|
||||
</ToggleElement>
|
||||
</ToggleWrapper>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import JSBI from 'jsbi'
|
||||
import React, { useContext, useRef, useState } from 'react'
|
||||
import { Settings, X } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
@@ -7,12 +6,7 @@ import styled, { ThemeContext } from 'styled-components'
|
||||
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
|
||||
import { ApplicationModal } from '../../state/application/actions'
|
||||
import { useModalOpen, useToggleSettingsMenu } from '../../state/application/hooks'
|
||||
import {
|
||||
useExpertModeManager,
|
||||
useUserTransactionTTL,
|
||||
useUserSlippageTolerance,
|
||||
useUserSingleHopOnly,
|
||||
} from '../../state/user/hooks'
|
||||
import { useExpertModeManager, useUserSingleHopOnly } from '../../state/user/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ButtonError } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
@@ -21,6 +15,7 @@ import QuestionHelper from '../QuestionHelper'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import Toggle from '../Toggle'
|
||||
import TransactionSettings from '../TransactionSettings'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
|
||||
const StyledMenuIcon = styled(Settings)`
|
||||
height: 20px;
|
||||
@@ -116,15 +111,12 @@ const ModalContentWrapper = styled.div`
|
||||
border-radius: 20px;
|
||||
`
|
||||
|
||||
export default function SettingsTab() {
|
||||
export default function SettingsTab({ placeholderSlippage }: { placeholderSlippage: Percent }) {
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.SETTINGS)
|
||||
const toggle = useToggleSettingsMenu()
|
||||
|
||||
const theme = useContext(ThemeContext)
|
||||
const [userSlippageTolerance, setUserslippageTolerance] = useUserSlippageTolerance()
|
||||
|
||||
const [ttl, setTtl] = useUserTransactionTTL()
|
||||
|
||||
const [expertMode, toggleExpertMode] = useExpertModeManager()
|
||||
|
||||
@@ -191,12 +183,7 @@ export default function SettingsTab() {
|
||||
<Text fontWeight={600} fontSize={14}>
|
||||
Transaction Settings
|
||||
</Text>
|
||||
<TransactionSettings
|
||||
rawSlippage={JSBI.toNumber(userSlippageTolerance.numerator)}
|
||||
setRawSlippage={setUserslippageTolerance}
|
||||
deadline={ttl}
|
||||
setDeadline={setTtl}
|
||||
/>
|
||||
<TransactionSettings placeholderSlippage={placeholderSlippage} />
|
||||
<Text fontWeight={600} fontSize={14}>
|
||||
Interface Settings
|
||||
</Text>
|
||||
|
||||
@@ -5,8 +5,8 @@ import Popover, { PopoverProps } from '../Popover'
|
||||
const TooltipContainer = styled.div`
|
||||
width: 256px;
|
||||
padding: 0.6rem 1rem;
|
||||
/* line-height: 150%; */
|
||||
font-weight: 400;
|
||||
word-break: break-word;
|
||||
`
|
||||
|
||||
interface TooltipProps extends Omit<PopoverProps, 'content'> {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React, { useState, useRef, useContext } from 'react'
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import styled, { ThemeContext } from 'styled-components'
|
||||
|
||||
import QuestionHelper from '../QuestionHelper'
|
||||
import { TYPE } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
|
||||
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/index'
|
||||
import { darken } from 'polished'
|
||||
import { useSetUserSlippageTolerance, useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
|
||||
|
||||
enum SlippageError {
|
||||
InvalidInput = 'InvalidInput',
|
||||
RiskyLow = 'RiskyLow',
|
||||
RiskyHigh = 'RiskyHigh',
|
||||
}
|
||||
|
||||
enum DeadlineError {
|
||||
@@ -64,7 +64,8 @@ const OptionCustom = styled(FancyButton)<{ active?: boolean; warning?: boolean }
|
||||
position: relative;
|
||||
padding: 0 0.75rem;
|
||||
flex: 1;
|
||||
border: ${({ theme, active, warning }) => active && `1px solid ${warning ? theme.red1 : theme.primary1}`};
|
||||
border: ${({ theme, active, warning }) =>
|
||||
active ? `1px solid ${warning ? theme.red1 : theme.primary1}` : warning && `1px solid ${theme.red1}`};
|
||||
:hover {
|
||||
border: ${({ theme, active, warning }) =>
|
||||
active && `1px solid ${warning ? darken(0.1, theme.red1) : darken(0.1, theme.primary1)}`};
|
||||
@@ -85,63 +86,63 @@ const SlippageEmojiContainer = styled.span`
|
||||
`}
|
||||
`
|
||||
|
||||
export interface SlippageTabsProps {
|
||||
rawSlippage: number
|
||||
setRawSlippage: (rawSlippage: number) => void
|
||||
deadline: number
|
||||
setDeadline: (deadline: number) => void
|
||||
export interface TransactionSettingsProps {
|
||||
placeholderSlippage: Percent // varies according to the context in which the settings dialog is placed
|
||||
}
|
||||
|
||||
export default function SlippageTabs({ rawSlippage, setRawSlippage, deadline, setDeadline }: SlippageTabsProps) {
|
||||
export default function TransactionSettings({ placeholderSlippage }: TransactionSettingsProps) {
|
||||
const theme = useContext(ThemeContext)
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>()
|
||||
const userSlippageTolerance = useUserSlippageTolerance()
|
||||
const setUserSlippageTolerance = useSetUserSlippageTolerance()
|
||||
|
||||
const [deadline, setDeadline] = useUserTransactionTTL()
|
||||
|
||||
const [slippageInput, setSlippageInput] = useState('')
|
||||
const [slippageError, setSlippageError] = useState<SlippageError | false>(false)
|
||||
|
||||
const [deadlineInput, setDeadlineInput] = useState('')
|
||||
const [deadlineError, setDeadlineError] = useState<DeadlineError | false>(false)
|
||||
|
||||
const slippageInputIsValid =
|
||||
slippageInput === '' || (rawSlippage / 100).toFixed(2) === Number.parseFloat(slippageInput).toFixed(2)
|
||||
const deadlineInputIsValid = deadlineInput === '' || (deadline / 60).toString() === deadlineInput
|
||||
|
||||
let slippageError: SlippageError | undefined
|
||||
if (slippageInput !== '' && !slippageInputIsValid) {
|
||||
slippageError = SlippageError.InvalidInput
|
||||
} else if (slippageInputIsValid && rawSlippage < 50) {
|
||||
slippageError = SlippageError.RiskyLow
|
||||
} else if (slippageInputIsValid && rawSlippage > 500) {
|
||||
slippageError = SlippageError.RiskyHigh
|
||||
} else {
|
||||
slippageError = undefined
|
||||
}
|
||||
|
||||
let deadlineError: DeadlineError | undefined
|
||||
if (deadlineInput !== '' && !deadlineInputIsValid) {
|
||||
deadlineError = DeadlineError.InvalidInput
|
||||
} else {
|
||||
deadlineError = undefined
|
||||
}
|
||||
|
||||
function parseCustomSlippage(value: string) {
|
||||
function parseSlippageInput(value: string) {
|
||||
// populate what the user typed and clear the error
|
||||
setSlippageInput(value)
|
||||
setSlippageError(false)
|
||||
|
||||
try {
|
||||
const valueAsIntFromRoundedFloat = Number.parseInt((Number.parseFloat(value) * 100).toString())
|
||||
if (!Number.isNaN(valueAsIntFromRoundedFloat) && valueAsIntFromRoundedFloat < 5000) {
|
||||
setRawSlippage(valueAsIntFromRoundedFloat)
|
||||
if (value.length === 0) {
|
||||
setUserSlippageTolerance('auto')
|
||||
} else {
|
||||
const parsed = Math.floor(Number.parseFloat(value) * 100)
|
||||
|
||||
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 5000) {
|
||||
setUserSlippageTolerance('auto')
|
||||
if (value !== '.') {
|
||||
setSlippageError(SlippageError.InvalidInput)
|
||||
}
|
||||
} else {
|
||||
setUserSlippageTolerance(new Percent(parsed, 10_000))
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
const tooLow = userSlippageTolerance !== 'auto' && userSlippageTolerance.lessThan(new Percent(5, 10_000))
|
||||
const tooHigh = userSlippageTolerance !== 'auto' && userSlippageTolerance.greaterThan(new Percent(1, 100))
|
||||
|
||||
function parseCustomDeadline(value: string) {
|
||||
// populate what the user typed and clear the error
|
||||
setDeadlineInput(value)
|
||||
setDeadlineError(false)
|
||||
|
||||
try {
|
||||
const valueAsInt: number = Number.parseInt(value) * 60
|
||||
if (!Number.isNaN(valueAsInt) && valueAsInt > 0) {
|
||||
setDeadline(valueAsInt)
|
||||
if (value.length === 0) {
|
||||
setDeadline(DEFAULT_DEADLINE_FROM_NOW)
|
||||
} else {
|
||||
const parsed: number = Math.floor(Number.parseFloat(value) * 60)
|
||||
if (!Number.isInteger(parsed) || parsed < 60) {
|
||||
setDeadlineError(DeadlineError.InvalidInput)
|
||||
} else {
|
||||
setDeadline(parsed)
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -156,71 +157,56 @@ export default function SlippageTabs({ rawSlippage, setRawSlippage, deadline, se
|
||||
<RowBetween>
|
||||
<Option
|
||||
onClick={() => {
|
||||
setSlippageInput('')
|
||||
setRawSlippage(10)
|
||||
parseSlippageInput('')
|
||||
}}
|
||||
active={rawSlippage === 10}
|
||||
active={userSlippageTolerance === 'auto'}
|
||||
>
|
||||
0.1%
|
||||
Auto
|
||||
</Option>
|
||||
<Option
|
||||
onClick={() => {
|
||||
setSlippageInput('')
|
||||
setRawSlippage(50)
|
||||
}}
|
||||
active={rawSlippage === 50}
|
||||
>
|
||||
0.5%
|
||||
</Option>
|
||||
<Option
|
||||
onClick={() => {
|
||||
setSlippageInput('')
|
||||
setRawSlippage(100)
|
||||
}}
|
||||
active={rawSlippage === 100}
|
||||
>
|
||||
1%
|
||||
</Option>
|
||||
<OptionCustom active={![10, 50, 100].includes(rawSlippage)} warning={!slippageInputIsValid} tabIndex={-1}>
|
||||
<OptionCustom active={userSlippageTolerance !== 'auto'} warning={!!slippageError} tabIndex={-1}>
|
||||
<RowBetween>
|
||||
{!!slippageInput &&
|
||||
(slippageError === SlippageError.RiskyLow || slippageError === SlippageError.RiskyHigh) ? (
|
||||
{tooLow || tooHigh ? (
|
||||
<SlippageEmojiContainer>
|
||||
<span role="img" aria-label="warning">
|
||||
⚠️
|
||||
</span>
|
||||
</SlippageEmojiContainer>
|
||||
) : null}
|
||||
{/* https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451 */}
|
||||
<Input
|
||||
ref={inputRef as any}
|
||||
placeholder={(rawSlippage / 100).toFixed(2)}
|
||||
value={slippageInput}
|
||||
placeholder={placeholderSlippage.toFixed(2)}
|
||||
value={
|
||||
slippageInput.length > 0
|
||||
? slippageInput
|
||||
: userSlippageTolerance === 'auto'
|
||||
? ''
|
||||
: userSlippageTolerance.toFixed(2)
|
||||
}
|
||||
onChange={(e) => parseSlippageInput(e.target.value)}
|
||||
onBlur={() => {
|
||||
parseCustomSlippage((rawSlippage / 100).toFixed(2))
|
||||
setSlippageInput('')
|
||||
setSlippageError(false)
|
||||
}}
|
||||
onChange={(e) => parseCustomSlippage(e.target.value)}
|
||||
color={!slippageInputIsValid ? 'red' : ''}
|
||||
color={slippageError ? 'red' : ''}
|
||||
/>
|
||||
%
|
||||
</RowBetween>
|
||||
</OptionCustom>
|
||||
</RowBetween>
|
||||
{!!slippageError && (
|
||||
{slippageError || tooLow || tooHigh ? (
|
||||
<RowBetween
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
paddingTop: '7px',
|
||||
color: slippageError === SlippageError.InvalidInput ? 'red' : '#F3841E',
|
||||
color: slippageError ? 'red' : '#F3841E',
|
||||
}}
|
||||
>
|
||||
{slippageError === SlippageError.InvalidInput
|
||||
{slippageError
|
||||
? 'Enter a valid slippage percentage'
|
||||
: slippageError === SlippageError.RiskyLow
|
||||
: tooLow
|
||||
? 'Your transaction may fail'
|
||||
: 'Your transaction may be frontrun'}
|
||||
</RowBetween>
|
||||
)}
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
|
||||
<AutoColumn gap="sm">
|
||||
@@ -231,15 +217,22 @@ export default function SlippageTabs({ rawSlippage, setRawSlippage, deadline, se
|
||||
<QuestionHelper text="Your transaction will revert if it is pending for more than this period of time." />
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<OptionCustom style={{ width: '80px' }} tabIndex={-1}>
|
||||
<OptionCustom style={{ width: '80px' }} warning={!!deadlineError} tabIndex={-1}>
|
||||
<Input
|
||||
color={!!deadlineError ? 'red' : undefined}
|
||||
onBlur={() => {
|
||||
parseCustomDeadline((deadline / 60).toString())
|
||||
}}
|
||||
placeholder={(deadline / 60).toString()}
|
||||
value={deadlineInput}
|
||||
placeholder={(DEFAULT_DEADLINE_FROM_NOW / 60).toString()}
|
||||
value={
|
||||
deadlineInput.length > 0
|
||||
? deadlineInput
|
||||
: deadline === DEFAULT_DEADLINE_FROM_NOW
|
||||
? ''
|
||||
: (deadline / 60).toString()
|
||||
}
|
||||
onChange={(e) => parseCustomDeadline(e.target.value)}
|
||||
onBlur={() => {
|
||||
setDeadlineInput('')
|
||||
setDeadlineError(false)
|
||||
}}
|
||||
color={deadlineError ? 'red' : ''}
|
||||
/>
|
||||
</OptionCustom>
|
||||
<TYPE.body style={{ paddingLeft: '8px' }} fontSize={14}>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Percent } 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 { useUserSlippageTolerance } from '../../state/user/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { computePriceImpactWithMaximumSlippage } from '../../utils/computePriceImpactWithMaximumSlippage'
|
||||
import { computeRealizedLPFeeAmount } from '../../utils/prices'
|
||||
@@ -13,13 +13,13 @@ import SwapRoute from './SwapRoute'
|
||||
|
||||
export interface AdvancedSwapDetailsProps {
|
||||
trade?: V2Trade | V3Trade
|
||||
allowedSlippage: Percent
|
||||
}
|
||||
|
||||
export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) {
|
||||
export function AdvancedSwapDetails({ trade, allowedSlippage }: AdvancedSwapDetailsProps) {
|
||||
const theme = useContext(ThemeContext)
|
||||
|
||||
const realizedLPFee = computeRealizedLPFeeAmount(trade)
|
||||
const [allowedSlippage] = useUserSlippageTolerance()
|
||||
|
||||
return !trade ? null : (
|
||||
<AutoColumn gap="8px">
|
||||
@@ -55,6 +55,17 @@ export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) {
|
||||
<FormattedPriceImpact priceImpact={computePriceImpactWithMaximumSlippage(trade, allowedSlippage)} />
|
||||
</TYPE.black>
|
||||
</RowBetween>
|
||||
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={12} fontWeight={400} color={theme.text2}>
|
||||
Slippage tolerance
|
||||
</TYPE.black>
|
||||
</RowFixed>
|
||||
<TYPE.black textAlign="right" fontSize={12} color={theme.text1}>
|
||||
{allowedSlippage.toFixed(2)}%
|
||||
</TYPE.black>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import Settings from '../Settings'
|
||||
import SettingsTab from '../Settings'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import { TYPE } from '../../theme'
|
||||
@@ -11,7 +12,7 @@ const StyledSwapHeader = styled.div`
|
||||
color: ${({ theme }) => theme.text2};
|
||||
`
|
||||
|
||||
export default function SwapHeader() {
|
||||
export default function SwapHeader({ allowedSlippage }: { allowedSlippage: Percent }) {
|
||||
return (
|
||||
<StyledSwapHeader>
|
||||
<RowBetween>
|
||||
@@ -21,9 +22,7 @@ export default function SwapHeader() {
|
||||
</TYPE.black>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
{/* <TradeInfo disabled={!trade} trade={trade} /> */}
|
||||
{/* <div style={{ width: '8px' }}></div> */}
|
||||
<Settings />
|
||||
<SettingsTab placeholderSlippage={allowedSlippage} />
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
</StyledSwapHeader>
|
||||
|
||||
@@ -135,7 +135,7 @@ export default function SwapModalHeader({
|
||||
</RowBetween>
|
||||
|
||||
<LightCard style={{ padding: '.75rem', marginTop: '0.5rem' }}>
|
||||
<AdvancedSwapDetails trade={trade} />
|
||||
<AdvancedSwapDetails trade={trade} allowedSlippage={allowedSlippage} />
|
||||
</LightCard>
|
||||
|
||||
{showAcceptChanges ? (
|
||||
|
||||
@@ -25,7 +25,7 @@ export default memo(function SwapRoute({ trade }: { trade: V2Trade | V3Trade })
|
||||
return (
|
||||
<Fragment key={i}>
|
||||
<Flex alignItems="end">
|
||||
<TYPE.black fontSize={14} color={theme.text1} ml="0.145rem" mr="0.145rem">
|
||||
<TYPE.black color={theme.text1} ml="0.145rem" mr="0.145rem">
|
||||
{currency.symbol}
|
||||
</TYPE.black>
|
||||
</Flex>
|
||||
|
||||
@@ -39,6 +39,37 @@ export const TRIBE = new Token(ChainId.MAINNET, '0xc7283b66Eb1EB5FB86327f08e1B58
|
||||
export const FRAX = new Token(ChainId.MAINNET, '0x853d955aCEf822Db058eb8505911ED77F175b99e', 18, 'FRAX', 'Frax')
|
||||
export const FXS = new Token(ChainId.MAINNET, '0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0', 18, 'FXS', 'Frax Share')
|
||||
export const renBTC = new Token(ChainId.MAINNET, '0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D', 8, 'renBTC', 'renBTC')
|
||||
export const UMA = new Token(
|
||||
ChainId.MAINNET,
|
||||
'0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828',
|
||||
18,
|
||||
'UMA',
|
||||
'UMA Voting Token v1'
|
||||
)
|
||||
|
||||
// Mirror Protocol compat.
|
||||
export const UST = new Token(ChainId.MAINNET, '0xa47c8bf37f92abed4a126bda807a7b7498661acd', 18, 'UST', 'Wrapped UST')
|
||||
export const MIR = new Token(ChainId.MAINNET, '0x09a3ecafa817268f77be1283176b946c4ff2e608', 18, 'MIR', 'Wrapped MIR')
|
||||
// List of all mirror's assets addresses.
|
||||
// Last pulled from : https://whitelist.mirror.finance/eth/tokenlists.json
|
||||
// TODO: Generate this programaticaly ?
|
||||
const mAssetsAdditionalBases: { [tokenAddress: string]: Token[] } = {
|
||||
[UST.address]: [MIR],
|
||||
[MIR.address]: [UST],
|
||||
'0xd36932143F6eBDEDD872D5Fb0651f4B72Fd15a84': [MIR, UST], // mAAPL
|
||||
'0x59A921Db27Dd6d4d974745B7FfC5c33932653442': [MIR, UST], // mGOOGL
|
||||
'0x21cA39943E91d704678F5D00b6616650F066fD63': [MIR, UST], // mTSLA
|
||||
'0xC8d674114bac90148d11D3C1d33C61835a0F9DCD': [MIR, UST], // mNFLX
|
||||
'0x13B02c8dE71680e71F0820c996E4bE43c2F57d15': [MIR, UST], // mQQQ
|
||||
'0xEdb0414627E6f1e3F082DE65cD4F9C693D78CCA9': [MIR, UST], // mTWTR
|
||||
'0x41BbEDd7286dAab5910a1f15d12CBda839852BD7': [MIR, UST], // mMSFT
|
||||
'0x0cae9e4d663793c2a2A0b211c1Cf4bBca2B9cAa7': [MIR, UST], // mAMZN
|
||||
'0x56aA298a19C93c6801FDde870fA63EF75Cc0aF72': [MIR, UST], // mBABA
|
||||
'0x1d350417d9787E000cc1b95d70E9536DcD91F373': [MIR, UST], // mIAU
|
||||
'0x9d1555d8cB3C846Bb4f7D5B1B1080872c3166676': [MIR, UST], // mSLV
|
||||
'0x31c63146a635EB7465e5853020b39713AC356991': [MIR, UST], // mUSO
|
||||
'0xf72FCd9DCF0190923Fadd44811E240Ef4533fc86': [MIR, UST], // mVIXY
|
||||
}
|
||||
|
||||
// Block time here is slightly higher (~1s) than average in order to avoid ongoing proposals past the displayed time
|
||||
export const AVERAGE_BLOCK_TIME_IN_SECS = 13
|
||||
@@ -84,8 +115,10 @@ export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = {
|
||||
|
||||
export const ADDITIONAL_BASES: { [chainId in ChainId]?: { [tokenAddress: string]: Token[] } } = {
|
||||
[ChainId.MAINNET]: {
|
||||
...mAssetsAdditionalBases,
|
||||
'0xA948E86885e12Fb09AfEF8C52142EBDbDf73cD18': [UNI[ChainId.MAINNET]],
|
||||
'0x561a4717537ff4AF5c687328c0f7E90a319705C0': [UNI[ChainId.MAINNET]],
|
||||
'0xa6e3454fec677772dd771788a079355e43910638': [UMA],
|
||||
[FEI.address]: [TRIBE],
|
||||
[TRIBE.address]: [FEI],
|
||||
[FRAX.address]: [FXS],
|
||||
@@ -205,10 +238,8 @@ export const SUPPORTED_WALLETS: { [key: string]: WalletInfo } = {
|
||||
|
||||
export const NetworkContextName = 'NETWORK'
|
||||
|
||||
// default allowed slippage, in bips
|
||||
export const INITIAL_ALLOWED_SLIPPAGE = new Percent(10, 10_000)
|
||||
// 20 minutes, denominated in seconds
|
||||
export const DEFAULT_DEADLINE_FROM_NOW = 60 * 20
|
||||
// 30 minutes, denominated in seconds
|
||||
export const DEFAULT_DEADLINE_FROM_NOW = 60 * 30
|
||||
|
||||
// used for rewards deadlines
|
||||
export const BIG_INT_SECONDS_IN_WEEK = JSBI.BigInt(60 * 60 * 24 * 7)
|
||||
|
||||
@@ -160,10 +160,10 @@ export function useSocksController(): Unisocks | null {
|
||||
) as Unisocks | null
|
||||
}
|
||||
|
||||
export function useV3NFTPositionManagerContract(): NonfungiblePositionManager | null {
|
||||
export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean): NonfungiblePositionManager | null {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const address = chainId ? NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[chainId] : undefined
|
||||
return useContract(address, NFTPositionManagerABI) as NonfungiblePositionManager | null
|
||||
return useContract(address, NFTPositionManagerABI, withSignerIfPossible) as NonfungiblePositionManager | null
|
||||
}
|
||||
|
||||
export function useV3Factory(): UniswapV3Factory | null {
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Router, Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { SwapRouter, Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import { ChainId, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import { useMemo } from 'react'
|
||||
import { INITIAL_ALLOWED_SLIPPAGE } from '../constants'
|
||||
import { SWAP_ROUTER_ADDRESSES } from '../constants/v3'
|
||||
import { getTradeVersion } from '../utils/getTradeVersion'
|
||||
import { useTransactionAdder } from '../state/transactions/hooks'
|
||||
@@ -51,7 +50,7 @@ interface FailedCall extends SwapCallEstimate {
|
||||
*/
|
||||
function useSwapCallArguments(
|
||||
trade: V2Trade | V3Trade | undefined, // trade to execute, required
|
||||
allowedSlippage: Percent = INITIAL_ALLOWED_SLIPPAGE, // in bips
|
||||
allowedSlippage: Percent, // in bips
|
||||
recipientAddressOrName: string | null, // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
|
||||
signatureData: SignatureData | null | undefined
|
||||
): SwapCall[] {
|
||||
@@ -138,7 +137,7 @@ function useSwapCallArguments(
|
||||
// and the user has approved the slippage adjusted input amount for the trade
|
||||
export function useSwapCallback(
|
||||
trade: V2Trade | V3Trade | undefined, // trade to execute, required
|
||||
allowedSlippage: Percent = INITIAL_ALLOWED_SLIPPAGE, // in bips
|
||||
allowedSlippage: Percent, // in bips
|
||||
recipientAddressOrName: string | null, // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
|
||||
signatureData: SignatureData | undefined | null
|
||||
): { state: SwapCallbackState; callback: null | (() => Promise<string>); error: string | null } {
|
||||
|
||||
18
src/hooks/useSwapSlippageTolerance.ts
Normal file
18
src/hooks/useSwapSlippageTolerance.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import { Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import { Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { useMemo } from 'react'
|
||||
import { useUserSlippageToleranceWithDefault } from '../state/user/hooks'
|
||||
|
||||
const V2_SWAP_DEFAULT_SLIPPAGE = new Percent(50, 10_000) // .50%
|
||||
const V3_SWAP_DEFAULT_SLIPPAGE = new Percent(50, 10_000) // .50%
|
||||
const ONE_TENTHS_PERCENT = new Percent(10, 10_000) // .10%
|
||||
|
||||
export default function useSwapSlippageTolerance(trade: V2Trade | V3Trade | undefined): Percent {
|
||||
const defaultSlippageTolerance = useMemo(() => {
|
||||
if (!trade) return ONE_TENTHS_PERCENT
|
||||
if (trade instanceof V2Trade) return V2_SWAP_DEFAULT_SLIPPAGE
|
||||
return V3_SWAP_DEFAULT_SLIPPAGE
|
||||
}, [trade])
|
||||
return useUserSlippageToleranceWithDefault(defaultSlippageTolerance)
|
||||
}
|
||||
@@ -1,126 +1,53 @@
|
||||
import { useSingleCallResult } from 'state/multicall/hooks'
|
||||
import { useMemo } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { PositionDetails } from 'types/position'
|
||||
import { useV3Pool } from './useContract'
|
||||
import { useV3NFTPositionManagerContract } from './useContract'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { computePoolAddress, Pool } from '@uniswap/v3-sdk'
|
||||
import { V3_CORE_FACTORY_ADDRESSES } from 'constants/v3'
|
||||
import { useActiveWeb3React } from 'hooks'
|
||||
import { Pool } from '@uniswap/v3-sdk'
|
||||
import { TokenAmount } from '@uniswap/sdk-core'
|
||||
import { useBlockNumber } from 'state/application/hooks'
|
||||
|
||||
// TODO port these utility functions to the SDK
|
||||
|
||||
function subIn256(x: BigNumber, y: BigNumber): BigNumber {
|
||||
const difference = x.sub(y)
|
||||
return difference.lt(0) ? BigNumber.from(2).pow(256).add(difference) : difference
|
||||
}
|
||||
|
||||
function getCounterfactualFees(
|
||||
feeGrowthGlobal: BigNumber,
|
||||
feeGrowthOutsideLower: BigNumber,
|
||||
feeGrowthOutsideUpper: BigNumber,
|
||||
feeGrowthInsideLast: BigNumber,
|
||||
pool: Pool,
|
||||
liquidity: BigNumber,
|
||||
tickLower: number,
|
||||
tickUpper: number
|
||||
) {
|
||||
let feeGrowthBelow: BigNumber
|
||||
if (pool.tickCurrent >= tickLower) {
|
||||
feeGrowthBelow = feeGrowthOutsideLower
|
||||
} else {
|
||||
feeGrowthBelow = subIn256(feeGrowthGlobal, feeGrowthOutsideLower)
|
||||
}
|
||||
|
||||
let feeGrowthAbove: BigNumber
|
||||
if (pool.tickCurrent < tickUpper) {
|
||||
feeGrowthAbove = feeGrowthOutsideUpper
|
||||
} else {
|
||||
feeGrowthAbove = subIn256(feeGrowthGlobal, feeGrowthOutsideUpper)
|
||||
}
|
||||
|
||||
const feeGrowthInside = subIn256(subIn256(feeGrowthGlobal, feeGrowthBelow), feeGrowthAbove)
|
||||
|
||||
return subIn256(feeGrowthInside, feeGrowthInsideLast).mul(liquidity).div(BigNumber.from(2).pow(128))
|
||||
}
|
||||
const MAX_UINT128 = BigNumber.from(2).pow(128).sub(1)
|
||||
|
||||
// compute current + counterfactual fees for a v3 position
|
||||
export function useV3PositionFees(
|
||||
pool?: Pool,
|
||||
positionDetails?: PositionDetails
|
||||
): [TokenAmount, TokenAmount] | [undefined, undefined] {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const positionManager = useV3NFTPositionManagerContract(false)
|
||||
const owner = useSingleCallResult(positionDetails?.tokenId ? positionManager : null, 'ownerOf', [
|
||||
positionDetails?.tokenId,
|
||||
]).result?.[0]
|
||||
|
||||
const poolAddress = useMemo(() => {
|
||||
try {
|
||||
return chainId && V3_CORE_FACTORY_ADDRESSES[chainId] && pool && positionDetails
|
||||
? computePoolAddress({
|
||||
factoryAddress: V3_CORE_FACTORY_ADDRESSES[chainId] as string,
|
||||
tokenA: pool.token0,
|
||||
tokenB: pool.token1,
|
||||
fee: positionDetails.fee,
|
||||
})
|
||||
: undefined
|
||||
} catch {
|
||||
return undefined
|
||||
const tokenId = positionDetails?.tokenId?.toHexString()
|
||||
const latestBlockNumber = useBlockNumber()
|
||||
|
||||
// TODO find a way to get this into multicall
|
||||
// because fees don't ever go down, we don't actually need to clear this state
|
||||
// latestBlockNumber is included to ensure data stays up-to-date fresh
|
||||
const [amounts, setAmounts] = useState<[BigNumber, BigNumber]>()
|
||||
useEffect(() => {
|
||||
if (positionManager && tokenId && owner && typeof latestBlockNumber === 'number') {
|
||||
positionManager.callStatic
|
||||
.collect(
|
||||
{
|
||||
tokenId,
|
||||
recipient: owner, // some tokens might fail if transferred to address(0)
|
||||
amount0Max: MAX_UINT128,
|
||||
amount1Max: MAX_UINT128,
|
||||
},
|
||||
{ from: owner } // need to simulate the call as the owner
|
||||
)
|
||||
.then((results) => {
|
||||
setAmounts([results.amount0, results.amount1])
|
||||
})
|
||||
}
|
||||
}, [chainId, pool, positionDetails])
|
||||
const poolContract = useV3Pool(poolAddress)
|
||||
}, [positionManager, tokenId, owner, latestBlockNumber])
|
||||
|
||||
// data fetching
|
||||
const feeGrowthGlobal0: BigNumber | undefined = useSingleCallResult(poolContract, 'feeGrowthGlobal0X128')?.result?.[0]
|
||||
const feeGrowthGlobal1: BigNumber | undefined = useSingleCallResult(poolContract, 'feeGrowthGlobal1X128')?.result?.[0]
|
||||
const { feeGrowthOutside0X128: feeGrowthOutsideLower0 } = (useSingleCallResult(poolContract, 'ticks', [
|
||||
positionDetails?.tickLower,
|
||||
])?.result ?? {}) as { feeGrowthOutside0X128?: BigNumber }
|
||||
const { feeGrowthOutside1X128: feeGrowthOutsideLower1 } = (useSingleCallResult(poolContract, 'ticks', [
|
||||
positionDetails?.tickLower,
|
||||
])?.result ?? {}) as { feeGrowthOutside1X128?: BigNumber }
|
||||
const { feeGrowthOutside0X128: feeGrowthOutsideUpper0 } = (useSingleCallResult(poolContract, 'ticks', [
|
||||
positionDetails?.tickUpper,
|
||||
])?.result ?? {}) as { feeGrowthOutside0X128?: BigNumber }
|
||||
const { feeGrowthOutside1X128: feeGrowthOutsideUpper1 } = (useSingleCallResult(poolContract, 'ticks', [
|
||||
positionDetails?.tickUpper,
|
||||
])?.result ?? {}) as { feeGrowthOutside1X128?: BigNumber }
|
||||
|
||||
// calculate fees
|
||||
const counterfactualFees0 =
|
||||
positionDetails && pool && feeGrowthGlobal0 && feeGrowthOutsideLower0 && feeGrowthOutsideUpper0
|
||||
? getCounterfactualFees(
|
||||
feeGrowthGlobal0,
|
||||
feeGrowthOutsideLower0,
|
||||
feeGrowthOutsideUpper0,
|
||||
positionDetails.feeGrowthInside0LastX128,
|
||||
pool,
|
||||
positionDetails.liquidity,
|
||||
positionDetails.tickLower,
|
||||
positionDetails.tickUpper
|
||||
)
|
||||
: undefined
|
||||
const counterfactualFees1 =
|
||||
positionDetails && pool && feeGrowthGlobal1 && feeGrowthOutsideLower1 && feeGrowthOutsideUpper1
|
||||
? getCounterfactualFees(
|
||||
feeGrowthGlobal1,
|
||||
feeGrowthOutsideLower1,
|
||||
feeGrowthOutsideUpper1,
|
||||
positionDetails.feeGrowthInside1LastX128,
|
||||
pool,
|
||||
positionDetails.liquidity,
|
||||
positionDetails.tickLower,
|
||||
positionDetails.tickUpper
|
||||
)
|
||||
: undefined
|
||||
|
||||
if (
|
||||
pool &&
|
||||
positionDetails?.tokensOwed0 &&
|
||||
positionDetails?.tokensOwed1 &&
|
||||
counterfactualFees0 &&
|
||||
counterfactualFees1
|
||||
) {
|
||||
if (pool && positionDetails && amounts) {
|
||||
return [
|
||||
new TokenAmount(pool.token0, positionDetails.tokensOwed0.add(counterfactualFees0).toString()),
|
||||
new TokenAmount(pool.token1, positionDetails.tokensOwed1.add(counterfactualFees1).toString()),
|
||||
new TokenAmount(pool.token0, positionDetails.tokensOwed0.add(amounts[0]).toString()),
|
||||
new TokenAmount(pool.token1, positionDetails.tokensOwed1.add(amounts[1]).toString()),
|
||||
]
|
||||
} else {
|
||||
return [undefined, undefined]
|
||||
|
||||
@@ -46,13 +46,6 @@ if (typeof GOOGLE_ANALYTICS_ID === 'string') {
|
||||
ReactGA.initialize('test', { testMode: true, debug: true })
|
||||
}
|
||||
|
||||
window.addEventListener('error', (error) => {
|
||||
ReactGA.exception({
|
||||
description: `${error.message} @ ${error.filename}:${error.lineno}:${error.colno}`,
|
||||
fatal: true,
|
||||
})
|
||||
})
|
||||
|
||||
function Updaters() {
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { useCallback, useContext, useMemo, useState, useEffect } from 'react'
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { Currency, TokenAmount, ETHER, currencyEquals } from '@uniswap/sdk-core'
|
||||
import { Currency, TokenAmount, ETHER, currencyEquals, Percent } from '@uniswap/sdk-core'
|
||||
import { WETH9 } from '@uniswap/sdk-core'
|
||||
import { AlertTriangle, AlertCircle } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import { ZERO_PERCENT } from '../../constants'
|
||||
import { useV3NFTPositionManagerContract } from '../../hooks/useContract'
|
||||
import { RouteComponentProps } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
@@ -13,7 +14,7 @@ import { YellowCard, OutlineCard, BlueCard, LightCard } from '../../components/C
|
||||
import { AutoColumn } from '../../components/Column'
|
||||
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
|
||||
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||
import { RowBetween } from '../../components/Row'
|
||||
import { RowBetween, RowFixed } from '../../components/Row'
|
||||
import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported'
|
||||
import { useUSDCValue } from '../../hooks/useUSDCPrice'
|
||||
import Review from './Review'
|
||||
@@ -25,7 +26,7 @@ import { useWalletModalToggle } from '../../state/application/hooks'
|
||||
import { Field, Bound } from '../../state/mint/v3/actions'
|
||||
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { useIsExpertMode, useUserSlippageTolerance } from '../../state/user/hooks'
|
||||
import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
|
||||
import { TYPE, ExternalLink } from '../../theme'
|
||||
import { maxAmountSpend } from '../../utils/maxAmountSpend'
|
||||
import AppBody from '../AppBody'
|
||||
@@ -51,6 +52,9 @@ import RateToggle from 'components/RateToggle'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { calculateGasMargin } from 'utils'
|
||||
import { AddRemoveTabs } from 'components/NavigationTabs'
|
||||
import HoverInlineText from 'components/HoverInlineText'
|
||||
|
||||
const DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE = new Percent(50, 10_000)
|
||||
|
||||
export default function AddLiquidity({
|
||||
match: {
|
||||
@@ -148,7 +152,7 @@ export default function AddLiquidity({
|
||||
|
||||
// txn values
|
||||
const deadline = useTransactionDeadline() // custom from users settings
|
||||
const [allowedSlippage] = useUserSlippageTolerance() // custom from users
|
||||
|
||||
const [txHash, setTxHash] = useState<string>('')
|
||||
|
||||
// get formatted amounts
|
||||
@@ -193,6 +197,10 @@ export default function AddLiquidity({
|
||||
chainId ? NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[chainId] : undefined
|
||||
)
|
||||
|
||||
const allowedSlippage = useUserSlippageToleranceWithDefault(
|
||||
outOfRange ? ZERO_PERCENT : DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE
|
||||
)
|
||||
|
||||
async function onAdd() {
|
||||
if (!chainId || !library || !account) return
|
||||
|
||||
@@ -396,7 +404,12 @@ export default function AddLiquidity({
|
||||
pendingText={pendingText}
|
||||
/>
|
||||
<AppBody>
|
||||
<AddRemoveTabs creating={false} adding={true} positionID={tokenId} />
|
||||
<AddRemoveTabs
|
||||
creating={false}
|
||||
adding={true}
|
||||
positionID={tokenId}
|
||||
defaultSlippage={DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE}
|
||||
/>
|
||||
<Wrapper>
|
||||
<AutoColumn gap="32px">
|
||||
{!hasExistingPosition && (
|
||||
@@ -486,8 +499,13 @@ export default function AddLiquidity({
|
||||
<TYPE.main>
|
||||
{price ? (
|
||||
<TYPE.main>
|
||||
{invertPrice ? price?.invert()?.toSignificant(8) : price?.toSignificant(8)}{' '}
|
||||
{quoteCurrency?.symbol}
|
||||
<RowFixed>
|
||||
<HoverInlineText
|
||||
maxCharacters={20}
|
||||
text={invertPrice ? price?.invert()?.toSignificant(5) : price?.toSignificant(5)}
|
||||
/>{' '}
|
||||
<span style={{ marginLeft: '4px' }}>{quoteCurrency?.symbol}</span>
|
||||
</RowFixed>
|
||||
</TYPE.main>
|
||||
) : (
|
||||
'-'
|
||||
@@ -540,7 +558,7 @@ export default function AddLiquidity({
|
||||
<TYPE.main fontSize={14} fontWeight={400} style={{ marginBottom: '.5rem', lineHeight: '125%' }}>
|
||||
Your liquidity will only earn fees when the market price of the pair is within your range.{' '}
|
||||
<ExternalLink
|
||||
href={'https://docs.uniswap.org/concepts/introduction/liquidity-user-guide'}
|
||||
href={'https://docs.uniswap.org/concepts/introduction/liquidity-user-guide#4-set-price-range'}
|
||||
style={{ fontSize: '14px' }}
|
||||
>
|
||||
Need help picking a range?
|
||||
@@ -568,10 +586,13 @@ export default function AddLiquidity({
|
||||
Current Price
|
||||
</TYPE.main>
|
||||
<TYPE.body fontWeight={500} textAlign="center" fontSize={20}>
|
||||
{invertPrice ? price.invert().toSignificant(3) : price.toSignificant(3)}{' '}
|
||||
<HoverInlineText
|
||||
maxCharacters={20}
|
||||
text={invertPrice ? price.invert().toSignificant(5) : price.toSignificant(5)}
|
||||
/>{' '}
|
||||
</TYPE.body>
|
||||
<TYPE.main fontWeight={500} textAlign="center" fontSize={12}>
|
||||
{quoteCurrency?.symbol} {' / '}
|
||||
{quoteCurrency?.symbol} {' per '}
|
||||
{baseCurrency.symbol}
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { Currency, currencyEquals, ETHER, TokenAmount, WETH9 } from '@uniswap/sdk-core'
|
||||
import { Currency, currencyEquals, ETHER, Percent, TokenAmount, WETH9 } from '@uniswap/sdk-core'
|
||||
import React, { useCallback, useContext, useState } from 'react'
|
||||
import { Plus } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
@@ -30,7 +30,7 @@ import { Field } from '../../state/mint/actions'
|
||||
import { useDerivedMintInfo, useMintActionHandlers, useMintState } from '../../state/mint/hooks'
|
||||
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { useIsExpertMode, useUserSlippageTolerance } from '../../state/user/hooks'
|
||||
import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { calculateGasMargin, calculateSlippageAmount } from '../../utils'
|
||||
import { maxAmountSpend } from '../../utils/maxAmountSpend'
|
||||
@@ -42,6 +42,8 @@ import { currencyId } from '../../utils/currencyId'
|
||||
import { PoolPriceBar } from './PoolPriceBar'
|
||||
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
|
||||
|
||||
const DEFAULT_ADD_V2_SLIPPAGE_TOLERANCE = new Percent(50, 10_000)
|
||||
|
||||
export default function AddLiquidity({
|
||||
match: {
|
||||
params: { currencyIdA, currencyIdB },
|
||||
@@ -90,7 +92,7 @@ export default function AddLiquidity({
|
||||
|
||||
// txn values
|
||||
const deadline = useTransactionDeadline() // custom from users settings
|
||||
const [allowedSlippage] = useUserSlippageTolerance() // custom from users
|
||||
const allowedSlippage = useUserSlippageToleranceWithDefault(DEFAULT_ADD_V2_SLIPPAGE_TOLERANCE) // custom from users
|
||||
const [txHash, setTxHash] = useState<string>('')
|
||||
|
||||
// get formatted amounts
|
||||
@@ -315,7 +317,7 @@ export default function AddLiquidity({
|
||||
return (
|
||||
<>
|
||||
<AppBody>
|
||||
<AddRemoveTabs creating={isCreate} adding={true} />
|
||||
<AddRemoveTabs creating={isCreate} adding={true} defaultSlippage={DEFAULT_ADD_V2_SLIPPAGE_TOLERANCE} />
|
||||
<Wrapper>
|
||||
<TransactionConfirmationModal
|
||||
isOpen={showConfirm}
|
||||
|
||||
@@ -8,6 +8,7 @@ import Polling from '../components/Header/Polling'
|
||||
// import URLWarning from '../components/Header/URLWarning'
|
||||
import Popups from '../components/Popups'
|
||||
import Web3ReactManager from '../components/Web3ReactManager'
|
||||
import ErrorBoundary from '../components/ErrorBoundary'
|
||||
import { ApplicationModal } from '../state/application/actions'
|
||||
import { useModalOpen, useToggleModal } from '../state/application/hooks'
|
||||
import DarkModeQueryParamReader from '../theme/DarkModeQueryParamReader'
|
||||
@@ -72,62 +73,69 @@ function TopLevelModals() {
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<Route component={GoogleAnalyticsReporter} />
|
||||
<Route component={DarkModeQueryParamReader} />
|
||||
<AppWrapper>
|
||||
<HeaderWrapper>
|
||||
<Header />
|
||||
</HeaderWrapper>
|
||||
<BodyWrapper>
|
||||
<ThemedBackground />
|
||||
<Popups />
|
||||
<Polling />
|
||||
<TopLevelModals />
|
||||
<Web3ReactManager>
|
||||
<Switch>
|
||||
<Route exact strict path="/vote" component={Vote} />
|
||||
<Route exact strict path="/vote/:id" component={VotePage} />
|
||||
<Route exact strict path="/claim" component={OpenClaimAddressModalAndRedirectToSwap} />
|
||||
<Route exact strict path="/uni" component={Earn} />
|
||||
<Route exact strict path="/uni/:currencyIdA/:currencyIdB" component={Manage} />
|
||||
<ErrorBoundary>
|
||||
<Suspense fallback={null}>
|
||||
<Route component={GoogleAnalyticsReporter} />
|
||||
<Route component={DarkModeQueryParamReader} />
|
||||
<AppWrapper>
|
||||
<HeaderWrapper>
|
||||
<Header />
|
||||
</HeaderWrapper>
|
||||
<BodyWrapper>
|
||||
<ThemedBackground />
|
||||
<Popups />
|
||||
<Polling />
|
||||
<TopLevelModals />
|
||||
<Web3ReactManager>
|
||||
<Switch>
|
||||
<Route exact strict path="/vote" component={Vote} />
|
||||
<Route exact strict path="/vote/:id" component={VotePage} />
|
||||
<Route exact strict path="/claim" component={OpenClaimAddressModalAndRedirectToSwap} />
|
||||
<Route exact strict path="/uni" component={Earn} />
|
||||
<Route exact strict path="/uni/:currencyIdA/:currencyIdB" component={Manage} />
|
||||
|
||||
<Route exact strict path="/send" component={RedirectPathToSwapOnly} />
|
||||
<Route exact strict path="/swap/:outputCurrency" component={RedirectToSwap} />
|
||||
<Route exact strict path="/swap" component={Swap} />
|
||||
<Route exact strict path="/send" component={RedirectPathToSwapOnly} />
|
||||
<Route exact strict path="/swap/:outputCurrency" component={RedirectToSwap} />
|
||||
<Route exact strict path="/swap" component={Swap} />
|
||||
|
||||
<Route exact strict path="/find" component={PoolFinder} />
|
||||
<Route exact strict path="/pool/v2" component={PoolV2} />
|
||||
<Route exact strict path="/pool" component={Pool} />
|
||||
<Route exact strict path="/pool/:tokenId" component={PositionPage} />
|
||||
<Route exact strict path="/find" component={PoolFinder} />
|
||||
<Route exact strict path="/pool/v2" component={PoolV2} />
|
||||
<Route exact strict path="/pool" component={Pool} />
|
||||
<Route exact strict path="/pool/:tokenId" component={PositionPage} />
|
||||
|
||||
<Route exact strict path="/add/v2/:currencyIdA?/:currencyIdB?" component={RedirectDuplicateTokenIdsV2} />
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path="/add/:currencyIdA?/:currencyIdB?/:feeAmount?"
|
||||
component={RedirectDuplicateTokenIds}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path="/add/v2/:currencyIdA?/:currencyIdB?"
|
||||
component={RedirectDuplicateTokenIdsV2}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path="/add/:currencyIdA?/:currencyIdB?/:feeAmount?"
|
||||
component={RedirectDuplicateTokenIds}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path="/increase/:currencyIdA?/:currencyIdB?/:feeAmount?/:tokenId?"
|
||||
component={AddLiquidity}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path="/increase/:currencyIdA?/:currencyIdB?/:feeAmount?/:tokenId?"
|
||||
component={AddLiquidity}
|
||||
/>
|
||||
|
||||
<Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
|
||||
<Route exact strict path="/remove/:tokenId" component={RemoveLiquidityV3} />
|
||||
<Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
|
||||
<Route exact strict path="/remove/:tokenId" component={RemoveLiquidityV3} />
|
||||
|
||||
<Route exact strict path="/migrate/v2" component={MigrateV2} />
|
||||
<Route exact strict path="/migrate/v2/:address" component={MigrateV2Pair} />
|
||||
<Route exact strict path="/migrate/v2" component={MigrateV2} />
|
||||
<Route exact strict path="/migrate/v2/:address" component={MigrateV2Pair} />
|
||||
|
||||
<Route component={RedirectPathToSwapOnly} />
|
||||
</Switch>
|
||||
</Web3ReactManager>
|
||||
<Marginer />
|
||||
</BodyWrapper>
|
||||
</AppWrapper>
|
||||
</Suspense>
|
||||
<Route component={RedirectPathToSwapOnly} />
|
||||
</Switch>
|
||||
</Web3ReactManager>
|
||||
<Marginer />
|
||||
</BodyWrapper>
|
||||
</AppWrapper>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { useCallback, useMemo, useState, useEffect } from 'react'
|
||||
import { Fraction, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
|
||||
import { Fraction, Percent, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
|
||||
import { FACTORY_ADDRESS, JSBI } from '@uniswap/v2-sdk'
|
||||
import { Redirect, RouteComponentProps } from 'react-router'
|
||||
import { Text } from 'rebass'
|
||||
import { AutoColumn } from '../../components/Column'
|
||||
import CurrencyLogo from '../../components/CurrencyLogo'
|
||||
import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
|
||||
import QuestionHelper from '../../components/QuestionHelper'
|
||||
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
|
||||
import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
@@ -16,7 +15,7 @@ import { usePairContract, useV2MigratorContract } from '../../hooks/useContract'
|
||||
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
|
||||
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { BackArrow, ExternalLink, TYPE } from '../../theme'
|
||||
import { getEtherscanLink, isAddress } from '../../utils'
|
||||
import { calculateGasMargin, getEtherscanLink, isAddress } from '../../utils'
|
||||
import { BodyWrapper } from '../AppBody'
|
||||
import { V3_MIGRATOR_ADDRESSES } from 'constants/v3'
|
||||
import { PoolState, usePool } from 'hooks/usePools'
|
||||
@@ -26,7 +25,7 @@ import { ApprovalState, useApproveCallback } from 'hooks/useApproveCallback'
|
||||
import { Dots } from 'components/swap/styleds'
|
||||
import { ButtonConfirmed } from 'components/Button'
|
||||
import useTransactionDeadline from 'hooks/useTransactionDeadline'
|
||||
import { useUserSlippageTolerance } from 'state/user/hooks'
|
||||
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
|
||||
import ReactGA from 'react-ga'
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks'
|
||||
@@ -46,9 +45,12 @@ import DoubleCurrencyLogo from 'components/DoubleLogo'
|
||||
import Badge, { BadgeVariant } from 'components/Badge'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { AppDispatch } from 'state'
|
||||
import SettingsTab from 'components/Settings'
|
||||
|
||||
const ZERO = JSBI.BigInt(0)
|
||||
|
||||
const DEFAULT_MIGRATE_SLIPPAGE_TOLERANCE = new Percent(75, 10_000)
|
||||
|
||||
function EmptyState({ message }: { message: string }) {
|
||||
return (
|
||||
<AutoColumn style={{ minHeight: 200, justifyContent: 'center', alignItems: 'center' }}>
|
||||
@@ -119,7 +121,7 @@ function V2PairMigration({
|
||||
|
||||
const deadline = useTransactionDeadline() // custom from users settings
|
||||
const blockTimestamp = useCurrentBlockTimestamp()
|
||||
const [allowedSlippage] = useUserSlippageTolerance() // custom from users
|
||||
const allowedSlippage = useUserSlippageToleranceWithDefault(DEFAULT_MIGRATE_SLIPPAGE_TOLERANCE) // custom from users
|
||||
|
||||
const currency0 = unwrappedToken(token0)
|
||||
const currency1 = unwrappedToken(token1)
|
||||
@@ -274,7 +276,7 @@ function V2PairMigration({
|
||||
|
||||
const deadlineToUse = signatureData?.deadline ?? deadline
|
||||
|
||||
const data = []
|
||||
const data: string[] = []
|
||||
|
||||
// permit if necessary
|
||||
if (signatureData) {
|
||||
@@ -324,19 +326,24 @@ function V2PairMigration({
|
||||
)
|
||||
|
||||
setConfirmingMigration(true)
|
||||
migrator
|
||||
.multicall(data)
|
||||
.then((response: TransactionResponse) => {
|
||||
ReactGA.event({
|
||||
category: 'Migrate',
|
||||
action: `${isNotUniswap ? 'SushiSwap' : 'V2'}->V3`,
|
||||
label: `${currency0.symbol}/${currency1.symbol}`,
|
||||
})
|
||||
|
||||
addTransaction(response, {
|
||||
summary: `Migrate ${currency0.symbol}/${currency1.symbol} liquidity to V3`,
|
||||
})
|
||||
setPendingMigrationHash(response.hash)
|
||||
migrator.estimateGas
|
||||
.multicall(data)
|
||||
.then((gasEstimate) => {
|
||||
return migrator
|
||||
.multicall(data, { gasLimit: calculateGasMargin(gasEstimate) })
|
||||
.then((response: TransactionResponse) => {
|
||||
ReactGA.event({
|
||||
category: 'Migrate',
|
||||
action: `${isNotUniswap ? 'SushiSwap' : 'V2'}->V3`,
|
||||
label: `${currency0.symbol}/${currency1.symbol}`,
|
||||
})
|
||||
|
||||
addTransaction(response, {
|
||||
summary: `Migrate ${currency0.symbol}/${currency1.symbol} liquidity to V3`,
|
||||
})
|
||||
setPendingMigrationHash(response.hash)
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
setConfirmingMigration(false)
|
||||
@@ -668,9 +675,7 @@ export default function MigrateV2Pair({
|
||||
<AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px">
|
||||
<BackArrow to="/migrate/v2" />
|
||||
<TYPE.mediumHeader>Migrate V2 Liquidity</TYPE.mediumHeader>
|
||||
<div style={{ opacity: 0 }}>
|
||||
<QuestionHelper text="Migrate your liquidity tokens from Uniswap V2 to Uniswap V3." />
|
||||
</div>
|
||||
<SettingsTab placeholderSlippage={DEFAULT_MIGRATE_SLIPPAGE_TOLERANCE} />
|
||||
</AutoRow>
|
||||
|
||||
{!account ? (
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import Squiggle from '../../assets/images/squiggle.png'
|
||||
import Texture from '../../assets/images/sandtexture.png'
|
||||
import Texture from '../../assets/images/sandtexture.webp'
|
||||
import { RowBetween } from 'components/Row'
|
||||
|
||||
const CTASection = styled.section`
|
||||
@@ -28,6 +28,7 @@ const CTA1 = styled(ExternalLink)`
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
|
||||
@@ -99,8 +100,8 @@ const HeaderText = styled(TYPE.label)`
|
||||
`
|
||||
|
||||
const ResponsiveColumn = styled(AutoColumn)`
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
height: 100%;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
gap: 8px;
|
||||
`};
|
||||
@@ -108,6 +109,7 @@ const ResponsiveColumn = styled(AutoColumn)`
|
||||
`
|
||||
|
||||
const StyledImage = styled.img`
|
||||
height: 114px;
|
||||
margin-top: -28px;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
height: 80px;
|
||||
@@ -134,12 +136,10 @@ export default function CTACards() {
|
||||
</CTA1>
|
||||
<CTA2 href={'https://info.uniswap.org/#/pools'}>
|
||||
<ResponsiveColumn>
|
||||
<AutoColumn gap="0px">
|
||||
<HeaderText style={{ alignSelf: 'flex-start' }}>{t('Top pools')}</HeaderText>
|
||||
<TYPE.body fontWeight={300} style={{ alignSelf: 'flex-start' }}>
|
||||
{t('Explore popular pools on Uniswap Analytics.')}
|
||||
</TYPE.body>
|
||||
</AutoColumn>
|
||||
<HeaderText style={{ alignSelf: 'flex-start' }}>{t('Top pools')}</HeaderText>
|
||||
<TYPE.body fontWeight={300} style={{ alignSelf: 'flex-start' }}>
|
||||
{t('Explore popular pools on Uniswap Analytics.')}
|
||||
</TYPE.body>
|
||||
<HeaderText style={{ alignSelf: 'flex-end' }}>{t('↗')}</HeaderText>
|
||||
</ResponsiveColumn>
|
||||
</CTA2>
|
||||
|
||||
@@ -16,7 +16,7 @@ import { ExternalLink, HideExtraSmall, TYPE } from 'theme'
|
||||
import Badge from 'components/Badge'
|
||||
import { calculateGasMargin, getEtherscanLink } from 'utils'
|
||||
import { ButtonConfirmed, ButtonPrimary, ButtonGray } from 'components/Button'
|
||||
import { DarkCard, DarkGreyCard, LightCard } from 'components/Card'
|
||||
import { DarkCard, LightCard } from 'components/Card'
|
||||
import CurrencyLogo from 'components/CurrencyLogo'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { currencyId } from 'utils/currencyId'
|
||||
@@ -147,9 +147,9 @@ function CurrentPriceCard({
|
||||
<AutoColumn gap="8px" justify="center">
|
||||
<ExtentsText>{t('Current price')}</ExtentsText>
|
||||
<TYPE.mediumHeader textAlign="center">
|
||||
{(inverted ? pool.token1Price : pool.token0Price).toSignificant(4)}{' '}
|
||||
{(inverted ? pool.token1Price : pool.token0Price).toSignificant(5)}{' '}
|
||||
</TYPE.mediumHeader>
|
||||
<ExtentsText>{currencyQuote?.symbol + ' / ' + currencyBase?.symbol}</ExtentsText>
|
||||
<ExtentsText>{currencyQuote?.symbol + ' per ' + currencyBase?.symbol}</ExtentsText>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
)
|
||||
@@ -420,7 +420,7 @@ export function PositionPage({
|
||||
borderRadius="12px"
|
||||
style={{ marginRight: '8px' }}
|
||||
>
|
||||
{t('Add Liquidity')}
|
||||
{t('Increase Liquidity')}
|
||||
</ButtonGray>
|
||||
) : null}
|
||||
{tokenId && !removed ? (
|
||||
@@ -498,11 +498,9 @@ export function PositionPage({
|
||||
{inverted ? position?.amount0.toSignificant(4) : position?.amount1.toSignificant(4)}
|
||||
</TYPE.main>
|
||||
{typeof ratio === 'number' && !removed ? (
|
||||
<DarkGreyCard padding="4px 6px" style={{ width: 'fit-content', marginLeft: '8px' }}>
|
||||
<TYPE.main color={theme.text2} fontSize={11}>
|
||||
{inverted ? ratio : 100 - ratio}%
|
||||
</TYPE.main>
|
||||
</DarkGreyCard>
|
||||
<Badge style={{ marginLeft: '10px' }}>
|
||||
<TYPE.main fontSize={11}>{inverted ? ratio : 100 - ratio}%</TYPE.main>
|
||||
</Badge>
|
||||
) : null}
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
@@ -516,11 +514,11 @@ export function PositionPage({
|
||||
{inverted ? position?.amount1.toSignificant(4) : position?.amount0.toSignificant(4)}
|
||||
</TYPE.main>
|
||||
{typeof ratio === 'number' && !removed ? (
|
||||
<DarkGreyCard padding="4px 6px" style={{ width: 'fit-content', marginLeft: '8px' }}>
|
||||
<Badge style={{ marginLeft: '10px' }}>
|
||||
<TYPE.main color={theme.text2} fontSize={11}>
|
||||
{inverted ? 100 - ratio : ratio}%
|
||||
</TYPE.main>
|
||||
</DarkGreyCard>
|
||||
</Badge>
|
||||
) : null}
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
@@ -640,8 +638,8 @@ export function PositionPage({
|
||||
<LightCard padding="12px" width="100%">
|
||||
<AutoColumn gap="8px" justify="center">
|
||||
<ExtentsText>Min price</ExtentsText>
|
||||
<TYPE.mediumHeader textAlign="center">{priceLower?.toSignificant(4)}</TYPE.mediumHeader>
|
||||
<ExtentsText> {currencyQuote?.symbol + ' / ' + currencyBase?.symbol}</ExtentsText>
|
||||
<TYPE.mediumHeader textAlign="center">{priceLower?.toSignificant(5)}</TYPE.mediumHeader>
|
||||
<ExtentsText> {currencyQuote?.symbol + ' per ' + currencyBase?.symbol}</ExtentsText>
|
||||
|
||||
{inRange && (
|
||||
<TYPE.small color={theme.text3}>
|
||||
@@ -655,8 +653,8 @@ export function PositionPage({
|
||||
<LightCard padding="12px" width="100%">
|
||||
<AutoColumn gap="8px" justify="center">
|
||||
<ExtentsText>Max price</ExtentsText>
|
||||
<TYPE.mediumHeader textAlign="center">{priceUpper?.toSignificant(4)}</TYPE.mediumHeader>
|
||||
<ExtentsText> {currencyQuote?.symbol + ' / ' + currencyBase?.symbol}</ExtentsText>
|
||||
<TYPE.mediumHeader textAlign="center">{priceUpper?.toSignificant(5)}</TYPE.mediumHeader>
|
||||
<ExtentsText> {currencyQuote?.symbol + ' per ' + currencyBase?.symbol}</ExtentsText>
|
||||
|
||||
{inRange && (
|
||||
<TYPE.small color={theme.text3}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { ButtonGray, ButtonPrimary } from 'components/Button'
|
||||
import { ButtonGray, ButtonOutlined, ButtonPrimary } from 'components/Button'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { FlyoutAlignment, NewMenu } from 'components/Menu'
|
||||
import { SwapPoolTabs } from 'components/NavigationTabs'
|
||||
@@ -217,14 +217,40 @@ export default function Pool() {
|
||||
)}
|
||||
</MainContentWrapper>
|
||||
<RowFixed justify="center" style={{ width: '100%' }}>
|
||||
<ButtonGray
|
||||
<ButtonOutlined
|
||||
as={Link}
|
||||
to="/pool/v2"
|
||||
id="import-pool-link"
|
||||
style={{ padding: '8px 16px', borderRadius: '12px', width: 'fit-content' }}
|
||||
style={{
|
||||
padding: '8px 16px',
|
||||
margin: '0 4px',
|
||||
borderRadius: '12px',
|
||||
width: 'fit-content',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
<TYPE.subHeader>{t('Looking for your V2 Liquidity')}?</TYPE.subHeader>
|
||||
</ButtonGray>
|
||||
<Layers size={14} style={{ marginRight: '8px' }} />
|
||||
|
||||
{t('View V2 Liquidity')}
|
||||
</ButtonOutlined>
|
||||
{positions && positions.length > 0 && (
|
||||
<ButtonOutlined
|
||||
as={Link}
|
||||
to="/migrate/v2"
|
||||
id="import-pool-link"
|
||||
style={{
|
||||
padding: '8px 16px',
|
||||
margin: '0 4px',
|
||||
borderRadius: '12px',
|
||||
width: 'fit-content',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
<ChevronsRight size={16} style={{ marginRight: '8px' }} />
|
||||
|
||||
{t('Migrate Liquidity')}
|
||||
</ButtonOutlined>
|
||||
)}
|
||||
</RowFixed>
|
||||
</AutoColumn>
|
||||
</AutoColumn>
|
||||
|
||||
@@ -9,7 +9,9 @@ import { ExternalLink, TYPE, HideSmall } from '../../theme'
|
||||
import { Text } from 'rebass'
|
||||
import Card from '../../components/Card'
|
||||
import { RowBetween, RowFixed } from '../../components/Row'
|
||||
import { ButtonPrimary, ButtonSecondary } from '../../components/Button'
|
||||
import { ButtonPrimary, ButtonSecondary, ButtonOutlined } from '../../components/Button'
|
||||
import { ChevronsRight } from 'react-feather'
|
||||
|
||||
import { AutoColumn } from '../../components/Column'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
@@ -151,7 +153,7 @@ export default function Pool() {
|
||||
</VoteCard>
|
||||
|
||||
<AutoColumn gap="lg" justify="center">
|
||||
<AutoColumn gap="lg" style={{ width: '100%' }}>
|
||||
<AutoColumn gap="md" style={{ width: '100%' }}>
|
||||
<TitleRow style={{ marginTop: '1rem' }} padding={'0'}>
|
||||
<HideSmall>
|
||||
<TYPE.mediumHeader style={{ marginTop: '0.5rem', justifySelf: 'flex-start' }}>
|
||||
@@ -170,7 +172,7 @@ export default function Pool() {
|
||||
to="/add/v2/ETH"
|
||||
>
|
||||
<Text fontWeight={500} fontSize={16}>
|
||||
Add Liquidity
|
||||
Add V2 Liquidity
|
||||
</Text>
|
||||
</ResponsiveButtonPrimary>
|
||||
</ButtonRow>
|
||||
@@ -211,6 +213,23 @@ export default function Pool() {
|
||||
/>
|
||||
)
|
||||
)}
|
||||
<RowFixed justify="center" style={{ width: '100%' }}>
|
||||
<ButtonOutlined
|
||||
as={Link}
|
||||
to="/migrate/v2"
|
||||
id="import-pool-link"
|
||||
style={{
|
||||
padding: '8px 16px',
|
||||
margin: '0 4px',
|
||||
borderRadius: '12px',
|
||||
width: 'fit-content',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
<ChevronsRight size={16} style={{ marginRight: '8px' }} />
|
||||
Migrate Liquidity to V3
|
||||
</ButtonOutlined>
|
||||
</RowFixed>
|
||||
</>
|
||||
) : (
|
||||
<EmptyProposals>
|
||||
|
||||
@@ -15,13 +15,13 @@ import { Text } from 'rebass'
|
||||
import CurrencyLogo from 'components/CurrencyLogo'
|
||||
import FormattedCurrencyAmount from 'components/FormattedCurrencyAmount'
|
||||
import { useV3NFTPositionManagerContract } from 'hooks/useContract'
|
||||
import { useUserSlippageTolerance } from 'state/user/hooks'
|
||||
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
|
||||
import useTransactionDeadline from 'hooks/useTransactionDeadline'
|
||||
import ReactGA from 'react-ga'
|
||||
import { useActiveWeb3React } from 'hooks'
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { useTransactionAdder } from 'state/transactions/hooks'
|
||||
import { WETH9, CurrencyAmount } from '@uniswap/sdk-core'
|
||||
import { WETH9, CurrencyAmount, Percent } from '@uniswap/sdk-core'
|
||||
import { TYPE } from 'theme'
|
||||
import { Wrapper, SmallMaxButton, ResponsiveHeaderText } from './styled'
|
||||
import Loader from 'components/Loader'
|
||||
@@ -37,6 +37,8 @@ import RangeBadge from 'components/Badge/RangeBadge'
|
||||
|
||||
export const UINT128MAX = BigNumber.from(2).pow(128).sub(1)
|
||||
|
||||
const DEFAULT_REMOVE_V3_LIQUIDITY_SLIPPAGE_TOLERANCE = new Percent(5, 100)
|
||||
|
||||
// redirect invalid tokenIds
|
||||
export default function RemoveLiquidityV3({
|
||||
location,
|
||||
@@ -89,7 +91,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
const [percentForSlider, onPercentSelectForSlider] = useDebouncedChangeHandler(percent, onPercentSelect)
|
||||
|
||||
const deadline = useTransactionDeadline() // custom from users settings
|
||||
const [allowedSlippage] = useUserSlippageTolerance() // custom from users
|
||||
const allowedSlippage = useUserSlippageToleranceWithDefault(DEFAULT_REMOVE_V3_LIQUIDITY_SLIPPAGE_TOLERANCE) // custom from users
|
||||
|
||||
const [showConfirm, setShowConfirm] = useState(false)
|
||||
const [attemptingTxn, setAttemptingTxn] = useState(false)
|
||||
@@ -274,7 +276,12 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
pendingText={pendingText}
|
||||
/>
|
||||
<AppBody>
|
||||
<AddRemoveTabs creating={false} adding={false} positionID={tokenId.toString()} />
|
||||
<AddRemoveTabs
|
||||
creating={false}
|
||||
adding={false}
|
||||
positionID={tokenId.toString()}
|
||||
defaultSlippage={DEFAULT_REMOVE_V3_LIQUIDITY_SLIPPAGE_TOLERANCE}
|
||||
/>
|
||||
<Wrapper>
|
||||
{position ? (
|
||||
<AutoColumn gap="lg">
|
||||
|
||||
@@ -40,9 +40,11 @@ import { useBurnActionHandlers } from '../../state/burn/hooks'
|
||||
import { useDerivedBurnInfo, useBurnState } from '../../state/burn/hooks'
|
||||
import { Field } from '../../state/burn/actions'
|
||||
import { useWalletModalToggle } from '../../state/application/hooks'
|
||||
import { useUserSlippageTolerance } from '../../state/user/hooks'
|
||||
import { useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
|
||||
const DEFAULT_REMOVE_LIQUIDITY_SLIPPAGE_TOLERANCE = new Percent(5, 100)
|
||||
|
||||
export default function RemoveLiquidity({
|
||||
history,
|
||||
match: {
|
||||
@@ -76,7 +78,7 @@ export default function RemoveLiquidity({
|
||||
// txn values
|
||||
const [txHash, setTxHash] = useState<string>('')
|
||||
const deadline = useTransactionDeadline()
|
||||
const [allowedSlippage] = useUserSlippageTolerance()
|
||||
const allowedSlippage = useUserSlippageToleranceWithDefault(DEFAULT_REMOVE_LIQUIDITY_SLIPPAGE_TOLERANCE)
|
||||
|
||||
const formattedAmounts = {
|
||||
[Field.LIQUIDITY_PERCENT]: parsedAmounts[Field.LIQUIDITY_PERCENT].equalTo('0')
|
||||
@@ -426,7 +428,7 @@ export default function RemoveLiquidity({
|
||||
return (
|
||||
<>
|
||||
<AppBody>
|
||||
<AddRemoveTabs creating={false} adding={false} />
|
||||
<AddRemoveTabs creating={false} adding={false} defaultSlippage={DEFAULT_REMOVE_LIQUIDITY_SLIPPAGE_TOLERANCE} />
|
||||
<Wrapper>
|
||||
<TransactionConfirmationModal
|
||||
isOpen={showConfirm}
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
useSwapActionHandlers,
|
||||
useSwapState,
|
||||
} from '../../state/swap/hooks'
|
||||
import { useExpertModeManager, useUserSingleHopOnly, useUserSlippageTolerance } from '../../state/user/hooks'
|
||||
import { useExpertModeManager, useUserSingleHopOnly } from '../../state/user/hooks'
|
||||
import { HideSmall, LinkStyledButton, TYPE } from '../../theme'
|
||||
import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact'
|
||||
import { computePriceImpactWithMaximumSlippage } from '../../utils/computePriceImpactWithMaximumSlippage'
|
||||
@@ -100,19 +100,21 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
// for expert mode
|
||||
const [isExpertMode] = useExpertModeManager()
|
||||
|
||||
// get custom setting values for user
|
||||
const [allowedSlippage] = useUserSlippageTolerance()
|
||||
// get version from the url
|
||||
const toggledVersion = useToggledVersion()
|
||||
|
||||
// swap state
|
||||
const { independentField, typedValue, recipient } = useSwapState()
|
||||
const {
|
||||
v2Trade,
|
||||
v3TradeState: { trade: v3Trade, state: v3TradeState },
|
||||
toggledTrade: trade,
|
||||
allowedSlippage,
|
||||
currencyBalances,
|
||||
parsedAmount,
|
||||
currencies,
|
||||
inputError: swapInputError,
|
||||
} = useDerivedSwapInfo()
|
||||
} = useDerivedSwapInfo(toggledVersion)
|
||||
|
||||
const { wrapType, execute: onWrap, inputError: wrapInputError } = useWrapCallback(
|
||||
currencies[Field.INPUT],
|
||||
@@ -121,13 +123,6 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
)
|
||||
const showWrap: boolean = wrapType !== WrapType.NOT_APPLICABLE
|
||||
const { address: recipientAddress } = useENSAddress(recipient)
|
||||
const toggledVersion = useToggledVersion()
|
||||
const trade = showWrap
|
||||
? undefined
|
||||
: {
|
||||
[Version.v2]: v2Trade,
|
||||
[Version.v3]: v3Trade ?? undefined,
|
||||
}[toggledVersion]
|
||||
|
||||
const parsedAmounts = useMemo(
|
||||
() =>
|
||||
@@ -357,7 +352,7 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
onDismiss={handleDismissTokenWarning}
|
||||
/>
|
||||
<AppBody>
|
||||
<SwapHeader />
|
||||
<SwapHeader allowedSlippage={allowedSlippage} />
|
||||
<Wrapper id="swap-page">
|
||||
<ConfirmSwapModal
|
||||
isOpen={showConfirm}
|
||||
@@ -484,7 +479,9 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
showInverted={showInverted}
|
||||
setShowInverted={setShowInverted}
|
||||
/>
|
||||
<MouseoverTooltipContent content={<AdvancedSwapDetails trade={trade} />}>
|
||||
<MouseoverTooltipContent
|
||||
content={<AdvancedSwapDetails trade={trade} allowedSlippage={allowedSlippage} />}
|
||||
>
|
||||
<StyledInfo />
|
||||
</MouseoverTooltipContent>
|
||||
</RowFixed>
|
||||
|
||||
@@ -235,7 +235,7 @@ export default function Vote() {
|
||||
})}
|
||||
</TopSection>
|
||||
<TYPE.subHeader color="text3">
|
||||
A minimum threshhold of 1% of the total UNI supply is required to submit proposals
|
||||
A minimum threshold of 1% of the total UNI supply is required to submit proposals
|
||||
</TYPE.subHeader>
|
||||
</PageWrapper>
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
TickMath,
|
||||
tickToPrice,
|
||||
TICK_SPACINGS,
|
||||
encodeSqrtRatioX96,
|
||||
} from '@uniswap/v3-sdk/dist/'
|
||||
import { Currency, CurrencyAmount, ETHER, Price, Rounding } from '@uniswap/sdk-core'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
@@ -185,16 +186,29 @@ export function useV3DerivedMintInfo(
|
||||
}
|
||||
}, [noLiquidity, startPriceTypedValue, invertPrice, token1, token0, pool])
|
||||
|
||||
// check for invalid price input (converts to invalid ratio)
|
||||
const invalidPrice = useMemo(() => {
|
||||
const sqrtRatioX96 = price ? encodeSqrtRatioX96(price.raw.numerator, price.raw.denominator) : undefined
|
||||
const invalid =
|
||||
price &&
|
||||
sqrtRatioX96 &&
|
||||
!(
|
||||
JSBI.greaterThanOrEqual(sqrtRatioX96, TickMath.MIN_SQRT_RATIO) &&
|
||||
JSBI.lessThan(sqrtRatioX96, TickMath.MAX_SQRT_RATIO)
|
||||
)
|
||||
return invalid
|
||||
}, [price])
|
||||
|
||||
// used for ratio calculation when pool not initialized
|
||||
const mockPool = useMemo(() => {
|
||||
if (tokenA && tokenB && feeAmount && price) {
|
||||
if (tokenA && tokenB && feeAmount && price && !invalidPrice) {
|
||||
const currentTick = priceToClosestTick(price)
|
||||
const currentSqrt = TickMath.getSqrtRatioAtTick(currentTick)
|
||||
return new Pool(tokenA, tokenB, feeAmount, currentSqrt, JSBI.BigInt(0), currentTick, [])
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}, [feeAmount, price, tokenA, tokenB])
|
||||
}, [feeAmount, invalidPrice, price, tokenA, tokenB])
|
||||
|
||||
// if pool exists use it, if not use the mock pool
|
||||
const poolForPosition: Pool | undefined = pool ?? mockPool
|
||||
@@ -374,6 +388,10 @@ export function useV3DerivedMintInfo(
|
||||
errorMessage = errorMessage ?? 'Invalid pair'
|
||||
}
|
||||
|
||||
if (invalidPrice) {
|
||||
errorMessage = errorMessage ?? 'Invalid price input'
|
||||
}
|
||||
|
||||
if (
|
||||
(!parsedAmounts[Field.CURRENCY_A] && !depositADisabled) ||
|
||||
(!parsedAmounts[Field.CURRENCY_B] && !depositBDisabled)
|
||||
|
||||
@@ -25,8 +25,8 @@ describe('hooks', () => {
|
||||
expect(
|
||||
queryParametersToSwapState(parse('?outputCurrency=invalid', { parseArrays: false, ignoreQueryPrefix: true }))
|
||||
).toEqual({
|
||||
[Field.INPUT]: { currencyId: '' },
|
||||
[Field.OUTPUT]: { currencyId: 'ETH' },
|
||||
[Field.INPUT]: { currencyId: 'ETH' },
|
||||
[Field.OUTPUT]: { currencyId: '' },
|
||||
typedValue: '',
|
||||
independentField: Field.INPUT,
|
||||
recipient: null,
|
||||
|
||||
@@ -2,13 +2,15 @@ import { Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import { useBestV3TradeExactIn, useBestV3TradeExactOut, V3TradeState } from '../../hooks/useBestV3Trade'
|
||||
import useENS from '../../hooks/useENS'
|
||||
import { parseUnits } from '@ethersproject/units'
|
||||
import { Currency, CurrencyAmount, ETHER, Token, TokenAmount } from '@uniswap/sdk-core'
|
||||
import { Currency, CurrencyAmount, ETHER, Token, TokenAmount, Percent } from '@uniswap/sdk-core'
|
||||
import { JSBI, Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { ParsedQs } from 'qs'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useCurrency } from '../../hooks/Tokens'
|
||||
import useSwapSlippageTolerance from '../../hooks/useSwapSlippageTolerance'
|
||||
import { Version } from '../../hooks/useToggledVersion'
|
||||
import { useV2TradeExactIn, useV2TradeExactOut } from '../../hooks/useV2Trade'
|
||||
import useParsedQueryString from '../../hooks/useParsedQueryString'
|
||||
import { isAddress } from '../../utils'
|
||||
@@ -16,7 +18,6 @@ import { AppDispatch, AppState } from '../index'
|
||||
import { useCurrencyBalances } from '../wallet/hooks'
|
||||
import { Field, replaceSwapState, selectCurrency, setRecipient, switchCurrencies, typeInput } from './actions'
|
||||
import { SwapState } from './reducer'
|
||||
import { useUserSlippageTolerance } from '../user/hooks'
|
||||
|
||||
export function useSwapState(): AppState['swap'] {
|
||||
return useSelector<AppState, AppState['swap']>((state) => state.swap)
|
||||
@@ -109,13 +110,17 @@ function involvesAddress(trade: V2Trade | V3Trade, checksummedAddress: string):
|
||||
}
|
||||
|
||||
// from the current swap inputs, compute the best trade and return it.
|
||||
export function useDerivedSwapInfo(): {
|
||||
export function useDerivedSwapInfo(
|
||||
toggledVersion: Version
|
||||
): {
|
||||
currencies: { [field in Field]?: Currency }
|
||||
currencyBalances: { [field in Field]?: CurrencyAmount }
|
||||
parsedAmount: CurrencyAmount | undefined
|
||||
v2Trade: V2Trade | undefined
|
||||
inputError?: string
|
||||
v2Trade: V2Trade | undefined
|
||||
v3TradeState: { trade: V3Trade | null; state: V3TradeState }
|
||||
toggledTrade: V2Trade | V3Trade | undefined
|
||||
allowedSlippage: Percent
|
||||
} {
|
||||
const { account } = useActiveWeb3React()
|
||||
|
||||
@@ -185,7 +190,8 @@ export function useDerivedSwapInfo(): {
|
||||
}
|
||||
}
|
||||
|
||||
const [allowedSlippage] = useUserSlippageTolerance()
|
||||
const toggledTrade = (toggledVersion === Version.v2 ? v2Trade : v3Trade.trade) ?? undefined
|
||||
const allowedSlippage = useSwapSlippageTolerance(toggledTrade)
|
||||
|
||||
// compare input balance to max input based on version
|
||||
const [balanceIn, amountIn] = [currencyBalances[Field.INPUT], v2Trade?.maximumAmountIn(allowedSlippage)]
|
||||
@@ -198,9 +204,11 @@ export function useDerivedSwapInfo(): {
|
||||
currencies,
|
||||
currencyBalances,
|
||||
parsedAmount,
|
||||
v2Trade: v2Trade ?? undefined,
|
||||
inputError,
|
||||
v2Trade: v2Trade ?? undefined,
|
||||
v3TradeState: v3Trade,
|
||||
toggledTrade,
|
||||
allowedSlippage,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,9 +217,8 @@ function parseCurrencyFromURLParameter(urlParam: any): string {
|
||||
const valid = isAddress(urlParam)
|
||||
if (valid) return valid
|
||||
if (urlParam.toUpperCase() === 'ETH') return 'ETH'
|
||||
if (valid === false) return 'ETH'
|
||||
}
|
||||
return 'ETH' ?? ''
|
||||
return ''
|
||||
}
|
||||
|
||||
function parseTokenAmountURLParameter(urlParam: any): string {
|
||||
@@ -236,12 +243,12 @@ function validatedRecipient(recipient: any): string | null {
|
||||
export function queryParametersToSwapState(parsedQs: ParsedQs): SwapState {
|
||||
let inputCurrency = parseCurrencyFromURLParameter(parsedQs.inputCurrency)
|
||||
let outputCurrency = parseCurrencyFromURLParameter(parsedQs.outputCurrency)
|
||||
if (inputCurrency === outputCurrency) {
|
||||
if (typeof parsedQs.outputCurrency === 'string') {
|
||||
inputCurrency = ''
|
||||
} else {
|
||||
outputCurrency = ''
|
||||
}
|
||||
if (inputCurrency === '' && outputCurrency === '') {
|
||||
// default to ETH input
|
||||
inputCurrency = 'ETH'
|
||||
} else if (inputCurrency === outputCurrency) {
|
||||
// clear output if identical
|
||||
outputCurrency = ''
|
||||
}
|
||||
|
||||
const recipient = validatedRecipient(parsedQs.recipient)
|
||||
|
||||
@@ -17,7 +17,7 @@ export const updateMatchesDarkMode = createAction<{ matchesDarkMode: boolean }>(
|
||||
export const updateUserDarkMode = createAction<{ userDarkMode: boolean }>('user/updateUserDarkMode')
|
||||
export const updateUserExpertMode = createAction<{ userExpertMode: boolean }>('user/updateUserExpertMode')
|
||||
export const updateUserSingleHopOnly = createAction<{ userSingleHopOnly: boolean }>('user/updateUserSingleHopOnly')
|
||||
export const updateUserSlippageTolerance = createAction<{ userSlippageTolerance: number }>(
|
||||
export const updateUserSlippageTolerance = createAction<{ userSlippageTolerance: number | 'auto' }>(
|
||||
'user/updateUserSlippageTolerance'
|
||||
)
|
||||
export const updateUserDeadline = createAction<{ userDeadline: number }>('user/updateUserDeadline')
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ChainId, Percent, Token } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import JSBI from 'jsbi'
|
||||
import flatMap from 'lodash.flatmap'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
|
||||
@@ -14,12 +15,12 @@ import {
|
||||
removeSerializedToken,
|
||||
SerializedPair,
|
||||
SerializedToken,
|
||||
toggleURLWarning,
|
||||
updateUserDarkMode,
|
||||
updateUserDeadline,
|
||||
updateUserExpertMode,
|
||||
updateUserSlippageTolerance,
|
||||
toggleURLWarning,
|
||||
updateUserSingleHopOnly,
|
||||
updateUserSlippageTolerance,
|
||||
} from './actions'
|
||||
|
||||
function serializeToken(token: Token): SerializedToken {
|
||||
@@ -100,22 +101,51 @@ export function useUserSingleHopOnly(): [boolean, (newSingleHopOnly: boolean) =>
|
||||
return [singleHopOnly, setSingleHopOnly]
|
||||
}
|
||||
|
||||
export function useUserSlippageTolerance(): [Percent, (slippageBips: number) => void] {
|
||||
export function useSetUserSlippageTolerance(): (slippageTolerance: Percent | 'auto') => void {
|
||||
const dispatch = useDispatch<AppDispatch>()
|
||||
|
||||
return useCallback(
|
||||
(userSlippageTolerance: Percent | 'auto') => {
|
||||
let value: 'auto' | number
|
||||
try {
|
||||
value =
|
||||
userSlippageTolerance === 'auto' ? 'auto' : JSBI.toNumber(userSlippageTolerance.multiply(10_000).quotient)
|
||||
} catch (error) {
|
||||
value = 'auto'
|
||||
}
|
||||
dispatch(
|
||||
updateUserSlippageTolerance({
|
||||
userSlippageTolerance: value,
|
||||
})
|
||||
)
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user's slippage tolerance, from the redux store, and a function to update the slippage tolerance
|
||||
*/
|
||||
export function useUserSlippageTolerance(): Percent | 'auto' {
|
||||
const userSlippageTolerance = useSelector<AppState, AppState['user']['userSlippageTolerance']>((state) => {
|
||||
return state.user.userSlippageTolerance
|
||||
})
|
||||
|
||||
const percentage = useMemo(() => new Percent(userSlippageTolerance, 10_000), [userSlippageTolerance])
|
||||
return useMemo(() => (userSlippageTolerance === 'auto' ? 'auto' : new Percent(userSlippageTolerance, 10_000)), [
|
||||
userSlippageTolerance,
|
||||
])
|
||||
}
|
||||
|
||||
const setUserSlippageTolerance = useCallback(
|
||||
(userSlippageTolerance: number) => {
|
||||
dispatch(updateUserSlippageTolerance({ userSlippageTolerance }))
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
return [percentage, setUserSlippageTolerance]
|
||||
/**
|
||||
* Same as above but replaces the auto with a default value
|
||||
* @param defaultSlippageTolerance the default value to replace auto with
|
||||
*/
|
||||
export function useUserSlippageToleranceWithDefault(defaultSlippageTolerance: Percent): Percent {
|
||||
const allowedSlippage = useUserSlippageTolerance()
|
||||
return useMemo(() => (allowedSlippage === 'auto' ? defaultSlippageTolerance : allowedSlippage), [
|
||||
allowedSlippage,
|
||||
defaultSlippageTolerance,
|
||||
])
|
||||
}
|
||||
|
||||
export function useUserTransactionTTL(): [number, (slippage: number) => void] {
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('swap reducer', () => {
|
||||
} as any)
|
||||
store.dispatch(updateVersion())
|
||||
expect(store.getState().userDeadline).toEqual(DEFAULT_DEADLINE_FROM_NOW)
|
||||
expect(store.getState().userSlippageTolerance).toEqual(10)
|
||||
expect(store.getState().userSlippageTolerance).toEqual('auto')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -31,7 +31,8 @@ export interface UserState {
|
||||
userSingleHopOnly: boolean // only allow swaps on direct pairs
|
||||
|
||||
// user defined slippage tolerance in bips, used in all txns
|
||||
userSlippageTolerance: number
|
||||
userSlippageTolerance: number | 'auto'
|
||||
userSlippageToleranceHasBeenMigratedToAuto: boolean // temporary flag for migration status
|
||||
|
||||
// deadline set by user in minutes, used in all txns
|
||||
userDeadline: number
|
||||
@@ -62,7 +63,8 @@ export const initialState: UserState = {
|
||||
matchesDarkMode: false,
|
||||
userExpertMode: false,
|
||||
userSingleHopOnly: false,
|
||||
userSlippageTolerance: 10,
|
||||
userSlippageTolerance: 'auto',
|
||||
userSlippageToleranceHasBeenMigratedToAuto: true,
|
||||
userDeadline: DEFAULT_DEADLINE_FROM_NOW,
|
||||
tokens: {},
|
||||
pairs: {},
|
||||
@@ -75,13 +77,26 @@ export default createReducer(initialState, (builder) =>
|
||||
.addCase(updateVersion, (state) => {
|
||||
// slippage isnt being tracked in local storage, reset to default
|
||||
// noinspection SuspiciousTypeOfGuard
|
||||
if (typeof state.userSlippageTolerance !== 'number') {
|
||||
state.userSlippageTolerance = 10
|
||||
if (
|
||||
typeof state.userSlippageTolerance !== 'number' ||
|
||||
!Number.isInteger(state.userSlippageTolerance) ||
|
||||
state.userSlippageTolerance < 0 ||
|
||||
state.userSlippageTolerance > 5000
|
||||
) {
|
||||
state.userSlippageTolerance = 'auto'
|
||||
} else {
|
||||
if (
|
||||
!state.userSlippageToleranceHasBeenMigratedToAuto &&
|
||||
[10, 50, 100].indexOf(state.userSlippageTolerance) !== -1
|
||||
) {
|
||||
state.userSlippageTolerance = 'auto'
|
||||
state.userSlippageToleranceHasBeenMigratedToAuto = true
|
||||
}
|
||||
}
|
||||
|
||||
// deadline isnt being tracked in local storage, reset to default
|
||||
// noinspection SuspiciousTypeOfGuard
|
||||
if (typeof state.userDeadline !== 'number') {
|
||||
if (typeof state.userDeadline !== 'number' || !Number.isInteger(state.userDeadline) || state.userDeadline < 60) {
|
||||
state.userDeadline = DEFAULT_DEADLINE_FROM_NOW
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ export function formatTokenAmount(amount: TokenAmount | undefined, sigFigs: numb
|
||||
return '0'
|
||||
}
|
||||
|
||||
if (parseFloat(amount.toFixed(sigFigs)) < 0.0001) {
|
||||
if (parseFloat(amount.toFixed(Math.min(sigFigs, amount.token.decimals))) < 0.0001) {
|
||||
return '<0.0001'
|
||||
}
|
||||
|
||||
return amount.toSignificant(sigFigs)
|
||||
return amount.toFixed(Math.min(sigFigs, amount.token.decimals))
|
||||
}
|
||||
|
||||
export function formatPrice(price: Price | undefined, sigFigs: number) {
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { Web3Provider } from '@ethersproject/providers'
|
||||
import { Web3Provider, Network } from '@ethersproject/providers'
|
||||
|
||||
class WorkaroundWeb3Provider extends Web3Provider {
|
||||
private _detectNetworkResult: Promise<Network> | null = null
|
||||
|
||||
async detectNetwork(): Promise<Network> {
|
||||
return this._detectNetworkResult ?? (this._detectNetworkResult = this._uncachedDetectNetwork())
|
||||
}
|
||||
}
|
||||
|
||||
export default function getLibrary(provider: any): Web3Provider {
|
||||
// latest ethers version tries to detect the network which fails
|
||||
const library = new Web3Provider(
|
||||
const library = new WorkaroundWeb3Provider(
|
||||
provider,
|
||||
typeof provider.chainId === 'number'
|
||||
? provider.chainId
|
||||
|
||||
6
src/utils/getUserAgent.ts
Normal file
6
src/utils/getUserAgent.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { UAParser } from 'ua-parser-js'
|
||||
|
||||
export function getUserAgent(): UAParser.IResult {
|
||||
const parser = new UAParser(window.navigator.userAgent)
|
||||
return parser.getResult()
|
||||
}
|
||||
@@ -28,11 +28,40 @@ describe('utils', () => {
|
||||
describe('#calculateSlippageAmount', () => {
|
||||
it('bounds are correct', () => {
|
||||
const tokenAmount = new TokenAmount(new Token(ChainId.MAINNET, AddressZero, 0), '100')
|
||||
expect(() => calculateSlippageAmount(tokenAmount, new Percent(-1, 10_000))).toThrow()
|
||||
expect(() => calculateSlippageAmount(tokenAmount, new Percent(-1, 10_000))).toThrow('Unexpected slippage')
|
||||
expect(() => calculateSlippageAmount(tokenAmount, new Percent(10_001, 10_000))).toThrow('Unexpected slippage')
|
||||
expect(calculateSlippageAmount(tokenAmount, new Percent(0, 10_000)).map((bound) => bound.toString())).toEqual([
|
||||
'100',
|
||||
'100',
|
||||
])
|
||||
expect(calculateSlippageAmount(tokenAmount, new Percent(5, 100)).map((bound) => bound.toString())).toEqual([
|
||||
'95',
|
||||
'105',
|
||||
])
|
||||
expect(calculateSlippageAmount(tokenAmount, new Percent(100, 10_000)).map((bound) => bound.toString())).toEqual([
|
||||
'99',
|
||||
'101',
|
||||
])
|
||||
expect(calculateSlippageAmount(tokenAmount, new Percent(200, 10_000)).map((bound) => bound.toString())).toEqual([
|
||||
'98',
|
||||
'102',
|
||||
])
|
||||
expect(
|
||||
calculateSlippageAmount(tokenAmount, new Percent(10000, 10_000)).map((bound) => bound.toString())
|
||||
).toEqual(['0', '200'])
|
||||
})
|
||||
it('works for 18 decimals', () => {
|
||||
const tokenAmount = new TokenAmount(new Token(ChainId.MAINNET, AddressZero, 18), '100')
|
||||
expect(() => calculateSlippageAmount(tokenAmount, new Percent(-1, 10_000))).toThrow('Unexpected slippage')
|
||||
expect(() => calculateSlippageAmount(tokenAmount, new Percent(10_001, 10_000))).toThrow('Unexpected slippage')
|
||||
expect(calculateSlippageAmount(tokenAmount, new Percent(0, 10_000)).map((bound) => bound.toString())).toEqual([
|
||||
'100',
|
||||
'100',
|
||||
])
|
||||
expect(calculateSlippageAmount(tokenAmount, new Percent(5, 100)).map((bound) => bound.toString())).toEqual([
|
||||
'95',
|
||||
'105',
|
||||
])
|
||||
expect(calculateSlippageAmount(tokenAmount, new Percent(100, 10_000)).map((bound) => bound.toString())).toEqual([
|
||||
'99',
|
||||
'101',
|
||||
@@ -44,7 +73,6 @@ describe('utils', () => {
|
||||
expect(
|
||||
calculateSlippageAmount(tokenAmount, new Percent(10000, 10_000)).map((bound) => bound.toString())
|
||||
).toEqual(['0', '200'])
|
||||
expect(() => calculateSlippageAmount(tokenAmount, new Percent(10001, 10_000))).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getAddress } from '@ethersproject/address'
|
||||
import { AddressZero } from '@ethersproject/constants'
|
||||
import { JsonRpcSigner, Web3Provider } from '@ethersproject/providers'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { ChainId, Percent, Token, CurrencyAmount, Currency, ETHER } from '@uniswap/sdk-core'
|
||||
import { ChainId, Percent, Token, CurrencyAmount, Currency, ETHER, Fraction } from '@uniswap/sdk-core'
|
||||
import { JSBI } from '@uniswap/v2-sdk'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk/dist/'
|
||||
import { TokenAddressMap } from '../state/lists/hooks'
|
||||
@@ -63,16 +63,13 @@ export function calculateGasMargin(value: BigNumber): BigNumber {
|
||||
return value.mul(BigNumber.from(10000).add(BigNumber.from(1000))).div(BigNumber.from(10000))
|
||||
}
|
||||
|
||||
const ONE = new Fraction(1, 1)
|
||||
export function calculateSlippageAmount(value: CurrencyAmount, slippage: Percent): [JSBI, JSBI] {
|
||||
if (
|
||||
JSBI.lessThan(slippage.numerator, JSBI.BigInt(0)) ||
|
||||
JSBI.greaterThan(slippage.numerator, JSBI.BigInt(10_000)) ||
|
||||
!JSBI.equal(slippage.denominator, JSBI.BigInt(10_000))
|
||||
)
|
||||
throw new Error('Unexpected slippage')
|
||||
if (slippage.lessThan(0) || slippage.greaterThan(ONE)) throw new Error('Unexpected slippage')
|
||||
const decimalScaled = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(value.currency.decimals))
|
||||
return [
|
||||
JSBI.divide(JSBI.multiply(value.raw, JSBI.subtract(JSBI.BigInt(10000), slippage.numerator)), JSBI.BigInt(10000)),
|
||||
JSBI.divide(JSBI.multiply(value.raw, JSBI.add(JSBI.BigInt(10000), slippage.numerator)), JSBI.BigInt(10000)),
|
||||
value.multiply(ONE.subtract(slippage)).multiply(decimalScaled).quotient,
|
||||
value.multiply(ONE.add(slippage)).multiply(decimalScaled).quotient,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -13,46 +13,48 @@ const THIRTY_BIPS_FEE = new Percent(JSBI.BigInt(30), JSBI.BigInt(10000))
|
||||
const ONE_HUNDRED_PERCENT = new Percent(JSBI.BigInt(10000), JSBI.BigInt(10000))
|
||||
const INPUT_FRACTION_AFTER_FEE = ONE_HUNDRED_PERCENT.subtract(THIRTY_BIPS_FEE)
|
||||
|
||||
// computes price breakdown for the trade
|
||||
export function computeRealizedLPFeeAmount(trade?: V2Trade | V3Trade | null): CurrencyAmount | undefined {
|
||||
// computes realized lp fee as a percent
|
||||
export function computeRealizedLPFeePercent(trade: V2Trade | V3Trade): Percent {
|
||||
let percent: Percent
|
||||
if (trade instanceof V2Trade) {
|
||||
// for each hop in our trade, take away the x*y=k price impact from 0.3% fees
|
||||
// e.g. for 3 tokens/2 hops: 1 - ((1 - .03) * (1-.03))
|
||||
const realizedLPFee = !trade
|
||||
? undefined
|
||||
: ONE_HUNDRED_PERCENT.subtract(
|
||||
trade.route.pairs.reduce<Fraction>(
|
||||
(currentFee: Fraction): Fraction => currentFee.multiply(INPUT_FRACTION_AFTER_FEE),
|
||||
ONE_HUNDRED_PERCENT
|
||||
)
|
||||
)
|
||||
|
||||
// the amount of the input that accrues to LPs
|
||||
return (
|
||||
realizedLPFee &&
|
||||
trade &&
|
||||
(trade.inputAmount instanceof TokenAmount
|
||||
? new TokenAmount(trade.inputAmount.token, realizedLPFee.multiply(trade.inputAmount.raw).quotient)
|
||||
: CurrencyAmount.ether(realizedLPFee.multiply(trade.inputAmount.raw).quotient))
|
||||
percent = ONE_HUNDRED_PERCENT.subtract(
|
||||
trade.route.pairs.reduce<Fraction>(
|
||||
(currentFee: Fraction): Fraction => currentFee.multiply(INPUT_FRACTION_AFTER_FEE),
|
||||
ONE_HUNDRED_PERCENT
|
||||
)
|
||||
)
|
||||
} else if (trade instanceof V3Trade) {
|
||||
const realizedLPFee = !trade
|
||||
? undefined
|
||||
: ONE_HUNDRED_PERCENT.subtract(
|
||||
trade.route.pools.reduce<Fraction>(
|
||||
(currentFee: Fraction, pool): Fraction =>
|
||||
currentFee.multiply(ONE_HUNDRED_PERCENT.subtract(new Fraction(pool.fee, 1_000_000))),
|
||||
ONE_HUNDRED_PERCENT
|
||||
)
|
||||
)
|
||||
return (
|
||||
realizedLPFee &&
|
||||
trade &&
|
||||
(trade.inputAmount instanceof TokenAmount
|
||||
? new TokenAmount(trade.inputAmount.token, realizedLPFee.multiply(trade.inputAmount.raw).quotient)
|
||||
: CurrencyAmount.ether(realizedLPFee.multiply(trade.inputAmount.raw).quotient))
|
||||
} else {
|
||||
percent = ONE_HUNDRED_PERCENT.subtract(
|
||||
trade.route.pools.reduce<Fraction>(
|
||||
(currentFee: Fraction, pool): Fraction =>
|
||||
currentFee.multiply(ONE_HUNDRED_PERCENT.subtract(new Fraction(pool.fee, 1_000_000))),
|
||||
ONE_HUNDRED_PERCENT
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return new Percent(percent.numerator, percent.denominator)
|
||||
}
|
||||
|
||||
// computes price breakdown for the trade
|
||||
export function computeRealizedLPFeeAmount(trade?: V2Trade | V3Trade | null): CurrencyAmount | undefined {
|
||||
if (trade instanceof V2Trade) {
|
||||
const realizedLPFee = computeRealizedLPFeePercent(trade)
|
||||
|
||||
// the amount of the input that accrues to LPs
|
||||
return trade.inputAmount instanceof TokenAmount
|
||||
? new TokenAmount(trade.inputAmount.token, realizedLPFee.multiply(trade.inputAmount.raw).quotient)
|
||||
: CurrencyAmount.ether(realizedLPFee.multiply(trade.inputAmount.raw).quotient)
|
||||
} else if (trade instanceof V3Trade) {
|
||||
const realizedLPFee = computeRealizedLPFeePercent(trade)
|
||||
|
||||
return trade.inputAmount instanceof TokenAmount
|
||||
? new TokenAmount(trade.inputAmount.token, realizedLPFee.multiply(trade.inputAmount.raw).quotient)
|
||||
: CurrencyAmount.ether(realizedLPFee.multiply(trade.inputAmount.raw).quotient)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
64
yarn.lock
64
yarn.lock
@@ -3410,10 +3410,10 @@
|
||||
lz-string "^1.4.4"
|
||||
pretty-format "^26.6.2"
|
||||
|
||||
"@typechain/ethers-v5@^6.0.5":
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-6.0.5.tgz#39bbf9baadd0e8d9efad9d16c60152b7cd9a467b"
|
||||
integrity sha512-KJh+EWuxmX1a17fQWS1ba8DCYcqK7UpdbqMZZwyfiv9FQfn8ZQJX17anbkCMOSU8TV3EvRuJ/vFEKGzKnpkO8g==
|
||||
"@typechain/ethers-v5@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-7.0.0.tgz#cadb5262b3827d1616c21f4ba86a36a71269bd7e"
|
||||
integrity sha512-ykNaqYcQ1yC928x8bogL9LECUg0osfqqHCKBhP7qbGlNfvC/bvTiIfnjQUgXUYWEJRx5r0Y78vcKMo8F3sJTBA==
|
||||
|
||||
"@types/anymatch@*":
|
||||
version "1.3.1"
|
||||
@@ -3638,13 +3638,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21"
|
||||
integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==
|
||||
|
||||
"@types/mkdirp@^0.5.2":
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f"
|
||||
integrity sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/multicodec@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/multicodec/-/multicodec-1.0.0.tgz#9c9c2df84ea5006c65a048873600f71c4565a397"
|
||||
@@ -3813,7 +3806,7 @@
|
||||
"@types/styled-system" "*"
|
||||
"@types/styled-system__css" "*"
|
||||
|
||||
"@types/resolve@0.0.8", "@types/resolve@^0.0.8":
|
||||
"@types/resolve@0.0.8":
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"
|
||||
integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==
|
||||
@@ -3888,6 +3881,11 @@
|
||||
"@testing-library/dom" "^7.11.0"
|
||||
cypress "*"
|
||||
|
||||
"@types/ua-parser-js@^0.7.35":
|
||||
version "0.7.35"
|
||||
resolved "https://registry.yarnpkg.com/@types/ua-parser-js/-/ua-parser-js-0.7.35.tgz#cca67a95deb9165e4b1f449471801e6489d3fe93"
|
||||
integrity sha512-PsPx0RLbo2Un8+ff2buzYJnZjzwhD3jQHPOG2PtVIeOhkRDddMcKU8vJtHpzzfLB95dkUi0qAkfLg2l2Fd0yrQ==
|
||||
|
||||
"@types/uglify-js@*":
|
||||
version "3.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.0.tgz#1cad8df1fb0b143c5aba08de5712ea9d1ff71124"
|
||||
@@ -4149,10 +4147,10 @@
|
||||
"@uniswap/v3-core" "1.0.0"
|
||||
base64-sol "1.0.1"
|
||||
|
||||
"@uniswap/v3-sdk@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-1.0.3.tgz#8f47b5f8cc8997992811a242ef202f9a8c4797cf"
|
||||
integrity sha512-izIrHTAXCeMhfye0nHntoAS0UTbpa8HyGSD++Zmy+kROeb2gSAcpXvnLHzRDIPxq0G4rOH0h05Y5fhHAxaXj5w==
|
||||
"@uniswap/v3-sdk@^1.0.8":
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-1.0.8.tgz#c85b229ac9448d19dfb1cb4a7891478c003a72f4"
|
||||
integrity sha512-Kg0P4KZI07m6B6L5EEtkEX/Q6QOdbWnVAHiy8y1XJOxORGw4DS0vklG5IRJAVhgLsVlosbmjxELsBjVMuKtypQ==
|
||||
dependencies:
|
||||
"@ethersproject/abi" "^5.0.12"
|
||||
"@ethersproject/solidity" "^5.0.9"
|
||||
@@ -18094,11 +18092,6 @@ ts-dedent@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.1.1.tgz#6dd56870bb5493895171334fa5d7e929107e5bbc"
|
||||
integrity sha512-riHuwnzAUCfdIeTBNUq7+Yj+ANnrMXo/7+Z74dIdudS7ys2k8aSGMzpJRMFDF7CLwUTbtvi1ZZff/Wl+XxmqIA==
|
||||
|
||||
ts-essentials@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-1.0.4.tgz#ce3b5dade5f5d97cf69889c11bf7d2da8555b15a"
|
||||
integrity sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ==
|
||||
|
||||
ts-essentials@^2.0.3:
|
||||
version "2.0.12"
|
||||
resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-2.0.12.tgz#c9303f3d74f75fa7528c3d49b80e089ab09d8745"
|
||||
@@ -18109,21 +18102,6 @@ ts-essentials@^7.0.1:
|
||||
resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.1.tgz#d205508cae0cdadfb73c89503140cf2228389e2d"
|
||||
integrity sha512-8lwh3QJtIc1UWhkQtr9XuksXu3O0YQdEE5g79guDfhCaU1FWTDIEDZ1ZSx4HTHUmlJZ8L812j3BZQ4a0aOUkSA==
|
||||
|
||||
ts-generator@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-generator/-/ts-generator-0.1.1.tgz#af46f2fb88a6db1f9785977e9590e7bcd79220ab"
|
||||
integrity sha512-N+ahhZxTLYu1HNTQetwWcx3so8hcYbkKBHTr4b4/YgObFTIKkOSSsaa+nal12w8mfrJAyzJfETXawbNjSfP2gQ==
|
||||
dependencies:
|
||||
"@types/mkdirp" "^0.5.2"
|
||||
"@types/prettier" "^2.1.1"
|
||||
"@types/resolve" "^0.0.8"
|
||||
chalk "^2.4.1"
|
||||
glob "^7.1.2"
|
||||
mkdirp "^0.5.1"
|
||||
prettier "^2.1.2"
|
||||
resolve "^1.8.1"
|
||||
ts-essentials "^1.0.0"
|
||||
|
||||
ts-pnp@1.2.0, ts-pnp@^1.1.6:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
|
||||
@@ -18250,18 +18228,20 @@ type@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d"
|
||||
integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==
|
||||
|
||||
typechain@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/typechain/-/typechain-4.0.3.tgz#e8fcd6c984676858c64eeeb155ea783a10b73779"
|
||||
integrity sha512-tmoHQeXZWHxIdeLK+i6dU0CU0vOd9Cndr3jFTZIMzak5/YpFZ8XoiYpTZcngygGBqZo+Z1EUmttLbW9KkFZLgQ==
|
||||
typechain@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/typechain/-/typechain-5.0.0.tgz#730e5fb4709964eed3db03be332b38ba6eaa1d9f"
|
||||
integrity sha512-Ko2/8co0FUmPUkaXPcb8PC3ncWa5P72nvkiNMgcomd4OAInltJlITF0kcW2cZmI2sFkvmaHV5TZmCnOHgo+i5Q==
|
||||
dependencies:
|
||||
"@types/prettier" "^2.1.1"
|
||||
command-line-args "^4.0.7"
|
||||
debug "^4.1.1"
|
||||
fs-extra "^7.0.0"
|
||||
glob "^7.1.6"
|
||||
js-sha3 "^0.8.0"
|
||||
lodash "^4.17.15"
|
||||
prettier "^2.1.2"
|
||||
ts-essentials "^7.0.1"
|
||||
ts-generator "^0.1.1"
|
||||
|
||||
typedarray-to-buffer@3.1.5, typedarray-to-buffer@^3.1.5:
|
||||
version "3.1.5"
|
||||
@@ -18285,7 +18265,7 @@ typical@^2.6.0, typical@^2.6.1:
|
||||
resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d"
|
||||
integrity sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=
|
||||
|
||||
ua-parser-js@^0.7.24:
|
||||
ua-parser-js@^0.7.24, ua-parser-js@^0.7.28:
|
||||
version "0.7.28"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31"
|
||||
integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==
|
||||
|
||||
Reference in New Issue
Block a user