diff --git a/cypress/integration/swap.test.ts b/cypress/integration/swap.test.ts
index 46216c2f44..3fb042ffc9 100644
--- a/cypress/integration/swap.test.ts
+++ b/cypress/integration/swap.test.ts
@@ -2,16 +2,27 @@ describe('Swap', () => {
beforeEach(() => {
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', () => {
- 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', () => {
- 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', () => {
- 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', () => {
diff --git a/src/components/PositionListItem/index.tsx b/src/components/PositionListItem/index.tsx
index 97d8df54f8..e8916e4066 100644
--- a/src/components/PositionListItem/index.tsx
+++ b/src/components/PositionListItem/index.tsx
@@ -19,7 +19,7 @@ import { PositionDetails } from 'types/position'
import { formatTickPrice } from 'utils/formatTickPrice'
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)`
align-items: center;
@@ -145,7 +145,7 @@ export function getPriceOrderingFromPositionForUI(position?: Position): {
const token1 = position.amount1.currency
// 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))) {
return {
priceLower: position.token0PriceUpper.invert(),
diff --git a/src/components/RoutingDiagram/RoutingDiagram.test.tsx b/src/components/RoutingDiagram/RoutingDiagram.test.tsx
index 7b80468ab1..5f6e9338e9 100644
--- a/src/components/RoutingDiagram/RoutingDiagram.test.tsx
+++ b/src/components/RoutingDiagram/RoutingDiagram.test.tsx
@@ -1,7 +1,7 @@
import { Protocol } from '@uniswap/router-sdk'
import { Currency, Percent } from '@uniswap/sdk-core'
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 RoutingDiagram, { RoutingDiagramEntry } from './RoutingDiagram'
@@ -10,16 +10,16 @@ const percent = (strings: TemplateStringsArray) => new Percent(parseInt(strings[
const singleRoute: RoutingDiagramEntry = {
percent: percent`100`,
- path: [[USDC, DAI, FeeAmount.LOW]],
+ path: [[USDC_MAINNET, DAI, FeeAmount.LOW]],
protocol: Protocol.V3,
}
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`,
path: [
- [USDC, WBTC, FeeAmount.MEDIUM],
+ [USDC_MAINNET, WBTC, FeeAmount.MEDIUM],
[WBTC, DAI, FeeAmount.HIGH],
],
protocol: Protocol.V3,
@@ -47,16 +47,16 @@ jest.mock('hooks/useTokenInfoFromActiveList', () => ({
}))
it('renders when no routes are provided', () => {
- const { asFragment } = render()
+ const { asFragment } = render()
expect(asFragment()).toMatchSnapshot()
})
it('renders single route', () => {
- const { asFragment } = render()
+ const { asFragment } = render()
expect(asFragment()).toMatchSnapshot()
})
it('renders multi route', () => {
- const { asFragment } = render()
+ const { asFragment } = render()
expect(asFragment()).toMatchSnapshot()
})
diff --git a/src/constants/routing.ts b/src/constants/routing.ts
index 66097a4133..9e06d5c157 100644
--- a/src/constants/routing.ts
+++ b/src/constants/routing.ts
@@ -18,8 +18,8 @@ import {
sETH2,
SWISE,
TRIBE,
- USDC,
USDC_ARBITRUM,
+ USDC_MAINNET,
USDC_OPTIMISM,
USDC_POLYGON,
USDT,
@@ -50,7 +50,13 @@ const WRAPPED_NATIVE_CURRENCIES_ONLY: ChainTokenList = Object.fromEntries(
// used to construct intermediary pairs for trading
export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = {
...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]: [
...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.OPTIMISM],
DAI_OPTIMISM,
@@ -101,7 +107,7 @@ export const COMMON_BASES: ChainCurrencyList = {
[SupportedChainId.MAINNET]: [
nativeOnChain(SupportedChainId.MAINNET),
DAI,
- USDC,
+ USDC_MAINNET,
USDT,
WBTC,
WRAPPED_NATIVE_CURRENCY[SupportedChainId.MAINNET],
@@ -154,7 +160,13 @@ export const COMMON_BASES: ChainCurrencyList = {
// used to construct the list of all pairs we consider by default in the frontend
export const BASES_TO_TRACK_LIQUIDITY_FOR: ChainTokenList = {
...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][] } = {
[SupportedChainId.MAINNET]: [
@@ -168,7 +180,7 @@ export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = {
'Compound USD Coin'
),
],
- [USDC, USDT],
+ [USDC_MAINNET, USDT],
[DAI, USDT],
],
}
diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts
index bee5ac6f92..d17f32f403 100644
--- a/src/constants/tokens.ts
+++ b/src/constants/tokens.ts
@@ -1,8 +1,23 @@
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 { SupportedChainId } from './chains'
+export { USDC_ARBITRUM, USDC_MAINNET, USDC_OPTIMISM, USDC_POLYGON }
+
export const AMPL = new Token(
SupportedChainId.MAINNET,
'0xD46bA6D942050d489DBd938a2C909A5d5039A161',
@@ -31,27 +46,19 @@ export const DAI_OPTIMISM = new Token(
'DAI',
'Dai stable coin'
)
-export const USDC = new Token(
- SupportedChainId.MAINNET,
- '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
- 6,
- 'USDC',
- 'USD//C'
-)
-export const USDC_ARBITRUM = new Token(
- SupportedChainId.ARBITRUM_ONE,
- '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8',
- 6,
- 'USDC',
- 'USD//C'
-)
-export const USDC_POLYGON = new Token(
- SupportedChainId.POLYGON,
- '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
- 6,
- 'USDC',
- 'USD//C'
-)
+export const USDC: { [chainId in SupportedChainId]: Token } = {
+ [SupportedChainId.MAINNET]: USDC_MAINNET,
+ [SupportedChainId.ARBITRUM_ONE]: USDC_ARBITRUM,
+ [SupportedChainId.OPTIMISM]: USDC_OPTIMISM,
+ [SupportedChainId.ARBITRUM_RINKEBY]: USDC_ARBITRUM_RINKEBY,
+ [SupportedChainId.OPTIMISTIC_KOVAN]: USDC_OPTIMISTIC_KOVAN,
+ [SupportedChainId.POLYGON]: USDC_POLYGON,
+ [SupportedChainId.POLYGON_MUMBAI]: USDC_POLYGON_MUMBAI,
+ [SupportedChainId.GOERLI]: USDC_GÖRLI,
+ [SupportedChainId.RINKEBY]: USDC_RINKEBY,
+ [SupportedChainId.KOVAN]: USDC_KOVAN,
+ [SupportedChainId.ROPSTEN]: USDC_ROPSTEN,
+}
export const DAI_POLYGON = new Token(
SupportedChainId.POLYGON,
'0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063',
@@ -73,13 +80,6 @@ export const WBTC_POLYGON = new Token(
'WBTC',
'Wrapped BTC'
)
-export const USDC_OPTIMISM = new Token(
- SupportedChainId.OPTIMISM,
- '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
- 6,
- 'USDC',
- 'USD//C'
-)
export const USDT = new Token(
SupportedChainId.MAINNET,
'0xdAC17F958D2ee523a2206206994597C13D831ec7',
@@ -296,3 +296,19 @@ export function nativeOnChain(chainId: number): NativeCurrency {
: 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,
+ },
+}
diff --git a/src/hooks/Tokens.ts b/src/hooks/Tokens.ts
index 4b2268f6aa..bed5ba2c8b 100644
--- a/src/hooks/Tokens.ts
+++ b/src/hooks/Tokens.ts
@@ -2,7 +2,7 @@ import { Currency, Token } from '@uniswap/sdk-core'
import { CHAIN_INFO } from 'constants/chainInfo'
import { L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from 'constants/chains'
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 { useMemo } from 'react'
@@ -159,7 +159,7 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool
// otherwise returns the token
export function useToken(tokenAddress?: string | null): Token | null | undefined {
const tokens = useAllTokens()
- return useTokenFromMap(tokens, tokenAddress)
+ return useTokenFromMapOrNetwork(tokens, tokenAddress)
}
export function useCurrency(currencyId?: string | null): Currency | null | undefined {
diff --git a/src/hooks/useBestTrade.test.ts b/src/hooks/useBestTrade.test.ts
index b15c423d57..0cf1759131 100644
--- a/src/hooks/useBestTrade.test.ts
+++ b/src/hooks/useBestTrade.test.ts
@@ -1,6 +1,6 @@
import { renderHook } from '@testing-library/react-hooks'
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 { useRoutingAPITrade } from '../state/routing/useRoutingAPITrade'
@@ -10,7 +10,7 @@ import { useClientSideV3Trade } from './useClientSideV3Trade'
import useDebounce from './useDebounce'
import useIsWindowVisible from './useIsWindowVisible'
-const USDCAmount = CurrencyAmount.fromRawAmount(USDC, '10000')
+const USDCAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '10000')
const DAIAmount = CurrencyAmount.fromRawAmount(DAI, '10000')
jest.mock('./useAutoRouterSupported')
@@ -126,10 +126,10 @@ describe('#useBestV3Trade ExactOut', () => {
expectRouterMock(TradeState.INVALID)
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(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC)
+ expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, USDC_MAINNET)
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})
@@ -138,17 +138,17 @@ describe('#useBestV3Trade ExactOut', () => {
expectRouterMock(TradeState.NO_ROUTE_FOUND)
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(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC)
+ expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, USDC_MAINNET)
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})
describe('when routing api is in non-error state', () => {
it('does not compute client side v3 trade if routing api is 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(result.current).toEqual({ state: TradeState.LOADING, trade: undefined })
@@ -157,7 +157,7 @@ describe('#useBestV3Trade ExactOut', () => {
it('does not compute client side v3 trade if routing api is 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(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
@@ -166,7 +166,7 @@ describe('#useBestV3Trade ExactOut', () => {
it('does not compute client side v3 trade if routing api is 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(result.current).toEqual({ state: TradeState.SYNCING, trade: undefined })
@@ -178,7 +178,7 @@ describe('#useBestV3Trade ExactOut', () => {
expectRouterMock(TradeState.INVALID)
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)
})
@@ -187,9 +187,9 @@ describe('#useBestV3Trade ExactOut', () => {
expectRouterMock(TradeState.NO_ROUTE_FOUND)
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 })
})
})
diff --git a/src/hooks/useERC20Permit.ts b/src/hooks/useERC20Permit.ts
index d8ccfe5773..71c0c750aa 100644
--- a/src/hooks/useERC20Permit.ts
+++ b/src/hooks/useERC20Permit.ts
@@ -10,7 +10,7 @@ import { useSingleCallResult } from 'lib/hooks/multicall'
import { useMemo, useState } from 'react'
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 useIsArgentWallet from './useIsArgentWallet'
@@ -36,7 +36,7 @@ const PERMITTABLE_TOKENS: {
}
} = {
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' },
[UNI[1].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
},
diff --git a/src/hooks/useUSDCPrice.ts b/src/hooks/useUSDCPrice.ts
index 03e60d2340..540f8e39b1 100644
--- a/src/hooks/useUSDCPrice.ts
+++ b/src/hooks/useUSDCPrice.ts
@@ -4,14 +4,14 @@ import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useMemo } from 'react'
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 { useClientSideV3Trade } from './useClientSideV3Trade'
// Stablecoin amounts used when calculating spot price for a given currency.
// The amount is large enough to filter low liquidity pairs.
export const STABLECOIN_AMOUNT_OUT: { [chainId: number]: CurrencyAmount } = {
- [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.OPTIMISM]: CurrencyAmount.fromRawAmount(DAI_OPTIMISM, 10_000e18),
[SupportedChainId.POLYGON]: CurrencyAmount.fromRawAmount(USDC_POLYGON, 10_000e6),
diff --git a/src/lib/components/Swap/Swap.fixture.tsx b/src/lib/components/Swap/Swap.fixture.tsx
index 07036b9ea2..e7b5e7403e 100644
--- a/src/lib/components/Swap/Swap.fixture.tsx
+++ b/src/lib/components/Swap/Swap.fixture.tsx
@@ -1,5 +1,5 @@
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 { useEffect } from 'react'
import { useSelect, useValue } from 'react-cosmos/fixture'
@@ -41,7 +41,7 @@ function Fixture() {
none: '',
Native: 'NATIVE',
DAI: DAI.address,
- USDC: USDC.address,
+ USDC: USDC_MAINNET.address,
}
const addressOptions = Object.keys(optionsToAddressMap)
const [defaultInput] = useSelect('defaultInputAddress', {
diff --git a/src/lib/hooks/useCurrency.ts b/src/lib/hooks/useCurrency.ts
index fd4e992ffb..8dd52f6cdb 100644
--- a/src/lib/hooks/useCurrency.ts
+++ b/src/lib/hooks/useCurrency.ts
@@ -7,9 +7,10 @@ import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { useMemo } from 'react'
+import { TOKEN_SHORTHANDS } from '../../constants/tokens'
import { isAddress } from '../../utils'
-import { useTokenMap } from './useTokenList'
-import { TokenMap } from './useTokenList'
+import { supportedChainId } from '../../utils/supportedChainId'
+import { TokenMap, useTokenMap } from './useTokenList'
// parse a name or symbol from a token response
const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/
@@ -28,35 +29,27 @@ function parseStringOrBytes32(str: string | undefined, bytes32: string | undefin
* Returns null if token is loading or null was passed.
* 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 address = isAddress(tokenAddress)
+ const formattedAddress = isAddress(tokenAddress)
- const tokenContract = useTokenContract(address ? address : undefined, false)
- const tokenContractBytes32 = useBytes32TokenContract(address ? address : undefined, false)
- const token: Token | undefined = address ? tokens[address] : undefined
+ const tokenContract = useTokenContract(formattedAddress ? formattedAddress : undefined, false)
+ const tokenContractBytes32 = useBytes32TokenContract(formattedAddress ? formattedAddress : undefined, false)
- const tokenName = useSingleCallResult(token ? undefined : tokenContract, 'name', undefined, NEVER_RELOAD)
- const tokenNameBytes32 = useSingleCallResult(
- token ? undefined : tokenContractBytes32,
- 'name',
- undefined,
- 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)
+ const tokenName = useSingleCallResult(tokenContract, 'name', undefined, NEVER_RELOAD)
+ const tokenNameBytes32 = useSingleCallResult(tokenContractBytes32, 'name', undefined, NEVER_RELOAD)
+ const symbol = useSingleCallResult(tokenContract, 'symbol', undefined, NEVER_RELOAD)
+ const symbolBytes32 = useSingleCallResult(tokenContractBytes32, 'symbol', undefined, NEVER_RELOAD)
+ const decimals = useSingleCallResult(tokenContract, 'decimals', undefined, NEVER_RELOAD)
return useMemo(() => {
- if (token) return token
- if (tokenAddress === null) return null
- if (!chainId || !address) return undefined
+ if (typeof tokenAddress !== 'string' || !chainId || !formattedAddress) return undefined
if (decimals.loading || symbol.loading || tokenName.loading) return null
if (decimals.result) {
return new Token(
chainId,
- address,
+ formattedAddress,
decimals.result[0],
parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], 'UNKNOWN'),
parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], 'Unknown Token')
@@ -64,14 +57,13 @@ export function useTokenFromMap(tokens: TokenMap, tokenAddress?: string | null):
}
return undefined
}, [
- address,
+ formattedAddress,
chainId,
decimals.loading,
decimals.result,
symbol.loading,
symbol.result,
symbolBytes32.result,
- token,
tokenAddress,
tokenName.loading,
tokenName.result,
@@ -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 null if token is loading or null was passed.
@@ -86,7 +92,7 @@ export function useTokenFromMap(tokens: TokenMap, tokenAddress?: string | null):
*/
export function useToken(tokenAddress?: string | null): Token | null | undefined {
const tokens = useTokenMap()
- return useTokenFromMap(tokens, tokenAddress)
+ return useTokenFromMapOrNetwork(tokens, tokenAddress)
}
/**
@@ -96,8 +102,14 @@ export function useToken(tokenAddress?: string | null): Token | null | undefined
*/
export function useCurrencyFromMap(tokens: TokenMap, currencyId?: string | null): Currency | null | undefined {
const nativeCurrency = useNativeCurrency()
+ const { chainId } = useActiveWeb3React()
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
diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx
index a626cbe82a..d6384b4b0b 100644
--- a/src/pages/Swap/index.tsx
+++ b/src/pages/Swap/index.tsx
@@ -33,6 +33,7 @@ import { ArrowWrapper, SwapCallbackError, Wrapper } from '../../components/swap/
import SwapHeader from '../../components/swap/SwapHeader'
import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
import TokenWarningModal from '../../components/TokenWarningModal'
+import { TOKEN_SHORTHANDS } from '../../constants/tokens'
import { useAllTokens, useCurrency } from '../../hooks/Tokens'
import { ApprovalState, useApprovalOptimizedTrade, useApproveCallbackFromTrade } from '../../hooks/useApproveCallback'
import useENSAddress from '../../hooks/useENSAddress'
@@ -54,6 +55,7 @@ import { LinkStyledButton, ThemedText } from '../../theme'
import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact'
import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { warningSeverity } from '../../utils/prices'
+import { supportedChainId } from '../../utils/supportedChainId'
import AppBody from '../AppBody'
const AlertWrapper = styled.div`
@@ -62,13 +64,13 @@ const AlertWrapper = styled.div`
`
export default function Swap({ history }: RouteComponentProps) {
- const { account } = useActiveWeb3React()
+ const { account, chainId } = useActiveWeb3React()
const loadedUrlParams = useDefaultsFromURLSearch()
// token warning stuff
const [loadedInputCurrency, loadedOutputCurrency] = [
- useCurrency(loadedUrlParams?.inputCurrencyId),
- useCurrency(loadedUrlParams?.outputCurrencyId),
+ useCurrency(loadedUrlParams?.[Field.INPUT]?.currencyId),
+ useCurrency(loadedUrlParams?.[Field.OUTPUT]?.currencyId),
]
const [dismissTokenWarning, setDismissTokenWarning] = useState(false)
const urlLoadedTokens: Token[] = useMemo(
@@ -84,10 +86,20 @@ export default function Swap({ history }: RouteComponentProps) {
const importTokensNotInDefault = useMemo(
() =>
urlLoadedTokens &&
- urlLoadedTokens.filter((token: Token) => {
- return !Boolean(token.address in defaultTokens)
- }),
- [defaultTokens, urlLoadedTokens]
+ urlLoadedTokens
+ .filter((token: Token) => {
+ return !Boolean(token.address in defaultTokens)
+ })
+ .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)
diff --git a/src/state/stake/hooks.tsx b/src/state/stake/hooks.tsx
index fd96779648..c4f14f52c8 100644
--- a/src/state/stake/hooks.tsx
+++ b/src/state/stake/hooks.tsx
@@ -10,7 +10,7 @@ import { NEVER_RELOAD, useMultipleContractSingleData } from 'lib/hooks/multicall
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
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 STAKING_REWARDS_INTERFACE = new Interface(STAKING_REWARDS_ABI)
@@ -31,7 +31,7 @@ export const STAKING_REWARDS_INFO: {
stakingRewardAddress: '0xa1484C3aa22a66C62b77E0AE78E15258bd0cB711',
},
{
- tokens: [WRAPPED_NATIVE_CURRENCY[1], USDC],
+ tokens: [WRAPPED_NATIVE_CURRENCY[1], USDC_MAINNET],
stakingRewardAddress: '0x7FBa4B8Dc5E7616e59622806932DBea72537A56b',
},
{
diff --git a/src/state/swap/hooks.test.ts b/src/state/swap/hooks.test.ts
index 22718c5e55..37be9d2528 100644
--- a/src/state/swap/hooks.test.ts
+++ b/src/state/swap/hooks.test.ts
@@ -27,8 +27,8 @@ describe('hooks', () => {
queryParametersToSwapState(parse('?outputCurrency=invalid', { parseArrays: false, ignoreQueryPrefix: true }))
).toEqual({
[Field.INPUT]: { currencyId: 'ETH' },
- [Field.OUTPUT]: { currencyId: null },
- typedValue: '',
+ [Field.OUTPUT]: { currencyId: 'USDC' },
+ typedValue: '1',
independentField: Field.INPUT,
recipient: null,
})
diff --git a/src/state/swap/hooks.tsx b/src/state/swap/hooks.tsx
index b91da25691..88011e6c5c 100644
--- a/src/state/swap/hooks.tsx
+++ b/src/state/swap/hooks.tsx
@@ -5,11 +5,12 @@ import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
import { useBestTrade } from 'hooks/useBestTrade'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
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 { InterfaceTrade, TradeState } from 'state/routing/types'
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
+import { TOKEN_SHORTHANDS } from '../../constants/tokens'
import { useCurrency } from '../../hooks/Tokens'
import useENS from '../../hooks/useENS'
import useParsedQueryString from '../../hooks/useParsedQueryString'
@@ -185,11 +186,13 @@ export function useDerivedSwapInfo(): {
)
}
-function parseCurrencyFromURLParameter(urlParam: any): string {
+function parseCurrencyFromURLParameter(urlParam: ParsedQs[string]): string {
if (typeof urlParam === 'string') {
const valid = isAddress(urlParam)
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 ''
}
@@ -216,9 +219,14 @@ function validatedRecipient(recipient: any): string | null {
export function queryParametersToSwapState(parsedQs: ParsedQs): SwapState {
let inputCurrency = parseCurrencyFromURLParameter(parsedQs.inputCurrency)
let outputCurrency = parseCurrencyFromURLParameter(parsedQs.outputCurrency)
- if (inputCurrency === '' && outputCurrency === '') {
- // default to ETH input
+ let typedValue = parseTokenAmountURLParameter(parsedQs.exactAmount)
+ const independentField = parseIndependentFieldURLParameter(parsedQs.exactField)
+
+ if (inputCurrency === '' && outputCurrency === '' && typedValue === '' && independentField === Field.INPUT) {
+ // Defaults to 1 ETH -> USDC
inputCurrency = 'ETH'
+ outputCurrency = 'USDC'
+ typedValue = '1'
} else if (inputCurrency === outputCurrency) {
// clear output if identical
outputCurrency = ''
@@ -233,42 +241,39 @@ export function queryParametersToSwapState(parsedQs: ParsedQs): SwapState {
[Field.OUTPUT]: {
currencyId: outputCurrency === '' ? null : outputCurrency ?? null,
},
- typedValue: parseTokenAmountURLParameter(parsedQs.exactAmount),
- independentField: parseIndependentFieldURLParameter(parsedQs.exactField),
+ typedValue,
+ independentField,
recipient,
}
}
// updates the swap state to use the defaults for a given network
-export function useDefaultsFromURLSearch():
- | { inputCurrencyId: string | undefined; outputCurrencyId: string | undefined }
- | undefined {
+export function useDefaultsFromURLSearch(): SwapState {
const { chainId } = useActiveWeb3React()
const dispatch = useAppDispatch()
const parsedQs = useParsedQueryString()
- const [result, setResult] = useState<
- { inputCurrencyId: string | undefined; outputCurrencyId: string | undefined } | undefined
- >()
+
+ const parsedSwapState = useMemo(() => {
+ return queryParametersToSwapState(parsedQs)
+ }, [parsedQs])
useEffect(() => {
if (!chainId) return
- const parsed = queryParametersToSwapState(parsedQs)
- const inputCurrencyId = parsed[Field.INPUT].currencyId ?? undefined
- const outputCurrencyId = parsed[Field.OUTPUT].currencyId ?? undefined
+ const inputCurrencyId = parsedSwapState[Field.INPUT].currencyId ?? undefined
+ const outputCurrencyId = parsedSwapState[Field.OUTPUT].currencyId ?? undefined
dispatch(
replaceSwapState({
- typedValue: parsed.typedValue,
- field: parsed.independentField,
+ typedValue: parsedSwapState.typedValue,
+ field: parsedSwapState.independentField,
inputCurrencyId,
outputCurrencyId,
- recipient: parsed.recipient,
+ recipient: parsedSwapState.recipient,
})
)
- setResult({ inputCurrencyId, outputCurrencyId })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dispatch, chainId])
- return result
+ return parsedSwapState
}
diff --git a/src/utils/supportedChainId.ts b/src/utils/supportedChainId.ts
index d3fef188ee..4e8cd7fff0 100644
--- a/src/utils/supportedChainId.ts
+++ b/src/utils/supportedChainId.ts
@@ -4,8 +4,8 @@ import { SupportedChainId } from '../constants/chains'
* 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
*/
-export function supportedChainId(chainId: number): number | undefined {
- if (chainId in SupportedChainId) {
+export function supportedChainId(chainId: number | undefined): SupportedChainId | undefined {
+ if (typeof chainId === 'number' && chainId in SupportedChainId) {
return chainId
}
return undefined