feat: include native currency in widget select (#3124)
* fix: token image for chains / natives * feat: include native currency in select - Updates widgets swap state to use Currency (and deals with downstream updates) - Refactors logoURI code to a new lib/hooks/useCurrencyLogoURIs - Adds native currency to useQueryTokenList NB: This does not build because tests must be updated to use Currency (they currently use mock tokens) * test: update fixtures to use real currency * fix: data uri color extraction * fix: token img state * fix: use new array
This commit is contained in:
parent
99f681818f
commit
850a20f6ad
@ -1,14 +1,25 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const { DefinePlugin } = require('webpack')
|
||||
|
||||
// Renders the cosmos fixtures in isolation, instead of using public/index.html.
|
||||
module.exports = (webpackConfig) => ({
|
||||
...webpackConfig,
|
||||
plugins: webpackConfig.plugins.map((plugin) =>
|
||||
plugin instanceof HtmlWebpackPlugin
|
||||
? new HtmlWebpackPlugin({
|
||||
templateContent: '<body></body>',
|
||||
})
|
||||
: plugin
|
||||
),
|
||||
plugins: webpackConfig.plugins.map((plugin) => {
|
||||
if (plugin instanceof HtmlWebpackPlugin) {
|
||||
return new HtmlWebpackPlugin({
|
||||
templateContent: '<body></body>',
|
||||
})
|
||||
}
|
||||
if (plugin instanceof DefinePlugin) {
|
||||
return new DefinePlugin({
|
||||
...plugin.definitions,
|
||||
'process.env': {
|
||||
...plugin.definitions['process.env'],
|
||||
REACT_APP_IS_WIDGET: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
return plugin
|
||||
}),
|
||||
})
|
||||
|
@ -56,6 +56,7 @@
|
||||
"@types/wcag-contrast": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
||||
"@typescript-eslint/parser": "^4.1.0",
|
||||
"@uniswap/default-token-list": "^3.0.0",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "1.0.1",
|
||||
@ -94,7 +95,7 @@
|
||||
"prettier": "^2.2.1",
|
||||
"qs": "^6.9.4",
|
||||
"react-confetti": "^6.0.0",
|
||||
"react-cosmos": "^5.6.3",
|
||||
"react-cosmos": "^5.6.6",
|
||||
"react-ga": "^2.5.7",
|
||||
"react-is": "^17.0.2",
|
||||
"react-markdown": "^4.3.1",
|
||||
@ -138,7 +139,7 @@
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=./custom-test-env.js",
|
||||
"test:e2e": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'",
|
||||
"widgets:start": "cross-env FAST_REFRESH=false REACT_APP_IS_WIDGET=true cosmos",
|
||||
"widgets:start": "cosmos",
|
||||
"widgets:build": "rollup --config --failAfterWarnings --configPlugin typescript2"
|
||||
},
|
||||
"browserslist": {
|
||||
|
@ -1,60 +1,18 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import EthereumLogo from 'assets/images/ethereum-logo.png'
|
||||
import MaticLogo from 'assets/svg/matic-token-icon.svg'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useHttpLocations from 'hooks/useHttpLocations'
|
||||
import React, { useMemo } from 'react'
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import Logo from '../Logo'
|
||||
|
||||
type Network = 'ethereum' | 'arbitrum' | 'optimism'
|
||||
|
||||
function chainIdToNetworkName(networkId: SupportedChainId): Network {
|
||||
switch (networkId) {
|
||||
case SupportedChainId.MAINNET:
|
||||
return 'ethereum'
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
return 'arbitrum'
|
||||
case SupportedChainId.OPTIMISM:
|
||||
return 'optimism'
|
||||
default:
|
||||
return 'ethereum'
|
||||
}
|
||||
}
|
||||
|
||||
export const getTokenLogoURL = (
|
||||
address: string,
|
||||
chainId: SupportedChainId = SupportedChainId.MAINNET
|
||||
): string | void => {
|
||||
const networkName = chainIdToNetworkName(chainId)
|
||||
const networksWithUrls = [SupportedChainId.ARBITRUM_ONE, SupportedChainId.MAINNET, SupportedChainId.OPTIMISM]
|
||||
if (networksWithUrls.includes(chainId)) {
|
||||
return `https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/${networkName}/assets/${address}/logo.png`
|
||||
}
|
||||
}
|
||||
|
||||
const StyledNativeLogo = styled.img<{ size: string }>`
|
||||
width: ${({ size }) => size};
|
||||
height: ${({ size }) => size};
|
||||
background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%);
|
||||
|
||||
border-radius: 50%;
|
||||
-mox-box-shadow: 0 0 1px white;
|
||||
-webkit-box-shadow: 0 0 1px white;
|
||||
box-shadow: 0 0 1px white;
|
||||
border: 0px solid rgba(255, 255, 255, 0);
|
||||
`
|
||||
|
||||
const StyledLogo = styled(Logo)<{ size: string }>`
|
||||
const StyledLogo = styled(Logo)<{ size: string; native: boolean }>`
|
||||
width: ${({ size }) => size};
|
||||
height: ${({ size }) => size};
|
||||
background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%);
|
||||
border-radius: 50%;
|
||||
-mox-box-shadow: 0 0 1px black;
|
||||
-webkit-box-shadow: 0 0 1px black;
|
||||
box-shadow: 0 0 1px black;
|
||||
-mox-box-shadow: 0 0 1px ${({ native }) => (native ? 'white' : 'black')};
|
||||
-webkit-box-shadow: 0 0 1px ${({ native }) => (native ? 'white' : 'black')};
|
||||
box-shadow: 0 0 1px ${({ native }) => (native ? 'white' : 'black')};
|
||||
border: 0px solid rgba(255, 255, 255, 0);
|
||||
`
|
||||
|
||||
@ -68,38 +26,16 @@ export default function CurrencyLogo({
|
||||
size?: string
|
||||
style?: React.CSSProperties
|
||||
}) {
|
||||
const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
|
||||
const logoURIs = useCurrencyLogoURIs(currency)
|
||||
|
||||
const srcs: string[] = useMemo(() => {
|
||||
if (!currency || currency.isNative) return []
|
||||
|
||||
if (currency.isToken) {
|
||||
const defaultUrls = []
|
||||
const url = getTokenLogoURL(currency.address, currency.chainId)
|
||||
if (url) {
|
||||
defaultUrls.push(url)
|
||||
}
|
||||
if (currency instanceof WrappedTokenInfo) {
|
||||
return [...uriLocations, ...defaultUrls]
|
||||
}
|
||||
return defaultUrls
|
||||
}
|
||||
return []
|
||||
}, [currency, uriLocations])
|
||||
|
||||
if (currency?.isNative) {
|
||||
let nativeLogoUrl: string
|
||||
switch (currency.chainId) {
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
case SupportedChainId.POLYGON:
|
||||
nativeLogoUrl = MaticLogo
|
||||
break
|
||||
default:
|
||||
nativeLogoUrl = EthereumLogo
|
||||
break
|
||||
}
|
||||
return <StyledNativeLogo src={nativeLogoUrl} alt="ethereum logo" size={size} style={style} {...rest} />
|
||||
}
|
||||
|
||||
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} {...rest} />
|
||||
return (
|
||||
<StyledLogo
|
||||
size={size}
|
||||
native={currency?.isNative ?? false}
|
||||
srcs={logoURIs}
|
||||
alt={`${currency?.symbol ?? 'token'} logo`}
|
||||
style={style}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
import { getTokenLogoURL } from './../components/CurrencyLogo/index'
|
||||
|
||||
export default function useAddTokenToMetamask(currencyToAdd: Currency | undefined): {
|
||||
addToken: () => void
|
||||
success: boolean | undefined
|
||||
@ -13,6 +12,7 @@ export default function useAddTokenToMetamask(currencyToAdd: Currency | undefine
|
||||
const token: Token | undefined = currencyToAdd?.wrapped
|
||||
|
||||
const [success, setSuccess] = useState<boolean | undefined>()
|
||||
const logoURL = useCurrencyLogoURIs(token)[0]
|
||||
|
||||
const addToken = useCallback(() => {
|
||||
if (library && library.provider.isMetaMask && library.provider.request && token) {
|
||||
@ -26,7 +26,7 @@ export default function useAddTokenToMetamask(currencyToAdd: Currency | undefine
|
||||
address: token.address,
|
||||
symbol: token.symbol,
|
||||
decimals: token.decimals,
|
||||
image: getTokenLogoURL(token.address),
|
||||
image: logoURL,
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -37,7 +37,7 @@ export default function useAddTokenToMetamask(currencyToAdd: Currency | undefine
|
||||
} else {
|
||||
setSuccess(false)
|
||||
}
|
||||
}, [library, token])
|
||||
}, [library, logoURL, token])
|
||||
|
||||
return { addToken, success }
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ const Column = styled.div<{
|
||||
css?: ReturnType<typeof css>
|
||||
}>`
|
||||
align-items: ${({ align }) => align ?? 'center'};
|
||||
background-color: inherit;
|
||||
color: ${({ color, theme }) => color && theme[color]};
|
||||
display: ${({ flex }) => (flex ? 'flex' : 'grid')};
|
||||
flex-direction: column;
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { AlertTriangle, ArrowRight, CheckCircle, Spinner, Trash2 } from 'lib/icons'
|
||||
import { DAI, ETH, UNI, USDC } from 'lib/mocks'
|
||||
import styled, { ThemedText } from 'lib/theme'
|
||||
import { Token } from 'lib/types'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
||||
import Button from './Button'
|
||||
@ -13,7 +12,7 @@ import TokenImg from './TokenImg'
|
||||
|
||||
interface ITokenAmount {
|
||||
value: number
|
||||
token: Token
|
||||
token: Currency
|
||||
}
|
||||
|
||||
export enum TransactionStatus {
|
||||
@ -28,25 +27,6 @@ interface ITransaction {
|
||||
status: TransactionStatus
|
||||
}
|
||||
|
||||
// TODO: integrate with web3-react context
|
||||
export const mockTxs: ITransaction[] = [
|
||||
{
|
||||
input: { value: 4170.15, token: USDC },
|
||||
output: { value: 4167.44, token: DAI },
|
||||
status: TransactionStatus.SUCCESS,
|
||||
},
|
||||
{
|
||||
input: { value: 1.23, token: ETH },
|
||||
output: { value: 4125.02, token: DAI },
|
||||
status: TransactionStatus.PENDING,
|
||||
},
|
||||
{
|
||||
input: { value: 10, token: UNI },
|
||||
output: { value: 2125.02, token: ETH },
|
||||
status: TransactionStatus.ERROR,
|
||||
},
|
||||
]
|
||||
|
||||
const TransactionRow = styled(Row)`
|
||||
padding: 0.5em 1em;
|
||||
|
||||
@ -94,7 +74,7 @@ function Transaction({ tx }: { tx: ITransaction }) {
|
||||
}
|
||||
|
||||
export default function RecentTransactionsDialog() {
|
||||
const [txs, setTxs] = useState(mockTxs)
|
||||
const [txs, setTxs] = useState([])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { atom } from 'jotai'
|
||||
import { useAtomValue } from 'jotai/utils'
|
||||
import useColor, { usePrefetchColor } from 'lib/hooks/useColor'
|
||||
import useCurrencyColor, { usePrefetchCurrencyColor } from 'lib/hooks/useCurrencyColor'
|
||||
import { inputAtom, outputAtom, useUpdateInputToken, useUpdateInputValue } from 'lib/state/swap'
|
||||
import styled, { DynamicThemeProvider, ThemedText } from 'lib/theme'
|
||||
import { ReactNode, useMemo } from 'react'
|
||||
@ -40,8 +40,8 @@ export default function Output({ disabled, children }: OutputProps) {
|
||||
const balance = 123.45
|
||||
|
||||
const overrideColor = useAtomValue(colorAtom)
|
||||
const dynamicColor = useColor(output.token)
|
||||
usePrefetchColor(input.token) // extract eagerly in case of reversal
|
||||
const dynamicColor = useCurrencyColor(output.token)
|
||||
usePrefetchCurrencyColor(input.token) // extract eagerly in case of reversal
|
||||
const color = overrideColor || dynamicColor
|
||||
const hasColor = output.token ? Boolean(color) || null : false
|
||||
|
||||
|
@ -1,13 +1,23 @@
|
||||
import { tokens } from '@uniswap/default-token-list'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { nativeOnChain } from 'constants/tokens'
|
||||
import { useUpdateAtom } from 'jotai/utils'
|
||||
import { DAI, ETH } from 'lib/mocks'
|
||||
import { transactionAtom } from 'lib/state/swap'
|
||||
import { useEffect } from 'react'
|
||||
import { useSelect } from 'react-cosmos/fixture'
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
import invariant from 'tiny-invariant'
|
||||
|
||||
import { Modal } from '../Dialog'
|
||||
import { StatusDialog } from './Status'
|
||||
|
||||
const ETH = nativeOnChain(SupportedChainId.MAINNET)
|
||||
const UNI = (function () {
|
||||
const token = tokens.find(({ symbol }) => symbol === 'UNI')
|
||||
invariant(token)
|
||||
return new WrappedTokenInfo(token)
|
||||
})()
|
||||
|
||||
function Fixture() {
|
||||
const setTransaction = useUpdateAtom(transactionAtom)
|
||||
|
||||
@ -17,7 +27,7 @@ function Fixture() {
|
||||
useEffect(() => {
|
||||
setTransaction({
|
||||
input: { token: ETH, value: 1 },
|
||||
output: { token: DAI, value: 4200 },
|
||||
output: { token: UNI, value: 42 },
|
||||
receipt: '',
|
||||
timestamp: Date.now(),
|
||||
})
|
||||
@ -27,7 +37,7 @@ function Fixture() {
|
||||
case 'PENDING':
|
||||
setTransaction({
|
||||
input: { token: ETH, value: 1 },
|
||||
output: { token: DAI, value: 4200 },
|
||||
output: { token: UNI, value: 42 },
|
||||
receipt: '',
|
||||
timestamp: Date.now(),
|
||||
})
|
||||
|
@ -1,12 +1,23 @@
|
||||
import { tokens } from '@uniswap/default-token-list'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { nativeOnChain } from 'constants/tokens'
|
||||
import { useUpdateAtom } from 'jotai/utils'
|
||||
import { DAI, ETH } from 'lib/mocks'
|
||||
import { Field, outputAtom, stateAtom } from 'lib/state/swap'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useValue } from 'react-cosmos/fixture'
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
import invariant from 'tiny-invariant'
|
||||
|
||||
import { Modal } from '../Dialog'
|
||||
import { SummaryDialog } from './Summary'
|
||||
|
||||
const ETH = nativeOnChain(SupportedChainId.MAINNET)
|
||||
const UNI = (function () {
|
||||
const token = tokens.find(({ symbol }) => symbol === 'UNI')
|
||||
invariant(token)
|
||||
return new WrappedTokenInfo(token)
|
||||
})()
|
||||
|
||||
function Fixture() {
|
||||
const setState = useUpdateAtom(stateAtom)
|
||||
const [, setInitialized] = useState(false)
|
||||
@ -15,7 +26,7 @@ function Fixture() {
|
||||
setState({
|
||||
activeInput: Field.INPUT,
|
||||
input: { token: ETH, value: 1, usdc: 4195 },
|
||||
output: { token: DAI, value: 4200, usdc: 4200 },
|
||||
output: { token: UNI, value: 42, usdc: 42 },
|
||||
swap: {
|
||||
lpFee: 0.0005,
|
||||
integratorFee: 0.00025,
|
||||
@ -30,7 +41,7 @@ function Fixture() {
|
||||
const setOutput = useUpdateAtom(outputAtom)
|
||||
const [price] = useValue('output value', { defaultValue: 4200 })
|
||||
useEffect(() => {
|
||||
setState((state) => ({ ...state, output: { token: DAI, value: price, usdc: price } }))
|
||||
setState((state) => ({ ...state, output: { token: UNI, value: price, usdc: price } }))
|
||||
}, [price, setOutput, setState])
|
||||
|
||||
return (
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { t } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { State } from 'lib/state/swap'
|
||||
import { ThemedText } from 'lib/theme'
|
||||
import { Token } from 'lib/types'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import Row from '../../Row'
|
||||
@ -24,8 +24,8 @@ function Detail({ label, value }: DetailProps) {
|
||||
|
||||
interface DetailsProps {
|
||||
swap: Required<State>['swap']
|
||||
input: Token
|
||||
output: Token
|
||||
input: Currency
|
||||
output: Currency
|
||||
}
|
||||
|
||||
export default function Details({
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { tokens } from '@uniswap/default-token-list'
|
||||
import { useAtom } from 'jotai'
|
||||
import { useUpdateAtom } from 'jotai/utils'
|
||||
import { inputAtom, outputAtom, swapAtom } from 'lib/state/swap'
|
||||
@ -61,7 +62,7 @@ function Fixture() {
|
||||
}
|
||||
}, [color, setColor])
|
||||
|
||||
return <Swap />
|
||||
return <Swap defaults={{ tokenList: tokens }} />
|
||||
}
|
||||
|
||||
export default <Fixture />
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { Input } from 'lib/state/swap'
|
||||
import styled, { keyframes, ThemedText } from 'lib/theme'
|
||||
import { Token } from 'lib/types'
|
||||
import { FocusEvent, ReactNode, useCallback, useRef, useState } from 'react'
|
||||
|
||||
import Button from '../Button'
|
||||
@ -49,7 +49,7 @@ interface TokenInputProps {
|
||||
disabled?: boolean
|
||||
onMax?: () => void
|
||||
onChangeInput: (input: number | undefined) => void
|
||||
onChangeToken: (token: Token) => void
|
||||
onChangeToken: (token: Currency) => void
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
|
@ -1,29 +1,31 @@
|
||||
import useNativeEvent from 'lib/hooks/useNativeEvent'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
|
||||
import { Slash } from 'lib/icons'
|
||||
import styled from 'lib/theme'
|
||||
import uriToHttp from 'lib/utils/uriToHttp'
|
||||
import { useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
const badSrcs = new Set<string>()
|
||||
|
||||
interface TokenImgProps {
|
||||
className?: string
|
||||
token: {
|
||||
name?: string
|
||||
symbol: string
|
||||
logoURI?: string
|
||||
}
|
||||
token: Currency
|
||||
}
|
||||
const TRANSPARENT_SRC = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
|
||||
|
||||
function TokenImg({ className, token }: TokenImgProps) {
|
||||
const [img, setImg] = useState<HTMLImageElement | null>(null)
|
||||
const src = token.logoURI ? uriToHttp(token.logoURI)[0] : TRANSPARENT_SRC
|
||||
useNativeEvent(img, 'error', () => {
|
||||
if (img) {
|
||||
// Use a local transparent gif to avoid the browser-dependent broken img icon.
|
||||
// The icon may still flash, but using a native event further reduces the duration.
|
||||
img.src = TRANSPARENT_SRC
|
||||
}
|
||||
})
|
||||
return <img className={className} src={src} alt={token.name || token.symbol} ref={setImg} />
|
||||
const srcs = useCurrencyLogoURIs(token)
|
||||
const [src, setSrc] = useState<string | undefined>()
|
||||
useEffect(() => {
|
||||
setSrc(srcs.find((src) => !badSrcs.has(src)))
|
||||
}, [srcs])
|
||||
const onError = useCallback(() => {
|
||||
if (src) badSrcs.add(src)
|
||||
setSrc(srcs.find((src) => !badSrcs.has(src)))
|
||||
}, [src, srcs])
|
||||
|
||||
if (src) {
|
||||
return <img className={className} src={src} alt={token.name || token.symbol} onError={onError} />
|
||||
}
|
||||
return <Slash className={className} color="secondary" />
|
||||
}
|
||||
|
||||
export default styled(TokenImg)<{ size?: number }>`
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import styled, { ThemedText } from 'lib/theme'
|
||||
import { Token } from 'lib/types'
|
||||
|
||||
import Button from '../Button'
|
||||
import Row from '../Row'
|
||||
@ -11,8 +11,8 @@ const TokenButton = styled(Button)`
|
||||
`
|
||||
|
||||
interface TokenBaseProps {
|
||||
value: Token
|
||||
onClick: (value: Token) => void
|
||||
value: Currency
|
||||
onClick: (value: Currency) => void
|
||||
}
|
||||
|
||||
export default function TokenBase({ value, onClick }: TokenBaseProps) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { ChevronDown } from 'lib/icons'
|
||||
import styled, { ThemedText } from 'lib/theme'
|
||||
import { Token } from 'lib/types'
|
||||
|
||||
import Button from '../Button'
|
||||
import Row from '../Row'
|
||||
@ -27,7 +27,7 @@ const TokenButtonRow = styled(Row)<{ collapsed: boolean }>`
|
||||
`
|
||||
|
||||
interface TokenButtonProps {
|
||||
value?: Token
|
||||
value?: Currency
|
||||
collapsed: boolean
|
||||
disabled?: boolean
|
||||
onClick: () => void
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
|
||||
import useCurrencyBalance from 'lib/hooks/useCurrencyBalance'
|
||||
import useNativeEvent from 'lib/hooks/useNativeEvent'
|
||||
@ -18,7 +19,6 @@ import {
|
||||
} from 'react'
|
||||
import AutoSizer from 'react-virtualized-auto-sizer'
|
||||
import { areEqual, FixedSizeList, FixedSizeListProps } from 'react-window'
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
import invariant from 'tiny-invariant'
|
||||
|
||||
import { BaseButton } from '../Button'
|
||||
@ -33,7 +33,7 @@ const TokenButton = styled(BaseButton)`
|
||||
`
|
||||
|
||||
const ITEM_SIZE = 56
|
||||
type ItemData = WrappedTokenInfo[]
|
||||
type ItemData = Currency[]
|
||||
interface FixedSizeTokenList extends FixedSizeList<ItemData>, ComponentClass<FixedSizeListProps<ItemData>> {}
|
||||
const TokenList = styled(FixedSizeList as unknown as FixedSizeTokenList)<{
|
||||
hover: number
|
||||
@ -57,13 +57,13 @@ const OnHover = styled.div<{ hover: number }>`
|
||||
|
||||
interface TokenOptionProps {
|
||||
index: number
|
||||
value: WrappedTokenInfo
|
||||
value: Currency
|
||||
style: CSSProperties
|
||||
}
|
||||
|
||||
interface BubbledEvent extends SyntheticEvent {
|
||||
index?: number
|
||||
token?: WrappedTokenInfo
|
||||
token?: Currency
|
||||
ref?: HTMLButtonElement
|
||||
}
|
||||
|
||||
@ -107,7 +107,10 @@ function TokenOption({ index, value, style }: TokenOptionProps) {
|
||||
)
|
||||
}
|
||||
|
||||
const itemKey = (index: number, tokens: ItemData) => tokens[index]?.address
|
||||
const itemKey = (index: number, tokens: ItemData) => {
|
||||
if (tokens[index].isNative) return 'native'
|
||||
return tokens[index].wrapped.address
|
||||
}
|
||||
const ItemRow = memo(function ItemRow({
|
||||
data: tokens,
|
||||
index,
|
||||
@ -127,8 +130,8 @@ interface TokenOptionsHandle {
|
||||
}
|
||||
|
||||
interface TokenOptionsProps {
|
||||
tokens: WrappedTokenInfo[]
|
||||
onSelect: (token: WrappedTokenInfo) => void
|
||||
tokens: Currency[]
|
||||
onSelect: (token: Currency) => void
|
||||
}
|
||||
|
||||
const TokenOptions = forwardRef<TokenOptionsHandle, TokenOptionsProps>(function TokenOptions(
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { useQueryTokenList } from 'lib/hooks/useTokenList'
|
||||
import styled, { ThemedText } from 'lib/theme'
|
||||
import { Token } from 'lib/types'
|
||||
import { ElementRef, useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import Column from '../Column'
|
||||
@ -17,11 +17,11 @@ const SearchInput = styled(StringInput)`
|
||||
${inputCss}
|
||||
`
|
||||
|
||||
export function TokenSelectDialog({ onSelect }: { onSelect: (token: Token) => void }) {
|
||||
export function TokenSelectDialog({ onSelect }: { onSelect: (token: Currency) => void }) {
|
||||
const [query, setQuery] = useState('')
|
||||
const tokens = useQueryTokenList(query)
|
||||
|
||||
const baseTokens: Token[] = [] // TODO(zzmp): Add base tokens to token list functionality
|
||||
const baseTokens: Currency[] = [] // TODO(zzmp): Add base tokens to token list functionality
|
||||
|
||||
// TODO(zzmp): Disable already selected tokens (passed as props?)
|
||||
|
||||
@ -49,7 +49,7 @@ export function TokenSelectDialog({ onSelect }: { onSelect: (token: Token) => vo
|
||||
{Boolean(baseTokens.length) && (
|
||||
<Row pad={0.75} gap={0.25} justify="flex-start" flex>
|
||||
{baseTokens.map((token) => (
|
||||
<TokenBase value={token} onClick={onSelect} key={token.address} />
|
||||
<TokenBase value={token} onClick={onSelect} key={token.wrapped.address} />
|
||||
))}
|
||||
</Row>
|
||||
)}
|
||||
@ -61,16 +61,16 @@ export function TokenSelectDialog({ onSelect }: { onSelect: (token: Token) => vo
|
||||
}
|
||||
|
||||
interface TokenSelectProps {
|
||||
value?: Token
|
||||
value?: Currency
|
||||
collapsed: boolean
|
||||
disabled?: boolean
|
||||
onSelect: (value: Token) => void
|
||||
onSelect: (value: Currency) => void
|
||||
}
|
||||
|
||||
export default function TokenSelect({ value, collapsed, disabled, onSelect }: TokenSelectProps) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const selectAndClose = useCallback(
|
||||
(value: Token) => {
|
||||
(value: Currency) => {
|
||||
onSelect(value)
|
||||
setOpen(false)
|
||||
},
|
||||
|
@ -1,77 +0,0 @@
|
||||
import { useTheme } from 'lib/theme'
|
||||
import { Token } from 'lib/types'
|
||||
import uriToHttp from 'lib/utils/uriToHttp'
|
||||
import Vibrant from 'node-vibrant/lib/bundle'
|
||||
import { useLayoutEffect, useState } from 'react'
|
||||
|
||||
const colors = new Map<string, string | undefined>()
|
||||
|
||||
function UriForEthToken(address: string) {
|
||||
return `https://raw.githubusercontent.com/uniswap/assets/master/blockchains/ethereum/assets/${address}/logo.png?color`
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the prominent color from a token.
|
||||
* NB: If cached, this function returns synchronously; using a callback allows sync or async returns.
|
||||
*/
|
||||
async function getColorFromToken(token: Token, cb: (color: string | undefined) => void = () => void 0) {
|
||||
const { address, chainId, logoURI } = token
|
||||
const key = chainId + address
|
||||
let color = colors.get(key)
|
||||
|
||||
if (!color && logoURI) {
|
||||
// Color extraction must use a CORS-compatible resource, but the resource is already cached.
|
||||
// Add a dummy parameter to force a different browser resource cache entry.
|
||||
// Without this, color extraction prevents resource caching.
|
||||
const uri = uriToHttp(logoURI)[0] + '?color'
|
||||
color = await getColorFromUriPath(uri)
|
||||
}
|
||||
|
||||
if (!color && chainId === 1) {
|
||||
const fallbackUri = UriForEthToken(address)
|
||||
color = await getColorFromUriPath(fallbackUri)
|
||||
}
|
||||
|
||||
colors.set(key, color)
|
||||
return cb(color)
|
||||
}
|
||||
|
||||
async function getColorFromUriPath(uri: string): Promise<string | undefined> {
|
||||
try {
|
||||
const palette = await Vibrant.from(uri).getPalette()
|
||||
return palette.Vibrant?.hex
|
||||
} catch {}
|
||||
return
|
||||
}
|
||||
|
||||
export function usePrefetchColor(token?: Token) {
|
||||
const theme = useTheme()
|
||||
|
||||
if (theme.tokenColorExtraction && token) {
|
||||
getColorFromToken(token)
|
||||
}
|
||||
}
|
||||
|
||||
export default function useColor(token?: Token) {
|
||||
const [color, setColor] = useState<string | undefined>(undefined)
|
||||
const theme = useTheme()
|
||||
|
||||
useLayoutEffect(() => {
|
||||
let stale = false
|
||||
|
||||
if (theme.tokenColorExtraction && token) {
|
||||
getColorFromToken(token, (color) => {
|
||||
if (!stale && color) {
|
||||
setColor(color)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
stale = true
|
||||
setColor(undefined)
|
||||
}
|
||||
}, [token, theme])
|
||||
|
||||
return color
|
||||
}
|
78
src/lib/hooks/useCurrencyColor.ts
Normal file
78
src/lib/hooks/useCurrencyColor.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { useTheme } from 'lib/theme'
|
||||
import Vibrant from 'node-vibrant/lib/bundle'
|
||||
import { useEffect, useLayoutEffect, useState } from 'react'
|
||||
|
||||
import useCurrencyLogoURIs from './useCurrencyLogoURIs'
|
||||
|
||||
const colors = new Map<string, string | undefined>()
|
||||
|
||||
/**
|
||||
* Extracts the prominent color from a token.
|
||||
* NB: If cached, this function returns synchronously; using a callback allows sync or async returns.
|
||||
*/
|
||||
async function getColorFromLogoURIs(logoURIs: string[], cb: (color: string | undefined) => void = () => void 0) {
|
||||
const key = logoURIs[0]
|
||||
let color = colors.get(key)
|
||||
|
||||
if (!color) {
|
||||
for (const logoURI of logoURIs) {
|
||||
let uri = logoURI
|
||||
if (logoURI.startsWith('http')) {
|
||||
// Color extraction must use a CORS-compatible resource, but the resource may already be cached.
|
||||
// Adds a dummy parameter to force a different browser resource cache entry. Without this, color extraction prevents resource caching.
|
||||
uri += '?color'
|
||||
}
|
||||
|
||||
color = await getColorFromUriPath(uri)
|
||||
if (color) break
|
||||
}
|
||||
}
|
||||
|
||||
colors.set(key, color)
|
||||
return cb(color)
|
||||
}
|
||||
|
||||
async function getColorFromUriPath(uri: string): Promise<string | undefined> {
|
||||
try {
|
||||
const palette = await Vibrant.from(uri).getPalette()
|
||||
return palette.Vibrant?.hex
|
||||
} catch {}
|
||||
return
|
||||
}
|
||||
|
||||
export function usePrefetchCurrencyColor(token?: Currency) {
|
||||
const theme = useTheme()
|
||||
const logoURIs = useCurrencyLogoURIs(token)
|
||||
|
||||
useEffect(() => {
|
||||
if (theme.tokenColorExtraction && token) {
|
||||
getColorFromLogoURIs(logoURIs)
|
||||
}
|
||||
}, [token, logoURIs, theme.tokenColorExtraction])
|
||||
}
|
||||
|
||||
export default function useCurrencyColor(token?: Currency) {
|
||||
const [color, setColor] = useState<string | undefined>(undefined)
|
||||
const theme = useTheme()
|
||||
const logoURIs = useCurrencyLogoURIs(token)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
let stale = false
|
||||
|
||||
if (theme.tokenColorExtraction && token) {
|
||||
getColorFromLogoURIs(logoURIs, (color) => {
|
||||
if (!stale && color) {
|
||||
setColor(color)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
stale = true
|
||||
setColor(undefined)
|
||||
}
|
||||
}, [token, logoURIs, theme.tokenColorExtraction])
|
||||
|
||||
return color
|
||||
}
|
59
src/lib/hooks/useCurrencyLogoURIs.ts
Normal file
59
src/lib/hooks/useCurrencyLogoURIs.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useHttpLocations from 'hooks/useHttpLocations'
|
||||
import { useMemo } from 'react'
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
|
||||
import EthereumLogo from '../../assets/images/ethereum-logo.png'
|
||||
import MaticLogo from '../../assets/svg/matic-token-icon.svg'
|
||||
|
||||
type Network = 'ethereum' | 'arbitrum' | 'optimism'
|
||||
|
||||
function chainIdToNetworkName(networkId: SupportedChainId): Network {
|
||||
switch (networkId) {
|
||||
case SupportedChainId.MAINNET:
|
||||
return 'ethereum'
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
return 'arbitrum'
|
||||
case SupportedChainId.OPTIMISM:
|
||||
return 'optimism'
|
||||
default:
|
||||
return 'ethereum'
|
||||
}
|
||||
}
|
||||
|
||||
function getNativeLogoURI(chainId: SupportedChainId = SupportedChainId.MAINNET): string {
|
||||
switch (chainId) {
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
case SupportedChainId.POLYGON:
|
||||
return MaticLogo
|
||||
default:
|
||||
return EthereumLogo
|
||||
}
|
||||
}
|
||||
|
||||
function getTokenLogoURI(address: string, chainId: SupportedChainId = SupportedChainId.MAINNET): string | void {
|
||||
const networkName = chainIdToNetworkName(chainId)
|
||||
const networksWithUrls = [SupportedChainId.ARBITRUM_ONE, SupportedChainId.MAINNET, SupportedChainId.OPTIMISM]
|
||||
if (networksWithUrls.includes(chainId)) {
|
||||
return `https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/${networkName}/assets/${address}/logo.png`
|
||||
}
|
||||
}
|
||||
|
||||
export default function useCurrencyLogoURIs(currency?: Currency | null): string[] {
|
||||
const locations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
|
||||
return useMemo(() => {
|
||||
const logoURIs = [...locations]
|
||||
if (currency) {
|
||||
if (currency.isNative) {
|
||||
logoURIs.push(getNativeLogoURI(currency.chainId))
|
||||
} else if (currency.isToken) {
|
||||
const logoURI = getTokenLogoURI(currency.address, currency.chainId)
|
||||
if (logoURI) {
|
||||
logoURIs.push(logoURI)
|
||||
}
|
||||
}
|
||||
}
|
||||
return logoURIs
|
||||
}, [currency, locations])
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { NativeCurrency } from '@uniswap/sdk-core'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { nativeOnChain } from 'constants/tokens'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
export default function useNativeCurrency(): Currency {
|
||||
export default function useNativeCurrency(): NativeCurrency {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
return useMemo(
|
||||
() =>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Token } from '@uniswap/sdk-core'
|
||||
import { NativeCurrency, Token } from '@uniswap/sdk-core'
|
||||
import { TokenInfo } from '@uniswap/token-lists'
|
||||
|
||||
import { isAddress } from '../../../utils'
|
||||
@ -6,12 +6,12 @@ import { isAddress } from '../../../utils'
|
||||
const alwaysTrue = () => true
|
||||
|
||||
/** Creates a filter function that filters tokens that do not match the query. */
|
||||
export function getTokenFilter<T extends Token | TokenInfo>(query: string): (token: T) => boolean {
|
||||
export function getTokenFilter<T extends Token | TokenInfo>(query: string): (token: T | NativeCurrency) => boolean {
|
||||
const searchingAddress = isAddress(query)
|
||||
|
||||
if (searchingAddress) {
|
||||
const lower = searchingAddress.toLowerCase()
|
||||
return (t: T) => ('isToken' in t ? searchingAddress === t.address : lower === t.address.toLowerCase())
|
||||
const address = searchingAddress.toLowerCase()
|
||||
return (t: T | NativeCurrency) => 'address' in t && address === t.address.toLowerCase()
|
||||
}
|
||||
|
||||
const queryParts = query
|
||||
@ -30,5 +30,5 @@ export function getTokenFilter<T extends Token | TokenInfo>(query: string): (tok
|
||||
return queryParts.every((p) => p.length === 0 || parts.some((sp) => sp.startsWith(p) || sp.endsWith(p)))
|
||||
}
|
||||
|
||||
return ({ name, symbol }: T): boolean => Boolean((symbol && match(symbol)) || (name && match(name)))
|
||||
return ({ name, symbol }: T | NativeCurrency): boolean => Boolean((symbol && match(symbol)) || (name && match(name)))
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import useDebounce from 'hooks/useDebounce'
|
||||
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
|
||||
import { useTokenBalances } from 'lib/hooks/useCurrencyBalance'
|
||||
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
||||
import { useMemo } from 'react'
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
|
||||
@ -10,13 +11,23 @@ import { tokenComparator, useSortTokensByQuery } from './sorting'
|
||||
export function useQueryTokens(query: string, tokens: WrappedTokenInfo[]) {
|
||||
const { account } = useActiveWeb3React()
|
||||
const balances = useTokenBalances(account, tokens)
|
||||
const sortedTokens = useMemo(() => [...tokens.sort(tokenComparator.bind(null, balances))], [balances, tokens])
|
||||
|
||||
const debouncedQuery = useDebounce(query, 200)
|
||||
const filteredTokens = useMemo(
|
||||
() => sortedTokens.filter(getTokenFilter(debouncedQuery)),
|
||||
[debouncedQuery, sortedTokens]
|
||||
const sortedTokens = useMemo(
|
||||
// Create a new array because sort is in-place and returns a referentially equivalent array.
|
||||
() => Array.from(tokens).sort(tokenComparator.bind(null, balances)),
|
||||
[balances, tokens]
|
||||
)
|
||||
|
||||
return useSortTokensByQuery(debouncedQuery, filteredTokens)
|
||||
const debouncedQuery = useDebounce(query, 200)
|
||||
const filter = useMemo(() => getTokenFilter(debouncedQuery), [debouncedQuery])
|
||||
const filteredTokens = useMemo(() => sortedTokens.filter(filter), [filter, sortedTokens])
|
||||
|
||||
const queriedTokens = useSortTokensByQuery(debouncedQuery, filteredTokens)
|
||||
|
||||
const native = useNativeCurrency()
|
||||
return useMemo(() => {
|
||||
if (native && filter(native)) {
|
||||
return [native, ...queriedTokens]
|
||||
}
|
||||
return queriedTokens
|
||||
}, [filter, native, queriedTokens])
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
HelpCircle as HelpCircleIcon,
|
||||
Info as InfoIcon,
|
||||
Settings as SettingsIcon,
|
||||
Slash as SlashIcon,
|
||||
Trash2 as Trash2Icon,
|
||||
X as XIcon,
|
||||
} from 'react-feather'
|
||||
@ -77,6 +78,7 @@ export const CreditCard = icon(CreditCardIcon)
|
||||
export const HelpCircle = icon(HelpCircleIcon)
|
||||
export const Info = icon(InfoIcon)
|
||||
export const Settings = icon(SettingsIcon)
|
||||
export const Slash = icon(SlashIcon)
|
||||
export const Trash2 = icon(Trash2Icon)
|
||||
export const X = icon(XIcon)
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
export const USDC = {
|
||||
name: 'USDCoin',
|
||||
symbol: 'USDC',
|
||||
chainId: 1,
|
||||
decimals: 18,
|
||||
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
||||
logoURI:
|
||||
'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png',
|
||||
}
|
||||
export const DAI = {
|
||||
name: 'DaiStablecoin',
|
||||
symbol: 'DAI',
|
||||
chainId: 1,
|
||||
decimals: 18,
|
||||
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
|
||||
logoURI: 'https://gemini.com/images/currencies/icons/default/dai.svg',
|
||||
}
|
||||
export const ETH = {
|
||||
name: 'Ether',
|
||||
symbol: 'ETH',
|
||||
chainId: 1,
|
||||
decimals: 18,
|
||||
address: 'ETHER',
|
||||
logoURI: 'https://raw.githubusercontent.com/Uniswap/interface/main/src/assets/images/ethereum-logo.png',
|
||||
}
|
||||
export const UNI = {
|
||||
name: 'Uniswap',
|
||||
symbol: 'UNI',
|
||||
chainId: 1,
|
||||
decimals: 18,
|
||||
address: '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984',
|
||||
logoURI: 'https://gemini.com/images/currencies/icons/default/uni.svg',
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { nativeOnChain } from 'constants/tokens'
|
||||
import { atom, WritableAtom } from 'jotai'
|
||||
import { atomWithImmer } from 'jotai/immer'
|
||||
import { useUpdateAtom } from 'jotai/utils'
|
||||
import { atomWithReset } from 'jotai/utils'
|
||||
import { ETH } from 'lib/mocks'
|
||||
import { Customizable, pickAtom, setCustomizable, setTogglable } from 'lib/state/atoms'
|
||||
import { Token } from 'lib/types'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
/** Max slippage, as a percentage. */
|
||||
@ -42,7 +43,7 @@ export enum Field {
|
||||
|
||||
export interface Input {
|
||||
value?: number
|
||||
token?: Token
|
||||
token?: Currency
|
||||
usdc?: number
|
||||
}
|
||||
|
||||
@ -62,7 +63,7 @@ export interface State {
|
||||
|
||||
export const stateAtom = atomWithImmer<State>({
|
||||
activeInput: Field.INPUT,
|
||||
input: { token: ETH },
|
||||
input: { token: nativeOnChain(SupportedChainId.MAINNET) },
|
||||
output: {},
|
||||
})
|
||||
|
||||
|
8
src/lib/types.d.ts
vendored
8
src/lib/types.d.ts
vendored
@ -1,8 +0,0 @@
|
||||
export interface Token {
|
||||
name: string
|
||||
symbol: string
|
||||
chainId: number
|
||||
decimals: number
|
||||
address: string
|
||||
logoURI?: string
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
"hooks/*": ["../hooks/*"],
|
||||
"state/*": ["../state/*"],
|
||||
"types/*": ["../types/*"],
|
||||
"utils/*": ["../utils/*"],
|
||||
},
|
||||
},
|
||||
"exclude": ["node_modules", "src/lib/**/*.test.*"],
|
||||
|
17
yarn.lock
17
yarn.lock
@ -5069,6 +5069,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/default-token-list/-/default-token-list-2.2.0.tgz#d85a5c2520f57f4920bd989dfc9f01e1b701a567"
|
||||
integrity sha512-vFPWoGzDjHP4i2l7yLaober/lZMmzOZXXirVF8XNyfNzRxgmYCWKO6SzKtfEUwxpd3/KUebgdK55II4Mnak62A==
|
||||
|
||||
"@uniswap/default-token-list@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/default-token-list/-/default-token-list-3.0.0.tgz#427ff2a65bbc77a5a24e60f6158441956773f684"
|
||||
integrity sha512-t0H96s1Mx2ga6cGMj/wP/3NWdX4c9yZFd0ydiTOWLhWf1i5RjhWWND/ZTdn8QhmNsdHlhrGsWrEU62xoAY11bw==
|
||||
|
||||
"@uniswap/governance@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/@uniswap/governance/-/governance-1.0.2.tgz"
|
||||
@ -17498,10 +17503,10 @@ react-cosmos-shared2@^5.6.3:
|
||||
react-is "^17.0.2"
|
||||
socket.io-client "2.2.0"
|
||||
|
||||
react-cosmos@^5.6.3:
|
||||
version "5.6.3"
|
||||
resolved "https://registry.yarnpkg.com/react-cosmos/-/react-cosmos-5.6.3.tgz#bd2c0e1334b2c9992ddb3a5d8dfcbe5fc723cbc8"
|
||||
integrity sha512-FG6VIc4prnqnNQ9ToBq2cNPkPxZcZRauL0tMkbi01aoS90c3sNoQt6koL/IsntSpPcR67KRe+OtJspq9OtBjxg==
|
||||
react-cosmos@^5.6.6:
|
||||
version "5.6.6"
|
||||
resolved "https://registry.yarnpkg.com/react-cosmos/-/react-cosmos-5.6.6.tgz#93d66e347a63da7dfe046c2cb23221dcf815ce9d"
|
||||
integrity sha512-RMLRjl2gFq9370N6QszjPRMaT5WsEBEkJBsFbz56h00xPnJAxsab8gu5yj6yDDDSFibL/jBgxjJLdqbF00HMNw==
|
||||
dependencies:
|
||||
"@skidding/launch-editor" "^2.2.3"
|
||||
"@skidding/webpack-hot-middleware" "^2.25.0"
|
||||
@ -17520,7 +17525,7 @@ react-cosmos@^5.6.3:
|
||||
react-cosmos-playground2 "^5.6.3"
|
||||
react-cosmos-plugin "^5.6.0"
|
||||
react-cosmos-shared2 "^5.6.3"
|
||||
react-error-overlay "^6.0.9"
|
||||
react-error-overlay "6.0.9"
|
||||
regenerator-runtime "^0.13.7"
|
||||
resolve-from "^5.0.0"
|
||||
slash "^3.0.0"
|
||||
@ -17584,7 +17589,7 @@ react-error-boundary@^3.1.0:
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
|
||||
react-error-overlay@^6.0.9:
|
||||
react-error-overlay@6.0.9, react-error-overlay@^6.0.9:
|
||||
version "6.0.9"
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
|
||||
integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
|
||||
|
Loading…
Reference in New Issue
Block a user