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
This commit is contained in:
parent
c25d2b894c
commit
fc34912b53
@ -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', () => {
|
||||
|
@ -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(),
|
||||
|
@ -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(<RoutingDiagram currencyIn={DAI} currencyOut={USDC} routes={[]} />)
|
||||
const { asFragment } = render(<RoutingDiagram currencyIn={DAI} currencyOut={USDC_MAINNET} routes={[]} />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
})
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
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()
|
||||
})
|
||||
|
@ -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],
|
||||
],
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 })
|
||||
})
|
||||
})
|
||||
|
@ -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' },
|
||||
},
|
||||
|
@ -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<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.OPTIMISM]: CurrencyAmount.fromRawAmount(DAI_OPTIMISM, 10_000e18),
|
||||
[SupportedChainId.POLYGON]: CurrencyAmount.fromRawAmount(USDC_POLYGON, 10_000e6),
|
||||
|
@ -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', {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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<boolean>(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)
|
||||
|
@ -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',
|
||||
},
|
||||
{
|
||||
|
@ -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,
|
||||
})
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user