Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbf39e4932 | ||
|
|
975570fa97 | ||
|
|
d6aa0e98a4 | ||
|
|
4644cd7b0a | ||
|
|
9ddedd8dab | ||
|
|
95030a52c5 | ||
|
|
1911f72536 | ||
|
|
85217452db | ||
|
|
f7a1a2ab58 | ||
|
|
66a2006284 | ||
|
|
610b7f4464 |
@@ -1,6 +1,5 @@
|
||||
describe('Swap', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage()
|
||||
cy.visit('/swap')
|
||||
})
|
||||
|
||||
@@ -13,8 +12,7 @@ describe('Swap', () => {
|
||||
cy.get('#list-introduction-choose-a-list').should('not.exist')
|
||||
})
|
||||
|
||||
// for some reason local storage is not being properly cleared
|
||||
it.skip('change list', () => {
|
||||
it('change list', () => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get('#list-introduction-choose-a-list').click()
|
||||
cy.get('#list-row-tokens-uniswap-eth .select-button').click()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
describe('Swap', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage()
|
||||
cy.visit('/swap')
|
||||
})
|
||||
it('can enter an amount into input', () => {
|
||||
@@ -59,6 +58,7 @@ describe('Swap', () => {
|
||||
cy.get('#toggle-expert-mode-button').click()
|
||||
cy.get('#confirm-expert-mode').click()
|
||||
})
|
||||
|
||||
it('add a recipient is visible', () => {
|
||||
cy.get('#add-recipient-button').should('be.visible')
|
||||
})
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
describe('Warning', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage()
|
||||
cy.visit('/swap?outputCurrency=0x0a40f26d74274b7f22b28556a27b35d97ce08e0a')
|
||||
})
|
||||
|
||||
it('Check that warning is displayed', () => {
|
||||
cy.get('.token-warning-container').should('be.visible')
|
||||
})
|
||||
|
||||
it('Check that warning hides after button dismissal', () => {
|
||||
cy.get('.token-dismiss-button').should('be.disabled')
|
||||
cy.get('.understand-checkbox').click()
|
||||
|
||||
@@ -73,6 +73,7 @@ Cypress.Commands.overwrite('visit', (original, url, options) => {
|
||||
...options,
|
||||
onBeforeLoad(win) {
|
||||
options && options.onBeforeLoad && options.onBeforeLoad(win)
|
||||
win.localStorage.clear()
|
||||
const provider = new JsonRpcProvider('https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
|
||||
const signer = new Wallet(PRIVATE_KEY_TEST_NEVER_USE, provider)
|
||||
win.ethereum = new CustomizedBridge(signer, provider)
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.5",
|
||||
"rebass": "^4.0.7",
|
||||
"redux-localstorage-simple": "^2.2.0",
|
||||
"redux-localstorage-simple": "^2.3.1",
|
||||
"serve": "^11.3.0",
|
||||
"start-server-and-test": "^1.11.0",
|
||||
"styled-components": "^4.2.0",
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
"pending": "Đang chờ xử lý",
|
||||
"selectToken": "Chọn một đồng tiền ảo",
|
||||
"searchOrPaste": "Tìm kiếm tên, biểu tượng, hoặc địa chỉ của đồng tiền ảo",
|
||||
"searchOrPasteMobile": "Tên, Biểu tương, hoặc Địa chỉ",
|
||||
"noExchange": "Không Tìm Thấy Giao Dịch",
|
||||
"searchOrPasteMobile": "Tên, Biểu tượng, hoặc Địa chỉ",
|
||||
"noExchange": "Không tìm thấy giao dịch",
|
||||
"exchangeRate": "Tỷ giá",
|
||||
"unknownError": "Rất tiếc! Xảy ra lỗi không xác định. Vui lòng làm mới trang, hoặc truy cập từ trình duyệt hay thiết bị khác.",
|
||||
"enterValueCont": "Nhập một giá trị {{ missingCurrencyValue }} để tiếp tục.",
|
||||
@@ -29,7 +29,7 @@
|
||||
"insufficientLiquidity": "Không đủ tính thanh khoản.",
|
||||
"unlockTokenCont": "Vui lòng mở khoá đồng tiền ảo để tiếp tục",
|
||||
"transactionDetails": "Chi tiết nâng cao",
|
||||
"hideDetails": "Che giấu chi tiết",
|
||||
"hideDetails": "Ẩn chi tiết",
|
||||
"slippageWarning": "Cảnh báo trượt giá",
|
||||
"highSlippageWarning": "Cảnh báo trượt giá cao",
|
||||
"youAreSelling": "Bạn đang bán",
|
||||
@@ -59,7 +59,7 @@
|
||||
"intoPool": "vào nhóm thanh khoản.",
|
||||
"outPool": "từ nhóm thanh khoản.",
|
||||
"youWillMint": "Bạn sẽ đúc tiền",
|
||||
"liquidityTokens": "dồng thanh khoản.",
|
||||
"liquidityTokens": "đồng thanh khoản.",
|
||||
"totalSupplyIs": "Tổng cung hiện tại của đồng thanh khoản là",
|
||||
"youAreSettingExRate": "Bạn đang đặt tỷ giá hối đoái ban đầu thành",
|
||||
"totalSupplyIs0": "Tổng cung hiện tại của đồng thanh khoản là 0.",
|
||||
|
||||
@@ -40,11 +40,12 @@ export default function Transaction({ hash }: { hash: string }) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const allTransactions = useAllTransactions()
|
||||
|
||||
const summary = allTransactions?.[hash]?.summary
|
||||
const pending = !allTransactions?.[hash]?.receipt
|
||||
const success =
|
||||
!pending &&
|
||||
(allTransactions[hash].receipt.status === 1 || typeof allTransactions[hash].receipt.status === 'undefined')
|
||||
const tx = allTransactions?.[hash]
|
||||
const summary = tx?.summary
|
||||
const pending = !tx?.receipt
|
||||
const success = !pending && tx && (tx.receipt?.status === 1 || typeof tx.receipt?.status === 'undefined')
|
||||
|
||||
if (!chainId) return null
|
||||
|
||||
return (
|
||||
<TransactionWrapper>
|
||||
|
||||
@@ -200,7 +200,7 @@ const MainWalletAction = styled(WalletAction)`
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
`
|
||||
|
||||
function renderTransactions(transactions) {
|
||||
function renderTransactions(transactions: string[]) {
|
||||
return (
|
||||
<TransactionListWrapper>
|
||||
{transactions.map((hash, i) => {
|
||||
@@ -212,8 +212,8 @@ function renderTransactions(transactions) {
|
||||
|
||||
interface AccountDetailsProps {
|
||||
toggleWalletModal: () => void
|
||||
pendingTransactions: any[]
|
||||
confirmedTransactions: any[]
|
||||
pendingTransactions: string[]
|
||||
confirmedTransactions: string[]
|
||||
ENSName?: string
|
||||
openOptions: () => void
|
||||
}
|
||||
@@ -282,15 +282,12 @@ export default function AccountDetails({
|
||||
</>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const clearAllTransactionsCallback = useCallback(
|
||||
(event: React.MouseEvent) => {
|
||||
event.preventDefault()
|
||||
dispatch(clearAllTransactions({ chainId }))
|
||||
},
|
||||
[dispatch, chainId]
|
||||
)
|
||||
const clearAllTransactionsCallback = useCallback(() => {
|
||||
if (chainId) dispatch(clearAllTransactions({ chainId }))
|
||||
}, [dispatch, chainId])
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -338,7 +335,7 @@ export default function AccountDetails({
|
||||
<>
|
||||
<div>
|
||||
{getStatusIcon()}
|
||||
<p> {shortenAddress(account)}</p>
|
||||
<p> {account && shortenAddress(account)}</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -349,17 +346,21 @@ export default function AccountDetails({
|
||||
<>
|
||||
<AccountControl>
|
||||
<div>
|
||||
<Copy toCopy={account}>
|
||||
<span style={{ marginLeft: '4px' }}>Copy Address</span>
|
||||
</Copy>
|
||||
<AddressLink
|
||||
hasENS={!!ENSName}
|
||||
isENS={true}
|
||||
href={getEtherscanLink(chainId, ENSName, 'address')}
|
||||
>
|
||||
<LinkIcon size={16} />
|
||||
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>
|
||||
</AddressLink>
|
||||
{account && (
|
||||
<Copy toCopy={account}>
|
||||
<span style={{ marginLeft: '4px' }}>Copy Address</span>
|
||||
</Copy>
|
||||
)}
|
||||
{chainId && account && (
|
||||
<AddressLink
|
||||
hasENS={!!ENSName}
|
||||
isENS={true}
|
||||
href={chainId && getEtherscanLink(chainId, ENSName, 'address')}
|
||||
>
|
||||
<LinkIcon size={16} />
|
||||
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>
|
||||
</AddressLink>
|
||||
)}
|
||||
</div>
|
||||
</AccountControl>
|
||||
</>
|
||||
@@ -367,17 +368,21 @@ export default function AccountDetails({
|
||||
<>
|
||||
<AccountControl>
|
||||
<div>
|
||||
<Copy toCopy={account}>
|
||||
<span style={{ marginLeft: '4px' }}>Copy Address</span>
|
||||
</Copy>
|
||||
<AddressLink
|
||||
hasENS={!!ENSName}
|
||||
isENS={false}
|
||||
href={getEtherscanLink(chainId, account, 'address')}
|
||||
>
|
||||
<LinkIcon size={16} />
|
||||
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>
|
||||
</AddressLink>
|
||||
{account && (
|
||||
<Copy toCopy={account}>
|
||||
<span style={{ marginLeft: '4px' }}>Copy Address</span>
|
||||
</Copy>
|
||||
)}
|
||||
{chainId && account && (
|
||||
<AddressLink
|
||||
hasENS={!!ENSName}
|
||||
isENS={false}
|
||||
href={getEtherscanLink(chainId, account, 'address')}
|
||||
>
|
||||
<LinkIcon size={16} />
|
||||
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>
|
||||
</AddressLink>
|
||||
)}
|
||||
</div>
|
||||
</AccountControl>
|
||||
</>
|
||||
|
||||
@@ -65,11 +65,6 @@ const Input = styled.input<{ error?: boolean }>`
|
||||
}
|
||||
`
|
||||
|
||||
interface Value {
|
||||
address: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export default function AddressInputPanel({
|
||||
id,
|
||||
value,
|
||||
@@ -106,7 +101,7 @@ export default function AddressInputPanel({
|
||||
<TYPE.black color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
Recipient
|
||||
</TYPE.black>
|
||||
{address && (
|
||||
{address && chainId && (
|
||||
<ExternalLink href={getEtherscanLink(chainId, name ?? address, 'address')} style={{ fontSize: '14px' }}>
|
||||
(View on Etherscan)
|
||||
</ExternalLink>
|
||||
|
||||
@@ -10,7 +10,7 @@ const Base = styled(RebassButton)<{
|
||||
padding?: string
|
||||
width?: string
|
||||
borderRadius?: string
|
||||
altDisbaledStyle?: boolean
|
||||
altDisabledStyle?: boolean
|
||||
}>`
|
||||
padding: ${({ padding }) => (padding ? padding : '18px')};
|
||||
width: ${({ width }) => (width ? width : '100%')};
|
||||
@@ -53,12 +53,13 @@ export const ButtonPrimary = styled(Base)`
|
||||
background-color: ${({ theme }) => darken(0.1, theme.primary1)};
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme, altDisbaledStyle }) => (altDisbaledStyle ? theme.primary1 : theme.bg3)};
|
||||
color: ${({ theme, altDisbaledStyle }) => (altDisbaledStyle ? 'white' : theme.text3)};
|
||||
background-color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? theme.primary1 : theme.bg3)};
|
||||
color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? 'white' : theme.text3)};
|
||||
cursor: auto;
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
outline: none;
|
||||
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.7' : '1')};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -253,11 +254,15 @@ const ButtonErrorStyle = styled(Base)`
|
||||
}
|
||||
`
|
||||
|
||||
export function ButtonConfirmed({ confirmed, ...rest }: { confirmed?: boolean } & ButtonProps) {
|
||||
export function ButtonConfirmed({
|
||||
confirmed,
|
||||
altDisabledStyle,
|
||||
...rest
|
||||
}: { confirmed?: boolean; altDisabledStyle?: boolean } & ButtonProps) {
|
||||
if (confirmed) {
|
||||
return <ButtonConfirmedStyle {...rest} />
|
||||
} else {
|
||||
return <ButtonPrimary {...rest} />
|
||||
return <ButtonPrimary {...rest} altDisabledStyle={altDisabledStyle} />
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,13 +137,13 @@ export default function CurrencyInputPanel({
|
||||
onMax,
|
||||
showMaxButton,
|
||||
label = 'Input',
|
||||
onCurrencySelect = null,
|
||||
currency = null,
|
||||
onCurrencySelect,
|
||||
currency,
|
||||
disableCurrencySelect = false,
|
||||
hideBalance = false,
|
||||
pair = null, // used for double token logo
|
||||
hideInput = false,
|
||||
otherCurrency = null,
|
||||
otherCurrency,
|
||||
id,
|
||||
showCommonBases
|
||||
}: CurrencyInputPanelProps) {
|
||||
@@ -151,7 +151,7 @@ export default function CurrencyInputPanel({
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const { account } = useActiveWeb3React()
|
||||
const selectedCurrencyBalance = useCurrencyBalance(account, currency)
|
||||
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
|
||||
const theme = useContext(ThemeContext)
|
||||
|
||||
const handleDismissSearch = useCallback(() => {
|
||||
@@ -231,7 +231,7 @@ export default function CurrencyInputPanel({
|
||||
</CurrencySelect>
|
||||
</InputRow>
|
||||
</Container>
|
||||
{!disableCurrencySelect && (
|
||||
{!disableCurrencySelect && onCurrencySelect && (
|
||||
<CurrencySearchModal
|
||||
isOpen={modalOpen}
|
||||
onDismiss={handleDismissSearch}
|
||||
|
||||
@@ -137,7 +137,7 @@ const NETWORK_LABELS: { [chainId in ChainId]: string | null } = {
|
||||
export default function Header() {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
|
||||
const userEthBalance = useETHBalances([account])[account]
|
||||
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
|
||||
const [isDark] = useDarkModeManager()
|
||||
|
||||
return (
|
||||
@@ -156,7 +156,7 @@ export default function Header() {
|
||||
<HeaderControls>
|
||||
<HeaderElement>
|
||||
<TestnetWrapper>
|
||||
{!isMobile && NETWORK_LABELS[chainId] && <NetworkCard>{NETWORK_LABELS[chainId]}</NetworkCard>}
|
||||
{!isMobile && chainId && NETWORK_LABELS[chainId] && <NetworkCard>{NETWORK_LABELS[chainId]}</NetworkCard>}
|
||||
</TestnetWrapper>
|
||||
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
|
||||
{account && userEthBalance ? (
|
||||
|
||||
@@ -5,7 +5,7 @@ import styled from 'styled-components'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import Jazzicon from 'jazzicon'
|
||||
|
||||
const StyledIdenticon = styled.div`
|
||||
const StyledIdenticonContainer = styled.div`
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
border-radius: 1.125rem;
|
||||
@@ -24,5 +24,6 @@ export default function Identicon() {
|
||||
}
|
||||
}, [account])
|
||||
|
||||
return <StyledIdenticon ref={ref} />
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
|
||||
return <StyledIdenticonContainer ref={ref as any} />
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ const StyledSVG = styled.svg<{ size: string; stroke?: string }>`
|
||||
* Takes in custom size and stroke for circle color, default to primary color as fill,
|
||||
* need ...rest for layered styles on top
|
||||
*/
|
||||
export default function Loader({ size = '16px', stroke = null, ...rest }: { size?: string; stroke?: string }) {
|
||||
export default function Loader({ size = '16px', stroke, ...rest }: { size?: string; stroke?: string }) {
|
||||
return (
|
||||
<StyledSVG viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" size={size} stroke={stroke} {...rest}>
|
||||
<path
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react'
|
||||
import { AlertTriangle } from 'react-feather'
|
||||
import { HelpCircle } from 'react-feather'
|
||||
import { ImageProps } from 'rebass'
|
||||
|
||||
const BAD_SRCS: { [tokenAddress: string]: true } = {}
|
||||
@@ -30,5 +30,5 @@ export default function Logo({ srcs, alt, ...rest }: LogoProps) {
|
||||
)
|
||||
}
|
||||
|
||||
return <AlertTriangle {...rest} />
|
||||
return <HelpCircle {...rest} />
|
||||
}
|
||||
|
||||
@@ -87,7 +87,8 @@ export default function Menu() {
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
|
||||
return (
|
||||
<StyledMenu ref={node}>
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
|
||||
<StyledMenu ref={node as any}>
|
||||
<StyledMenuButton onClick={toggle}>
|
||||
<StyledMenuIcon />
|
||||
</StyledMenuButton>
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function Modal({
|
||||
onDismiss,
|
||||
minHeight = false,
|
||||
maxHeight = 50,
|
||||
initialFocusRef = null,
|
||||
initialFocusRef,
|
||||
children
|
||||
}: ModalProps) {
|
||||
const fadeTransition = useTransition(isOpen, null, {
|
||||
|
||||
@@ -46,7 +46,7 @@ export const Input = React.memo(function InnerInput({
|
||||
...rest
|
||||
}: {
|
||||
value: string | number
|
||||
onUserInput: (string) => void
|
||||
onUserInput: (input: string) => void
|
||||
error?: boolean
|
||||
fontSize?: string
|
||||
align?: 'right' | 'left'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Placement } from '@popperjs/core'
|
||||
import { transparentize } from 'polished'
|
||||
import React, { useState } from 'react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { usePopper } from 'react-popper'
|
||||
import styled from 'styled-components'
|
||||
import useInterval from '../../hooks/useInterval'
|
||||
@@ -83,9 +83,9 @@ export interface PopoverProps {
|
||||
}
|
||||
|
||||
export default function Popover({ content, show, children, placement = 'auto' }: PopoverProps) {
|
||||
const [referenceElement, setReferenceElement] = useState<HTMLDivElement>(null)
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement>(null)
|
||||
const [arrowElement, setArrowElement] = useState<HTMLDivElement>(null)
|
||||
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null)
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
|
||||
const [arrowElement, setArrowElement] = useState<HTMLDivElement | null>(null)
|
||||
const { styles, update, attributes } = usePopper(referenceElement, popperElement, {
|
||||
placement,
|
||||
strategy: 'fixed',
|
||||
@@ -94,17 +94,20 @@ export default function Popover({ content, show, children, placement = 'auto' }:
|
||||
{ name: 'arrow', options: { element: arrowElement } }
|
||||
]
|
||||
})
|
||||
useInterval(update, show ? 100 : null)
|
||||
const updateCallback = useCallback(() => {
|
||||
update && update()
|
||||
}, [update])
|
||||
useInterval(updateCallback, show ? 100 : null)
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReferenceElement ref={setReferenceElement}>{children}</ReferenceElement>
|
||||
<ReferenceElement ref={setReferenceElement as any}>{children}</ReferenceElement>
|
||||
<Portal>
|
||||
<PopoverContainer show={show} ref={setPopperElement} style={styles.popper} {...attributes.popper}>
|
||||
<PopoverContainer show={show} ref={setPopperElement as any} style={styles.popper} {...attributes.popper}>
|
||||
{content}
|
||||
<Arrow
|
||||
className={`arrow-${attributes.popper?.['data-popper-placement'] ?? ''}`}
|
||||
ref={setArrowElement}
|
||||
ref={setArrowElement as any}
|
||||
style={styles.arrow}
|
||||
{...attributes.arrow}
|
||||
/>
|
||||
|
||||
@@ -44,7 +44,8 @@ export default function ListUpdatePopup({
|
||||
return diffTokenLists(oldList.tokens, newList.tokens)
|
||||
}, [newList.tokens, oldList.tokens])
|
||||
const numTokensChanged = useMemo(
|
||||
() => Object.keys(tokensChanged).reduce((memo, chainId) => memo + Object.keys(tokensChanged[chainId]).length, 0),
|
||||
() =>
|
||||
Object.keys(tokensChanged).reduce((memo, chainId: any) => memo + Object.keys(tokensChanged[chainId]).length, 0),
|
||||
[tokensChanged]
|
||||
)
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function PopupItem({
|
||||
const removePopup = useRemovePopup()
|
||||
const removeThisPopup = useCallback(() => removePopup(popKey), [popKey, removePopup])
|
||||
useEffect(() => {
|
||||
if (removeAfterMs === null) return
|
||||
if (removeAfterMs === null) return undefined
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
removeThisPopup()
|
||||
@@ -81,7 +81,11 @@ export default function PopupItem({
|
||||
popupContent = <ListUpdatePopup popKey={popKey} listUrl={listUrl} oldList={oldList} newList={newList} auto={auto} />
|
||||
}
|
||||
|
||||
const faderStyle = useSpring({ from: { width: '100%' }, to: { width: '0%' }, config: { duration: removeAfterMs } })
|
||||
const faderStyle = useSpring({
|
||||
from: { width: '100%' },
|
||||
to: { width: '0%' },
|
||||
config: { duration: removeAfterMs ?? undefined }
|
||||
})
|
||||
|
||||
return (
|
||||
<Popup>
|
||||
|
||||
@@ -32,7 +32,9 @@ export default function TransactionPopup({
|
||||
</div>
|
||||
<AutoColumn gap="8px">
|
||||
<TYPE.body fontWeight={500}>{summary ?? 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)}</TYPE.body>
|
||||
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')}>View on Etherscan</ExternalLink>
|
||||
{chainId && (
|
||||
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')}>View on Etherscan</ExternalLink>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</RowNoFlex>
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ function V1PositionCard({ token, V1LiquidityBalance }: PositionCardProps) {
|
||||
<RowFixed>
|
||||
<DoubleCurrencyLogo currency0={token} margin={true} size={20} />
|
||||
<Text fontWeight={500} fontSize={20} style={{ marginLeft: '' }}>
|
||||
{`${token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH`}
|
||||
{`${chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH`}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={12}
|
||||
|
||||
@@ -46,7 +46,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
|
||||
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
|
||||
const userPoolBalance = useTokenBalance(account, pair.liquidityToken)
|
||||
const userPoolBalance = useTokenBalance(account ?? undefined, pair.liquidityToken)
|
||||
const totalPoolTokens = useTotalSupply(pair.liquidityToken)
|
||||
|
||||
const [token0Deposited, token1Deposited] =
|
||||
@@ -131,7 +131,7 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) {
|
||||
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
|
||||
const userPoolBalance = useTokenBalance(account, pair.liquidityToken)
|
||||
const userPoolBalance = useTokenBalance(account ?? undefined, pair.liquidityToken)
|
||||
const totalPoolTokens = useTotalSupply(pair.liquidityToken)
|
||||
|
||||
const poolTokenPercentage =
|
||||
|
||||
79
src/components/ProgressSteps/index.tsx
Normal file
79
src/components/ProgressSteps/index.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { RowBetween } from '../Row'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { transparentize } from 'polished'
|
||||
|
||||
const Wrapper = styled(AutoColumn)`
|
||||
margin-top: 1.25rem;
|
||||
`
|
||||
|
||||
const Grouping = styled(RowBetween)`
|
||||
width: 50%;
|
||||
`
|
||||
|
||||
const Circle = styled.div<{ confirmed?: boolean; disabled?: boolean }>`
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
background-color: ${({ theme, confirmed, disabled }) =>
|
||||
disabled ? theme.bg4 : confirmed ? theme.green1 : theme.primary1};
|
||||
border-radius: 50%;
|
||||
color: ${({ theme }) => theme.white};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 8px;
|
||||
font-size: 12px;
|
||||
`
|
||||
|
||||
const CircleRow = styled.div`
|
||||
width: calc(100% - 20px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const Connector = styled.div<{ prevConfirmed?: boolean }>`
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: ;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
${({ theme, prevConfirmed }) => transparentize(0.5, prevConfirmed ? theme.green1 : theme.primary1)} 0%,
|
||||
${({ theme, prevConfirmed }) => (prevConfirmed ? theme.primary1 : theme.bg4)} 80%
|
||||
);
|
||||
opacity: 0.6;
|
||||
`
|
||||
|
||||
interface ProgressCirclesProps {
|
||||
steps: boolean[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on array of steps, create a step counter of circles.
|
||||
* A circle can be enabled, disabled, or confirmed. States are derived
|
||||
* from previous step.
|
||||
*
|
||||
* An extra circle is added to represent the ability to swap, add, or remove.
|
||||
* This step will never be marked as complete (because no 'txn done' state in body ui).
|
||||
*
|
||||
* @param steps array of booleans where true means step is complete
|
||||
*/
|
||||
export default function ProgressCircles({ steps }: ProgressCirclesProps) {
|
||||
return (
|
||||
<Wrapper justify={'center'}>
|
||||
<Grouping>
|
||||
{steps.map((step, i) => {
|
||||
return (
|
||||
<CircleRow key={i}>
|
||||
<Circle confirmed={step} disabled={!steps[i - 1] && i !== 0}>
|
||||
{step ? '✓' : i + 1}
|
||||
</Circle>
|
||||
<Connector prevConfirmed={step} />
|
||||
</CircleRow>
|
||||
)
|
||||
})}
|
||||
<Circle disabled={!steps[steps.length - 1]}>{steps.length + 1}</Circle>
|
||||
</Grouping>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export default function CommonBases({
|
||||
selectedCurrency
|
||||
}: {
|
||||
chainId?: ChainId
|
||||
selectedCurrency?: Currency
|
||||
selectedCurrency?: Currency | null
|
||||
onSelect: (currency: Currency) => void
|
||||
}) {
|
||||
return (
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useSelectedTokenList, WrappedTokenInfo } from '../../state/lists/hooks'
|
||||
import { useAddUserToken, useRemoveUserAddedToken } from '../../state/user/hooks'
|
||||
import { useCurrencyBalance } from '../../state/wallet/hooks'
|
||||
import { LinkStyledButton, TYPE } from '../../theme'
|
||||
import { useIsUserAddedToken } from '../../hooks/Tokens'
|
||||
import Column from '../Column'
|
||||
import { RowFixed } from '../Row'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
@@ -96,12 +97,13 @@ function CurrencyRow({
|
||||
const key = currencyKey(currency)
|
||||
const selectedTokenList = useSelectedTokenList()
|
||||
const isOnSelectedList = isTokenOnList(selectedTokenList, currency)
|
||||
const customAdded = Boolean(!isOnSelectedList && currency instanceof Token)
|
||||
const customAdded = useIsUserAddedToken(currency)
|
||||
const balance = useCurrencyBalance(account ?? undefined, currency)
|
||||
|
||||
const removeToken = useRemoveUserAddedToken()
|
||||
const addToken = useAddUserToken()
|
||||
|
||||
// only show add or remove buttons if not on selected list
|
||||
return (
|
||||
<MenuItem
|
||||
style={style}
|
||||
@@ -116,7 +118,7 @@ function CurrencyRow({
|
||||
{currency.symbol}
|
||||
</Text>
|
||||
<FadedSpan>
|
||||
{customAdded ? (
|
||||
{!isOnSelectedList && customAdded ? (
|
||||
<TYPE.main fontWeight={500}>
|
||||
Added by user
|
||||
<LinkStyledButton
|
||||
@@ -163,9 +165,9 @@ export default function CurrencyList({
|
||||
}: {
|
||||
height: number
|
||||
currencies: Currency[]
|
||||
selectedCurrency: Currency | undefined
|
||||
selectedCurrency?: Currency | null
|
||||
onCurrencySelect: (currency: Currency) => void
|
||||
otherCurrency: Currency | undefined
|
||||
otherCurrency?: Currency | null
|
||||
fixedListRef?: MutableRefObject<FixedSizeList | undefined>
|
||||
showETH: boolean
|
||||
}) {
|
||||
|
||||
@@ -26,9 +26,9 @@ import AutoSizer from 'react-virtualized-auto-sizer'
|
||||
interface CurrencySearchProps {
|
||||
isOpen: boolean
|
||||
onDismiss: () => void
|
||||
selectedCurrency?: Currency
|
||||
selectedCurrency?: Currency | null
|
||||
onCurrencySelect: (currency: Currency) => void
|
||||
otherSelectedCurrency?: Currency
|
||||
otherSelectedCurrency?: Currency | null
|
||||
showCommonBases?: boolean
|
||||
onChangeList: () => void
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import { ListSelect } from './ListSelect'
|
||||
interface CurrencySearchModalProps {
|
||||
isOpen: boolean
|
||||
onDismiss: () => void
|
||||
selectedCurrency?: Currency
|
||||
selectedCurrency?: Currency | null
|
||||
onCurrencySelect: (currency: Currency) => void
|
||||
otherSelectedCurrency?: Currency
|
||||
otherSelectedCurrency?: Currency | null
|
||||
showCommonBases?: boolean
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,6 @@ export function filterTokens(tokens: Token[], search: string): Token[] {
|
||||
return tokens.filter(token => {
|
||||
const { symbol, name } = token
|
||||
|
||||
return matchesSearch(symbol) || matchesSearch(name)
|
||||
return (symbol && matchesSearch(symbol)) || (name && matchesSearch(name))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
@@ -141,7 +141,8 @@ export default function SettingsTab() {
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
|
||||
return (
|
||||
<StyledMenu ref={node}>
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
|
||||
<StyledMenu ref={node as any}>
|
||||
<Modal isOpen={showConfirmation} onDismiss={() => setShowConfirmation(false)} maxHeight={100}>
|
||||
<ModalContentWrapper>
|
||||
<AutoColumn gap="lg">
|
||||
|
||||
@@ -53,7 +53,7 @@ function TokenWarningCard({ token }: TokenWarningCardProps) {
|
||||
if (userToken.equals(token)) {
|
||||
return false
|
||||
}
|
||||
return userToken.symbol.toLowerCase() === tokenSymbol || userToken.name.toLowerCase() === tokenName
|
||||
return userToken.symbol?.toLowerCase() === tokenSymbol || userToken.name?.toLowerCase() === tokenName
|
||||
})
|
||||
}, [token, chainId, allTokens, tokenSymbol, tokenName])
|
||||
|
||||
@@ -72,9 +72,11 @@ function TokenWarningCard({ token }: TokenWarningCardProps) {
|
||||
? `${token.name} (${token.symbol})`
|
||||
: token.name || token.symbol}{' '}
|
||||
</TYPE.main>
|
||||
<ExternalLink style={{ fontWeight: 400 }} href={getEtherscanLink(chainId, token.address, 'token')}>
|
||||
<TYPE.blue title={token.address}>{shortenAddress(token.address)} (View on Etherscan)</TYPE.blue>
|
||||
</ExternalLink>
|
||||
{chainId && (
|
||||
<ExternalLink style={{ fontWeight: 400 }} href={getEtherscanLink(chainId, token.address, 'token')}>
|
||||
<TYPE.blue title={token.address}>{shortenAddress(token.address)} (View on Etherscan)</TYPE.blue>
|
||||
</ExternalLink>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</AutoRow>
|
||||
</Wrapper>
|
||||
|
||||
@@ -91,11 +91,13 @@ function TransactionSubmittedContent({
|
||||
Transaction Submitted
|
||||
</Text>
|
||||
|
||||
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')}>
|
||||
<Text fontWeight={500} fontSize={14} color={theme.primary1}>
|
||||
View on Etherscan
|
||||
</Text>
|
||||
</ExternalLink>
|
||||
{chainId && hash && (
|
||||
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')}>
|
||||
<Text fontWeight={500} fontSize={14} color={theme.primary1}>
|
||||
View on Etherscan
|
||||
</Text>
|
||||
</ExternalLink>
|
||||
)}
|
||||
<ButtonPrimary onClick={onDismiss} style={{ margin: '20px 0 0 0' }}>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
Close
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
@@ -104,48 +104,44 @@ export default function SlippageTabs({ rawSlippage, setRawSlippage, deadline, se
|
||||
slippageInput === '' || (rawSlippage / 100).toFixed(2) === Number.parseFloat(slippageInput).toFixed(2)
|
||||
const deadlineInputIsValid = deadlineInput === '' || (deadline / 60).toString() === deadlineInput
|
||||
|
||||
let slippageError: SlippageError
|
||||
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
|
||||
let deadlineError: DeadlineError | undefined
|
||||
if (deadlineInput !== '' && !deadlineInputIsValid) {
|
||||
deadlineError = DeadlineError.InvalidInput
|
||||
} else {
|
||||
deadlineError = undefined
|
||||
}
|
||||
|
||||
function parseCustomSlippage(event) {
|
||||
setSlippageInput(event.target.value)
|
||||
function parseCustomSlippage(value: string) {
|
||||
setSlippageInput(value)
|
||||
|
||||
let valueAsIntFromRoundedFloat: number
|
||||
try {
|
||||
valueAsIntFromRoundedFloat = Number.parseInt((Number.parseFloat(event.target.value) * 100).toString())
|
||||
const valueAsIntFromRoundedFloat = Number.parseInt((Number.parseFloat(value) * 100).toString())
|
||||
if (!Number.isNaN(valueAsIntFromRoundedFloat) && valueAsIntFromRoundedFloat < 5000) {
|
||||
setRawSlippage(valueAsIntFromRoundedFloat)
|
||||
}
|
||||
} catch {}
|
||||
|
||||
if (
|
||||
typeof valueAsIntFromRoundedFloat === 'number' &&
|
||||
!Number.isNaN(valueAsIntFromRoundedFloat) &&
|
||||
valueAsIntFromRoundedFloat < 5000
|
||||
) {
|
||||
setRawSlippage(valueAsIntFromRoundedFloat)
|
||||
}
|
||||
}
|
||||
|
||||
function parseCustomDeadline(event) {
|
||||
setDeadlineInput(event.target.value)
|
||||
function parseCustomDeadline(value: string) {
|
||||
setDeadlineInput(value)
|
||||
|
||||
let valueAsInt: number
|
||||
try {
|
||||
valueAsInt = Number.parseInt(event.target.value) * 60
|
||||
const valueAsInt: number = Number.parseInt(value) * 60
|
||||
if (!Number.isNaN(valueAsInt) && valueAsInt > 0) {
|
||||
setDeadline(valueAsInt)
|
||||
}
|
||||
} catch {}
|
||||
|
||||
if (typeof valueAsInt === 'number' && !Number.isNaN(valueAsInt) && valueAsInt > 0) {
|
||||
setDeadline(valueAsInt)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -195,14 +191,15 @@ export default function SlippageTabs({ rawSlippage, setRawSlippage, deadline, se
|
||||
</span>
|
||||
</SlippageEmojiContainer>
|
||||
) : null}
|
||||
{/* https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451 */}
|
||||
<Input
|
||||
ref={inputRef}
|
||||
ref={inputRef as any}
|
||||
placeholder={(rawSlippage / 100).toFixed(2)}
|
||||
value={slippageInput}
|
||||
onBlur={() => {
|
||||
parseCustomSlippage({ target: { value: (rawSlippage / 100).toFixed(2) } })
|
||||
parseCustomSlippage((rawSlippage / 100).toFixed(2))
|
||||
}}
|
||||
onChange={parseCustomSlippage}
|
||||
onChange={e => parseCustomSlippage(e.target.value)}
|
||||
color={!slippageInputIsValid ? 'red' : ''}
|
||||
/>
|
||||
%
|
||||
@@ -238,11 +235,11 @@ export default function SlippageTabs({ rawSlippage, setRawSlippage, deadline, se
|
||||
<Input
|
||||
color={!!deadlineError ? 'red' : undefined}
|
||||
onBlur={() => {
|
||||
parseCustomDeadline({ target: { value: (deadline / 60).toString() } })
|
||||
parseCustomDeadline((deadline / 60).toString())
|
||||
}}
|
||||
placeholder={(deadline / 60).toString()}
|
||||
value={deadlineInput}
|
||||
onChange={parseCustomDeadline}
|
||||
onChange={e => parseCustomDeadline(e.target.value)}
|
||||
/>
|
||||
</OptionCustom>
|
||||
<TYPE.body style={{ paddingLeft: '8px' }} fontSize={14}>
|
||||
|
||||
@@ -73,7 +73,7 @@ const SubHeader = styled.div`
|
||||
font-size: 12px;
|
||||
`
|
||||
|
||||
const IconWrapper = styled.div<{ size?: number }>`
|
||||
const IconWrapper = styled.div<{ size?: number | null }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap};
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -90,7 +90,7 @@ const IconWrapper = styled.div<{ size?: number }>`
|
||||
export default function Option({
|
||||
link = null,
|
||||
clickable = true,
|
||||
size = null,
|
||||
size,
|
||||
onClick = null,
|
||||
color,
|
||||
header,
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function PendingView({
|
||||
<ErrorButton
|
||||
onClick={() => {
|
||||
setPendingError(false)
|
||||
tryActivation(connector)
|
||||
connector && tryActivation(connector)
|
||||
}}
|
||||
>
|
||||
Try Again
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ReactComponent as Close } from '../../assets/images/x.svg'
|
||||
import { injected, fortmatic, portis } from '../../connectors'
|
||||
import { OVERLAY_READY } from '../../connectors/Fortmatic'
|
||||
import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
|
||||
import { AbstractConnector } from '@web3-react/abstract-connector'
|
||||
|
||||
const CloseIcon = styled.div`
|
||||
position: absolute;
|
||||
@@ -128,7 +129,7 @@ export default function WalletModal({
|
||||
|
||||
const [walletView, setWalletView] = useState(WALLET_VIEWS.ACCOUNT)
|
||||
|
||||
const [pendingWallet, setPendingWallet] = useState()
|
||||
const [pendingWallet, setPendingWallet] = useState<AbstractConnector | undefined>()
|
||||
|
||||
const [pendingError, setPendingError] = useState<boolean>()
|
||||
|
||||
@@ -161,7 +162,7 @@ export default function WalletModal({
|
||||
}
|
||||
}, [setWalletView, active, error, connector, walletModalOpen, activePrevious, connectorPrevious])
|
||||
|
||||
const tryActivation = async connector => {
|
||||
const tryActivation = async (connector: AbstractConnector | undefined) => {
|
||||
let name = ''
|
||||
Object.keys(SUPPORTED_WALLETS).map(key => {
|
||||
if (connector === SUPPORTED_WALLETS[key].connector) {
|
||||
@@ -183,13 +184,14 @@ export default function WalletModal({
|
||||
connector.walletConnectProvider = undefined
|
||||
}
|
||||
|
||||
activate(connector, undefined, true).catch(error => {
|
||||
if (error instanceof UnsupportedChainIdError) {
|
||||
activate(connector) // a little janky...can't use setError because the connector isn't set
|
||||
} else {
|
||||
setPendingError(true)
|
||||
}
|
||||
})
|
||||
connector &&
|
||||
activate(connector, undefined, true).catch(error => {
|
||||
if (error instanceof UnsupportedChainIdError) {
|
||||
activate(connector) // a little janky...can't use setError because the connector isn't set
|
||||
} else {
|
||||
setPendingError(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// close wallet modal if fortmatic modal is active
|
||||
@@ -358,7 +360,7 @@ export default function WalletModal({
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={walletModalOpen} onDismiss={toggleWalletModal} minHeight={null} maxHeight={90}>
|
||||
<Modal isOpen={walletModalOpen} onDismiss={toggleWalletModal} minHeight={false} maxHeight={90}>
|
||||
<Wrapper>{getModalContent()}</Wrapper>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ const Message = styled.h2`
|
||||
color: ${({ theme }) => theme.secondary1};
|
||||
`
|
||||
|
||||
export default function Web3ReactManager({ children }) {
|
||||
export default function Web3ReactManager({ children }: { children: JSX.Element }) {
|
||||
const { t } = useTranslation()
|
||||
const { active } = useWeb3React()
|
||||
const { active: networkActive, error: networkError, activate: activateNetwork } = useWeb3React(NetworkContextName)
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import styled, { css } from 'styled-components'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useWeb3React, UnsupportedChainIdError } from '@web3-react/core'
|
||||
import { AbstractConnector } from '@web3-react/abstract-connector'
|
||||
import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
|
||||
import { darken, lighten } from 'polished'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Activity } from 'react-feather'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled, { css } from 'styled-components'
|
||||
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
|
||||
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
|
||||
import PortisIcon from '../../assets/images/portisIcon.png'
|
||||
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
|
||||
import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors'
|
||||
import { NetworkContextName } from '../../constants'
|
||||
import useENSName from '../../hooks/useENSName'
|
||||
import { useHasSocks } from '../../hooks/useSocksBalance'
|
||||
import { useWalletModalToggle } from '../../state/application/hooks'
|
||||
import { isTransactionRecent, useAllTransactions } from '../../state/transactions/hooks'
|
||||
import { TransactionDetails } from '../../state/transactions/reducer'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { ButtonSecondary } from '../Button'
|
||||
|
||||
import Identicon from '../Identicon'
|
||||
import PortisIcon from '../../assets/images/portisIcon.png'
|
||||
import WalletModal from '../WalletModal'
|
||||
import { ButtonSecondary } from '../Button'
|
||||
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
|
||||
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
|
||||
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
|
||||
import Loader from '../Loader'
|
||||
|
||||
import { RowBetween } from '../Row'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { useAllTransactions } from '../../state/transactions/hooks'
|
||||
import { NetworkContextName } from '../../constants'
|
||||
import { injected, walletconnect, walletlink, fortmatic, portis } from '../../connectors'
|
||||
import Loader from '../Loader'
|
||||
import WalletModal from '../WalletModal'
|
||||
|
||||
const IconWrapper = styled.div<{ size?: number }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap};
|
||||
@@ -118,104 +119,114 @@ const NetworkIcon = styled(Activity)`
|
||||
`
|
||||
|
||||
// we want the latest one to come first, so return negative if a is after b
|
||||
function newTranscationsFirst(a: TransactionDetails, b: TransactionDetails) {
|
||||
function newTransactionsFirst(a: TransactionDetails, b: TransactionDetails) {
|
||||
return b.addedTime - a.addedTime
|
||||
}
|
||||
|
||||
function recentTransactionsOnly(a: TransactionDetails) {
|
||||
return new Date().getTime() - a.addedTime < 86_400_000
|
||||
}
|
||||
|
||||
const SOCK = (
|
||||
<span role="img" aria-label="has socks emoji" style={{ marginTop: -4, marginBottom: -4 }}>
|
||||
🧦
|
||||
</span>
|
||||
)
|
||||
|
||||
export default function Web3Status() {
|
||||
const { t } = useTranslation()
|
||||
const { active, account, connector, error } = useWeb3React()
|
||||
const contextNetwork = useWeb3React(NetworkContextName)
|
||||
// eslint-disable-next-line react/prop-types
|
||||
function StatusIcon({ connector }: { connector: AbstractConnector }) {
|
||||
if (connector === injected) {
|
||||
return <Identicon />
|
||||
} else if (connector === walletconnect) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={WalletConnectIcon} alt={''} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === walletlink) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={CoinbaseWalletIcon} alt={''} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === fortmatic) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={FortmaticIcon} alt={''} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === portis) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={PortisIcon} alt={''} />
|
||||
</IconWrapper>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const { ENSName } = useENSName(account)
|
||||
function Web3StatusInner() {
|
||||
const { t } = useTranslation()
|
||||
const { account, connector, error } = useWeb3React()
|
||||
|
||||
const { ENSName } = useENSName(account ?? undefined)
|
||||
|
||||
const allTransactions = useAllTransactions()
|
||||
|
||||
const sortedRecentTransactions = useMemo(() => {
|
||||
const txs = Object.values(allTransactions)
|
||||
return txs.filter(recentTransactionsOnly).sort(newTranscationsFirst)
|
||||
return txs.filter(isTransactionRecent).sort(newTransactionsFirst)
|
||||
}, [allTransactions])
|
||||
|
||||
const pending = sortedRecentTransactions.filter(tx => !tx.receipt).map(tx => tx.hash)
|
||||
const confirmed = sortedRecentTransactions.filter(tx => tx.receipt).map(tx => tx.hash)
|
||||
|
||||
const hasPendingTransactions = !!pending.length
|
||||
const hasSocks = useHasSocks()
|
||||
const toggleWalletModal = useWalletModalToggle()
|
||||
|
||||
// handle the logo we want to show with the account
|
||||
function getStatusIcon() {
|
||||
if (connector === injected) {
|
||||
return <Identicon />
|
||||
} else if (connector === walletconnect) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={WalletConnectIcon} alt={''} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === walletlink) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={CoinbaseWalletIcon} alt={''} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === fortmatic) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={FortmaticIcon} alt={''} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === portis) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={PortisIcon} alt={''} />
|
||||
</IconWrapper>
|
||||
)
|
||||
}
|
||||
if (account) {
|
||||
return (
|
||||
<Web3StatusConnected id="web3-status-connected" onClick={toggleWalletModal} pending={hasPendingTransactions}>
|
||||
{hasPendingTransactions ? (
|
||||
<RowBetween>
|
||||
<Text>{pending?.length} Pending</Text> <Loader stroke="white" />
|
||||
</RowBetween>
|
||||
) : (
|
||||
<>
|
||||
{hasSocks ? SOCK : null}
|
||||
<Text>{ENSName || shortenAddress(account)}</Text>
|
||||
</>
|
||||
)}
|
||||
{!hasPendingTransactions && connector && <StatusIcon connector={connector} />}
|
||||
</Web3StatusConnected>
|
||||
)
|
||||
} else if (error) {
|
||||
return (
|
||||
<Web3StatusError onClick={toggleWalletModal}>
|
||||
<NetworkIcon />
|
||||
<Text>{error instanceof UnsupportedChainIdError ? 'Wrong Network' : 'Error'}</Text>
|
||||
</Web3StatusError>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Web3StatusConnect id="connect-wallet" onClick={toggleWalletModal} faded={!account}>
|
||||
<Text>{t('Connect to a wallet')}</Text>
|
||||
</Web3StatusConnect>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function getWeb3Status() {
|
||||
if (account) {
|
||||
return (
|
||||
<Web3StatusConnected id="web3-status-connected" onClick={toggleWalletModal} pending={hasPendingTransactions}>
|
||||
{hasPendingTransactions ? (
|
||||
<RowBetween>
|
||||
<Text>{pending?.length} Pending</Text> <Loader stroke="white" />
|
||||
</RowBetween>
|
||||
) : (
|
||||
<>
|
||||
{hasSocks ? SOCK : null}
|
||||
<Text>{ENSName || shortenAddress(account)}</Text>
|
||||
</>
|
||||
)}
|
||||
{!hasPendingTransactions && getStatusIcon()}
|
||||
</Web3StatusConnected>
|
||||
)
|
||||
} else if (error) {
|
||||
return (
|
||||
<Web3StatusError onClick={toggleWalletModal}>
|
||||
<NetworkIcon />
|
||||
<Text>{error instanceof UnsupportedChainIdError ? 'Wrong Network' : 'Error'}</Text>
|
||||
</Web3StatusError>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Web3StatusConnect id="connect-wallet" onClick={toggleWalletModal} faded={!account}>
|
||||
<Text>{t('Connect to a wallet')}</Text>
|
||||
</Web3StatusConnect>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default function Web3Status() {
|
||||
const { active, account } = useWeb3React()
|
||||
const contextNetwork = useWeb3React(NetworkContextName)
|
||||
|
||||
const { ENSName } = useENSName(account ?? undefined)
|
||||
|
||||
const allTransactions = useAllTransactions()
|
||||
|
||||
const sortedRecentTransactions = useMemo(() => {
|
||||
const txs = Object.values(allTransactions)
|
||||
return txs.filter(isTransactionRecent).sort(newTransactionsFirst)
|
||||
}, [allTransactions])
|
||||
|
||||
const pending = sortedRecentTransactions.filter(tx => !tx.receipt).map(tx => tx.hash)
|
||||
const confirmed = sortedRecentTransactions.filter(tx => tx.receipt).map(tx => tx.hash)
|
||||
|
||||
if (!contextNetwork.active && !active) {
|
||||
return null
|
||||
@@ -223,8 +234,8 @@ export default function Web3Status() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{getWeb3Status()}
|
||||
<WalletModal ENSName={ENSName} pendingTransactions={pending} confirmedTransactions={confirmed} />
|
||||
<Web3StatusInner />
|
||||
<WalletModal ENSName={ENSName ?? undefined} pendingTransactions={pending} confirmedTransactions={confirmed} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import ReactGA from 'react-ga'
|
||||
import { RouteComponentProps } from 'react-router-dom'
|
||||
|
||||
// fires a GA pageview every time the route changes
|
||||
export default function GoogleAnalyticsReporter({ location: { pathname, search } }: RouteComponentProps) {
|
||||
export default function GoogleAnalyticsReporter({ location: { pathname, search } }: RouteComponentProps): null {
|
||||
useEffect(() => {
|
||||
ReactGA.pageview(`${pathname}${search}`)
|
||||
}, [pathname, search])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { Currency, Price } from '@uniswap/sdk'
|
||||
import { Price } from '@uniswap/sdk'
|
||||
import { useContext } from 'react'
|
||||
import { Repeat } from 'react-feather'
|
||||
import { Text } from 'rebass'
|
||||
@@ -8,27 +8,19 @@ import { StyledBalanceMaxMini } from './styleds'
|
||||
|
||||
interface TradePriceProps {
|
||||
price?: Price
|
||||
inputCurrency?: Currency
|
||||
outputCurrency?: Currency
|
||||
showInverted: boolean
|
||||
setShowInverted: (showInverted: boolean) => void
|
||||
}
|
||||
|
||||
export default function TradePrice({
|
||||
price,
|
||||
inputCurrency,
|
||||
outputCurrency,
|
||||
showInverted,
|
||||
setShowInverted
|
||||
}: TradePriceProps) {
|
||||
export default function TradePrice({ price, showInverted, setShowInverted }: TradePriceProps) {
|
||||
const theme = useContext(ThemeContext)
|
||||
|
||||
const formattedPrice = showInverted ? price?.toSignificant(6) : price?.invert()?.toSignificant(6)
|
||||
|
||||
const show = Boolean(inputCurrency && outputCurrency)
|
||||
const show = Boolean(price?.baseCurrency && price?.quoteCurrency)
|
||||
const label = showInverted
|
||||
? `${outputCurrency?.symbol} per ${inputCurrency?.symbol}`
|
||||
: `${inputCurrency?.symbol} per ${outputCurrency?.symbol}`
|
||||
? `${price?.quoteCurrency?.symbol} per ${price?.baseCurrency?.symbol}`
|
||||
: `${price?.baseCurrency?.symbol} per ${price?.quoteCurrency?.symbol}`
|
||||
|
||||
return (
|
||||
<Text
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
@@ -16,6 +16,7 @@ export class FortmaticConnector extends FortmaticConnectorCore {
|
||||
async activate() {
|
||||
if (!this.fortmatic) {
|
||||
const { default: Fortmatic } = await import('fortmatic')
|
||||
|
||||
const { apiKey, chainId } = this as any
|
||||
if (chainId in CHAIN_ID_NETWORK_ARGUMENT) {
|
||||
this.fortmatic = new Fortmatic(apiKey, CHAIN_ID_NETWORK_ARGUMENT[chainId as FormaticSupportedChains])
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
@@ -11,10 +11,10 @@ export const DEFAULT_LIST_OF_LISTS: string[] = [
|
||||
'erc20.cmc.eth',
|
||||
'stablecoin.cmc.eth',
|
||||
'tokenlist.zerion.eth',
|
||||
'tokenlist.aave.eth',
|
||||
'https://www.coingecko.com/tokens_list/uniswap/defi_100/v_0_0_0.json',
|
||||
'https://app.tryroll.com/tokens.json',
|
||||
'https://raw.githubusercontent.com/compound-finance/token-list/master/compound.tokenlist.json',
|
||||
'ipfs://QmVNCFc3y1DMt8n4K42d8BYubUhQ7FgcNxzEHxSEHszUhL', // aave token list
|
||||
'https://defiprime.com/defiprime.tokenlist.json',
|
||||
'https://umaproject.org/uma.tokenlist.json'
|
||||
]
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
@@ -167,6 +167,8 @@ export function isTradeBetter(
|
||||
tradeB: Trade | undefined,
|
||||
minimumDelta: Percent = ZERO_PERCENT
|
||||
): boolean | undefined {
|
||||
if (tradeA && !tradeB) return false
|
||||
if (tradeB && !tradeA) return true
|
||||
if (!tradeA || !tradeB) return undefined
|
||||
|
||||
if (
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
8
src/ethereum.d.ts
vendored
8
src/ethereum.d.ts
vendored
@@ -1,8 +0,0 @@
|
||||
interface Window {
|
||||
ethereum?: {
|
||||
isMetaMask?: true
|
||||
on?: (...args: any[]) => void
|
||||
removeListener?: (...args: any[]) => void
|
||||
}
|
||||
web3?: {}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { parseBytes32String } from '@ethersproject/strings'
|
||||
import { Currency, ETHER, Token } from '@uniswap/sdk'
|
||||
import { Currency, ETHER, Token, currencyEquals } from '@uniswap/sdk'
|
||||
import { useMemo } from 'react'
|
||||
import { useSelectedTokenList } from '../state/lists/hooks'
|
||||
import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
|
||||
@@ -32,6 +32,12 @@ export function useAllTokens(): { [address: string]: Token } {
|
||||
}, [chainId, userAddedTokens, allTokens])
|
||||
}
|
||||
|
||||
// Check if currency is included in custom list from user storage
|
||||
export function useIsUserAddedToken(currency: Currency): boolean {
|
||||
const userAddedTokens = useUserAddedTokens()
|
||||
return !!userAddedTokens.find(token => currencyEquals(currency, token))
|
||||
}
|
||||
|
||||
// parse a name or symbol from a token response
|
||||
const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/
|
||||
function parseStringOrBytes32(str: string | undefined, bytes32: string | undefined, defaultValue: string): string {
|
||||
|
||||
8
src/hooks/ethereum.d.ts
vendored
8
src/hooks/ethereum.d.ts
vendored
@@ -1,8 +0,0 @@
|
||||
interface Window {
|
||||
ethereum?: {
|
||||
isMetaMask?: true
|
||||
on?: (...args: any[]) => void
|
||||
removeListener?: (...args: any[]) => void
|
||||
}
|
||||
web3?: {}
|
||||
}
|
||||
@@ -82,6 +82,6 @@ export function useInactiveListener(suppress = false) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
return undefined
|
||||
}, [active, error, suppress, activate])
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export default function useCopyClipboard(timeout = 500): [boolean, (toCopy: stri
|
||||
clearTimeout(hide)
|
||||
}
|
||||
}
|
||||
return
|
||||
return undefined
|
||||
}, [isCopied, setIsCopied, timeout])
|
||||
|
||||
return [isCopied, staticCopy]
|
||||
|
||||
@@ -20,6 +20,6 @@ export default function useInterval(callback: () => void, delay: null | number,
|
||||
const id = setInterval(tick, delay)
|
||||
return () => clearInterval(id)
|
||||
}
|
||||
return
|
||||
return undefined
|
||||
}, [delay, leading])
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function useIsWindowVisible(): boolean {
|
||||
}, [setFocused])
|
||||
|
||||
useEffect(() => {
|
||||
if (!VISIBILITY_STATE_SUPPORTED) return
|
||||
if (!VISIBILITY_STATE_SUPPORTED) return undefined
|
||||
|
||||
document.addEventListener('visibilitychange', listener)
|
||||
return () => {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.strict.json",
|
||||
"include": [
|
||||
"**/*",
|
||||
"../../../node_modules/eslint-plugin-react/lib/types.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -67,14 +67,14 @@ export function V1LiquidityInfo({
|
||||
<div style={{ marginLeft: '.75rem' }}>
|
||||
<TYPE.mediumHeader>
|
||||
{<FormattedPoolCurrencyAmount currencyAmount={liquidityTokenAmount} />}{' '}
|
||||
{token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH
|
||||
{chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH
|
||||
</TYPE.mediumHeader>
|
||||
</div>
|
||||
</AutoRow>
|
||||
|
||||
<RowBetween my="1rem">
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
Pooled {token.equals(WETH[chainId]) ? 'WETH' : token.symbol}:
|
||||
Pooled {chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}:
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
@@ -107,7 +107,7 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount
|
||||
const [v2PairState, v2Pair] = usePair(chainId ? WETH[chainId] : undefined, token)
|
||||
const isFirstLiquidityProvider: boolean = v2PairState === PairState.NOT_EXISTS
|
||||
|
||||
const v2SpotPrice = v2Pair?.reserveOf(token)?.divide(v2Pair?.reserveOf(WETH[chainId]))
|
||||
const v2SpotPrice = chainId && v2Pair ? v2Pair.reserveOf(token).divide(v2Pair.reserveOf(WETH[chainId])) : undefined
|
||||
|
||||
const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
|
||||
const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)
|
||||
@@ -158,11 +158,11 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount
|
||||
: tokenWorth?.numerator
|
||||
|
||||
const addTransaction = useTransactionAdder()
|
||||
const isMigrationPending = useIsTransactionPending(pendingMigrationHash)
|
||||
const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)
|
||||
|
||||
const migrator = useV2MigratorContract()
|
||||
const migrate = useCallback(() => {
|
||||
if (!minAmountToken || !minAmountETH) return
|
||||
if (!minAmountToken || !minAmountETH || !migrator) return
|
||||
|
||||
setConfirmingMigration(true)
|
||||
migrator
|
||||
@@ -194,16 +194,18 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount
|
||||
|
||||
const largePriceDifference = !!priceDifferenceAbs && !priceDifferenceAbs.lessThan(JSBI.BigInt(5))
|
||||
|
||||
const isSuccessfullyMigrated = !!pendingMigrationHash && !!noLiquidityTokens
|
||||
const isSuccessfullyMigrated = !!pendingMigrationHash && noLiquidityTokens
|
||||
|
||||
return (
|
||||
<AutoColumn gap="20px">
|
||||
<TYPE.body my={9} style={{ fontWeight: 400 }}>
|
||||
This tool will safely migrate your V1 liquidity to V2 with minimal price risk. The process is completely
|
||||
trustless thanks to the{' '}
|
||||
<ExternalLink href={getEtherscanLink(chainId, MIGRATOR_ADDRESS, 'address')}>
|
||||
<TYPE.blue display="inline">Uniswap migration contract↗</TYPE.blue>
|
||||
</ExternalLink>
|
||||
{chainId && (
|
||||
<ExternalLink href={getEtherscanLink(chainId, MIGRATOR_ADDRESS, 'address')}>
|
||||
<TYPE.blue display="inline">Uniswap migration contract↗</TYPE.blue>
|
||||
</ExternalLink>
|
||||
)}
|
||||
.
|
||||
</TYPE.body>
|
||||
|
||||
@@ -242,7 +244,7 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount
|
||||
|
||||
<RowBetween>
|
||||
<TYPE.body color="inherit">Price Difference:</TYPE.body>
|
||||
<TYPE.black color="inherit">{priceDifferenceAbs.toSignificant(4)}%</TYPE.black>
|
||||
<TYPE.black color="inherit">{priceDifferenceAbs?.toSignificant(4)}%</TYPE.black>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</YellowCard>
|
||||
@@ -336,12 +338,12 @@ export default function MigrateV1Exchange({
|
||||
|
||||
const liquidityToken: Token | undefined = useMemo(
|
||||
() =>
|
||||
validatedAddress && token
|
||||
validatedAddress && chainId && token
|
||||
? new Token(chainId, validatedAddress, 18, `UNI-V1-${token.symbol}`, 'Uniswap V1')
|
||||
: undefined,
|
||||
[chainId, validatedAddress, token]
|
||||
)
|
||||
const userLiquidityBalance = useTokenBalance(account, liquidityToken)
|
||||
const userLiquidityBalance = useTokenBalance(account ?? undefined, liquidityToken)
|
||||
|
||||
// redirect for invalid url params
|
||||
if (!validatedAddress || tokenAddress === AddressZero) {
|
||||
@@ -362,7 +364,7 @@ export default function MigrateV1Exchange({
|
||||
|
||||
{!account ? (
|
||||
<TYPE.largeHeader>You must connect an account.</TYPE.largeHeader>
|
||||
) : validatedAddress && token?.equals(WETH[chainId]) ? (
|
||||
) : validatedAddress && chainId && token?.equals(WETH[chainId]) ? (
|
||||
<>
|
||||
<TYPE.body my={9} style={{ fontWeight: 400 }}>
|
||||
Because Uniswap V2 uses WETH under the hood, your Uniswap V1 WETH/ETH liquidity cannot be migrated. You
|
||||
|
||||
@@ -58,7 +58,7 @@ function V1PairRemoval({
|
||||
: new TokenAmount(token, ZERO)
|
||||
|
||||
const addTransaction = useTransactionAdder()
|
||||
const isRemovalPending = useIsTransactionPending(pendingRemovalHash)
|
||||
const isRemovalPending = useIsTransactionPending(pendingRemovalHash ?? undefined)
|
||||
|
||||
const remove = useCallback(() => {
|
||||
if (!liquidityTokenAmount) return
|
||||
@@ -79,11 +79,11 @@ function V1PairRemoval({
|
||||
})
|
||||
|
||||
addTransaction(response, {
|
||||
summary: `Remove ${token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH V1 liquidity`
|
||||
summary: `Remove ${chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH V1 liquidity`
|
||||
})
|
||||
setPendingRemovalHash(response.hash)
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error: Error) => {
|
||||
console.error(error)
|
||||
setConfirmingRemoval(false)
|
||||
})
|
||||
@@ -91,7 +91,7 @@ function V1PairRemoval({
|
||||
|
||||
const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)
|
||||
|
||||
const isSuccessfullyRemoved = !!pendingRemovalHash && !!noLiquidityTokens
|
||||
const isSuccessfullyRemoved = !!pendingRemovalHash && noLiquidityTokens
|
||||
|
||||
return (
|
||||
<AutoColumn gap="20px">
|
||||
@@ -119,7 +119,7 @@ function V1PairRemoval({
|
||||
</LightCard>
|
||||
<TYPE.darkGray style={{ textAlign: 'center' }}>
|
||||
{`Your Uniswap V1 ${
|
||||
token.equals(WETH[chainId]) ? 'WETH' : token.symbol
|
||||
chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol
|
||||
}/ETH liquidity will be redeemed for underlying assets.`}
|
||||
</TYPE.darkGray>
|
||||
</AutoColumn>
|
||||
@@ -140,12 +140,12 @@ export default function RemoveV1Exchange({
|
||||
|
||||
const liquidityToken: Token | undefined = useMemo(
|
||||
() =>
|
||||
validatedAddress && token
|
||||
validatedAddress && chainId && token
|
||||
? new Token(chainId, validatedAddress, 18, `UNI-V1-${token.symbol}`, 'Uniswap V1')
|
||||
: undefined,
|
||||
[chainId, validatedAddress, token]
|
||||
)
|
||||
const userLiquidityBalance = useTokenBalance(account, liquidityToken)
|
||||
const userLiquidityBalance = useTokenBalance(account ?? undefined, liquidityToken)
|
||||
|
||||
// redirect for invalid url params
|
||||
if (!validatedAddress || tokenAddress === AddressZero) {
|
||||
@@ -166,7 +166,7 @@ export default function RemoveV1Exchange({
|
||||
|
||||
{!account ? (
|
||||
<TYPE.largeHeader>You must connect an account.</TYPE.largeHeader>
|
||||
) : userLiquidityBalance && token ? (
|
||||
) : userLiquidityBalance && token && exchangeContract ? (
|
||||
<V1PairRemoval
|
||||
exchangeContract={exchangeContract}
|
||||
liquidityTokenAmount={userLiquidityBalance}
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function MigrateV1() {
|
||||
// automatically add the search token
|
||||
const token = useToken(tokenSearch)
|
||||
const selectedTokenListTokens = useSelectedTokenList()
|
||||
const isOnSelectedList = isTokenOnList(selectedTokenListTokens, token)
|
||||
const isOnSelectedList = isTokenOnList(selectedTokenListTokens, token ?? undefined)
|
||||
const allTokens = useAllTokens()
|
||||
const addToken = useAddUserToken()
|
||||
useEffect(() => {
|
||||
@@ -41,27 +41,26 @@ export default function MigrateV1() {
|
||||
// get V1 LP balances
|
||||
const V1Exchanges = useAllTokenV1Exchanges()
|
||||
const V1LiquidityTokens: Token[] = useMemo(() => {
|
||||
return Object.keys(V1Exchanges).map(
|
||||
exchangeAddress => new Token(chainId, exchangeAddress, 18, 'UNI-V1', 'Uniswap V1')
|
||||
)
|
||||
return chainId
|
||||
? Object.keys(V1Exchanges).map(exchangeAddress => new Token(chainId, exchangeAddress, 18, 'UNI-V1', 'Uniswap V1'))
|
||||
: []
|
||||
}, [chainId, V1Exchanges])
|
||||
const [V1LiquidityBalances, V1LiquidityBalancesLoading] = useTokenBalancesWithLoadingIndicator(
|
||||
account,
|
||||
account ?? undefined,
|
||||
V1LiquidityTokens
|
||||
)
|
||||
const allV1PairsWithLiquidity = V1LiquidityTokens.filter(V1LiquidityToken => {
|
||||
return (
|
||||
V1LiquidityBalances?.[V1LiquidityToken.address] &&
|
||||
JSBI.greaterThan(V1LiquidityBalances[V1LiquidityToken.address].raw, JSBI.BigInt(0))
|
||||
)
|
||||
const balance = V1LiquidityBalances?.[V1LiquidityToken.address]
|
||||
return balance && JSBI.greaterThan(balance.raw, JSBI.BigInt(0))
|
||||
}).map(V1LiquidityToken => {
|
||||
return (
|
||||
const balance = V1LiquidityBalances[V1LiquidityToken.address]
|
||||
return balance ? (
|
||||
<V1PositionCard
|
||||
key={V1LiquidityToken.address}
|
||||
token={V1Exchanges[V1LiquidityToken.address]}
|
||||
V1LiquidityBalance={V1LiquidityBalances[V1LiquidityToken.address]}
|
||||
V1LiquidityBalance={balance}
|
||||
/>
|
||||
)
|
||||
) : null
|
||||
})
|
||||
|
||||
// should never always be false, because a V1 exhchange exists for WETH on all testnets
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
@@ -634,14 +634,7 @@ export default function RemoveLiquidity({
|
||||
<RowBetween>
|
||||
<div />
|
||||
<div>
|
||||
1 {currencyB?.symbol} ={' '}
|
||||
{tokenB
|
||||
? pair
|
||||
.priceOf(tokenB)
|
||||
.invert()
|
||||
.toSignificant(6)
|
||||
: '-'}{' '}
|
||||
{currencyA?.symbol}
|
||||
1 {currencyB?.symbol} = {tokenB ? pair.priceOf(tokenB).toSignificant(6) : '-'} {currencyA?.symbol}
|
||||
</div>
|
||||
</RowBetween>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.strict.json",
|
||||
"include": [
|
||||
"**/*",
|
||||
"../../../node_modules/eslint-plugin-react/lib/types.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import ReactGA from 'react-ga'
|
||||
import { Text } from 'rebass'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
import AddressInputPanel from '../../components/AddressInputPanel'
|
||||
import { ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button'
|
||||
import { ButtonError, ButtonLight, ButtonPrimary, ButtonConfirmed } from '../../components/Button'
|
||||
import Card, { GreyCard } from '../../components/Card'
|
||||
import { AutoColumn } from '../../components/Column'
|
||||
import ConfirmSwapModal from '../../components/swap/ConfirmSwapModal'
|
||||
@@ -15,9 +15,10 @@ import { AutoRow, RowBetween } from '../../components/Row'
|
||||
import AdvancedSwapDetailsDropdown from '../../components/swap/AdvancedSwapDetailsDropdown'
|
||||
import BetterTradeLink from '../../components/swap/BetterTradeLink'
|
||||
import confirmPriceImpactWithoutFee from '../../components/swap/confirmPriceImpactWithoutFee'
|
||||
import { ArrowWrapper, BottomGrouping, Dots, SwapCallbackError, Wrapper } from '../../components/swap/styleds'
|
||||
import { ArrowWrapper, BottomGrouping, SwapCallbackError, Wrapper } from '../../components/swap/styleds'
|
||||
import TradePrice from '../../components/swap/TradePrice'
|
||||
import TokenWarningModal from '../../components/TokenWarningModal'
|
||||
import ProgressSteps from '../../components/ProgressSteps'
|
||||
|
||||
import { BETTER_TRADE_LINK_THRESHOLD, INITIAL_ALLOWED_SLIPPAGE } from '../../constants'
|
||||
import { getTradeVersion, isTradeBetter } from '../../data/V1'
|
||||
@@ -42,6 +43,7 @@ import { maxAmountSpend } from '../../utils/maxAmountSpend'
|
||||
import { computeTradePriceBreakdown, warningSeverity } from '../../utils/prices'
|
||||
import AppBody from '../AppBody'
|
||||
import { ClickableText } from '../Pool/styleds'
|
||||
import Loader from '../../components/Loader'
|
||||
|
||||
export default function Swap() {
|
||||
const loadedUrlParams = useDefaultsFromURLSearch()
|
||||
@@ -294,7 +296,7 @@ export default function Swap() {
|
||||
|
||||
<AutoColumn gap={'md'}>
|
||||
<CurrencyInputPanel
|
||||
label={independentField === Field.OUTPUT && !showWrap ? 'From (estimated)' : 'From'}
|
||||
label={independentField === Field.OUTPUT && !showWrap && trade ? 'From (estimated)' : 'From'}
|
||||
value={formattedAmounts[Field.INPUT]}
|
||||
showMaxButton={!atMaxAmountInput}
|
||||
currency={currencies[Field.INPUT]}
|
||||
@@ -304,9 +306,8 @@ export default function Swap() {
|
||||
otherCurrency={currencies[Field.OUTPUT]}
|
||||
id="swap-currency-input"
|
||||
/>
|
||||
|
||||
<AutoColumn justify="space-between">
|
||||
<AutoRow justify="space-between" style={{ padding: '0 1rem' }}>
|
||||
<AutoRow justify={isExpertMode ? 'space-between' : 'center'} style={{ padding: '0 1rem' }}>
|
||||
<ArrowWrapper clickable>
|
||||
<ArrowDown
|
||||
size="16"
|
||||
@@ -327,7 +328,7 @@ export default function Swap() {
|
||||
<CurrencyInputPanel
|
||||
value={formattedAmounts[Field.OUTPUT]}
|
||||
onUserInput={handleTypeOutput}
|
||||
label={independentField === Field.INPUT && !showWrap ? 'To (estimated)' : 'To'}
|
||||
label={independentField === Field.INPUT && !showWrap && trade ? 'To (estimated)' : 'To'}
|
||||
showMaxButton={false}
|
||||
currency={currencies[Field.OUTPUT]}
|
||||
onCurrencySelect={handleOutputSelect}
|
||||
@@ -352,19 +353,18 @@ export default function Swap() {
|
||||
{showWrap ? null : (
|
||||
<Card padding={'.25rem .75rem 0 .75rem'} borderRadius={'20px'}>
|
||||
<AutoColumn gap="4px">
|
||||
<RowBetween align="center">
|
||||
<Text fontWeight={500} fontSize={14} color={theme.text2}>
|
||||
Price
|
||||
</Text>
|
||||
<TradePrice
|
||||
inputCurrency={currencies[Field.INPUT]}
|
||||
outputCurrency={currencies[Field.OUTPUT]}
|
||||
price={trade?.executionPrice}
|
||||
showInverted={showInverted}
|
||||
setShowInverted={setShowInverted}
|
||||
/>
|
||||
</RowBetween>
|
||||
|
||||
{Boolean(trade) && (
|
||||
<RowBetween align="center">
|
||||
<Text fontWeight={500} fontSize={14} color={theme.text2}>
|
||||
Price
|
||||
</Text>
|
||||
<TradePrice
|
||||
price={trade?.executionPrice}
|
||||
showInverted={showInverted}
|
||||
setShowInverted={setShowInverted}
|
||||
/>
|
||||
</RowBetween>
|
||||
)}
|
||||
{allowedSlippage !== INITIAL_ALLOWED_SLIPPAGE && (
|
||||
<RowBetween align="center">
|
||||
<ClickableText fontWeight={500} fontSize={14} color={theme.text2} onClick={toggleSettings}>
|
||||
@@ -393,20 +393,23 @@ export default function Swap() {
|
||||
</GreyCard>
|
||||
) : showApproveFlow ? (
|
||||
<RowBetween>
|
||||
<ButtonPrimary
|
||||
<ButtonConfirmed
|
||||
onClick={approveCallback}
|
||||
disabled={approval !== ApprovalState.NOT_APPROVED || approvalSubmitted}
|
||||
width="48%"
|
||||
altDisbaledStyle={approval === ApprovalState.PENDING} // show solid button while waiting
|
||||
altDisabledStyle={approval === ApprovalState.PENDING} // show solid button while waiting
|
||||
confirmed={approval === ApprovalState.APPROVED}
|
||||
>
|
||||
{approval === ApprovalState.PENDING ? (
|
||||
<Dots>Approving</Dots>
|
||||
<AutoRow gap="6px" justify="center">
|
||||
Approving <Loader stroke="white" />
|
||||
</AutoRow>
|
||||
) : approvalSubmitted && approval === ApprovalState.APPROVED ? (
|
||||
'Approved'
|
||||
) : (
|
||||
'Approve ' + currencies[Field.INPUT]?.symbol
|
||||
)}
|
||||
</ButtonPrimary>
|
||||
</ButtonConfirmed>
|
||||
<ButtonError
|
||||
onClick={() => {
|
||||
if (isExpertMode) {
|
||||
@@ -463,6 +466,7 @@ export default function Swap() {
|
||||
</Text>
|
||||
</ButtonError>
|
||||
)}
|
||||
{showApproveFlow && <ProgressSteps steps={[approval === ApprovalState.APPROVED]} />}
|
||||
{isExpertMode && swapErrorMessage ? <SwapCallbackError error={swapErrorMessage} /> : null}
|
||||
{betterTradeLinkVersion && <BetterTradeLink version={betterTradeLinkVersion} />}
|
||||
</BottomGrouping>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
25
src/react-app-env.d.ts
vendored
25
src/react-app-env.d.ts
vendored
@@ -1 +1,26 @@
|
||||
/// <reference types="react-scripts" />
|
||||
|
||||
declare module 'jazzicon' {
|
||||
export default function(diameter: number, seed: number): HTMLElement
|
||||
}
|
||||
|
||||
declare module 'fortmatic'
|
||||
|
||||
interface Window {
|
||||
ethereum?: {
|
||||
isMetaMask?: true
|
||||
on?: (...args: any[]) => void
|
||||
removeListener?: (...args: any[]) => void
|
||||
}
|
||||
web3?: {}
|
||||
}
|
||||
|
||||
declare module 'content-hash' {
|
||||
declare function decode(x: string): string
|
||||
declare function getCodec(x: string): string
|
||||
}
|
||||
|
||||
declare module 'multihashes' {
|
||||
declare function decode(buff: Uint8Array): { code: number; name: string; length: number; digest: Uint8Array }
|
||||
declare function toB58String(hash: Uint8Array): string
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ export type PopupContent =
|
||||
}
|
||||
}
|
||||
|
||||
export const updateBlockNumber = createAction<{ chainId: number; blockNumber: number }>('updateBlockNumber')
|
||||
export const toggleWalletModal = createAction<void>('toggleWalletModal')
|
||||
export const toggleSettingsMenu = createAction<void>('toggleSettingsMenu')
|
||||
export const addPopup = createAction<{ key?: string; removeAfterMs?: number | null; content: PopupContent }>('addPopup')
|
||||
export const removePopup = createAction<{ key: string }>('removePopup')
|
||||
export const updateBlockNumber = createAction<{ chainId: number; blockNumber: number }>('app/updateBlockNumber')
|
||||
export const toggleWalletModal = createAction<void>('app/toggleWalletModal')
|
||||
export const toggleSettingsMenu = createAction<void>('app/toggleSettingsMenu')
|
||||
export const addPopup = createAction<{ key?: string; removeAfterMs?: number | null; content: PopupContent }>(
|
||||
'app/addPopup'
|
||||
)
|
||||
export const removePopup = createAction<{ key: string }>('app/removePopup')
|
||||
|
||||
@@ -5,7 +5,7 @@ import useIsWindowVisible from '../../hooks/useIsWindowVisible'
|
||||
import { updateBlockNumber } from './actions'
|
||||
import { useDispatch } from 'react-redux'
|
||||
|
||||
export default function Updater() {
|
||||
export default function Updater(): null {
|
||||
const { library, chainId } = useActiveWeb3React()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function Updater() {
|
||||
|
||||
// attach/detach listeners
|
||||
useEffect(() => {
|
||||
if (!library || !chainId || !windowVisible) return
|
||||
if (!library || !chainId || !windowVisible) return undefined
|
||||
|
||||
setState({ chainId, blockNumber: null })
|
||||
|
||||
|
||||
@@ -7,4 +7,4 @@ export enum Field {
|
||||
CURRENCY_B = 'CURRENCY_B'
|
||||
}
|
||||
|
||||
export const typeInput = createAction<{ field: Field; typedValue: string }>('typeInputBurn')
|
||||
export const typeInput = createAction<{ field: Field; typedValue: string }>('burn/typeInputBurn')
|
||||
|
||||
5
src/state/global/actions.ts
Normal file
5
src/state/global/actions.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { createAction } from '@reduxjs/toolkit'
|
||||
|
||||
// fired once when the app reloads but before the app renders
|
||||
// allows any updates to be applied to store data loaded from localStorage
|
||||
export const updateVersion = createAction<void>('global/updateVersion')
|
||||
@@ -2,6 +2,7 @@ import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
|
||||
import { save, load } from 'redux-localstorage-simple'
|
||||
|
||||
import application from './application/reducer'
|
||||
import { updateVersion } from './global/actions'
|
||||
import user from './user/reducer'
|
||||
import transactions from './transactions/reducer'
|
||||
import swap from './swap/reducer'
|
||||
@@ -10,8 +11,6 @@ import lists from './lists/reducer'
|
||||
import burn from './burn/reducer'
|
||||
import multicall from './multicall/reducer'
|
||||
|
||||
import { updateVersion } from './user/actions'
|
||||
|
||||
const PERSISTED_KEYS: string[] = ['user', 'transactions', 'lists']
|
||||
|
||||
const store = configureStore({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createStore, Store } from 'redux'
|
||||
import { DEFAULT_LIST_OF_LISTS, DEFAULT_TOKEN_LIST_URL } from '../../constants/lists'
|
||||
import { updateVersion } from '../user/actions'
|
||||
import { updateVersion } from '../global/actions'
|
||||
import { fetchTokenList, acceptListUpdate, addList, removeList, selectList } from './actions'
|
||||
import reducer, { ListsState } from './reducer'
|
||||
import UNISWAP_DEFAULT_TOKEN_LIST from '@uniswap/default-token-list'
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createReducer } from '@reduxjs/toolkit'
|
||||
import { getVersionUpgrade, VersionUpgrade } from '@uniswap/token-lists'
|
||||
import { TokenList } from '@uniswap/token-lists/dist/types'
|
||||
import { DEFAULT_LIST_OF_LISTS, DEFAULT_TOKEN_LIST_URL } from '../../constants/lists'
|
||||
import { updateVersion } from '../user/actions'
|
||||
import { updateVersion } from '../global/actions'
|
||||
import { acceptListUpdate, addList, fetchTokenList, removeList, selectList } from './actions'
|
||||
import UNISWAP_DEFAULT_LIST from '@uniswap/default-token-list'
|
||||
|
||||
|
||||
@@ -5,5 +5,5 @@ export enum Field {
|
||||
CURRENCY_B = 'CURRENCY_B'
|
||||
}
|
||||
|
||||
export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('typeInputMint')
|
||||
export const resetMintState = createAction<void>('resetMintState')
|
||||
export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('mint/typeInputMint')
|
||||
export const resetMintState = createAction<void>('mint/resetMintState')
|
||||
|
||||
@@ -72,7 +72,7 @@ export function useDerivedMintInfo(
|
||||
if (otherTypedValue && currencies[dependentField]) {
|
||||
return tryParseAmount(otherTypedValue, currencies[dependentField])
|
||||
}
|
||||
return
|
||||
return undefined
|
||||
} else if (independentAmount) {
|
||||
// we wrap the currencies just to get the price in terms of the other token
|
||||
const wrappedIndependentAmount = wrappedCurrencyAmount(independentAmount, chainId)
|
||||
@@ -85,9 +85,9 @@ export function useDerivedMintInfo(
|
||||
: pair.priceOf(tokenB).quote(wrappedIndependentAmount)
|
||||
return dependentCurrency === ETHER ? CurrencyAmount.ether(dependentTokenAmount.raw) : dependentTokenAmount
|
||||
}
|
||||
return
|
||||
return undefined
|
||||
} else {
|
||||
return
|
||||
return undefined
|
||||
}
|
||||
}, [noLiquidity, otherTypedValue, currencies, dependentField, independentAmount, currencyA, chainId, currencyB, pair])
|
||||
const parsedAmounts: { [field in Field]: CurrencyAmount | undefined } = {
|
||||
@@ -95,18 +95,18 @@ export function useDerivedMintInfo(
|
||||
[Field.CURRENCY_B]: independentField === Field.CURRENCY_A ? dependentAmount : independentAmount
|
||||
}
|
||||
|
||||
const token0Price = pair?.token0Price
|
||||
const price = useMemo(() => {
|
||||
if (noLiquidity) {
|
||||
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
|
||||
if (currencyAAmount && currencyBAmount) {
|
||||
return new Price(currencyAAmount.currency, currencyBAmount.currency, currencyAAmount.raw, currencyBAmount.raw)
|
||||
}
|
||||
return
|
||||
return undefined
|
||||
} else {
|
||||
return token0Price
|
||||
const wrappedCurrencyA = wrappedCurrency(currencyA, chainId)
|
||||
return pair && wrappedCurrencyA ? pair.priceOf(wrappedCurrencyA) : undefined
|
||||
}
|
||||
}, [noLiquidity, token0Price, parsedAmounts])
|
||||
}, [chainId, currencyA, noLiquidity, pair, parsedAmounts])
|
||||
|
||||
// liquidity minted
|
||||
const liquidityMinted = useMemo(() => {
|
||||
@@ -118,7 +118,7 @@ export function useDerivedMintInfo(
|
||||
if (pair && totalSupply && tokenAmountA && tokenAmountB) {
|
||||
return pair.getLiquidityMinted(totalSupply, tokenAmountA, tokenAmountB)
|
||||
} else {
|
||||
return
|
||||
return undefined
|
||||
}
|
||||
}, [parsedAmounts, chainId, pair, totalSupply])
|
||||
|
||||
@@ -126,7 +126,7 @@ export function useDerivedMintInfo(
|
||||
if (liquidityMinted && totalSupply) {
|
||||
return new Percent(liquidityMinted.raw, totalSupply.add(liquidityMinted).raw)
|
||||
} else {
|
||||
return
|
||||
return undefined
|
||||
}
|
||||
}, [liquidityMinted, totalSupply])
|
||||
|
||||
|
||||
@@ -34,23 +34,23 @@ export interface ListenerOptions {
|
||||
}
|
||||
|
||||
export const addMulticallListeners = createAction<{ chainId: number; calls: Call[]; options?: ListenerOptions }>(
|
||||
'addMulticallListeners'
|
||||
'multicall/addMulticallListeners'
|
||||
)
|
||||
export const removeMulticallListeners = createAction<{ chainId: number; calls: Call[]; options?: ListenerOptions }>(
|
||||
'removeMulticallListeners'
|
||||
'multicall/removeMulticallListeners'
|
||||
)
|
||||
export const fetchingMulticallResults = createAction<{ chainId: number; calls: Call[]; fetchingBlockNumber: number }>(
|
||||
'fetchingMulticallResults'
|
||||
'multicall/fetchingMulticallResults'
|
||||
)
|
||||
export const errorFetchingMulticallResults = createAction<{
|
||||
chainId: number
|
||||
calls: Call[]
|
||||
fetchingBlockNumber: number
|
||||
}>('errorFetchingMulticallResults')
|
||||
}>('multicall/errorFetchingMulticallResults')
|
||||
export const updateMulticallResults = createAction<{
|
||||
chainId: number
|
||||
blockNumber: number
|
||||
results: {
|
||||
[callKey: string]: string | null
|
||||
}
|
||||
}>('updateMulticallResults')
|
||||
}>('multicall/updateMulticallResults')
|
||||
|
||||
@@ -30,7 +30,8 @@ function isMethodArg(x: unknown): x is MethodArg {
|
||||
|
||||
function isValidMethodArgs(x: unknown): x is MethodArgs | undefined {
|
||||
return (
|
||||
x === undefined || (Array.isArray(x) && x.every(y => isMethodArg(y) || (Array.isArray(y) && y.every(isMethodArg))))
|
||||
x === undefined ||
|
||||
(Array.isArray(x) && x.every(xi => isMethodArg(xi) || (Array.isArray(xi) && xi.every(isMethodArg))))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,7 +68,7 @@ function useCallsData(calls: (Call | undefined)[], options?: ListenerOptions): C
|
||||
// update listeners when there is an actual change that persists for at least 100ms
|
||||
useEffect(() => {
|
||||
const callKeys: string[] = JSON.parse(serializedCallKeys)
|
||||
if (!chainId || callKeys.length === 0) return
|
||||
if (!chainId || callKeys.length === 0) return undefined
|
||||
const calls = callKeys.map(key => parseCallKey(key))
|
||||
dispatch(
|
||||
addMulticallListeners({
|
||||
|
||||
@@ -110,7 +110,7 @@ export function outdatedListeningKeys(
|
||||
})
|
||||
}
|
||||
|
||||
export default function Updater() {
|
||||
export default function Updater(): null {
|
||||
const dispatch = useDispatch<AppDispatch>()
|
||||
const state = useSelector<AppState, AppState['multicall']>(state => state.multicall)
|
||||
// wait for listeners to settle before triggering updates
|
||||
|
||||
@@ -5,14 +5,14 @@ export enum Field {
|
||||
OUTPUT = 'OUTPUT'
|
||||
}
|
||||
|
||||
export const selectCurrency = createAction<{ field: Field; currencyId: string }>('selectCurrency')
|
||||
export const switchCurrencies = createAction<void>('switchCurrencies')
|
||||
export const typeInput = createAction<{ field: Field; typedValue: string }>('typeInput')
|
||||
export const selectCurrency = createAction<{ field: Field; currencyId: string }>('swap/selectCurrency')
|
||||
export const switchCurrencies = createAction<void>('swap/switchCurrencies')
|
||||
export const typeInput = createAction<{ field: Field; typedValue: string }>('swap/typeInput')
|
||||
export const replaceSwapState = createAction<{
|
||||
field: Field
|
||||
typedValue: string
|
||||
inputCurrencyId?: string
|
||||
outputCurrencyId?: string
|
||||
recipient: string | null
|
||||
}>('replaceSwapState')
|
||||
export const setRecipient = createAction<{ recipient: string | null }>('setRecipient')
|
||||
}>('swap/replaceSwapState')
|
||||
export const setRecipient = createAction<{ recipient: string | null }>('swap/setRecipient')
|
||||
|
||||
@@ -71,7 +71,7 @@ export function useSwapActionHandlers(): {
|
||||
// try to parse a user entered amount for a given token
|
||||
export function tryParseAmount(value?: string, currency?: Currency): CurrencyAmount | undefined {
|
||||
if (!value || !currency) {
|
||||
return
|
||||
return undefined
|
||||
}
|
||||
try {
|
||||
const typedValueParsed = parseUnits(value, currency.decimals).toString()
|
||||
@@ -85,7 +85,7 @@ export function tryParseAmount(value?: string, currency?: Currency): CurrencyAmo
|
||||
console.debug(`Failed to parse input amount: "${value}"`, error)
|
||||
}
|
||||
// necessary for all paths to return a value
|
||||
return
|
||||
return undefined
|
||||
}
|
||||
|
||||
const BAD_RECIPIENT_ADDRESSES: string[] = [
|
||||
|
||||
@@ -50,6 +50,14 @@ export function useIsTransactionPending(transactionHash?: string): boolean {
|
||||
return !transactions[transactionHash].receipt
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a transaction happened in the last day (86400 seconds * 1000 milliseconds / second)
|
||||
* @param tx to check for recency
|
||||
*/
|
||||
export function isTransactionRecent(tx: TransactionDetails): boolean {
|
||||
return new Date().getTime() - tx.addedTime < 86_400_000
|
||||
}
|
||||
|
||||
// returns whether a token has a pending approval transaction
|
||||
export function useHasPendingApproval(tokenAddress: string | undefined, spender: string | undefined): boolean {
|
||||
const allTransactions = useAllTransactions()
|
||||
@@ -58,13 +66,14 @@ export function useHasPendingApproval(tokenAddress: string | undefined, spender:
|
||||
typeof tokenAddress === 'string' &&
|
||||
typeof spender === 'string' &&
|
||||
Object.keys(allTransactions).some(hash => {
|
||||
if (allTransactions[hash]?.receipt) {
|
||||
const tx = allTransactions[hash]
|
||||
if (!tx) return false
|
||||
if (tx.receipt) {
|
||||
return false
|
||||
} else {
|
||||
return (
|
||||
allTransactions[hash]?.approval?.tokenAddress === tokenAddress &&
|
||||
allTransactions[hash]?.approval?.spender === spender
|
||||
)
|
||||
const approval = tx.approval
|
||||
if (!approval) return false
|
||||
return approval.spender === spender && approval.tokenAddress === tokenAddress && isTransactionRecent(tx)
|
||||
}
|
||||
}),
|
||||
[allTransactions, spender, tokenAddress]
|
||||
|
||||
@@ -26,7 +26,7 @@ export function shouldCheck(
|
||||
}
|
||||
}
|
||||
|
||||
export default function Updater() {
|
||||
export default function Updater(): null {
|
||||
const { chainId, library } = useActiveWeb3React()
|
||||
|
||||
const lastBlockNumber = useBlockNumber()
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
@@ -13,17 +13,16 @@ export interface SerializedPair {
|
||||
token1: SerializedToken
|
||||
}
|
||||
|
||||
export const updateVersion = createAction<void>('updateVersion')
|
||||
export const updateMatchesDarkMode = createAction<{ matchesDarkMode: boolean }>('updateMatchesDarkMode')
|
||||
export const updateUserDarkMode = createAction<{ userDarkMode: boolean }>('updateUserDarkMode')
|
||||
export const updateUserExpertMode = createAction<{ userExpertMode: boolean }>('updateUserExpertMode')
|
||||
export const updateMatchesDarkMode = createAction<{ matchesDarkMode: boolean }>('user/updateMatchesDarkMode')
|
||||
export const updateUserDarkMode = createAction<{ userDarkMode: boolean }>('user/updateUserDarkMode')
|
||||
export const updateUserExpertMode = createAction<{ userExpertMode: boolean }>('user/updateUserExpertMode')
|
||||
export const updateUserSlippageTolerance = createAction<{ userSlippageTolerance: number }>(
|
||||
'updateUserSlippageTolerance'
|
||||
'user/updateUserSlippageTolerance'
|
||||
)
|
||||
export const updateUserDeadline = createAction<{ userDeadline: number }>('updateUserDeadline')
|
||||
export const addSerializedToken = createAction<{ serializedToken: SerializedToken }>('addSerializedToken')
|
||||
export const removeSerializedToken = createAction<{ chainId: number; address: string }>('removeSerializedToken')
|
||||
export const addSerializedPair = createAction<{ serializedPair: SerializedPair }>('addSerializedPair')
|
||||
export const updateUserDeadline = createAction<{ userDeadline: number }>('user/updateUserDeadline')
|
||||
export const addSerializedToken = createAction<{ serializedToken: SerializedToken }>('user/addSerializedToken')
|
||||
export const removeSerializedToken = createAction<{ chainId: number; address: string }>('user/removeSerializedToken')
|
||||
export const addSerializedPair = createAction<{ serializedPair: SerializedPair }>('user/addSerializedPair')
|
||||
export const removeSerializedPair = createAction<{ chainId: number; tokenAAddress: string; tokenBAddress: string }>(
|
||||
'removeSerializedPair'
|
||||
'user/removeSerializedPair'
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createStore, Store } from 'redux'
|
||||
import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE } from '../../constants'
|
||||
import { updateVersion } from './actions'
|
||||
import { updateVersion } from '../global/actions'
|
||||
import reducer, { initialState, UserState } from './reducer'
|
||||
|
||||
describe('swap reducer', () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { INITIAL_ALLOWED_SLIPPAGE, DEFAULT_DEADLINE_FROM_NOW } from '../../constants'
|
||||
import { createReducer } from '@reduxjs/toolkit'
|
||||
import { updateVersion } from '../global/actions'
|
||||
import {
|
||||
addSerializedPair,
|
||||
addSerializedToken,
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
SerializedToken,
|
||||
updateMatchesDarkMode,
|
||||
updateUserDarkMode,
|
||||
updateVersion,
|
||||
updateUserExpertMode,
|
||||
updateUserSlippageTolerance,
|
||||
updateUserDeadline
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useDispatch } from 'react-redux'
|
||||
import { AppDispatch } from '../index'
|
||||
import { updateMatchesDarkMode } from './actions'
|
||||
|
||||
export default function Updater() {
|
||||
export default function Updater(): null {
|
||||
const dispatch = useDispatch<AppDispatch>()
|
||||
|
||||
// keep dark mode in sync with the system
|
||||
|
||||
@@ -90,7 +90,7 @@ export function useTokenBalances(
|
||||
// get the balance for a single token/account combo
|
||||
export function useTokenBalance(account?: string, token?: Token): TokenAmount | undefined {
|
||||
const tokenBalances = useTokenBalances(account, [token])
|
||||
if (!token) return
|
||||
if (!token) return undefined
|
||||
return tokenBalances[token.address]
|
||||
}
|
||||
|
||||
@@ -109,10 +109,10 @@ export function useCurrencyBalances(
|
||||
return useMemo(
|
||||
() =>
|
||||
currencies?.map(currency => {
|
||||
if (!account || !currency) return
|
||||
if (!account || !currency) return undefined
|
||||
if (currency instanceof Token) return tokenBalances[currency.address]
|
||||
if (currency === ETHER) return ethBalance[account]
|
||||
return
|
||||
return undefined
|
||||
}) ?? [],
|
||||
[account, currencies, ethBalance, tokenBalances]
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import { parse } from 'qs'
|
||||
import { AppDispatch } from '../state'
|
||||
import { updateUserDarkMode } from '../state/user/actions'
|
||||
|
||||
export default function DarkModeQueryParamReader({ location: { search } }: RouteComponentProps) {
|
||||
export default function DarkModeQueryParamReader({ location: { search } }: RouteComponentProps): null {
|
||||
const dispatch = useDispatch<AppDispatch>()
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
4
src/utils/content-hash.d.ts
vendored
4
src/utils/content-hash.d.ts
vendored
@@ -1,4 +0,0 @@
|
||||
declare module 'content-hash' {
|
||||
declare function decode(x: string): string
|
||||
declare function getCodec(x: string): string
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { MIN_ETH } from '../constants'
|
||||
* @param currencyAmount to return max of
|
||||
*/
|
||||
export function maxAmountSpend(currencyAmount?: CurrencyAmount): CurrencyAmount | undefined {
|
||||
if (!currencyAmount) return
|
||||
if (!currencyAmount) return undefined
|
||||
if (currencyAmount.currency === ETHER) {
|
||||
if (JSBI.greaterThan(currencyAmount.raw, MIN_ETH)) {
|
||||
return CurrencyAmount.ether(JSBI.subtract(currencyAmount.raw, MIN_ETH))
|
||||
|
||||
4
src/utils/multihashes.d.ts
vendored
4
src/utils/multihashes.d.ts
vendored
@@ -1,4 +0,0 @@
|
||||
declare module 'multihashes' {
|
||||
declare function decode(buff: Uint8Array): { code: number; name: string; length: number; digest: Uint8Array }
|
||||
declare function toB58String(hash: Uint8Array): string
|
||||
}
|
||||
@@ -2,6 +2,6 @@ const ENS_NAME_REGEX = /^(([a-zA-Z0-9]+\.)+)eth(\/.*)?$/
|
||||
|
||||
export function parseENSAddress(ensAddress: string): { ensName: string; ensPath: string | undefined } | undefined {
|
||||
const match = ensAddress.match(ENS_NAME_REGEX)
|
||||
if (!match) return
|
||||
if (!match) return undefined
|
||||
return { ensName: `${match[1].toLowerCase()}eth`, ensPath: match[3] }
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.strict.json",
|
||||
"include": ["**/*"]
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export default function useUSDCPrice(currency?: Currency): Price | undefined {
|
||||
|
||||
return useMemo(() => {
|
||||
if (!currency || !wrapped || !chainId) {
|
||||
return
|
||||
return undefined
|
||||
}
|
||||
// handle weth/eth
|
||||
if (wrapped.equals(WETH[chainId])) {
|
||||
@@ -61,6 +61,6 @@ export default function useUSDCPrice(currency?: Currency): Price | undefined {
|
||||
return new Price(currency, USDC, usdcPrice.denominator, usdcPrice.numerator)
|
||||
}
|
||||
}
|
||||
return
|
||||
return undefined
|
||||
}, [chainId, currency, ethPair, ethPairState, usdcEthPair, usdcEthPairState, usdcPair, usdcPairState, wrapped])
|
||||
}
|
||||
|
||||
@@ -8,11 +8,18 @@
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"alwaysStrict": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitReturns": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
@@ -29,8 +36,7 @@
|
||||
"cypress"
|
||||
],
|
||||
"include": [
|
||||
"**/*.js",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
"./src/**/*.ts",
|
||||
"./src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user