feat: integrate widget settings/value (#4499)
* widgets: v2.2.0 * refactor: wrap Widget * feat: integrate widget settings * refactor: import from widgets * fix: better settings integration * fix: include types * chore: formatting * build: bump widgets version * feat: integrate widget value * feat: integrate widget token selection * feat: clean up widget integration * build: bump widgets version * refactor: mv widget wrapper to components * refactor: split widget integrations * refactor: value -> inputs * fix: consolidate slippage hooks * fix: memoize currency search modal * refactor: clarify widget code * fix: allow loading token in widget * fix: add TradeType helpers
This commit is contained in:
parent
cbf165dc40
commit
723db9d0ea
@ -145,7 +145,7 @@
|
|||||||
"@uniswap/v3-core": "1.0.0",
|
"@uniswap/v3-core": "1.0.0",
|
||||||
"@uniswap/v3-periphery": "^1.1.1",
|
"@uniswap/v3-periphery": "^1.1.1",
|
||||||
"@uniswap/v3-sdk": "^3.9.0",
|
"@uniswap/v3-sdk": "^3.9.0",
|
||||||
"@uniswap/widgets": "^2.1.1",
|
"@uniswap/widgets": "^2.3.1",
|
||||||
"@vanilla-extract/css": "^1.7.2",
|
"@vanilla-extract/css": "^1.7.2",
|
||||||
"@vanilla-extract/css-utils": "^0.1.2",
|
"@vanilla-extract/css-utils": "^0.1.2",
|
||||||
"@vanilla-extract/dynamic": "^2.0.2",
|
"@vanilla-extract/dynamic": "^2.0.2",
|
||||||
|
@ -3,7 +3,7 @@ import { TokenList } from '@uniswap/token-lists'
|
|||||||
import TokenSafety from 'components/TokenSafety'
|
import TokenSafety from 'components/TokenSafety'
|
||||||
import { TokenSafetyVariant, useTokenSafetyFlag } from 'featureFlags/flags/tokenSafety'
|
import { TokenSafetyVariant, useTokenSafetyFlag } from 'featureFlags/flags/tokenSafety'
|
||||||
import usePrevious from 'hooks/usePrevious'
|
import usePrevious from 'hooks/usePrevious'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { memo, useCallback, useEffect, useState } from 'react'
|
||||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||||
import { useUserAddedTokens } from 'state/user/hooks'
|
import { useUserAddedTokens } from 'state/user/hooks'
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ export enum CurrencyModalView {
|
|||||||
tokenSafety,
|
tokenSafety,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CurrencySearchModal({
|
export default memo(function CurrencySearchModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onDismiss,
|
onDismiss,
|
||||||
onCurrencySelect,
|
onCurrencySelect,
|
||||||
@ -170,4 +170,4 @@ export default function CurrencySearchModal({
|
|||||||
{content}
|
{content}
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
@ -7,7 +7,7 @@ import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
|
|||||||
import ms from 'ms.macro'
|
import ms from 'ms.macro'
|
||||||
import { darken } from 'polished'
|
import { darken } from 'polished'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useSetUserSlippageTolerance, useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
|
import { useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
|
||||||
import styled, { useTheme } from 'styled-components/macro'
|
import styled, { useTheme } from 'styled-components/macro'
|
||||||
|
|
||||||
import { ThemedText } from '../../theme'
|
import { ThemedText } from '../../theme'
|
||||||
@ -112,8 +112,7 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
|
|||||||
const redesignFlag = useRedesignFlag()
|
const redesignFlag = useRedesignFlag()
|
||||||
const redesignFlagEnabled = redesignFlag === RedesignVariant.Enabled
|
const redesignFlagEnabled = redesignFlag === RedesignVariant.Enabled
|
||||||
|
|
||||||
const userSlippageTolerance = useUserSlippageTolerance()
|
const [userSlippageTolerance, setUserSlippageTolerance] = useUserSlippageTolerance()
|
||||||
const setUserSlippageTolerance = useSetUserSlippageTolerance()
|
|
||||||
|
|
||||||
const [deadline, setDeadline] = useUserTransactionTTL()
|
const [deadline, setDeadline] = useUserTransactionTTL()
|
||||||
|
|
||||||
|
49
src/components/Widget/index.tsx
Normal file
49
src/components/Widget/index.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { Currency, SwapWidget } from '@uniswap/widgets'
|
||||||
|
import { useWeb3React } from '@web3-react/core'
|
||||||
|
import { RPC_URLS } from 'constants/networks'
|
||||||
|
import { useActiveLocale } from 'hooks/useActiveLocale'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useIsDarkMode } from 'state/user/hooks'
|
||||||
|
import { DARK_THEME, LIGHT_THEME } from 'theme/widget'
|
||||||
|
|
||||||
|
import { useSyncWidgetInputs } from './inputs'
|
||||||
|
import { useSyncWidgetSettings } from './settings'
|
||||||
|
import { useSyncWidgetTransactions } from './transactions'
|
||||||
|
|
||||||
|
export const WIDGET_WIDTH = 320
|
||||||
|
|
||||||
|
const WIDGET_ROUTER_URL = 'https://api.uniswap.org/v1/'
|
||||||
|
|
||||||
|
export interface WidgetProps {
|
||||||
|
defaultToken?: Currency
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Widget({ defaultToken }: WidgetProps) {
|
||||||
|
const locale = useActiveLocale()
|
||||||
|
const darkMode = useIsDarkMode()
|
||||||
|
const theme = useMemo(() => (darkMode ? DARK_THEME : LIGHT_THEME), [darkMode])
|
||||||
|
const { provider } = useWeb3React()
|
||||||
|
|
||||||
|
const { inputs, tokenSelector } = useSyncWidgetInputs(defaultToken)
|
||||||
|
const { settings } = useSyncWidgetSettings()
|
||||||
|
const { transactions } = useSyncWidgetTransactions()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SwapWidget
|
||||||
|
hideConnectionUI
|
||||||
|
jsonRpcUrlMap={RPC_URLS}
|
||||||
|
routerUrl={WIDGET_ROUTER_URL}
|
||||||
|
width={WIDGET_WIDTH}
|
||||||
|
locale={locale}
|
||||||
|
theme={theme}
|
||||||
|
// defaultChainId is excluded - it is always inferred from the passed provider
|
||||||
|
provider={provider}
|
||||||
|
{...inputs}
|
||||||
|
{...settings}
|
||||||
|
{...transactions}
|
||||||
|
/>
|
||||||
|
{tokenSelector}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
88
src/components/Widget/inputs.tsx
Normal file
88
src/components/Widget/inputs.tsx
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { Currency, Field, SwapController, SwapEventHandlers, TradeType } from '@uniswap/widgets'
|
||||||
|
import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal'
|
||||||
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integrates the Widget's inputs.
|
||||||
|
* Treats the Widget as a controlled component, using the app's own token selector for selection.
|
||||||
|
*/
|
||||||
|
export function useSyncWidgetInputs(defaultToken?: Currency) {
|
||||||
|
const [type, setType] = useState(TradeType.EXACT_INPUT)
|
||||||
|
const [amount, setAmount] = useState<string>()
|
||||||
|
const onAmountChange = useCallback((field: Field, amount: string) => {
|
||||||
|
setType(toTradeType(field))
|
||||||
|
setAmount(amount)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const [tokens, setTokens] = useState<{ [Field.INPUT]?: Currency; [Field.OUTPUT]?: Currency }>({
|
||||||
|
[Field.OUTPUT]: defaultToken,
|
||||||
|
})
|
||||||
|
const onSwitchTokens = useCallback(() => {
|
||||||
|
setType((type) => invertTradeType(type))
|
||||||
|
setTokens((tokens) => ({
|
||||||
|
[Field.INPUT]: tokens[Field.OUTPUT],
|
||||||
|
[Field.OUTPUT]: tokens[Field.INPUT],
|
||||||
|
}))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const [selectingField, setSelectingField] = useState<Field>()
|
||||||
|
const otherField = useMemo(() => (selectingField === Field.INPUT ? Field.OUTPUT : Field.INPUT), [selectingField])
|
||||||
|
const [selectingToken, otherToken] = useMemo(() => {
|
||||||
|
if (selectingField === undefined) return [undefined, undefined]
|
||||||
|
return [tokens[selectingField], tokens[otherField]]
|
||||||
|
}, [otherField, selectingField, tokens])
|
||||||
|
const onTokenSelectorClick = useCallback((field: Field) => {
|
||||||
|
setSelectingField(field)
|
||||||
|
return false
|
||||||
|
}, [])
|
||||||
|
const onTokenSelect = useCallback(
|
||||||
|
(token: Currency) => {
|
||||||
|
if (selectingField === undefined) return
|
||||||
|
setType(TradeType.EXACT_INPUT)
|
||||||
|
setTokens(() => {
|
||||||
|
return {
|
||||||
|
[otherField]: token === otherToken ? selectingToken : otherToken,
|
||||||
|
[selectingField]: token,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[otherField, otherToken, selectingField, selectingToken]
|
||||||
|
)
|
||||||
|
const tokenSelector = (
|
||||||
|
<CurrencySearchModal
|
||||||
|
isOpen={selectingField !== undefined}
|
||||||
|
onDismiss={() => setSelectingField(undefined)}
|
||||||
|
selectedCurrency={selectingToken}
|
||||||
|
otherSelectedCurrency={otherToken}
|
||||||
|
onCurrencySelect={onTokenSelect}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
const value: SwapController = useMemo(() => ({ type, amount, ...tokens }), [amount, tokens, type])
|
||||||
|
const valueHandlers: SwapEventHandlers = useMemo(
|
||||||
|
() => ({ onAmountChange, onSwitchTokens, onTokenSelectorClick }),
|
||||||
|
[onAmountChange, onSwitchTokens, onTokenSelectorClick]
|
||||||
|
)
|
||||||
|
|
||||||
|
return { inputs: { value, ...valueHandlers }, tokenSelector }
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(zzmp): Move to @uniswap/widgets.
|
||||||
|
function toTradeType(modifiedField: Field) {
|
||||||
|
switch (modifiedField) {
|
||||||
|
case Field.INPUT:
|
||||||
|
return TradeType.EXACT_INPUT
|
||||||
|
case Field.OUTPUT:
|
||||||
|
return TradeType.EXACT_OUTPUT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(zzmp): Include in @uniswap/sdk-core (on TradeType, if possible).
|
||||||
|
function invertTradeType(tradeType: TradeType) {
|
||||||
|
switch (tradeType) {
|
||||||
|
case TradeType.EXACT_INPUT:
|
||||||
|
return TradeType.EXACT_OUTPUT
|
||||||
|
case TradeType.EXACT_OUTPUT:
|
||||||
|
return TradeType.EXACT_INPUT
|
||||||
|
}
|
||||||
|
}
|
57
src/components/Widget/settings.ts
Normal file
57
src/components/Widget/settings.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { Percent } from '@uniswap/sdk-core'
|
||||||
|
import { Slippage, SwapEventHandlers, SwapSettingsController } from '@uniswap/widgets'
|
||||||
|
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
|
||||||
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
|
import { useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integrates the Widget's settings, keeping the widget and app settings in sync.
|
||||||
|
* NB: This acts as an integration layer, so certain values are duplicated in order to translate
|
||||||
|
* between app and widget representations.
|
||||||
|
*/
|
||||||
|
export function useSyncWidgetSettings() {
|
||||||
|
const [appTtl, setAppTtl] = useUserTransactionTTL()
|
||||||
|
const [widgetTtl, setWidgetTtl] = useState<number | undefined>(appTtl / 60)
|
||||||
|
const onTransactionDeadlineChange = useCallback(
|
||||||
|
(widgetTtl: number | undefined) => {
|
||||||
|
setWidgetTtl(widgetTtl)
|
||||||
|
const appTtl = widgetTtl === undefined ? widgetTtl : widgetTtl * 60
|
||||||
|
setAppTtl(appTtl ?? DEFAULT_DEADLINE_FROM_NOW)
|
||||||
|
},
|
||||||
|
[setAppTtl]
|
||||||
|
)
|
||||||
|
|
||||||
|
const [appSlippage, setAppSlippage] = useUserSlippageTolerance()
|
||||||
|
const [widgetSlippage, setWidgetSlippage] = useState<string | undefined>(
|
||||||
|
appSlippage === 'auto' ? undefined : appSlippage.toFixed(2)
|
||||||
|
)
|
||||||
|
const onSlippageChange = useCallback(
|
||||||
|
(widgetSlippage: Slippage) => {
|
||||||
|
setWidgetSlippage(widgetSlippage.max)
|
||||||
|
if (widgetSlippage.auto || !widgetSlippage.max) {
|
||||||
|
setAppSlippage('auto')
|
||||||
|
} else {
|
||||||
|
setAppSlippage(new Percent(Math.floor(Number(widgetSlippage.max) * 100), 10_000))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[setAppSlippage]
|
||||||
|
)
|
||||||
|
|
||||||
|
const onSettingsReset = useCallback(() => {
|
||||||
|
setWidgetTtl(undefined)
|
||||||
|
setAppTtl(DEFAULT_DEADLINE_FROM_NOW)
|
||||||
|
setWidgetSlippage(undefined)
|
||||||
|
setAppSlippage('auto')
|
||||||
|
}, [setAppSlippage, setAppTtl])
|
||||||
|
|
||||||
|
const settings: SwapSettingsController = useMemo(() => {
|
||||||
|
const auto = appSlippage === 'auto'
|
||||||
|
return { slippage: { auto, max: widgetSlippage }, transactionTtl: widgetTtl }
|
||||||
|
}, [widgetSlippage, widgetTtl, appSlippage])
|
||||||
|
const settingsHandlers: SwapEventHandlers = useMemo(
|
||||||
|
() => ({ onSettingsReset, onSlippageChange, onTransactionDeadlineChange }),
|
||||||
|
[onSettingsReset, onSlippageChange, onTransactionDeadlineChange]
|
||||||
|
)
|
||||||
|
|
||||||
|
return { settings: { settings, ...settingsHandlers } }
|
||||||
|
}
|
18
src/components/Widget/transactions.ts
Normal file
18
src/components/Widget/transactions.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { TransactionReceipt } from '@ethersproject/abstract-provider'
|
||||||
|
import { TransactionEventHandlers } from '@uniswap/widgets'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
|
/** Integrates the Widget's transactions, showing the widget's transactions in the app. */
|
||||||
|
export function useSyncWidgetTransactions() {
|
||||||
|
// TODO(jfrankfurt): Integrate widget transactions with app transaction tracking.
|
||||||
|
const txHandlers: TransactionEventHandlers = useMemo(
|
||||||
|
() => ({
|
||||||
|
onTxSubmit: (hash: string, tx: unknown) => console.log('onTxSubmit'),
|
||||||
|
onTxSuccess: (hash: string, receipt: TransactionReceipt) => console.log('onTxSuccess'),
|
||||||
|
onTxFail: (hash: string, receipt: TransactionReceipt) => console.log('onTxFail'),
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
return { transactions: { ...txHandlers } }
|
||||||
|
}
|
@ -119,7 +119,7 @@ export default function SwapModalFooter({
|
|||||||
swapQuoteReceivedDate: Date | undefined
|
swapQuoteReceivedDate: Date | undefined
|
||||||
}) {
|
}) {
|
||||||
const transactionDeadlineSecondsSinceEpoch = useTransactionDeadline()?.toNumber() // in seconds since epoch
|
const transactionDeadlineSecondsSinceEpoch = useTransactionDeadline()?.toNumber() // in seconds since epoch
|
||||||
const isAutoSlippage = useUserSlippageTolerance() === 'auto'
|
const isAutoSlippage = useUserSlippageTolerance()[0] === 'auto'
|
||||||
const [clientSideRouter] = useClientSideRouter()
|
const [clientSideRouter] = useClientSideRouter()
|
||||||
const tokenInAmountUsd = useStablecoinValue(trade.inputAmount)?.toFixed(2)
|
const tokenInAmountUsd = useStablecoinValue(trade.inputAmount)?.toFixed(2)
|
||||||
const tokenOutAmountUsd = useStablecoinValue(trade.outputAmount)?.toFixed(2)
|
const tokenOutAmountUsd = useStablecoinValue(trade.outputAmount)?.toFixed(2)
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { SwapWidget } from '@uniswap/widgets'
|
|
||||||
import { useWeb3React } from '@web3-react/core'
|
import { useWeb3React } from '@web3-react/core'
|
||||||
import {
|
import {
|
||||||
LARGE_MEDIA_BREAKPOINT,
|
LARGE_MEDIA_BREAKPOINT,
|
||||||
@ -12,20 +11,16 @@ import LoadingTokenDetail from 'components/Tokens/TokenDetails/LoadingTokenDetai
|
|||||||
import NetworkBalance from 'components/Tokens/TokenDetails/NetworkBalance'
|
import NetworkBalance from 'components/Tokens/TokenDetails/NetworkBalance'
|
||||||
import TokenDetail from 'components/Tokens/TokenDetails/TokenDetail'
|
import TokenDetail from 'components/Tokens/TokenDetails/TokenDetail'
|
||||||
import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage'
|
import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage'
|
||||||
|
import Widget, { WIDGET_WIDTH } from 'components/Widget'
|
||||||
import { getChainInfo } from 'constants/chainInfo'
|
import { getChainInfo } from 'constants/chainInfo'
|
||||||
import { L1_CHAIN_IDS, L2_CHAIN_IDS, SupportedChainId, TESTNET_CHAIN_IDS } from 'constants/chains'
|
import { L1_CHAIN_IDS, L2_CHAIN_IDS, SupportedChainId, TESTNET_CHAIN_IDS } from 'constants/chains'
|
||||||
import { checkWarning } from 'constants/tokenSafety'
|
import { checkWarning } from 'constants/tokenSafety'
|
||||||
import { useToken } from 'hooks/Tokens'
|
import { useToken } from 'hooks/Tokens'
|
||||||
import { useActiveLocale } from 'hooks/useActiveLocale'
|
|
||||||
import { useNetworkTokenBalances } from 'hooks/useNetworkTokenBalances'
|
import { useNetworkTokenBalances } from 'hooks/useNetworkTokenBalances'
|
||||||
import { useCallback, useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import { useIsDarkMode } from 'state/user/hooks'
|
|
||||||
import styled from 'styled-components/macro'
|
import styled from 'styled-components/macro'
|
||||||
import { DARK_THEME, LIGHT_THEME } from 'theme/token-details-widget-theme'
|
|
||||||
import { ROUTER_URL, RPC_URL_MAP } from 'utils/token-details-widget-config'
|
|
||||||
|
|
||||||
const WIDGET_WIDTH = 320
|
|
||||||
const Footer = styled.div`
|
const Footer = styled.div`
|
||||||
display: none;
|
display: none;
|
||||||
@media only screen and (max-width: ${LARGE_MEDIA_BREAKPOINT}) {
|
@media only screen and (max-width: ${LARGE_MEDIA_BREAKPOINT}) {
|
||||||
@ -78,26 +73,13 @@ function NetworkBalances(tokenAddress: string) {
|
|||||||
|
|
||||||
export default function TokenDetails() {
|
export default function TokenDetails() {
|
||||||
const { tokenAddress } = useParams<{ tokenAddress?: string }>()
|
const { tokenAddress } = useParams<{ tokenAddress?: string }>()
|
||||||
const tokenSymbol = useToken(tokenAddress)?.symbol
|
const token = useToken(tokenAddress)
|
||||||
|
|
||||||
const darkMode = useIsDarkMode()
|
|
||||||
const widgetTheme = useMemo(() => (darkMode ? DARK_THEME : LIGHT_THEME), [darkMode])
|
|
||||||
const locale = useActiveLocale()
|
|
||||||
const onTxSubmit = useCallback(() => {
|
|
||||||
console.log('onTxSubmit')
|
|
||||||
}, [])
|
|
||||||
const onTxSuccess = useCallback(() => {
|
|
||||||
console.log('onTxSuccess')
|
|
||||||
}, [])
|
|
||||||
const onTxFail = useCallback(() => {
|
|
||||||
console.log('onTxFail')
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const tokenWarning = tokenAddress ? checkWarning(tokenAddress) : null
|
const tokenWarning = tokenAddress ? checkWarning(tokenAddress) : null
|
||||||
/* network balance handling */
|
/* network balance handling */
|
||||||
|
|
||||||
const { data: networkData } = tokenAddress ? NetworkBalances(tokenAddress) : { data: null }
|
const { data: networkData } = tokenAddress ? NetworkBalances(tokenAddress) : { data: null }
|
||||||
const { chainId: connectedChainId, provider } = useWeb3React()
|
const { chainId: connectedChainId } = useWeb3React()
|
||||||
const totalBalance = 4.3 // dummy data
|
const totalBalance = 4.3 // dummy data
|
||||||
|
|
||||||
const chainsToList = useMemo(() => {
|
const chainsToList = useMemo(() => {
|
||||||
@ -113,7 +95,7 @@ export default function TokenDetails() {
|
|||||||
? chainsToList.map((chainId) => {
|
? chainsToList.map((chainId) => {
|
||||||
const amount = networkData[chainId]
|
const amount = networkData[chainId]
|
||||||
const fiatValue = amount // for testing purposes
|
const fiatValue = amount // for testing purposes
|
||||||
if (!fiatValue || !tokenSymbol) return null
|
if (!fiatValue || !token?.symbol) return null
|
||||||
const chainInfo = getChainInfo(chainId)
|
const chainInfo = getChainInfo(chainId)
|
||||||
const networkColor = chainInfo.color
|
const networkColor = chainInfo.color
|
||||||
if (!chainInfo) return null
|
if (!chainInfo) return null
|
||||||
@ -122,7 +104,7 @@ export default function TokenDetails() {
|
|||||||
key={chainId}
|
key={chainId}
|
||||||
logoUrl={chainInfo.logoUrl}
|
logoUrl={chainInfo.logoUrl}
|
||||||
balance={'1'}
|
balance={'1'}
|
||||||
tokenSymbol={tokenSymbol}
|
tokenSymbol={token?.symbol}
|
||||||
fiatValue={fiatValue.toSignificant(2)}
|
fiatValue={fiatValue.toSignificant(2)}
|
||||||
label={chainInfo.label}
|
label={chainInfo.label}
|
||||||
networkColor={networkColor}
|
networkColor={networkColor}
|
||||||
@ -137,22 +119,7 @@ export default function TokenDetails() {
|
|||||||
<>
|
<>
|
||||||
<TokenDetail address={tokenAddress} />
|
<TokenDetail address={tokenAddress} />
|
||||||
<RightPanel>
|
<RightPanel>
|
||||||
<SwapWidget
|
<Widget defaultToken={token ?? undefined} />
|
||||||
defaultChainId={connectedChainId}
|
|
||||||
defaultInputTokenAddress={'NATIVE'}
|
|
||||||
defaultOutputTokenAddress={tokenAddress}
|
|
||||||
hideConnectionUI
|
|
||||||
jsonRpcUrlMap={RPC_URL_MAP}
|
|
||||||
locale={locale}
|
|
||||||
onTxSubmit={onTxSubmit}
|
|
||||||
onTxSuccess={onTxSuccess}
|
|
||||||
onTxFail={onTxFail}
|
|
||||||
provider={provider}
|
|
||||||
routerUrl={ROUTER_URL}
|
|
||||||
theme={widgetTheme}
|
|
||||||
// tokenList={[]}
|
|
||||||
width={WIDGET_WIDTH}
|
|
||||||
/>
|
|
||||||
{tokenWarning && <TokenSafetyMessage tokenAddress={tokenAddress} warning={tokenWarning} />}
|
{tokenWarning && <TokenSafetyMessage tokenAddress={tokenAddress} warning={tokenWarning} />}
|
||||||
<BalanceSummary address={tokenAddress} totalBalance={totalBalance} networkBalances={balancesByNetwork} />
|
<BalanceSummary address={tokenAddress} totalBalance={totalBalance} networkBalances={balancesByNetwork} />
|
||||||
</RightPanel>
|
</RightPanel>
|
||||||
|
@ -144,10 +144,20 @@ export function useClientSideRouter(): [boolean, (userClientSideRouter: boolean)
|
|||||||
return [clientSideRouter, setClientSideRouter]
|
return [clientSideRouter, setClientSideRouter]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSetUserSlippageTolerance(): (slippageTolerance: Percent | 'auto') => void {
|
/**
|
||||||
const dispatch = useAppDispatch()
|
* Return the user's slippage tolerance, from the redux store, and a function to update the slippage tolerance
|
||||||
|
*/
|
||||||
|
export function useUserSlippageTolerance(): [Percent | 'auto', (slippageTolerance: Percent | 'auto') => void] {
|
||||||
|
const userSlippageToleranceRaw = useAppSelector((state) => {
|
||||||
|
return state.user.userSlippageTolerance
|
||||||
|
})
|
||||||
|
const userSlippageTolerance = useMemo(
|
||||||
|
() => (userSlippageToleranceRaw === 'auto' ? 'auto' : new Percent(userSlippageToleranceRaw, 10_000)),
|
||||||
|
[userSlippageToleranceRaw]
|
||||||
|
)
|
||||||
|
|
||||||
return useCallback(
|
const dispatch = useAppDispatch()
|
||||||
|
const setUserSlippageTolerance = useCallback(
|
||||||
(userSlippageTolerance: Percent | 'auto') => {
|
(userSlippageTolerance: Percent | 'auto') => {
|
||||||
let value: 'auto' | number
|
let value: 'auto' | number
|
||||||
try {
|
try {
|
||||||
@ -164,19 +174,10 @@ export function useSetUserSlippageTolerance(): (slippageTolerance: Percent | 'au
|
|||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the user's slippage tolerance, from the redux store, and a function to update the slippage tolerance
|
|
||||||
*/
|
|
||||||
export function useUserSlippageTolerance(): Percent | 'auto' {
|
|
||||||
const userSlippageTolerance = useAppSelector((state) => {
|
|
||||||
return state.user.userSlippageTolerance
|
|
||||||
})
|
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => (userSlippageTolerance === 'auto' ? 'auto' : new Percent(userSlippageTolerance, 10_000)),
|
() => [userSlippageTolerance, setUserSlippageTolerance],
|
||||||
[userSlippageTolerance]
|
[setUserSlippageTolerance, userSlippageTolerance]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +201,7 @@ export function useUserHideClosedPositions(): [boolean, (newHideClosedPositions:
|
|||||||
* @param defaultSlippageTolerance the default value to replace auto with
|
* @param defaultSlippageTolerance the default value to replace auto with
|
||||||
*/
|
*/
|
||||||
export function useUserSlippageToleranceWithDefault(defaultSlippageTolerance: Percent): Percent {
|
export function useUserSlippageToleranceWithDefault(defaultSlippageTolerance: Percent): Percent {
|
||||||
const allowedSlippage = useUserSlippageTolerance()
|
const allowedSlippage = useUserSlippageTolerance()[0]
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => (allowedSlippage === 'auto' ? defaultSlippageTolerance : allowedSlippage),
|
() => (allowedSlippage === 'auto' ? defaultSlippageTolerance : allowedSlippage),
|
||||||
[allowedSlippage, defaultSlippageTolerance]
|
[allowedSlippage, defaultSlippageTolerance]
|
||||||
|
@ -17,6 +17,7 @@ export const LIGHT_THEME = {
|
|||||||
warning: colorsLight.accentWarning,
|
warning: colorsLight.accentWarning,
|
||||||
error: colorsLight.accentCritical,
|
error: colorsLight.accentCritical,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DARK_THEME = {
|
export const DARK_THEME = {
|
||||||
// surface
|
// surface
|
||||||
container: colorsDark.backgroundSurface,
|
container: colorsDark.backgroundSurface,
|
@ -1,8 +0,0 @@
|
|||||||
import { SupportedChainId } from 'constants/chains'
|
|
||||||
import { RPC_URLS } from 'constants/networks'
|
|
||||||
|
|
||||||
export const ROUTER_URL = 'https://api.uniswap.org/v1/'
|
|
||||||
export const RPC_URL_MAP = Object.keys(RPC_URLS).reduce(
|
|
||||||
(acc, cur) => ({ ...acc, [cur]: [RPC_URLS[cur as unknown as SupportedChainId]] }),
|
|
||||||
{}
|
|
||||||
)
|
|
56
yarn.lock
56
yarn.lock
@ -1074,7 +1074,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||||
version "7.18.9"
|
version "7.18.9"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
|
||||||
integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
|
integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
|
||||||
@ -3678,7 +3678,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-redux@^7.1.24":
|
"@types/react-redux@^7.1.20", "@types/react-redux@^7.1.24":
|
||||||
version "7.1.24"
|
version "7.1.24"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.24.tgz#6caaff1603aba17b27d20f8ad073e4c077e975c0"
|
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.24.tgz#6caaff1603aba17b27d20f8ad073e4c077e975c0"
|
||||||
integrity sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==
|
integrity sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==
|
||||||
@ -4212,10 +4212,10 @@
|
|||||||
"@uniswap/v3-core" "1.0.0"
|
"@uniswap/v3-core" "1.0.0"
|
||||||
"@uniswap/v3-periphery" "^1.0.1"
|
"@uniswap/v3-periphery" "^1.0.1"
|
||||||
|
|
||||||
"@uniswap/widgets@^2.1.1":
|
"@uniswap/widgets@^2.3.1":
|
||||||
version "2.1.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@uniswap/widgets/-/widgets-2.1.1.tgz#13aea0255b7516d86de6838d4de2507a9f84e0df"
|
resolved "https://registry.yarnpkg.com/@uniswap/widgets/-/widgets-2.3.1.tgz#e9973cb34d19534ccf7a7dbe79de63bc5fe7e1e9"
|
||||||
integrity sha512-ZtJhZni1t1tM0ZsAu5INFDMa588RmRkXby8DAOou/RXz3dzh7jTKm7+wEph6QE9NE5eTdVvVgqbwH6QPLR3nuQ==
|
integrity sha512-b6eL/fIjAkj52dj0TVcVF5FeQ1r3dAtPNoO4Go4zABVHlj6c4m6dariY3VQBkNnd2vwt40XBELtiuYXZn3DGjA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.17.0"
|
"@babel/runtime" "^7.17.0"
|
||||||
"@fontsource/ibm-plex-mono" "^4.5.1"
|
"@fontsource/ibm-plex-mono" "^4.5.1"
|
||||||
@ -4251,12 +4251,17 @@
|
|||||||
polished "^3.3.2"
|
polished "^3.3.2"
|
||||||
popper-max-size-modifier "^0.2.0"
|
popper-max-size-modifier "^0.2.0"
|
||||||
qrcode "^1.5.0"
|
qrcode "^1.5.0"
|
||||||
|
react "^17.0.1"
|
||||||
|
react-dom "^17.0.1"
|
||||||
react-feather "^2.0.8"
|
react-feather "^2.0.8"
|
||||||
react-popper "^2.2.3"
|
react-popper "^2.2.3"
|
||||||
|
react-redux "^7.2.2"
|
||||||
react-virtualized-auto-sizer "^1.0.2"
|
react-virtualized-auto-sizer "^1.0.2"
|
||||||
react-window "^1.8.5"
|
react-window "^1.8.5"
|
||||||
rebass "^4.0.7"
|
rebass "^4.0.7"
|
||||||
|
redux "^4.1.2"
|
||||||
setimmediate "^1.0.5"
|
setimmediate "^1.0.5"
|
||||||
|
styled-components "^5.3.0"
|
||||||
tiny-invariant "^1.2.0"
|
tiny-invariant "^1.2.0"
|
||||||
wcag-contrast "^3.0.0"
|
wcag-contrast "^3.0.0"
|
||||||
wicg-inert "^3.1.1"
|
wicg-inert "^3.1.1"
|
||||||
@ -14663,6 +14668,15 @@ react-dev-utils@^11.0.3:
|
|||||||
strip-ansi "6.0.0"
|
strip-ansi "6.0.0"
|
||||||
text-table "0.2.0"
|
text-table "0.2.0"
|
||||||
|
|
||||||
|
react-dom@^17.0.1:
|
||||||
|
version "17.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||||
|
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
scheduler "^0.20.2"
|
||||||
|
|
||||||
react-dom@^18.2.0:
|
react-dom@^18.2.0:
|
||||||
version "18.2.0"
|
version "18.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
||||||
@ -14758,6 +14772,18 @@ react-query@^3.39.1:
|
|||||||
broadcast-channel "^3.4.1"
|
broadcast-channel "^3.4.1"
|
||||||
match-sorter "^6.0.2"
|
match-sorter "^6.0.2"
|
||||||
|
|
||||||
|
react-redux@^7.2.2:
|
||||||
|
version "7.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.8.tgz#a894068315e65de5b1b68899f9c6ee0923dd28de"
|
||||||
|
integrity sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.15.4"
|
||||||
|
"@types/react-redux" "^7.1.20"
|
||||||
|
hoist-non-react-statics "^3.3.2"
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
react-is "^17.0.2"
|
||||||
|
|
||||||
react-redux@^8.0.2:
|
react-redux@^8.0.2:
|
||||||
version "8.0.2"
|
version "8.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.2.tgz#bc2a304bb21e79c6808e3e47c50fe1caf62f7aad"
|
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.2.tgz#bc2a304bb21e79c6808e3e47c50fe1caf62f7aad"
|
||||||
@ -14926,6 +14952,14 @@ react-window@^1.8.5:
|
|||||||
"@babel/runtime" "^7.0.0"
|
"@babel/runtime" "^7.0.0"
|
||||||
memoize-one ">=3.1.1 <6"
|
memoize-one ">=3.1.1 <6"
|
||||||
|
|
||||||
|
react@^17.0.1:
|
||||||
|
version "17.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
|
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
react@^18.2.0:
|
react@^18.2.0:
|
||||||
version "18.2.0"
|
version "18.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||||
@ -15621,6 +15655,14 @@ saxes@^5.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
xmlchars "^2.2.0"
|
xmlchars "^2.2.0"
|
||||||
|
|
||||||
|
scheduler@^0.20.2:
|
||||||
|
version "0.20.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
|
||||||
|
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
scheduler@^0.23.0:
|
scheduler@^0.23.0:
|
||||||
version "0.23.0"
|
version "0.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
||||||
@ -16426,7 +16468,7 @@ style-loader@1.3.0:
|
|||||||
loader-utils "^2.0.0"
|
loader-utils "^2.0.0"
|
||||||
schema-utils "^2.7.0"
|
schema-utils "^2.7.0"
|
||||||
|
|
||||||
styled-components@^5.3.5:
|
styled-components@^5.3.0, styled-components@^5.3.5:
|
||||||
version "5.3.5"
|
version "5.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.5.tgz#a750a398d01f1ca73af16a241dec3da6deae5ec4"
|
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.5.tgz#a750a398d01f1ca73af16a241dec3da6deae5ec4"
|
||||||
integrity sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg==
|
integrity sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg==
|
||||||
|
Loading…
Reference in New Issue
Block a user