Commit fc34912b authored by Moody Salem's avatar Moody Salem Committed by GitHub

feat(swap): default 1 native to usdc on the swap page (#3347)

* feat(swap): default 1 eth to usdc on the swap page

* fix unit tests

* fix tests

* fix the issue better

* use the token list logo

* fix integration tests for swap and add one for eth/usdc

* address comments
parent c25d2b89
...@@ -2,16 +2,27 @@ describe('Swap', () => { ...@@ -2,16 +2,27 @@ describe('Swap', () => {
beforeEach(() => { beforeEach(() => {
cy.visit('/swap') cy.visit('/swap')
}) })
it('starts with an ETH/USDC swap and quotes it', () => {
cy.get('#swap-currency-input .token-amount-input').should('have.value', '1')
cy.get('#swap-currency-input .token-symbol-container').should('contain.text', 'ETH')
cy.get('#swap-currency-output .token-amount-input').should('not.have.value', '')
cy.get('#swap-currency-output .token-symbol-container').should('contain.text', 'USDC')
})
it('can enter an amount into input', () => { it('can enter an amount into input', () => {
cy.get('#swap-currency-input .token-amount-input').type('0.001', { delay: 200 }).should('have.value', '0.001') cy.get('#swap-currency-input .token-amount-input')
.clear()
.type('0.001', { delay: 200 })
.should('have.value', '0.001')
}) })
it('zero swap amount', () => { it('zero swap amount', () => {
cy.get('#swap-currency-input .token-amount-input').type('0.0', { delay: 200 }).should('have.value', '0.0') cy.get('#swap-currency-input .token-amount-input').clear().type('0.0', { delay: 200 }).should('have.value', '0.0')
}) })
it('invalid swap amount', () => { it('invalid swap amount', () => {
cy.get('#swap-currency-input .token-amount-input').type('\\', { delay: 200 }).should('have.value', '') cy.get('#swap-currency-input .token-amount-input').clear().type('\\', { delay: 200 }).should('have.value', '')
}) })
it('can enter an amount into output', () => { it('can enter an amount into output', () => {
......
...@@ -19,7 +19,7 @@ import { PositionDetails } from 'types/position' ...@@ -19,7 +19,7 @@ import { PositionDetails } from 'types/position'
import { formatTickPrice } from 'utils/formatTickPrice' import { formatTickPrice } from 'utils/formatTickPrice'
import { unwrappedToken } from 'utils/unwrappedToken' import { unwrappedToken } from 'utils/unwrappedToken'
import { DAI, USDC, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens' import { DAI, USDC_MAINNET, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
const LinkRow = styled(Link)` const LinkRow = styled(Link)`
align-items: center; align-items: center;
...@@ -145,7 +145,7 @@ export function getPriceOrderingFromPositionForUI(position?: Position): { ...@@ -145,7 +145,7 @@ export function getPriceOrderingFromPositionForUI(position?: Position): {
const token1 = position.amount1.currency const token1 = position.amount1.currency
// if token0 is a dollar-stable asset, set it as the quote token // if token0 is a dollar-stable asset, set it as the quote token
const stables = [DAI, USDC, USDT] const stables = [DAI, USDC_MAINNET, USDT]
if (stables.some((stable) => stable.equals(token0))) { if (stables.some((stable) => stable.equals(token0))) {
return { return {
priceLower: position.token0PriceUpper.invert(), priceLower: position.token0PriceUpper.invert(),
......
import { Protocol } from '@uniswap/router-sdk' import { Protocol } from '@uniswap/router-sdk'
import { Currency, Percent } from '@uniswap/sdk-core' import { Currency, Percent } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk' import { FeeAmount } from '@uniswap/v3-sdk'
import { DAI, USDC, WBTC } from 'constants/tokens' import { DAI, USDC_MAINNET, WBTC } from 'constants/tokens'
import { render } from 'test-utils' import { render } from 'test-utils'
import RoutingDiagram, { RoutingDiagramEntry } from './RoutingDiagram' import RoutingDiagram, { RoutingDiagramEntry } from './RoutingDiagram'
...@@ -10,16 +10,16 @@ const percent = (strings: TemplateStringsArray) => new Percent(parseInt(strings[ ...@@ -10,16 +10,16 @@ const percent = (strings: TemplateStringsArray) => new Percent(parseInt(strings[
const singleRoute: RoutingDiagramEntry = { const singleRoute: RoutingDiagramEntry = {
percent: percent`100`, percent: percent`100`,
path: [[USDC, DAI, FeeAmount.LOW]], path: [[USDC_MAINNET, DAI, FeeAmount.LOW]],
protocol: Protocol.V3, protocol: Protocol.V3,
} }
const multiRoute: RoutingDiagramEntry[] = [ const multiRoute: RoutingDiagramEntry[] = [
{ percent: percent`75`, path: [[USDC, DAI, FeeAmount.LOWEST]], protocol: Protocol.V2 }, { percent: percent`75`, path: [[USDC_MAINNET, DAI, FeeAmount.LOWEST]], protocol: Protocol.V2 },
{ {
percent: percent`25`, percent: percent`25`,
path: [ path: [
[USDC, WBTC, FeeAmount.MEDIUM], [USDC_MAINNET, WBTC, FeeAmount.MEDIUM],
[WBTC, DAI, FeeAmount.HIGH], [WBTC, DAI, FeeAmount.HIGH],
], ],
protocol: Protocol.V3, protocol: Protocol.V3,
...@@ -47,16 +47,16 @@ jest.mock('hooks/useTokenInfoFromActiveList', () => ({ ...@@ -47,16 +47,16 @@ jest.mock('hooks/useTokenInfoFromActiveList', () => ({
})) }))
it('renders when no routes are provided', () => { it('renders when no routes are provided', () => {
const { asFragment } = render(<RoutingDiagram currencyIn={DAI} currencyOut={USDC} routes={[]} />) const { asFragment } = render(<RoutingDiagram currencyIn={DAI} currencyOut={USDC_MAINNET} routes={[]} />)
expect(asFragment()).toMatchSnapshot() expect(asFragment()).toMatchSnapshot()
}) })
it('renders single route', () => { it('renders single route', () => {
const { asFragment } = render(<RoutingDiagram currencyIn={USDC} currencyOut={DAI} routes={[singleRoute]} />) const { asFragment } = render(<RoutingDiagram currencyIn={USDC_MAINNET} currencyOut={DAI} routes={[singleRoute]} />)
expect(asFragment()).toMatchSnapshot() expect(asFragment()).toMatchSnapshot()
}) })
it('renders multi route', () => { it('renders multi route', () => {
const { asFragment } = render(<RoutingDiagram currencyIn={USDC} currencyOut={DAI} routes={multiRoute} />) const { asFragment } = render(<RoutingDiagram currencyIn={USDC_MAINNET} currencyOut={DAI} routes={multiRoute} />)
expect(asFragment()).toMatchSnapshot() expect(asFragment()).toMatchSnapshot()
}) })
...@@ -18,8 +18,8 @@ import { ...@@ -18,8 +18,8 @@ import {
sETH2, sETH2,
SWISE, SWISE,
TRIBE, TRIBE,
USDC,
USDC_ARBITRUM, USDC_ARBITRUM,
USDC_MAINNET,
USDC_OPTIMISM, USDC_OPTIMISM,
USDC_POLYGON, USDC_POLYGON,
USDT, USDT,
...@@ -50,7 +50,13 @@ const WRAPPED_NATIVE_CURRENCIES_ONLY: ChainTokenList = Object.fromEntries( ...@@ -50,7 +50,13 @@ const WRAPPED_NATIVE_CURRENCIES_ONLY: ChainTokenList = Object.fromEntries(
// used to construct intermediary pairs for trading // used to construct intermediary pairs for trading
export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = { export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = {
...WRAPPED_NATIVE_CURRENCIES_ONLY, ...WRAPPED_NATIVE_CURRENCIES_ONLY,
[SupportedChainId.MAINNET]: [...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC], [SupportedChainId.MAINNET]: [
...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.MAINNET],
DAI,
USDC_MAINNET,
USDT,
WBTC,
],
[SupportedChainId.OPTIMISM]: [ [SupportedChainId.OPTIMISM]: [
...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.OPTIMISM], ...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.OPTIMISM],
DAI_OPTIMISM, DAI_OPTIMISM,
...@@ -101,7 +107,7 @@ export const COMMON_BASES: ChainCurrencyList = { ...@@ -101,7 +107,7 @@ export const COMMON_BASES: ChainCurrencyList = {
[SupportedChainId.MAINNET]: [ [SupportedChainId.MAINNET]: [
nativeOnChain(SupportedChainId.MAINNET), nativeOnChain(SupportedChainId.MAINNET),
DAI, DAI,
USDC, USDC_MAINNET,
USDT, USDT,
WBTC, WBTC,
WRAPPED_NATIVE_CURRENCY[SupportedChainId.MAINNET], WRAPPED_NATIVE_CURRENCY[SupportedChainId.MAINNET],
...@@ -154,7 +160,13 @@ export const COMMON_BASES: ChainCurrencyList = { ...@@ -154,7 +160,13 @@ export const COMMON_BASES: ChainCurrencyList = {
// used to construct the list of all pairs we consider by default in the frontend // used to construct the list of all pairs we consider by default in the frontend
export const BASES_TO_TRACK_LIQUIDITY_FOR: ChainTokenList = { export const BASES_TO_TRACK_LIQUIDITY_FOR: ChainTokenList = {
...WRAPPED_NATIVE_CURRENCIES_ONLY, ...WRAPPED_NATIVE_CURRENCIES_ONLY,
[SupportedChainId.MAINNET]: [...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC], [SupportedChainId.MAINNET]: [
...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.MAINNET],
DAI,
USDC_MAINNET,
USDT,
WBTC,
],
} }
export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = { export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = {
[SupportedChainId.MAINNET]: [ [SupportedChainId.MAINNET]: [
...@@ -168,7 +180,7 @@ export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = { ...@@ -168,7 +180,7 @@ export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = {
'Compound USD Coin' 'Compound USD Coin'
), ),
], ],
[USDC, USDT], [USDC_MAINNET, USDT],
[DAI, USDT], [DAI, USDT],
], ],
} }
import { Currency, Ether, NativeCurrency, Token, WETH9 } from '@uniswap/sdk-core' import { Currency, Ether, NativeCurrency, Token, WETH9 } from '@uniswap/sdk-core'
import {
USDC_ARBITRUM,
USDC_ARBITRUM_RINKEBY,
USDC_GÖRLI,
USDC_KOVAN,
USDC_MAINNET,
USDC_OPTIMISM,
USDC_OPTIMISTIC_KOVAN,
USDC_POLYGON,
USDC_POLYGON_MUMBAI,
USDC_RINKEBY,
USDC_ROPSTEN,
} from '@uniswap/smart-order-router'
import { UNI_ADDRESS } from './addresses' import { UNI_ADDRESS } from './addresses'
import { SupportedChainId } from './chains' import { SupportedChainId } from './chains'
export { USDC_ARBITRUM, USDC_MAINNET, USDC_OPTIMISM, USDC_POLYGON }
export const AMPL = new Token( export const AMPL = new Token(
SupportedChainId.MAINNET, SupportedChainId.MAINNET,
'0xD46bA6D942050d489DBd938a2C909A5d5039A161', '0xD46bA6D942050d489DBd938a2C909A5d5039A161',
...@@ -31,27 +46,19 @@ export const DAI_OPTIMISM = new Token( ...@@ -31,27 +46,19 @@ export const DAI_OPTIMISM = new Token(
'DAI', 'DAI',
'Dai stable coin' 'Dai stable coin'
) )
export const USDC = new Token( export const USDC: { [chainId in SupportedChainId]: Token } = {
SupportedChainId.MAINNET, [SupportedChainId.MAINNET]: USDC_MAINNET,
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', [SupportedChainId.ARBITRUM_ONE]: USDC_ARBITRUM,
6, [SupportedChainId.OPTIMISM]: USDC_OPTIMISM,
'USDC', [SupportedChainId.ARBITRUM_RINKEBY]: USDC_ARBITRUM_RINKEBY,
'USD//C' [SupportedChainId.OPTIMISTIC_KOVAN]: USDC_OPTIMISTIC_KOVAN,
) [SupportedChainId.POLYGON]: USDC_POLYGON,
export const USDC_ARBITRUM = new Token( [SupportedChainId.POLYGON_MUMBAI]: USDC_POLYGON_MUMBAI,
SupportedChainId.ARBITRUM_ONE, [SupportedChainId.GOERLI]: USDC_GÖRLI,
'0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', [SupportedChainId.RINKEBY]: USDC_RINKEBY,
6, [SupportedChainId.KOVAN]: USDC_KOVAN,
'USDC', [SupportedChainId.ROPSTEN]: USDC_ROPSTEN,
'USD//C' }
)
export const USDC_POLYGON = new Token(
SupportedChainId.POLYGON,
'0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
6,
'USDC',
'USD//C'
)
export const DAI_POLYGON = new Token( export const DAI_POLYGON = new Token(
SupportedChainId.POLYGON, SupportedChainId.POLYGON,
'0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063',
...@@ -73,13 +80,6 @@ export const WBTC_POLYGON = new Token( ...@@ -73,13 +80,6 @@ export const WBTC_POLYGON = new Token(
'WBTC', 'WBTC',
'Wrapped BTC' 'Wrapped BTC'
) )
export const USDC_OPTIMISM = new Token(
SupportedChainId.OPTIMISM,
'0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
6,
'USDC',
'USD//C'
)
export const USDT = new Token( export const USDT = new Token(
SupportedChainId.MAINNET, SupportedChainId.MAINNET,
'0xdAC17F958D2ee523a2206206994597C13D831ec7', '0xdAC17F958D2ee523a2206206994597C13D831ec7',
...@@ -296,3 +296,19 @@ export function nativeOnChain(chainId: number): NativeCurrency { ...@@ -296,3 +296,19 @@ export function nativeOnChain(chainId: number): NativeCurrency {
: ExtendedEther.onChain(chainId)) : ExtendedEther.onChain(chainId))
) )
} }
export const TOKEN_SHORTHANDS: { [shorthand: string]: { [chainId in SupportedChainId]?: string } } = {
USDC: {
[SupportedChainId.MAINNET]: USDC_MAINNET.address,
[SupportedChainId.ARBITRUM_ONE]: USDC_ARBITRUM.address,
[SupportedChainId.OPTIMISM]: USDC_OPTIMISM.address,
[SupportedChainId.ARBITRUM_RINKEBY]: USDC_ARBITRUM_RINKEBY.address,
[SupportedChainId.OPTIMISTIC_KOVAN]: USDC_OPTIMISTIC_KOVAN.address,
[SupportedChainId.POLYGON]: USDC_POLYGON.address,
[SupportedChainId.POLYGON_MUMBAI]: USDC_POLYGON_MUMBAI.address,
[SupportedChainId.GOERLI]: USDC_GÖRLI.address,
[SupportedChainId.RINKEBY]: USDC_RINKEBY.address,
[SupportedChainId.KOVAN]: USDC_KOVAN.address,
[SupportedChainId.ROPSTEN]: USDC_ROPSTEN.address,
},
}
...@@ -2,7 +2,7 @@ import { Currency, Token } from '@uniswap/sdk-core' ...@@ -2,7 +2,7 @@ import { Currency, Token } from '@uniswap/sdk-core'
import { CHAIN_INFO } from 'constants/chainInfo' import { CHAIN_INFO } from 'constants/chainInfo'
import { L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from 'constants/chains' import { L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React' import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useCurrencyFromMap, useTokenFromMap } from 'lib/hooks/useCurrency' import { useCurrencyFromMap, useTokenFromMapOrNetwork } from 'lib/hooks/useCurrency'
import { getTokenFilter } from 'lib/hooks/useTokenList/filtering' import { getTokenFilter } from 'lib/hooks/useTokenList/filtering'
import { useMemo } from 'react' import { useMemo } from 'react'
...@@ -159,7 +159,7 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool ...@@ -159,7 +159,7 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool
// otherwise returns the token // otherwise returns the token
export function useToken(tokenAddress?: string | null): Token | null | undefined { export function useToken(tokenAddress?: string | null): Token | null | undefined {
const tokens = useAllTokens() const tokens = useAllTokens()
return useTokenFromMap(tokens, tokenAddress) return useTokenFromMapOrNetwork(tokens, tokenAddress)
} }
export function useCurrency(currencyId?: string | null): Currency | null | undefined { export function useCurrency(currencyId?: string | null): Currency | null | undefined {
......
import { renderHook } from '@testing-library/react-hooks' import { renderHook } from '@testing-library/react-hooks'
import { CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { DAI, USDC } from 'constants/tokens' import { DAI, USDC_MAINNET } from 'constants/tokens'
import { TradeState } from 'state/routing/types' import { TradeState } from 'state/routing/types'
import { useRoutingAPITrade } from '../state/routing/useRoutingAPITrade' import { useRoutingAPITrade } from '../state/routing/useRoutingAPITrade'
...@@ -10,7 +10,7 @@ import { useClientSideV3Trade } from './useClientSideV3Trade' ...@@ -10,7 +10,7 @@ import { useClientSideV3Trade } from './useClientSideV3Trade'
import useDebounce from './useDebounce' import useDebounce from './useDebounce'
import useIsWindowVisible from './useIsWindowVisible' import useIsWindowVisible from './useIsWindowVisible'
const USDCAmount = CurrencyAmount.fromRawAmount(USDC, '10000') const USDCAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '10000')
const DAIAmount = CurrencyAmount.fromRawAmount(DAI, '10000') const DAIAmount = CurrencyAmount.fromRawAmount(DAI, '10000')
jest.mock('./useAutoRouterSupported') jest.mock('./useAutoRouterSupported')
...@@ -126,10 +126,10 @@ describe('#useBestV3Trade ExactOut', () => { ...@@ -126,10 +126,10 @@ describe('#useBestV3Trade ExactOut', () => {
expectRouterMock(TradeState.INVALID) expectRouterMock(TradeState.INVALID)
expectClientSideMock(TradeState.VALID) expectClientSideMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC)) const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, USDC) expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, USDC_MAINNET)
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC) expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined }) expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
}) })
...@@ -138,17 +138,17 @@ describe('#useBestV3Trade ExactOut', () => { ...@@ -138,17 +138,17 @@ describe('#useBestV3Trade ExactOut', () => {
expectRouterMock(TradeState.NO_ROUTE_FOUND) expectRouterMock(TradeState.NO_ROUTE_FOUND)
expectClientSideMock(TradeState.VALID) expectClientSideMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC)) const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, USDC) expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, USDC_MAINNET)
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC) expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined }) expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
}) })
describe('when routing api is in non-error state', () => { describe('when routing api is in non-error state', () => {
it('does not compute client side v3 trade if routing api is LOADING', () => { it('does not compute client side v3 trade if routing api is LOADING', () => {
expectRouterMock(TradeState.LOADING) expectRouterMock(TradeState.LOADING)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC)) const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined) expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.LOADING, trade: undefined }) expect(result.current).toEqual({ state: TradeState.LOADING, trade: undefined })
...@@ -157,7 +157,7 @@ describe('#useBestV3Trade ExactOut', () => { ...@@ -157,7 +157,7 @@ describe('#useBestV3Trade ExactOut', () => {
it('does not compute client side v3 trade if routing api is VALID', () => { it('does not compute client side v3 trade if routing api is VALID', () => {
expectRouterMock(TradeState.VALID) expectRouterMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC)) const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined) expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined }) expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
...@@ -166,7 +166,7 @@ describe('#useBestV3Trade ExactOut', () => { ...@@ -166,7 +166,7 @@ describe('#useBestV3Trade ExactOut', () => {
it('does not compute client side v3 trade if routing api is SYNCING', () => { it('does not compute client side v3 trade if routing api is SYNCING', () => {
expectRouterMock(TradeState.SYNCING) expectRouterMock(TradeState.SYNCING)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC)) const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined) expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.SYNCING, trade: undefined }) expect(result.current).toEqual({ state: TradeState.SYNCING, trade: undefined })
...@@ -178,7 +178,7 @@ describe('#useBestV3Trade ExactOut', () => { ...@@ -178,7 +178,7 @@ describe('#useBestV3Trade ExactOut', () => {
expectRouterMock(TradeState.INVALID) expectRouterMock(TradeState.INVALID)
expectClientSideMock(TradeState.VALID) expectClientSideMock(TradeState.VALID)
renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC)) renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined) expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
}) })
...@@ -187,9 +187,9 @@ describe('#useBestV3Trade ExactOut', () => { ...@@ -187,9 +187,9 @@ describe('#useBestV3Trade ExactOut', () => {
expectRouterMock(TradeState.NO_ROUTE_FOUND) expectRouterMock(TradeState.NO_ROUTE_FOUND)
expectClientSideMock(TradeState.VALID) expectClientSideMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC)) const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC) expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined }) expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
}) })
}) })
......
...@@ -10,7 +10,7 @@ import { useSingleCallResult } from 'lib/hooks/multicall' ...@@ -10,7 +10,7 @@ import { useSingleCallResult } from 'lib/hooks/multicall'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { SWAP_ROUTER_ADDRESSES, V3_ROUTER_ADDRESS } from '../constants/addresses' import { SWAP_ROUTER_ADDRESSES, V3_ROUTER_ADDRESS } from '../constants/addresses'
import { DAI, UNI, USDC } from '../constants/tokens' import { DAI, UNI, USDC_MAINNET } from '../constants/tokens'
import { useEIP2612Contract } from './useContract' import { useEIP2612Contract } from './useContract'
import useIsArgentWallet from './useIsArgentWallet' import useIsArgentWallet from './useIsArgentWallet'
...@@ -36,7 +36,7 @@ const PERMITTABLE_TOKENS: { ...@@ -36,7 +36,7 @@ const PERMITTABLE_TOKENS: {
} }
} = { } = {
1: { 1: {
[USDC.address]: { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' }, [USDC_MAINNET.address]: { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
[DAI.address]: { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' }, [DAI.address]: { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' },
[UNI[1].address]: { type: PermitType.AMOUNT, name: 'Uniswap' }, [UNI[1].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
}, },
......
...@@ -4,14 +4,14 @@ import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' ...@@ -4,14 +4,14 @@ import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useMemo } from 'react' import { useMemo } from 'react'
import { SupportedChainId } from '../constants/chains' import { SupportedChainId } from '../constants/chains'
import { DAI_OPTIMISM, USDC, USDC_ARBITRUM, USDC_POLYGON } from '../constants/tokens' import { DAI_OPTIMISM, USDC_ARBITRUM, USDC_MAINNET, USDC_POLYGON } from '../constants/tokens'
import { useBestV2Trade } from './useBestV2Trade' import { useBestV2Trade } from './useBestV2Trade'
import { useClientSideV3Trade } from './useClientSideV3Trade' import { useClientSideV3Trade } from './useClientSideV3Trade'
// Stablecoin amounts used when calculating spot price for a given currency. // Stablecoin amounts used when calculating spot price for a given currency.
// The amount is large enough to filter low liquidity pairs. // The amount is large enough to filter low liquidity pairs.
export const STABLECOIN_AMOUNT_OUT: { [chainId: number]: CurrencyAmount<Token> } = { export const STABLECOIN_AMOUNT_OUT: { [chainId: number]: CurrencyAmount<Token> } = {
[SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC, 100_000e6), [SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC_MAINNET, 100_000e6),
[SupportedChainId.ARBITRUM_ONE]: CurrencyAmount.fromRawAmount(USDC_ARBITRUM, 10_000e6), [SupportedChainId.ARBITRUM_ONE]: CurrencyAmount.fromRawAmount(USDC_ARBITRUM, 10_000e6),
[SupportedChainId.OPTIMISM]: CurrencyAmount.fromRawAmount(DAI_OPTIMISM, 10_000e18), [SupportedChainId.OPTIMISM]: CurrencyAmount.fromRawAmount(DAI_OPTIMISM, 10_000e18),
[SupportedChainId.POLYGON]: CurrencyAmount.fromRawAmount(USDC_POLYGON, 10_000e6), [SupportedChainId.POLYGON]: CurrencyAmount.fromRawAmount(USDC_POLYGON, 10_000e6),
......
import { tokens } from '@uniswap/default-token-list' import { tokens } from '@uniswap/default-token-list'
import { DAI, USDC } from 'constants/tokens' import { DAI, USDC_MAINNET } from 'constants/tokens'
import { useUpdateAtom } from 'jotai/utils' import { useUpdateAtom } from 'jotai/utils'
import { useEffect } from 'react' import { useEffect } from 'react'
import { useSelect, useValue } from 'react-cosmos/fixture' import { useSelect, useValue } from 'react-cosmos/fixture'
...@@ -41,7 +41,7 @@ function Fixture() { ...@@ -41,7 +41,7 @@ function Fixture() {
none: '', none: '',
Native: 'NATIVE', Native: 'NATIVE',
DAI: DAI.address, DAI: DAI.address,
USDC: USDC.address, USDC: USDC_MAINNET.address,
} }
const addressOptions = Object.keys(optionsToAddressMap) const addressOptions = Object.keys(optionsToAddressMap)
const [defaultInput] = useSelect('defaultInputAddress', { const [defaultInput] = useSelect('defaultInputAddress', {
......
...@@ -7,9 +7,10 @@ import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall' ...@@ -7,9 +7,10 @@ import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall'
import useNativeCurrency from 'lib/hooks/useNativeCurrency' import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { useMemo } from 'react' import { useMemo } from 'react'
import { TOKEN_SHORTHANDS } from '../../constants/tokens'
import { isAddress } from '../../utils' import { isAddress } from '../../utils'
import { useTokenMap } from './useTokenList' import { supportedChainId } from '../../utils/supportedChainId'
import { TokenMap } from './useTokenList' import { TokenMap, useTokenMap } from './useTokenList'
// parse a name or symbol from a token response // parse a name or symbol from a token response
const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/ const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/
...@@ -28,35 +29,27 @@ function parseStringOrBytes32(str: string | undefined, bytes32: string | undefin ...@@ -28,35 +29,27 @@ function parseStringOrBytes32(str: string | undefined, bytes32: string | undefin
* Returns null if token is loading or null was passed. * Returns null if token is loading or null was passed.
* Returns undefined if tokenAddress is invalid or token does not exist. * Returns undefined if tokenAddress is invalid or token does not exist.
*/ */
export function useTokenFromMap(tokens: TokenMap, tokenAddress?: string | null): Token | null | undefined { export function useTokenFromNetwork(tokenAddress: string | null | undefined): Token | null | undefined {
const { chainId } = useActiveWeb3React() const { chainId } = useActiveWeb3React()
const address = isAddress(tokenAddress) const formattedAddress = isAddress(tokenAddress)
const tokenContract = useTokenContract(address ? address : undefined, false) const tokenContract = useTokenContract(formattedAddress ? formattedAddress : undefined, false)
const tokenContractBytes32 = useBytes32TokenContract(address ? address : undefined, false) const tokenContractBytes32 = useBytes32TokenContract(formattedAddress ? formattedAddress : undefined, false)
const token: Token | undefined = address ? tokens[address] : undefined
const tokenName = useSingleCallResult(token ? undefined : tokenContract, 'name', undefined, NEVER_RELOAD) const tokenName = useSingleCallResult(tokenContract, 'name', undefined, NEVER_RELOAD)
const tokenNameBytes32 = useSingleCallResult( const tokenNameBytes32 = useSingleCallResult(tokenContractBytes32, 'name', undefined, NEVER_RELOAD)
token ? undefined : tokenContractBytes32, const symbol = useSingleCallResult(tokenContract, 'symbol', undefined, NEVER_RELOAD)
'name', const symbolBytes32 = useSingleCallResult(tokenContractBytes32, 'symbol', undefined, NEVER_RELOAD)
undefined, const decimals = useSingleCallResult(tokenContract, 'decimals', undefined, NEVER_RELOAD)
NEVER_RELOAD
)
const symbol = useSingleCallResult(token ? undefined : tokenContract, 'symbol', undefined, NEVER_RELOAD)
const symbolBytes32 = useSingleCallResult(token ? undefined : tokenContractBytes32, 'symbol', undefined, NEVER_RELOAD)
const decimals = useSingleCallResult(token ? undefined : tokenContract, 'decimals', undefined, NEVER_RELOAD)
return useMemo(() => { return useMemo(() => {
if (token) return token if (typeof tokenAddress !== 'string' || !chainId || !formattedAddress) return undefined
if (tokenAddress === null) return null
if (!chainId || !address) return undefined
if (decimals.loading || symbol.loading || tokenName.loading) return null if (decimals.loading || symbol.loading || tokenName.loading) return null
if (decimals.result) { if (decimals.result) {
return new Token( return new Token(
chainId, chainId,
address, formattedAddress,
decimals.result[0], decimals.result[0],
parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], 'UNKNOWN'), parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], 'UNKNOWN'),
parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], 'Unknown Token') parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], 'Unknown Token')
...@@ -64,14 +57,13 @@ export function useTokenFromMap(tokens: TokenMap, tokenAddress?: string | null): ...@@ -64,14 +57,13 @@ export function useTokenFromMap(tokens: TokenMap, tokenAddress?: string | null):
} }
return undefined return undefined
}, [ }, [
address, formattedAddress,
chainId, chainId,
decimals.loading, decimals.loading,
decimals.result, decimals.result,
symbol.loading, symbol.loading,
symbol.result, symbol.result,
symbolBytes32.result, symbolBytes32.result,
token,
tokenAddress, tokenAddress,
tokenName.loading, tokenName.loading,
tokenName.result, tokenName.result,
...@@ -79,6 +71,20 @@ export function useTokenFromMap(tokens: TokenMap, tokenAddress?: string | null): ...@@ -79,6 +71,20 @@ export function useTokenFromMap(tokens: TokenMap, tokenAddress?: string | null):
]) ])
} }
/**
* Returns a Token from the tokenAddress.
* Returns null if token is loading or null was passed.
* Returns undefined if tokenAddress is invalid or token does not exist.
*/
export function useTokenFromMapOrNetwork(tokens: TokenMap, tokenAddress?: string | null): Token | null | undefined {
const address = isAddress(tokenAddress)
const token: Token | undefined = address ? tokens[address] : undefined
const tokenFromNetwork = useTokenFromNetwork(token ? undefined : address ? address : undefined)
return tokenFromNetwork ?? token
}
/** /**
* Returns a Token from the tokenAddress. * Returns a Token from the tokenAddress.
* Returns null if token is loading or null was passed. * Returns null if token is loading or null was passed.
...@@ -86,7 +92,7 @@ export function useTokenFromMap(tokens: TokenMap, tokenAddress?: string | null): ...@@ -86,7 +92,7 @@ export function useTokenFromMap(tokens: TokenMap, tokenAddress?: string | null):
*/ */
export function useToken(tokenAddress?: string | null): Token | null | undefined { export function useToken(tokenAddress?: string | null): Token | null | undefined {
const tokens = useTokenMap() const tokens = useTokenMap()
return useTokenFromMap(tokens, tokenAddress) return useTokenFromMapOrNetwork(tokens, tokenAddress)
} }
/** /**
...@@ -96,8 +102,14 @@ export function useToken(tokenAddress?: string | null): Token | null | undefined ...@@ -96,8 +102,14 @@ export function useToken(tokenAddress?: string | null): Token | null | undefined
*/ */
export function useCurrencyFromMap(tokens: TokenMap, currencyId?: string | null): Currency | null | undefined { export function useCurrencyFromMap(tokens: TokenMap, currencyId?: string | null): Currency | null | undefined {
const nativeCurrency = useNativeCurrency() const nativeCurrency = useNativeCurrency()
const { chainId } = useActiveWeb3React()
const isNative = Boolean(nativeCurrency && currencyId?.toUpperCase() === 'ETH') const isNative = Boolean(nativeCurrency && currencyId?.toUpperCase() === 'ETH')
const token = useTokenFromMap(tokens, isNative ? undefined : currencyId) const shorthandMatchAddress = useMemo(() => {
const chain = supportedChainId(chainId)
return chain && currencyId ? TOKEN_SHORTHANDS[currencyId.toUpperCase()]?.[chain] : undefined
}, [chainId, currencyId])
const token = useTokenFromMapOrNetwork(tokens, isNative ? undefined : shorthandMatchAddress ?? currencyId)
if (currencyId === null || currencyId === undefined) return currencyId if (currencyId === null || currencyId === undefined) return currencyId
......
...@@ -33,6 +33,7 @@ import { ArrowWrapper, SwapCallbackError, Wrapper } from '../../components/swap/ ...@@ -33,6 +33,7 @@ import { ArrowWrapper, SwapCallbackError, Wrapper } from '../../components/swap/
import SwapHeader from '../../components/swap/SwapHeader' import SwapHeader from '../../components/swap/SwapHeader'
import { SwitchLocaleLink } from '../../components/SwitchLocaleLink' import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
import TokenWarningModal from '../../components/TokenWarningModal' import TokenWarningModal from '../../components/TokenWarningModal'
import { TOKEN_SHORTHANDS } from '../../constants/tokens'
import { useAllTokens, useCurrency } from '../../hooks/Tokens' import { useAllTokens, useCurrency } from '../../hooks/Tokens'
import { ApprovalState, useApprovalOptimizedTrade, useApproveCallbackFromTrade } from '../../hooks/useApproveCallback' import { ApprovalState, useApprovalOptimizedTrade, useApproveCallbackFromTrade } from '../../hooks/useApproveCallback'
import useENSAddress from '../../hooks/useENSAddress' import useENSAddress from '../../hooks/useENSAddress'
...@@ -54,6 +55,7 @@ import { LinkStyledButton, ThemedText } from '../../theme' ...@@ -54,6 +55,7 @@ import { LinkStyledButton, ThemedText } from '../../theme'
import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact' import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact'
import { maxAmountSpend } from '../../utils/maxAmountSpend' import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { warningSeverity } from '../../utils/prices' import { warningSeverity } from '../../utils/prices'
import { supportedChainId } from '../../utils/supportedChainId'
import AppBody from '../AppBody' import AppBody from '../AppBody'
const AlertWrapper = styled.div` const AlertWrapper = styled.div`
...@@ -62,13 +64,13 @@ const AlertWrapper = styled.div` ...@@ -62,13 +64,13 @@ const AlertWrapper = styled.div`
` `
export default function Swap({ history }: RouteComponentProps) { export default function Swap({ history }: RouteComponentProps) {
const { account } = useActiveWeb3React() const { account, chainId } = useActiveWeb3React()
const loadedUrlParams = useDefaultsFromURLSearch() const loadedUrlParams = useDefaultsFromURLSearch()
// token warning stuff // token warning stuff
const [loadedInputCurrency, loadedOutputCurrency] = [ const [loadedInputCurrency, loadedOutputCurrency] = [
useCurrency(loadedUrlParams?.inputCurrencyId), useCurrency(loadedUrlParams?.[Field.INPUT]?.currencyId),
useCurrency(loadedUrlParams?.outputCurrencyId), useCurrency(loadedUrlParams?.[Field.OUTPUT]?.currencyId),
] ]
const [dismissTokenWarning, setDismissTokenWarning] = useState<boolean>(false) const [dismissTokenWarning, setDismissTokenWarning] = useState<boolean>(false)
const urlLoadedTokens: Token[] = useMemo( const urlLoadedTokens: Token[] = useMemo(
...@@ -84,10 +86,20 @@ export default function Swap({ history }: RouteComponentProps) { ...@@ -84,10 +86,20 @@ export default function Swap({ history }: RouteComponentProps) {
const importTokensNotInDefault = useMemo( const importTokensNotInDefault = useMemo(
() => () =>
urlLoadedTokens && urlLoadedTokens &&
urlLoadedTokens.filter((token: Token) => { urlLoadedTokens
return !Boolean(token.address in defaultTokens) .filter((token: Token) => {
}), return !Boolean(token.address in defaultTokens)
[defaultTokens, urlLoadedTokens] })
.filter((token: Token) => {
// Any token addresses that are loaded from the shorthands map do not need to show the import URL
const supported = supportedChainId(chainId)
if (!supported) return true
return !Object.keys(TOKEN_SHORTHANDS).some((shorthand) => {
const shorthandTokenAddress = TOKEN_SHORTHANDS[shorthand][supported]
return shorthandTokenAddress && shorthandTokenAddress === token.address
})
}),
[chainId, defaultTokens, urlLoadedTokens]
) )
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
......
...@@ -10,7 +10,7 @@ import { NEVER_RELOAD, useMultipleContractSingleData } from 'lib/hooks/multicall ...@@ -10,7 +10,7 @@ import { NEVER_RELOAD, useMultipleContractSingleData } from 'lib/hooks/multicall
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { ReactNode, useMemo } from 'react' import { ReactNode, useMemo } from 'react'
import { DAI, UNI, USDC, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens' import { DAI, UNI, USDC_MAINNET, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
const { abi: STAKING_REWARDS_ABI } = StakingRewardsJson const { abi: STAKING_REWARDS_ABI } = StakingRewardsJson
const STAKING_REWARDS_INTERFACE = new Interface(STAKING_REWARDS_ABI) const STAKING_REWARDS_INTERFACE = new Interface(STAKING_REWARDS_ABI)
...@@ -31,7 +31,7 @@ export const STAKING_REWARDS_INFO: { ...@@ -31,7 +31,7 @@ export const STAKING_REWARDS_INFO: {
stakingRewardAddress: '0xa1484C3aa22a66C62b77E0AE78E15258bd0cB711', stakingRewardAddress: '0xa1484C3aa22a66C62b77E0AE78E15258bd0cB711',
}, },
{ {
tokens: [WRAPPED_NATIVE_CURRENCY[1], USDC], tokens: [WRAPPED_NATIVE_CURRENCY[1], USDC_MAINNET],
stakingRewardAddress: '0x7FBa4B8Dc5E7616e59622806932DBea72537A56b', stakingRewardAddress: '0x7FBa4B8Dc5E7616e59622806932DBea72537A56b',
}, },
{ {
......
...@@ -27,8 +27,8 @@ describe('hooks', () => { ...@@ -27,8 +27,8 @@ describe('hooks', () => {
queryParametersToSwapState(parse('?outputCurrency=invalid', { parseArrays: false, ignoreQueryPrefix: true })) queryParametersToSwapState(parse('?outputCurrency=invalid', { parseArrays: false, ignoreQueryPrefix: true }))
).toEqual({ ).toEqual({
[Field.INPUT]: { currencyId: 'ETH' }, [Field.INPUT]: { currencyId: 'ETH' },
[Field.OUTPUT]: { currencyId: null }, [Field.OUTPUT]: { currencyId: 'USDC' },
typedValue: '', typedValue: '1',
independentField: Field.INPUT, independentField: Field.INPUT,
recipient: null, recipient: null,
}) })
......
...@@ -5,11 +5,12 @@ import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance' ...@@ -5,11 +5,12 @@ import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
import { useBestTrade } from 'hooks/useBestTrade' import { useBestTrade } from 'hooks/useBestTrade'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { ParsedQs } from 'qs' import { ParsedQs } from 'qs'
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react' import { ReactNode, useCallback, useEffect, useMemo } from 'react'
import { useAppDispatch, useAppSelector } from 'state/hooks' import { useAppDispatch, useAppSelector } from 'state/hooks'
import { InterfaceTrade, TradeState } from 'state/routing/types' import { InterfaceTrade, TradeState } from 'state/routing/types'
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks' import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
import { TOKEN_SHORTHANDS } from '../../constants/tokens'
import { useCurrency } from '../../hooks/Tokens' import { useCurrency } from '../../hooks/Tokens'
import useENS from '../../hooks/useENS' import useENS from '../../hooks/useENS'
import useParsedQueryString from '../../hooks/useParsedQueryString' import useParsedQueryString from '../../hooks/useParsedQueryString'
...@@ -185,11 +186,13 @@ export function useDerivedSwapInfo(): { ...@@ -185,11 +186,13 @@ export function useDerivedSwapInfo(): {
) )
} }
function parseCurrencyFromURLParameter(urlParam: any): string { function parseCurrencyFromURLParameter(urlParam: ParsedQs[string]): string {
if (typeof urlParam === 'string') { if (typeof urlParam === 'string') {
const valid = isAddress(urlParam) const valid = isAddress(urlParam)
if (valid) return valid if (valid) return valid
if (urlParam.toUpperCase() === 'ETH') return 'ETH' const upper = urlParam.toUpperCase()
if (upper === 'ETH') return 'ETH'
if (upper in TOKEN_SHORTHANDS) return upper
} }
return '' return ''
} }
...@@ -216,9 +219,14 @@ function validatedRecipient(recipient: any): string | null { ...@@ -216,9 +219,14 @@ function validatedRecipient(recipient: any): string | null {
export function queryParametersToSwapState(parsedQs: ParsedQs): SwapState { export function queryParametersToSwapState(parsedQs: ParsedQs): SwapState {
let inputCurrency = parseCurrencyFromURLParameter(parsedQs.inputCurrency) let inputCurrency = parseCurrencyFromURLParameter(parsedQs.inputCurrency)
let outputCurrency = parseCurrencyFromURLParameter(parsedQs.outputCurrency) let outputCurrency = parseCurrencyFromURLParameter(parsedQs.outputCurrency)
if (inputCurrency === '' && outputCurrency === '') { let typedValue = parseTokenAmountURLParameter(parsedQs.exactAmount)
// default to ETH input const independentField = parseIndependentFieldURLParameter(parsedQs.exactField)
if (inputCurrency === '' && outputCurrency === '' && typedValue === '' && independentField === Field.INPUT) {
// Defaults to 1 ETH -> USDC
inputCurrency = 'ETH' inputCurrency = 'ETH'
outputCurrency = 'USDC'
typedValue = '1'
} else if (inputCurrency === outputCurrency) { } else if (inputCurrency === outputCurrency) {
// clear output if identical // clear output if identical
outputCurrency = '' outputCurrency = ''
...@@ -233,42 +241,39 @@ export function queryParametersToSwapState(parsedQs: ParsedQs): SwapState { ...@@ -233,42 +241,39 @@ export function queryParametersToSwapState(parsedQs: ParsedQs): SwapState {
[Field.OUTPUT]: { [Field.OUTPUT]: {
currencyId: outputCurrency === '' ? null : outputCurrency ?? null, currencyId: outputCurrency === '' ? null : outputCurrency ?? null,
}, },
typedValue: parseTokenAmountURLParameter(parsedQs.exactAmount), typedValue,
independentField: parseIndependentFieldURLParameter(parsedQs.exactField), independentField,
recipient, recipient,
} }
} }
// updates the swap state to use the defaults for a given network // updates the swap state to use the defaults for a given network
export function useDefaultsFromURLSearch(): export function useDefaultsFromURLSearch(): SwapState {
| { inputCurrencyId: string | undefined; outputCurrencyId: string | undefined }
| undefined {
const { chainId } = useActiveWeb3React() const { chainId } = useActiveWeb3React()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const parsedQs = useParsedQueryString() const parsedQs = useParsedQueryString()
const [result, setResult] = useState<
{ inputCurrencyId: string | undefined; outputCurrencyId: string | undefined } | undefined const parsedSwapState = useMemo(() => {
>() return queryParametersToSwapState(parsedQs)
}, [parsedQs])
useEffect(() => { useEffect(() => {
if (!chainId) return if (!chainId) return
const parsed = queryParametersToSwapState(parsedQs) const inputCurrencyId = parsedSwapState[Field.INPUT].currencyId ?? undefined
const inputCurrencyId = parsed[Field.INPUT].currencyId ?? undefined const outputCurrencyId = parsedSwapState[Field.OUTPUT].currencyId ?? undefined
const outputCurrencyId = parsed[Field.OUTPUT].currencyId ?? undefined
dispatch( dispatch(
replaceSwapState({ replaceSwapState({
typedValue: parsed.typedValue, typedValue: parsedSwapState.typedValue,
field: parsed.independentField, field: parsedSwapState.independentField,
inputCurrencyId, inputCurrencyId,
outputCurrencyId, outputCurrencyId,
recipient: parsed.recipient, recipient: parsedSwapState.recipient,
}) })
) )
setResult({ inputCurrencyId, outputCurrencyId })
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [dispatch, chainId]) }, [dispatch, chainId])
return result return parsedSwapState
} }
...@@ -4,8 +4,8 @@ import { SupportedChainId } from '../constants/chains' ...@@ -4,8 +4,8 @@ import { SupportedChainId } from '../constants/chains'
* Returns the input chain ID if chain is supported. If not, return undefined * Returns the input chain ID if chain is supported. If not, return undefined
* @param chainId a chain ID, which will be returned if it is a supported chain ID * @param chainId a chain ID, which will be returned if it is a supported chain ID
*/ */
export function supportedChainId(chainId: number): number | undefined { export function supportedChainId(chainId: number | undefined): SupportedChainId | undefined {
if (chainId in SupportedChainId) { if (typeof chainId === 'number' && chainId in SupportedChainId) {
return chainId return chainId
} }
return undefined return undefined
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment