Transaction completed in
@@ -372,12 +364,12 @@ function L2Content({
export interface ConfirmationModalProps {
isOpen: boolean
onDismiss: () => void
- hash?: string | undefined
- content?: () => ReactNode
+ hash?: string | undefined // mod
+ content?: () => ReactNode // mod
attemptingTxn: boolean
pendingText: ReactNode
- currencyToAdd?: Currency
- operationType: OperationType
+ currencyToAdd?: Currency | undefined
+ operationType: OperationType // mod
}
export default function TransactionConfirmationModal({
@@ -388,7 +380,7 @@ export default function TransactionConfirmationModal({
pendingText,
content,
currencyToAdd,
- operationType,
+ operationType, // mod
}: ConfirmationModalProps) {
const { chainId } = useActiveWeb3React()
@@ -398,7 +390,7 @@ export default function TransactionConfirmationModal({
// confirmation screen
return (
- //
+ //
{isL2 && (hash || attemptingTxn) ? (
diff --git a/src/custom/components/TransactionConfirmationModal/index.tsx b/src/custom/components/TransactionConfirmationModal/index.tsx
index 7f6f8773dd..06969c6289 100644
--- a/src/custom/components/TransactionConfirmationModal/index.tsx
+++ b/src/custom/components/TransactionConfirmationModal/index.tsx
@@ -4,10 +4,7 @@ import { useWalletInfo } from 'hooks/useWalletInfo'
import { SupportedChainId as ChainId } from 'constants/chains'
import React, { ReactNode, useContext, useMemo } from 'react'
import styled, { ThemeContext } from 'styled-components/macro'
-import {
- CloseIcon,
- // CustomLightSpinner
-} from 'theme'
+import { CloseIcon } from 'theme'
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { ExternalLink } from 'theme'
@@ -21,7 +18,6 @@ import GameIcon from 'assets/cow-swap/game.gif'
import { Link } from 'react-router-dom'
import { ConfirmationModalContent as ConfirmationModalContentMod } from './TransactionConfirmationModalMod'
import { ColumnCenter } from 'components/Column'
-// import { lighten } from 'polished'
import { getStatusIcon } from 'components/AccountDetails'
import { shortenAddress } from 'utils'
import { getChainCurrencySymbols } from 'utils/xdai/hack'
diff --git a/src/custom/components/TransactionSettings/TransactionSettingsMod.tsx b/src/custom/components/TransactionSettings/TransactionSettingsMod.tsx
index 9075afe9cb..e90ebe87aa 100644
--- a/src/custom/components/TransactionSettings/TransactionSettingsMod.tsx
+++ b/src/custom/components/TransactionSettings/TransactionSettingsMod.tsx
@@ -1,17 +1,20 @@
import { Trans } from '@lingui/macro'
import { Percent } from '@uniswap/sdk-core'
+import { L2_CHAIN_IDS } from '@src/constants/chains'
+import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+// import ms from 'ms.macro'
+import { darken } from 'polished'
import { useContext, useState } from 'react'
+import { useSetUserSlippageTolerance, useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
import styled, { ThemeContext } from 'styled-components/macro'
-import { TYPE } from 'theme'
+import { ThemedText } from 'theme'
import { AutoColumn } from 'components/Column'
import QuestionHelper from '../QuestionHelper'
import { RowBetween, RowFixed } from 'components/Row'
-import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
-import { darken } from 'polished'
-import { useSetUserSlippageTolerance, useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
-import { L2_CHAIN_IDS } from '@src/constants/chains'
-import { useActiveWeb3React } from 'hooks/web3'
+
+// MOD imports
import { INPUT_OUTPUT_EXPLANATION, MINIMUM_ORDER_VALID_TO_TIME_SECONDS } from 'constants/index'
enum SlippageError {
@@ -85,7 +88,7 @@ export const OptionCustom = styled(FancyButton)<{ active?: boolean; warning?: bo
const SlippageEmojiContainer = styled.span`
color: #f3841e;
${({ theme }) => theme.mediaWidth.upToSmall`
- display: none;
+ display: none;
`}
`
@@ -93,6 +96,8 @@ export interface TransactionSettingsProps {
placeholderSlippage: Percent // varies according to the context in which the settings dialog is placed
}
+// const THREE_DAYS_IN_SECONDS = ms`3 days` / 1000
+
export default function TransactionSettings({ placeholderSlippage }: TransactionSettingsProps) {
const { chainId } = useActiveWeb3React()
const theme = useContext(ThemeContext)
@@ -160,9 +165,9 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
-
+
MEV protected slippage
-
+
-
+
Transaction deadline
-
+
-
+
minutes
-
+
)}
diff --git a/src/custom/components/TransactionSettings/index.tsx b/src/custom/components/TransactionSettings/index.tsx
index 1ae13f9f59..baeee00632 100644
--- a/src/custom/components/TransactionSettings/index.tsx
+++ b/src/custom/components/TransactionSettings/index.tsx
@@ -54,28 +54,6 @@ const Wrapper = styled.div`
}
`
-// type SetRawSlippage = (rawSlippage: number) => void
-// type SetSlippageInput = (value: React.SetStateAction) => void
-
-/* function parseCustomSlippage(value: string, setRawSlippage: SetRawSlippage, setSlippageInput: SetSlippageInput): void {
- // we don't allow negative slippage to be input
- if (isNaN(Number(value)) || Number(value) < 0) {
- return batchedUpdates(() => {
- setSlippageInput('0')
- setRawSlippage(0)
- })
- }
-
- setSlippageInput(value)
-
- try {
- const valueAsIntFromRoundedFloat = Number.parseInt((Number.parseFloat(value) * 100).toString())
- if (!Number.isNaN(valueAsIntFromRoundedFloat) && valueAsIntFromRoundedFloat < 5000) {
- setRawSlippage(valueAsIntFromRoundedFloat)
- }
- } catch {}
-} */
-
export type TransactionSettingsProps = Omit
export default function SlippageTabs(params: TransactionSettingsProps) {
diff --git a/src/custom/components/Version/index.tsx b/src/custom/components/Version/index.tsx
index 74cb8ebd49..1c017f0245 100644
--- a/src/custom/components/Version/index.tsx
+++ b/src/custom/components/Version/index.tsx
@@ -1,5 +1,5 @@
import styled from 'styled-components/macro'
-import { ExternalLink, TYPE } from 'theme'
+import { ExternalLink, ThemedText } from 'theme'
import { version as WEB_VERSION } from '@src/../package.json'
import { version as CONTRACTS_VERSION } from '@gnosis.pm/gp-v2-contracts/package.json'
@@ -83,7 +83,7 @@ const VersionsExternalLink = styled(ExternalLink)<{ isUnclickable?: boolean }>`
`}
`
-const VersionsLinkWrapper = styled(TYPE.small)`
+const VersionsLinkWrapper = styled(ThemedText.Small)`
display: flex;
justify-content: center;
align-items: center;
diff --git a/src/custom/components/WalletModal/Option/OptionMod.tsx b/src/custom/components/WalletModal/Option/OptionMod.tsx
index e538016a7a..42c3a72fa1 100644
--- a/src/custom/components/WalletModal/Option/OptionMod.tsx
+++ b/src/custom/components/WalletModal/Option/OptionMod.tsx
@@ -1,4 +1,6 @@
+import React from 'react'
import styled from 'styled-components/macro'
+
import { ExternalLink } from 'theme'
const InfoCard = styled.button<{ active?: boolean }>`
diff --git a/src/custom/components/WalletModal/WalletModalMod.tsx b/src/custom/components/WalletModal/WalletModalMod.tsx
index b4f2cc74ce..f63288d945 100644
--- a/src/custom/components/WalletModal/WalletModalMod.tsx
+++ b/src/custom/components/WalletModal/WalletModalMod.tsx
@@ -1,12 +1,15 @@
import { Trans } from '@lingui/macro'
-import { AbstractConnector } from '@web3-react/abstract-connector'
-import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
-import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
-import { AutoRow } from 'components/Row'
+import { AutoColumn } from 'components/Column'
+// import { PrivacyPolicy } from 'components/PrivacyPolicy'
+import { /*Row,*/ AutoRow /*, RowBetween*/ } from 'components/Row'
// import { useWalletConnectMonitoringEventCallback } from 'hooks/useMonitoringEventCallback'
import { useEffect, useState } from 'react'
+// import { ArrowLeft, ArrowRight, Info } from 'react-feather'
import ReactGA from 'react-ga'
import styled from 'styled-components/macro'
+import { AbstractConnector } from 'web3-react-abstract-connector'
+import { UnsupportedChainIdError, useWeb3React } from 'web3-react-core'
+import { WalletConnectConnector } from 'web3-react-walletconnect-connector'
import MetamaskIcon from 'assets/images/metamask.png'
import { ReactComponent as Close } from 'assets/images/x.svg'
@@ -16,16 +19,16 @@ import { SUPPORTED_WALLETS } from 'constants/index'
import usePrevious from 'hooks/usePrevious'
import { useModalOpen, useWalletModalToggle } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
-import {
- // ExternalLink,
- TYPE,
-} from 'theme'
+import { /*ExternalLink,*/ ThemedText } from 'theme'
import { isMobile } from 'react-device-detect'
// import AccountDetails from 'components/AccountDetails'
-import ModalMod from '@src/components/Modal'
+import { /*Card,*/ LightCard } from 'components/Card'
+// import Modal from '../Modal'
import Option from 'components/WalletModal/Option'
import PendingView from 'components/WalletModal/PendingView'
-import { LightCard } from 'components/Card'
+
+// MOD imports
+import ModalMod from '@src/components/Modal'
export const CloseIcon = styled.div`
position: absolute;
@@ -111,11 +114,22 @@ export const HoverText = styled.div`
}
`
+/* const LinkCard = styled(Card)`
+ background-color: ${({ theme }) => theme.bg1};
+ color: ${({ theme }) => theme.text3};
+
+ :hover {
+ cursor: pointer;
+ filter: brightness(0.9);
+ }
+` */
+
const WALLET_VIEWS = {
OPTIONS: 'options',
OPTIONS_SECONDARY: 'options_secondary',
ACCOUNT: 'account',
PENDING: 'pending',
+ LEGAL: 'legal',
}
// MOD
@@ -145,6 +159,7 @@ export default function WalletModal({
const { active, account, connector, activate, error } = useWeb3React()
const [walletView, setWalletView] = useState(WALLET_VIEWS.ACCOUNT)
+ // const previousWalletView = usePrevious(walletView)
const [pendingWallet, setPendingWallet] = useState()
@@ -334,7 +349,7 @@ export default function WalletModal({
{error instanceof UnsupportedChainIdError ? (
- Please connect to the appropriate network.
+ Please connect to a supported network in the dropdown menu or in your wallet.
) : (
Error connecting. Try refreshing the page.
@@ -343,17 +358,41 @@ export default function WalletModal({
)
}
- // if (account && walletView === WALLET_VIEWS.ACCOUNT) {
- // return (
- // setWalletView(WALLET_VIEWS.OPTIONS)}
- // />
- // )
- // }
+ /* if (walletView === WALLET_VIEWS.LEGAL) {
+ return (
+
+
+ {
+ setWalletView(
+ (previousWalletView === WALLET_VIEWS.LEGAL ? WALLET_VIEWS.ACCOUNT : previousWalletView) ??
+ WALLET_VIEWS.ACCOUNT
+ )
+ }}
+ >
+
+
+
+
+ Legal & Privacy
+
+
+
+
+
+ )
+ }
+ if (account && walletView === WALLET_VIEWS.ACCOUNT) {
+ return (
+ setWalletView(WALLET_VIEWS.OPTIONS)}
+ />
+ )
+ } */
return (
@@ -379,31 +418,43 @@ export default function WalletModal({
)}
-
-
-
- {/*
+
+
+
+
+ {/*
By connecting a wallet, you agree to Uniswap Labs’{' '}
Terms of Service and
acknowledge that you have read and understand the{' '}
Uniswap protocol disclaimer .
*/}
-
-
-
-
-
- {walletView === WALLET_VIEWS.PENDING ? (
-
- ) : (
- {getOptions()}
- )}
- {walletView !== WALLET_VIEWS.PENDING && }
+
+
+
+
+ {walletView === WALLET_VIEWS.PENDING ? (
+
+ ) : (
+ {getOptions()}
+ )}
+ {/* setWalletView(WALLET_VIEWS.LEGAL)}>
+
+
+
+
+ How this app uses APIs
+
+
+
+
+ */}
+ {walletView !== WALLET_VIEWS.PENDING && }
+
)
diff --git a/src/custom/components/Web3Status/Web3StatusMod.tsx b/src/custom/components/Web3Status/Web3StatusMod.tsx
index 5a1ea3b62e..d3c7cbea44 100644
--- a/src/custom/components/Web3Status/Web3StatusMod.tsx
+++ b/src/custom/components/Web3Status/Web3StatusMod.tsx
@@ -1,50 +1,51 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
-import { AbstractConnector } from '@web3-react/abstract-connector'
-import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
+// import { Connector } from '@web3-react/types'
import { darken, lighten } from 'polished'
// import { useMemo } from 'react'
import { Activity } from 'react-feather'
import styled, { css } from 'styled-components/macro'
+import { AbstractConnector } from 'web3-react-abstract-connector'
+import { UnsupportedChainIdError, useWeb3React } from 'web3-react-core'
-// import CoinbaseWalletIcon from 'assets/images/coinbaseWalletIcon.svg'
-// import FortmaticIcon from 'assets/images/fortmaticIcon.png'
-// import PortisIcon from 'assets/images/portisIcon.png'
-// import WalletConnectIcon from 'assets/images/walletConnectIcon.svg'
-// import { fortmatic, injected, portis, walletconnect, walletlink } from 'connectors'
-// import { NetworkContextName } from 'constants/index'
+// import { NetworkContextName } from '../../constants/misc'
import useENSName from 'hooks/useENSName'
import { useHasSocks } from 'hooks/useSocksBalance'
import { useWalletModalToggle } from 'state/application/hooks'
-// import { isTransactionRecent, useAllTransactions } from 'state/enhancedTransactions/hooks'
-import { EnhancedTransactionDetails } from 'state/enhancedTransactions/reducer'
+// import { isTransactionRecent, useAllTransactions } from '../../state/transactions/hooks'
+// import { TransactionDetails } from '../../state/transactions/reducer'
import { shortenAddress } from 'utils'
-import { ButtonSecondary } from 'components/Button'
-
-// import Identicon from 'components/Identicon'
+// import { ButtonSecondary } from 'components/Button'
+// import StatusIcon from '../Identicon/StatusIcon'
import Loader from 'components/Loader'
-
import { RowBetween } from 'components/Row'
-// import WalletModal from 'components/WalletModal'
+// import WalletModal from '../WalletModal'
-// const IconWrapper = styled.div<{ size?: number }>`
-// ${({ theme }) => theme.flexColumnNoWrap};
-// align-items: center;
-// justify-content: center;
-// & > * {
-// height: ${({ size }) => (size ? size + 'px' : '32px')};
-// width: ${({ size }) => (size ? size + 'px' : '32px')};
-// }
-// `
+// MOD imports
+import { EnhancedTransactionDetails } from 'state/enhancedTransactions/reducer'
+import { Web3StatusGeneric, Web3StatusError, Web3StatusConnect, WrappedStatusIcon } from '@src/components/Web3Status'
+
+/* const IconWrapper = styled.div<{ size?: number }>`
+ ${({ theme }) => theme.flexColumnNoWrap};
+ align-items: center;
+ justify-content: center;
+ & > * {
+ height: ${({ size }) => (size ? size + 'px' : '32px')};
+ width: ${({ size }) => (size ? size + 'px' : '32px')};
+ }
+`
const Web3StatusGeneric = styled(ButtonSecondary)`
${({ theme }) => theme.flexRowNoWrap}
width: 100%;
align-items: center;
padding: 0.5rem;
- border-radius: 12px;
+ border-radius: 14px;
cursor: pointer;
user-select: none;
+ height: 36px;
+ margin-right: 2px;
+ margin-left: 1px;
:focus {
outline: none;
}
@@ -86,7 +87,7 @@ const Web3StatusConnect = styled(Web3StatusGeneric)<{ faded?: boolean }>`
color: ${({ theme }) => darken(0.05, theme.primaryText1)};
}
`}
-`
+` */
export const Web3StatusConnected = styled(Web3StatusGeneric)<{ pending?: boolean; clickDisabled?: boolean }>`
background-color: ${({ pending, theme }) => (pending ? theme.primary1 : theme.bg2)};
@@ -145,39 +146,13 @@ function Sock() {
)
}
-// eslint-disable-next-line react/prop-types
-/*
-function StatusIcon({ connector }: { connector: AbstractConnector }) {
- if (connector === injected) {
- return
- } else if (connector === walletconnect) {
- return (
-
-
-
- )
- } else if (connector === walletlink) {
- return (
-
-
-
- )
- } else if (connector === fortmatic) {
- return (
-
-
-
- )
- } else if (connector === portis) {
- return (
-
-
-
- )
- }
- return null
-}
-*/
+/* function WrappedStatusIcon({ connector }: { connector: AbstractConnector | Connector }) {
+ return (
+
+
+
+ )
+} */
export function Web3StatusInner({
pendingCount,
@@ -194,8 +169,7 @@ export function Web3StatusInner({
const { ENSName } = useENSName(account ?? undefined)
- /*
- const allTransactions = useAllTransactions()
+ /* const allTransactions = useAllTransactions()
const sortedRecentTransactions = useMemo(() => {
const txs = Object.values(allTransactions)
@@ -204,8 +178,7 @@ export function Web3StatusInner({
const pending = sortedRecentTransactions.filter((tx) => !tx.receipt).map((tx) => tx.hash)
- const hasPendingTransactions = !!pending.length
- */
+ const hasPendingTransactions = !!pending.length */
const hasPendingTransactions = !!pendingCount
const hasSocks = useHasSocks()
const toggleWalletModal = useWalletModalToggle()
@@ -232,8 +205,8 @@ export function Web3StatusInner({
{ENSName || shortenAddress(account)}
>
)}
- {/* {!hasPendingTransactions && connector && } */}
- {!hasPendingTransactions && connector && }
+ {/* {!hasPendingTransactions && connector && } */}
+ {!hasPendingTransactions && connector && }
)
} else if (error) {
@@ -284,7 +257,9 @@ export function Web3StatusInner({
return (
<>
-
+ {(contextNetwork.active || active) && (
+
+ )}
>
)
} */
diff --git a/src/custom/components/swap/AdvancedSwapDetails/AdvancedSwapDetailsMod.tsx b/src/custom/components/swap/AdvancedSwapDetails/AdvancedSwapDetailsMod.tsx
index 150160d596..9af9caf180 100644
--- a/src/custom/components/swap/AdvancedSwapDetails/AdvancedSwapDetailsMod.tsx
+++ b/src/custom/components/swap/AdvancedSwapDetails/AdvancedSwapDetailsMod.tsx
@@ -1,46 +1,54 @@
// import { Trans } from '@lingui/macro'
-import { Percent /*, Currency, TradeType */ } from '@uniswap/sdk-core'
-// import { Trade as V2Trade } from '@uniswap/v2-sdk'
-// import { Trade as V3Trade } from '@uniswap/v3-sdk'
+import { /*Currency,*/ Percent /*, TradeType*/ } from '@uniswap/sdk-core'
+// import Card from 'components/Card'
// import { LoadingRows } from 'components/Loader/styled'
+// import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
+// import useActiveWeb3React from 'hooks/useActiveWeb3React'
// import { useContext, useMemo } from 'react'
-// import { ThemeContext } from 'styled-components/macro'
+// import { InterfaceTrade } from 'state/routing/types'
+// import styled, { ThemeContext } from 'styled-components/macro'
-// import { TYPE } from 'theme'
-// import { computeRealizedLPFeePercent } from 'utils/prices'
-// import { AutoColumn } from 'components/Column'
-// import { RowBetween, RowFixed } from 'components/Row'
-// import FormattedPriceImpact from 'components/swap/FormattedPriceImpact'
-// import { TransactionDetailsLabel } from './styleds'
+// import { Separator, ThemedText } from '../../theme'
+// import { computeRealizedLPFeePercent } from '../../utils/prices'
+// import { AutoColumn } from '../Column'
+// import { RowBetween, RowFixed } from '../Row'
+// import FormattedPriceImpact from './FormattedPriceImpact'
+// MOD imports
import TradeGp from 'state/swap/TradeGp'
import TradeSummary from '../TradeSummary'
+/* const StyledCard = styled(Card)`
+ padding: 0;
+` */
+
export interface AdvancedSwapDetailsProps {
- // trade?: V2Trade | V3Trade
+ // trade?: InterfaceTrade
trade?: TradeGp
allowedSlippage: Percent
+ // syncing?: boolean
+ // hideRouteDiagram?: boolean
showHelpers?: boolean
showFee?: boolean
}
-// function TextWithLoadingPlaceholder({
-// syncing,
-// width,
-// children,
-// }: {
-// syncing: boolean
-// width: number
-// children: JSX.Element
-// }) {
-// return syncing ? (
-//
-//
-//
-// ) : (
-// children
-// )
-// }
+/* function TextWithLoadingPlaceholder({
+ syncing,
+ width,
+ children,
+}: {
+ syncing: boolean
+ width: number
+ children: JSX.Element
+}) {
+ return syncing ? (
+
+
+
+ ) : (
+ children
+ )
+} */
export function AdvancedSwapDetails({
trade,
@@ -48,80 +56,80 @@ export function AdvancedSwapDetails({
showHelpers = true,
showFee = true,
}: AdvancedSwapDetailsProps) {
- // const theme = useContext(ThemeContext)
-
- /*
- const { realizedLPFee, priceImpact } = useMemo(() => {
- if (!trade) return { realizedLPFee: undefined, priceImpact: undefined }
+ /* const theme = useContext(ThemeContext)
+ const { chainId } = useActiveWeb3React()
+ const { expectedOutputAmount, priceImpact } = useMemo(() => {
+ if (!trade) return { expectedOutputAmount: undefined, priceImpact: undefined }
+ const expectedOutputAmount = trade.outputAmount
const realizedLpFeePercent = computeRealizedLPFeePercent(trade)
- const realizedLPFee = trade.inputAmount.multiply(realizedLpFeePercent)
const priceImpact = trade.priceImpact.subtract(realizedLpFeePercent)
- return { priceImpact, realizedLPFee }
- }, [trade])
- */
-
- if (!trade) return null
-
- return
- /*
-
-
- Transaction Details
-
-
-
-
- Liquidity Provider Fee
-
-
-
-
- {realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${realizedLPFee.currency.symbol}` : '-'}
-
-
-
-
-
-
-
- Price Impact
-
-
-
-
-
-
-
-
-
-
-
-
- Allowed Slippage
-
-
-
-
- {allowedSlippage.toFixed(2)}%
-
-
-
+ return { expectedOutputAmount, priceImpact }
+ }, [trade]) */
-
-
-
- {trade.tradeType === TradeType.EXACT_INPUT ? Minimum received : Maximum sent }
-
-
-
-
- {trade.tradeType === TradeType.EXACT_INPUT
- ? `${trade.minimumAmountOut(allowedSlippage).toSignificant(6)} ${trade.outputAmount.currency.symbol}`
- : `${trade.maximumAmountIn(allowedSlippage).toSignificant(6)} ${trade.inputAmount.currency.symbol}`}
-
-
-
-
- */
+ return !trade ? null : (
+
+ /*
+
+
+
+
+ Expected Output
+
+
+
+
+ {expectedOutputAmount
+ ? `${expectedOutputAmount.toSignificant(6)} ${expectedOutputAmount.currency.symbol}`
+ : '-'}
+
+
+
+
+
+
+ Price Impact
+
+
+
+
+
+
+
+
+
+
+
+
+ {trade.tradeType === TradeType.EXACT_INPUT ? (
+ Minimum received
+ ) : (
+ Maximum sent
+ )}{' '}
+ after slippage ({allowedSlippage.toFixed(2)}%)
+
+
+
+
+ {trade.tradeType === TradeType.EXACT_INPUT
+ ? `${trade.minimumAmountOut(allowedSlippage).toSignificant(6)} ${trade.outputAmount.currency.symbol}`
+ : `${trade.maximumAmountIn(allowedSlippage).toSignificant(6)} ${trade.inputAmount.currency.symbol}`}
+
+
+
+ {!trade?.gasUseEstimateUSD || !chainId || !SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId) ? null : (
+
+
+ Network Fee
+
+
+
+ ~${trade.gasUseEstimateUSD.toFixed(2)}
+
+
+
+ )}
+
+ */
+ )
}
diff --git a/src/custom/components/swap/AdvancedSwapDetailsDropdown/AdvancedSwapDetailsDropdownMod.tsx b/src/custom/components/swap/AdvancedSwapDetailsDropdown/AdvancedSwapDetailsDropdownMod.tsx
deleted file mode 100644
index 791eb54f54..0000000000
--- a/src/custom/components/swap/AdvancedSwapDetailsDropdown/AdvancedSwapDetailsDropdownMod.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Percent } from '@uniswap/sdk-core'
-import styled from 'styled-components/macro'
-import { useLastTruthy } from 'hooks/useLast'
-import { AdvancedSwapDetails /* , AdvancedSwapDetailsProps */ } from 'components/swap/AdvancedSwapDetails'
-import TradeGp from 'state/swap/TradeGp'
-
-const AdvancedDetailsFooter = styled.div<{ show: boolean }>`
- width: 100%;
- border-bottom-left-radius: 20px;
- border-bottom-right-radius: 20px;
- color: ${({ theme }) => theme.text2};
-`
-
-interface AdvancedSwapDetailsProps {
- trade?: TradeGp
- allowedSlippage: Percent
-}
-
-export default function AdvancedSwapDetailsDropdown({ trade, ...rest }: AdvancedSwapDetailsProps) {
- const lastTrade = useLastTruthy(trade)
-
- return (
-
-
-
- )
-}
diff --git a/src/custom/components/swap/AdvancedSwapDetailsDropdown/index.ts b/src/custom/components/swap/AdvancedSwapDetailsDropdown/index.ts
deleted file mode 100644
index 8a21838efa..0000000000
--- a/src/custom/components/swap/AdvancedSwapDetailsDropdown/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './AdvancedSwapDetailsDropdownMod'
diff --git a/src/custom/components/swap/ConfirmSwapModal/ConfirmSwapModalMod.tsx b/src/custom/components/swap/ConfirmSwapModal/ConfirmSwapModalMod.tsx
index 2d82218068..d35bf37bc7 100644
--- a/src/custom/components/swap/ConfirmSwapModal/ConfirmSwapModalMod.tsx
+++ b/src/custom/components/swap/ConfirmSwapModal/ConfirmSwapModalMod.tsx
@@ -1,8 +1,10 @@
import { Trans } from '@lingui/macro'
+// import { Trade } from '@uniswap/router-sdk'
import { /* Currency, */ Percent /* , TradeType */ } from '@uniswap/sdk-core'
-// import { Trade as V2Trade } from '@uniswap/v2-sdk'
-// import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { ReactNode, useCallback, useMemo } from 'react'
+// import { InterfaceTrade } from 'state/routing/types'
+// import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer'
+
import TransactionConfirmationModal, {
ConfirmationModalContent,
OperationType,
@@ -10,7 +12,8 @@ import TransactionConfirmationModal, {
} from 'components/TransactionConfirmationModal'
import SwapModalFooter from 'components/swap/SwapModalFooter'
import SwapModalHeader from 'components/swap/SwapModalHeader'
-// MOD
+
+// MOD imports
import TradeGp from 'state/swap/TradeGp'
import { useWalletInfo } from 'hooks/useWalletInfo'
@@ -45,7 +48,7 @@ export default function ConfirmSwapModal({
onConfirm,
onDismiss,
recipient,
- priceImpact,
+ priceImpact, // mod
swapErrorMessage,
isOpen,
attemptingTxn,
@@ -53,8 +56,8 @@ export default function ConfirmSwapModal({
PendingTextComponent, // mod
}: {
isOpen: boolean
- // trade: V2Trade | V3Trade | undefined
- // originalTrade: V2Trade | V3Trade | undefined
+ // trade: InterfaceTrade | undefined
+ // originalTrade: Trade | undefined
trade: TradeGp | undefined
originalTrade: TradeGp | undefined
attemptingTxn: boolean
@@ -70,17 +73,6 @@ export default function ConfirmSwapModal({
}) {
const { allowsOffchainSigning } = useWalletInfo()
const showAcceptChanges = useMemo(
- /*
- () =>
- Boolean(
- (trade instanceof V2Trade &&
- originalTrade instanceof V2Trade &&
- tradeMeaningfullyDiffers(trade, originalTrade)) ||
- (trade instanceof V3Trade &&
- originalTrade instanceof V3Trade &&
- tradeMeaningfullyDiffers(trade, originalTrade))
- ),
- */
() => Boolean(trade && originalTrade && tradeMeaningfullyDiffers(trade, originalTrade)),
[originalTrade, trade]
)
diff --git a/src/custom/components/swap/SwapModalFooter/SwapModalFooterMod.tsx b/src/custom/components/swap/SwapModalFooter/SwapModalFooterMod.tsx
index 50cc968d88..ebe9a6c4bc 100644
--- a/src/custom/components/swap/SwapModalFooter/SwapModalFooterMod.tsx
+++ b/src/custom/components/swap/SwapModalFooter/SwapModalFooterMod.tsx
@@ -1,18 +1,19 @@
import { Trans } from '@lingui/macro'
+// import { Trade } from '@uniswap/router-sdk'
// import { Currency, TradeType } from '@uniswap/sdk-core'
-// import { Trade as V2Trade } from '@uniswap/v2-sdk'
-// import { Trade as V3Trade } from '@uniswap/v3-sdk'
-
-// import { ReactNode } from 'react'
+import { ReactNode } from 'react'
import { Text } from 'rebass'
+
import { ButtonError } from 'components/Button'
import { AutoRow } from 'components/Row'
import { SwapCallbackError } from 'components/swap/styleds'
+
+// MOD imports
import { ButtonSize } from 'theme'
export interface SwapModalFooterProps {
onConfirm: () => void
- swapErrorMessage: React.ReactNode | undefined
+ swapErrorMessage: ReactNode | undefined
disabledConfirm: boolean
}
@@ -22,7 +23,7 @@ export default function SwapModalFooter({
disabledConfirm,
}: /*
{
- trade: V2Trade | V3Trade
+ trade: Trade
onConfirm: () => void
swapErrorMessage: ReactNode | undefined
disabledConfirm: boolean
diff --git a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx
index 62bcd6fb46..78a2a0eb3e 100644
--- a/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx
+++ b/src/custom/components/swap/SwapModalHeader/SwapModalHeaderMod.tsx
@@ -1,28 +1,26 @@
import { Trans } from '@lingui/macro'
import { /* Currency, */ Percent, TradeType } from '@uniswap/sdk-core'
-// import { Trade as V2Trade } from '@uniswap/v2-sdk'
-// import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { useContext, useState, useMemo } from 'react'
-import { ArrowDown, AlertTriangle } from 'react-feather'
+import { AlertTriangle, ArrowDown } from 'react-feather'
import { Text } from 'rebass'
+// import { InterfaceTrade } from 'state/routing/types'
import styled, { ThemeContext } from 'styled-components/macro'
+
import { useHigherUSDValue /* , useUSDCValue */ } from 'hooks/useUSDCPrice'
-import { TYPE } from 'theme'
+import { ThemedText } from 'theme'
import { isAddress, shortenAddress } from 'utils'
-import { ButtonPrimary } from 'components/Button'
// import { computeFiatValuePriceImpact } from 'utils/computeFiatValuePriceImpact'
+import { ButtonPrimary } from 'components/Button'
+// import { LightCard } from '../Card'
import { AutoColumn } from 'components/Column'
import { FiatValue } from 'components/CurrencyInputPanel/FiatValue'
import CurrencyLogo from 'components/CurrencyLogo'
import { RowBetween, RowFixed } from 'components/Row'
-import { TruncatedText, SwapShowAcceptChanges } from 'components/swap/styleds'
-
-import { AdvancedSwapDetails } from 'components/swap/AdvancedSwapDetails'
-// import { LightCard } from '../Card'
-
// import TradePrice from 'components/swap/TradePrice'
+import { AdvancedSwapDetails } from 'components/swap/AdvancedSwapDetails'
+import { SwapShowAcceptChanges, TruncatedText } from 'components/swap/styleds'
-// MOD
+// MOD imports
import TradeGp from 'state/swap/TradeGp'
import { AMOUNT_PRECISION, INPUT_OUTPUT_EXPLANATION } from 'constants/index'
import { computeSlippageAdjustedAmounts } from 'utils/prices'
@@ -73,15 +71,16 @@ export default function SwapModalHeader({
allowedSlippage,
recipient,
showAcceptChanges,
- priceImpact,
onAcceptChanges,
+ // mod
+ priceImpact,
LightCard,
HighFeeWarning,
NoImpactWarning,
allowsOffchainSigning,
}: /*
{
- trade: V2Trade | V3Trade
+ trade: InterfaceTrade
allowedSlippage: Percent
recipient: string | null
showAcceptChanges: boolean
@@ -98,9 +97,9 @@ SwapModalHeaderProps) {
const [showInverted, setShowInverted] = useState(false)
+ // const fiatValueInput = useUSDCValue(trade.inputAmount)
+ // const fiatValueOutput = useUSDCValue(trade.outputAmount)
// show fiatValue for unadjusted trade amounts!
- // const fiatValueInput = useUSDCValue(trade.inputAmountWithoutFee)
- // const fiatValueOutput = useUSDCValue(trade.outputAmountWithoutFee)
const fiatValueInput = useHigherUSDValue(trade.inputAmountWithoutFee)
const fiatValueOutput = useHigherUSDValue(trade.outputAmountWithoutFee)
@@ -124,9 +123,9 @@ SwapModalHeaderProps) {
-
+
From
-
+
@@ -146,6 +145,15 @@ SwapModalHeaderProps) {
{formatSmart(trade.inputAmountWithoutFee, AMOUNT_PRECISION)}
+ {/*
+
+
+ {trade.inputAmount.currency.symbol}
+
+
+
+
+ */}
@@ -174,14 +182,19 @@ SwapModalHeaderProps) {
>
-
+
To
-
-
+
+
-
+
+ {/*
+
+ {trade.outputAmount.toSignificant(6)}
+
+ */}
@@ -198,6 +211,14 @@ SwapModalHeaderProps) {
+ {/*
+
+
+
+ */}
{!!exactOutLabel && (
@@ -223,25 +244,20 @@ SwapModalHeaderProps) {
width="90%"
margin="auto"
/>
- {/*
-
- Price
-
+ {/*
- */}
-
+ */}
-
{showAcceptChanges ? (
-
+
Price Updated
-
+
{trade.tradeType === TradeType.EXACT_INPUT ? (
-
+
Output is estimated. You will receive at least{' '}
@@ -264,9 +280,9 @@ SwapModalHeaderProps) {
{' '}
or the swap will not execute. {INPUT_OUTPUT_EXPLANATION}
-
+
) : (
-
+
Input is estimated. You will sell at most{' '}
@@ -276,17 +292,17 @@ SwapModalHeaderProps) {
{/* or the transaction will revert. */}
or the swap will not execute. {INPUT_OUTPUT_EXPLANATION}
-
+
)}
{recipient !== null ? (
-
+
Output will be sent to{' '}
{isAddress(recipient) ? shortenAddress(recipient) : recipient}
-
+
) : null}
{/* High Fee Warning */}
diff --git a/src/custom/components/swap/TradePrice/TradePriceMod.tsx b/src/custom/components/swap/TradePrice/TradePriceMod.tsx
index 6c6d2e226b..3becdd13b5 100644
--- a/src/custom/components/swap/TradePrice/TradePriceMod.tsx
+++ b/src/custom/components/swap/TradePrice/TradePriceMod.tsx
@@ -4,7 +4,9 @@ import { Currency, Price } from '@uniswap/sdk-core'
import { useCallback, useContext } from 'react'
import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components/macro'
-// import { TYPE } from 'theme'
+// import { ThemedText } from 'theme'
+
+// MOD imports
import { formatMax, formatSmart } from 'utils/format' // mod
import { LightGreyText } from 'pages/Swap'
@@ -26,6 +28,12 @@ const StyledPriceContainer = styled.button`
padding: 0;
grid-template-columns: 1fr auto;
grid-gap: 0.25rem;
+ /* display: flex;
+ flex-direction: row;
+ text-align: left;
+ flex-wrap: wrap;
+ padding: 8px 0;
+ user-select: text; */
`
export default function TradePrice({ price, showInverted, fiatValue, setShowInverted }: TradePriceProps) {
@@ -53,7 +61,13 @@ export default function TradePrice({ price, showInverted, fiatValue, setShowInve
const fiatText = ` (≈$${fiatValue})`
return (
-
+ {
+ e.stopPropagation() // dont want this click to affect dropdowns / hovers
+ flipPrice()
+ }}
+ // title={text}
+ >
{/* {text} */}
{baseText}
@@ -61,9 +75,9 @@ export default function TradePrice({ price, showInverted, fiatValue, setShowInve
{fiatValue && {fiatText} }
{' '}
{/* {usdcPrice && (
-
+
(${usdcPrice.toSignificant(6, { groupSeparator: ',' })})
-
+
)} */}
)
diff --git a/src/custom/components/swap/TradePrice/index.tsx b/src/custom/components/swap/TradePrice/index.tsx
index 8d7a3b1427..b63a719d80 100644
--- a/src/custom/components/swap/TradePrice/index.tsx
+++ b/src/custom/components/swap/TradePrice/index.tsx
@@ -2,7 +2,8 @@ import { useMemo } from 'react'
import TradePriceMod, { TradePriceProps } from './TradePriceMod'
import { useHigherUSDValue /* useUSDCValue */ } from 'hooks/useUSDCPrice'
import { formatSmart } from 'utils/format'
-import { tryParseAmount } from 'state/swap/hooks'
+import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
+
import { FIAT_PRECISION } from 'constants/index'
export * from './TradePriceMod'
@@ -13,8 +14,8 @@ export default function TradePrice(props: Omit) {
const priceSide = useMemo(
() =>
!showInverted
- ? tryParseAmount(price.invert().toFixed(price.baseCurrency.decimals), price.baseCurrency)
- : tryParseAmount(price.toFixed(price.quoteCurrency.decimals), price.quoteCurrency),
+ ? tryParseCurrencyAmount(price.invert().toFixed(price.baseCurrency.decimals), price.baseCurrency)
+ : tryParseCurrencyAmount(price.toFixed(price.quoteCurrency.decimals), price.quoteCurrency),
[price, showInverted]
)
// const amount = useUSDCValue(priceSide)
diff --git a/src/custom/components/swap/TradeSummary/RowFee.tsx b/src/custom/components/swap/TradeSummary/RowFee.tsx
index 08971dfeab..6cebab6d4e 100644
--- a/src/custom/components/swap/TradeSummary/RowFee.tsx
+++ b/src/custom/components/swap/TradeSummary/RowFee.tsx
@@ -1,7 +1,7 @@
import { useContext, useMemo } from 'react'
import { CurrencyAmount, Currency, TradeType, Token } from '@uniswap/sdk-core'
import { ThemeContext } from 'styled-components/macro'
-import { TYPE } from 'theme'
+import { ThemedText } from 'theme'
import { formatMax, formatSmart } from 'utils/format'
import TradeGp from 'state/swap/TradeGp'
@@ -81,9 +81,9 @@ export function RowFee({
return (
-
+
Fees {includeGasMessage}
-
+
{showHelpers && (
@@ -91,9 +91,9 @@ export function RowFee({
)}
-
+
{feeToken} {feeUsd && {feeUsd} }
-
+
)
}
diff --git a/src/custom/components/swap/TradeSummary/RowReceivedAfterSlippage.tsx b/src/custom/components/swap/TradeSummary/RowReceivedAfterSlippage.tsx
index 264a8e9366..b8fa170d8f 100644
--- a/src/custom/components/swap/TradeSummary/RowReceivedAfterSlippage.tsx
+++ b/src/custom/components/swap/TradeSummary/RowReceivedAfterSlippage.tsx
@@ -2,7 +2,7 @@ import { useContext, useMemo } from 'react'
import { Percent, TradeType } from '@uniswap/sdk-core'
import { ThemeContext } from 'styled-components/macro'
import { Trans } from '@lingui/macro'
-import { TYPE } from 'theme'
+import { ThemedText } from 'theme'
import { Field } from 'state/swap/actions'
import { getMinimumReceivedTooltip } from 'utils/tooltips'
@@ -50,13 +50,13 @@ export function RowReceivedAfterSlippage({
return (
-
+
{trade.tradeType === TradeType.EXACT_INPUT ? (
Minimum received (incl. fee)
) : (
Maximum sent (incl. fee)
)}
-
+
{showHelpers && (
-
+
{`${formatSmart(swapAmount, AMOUNT_PRECISION) || '-'} ${symbol}`}
-
+
)
}
diff --git a/src/custom/components/swap/TradeSummary/RowSlippage.tsx b/src/custom/components/swap/TradeSummary/RowSlippage.tsx
index 7a23ce2f09..271740339b 100644
--- a/src/custom/components/swap/TradeSummary/RowSlippage.tsx
+++ b/src/custom/components/swap/TradeSummary/RowSlippage.tsx
@@ -2,7 +2,7 @@ import { useContext } from 'react'
import { Percent } from '@uniswap/sdk-core'
import { ThemeContext } from 'styled-components/macro'
import { Trans } from '@lingui/macro'
-import { TYPE } from 'theme'
+import { ThemedText } from 'theme'
import { RowBetween, RowFixed } from 'components/Row'
import { MouseoverTooltipContent } from 'components/Tooltip'
@@ -33,7 +33,7 @@ export function RowSlippage({
return (
-
+
{showSettingOnClick ? (
Slippage tolerance
@@ -41,7 +41,7 @@ export function RowSlippage({
) : (
Slippage tolerance
)}
-
+
-
+
{showSettingOnClick ? (
{displaySlippage}
) : (
{displaySlippage}
)}
-
+
)
}
diff --git a/src/custom/components/swap/UnsupportedCurrencyFooter/UnsupportedCurrencyFooterMod.tsx b/src/custom/components/swap/UnsupportedCurrencyFooter/UnsupportedCurrencyFooterMod.tsx
index 721dd6ab4c..17a6e4744b 100644
--- a/src/custom/components/swap/UnsupportedCurrencyFooter/UnsupportedCurrencyFooterMod.tsx
+++ b/src/custom/components/swap/UnsupportedCurrencyFooter/UnsupportedCurrencyFooterMod.tsx
@@ -6,14 +6,15 @@ import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import Modal from 'components/Modal'
import { AutoRow, RowBetween } from 'components/Row'
-import { useActiveWeb3React } from 'hooks/web3'
-import { ReactNode, useState } from 'react'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { useState } from 'react'
import styled from 'styled-components/macro'
-import { CloseIcon, ExternalLink, TYPE, Z_INDEX } from 'theme'
+import { CloseIcon, ExternalLink, ThemedText, Z_INDEX } from 'theme'
+
import { useIsUnsupportedToken } from 'state/lists/hooks'
// import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
-// MOD
+// MOD imports
import { getEtherscanLink } from 'utils'
export const DetailsFooter = styled.div<{ show: boolean }>`
@@ -39,9 +40,9 @@ const StyledButtonEmpty = styled(ButtonEmpty)`
text-decoration: none;
`
-export const AddressText = styled(TYPE.blue)`
+export const AddressText = styled(ThemedText.Blue)`
font-size: 12px;
- word-break: break-all;
+ word-break: break-all; // mod
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 10px;
@@ -52,9 +53,9 @@ export const AddressText = styled(TYPE.blue)`
export interface UnsupportedCurrencyFooterParams {
show: boolean
currencies: (Currency | null | undefined)[]
- detailsTitle?: ReactNode
- detailsText?: ReactNode
- showDetailsText?: ReactNode
+ detailsTitle?: React.ReactNode
+ detailsText?: React.ReactNode
+ showDetailsText?: React.ReactNode
}
export default function UnsupportedCurrencyFooter({
@@ -65,7 +66,7 @@ export default function UnsupportedCurrencyFooter({
showDetailsText,
}: /* {
show: boolean
- currencies: (Currency | undefined)[]
+ currencies: (Currency | undefined | null)[]
} */
UnsupportedCurrencyFooterParams) {
const { chainId } = useActiveWeb3React()
@@ -78,7 +79,7 @@ UnsupportedCurrencyFooterParams) {
})
: []
- // const unsupportedTokens: { [address: string]: Token } = useUnsupportedTokens()
+ // const unsupportedTokens = useUnsupportedTokens()
const isUnsupportedToken = useIsUnsupportedToken()
@@ -88,10 +89,10 @@ UnsupportedCurrencyFooterParams) {
- {/* Unsupported Assets Assets */}
-
+
+ {/* Unsupported Assets */}
{detailsTitle}
-
+
setShowDetails(false)} />
{tokens.map((token) => {
@@ -104,10 +105,9 @@ UnsupportedCurrencyFooterParams) {
- {token.symbol}
+ {token.symbol}
{chainId && (
- //
{token.address}
- //
)}
@@ -127,22 +126,22 @@ UnsupportedCurrencyFooterParams) {
)
})}
-
+
{/* Some assets are not available through this interface because they may not work well with our smart
contract or we are unable to allow trading for legal reasons. */}
{detailsText}
-
+
setShowDetails(true)}>
- {/*
+ {/*
Read more about unsupported assets
- */}
-
+ */}
+
{showDetailsText}
-
+
)
diff --git a/src/custom/connectors/Fortmatic.ts b/src/custom/connectors/Fortmatic.ts
index cf75c87f92..43d61422f6 100644
--- a/src/custom/connectors/Fortmatic.ts
+++ b/src/custom/connectors/Fortmatic.ts
@@ -1,4 +1,6 @@
-import { FortmaticConnector as FortmaticConnectorCore } from '@web3-react/fortmatic-connector'
+import { FortmaticConnector as FortmaticConnectorCore } from 'web3-react-fortmatic-connector'
+
+// MOD imports
import { isProd, isBarn } from 'utils/environments'
import { SupportedChainId } from 'constants/chains'
diff --git a/src/custom/connectors/index.ts b/src/custom/connectors/index.ts
index 4bf9dd9fdd..4694fc4bd2 100644
--- a/src/custom/connectors/index.ts
+++ b/src/custom/connectors/index.ts
@@ -1,18 +1,26 @@
import { Web3Provider } from '@ethersproject/providers'
import { SafeAppConnector } from '@gnosis.pm/safe-apps-web3-react'
-import { InjectedConnector } from '@web3-react/injected-connector'
-import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
-import { WalletLinkConnector } from '@web3-react/walletlink-connector'
-import { PortisConnector } from '@web3-react/portis-connector'
-
+import { ALL_SUPPORTED_CHAIN_IDS /*, SupportedChainId*/ } from 'constants/chains'
+// import { INFURA_NETWORK_URLS } from 'constants/infura'
+import { InjectedConnector } from 'web3-react-injected-connector'
+import { PortisConnector } from 'web3-react-portis-connector'
+import { WalletConnectConnector } from 'web3-react-walletconnect-connector'
+import { WalletLinkConnector } from 'web3-react-walletlink-connector'
+// import UNISWAP_LOGO_URL from '../assets/svg/logo.svg'
+import getLibrary from '../utils/getLibrary'
import { FortmaticConnector, getFortmaticApiKey } from 'connectors/Fortmatic'
import { NetworkConnector } from 'connectors/NetworkConnector'
+
+// MOD imports
import { AbstractConnector } from '@web3-react/abstract-connector'
export * from '@src/connectors'
export const WALLET_CONNECT_BRIDGE = process.env.WALLET_CONNECT_BRIDGE || 'wss://safe-walletconnect.gnosis.io'
+// const FORMATIC_KEY = process.env.REACT_APP_FORTMATIC_KEY
+const PORTIS_ID = process.env.REACT_APP_PORTIS_ID
+
type RpcNetworks = { [chainId: number]: string }
export function getSupportedChainIds(): number[] {
@@ -72,21 +80,23 @@ const [rpcNetworks, supportedChainIds] = getRpcNetworks()
export const NETWORK_CHAIN_ID = supportedChainIds[0]
export const network = new NetworkConnector({
- urls: rpcNetworks,
- defaultChainId: NETWORK_CHAIN_ID,
+ urls: rpcNetworks, // INFURA_NETWORK_URLS
+ defaultChainId: NETWORK_CHAIN_ID, // 1
})
let networkLibrary: Web3Provider | undefined
export function getNetworkLibrary(): Web3Provider {
- return (networkLibrary = networkLibrary ?? new Web3Provider(network.provider as any))
+ return (networkLibrary = networkLibrary ?? getLibrary(network.provider))
}
-export const injected = new InjectedConnector({ supportedChainIds })
+export const injected = new InjectedConnector({
+ supportedChainIds: ALL_SUPPORTED_CHAIN_IDS,
+})
export const gnosisSafe = new SafeAppConnector()
-// mainnet only
export const walletconnect = new WalletConnectConnector({
+ supportedChainIds: ALL_SUPPORTED_CHAIN_IDS,
rpc: rpcNetworks,
bridge: WALLET_CONNECT_BRIDGE,
qrcode: true,
@@ -100,13 +110,12 @@ export const fortmatic = new FortmaticConnector({
// mainnet only
export const portis = new PortisConnector({
- dAppId: process.env.REACT_APP_PORTIS_ID ?? '',
+ dAppId: PORTIS_ID ?? '',
// TODO: Allow to configure multiple networks in portis
// networks: supportedChainIds
networks: [NETWORK_CHAIN_ID],
})
-// mainnet only
export const walletlink = new WalletLinkConnector({
url: rpcNetworks[NETWORK_CHAIN_ID],
appName: 'CowSwap',
diff --git a/src/custom/constants/chainInfo/chainInfoMod.ts b/src/custom/constants/chainInfo/chainInfoMod.ts
new file mode 100644
index 0000000000..76d23a0b1a
--- /dev/null
+++ b/src/custom/constants/chainInfo/chainInfoMod.ts
@@ -0,0 +1,187 @@
+// import ethereumLogoUrl from 'assets/images/ethereum-logo.png'
+// import arbitrumLogoUrl from 'assets/svg/arbitrum_logo.svg'
+// import optimismLogoUrl from 'assets/svg/optimistic_ethereum.svg'
+// import polygonMaticLogo from 'assets/svg/polygon-matic-logo.svg'
+// import ms from 'ms.macro'
+
+import { SupportedChainId, SupportedL1ChainId, SupportedL2ChainId } from 'constants/chains'
+// import { ARBITRUM_LIST, OPTIMISM_LIST } from 'constants/lists'
+
+// MOD imports
+import EthereumLogo from 'assets/cow-swap/network-mainnet-logo.svg'
+import RinkebyLogo from 'assets/cow-swap/network-rinkeby-logo.svg'
+import GnosisChainLogo from 'assets/cow-swap/network-gnosis-chain-logo.svg'
+import { CHAIN_INFO as UNI_CHAIN_INFO, NetworkType } from '@src/constants/chainInfo'
+
+export * from '@src/constants/chainInfo'
+
+/* export enum NetworkType {
+ L1,
+ L2,
+} */
+
+interface BaseChainInfo {
+ readonly networkType: NetworkType
+ readonly blockWaitMsBeforeWarning?: number
+ readonly docs: string
+ readonly bridge?: string
+ readonly explorer: string
+ readonly infoLink: string
+ readonly logoUrl: string
+ readonly label: string
+ readonly helpCenterUrl?: string
+ readonly nativeCurrency: {
+ name: string // e.g. 'Goerli ETH',
+ symbol: string // e.g. 'gorETH',
+ decimals: number // e.g. 18,
+ }
+}
+
+export interface L1ChainInfo extends BaseChainInfo {
+ readonly networkType: NetworkType.L1
+}
+
+export interface L2ChainInfo extends BaseChainInfo {
+ readonly networkType: NetworkType.L2
+ readonly bridge: string
+ readonly statusPage?: string
+ readonly defaultListUrl: string
+}
+
+export type ChainInfoMap = { readonly [chainId: number]: L1ChainInfo | L2ChainInfo } & {
+ readonly [chainId in SupportedL2ChainId]: L2ChainInfo
+} & { readonly [chainId in SupportedL1ChainId]: L1ChainInfo }
+
+export const CHAIN_INFO: ChainInfoMap = {
+ ...UNI_CHAIN_INFO,
+ [SupportedChainId.MAINNET]: {
+ networkType: NetworkType.L1,
+ docs: 'https://docs.cow.fi/',
+ explorer: 'https://etherscan.io/',
+ infoLink: 'https://cow.fi/',
+ label: 'Ethereum',
+ logoUrl: EthereumLogo,
+ nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
+ },
+ [SupportedChainId.RINKEBY]: {
+ networkType: NetworkType.L1,
+ docs: 'https://docs.cow.fi/',
+ explorer: 'https://rinkeby.etherscan.io/',
+ infoLink: 'https://cow.fi',
+ label: 'Rinkeby',
+ logoUrl: RinkebyLogo,
+ nativeCurrency: { name: 'Rinkeby Ether', symbol: 'rETH', decimals: 18 },
+ },
+ /*[SupportedChainId.ROPSTEN]: {
+ networkType: NetworkType.L1,
+ docs: 'https://docs.uniswap.org/',
+ explorer: 'https://ropsten.etherscan.io/',
+ infoLink: 'https://info.uniswap.org/#/',
+ label: 'Ropsten',
+ logoUrl: ethereumLogoUrl,
+ nativeCurrency: { name: 'Ropsten Ether', symbol: 'ropETH', decimals: 18 },
+ },
+ [SupportedChainId.KOVAN]: {
+ networkType: NetworkType.L1,
+ docs: 'https://docs.uniswap.org/',
+ explorer: 'https://kovan.etherscan.io/',
+ infoLink: 'https://info.uniswap.org/#/',
+ label: 'Kovan',
+ logoUrl: ethereumLogoUrl,
+ nativeCurrency: { name: 'Kovan Ether', symbol: 'kovETH', decimals: 18 },
+ },
+ [SupportedChainId.GOERLI]: {
+ networkType: NetworkType.L1,
+ docs: 'https://docs.uniswap.org/',
+ explorer: 'https://goerli.etherscan.io/',
+ infoLink: 'https://info.uniswap.org/#/',
+ label: 'Görli',
+ logoUrl: ethereumLogoUrl,
+ nativeCurrency: { name: 'Görli Ether', symbol: 'görETH', decimals: 18 },
+ },
+ [SupportedChainId.OPTIMISM]: {
+ networkType: NetworkType.L2,
+ blockWaitMsBeforeWarning: ms`25m`,
+ bridge: 'https://gateway.optimism.io/?chainId=1',
+ defaultListUrl: OPTIMISM_LIST,
+ docs: 'https://optimism.io/',
+ explorer: 'https://optimistic.etherscan.io/',
+ infoLink: 'https://info.uniswap.org/#/optimism/',
+ label: 'Optimism',
+ logoUrl: optimismLogoUrl,
+ statusPage: 'https://optimism.io/status',
+ helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ',
+ nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
+ },
+ [SupportedChainId.OPTIMISTIC_KOVAN]: {
+ networkType: NetworkType.L2,
+ blockWaitMsBeforeWarning: ms`25m`,
+ bridge: 'https://gateway.optimism.io/',
+ defaultListUrl: OPTIMISM_LIST,
+ docs: 'https://optimism.io/',
+ explorer: 'https://optimistic.etherscan.io/',
+ infoLink: 'https://info.uniswap.org/#/optimism/',
+ label: 'Optimistic Kovan',
+ logoUrl: optimismLogoUrl,
+ statusPage: 'https://optimism.io/status',
+ helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ',
+ nativeCurrency: { name: 'Optimistic Kovan Ether', symbol: 'kovOpETH', decimals: 18 },
+ },
+ [SupportedChainId.ARBITRUM_ONE]: {
+ networkType: NetworkType.L2,
+ blockWaitMsBeforeWarning: ms`10m`,
+ bridge: 'https://bridge.arbitrum.io/',
+ docs: 'https://offchainlabs.com/',
+ explorer: 'https://arbiscan.io/',
+ infoLink: 'https://info.uniswap.org/#/arbitrum',
+ label: 'Arbitrum',
+ logoUrl: arbitrumLogoUrl,
+ defaultListUrl: ARBITRUM_LIST,
+ helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
+ nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
+ },
+ [SupportedChainId.ARBITRUM_RINKEBY]: {
+ networkType: NetworkType.L2,
+ blockWaitMsBeforeWarning: ms`10m`,
+ bridge: 'https://bridge.arbitrum.io/',
+ docs: 'https://offchainlabs.com/',
+ explorer: 'https://rinkeby-explorer.arbitrum.io/',
+ infoLink: 'https://info.uniswap.org/#/arbitrum/',
+ label: 'Arbitrum Rinkeby',
+ logoUrl: arbitrumLogoUrl,
+ defaultListUrl: ARBITRUM_LIST,
+ helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
+ nativeCurrency: { name: 'Rinkeby Arbitrum Ether', symbol: 'rinkArbETH', decimals: 18 },
+ },
+ [SupportedChainId.POLYGON]: {
+ networkType: NetworkType.L1,
+ blockWaitMsBeforeWarning: ms`10m`,
+ bridge: 'https://wallet.polygon.technology/bridge',
+ docs: 'https://polygon.io/',
+ explorer: 'https://polygonscan.com/',
+ infoLink: 'https://info.uniswap.org/#/polygon/',
+ label: 'Polygon',
+ logoUrl: polygonMaticLogo,
+ nativeCurrency: { name: 'Polygon Matic', symbol: 'MATIC', decimals: 18 },
+ },
+ [SupportedChainId.POLYGON_MUMBAI]: {
+ networkType: NetworkType.L1,
+ blockWaitMsBeforeWarning: ms`10m`,
+ bridge: 'https://wallet.polygon.technology/bridge',
+ docs: 'https://polygon.io/',
+ explorer: 'https://mumbai.polygonscan.com/',
+ infoLink: 'https://info.uniswap.org/#/polygon/',
+ label: 'Polygon Mumbai',
+ logoUrl: polygonMaticLogo,
+ nativeCurrency: { name: 'Polygon Mumbai Matic', symbol: 'mMATIC', decimals: 18 },
+ },*/
+ [SupportedChainId.XDAI]: {
+ networkType: NetworkType.L1,
+ docs: 'https://docs.gnosischain.com/',
+ explorer: 'https://blockscout.com/xdai/mainnet/',
+ infoLink: 'https://www.xdaichain.com/',
+ label: 'Gnosis Chain',
+ logoUrl: GnosisChainLogo,
+ nativeCurrency: { name: 'xDai', symbol: 'XDAI', decimals: 18 },
+ },
+}
diff --git a/src/custom/constants/chainInfo/index.ts b/src/custom/constants/chainInfo/index.ts
new file mode 100644
index 0000000000..455333e6bf
--- /dev/null
+++ b/src/custom/constants/chainInfo/index.ts
@@ -0,0 +1 @@
+export * from './chainInfoMod'
diff --git a/src/custom/constants/chains/chainsMod.ts b/src/custom/constants/chains/chainsMod.ts
index 275e1d3930..d0d5ac393e 100644
--- a/src/custom/constants/chains/chainsMod.ts
+++ b/src/custom/constants/chains/chainsMod.ts
@@ -1,195 +1,80 @@
-// import ethereumLogoUrl from 'assets/images/ethereum-logo.png'
-// import arbitrumLogoUrl from 'assets/svg/arbitrum_logo.svg'
-// import optimismLogoUrl from 'assets/svg/optimistic_ethereum.svg'
-// import ms from 'ms.macro'
+/**
+ * List of all the networks supported by the CowSwap Interface
+ */
+
+import { CHAIN_IDS_TO_NAMES as UNI_CHAIN_IDS_TO_NAMES } from '@src/constants/chains'
-import EthereumLogo from 'assets/cow-swap/network-mainnet-logo.svg' // mod
-import RinkebyLogo from 'assets/cow-swap/network-rinkeby-logo.svg' // mod
-import GnosisChainLogo from 'assets/cow-swap/network-gnosis-chain-logo.svg' // mod
export * from '@src/constants/chains'
export enum SupportedChainId {
MAINNET = 1,
// ROPSTEN = 3,
RINKEBY = 4,
- // GOERLI = 5,
- // KOVAN = 42,
+ /* GOERLI = 5,
+ KOVAN = 42,
+
+ ARBITRUM_ONE = 42161,
+ ARBITRUM_RINKEBY = 421611,
+
+ OPTIMISM = 10,
+ OPTIMISTIC_KOVAN = 69,
- // ARBITRUM_ONE = 42161,
- // ARBITRUM_RINKEBY = 421611,
- // OPTIMISM = 10,
- // OPTIMISTIC_KOVAN = 69,
+ POLYGON = 137,
+ POLYGON_MUMBAI = 80001, */
XDAI = 100,
}
-export const ALL_SUPPORTED_CHAIN_IDS: SupportedChainId[] = [
- SupportedChainId.MAINNET,
- // SupportedChainId.ROPSTEN,
- SupportedChainId.RINKEBY,
- // SupportedChainId.GOERLI,
- // SupportedChainId.KOVAN,
+export const CHAIN_IDS_TO_NAMES = {
+ ...UNI_CHAIN_IDS_TO_NAMES,
+ [SupportedChainId.XDAI]: 'gnosis_chain',
+ /* [SupportedChainId.MAINNET]: 'mainnet',
+ [SupportedChainId.ROPSTEN]: 'ropsten',
+ [SupportedChainId.RINKEBY]: 'rinkeby',
+ [SupportedChainId.GOERLI]: 'goerli',
+ [SupportedChainId.KOVAN]: 'kovan',
+ [SupportedChainId.POLYGON]: 'polygon',
+ [SupportedChainId.POLYGON_MUMBAI]: 'polygon_mumbai',
+ [SupportedChainId.ARBITRUM_ONE]: 'arbitrum',
+ [SupportedChainId.ARBITRUM_RINKEBY]: 'arbitrum_rinkeby',
+ [SupportedChainId.OPTIMISM]: 'optimism',
+ [SupportedChainId.OPTIMISTIC_KOVAN]: 'optimistic_kovan', */
+}
- // SupportedChainId.ARBITRUM_ONE,
- // SupportedChainId.ARBITRUM_RINKEBY,
- // SupportedChainId.OPTIMISM,
- // SupportedChainId.OPTIMISTIC_KOVAN,
+/**
+ * Array of all the supported chain IDs
+ */
+export const ALL_SUPPORTED_CHAIN_IDS: SupportedChainId[] = Object.values(SupportedChainId).filter(
+ (id) => typeof id === 'number'
+) as SupportedChainId[]
- SupportedChainId.XDAI,
-]
+export const SUPPORTED_GAS_ESTIMATE_CHAIN_IDS = [SupportedChainId.MAINNET /*, SupportedChainId.POLYGON*/]
+/**
+ * All the chain IDs that are running the Ethereum protocol.
+ */
export const L1_CHAIN_IDS = [
SupportedChainId.MAINNET,
// SupportedChainId.ROPSTEN,
SupportedChainId.RINKEBY,
- // SupportedChainId.GOERLI,
- // SupportedChainId.KOVAN,
+ /* SupportedChainId.GOERLI,
+ SupportedChainId.KOVAN,
+ SupportedChainId.POLYGON,
+ SupportedChainId.POLYGON_MUMBAI, */
SupportedChainId.XDAI,
] as const
export type SupportedL1ChainId = typeof L1_CHAIN_IDS[number]
+/**
+ * Controls some L2 specific behavior, e.g. slippage tolerance, special UI behavior.
+ * The expectation is that all of these networks have immediate transaction confirmation.
+ */
export const L2_CHAIN_IDS = [
- // SupportedChainId.ARBITRUM_ONE,
- // SupportedChainId.ARBITRUM_RINKEBY,
- // SupportedChainId.OPTIMISM,
- // SupportedChainId.OPTIMISTIC_KOVAN,
+ /* SupportedChainId.ARBITRUM_ONE,
+ SupportedChainId.ARBITRUM_RINKEBY,
+ SupportedChainId.OPTIMISM,
+ SupportedChainId.OPTIMISTIC_KOVAN, */
] as const
export type SupportedL2ChainId = typeof L2_CHAIN_IDS[number]
-
-export interface L1ChainInfo {
- readonly blockWaitMsBeforeWarning?: number
- readonly docs: string
- readonly explorer: string
- readonly infoLink: string
- readonly label: string
- readonly logoUrl?: string
- readonly rpcUrls?: string[]
- readonly nativeCurrency: {
- name: string // 'Goerli ETH',
- symbol: string // 'gorETH',
- decimals: number //18,
- }
-}
-export interface L2ChainInfo extends L1ChainInfo {
- readonly bridge: string
- readonly logoUrl: string
- readonly statusPage?: string
-}
-
-export type ChainInfo = { readonly [chainId: number]: (L1ChainInfo & { logoUrl: string }) | L2ChainInfo } & {
- readonly [chainId in SupportedL2ChainId]: L2ChainInfo
-} & { readonly [chainId in SupportedL1ChainId]: L1ChainInfo }
-
-export const CHAIN_INFO: ChainInfo = {
- /* [SupportedChainId.ARBITRUM_ONE]: {
- blockWaitMsBeforeWarning: ms`10m`,
- bridge: 'https://bridge.arbitrum.io/',
- docs: 'https://offchainlabs.com/',
- explorer: 'https://arbiscan.io/',
- infoLink: 'https://info.uniswap.org/#/arbitrum',
- label: 'Arbitrum',
- logoUrl: arbitrumLogoUrl,
- nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
- rpcUrls: ['https://arb1.arbitrum.io/rpc'],
- },
- [SupportedChainId.ARBITRUM_RINKEBY]: {
- blockWaitMsBeforeWarning: ms`10m`,
- bridge: 'https://bridge.arbitrum.io/',
- docs: 'https://offchainlabs.com/',
- explorer: 'https://rinkeby-explorer.arbitrum.io/',
- infoLink: 'https://info.uniswap.org/#/arbitrum/',
- label: 'Arbitrum Rinkeby',
- logoUrl: arbitrumLogoUrl,
- nativeCurrency: { name: 'Rinkeby ArbETH', symbol: 'rinkArbETH', decimals: 18 },
- rpcUrls: ['https://rinkeby.arbitrum.io/rpc'],
- }, */
- [SupportedChainId.MAINNET]: {
- docs: 'https://docs.uniswap.org/',
- explorer: 'https://gnosis-protocol.io/mainnet',
- infoLink: '',
- label: 'Ethereum',
- logoUrl: EthereumLogo, // mod
- nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
- },
- [SupportedChainId.RINKEBY]: {
- docs: 'https://docs.uniswap.org/',
- explorer: 'https://gnosis-protocol.io/rinkeby',
- infoLink: '',
- label: 'Rinkeby',
- nativeCurrency: { name: 'Rinkeby ETH', symbol: 'rinkETH', decimals: 18 },
- logoUrl: RinkebyLogo, // mod
- },
- /* [SupportedChainId.ROPSTEN]: {
- docs: 'https://docs.uniswap.org/',
- explorer: 'https://ropsten.etherscan.io/',
- infoLink: 'https://info.uniswap.org/#/',
- label: 'Ropsten',
- nativeCurrency: { name: 'Ropsten ETH', symbol: 'ropETH', decimals: 18 },
- },
- [SupportedChainId.KOVAN]: {
- docs: 'https://docs.uniswap.org/',
- explorer: 'https://kovan.etherscan.io/',
- infoLink: 'https://info.uniswap.org/#/',
- label: 'Kovan',
- nativeCurrency: { name: 'Kovan ETH', symbol: 'kovETH', decimals: 18 },
- },
- [SupportedChainId.GOERLI]: {
- docs: 'https://docs.uniswap.org/',
- explorer: 'https://goerli.etherscan.io/',
- infoLink: 'https://info.uniswap.org/#/',
- label: 'Görli',
- nativeCurrency: { name: 'Görli ETH', symbol: 'görETH', decimals: 18 },
- },
- [SupportedChainId.OPTIMISM]: {
- blockWaitMsBeforeWarning: ms`10m`,
- bridge: 'https://gateway.optimism.io/',
- docs: 'https://optimism.io/',
- explorer: 'https://optimistic.etherscan.io/',
- infoLink: 'https://info.uniswap.org/#/optimism',
- label: 'OΞ',
- logoUrl: optimismLogoUrl,
- nativeCurrency: { name: 'Optimistic ETH', symbol: 'ETH', decimals: 18 },
- rpcUrls: ['https://mainnet.optimism.io'],
- statusPage: 'https://optimism.io/status',
- },
- [SupportedChainId.OPTIMISTIC_KOVAN]: {
- blockWaitMsBeforeWarning: ms`10m`,
- bridge: 'https://gateway.optimism.io/',
- docs: 'https://optimism.io/',
- explorer: 'https://optimistic.etherscan.io/',
- infoLink: 'https://info.uniswap.org/#/optimism',
- label: 'Optimistic Kovan',
- rpcUrls: ['https://kovan.optimism.io'],
- logoUrl: optimismLogoUrl,
- nativeCurrency: { name: 'Optimistic kovETH', symbol: 'kovOpETH', decimals: 18 },
- statusPage: 'https://optimism.io/status',
- }, */
- [SupportedChainId.XDAI]: {
- docs: 'https://docs.uniswap.org/',
- explorer: 'https://blockscout.com/xdai/mainnet/',
- infoLink: '',
- label: 'Gnosis Chain',
- rpcUrls: ['https://rpc.gnosischain.com/'],
- logoUrl: GnosisChainLogo, // mod
- nativeCurrency: { name: 'xDai', symbol: 'XDAI', decimals: 18 },
- },
-}
-
-export const ARBITRUM_HELP_CENTER_LINK = 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum'
-export const OPTIMISM_HELP_CENTER_LINK =
- 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ'
-
-export const NETWORK_LABELS: { [chainId in SupportedChainId | number]: string } = {
- // [SupportedChainId.MAINNET]: 'Mainnet',
- [SupportedChainId.MAINNET]: 'Ethereum', // mod
- [SupportedChainId.RINKEBY]: 'Rinkeby',
- // [SupportedChainId.ROPSTEN]: 'Ropsten',
- // [SupportedChainId.GOERLI]: 'Görli',
- // [SupportedChainId.KOVAN]: 'Kovan',
- // [SupportedChainId.XDAI]: 'XDai',
- [SupportedChainId.XDAI]: 'Gnosis Chain', // mod
- // [SupportedChainId.ARBITRUM_KOVAN]: 'kArbitrum',
- // [SupportedChainId.ARBITRUM_ONE]: 'Arbitrum One',
-}
diff --git a/src/custom/constants/routing/routingMod.ts b/src/custom/constants/routing/routingMod.ts
index 2fd61d5298..8e5fc8bf5b 100644
--- a/src/custom/constants/routing/routingMod.ts
+++ b/src/custom/constants/routing/routingMod.ts
@@ -1,32 +1,29 @@
// a list of tokens by chain
import { Currency /* , Token */ } from '@uniswap/sdk-core'
+
import { SupportedChainId } from 'constants/chains'
import {
// AMPL,
DAI,
- // DAI_ARBITRUM_ONE,
- // DAI_OPTIMISM,
- // ETH2X_FLI,
- // ExtendedEther,
- // FEI,
- // FRAX,
- // FXS,
- // renBTC,
- // TRIBE,
- USDC,
- // USDC_ARBITRUM,
// USDC_OPTIMISM,
+ // USDC_POLYGON,
USDT,
- // USDT_ARBITRUM_ONE,
- // USDT_OPTIMISM,
+ /* USDT_ARBITRUM_ONE,
+ USDT_OPTIMISM,
+ USDT_POLYGON, */
WBTC,
- // WBTC_ARBITRUM_ONE,
- // WBTC_OPTIMISM,
- WETH9_EXTENDED,
+ /* WBTC_ARBITRUM_ONE,
+ WBTC_OPTIMISM,
+ WBTC_POLYGON,
+ WETH_POLYGON,
+ WETH_POLYGON_MUMBAI, */
+ WRAPPED_NATIVE_CURRENCY,
COW,
+ USDC_MAINNET,
} from 'constants/tokens'
-import { USDC_XDAI, /* USDT_XDAI, */ WBTC_XDAI, WETH_XDAI } from 'utils/xdai/constants'
+// MOD imports
+import { USDC_XDAI, WBTC_XDAI, WETH_XDAI } from 'utils/xdai/constants'
import { DAI_RINKEBY, USDC_RINKEBY, USDT_RINKEBY } from 'utils/rinkeby/constants'
/* type ChainTokenList = {
@@ -37,25 +34,45 @@ type ChainCurrencyList = {
readonly [chainId: number]: Currency[]
}
-/* const WETH_ONLY: ChainTokenList = Object.fromEntries(
- Object.entries(WETH9_EXTENDED).map(([key, value]) => [key, [value]])
+/* const WRAPPED_NATIVE_CURRENCIES_ONLY: ChainTokenList = Object.fromEntries(
+ Object.entries(WRAPPED_NATIVE_CURRENCY).map(([key, value]) => [key, [value]])
) */
// used to construct intermediary pairs for trading
/* export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = {
- ...WETH_ONLY,
- [SupportedChainId.MAINNET]: [...WETH_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC],
- [SupportedChainId.OPTIMISM]: [...WETH_ONLY[SupportedChainId.OPTIMISM], DAI_OPTIMISM, USDT_OPTIMISM, WBTC_OPTIMISM],
- [SupportedChainId.ARBITRUM_ONE]: [
- ...WETH_ONLY[SupportedChainId.ARBITRUM_ONE],
+ ...WRAPPED_NATIVE_CURRENCIES_ONLY,
+ [SupportedChainId.MAINNET]: [
+ ...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.MAINNET],
+ DAI,
+ USDC_MAINNET,
+ USDT,
+ WBTC,
+ ],
+ [SupportedChainId.OPTIMISM]: [
+ ...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.OPTIMISM],
+ DAI_OPTIMISM,
+ USDT_OPTIMISM,
+ WBTC_OPTIMISM,
+ ],
+ [SupportedChainId.ARBITRUM_ONE]: [
+ ...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.ARBITRUM_ONE],
DAI_ARBITRUM_ONE,
USDT_ARBITRUM_ONE,
WBTC_ARBITRUM_ONE,
],
+ [SupportedChainId.POLYGON]: [
+ ...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.POLYGON],
+ DAI_POLYGON,
+ USDC_POLYGON,
+ USDT_POLYGON,
+ WETH_POLYGON,
+ ],
}
export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = {
[SupportedChainId.MAINNET]: {
'0xF16E4d813f4DcfDe4c5b44f305c908742De84eF0': [ETH2X_FLI],
+ [rETH2.address]: [sETH2],
+ [SWISE.address]: [sETH2],
[FEI.address]: [TRIBE],
[TRIBE.address]: [FEI],
[FRAX.address]: [FXS],
@@ -70,7 +87,7 @@ export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: To
*/
/* export const CUSTOM_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = {
[SupportedChainId.MAINNET]: {
- [AMPL.address]: [DAI, WETH9_EXTENDED[SupportedChainId.MAINNET]],
+ [AMPL.address]: [DAI, WRAPPED_NATIVE_CURRENCY[SupportedChainId.MAINNET]],
},
} */
@@ -79,71 +96,82 @@ export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: To
*/
export const COMMON_BASES: ChainCurrencyList = {
[SupportedChainId.MAINNET]: [
- // ExtendedEther.onChain(SupportedChainId.MAINNET),
+ // nativeOnChain(SupportedChainId.MAINNET),
DAI,
COW[SupportedChainId.MAINNET],
- USDC,
+ USDC_MAINNET,
USDT,
WBTC,
- WETH9_EXTENDED[SupportedChainId.MAINNET],
+ WRAPPED_NATIVE_CURRENCY[SupportedChainId.MAINNET],
],
- // [SupportedChainId.ROPSTEN]: [
- // // ExtendedEther.onChain(SupportedChainId.ROPSTEN),
- // WETH9_EXTENDED[SupportedChainId.ROPSTEN],
- // ],
+ /* [SupportedChainId.ROPSTEN]: [
+ nativeOnChain(SupportedChainId.ROPSTEN),
+ WRAPPED_NATIVE_CURRENCY[SupportedChainId.ROPSTEN],
+ ], */
[SupportedChainId.RINKEBY]: [
// ExtendedEther.onChain(SupportedChainId.RINKEBY),
- WETH9_EXTENDED[SupportedChainId.RINKEBY],
+ WRAPPED_NATIVE_CURRENCY[SupportedChainId.RINKEBY],
COW[SupportedChainId.RINKEBY],
DAI_RINKEBY,
USDC_RINKEBY,
USDT_RINKEBY,
],
- // [SupportedChainId.GOERLI]: [ExtendedEther.onChain(SupportedChainId.GOERLI), WETH9_EXTENDED[SupportedChainId.GOERLI]],
- // [SupportedChainId.KOVAN]: [ExtendedEther.onChain(SupportedChainId.KOVAN), WETH9_EXTENDED[SupportedChainId.KOVAN]],
- // [SupportedChainId.ARBITRUM_ONE]: [
- // ExtendedEther.onChain(SupportedChainId.ARBITRUM_ONE),
- // DAI_ARBITRUM_ONE,
- // USDC_ARBITRUM,
- // USDT_ARBITRUM_ONE,
- // WBTC_ARBITRUM_ONE,
- // WETH9_EXTENDED[SupportedChainId.ARBITRUM_ONE],
- // ],
- // [SupportedChainId.ARBITRUM_RINKEBY]: [
- // ExtendedEther.onChain(SupportedChainId.ARBITRUM_RINKEBY),
- // WETH9_EXTENDED[SupportedChainId.ARBITRUM_RINKEBY],
- // ],
- // [SupportedChainId.OPTIMISM]: [
- // ExtendedEther.onChain(SupportedChainId.OPTIMISM),
- // DAI_OPTIMISM,
- // USDC_OPTIMISM,
- // USDT_OPTIMISM,
- // WBTC_OPTIMISM,
- // ],
+ /* [SupportedChainId.GOERLI]: [nativeOnChain(SupportedChainId.GOERLI), WRAPPED_NATIVE_CURRENCY[SupportedChainId.GOERLI]],
+ [SupportedChainId.KOVAN]: [nativeOnChain(SupportedChainId.KOVAN), WRAPPED_NATIVE_CURRENCY[SupportedChainId.KOVAN]],
+ [SupportedChainId.ARBITRUM_ONE]: [
+ nativeOnChain(SupportedChainId.ARBITRUM_ONE),
+ DAI_ARBITRUM_ONE,
+ USDC_ARBITRUM,
+ USDT_ARBITRUM_ONE,
+ WBTC_ARBITRUM_ONE,
+ WRAPPED_NATIVE_CURRENCY[SupportedChainId.ARBITRUM_ONE],
+ ],
+ [SupportedChainId.ARBITRUM_RINKEBY]: [
+ nativeOnChain(SupportedChainId.ARBITRUM_RINKEBY),
+ WRAPPED_NATIVE_CURRENCY[SupportedChainId.ARBITRUM_RINKEBY],
+ ],
+ [SupportedChainId.OPTIMISM]: [
+ nativeOnChain(SupportedChainId.OPTIMISM),
+ DAI_OPTIMISM,
+ USDC_OPTIMISM,
+ USDT_OPTIMISM,
+ WBTC_OPTIMISM,
+ ],
+ [SupportedChainId.OPTIMISTIC_KOVAN]: [nativeOnChain(SupportedChainId.OPTIMISTIC_KOVAN)],
+ [SupportedChainId.POLYGON]: [
+ nativeOnChain(SupportedChainId.POLYGON),
+ WETH_POLYGON,
+ USDC_POLYGON,
+ DAI_POLYGON,
+ USDT_POLYGON,
+ WBTC_POLYGON,
+ ],
+ [SupportedChainId.POLYGON_MUMBAI]: [
+ nativeOnChain(SupportedChainId.POLYGON_MUMBAI),
+ WRAPPED_NATIVE_CURRENCY[SupportedChainId.POLYGON_MUMBAI],
+ WETH_POLYGON_MUMBAI,
+ ], */
[SupportedChainId.XDAI]: [
- // ExtendedEther.onChain(SupportedChainId.XDA),
+ // nativeOnChain(SupportedChainId.XDAI),
USDC_XDAI,
+ WBTC_XDAI,
COW[SupportedChainId.XDAI],
/*USDT_XDAI,*/ WBTC_XDAI,
- WETH9_EXTENDED[100],
WETH_XDAI,
+ WRAPPED_NATIVE_CURRENCY[SupportedChainId.XDAI],
], // mod
- // [SupportedChainId.ARBITRUM_ONE]: [
- // ExtendedEther.onChain(SupportedChainId.ARBITRUM_ONE),
- // WETH9_EXTENDED[SupportedChainId.ARBITRUM_ONE],
- // ],
- // [SupportedChainId.ARBITRUM_RINKEBY]: [
- // ExtendedEther.onChain(SupportedChainId.ARBITRUM_RINKEBY),
- // WETH9_EXTENDED[SupportedChainId.ARBITRUM_RINKEBY],
- // ],
- // [SupportedChainId.OPTIMISM]: [ExtendedEther.onChain(SupportedChainId.OPTIMISM)],
- // [SupportedChainId.OPTIMISTIC_KOVAN]: [ExtendedEther.onChain(SupportedChainId.OPTIMISTIC_KOVAN)],
}
// used to construct the list of all pairs we consider by default in the frontend
/* export const BASES_TO_TRACK_LIQUIDITY_FOR: ChainTokenList = {
- ...WETH_ONLY,
- [SupportedChainId.MAINNET]: [...WETH_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC],
+ ...WRAPPED_NATIVE_CURRENCIES_ONLY,
+ [SupportedChainId.MAINNET]: [
+ ...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.MAINNET],
+ DAI,
+ USDC_MAINNET,
+ USDT,
+ WBTC,
+ ],
}
export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = {
[SupportedChainId.MAINNET]: [
@@ -157,7 +185,7 @@ export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = {
'Compound USD Coin'
),
],
- [USDC, USDT],
+ [USDC_MAINNET, USDT],
[DAI, USDT],
],
} */
diff --git a/src/custom/constants/tokens/index.ts b/src/custom/constants/tokens/index.ts
index 48f4942998..a50b4820dc 100644
--- a/src/custom/constants/tokens/index.ts
+++ b/src/custom/constants/tokens/index.ts
@@ -1,7 +1,7 @@
import { ChainId } from '@uniswap/sdk'
import { WETH9, Token } from '@uniswap/sdk-core'
import { DAI_RINKEBY, USDC_RINKEBY, USDT_RINKEBY, WBTC_RINKEBY } from 'utils/rinkeby/constants'
-import { DAI, USDC as USDC_MAINNET, USDT, WBTC } from '@src/constants/tokens'
+import { DAI, USDC_MAINNET, USDT, WBTC } from '@src/constants/tokens'
import { USDC_XDAI, /*USDT_XDAI,*/ WBTC_XDAI, WETH_XDAI, WXDAI } from 'utils/xdai/constants'
import { SupportedChainId } from 'constants/chains'
import { V_COW_CONTRACT_ADDRESS, COW_CONTRACT_ADDRESS } from 'constants/index'
@@ -112,15 +112,6 @@ export const GNO: Record
= {
[SupportedChainId.RINKEBY]: GNO_RINKEBY,
}
-/**
- * USDC token
- */
-export const USDC_BY_CHAIN: Record = {
- [SupportedChainId.MAINNET]: USDC_MAINNET,
- [SupportedChainId.XDAI]: USDC_XDAI,
- [SupportedChainId.RINKEBY]: USDC_RINKEBY,
-}
-
export const ADDRESS_IMAGE_OVERRIDE = {
// Rinkeby
[DAI_RINKEBY.address]: getTrustImage(DAI.address),
diff --git a/src/custom/constants/tokens/tokensMod.ts b/src/custom/constants/tokens/tokensMod.ts
index 3af9cec80f..8015b09b04 100644
--- a/src/custom/constants/tokens/tokensMod.ts
+++ b/src/custom/constants/tokens/tokensMod.ts
@@ -1,11 +1,31 @@
-import { /* WETH9, */ Token, /* Ether, */ NativeCurrency, Currency } 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 '@src/constants/addresses'
+// import { UNI_ADDRESS } from './addresses'
import { SupportedChainId } from 'constants/chains'
-// MOD
-import { WETH9_EXTENDED as WETH9_EXTENDED_UNI } from '@src/constants/tokens'
-import { WXDAI, XDAI_NAME, XDAI_SYMBOL } from 'utils/xdai/constants'
+// export { USDC_ARBITRUM, USDC_MAINNET, USDC_OPTIMISM, USDC_POLYGON }
+
+// MOD imports
+import { USDC_XDAI, WXDAI, XDAI_NAME, XDAI_SYMBOL } from 'utils/xdai/constants'
+import {
+ ExtendedEther,
+ WRAPPED_NATIVE_CURRENCY as UNI_WRAPPED_NATIVE_CURRENCY,
+ TOKEN_SHORTHANDS as UNI_TOKEN_SHORTHANDS,
+ USDC as UNI_USDC,
+} from '@src/constants/tokens'
export * from '@src/constants/tokens'
@@ -37,27 +57,42 @@ export const DAI_OPTIMISM = new Token(
18,
'DAI',
'Dai stable coin'
+) */
+export const USDC: { [chainId in SupportedChainId]: Token } = {
+ ...UNI_USDC,
+ [SupportedChainId.XDAI]: USDC_XDAI,
+ /* [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',
+ 18,
+ 'DAI',
+ 'Dai Stablecoin'
)
-export const USDC = new Token(
- SupportedChainId.MAINNET,
- '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
- 6,
- 'USDC',
- 'USD//C'
-)
-export const USDC_ARBITRUM = new Token(
- SupportedChainId.ARBITRUM_ONE,
- '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8',
+export const USDT_POLYGON = new Token(
+ SupportedChainId.POLYGON,
+ '0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
6,
- 'USDC',
- 'USD//C'
+ 'USDT',
+ 'Tether USD'
)
-export const USDC_OPTIMISM = new Token(
- SupportedChainId.OPTIMISM,
- '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
- 6,
- 'USDC',
- 'USD//C'
+export const WBTC_POLYGON = new Token(
+ SupportedChainId.POLYGON,
+ '0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6',
+ 8,
+ 'WBTC',
+ 'Wrapped BTC'
)
export const USDT = new Token(
SupportedChainId.MAINNET,
@@ -135,7 +170,7 @@ export const renBTC = new Token(
8,
'renBTC',
'renBTC'
-)
+)
export const ETH2X_FLI = new Token(
SupportedChainId.MAINNET,
'0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD',
@@ -143,6 +178,42 @@ export const ETH2X_FLI = new Token(
'ETH2x-FLI',
'ETH 2x Flexible Leverage Index'
)
+export const sETH2 = new Token(
+ SupportedChainId.MAINNET,
+ '0xFe2e637202056d30016725477c5da089Ab0A043A',
+ 18,
+ 'sETH2',
+ 'StakeWise Staked ETH2'
+)
+export const rETH2 = new Token(
+ SupportedChainId.MAINNET,
+ '0x20BC832ca081b91433ff6c17f85701B6e92486c5',
+ 18,
+ 'rETH2',
+ 'StakeWise Reward ETH2'
+)
+export const SWISE = new Token(
+ SupportedChainId.MAINNET,
+ '0x48C3399719B582dD63eB5AADf12A40B4C3f52FA2',
+ 18,
+ 'SWISE',
+ 'StakeWise'
+)
+export const WETH_POLYGON_MUMBAI = new Token(
+ SupportedChainId.POLYGON_MUMBAI,
+ '0xa6fa4fb5f76172d178d61b04b0ecd319c5d1c0aa',
+ 18,
+ 'WETH',
+ 'Wrapped Ether'
+)
+
+export const WETH_POLYGON = new Token(
+ SupportedChainId.POLYGON,
+ '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619',
+ 18,
+ 'WETH',
+ 'Wrapped Ether'
+)
export const UNI: { [chainId: number]: Token } = {
[SupportedChainId.MAINNET]: new Token(SupportedChainId.MAINNET, UNI_ADDRESS[1], 18, 'UNI', 'Uniswap'),
[SupportedChainId.RINKEBY]: new Token(SupportedChainId.RINKEBY, UNI_ADDRESS[4], 18, 'UNI', 'Uniswap'),
@@ -151,38 +222,111 @@ export const UNI: { [chainId: number]: Token } = {
[SupportedChainId.KOVAN]: new Token(SupportedChainId.KOVAN, UNI_ADDRESS[42], 18, 'UNI', 'Uniswap'),
} */
-export const WETH9_EXTENDED: { [chainId: number]: Token } = {
- ...WETH9_EXTENDED_UNI,
+export const WRAPPED_NATIVE_CURRENCY: { [chainId: number]: Token } = {
+ ...UNI_WRAPPED_NATIVE_CURRENCY,
[SupportedChainId.XDAI]: WXDAI,
+ /* ...WETH9,
+ [SupportedChainId.OPTIMISM]: new Token(
+ SupportedChainId.OPTIMISM,
+ '0x4200000000000000000000000000000000000006',
+ 18,
+ 'WETH',
+ 'Wrapped Ether'
+ ),
+ [SupportedChainId.OPTIMISTIC_KOVAN]: new Token(
+ SupportedChainId.OPTIMISTIC_KOVAN,
+ '0x4200000000000000000000000000000000000006',
+ 18,
+ 'WETH',
+ 'Wrapped Ether'
+ ),
+ [SupportedChainId.ARBITRUM_ONE]: new Token(
+ SupportedChainId.ARBITRUM_ONE,
+ '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
+ 18,
+ 'WETH',
+ 'Wrapped Ether'
+ ),
+ [SupportedChainId.ARBITRUM_RINKEBY]: new Token(
+ SupportedChainId.ARBITRUM_RINKEBY,
+ '0xB47e6A5f8b33b3F17603C83a0535A9dcD7E32681',
+ 18,
+ 'WETH',
+ 'Wrapped Ether'
+ ),
+ [SupportedChainId.POLYGON]: new Token(
+ SupportedChainId.POLYGON,
+ '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
+ 18,
+ 'WMATIC',
+ 'Wrapped MATIC'
+ ),
+ [SupportedChainId.POLYGON_MUMBAI]: new Token(
+ SupportedChainId.POLYGON_MUMBAI,
+ '0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889',
+ 18,
+ 'WMATIC',
+ 'Wrapped MATIC'
+ ), */
+}
+
+function isGnosisChain(chainId: number): chainId is SupportedChainId.XDAI {
+ return chainId === SupportedChainId.XDAI
}
-export class GpEther extends NativeCurrency {
- constructor(chainId: number, decimals = 18, symbol = 'ETH', name = 'Ether') {
- super(chainId, decimals, symbol, name)
+class GnosisChainNativeCurrency extends NativeCurrency {
+ equals(other: Currency): boolean {
+ return other.isNative && other.chainId === this.chainId
}
- public get wrapped(): Token {
- if (this.chainId in WETH9_EXTENDED) return WETH9_EXTENDED[this.chainId]
- throw new Error('Unsupported chain ID')
+ get wrapped(): Token {
+ if (!isGnosisChain(this.chainId)) throw new Error('Not Gnosis Chain')
+ return WRAPPED_NATIVE_CURRENCY[this.chainId]
}
- private static _etherCache: { [chainId: number]: GpEther } = {}
+ public constructor(chainId: number) {
+ if (!isGnosisChain(chainId)) throw new Error('Not Gnosis Chain')
+ super(chainId, 18, XDAI_SYMBOL, XDAI_NAME)
+ }
+}
- public static onChain(chainId: number): GpEther {
- if (this._etherCache[chainId]) return this._etherCache[chainId]
+export class GpEther extends Ether {
+ public get wrapped(): Token {
+ if (this.chainId in WRAPPED_NATIVE_CURRENCY) return WRAPPED_NATIVE_CURRENCY[this.chainId]
+ throw new Error('Unsupported chain ID')
+ }
- switch (chainId) {
- case SupportedChainId.XDAI:
- this._etherCache[chainId] = new GpEther(chainId, 18, XDAI_SYMBOL, XDAI_NAME)
- break
- default:
- this._etherCache[chainId] = new GpEther(chainId)
- }
+ private static _cachedExtendedEther: { [chainId: number]: NativeCurrency } = {}
- return this._etherCache[chainId]
+ public static onChain(chainId: number): ExtendedEther {
+ return this._cachedExtendedEther[chainId] ?? (this._cachedExtendedEther[chainId] = new ExtendedEther(chainId))
}
+}
- public equals(other: Currency): boolean {
- return other.isNative && other.chainId === this.chainId
- }
+const cachedNativeCurrency: { [chainId: number]: NativeCurrency } = {}
+export function nativeOnChain(chainId: number): NativeCurrency {
+ return (
+ cachedNativeCurrency[chainId] ??
+ (cachedNativeCurrency[chainId] = isGnosisChain(chainId)
+ ? new GnosisChainNativeCurrency(chainId)
+ : ExtendedEther.onChain(chainId))
+ )
+}
+
+export const TOKEN_SHORTHANDS: { [shorthand: string]: { [chainId in SupportedChainId]?: string } } = {
+ USDC: {
+ ...UNI_TOKEN_SHORTHANDS['USDC'],
+ [SupportedChainId.XDAI]: USDC_XDAI.address,
+ /*[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/custom/hooks/Tokens/TokensMod.ts b/src/custom/hooks/Tokens/TokensMod.ts
new file mode 100644
index 0000000000..44478f3944
--- /dev/null
+++ b/src/custom/hooks/Tokens/TokensMod.ts
@@ -0,0 +1,173 @@
+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, useTokenFromMapOrNetwork } from 'lib/hooks/useCurrency'
+import { getTokenFilter } from 'lib/hooks/useTokenList/filtering'
+import { useMemo } from 'react'
+
+import { useAllLists, useCombinedActiveList, useInactiveListUrls } from 'state/lists/hooks'
+import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
+import { useUserAddedTokens } from 'state/user/hooks' */
+import { /*TokenAddressMap,*/ useUnsupportedTokenList } from 'state/lists/hooks'
+
+// MOD imports
+import { useTokensFromMap } from '@src/hooks/Tokens'
+
+export * from '@src/hooks/Tokens'
+
+// reduce token map into standard address <-> Token mapping, optionally include user added tokens
+/* function useTokensFromMap(tokenMap: TokenAddressMap, includeUserAdded: boolean): { [address: string]: Token } {
+ const { chainId } = useActiveWeb3React()
+ const userAddedTokens = useUserAddedTokens()
+
+ return useMemo(() => {
+ if (!chainId) return {}
+
+ // reduce to just tokens
+ const mapWithoutUrls = Object.keys(tokenMap[chainId] ?? {}).reduce<{ [address: string]: Token }>(
+ (newMap, address) => {
+ newMap[address] = tokenMap[chainId][address].token
+ return newMap
+ },
+ {}
+ )
+
+ if (includeUserAdded) {
+ return (
+ userAddedTokens
+ // reduce into all ALL_TOKENS filtered by the current chain
+ .reduce<{ [address: string]: Token }>(
+ (tokenMap, token) => {
+ tokenMap[token.address] = token
+ return tokenMap
+ },
+ // must make a copy because reduce modifies the map, and we do not
+ // want to make a copy in every iteration
+ { ...mapWithoutUrls }
+ )
+ )
+ }
+
+ return mapWithoutUrls
+ }, [chainId, userAddedTokens, tokenMap, includeUserAdded])
+}
+
+export function useAllTokens(): { [address: string]: Token } {
+ const allTokens = useCombinedActiveList()
+ return useTokensFromMap(allTokens, true)
+}
+
+type BridgeInfo = Record<
+ SupportedChainId,
+ {
+ tokenAddress: string
+ originBridgeAddress: string
+ destBridgeAddress: string
+ }
+> */
+
+export function useUnsupportedTokens(): { [address: string]: Token } {
+ // const { chainId } = useActiveWeb3React()
+ // const listsByUrl = useAllLists()
+ const unsupportedTokensMap = useUnsupportedTokenList()
+ const unsupportedTokens = useTokensFromMap(unsupportedTokensMap, false)
+
+ // checks the default L2 lists to see if `bridgeInfo` has an L1 address value that is unsupported
+ /* const l2InferredBlockedTokens: typeof unsupportedTokens = useMemo(() => {
+ if (!chainId || !L2_CHAIN_IDS.includes(chainId)) {
+ return {}
+ }
+
+ if (!listsByUrl) {
+ return {}
+ }
+
+ const listUrl = CHAIN_INFO[chainId as SupportedL2ChainId].defaultListUrl
+ const { current: list } = listsByUrl[listUrl]
+ if (!list) {
+ return {}
+ }
+
+ const unsupportedSet = new Set(Object.keys(unsupportedTokens))
+
+ return list.tokens.reduce((acc, tokenInfo) => {
+ const bridgeInfo = tokenInfo.extensions?.bridgeInfo as unknown as BridgeInfo
+ if (
+ bridgeInfo &&
+ bridgeInfo[SupportedChainId.MAINNET] &&
+ bridgeInfo[SupportedChainId.MAINNET].tokenAddress &&
+ unsupportedSet.has(bridgeInfo[SupportedChainId.MAINNET].tokenAddress)
+ ) {
+ const address = bridgeInfo[SupportedChainId.MAINNET].tokenAddress
+ // don't rely on decimals--it's possible that a token could be bridged w/ different decimals on the L2
+ return { ...acc, [address]: new Token(SupportedChainId.MAINNET, address, tokenInfo.decimals) }
+ }
+ return acc
+ }, {})
+ }, [chainId, listsByUrl, unsupportedTokens])*/
+
+ return { ...unsupportedTokens /*, ...l2InferredBlockedTokens*/ }
+}
+
+/* export function useSearchInactiveTokenLists(search: string | undefined, minResults = 10): WrappedTokenInfo[] {
+ const lists = useAllLists()
+ const inactiveUrls = useInactiveListUrls()
+ const { chainId } = useActiveWeb3React()
+ const activeTokens = useAllTokens()
+ return useMemo(() => {
+ if (!search || search.trim().length === 0) return []
+ const tokenFilter = getTokenFilter(search)
+ const result: WrappedTokenInfo[] = []
+ const addressSet: { [address: string]: true } = {}
+ for (const url of inactiveUrls) {
+ const list = lists[url].current
+ if (!list) continue
+ for (const tokenInfo of list.tokens) {
+ if (tokenInfo.chainId === chainId && tokenFilter(tokenInfo)) {
+ const wrapped: WrappedTokenInfo = new WrappedTokenInfo(tokenInfo, list)
+ if (!(wrapped.address in activeTokens) && !addressSet[wrapped.address]) {
+ addressSet[wrapped.address] = true
+ result.push(wrapped)
+ if (result.length >= minResults) return result
+ }
+ }
+ }
+ }
+ return result
+ }, [activeTokens, chainId, inactiveUrls, lists, minResults, search])
+}
+
+export function useIsTokenActive(token: Token | undefined | null): boolean {
+ const activeTokens = useAllTokens()
+
+ if (!activeTokens || !token) {
+ return false
+ }
+
+ return !!activeTokens[token.address]
+}
+
+// Check if currency is included in custom list from user storage
+export function useIsUserAddedToken(currency: Currency | undefined | null): boolean {
+ const userAddedTokens = useUserAddedTokens()
+
+ if (!currency) {
+ return false
+ }
+
+ return !!userAddedTokens.find((token) => currency.equals(token))
+}
+
+// undefined if invalid or does not exist
+// null if loading or null was passed
+// otherwise returns the token
+export function useToken(tokenAddress?: string | null): Token | null | undefined {
+ const tokens = useAllTokens()
+ return useTokenFromMapOrNetwork(tokens, tokenAddress)
+}
+
+export function useCurrency(currencyId?: string | null): Currency | null | undefined {
+ const tokens = useAllTokens()
+ return useCurrencyFromMap(tokens, currencyId)
+} */
diff --git a/src/custom/hooks/Tokens/index.ts b/src/custom/hooks/Tokens/index.ts
new file mode 100644
index 0000000000..e1c2157d99
--- /dev/null
+++ b/src/custom/hooks/Tokens/index.ts
@@ -0,0 +1 @@
+export * from './TokensMod'
diff --git a/src/custom/hooks/index.tsx b/src/custom/hooks/index.tsx
index 18ed6254c8..52bfb4825b 100644
--- a/src/custom/hooks/index.tsx
+++ b/src/custom/hooks/index.tsx
@@ -1,9 +1,10 @@
import { useMemo } from 'react'
import { Web3Provider } from '@ethersproject/providers'
-import { useActiveWeb3React } from '@src/hooks/web3'
+import { useActiveWeb3React } from 'hooks/web3'
export * from '@src/hooks/web3'
+// TODO: I think this might not be really necessary, consider removing it
/**
* Provides a Web3Provider instance for active web3 connection, if any
* Contrary to `getNetworkLibrary` that returns it for the default chainId
diff --git a/src/custom/hooks/useApproveCallback/index.ts b/src/custom/hooks/useApproveCallback/index.ts
index 2b958691a4..3430c0978e 100644
--- a/src/custom/hooks/useApproveCallback/index.ts
+++ b/src/custom/hooks/useApproveCallback/index.ts
@@ -1,5 +1,5 @@
import { Currency, CurrencyAmount, MaxUint256, Percent } from '@uniswap/sdk-core'
-import { useActiveWeb3React } from '@src/hooks/web3'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { Field } from '@src/state/swap/actions'
import { computeSlippageAdjustedAmounts } from 'utils/prices'
import { useMemo } from 'react'
@@ -7,12 +7,13 @@ import { GP_VAULT_RELAYER, V_COW_CONTRACT_ADDRESS } from 'constants/index'
import TradeGp from 'state/swap/TradeGp'
import { ApproveCallbackParams, useApproveCallback } from './useApproveCallbackMod'
-export { ApprovalState, useApproveCallback } from './useApproveCallbackMod'
import { ClaimType } from 'state/claim/hooks'
import { supportedChainId } from 'utils/supportedChainId'
import { EnhancedUserClaimData } from 'pages/Claim/types'
+export { ApprovalState, useApproveCallback } from './useApproveCallbackMod'
+
type ApproveCallbackFromTradeParams = Pick<
ApproveCallbackParams,
'openTransactionConfirmationModal' | 'closeModals' | 'amountToCheckAgainstAllowance'
diff --git a/src/custom/hooks/useApproveCallback/useApproveCallbackMod.ts b/src/custom/hooks/useApproveCallback/useApproveCallbackMod.ts
index 823d803199..236e68439f 100644
--- a/src/custom/hooks/useApproveCallback/useApproveCallbackMod.ts
+++ b/src/custom/hooks/useApproveCallback/useApproveCallbackMod.ts
@@ -1,12 +1,20 @@
-import { MaxUint256 } from '@ethersproject/constants'
-import { TransactionResponse } from '@ethersproject/providers'
-import { BigNumber } from '@ethersproject/bignumber'
+// import { Trade } from '@uniswap/router-sdk'
import { Currency, CurrencyAmount /* , Percent, TradeType */ } from '@uniswap/sdk-core'
// import { Trade as V2Trade } from '@uniswap/v2-sdk'
// import { Trade as V3Trade } from '@uniswap/v3-sdk'
+// import useSwapApproval, { useSwapApprovalOptimizedTrade } from 'lib/hooks/swap/useSwapApproval'
+// import { ApprovalState, useApproval } from 'lib/hooks/useApproval'
import { useCallback, useMemo } from 'react'
-// import { SWAP_ROUTER_ADDRESSES, V2_ROUTER_ADDRESS } from 'constants/addresses'
+// import { TransactionType } from '../state/transactions/actions'
+// import { useHasPendingApproval, useTransactionAdder } from '../state/transactions/hooks'
+// export { ApprovalState } from 'lib/hooks/useApproval'
+
+// MOD imports
+import { MaxUint256 } from '@ethersproject/constants'
+import { TransactionResponse } from '@ethersproject/providers'
+import { BigNumber } from '@ethersproject/bignumber'
+
import { useHasPendingApproval, useTransactionAdder } from 'state/enhancedTransactions/hooks'
import { calculateGasMargin } from 'utils/calculateGasMargin'
import { useTokenContract } from 'hooks/useContract'
@@ -34,6 +42,18 @@ export interface ApproveCallbackParams {
amountToCheckAgainstAllowance?: CurrencyAmount
}
+/* function useGetAndTrackApproval(getApproval: ReturnType[1]) {
+ const addTransaction = useTransactionAdder()
+ return useCallback(() => {
+ return getApproval().then((pending) => {
+ if (pending) {
+ const { response, tokenAddress, spenderAddress: spender } = pending
+ addTransaction(response, { type: TransactionType.APPROVAL, tokenAddress, spender })
+ }
+ })
+ }, [addTransaction, getApproval])
+} */
+
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback({
openTransactionConfirmationModal,
@@ -49,6 +69,7 @@ export function useApproveCallback({
const spenderCurrency = useCurrency(spender)
// TODO: Nice to have, can be deleted
+ // eslint-disable-next-line no-lone-blocks
{
process.env.NODE_ENV !== 'production' &&
console.debug(`
@@ -143,7 +164,7 @@ export function useApproveCallback({
return (
tokenContract
.approve(spender, useExact ? amountToApprove.quotient.toString() : MaxUint256, {
- gasLimit: calculateGasMargin(chainId, estimatedGas),
+ gasLimit: calculateGasMargin(estimatedGas),
})
.then((response: TransactionResponse) => {
addTransaction({
@@ -217,7 +238,7 @@ export function useApproveCallback({
return (
tokenContract
.approve(spender, '0', {
- gasLimit: calculateGasMargin(chainId, estimatedGas),
+ gasLimit: calculateGasMargin(estimatedGas),
})
.then((response: TransactionResponse) => {
addTransaction({
@@ -249,30 +270,21 @@ export function useApproveCallback({
return { approvalState, approve, revokeApprove, isPendingApproval: pendingApproval }
}
-// wraps useApproveCallback in the context of a swap
-// export function useApproveCallbackFromTrade(
-// openTransactionConfirmationModal: (message: string) => void,
-// closeModals: () => void,
-// trade: V2Trade | V3Trade | undefined,
-// allowedSlippage: Percent
-// ) {
-// const { chainId } = useActiveWeb3React()
-// const v3SwapRouterAddress = chainId ? SWAP_ROUTER_ADDRESSES[chainId] : undefined
+/* export function useApprovalOptimizedTrade(
+ trade: Trade | undefined,
+ allowedSlippage: Percent
+) {
+ return useSwapApprovalOptimizedTrade(trade, allowedSlippage, useHasPendingApproval)
+}
-// const amountToApprove = useMemo(
-// () => (trade && trade.inputAmount.currency.isToken ? trade.maximumAmountIn(allowedSlippage) : undefined),
-// [trade, allowedSlippage]
-// )
-// return useApproveCallback(
-// openTransactionConfirmationModal,
-// closeModals,
-// amountToApprove,
-// chainId
-// ? trade instanceof V2Trade
-// ? V2_ROUTER_ADDRESS[chainId]
-// : trade instanceof V3Trade
-// ? v3SwapRouterAddress
-// : undefined
-// : undefined
-// )
-// }
+export function useApproveCallbackFromTrade(
+ trade:
+ | V2Trade
+ | V3Trade
+ | Trade
+ | undefined,
+ allowedSlippage: Percent
+): [ApprovalState, () => Promise] {
+ const [approval, getApproval] = useSwapApproval(trade, allowedSlippage, useHasPendingApproval)
+ return [approval, useGetAndTrackApproval(getApproval)]
+} */
diff --git a/src/custom/hooks/useChangeNetworks.ts b/src/custom/hooks/useChangeNetworks.ts
index 25c1be86ef..bfde2ffd6c 100644
--- a/src/custom/hooks/useChangeNetworks.ts
+++ b/src/custom/hooks/useChangeNetworks.ts
@@ -1,94 +1,88 @@
-import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
-import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
+import { useCallback, useEffect, useRef } from 'react'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useActiveWeb3React } from 'hooks/web3'
-import { useAppSelector } from 'state/hooks'
-import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
import { switchToNetwork } from 'utils/switchToNetwork'
-import { supportedChainId } from 'utils/supportedChainId'
-import { useWalletModalToggle } from '../state/application/hooks'
-
-type ChangeNetworksParams = Pick, 'account' | 'chainId' | 'library'>
-
-export default function useChangeNetworks({ account, chainId: preChainId, library }: ChangeNetworksParams) {
- const { error } = useWeb3React() // MOD: check unsupported network
- const nodeRef = useRef()
-
- const [localOpen, setLocalOpen] = useState(false)
- const isModalOpen = localOpen
-
- const toggleWalletModal = useWalletModalToggle()
- const closeModal = useCallback(() => setLocalOpen(false), [])
- const openModal = useCallback(() => setLocalOpen(true), [])
- const toggleModal = useCallback(() => {
- if (isModalOpen) {
- closeModal()
- } else {
- openModal()
- }
- }, [closeModal, isModalOpen, openModal])
-
- useOnClickOutside(nodeRef, isModalOpen ? closeModal : undefined)
-
- const implements3085 = useAppSelector((state) => state.application.implements3085)
-
- // MOD: get supported chain and check unsupported
- const [chainId, isUnsupportedChain] = useMemo(() => {
- const chainId = supportedChainId(preChainId)
-
- return [chainId, error instanceof UnsupportedChainIdError] // Mod - return if chainId is unsupported
- }, [preChainId, error])
+import { useModalOpen, useToggleModal } from '../state/application/hooks'
+import { CHAIN_INFO } from 'constants/chainInfo'
+import useParsedQueryString from '@src/hooks/useParsedQueryString'
+import usePrevious from '@src/hooks/usePrevious'
+import { addPopup, ApplicationModal } from '@src/state/application/reducer'
+import { useAppDispatch } from '@src/state/hooks'
+import { replaceURLParam } from '@src/utils/routes'
+import { getChainNameFromId, getParsedChainId } from 'components/Header/NetworkSelector'
+import { useHistory } from 'react-router-dom'
+
+type ChangeNetworksParams = Pick, 'chainId' | 'library'>
+
+/**
+ * Hook extracted from Header/NetworkSelector component pretty much verbatim
+ *
+ * @param chainId
+ * @param library
+ */
+export default function useChangeNetworks({ chainId, library }: ChangeNetworksParams) {
+ const parsedQs = useParsedQueryString()
+ const { urlChain, urlChainId } = getParsedChainId(parsedQs)
+ const prevChainId = usePrevious(chainId)
+ const node = useRef()
+ const open = useModalOpen(ApplicationModal.NETWORK_SELECTOR)
+ const toggle = useToggleModal(ApplicationModal.NETWORK_SELECTOR)
+ useOnClickOutside(node, open ? toggle : undefined)
+
+ const history = useHistory()
const info = chainId ? CHAIN_INFO[chainId] : undefined
- const showSelector = Boolean(!account || implements3085)
- const mainnetInfo = CHAIN_INFO[SupportedChainId.MAINNET]
-
- const conditionalToggle = useCallback(() => {
- if (showSelector) {
- toggleModal()
- }
- }, [showSelector, toggleModal])
-
- // MOD: checks if a requested network switch was sent
- // used for when user disconnected and selects a network internally
- // if 3085 supported, will connect wallet and change network
- const [queuedNetworkSwitch, setQueuedNetworkSwitch] = useState(null)
-
- const networkCallback = useCallback(
- (supportedChainId) => {
- if (!account) {
- toggleWalletModal()
- return setQueuedNetworkSwitch(supportedChainId)
- } else if (implements3085 && library && supportedChainId) {
- switchToNetwork({ library, chainId: supportedChainId })
-
- return isModalOpen && closeModal()
- }
-
- return
+ const dispatch = useAppDispatch()
+
+ const handleChainSwitch = useCallback(
+ (targetChain: number, skipToggle?: boolean) => {
+ if (!library) return
+ switchToNetwork({ library, chainId: targetChain })
+ .then(() => {
+ if (!skipToggle) {
+ toggle()
+ }
+ history.replace({
+ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(targetChain)),
+ })
+ })
+ .catch((error) => {
+ console.error('Failed to switch networks', error)
+
+ // we want app network <-> chainId param to be in sync, so if user changes the network by changing the URL
+ // but the request fails, revert the URL back to current chainId
+ if (chainId) {
+ history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
+ }
+
+ if (!skipToggle) {
+ toggle()
+ }
+
+ dispatch(addPopup({ content: { failedSwitchNetwork: targetChain }, key: `failed-network-switch` }))
+ })
},
- [account, implements3085, library, toggleWalletModal, isModalOpen, closeModal]
+ [dispatch, library, toggle, history, chainId]
)
- // if wallet supports 3085
useEffect(() => {
- if (queuedNetworkSwitch && account && chainId && implements3085) {
- networkCallback(queuedNetworkSwitch)
- setQueuedNetworkSwitch(null)
+ if (!chainId || !prevChainId) return
+
+ // when network change originates from wallet or dropdown selector, just update URL
+ if (chainId !== prevChainId) {
+ history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
+ // otherwise assume network change originates from URL
+ } else if (urlChainId && urlChainId !== chainId) {
+ handleChainSwitch(urlChainId, true)
}
- }, [networkCallback, queuedNetworkSwitch, chainId, account, implements3085])
+ }, [chainId, urlChainId, prevChainId, handleChainSwitch, history])
- return {
- callback: networkCallback,
- conditionalToggle,
- openModal,
- closeModal,
- isModalOpen,
- isUnsupportedChain,
- chainInfo: info,
- mainnetInfo,
- showSelector,
- nodeRef,
- }
+ // set chain parameter on initial load if not there
+ useEffect(() => {
+ if (chainId && !urlChainId) {
+ history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
+ }
+ }, [chainId, history, urlChainId, urlChain])
+ return { chainId, library, node, open, toggle, info, handleChainSwitch }
}
diff --git a/src/custom/hooks/useContract.ts b/src/custom/hooks/useContract/index.ts
similarity index 94%
rename from src/custom/hooks/useContract.ts
rename to src/custom/hooks/useContract/index.ts
index a4d49afd29..3e1bd862a2 100644
--- a/src/custom/hooks/useContract.ts
+++ b/src/custom/hooks/useContract/index.ts
@@ -1,22 +1,25 @@
import { Contract } from '@ethersproject/contracts'
import { Web3Provider } from '@ethersproject/providers'
-import { useActiveWeb3React } from 'hooks/web3'
-
-import { useContract } from '@src/hooks/useContract'
import { GP_SETTLEMENT_CONTRACT_ADDRESS, V_COW_CONTRACT_ADDRESS } from 'constants/index'
import { SupportedChainId as ChainId } from 'constants/chains'
+import GPv2_SETTLEMENT_ABI from 'abis/GPv2Settlement.json'
+import V_COW_ABI from 'abis/vCow.json'
import ENS_ABI from 'abis/ens-registrar.json'
-import { getContract } from 'utils'
import ERC20_ABI from 'abis/erc20.json'
import ERC20_BYTES32_ABI from 'abis/erc20_bytes32.json'
+import { Erc20, GPv2Settlement, VCow } from 'abis/types'
-import { GPv2Settlement, Erc20, VCow } from 'abis/types'
-import GPv2_SETTLEMENT_ABI from 'abis/GPv2Settlement.json'
-import V_COW_ABI from 'abis/vCow.json'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { getContract } from 'utils'
+
+import { useContract } from '@src/hooks/useContract'
export * from '@src/hooks/useContract'
+export * from './useContractMod'
+
+// Custom (non-MOD) hooks
export function useGP2SettlementContract(): GPv2Settlement | null {
const { chainId } = useActiveWeb3React()
@@ -38,8 +41,6 @@ export function useENSRegistrarContract(withSignerIfPossible?: boolean): Contrac
if (chainId) {
switch (chainId) {
case ChainId.MAINNET:
- // case ChainId.GOERLI:
- // case ChainId.ROPSTEN:
case ChainId.RINKEBY:
address = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'
break
diff --git a/src/custom/hooks/useContract/useContractMod.ts b/src/custom/hooks/useContract/useContractMod.ts
new file mode 100644
index 0000000000..8d7efa0dae
--- /dev/null
+++ b/src/custom/hooks/useContract/useContractMod.ts
@@ -0,0 +1,159 @@
+// import { Contract } from '@ethersproject/contracts'
+// import IUniswapV2PairJson from '@uniswap/v2-core/build/IUniswapV2Pair.json'
+// import IUniswapV2Router02Json from '@uniswap/v2-periphery/build/IUniswapV2Router02.json'
+// import QuoterJson from '@uniswap/v3-periphery/artifacts/contracts/lens/Quoter.sol/Quoter.json'
+// import TickLensJson from '@uniswap/v3-periphery/artifacts/contracts/lens/TickLens.sol/TickLens.json'
+// import UniswapInterfaceMulticallJson from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json'
+// import NonfungiblePositionManagerJson from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
+// import V3MigratorJson from '@uniswap/v3-periphery/artifacts/contracts/V3Migrator.sol/V3Migrator.json'
+// import ARGENT_WALLET_DETECTOR_ABI from 'abis/argent-wallet-detector.json'
+// import EIP_2612 from 'abis/eip_2612.json'
+// import ENS_PUBLIC_RESOLVER_ABI from 'abis/ens-public-resolver.json'
+import ENS_ABI from 'abis/ens-registrar.json'
+// import ERC20_ABI from 'abis/erc20.json'
+// import ERC20_BYTES32_ABI from 'abis/erc20_bytes32.json'
+// import ERC721_ABI from 'abis/erc721.json'
+// import ERC1155_ABI from 'abis/erc1155.json'
+// import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Erc721, Erc1155, Weth } from 'abis/types'
+// import WETH_ABI from 'abis/weth.json'
+// import {
+// ARGENT_WALLET_DETECTOR_ADDRESS,
+// ENS_REGISTRAR_ADDRESSES,
+// MULTICALL_ADDRESS,
+// NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
+// QUOTER_ADDRESSES,
+// TICK_LENS_ADDRESSES,
+// V2_ROUTER_ADDRESS,
+// V3_MIGRATOR_ADDRESSES,
+// } from 'constants/addresses'
+// import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+// import { useMemo } from 'react'
+// import { NonfungiblePositionManager, Quoter, TickLens, UniswapInterfaceMulticall } from 'types/v3'
+// import { V3Migrator } from 'types/v3/V3Migrator'
+
+// import { getContract } from 'utils'
+
+// MOD imports
+import { useContract } from '@src/hooks/useContract'
+import { SupportedChainId as ChainId } from 'constants/chains'
+
+/* const { abi: IUniswapV2PairABI } = IUniswapV2PairJson
+const { abi: IUniswapV2Router02ABI } = IUniswapV2Router02Json
+const { abi: QuoterABI } = QuoterJson
+const { abi: TickLensABI } = TickLensJson
+const { abi: MulticallABI } = UniswapInterfaceMulticallJson
+const { abi: NFTPositionManagerABI } = NonfungiblePositionManagerJson
+const { abi: V2MigratorABI } = V3MigratorJson
+
+// returns null on errors
+export function useContract(
+ addressOrAddressMap: string | { [chainId: number]: string } | undefined,
+ ABI: any,
+ withSignerIfPossible = true
+): T | null {
+ const { library, account, chainId } = useActiveWeb3React()
+
+ return useMemo(() => {
+ if (!addressOrAddressMap || !ABI || !library || !chainId) return null
+ let address: string | undefined
+ if (typeof addressOrAddressMap === 'string') address = addressOrAddressMap
+ else address = addressOrAddressMap[chainId]
+ if (!address) return null
+ try {
+ return getContract(address, ABI, library, withSignerIfPossible && account ? account : undefined)
+ } catch (error) {
+ console.error('Failed to get contract', error)
+ return null
+ }
+ }, [addressOrAddressMap, ABI, library, chainId, withSignerIfPossible, account]) as T
+}
+
+export function useV2MigratorContract() {
+ return useContract(V3_MIGRATOR_ADDRESSES, V2MigratorABI, true)
+}
+
+export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean) {
+ return useContract(tokenAddress, ERC20_ABI, withSignerIfPossible)
+}
+
+export function useWETHContract(withSignerIfPossible?: boolean) {
+ const { chainId } = useActiveWeb3React()
+ return useContract(
+ chainId ? WRAPPED_NATIVE_CURRENCY[chainId]?.address : undefined,
+ WETH_ABI,
+ withSignerIfPossible
+ )
+}
+
+export function useERC721Contract(nftAddress?: string) {
+ return useContract(nftAddress, ERC721_ABI, false)
+}
+
+export function useERC1155Contract(nftAddress?: string) {
+ return useContract(nftAddress, ERC1155_ABI, false)
+}
+
+export function useArgentWalletDetectorContract() {
+ return useContract(ARGENT_WALLET_DETECTOR_ADDRESS, ARGENT_WALLET_DETECTOR_ABI, false)
+} */
+
+export function useENSRegistrarContract(withSignerIfPossible?: boolean) {
+ // return useContract(ENS_REGISTRAR_ADDRESSES, ENS_ABI, withSignerIfPossible)
+ const { chainId } = useActiveWeb3React()
+ let address: string | undefined
+ if (chainId) {
+ switch (chainId) {
+ case ChainId.MAINNET:
+ case ChainId.RINKEBY:
+ address = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'
+ break
+ case ChainId.XDAI:
+ address = '0x25D2252Ec30de7830b6825D6b4A08E70a581cD6a'
+ break
+ }
+ }
+ return useContract(address, ENS_ABI, withSignerIfPossible)
+}
+
+/* export function useENSResolverContract(address: string | undefined, withSignerIfPossible?: boolean) {
+ return useContract(address, ENS_PUBLIC_RESOLVER_ABI, withSignerIfPossible)
+}
+
+export function useBytes32TokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
+ return useContract(tokenAddress, ERC20_BYTES32_ABI, withSignerIfPossible)
+}
+
+export function useEIP2612Contract(tokenAddress?: string): Contract | null {
+ return useContract(tokenAddress, EIP_2612, false)
+}
+
+export function usePairContract(pairAddress?: string, withSignerIfPossible?: boolean): Contract | null {
+ return useContract(pairAddress, IUniswapV2PairABI, withSignerIfPossible)
+}
+
+export function useV2RouterContract(): Contract | null {
+ return useContract(V2_ROUTER_ADDRESS, IUniswapV2Router02ABI, true)
+}
+
+export function useInterfaceMulticall() {
+ return useContract(MULTICALL_ADDRESS, MulticallABI, false) as UniswapInterfaceMulticall
+}
+
+export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean): NonfungiblePositionManager | null {
+ return useContract(
+ NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
+ NFTPositionManagerABI,
+ withSignerIfPossible
+ )
+}
+
+export function useV3Quoter() {
+ return useContract(QUOTER_ADDRESSES, QuoterABI)
+}
+
+export function useTickLens(): TickLens | null {
+ const { chainId } = useActiveWeb3React()
+ const address = chainId ? TICK_LENS_ADDRESSES[chainId] : undefined
+ return useContract(address, TickLensABI) as TickLens | null
+} */
diff --git a/src/custom/hooks/useCowBalanceAndSubsidy.ts b/src/custom/hooks/useCowBalanceAndSubsidy.ts
index 34b58bbee4..c2e8da086f 100644
--- a/src/custom/hooks/useCowBalanceAndSubsidy.ts
+++ b/src/custom/hooks/useCowBalanceAndSubsidy.ts
@@ -6,7 +6,7 @@ import { CurrencyAmount } from '@uniswap/sdk-core'
import { getDiscountFromBalance } from 'components/CowSubsidyModal/utils'
import { useVCowData } from 'state/cowToken/hooks'
import { useTokenBalance } from 'state/wallet/hooks'
-import { useActiveWeb3React } from '.'
+import { useActiveWeb3React } from 'hooks/web3'
import { COW } from 'constants/tokens'
import { SupportedChainId } from 'constants/chains'
diff --git a/src/custom/hooks/useERC20Permit/useERC20PermitMod.ts b/src/custom/hooks/useERC20Permit/useERC20PermitMod.ts
index d79a0604bc..9b78401494 100644
--- a/src/custom/hooks/useERC20Permit/useERC20PermitMod.ts
+++ b/src/custom/hooks/useERC20Permit/useERC20PermitMod.ts
@@ -1,130 +1,133 @@
+import { BigNumber } from '@ethersproject/bignumber'
// import { splitSignature } from '@ethersproject/bytes'
-import { Currency, CurrencyAmount, Percent /*, TradeType, Token */ } from '@uniswap/sdk-core'
+// import { Trade } from '@uniswap/router-sdk'
+import { Currency, CurrencyAmount, Percent /*, TradeType */ } from '@uniswap/sdk-core'
// import { Trade as V2Trade } from '@uniswap/v2-sdk'
// import { Trade as V3Trade } from '@uniswap/v3-sdk'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
// import JSBI from 'jsbi'
+// import { useSingleCallResult } from 'lib/hooks/multicall'
import { useMemo /* , useState */ } from 'react'
-// import { SWAP_ROUTER_ADDRESSES } from 'constants/addresses'
-// import { DAI, UNI, USDC } from 'constants/tokens'
-// import { useSingleCallResult } from 'state/multicall/hooks'
-// import { useEIP2612Contract } from 'hooks/useContract'
-// import useIsArgentWallet from 'hooks/useIsArgentWallet'
-// import useTransactionDeadline from 'hooks/useTransactionDeadline'
-import { useActiveWeb3React } from 'hooks/web3'
+// import { SWAP_ROUTER_ADDRESSES, V3_ROUTER_ADDRESS } from '../constants/addresses'
+// import { DAI, UNI, USDC_MAINNET } from '../constants/tokens'
+// import { useEIP2612Contract } from './useContract'
+// import useIsArgentWallet from './useIsArgentWallet'
+// MOD imports
import { PermitInfo, SignatureData, UseERC20PermitState } from '@src/hooks/useERC20Permit'
import TradeGp from 'state/swap/TradeGp'
import { GP_VAULT_RELAYER } from 'custom/constants'
export * from '@src/hooks/useERC20Permit'
-// enum PermitType {
-// AMOUNT = 1,
-// ALLOWED = 2,
-// }
+/* export enum PermitType {
+ AMOUNT = 1,
+ ALLOWED = 2,
+}
-// // 20 minutes to submit after signing
-// const PERMIT_VALIDITY_BUFFER = 20 * 60
+// 20 minutes to submit after signing
+const PERMIT_VALIDITY_BUFFER = 20 * 60
-// interface PermitInfo {
-// type: PermitType
-// name: string
-// // version is optional, and if omitted, will not be included in the domain
-// version?: string
-// }
+export interface PermitInfo {
+ type: PermitType
+ name: string
+ // version is optional, and if omitted, will not be included in the domain
+ version?: string
+}
-// // todo: read this information from extensions on token lists or elsewhere (permit registry?)
-// const PERMITTABLE_TOKENS: {
-// [chainId: number]: {
-// [checksummedTokenAddress: string]: PermitInfo
-// }
-// } = {
-// [1]: {
-// [USDC.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]: {
-// ['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735']: { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' },
-// [UNI[4].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
-// },
-// [3]: {
-// [UNI[3].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
-// ['0x07865c6E87B9F70255377e024ace6630C1Eaa37F']: { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
-// },
-// [5]: {
-// [UNI[5].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
-// },
-// [42]: {
-// [UNI[42].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
-// },
-// }
+// todo: read this information from extensions on token lists or elsewhere (permit registry?)
+const PERMITTABLE_TOKENS: {
+ [chainId: number]: {
+ [checksummedTokenAddress: string]: PermitInfo
+ }
+} = {
+ 1: {
+ [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: {
+ '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735': { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' },
+ [UNI[4].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
+ },
+ 3: {
+ [UNI[3].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
+ '0x07865c6E87B9F70255377e024ace6630C1Eaa37F': { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
+ },
+ 5: {
+ [UNI[5].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
+ },
+ 42: {
+ [UNI[42].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
+ },
+}
-// export enum UseERC20PermitState {
-// // returned for any reason, e.g. it is an argent wallet, or the currency does not support it
-// NOT_APPLICABLE,
-// LOADING,
-// NOT_SIGNED,
-// SIGNED,
-// }
+export enum UseERC20PermitState {
+ // returned for any reason, e.g. it is an argent wallet, or the currency does not support it
+ NOT_APPLICABLE,
+ LOADING,
+ NOT_SIGNED,
+ SIGNED,
+}
-// interface BaseSignatureData {
-// v: number
-// r: string
-// s: string
-// deadline: number
-// nonce: number
-// owner: string
-// spender: string
-// chainId: number
-// tokenAddress: string
-// permitType: PermitType
-// }
+interface BaseSignatureData {
+ v: number
+ r: string
+ s: string
+ deadline: number
+ nonce: number
+ owner: string
+ spender: string
+ chainId: number
+ tokenAddress: string
+ permitType: PermitType
+}
-// export interface StandardSignatureData extends BaseSignatureData {
-// amount: string
-// }
+interface StandardSignatureData extends BaseSignatureData {
+ amount: string
+}
-// export interface AllowedSignatureData extends BaseSignatureData {
-// allowed: true
-// }
+interface AllowedSignatureData extends BaseSignatureData {
+ allowed: true
+}
-// export type SignatureData = StandardSignatureData | AllowedSignatureData
+export type SignatureData = StandardSignatureData | AllowedSignatureData
-// const EIP712_DOMAIN_TYPE = [
-// { name: 'name', type: 'string' },
-// { name: 'version', type: 'string' },
-// { name: 'chainId', type: 'uint256' },
-// { name: 'verifyingContract', type: 'address' },
-// ]
+const EIP712_DOMAIN_TYPE = [
+ { name: 'name', type: 'string' },
+ { name: 'version', type: 'string' },
+ { name: 'chainId', type: 'uint256' },
+ { name: 'verifyingContract', type: 'address' },
+]
-// const EIP712_DOMAIN_TYPE_NO_VERSION = [
-// { name: 'name', type: 'string' },
-// { name: 'chainId', type: 'uint256' },
-// { name: 'verifyingContract', type: 'address' },
-// ]
+const EIP712_DOMAIN_TYPE_NO_VERSION = [
+ { name: 'name', type: 'string' },
+ { name: 'chainId', type: 'uint256' },
+ { name: 'verifyingContract', type: 'address' },
+]
-// const EIP2612_TYPE = [
-// { name: 'owner', type: 'address' },
-// { name: 'spender', type: 'address' },
-// { name: 'value', type: 'uint256' },
-// { name: 'nonce', type: 'uint256' },
-// { name: 'deadline', type: 'uint256' },
-// ]
+const EIP2612_TYPE = [
+ { name: 'owner', type: 'address' },
+ { name: 'spender', type: 'address' },
+ { name: 'value', type: 'uint256' },
+ { name: 'nonce', type: 'uint256' },
+ { name: 'deadline', type: 'uint256' },
+]
-// const PERMIT_ALLOWED_TYPE = [
-// { name: 'holder', type: 'address' },
-// { name: 'spender', type: 'address' },
-// { name: 'nonce', type: 'uint256' },
-// { name: 'expiry', type: 'uint256' },
-// { name: 'allowed', type: 'bool' },
-// ]
+const PERMIT_ALLOWED_TYPE = [
+ { name: 'holder', type: 'address' },
+ { name: 'spender', type: 'address' },
+ { name: 'nonce', type: 'uint256' },
+ { name: 'expiry', type: 'uint256' },
+ { name: 'allowed', type: 'bool' },
+] */
/* eslint-disable @typescript-eslint/no-unused-vars */
export function useERC20Permit(
currencyAmount: CurrencyAmount | null | undefined,
spender: string | null | undefined,
+ transactionDeadline: BigNumber | undefined,
overridePermitInfo: PermitInfo | undefined | null
): {
signatureData: SignatureData | null
@@ -139,170 +142,163 @@ export function useERC20Permit(
}
}
/* eslint-enable @typescript-eslint/no-unused-vars */
-// const { account, chainId, library } = useActiveWeb3React()
-// const transactionDeadline = useTransactionDeadline()
-// const tokenAddress = currencyAmount?.currency?.isToken ? currencyAmount.currency.address : undefined
-// const eip2612Contract = useEIP2612Contract(tokenAddress)
-// const isArgentWallet = useIsArgentWallet()
-// const nonceInputs = useMemo(() => [account ?? undefined], [account])
-// const tokenNonceState = useSingleCallResult(eip2612Contract, 'nonces', nonceInputs)
-// const permitInfo =
-// overridePermitInfo ?? (chainId && tokenAddress ? PERMITTABLE_TOKENS[chainId]?.[tokenAddress] : undefined)
-
-// const [signatureData, setSignatureData] = useState(null)
+/* const { account, chainId, library } = useActiveWeb3React()
+ const tokenAddress = currencyAmount?.currency?.isToken ? currencyAmount.currency.address : undefined
+ const eip2612Contract = useEIP2612Contract(tokenAddress)
+ const isArgentWallet = useIsArgentWallet()
+ const nonceInputs = useMemo(() => [account ?? undefined], [account])
+ const tokenNonceState = useSingleCallResult(eip2612Contract, 'nonces', nonceInputs)
+ const permitInfo =
+ overridePermitInfo ?? (chainId && tokenAddress ? PERMITTABLE_TOKENS[chainId]?.[tokenAddress] : undefined)
-// return useMemo(() => {
-// if (
-// isArgentWallet ||
-// !currencyAmount ||
-// !eip2612Contract ||
-// !account ||
-// !chainId ||
-// !transactionDeadline ||
-// !library ||
-// !tokenNonceState.valid ||
-// !tokenAddress ||
-// !spender ||
-// !permitInfo
-// ) {
-// return {
-// state: UseERC20PermitState.NOT_APPLICABLE,
-// signatureData: null,
-// gatherPermitSignature: null,
-// }
-// }
+ const [signatureData, setSignatureData] = useState(null)
-// const nonceNumber = tokenNonceState.result?.[0]?.toNumber()
-// if (tokenNonceState.loading || typeof nonceNumber !== 'number') {
-// return {
-// state: UseERC20PermitState.LOADING,
-// signatureData: null,
-// gatherPermitSignature: null,
-// }
-// }
+ return useMemo(() => {
+ if (
+ isArgentWallet ||
+ !currencyAmount ||
+ !eip2612Contract ||
+ !account ||
+ !chainId ||
+ !transactionDeadline ||
+ !library ||
+ !tokenNonceState.valid ||
+ !tokenAddress ||
+ !spender ||
+ !permitInfo
+ ) {
+ return {
+ state: UseERC20PermitState.NOT_APPLICABLE,
+ signatureData: null,
+ gatherPermitSignature: null,
+ }
+ }
-// const isSignatureDataValid =
-// signatureData &&
-// signatureData.owner === account &&
-// signatureData.deadline >= transactionDeadline.toNumber() &&
-// signatureData.tokenAddress === tokenAddress &&
-// signatureData.nonce === nonceNumber &&
-// signatureData.spender === spender &&
-// ('allowed' in signatureData || JSBI.equal(JSBI.BigInt(signatureData.amount), currencyAmount.quotient))
+ const nonceNumber = tokenNonceState.result?.[0]?.toNumber()
+ if (tokenNonceState.loading || typeof nonceNumber !== 'number') {
+ return {
+ state: UseERC20PermitState.LOADING,
+ signatureData: null,
+ gatherPermitSignature: null,
+ }
+ }
-// return {
-// state: isSignatureDataValid ? UseERC20PermitState.SIGNED : UseERC20PermitState.NOT_SIGNED,
-// signatureData: isSignatureDataValid ? signatureData : null,
-// gatherPermitSignature: async function gatherPermitSignature() {
-// const allowed = permitInfo.type === PermitType.ALLOWED
-// const signatureDeadline = transactionDeadline.toNumber() + PERMIT_VALIDITY_BUFFER
-// const value = currencyAmount.quotient.toString()
+ const isSignatureDataValid =
+ signatureData &&
+ signatureData.owner === account &&
+ signatureData.deadline >= transactionDeadline.toNumber() &&
+ signatureData.tokenAddress === tokenAddress &&
+ signatureData.nonce === nonceNumber &&
+ signatureData.spender === spender &&
+ ('allowed' in signatureData || JSBI.equal(JSBI.BigInt(signatureData.amount), currencyAmount.quotient))
-// const message = allowed
-// ? {
-// holder: account,
-// spender,
-// allowed,
-// nonce: nonceNumber,
-// expiry: signatureDeadline,
-// }
-// : {
-// owner: account,
-// spender,
-// value,
-// nonce: nonceNumber,
-// deadline: signatureDeadline,
-// }
-// const domain = permitInfo.version
-// ? {
-// name: permitInfo.name,
-// version: permitInfo.version,
-// verifyingContract: tokenAddress,
-// chainId,
-// }
-// : {
-// name: permitInfo.name,
-// verifyingContract: tokenAddress,
-// chainId,
-// }
-// const data = JSON.stringify({
-// types: {
-// EIP712Domain: permitInfo.version ? EIP712_DOMAIN_TYPE : EIP712_DOMAIN_TYPE_NO_VERSION,
-// Permit: allowed ? PERMIT_ALLOWED_TYPE : EIP2612_TYPE,
-// },
-// domain,
-// primaryType: 'Permit',
-// message,
-// })
+ return {
+ state: isSignatureDataValid ? UseERC20PermitState.SIGNED : UseERC20PermitState.NOT_SIGNED,
+ signatureData: isSignatureDataValid ? signatureData : null,
+ gatherPermitSignature: async function gatherPermitSignature() {
+ const allowed = permitInfo.type === PermitType.ALLOWED
+ const signatureDeadline = transactionDeadline.toNumber() + PERMIT_VALIDITY_BUFFER
+ const value = currencyAmount.quotient.toString()
-// return library
-// .send('eth_signTypedData_v4', [account, data])
-// .then(splitSignature)
-// .then((signature) => {
-// setSignatureData({
-// v: signature.v,
-// r: signature.r,
-// s: signature.s,
-// deadline: signatureDeadline,
-// ...(allowed ? { allowed } : { amount: value }),
-// nonce: nonceNumber,
-// chainId,
-// owner: account,
-// spender,
-// tokenAddress,
-// permitType: permitInfo.type,
-// })
-// })
-// },
-// }
-// }, [
-// currencyAmount,
-// eip2612Contract,
-// account,
-// chainId,
-// isArgentWallet,
-// transactionDeadline,
-// library,
-// tokenNonceState.loading,
-// tokenNonceState.valid,
-// tokenNonceState.result,
-// tokenAddress,
-// spender,
-// permitInfo,
-// signatureData,
-// ])
-// }
+ const message = allowed
+ ? {
+ holder: account,
+ spender,
+ allowed,
+ nonce: nonceNumber,
+ expiry: signatureDeadline,
+ }
+ : {
+ owner: account,
+ spender,
+ value,
+ nonce: nonceNumber,
+ deadline: signatureDeadline,
+ }
+ const domain = permitInfo.version
+ ? {
+ name: permitInfo.name,
+ version: permitInfo.version,
+ verifyingContract: tokenAddress,
+ chainId,
+ }
+ : {
+ name: permitInfo.name,
+ verifyingContract: tokenAddress,
+ chainId,
+ }
+ const data = JSON.stringify({
+ types: {
+ EIP712Domain: permitInfo.version ? EIP712_DOMAIN_TYPE : EIP712_DOMAIN_TYPE_NO_VERSION,
+ Permit: allowed ? PERMIT_ALLOWED_TYPE : EIP2612_TYPE,
+ },
+ domain,
+ primaryType: 'Permit',
+ message,
+ })
-// const REMOVE_V2_LIQUIDITY_PERMIT_INFO: PermitInfo = {
-// version: '1',
-// name: 'Uniswap V2',
-// type: PermitType.AMOUNT,
-// }
-
-// export function useV2LiquidityTokenPermit(
-// liquidityAmount: CurrencyAmount | null | undefined,
-// spender: string | null | undefined
-// ) {
-// return useERC20Permit(liquidityAmount, spender, REMOVE_V2_LIQUIDITY_PERMIT_INFO)
-// }
+ return library
+ .send('eth_signTypedData_v4', [account, data])
+ .then(splitSignature)
+ .then((signature) => {
+ setSignatureData({
+ v: signature.v,
+ r: signature.r,
+ s: signature.s,
+ deadline: signatureDeadline,
+ ...(allowed ? { allowed } : { amount: value }),
+ nonce: nonceNumber,
+ chainId,
+ owner: account,
+ spender,
+ tokenAddress,
+ permitType: permitInfo.type,
+ })
+ })
+ },
+ }
+ }, [
+ currencyAmount,
+ eip2612Contract,
+ account,
+ chainId,
+ isArgentWallet,
+ transactionDeadline,
+ library,
+ tokenNonceState.loading,
+ tokenNonceState.valid,
+ tokenNonceState.result,
+ tokenAddress,
+ spender,
+ permitInfo,
+ signatureData,
+ ])
+} */
export function useERC20PermitFromTrade(
- // trade: V2Trade | V3Trade | undefined,
+ /* trade:
+ | V2Trade
+ | V3Trade
+ | Trade
+ | undefined, */
trade: TradeGp | undefined,
- allowedSlippage: Percent
+ allowedSlippage: Percent,
+ transactionDeadline: BigNumber | undefined
) {
const { chainId } = useActiveWeb3React()
+ /* const swapRouterAddress = chainId
+ ? // v2 router does not support
+ trade instanceof V2Trade
+ ? undefined
+ : trade instanceof V3Trade
+ ? V3_ROUTER_ADDRESS[chainId]
+ : SWAP_ROUTER_ADDRESSES[chainId]
+ : undefined */
const vaultRelayerAddress = chainId ? GP_VAULT_RELAYER[chainId] : undefined
const amountToApprove = useMemo(
() => (trade ? trade.maximumAmountIn(allowedSlippage) : undefined),
[trade, allowedSlippage]
)
- return useERC20Permit(
- amountToApprove,
- // v2 router does not support
- // trade instanceof V2Trade ? undefined : trade instanceof V3Trade ? swapRouterAddress : undefined,
- vaultRelayerAddress,
- null
- )
+ return useERC20Permit(amountToApprove, vaultRelayerAddress, transactionDeadline, null)
}
diff --git a/src/custom/hooks/useFetchListCallback/useFetchListCallbackMod.ts b/src/custom/hooks/useFetchListCallback/useFetchListCallbackMod.ts
index 73c1dcf71a..ec2ad16cae 100644
--- a/src/custom/hooks/useFetchListCallback/useFetchListCallbackMod.ts
+++ b/src/custom/hooks/useFetchListCallback/useFetchListCallbackMod.ts
@@ -1,13 +1,13 @@
import { nanoid } from '@reduxjs/toolkit'
import { TokenList } from '@uniswap/token-lists'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import getTokenList from 'lib/hooks/useTokenList/fetchTokenList'
+import resolveENSContentHash from 'lib/utils/resolveENSContentHash'
import { useCallback } from 'react'
import { useAppDispatch } from 'state/hooks'
import { getNetworkLibrary } from 'connectors'
import { fetchTokenList } from 'state/lists/actions'
-import getTokenList from 'utils/getTokenList'
-import resolveENSContentHash from 'utils/resolveENSContentHash'
-import { useActiveWeb3React } from 'hooks/web3'
export function useFetchListCallback(): (listUrl: string, sendDispatch?: boolean) => Promise {
const { chainId, library } = useActiveWeb3React()
diff --git a/src/custom/hooks/useGetGpPriceStrategy.ts b/src/custom/hooks/useGetGpPriceStrategy.ts
index 1252226b3e..3b79a3a942 100644
--- a/src/custom/hooks/useGetGpPriceStrategy.ts
+++ b/src/custom/hooks/useGetGpPriceStrategy.ts
@@ -2,7 +2,7 @@ import ms from 'ms.macro'
import { useState, useEffect, useCallback } from 'react'
import { DEFAULT_GP_PRICE_STRATEGY } from 'constants/index'
import { getPriceStrategy, PriceStrategy } from 'api/gnosisProtocol/api'
-import { useActiveWeb3React } from 'hooks'
+import { useActiveWeb3React } from 'hooks/web3'
import { supportedChainId } from 'utils/supportedChainId'
import { SupportedChainId } from 'constants/chains'
diff --git a/src/custom/hooks/useIsSwapUnsupported/useIsSwapUnsupportedMod.ts b/src/custom/hooks/useIsSwapUnsupported/useIsSwapUnsupportedMod.ts
index 59b4bdc3ff..0c0da6fddd 100644
--- a/src/custom/hooks/useIsSwapUnsupported/useIsSwapUnsupportedMod.ts
+++ b/src/custom/hooks/useIsSwapUnsupported/useIsSwapUnsupportedMod.ts
@@ -1,5 +1,6 @@
import { Currency /* , Token */ } from '@uniswap/sdk-core'
// import { useMemo } from 'react'
+
import { useIsUnsupportedToken } from 'state/lists/hooks'
/**
@@ -7,24 +8,21 @@ import { useIsUnsupportedToken } from 'state/lists/hooks'
* @param currencyIn the input currency to check
* @param currencyOut the output currency to check
*/
-export function useIsSwapUnsupported(currencyIn?: Currency, currencyOut?: Currency): boolean {
+export function useIsSwapUnsupported(currencyIn?: Currency | null, currencyOut?: Currency | null): boolean {
const isUnsupported = useIsUnsupportedToken()
const tokenIn = currencyIn?.wrapped
const tokenOut = currencyOut?.wrapped
- /*
- const unsupportedTokens: { [address: string]: Token } = useUnsupportedTokens()
-
+ /* const unsupportedTokens = useUnsupportedTokens()
return useMemo(() => {
- // if unsupported list loaded & either token on list, mark as unsupported
- return Boolean(
- unsupportedTokens &&
- ((currencyIn?.isToken && unsupportedTokens[currencyIn.address]) ||
- (currencyOut?.isToken && unsupportedTokens[currencyOut.address]))
- )
- }, [currencyIn, currencyOut, unsupportedTokens])
- */
+ if (!unsupportedTokens) {
+ return false
+ }
+ const currencyInUnsupported = Boolean(currencyIn?.isToken && unsupportedTokens[currencyIn.address])
+ const currencyOutUnsupported = Boolean(currencyOut?.isToken && unsupportedTokens[currencyOut.address])
+ return currencyInUnsupported || currencyOutUnsupported
+ }, [currencyIn, currencyOut, unsupportedTokens]) */
return isUnsupported(tokenIn?.address) || isUnsupported(tokenOut?.address)
}
diff --git a/src/custom/hooks/useNetworkName.ts b/src/custom/hooks/useNetworkName.ts
index 6dcbfac731..a0f180b6c1 100644
--- a/src/custom/hooks/useNetworkName.ts
+++ b/src/custom/hooks/useNetworkName.ts
@@ -1,6 +1,6 @@
import { useMemo } from 'react'
import { useActiveWeb3React } from 'hooks/web3'
-import { CHAIN_INFO } from 'constants/chains'
+import { CHAIN_INFO } from 'constants/chainInfo'
import { supportedChainId } from 'utils/supportedChainId'
export default function useNetworkName(): string | undefined {
diff --git a/src/custom/hooks/usePresignOrder.ts b/src/custom/hooks/usePresignOrder.ts
index 0ca4e34291..19b17f9094 100644
--- a/src/custom/hooks/usePresignOrder.ts
+++ b/src/custom/hooks/usePresignOrder.ts
@@ -35,7 +35,7 @@ export function usePresignOrder(): PresignOrder | null {
})
const txReceipt = await settlementContract.setPreSignature(orderId, true, {
- gasLimit: calculateGasMargin(chainId, estimatedGas),
+ gasLimit: calculateGasMargin(estimatedGas),
})
console.log('Sent transaction for presigning', orderId, txReceipt)
diff --git a/src/custom/hooks/useSwapCallback.ts b/src/custom/hooks/useSwapCallback.ts
index 9deb4ef102..343c5e42c7 100644
--- a/src/custom/hooks/useSwapCallback.ts
+++ b/src/custom/hooks/useSwapCallback.ts
@@ -1,17 +1,22 @@
-import { useMemo } from 'react'
-import { Currency, /* Ether as ETHER, */ Percent, TradeType, CurrencyAmount, Token } from '@uniswap/sdk-core'
-import { OrderKind } from '@gnosis.pm/gp-v2-contracts'
+// eslint-disable-next-line no-restricted-imports
+import { Currency, Percent, TradeType, CurrencyAmount, Token } from '@uniswap/sdk-core'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { SwapCallbackState /*, useSwapCallback as useLibSwapCallBack */ } from 'lib/hooks/swap/useSwapCallback'
+import { /* ReactNode, */ useMemo } from 'react'
+
+// import { TransactionType } from '../state/transactions/actions'
+// import { useTransactionAdder } from '../state/transactions/hooks'
+// import { currencyId } from '../utils/currencyId'
+import useENS from '@src/hooks/useENS'
+// import { SignatureData } from './useERC20Permit'
+// import { AnyTrade } from './useSwapCallArguments'
+// import useTransactionDeadline from './useTransactionDeadline'
+// MOD
+import { OrderKind } from '@gnosis.pm/gp-v2-contracts'
import { INITIAL_ALLOWED_SLIPPAGE_PERCENT, NATIVE_CURRENCY_BUY_TOKEN } from 'constants/index'
-
import { AddOrderCallback, AddUnserialisedPendingOrderParams, useAddPendingOrder } from 'state/orders/hooks'
-
-import { SwapCallbackState } from '@src/hooks/useSwapCallback'
-import useENS from '@src/hooks/useENS'
-
-import { useActiveWeb3React } from 'hooks/web3'
import { useWrapEther, Wrap } from 'hooks/useWrapEther'
-
import { computeSlippageAdjustedAmounts } from 'utils/prices'
import { signAndPostOrder } from 'utils/trade'
import TradeGp from 'state/swap/TradeGp'
@@ -94,7 +99,7 @@ interface SwapParams {
}
/**
- * Internal swap function that does the actually swap logic.
+ * Internal swap function that does the actual swap logic.
*
* @param params All the required swap dependencies
* @returns
@@ -250,7 +255,67 @@ async function _swap(params: SwapParams): Promise {
// returns a function that will execute a swap, if the parameters are all valid
// and the user has approved the slippage adjusted input amount for the trade
+/* export function useSwapCallback(
+ trade: AnyTrade | undefined, // trade to execute, required
+ allowedSlippage: Percent, // in bips
+ recipientAddressOrName: string | null, // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
+ signatureData: SignatureData | undefined | null
+): { state: SwapCallbackState; callback: null | (() => Promise); error: ReactNode | null } {
+ const { account } = useActiveWeb3React()
+
+ const deadline = useTransactionDeadline()
+
+ const addTransaction = useTransactionAdder()
+
+ const { address: recipientAddress } = useENS(recipientAddressOrName)
+ const recipient = recipientAddressOrName === null ? account : recipientAddress
+
+ const {
+ state,
+ callback: libCallback,
+ error,
+ } = useLibSwapCallBack({ trade, allowedSlippage, recipientAddressOrName: recipient, signatureData, deadline })
+
+ const callback = useMemo(() => {
+ if (!libCallback || !trade) {
+ return null
+ }
+ return () =>
+ libCallback().then((response) => {
+ addTransaction(
+ response,
+ trade.tradeType === TradeType.EXACT_INPUT
+ ? {
+ type: TransactionType.SWAP,
+ tradeType: TradeType.EXACT_INPUT,
+ inputCurrencyId: currencyId(trade.inputAmount.currency),
+ inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
+ expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
+ outputCurrencyId: currencyId(trade.outputAmount.currency),
+ minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(allowedSlippage).quotient.toString(),
+ }
+ : {
+ type: TransactionType.SWAP,
+ tradeType: TradeType.EXACT_OUTPUT,
+ inputCurrencyId: currencyId(trade.inputAmount.currency),
+ maximumInputCurrencyAmountRaw: trade.maximumAmountIn(allowedSlippage).quotient.toString(),
+ outputCurrencyId: currencyId(trade.outputAmount.currency),
+ outputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
+ expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
+ }
+ )
+ return response.hash
+ })
+ }, [addTransaction, allowedSlippage, libCallback, trade])
+
+ return {
+ state,
+ callback,
+ error,
+ }
+} */
+// MOD
/**
* Returns a callback function that will execute the swap (or null if any required param is missing)
*
diff --git a/src/custom/hooks/useTokenLazy.ts b/src/custom/hooks/useTokenLazy.ts
index 54a522bc30..969bd38ca5 100644
--- a/src/custom/hooks/useTokenLazy.ts
+++ b/src/custom/hooks/useTokenLazy.ts
@@ -6,7 +6,7 @@ import { Contract } from '@ethersproject/contracts'
import { useActiveWeb3React } from 'hooks/web3'
import { getBytes32TokenContract, getTokenContract } from 'hooks/useContract'
-import { parseStringOrBytes32 } from 'hooks/Tokens'
+import { parseStringOrBytes32 } from 'lib/hooks/useCurrency'
import { useAddUserToken } from 'state/user/hooks'
import { Erc20 } from 'abis/types'
import { retry } from 'utils/retry'
diff --git a/src/custom/hooks/useUSDCPrice/index.ts b/src/custom/hooks/useUSDCPrice/index.ts
index e7c5fbe235..701d76cd17 100644
--- a/src/custom/hooks/useUSDCPrice/index.ts
+++ b/src/custom/hooks/useUSDCPrice/index.ts
@@ -1,23 +1,23 @@
-import { CurrencyAmount, Currency, Price, Token } from '@uniswap/sdk-core'
+import { Currency, CurrencyAmount, Price, Token /*, TradeType*/ } from '@uniswap/sdk-core'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useEffect, useMemo, useRef, useState } from 'react'
+
import { SupportedChainId } from 'constants/chains'
-/* import { DAI_OPTIMISM, USDC, USDC_ARBITRUM } from '../constants/tokens'
-import { useBestV2Trade } from './useBestV2Trade'
-import { useClientSideV3Trade } from './useClientSideV3Trade' */
-import { useActiveWeb3React } from 'hooks/web3'
+import { /*DAI_OPTIMISM,*/ USDC /*, USDC_ARBITRUM, USDC_MAINNET, USDC_POLYGON*/ } from 'constants/tokens'
+// import { useBestV2Trade } from './useBestV2Trade'
+// import { useClientSideV3Trade } from './useClientSideV3Trade'
+// MOD imports
import { supportedChainId } from 'utils/supportedChainId'
import { STABLECOIN_AMOUNT_OUT as STABLECOIN_AMOUNT_OUT_UNI } from 'hooks/useUSDCPrice'
import { stringToCurrency } from 'state/swap/extension'
-import { USDC_XDAI } from 'utils/xdai/constants'
import { OrderKind } from 'state/orders/actions'
import { unstable_batchedUpdates as batchedUpdate } from 'react-dom'
import { getUSDPriceQuote, toPriceInformation } from 'api/coingecko'
-import { tryParseAmount } from 'state/swap/hooks'
import { DEFAULT_NETWORK_FOR_LISTS } from 'constants/lists'
import { currencyId } from 'utils/currencyId'
-import { USDC } from 'constants/tokens'
-import { useBlockNumber } from 'state/application/hooks'
+import useBlockNumber from 'lib/hooks/useBlockNumber'
import useGetGpPriceStrategy from 'hooks/useGetGpPriceStrategy'
import { MAX_VALID_TO_EPOCH } from 'hooks/useSwapCallback'
import { useIsQuoteLoading } from 'state/price/hooks'
@@ -31,9 +31,13 @@ const getGpUsdcPriceResolveOnlyLastCall = onlyResolvesLast(getGpUsdcPrice)
const STABLECOIN_AMOUNT_OUT: { [chain in SupportedChainId]: CurrencyAmount } = {
...STABLECOIN_AMOUNT_OUT_UNI,
// MOD: lowers threshold from 100k to 100
- [SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC, 100e6),
- [SupportedChainId.RINKEBY]: CurrencyAmount.fromRawAmount(USDC, 100e6),
- [SupportedChainId.XDAI]: CurrencyAmount.fromRawAmount(USDC_XDAI, 10_000e6),
+ // [SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC_MAINNET, 100_000e6),
+ [SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.MAINNET], 100e6),
+ [SupportedChainId.RINKEBY]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.RINKEBY], 100e6),
+ // [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),
+ [SupportedChainId.XDAI]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.XDAI], 10_000e6),
}
/**
@@ -52,7 +56,7 @@ export default function useUSDCPrice(currency?: Currency) {
const sellTokenAddress = currency?.wrapped.address
const sellTokenDecimals = currency?.wrapped.decimals
- /*
+ /* // TODO(#2808): remove dependency on useBestV2Trade
const v2USDCTrade = useBestV2Trade(TradeType.EXACT_OUTPUT, amountOut, currency, {
maxHops: 2,
})
@@ -73,13 +77,12 @@ export default function useUSDCPrice(currency?: Currency) {
const { numerator, denominator } = v2USDCTrade.route.midPrice
return new Price(currency, stablecoin, denominator, numerator)
} else if (v3USDCTrade.trade) {
- const { numerator, denominator } = v3USDCTrade.trade.route.midPrice
+ const { numerator, denominator } = v3USDCTrade.trade.routes[0].midPrice
return new Price(currency, stablecoin, denominator, numerator)
}
return undefined
- }, [currency, stablecoin, v2USDCTrade, v3USDCTrade.trade])
- */
+ }, [currency, stablecoin, v2USDCTrade, v3USDCTrade.trade]) */
useEffect(() => {
const supportedChain = supportedChainId(chainId)
@@ -193,7 +196,7 @@ export function useCoingeckoUsdPrice(currency?: Currency) {
useEffect(() => {
const isSupportedChainId = supportedChainId(chainId)
- const baseAmount = tryParseAmount('1', currencyRef.current)
+ const baseAmount = tryParseCurrencyAmount('1', currencyRef.current)
if (!isSupportedChainId || !tokenAddress || !baseAmount) return
@@ -212,7 +215,7 @@ export function useCoingeckoUsdPrice(currency?: Currency) {
// we need to parse all USD returned amounts
// and convert to the same currencyRef.current for both sides (SDK math invariant)
// in our case we stick to the USDC paradigm
- const quoteAmount = tryParseAmount(apiUsdPrice.toString(), USDC)
+ const quoteAmount = tryParseCurrencyAmount(apiUsdPrice.toString(), USDC[isSupportedChainId])
// parse failure is unlikely - type safe
if (!quoteAmount) return
// create a new Price object
@@ -261,3 +264,28 @@ export function useHigherUSDValue(currencyAmount: CurrencyAmount | und
return coingeckoUsdPrice || gpUsdPrice
}
+
+/**
+ *
+ * @param fiatValue string representation of a USD amount
+ * @returns CurrencyAmount where currency is stablecoin on active chain
+ */
+// TODO: new function, check whether it's usueful anywhere
+/* export function useStablecoinAmountFromFiatValue(fiatValue: string | null | undefined) {
+ const { chainId } = useActiveWeb3React()
+ const stablecoin = chainId ? STABLECOIN_AMOUNT_OUT[chainId]?.currency : undefined
+
+ if (fiatValue === null || fiatValue === undefined || !chainId || !stablecoin) {
+ return undefined
+ }
+
+ // trim for decimal precision when parsing
+ const parsedForDecimals = parseFloat(fiatValue).toFixed(stablecoin.decimals).toString()
+
+ try {
+ // parse USD string into CurrencyAmount based on stablecoin decimals
+ return tryParseCurrencyAmount(parsedForDecimals, stablecoin)
+ } catch (error) {
+ return undefined
+ }
+} */
diff --git a/src/custom/hooks/useWalletInfo.ts b/src/custom/hooks/useWalletInfo.ts
index 690a68a847..aa36a4c3fb 100644
--- a/src/custom/hooks/useWalletInfo.ts
+++ b/src/custom/hooks/useWalletInfo.ts
@@ -1,6 +1,6 @@
import WalletConnectProvider from '@walletconnect/web3-provider'
import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
-import { useWeb3React } from '@web3-react/core'
+import { useWeb3React } from 'web3-react-core'
import { Web3Provider } from '@ethersproject/providers'
import useENSName from '@src/hooks/useENSName'
import { useEffect, useState } from 'react'
diff --git a/src/custom/hooks/useWrapCallback.ts b/src/custom/hooks/useWrapCallback.ts
index cb00c0033a..ff6d8ddbcf 100644
--- a/src/custom/hooks/useWrapCallback.ts
+++ b/src/custom/hooks/useWrapCallback.ts
@@ -1,15 +1,21 @@
// eslint-disable-next-line no-restricted-imports
import { t } from '@lingui/macro'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
+// import useActiveWeb3React from 'hooks/useActiveWeb3React'
+// import useNativeCurrency from 'lib/hooks/useNativeCurrency'
+// import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useMemo } from 'react'
-import { WETH9_EXTENDED } from 'constants/tokens'
-import { BigNumber } from '@ethersproject/bignumber'
-import { TransactionResponse } from '@ethersproject/providers'
-import { getChainCurrencySymbols } from 'utils/xdai/hack'
-import { Contract } from '@ethersproject/contracts'
+import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
+// import { TransactionType } from '../state/transactions/actions'
import { useTransactionAdder } from 'state/enhancedTransactions/hooks'
import { useCurrencyBalance } from 'state/wallet/hooks'
import { useWETHContract } from 'hooks/useContract'
+
+// MOD imports
+import { BigNumber } from '@ethersproject/bignumber'
+import { TransactionResponse } from '@ethersproject/providers'
+import { Contract } from '@ethersproject/contracts'
+import { getChainCurrencySymbols } from 'utils/xdai/hack'
import { AMOUNT_PRECISION, RADIX_HEX } from 'constants/index'
import { SupportedChainId as ChainId } from 'constants/chains'
import { supportedChainId } from 'utils/supportedChainId'
@@ -50,6 +56,14 @@ interface GetWrapUnwrapCallback {
const NOT_APPLICABLE = { wrapType: WrapType.NOT_APPLICABLE }
+/* enum WrapInputError {
+ NO_ERROR, // must be equal to 0 so all other errors are truthy
+ ENTER_NATIVE_AMOUNT,
+ ENTER_WRAPPED_AMOUNT,
+ INSUFFICIENT_NATIVE_BALANCE,
+ INSUFFICIENT_WRAPPED_BALANCE,
+} */
+
function _handleGasEstimateError(error: any) {
console.log(
'[useWrapCallback] Error estimating gas for wrap/unwrap. Using default gas limit ' +
@@ -96,7 +110,7 @@ function _getWrapUnwrapCallback(params: GetWrapUnwrapCallback): WrapUnwrapCallba
operationType = OperationType.WRAP_ETHER
wrapUnwrap = async () => {
const estimatedGas = await wethContract.estimateGas.deposit({ value: amountHex }).catch(_handleGasEstimateError)
- const gasLimit = calculateGasMargin(chainId, estimatedGas)
+ const gasLimit = calculateGasMargin(estimatedGas)
return wethContract.deposit({ value: amountHex, gasLimit })
}
@@ -107,7 +121,7 @@ function _getWrapUnwrapCallback(params: GetWrapUnwrapCallback): WrapUnwrapCallba
operationType = OperationType.UNWRAP_WETH
wrapUnwrap = async () => {
const estimatedGas = await wethContract.estimateGas.withdraw(amountHex).catch(_handleGasEstimateError)
- const gasLimit = calculateGasMargin(chainId, estimatedGas)
+ const gasLimit = calculateGasMargin(estimatedGas)
return wethContract.withdraw(amountHex, { gasLimit })
}
const baseSummary = t`${formatSmart(inputAmount, AMOUNT_PRECISION)} ${wrapped} to ${native}`
@@ -143,6 +157,25 @@ function _getWrapUnwrapCallback(params: GetWrapUnwrapCallback): WrapUnwrapCallba
}
}
+/* export function WrapErrorText({ wrapInputError }: { wrapInputError: WrapInputError }) {
+ const native = useNativeCurrency()
+ const wrapped = native?.wrapped
+
+ switch (wrapInputError) {
+ case WrapInputError.NO_ERROR:
+ return null
+ case WrapInputError.ENTER_NATIVE_AMOUNT:
+ return Enter {native?.symbol} amount
+ case WrapInputError.ENTER_WRAPPED_AMOUNT:
+ return Enter {wrapped?.symbol} amount
+
+ case WrapInputError.INSUFFICIENT_NATIVE_BALANCE:
+ return Insufficient {native?.symbol} balance
+ case WrapInputError.INSUFFICIENT_WRAPPED_BALANCE:
+ return Insufficient {wrapped?.symbol} balance
+ }
+} */
+
/**
* Given the selected input and output currency, return a wrap callback
* @param inputCurrency the selected input currency
@@ -152,8 +185,8 @@ function _getWrapUnwrapCallback(params: GetWrapUnwrapCallback): WrapUnwrapCallba
export default function useWrapCallback(
openTransactionConfirmationModal: (message: string, operationType: OperationType) => void,
closeModals: () => void,
- inputCurrency?: Currency,
- outputCurrency?: Currency,
+ inputCurrency?: Currency | null,
+ outputCurrency?: Currency | null,
inputAmount?: CurrencyAmount,
isEthTradeOverride?: boolean
): WrapUnwrapCallback {
@@ -162,11 +195,15 @@ export default function useWrapCallback(
const wethContract = useWETHContract()
const balance = useCurrencyBalance(account ?? undefined, inputCurrency)
// we can always parse the amount typed as the input currency, since wrapping is 1:1
+ /* const inputAmount = useMemo(
+ () => tryParseCurrencyAmount(typedValue, inputCurrency ?? undefined),
+ [inputCurrency, typedValue]
+ ) */
const addTransaction = useTransactionAdder()
return useMemo(() => {
if (!wethContract || !chainId || !inputCurrency || !outputCurrency) return NOT_APPLICABLE
- const weth = WETH9_EXTENDED[chainId]
+ const weth = WRAPPED_NATIVE_CURRENCY[chainId]
if (!weth) return NOT_APPLICABLE
const isWrappingEther = inputCurrency.isNative && (isEthTradeOverride || weth.equals(outputCurrency))
diff --git a/src/custom/hooks/web3.ts b/src/custom/hooks/web3.ts
index 92711a669e..abe7e4226e 100644
--- a/src/custom/hooks/web3.ts
+++ b/src/custom/hooks/web3.ts
@@ -1,21 +1,25 @@
-import { IS_IN_IFRAME } from 'constants/misc'
-import { useWeb3React } from '@web3-react/core'
-import { AbstractConnector } from '@web3-react/abstract-connector'
+// import { EthereumProvider } from '@src/lib/ethereum'
import { useEffect, useState, useCallback } from 'react'
+import { useWeb3React } from 'web3-react-core'
import { injected, gnosisSafe, walletconnect, getProviderType, WalletProvider, fortmatic, walletlink } from 'connectors'
+import { IS_IN_IFRAME } from 'constants/misc'
import { isMobile } from 'utils/userAgent'
+// MOD imports
import { STORAGE_KEY_LAST_PROVIDER, WAITING_TIME_RECONNECT_LAST_PROVIDER } from 'constants/index'
+import { AbstractConnector } from '@web3-react/abstract-connector'
// exports from the original file
-export { useInactiveListener, useActiveWeb3React } from '@src/hooks/web3'
+export { useInactiveListener } from '@src/hooks/web3'
+export { default as useActiveWeb3React } from 'hooks/useActiveWeb3React'
enum DefaultProvidersInjected {
METAMASK = WalletProvider.INJECTED,
COINBASE_WALLET = WalletProvider.WALLET_LINK,
}
+// TODO: original from uniswap has gnosis-safe connection details, could be re-used
export function useEagerConnect() {
const { activate, active, connector } = useWeb3React()
const [tried, setTried] = useState(false)
@@ -109,7 +113,7 @@ export function useEagerConnect() {
}
}, [connectInjected, active, connectSafe, triedSafe, reconnectUninjectedProvider]) // intentionally only running on mount (make sure it's only mounted once :))
- // if the connection worked, wait until we get confirmation of that to flip the flag
+ // wait until we get confirmation of a connection to flip the flag
useEffect(() => {
let timeout: NodeJS.Timeout | undefined
@@ -162,3 +166,45 @@ export function setDefaultInjected(providerName: DefaultProvidersInjected) {
ethereum.setSelectedProvider(provider)
}
}
+
+// TODO: new funciton, checker whether we can use it
+/**
+ * Use for network and injected - logs user in
+ * and out after checking what network theyre on
+ */
+/* export function useInactiveListener(suppress = false) {
+ const { active, error, activate } = useWeb3React()
+
+ useEffect(() => {
+ const ethereum = window.ethereum as EthereumProvider | undefined
+
+ if (ethereum && ethereum.on && !active && !error && !suppress) {
+ const handleChainChanged = () => {
+ // eat errors
+ activate(injected, undefined, true).catch((error) => {
+ console.error('Failed to activate after chain changed', error)
+ })
+ }
+
+ const handleAccountsChanged = (accounts: string[]) => {
+ if (accounts.length > 0) {
+ // eat errors
+ activate(injected, undefined, true).catch((error) => {
+ console.error('Failed to activate after accounts changed', error)
+ })
+ }
+ }
+
+ ethereum.on('chainChanged', handleChainChanged)
+ ethereum.on('accountsChanged', handleAccountsChanged)
+
+ return () => {
+ if (ethereum.removeListener) {
+ ethereum.removeListener('chainChanged', handleChainChanged)
+ ethereum.removeListener('accountsChanged', handleAccountsChanged)
+ }
+ }
+ }
+ return undefined
+ }, [active, error, suppress, activate])
+} */
diff --git a/src/custom/i18n.tsx b/src/custom/i18n.tsx
deleted file mode 100644
index 8a78953525..0000000000
--- a/src/custom/i18n.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-import { useEffect, useState } from 'react'
-import { i18n } from '@lingui/core'
-import { I18nProvider } from '@lingui/react'
-import { ReactNode } from 'react'
-// import { useActiveLocale, useSetLocaleFromUrl } from 'hooks/useActiveLocale'
-// import { SupportedLocale } from 'constants/locales'
-import {
- // af,
- // ar,
- // ca,
- // cs,
- // da,
- // de,
- // el,
- en,
- // es,
- // fi,
- // fr,
- // he,
- // hu,
- // id,
- // it,
- // ja,
- // ko,
- // nl,
- // no,
- // pl,
- // pt,
- // ro,
- // ru,
- // sr,
- // sv,
- // tr,
- // uk,
- // vi,
- // zh,
- PluralCategory,
-} from 'make-plural/plurals'
-
-type LocalePlural = {
- // [key in SupportedLocale]: (n: number | string, ord?: boolean) => PluralCategory
- ['en-US']: (n: number | string, ord?: boolean) => PluralCategory
-}
-
-const plurals: LocalePlural = {
- // 'af-ZA': af,
- // 'ar-SA': ar,
- // 'ca-ES': ca,
- // 'cs-CZ': cs,
- // 'da-DK': da,
- // 'de-DE': de,
- // 'el-GR': el,
- 'en-US': en,
- // 'es-ES': es,
- // 'fi-FI': fi,
- // 'fr-FR': fr,
- // 'he-IL': he,
- // 'hu-HU': hu,
- // 'id-ID': id,
- // 'it-IT': it,
- // 'ja-JP': ja,
- // 'ko-KR': ko,
- // 'nl-NL': nl,
- // 'no-NO': no,
- // 'pl-PL': pl,
- // 'pt-BR': pt,
- // 'pt-PT': pt,
- // 'ro-RO': ro,
- // 'ru-RU': ru,
- // 'sr-SP': sr,
- // 'sv-SE': sv,
- // 'tr-TR': tr,
- // 'uk-UA': uk,
- // 'vi-VN': vi,
- // 'zh-CN': zh,
- // 'zh-TW': zh,
-}
-
-const DEFAULT_LOCALE = 'en-US' // mod
-type SupportedLocale = typeof DEFAULT_LOCALE // mod
-
-export async function dynamicActivate(locale: SupportedLocale) {
- const { messages } = await import(`@lingui/loader!../locales/${locale}.po`)
- i18n.loadLocaleData(locale, { plurals: () => plurals[locale] })
- i18n.load(locale, messages)
- i18n.activate(locale)
-}
-
-export function LanguageProvider({ children }: { children: ReactNode }) {
- // useSetLocaleFromUrl()
- // const locale = useActiveLocale()
- const [loaded, setLoaded] = useState(false)
-
- useEffect(
- () => {
- // dynamicActivate(locale)
- dynamicActivate(DEFAULT_LOCALE) // mod
- .then(() => {
- setLoaded(true)
- })
- .catch((error) => {
- console.error('Failed to activate locale', /* locale, */ error)
- })
- },
- [
- /* locale */
- ]
- )
-
- // prevent the app from rendering with placeholder text before the locale is loaded
- if (!loaded) return null
-
- return (
-
- {children}
-
- )
-}
diff --git a/src/custom/lib/hooks/useCurrencyLogoURIs/index.ts b/src/custom/lib/hooks/useCurrencyLogoURIs/index.ts
new file mode 100644
index 0000000000..0c39c06ffc
--- /dev/null
+++ b/src/custom/lib/hooks/useCurrencyLogoURIs/index.ts
@@ -0,0 +1,2 @@
+export * from '@src/lib/hooks/useCurrencyLogoURIs'
+export { default } from './useCurrencyLogoURIsMod'
diff --git a/src/custom/lib/hooks/useCurrencyLogoURIs/useCurrencyLogoURIsMod.ts b/src/custom/lib/hooks/useCurrencyLogoURIs/useCurrencyLogoURIsMod.ts
new file mode 100644
index 0000000000..3a82e01efc
--- /dev/null
+++ b/src/custom/lib/hooks/useCurrencyLogoURIs/useCurrencyLogoURIsMod.ts
@@ -0,0 +1,74 @@
+import { Currency } from '@uniswap/sdk-core'
+import { SupportedChainId } from 'constants/chains'
+import useHttpLocations from 'hooks/useHttpLocations'
+import { useMemo } from 'react'
+import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
+
+import EthereumLogo from 'assets/images/ethereum-logo.png'
+// import MaticLogo from '../../assets/svg/matic-token-icon.svg'
+
+// MOD imports
+import XDaiLogo from 'assets/cow-swap/xdai.png'
+import { ADDRESS_IMAGE_OVERRIDE } from 'constants/tokens'
+import { NATIVE_CURRENCY_BUY_ADDRESS } from 'constants/index'
+
+type Network = 'ethereum' | /*'arbitrum' | 'optimism'*/ 'xdai' | 'rinkeby'
+
+function chainIdToNetworkName(networkId: SupportedChainId): Network {
+ switch (networkId) {
+ case SupportedChainId.MAINNET:
+ return 'ethereum'
+ // mod
+ case SupportedChainId.XDAI:
+ return 'xdai'
+ /* case SupportedChainId.ARBITRUM_ONE:
+ return 'arbitrum'
+ case SupportedChainId.OPTIMISM:
+ return 'optimism' */
+ default:
+ return 'ethereum'
+ }
+}
+
+function getNativeLogoURI(chainId: SupportedChainId = SupportedChainId.MAINNET): string {
+ switch (chainId) {
+ // mod
+ case SupportedChainId.XDAI:
+ return XDaiLogo
+ /*case SupportedChainId.POLYGON_MUMBAI:
+ case SupportedChainId.POLYGON:
+ return MaticLogo*/
+ default:
+ return EthereumLogo
+ }
+}
+
+function getTokenLogoURI(address: string, chainId: SupportedChainId = SupportedChainId.MAINNET): string | void {
+ const networkName = chainIdToNetworkName(chainId)
+ // const networksWithUrls = [SupportedChainId.ARBITRUM_ONE, SupportedChainId.MAINNET, SupportedChainId.OPTIMISM]
+ const networksWithUrls = [SupportedChainId.MAINNET, SupportedChainId.XDAI]
+ if (networksWithUrls.includes(chainId)) {
+ return `https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/${networkName}/assets/${address}/logo.png`
+ }
+}
+
+export default function useCurrencyLogoURIs(currency?: Currency | null): string[] {
+ const locations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
+ return useMemo(() => {
+ const logoURIs = [...locations]
+ if (currency) {
+ // mod: CowSwap Native buy orders have address set to EeeEE... rather than `isNative` flag
+ if (currency.isNative || currency.address === NATIVE_CURRENCY_BUY_ADDRESS) {
+ logoURIs.push(getNativeLogoURI(currency.chainId))
+ } else if (currency.isToken) {
+ // mod
+ const imageOverride = ADDRESS_IMAGE_OVERRIDE[currency.address]
+ const logoURI = imageOverride || getTokenLogoURI(currency.address, currency.chainId)
+ if (logoURI) {
+ logoURIs.push(logoURI)
+ }
+ }
+ }
+ return logoURIs
+ }, [currency, locations])
+}
diff --git a/src/custom/pages/App/AppMod.tsx b/src/custom/pages/App/AppMod.tsx
index fc4b2b591c..3e85d5c3e8 100644
--- a/src/custom/pages/App/AppMod.tsx
+++ b/src/custom/pages/App/AppMod.tsx
@@ -1,6 +1,7 @@
+import Loader from 'components/Loader'
import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader'
-import { Suspense, /* PropsWithChildren, */ ReactNode, useState, useEffect } from 'react'
-import { Route, Switch, useLocation } from 'react-router-dom'
+import { /*Lazy,*/ Suspense, /* PropsWithChildren, */ ReactNode, useState, useEffect } from 'react'
+import { /*Redirect,*/ Route, Switch, useLocation } from 'react-router-dom'
import styled from 'styled-components/macro'
import GoogleAnalyticsReporter from 'components/analytics/GoogleAnalyticsReporter'
import AddressClaimModal from 'components/claim/AddressClaimModal'
@@ -9,15 +10,12 @@ import Header from 'components/Header'
import Polling from 'components/Header/Polling'
import Popups from 'components/Popups'
import Web3ReactManager from 'components/Web3ReactManager'
-import { ApplicationModal } from 'state/application/reducer'
import { useModalOpen, useToggleModal } from 'state/application/hooks'
+import { ApplicationModal } from 'state/application/reducer'
import DarkModeQueryParamReader from 'theme'
/* import AddLiquidity from './AddLiquidity'
-import {
- RedirectDuplicateTokenIds,
-} from './AddLiquidity/redirects'
+import { RedirectDuplicateTokenIds } from './AddLiquidity/redirects'
import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects'
-import CreateProposal from './CreateProposal'
import Earn from './Earn'
import Manage from './Earn/Manage'
import MigrateV2 from './MigrateV2'
@@ -30,15 +28,17 @@ import RemoveLiquidity from './RemoveLiquidity'
import RemoveLiquidityV3 from './RemoveLiquidity/V3'
import Swap from './Swap'
import { OpenClaimAddressModalAndRedirectToSwap, RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects'
-import Vote from './Vote'
-import VotePage from './Vote/VotePage'
*/
+
+// MOD imports
import ReferralLinkUpdater from 'state/affiliate/updater'
import URLWarning from 'components/Header/URLWarning'
import Footer from 'components/Footer'
import { BodyWrapper } from '.'
import * as CSS from 'csstype' // mod
+// const Vote = lazy(() => import('./Vote'))
+
interface AppWrapProps {
bgBlur?: boolean
}
@@ -47,6 +47,7 @@ const AppWrapper = styled.div>`
display: flex;
flex-flow: column;
align-items: flex-start;
+ // MOD
min-height: 100vh;
/* overflow-x: hidden; */ // mod
&:after {
@@ -75,7 +76,7 @@ const AppWrapper = styled.div>`
z-index: 1;
${({ theme }) => theme.mediaWidth.upToSmall`
- padding: 6rem 16px 16px 16px;
+ padding: 4rem 8px 16px 8px;
`};
` */
@@ -83,6 +84,9 @@ const HeaderWrapper = styled.div`
${({ theme }) => theme.flexRowNoWrap}
width: 100%;
justify-content: space-between;
+ /* position: fixed;
+ top: 0;
+ z-index: 2; */
`
const FooterWrapper = styled(HeaderWrapper)`
@@ -108,21 +112,21 @@ export default function App(props?: { children?: ReactNode }) {
}, [location.pathname])
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+ }>
{props && props.children}
{/*
@@ -164,14 +168,14 @@ export default function App(props?: { children?: ReactNode }) {
*/}
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
)
}
diff --git a/src/custom/pages/Claim/ClaimAddress.tsx b/src/custom/pages/Claim/ClaimAddress.tsx
index 20730144f7..a561930069 100644
--- a/src/custom/pages/Claim/ClaimAddress.tsx
+++ b/src/custom/pages/Claim/ClaimAddress.tsx
@@ -2,13 +2,13 @@ import { useMemo } from 'react'
import { Trans } from '@lingui/macro'
import { ButtonSecondary } from 'components/Button'
import Circle from 'assets/images/blue-loader.svg'
-import { CustomLightSpinner, TYPE } from 'theme'
+import { CustomLightSpinner, ThemedText } from 'theme'
import { CheckAddress, InputField, InputFieldTitle, InputErrorText } from './styled'
import { ClaimCommonTypes } from './types'
import useENS from 'hooks/useENS'
import { useClaimDispatchers, useClaimState } from 'state/claim/hooks'
import { ClaimStatus } from 'state/claim/actions'
-import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
+import { UnsupportedChainIdError, useWeb3React } from 'web3-react-core'
export type ClaimAddressProps = Pick & {
toggleWalletModal: () => void
@@ -56,9 +56,9 @@ export default function ClaimAddress({ account, toggleWalletModal }: ClaimAddres
{showInputError && (
-
+
Enter valid address or ENS
-
+
)}
diff --git a/src/custom/pages/Claim/ClaimSummary.tsx b/src/custom/pages/Claim/ClaimSummary.tsx
index 04d381b13a..e0ce664f08 100644
--- a/src/custom/pages/Claim/ClaimSummary.tsx
+++ b/src/custom/pages/Claim/ClaimSummary.tsx
@@ -9,7 +9,7 @@ import { ClaimStatus } from 'state/claim/actions'
import { AMOUNT_PRECISION } from 'constants/index'
import { useTokenBalance } from 'state/wallet/hooks'
import { V_COW } from 'constants/tokens'
-import { useActiveWeb3React } from 'hooks'
+import { useActiveWeb3React } from 'hooks/web3'
import { JSBI } from '@uniswap/sdk'
type ClaimSummaryProps = Pick & {
diff --git a/src/custom/pages/Claim/ClaimsOnOtherChainsBanner.tsx b/src/custom/pages/Claim/ClaimsOnOtherChainsBanner.tsx
index 2d6632e5e7..d78b0628ae 100644
--- a/src/custom/pages/Claim/ClaimsOnOtherChainsBanner.tsx
+++ b/src/custom/pages/Claim/ClaimsOnOtherChainsBanner.tsx
@@ -1,12 +1,13 @@
import { useMemo, Fragment } from 'react'
import styled from 'styled-components/macro'
-import { NETWORK_LABELS, SupportedChainId } from 'constants/chains'
+import { SupportedChainId } from 'constants/chains'
import { useClaimState } from 'state/claim/hooks'
import useChangeNetworks from 'hooks/useChangeNetworks'
import { useActiveWeb3React } from 'hooks/web3'
import NotificationBanner from '@src/custom/components/NotificationBanner'
import { AlertTriangle } from 'react-feather'
import { ClaimInfo } from 'state/claim/reducer'
+import { CHAIN_INFO } from 'constants/chainInfo'
const ChainSpan = styled.span``
const Wrapper = styled.div`
@@ -58,9 +59,7 @@ function _shouldNotDisplayBannerForChain(
const { available, total } = claimInfo
return (
// If this is the same network
- // Important, the double equal comparison is intentional!
- // Don't be dumb like me and change to triple equal and spend awhile figuring out why it doesn't work...
- checkedChain == chainId ||
+ Number(checkedChain) === chainId ||
// If total claims is 0
total === 0 ||
// If there are no available
@@ -70,7 +69,7 @@ function _shouldNotDisplayBannerForChain(
function ClaimsOnOtherChainsBanner({ className }: { className?: string }) {
const { account, library, chainId } = useActiveWeb3React()
- const { callback } = useChangeNetworks({ account, library, chainId })
+ const { handleChainSwitch } = useChangeNetworks({ library, chainId })
const { claimInfoPerAccount, activeClaimAccount } = useClaimState()
@@ -103,12 +102,12 @@ function ClaimsOnOtherChainsBanner({ className }: { className?: string }) {
This account has available claims on
{chainsWithClaims.map((chainId, index, array) => {
- const changeNetworksCallback = () => callback(chainId)
+ const changeNetworksCallback = () => handleChainSwitch(chainId, true) // true to avoid opening the dropdown
const isLastInMultiple = index === array.length - 1 && array.length > 1
return (
{isLastInMultiple && ' and'}
- {NETWORK_LABELS[chainId]}
+ {CHAIN_INFO[chainId].label}
)
})}
diff --git a/src/custom/pages/Claim/FooterNavButtons.tsx b/src/custom/pages/Claim/FooterNavButtons.tsx
index 0807f940dd..9c259e9416 100644
--- a/src/custom/pages/Claim/FooterNavButtons.tsx
+++ b/src/custom/pages/Claim/FooterNavButtons.tsx
@@ -1,5 +1,5 @@
import { ReactNode, useEffect, useRef } from 'react'
-import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
+import { UnsupportedChainIdError, useWeb3React } from 'web3-react-core'
import { Trans } from '@lingui/macro'
import { isAddress } from '@ethersproject/address'
import {
diff --git a/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx b/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx
index 4959730bc1..2b75b2e620 100644
--- a/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx
+++ b/src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx
@@ -29,7 +29,7 @@ import { ButtonConfirmed } from 'components/Button'
import { ButtonSize } from 'theme'
import Loader from 'components/Loader'
import { useErrorModal } from 'hooks/useErrorMessageAndModal'
-import { tryParseAmount } from 'state/swap/hooks'
+import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { calculateInvestmentAmounts, calculatePercentage } from 'state/claim/hooks/utils'
import { AMOUNT_PRECISION, PERCENTAGE_PRECISION } from 'constants/index'
import { useGasPrices } from 'state/gas/hooks'
@@ -130,10 +130,7 @@ export default function InvestOption({ claim, openModal, closeModal }: InvestOpt
// Based on how much gas will be used (estimatedGas) and current gas prices (if available)
// calculate how much that would cost in native currency.
// We pick `fast` to be conservative. Also, it's non-blocking, so the user is aware but can proceed
- const amount = calculateGasMargin(
- chainId,
- BigNumber.from(estimatedGas).mul(gasPrice?.fast || AVG_APPROVE_COST_GWEI)
- )
+ const amount = calculateGasMargin(BigNumber.from(estimatedGas).mul(gasPrice?.fast || AVG_APPROVE_COST_GWEI))
return CurrencyAmount.fromRawAmount(token, amount.toString())
}, [chainId, estimatedGas, gasPrice?.fast, isNative, token])
@@ -233,7 +230,7 @@ export default function InvestOption({ claim, openModal, closeModal }: InvestOpt
useEffect(() => {
let error = null
- const parsedAmount = tryParseAmount(typedValue, token)
+ const parsedAmount = tryParseCurrencyAmount(typedValue, token)
if (!maxCost || !balance) {
return
@@ -303,7 +300,7 @@ export default function InvestOption({ claim, openModal, closeModal }: InvestOpt
useEffect(() => {
const warnings = []
- const parsedAmount = tryParseAmount(typedValue, token)
+ const parsedAmount = tryParseCurrencyAmount(typedValue, token)
if (!parsedAmount || !maxCost || !balance || inputError) {
setInputWarnings([])
diff --git a/src/custom/pages/Claim/index.tsx b/src/custom/pages/Claim/index.tsx
index 43b39760a6..0ea5cef116 100644
--- a/src/custom/pages/Claim/index.tsx
+++ b/src/custom/pages/Claim/index.tsx
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo } from 'react'
-import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
+import { UnsupportedChainIdError, useWeb3React } from 'web3-react-core'
import { useActiveWeb3React } from 'hooks/web3'
import useENS from 'hooks/useENS'
@@ -171,7 +171,7 @@ export default function Claim() {
if (!selected.length) {
console.log('Starting claiming with', claimInputData)
sendTransaction(claimInputData)
- } else if (investFlowStep == 2) {
+ } else if (investFlowStep === 2) {
console.log('Starting claiming with', claimInputData)
sendTransaction(claimInputData)
} else {
diff --git a/src/custom/pages/Profile/VCOWDropdown.tsx b/src/custom/pages/Profile/VCOWDropdown.tsx
index b3b8ac3c3d..075c75990b 100644
--- a/src/custom/pages/Profile/VCOWDropdown.tsx
+++ b/src/custom/pages/Profile/VCOWDropdown.tsx
@@ -113,7 +113,7 @@ export const DropdownWrapper = styled.button<{ hasBalance?: boolean }>`
:focus {
cursor: ${({ hasBalance }) => (hasBalance ? 'pointer' : 'inherit')};
outline: none;
- border: ${({ hasBalance }) => (hasBalance ? '1px solid ${({ theme }) => theme.bg3}' : '1px solid transparent')};
+ border: ${({ hasBalance }) => (hasBalance ? `1px solid $\{({ theme }) => theme.bg3}` : '1px solid transparent')};
}
${({ theme }) => theme.mediaWidth.upToVerySmall`
min-width: 100%;
diff --git a/src/custom/pages/Profile/index.tsx b/src/custom/pages/Profile/index.tsx
index cf70250be9..7367f36127 100644
--- a/src/custom/pages/Profile/index.tsx
+++ b/src/custom/pages/Profile/index.tsx
@@ -33,7 +33,7 @@ import { getExplorerAddressLink } from 'utils/explorer'
import useTimeAgo from 'hooks/useTimeAgo'
import { MouseoverTooltipContent } from 'components/Tooltip'
import NotificationBanner from 'components/NotificationBanner'
-import { SupportedChainId as ChainId } from 'constants/chains'
+import { SupportedChainId, SupportedChainId as ChainId } from 'constants/chains'
import AffiliateStatusCheck from 'components/AffiliateStatusCheck'
import { useHasOrders } from 'api/gnosisProtocol/hooks'
import { Title, SectionTitle, HelpCircle } from 'components/Page'
@@ -62,7 +62,7 @@ export default function Profile() {
const { account, chainId = ChainId.MAINNET, library } = useActiveWeb3React()
const { profileData, isLoading, error } = useFetchProfile()
const lastUpdated = useTimeAgo(profileData?.lastUpdated)
- const isTradesTooltipVisible = account && chainId == 1 && !!profileData?.totalTrades
+ const isTradesTooltipVisible = account && chainId === SupportedChainId.MAINNET && !!profileData?.totalTrades
const hasOrders = useHasOrders(account)
const setSwapVCowStatus = useSetSwapVCowStatus()
diff --git a/src/custom/pages/Swap/SwapMod.tsx b/src/custom/pages/Swap/SwapMod.tsx
index b93b2182ab..0a6f416519 100644
--- a/src/custom/pages/Swap/SwapMod.tsx
+++ b/src/custom/pages/Swap/SwapMod.tsx
@@ -1,57 +1,50 @@
+/* eslint-disable react-hooks/rules-of-hooks */
+// TODO: understand why and re-enable rules-of-hooks
import { Trans } from '@lingui/macro'
+// import { Trade } from '@uniswap/router-sdk'
import { Currency, CurrencyAmount, Token, TradeType } from '@uniswap/sdk-core'
// import { Trade as V2Trade } from '@uniswap/v2-sdk'
// import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert'
-// import { AdvancedSwapDetails } from 'components/swap/AdvancedSwapDetails'
+// import SwapDetailsDropdown from 'components/swap/SwapDetailsDropdown'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
-import { MouseoverTooltip /* , MouseoverTooltipContent */ } from 'components/Tooltip'
+import { MouseoverTooltip } from 'components/Tooltip'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { useSwapCallback } from 'hooks/useSwapCallback'
+import useTransactionDeadline from 'hooks/useTransactionDeadline'
// import JSBI from 'jsbi'
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
-import { ArrowDown, /*, ArrowLeft */ CheckCircle, HelpCircle /* , Info */ } from 'react-feather'
+import { ArrowDown, CheckCircle, HelpCircle } from 'react-feather'
import ReactGA from 'react-ga'
-// import { Link, RouteComponentProps } from 'react-router-dom'
+// import { RouteComponentProps } from 'react-router-dom'
import { Text } from 'rebass'
-import { /* styled, */ ThemeContext } from 'styled-components/macro'
+// import { TradeState } from 'state/routing/types'
+import styled, { ThemeContext } from 'styled-components/macro'
+
import AddressInputPanel from 'components/AddressInputPanel'
-import {
- ButtonConfirmed,
- /* ButtonError,
- ButtonGray,
- ButtonLight,
- ButtonPrimary */
-} from 'components/Button'
+import { ButtonConfirmed /*, ButtonError, ButtonLight, ButtonPrimary*/ } from 'components/Button'
import Card, { GreyCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
import CurrencyInputPanel from 'components/CurrencyInputPanel'
import CurrencyLogo from 'components/CurrencyLogo'
import Loader from 'components/Loader'
-import { /* Row, */ AutoRow /*, RowFixed */ } from 'components/Row'
-// import BetterTradeLink from 'components/swap/BetterTradeLink'
+import { AutoRow } from 'components/Row'
import confirmPriceImpactWithoutFee from 'components/swap/confirmPriceImpactWithoutFee'
import ConfirmSwapModal from 'components/swap/ConfirmSwapModal'
-import { /* ArrowWrapper, Dots, */ /* SwapCallbackError, */ Wrapper } from 'components/swap/styleds'
+import { ArrowWrapper /*, SwapCallbackError*/, Wrapper } from 'components/swap/styleds'
import SwapHeader from 'components/swap/SwapHeader'
-// import TradePrice from 'components/swap/TradePrice'
// import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
import TokenWarningModal from 'components/TokenWarningModal'
+import { TOKEN_SHORTHANDS } from 'constants/tokens'
import { useAllTokens, useCurrency } from 'hooks/Tokens'
-import { ApprovalState, useApproveCallbackFromTrade } from 'hooks/useApproveCallback'
-// import { V3TradeState } from '../../hooks/useBestV3Trade'
+import { ApprovalState /*, useApprovalOptimizedTrade*/, useApproveCallbackFromTrade } from 'hooks/useApproveCallback'
import useENSAddress from 'hooks/useENSAddress'
import { useERC20PermitFromTrade, UseERC20PermitState } from 'hooks/useERC20Permit'
+// import useIsArgentWallet from '../../hooks/useIsArgentWallet'
import { useIsSwapUnsupported } from 'hooks/useIsSwapUnsupported'
-import { useSwapCallback } from 'hooks/useSwapCallback'
-import { /* useToggledVersion, */ Version } from 'hooks/useToggledVersion'
-import { useHigherUSDValue /* , useUSDCValue */ } from 'hooks/useUSDCPrice'
-import useWrapCallback, { WrapType } from 'hooks/useWrapCallback'
-import { useActiveWeb3React } from 'hooks/web3'
-import {
- useCloseModals,
- useModalOpen,
- useOpenModal,
- useWalletModalToggle /*, useToggleSettingsMenu */,
-} from 'state/application/hooks'
+import { useHigherUSDValue /*, useUSDCValue*/ } from 'hooks/useUSDCPrice'
+import useWrapCallback, { /*WrapErrorText, */ WrapType } from 'hooks/useWrapCallback'
+import { useCloseModals, useModalOpen, useOpenModal, useWalletModalToggle } from 'state/application/hooks'
import { Field } from 'state/swap/actions'
import {
useDefaultsFromURLSearch,
@@ -64,23 +57,21 @@ import {
useUnknownImpactWarning,
} from 'state/swap/hooks'
import { useExpertModeManager, useRecipientToggleManager } from 'state/user/hooks'
-import { /* HideSmall, */ LinkStyledButton, TYPE, ButtonSize } from 'theme'
-// import { computeFiatValuePriceImpact } from 'utils/computeFiatValuePriceImpact'
-// import { getTradeVersion } from 'utils/getTradeVersion'
-// import { isTradeBetter } from 'utils/isTradeBetter'
+import { ButtonSize, LinkStyledButton, ThemedText } from 'theme'
+// import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact'
import { maxAmountSpend } from 'utils/maxAmountSpend'
-// import { warningSeverity } from 'utils/prices'
-// MOD
+import { computeSlippageAdjustedAmounts /*, warningSeverity */ } from 'utils/prices'
+import { supportedChainId } from 'utils/supportedChainId'
+// import AppBody from 'pages/AppBody'
+
+// MOD imports
import { AMOUNT_PRECISION, INITIAL_ALLOWED_SLIPPAGE_PERCENT } from 'constants/index'
-import { computeSlippageAdjustedAmounts } from 'utils/prices'
import FeeInformationTooltip from 'components/swap/FeeInformationTooltip'
import { useWalletInfo } from 'hooks/useWalletInfo'
import { HashLink } from 'react-router-hash-link'
-// import { logTradeDetails } from 'state/swap/utils'
import { useGetQuoteAndStatus } from 'state/price/hooks'
import { SwapProps, ButtonError, ButtonPrimary } from '.' // mod
import TradeGp from 'state/swap/TradeGp'
-import AdvancedSwapDetailsDropdown from 'components/swap/AdvancedSwapDetailsDropdown'
import { formatSmart } from 'utils/format'
import { RowSlippage } from 'components/swap/TradeSummary/RowSlippage'
import usePrevious from 'hooks/usePrevious'
@@ -94,17 +85,10 @@ import { GpEther } from 'constants/tokens'
import { SupportedChainId } from 'constants/chains'
import CowSubsidyModal from 'components/CowSubsidyModal'
-// MOD - exported in ./styleds to avoid circ dep
-// export const StyledInfo = styled(Info)`
-// opacity: 0.4;
-// color: ${({ theme }) => theme.text1};
-// height: 16px;
-// width: 16px;
-// :hover {
-// opacity: 0.8;
-// }
-// `
-
+const AlertWrapper = styled.div`
+ max-width: 460px;
+ width: 100%;
+`
export default function Swap({
history,
location,
@@ -129,11 +113,11 @@ export default function Swap({
// 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(
- process.env.REACT_APP_DISABLE_TOKEN_WARNING === 'true'
+ process.env.REACT_APP_DISABLE_TOKEN_WARNING === 'true' // mod
)
const urlLoadedTokens: Token[] = useMemo(
() => [loadedInputCurrency, loadedOutputCurrency]?.filter((c): c is Token => c?.isToken ?? false) ?? [],
@@ -145,11 +129,24 @@ export default function Swap({
// dismiss warning if all imported tokens are in active lists
const defaultTokens = useAllTokens()
- const importTokensNotInDefault =
- urlLoadedTokens &&
- urlLoadedTokens.filter((token: Token) => {
- return !Boolean(token.address in defaultTokens)
- })
+ const importTokensNotInDefault = useMemo(
+ () =>
+ 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)
@@ -161,7 +158,6 @@ export default function Swap({
const [transactionConfirmationModalMsg, setTransactionConfirmationModalMsg] = useState()
const openTransactionConfirmationModalAux = useOpenModal(ApplicationModal.TRANSACTION_CONFIRMATION)
const closeModals = useCloseModals()
- // const toggleTransactionConfirmation = useToggleTransactionConfirmation()
const showTransactionConfirmationModal = useModalOpen(ApplicationModal.TRANSACTION_CONFIRMATION)
const openTransactionConfirmationModal = useCallback(
@@ -181,21 +177,16 @@ export default function Swap({
const [recipientToggleVisible] = useRecipientToggleManager()
- // get version from the url
- // const toggledVersion = useToggledVersion()
-
// swap state
const { independentField, typedValue, recipient, INPUT, OUTPUT } = useSwapState() // MOD: adds INPUT/OUTPUT
const {
- v2Trade,
- // v3TradeState: { trade: v3Trade, state: v3TradeState },
- // toggledTrade: trade,
+ v2Trade, // trade: { state: tradeState, trade },
allowedSlippage,
currencyBalances,
parsedAmount,
currencies,
inputError: swapInputError,
- } = useDerivedSwapInfo(/* toggledVersion */)
+ } = useDerivedSwapInfo()
// detects trade load
const { quote, isGettingNewQuote } = useGetQuoteAndStatus({ token: INPUT.currencyId, chainId })
@@ -219,11 +210,7 @@ export default function Swap({
address: INPUT.currencyId,
})
- const tradesByVersion = {
- [Version.v2]: v2Trade,
- // [Version.v3]: v3Trade
- }
- const tradeCurrentVersion = tradesByVersion[Version.v2]
+ const tradeCurrentVersion = v2Trade
// nativeInput only applies to useWrapCallback and any function that is native
// currency specific - use slippage/fee adjusted native currency for exactOUT orders
@@ -250,10 +237,6 @@ export default function Swap({
const showWrap: boolean = !isNativeInSwap && wrapType !== WrapType.NOT_APPLICABLE
const { address: recipientAddress } = useENSAddress(recipient)
const trade = showWrap ? undefined : tradeCurrentVersion
- // const defaultTrade = showWrap ? undefined : tradesByVersion[DEFAULT_VERSION]
-
- // const betterTradeLinkV2: Version | undefined =
- // toggledVersion === Version.v1 && isTradeBetter(v1Trade, v2Trade) ? Version.v2 : undefined
const parsedAmounts = useMemo(
() =>
@@ -271,6 +254,16 @@ export default function Swap({
[independentField, parsedAmount, showWrap, trade]
)
+ // const [routeNotFound, routeIsLoading, routeIsSyncing] = useMemo(
+ // () => [!trade?.swaps, TradeState.LOADING === tradeState, TradeState.SYNCING === tradeState],
+ // [trade, tradeState]
+ // )
+
+ // const fiatValueInput = useUSDCValue(parsedAmounts[Field.INPUT])
+ // const fiatValueOutput = useUSDCValue(parsedAmounts[Field.OUTPUT])
+ const fiatValueInput = useHigherUSDValue(parsedAmounts[Field.INPUT])
+ const fiatValueOutput = useHigherUSDValue(parsedAmounts[Field.OUTPUT])
+
const priceImpactParams = usePriceImpact({ abTrade: v2Trade, parsedAmounts, isWrapping: !!onWrap })
const { priceImpact, error: priceImpactError, loading: priceImpactLoading } = priceImpactParams
@@ -279,11 +272,6 @@ export default function Swap({
// don't show the unknown impact warning on: no trade, wrapping native, no error, or it's loading impact
const hideUnknownImpactWarning = !trade || !!onWrap || !priceImpactError || priceImpactLoading
- // const fiatValueInput = useUSDCValue(parsedAmounts[Field.INPUT])
- // const fiatValueOutput = useUSDCValue(parsedAmounts[Field.OUTPUT])
- const fiatValueInput = useHigherUSDValue(parsedAmounts[Field.INPUT])
- const fiatValueOutput = useHigherUSDValue(parsedAmounts[Field.OUTPUT])
-
const { onSwitchTokens, onCurrencySelection, onUserInput, onChangeRecipient } = useSwapActionHandlers()
// const isValid = !swapInputError
const isValid = !swapInputError && feeWarningAccepted && impactWarningAccepted // mod
@@ -311,7 +299,7 @@ export default function Swap({
// modal and loading
const [{ showConfirm, tradeToConfirm, swapErrorMessage, attemptingTxn, txHash }, setSwapState] = useState<{
showConfirm: boolean
- // tradeToConfirm: V2Trade | V3Trade | undefined
+ // tradeToConfirm: Trade | undefined
tradeToConfirm: TradeGp | undefined
attemptingTxn: boolean
swapErrorMessage: string | undefined
@@ -324,18 +312,27 @@ export default function Swap({
txHash: undefined,
})
- const formattedAmounts = {
- [independentField]: typedValue,
- [dependentField]: showWrap
- ? parsedAmounts[independentField]?.toExact() ?? ''
- : formatSmart(parsedAmounts[dependentField], AMOUNT_PRECISION) ?? '',
- }
+ const formattedAmounts = useMemo(
+ () => ({
+ [independentField]: typedValue,
+ [dependentField]: showWrap
+ ? parsedAmounts[independentField]?.toExact() ?? ''
+ : formatSmart(parsedAmounts[dependentField], AMOUNT_PRECISION) ?? '',
+ }),
+ [dependentField, independentField, parsedAmounts, showWrap, typedValue]
+ )
/* const userHasSpecifiedInputOutput = Boolean(
currencies[Field.INPUT] && currencies[Field.OUTPUT] && parsedAmounts[independentField]?.greaterThan(JSBI.BigInt(0))
)
- const routeNotFound = !trade?.route
- const isLoadingRoute = toggledVersion === Version.v3 && V3TradeState.LOADING === v3TradeState */
+
+ const approvalOptimizedTrade = useApprovalOptimizedTrade(trade, allowedSlippage)
+ const approvalOptimizedTradeString =
+ approvalOptimizedTrade instanceof V2Trade
+ ? 'V2SwapRouter'
+ : approvalOptimizedTrade instanceof V3Trade
+ ? 'V3SwapRouter'
+ : 'SwapRouter'*/
// check whether the user has approved the router on the input token
const { approvalState, approve: approveCallback } = useApproveCallbackFromTrade({
@@ -345,15 +342,16 @@ export default function Swap({
trade,
allowedSlippage,
})
- const prevApprovalState = usePrevious(approvalState)
+ const transactionDeadline = useTransactionDeadline()
+ const prevApprovalState = usePrevious(approvalState) // mod
const {
state: signatureState,
// signatureData,
gatherPermitSignature,
- } = useERC20PermitFromTrade(trade, allowedSlippage)
+ } = useERC20PermitFromTrade(trade, allowedSlippage, transactionDeadline)
const handleApprove = useCallback(async () => {
- let approveRequired = false
+ let approveRequired = false // mod
if (signatureState === UseERC20PermitState.NOT_SIGNED && gatherPermitSignature) {
try {
await gatherPermitSignature()
@@ -368,9 +366,14 @@ export default function Swap({
}
if (approveRequired) {
+ ReactGA.event({
+ category: 'Swap',
+ action: 'Approve',
+ label: v2Trade?.inputAmount?.currency.symbol,
+ })
return approveCallback().catch((error) => console.error('Error setting the allowance for token', error))
}
- }, [approveCallback, gatherPermitSignature, signatureState])
+ }, [approveCallback, gatherPermitSignature, signatureState, v2Trade?.inputAmount?.currency.symbol])
// check if user has gone through approval process, used to show two step buttons, reset on token change
const [approvalSubmitted, setApprovalSubmitted] = useState(false)
@@ -386,13 +389,17 @@ export default function Swap({
useEffect(() => {
if (approvalState === ApprovalState.PENDING) {
setApprovalSubmitted(true)
+ // mod
} else if (prevApprovalState === ApprovalState.PENDING && approvalState === ApprovalState.NOT_APPROVED) {
// user canceled the approval tx, reset the UI
setApprovalSubmitted(false)
}
}, [approvalState, approvalSubmitted, prevApprovalState])
- const maxInputAmount: CurrencyAmount | undefined = maxAmountSpend(currencyBalances[Field.INPUT])
+ const maxInputAmount: CurrencyAmount | undefined = useMemo(
+ () => maxAmountSpend(currencyBalances[Field.INPUT]),
+ [currencyBalances]
+ )
const showMaxButton = Boolean(maxInputAmount?.greaterThan(0) && !parsedAmounts[Field.INPUT]?.equalTo(maxInputAmount))
// the callback to execute the swap
@@ -433,9 +440,11 @@ export default function Swap({
? 'Swap w/o Send + recipient'
: 'Swap w/ Send',
label: [
+ // approvalOptimizedTradeString,
+ // approvalOptimizedTrade?.inputAmount?.currency?.symbol,
+ // approvalOptimizedTrade?.outputAmount?.currency?.symbol,
trade?.inputAmount?.currency?.symbol,
trade?.outputAmount?.currency?.symbol,
- // getTradeVersion(trade),
'MH',
].join('/'),
})
@@ -465,11 +474,14 @@ export default function Swap({
: priceImpact
: executionPriceImpact ?? priceImpact
)
- }, [priceImpact, trade]) */
+ }, [priceImpact, trade])
+
+ const isArgentWallet = useIsArgentWallet() */
// show approve flow when: no error on inputs, not approved or pending, or approved in current session
// never show if price impact is above threshold in non expert mode
const showApproveFlow =
+ // !isArgentWallet &&
!swapInputError &&
(approvalState === ApprovalState.NOT_APPROVED ||
approvalState === ApprovalState.PENDING ||
@@ -498,6 +510,10 @@ export default function Swap({
const handleMaxInput = useCallback(() => {
maxInputAmount && onUserInput(Field.INPUT, maxInputAmount.toExact())
+ ReactGA.event({
+ category: 'Swap',
+ action: 'Max',
+ })
}, [maxInputAmount, onUserInput])
const handleOutputSelect = useCallback(
@@ -596,8 +612,9 @@ export default function Swap({
fiatValue={fiatValueInput ?? undefined}
onCurrencySelect={handleInputSelect}
otherCurrency={currencies[Field.OUTPUT]}
- showCommonBases
+ showCommonBases={true}
id="swap-currency-input"
+ // loading={independentField === Field.OUTPUT && routeIsSyncing}
/>
{/* UNI ARROW SWITCHER */}
{/*
@@ -656,99 +673,42 @@ export default function Swap({
currency={currencies[Field.OUTPUT]}
onCurrencySelect={handleOutputSelect}
otherCurrency={currencies[Field.INPUT]}
- showCommonBases
+ showCommonBases={true}
id="swap-currency-output"
+ // loading={independentField === Field.INPUT && routeIsSyncing}
/>
{recipient !== null && !showWrap ? (
<>
-
+
+
+
onChangeRecipient(null)}>
- - Remove send
+ - Remove recipient
>
) : null}
-
- {showWrap ? null : (
- /*
-
-
- {[V3TradeState.VALID, V3TradeState.SYNCING, V3TradeState.NO_ROUTE_FOUND].includes(v3TradeState) &&
- (toggledVersion === Version.v3 && isTradeBetter(v3Trade, v2Trade) ? (
-
- ) : toggledVersion === Version.v2 && isTradeBetter(v2Trade, v3Trade) ? (
-
- ) : (
- toggledVersion === Version.v2 && (
-
-
-
-
- Back to
- V3
-
-
-
- )
- ))}
-
- {toggledVersion === Version.v3 && trade && isTradeBetter(v2Trade, v3Trade) && (
-
-
- V3
-
-
- )}
-
- {trade ? (
-
-
- }
- >
-
-
-
- ) : null}
-
- */
+ {/*
+ {!showWrap && userHasSpecifiedInputOutput && (trade || routeIsLoading || routeIsSyncing) && (
+
+ )}
+ */}
+ {!showWrap && (
{trade && (
@@ -756,14 +716,6 @@ export default function Swap({
)}
{!isExpertMode && !allowedSlippage.equalTo(INITIAL_ALLOWED_SLIPPAGE_PERCENT) && (
- //
- //
- // Slippage Tolerance
- //
- //
- // {formatSmart(allowedSlippage, PERCENTAGE_PRECISION)}%
- //
- //
)}
{(isFeeGreater || trade) && fee && }
@@ -802,9 +754,9 @@ export default function Swap({
{swapIsUnsupported ? (
-
+
Unsupported Token
-
+
) : !account ? (
@@ -834,39 +786,38 @@ export default function Swap({
) : quote?.error === 'fee-exceeds-sell-amount' ? (
) : quote?.error === 'insufficient-liquidity' ? (
- // ) : noRoute && userHasSpecifiedInputOutput ? (
-
+
Insufficient liquidity for this trade.
-
- {/* {singleHopOnly && Try enabling multi-hop trades. } */}
+
+ {/* {singleHopOnly && Try enabling multi-hop trades. } */}
) : quote?.error === 'zero-price' ? (
-
+
Invalid price. Try increasing input/output amount.
-
- {/* {singleHopOnly && Try enabling multi-hop trades. } */}
+
+ {/* {singleHopOnly && Try enabling multi-hop trades. } */}
) : quote?.error === 'transfer-eth-to-smart-contract' ? (
-
+
Buying {GpEther.onChain(chainId || SupportedChainId.MAINNET).symbol} with smart contract wallets is
not currently supported
-
- {/* {singleHopOnly && Try enabling multi-hop trades. } */}
+
+ {/* {singleHopOnly && Try enabling multi-hop trades. } */}
) : quote?.error === 'fetch-quote-error' ? (
-
+
Error loading price. Try again later.
-
+
) : quote?.error === 'offline-browser' ? (
- Error loading price. You are currently offline.
+ Error loading price. You are currently offline.
) : showApproveFlow ? (
@@ -912,7 +863,7 @@ export default function Swap({
- You must give the GP smart contracts permission to use your{' '}
+ You must give the CoW Protocol smart contracts permission to use your{' '}
{currencies[Field.INPUT]?.symbol}. You only have to do this once per token.
}
@@ -942,7 +893,10 @@ export default function Swap({
id="swap-button"
disabled={
!isValid ||
- (approvalState !== ApprovalState.APPROVED && signatureState !== UseERC20PermitState.SIGNED) // || priceImpactTooHigh
+ // routeIsSyncing ||
+ // routeIsLoading ||
+ (approvalState !== ApprovalState.APPROVED && signatureState !== UseERC20PermitState.SIGNED)
+ // priceImpactTooHigh
}
// error={isValid && priceImpactSeverity > 2}
>
@@ -952,7 +906,7 @@ export default function Swap({
{/*
{priceImpactTooHigh ? (
High Price Impact
- ) : priceImpactSeverity > 2 ? (
+ ) : trade && priceImpactSeverity > 2 ? (
Swap Anyway
) : (
Swap
@@ -978,7 +932,7 @@ export default function Swap({
}
}}
id="swap-button"
- disabled={!isValid /*|| priceImpactTooHigh */ || !!swapCallbackError}
+ disabled={!isValid /*|| routeIsSyncing || routeIsLoading || priceImpactTooHigh*/ || !!swapCallbackError}
// error={isValid && priceImpactSeverity > 2 && !swapCallbackError}
>
@@ -987,10 +941,12 @@ export default function Swap({
{/*
{swapInputError ? (
swapInputError
- ) : priceImpactTooHigh ? (
- Price Impact Too High
+ ) : routeIsSyncing || routeIsLoading ? (
+ Swap
) : priceImpactSeverity > 2 ? (
Swap Anyway
+ ) : priceImpactTooHigh ? (
+ Price Impact Too High
) : (
Swap
)}
@@ -1001,7 +957,10 @@ export default function Swap({
- {/* */}
+
+
+
+ {/* */}
{!swapIsUnsupported ? null : !isSupportedWallet ? (
- ) : !swapIsUnsupported ? (
-
) : (
)}
diff --git a/src/custom/pages/Swap/index.tsx b/src/custom/pages/Swap/index.tsx
index 5aaa67e234..f31614791e 100644
--- a/src/custom/pages/Swap/index.tsx
+++ b/src/custom/pages/Swap/index.tsx
@@ -4,7 +4,7 @@ import styled, { DefaultTheme } from 'styled-components/macro'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { BoxProps, Text } from 'rebass'
-import { ButtonSize, TYPE } from 'theme/index'
+import { ButtonSize, ThemedText } from 'theme/index'
import SwapMod from './SwapMod'
import { AutoRow, RowBetween } from 'components/Row'
@@ -211,7 +211,7 @@ export const FeesDiscount: React.FC = ({ onClick, theme, ...b
return (
-
+
Fees discount {' '}
= ({ onClick, theme, ...b
-
+
@@ -308,7 +308,7 @@ function SwitchToWethBtn({ wrappedToken }: SwitchToWethBtnProps) {
})
}
>
- Switch to {wrappedToken.symbol}
+ Switch to {wrappedToken.symbol}
)
}
@@ -362,12 +362,12 @@ const TradeLoading = ({ showButton = false }: TradeLoadingProps) => {
const InsideContent = useCallback(
() => (
-
+
{isLongLoad && Hang in there. Calculating best price }
-
+
),
[isLongLoad]
)
diff --git a/src/custom/pages/error/AnySwapAffectedUsers/RedirectAnySwapAffectedUsers.tsx b/src/custom/pages/error/AnySwapAffectedUsers/RedirectAnySwapAffectedUsers.tsx
index 6a046a90db..bc532dc9f8 100644
--- a/src/custom/pages/error/AnySwapAffectedUsers/RedirectAnySwapAffectedUsers.tsx
+++ b/src/custom/pages/error/AnySwapAffectedUsers/RedirectAnySwapAffectedUsers.tsx
@@ -13,6 +13,7 @@ export default function RedirectAnySwapAffectedUsers() {
const isAnySwapAffectedUser = useIsAnySwapAffectedUser()
useEffect(() => {
+ // eslint-disable-next-line no-restricted-globals
if (isAnySwapAffectedUser && location.pathname !== WARNING_PAGE) {
// Redirect to warning page
history.push(WARNING_PAGE)
diff --git a/src/custom/pages/error/AnySwapAffectedUsers/index.tsx b/src/custom/pages/error/AnySwapAffectedUsers/index.tsx
index 57e5a7148e..0694633fe9 100644
--- a/src/custom/pages/error/AnySwapAffectedUsers/index.tsx
+++ b/src/custom/pages/error/AnySwapAffectedUsers/index.tsx
@@ -7,7 +7,7 @@ import { ExternalLink as ExternalLinkTheme } from 'theme'
const ExternalLink = styled(ExternalLinkTheme)``
const Wrapper = styled(Page)`
- ${GdocsListStyle}
+ ${GdocsListStyle};
min-height: auto;
padding-bottom: 32px;
${({ theme }) => theme.mediaWidth.upToSmall`
diff --git a/src/custom/pages/error/AnySwapAffectedUsers/useIsAnySwapAffectedUser.ts b/src/custom/pages/error/AnySwapAffectedUsers/useIsAnySwapAffectedUser.ts
index 8e99aabd65..648021e2e2 100644
--- a/src/custom/pages/error/AnySwapAffectedUsers/useIsAnySwapAffectedUser.ts
+++ b/src/custom/pages/error/AnySwapAffectedUsers/useIsAnySwapAffectedUser.ts
@@ -1,5 +1,5 @@
-import { useMultipleContractSingleData } from 'state/multicall/hooks'
-import { WETH9_EXTENDED as WETH } from 'constants/tokens'
+import { useMultipleContractSingleData } from 'lib/hooks/multicall'
+import { WRAPPED_NATIVE_CURRENCY as WETH } from 'constants/tokens'
import { Interface } from '@ethersproject/abi'
import ERC20_ABI from 'abis/erc20.json'
import { Erc20Interface } from '@src/abis/types/Erc20'
@@ -34,20 +34,16 @@ export default function useIsAnySwapAffectedUser() {
{ blocksPerFetch: BLOCKS_PER_FETCH }
)
- const isAffected = useMemo(() => {
+ return useMemo(() => {
// The error affects Mainnet
if (chainId !== ChainId.MAINNET) {
return false
}
// Check if any of the tokens has allowance in the router contract
- const hasAllowance = result.some(({ result, loading, error, valid }) => {
+ return result.some(({ result, loading, error, valid }) => {
const allowance = valid && !loading && !error && result ? (result[0] as BigNumber) : undefined
return allowance ? !allowance.isZero() : false
})
-
- return hasAllowance
}, [chainId, result])
-
- return isAffected
}
diff --git a/src/custom/state/application/hooks.ts b/src/custom/state/application/hooks.ts
index b2f6ff066d..2bb60aae3d 100644
--- a/src/custom/state/application/hooks.ts
+++ b/src/custom/state/application/hooks.ts
@@ -1,7 +1,9 @@
import { createAction } from '@reduxjs/toolkit'
-
-import { ApplicationModal } from 'state/application/reducer'
import { useToggleModal } from '@src/state/application/hooks'
+import { useCallback } from 'react'
+import { ApplicationModal } from 'state/application/reducer'
+import { useAppDispatch } from 'state/hooks'
+
export * from '@src/state/application/hooks'
export const setOpenModal = createAction('application/setOpenModal')
@@ -9,3 +11,14 @@ export const setOpenModal = createAction('application/s
export function useToggleTransactionConfirmation(): () => void {
return useToggleModal(ApplicationModal.TRANSACTION_CONFIRMATION)
}
+
+// TODO: These two seem to be gone from original. Check whether they have been replaced
+export function useOpenModal(modal: ApplicationModal): () => void {
+ const dispatch = useAppDispatch()
+ return useCallback(() => dispatch(setOpenModal(modal)), [dispatch, modal])
+}
+
+export function useCloseModals(): () => void {
+ const dispatch = useAppDispatch()
+ return useCallback(() => dispatch(setOpenModal(null)), [dispatch])
+}
diff --git a/src/custom/state/application/updater.ts b/src/custom/state/application/updater.ts
index e20862fa21..517065badd 100644
--- a/src/custom/state/application/updater.ts
+++ b/src/custom/state/application/updater.ts
@@ -1,137 +1,56 @@
-import { CHAIN_INFO } from 'constants/chains'
-import useDebounce from 'hooks/useDebounce'
-import useIsWindowVisible from 'hooks/useIsWindowVisible'
-import { useActiveWeb3React } from 'hooks/web3'
-import ms from 'ms.macro'
-import { useCallback, useEffect, useRef, useState } from 'react'
-// import { api, CHAIN_TAG } from 'state/data/enhanced'
-import { useAppDispatch, useAppSelector } from 'state/hooks'
-import { supportedChainId } from 'utils/supportedChainId'
-import { switchToNetwork } from 'utils/switchToNetwork'
-
-import { useBlockNumber } from '@src/state/application/hooks'
-import {
- setChainConnectivityWarning,
- setImplements3085,
- updateBlockNumber,
- updateChainId,
-} from 'state/application/reducer'
-
-// function useQueryCacheInvalidator() {
+// import useActiveWeb3React from 'hooks/useActiveWeb3React'
+// import useDebounce from 'hooks/useDebounce'
+// import useIsWindowVisible from 'hooks/useIsWindowVisible'
+// import { useCallback, useEffect, useRef, useState } from 'react'
+// // import { api, CHAIN_TAG } from 'state/data/enhanced'
+// import { useAppDispatch, useAppSelector } from 'state/hooks'
+// import { supportedChainId } from 'utils/supportedChainId'
+//
+// import { updateChainId } from 'state/application/reducer'
+//
+// // MOD imports
+// import { useBlockNumber } from '@src/state/application/hooks'
+// import ms from 'ms.macro'
+// import { CHAIN_INFO } from 'constants/chains'
+// import { switchToNetwork } from 'utils/switchToNetwork'
+//
+// /* function useQueryCacheInvalidator() {
// const dispatch = useAppDispatch()
-
+//
// // subscribe to `chainId` changes in the redux store rather than Web3
// // this will ensure that when `invalidateTags` is called, the latest
// // `chainId` is available in redux to build the subgraph url
// const chainId = useAppSelector((state) => state.application.chainId)
-
+//
// useEffect(() => {
// dispatch(api.util.invalidateTags([CHAIN_TAG]))
// }, [chainId, dispatch])
+// } */
+//
+// export default function Updater(): null {
+// const { chainId, library } = useActiveWeb3React()
+// const dispatch = useAppDispatch()
+// const windowVisible = useIsWindowVisible()
+//
+// const [activeChainId, setActiveChainId] = useState(chainId)
+//
+// // useQueryCacheInvalidator()
+//
+// useEffect(() => {
+// if (library && chainId && windowVisible) {
+// setActiveChainId(chainId)
+// }
+// }, [dispatch, chainId, library, windowVisible])
+//
+// const debouncedChainId = useDebounce(activeChainId, 100)
+//
+// useEffect(() => {
+// const chainId = debouncedChainId ? supportedChainId(debouncedChainId) ?? null : null
+// dispatch(updateChainId({ chainId }))
+// }, [dispatch, debouncedChainId])
+//
+// return null
// }
-
-const NETWORK_HEALTH_CHECK_MS = ms`15s`
-const DEFAULT_MS_BEFORE_WARNING = ms`10m`
-
-function useBlockWarningTimer() {
- const { chainId } = useActiveWeb3React()
- const dispatch = useAppDispatch()
- const chainConnectivityWarningActive = useAppSelector((state) => state.application.chainConnectivityWarning)
- const timeout = useRef()
- const isWindowVisible = useIsWindowVisible()
- const [msSinceLastBlock, setMsSinceLastBlock] = useState(0)
- const currentBlock = useBlockNumber()
-
- useEffect(() => {
- setMsSinceLastBlock(0)
- }, [currentBlock])
-
- useEffect(() => {
- const waitMsBeforeWarning =
- (chainId ? CHAIN_INFO[chainId]?.blockWaitMsBeforeWarning : DEFAULT_MS_BEFORE_WARNING) ?? DEFAULT_MS_BEFORE_WARNING
-
- timeout.current = setTimeout(() => {
- setMsSinceLastBlock(NETWORK_HEALTH_CHECK_MS + msSinceLastBlock)
- if (msSinceLastBlock > waitMsBeforeWarning && isWindowVisible) {
- dispatch(setChainConnectivityWarning({ warn: true }))
- } else if (chainConnectivityWarningActive) {
- dispatch(setChainConnectivityWarning({ warn: false }))
- }
- }, NETWORK_HEALTH_CHECK_MS)
-
- return function cleanup() {
- if (timeout.current) {
- clearTimeout(timeout.current)
- }
- }
- }, [chainId, chainConnectivityWarningActive, dispatch, isWindowVisible, msSinceLastBlock, setMsSinceLastBlock])
-}
-
-export default function Updater(): null {
- const { account, chainId, library } = useActiveWeb3React()
- const dispatch = useAppDispatch()
- const windowVisible = useIsWindowVisible()
-
- const [state, setState] = useState<{ chainId: number | undefined; blockNumber: number | null }>({
- chainId,
- blockNumber: null,
- })
-
- useBlockWarningTimer()
- // useQueryCacheInvalidator()
-
- const blockNumberCallback = useCallback(
- (blockNumber: number) => {
- setState((state) => {
- if (chainId === state.chainId) {
- if (typeof state.blockNumber !== 'number') return { chainId, blockNumber }
- return { chainId, blockNumber: Math.max(blockNumber, state.blockNumber) }
- }
- return state
- })
- },
- [chainId, setState]
- )
-
- // attach/detach listeners
- useEffect(() => {
- if (!library || !chainId || !windowVisible) return undefined
-
- setState({ chainId, blockNumber: null })
-
- library
- .getBlockNumber()
- .then(blockNumberCallback)
- .catch((error) => console.error(`Failed to get block number for chainId: ${chainId}`, error))
-
- library.on('block', blockNumberCallback)
- return () => {
- library.removeListener('block', blockNumberCallback)
- }
- }, [dispatch, chainId, library, blockNumberCallback, windowVisible])
-
- const debouncedState = useDebounce(state, 100)
-
- useEffect(() => {
- if (!debouncedState.chainId || !debouncedState.blockNumber || !windowVisible) return
- dispatch(updateBlockNumber({ chainId: debouncedState.chainId, blockNumber: debouncedState.blockNumber }))
- }, [windowVisible, dispatch, debouncedState.blockNumber, debouncedState.chainId])
-
- useEffect(() => {
- dispatch(
- updateChainId({ chainId: debouncedState.chainId ? supportedChainId(debouncedState.chainId) ?? null : null })
- )
- }, [dispatch, debouncedState.chainId])
-
- useEffect(() => {
- if (!account || !library?.provider?.request || !library?.provider?.isMetaMask) {
- dispatch(setImplements3085({ implements3085: false }))
- return
- }
- switchToNetwork({ library })
- .then((x) => x ?? dispatch(setImplements3085({ implements3085: true })))
- .catch(() => dispatch(setImplements3085({ implements3085: false })))
- }, [account, chainId, dispatch, library])
-
- return null
-}
+// TODO: is this still needed? Maybe not, could remove it entirely
+import updater from '@src/state/application/updater'
+export default updater
diff --git a/src/custom/state/claim/hooks/index.ts b/src/custom/state/claim/hooks/index.ts
index 0b0bfbb063..86632779ea 100644
--- a/src/custom/state/claim/hooks/index.ts
+++ b/src/custom/state/claim/hooks/index.ts
@@ -10,7 +10,7 @@ import { VCow as VCowType } from 'abis/types'
import { useVCowContract } from 'hooks/useContract'
import { useActiveWeb3React } from 'hooks/web3'
-import { useSingleContractMultipleData, useSingleCallResult, Result } from 'state/multicall/hooks'
+import { useSingleContractMultipleData, useSingleCallResult, CallStateResult } from 'lib/hooks/multicall'
import { useTransactionAdder } from 'state/enhancedTransactions/hooks'
import { GpEther, V_COW } from 'constants/tokens'
@@ -29,8 +29,6 @@ import {
import { SupportedChainId } from 'constants/chains'
import { useAllClaimingTransactionIndices } from 'state/enhancedTransactions/hooks'
-export { useUserClaimData, useUserHasAvailableClaim } from '@src/state/claim/hooks'
-
import { AppDispatch } from 'state'
import { useSelector, useDispatch } from 'react-redux'
import { AppState } from 'state'
@@ -66,6 +64,8 @@ import { ClaimInfo } from 'state/claim/reducer'
import { OperationType } from '@src/custom/components/TransactionConfirmationModal'
import { APPROVE_GAS_LIMIT_DEFAULT } from 'hooks/useApproveCallback/useApproveCallbackMod'
+export { useUserClaimData, useUserHasAvailableClaim } from '@src/state/claim/hooks'
+
const CLAIMS_REPO_BRANCH = 'gip-13'
export const CLAIMS_REPO = `https://raw.githubusercontent.com/gnosis/cow-merkle-drop/${CLAIMS_REPO_BRANCH}/`
@@ -315,7 +315,7 @@ function useDeploymentTimestamp(): number | null {
}
// Invalidate timestamp
- if (chainId != oldChainId.current) {
+ if (chainId !== oldChainId.current) {
setTimestamp(null)
oldChainId.current = chainId
}
@@ -579,7 +579,7 @@ export function useClaimCallback(account: string | null | undefined): {
const extendedArgs = _extendFinalArg(args, {
from: connectedAccount, // add the `from` as the connected account
- gasLimit: calculateGasMargin(chainId, gasLimit),
+ gasLimit: calculateGasMargin(gasLimit),
})
return vCowContract.claimMany(...extendedArgs).then((response: TransactionResponse) => {
@@ -1009,7 +1009,7 @@ export const useClaimLinks = () => {
/**
* Hook that parses the result input with BigNumber value to CurrencyAmount
*/
-function useParseVCowResult(result: Result | undefined) {
+function useParseVCowResult(result: CallStateResult | undefined) {
const { chainId } = useActiveWeb3React()
const vCowToken = chainId ? V_COW[chainId] : undefined
diff --git a/src/custom/state/claim/hooks/utils.ts b/src/custom/state/claim/hooks/utils.ts
index c0d5ec43c0..1d97c86661 100644
--- a/src/custom/state/claim/hooks/utils.ts
+++ b/src/custom/state/claim/hooks/utils.ts
@@ -1,7 +1,7 @@
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
import { SupportedChainId } from 'constants/chains'
-import { GNO, GpEther, USDC_BY_CHAIN } from 'constants/tokens'
+import { GNO, GpEther, USDC } from 'constants/tokens'
import { ONE_HUNDRED_PERCENT, ZERO_PERCENT } from 'constants/misc'
import {
@@ -119,7 +119,7 @@ export function claimTypeToToken(type: ClaimType, chainId: SupportedChainId) {
case ClaimType.GnoOption:
return GNO[chainId]
case ClaimType.Investor:
- return USDC_BY_CHAIN[chainId]
+ return USDC[chainId]
case ClaimType.UserOption:
return GpEther.onChain(chainId)
case ClaimType.Advisor:
diff --git a/src/custom/state/claim/updater.tsx b/src/custom/state/claim/updater.tsx
index 7894783942..4a11a5d509 100644
--- a/src/custom/state/claim/updater.tsx
+++ b/src/custom/state/claim/updater.tsx
@@ -1,7 +1,7 @@
import { useEffect, useMemo } from 'react'
import { SupportedChainId } from 'constants/chains'
import { ClassifiedUserClaims, useClaimDispatchers, useClaimState, useClassifiedUserClaims } from './hooks'
-import { useActiveWeb3React } from 'hooks'
+import { useActiveWeb3React } from 'hooks/web3'
import { ClaimInfo } from 'state/claim/reducer'
export default function Updater() {
diff --git a/src/custom/state/cowToken/hooks.ts b/src/custom/state/cowToken/hooks.ts
index a57cbca391..931b32f942 100644
--- a/src/custom/state/cowToken/hooks.ts
+++ b/src/custom/state/cowToken/hooks.ts
@@ -5,7 +5,7 @@ import { TransactionResponse } from '@ethersproject/providers'
import { useVCowContract } from 'hooks/useContract'
import { useActiveWeb3React } from 'hooks/web3'
-import { useSingleCallResult, Result } from 'state/multicall/hooks'
+import { useSingleCallResult, CallStateResult as Result } from 'lib/hooks/multicall'
import { useTransactionAdder } from 'state/enhancedTransactions/hooks'
import { V_COW, COW } from 'constants/tokens'
import { AppState } from 'state'
diff --git a/src/custom/state/enhancedTransactions/hooks/TransactionHooksMod.tsx b/src/custom/state/enhancedTransactions/hooks/TransactionHooksMod.tsx
index 2afc57e86b..df5529354a 100644
--- a/src/custom/state/enhancedTransactions/hooks/TransactionHooksMod.tsx
+++ b/src/custom/state/enhancedTransactions/hooks/TransactionHooksMod.tsx
@@ -1,40 +1,41 @@
// import { TransactionResponse } from '@ethersproject/providers'
+// import { Token } from '@uniswap/sdk-core'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+// import { useTransactionMonitoringEventCallback } from 'hooks/useMonitoringEventCallback'
import { /* useCallback,*/ useMemo } from 'react'
import { /* useAppDispatch,*/ useAppSelector } from 'state/hooks'
-import { useActiveWeb3React } from 'hooks/web3'
-// import { addTransaction } from '../actions'
+// import { addTransaction, TransactionInfo, TransactionType } from './actions'
+// import { TransactionDetails } from './reducer'
+
+// MOD imports
import { EnhancedTransactionDetails } from '../reducer'
+// import { TransactionDetails } from '@src/state/transactions/reducer'
+// import { TransactionType } from '@src/state/transactions/actions'
+
+// helper that can take a ethers library transaction response and add it to the list of transactions
+/* export function useTransactionAdder(): (response: TransactionResponse, info: TransactionInfo) => void {
+ const { chainId, account } = useActiveWeb3React()
+ const dispatch = useAppDispatch()
+
+ const logMonitoringEvent = useTransactionMonitoringEventCallback()
-// // helper that can take a ethers library transaction response and add it to the list of transactions
-// export function useTransactionAdder(): (
-// response: TransactionResponse,
-// customData?: { summary?: string; approval?: { tokenAddress: string; spender: string }; claim?: { recipient: string } }
-// ) => void {
-// const { chainId, account } = useActiveWeb3React()
-// const dispatch = useAppDispatch()
-
-// return useCallback(
-// (
-// response: TransactionResponse,
-// {
-// summary,
-// approval,
-// claim,
-// }: { summary?: string; claim?: { recipient: string }; approval?: { tokenAddress: string; spender: string } } = {}
-// ) => {
-// if (!account) return
-// if (!chainId) return
-
-// const { hash } = response
-// if (!hash) {
-// throw Error('No transaction hash found.')
-// }
-// dispatch(addTransaction({ hash, from: account, chainId, approval, summary, claim }))
-// },
-// [dispatch, chainId, account]
-// )
-// }
+ return useCallback(
+ (response: TransactionResponse, info: TransactionInfo) => {
+ if (!account) return
+ if (!chainId) return
+
+ const { hash } = response
+ if (!hash) {
+ throw Error('No transaction hash found.')
+ }
+ dispatch(addTransaction({ hash, from: account, info, chainId }))
+
+ logMonitoringEvent(info, response)
+ },
+ [account, chainId, dispatch, logMonitoringEvent]
+ )
+} */
// returns all the transactions for the current chain
export function useAllTransactions(): { [txHash: string]: EnhancedTransactionDetails } {
@@ -45,6 +46,16 @@ export function useAllTransactions(): { [txHash: string]: EnhancedTransactionDet
return chainId ? state[chainId] ?? {} : {}
}
+/* export function useTransaction(transactionHash?: string): TransactionDetails | undefined {
+ const allTransactions = useAllTransactions()
+
+ if (!transactionHash) {
+ return undefined
+ }
+
+ return allTransactions[transactionHash]
+} */
+
export function useIsTransactionPending(transactionHash?: string): boolean {
const transactions = useAllTransactions()
@@ -53,6 +64,14 @@ export function useIsTransactionPending(transactionHash?: string): boolean {
return !transactions[transactionHash].receipt
}
+/* export function useIsTransactionConfirmed(transactionHash?: string): boolean {
+ const transactions = useAllTransactions()
+
+ if (!transactionHash || !transactions[transactionHash]) return false
+
+ return Boolean(transactions[transactionHash].receipt)
+} */
+
/**
* Returns whether a transaction happened in the last day (86400 seconds * 1000 milliseconds / second)
* @param tx to check for recency
@@ -85,20 +104,20 @@ export function useHasPendingApproval(tokenAddress: string | undefined, spender:
// watch for submissions to claim
// return null if not done loading, return undefined if not found
-// export function useUserHasSubmittedClaim(account?: string): {
-// claimSubmitted: boolean
-// claimTxn: EnhancedTransactionDetails | undefined
-// } {
-// const allTransactions = useAllTransactions()
-
-// // get the txn if it has been submitted
-// const claimTxn = useMemo(() => {
-// const txnIndex = Object.keys(allTransactions).find((hash) => {
-// const tx = allTransactions[hash]
-// return tx.claim && tx.claim.recipient === account
-// })
-// return txnIndex && allTransactions[txnIndex] ? allTransactions[txnIndex] : undefined
-// }, [account, allTransactions])
-
-// return { claimSubmitted: Boolean(claimTxn), claimTxn }
-// }
+/* export function useUserHasSubmittedClaim(account?: string): {
+ claimSubmitted: boolean
+ claimTxn: TransactionDetails | undefined
+} {
+ const allTransactions = useAllTransactions()
+
+ // get the txn if it has been submitted
+ const claimTxn = useMemo(() => {
+ const txnIndex = Object.keys(allTransactions).find((hash) => {
+ const tx = allTransactions[hash]
+ return tx.info.type === TransactionType.CLAIM && tx.info.recipient === account
+ })
+ return txnIndex && allTransactions[txnIndex] ? allTransactions[txnIndex] : undefined
+ }, [account, allTransactions])
+
+ return { claimSubmitted: Boolean(claimTxn), claimTxn }
+} */
diff --git a/src/custom/state/enhancedTransactions/updater/FinalizeTxUpdater.tsx b/src/custom/state/enhancedTransactions/updater/FinalizeTxUpdater.tsx
index 168cd2f410..8a5bf85b54 100644
--- a/src/custom/state/enhancedTransactions/updater/FinalizeTxUpdater.tsx
+++ b/src/custom/state/enhancedTransactions/updater/FinalizeTxUpdater.tsx
@@ -6,8 +6,8 @@ import { useEffect, useMemo } from 'react'
import { useAppDispatch } from 'state/hooks'
// import { SupportedChainId } from 'constants/chains'
import { useActiveWeb3React } from 'hooks/web3'
-import { updateBlockNumber } from 'state/application/reducer'
-import { useAddPopup, useBlockNumber } from 'state/application/hooks'
+import { useAddPopup } from 'state/application/hooks'
+import useBlockNumber from 'lib/hooks/useBlockNumber'
import { checkedTransaction, finalizeTransaction, updateSafeTransaction } from '../actions'
import { EnhancedTransactionDetails, HashType } from '../reducer'
import { GetReceipt, useGetReceipt } from 'hooks/useGetReceipt'
@@ -58,7 +58,7 @@ function finalizeEthereumTransaction(
transaction: EnhancedTransactionDetails,
params: CheckEthereumTransactions
) {
- const { chainId, lastBlockNumber, addPopup, dispatch } = params
+ const { chainId, addPopup, dispatch } = params
const { hash } = transaction
console.log(`[FinalizeTxUpdater] Transaction ${receipt.transactionHash} has been mined`, receipt)
@@ -90,11 +90,6 @@ function finalizeEthereumTransaction(
},
hash
)
-
- // the receipt was fetched before the block, fast forward to that block to trigger balance updates
- if (receipt.blockNumber > lastBlockNumber) {
- dispatch(updateBlockNumber({ chainId, blockNumber: receipt.blockNumber }))
- }
}
function checkEthereumTransactions(params: CheckEthereumTransactions): Cancel[] {
diff --git a/src/custom/state/global/actions/actionsMod.ts b/src/custom/state/global/actions/actionsMod.ts
index a99860e2df..d6550de9af 100644
--- a/src/custom/state/global/actions/actionsMod.ts
+++ b/src/custom/state/global/actions/actionsMod.ts
@@ -1,4 +1,6 @@
import { createAction } from '@reduxjs/toolkit'
+
+// MOD imports
import { WithChainId } from 'state/lists/actions'
// fired once when the app reloads but before the app renders
diff --git a/src/custom/state/hooks/hooksMod.ts b/src/custom/state/hooks/hooksMod.ts
index 05ae99d3a5..cc39a50354 100644
--- a/src/custom/state/hooks/hooksMod.ts
+++ b/src/custom/state/hooks/hooksMod.ts
@@ -1,6 +1,7 @@
-import { TypedUseSelectorHook, useSelector } from 'react-redux'
-import { AppState } from 'state'
-
-export * from '@src/state/hooks'
+import { TypedUseSelectorHook /*, useDispatch*/, useSelector } from 'react-redux'
+import { /*AppDispatch,*/ AppState } from 'state'
+// export const useAppDispatch = () => useDispatch()
export const useAppSelector: TypedUseSelectorHook = useSelector
+
+export * from '@src/state/hooks'
diff --git a/src/custom/state/index.ts b/src/custom/state/index.ts
index 27a2468722..4cb9e64c96 100644
--- a/src/custom/state/index.ts
+++ b/src/custom/state/index.ts
@@ -1,26 +1,27 @@
import { configureStore, StateFromReducersMapObject } from '@reduxjs/toolkit'
-import { save, load } from 'redux-localstorage-simple'
+// import { setupListeners } from '@reduxjs/toolkit/query/react'
+import multicall from 'lib/state/multicall'
+import { load, save } from 'redux-localstorage-simple'
import application from 'state/application/reducer'
-// import { updateVersion } from '@src/state/global/actions'
-import user from '@src/state/user/reducer'
-import swap from '@src/state/swap/reducer'
-// import mint from '@src/state/mint/reducer'
-// import mintV3 from '@src/state/mint/v3/reducer'
-// import lists from '@src/state/lists/reducer'
-// import burn from '@src/state/burn/reducer'
-// import burnV3 from '@src/state/burn/v3/reducer'
-import logs from '@src/state/logs/slice'
-import multicall from '@src/state/multicall/reducer'
-// import { api as dataApi } from '@src/state/data/slice'
-// import { routingApi } from '@src/state/routing/slice'
-// CUSTOM REDUCERS
+// import burn from './burn/reducer'
+// import burnV3 from './burn/v3/reducer'
+// import { api as dataApi } from './data/slice'
+import { updateVersion } from 'state/global/actions'
import lists from 'state/lists/reducer'
+import logs from '@src/state/logs/slice'
+// import mint from './mint/reducer'
+// import mintV3 from './mint/v3/reducer'
+// import { routingApi } from './routing/slice'
+import swap from '@src/state/swap/reducer'
+// import transactions from './transactions/reducer'
+import user from '@src/state/user/reducer'
+
+// MOD imports
import orders from 'state/orders/reducer'
import price from 'state/price/reducer'
import gas from 'state/gas/reducer'
import profile from 'state/profile/reducer'
-import { updateVersion } from 'state/global/actions'
import affiliate from 'state/affiliate/reducer'
import enhancedTransactions from 'state/enhancedTransactions/reducer'
import claim from 'state/claim/reducer'
@@ -33,16 +34,17 @@ import { DEFAULT_NETWORK_FOR_LISTS } from 'constants/lists'
const UNISWAP_REDUCERS = {
application,
user,
+ // transactions,
swap,
- // mint,
- // mintV3,
- // burn,
- // burnV3,
- multicall,
+ /* mint,
+ mintV3,
+ burn,
+ burnV3, */
+ multicall: multicall.reducer,
// lists,
logs,
- // [dataApi.reducerPath]: dataApi.reducer,
- // [routingApi.reducerPath]: routingApi.reducer,
+ /* [dataApi.reducerPath]: dataApi.reducer,
+ [routingApi.reducerPath]: routingApi.reducer, */
}
const reducers = {
@@ -76,10 +78,15 @@ const store = configureStore({
// this instantiate the app / reducers in several places using the default chainId
store.dispatch(updateVersion({ chainId: DEFAULT_NETWORK_FOR_LISTS }))
+// TODO: this is new, should we enable it?
+// setupListeners(store.dispatch)
+
export default store
// need to AppState derive from something other than store
// otherwise get circular reference
// if we want to use AppState in Middleware<, AppState>
+// TODO: the original does not do that, maybe no longer needed?
+// export type AppState = ReturnType
export type AppState = StateFromReducersMapObject
export type AppDispatch = typeof store.dispatch
diff --git a/src/custom/state/lists/actions/actionsMod.ts b/src/custom/state/lists/actions/actionsMod.ts
index 8adb658f26..5bfcbdd520 100644
--- a/src/custom/state/lists/actions/actionsMod.ts
+++ b/src/custom/state/lists/actions/actionsMod.ts
@@ -1,6 +1,9 @@
import { ActionCreatorWithPayload, createAction } from '@reduxjs/toolkit'
import { TokenList, Version } from '@uniswap/token-lists'
+
+// MOD imports
import { SupportedChainId as ChainId } from 'constants/chains'
+
export { SupportedChainId as ChainId } from 'constants/chains'
export interface WithChainId {
diff --git a/src/custom/state/lists/hooks/hooksMod.ts b/src/custom/state/lists/hooks/hooksMod.ts
index 779feec4df..2c07817d7f 100644
--- a/src/custom/state/lists/hooks/hooksMod.ts
+++ b/src/custom/state/lists/hooks/hooksMod.ts
@@ -1,15 +1,18 @@
-import DEFAULT_TOKEN_LIST from '@uniswap/default-token-list'
-import { TokenList } from '@uniswap/token-lists'
+// import { ChainTokenMap, tokensToChainTokenMap } from 'lib/hooks/useTokenList/utils'
import { useMemo, useCallback } from 'react'
import { useAppSelector, useAppDispatch } from 'state/hooks'
+
import sortByListPriority from 'utils/listSort'
import BROKEN_LIST from 'constants/tokenLists/broken.tokenlist.json'
import UNSUPPORTED_TOKEN_LIST from 'constants/tokenLists/unsupported.tokenlist.json'
import { AppState } from 'state'
-import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
import { UNSUPPORTED_LIST_URLS, DEFAULT_NETWORK_FOR_LISTS } from 'constants/lists'
-// MOD
+
+// MOD imports
+import { TokenList } from '@uniswap/token-lists'
+import DEFAULT_TOKEN_LIST from '@uniswap/default-token-list'
+import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
import { useActiveWeb3React } from 'hooks/web3'
import {
addGpUnsupportedToken,
@@ -23,10 +26,7 @@ import { SupportedChainId as ChainId } from 'constants/chains'
import { supportedChainId } from 'utils/supportedChainId'
import { TokenAddressMap, combineMaps } from '@src/state/lists/hooks'
-/*
-export type TokenAddressMap = Readonly<{
- [chainId: number]: Readonly<{ [tokenAddress: string]: { token: WrappedTokenInfo; list: TokenList } }>
-}>
+/* export type TokenAddressMap = ChainTokenMap
type Mutable = {
-readonly [P in keyof T]: Mutable
@@ -104,8 +104,7 @@ export function useAllLists(): AppState['lists'][ChainId]['byUrl'] {
* @param map1 the base token map
* @param map2 the map of additioanl tokens to add to the base map
*/
-/*
-export function combineMaps(map1: TokenAddressMap, map2: TokenAddressMap): TokenAddressMap {
+/* export function combineMaps(map1: TokenAddressMap, map2: TokenAddressMap): TokenAddressMap {
const chainIds = Object.keys(
Object.keys(map1)
.concat(Object.keys(map2))
@@ -123,8 +122,7 @@ export function combineMaps(map1: TokenAddressMap, map2: TokenAddressMap): Token
}
return memo
}, {}) as TokenAddressMap
-}
-*/
+} */
// merge tokens contained within lists from urls
export function useCombinedTokenMapFromUrls(urls: string[] | undefined): TokenAddressMap {
diff --git a/src/custom/state/lists/reducer/reducerMod.ts b/src/custom/state/lists/reducer/reducerMod.ts
index 4f67b9d4c7..859f25c167 100644
--- a/src/custom/state/lists/reducer/reducerMod.ts
+++ b/src/custom/state/lists/reducer/reducerMod.ts
@@ -1,3 +1,5 @@
+import { createReducer } from '@reduxjs/toolkit'
+import { getVersionUpgrade, VersionUpgrade } from '@uniswap/token-lists'
import {
// DEFAULT_ACTIVE_LIST_URLS,
DEFAULT_LIST_OF_LISTS_BY_NETWORK,
@@ -5,10 +7,10 @@ import {
DEFAULT_NETWORK_FOR_LISTS,
UNSUPPORTED_LIST_URLS,
} from 'constants/lists'
-import { createReducer } from '@reduxjs/toolkit'
-import { getVersionUpgrade, VersionUpgrade } from '@uniswap/token-lists'
-import { TokenList } from '@uniswap/token-lists'
// import { DEFAULT_LIST_OF_LISTS } from '@src/constants/lists'
+
+// MOD imports
+import { TokenList } from '@uniswap/token-lists'
import { updateVersion } from 'state/global/actions'
import {
acceptListUpdate,
diff --git a/src/custom/state/lists/updater/updaterMod.ts b/src/custom/state/lists/updater/updaterMod.ts
index 6e48c160eb..c91dd58a65 100644
--- a/src/custom/state/lists/updater/updaterMod.ts
+++ b/src/custom/state/lists/updater/updaterMod.ts
@@ -1,17 +1,18 @@
import { getVersionUpgrade, minVersionBump, VersionUpgrade } from '@uniswap/token-lists'
import { supportedChainId } from 'utils/supportedChainId'
import { /* ARBITRUM_LIST, OPTIMISM_LIST, */ DEFAULT_NETWORK_FOR_LISTS, UNSUPPORTED_LIST_URLS } from 'constants/lists'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import useInterval from 'lib/hooks/useInterval'
import { useCallback, useEffect } from 'react'
import { useAppDispatch } from 'state/hooks'
import { useAllLists } from 'state/lists/hooks'
import { useFetchListCallback } from 'hooks/useFetchListCallback'
-import useInterval from 'hooks/useInterval'
import useIsWindowVisible from 'hooks/useIsWindowVisible'
-import { useActiveWeb3React } from 'hooks/web3'
import { acceptListUpdate /* , enableList */ } from 'state/lists/actions'
import { useActiveListUrls } from 'state/lists/hooks'
+// MOD imports
// MOD: add updateVersion for chainId change init
import { updateVersion } from 'state/global/actions'
@@ -37,12 +38,10 @@ export default function Updater(): null {
/* useEffect(() => {
if (chainId && [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)) {
- // dispatch(enableList(OPTIMISM_LIST))
- dispatch(enableList({ url: OPTIMISM_LIST, chainId }))
+ dispatch(enableList(OPTIMISM_LIST))
}
if (chainId && [SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_RINKEBY].includes(chainId)) {
- // dispatch(enableList(ARBITRUM_LIST))
- dispatch(enableList({ url: ARBITRUM_LIST, chainId }))
+ dispatch(enableList(ARBITRUM_LIST))
}
}, [chainId, dispatch]) */
// fetch all lists every 10 minutes, but only after we initialize library
diff --git a/src/custom/state/orders/actions.ts b/src/custom/state/orders/actions.ts
index ff30fd5d1e..cda54d6b72 100644
--- a/src/custom/state/orders/actions.ts
+++ b/src/custom/state/orders/actions.ts
@@ -152,8 +152,9 @@ export const cancelOrdersBatch = createAction('order/ca
export const clearOrders = createAction<{ chainId: ChainId }>('order/clearOrders')
-export const updateLastCheckedBlock =
- createAction<{ chainId: ChainId; lastCheckedBlock: number }>('order/updateLastCheckedBlock')
+export const updateLastCheckedBlock = createAction<{ chainId: ChainId; lastCheckedBlock: number }>(
+ 'order/updateLastCheckedBlock'
+)
export type SetIsOrderUnfillableParams = {
id: OrderID
diff --git a/src/custom/state/orders/utils.test.ts b/src/custom/state/orders/utils.test.ts
index 2b5abf4679..2ee5952cbe 100644
--- a/src/custom/state/orders/utils.test.ts
+++ b/src/custom/state/orders/utils.test.ts
@@ -5,7 +5,7 @@
import { PriceInformation } from 'utils/price'
import { OrderKind } from 'state/orders/actions'
-import { USDC, USDT } from 'constants/tokens'
+import { USDC_MAINNET as USDC, USDT } from 'constants/tokens'
import { generateOrder } from 'state/orders/mocks'
diff --git a/src/custom/state/orders/utils.ts b/src/custom/state/orders/utils.ts
index 009a22f530..1839c9fd3a 100644
--- a/src/custom/state/orders/utils.ts
+++ b/src/custom/state/orders/utils.ts
@@ -59,7 +59,7 @@ function isPresignPending(order: Pick): boolean {
* An order is considered presigned, when it transitions from "presignaturePending" to just "pending"
*/
function isOrderPresigned(order: Pick): boolean {
- return order.signingScheme == 'presign' && order.status === 'open'
+ return order.signingScheme === 'presign' && order.status === 'open'
}
export function classifyOrder(
diff --git a/src/custom/state/price/updater.ts b/src/custom/state/price/updater.ts
index 3ef0b58023..657533a75f 100644
--- a/src/custom/state/price/updater.ts
+++ b/src/custom/state/price/updater.ts
@@ -6,7 +6,8 @@ import { UnsupportedToken } from 'api/gnosisProtocol'
import { FeeQuoteParams as FeeQuoteParamsFull } from 'utils/price'
import { OrderKind } from '@gnosis.pm/gp-v2-contracts'
-import { useSwapState, tryParseAmount } from 'state/swap/hooks'
+import { useSwapState } from 'state/swap/hooks'
+import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { Field } from 'state/swap/actions'
import { useIsUnsupportedTokenGp } from 'state/lists/hooks/hooksMod'
@@ -163,7 +164,10 @@ export default function FeesUpdater(): null {
// Don't refetch if the amount is missing
const kind = independentField === Field.INPUT ? OrderKind.SELL : OrderKind.BUY
- const amount = tryParseAmount(typedValue, (kind === OrderKind.SELL ? sellCurrency : buyCurrency) ?? undefined)
+ const amount = tryParseCurrencyAmount(
+ typedValue,
+ (kind === OrderKind.SELL ? sellCurrency : buyCurrency) ?? undefined
+ )
if (!amount) return
const fromDecimals = sellCurrency?.decimals ?? DEFAULT_DECIMALS
diff --git a/src/custom/state/profile/hooks.tsx b/src/custom/state/profile/hooks.tsx
index 83013acc34..400a975183 100644
--- a/src/custom/state/profile/hooks.tsx
+++ b/src/custom/state/profile/hooks.tsx
@@ -20,7 +20,7 @@ export function useCloseAnnouncement(): (contentHash?: string) => void {
return useCallback(
(contentHash?: string) => {
if (contentHash) {
- dispatch(closeAnnouncement({ contentHash })), [dispatch, contentHash]
+ dispatch(closeAnnouncement({ contentHash }))
}
},
[dispatch]
diff --git a/src/custom/state/swap/hooks.ts b/src/custom/state/swap/hooks.tsx
similarity index 60%
rename from src/custom/state/swap/hooks.ts
rename to src/custom/state/swap/hooks.tsx
index 1ac3b93e95..89f51e281b 100644
--- a/src/custom/state/swap/hooks.ts
+++ b/src/custom/state/swap/hooks.tsx
@@ -1,19 +1,18 @@
-// eslint-disable-next-line no-restricted-imports
-import { t } from '@lingui/macro'
-// import JSBI from 'jsbi'
-// import { Trade as V3Trade } from '@uniswap/v3-sdk'
-// import { useBestV3TradeExactIn, useBestV3TradeExactOut, V3TradeState } from '../../hooks/useBestV3Trade'
-import useENS from 'hooks/useENS'
-// import { parseUnits } from '@ethersproject/units'
+import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Percent, Token /* TradeType, */ } from '@uniswap/sdk-core'
-// import { Trade as V2Trade } from '@uniswap/v2-sdk'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+// import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
+// import { useBestTrade } from 'hooks/useBestTrade'
+import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { ParsedQs } from 'qs'
-import { useCallback, useEffect, useMemo, useState } from 'react'
-import { useActiveWeb3React } from 'hooks/web3'
+import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
+import { useAppDispatch /* , useAppSelector */ } from 'state/hooks'
+// import { InterfaceTrade, TradeState } from 'state/routing/types'
+import { useIsExpertMode, useUserSlippageToleranceWithDefault } from 'state/user/hooks'
+
+// import { TOKEN_SHORTHANDS } from '../../constants/tokens'
import { useCurrency } from 'hooks/Tokens'
-// import useSwapSlippageTolerance from '../../hooks/useSwapSlippageTolerance'
-// import { Version } from 'hooks/useToggledVersion'
-// import { useV2TradeExactIn, useV2TradeExactOut } from 'hooks/useV2Trade'
+import useENS from 'hooks/useENS'
import useParsedQueryString from 'hooks/useParsedQueryString'
import { isAddress } from 'utils'
// import { AppState } from 'state'
@@ -23,34 +22,41 @@ import {
replaceSwapState /* , selectCurrency, setRecipient, switchCurrencies, typeInput */,
} from 'state/swap/actions'
import { SwapState } from 'state/swap/reducer'
-// import { useUserSingleHopOnly } from 'state/user/hooks'
-import { useAppDispatch /* , useAppSelector */ } from 'state/hooks'
// MOD
-import { tryParseAmount, useSwapState, BAD_RECIPIENT_ADDRESSES } from 'state/swap/hooks'
+import { useSwapState, BAD_RECIPIENT_ADDRESSES } from '@src/state/swap/hooks'
import { useGetQuoteAndStatus, useQuote } from '../price/hooks'
import { registerOnWindow } from 'utils/misc'
import { useTradeExactInWithFee, useTradeExactOutWithFee, stringToCurrency } from './extension'
-import { /* DEFAULT_LIST_OF_LISTS, */ DEFAULT_NETWORK_FOR_LISTS } from 'constants/lists'
+import { DEFAULT_NETWORK_FOR_LISTS } from 'constants/lists'
import { FEE_SIZE_THRESHOLD, INITIAL_ALLOWED_SLIPPAGE_PERCENT, WETH_LOGO_URI, XDAI_LOGO_URI } from 'constants/index'
import TradeGp from './TradeGp'
-import { SupportedChainId as ChainId } from 'constants/chains'
-import { WETH9_EXTENDED as WETH, GpEther as ETHER } from 'constants/tokens'
+import { SupportedChainId, SupportedChainId as ChainId } from 'constants/chains'
+import { WRAPPED_NATIVE_CURRENCY as WETH, GpEther as ETHER, USDC } from 'constants/tokens'
-import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '@src/state/user/hooks'
-import { PriceImpact } from 'hooks/usePriceImpact'
import { isWrappingTrade } from './utils'
+import {
+ parseCurrencyFromURLParameter,
+ parseTokenAmountURLParameter,
+ parseIndependentFieldURLParameter,
+ validatedRecipient,
+} from '@src/state/swap/hooks'
+import { PriceImpact } from 'hooks/usePriceImpact'
+
export * from '@src/state/swap/hooks'
+/* export function useSwapState(): AppState['swap'] {
+ return useAppSelector((state) => state.swap)
+} */
+
interface DerivedSwapInfo {
- currencies: { [field in Field]?: Currency }
+ currencies: { [field in Field]?: Currency | null }
currencyBalances: { [field in Field]?: CurrencyAmount }
parsedAmount: CurrencyAmount | undefined
- inputError?: string
+ inputError?: ReactNode
v2Trade: TradeGp | undefined
- toggledTrade: TradeGp | null
allowedSlippage: Percent
}
@@ -66,7 +72,7 @@ interface DerivedSwapInfo {
dispatch(
selectCurrency({
field,
- currencyId: currency.isToken ? currency.address : currency.isNative ? 'ETH' : ''
+ currencyId: currency.isToken ? currency.address : currency.isNative ? 'ETH' : '',
})
)
},
@@ -99,49 +105,46 @@ interface DerivedSwapInfo {
}
} */
-// try to parse a user entered amount for a given token
-/* export function tryParseAmount(value?: string, currency?: T): CurrencyAmount | undefined {
- if (!value || !currency) {
- return undefined
- }
- try {
- // Here we drop everything after given token decimals to avoid parsing issues
- const truncatedValue = currency.decimals ? truncateOnMaxDecimals(value, currency.decimals) : value
- const typedValueParsed = parseUnits(truncatedValue, currency.decimals).toString()
- if (typedValueParsed !== '0') {
- return CurrencyAmount.fromRawAmount(currency, JSBI.BigInt(typedValueParsed))
- }
- } catch (error) {
- // should fail if the user specifies too many decimal places of precision (or maybe exceed max uint?)
- console.debug(`Failed to parse input amount: "${value}"`, error)
- }
- // necessary for all paths to return a value
- return undefined
-} */
-
/* const BAD_RECIPIENT_ADDRESSES: { [address: string]: true } = {
'0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f': true, // v2 factory
'0xf164fC0Ec4E93095b804a4795bBe1e041497b92a': true, // v2 router 01
- '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D': true // v2 router 02
+ '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D': true, // v2 router 02
} */
/**
- * Returns true if any of the pairs or tokens in a trade have the given checksummed address
- * @param trade to check for the given address
- * @param checksummedAddress address to check in the pairs and tokens
+ * useHighFeeWarning
+ * @description checks whether fee vs trade inputAmount = high fee warning
+ * @description returns params related to high fee and a cb for checking/unchecking fee acceptance
+ * @param trade TradeGp param
*/
-/* function involvesAddress(
- trade: V2Trade | V3Trade,
- checksummedAddress: string
-): boolean {
- const path = trade instanceof V2Trade ? trade.route.path : trade.route.tokenPath
- return (
- path.some((token) => token.address === checksummedAddress) ||
- (trade instanceof V2Trade
- ? trade.route.pairs.some((pair) => pair.liquidityToken.address === checksummedAddress)
- : false)
- )
-} */
+export function useHighFeeWarning(trade?: TradeGp) {
+ const isExpertMode = useIsExpertMode()
+ const { INPUT, OUTPUT, independentField } = useSwapState()
+
+ const [feeWarningAccepted, setFeeWarningAccepted] = useState(false) // mod - high fee warning disable state
+
+ // only considers inputAmount vs fee (fee is in input token)
+ const [isHighFee, feePercentage] = useMemo(() => {
+ if (!trade) return [false, undefined]
+
+ const { inputAmount, fee } = trade
+ const feePercentage = fee.feeAsCurrency.divide(inputAmount).asFraction
+ return [feePercentage.greaterThan(FEE_SIZE_THRESHOLD), feePercentage.multiply('100')]
+ }, [trade])
+
+ // reset the state when users change swap params
+ useEffect(() => {
+ setFeeWarningAccepted(false)
+ }, [INPUT.currencyId, OUTPUT.currencyId, independentField])
+
+ return {
+ isHighFee,
+ feePercentage,
+ // we only care/check about feeWarning being accepted if the fee is actually high..
+ feeWarningAccepted: _computeFeeWarningAcceptedState({ feeWarningAccepted, isHighFee, isExpertMode }),
+ setFeeWarningAccepted,
+ }
+}
function _computeFeeWarningAcceptedState({
feeWarningAccepted,
@@ -164,25 +167,6 @@ function _computeFeeWarningAcceptedState({
}
}
-function _computeUnknownPriceImpactAcceptedState({
- impactWarningAccepted,
- priceImpactParams,
- isExpertMode,
-}: {
- impactWarningAccepted: boolean
- priceImpactParams?: PriceImpact
- isExpertMode: boolean
-}) {
- if (isExpertMode || impactWarningAccepted) return true
- else {
- if (priceImpactParams?.error) {
- return impactWarningAccepted
- }
- }
-
- return true
-}
-
export function useUnknownImpactWarning(priceImpactParams?: PriceImpact) {
const isExpertMode = useIsExpertMode()
const { INPUT, OUTPUT, independentField } = useSwapState()
@@ -204,39 +188,23 @@ export function useUnknownImpactWarning(priceImpactParams?: PriceImpact) {
}
}
-/**
- * useHighFeeWarning
- * @description checks whether fee vs trade inputAmount = high fee warning
- * @description returns params related to high fee and a cb for checking/unchecking fee acceptance
- * @param trade TradeGp param
- */
-export function useHighFeeWarning(trade?: TradeGp) {
- const isExpertMode = useIsExpertMode()
- const { INPUT, OUTPUT, independentField } = useSwapState()
-
- const [feeWarningAccepted, setFeeWarningAccepted] = useState(false) // mod - high fee warning disable state
-
- // only considers inputAmount vs fee (fee is in input token)
- const [isHighFee, feePercentage] = useMemo(() => {
- if (!trade) return [false, undefined]
-
- const { inputAmount, fee } = trade
- const feePercentage = fee.feeAsCurrency.divide(inputAmount).asFraction
- return [feePercentage.greaterThan(FEE_SIZE_THRESHOLD), feePercentage.multiply('100')]
- }, [trade])
-
- // reset the state when users change swap params
- useEffect(() => {
- setFeeWarningAccepted(false)
- }, [INPUT.currencyId, OUTPUT.currencyId, independentField])
-
- return {
- isHighFee,
- feePercentage,
- // we only care/check about feeWarning being accepted if the fee is actually high..
- feeWarningAccepted: _computeFeeWarningAcceptedState({ feeWarningAccepted, isHighFee, isExpertMode }),
- setFeeWarningAccepted,
+function _computeUnknownPriceImpactAcceptedState({
+ impactWarningAccepted,
+ priceImpactParams,
+ isExpertMode,
+}: {
+ impactWarningAccepted: boolean
+ priceImpactParams?: PriceImpact
+ isExpertMode: boolean
+}) {
+ if (isExpertMode || impactWarningAccepted) return true
+ else {
+ if (priceImpactParams?.error) {
+ return impactWarningAccepted
+ }
}
+
+ return true
}
// from the current swap inputs, compute the best trade and return it.
@@ -245,17 +213,13 @@ export function useDerivedSwapInfo(): /* {
currencyBalances: { [field in Field]?: CurrencyAmount }
parsedAmount: CurrencyAmount | undefined
inputError?: ReactNode
- v2Trade: V2Trade | undefined
- v3Trade: {
- trade: V3Trade | null
- state: V3TradeState
+ trade: {
+ trade: InterfaceTrade | undefined
+ state: TradeState
}
- bestTrade: V2Trade | V3Trade | undefined
allowedSlippage: Percent
} */ DerivedSwapInfo {
- const { account, chainId } = useActiveWeb3React()
-
- // const [singleHopOnly] = useUserSingleHopOnly()
+ const { account, chainId } = useActiveWeb3React() // MOD: chainId
const {
independentField,
@@ -270,26 +234,24 @@ export function useDerivedSwapInfo(): /* {
const recipientLookup = useENS(recipient ?? undefined)
const to: string | null = (recipient === null ? account : recipientLookup.address) ?? null
- const relevantTokenBalances = useCurrencyBalances(account ?? undefined, [
- inputCurrency ?? undefined,
- outputCurrency ?? undefined,
- ])
+ const relevantTokenBalances = useCurrencyBalances(
+ account ?? undefined,
+ useMemo(() => [inputCurrency ?? undefined, outputCurrency ?? undefined], [inputCurrency, outputCurrency])
+ )
const isExactIn: boolean = independentField === Field.INPUT
- const parsedAmount = tryParseAmount(typedValue, (isExactIn ? inputCurrency : outputCurrency) ?? undefined)
-
- /* const bestV2TradeExactIn = useV2TradeExactIn(isExactIn ? parsedAmount : undefined, outputCurrency ?? undefined, {
- maxHops: singleHopOnly ? 1 : undefined
- })
- const bestV2TradeExactOut = useV2TradeExactOut(inputCurrency ?? undefined, !isExactIn ? parsedAmount : undefined, {
- maxHops: singleHopOnly ? 1 : undefined */
-
- /* const bestV3TradeExactIn = useBestV3TradeExactIn(isExactIn ? parsedAmount : undefined, outputCurrency ?? undefined)
- const bestV3TradeExactOut = useBestV3TradeExactOut(inputCurrency ?? undefined, !isExactIn ? parsedAmount : undefined)
+ const parsedAmount = useMemo(
+ () => tryParseCurrencyAmount(typedValue, (isExactIn ? inputCurrency : outputCurrency) ?? undefined),
+ [inputCurrency, isExactIn, outputCurrency, typedValue]
+ )
- const v2Trade = isExactIn ? bestV2TradeExactIn : bestV2TradeExactOut
- const v3Trade = (isExactIn ? bestV3TradeExactIn : bestV3TradeExactOut) ?? undefined */
+ /* const trade = useBestTrade(
+ isExactIn ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT,
+ parsedAmount,
+ (isExactIn ? outputCurrency : inputCurrency) ?? undefined
+ ) */
+ // -- MOD --
const { quote } = useGetQuoteAndStatus({
token: inputCurrencyId,
chainId,
@@ -320,75 +282,85 @@ export function useDerivedSwapInfo(): /* {
const v2Trade = isExactIn ? bestTradeExactIn : bestTradeExactOut
registerOnWindow({ trade: v2Trade })
+ // -- MOD --
- const currencyBalances = {
- [Field.INPUT]: relevantTokenBalances[0],
- [Field.OUTPUT]: relevantTokenBalances[1],
- }
+ const currencyBalances = useMemo(
+ () => ({
+ [Field.INPUT]: relevantTokenBalances[0],
+ [Field.OUTPUT]: relevantTokenBalances[1],
+ }),
+ [relevantTokenBalances]
+ )
- const currencies: { [field in Field]?: Currency } = {
- [Field.INPUT]: inputCurrency ?? undefined,
- [Field.OUTPUT]: outputCurrency ?? undefined,
- }
+ const currencies: { [field in Field]?: Currency | null } = useMemo(
+ () => ({
+ [Field.INPUT]: inputCurrency,
+ [Field.OUTPUT]: outputCurrency,
+ }),
+ [inputCurrency, outputCurrency]
+ )
- let inputError: string | undefined
- if (!account) {
- inputError = t`Connect Wallet`
- }
+ // allowed slippage is either auto slippage, or custom user defined slippage if auto slippage disabled
+ // TODO: check whether we want to enable auto slippage tolerance
+ // const autoSlippageTolerance = useAutoSlippageTolerance(trade.trade) // mod
+ // const allowedSlippage = useUserSlippageToleranceWithDefault(autoSlippageTolerance) // mod
+ const allowedSlippage = useUserSlippageToleranceWithDefault(INITIAL_ALLOWED_SLIPPAGE_PERCENT) // mod
- if (!parsedAmount) {
- inputError = inputError ?? t`Enter an amount`
- }
+ const inputError = useMemo(() => {
+ let inputError: ReactNode | undefined
- if (!currencies[Field.INPUT] || !currencies[Field.OUTPUT]) {
- inputError = inputError ?? t`Select a token`
- }
+ if (!account) {
+ inputError = Connect Wallet
+ }
- const formattedTo = isAddress(to)
- if (!to || !formattedTo) {
- inputError = inputError ?? t`Enter a recipient`
- } else {
- if (
- BAD_RECIPIENT_ADDRESSES[formattedTo]
- // TODO: review if we need this:
- /*
- (bestV2TradeExactIn && involvesAddress(bestV2TradeExactIn, formattedTo)) ||
- (bestV2TradeExactOut && involvesAddress(bestV2TradeExactOut, formattedTo)) */
- ) {
- inputError = inputError ?? t`Invalid recipient`
+ if (!currencies[Field.INPUT] || !currencies[Field.OUTPUT]) {
+ inputError = inputError ?? Select a token
}
- }
- // TODO: we don't use toggled Trades
- const toggledTrade = v2Trade /* (toggledVersion === Version.v2 ? v2Trade : v3Trade.trade) ?? undefined */
- // const allowedSlippage = useSwapSlippageTolerance(toggledTrade)
+ if (!parsedAmount) {
+ inputError = inputError ?? Enter an amount
+ }
- const allowedSlippage = useUserSlippageToleranceWithDefault(INITIAL_ALLOWED_SLIPPAGE_PERCENT)
+ const formattedTo = isAddress(to)
+ if (!to || !formattedTo) {
+ inputError = inputError ?? Enter a recipient
+ } else {
+ if (BAD_RECIPIENT_ADDRESSES[formattedTo]) {
+ inputError = inputError ?? Invalid recipient
+ }
+ }
- // compare input balance to max input based on version
- const [balanceIn, amountIn] = [currencyBalances[Field.INPUT], v2Trade?.maximumAmountIn(allowedSlippage)]
+ // compare input balance to max input based on version
+ // const [balanceIn, amountIn] = [currencyBalances[Field.INPUT], trade.trade?.maximumAmountIn(allowedSlippage)] // mod
+ const [balanceIn, amountIn] = [currencyBalances[Field.INPUT], v2Trade?.maximumAmountIn(allowedSlippage)] // mod
- if (balanceIn && amountIn && balanceIn.lessThan(amountIn)) {
- inputError = t`Insufficient ${amountIn.currency.symbol} balance`
- }
+ if (balanceIn && amountIn && balanceIn.lessThan(amountIn)) {
+ inputError = Insufficient {amountIn.currency.symbol} balance
+ }
- return {
- currencies,
- currencyBalances,
- parsedAmount,
- inputError,
- v2Trade: v2Trade ?? undefined,
- // v3TradeState: v3Trade,
- toggledTrade,
- allowedSlippage,
- }
+ return inputError
+ }, [account, allowedSlippage, currencies, currencyBalances, parsedAmount, to, v2Trade]) // mod
+
+ return useMemo(
+ () => ({
+ currencies,
+ currencyBalances,
+ parsedAmount,
+ inputError,
+ v2Trade: v2Trade ?? undefined, // mod
+ allowedSlippage,
+ }),
+ [allowedSlippage, currencies, currencyBalances, inputError, parsedAmount, v2Trade] // mod
+ )
}
-export function parseCurrencyFromURLParameter(urlParam: any): string {
+/* export 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 ''
}
@@ -410,14 +382,24 @@ export function validatedRecipient(recipient: any): string | null {
if (ENS_NAME_REGEX.test(recipient)) return recipient
if (ADDRESS_REGEX.test(recipient)) return recipient
return null
-}
+} */
-export function queryParametersToSwapState(parsedQs: ParsedQs, defaultInputCurrency = ''): SwapState {
+// mod: defaultInputCurrency and chainId parameters
+export function queryParametersToSwapState(
+ parsedQs: ParsedQs,
+ defaultInputCurrency = '',
+ chainId: SupportedChainId | undefined = undefined
+): SwapState {
let inputCurrency = parseCurrencyFromURLParameter(parsedQs.inputCurrency)
let outputCurrency = parseCurrencyFromURLParameter(parsedQs.outputCurrency)
- if (inputCurrency === '' && outputCurrency === '') {
- // default to ETH input
- inputCurrency = defaultInputCurrency
+ let typedValue = parseTokenAmountURLParameter(parsedQs.exactAmount)
+ const independentField = parseIndependentFieldURLParameter(parsedQs.exactField)
+
+ if (inputCurrency === '' && outputCurrency === '' && typedValue === '' && independentField === Field.INPUT) {
+ // Defaults to 1 ETH -> USDC
+ inputCurrency = defaultInputCurrency // 'ETH' // mod
+ outputCurrency = chainId ? USDC[chainId].address : 'USDC' // mod
+ typedValue = '1'
} else if (inputCurrency === outputCurrency) {
// clear output if identical
outputCurrency = ''
@@ -432,42 +414,46 @@ export function queryParametersToSwapState(parsedQs: ParsedQs, defaultInputCurre
[Field.OUTPUT]: {
currencyId: outputCurrency === '' ? null : outputCurrency ?? null,
},
- typedValue: parseTokenAmountURLParameter(parsedQs.exactAmount),
- independentField: parseIndependentFieldURLParameter(parsedQs.exactField),
+ typedValue,
+ independentField,
recipient,
}
}
-type DefaultFromUrlSearch = { inputCurrencyId: string | undefined; outputCurrencyId: string | undefined } | undefined
// updates the swap state to use the defaults for a given network
-export function useDefaultsFromURLSearch(): DefaultFromUrlSearch {
+export function useDefaultsFromURLSearch(): SwapState {
const { chainId } = useActiveWeb3React()
- const replaceSwapState = useReplaceSwapState()
+ const dispatch = useAppDispatch()
const parsedQs = useParsedQueryString()
- const [result, setResult] = useState()
+
+ // TODO: check whether we can use the new function for native currency
+ // This is not a great fix for setting a default token
+ // but it is better and easiest considering updating default files
+ const defaultInputToken = WETH[chainId || 1].address // mod
+
+ const parsedSwapState = useMemo(() => {
+ return queryParametersToSwapState(parsedQs, defaultInputToken, chainId) // mod
+ }, [chainId, defaultInputToken, parsedQs]) // mod
useEffect(() => {
if (!chainId) return
- // This is not a great fix for setting a default token
- // but it is better and easiest considering updating default files
- const defaultInputToken = WETH[chainId].address
- const parsed = queryParametersToSwapState(parsedQs, defaultInputToken)
- const inputCurrencyId = parsed[Field.INPUT].currencyId ?? undefined
- const outputCurrencyId = parsed[Field.OUTPUT].currencyId ?? undefined
-
- replaceSwapState({
- typedValue: parsed.typedValue,
- field: parsed.independentField,
- inputCurrencyId,
- outputCurrencyId,
- recipient: parsed.recipient,
- })
-
- setResult({ inputCurrencyId, outputCurrencyId })
+ const inputCurrencyId = parsedSwapState[Field.INPUT].currencyId ?? undefined
+ const outputCurrencyId = parsedSwapState[Field.OUTPUT].currencyId ?? undefined
+
+ dispatch(
+ replaceSwapState({
+ typedValue: parsedSwapState.typedValue,
+ field: parsedSwapState.independentField,
+ inputCurrencyId,
+ outputCurrencyId,
+ recipient: parsedSwapState.recipient,
+ })
+ )
+
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [chainId, parsedQs])
- return result
+ return parsedSwapState
}
// MODS
@@ -486,7 +472,7 @@ export function useReplaceSwapState() {
}
interface CurrencyWithAddress {
- currency?: Currency
+ currency?: Currency | null
address?: string | null
}
@@ -499,6 +485,7 @@ export function useDetectNativeToken(input?: CurrencyWithAddress, output?: Curre
}
)
+ // TODO: check the new native currency function
const native = ETHER.onChain(chainId || DEFAULT_NETWORK_FOR_LISTS)
const [isNativeIn, isNativeOut] = [input?.currency?.isNative, output?.currency?.isNative]
diff --git a/src/custom/state/swap/trade.test.ts b/src/custom/state/swap/trade.test.ts
index 31d7469a0d..3268411afb 100644
--- a/src/custom/state/swap/trade.test.ts
+++ b/src/custom/state/swap/trade.test.ts
@@ -4,7 +4,7 @@ import { CurrencyAmount, Fraction, Price, Currency, Percent, Token, TradeType }
import { OrderKind } from '@gnosis.pm/gp-v2-contracts'
import { stringToCurrency } from './extension'
import { SupportedChainId as ChainId } from 'constants/chains'
-import { WETH9_EXTENDED as WETH } from 'constants/tokens'
+import { WRAPPED_NATIVE_CURRENCY as WETH } from 'constants/tokens'
import Trade, { _constructTradePrice } from './TradeGp'
import TradeGp from './TradeGp'
diff --git a/src/custom/state/swap/utils.ts b/src/custom/state/swap/utils.ts
index 0c4807d3f5..e764a0378e 100644
--- a/src/custom/state/swap/utils.ts
+++ b/src/custom/state/swap/utils.ts
@@ -1,14 +1,14 @@
import { SupportedChainId } from 'constants/chains'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import TradeGp from './TradeGp'
-import { WETH9_EXTENDED } from 'constants/tokens'
+import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
export function isWrappingTrade(
sellCurrency: Currency | null | undefined,
buyCurrency: Currency | null | undefined,
chainId?: SupportedChainId
): boolean {
- const wethByChain = WETH9_EXTENDED[chainId || SupportedChainId.MAINNET]
+ const wethByChain = WRAPPED_NATIVE_CURRENCY[chainId || SupportedChainId.MAINNET]
return Boolean(
(sellCurrency?.isNative && buyCurrency?.wrapped.equals(wethByChain)) ||
(buyCurrency?.isNative && sellCurrency?.wrapped.equals(wethByChain))
diff --git a/src/custom/theme/baseTheme.tsx b/src/custom/theme/baseTheme.tsx
index b7474cf09f..fb0be1cda3 100644
--- a/src/custom/theme/baseTheme.tsx
+++ b/src/custom/theme/baseTheme.tsx
@@ -18,7 +18,7 @@ import { AutoColumn } from 'components/Column'
import { RowBetween } from 'components/Row'
import { ModalContentWrapper } from 'components/Settings/SettingsMod'
-export { TYPE } from '@src/theme'
+export { ThemedText } from '@src/theme'
export * from '@src/theme/components'
export function colors(darkMode: boolean): Colors {
diff --git a/src/custom/theme/cowSwapTheme.tsx b/src/custom/theme/cowSwapTheme.tsx
index c8ff3f2f18..b76c6928bc 100644
--- a/src/custom/theme/cowSwapTheme.tsx
+++ b/src/custom/theme/cowSwapTheme.tsx
@@ -12,7 +12,7 @@ import {
import { theme as themeUniswap, MEDIA_WIDTHS as MEDIA_WIDTHS_UNISWAP } from '@src/theme'
import { useIsDarkMode } from 'state/user/hooks'
-export { TYPE, MEDIA_WIDTHS } from '@src/theme'
+export { MEDIA_WIDTHS, ThemedText } from '@src/theme'
export * from '@src/theme/components'
export function colors(darkMode: boolean): Colors {
diff --git a/src/custom/types/ethers.d.ts b/src/custom/types/ethers.d.ts
deleted file mode 100644
index 12aac6c326..0000000000
--- a/src/custom/types/ethers.d.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * Additional typings extensions for Ethers.js.
- */
-
-import { ethers } from 'ethers'
-
-declare module 'ethers' {
- /**
- * A signature-like type.
- */
- export type SignatureLike = Parameters[0]
-
- /**
- * EIP-712 typed data domain.
- */
- export type TypedDataDomain = Parameters[0]
-
- interface Signer {
- /**
- * Signs the typed data value with types data structure for domain using the
- * EIP-712 specification.
- */
- _signTypedData?: typeof ethers.VoidSigner.prototype._signTypedData
- }
-}
diff --git a/src/custom/utils/addNetwork.ts b/src/custom/utils/addNetwork.ts
deleted file mode 100644
index 28faed1df9..0000000000
--- a/src/custom/utils/addNetwork.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { BigNumber } from '@ethersproject/bignumber'
-import { hexStripZeros } from '@ethersproject/bytes'
-import { Web3Provider } from '@ethersproject/providers'
-import { L1ChainInfo, L2ChainInfo, SupportedChainId } from 'constants/chains'
-
-interface AddNetworkArguments {
- library: Web3Provider
- chainId: SupportedChainId
- info: L1ChainInfo | L2ChainInfo
-}
-
-// provider.request returns Promise, but wallet_switchEthereumChain must return null or throw
-// see https://github.com/rekmarks/EIPs/blob/3326-create/EIPS/eip-3326.md for more info on wallet_switchEthereumChain
-export async function addNetwork({ library, chainId, info }: AddNetworkArguments): Promise {
- if (!library?.provider?.request) {
- return
- }
- const formattedChainId = hexStripZeros(BigNumber.from(chainId).toHexString())
- try {
- await library?.provider.request({
- method: 'wallet_addEthereumChain',
- params: [
- {
- chainId: formattedChainId,
- chainName: info.label,
- rpcUrls: info.rpcUrls,
- nativeCurrency: info.nativeCurrency,
- blockExplorerUrls: [info.explorer],
- },
- ],
- })
- } catch (error) {
- console.error('error adding eth network: ', chainId, info, error)
- throw new Error(error?.message)
- }
-}
diff --git a/src/custom/utils/prices/index.ts b/src/custom/utils/prices/index.ts
index 1917e4a035..06caa1176c 100644
--- a/src/custom/utils/prices/index.ts
+++ b/src/custom/utils/prices/index.ts
@@ -1,2 +1,33 @@
+import { Currency, CurrencyAmount, Percent /* , Fraction, TradeType */ } from '@uniswap/sdk-core'
+
+import TradeGp from 'state/swap/TradeGp'
+import { Field } from 'state/swap/actions'
+
+// There's no MOD file as originals aren't touched, only new functions added
export * from '@src/utils/prices'
-export { computeSlippageAdjustedAmounts, formatExecutionPrice } from './pricesMod'
+
+// computes the minimum amount out and maximum amount in for a trade given a user specified allowed slippage in bips
+export function computeSlippageAdjustedAmounts(
+ // trade: Trade | undefined,
+ trade: TradeGp | undefined,
+ allowedSlippage: Percent
+): { [field in Field]?: CurrencyAmount } {
+ return {
+ [Field.INPUT]: trade?.maximumAmountIn(allowedSlippage),
+ [Field.OUTPUT]: trade?.minimumAmountOut(allowedSlippage),
+ }
+}
+
+// export function formatExecutionPrice(trade?: Trade, inverted?: boolean): string {
+export function formatExecutionPrice(trade?: TradeGp, inverted?: boolean): string {
+ if (!trade) {
+ return ''
+ }
+ return inverted
+ ? `${trade.executionPrice.invert().toSignificant(6)} ${trade.inputAmount.currency.symbol} / ${
+ trade.outputAmount.currency.symbol
+ }`
+ : `${trade.executionPrice.toSignificant(6)} ${trade.outputAmount.currency.symbol} / ${
+ trade.inputAmount.currency.symbol
+ }`
+}
diff --git a/src/custom/utils/prices/pricesMod.ts b/src/custom/utils/prices/pricesMod.ts
deleted file mode 100644
index 53e81fb359..0000000000
--- a/src/custom/utils/prices/pricesMod.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-import { Currency, CurrencyAmount, Percent /* , Fraction, TradeType */ } from '@uniswap/sdk-core'
-// import { Trade as V2Trade } from '@uniswap/v2-sdk'
-// import { Trade as V3Trade } from '@uniswap/v3-sdk'
-// import JSBI from 'jsbi'
-
-/* import {
- ALLOWED_PRICE_IMPACT_HIGH,
- ALLOWED_PRICE_IMPACT_LOW,
- ALLOWED_PRICE_IMPACT_MEDIUM,
- BLOCKED_PRICE_IMPACT_NON_EXPERT,
-} from 'constants/misc' */
-
-import { Field } from 'state/swap/actions'
-import TradeGp from 'state/swap/TradeGp'
-
-/* const THIRTY_BIPS_FEE = new Percent(JSBI.BigInt(30), JSBI.BigInt(10000))
-const ONE_HUNDRED_PERCENT = new Percent(JSBI.BigInt(10000), JSBI.BigInt(10000))
-const INPUT_FRACTION_AFTER_FEE = ONE_HUNDRED_PERCENT.subtract(THIRTY_BIPS_FEE) */
-
-/*
-// computes realized lp fee as a percent
-export function computeRealizedLPFeePercent(
- trade: V2Trade | V3Trade
-): Percent {
- let percent: Percent
- if (trade instanceof V2Trade) {
- // for each hop in our trade, take away the x*y=k price impact from 0.3% fees
- // e.g. for 3 tokens/2 hops: 1 - ((1 - .03) * (1-.03))
- percent = ONE_HUNDRED_PERCENT.subtract(
- trade.route.pairs.reduce(
- (currentFee: Percent): Percent => currentFee.multiply(INPUT_FRACTION_AFTER_FEE),
- ONE_HUNDRED_PERCENT
- )
- )
- } else {
- //TODO(judo): validate this
- percent = ZERO_PERCENT
- for (const swap of trade.swaps) {
- const { numerator, denominator } = swap.inputAmount.divide(trade.inputAmount)
- const overallPercent = new Percent(numerator, denominator)
-
- const routeRealizedLPFeePercent = overallPercent.multiply(
- ONE_HUNDRED_PERCENT.subtract(
- swap.route.pools.reduce(
- (currentFee: Percent, pool): Percent =>
- currentFee.multiply(ONE_HUNDRED_PERCENT.subtract(new Fraction(pool.fee, 1_000_000))),
- ONE_HUNDRED_PERCENT
- )
- )
- )
-
- percent = percent.add(routeRealizedLPFeePercent)
- }
- }
-
- return new Percent(percent.numerator, percent.denominator)
-}
-*/
-
-// computes price breakdown for the trade
-/* export function computeRealizedLPFeeAmount(
- trade?: V2Trade | V3Trade | null
-): CurrencyAmount | undefined {
- if (trade) {
- const realizedLPFee = computeRealizedLPFeePercent(trade)
-
- // the amount of the input that accrues to LPs
- return CurrencyAmount.fromRawAmount(trade.inputAmount.currency, trade.inputAmount.multiply(realizedLPFee).quotient)
- }
-
- return undefined
-}
-
-const IMPACT_TIERS = [
- BLOCKED_PRICE_IMPACT_NON_EXPERT,
- ALLOWED_PRICE_IMPACT_HIGH,
- ALLOWED_PRICE_IMPACT_MEDIUM,
- ALLOWED_PRICE_IMPACT_LOW,
-]
-
-type WarningSeverity = 0 | 1 | 2 | 3 | 4
-export function warningSeverity(priceImpact: Percent | undefined): WarningSeverity {
- if (!priceImpact) return 4
- let impact: WarningSeverity = IMPACT_TIERS.length as WarningSeverity
- for (const impactLevel of IMPACT_TIERS) {
- if (impactLevel.lessThan(priceImpact)) return impact
- impact--
- }
- return 0
-} */
-
-// computes the minimum amount out and maximum amount in for a trade given a user specified allowed slippage in bips
-export function computeSlippageAdjustedAmounts(
- // trade: Trade | undefined,
- trade: TradeGp | undefined,
- allowedSlippage: Percent
-): { [field in Field]?: CurrencyAmount } {
- return {
- [Field.INPUT]: trade?.maximumAmountIn(allowedSlippage),
- [Field.OUTPUT]: trade?.minimumAmountOut(allowedSlippage),
- }
-}
-
-// export function formatExecutionPrice(trade?: Trade, inverted?: boolean): string {
-export function formatExecutionPrice(trade?: TradeGp, inverted?: boolean): string {
- if (!trade) {
- return ''
- }
- return inverted
- ? `${trade.executionPrice.invert().toSignificant(6)} ${trade.inputAmount.currency.symbol} / ${
- trade.outputAmount.currency.symbol
- }`
- : `${trade.executionPrice.toSignificant(6)} ${trade.outputAmount.currency.symbol} / ${
- trade.inputAmount.currency.symbol
- }`
-}
diff --git a/src/custom/utils/switchToNetwork.ts b/src/custom/utils/switchToNetwork.ts
index 2980634f9d..f591f4f56e 100644
--- a/src/custom/utils/switchToNetwork.ts
+++ b/src/custom/utils/switchToNetwork.ts
@@ -1,27 +1,58 @@
import { BigNumber } from '@ethersproject/bignumber'
import { hexStripZeros } from '@ethersproject/bytes'
import { Web3Provider } from '@ethersproject/providers'
-import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
-
-import { addNetwork } from 'utils/addNetwork'
+import { CHAIN_INFO } from 'constants/chainInfo'
+import { SupportedChainId } from 'constants/chains'
+import { INFURA_NETWORK_URLS } from 'constants/infura'
interface SwitchNetworkArguments {
library: Web3Provider
chainId?: SupportedChainId
}
+export function getRpcUrls(chainId: SupportedChainId): [string] {
+ switch (chainId) {
+ case SupportedChainId.MAINNET:
+ case SupportedChainId.RINKEBY:
+ /*case SupportedChainId.ROPSTEN:
+ case SupportedChainId.KOVAN:
+ case SupportedChainId.GOERLI:*/
+ return [INFURA_NETWORK_URLS[chainId]]
+ /*case SupportedChainId.OPTIMISM:
+ return ['https://mainnet.optimism.io']
+ case SupportedChainId.OPTIMISTIC_KOVAN:
+ return ['https://kovan.optimism.io']
+ case SupportedChainId.ARBITRUM_ONE:
+ return ['https://arb1.arbitrum.io/rpc']
+ case SupportedChainId.ARBITRUM_RINKEBY:
+ return ['https://rinkeby.arbitrum.io/rpc']
+ case SupportedChainId.POLYGON:
+ return ['https://polygon-rpc.com/']
+ case SupportedChainId.POLYGON_MUMBAI:
+ return ['https://rpc-endpoints.superfluid.dev/mumbai']*/
+ case SupportedChainId.XDAI:
+ return ['https://rpc.gnosischain.com/']
+ default:
+ }
+ // Our API-keyed URLs will fail security checks when used with external wallets.
+ throw new Error('RPC URLs must use public endpoints')
+}
+
// provider.request returns Promise, but wallet_switchEthereumChain must return null or throw
// see https://github.com/rekmarks/EIPs/blob/3326-create/EIPS/eip-3326.md for more info on wallet_switchEthereumChain
export async function switchToNetwork({ library, chainId }: SwitchNetworkArguments): Promise {
if (!library?.provider?.request) {
return
}
+
+ // mod
if (!chainId && library?.getNetwork) {
;({ chainId } = await library.getNetwork())
}
+
const formattedChainId = hexStripZeros(BigNumber.from(chainId).toHexString())
try {
- await library?.provider.request({
+ await library.provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: formattedChainId }],
})
@@ -30,13 +61,26 @@ export async function switchToNetwork({ library, chainId }: SwitchNetworkArgumen
if (error.code === 4902 && chainId !== undefined) {
const info = CHAIN_INFO[chainId]
- // MOD - need to handle these errors, else loops
+ await library.provider.request({
+ method: 'wallet_addEthereumChain',
+ params: [
+ {
+ chainId: formattedChainId,
+ chainName: info.label,
+ rpcUrls: getRpcUrls(chainId),
+ nativeCurrency: info.nativeCurrency,
+ blockExplorerUrls: [info.explorer],
+ },
+ ],
+ })
+ // metamask (only known implementer) automatically switches after a network is added
+ // the second call is done here because that behavior is not a part of the spec and cannot be relied upon in the future
+ // metamask's behavior when switching to the current network is just to return null (a no-op)
try {
- // metamask (only known implementer) automatically switches after a network is added
- // the second call is done here because that behavior is not a part of the spec and cannot be relied upon in the future
- // metamask's behavior when switching to the current network is just to return null (a no-op)
- await addNetwork({ library, chainId, info })
- await switchToNetwork({ library, chainId })
+ await library.provider.request({
+ method: 'wallet_switchEthereumChain',
+ params: [{ chainId: formattedChainId }],
+ })
} catch (error) {
console.error(`Error in ADDING/SWITCHING ${chainId}:`, error)
}
diff --git a/src/custom/utils/tokens.ts b/src/custom/utils/tokens.ts
index 5842db218d..5ee370901b 100644
--- a/src/custom/utils/tokens.ts
+++ b/src/custom/utils/tokens.ts
@@ -1,13 +1,9 @@
import { SupportedChainId as ChainId } from 'constants/chains'
-import { GpEther as ETHER, WETH9_EXTENDED as WETH } from 'constants/tokens'
+import { GpEther as ETHER, WRAPPED_NATIVE_CURRENCY as WETH } from 'constants/tokens'
import { NATIVE_CURRENCY_BUY_ADDRESS } from '../constants'
export function isNativeAddress(tokenAddress: string, chainId: ChainId) {
- if (tokenAddress === 'ETH' || tokenAddress === ETHER.onChain(chainId).symbol) {
- return true
- }
-
- return false
+ return tokenAddress === 'ETH' || tokenAddress === ETHER.onChain(chainId).symbol
}
export function toErc20Address(tokenAddress: string, chainId: ChainId): string {
diff --git a/src/hooks/Tokens.ts b/src/hooks/Tokens.ts
index b24ce100b9..0865ababb9 100644
--- a/src/hooks/Tokens.ts
+++ b/src/hooks/Tokens.ts
@@ -1,22 +1,18 @@
-import { arrayify } from '@ethersproject/bytes'
-import { parseBytes32String } from '@ethersproject/strings'
import { Currency, Token } from '@uniswap/sdk-core'
-import { SupportedChainId } from 'constants/chains'
+import { CHAIN_INFO } from '@src/constants/chainInfo'
+import { L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from '@src/constants/chains'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { useCurrencyFromMap, useTokenFromMapOrNetwork } from 'lib/hooks/useCurrency'
+import { getTokenFilter } from 'lib/hooks/useTokenList/filtering'
import { useMemo } from 'react'
-import { createTokenFilterFunction } from '../components/SearchModal/filtering'
-import { GpEther as ExtendedEther, WETH9_EXTENDED } from 'constants/tokens'
import { useAllLists, useCombinedActiveList, useInactiveListUrls } from 'state/lists/hooks'
import { WrappedTokenInfo } from '../state/lists/wrappedTokenInfo'
-import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
-import { useUserAddedTokens } from '../state/user/hooks'
-import { isAddress } from '../utils'
-import { TokenAddressMap, useUnsupportedTokenList } from 'state/lists/hooks'
-import { useBytes32TokenContract, useTokenContract } from './useContract'
-import { useActiveWeb3React } from './web3'
+import { useUserAddedTokens } from 'state/user/hooks'
+import { TokenAddressMap, useUnsupportedTokenList } from './../state/lists/hooks'
// reduce token map into standard address <-> Token mapping, optionally include user added tokens
-function useTokensFromMap(tokenMap: TokenAddressMap, includeUserAdded: boolean): { [address: string]: Token } {
+export function useTokensFromMap(tokenMap: TokenAddressMap, includeUserAdded: boolean): { [address: string]: Token } {
const { chainId } = useActiveWeb3React()
const userAddedTokens = useUserAddedTokens()
@@ -57,9 +53,56 @@ export function useAllTokens(): { [address: string]: Token } {
return useTokensFromMap(allTokens, true)
}
+type BridgeInfo = Record<
+ SupportedChainId,
+ {
+ tokenAddress: string
+ originBridgeAddress: string
+ destBridgeAddress: string
+ }
+>
+
export function useUnsupportedTokens(): { [address: string]: Token } {
+ const { chainId } = useActiveWeb3React()
+ const listsByUrl = useAllLists()
const unsupportedTokensMap = useUnsupportedTokenList()
- return useTokensFromMap(unsupportedTokensMap, false)
+ const unsupportedTokens = useTokensFromMap(unsupportedTokensMap, false)
+
+ // checks the default L2 lists to see if `bridgeInfo` has an L1 address value that is unsupported
+ const l2InferredBlockedTokens: typeof unsupportedTokens = useMemo(() => {
+ if (!chainId || !L2_CHAIN_IDS.includes(chainId)) {
+ return {}
+ }
+
+ if (!listsByUrl) {
+ return {}
+ }
+
+ const listUrl = CHAIN_INFO[chainId as SupportedL2ChainId].defaultListUrl
+ const { current: list } = listsByUrl[listUrl]
+ if (!list) {
+ return {}
+ }
+
+ const unsupportedSet = new Set(Object.keys(unsupportedTokens))
+
+ return list.tokens.reduce((acc, tokenInfo) => {
+ const bridgeInfo = tokenInfo.extensions?.bridgeInfo as unknown as BridgeInfo
+ if (
+ bridgeInfo &&
+ bridgeInfo[SupportedChainId.MAINNET] &&
+ bridgeInfo[SupportedChainId.MAINNET].tokenAddress &&
+ unsupportedSet.has(bridgeInfo[SupportedChainId.MAINNET].tokenAddress)
+ ) {
+ const address = bridgeInfo[SupportedChainId.MAINNET].tokenAddress
+ // don't rely on decimals--it's possible that a token could be bridged w/ different decimals on the L2
+ return { ...acc, [address]: new Token(SupportedChainId.MAINNET, address, tokenInfo.decimals) }
+ }
+ return acc
+ }, {})
+ }, [chainId, listsByUrl, unsupportedTokens])
+
+ return { ...unsupportedTokens, ...l2InferredBlockedTokens }
}
export function useSearchInactiveTokenLists(search: string | undefined, minResults = 10): WrappedTokenInfo[] {
@@ -69,7 +112,7 @@ export function useSearchInactiveTokenLists(search: string | undefined, minResul
const activeTokens = useAllTokens()
return useMemo(() => {
if (!search || search.trim().length === 0) return []
- const tokenFilter = createTokenFilterFunction(search)
+ const tokenFilter = getTokenFilter(search)
const result: WrappedTokenInfo[] = []
const addressSet: { [address: string]: true } = {}
for (const url of inactiveUrls) {
@@ -111,91 +154,15 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool
return !!userAddedTokens.find((token) => currency.equals(token))
}
-// parse a name or symbol from a token response
-const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/
-
-export function parseStringOrBytes32(
- str: string | undefined,
- bytes32: string | undefined,
- defaultValue: string
-): string {
- return str && str.length > 0
- ? str
- : // need to check for proper bytes string and valid terminator
- bytes32 && BYTES32_REGEX.test(bytes32) && arrayify(bytes32)[31] === 0
- ? parseBytes32String(bytes32)
- : defaultValue
-}
-
// undefined if invalid or does not exist
// null if loading or null was passed
// otherwise returns the token
-export function useToken(tokenAddress?: string | null): Token | undefined | null {
- const { chainId } = useActiveWeb3React()
+export function useToken(tokenAddress?: string | null): Token | null | undefined {
const tokens = useAllTokens()
-
- const address = 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 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)
-
- return useMemo(() => {
- if (token) return token
- if (tokenAddress === null) return null
- if (!chainId || !address) return undefined
- if (decimals.loading || symbol.loading || tokenName.loading) return null
- if (decimals.result) {
- return new Token(
- chainId,
- address,
- decimals.result[0],
- parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], 'UNKNOWN'),
- parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], 'Unknown Token')
- )
- }
- return undefined
- }, [
- address,
- chainId,
- decimals.loading,
- decimals.result,
- symbol.loading,
- symbol.result,
- symbolBytes32.result,
- token,
- tokenAddress,
- tokenName.loading,
- tokenName.result,
- tokenNameBytes32.result,
- ])
+ return useTokenFromMapOrNetwork(tokens, tokenAddress)
}
-export function useCurrency(currencyId: string | null | undefined): Currency | null | undefined {
- const { chainId } = useActiveWeb3React()
- const isETH = currencyId?.toUpperCase() === 'ETH'
- const token = useToken(isETH ? undefined : currencyId)
- const extendedEther = useMemo(
- () =>
- chainId
- ? ExtendedEther.onChain(chainId)
- : // display mainnet when not connected
- ExtendedEther.onChain(SupportedChainId.MAINNET),
- [chainId]
- )
- const weth = chainId ? WETH9_EXTENDED[chainId] : undefined
- if (currencyId === null || currencyId === undefined) return currencyId
- if (weth?.address?.toUpperCase() === currencyId?.toUpperCase()) return weth
- return isETH ? extendedEther : token
+export function useCurrency(currencyId?: string | null): Currency | null | undefined {
+ const tokens = useAllTokens()
+ return useCurrencyFromMap(tokens, currencyId)
}
diff --git a/src/hooks/useActiveWeb3React.ts b/src/hooks/useActiveWeb3React.ts
new file mode 100644
index 0000000000..c3529196bb
--- /dev/null
+++ b/src/hooks/useActiveWeb3React.ts
@@ -0,0 +1,23 @@
+/* eslint-disable react-hooks/rules-of-hooks */
+import { Web3Provider } from '@ethersproject/providers'
+import { default as useWidgetsWeb3React } from 'lib/hooks/useActiveWeb3React'
+import { useWeb3React } from 'web3-react-core'
+
+import { NetworkContextName } from '../constants/misc'
+
+export default function useActiveWeb3React() {
+ if (process.env.REACT_APP_IS_WIDGET) {
+ return useWidgetsWeb3React()
+ }
+
+ const interfaceContext = useWeb3React()
+ const interfaceNetworkContext = useWeb3React(
+ process.env.REACT_APP_IS_WIDGET ? undefined : NetworkContextName
+ )
+
+ if (interfaceContext.active) {
+ return interfaceContext
+ }
+
+ return interfaceNetworkContext
+}
diff --git a/src/hooks/useAddTokenToMetamask.ts b/src/hooks/useAddTokenToMetamask.ts
index a8a582ed5e..7de7052863 100644
--- a/src/hooks/useAddTokenToMetamask.ts
+++ b/src/hooks/useAddTokenToMetamask.ts
@@ -1,9 +1,8 @@
import { Currency, Token } from '@uniswap/sdk-core'
-import { useActiveWeb3React } from 'hooks/web3'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
import { useCallback, useState } from 'react'
-import { getTokenLogoURL } from './../components/CurrencyLogo/index'
-
export default function useAddTokenToMetamask(currencyToAdd: Currency | undefined): {
addToken: () => void
success: boolean | undefined
@@ -13,6 +12,7 @@ export default function useAddTokenToMetamask(currencyToAdd: Currency | undefine
const token: Token | undefined = currencyToAdd?.wrapped
const [success, setSuccess] = useState()
+ const logoURL = useCurrencyLogoURIs(token)[0]
const addToken = useCallback(() => {
if (library && library.provider.isMetaMask && library.provider.request && token) {
@@ -26,7 +26,7 @@ export default function useAddTokenToMetamask(currencyToAdd: Currency | undefine
address: token.address,
symbol: token.symbol,
decimals: token.decimals,
- image: getTokenLogoURL(token.address),
+ image: logoURL,
},
},
})
@@ -37,7 +37,7 @@ export default function useAddTokenToMetamask(currencyToAdd: Currency | undefine
} else {
setSuccess(false)
}
- }, [library, token])
+ }, [library, logoURL, token])
return { addToken, success }
}
diff --git a/src/hooks/useAllCurrencyCombinations.ts b/src/hooks/useAllCurrencyCombinations.ts
index a15ae64d62..4edcf37acd 100644
--- a/src/hooks/useAllCurrencyCombinations.ts
+++ b/src/hooks/useAllCurrencyCombinations.ts
@@ -48,7 +48,7 @@ export function useAllCurrencyCombinations(currencyA?: Currency, currencyB?: Cur
const firstIndexInOtherPairs = otherPairs.findIndex(([t0Other, t1Other]) => {
return (t0.equals(t0Other) && t1.equals(t1Other)) || (t0.equals(t1Other) && t1.equals(t0Other))
})
- // only accept the first occurence of the same 2 tokens
+ // only accept the first occurrence of the same 2 tokens
return firstIndexInOtherPairs === i
})
// optionally filter out some pairs for tokens with custom bases defined
diff --git a/src/hooks/useAllV3Routes.ts b/src/hooks/useAllV3Routes.ts
index 22ca97821c..01f1093077 100644
--- a/src/hooks/useAllV3Routes.ts
+++ b/src/hooks/useAllV3Routes.ts
@@ -1,9 +1,9 @@
import { Currency } from '@uniswap/sdk-core'
import { Pool, Route } from '@uniswap/v3-sdk'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useMemo } from 'react'
import { useV3SwapPools } from './useV3SwapPools'
-import { useActiveWeb3React } from './web3'
/**
* Returns true if poolA is equivalent to poolB
diff --git a/src/hooks/useApproveCallback.ts b/src/hooks/useApproveCallback.ts
index f440f2ec69..4c8b5a9654 100644
--- a/src/hooks/useApproveCallback.ts
+++ b/src/hooks/useApproveCallback.ts
@@ -1,23 +1,25 @@
-import { MaxUint256 } from '@ethersproject/constants'
-import { TransactionResponse } from '@ethersproject/providers'
+import { Trade } from '@uniswap/router-sdk'
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
-import { useCallback, useMemo } from 'react'
+import useSwapApproval, { useSwapApprovalOptimizedTrade } from 'lib/hooks/swap/useSwapApproval'
+import { ApprovalState, useApproval } from 'lib/hooks/useApproval'
+import { useCallback } from 'react'
-import { SWAP_ROUTER_ADDRESSES, V2_ROUTER_ADDRESS } from '../constants/addresses'
import { TransactionType } from '../state/transactions/actions'
import { useHasPendingApproval, useTransactionAdder } from '../state/transactions/hooks'
-import { calculateGasMargin } from '../utils/calculateGasMargin'
-import { useTokenContract } from './useContract'
-import { useTokenAllowance } from './useTokenAllowance'
-import { useActiveWeb3React } from './web3'
+export { ApprovalState } from 'lib/hooks/useApproval'
-export enum ApprovalState {
- UNKNOWN = 'UNKNOWN',
- NOT_APPROVED = 'NOT_APPROVED',
- PENDING = 'PENDING',
- APPROVED = 'APPROVED',
+function useGetAndTrackApproval(getApproval: ReturnType[1]) {
+ const addTransaction = useTransactionAdder()
+ return useCallback(() => {
+ return getApproval().then((pending) => {
+ if (pending) {
+ const { response, tokenAddress, spenderAddress: spender } = pending
+ addTransaction(response, { type: TransactionType.APPROVAL, tokenAddress, spender })
+ }
+ })
+ }, [addTransaction, getApproval])
}
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
@@ -25,101 +27,25 @@ export function useApproveCallback(
amountToApprove?: CurrencyAmount,
spender?: string
): [ApprovalState, () => Promise] {
- const { account, chainId } = useActiveWeb3React()
- const token = amountToApprove?.currency?.isToken ? amountToApprove.currency : undefined
- const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
- const pendingApproval = useHasPendingApproval(token?.address, spender)
-
- // check the current approval status
- const approvalState: ApprovalState = useMemo(() => {
- if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
- if (amountToApprove.currency.isNative) return ApprovalState.APPROVED
- // we might not have enough data to know whether or not we need to approve
- if (!currentAllowance) return ApprovalState.UNKNOWN
-
- // amountToApprove will be defined if currentAllowance is
- return currentAllowance.lessThan(amountToApprove)
- ? pendingApproval
- ? ApprovalState.PENDING
- : ApprovalState.NOT_APPROVED
- : ApprovalState.APPROVED
- }, [amountToApprove, currentAllowance, pendingApproval, spender])
-
- const tokenContract = useTokenContract(token?.address)
- const addTransaction = useTransactionAdder()
-
- const approve = useCallback(async (): Promise => {
- if (approvalState !== ApprovalState.NOT_APPROVED) {
- console.error('approve was called unnecessarily')
- return
- }
- if (!chainId) {
- console.error('no chainId')
- return
- }
-
- if (!token) {
- console.error('no token')
- return
- }
-
- if (!tokenContract) {
- console.error('tokenContract is null')
- return
- }
-
- if (!amountToApprove) {
- console.error('missing amount to approve')
- return
- }
-
- if (!spender) {
- console.error('no spender')
- return
- }
-
- let useExact = false
- const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
- // general fallback for tokens who restrict approval amounts
- useExact = true
- return tokenContract.estimateGas.approve(spender, amountToApprove.quotient.toString())
- })
-
- return tokenContract
- .approve(spender, useExact ? amountToApprove.quotient.toString() : MaxUint256, {
- gasLimit: calculateGasMargin(chainId, estimatedGas),
- })
- .then((response: TransactionResponse) => {
- addTransaction(response, { type: TransactionType.APPROVAL, tokenAddress: token.address, spender })
- })
- .catch((error: Error) => {
- console.debug('Failed to approve token', error)
- throw error
- })
- }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction, chainId])
+ const [approval, getApproval] = useApproval(amountToApprove, spender, useHasPendingApproval)
+ return [approval, useGetAndTrackApproval(getApproval)]
+}
- return [approvalState, approve]
+export function useApprovalOptimizedTrade(
+ trade: Trade | undefined,
+ allowedSlippage: Percent
+) {
+ return useSwapApprovalOptimizedTrade(trade, allowedSlippage, useHasPendingApproval)
}
-// wraps useApproveCallback in the context of a swap
export function useApproveCallbackFromTrade(
- trade: V2Trade | V3Trade | undefined,
+ trade:
+ | V2Trade
+ | V3Trade
+ | Trade
+ | undefined,
allowedSlippage: Percent
-) {
- const { chainId } = useActiveWeb3React()
- const v3SwapRouterAddress = chainId ? SWAP_ROUTER_ADDRESSES[chainId] : undefined
- const amountToApprove = useMemo(
- () => (trade && trade.inputAmount.currency.isToken ? trade.maximumAmountIn(allowedSlippage) : undefined),
- [trade, allowedSlippage]
- )
- return useApproveCallback(
- amountToApprove,
- chainId
- ? trade instanceof V2Trade
- ? V2_ROUTER_ADDRESS[chainId]
- : trade instanceof V3Trade
- ? v3SwapRouterAddress
- : undefined
- : undefined
- )
+): [ApprovalState, () => Promise] {
+ const [approval, getApproval] = useSwapApproval(trade, allowedSlippage, useHasPendingApproval)
+ return [approval, useGetAndTrackApproval(getApproval)]
}
diff --git a/src/hooks/useArgentWalletContract.ts b/src/hooks/useArgentWalletContract.ts
index fa814feee4..b9e00b128c 100644
--- a/src/hooks/useArgentWalletContract.ts
+++ b/src/hooks/useArgentWalletContract.ts
@@ -1,8 +1,9 @@
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+
import ArgentWalletContractABI from '../abis/argent-wallet-contract.json'
import { ArgentWalletContract } from '../abis/types'
import { useContract } from './useContract'
import useIsArgentWallet from './useIsArgentWallet'
-import { useActiveWeb3React } from './web3'
export function useArgentWalletContract(): ArgentWalletContract | null {
const { account } = useActiveWeb3React()
diff --git a/src/hooks/useAutoRouterSupported.tsx b/src/hooks/useAutoRouterSupported.tsx
new file mode 100644
index 0000000000..fb587f0c23
--- /dev/null
+++ b/src/hooks/useAutoRouterSupported.tsx
@@ -0,0 +1,7 @@
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { AUTO_ROUTER_SUPPORTED_CHAINS } from 'lib/hooks/routing/clientSideSmartOrderRouter'
+
+export default function useAutoRouterSupported(): boolean {
+ const { chainId } = useActiveWeb3React()
+ return Boolean(chainId && AUTO_ROUTER_SUPPORTED_CHAINS.includes(chainId))
+}
diff --git a/src/hooks/useAutoSlippageTolerance.ts b/src/hooks/useAutoSlippageTolerance.ts
new file mode 100644
index 0000000000..2fc376dafc
--- /dev/null
+++ b/src/hooks/useAutoSlippageTolerance.ts
@@ -0,0 +1,78 @@
+import { Trade } from '@uniswap/router-sdk'
+import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
+import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
+import { L2_CHAIN_IDS } from '@src/constants/chains'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import JSBI from 'jsbi'
+import useNativeCurrency from 'lib/hooks/useNativeCurrency'
+import { useMemo } from 'react'
+import { InterfaceTrade } from 'state/routing/types'
+
+import useGasPrice from './useGasPrice'
+import useUSDCPrice, { useUSDCValue } from './useUSDCPrice'
+
+const V3_SWAP_DEFAULT_SLIPPAGE = new Percent(50, 10_000) // .50%
+const ONE_TENTHS_PERCENT = new Percent(10, 10_000) // .10%
+
+/**
+ * Return a guess of the gas cost used in computing slippage tolerance for a given trade
+ * @param trade the trade for which to _guess_ the amount of gas it would cost to execute
+ */
+function guesstimateGas(trade: Trade | undefined): number | undefined {
+ if (!!trade) {
+ return 100_000 + trade.swaps.reduce((memo, swap) => swap.route.pools.length + memo, 0) * 30_000
+ }
+ return undefined
+}
+
+const MIN_AUTO_SLIPPAGE_TOLERANCE = new Percent(5, 1000) // 0.5%
+const MAX_AUTO_SLIPPAGE_TOLERANCE = new Percent(25, 100) // 25%
+
+/**
+ * Returns slippage tolerance based on values from current trade, gas estimates from api, and active network.
+ */
+export default function useAutoSlippageTolerance(
+ trade: InterfaceTrade | undefined
+): Percent {
+ const { chainId } = useActiveWeb3React()
+ const onL2 = chainId && L2_CHAIN_IDS.includes(chainId)
+ const outputDollarValue = useUSDCValue(trade?.outputAmount)
+ const nativeGasPrice = useGasPrice()
+
+ const gasEstimate = guesstimateGas(trade)
+ const nativeCurrency = useNativeCurrency()
+ const nativeCurrencyPrice = useUSDCPrice(nativeCurrency ?? undefined)
+
+ return useMemo(() => {
+ if (!trade || onL2) return ONE_TENTHS_PERCENT
+
+ const nativeGasCost =
+ nativeGasPrice && typeof gasEstimate === 'number'
+ ? JSBI.multiply(nativeGasPrice, JSBI.BigInt(gasEstimate))
+ : undefined
+ const dollarGasCost =
+ nativeCurrency && nativeGasCost && nativeCurrencyPrice
+ ? nativeCurrencyPrice.quote(CurrencyAmount.fromRawAmount(nativeCurrency, nativeGasCost))
+ : undefined
+
+ // if valid estimate from api and using api trade, use gas estimate from api
+ // NOTE - dont use gas estimate for L2s yet - need to verify accuracy
+ // if not, use local heuristic
+ const dollarCostToUse =
+ chainId && SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId) && trade?.gasUseEstimateUSD
+ ? trade.gasUseEstimateUSD
+ : dollarGasCost
+
+ if (outputDollarValue && dollarCostToUse) {
+ // the rationale is that a user will not want their trade to fail for a loss due to slippage that is less than
+ // the cost of the gas of the failed transaction
+ const fraction = dollarCostToUse.asFraction.divide(outputDollarValue.asFraction)
+ const result = new Percent(fraction.numerator, fraction.denominator)
+ if (result.greaterThan(MAX_AUTO_SLIPPAGE_TOLERANCE)) return MAX_AUTO_SLIPPAGE_TOLERANCE
+ if (result.lessThan(MIN_AUTO_SLIPPAGE_TOLERANCE)) return MIN_AUTO_SLIPPAGE_TOLERANCE
+ return result
+ }
+
+ return V3_SWAP_DEFAULT_SLIPPAGE
+ }, [trade, onL2, nativeGasPrice, gasEstimate, nativeCurrency, nativeCurrencyPrice, chainId, outputDollarValue])
+}
diff --git a/src/hooks/useBestTrade.test.ts b/src/hooks/useBestTrade.test.ts
new file mode 100644
index 0000000000..2154e3293c
--- /dev/null
+++ b/src/hooks/useBestTrade.test.ts
@@ -0,0 +1,199 @@
+/**
+ * @jest-environment ./custom-test-env.js
+ */
+import { renderHook } from '@testing-library/react-hooks'
+import { CurrencyAmount, TradeType } from '@uniswap/sdk-core'
+import { DAI, USDC_MAINNET } from 'constants/tokens'
+import { TradeState } from 'state/routing/types'
+
+import { useRoutingAPITrade } from '../state/routing/useRoutingAPITrade'
+import useAutoRouterSupported from './useAutoRouterSupported'
+import { useBestTrade } from './useBestTrade'
+import { useClientSideV3Trade } from './useClientSideV3Trade'
+import useDebounce from './useDebounce'
+import useIsWindowVisible from './useIsWindowVisible'
+
+const USDCAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '10000')
+const DAIAmount = CurrencyAmount.fromRawAmount(DAI, '10000')
+
+jest.mock('./useAutoRouterSupported')
+jest.mock('./useClientSideV3Trade')
+jest.mock('./useDebounce')
+jest.mock('./useIsWindowVisible')
+jest.mock('state/routing/useRoutingAPITrade')
+jest.mock('state/user/hooks')
+
+const mockUseDebounce = useDebounce as jest.MockedFunction
+const mockUseAutoRouterSupported = useAutoRouterSupported as jest.MockedFunction
+const mockUseIsWindowVisible = useIsWindowVisible as jest.MockedFunction
+
+const mockUseRoutingAPITrade = useRoutingAPITrade as jest.MockedFunction
+const mockUseClientSideV3Trade = useClientSideV3Trade as jest.MockedFunction
+
+// helpers to set mock expectations
+const expectRouterMock = (state: TradeState) => {
+ mockUseRoutingAPITrade.mockReturnValue({ state, trade: undefined })
+}
+
+const expectClientSideMock = (state: TradeState) => {
+ mockUseClientSideV3Trade.mockReturnValue({ state, trade: undefined })
+}
+
+beforeEach(() => {
+ // ignore debounced value
+ mockUseDebounce.mockImplementation((value) => value)
+
+ mockUseIsWindowVisible.mockReturnValue(true)
+ mockUseAutoRouterSupported.mockReturnValue(true)
+})
+
+describe('#useBestV3Trade ExactIn', () => {
+ it('does not compute routing api trade when routing API is not supported', () => {
+ mockUseAutoRouterSupported.mockReturnValue(false)
+ expectRouterMock(TradeState.INVALID)
+ expectClientSideMock(TradeState.VALID)
+
+ const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
+
+ expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, DAI)
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
+ expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
+ })
+
+ it('does not compute routing api trade when window is not focused', () => {
+ mockUseIsWindowVisible.mockReturnValue(false)
+ expectRouterMock(TradeState.NO_ROUTE_FOUND)
+ expectClientSideMock(TradeState.VALID)
+
+ const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
+
+ expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, DAI)
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
+ 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_INPUT, USDCAmount, DAI))
+
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
+ expect(result.current).toEqual({ state: TradeState.LOADING, trade: undefined })
+ })
+
+ it('does not compute client side v3 trade if routing api is VALID', () => {
+ expectRouterMock(TradeState.VALID)
+
+ const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
+
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
+ expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
+ })
+
+ it('does not compute client side v3 trade if routing api is SYNCING', () => {
+ expectRouterMock(TradeState.SYNCING)
+
+ const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
+
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
+ expect(result.current).toEqual({ state: TradeState.SYNCING, trade: undefined })
+ })
+ })
+
+ describe('when routing api is in error state', () => {
+ it('does not compute client side v3 trade if routing api is INVALID', () => {
+ expectRouterMock(TradeState.INVALID)
+ expectClientSideMock(TradeState.VALID)
+
+ renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
+
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
+ })
+
+ it('computes client side v3 trade if routing api is NO_ROUTE_FOUND', () => {
+ expectRouterMock(TradeState.NO_ROUTE_FOUND)
+ expectClientSideMock(TradeState.VALID)
+
+ const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
+
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
+ expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
+ })
+ })
+})
+
+describe('#useBestV3Trade ExactOut', () => {
+ it('does not compute routing api trade when routing API is not supported', () => {
+ mockUseAutoRouterSupported.mockReturnValue(false)
+ expectRouterMock(TradeState.INVALID)
+ expectClientSideMock(TradeState.VALID)
+
+ const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
+
+ 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 })
+ })
+
+ it('does not compute routing api trade when window is not focused', () => {
+ mockUseIsWindowVisible.mockReturnValue(false)
+ expectRouterMock(TradeState.NO_ROUTE_FOUND)
+ expectClientSideMock(TradeState.VALID)
+
+ const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
+
+ 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_MAINNET))
+
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
+ expect(result.current).toEqual({ state: TradeState.LOADING, trade: undefined })
+ })
+
+ 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_MAINNET))
+
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
+ expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
+ })
+
+ 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_MAINNET))
+
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
+ expect(result.current).toEqual({ state: TradeState.SYNCING, trade: undefined })
+ })
+ })
+
+ describe('when routing api is in error state', () => {
+ it('computes client side v3 trade if routing api is INVALID', () => {
+ expectRouterMock(TradeState.INVALID)
+ expectClientSideMock(TradeState.VALID)
+
+ renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
+
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
+ })
+
+ it('computes client side v3 trade if routing api is NO_ROUTE_FOUND', () => {
+ expectRouterMock(TradeState.NO_ROUTE_FOUND)
+ expectClientSideMock(TradeState.VALID)
+
+ const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
+
+ expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
+ expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
+ })
+ })
+})
diff --git a/src/hooks/useBestV3Trade.ts b/src/hooks/useBestTrade.ts
similarity index 60%
rename from src/hooks/useBestV3Trade.ts
rename to src/hooks/useBestTrade.ts
index bf650163a6..57db09e1ba 100644
--- a/src/hooks/useBestV3Trade.ts
+++ b/src/hooks/useBestTrade.ts
@@ -1,36 +1,38 @@
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
-import { Trade } from '@uniswap/v3-sdk'
-import { V3TradeState } from 'state/routing/types'
+import { useMemo } from 'react'
+import { InterfaceTrade, TradeState } from 'state/routing/types'
import { useRoutingAPITrade } from 'state/routing/useRoutingAPITrade'
-import { useRoutingAPIEnabled } from 'state/user/hooks'
+import useAutoRouterSupported from './useAutoRouterSupported'
import { useClientSideV3Trade } from './useClientSideV3Trade'
import useDebounce from './useDebounce'
import useIsWindowVisible from './useIsWindowVisible'
/**
- * Returns the best v3 trade for a desired swap.
- * Uses optimized routes from the Routing API and falls back to the v3 router.
+ * Returns the best v2+v3 trade for a desired swap.
* @param tradeType whether the swap is an exact in/out
* @param amountSpecified the exact amount to swap in/out
* @param otherCurrency the desired output/payment currency
*/
-export function useBestV3Trade(
+export function useBestTrade(
tradeType: TradeType,
amountSpecified?: CurrencyAmount,
otherCurrency?: Currency
): {
- state: V3TradeState
- trade: Trade | null
+ state: TradeState
+ trade: InterfaceTrade | undefined
} {
- const routingAPIEnabled = useRoutingAPIEnabled()
+ const autoRouterSupported = useAutoRouterSupported()
const isWindowVisible = useIsWindowVisible()
- const [debouncedAmount, debouncedOtherCurrency] = useDebounce([amountSpecified, otherCurrency], 200)
+ const [debouncedAmount, debouncedOtherCurrency] = useDebounce(
+ useMemo(() => [amountSpecified, otherCurrency], [amountSpecified, otherCurrency]),
+ 200
+ )
const routingAPITrade = useRoutingAPITrade(
tradeType,
- routingAPIEnabled && isWindowVisible ? debouncedAmount : undefined,
+ autoRouterSupported && isWindowVisible ? debouncedAmount : undefined,
debouncedOtherCurrency
)
@@ -48,18 +50,22 @@ export function useBestV3Trade(
!amountSpecified.currency.equals(routingAPITrade.trade.outputAmount.currency) ||
!debouncedOtherCurrency?.equals(routingAPITrade.trade.inputAmount.currency))
- const useFallback = !routingAPIEnabled || (!debouncing && routingAPITrade.state === V3TradeState.NO_ROUTE_FOUND)
+ const useFallback = !autoRouterSupported || (!debouncing && routingAPITrade.state === TradeState.NO_ROUTE_FOUND)
- // only use client side router if routing api trade failed
+ // only use client side router if routing api trade failed or is not supported
const bestV3Trade = useClientSideV3Trade(
tradeType,
useFallback ? debouncedAmount : undefined,
useFallback ? debouncedOtherCurrency : undefined
)
- return {
- ...(useFallback ? bestV3Trade : routingAPITrade),
- ...(debouncing ? { state: V3TradeState.SYNCING } : {}),
- ...(isLoading ? { state: V3TradeState.LOADING } : {}),
- }
+ // only return gas estimate from api if routing api trade is used
+ return useMemo(
+ () => ({
+ ...(useFallback ? bestV3Trade : routingAPITrade),
+ ...(debouncing ? { state: TradeState.SYNCING } : {}),
+ ...(isLoading ? { state: TradeState.LOADING } : {}),
+ }),
+ [bestV3Trade, debouncing, isLoading, routingAPITrade, useFallback]
+ )
}
diff --git a/src/hooks/useBestV3Trade.test.ts b/src/hooks/useBestV3Trade.test.ts
deleted file mode 100644
index e882de0b9b..0000000000
--- a/src/hooks/useBestV3Trade.test.ts
+++ /dev/null
@@ -1,204 +0,0 @@
-/**
- * @jest-environment ./custom-test-env.js
- */
-
-import { renderHook } from '@testing-library/react-hooks'
-import { CurrencyAmount, TradeType } from '@uniswap/sdk-core'
-import { DAI, USDC } from 'constants/tokens'
-import { V3TradeState } from 'state/routing/types'
-import { useRoutingAPIEnabled } from 'state/user/hooks'
-
-import { useRoutingAPITrade } from '../state/routing/useRoutingAPITrade'
-import { useBestV3Trade } from './useBestV3Trade'
-import { useClientSideV3Trade } from './useClientSideV3Trade'
-import useDebounce from './useDebounce'
-import useIsWindowVisible from './useIsWindowVisible'
-
-const USDCAmount = CurrencyAmount.fromRawAmount(USDC, '10000')
-const DAIAmount = CurrencyAmount.fromRawAmount(DAI, '10000')
-
-jest.mock('./useDebounce')
-const mockUseDebounce = useDebounce as jest.MockedFunction
-
-// mock modules containing hooks
-jest.mock('state/routing/useRoutingAPITrade')
-jest.mock('./useClientSideV3Trade')
-jest.mock('state/user/hooks')
-jest.mock('./useIsWindowVisible')
-
-const mockUseRoutingAPIEnabled = useRoutingAPIEnabled as jest.MockedFunction
-const mockUseIsWindowVisible = useIsWindowVisible as jest.MockedFunction
-
-// useRouterTrade mocks
-const mockUseRoutingAPITrade = useRoutingAPITrade as jest.MockedFunction
-
-// useClientSideV3Trade mocks
-const mockUseClientSideV3Trade = useClientSideV3Trade as jest.MockedFunction
-
-// helpers to set mock expectations
-const expectRouterMock = (state: V3TradeState) => {
- mockUseRoutingAPITrade.mockReturnValue({ state, trade: null })
-}
-
-const expectClientSideMock = (state: V3TradeState) => {
- mockUseClientSideV3Trade.mockReturnValue({ state, trade: null })
-}
-
-beforeEach(() => {
- // ignore debounced value
- mockUseDebounce.mockImplementation((value) => value)
-
- mockUseIsWindowVisible.mockReturnValue(true)
- mockUseRoutingAPIEnabled.mockReturnValue(true)
-})
-
-describe('#useBestV3TradeExactIn', () => {
- it('does not compute routing api trade when routing API is disabled', () => {
- mockUseRoutingAPIEnabled.mockReturnValue(false)
- expectRouterMock(V3TradeState.INVALID)
- expectClientSideMock(V3TradeState.VALID)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_INPUT, USDCAmount, DAI))
-
- expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, DAI)
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
- expect(result.current).toEqual({ state: V3TradeState.VALID, trade: null })
- })
-
- it('does not compute routing api trade when window is not focused', () => {
- mockUseIsWindowVisible.mockReturnValue(false)
- expectRouterMock(V3TradeState.NO_ROUTE_FOUND)
- expectClientSideMock(V3TradeState.VALID)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_INPUT, USDCAmount, DAI))
-
- expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, DAI)
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
- expect(result.current).toEqual({ state: V3TradeState.VALID, trade: null })
- })
-
- describe('when routing api is in non-error state', () => {
- it('does not compute client side v3 trade if routing api is LOADING', () => {
- expectRouterMock(V3TradeState.LOADING)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_INPUT, USDCAmount, DAI))
-
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
- expect(result.current).toEqual({ state: V3TradeState.LOADING, trade: null })
- })
-
- it('does not compute client side v3 trade if routing api is VALID', () => {
- expectRouterMock(V3TradeState.VALID)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_INPUT, USDCAmount, DAI))
-
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
- expect(result.current).toEqual({ state: V3TradeState.VALID, trade: null })
- })
-
- it('does not compute client side v3 trade if routing api is SYNCING', () => {
- expectRouterMock(V3TradeState.SYNCING)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_INPUT, USDCAmount, DAI))
-
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
- expect(result.current).toEqual({ state: V3TradeState.SYNCING, trade: null })
- })
- })
-
- describe('when routing api is in error state', () => {
- it('does not compute client side v3 trade if routing api is INVALID', () => {
- expectRouterMock(V3TradeState.INVALID)
- expectClientSideMock(V3TradeState.VALID)
-
- renderHook(() => useBestV3Trade(TradeType.EXACT_INPUT, USDCAmount, DAI))
-
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
- })
-
- it('computes client side v3 trade if routing api is NO_ROUTE_FOUND', () => {
- expectRouterMock(V3TradeState.NO_ROUTE_FOUND)
- expectClientSideMock(V3TradeState.VALID)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_INPUT, USDCAmount, DAI))
-
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
- expect(result.current).toEqual({ state: V3TradeState.VALID, trade: null })
- })
- })
-})
-
-describe('#useBestV3TradeExactOut', () => {
- it('does not compute routing api trade when routing API is disabled', () => {
- mockUseRoutingAPIEnabled.mockReturnValue(false)
- expectRouterMock(V3TradeState.INVALID)
- expectClientSideMock(V3TradeState.VALID)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_OUTPUT, DAIAmount, USDC))
-
- expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, USDC)
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC)
- expect(result.current).toEqual({ state: V3TradeState.VALID, trade: null })
- })
-
- it('does not compute routing api trade when window is not focused', () => {
- mockUseIsWindowVisible.mockReturnValue(false)
- expectRouterMock(V3TradeState.NO_ROUTE_FOUND)
- expectClientSideMock(V3TradeState.VALID)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_OUTPUT, DAIAmount, USDC))
-
- expect(mockUseRoutingAPITrade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, USDC)
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC)
- expect(result.current).toEqual({ state: V3TradeState.VALID, trade: null })
- })
- describe('when routing api is in non-error state', () => {
- it('does not compute client side v3 trade if routing api is LOADING', () => {
- expectRouterMock(V3TradeState.LOADING)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_OUTPUT, DAIAmount, USDC))
-
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
- expect(result.current).toEqual({ state: V3TradeState.LOADING, trade: null })
- })
-
- it('does not compute client side v3 trade if routing api is VALID', () => {
- expectRouterMock(V3TradeState.VALID)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_OUTPUT, DAIAmount, USDC))
-
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
- expect(result.current).toEqual({ state: V3TradeState.VALID, trade: null })
- })
-
- it('does not compute client side v3 trade if routing api is SYNCING', () => {
- expectRouterMock(V3TradeState.SYNCING)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_OUTPUT, DAIAmount, USDC))
-
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
- expect(result.current).toEqual({ state: V3TradeState.SYNCING, trade: null })
- })
- })
-
- describe('when routing api is in error state', () => {
- it('computes client side v3 trade if routing api is INVALID', () => {
- expectRouterMock(V3TradeState.INVALID)
- expectClientSideMock(V3TradeState.VALID)
-
- renderHook(() => useBestV3Trade(TradeType.EXACT_OUTPUT, DAIAmount, USDC))
-
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
- })
-
- it('computes client side v3 trade if routing api is NO_ROUTE_FOUND', () => {
- expectRouterMock(V3TradeState.NO_ROUTE_FOUND)
- expectClientSideMock(V3TradeState.VALID)
-
- const { result } = renderHook(() => useBestV3Trade(TradeType.EXACT_OUTPUT, DAIAmount, USDC))
-
- expect(mockUseClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC)
- expect(result.current).toEqual({ state: V3TradeState.VALID, trade: null })
- })
- })
-})
diff --git a/src/hooks/useClientSideV3Trade.ts b/src/hooks/useClientSideV3Trade.ts
index 88fcdc932e..912f5c9246 100644
--- a/src/hooks/useClientSideV3Trade.ts
+++ b/src/hooks/useClientSideV3Trade.ts
@@ -1,18 +1,16 @@
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
-import { Route, SwapQuoter, Trade } from '@uniswap/v3-sdk'
+import { Route, SwapQuoter } from '@uniswap/v3-sdk'
import { SupportedChainId } from '@src/constants/chains'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
import JSBI from 'jsbi'
+import { useSingleContractWithCallData } from 'lib/hooks/multicall'
import { useMemo } from 'react'
-import { V3TradeState } from 'state/routing/types'
+import { InterfaceTrade, TradeState } from 'state/routing/types'
-import { useSingleContractWithCallData } from '../state/multicall/hooks'
import { useAllV3Routes } from './useAllV3Routes'
import { useV3Quoter } from './useContract'
-import { useActiveWeb3React } from './web3'
const QUOTE_GAS_OVERRIDES: { [chainId: number]: number } = {
- [SupportedChainId.OPTIMISM]: 6_000_000,
- [SupportedChainId.OPTIMISTIC_KOVAN]: 6_000_000,
[SupportedChainId.ARBITRUM_ONE]: 25_000_000,
[SupportedChainId.ARBITRUM_RINKEBY]: 25_000_000,
}
@@ -29,7 +27,7 @@ export function useClientSideV3Trade(
tradeType: TTradeType,
amountSpecified?: CurrencyAmount,
otherCurrency?: Currency
-): { state: V3TradeState; trade: Trade | null } {
+): { state: TradeState; trade: InterfaceTrade | undefined } {
const [currencyIn, currencyOut] = useMemo(
() =>
tradeType === TradeType.EXACT_INPUT
@@ -63,15 +61,15 @@ export function useClientSideV3Trade(
: amountSpecified.currency.equals(currencyIn))
) {
return {
- state: V3TradeState.INVALID,
- trade: null,
+ state: TradeState.INVALID,
+ trade: undefined,
}
}
if (routesLoading || quotesResults.some(({ loading }) => loading)) {
return {
- state: V3TradeState.LOADING,
- trade: null,
+ state: TradeState.LOADING,
+ trade: undefined,
}
}
@@ -119,18 +117,23 @@ export function useClientSideV3Trade(
if (!bestRoute || !amountIn || !amountOut) {
return {
- state: V3TradeState.NO_ROUTE_FOUND,
- trade: null,
+ state: TradeState.NO_ROUTE_FOUND,
+ trade: undefined,
}
}
return {
- state: V3TradeState.VALID,
- trade: Trade.createUncheckedTrade({
- route: bestRoute,
+ state: TradeState.VALID,
+ trade: new InterfaceTrade({
+ v2Routes: [],
+ v3Routes: [
+ {
+ routev3: bestRoute,
+ inputAmount: amountIn,
+ outputAmount: amountOut,
+ },
+ ],
tradeType,
- inputAmount: amountIn,
- outputAmount: amountOut,
}),
}
}, [amountSpecified, currencyIn, currencyOut, quotesResults, routes, routesLoading, tradeType])
diff --git a/src/hooks/useColor.ts b/src/hooks/useColor.ts
index bb60c8ff69..8c05013baa 100644
--- a/src/hooks/useColor.ts
+++ b/src/hooks/useColor.ts
@@ -1,10 +1,10 @@
import { Token } from '@uniswap/sdk-core'
import { SupportedChainId } from 'constants/chains'
-import Vibrant from 'node-vibrant/lib/bundle'
+import uriToHttp from 'lib/utils/uriToHttp'
+import Vibrant from 'node-vibrant/lib/bundle.js'
import { shade } from 'polished'
import { useLayoutEffect, useState } from 'react'
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
-import uriToHttp from 'utils/uriToHttp'
import { hex } from 'wcag-contrast'
function URIForEthToken(address: string) {
diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts
index e07b74595d..fe52cf0edc 100644
--- a/src/hooks/useContract.ts
+++ b/src/hooks/useContract.ts
@@ -1,43 +1,46 @@
import { Contract } from '@ethersproject/contracts'
-import { abi as GOVERNANCE_ABI } from '@uniswap/governance/build/GovernorAlpha.json'
-import { abi as UNI_ABI } from '@uniswap/governance/build/Uni.json'
-import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
-import { abi as MERKLE_DISTRIBUTOR_ABI } from '@uniswap/merkle-distributor/build/MerkleDistributor.json'
-import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
-import { abi as IUniswapV2Router02ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router02.json'
-import { abi as QuoterABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/Quoter.sol/Quoter.json'
-import { abi as MulticallABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json'
-import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
-import { abi as V2MigratorABI } from '@uniswap/v3-periphery/artifacts/contracts/V3Migrator.sol/V3Migrator.json'
+import IUniswapV2PairJson from '@uniswap/v2-core/build/IUniswapV2Pair.json'
+import IUniswapV2Router02Json from '@uniswap/v2-periphery/build/IUniswapV2Router02.json'
+import QuoterJson from '@uniswap/v3-periphery/artifacts/contracts/lens/Quoter.sol/Quoter.json'
+import TickLensJson from '@uniswap/v3-periphery/artifacts/contracts/lens/TickLens.sol/TickLens.json'
+import UniswapInterfaceMulticallJson from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json'
+import NonfungiblePositionManagerJson from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
+import V3MigratorJson from '@uniswap/v3-periphery/artifacts/contracts/V3Migrator.sol/V3Migrator.json'
import ARGENT_WALLET_DETECTOR_ABI from 'abis/argent-wallet-detector.json'
import EIP_2612 from 'abis/eip_2612.json'
import ENS_PUBLIC_RESOLVER_ABI from 'abis/ens-public-resolver.json'
import ENS_ABI from 'abis/ens-registrar.json'
import ERC20_ABI from 'abis/erc20.json'
import ERC20_BYTES32_ABI from 'abis/erc20_bytes32.json'
-import GOVERNOR_BRAVO_ABI from 'abis/governor-bravo.json'
+import ERC721_ABI from 'abis/erc721.json'
+import ERC1155_ABI from 'abis/erc1155.json'
+import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Erc721, Erc1155, Weth } from 'abis/types'
import WETH_ABI from 'abis/weth.json'
import {
ARGENT_WALLET_DETECTOR_ADDRESS,
ENS_REGISTRAR_ADDRESSES,
- GOVERNANCE_ALPHA_V0_ADDRESSES,
- GOVERNANCE_ALPHA_V1_ADDRESSES,
- GOVERNANCE_BRAVO_ADDRESSES,
- MERKLE_DISTRIBUTOR_ADDRESS,
MULTICALL_ADDRESS,
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
QUOTER_ADDRESSES,
+ TICK_LENS_ADDRESSES,
V2_ROUTER_ADDRESS,
V3_MIGRATOR_ADDRESSES,
} from 'constants/addresses'
+import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useMemo } from 'react'
-import { NonfungiblePositionManager, Quoter, UniswapInterfaceMulticall } from 'types/v3'
+import { NonfungiblePositionManager, Quoter, TickLens, UniswapInterfaceMulticall } from 'types/v3'
import { V3Migrator } from 'types/v3/V3Migrator'
+
import { getContract } from 'utils'
-import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Weth } from '../abis/types'
-import { UNI, WETH9_EXTENDED } from 'constants/tokens'
-import { useActiveWeb3React } from './web3'
+const { abi: IUniswapV2PairABI } = IUniswapV2PairJson
+const { abi: IUniswapV2Router02ABI } = IUniswapV2Router02Json
+const { abi: QuoterABI } = QuoterJson
+const { abi: TickLensABI } = TickLensJson
+const { abi: MulticallABI } = UniswapInterfaceMulticallJson
+const { abi: NFTPositionManagerABI } = NonfungiblePositionManagerJson
+const { abi: V2MigratorABI } = V3MigratorJson
// returns null on errors
export function useContract(
@@ -72,7 +75,19 @@ export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: b
export function useWETHContract(withSignerIfPossible?: boolean) {
const { chainId } = useActiveWeb3React()
- return useContract(chainId ? WETH9_EXTENDED[chainId]?.address : undefined, WETH_ABI, withSignerIfPossible)
+ return useContract(
+ chainId ? WRAPPED_NATIVE_CURRENCY[chainId]?.address : undefined,
+ WETH_ABI,
+ withSignerIfPossible
+ )
+}
+
+export function useERC721Contract(nftAddress?: string) {
+ return useContract(nftAddress, ERC721_ABI, false)
+}
+
+export function useERC1155Contract(nftAddress?: string) {
+ return useContract(nftAddress, ERC1155_ABI, false)
}
export function useArgentWalletDetectorContract() {
@@ -103,37 +118,10 @@ export function useV2RouterContract(): Contract | null {
return useContract(V2_ROUTER_ADDRESS, IUniswapV2Router02ABI, true)
}
-export function useMulticall2Contract() {
+export function useInterfaceMulticall() {
return useContract(MULTICALL_ADDRESS, MulticallABI, false) as UniswapInterfaceMulticall
}
-export function useMerkleDistributorContract() {
- return useContract(MERKLE_DISTRIBUTOR_ADDRESS, MERKLE_DISTRIBUTOR_ABI, true)
-}
-
-export function useGovernanceV0Contract(): Contract | null {
- return useContract(GOVERNANCE_ALPHA_V0_ADDRESSES, GOVERNANCE_ABI, false)
-}
-
-export function useGovernanceV1Contract(): Contract | null {
- return useContract(GOVERNANCE_ALPHA_V1_ADDRESSES, GOVERNANCE_ABI, false)
-}
-
-export function useGovernanceBravoContract(): Contract | null {
- return useContract(GOVERNANCE_BRAVO_ADDRESSES, GOVERNOR_BRAVO_ABI, true)
-}
-
-export const useLatestGovernanceContract = useGovernanceBravoContract
-
-export function useUniContract() {
- const { chainId } = useActiveWeb3React()
- return useContract(chainId ? UNI[chainId]?.address : undefined, UNI_ABI, true)
-}
-
-export function useStakingContract(stakingAddress?: string, withSignerIfPossible?: boolean) {
- return useContract(stakingAddress, STAKING_REWARDS_ABI, withSignerIfPossible)
-}
-
export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean): NonfungiblePositionManager | null {
return useContract(
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
@@ -145,3 +133,9 @@ export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean):
export function useV3Quoter() {
return useContract(QUOTER_ADDRESSES, QuoterABI)
}
+
+export function useTickLens(): TickLens | null {
+ const { chainId } = useActiveWeb3React()
+ const address = chainId ? TICK_LENS_ADDRESSES[chainId] : undefined
+ return useContract(address, TickLensABI) as TickLens | null
+}
diff --git a/src/hooks/useCurrentBlockTimestamp.ts b/src/hooks/useCurrentBlockTimestamp.ts
index 9124062bad..35118dcfda 100644
--- a/src/hooks/useCurrentBlockTimestamp.ts
+++ b/src/hooks/useCurrentBlockTimestamp.ts
@@ -1,10 +1,10 @@
import { BigNumber } from '@ethersproject/bignumber'
+import { useSingleCallResult } from 'lib/hooks/multicall'
-import { useSingleCallResult } from '../state/multicall/hooks'
-import { useMulticall2Contract } from './useContract'
+import { useInterfaceMulticall } from './useContract'
// gets the current timestamp from the blockchain
export default function useCurrentBlockTimestamp(): BigNumber | undefined {
- const multicall = useMulticall2Contract()
+ const multicall = useInterfaceMulticall()
return useSingleCallResult(multicall, 'getCurrentBlockTimestamp')?.result?.[0]
}
diff --git a/src/hooks/useDebounce.ts b/src/hooks/useDebounce.ts
index f753299da4..c03ee2bc68 100644
--- a/src/hooks/useDebounce.ts
+++ b/src/hooks/useDebounce.ts
@@ -1,5 +1,9 @@
import { useEffect, useState } from 'react'
+/**
+ * Debounces updates to a value.
+ * Non-primitives *must* wrap the value in useMemo, or the value will be updated due to referential inequality.
+ */
// modified from https://usehooks.com/useDebounce/
export default function useDebounce(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value)
diff --git a/src/hooks/useENS.ts b/src/hooks/useENS.ts
index c36bd5bd28..81701f22b8 100644
--- a/src/hooks/useENS.ts
+++ b/src/hooks/useENS.ts
@@ -1,3 +1,5 @@
+import { useMemo } from 'react'
+
import { isAddress } from '../utils'
import useENSAddress from './useENSAddress'
import useENSName from './useENSName'
@@ -15,9 +17,12 @@ export default function useENS(nameOrAddress?: string | null): {
const reverseLookup = useENSName(validated ? validated : undefined)
const lookup = useENSAddress(nameOrAddress)
- return {
- loading: reverseLookup.loading || lookup.loading,
- address: validated ? validated : lookup.address,
- name: reverseLookup.ENSName ? reverseLookup.ENSName : !validated && lookup.address ? nameOrAddress || null : null,
- }
+ return useMemo(
+ () => ({
+ loading: reverseLookup.loading || lookup.loading,
+ address: validated ? validated : lookup.address,
+ name: reverseLookup.ENSName ? reverseLookup.ENSName : !validated && lookup.address ? nameOrAddress || null : null,
+ }),
+ [lookup.address, lookup.loading, nameOrAddress, reverseLookup.ENSName, reverseLookup.loading, validated]
+ )
}
diff --git a/src/hooks/useENSAddress.ts b/src/hooks/useENSAddress.ts
index a4565d9e95..c64e5ee538 100644
--- a/src/hooks/useENSAddress.ts
+++ b/src/hooks/useENSAddress.ts
@@ -1,7 +1,7 @@
-import { namehash } from '@ethersproject/hash'
+import { useSingleCallResult } from 'lib/hooks/multicall'
import { useMemo } from 'react'
+import { safeNamehash } from 'utils/safeNamehash'
-import { useSingleCallResult } from '../state/multicall/hooks'
import isZero from '../utils/isZero'
import { useENSRegistrarContract, useENSResolverContract } from './useContract'
import useDebounce from './useDebounce'
@@ -11,14 +11,10 @@ import useDebounce from './useDebounce'
*/
export default function useENSAddress(ensName?: string | null): { loading: boolean; address: string | null } {
const debouncedName = useDebounce(ensName, 200)
- const ensNodeArgument = useMemo(() => {
- if (!debouncedName) return [undefined]
- try {
- return debouncedName ? [namehash(debouncedName)] : [undefined]
- } catch (error) {
- return [undefined]
- }
- }, [debouncedName])
+ const ensNodeArgument = useMemo(
+ () => [debouncedName === null ? undefined : safeNamehash(debouncedName)],
+ [debouncedName]
+ )
const registrarContract = useENSRegistrarContract(false)
const resolverAddress = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument)
const resolverAddressResult = resolverAddress.result?.[0]
@@ -29,8 +25,11 @@ export default function useENSAddress(ensName?: string | null): { loading: boole
const addr = useSingleCallResult(resolverContract, 'addr', ensNodeArgument)
const changed = debouncedName !== ensName
- return {
- address: changed ? null : addr.result?.[0] ?? null,
- loading: changed || resolverAddress.loading || addr.loading,
- }
+ return useMemo(
+ () => ({
+ address: changed ? null : addr.result?.[0] ?? null,
+ loading: changed || resolverAddress.loading || addr.loading,
+ }),
+ [addr.loading, addr.result, changed, resolverAddress.loading]
+ )
}
diff --git a/src/hooks/useENSAvatar.ts b/src/hooks/useENSAvatar.ts
new file mode 100644
index 0000000000..0652634493
--- /dev/null
+++ b/src/hooks/useENSAvatar.ts
@@ -0,0 +1,149 @@
+import { BigNumber } from '@ethersproject/bignumber'
+import { hexZeroPad } from '@ethersproject/bytes'
+import { namehash } from '@ethersproject/hash'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { useSingleCallResult } from 'lib/hooks/multicall'
+import uriToHttp from 'lib/utils/uriToHttp'
+import { useEffect, useMemo, useState } from 'react'
+import { safeNamehash } from 'utils/safeNamehash'
+
+import { isAddress } from '../utils'
+import isZero from '../utils/isZero'
+import { useENSRegistrarContract, useENSResolverContract, useERC721Contract, useERC1155Contract } from './useContract'
+import useDebounce from './useDebounce'
+import useENSName from './useENSName'
+
+/**
+ * Returns the ENS avatar URI, if available.
+ * Spec: https://gist.github.com/Arachnid/9db60bd75277969ee1689c8742b75182.
+ */
+export default function useENSAvatar(
+ address?: string,
+ enforceOwnership = true
+): { avatar: string | null; loading: boolean } {
+ const debouncedAddress = useDebounce(address, 200)
+ const node = useMemo(() => {
+ if (!debouncedAddress || !isAddress(debouncedAddress)) return undefined
+ return namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`)
+ }, [debouncedAddress])
+
+ const addressAvatar = useAvatarFromNode(node)
+ const ENSName = useENSName(address).ENSName
+ const nameAvatar = useAvatarFromNode(ENSName === null ? undefined : safeNamehash(ENSName))
+ let avatar = addressAvatar.avatar || nameAvatar.avatar
+
+ const nftAvatar = useAvatarFromNFT(avatar, enforceOwnership)
+ avatar = nftAvatar.avatar || avatar
+
+ const http = avatar && uriToHttp(avatar)[0]
+
+ const changed = debouncedAddress !== address
+ return useMemo(
+ () => ({
+ avatar: changed ? null : http ?? null,
+ loading: changed || addressAvatar.loading || nameAvatar.loading || nftAvatar.loading,
+ }),
+ [addressAvatar.loading, changed, http, nameAvatar.loading, nftAvatar.loading]
+ )
+}
+
+function useAvatarFromNode(node?: string): { avatar?: string; loading: boolean } {
+ const nodeArgument = useMemo(() => [node], [node])
+ const textArgument = useMemo(() => [node, 'avatar'], [node])
+ const registrarContract = useENSRegistrarContract(false)
+ const resolverAddress = useSingleCallResult(registrarContract, 'resolver', nodeArgument)
+ const resolverAddressResult = resolverAddress.result?.[0]
+ const resolverContract = useENSResolverContract(
+ resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined,
+ false
+ )
+ const avatar = useSingleCallResult(resolverContract, 'text', textArgument)
+
+ return useMemo(
+ () => ({
+ avatar: avatar.result?.[0],
+ loading: resolverAddress.loading || avatar.loading,
+ }),
+ [avatar.loading, avatar.result, resolverAddress.loading]
+ )
+}
+
+function useAvatarFromNFT(nftUri = '', enforceOwnership: boolean): { avatar?: string; loading: boolean } {
+ const parts = nftUri.toLowerCase().split(':')
+ const protocol = parts[0]
+ // ignore the chain from eip155
+ // TODO: when we are able, pull only from the specified chain
+ const [, erc] = parts[1]?.split('/') ?? []
+ const [contractAddress, id] = parts[2]?.split('/') ?? []
+ const isERC721 = protocol === 'eip155' && erc === 'erc721'
+ const isERC1155 = protocol === 'eip155' && erc === 'erc1155'
+ const erc721 = useERC721Uri(isERC721 ? contractAddress : undefined, id, enforceOwnership)
+ const erc1155 = useERC1155Uri(isERC1155 ? contractAddress : undefined, id, enforceOwnership)
+ const uri = erc721.uri || erc1155.uri
+ const http = uri && uriToHttp(uri)[0]
+
+ const [loading, setLoading] = useState(false)
+ const [avatar, setAvatar] = useState(undefined)
+ useEffect(() => {
+ setAvatar(undefined)
+ if (http) {
+ setLoading(true)
+ fetch(http)
+ .then((res) => res.json())
+ .then(({ image }) => {
+ setAvatar(image)
+ })
+ .catch((e) => console.warn(e))
+ .finally(() => {
+ setLoading(false)
+ })
+ }
+ }, [http])
+
+ return useMemo(
+ () => ({ avatar, loading: erc721.loading || erc1155.loading || loading }),
+ [avatar, erc1155.loading, erc721.loading, loading]
+ )
+}
+
+function useERC721Uri(
+ contractAddress: string | undefined,
+ id: string | undefined,
+ enforceOwnership: boolean
+): { uri?: string; loading: boolean } {
+ const idArgument = useMemo(() => [id], [id])
+ const { account } = useActiveWeb3React()
+ const contract = useERC721Contract(contractAddress)
+ const owner = useSingleCallResult(contract, 'ownerOf', idArgument)
+ const uri = useSingleCallResult(contract, 'tokenURI', idArgument)
+ return useMemo(
+ () => ({
+ uri: !enforceOwnership || account === owner.result?.[0] ? uri.result?.[0] : undefined,
+ loading: owner.loading || uri.loading,
+ }),
+ [account, enforceOwnership, owner.loading, owner.result, uri.loading, uri.result]
+ )
+}
+
+function useERC1155Uri(
+ contractAddress: string | undefined,
+ id: string | undefined,
+ enforceOwnership: boolean
+): { uri?: string; loading: boolean } {
+ const { account } = useActiveWeb3React()
+ const idArgument = useMemo(() => [id], [id])
+ const accountArgument = useMemo(() => [account || '', id], [account, id])
+ const contract = useERC1155Contract(contractAddress)
+ const balance = useSingleCallResult(contract, 'balanceOf', accountArgument)
+ const uri = useSingleCallResult(contract, 'uri', idArgument)
+ // ERC-1155 allows a generic {id} in the URL, so prepare to replace if relevant,
+ // in lowercase hexadecimal (with no 0x prefix) and leading zero padded to 64 hex characters.
+ const idHex = id ? hexZeroPad(BigNumber.from(id).toHexString(), 32).substring(2) : id
+ return useMemo(
+ () => ({
+ uri: !enforceOwnership || balance.result?.[0] > 0 ? uri.result?.[0]?.replaceAll('{id}', idHex) : undefined,
+ loading: balance.loading || uri.loading,
+ }),
+ [balance.loading, balance.result, enforceOwnership, uri.loading, uri.result, idHex]
+ )
+}
diff --git a/src/hooks/useENSContentHash.ts b/src/hooks/useENSContentHash.ts
index 9f002d092a..863fb74c61 100644
--- a/src/hooks/useENSContentHash.ts
+++ b/src/hooks/useENSContentHash.ts
@@ -1,7 +1,7 @@
-import { namehash } from '@ethersproject/hash'
+import { useSingleCallResult } from 'lib/hooks/multicall'
import { useMemo } from 'react'
+import { safeNamehash } from 'utils/safeNamehash'
-import { useSingleCallResult } from '../state/multicall/hooks'
import isZero from '../utils/isZero'
import { useENSRegistrarContract, useENSResolverContract } from './useContract'
@@ -9,14 +9,7 @@ import { useENSRegistrarContract, useENSResolverContract } from './useContract'
* Does a lookup for an ENS name to find its contenthash.
*/
export default function useENSContentHash(ensName?: string | null): { loading: boolean; contenthash: string | null } {
- const ensNodeArgument = useMemo(() => {
- if (!ensName) return [undefined]
- try {
- return ensName ? [namehash(ensName)] : [undefined]
- } catch (error) {
- return [undefined]
- }
- }, [ensName])
+ const ensNodeArgument = useMemo(() => [ensName === null ? undefined : safeNamehash(ensName)], [ensName])
const registrarContract = useENSRegistrarContract(false)
const resolverAddressResult = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument)
const resolverAddress = resolverAddressResult.result?.[0]
@@ -26,8 +19,11 @@ export default function useENSContentHash(ensName?: string | null): { loading: b
)
const contenthash = useSingleCallResult(resolverContract, 'contenthash', ensNodeArgument)
- return {
- contenthash: contenthash.result?.[0] ?? null,
- loading: resolverAddressResult.loading || contenthash.loading,
- }
+ return useMemo(
+ () => ({
+ contenthash: contenthash.result?.[0] ?? null,
+ loading: resolverAddressResult.loading || contenthash.loading,
+ }),
+ [contenthash.loading, contenthash.result, resolverAddressResult.loading]
+ )
}
diff --git a/src/hooks/useENSName.ts b/src/hooks/useENSName.ts
index f0b7d1d8bf..df364d0477 100644
--- a/src/hooks/useENSName.ts
+++ b/src/hooks/useENSName.ts
@@ -1,11 +1,12 @@
import { namehash } from '@ethersproject/hash'
+import { useSingleCallResult } from 'lib/hooks/multicall'
import { useMemo } from 'react'
-import { useSingleCallResult } from '../state/multicall/hooks'
import { isAddress } from '../utils'
import isZero from '../utils/isZero'
import { useENSRegistrarContract, useENSResolverContract } from './useContract'
import useDebounce from './useDebounce'
+import useENSAddress from './useENSAddress'
/**
* Does a reverse lookup for an address to find its ENS name.
@@ -15,11 +16,7 @@ export default function useENSName(address?: string): { ENSName: string | null;
const debouncedAddress = useDebounce(address, 200)
const ensNodeArgument = useMemo(() => {
if (!debouncedAddress || !isAddress(debouncedAddress)) return [undefined]
- try {
- return debouncedAddress ? [namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`)] : [undefined]
- } catch (error) {
- return [undefined]
- }
+ return [namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`)]
}, [debouncedAddress])
const registrarContract = useENSRegistrarContract(false)
const resolverAddress = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument)
@@ -28,11 +25,22 @@ export default function useENSName(address?: string): { ENSName: string | null;
resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined,
false
)
- const name = useSingleCallResult(resolverContract, 'name', ensNodeArgument)
+ const nameCallRes = useSingleCallResult(resolverContract, 'name', ensNodeArgument)
+ const name = nameCallRes.result?.[0]
+
+ /* ENS does not enforce that an address owns a .eth domain before setting it as a reverse proxy
+ and recommends that you perform a match on the forward resolution
+ see: https://docs.ens.domains/dapp-developer-guide/resolving-names#reverse-resolution
+ */
+ const fwdAddr = useENSAddress(name)
+ const checkedName = address === fwdAddr?.address ? name : null
const changed = debouncedAddress !== address
- return {
- ENSName: changed ? null : name.result?.[0] ?? null,
- loading: changed || resolverAddress.loading || name.loading,
- }
+ return useMemo(
+ () => ({
+ ENSName: changed ? null : checkedName,
+ loading: changed || resolverAddress.loading || nameCallRes.loading,
+ }),
+ [changed, nameCallRes.loading, checkedName, resolverAddress.loading]
+ )
}
diff --git a/src/hooks/useERC20Permit.ts b/src/hooks/useERC20Permit.ts
index 9ec184da5f..71c0c750aa 100644
--- a/src/hooks/useERC20Permit.ts
+++ b/src/hooks/useERC20Permit.ts
@@ -1,19 +1,20 @@
+import { BigNumber } from '@ethersproject/bignumber'
import { splitSignature } from '@ethersproject/bytes'
-import { Currency, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core'
+import { Trade } from '@uniswap/router-sdk'
+import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
import JSBI from 'jsbi'
+import { useSingleCallResult } from 'lib/hooks/multicall'
import { useMemo, useState } from 'react'
-import { SWAP_ROUTER_ADDRESSES } from '../constants/addresses'
-import { DAI, UNI, USDC } from '../constants/tokens'
-import { useSingleCallResult } from '../state/multicall/hooks'
+import { SWAP_ROUTER_ADDRESSES, V3_ROUTER_ADDRESS } from '../constants/addresses'
+import { DAI, UNI, USDC_MAINNET } from '../constants/tokens'
import { useEIP2612Contract } from './useContract'
import useIsArgentWallet from './useIsArgentWallet'
-import useTransactionDeadline from './useTransactionDeadline'
-import { useActiveWeb3React } from './web3'
-enum PermitType {
+export enum PermitType {
AMOUNT = 1,
ALLOWED = 2,
}
@@ -34,23 +35,23 @@ const PERMITTABLE_TOKENS: {
[checksummedTokenAddress: string]: PermitInfo
}
} = {
- [1]: {
- [USDC.address]: { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
+ 1: {
+ [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]: {
- ['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735']: { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' },
+ 4: {
+ '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735': { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' },
[UNI[4].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
},
- [3]: {
+ 3: {
[UNI[3].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
- ['0x07865c6E87B9F70255377e024ace6630C1Eaa37F']: { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
+ '0x07865c6E87B9F70255377e024ace6630C1Eaa37F': { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
},
- [5]: {
+ 5: {
[UNI[5].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
},
- [42]: {
+ 42: {
[UNI[42].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
},
}
@@ -118,6 +119,7 @@ const PERMIT_ALLOWED_TYPE = [
export function useERC20Permit(
currencyAmount: CurrencyAmount | null | undefined,
spender: string | null | undefined,
+ transactionDeadline: BigNumber | undefined,
overridePermitInfo: PermitInfo | undefined | null
): {
signatureData: SignatureData | null
@@ -125,7 +127,6 @@ export function useERC20Permit(
gatherPermitSignature: null | (() => Promise)
} {
const { account, chainId, library } = useActiveWeb3React()
- const transactionDeadline = useTransactionDeadline()
const tokenAddress = currencyAmount?.currency?.isToken ? currencyAmount.currency.address : undefined
const eip2612Contract = useEIP2612Contract(tokenAddress)
const isArgentWallet = useIsArgentWallet()
@@ -258,34 +259,28 @@ export function useERC20Permit(
])
}
-const REMOVE_V2_LIQUIDITY_PERMIT_INFO: PermitInfo = {
- version: '1',
- name: 'Uniswap V2',
- type: PermitType.AMOUNT,
-}
-
-export function useV2LiquidityTokenPermit(
- liquidityAmount: CurrencyAmount | null | undefined,
- spender: string | null | undefined
-) {
- return useERC20Permit(liquidityAmount, spender, REMOVE_V2_LIQUIDITY_PERMIT_INFO)
-}
-
export function useERC20PermitFromTrade(
- trade: V2Trade | V3Trade | undefined,
- allowedSlippage: Percent
+ trade:
+ | V2Trade
+ | V3Trade
+ | Trade
+ | undefined,
+ allowedSlippage: Percent,
+ transactionDeadline: BigNumber | undefined
) {
const { chainId } = useActiveWeb3React()
- const swapRouterAddress = chainId ? SWAP_ROUTER_ADDRESSES[chainId] : undefined
+ const swapRouterAddress = chainId
+ ? // v2 router does not support
+ trade instanceof V2Trade
+ ? undefined
+ : trade instanceof V3Trade
+ ? V3_ROUTER_ADDRESS[chainId]
+ : SWAP_ROUTER_ADDRESSES[chainId]
+ : undefined
const amountToApprove = useMemo(
() => (trade ? trade.maximumAmountIn(allowedSlippage) : undefined),
[trade, allowedSlippage]
)
- return useERC20Permit(
- amountToApprove,
- // v2 router does not support
- trade instanceof V2Trade ? undefined : trade instanceof V3Trade ? swapRouterAddress : undefined,
- null
- )
+ return useERC20Permit(amountToApprove, swapRouterAddress, transactionDeadline, null)
}
diff --git a/src/hooks/useFeeTierDistribution.ts b/src/hooks/useFeeTierDistribution.ts
index 14209a282a..3bfe79c5e7 100644
--- a/src/hooks/useFeeTierDistribution.ts
+++ b/src/hooks/useFeeTierDistribution.ts
@@ -1,10 +1,10 @@
import { skipToken } from '@reduxjs/toolkit/query/react'
import { Currency, Token } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
+import useBlockNumber from 'lib/hooks/useBlockNumber'
import ms from 'ms.macro'
import { useMemo } from 'react'
import ReactGA from 'react-ga'
-import { useBlockNumber } from 'state/application/hooks'
import { useFeeTierDistributionQuery } from 'state/data/enhanced'
import { FeeTierDistributionQuery } from 'state/data/generated'
@@ -19,11 +19,7 @@ interface FeeTierDistribution {
largestUsageFeeTier?: FeeAmount | undefined
// distributions as percentages of overall liquidity
- distributions?: {
- [FeeAmount.LOW]: number | undefined
- [FeeAmount.MEDIUM]: number | undefined
- [FeeAmount.HIGH]: number | undefined
- }
+ distributions?: Record
}
export function useFeeTierDistribution(
@@ -36,6 +32,7 @@ export function useFeeTierDistribution(
)
// fetch all pool states to determine pool state
+ const [poolStateVeryLow] = usePool(currencyA, currencyB, FeeAmount.LOWEST)
const [poolStateLow] = usePool(currencyA, currencyB, FeeAmount.LOW)
const [poolStateMedium] = usePool(currencyA, currencyB, FeeAmount.MEDIUM)
const [poolStateHigh] = usePool(currencyA, currencyB, FeeAmount.HIGH)
@@ -58,10 +55,13 @@ export function useFeeTierDistribution(
!isLoading &&
!isError &&
distributions &&
+ poolStateVeryLow !== PoolState.LOADING &&
poolStateLow !== PoolState.LOADING &&
poolStateMedium !== PoolState.LOADING &&
poolStateHigh !== PoolState.LOADING
? {
+ [FeeAmount.LOWEST]:
+ poolStateVeryLow === PoolState.EXISTS ? (distributions[FeeAmount.LOWEST] ?? 0) * 100 : undefined,
[FeeAmount.LOW]: poolStateLow === PoolState.EXISTS ? (distributions[FeeAmount.LOW] ?? 0) * 100 : undefined,
[FeeAmount.MEDIUM]:
poolStateMedium === PoolState.EXISTS ? (distributions[FeeAmount.MEDIUM] ?? 0) * 100 : undefined,
@@ -76,7 +76,17 @@ export function useFeeTierDistribution(
distributions: percentages,
largestUsageFeeTier: largestUsageFeeTier === -1 ? undefined : largestUsageFeeTier,
}
- }, [isLoading, isFetching, isUninitialized, isError, distributions, poolStateLow, poolStateMedium, poolStateHigh])
+ }, [
+ isLoading,
+ isFetching,
+ isUninitialized,
+ isError,
+ distributions,
+ poolStateVeryLow,
+ poolStateLow,
+ poolStateMedium,
+ poolStateHigh,
+ ])
}
function usePoolTVL(token0: Token | undefined, token1: Token | undefined) {
@@ -124,10 +134,11 @@ function usePoolTVL(token0: Token | undefined, token1: Token | undefined) {
return acc
},
{
+ [FeeAmount.LOWEST]: [undefined, undefined],
[FeeAmount.LOW]: [undefined, undefined],
[FeeAmount.MEDIUM]: [undefined, undefined],
[FeeAmount.HIGH]: [undefined, undefined],
- }
+ } as Record
)
// sum total tvl for token0 and token1
@@ -144,7 +155,13 @@ function usePoolTVL(token0: Token | undefined, token1: Token | undefined) {
const mean = (tvl0: number | undefined, sumTvl0: number, tvl1: number | undefined, sumTvl1: number) =>
tvl0 === undefined && tvl1 === undefined ? undefined : ((tvl0 ?? 0) + (tvl1 ?? 0)) / (sumTvl0 + sumTvl1) || 0
- const distributions = {
+ const distributions: Record = {
+ [FeeAmount.LOWEST]: mean(
+ tvlByFeeTier[FeeAmount.LOWEST][0],
+ sumToken0Tvl,
+ tvlByFeeTier[FeeAmount.LOWEST][1],
+ sumToken1Tvl
+ ),
[FeeAmount.LOW]: mean(tvlByFeeTier[FeeAmount.LOW][0], sumToken0Tvl, tvlByFeeTier[FeeAmount.LOW][1], sumToken1Tvl),
[FeeAmount.MEDIUM]: mean(
tvlByFeeTier[FeeAmount.MEDIUM][0],
diff --git a/src/hooks/useFetchListCallback.ts b/src/hooks/useFetchListCallback.ts
index d84d6fe132..d0cbf45526 100644
--- a/src/hooks/useFetchListCallback.ts
+++ b/src/hooks/useFetchListCallback.ts
@@ -1,13 +1,13 @@
import { nanoid } from '@reduxjs/toolkit'
import { TokenList } from '@uniswap/token-lists'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import getTokenList from 'lib/hooks/useTokenList/fetchTokenList'
+import resolveENSContentHash from 'lib/utils/resolveENSContentHash'
import { useCallback } from 'react'
import { useAppDispatch } from 'state/hooks'
import { getNetworkLibrary } from 'connectors'
import { fetchTokenList } from '@src/state/lists/actions'
-import getTokenList from '../utils/getTokenList'
-import resolveENSContentHash from '../utils/resolveENSContentHash'
-import { useActiveWeb3React } from './web3'
export function useFetchListCallback(): (listUrl: string, sendDispatch?: boolean) => Promise {
const { chainId, library } = useActiveWeb3React()
diff --git a/src/hooks/useGasPrice.ts b/src/hooks/useGasPrice.ts
new file mode 100644
index 0000000000..81cb33dcd0
--- /dev/null
+++ b/src/hooks/useGasPrice.ts
@@ -0,0 +1,26 @@
+import JSBI from 'jsbi'
+import { useSingleCallResult } from 'lib/hooks/multicall'
+
+import { useContract } from './useContract'
+import useENSAddress from './useENSAddress'
+
+const CHAIN_DATA_ABI = [
+ {
+ inputs: [],
+ name: 'latestAnswer',
+ outputs: [{ internalType: 'int256', name: '', type: 'int256' }],
+ stateMutability: 'view',
+ type: 'function',
+ },
+]
+
+/**
+ * Returns the price of 1 gas in WEI for the currently selected network using the chainlink fast gas price oracle
+ */
+export default function useGasPrice(): JSBI | undefined {
+ const { address } = useENSAddress('fast-gas-gwei.data.eth')
+ const contract = useContract(address ?? undefined, CHAIN_DATA_ABI, false)
+
+ const resultStr = useSingleCallResult(contract, 'latestAnswer').result?.[0]?.toString()
+ return typeof resultStr === 'string' ? JSBI.BigInt(resultStr) : undefined
+}
diff --git a/src/hooks/useHttpLocations.ts b/src/hooks/useHttpLocations.ts
index 37c49b7b82..82e0d16bb2 100644
--- a/src/hooks/useHttpLocations.ts
+++ b/src/hooks/useHttpLocations.ts
@@ -1,8 +1,8 @@
+import contenthashToUri from 'lib/utils/contenthashToUri'
+import parseENSAddress from 'lib/utils/parseENSAddress'
+import uriToHttp from 'lib/utils/uriToHttp'
import { useMemo } from 'react'
-import contenthashToUri from '../utils/contenthashToUri'
-import { parseENSAddress } from '../utils/parseENSAddress'
-import uriToHttp from '../utils/uriToHttp'
import useENSContentHash from './useENSContentHash'
export default function useHttpLocations(uri: string | undefined): string[] {
diff --git a/src/hooks/useIsArgentWallet.ts b/src/hooks/useIsArgentWallet.ts
index 8e34375a7d..626fa9f48d 100644
--- a/src/hooks/useIsArgentWallet.ts
+++ b/src/hooks/useIsArgentWallet.ts
@@ -1,8 +1,8 @@
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall'
import { useMemo } from 'react'
-import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
import { useArgentWalletDetectorContract } from './useContract'
-import { useActiveWeb3React } from './web3'
export default function useIsArgentWallet(): boolean {
const { account } = useActiveWeb3React()
diff --git a/src/hooks/useIsSwapUnsupported.ts b/src/hooks/useIsSwapUnsupported.ts
index e7fe192acd..199366eb52 100644
--- a/src/hooks/useIsSwapUnsupported.ts
+++ b/src/hooks/useIsSwapUnsupported.ts
@@ -1,4 +1,4 @@
-import { Currency, Token } from '@uniswap/sdk-core'
+import { Currency } from '@uniswap/sdk-core'
import { useMemo } from 'react'
import { useUnsupportedTokens } from 'hooks/Tokens'
@@ -9,14 +9,13 @@ import { useUnsupportedTokens } from 'hooks/Tokens'
* @param currencyOut the output currency to check
*/
export function useIsSwapUnsupported(currencyIn?: Currency | null, currencyOut?: Currency | null): boolean {
- const unsupportedTokens: { [address: string]: Token } = useUnsupportedTokens()
-
+ const unsupportedTokens = useUnsupportedTokens()
return useMemo(() => {
- // if unsupported list loaded & either token on list, mark as unsupported
- return Boolean(
- unsupportedTokens &&
- ((currencyIn?.isToken && unsupportedTokens[currencyIn.address]) ||
- (currencyOut?.isToken && unsupportedTokens[currencyOut.address]))
- )
+ if (!unsupportedTokens) {
+ return false
+ }
+ const currencyInUnsupported = Boolean(currencyIn?.isToken && unsupportedTokens[currencyIn.address])
+ const currencyOutUnsupported = Boolean(currencyOut?.isToken && unsupportedTokens[currencyOut.address])
+ return currencyInUnsupported || currencyOutUnsupported
}, [currencyIn, currencyOut, unsupportedTokens])
}
diff --git a/src/hooks/useIsWindowVisible.ts b/src/hooks/useIsWindowVisible.ts
index efefad5452..289b4d6729 100644
--- a/src/hooks/useIsWindowVisible.ts
+++ b/src/hooks/useIsWindowVisible.ts
@@ -1,9 +1,11 @@
import { useCallback, useEffect, useState } from 'react'
-const VISIBILITY_STATE_SUPPORTED = 'visibilityState' in document
+function isVisibilityStateSupported() {
+ return 'visibilityState' in document
+}
function isWindowVisible() {
- return !VISIBILITY_STATE_SUPPORTED || document.visibilityState !== 'hidden'
+ return !isVisibilityStateSupported() || document.visibilityState !== 'hidden'
}
/**
@@ -16,7 +18,7 @@ export default function useIsWindowVisible(): boolean {
}, [setFocused])
useEffect(() => {
- if (!VISIBILITY_STATE_SUPPORTED) return undefined
+ if (!isVisibilityStateSupported()) return undefined
document.addEventListener('visibilitychange', listener)
return () => {
diff --git a/src/hooks/useMachineTime.ts b/src/hooks/useMachineTime.ts
new file mode 100644
index 0000000000..2b717e1509
--- /dev/null
+++ b/src/hooks/useMachineTime.ts
@@ -0,0 +1,13 @@
+import useInterval from 'lib/hooks/useInterval'
+import { useState } from 'react'
+
+const useMachineTimeMs = (updateInterval: number): number => {
+ const [now, setNow] = useState(Date.now())
+
+ useInterval(() => {
+ setNow(Date.now())
+ }, updateInterval)
+ return now
+}
+
+export default useMachineTimeMs
diff --git a/src/hooks/useMonitoringEventCallback.ts b/src/hooks/useMonitoringEventCallback.ts
index 87296f6fcf..86c10abee0 100644
--- a/src/hooks/useMonitoringEventCallback.ts
+++ b/src/hooks/useMonitoringEventCallback.ts
@@ -1,11 +1,10 @@
import { TransactionResponse } from '@ethersproject/providers'
import { initializeApp } from 'firebase/app'
import { getDatabase, push, ref } from 'firebase/database'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useCallback } from 'react'
import { TransactionInfo, TransactionType } from 'state/transactions/actions'
-import { useActiveWeb3React } from './web3'
-
type PartialTransactionResponse = Pick
const SUPPORTED_TRANSACTION_TYPES = [
@@ -42,7 +41,7 @@ function useMonitoringEventCallback() {
try {
push(ref(db, 'trm'), {
chainId,
- origin: location.origin,
+ origin: window.location.origin,
timestamp: Date.now(),
tx: transactionResponse,
type,
diff --git a/src/hooks/usePoolTickData.ts b/src/hooks/usePoolTickData.ts
index 334c95f00d..e47ab08399 100644
--- a/src/hooks/usePoolTickData.ts
+++ b/src/hooks/usePoolTickData.ts
@@ -1,50 +1,176 @@
import { skipToken } from '@reduxjs/toolkit/query/react'
import { Currency } from '@uniswap/sdk-core'
-import { FeeAmount, Pool, TICK_SPACINGS, tickToPrice } from '@uniswap/v3-sdk'
+import { ChainId } from '@uniswap/smart-order-router'
+import { FeeAmount, nearestUsableTick, Pool, TICK_SPACINGS, tickToPrice } from '@uniswap/v3-sdk'
+import { ZERO_ADDRESS } from 'constants/misc'
import JSBI from 'jsbi'
+import { useSingleContractMultipleData } from 'lib/hooks/multicall'
import ms from 'ms.macro'
-import { useMemo } from 'react'
+import { useEffect, useMemo, useState } from 'react'
import { useAllV3TicksQuery } from 'state/data/enhanced'
-import { AllV3TicksQuery } from 'state/data/generated'
import computeSurroundingTicks from 'utils/computeSurroundingTicks'
+import { useTickLens } from './useContract'
import { PoolState, usePool } from './usePools'
const PRICE_FIXED_DIGITS = 8
+const CHAIN_IDS_MISSING_SUBGRAPH_DATA = [ChainId.ARBITRUM_ONE, ChainId.ARBITRUM_RINKEBY]
+
+export interface TickData {
+ tick: number
+ liquidityNet: JSBI
+ liquidityGross: JSBI
+}
// Tick with fields parsed to JSBIs, and active liquidity computed.
export interface TickProcessed {
- tickIdx: number
+ tick: number
liquidityActive: JSBI
liquidityNet: JSBI
price0: string
}
+const REFRESH_FREQUENCY = { blocksPerFetch: 2 }
+
const getActiveTick = (tickCurrent: number | undefined, feeAmount: FeeAmount | undefined) =>
tickCurrent && feeAmount ? Math.floor(tickCurrent / TICK_SPACINGS[feeAmount]) * TICK_SPACINGS[feeAmount] : undefined
-// Fetches all ticks for a given pool
-export function useAllV3Ticks(
+const bitmapIndex = (tick: number, tickSpacing: number) => {
+ return Math.floor(tick / tickSpacing / 256)
+}
+
+function useTicksFromTickLens(
currencyA: Currency | undefined,
currencyB: Currency | undefined,
- feeAmount: FeeAmount | undefined
+ feeAmount: FeeAmount | undefined,
+ numSurroundingTicks: number | undefined = 125
) {
+ const [tickDataLatestSynced, setTickDataLatestSynced] = useState([])
+
+ const [poolState, pool] = usePool(currencyA, currencyB, feeAmount)
+
+ const tickSpacing = feeAmount && TICK_SPACINGS[feeAmount]
+
+ // Find nearest valid tick for pool in case tick is not initialized.
+ const activeTick = pool?.tickCurrent && tickSpacing ? nearestUsableTick(pool?.tickCurrent, tickSpacing) : undefined
+
const poolAddress =
- currencyA && currencyB && feeAmount ? Pool.getAddress(currencyA?.wrapped, currencyB?.wrapped, feeAmount) : undefined
+ currencyA && currencyB && feeAmount && poolState === PoolState.EXISTS
+ ? Pool.getAddress(currencyA?.wrapped, currencyB?.wrapped, feeAmount)
+ : undefined
+
+ // it is also possible to grab all tick data but it is extremely slow
+ // bitmapIndex(nearestUsableTick(TickMath.MIN_TICK, tickSpacing), tickSpacing)
+ const minIndex = useMemo(
+ () =>
+ tickSpacing && activeTick ? bitmapIndex(activeTick - numSurroundingTicks * tickSpacing, tickSpacing) : undefined,
+ [tickSpacing, activeTick, numSurroundingTicks]
+ )
+
+ const maxIndex = useMemo(
+ () =>
+ tickSpacing && activeTick ? bitmapIndex(activeTick + numSurroundingTicks * tickSpacing, tickSpacing) : undefined,
+ [tickSpacing, activeTick, numSurroundingTicks]
+ )
+
+ const tickLensArgs: [string, number][] = useMemo(
+ () =>
+ maxIndex && minIndex && poolAddress && poolAddress !== ZERO_ADDRESS
+ ? new Array(maxIndex - minIndex + 1)
+ .fill(0)
+ .map((_, i) => i + minIndex)
+ .map((wordIndex) => [poolAddress, wordIndex])
+ : [],
+ [minIndex, maxIndex, poolAddress]
+ )
+
+ const tickLens = useTickLens()
+ const callStates = useSingleContractMultipleData(
+ tickLensArgs.length > 0 ? tickLens : undefined,
+ 'getPopulatedTicksInWord',
+ tickLensArgs,
+ REFRESH_FREQUENCY
+ )
+
+ const isError = useMemo(() => callStates.some(({ error }) => error), [callStates])
+ const isLoading = useMemo(() => callStates.some(({ loading }) => loading), [callStates])
+ const IsSyncing = useMemo(() => callStates.some(({ syncing }) => syncing), [callStates])
+ const isValid = useMemo(() => callStates.some(({ valid }) => valid), [callStates])
+
+ const tickData: TickData[] = useMemo(
+ () =>
+ callStates
+ .map(({ result }) => result?.populatedTicks)
+ .reduce(
+ (accumulator, current) => [
+ ...accumulator,
+ ...(current?.map((tickData: TickData) => {
+ return {
+ tick: tickData.tick,
+ liquidityNet: JSBI.BigInt(tickData.liquidityNet),
+ liquidityGross: JSBI.BigInt(tickData.liquidityGross),
+ }
+ }) ?? []),
+ ],
+ []
+ ),
+ [callStates]
+ )
+
+ // reset on input change
+ useEffect(() => {
+ setTickDataLatestSynced([])
+ }, [currencyA, currencyB, feeAmount])
- const { isLoading, isError, error, isUninitialized, data } = useAllV3TicksQuery(
- poolAddress ? { poolAddress: poolAddress?.toLowerCase(), skip: 0 } : skipToken,
- {
- pollingInterval: ms`30s`,
+ // return the latest synced tickData even if we are still loading the newest data
+ useEffect(() => {
+ if (!IsSyncing && !isLoading && !isError && isValid) {
+ setTickDataLatestSynced(tickData.sort((a, b) => a.tick - b.tick))
}
+ }, [isError, isLoading, IsSyncing, tickData, isValid])
+
+ return useMemo(
+ () => ({ isLoading, IsSyncing, isError, isValid, tickData: tickDataLatestSynced }),
+ [isLoading, IsSyncing, isError, isValid, tickDataLatestSynced]
)
+}
+
+function useTicksFromSubgraph(
+ currencyA: Currency | undefined,
+ currencyB: Currency | undefined,
+ feeAmount: FeeAmount | undefined
+) {
+ const poolAddress =
+ currencyA && currencyB && feeAmount ? Pool.getAddress(currencyA?.wrapped, currencyB?.wrapped, feeAmount) : undefined
+
+ return useAllV3TicksQuery(poolAddress ? { poolAddress: poolAddress?.toLowerCase(), skip: 0 } : skipToken, {
+ pollingInterval: ms`30s`,
+ })
+}
+
+// Fetches all ticks for a given pool
+function useAllV3Ticks(
+ currencyA: Currency | undefined,
+ currencyB: Currency | undefined,
+ feeAmount: FeeAmount | undefined
+): {
+ isLoading: boolean
+ isUninitialized: boolean
+ isError: boolean
+ error: unknown
+ ticks: TickData[] | undefined
+} {
+ const useSubgraph = currencyA ? !CHAIN_IDS_MISSING_SUBGRAPH_DATA.includes(currencyA.chainId) : true
+
+ const tickLensTickData = useTicksFromTickLens(!useSubgraph ? currencyA : undefined, currencyB, feeAmount)
+ const subgraphTickData = useTicksFromSubgraph(useSubgraph ? currencyA : undefined, currencyB, feeAmount)
return {
- isLoading,
- isUninitialized,
- isError,
- error,
- ticks: data?.ticks as AllV3TicksQuery['ticks'],
+ isLoading: useSubgraph ? subgraphTickData.isLoading : tickLensTickData.isLoading,
+ isUninitialized: useSubgraph ? subgraphTickData.isUninitialized : false,
+ isError: useSubgraph ? subgraphTickData.isError : tickLensTickData.isError,
+ error: useSubgraph ? subgraphTickData.error : tickLensTickData.isError,
+ ticks: useSubgraph ? subgraphTickData.data?.ticks : tickLensTickData.tickData,
}
}
@@ -94,7 +220,7 @@ export function usePoolActiveLiquidity(
// find where the active tick would be to partition the array
// if the active tick is initialized, the pivot will be an element
// if not, take the previous tick as pivot
- const pivot = ticks.findIndex(({ tickIdx }) => tickIdx > activeTick) - 1
+ const pivot = ticks.findIndex(({ tick }) => tick > activeTick) - 1
if (pivot < 0) {
// consider setting a local error
@@ -111,9 +237,8 @@ export function usePoolActiveLiquidity(
const activeTickProcessed: TickProcessed = {
liquidityActive: JSBI.BigInt(pool[1]?.liquidity ?? 0),
- tickIdx: activeTick,
- liquidityNet:
- Number(ticks[pivot].tickIdx) === activeTick ? JSBI.BigInt(ticks[pivot].liquidityNet) : JSBI.BigInt(0),
+ tick: activeTick,
+ liquidityNet: Number(ticks[pivot].tick) === activeTick ? JSBI.BigInt(ticks[pivot].liquidityNet) : JSBI.BigInt(0),
price0: tickToPrice(token0, token1, activeTick).toFixed(PRICE_FIXED_DIGITS),
}
diff --git a/src/hooks/usePools.ts b/src/hooks/usePools.ts
index d179b1ac54..1b338a2ac7 100644
--- a/src/hooks/usePools.ts
+++ b/src/hooks/usePools.ts
@@ -1,14 +1,16 @@
import { Interface } from '@ethersproject/abi'
import { Currency, Token } from '@uniswap/sdk-core'
-import { abi as IUniswapV3PoolStateABI } from '@uniswap/v3-core/artifacts/contracts/interfaces/pool/IUniswapV3PoolState.sol/IUniswapV3PoolState.json'
+import IUniswapV3PoolStateJson from '@uniswap/v3-core/artifacts/contracts/interfaces/pool/IUniswapV3PoolState.sol/IUniswapV3PoolState.json'
import { computePoolAddress } from '@uniswap/v3-sdk'
import { FeeAmount, Pool } from '@uniswap/v3-sdk'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { useMultipleContractSingleData } from 'lib/hooks/multicall'
import { useMemo } from 'react'
import { V3_CORE_FACTORY_ADDRESSES } from '../constants/addresses'
-import { useMultipleContractSingleData } from '../state/multicall/hooks'
import { IUniswapV3PoolStateInterface } from '../types/v3/IUniswapV3PoolState'
-import { useActiveWeb3React } from './web3'
+
+const { abi: IUniswapV3PoolStateABI } = IUniswapV3PoolStateJson
const POOL_STATE_INTERFACE = new Interface(IUniswapV3PoolStateABI) as IUniswapV3PoolStateInterface
diff --git a/src/hooks/usePositionTokenURI.ts b/src/hooks/usePositionTokenURI.ts
index 2f42eedf8a..4056d88c87 100644
--- a/src/hooks/usePositionTokenURI.ts
+++ b/src/hooks/usePositionTokenURI.ts
@@ -1,8 +1,8 @@
import { BigNumber } from '@ethersproject/bignumber'
import JSBI from 'jsbi'
+import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall'
import { useMemo } from 'react'
-import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
import { useV3NFTPositionManagerContract } from './useContract'
type TokenId = number | JSBI | BigNumber
diff --git a/src/hooks/useRouter.ts b/src/hooks/useRouter.ts
deleted file mode 100644
index 08aa614700..0000000000
--- a/src/hooks/useRouter.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { skipToken } from '@reduxjs/toolkit/query/react'
-import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
-import ms from 'ms.macro'
-import { useBlockNumber } from 'state/application/hooks'
-import { useGetQuoteQuery } from 'state/routing/slice'
-import { useActiveWeb3React } from './web3'
-
-export function useRouterTradeExactIn(amountIn?: CurrencyAmount, currencyOut?: Currency) {
- const { account } = useActiveWeb3React()
-
- const blockNumber = useBlockNumber()
-
- const { isLoading, isError, data } = useGetQuoteQuery(
- amountIn && currencyOut && account && blockNumber
- ? {
- tokenInAddress: amountIn.currency.wrapped.address,
- tokenInChainId: amountIn.currency.chainId,
- tokenOutAddress: currencyOut.wrapped.address,
- tokenOutChainId: currencyOut.chainId,
- amount: amountIn.quotient.toString(),
- type: 'exactIn',
- }
- : skipToken,
- { pollingInterval: ms`10s` }
- )
-
- // todo(judo): validate block number for freshness
-
- return !isLoading && !isError ? data?.routeString : undefined
-}
diff --git a/src/hooks/useSocksBalance.ts b/src/hooks/useSocksBalance.ts
index 87f87c1dc3..4c06522cac 100644
--- a/src/hooks/useSocksBalance.ts
+++ b/src/hooks/useSocksBalance.ts
@@ -1,11 +1,10 @@
import { Token } from '@uniswap/sdk-core'
import { SOCKS_CONTROLLER_ADDRESSES } from 'constants/addresses'
import { SupportedChainId } from 'constants/chains'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useMemo } from 'react'
import { useTokenBalance } from 'state/wallet/hooks'
-import { useActiveWeb3React } from './web3'
-
// technically a 721, not an ERC20, but suffices for our purposes
const SOCKS = new Token(SupportedChainId.MAINNET, SOCKS_CONTROLLER_ADDRESSES[SupportedChainId.MAINNET], 0)
diff --git a/src/hooks/useSwapCallArguments.tsx b/src/hooks/useSwapCallArguments.tsx
new file mode 100644
index 0000000000..efe0458899
--- /dev/null
+++ b/src/hooks/useSwapCallArguments.tsx
@@ -0,0 +1,184 @@
+import { BigNumber } from '@ethersproject/bignumber'
+import { SwapRouter, Trade } from '@uniswap/router-sdk'
+import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
+import { Router as V2SwapRouter, Trade as V2Trade } from '@uniswap/v2-sdk'
+import { FeeOptions, SwapRouter as V3SwapRouter, Trade as V3Trade } from '@uniswap/v3-sdk'
+import { SWAP_ROUTER_ADDRESSES, V3_ROUTER_ADDRESS } from 'constants/addresses'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { useMemo } from 'react'
+import approveAmountCalldata from 'utils/approveAmountCalldata'
+
+import { useArgentWalletContract } from './useArgentWalletContract'
+import { useV2RouterContract } from './useContract'
+import useENS from './useENS'
+import { SignatureData } from './useERC20Permit'
+
+export type AnyTrade =
+ | V2Trade
+ | V3Trade
+ | Trade
+
+interface SwapCall {
+ address: string
+ calldata: string
+ value: string
+}
+
+/**
+ * Returns the swap calls that can be used to make the trade
+ * @param trade trade to execute
+ * @param allowedSlippage user allowed slippage
+ * @param recipientAddressOrName the ENS name or address of the recipient of the swap output
+ * @param signatureData the signature data of the permit of the input token amount, if available
+ */
+export function useSwapCallArguments(
+ trade: AnyTrade | undefined,
+ allowedSlippage: Percent,
+ recipientAddressOrName: string | null | undefined,
+ signatureData: SignatureData | null | undefined,
+ deadline: BigNumber | undefined,
+ feeOptions: FeeOptions | undefined
+): SwapCall[] {
+ const { account, chainId, library } = useActiveWeb3React()
+
+ const { address: recipientAddress } = useENS(recipientAddressOrName)
+ const recipient = recipientAddressOrName === null ? account : recipientAddress
+ const routerContract = useV2RouterContract()
+ const argentWalletContract = useArgentWalletContract()
+
+ return useMemo(() => {
+ if (!trade || !recipient || !library || !account || !chainId || !deadline) return []
+
+ if (trade instanceof V2Trade) {
+ if (!routerContract) return []
+ const swapMethods = []
+
+ swapMethods.push(
+ V2SwapRouter.swapCallParameters(trade, {
+ feeOnTransfer: false,
+ allowedSlippage,
+ recipient,
+ deadline: deadline.toNumber(),
+ })
+ )
+
+ if (trade.tradeType === TradeType.EXACT_INPUT) {
+ swapMethods.push(
+ V2SwapRouter.swapCallParameters(trade, {
+ feeOnTransfer: true,
+ allowedSlippage,
+ recipient,
+ deadline: deadline.toNumber(),
+ })
+ )
+ }
+ return swapMethods.map(({ methodName, args, value }) => {
+ if (argentWalletContract && trade.inputAmount.currency.isToken) {
+ return {
+ address: argentWalletContract.address,
+ calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
+ [
+ approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), routerContract.address),
+ {
+ to: routerContract.address,
+ value,
+ data: routerContract.interface.encodeFunctionData(methodName, args),
+ },
+ ],
+ ]),
+ value: '0x0',
+ }
+ } else {
+ return {
+ address: routerContract.address,
+ calldata: routerContract.interface.encodeFunctionData(methodName, args),
+ value,
+ }
+ }
+ })
+ } else {
+ // swap options shared by v3 and v2+v3 swap routers
+ const sharedSwapOptions = {
+ fee: feeOptions,
+ recipient,
+ slippageTolerance: allowedSlippage,
+ ...(signatureData
+ ? {
+ inputTokenPermit:
+ 'allowed' in signatureData
+ ? {
+ expiry: signatureData.deadline,
+ nonce: signatureData.nonce,
+ s: signatureData.s,
+ r: signatureData.r,
+ v: signatureData.v as any,
+ }
+ : {
+ deadline: signatureData.deadline,
+ amount: signatureData.amount,
+ s: signatureData.s,
+ r: signatureData.r,
+ v: signatureData.v as any,
+ },
+ }
+ : {}),
+ }
+
+ const swapRouterAddress = chainId
+ ? trade instanceof V3Trade
+ ? V3_ROUTER_ADDRESS[chainId]
+ : SWAP_ROUTER_ADDRESSES[chainId]
+ : undefined
+ if (!swapRouterAddress) return []
+
+ const { value, calldata } =
+ trade instanceof V3Trade
+ ? V3SwapRouter.swapCallParameters(trade, {
+ ...sharedSwapOptions,
+ deadline: deadline.toString(),
+ })
+ : SwapRouter.swapCallParameters(trade, {
+ ...sharedSwapOptions,
+ deadlineOrPreviousBlockhash: deadline.toString(),
+ })
+
+ if (argentWalletContract && trade.inputAmount.currency.isToken) {
+ return [
+ {
+ address: argentWalletContract.address,
+ calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
+ [
+ approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), swapRouterAddress),
+ {
+ to: swapRouterAddress,
+ value,
+ data: calldata,
+ },
+ ],
+ ]),
+ value: '0x0',
+ },
+ ]
+ }
+ return [
+ {
+ address: swapRouterAddress,
+ calldata,
+ value,
+ },
+ ]
+ }
+ }, [
+ account,
+ allowedSlippage,
+ argentWalletContract,
+ chainId,
+ deadline,
+ feeOptions,
+ library,
+ recipient,
+ routerContract,
+ signatureData,
+ trade,
+ ])
+}
diff --git a/src/hooks/useSwapCallback.tsx b/src/hooks/useSwapCallback.tsx
index 1f24f3d8ad..16dbcfd425 100644
--- a/src/hooks/useSwapCallback.tsx
+++ b/src/hooks/useSwapCallback.tsx
@@ -1,412 +1,75 @@
-import { BigNumber } from '@ethersproject/bignumber'
// eslint-disable-next-line no-restricted-imports
-import { t, Trans } from '@lingui/macro'
-import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
-import { Router, Trade as V2Trade } from '@uniswap/v2-sdk'
-import { SwapRouter, Trade as V3Trade } from '@uniswap/v3-sdk'
+import { Percent, TradeType } from '@uniswap/sdk-core'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import { SwapCallbackState, useSwapCallback as useLibSwapCallBack } from 'lib/hooks/swap/useSwapCallback'
import { ReactNode, useMemo } from 'react'
-import { SWAP_ROUTER_ADDRESSES } from '../constants/addresses'
import { TransactionType } from '../state/transactions/actions'
import { useTransactionAdder } from '../state/transactions/hooks'
-import approveAmountCalldata from '../utils/approveAmountCalldata'
-import { calculateGasMargin } from '../utils/calculateGasMargin'
import { currencyId } from '../utils/currencyId'
-import isZero from '../utils/isZero'
-import { useArgentWalletContract } from './useArgentWalletContract'
-import { useV2RouterContract } from './useContract'
import useENS from './useENS'
import { SignatureData } from './useERC20Permit'
+import { AnyTrade } from './useSwapCallArguments'
import useTransactionDeadline from './useTransactionDeadline'
-import { useActiveWeb3React } from './web3'
-
-export enum SwapCallbackState {
- INVALID,
- LOADING,
- VALID,
-}
-
-interface SwapCall {
- address: string
- calldata: string
- value: string
-}
-
-interface SwapCallEstimate {
- call: SwapCall
-}
-
-interface SuccessfulCall extends SwapCallEstimate {
- call: SwapCall
- gasEstimate: BigNumber
-}
-
-interface FailedCall extends SwapCallEstimate {
- call: SwapCall
- error: Error
-}
-
-/**
- * Returns the swap calls that can be used to make the trade
- * @param trade trade to execute
- * @param allowedSlippage user allowed slippage
- * @param recipientAddressOrName the ENS name or address of the recipient of the swap output
- * @param signatureData the signature data of the permit of the input token amount, if available
- */
-function useSwapCallArguments(
- trade: V2Trade | V3Trade | undefined, // trade to execute, required
- allowedSlippage: Percent, // in bips
- recipientAddressOrName: string | null, // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
- signatureData: SignatureData | null | undefined
-): SwapCall[] {
- const { account, chainId, library } = useActiveWeb3React()
-
- const { address: recipientAddress } = useENS(recipientAddressOrName)
- const recipient = recipientAddressOrName === null ? account : recipientAddress
- const deadline = useTransactionDeadline()
- const routerContract = useV2RouterContract()
- const argentWalletContract = useArgentWalletContract()
-
- return useMemo(() => {
- if (!trade || !recipient || !library || !account || !chainId || !deadline) return []
-
- if (trade instanceof V2Trade) {
- if (!routerContract) return []
- const swapMethods = []
-
- swapMethods.push(
- Router.swapCallParameters(trade, {
- feeOnTransfer: false,
- allowedSlippage,
- recipient,
- deadline: deadline.toNumber(),
- })
- )
-
- if (trade.tradeType === TradeType.EXACT_INPUT) {
- swapMethods.push(
- Router.swapCallParameters(trade, {
- feeOnTransfer: true,
- allowedSlippage,
- recipient,
- deadline: deadline.toNumber(),
- })
- )
- }
- return swapMethods.map(({ methodName, args, value }) => {
- if (argentWalletContract && trade.inputAmount.currency.isToken) {
- return {
- address: argentWalletContract.address,
- calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
- [
- approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), routerContract.address),
- {
- to: routerContract.address,
- value,
- data: routerContract.interface.encodeFunctionData(methodName, args),
- },
- ],
- ]),
- value: '0x0',
- }
- } else {
- return {
- address: routerContract.address,
- calldata: routerContract.interface.encodeFunctionData(methodName, args),
- value,
- }
- }
- })
- } else {
- // trade is V3Trade
- const swapRouterAddress = chainId ? SWAP_ROUTER_ADDRESSES[chainId] : undefined
- if (!swapRouterAddress) return []
-
- const { value, calldata } = SwapRouter.swapCallParameters(trade, {
- recipient,
- slippageTolerance: allowedSlippage,
- deadline: deadline.toString(),
- ...(signatureData
- ? {
- inputTokenPermit:
- 'allowed' in signatureData
- ? {
- expiry: signatureData.deadline,
- nonce: signatureData.nonce,
- s: signatureData.s,
- r: signatureData.r,
- v: signatureData.v as any,
- }
- : {
- deadline: signatureData.deadline,
- amount: signatureData.amount,
- s: signatureData.s,
- r: signatureData.r,
- v: signatureData.v as any,
- },
- }
- : {}),
- })
- if (argentWalletContract && trade.inputAmount.currency.isToken) {
- return [
- {
- address: argentWalletContract.address,
- calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
- [
- approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), swapRouterAddress),
- {
- to: swapRouterAddress,
- value,
- data: calldata,
- },
- ],
- ]),
- value: '0x0',
- },
- ]
- }
- return [
- {
- address: swapRouterAddress,
- calldata,
- value,
- },
- ]
- }
- }, [
- account,
- allowedSlippage,
- argentWalletContract,
- chainId,
- deadline,
- library,
- recipient,
- routerContract,
- signatureData,
- trade,
- ])
-}
-
-/**
- * This is hacking out the revert reason from the ethers provider thrown error however it can.
- * This object seems to be undocumented by ethers.
- * @param error an error from the ethers provider
- */
-function swapErrorToUserReadableMessage(error: any): ReactNode {
- let reason: string | undefined
- while (Boolean(error)) {
- reason = error.reason ?? error.message ?? reason
- error = error.error ?? error.data?.originalError
- }
-
- if (reason?.indexOf('execution reverted: ') === 0) reason = reason.substr('execution reverted: '.length)
-
- switch (reason) {
- case 'UniswapV2Router: EXPIRED':
- return (
-
- The transaction could not be sent because the deadline has passed. Please check that your transaction deadline
- is not too low.
-
- )
- case 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT':
- case 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT':
- return (
-
- This transaction will not succeed either due to price movement or fee on transfer. Try increasing your
- slippage tolerance.
-
- )
- case 'TransferHelper: TRANSFER_FROM_FAILED':
- return The input token cannot be transferred. There may be an issue with the input token.
- case 'UniswapV2: TRANSFER_FAILED':
- return The output token cannot be transferred. There may be an issue with the output token.
- case 'UniswapV2: K':
- return (
-
- The Uniswap invariant x*y=k was not satisfied by the swap. This usually means one of the tokens you are
- swapping incorporates custom behavior on transfer.
-
- )
- case 'Too little received':
- case 'Too much requested':
- case 'STF':
- return (
-
- This transaction will not succeed due to price movement. Try increasing your slippage tolerance. Note: fee on
- transfer and rebase tokens are incompatible with Uniswap V3.
-
- )
- case 'TF':
- return (
-
- The output token cannot be transferred. There may be an issue with the output token. Note: fee on transfer and
- rebase tokens are incompatible with Uniswap V3.
-
- )
- default:
- if (reason?.indexOf('undefined is not an object') !== -1) {
- console.error(error, reason)
- return (
-
- An error occurred when trying to execute this swap. You may need to increase your slippage tolerance. If
- that does not work, there may be an incompatibility with the token you are trading. Note: fee on transfer
- and rebase tokens are incompatible with Uniswap V3.
-
- )
- }
- return (
-
- Unknown error{reason ? `: "${reason}"` : ''}. Try increasing your slippage tolerance. Note: fee on transfer
- and rebase tokens are incompatible with Uniswap V3.
-
- )
- }
-}
// returns a function that will execute a swap, if the parameters are all valid
// and the user has approved the slippage adjusted input amount for the trade
export function useSwapCallback(
- trade: V2Trade | V3Trade | undefined, // trade to execute, required
+ trade: AnyTrade | undefined, // trade to execute, required
allowedSlippage: Percent, // in bips
recipientAddressOrName: string | null, // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
signatureData: SignatureData | undefined | null
): { state: SwapCallbackState; callback: null | (() => Promise); error: ReactNode | null } {
- const { account, chainId, library } = useActiveWeb3React()
+ const { account } = useActiveWeb3React()
- const swapCalls = useSwapCallArguments(trade, allowedSlippage, recipientAddressOrName, signatureData)
+ const deadline = useTransactionDeadline()
const addTransaction = useTransactionAdder()
const { address: recipientAddress } = useENS(recipientAddressOrName)
const recipient = recipientAddressOrName === null ? account : recipientAddress
- return useMemo(() => {
- if (!trade || !library || !account || !chainId) {
- return { state: SwapCallbackState.INVALID, callback: null, error: Missing dependencies }
- }
- if (!recipient) {
- if (recipientAddressOrName !== null) {
- return { state: SwapCallbackState.INVALID, callback: null, error: Invalid recipient }
- } else {
- return { state: SwapCallbackState.LOADING, callback: null, error: null }
- }
- }
-
- return {
- state: SwapCallbackState.VALID,
- callback: async function onSwap(): Promise {
- const estimatedCalls: SwapCallEstimate[] = await Promise.all(
- swapCalls.map((call) => {
- const { address, calldata, value } = call
+ const {
+ state,
+ callback: libCallback,
+ error,
+ } = useLibSwapCallBack({ trade, allowedSlippage, recipientAddressOrName: recipient, signatureData, deadline })
- const tx =
- !value || isZero(value)
- ? { from: account, to: address, data: calldata }
- : {
- from: account,
- to: address,
- data: calldata,
- value,
- }
-
- return library
- .estimateGas(tx)
- .then((gasEstimate) => {
- return {
- call,
- gasEstimate,
- }
- })
- .catch((gasError) => {
- console.debug('Gas estimate failed, trying eth_call to extract error', call)
-
- return library
- .call(tx)
- .then((result) => {
- console.debug('Unexpected successful call after failed estimate gas', call, gasError, result)
- return { call, error: Unexpected issue with estimating the gas. Please try again. }
- })
- .catch((callError) => {
- console.debug('Call threw error', call, callError)
- return { call, error: swapErrorToUserReadableMessage(callError) }
- })
- })
- })
- )
-
- // a successful estimation is a bignumber gas estimate and the next call is also a bignumber gas estimate
- let bestCallOption: SuccessfulCall | SwapCallEstimate | undefined = estimatedCalls.find(
- (el, ix, list): el is SuccessfulCall =>
- 'gasEstimate' in el && (ix === list.length - 1 || 'gasEstimate' in list[ix + 1])
+ const callback = useMemo(() => {
+ if (!libCallback || !trade) {
+ return null
+ }
+ return () =>
+ libCallback().then((response) => {
+ addTransaction(
+ response,
+ trade.tradeType === TradeType.EXACT_INPUT
+ ? {
+ type: TransactionType.SWAP,
+ tradeType: TradeType.EXACT_INPUT,
+ inputCurrencyId: currencyId(trade.inputAmount.currency),
+ inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
+ expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
+ outputCurrencyId: currencyId(trade.outputAmount.currency),
+ minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(allowedSlippage).quotient.toString(),
+ }
+ : {
+ type: TransactionType.SWAP,
+ tradeType: TradeType.EXACT_OUTPUT,
+ inputCurrencyId: currencyId(trade.inputAmount.currency),
+ maximumInputCurrencyAmountRaw: trade.maximumAmountIn(allowedSlippage).quotient.toString(),
+ outputCurrencyId: currencyId(trade.outputAmount.currency),
+ outputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
+ expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
+ }
)
+ return response.hash
+ })
+ }, [addTransaction, allowedSlippage, libCallback, trade])
- // check if any calls errored with a recognizable error
- if (!bestCallOption) {
- const errorCalls = estimatedCalls.filter((call): call is FailedCall => 'error' in call)
- if (errorCalls.length > 0) throw errorCalls[errorCalls.length - 1].error
- const firstNoErrorCall = estimatedCalls.find(
- (call): call is SwapCallEstimate => !('error' in call)
- )
- if (!firstNoErrorCall) throw new Error(t`Unexpected error. Could not estimate gas for the swap.`)
- bestCallOption = firstNoErrorCall
- }
-
- const {
- call: { address, calldata, value },
- } = bestCallOption
-
- return library
- .getSigner()
- .sendTransaction({
- from: account,
- to: address,
- data: calldata,
- // let the wallet try if we can't estimate the gas
- ...('gasEstimate' in bestCallOption
- ? { gasLimit: calculateGasMargin(chainId, bestCallOption.gasEstimate) }
- : {}),
- ...(value && !isZero(value) ? { value } : {}),
- })
- .then((response) => {
- addTransaction(
- response,
- trade.tradeType === TradeType.EXACT_INPUT
- ? {
- type: TransactionType.SWAP,
- tradeType: TradeType.EXACT_INPUT,
- inputCurrencyId: currencyId(trade.inputAmount.currency),
- inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
- expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
- outputCurrencyId: currencyId(trade.outputAmount.currency),
- minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(allowedSlippage).quotient.toString(),
- }
- : {
- type: TransactionType.SWAP,
- tradeType: TradeType.EXACT_OUTPUT,
- inputCurrencyId: currencyId(trade.inputAmount.currency),
- maximumInputCurrencyAmountRaw: trade.maximumAmountIn(allowedSlippage).quotient.toString(),
- outputCurrencyId: currencyId(trade.outputAmount.currency),
- outputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
- expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
- }
- )
-
- return response.hash
- })
- .catch((error) => {
- // if the user rejected the tx, pass this along
- if (error?.code === 4001) {
- throw new Error(t`Transaction rejected.`)
- } else {
- // otherwise, the error was unexpected and we need to convey that
- console.error(`Swap failed`, error, address, calldata, value)
-
- throw new Error(t`Swap failed: ${swapErrorToUserReadableMessage(error)}`)
- }
- })
- },
- error: null,
- }
- }, [trade, library, account, chainId, recipient, recipientAddressOrName, swapCalls, addTransaction, allowedSlippage])
+ return {
+ state,
+ callback,
+ error,
+ }
}
diff --git a/src/hooks/useSwapSlippageTolerance.ts b/src/hooks/useSwapSlippageTolerance.ts
deleted file mode 100644
index 6405caff17..0000000000
--- a/src/hooks/useSwapSlippageTolerance.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
-import { Trade as V2Trade } from '@uniswap/v2-sdk'
-import { Trade as V3Trade } from '@uniswap/v3-sdk'
-import { L2_CHAIN_IDS } from '@src/constants/chains'
-import { useMemo } from 'react'
-
-import { useUserSlippageToleranceWithDefault } from '../state/user/hooks'
-import { useActiveWeb3React } from './web3'
-
-const V2_SWAP_DEFAULT_SLIPPAGE = new Percent(50, 10_000) // .50%
-const V3_SWAP_DEFAULT_SLIPPAGE = new Percent(50, 10_000) // .50%
-const ONE_TENTHS_PERCENT = new Percent(10, 10_000) // .10%
-
-export default function useSwapSlippageTolerance(
- trade: V2Trade | V3Trade | undefined
-): Percent {
- const { chainId } = useActiveWeb3React()
- const onL2 = chainId && L2_CHAIN_IDS.includes(chainId)
- const defaultSlippageTolerance = useMemo(() => {
- if (!trade || onL2) return ONE_TENTHS_PERCENT
- if (trade instanceof V2Trade) return V2_SWAP_DEFAULT_SLIPPAGE
- return V3_SWAP_DEFAULT_SLIPPAGE
- }, [onL2, trade])
- return useUserSlippageToleranceWithDefault(defaultSlippageTolerance)
-}
diff --git a/src/hooks/useToggledVersion.ts b/src/hooks/useToggledVersion.ts
deleted file mode 100644
index 71f3d5359e..0000000000
--- a/src/hooks/useToggledVersion.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import useParsedQueryString from './useParsedQueryString'
-
-export enum Version {
- v2 = 'V2',
- v3 = 'V3',
-}
-
-export default function useToggledVersion(): Version | undefined {
- const { use } = useParsedQueryString()
- if (typeof use !== 'string') {
- return undefined
- }
- switch (use.toLowerCase()) {
- case 'v2':
- return Version.v2
- case 'v3':
- return Version.v3
- default:
- return undefined
- }
-}
diff --git a/src/hooks/useTokenAllowance.ts b/src/hooks/useTokenAllowance.ts
index f1edb8dc28..90bf21bc8d 100644
--- a/src/hooks/useTokenAllowance.ts
+++ b/src/hooks/useTokenAllowance.ts
@@ -1,7 +1,7 @@
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
+import { useSingleCallResult } from 'lib/hooks/multicall'
import { useMemo } from 'react'
-import { useSingleCallResult } from '../state/multicall/hooks'
import { useTokenContract } from './useContract'
export function useTokenAllowance(token?: Token, owner?: string, spender?: string): CurrencyAmount | undefined {
diff --git a/src/hooks/useTokenInfoFromActiveList.ts b/src/hooks/useTokenInfoFromActiveList.ts
index dbd50d66ed..ad448c27a3 100644
--- a/src/hooks/useTokenInfoFromActiveList.ts
+++ b/src/hooks/useTokenInfoFromActiveList.ts
@@ -1,5 +1,5 @@
import { Currency } from '@uniswap/sdk-core'
-import { useActiveWeb3React } from 'hooks/web3'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useMemo } from 'react'
import { useCombinedActiveList } from 'state/lists/hooks'
diff --git a/src/hooks/useTotalSupply.ts b/src/hooks/useTotalSupply.ts
index 8d424d6d40..90b09a0cb5 100644
--- a/src/hooks/useTotalSupply.ts
+++ b/src/hooks/useTotalSupply.ts
@@ -1,7 +1,7 @@
import { BigNumber } from '@ethersproject/bignumber'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
+import { useSingleCallResult } from 'lib/hooks/multicall'
-import { useSingleCallResult } from '../state/multicall/hooks'
import { useTokenContract } from './useContract'
// returns undefined if input token is undefined, or fails to get token contract,
diff --git a/src/hooks/useTransactionDeadline.ts b/src/hooks/useTransactionDeadline.ts
index fcae17fc27..88fc655905 100644
--- a/src/hooks/useTransactionDeadline.ts
+++ b/src/hooks/useTransactionDeadline.ts
@@ -1,11 +1,11 @@
import { BigNumber } from '@ethersproject/bignumber'
import { L2_CHAIN_IDS } from '@src/constants/chains'
import { L2_DEADLINE_FROM_NOW } from 'constants/misc'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useMemo } from 'react'
import { useAppSelector } from 'state/hooks'
import useCurrentBlockTimestamp from './useCurrentBlockTimestamp'
-import { useActiveWeb3React } from './web3'
// combines the block timestamp with the user setting to give the deadline that should be used for any submitted transaction
export default function useTransactionDeadline(): BigNumber | undefined {
diff --git a/src/hooks/useUSDCPrice.ts b/src/hooks/useUSDCPrice.ts
index 7e1bc5c407..540f8e39b1 100644
--- a/src/hooks/useUSDCPrice.ts
+++ b/src/hooks/useUSDCPrice.ts
@@ -1,18 +1,20 @@
import { Currency, CurrencyAmount, Price, Token, TradeType } from '@uniswap/sdk-core'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useMemo } from 'react'
import { SupportedChainId } from '../constants/chains'
-import { DAI_OPTIMISM, USDC, USDC_ARBITRUM } from '../constants/tokens'
+import { DAI_OPTIMISM, USDC_ARBITRUM, USDC_MAINNET, USDC_POLYGON } from '../constants/tokens'
import { useBestV2Trade } from './useBestV2Trade'
import { useClientSideV3Trade } from './useClientSideV3Trade'
-import { useActiveWeb3React } from './web3'
// 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),
}
/**
@@ -20,11 +22,12 @@ export const STABLECOIN_AMOUNT_OUT: { [chainId: number]: CurrencyAmount }
* @param currency currency to compute the USDC price of
*/
export default function useUSDCPrice(currency?: Currency): Price | undefined {
- const { chainId } = useActiveWeb3React()
+ const chainId = currency?.chainId
const amountOut = chainId ? STABLECOIN_AMOUNT_OUT[chainId] : undefined
const stablecoin = amountOut?.currency
+ // TODO(#2808): remove dependency on useBestV2Trade
const v2USDCTrade = useBestV2Trade(TradeType.EXACT_OUTPUT, amountOut, currency, {
maxHops: 2,
})
@@ -45,7 +48,7 @@ export default function useUSDCPrice(currency?: Currency): Price | undefine
}
}, [currencyAmount, price])
}
+
+/**
+ *
+ * @param fiatValue string representation of a USD amount
+ * @returns CurrencyAmount where currency is stablecoin on active chain
+ */
+export function useStablecoinAmountFromFiatValue(fiatValue: string | null | undefined) {
+ const { chainId } = useActiveWeb3React()
+ const stablecoin = chainId ? STABLECOIN_AMOUNT_OUT[chainId]?.currency : undefined
+
+ if (fiatValue === null || fiatValue === undefined || !chainId || !stablecoin) {
+ return undefined
+ }
+
+ // trim for decimal precision when parsing
+ const parsedForDecimals = parseFloat(fiatValue).toFixed(stablecoin.decimals).toString()
+
+ try {
+ // parse USD string into CurrencyAmount based on stablecoin decimals
+ return tryParseCurrencyAmount(parsedForDecimals, stablecoin)
+ } catch (error) {
+ return undefined
+ }
+}
diff --git a/src/hooks/useV2LiquidityTokenPermit.ts b/src/hooks/useV2LiquidityTokenPermit.ts
new file mode 100644
index 0000000000..2fa9b257a2
--- /dev/null
+++ b/src/hooks/useV2LiquidityTokenPermit.ts
@@ -0,0 +1,18 @@
+import { CurrencyAmount, Token } from '@uniswap/sdk-core'
+
+import { PermitInfo, PermitType, useERC20Permit } from './useERC20Permit'
+import useTransactionDeadline from './useTransactionDeadline'
+
+const REMOVE_V2_LIQUIDITY_PERMIT_INFO: PermitInfo = {
+ version: '1',
+ name: 'Uniswap V2',
+ type: PermitType.AMOUNT,
+}
+
+export function useV2LiquidityTokenPermit(
+ liquidityAmount: CurrencyAmount | null | undefined,
+ spender: string | null | undefined
+) {
+ const transactionDeadline = useTransactionDeadline()
+ return useERC20Permit(liquidityAmount, spender, transactionDeadline, REMOVE_V2_LIQUIDITY_PERMIT_INFO)
+}
diff --git a/src/hooks/useV2Pairs.ts b/src/hooks/useV2Pairs.ts
index 946e134e2f..253c153a77 100644
--- a/src/hooks/useV2Pairs.ts
+++ b/src/hooks/useV2Pairs.ts
@@ -1,11 +1,13 @@
import { Interface } from '@ethersproject/abi'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
-import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
+import IUniswapV2PairJson from '@uniswap/v2-core/build/IUniswapV2Pair.json'
import { computePairAddress, Pair } from '@uniswap/v2-sdk'
+import { useMultipleContractSingleData } from 'lib/hooks/multicall'
import { useMemo } from 'react'
import { V2_FACTORY_ADDRESSES } from '../constants/addresses'
-import { useMultipleContractSingleData } from '../state/multicall/hooks'
+
+const { abi: IUniswapV2PairABI } = IUniswapV2PairJson
const PAIR_INTERFACE = new Interface(IUniswapV2PairABI)
diff --git a/src/hooks/useV3PositionFees.ts b/src/hooks/useV3PositionFees.ts
index 15f98965ea..d292793a0e 100644
--- a/src/hooks/useV3PositionFees.ts
+++ b/src/hooks/useV3PositionFees.ts
@@ -1,9 +1,9 @@
import { BigNumber } from '@ethersproject/bignumber'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
import { Pool } from '@uniswap/v3-sdk'
+import { useSingleCallResult } from 'lib/hooks/multicall'
+import useBlockNumber from 'lib/hooks/useBlockNumber'
import { useEffect, useState } from 'react'
-import { useBlockNumber } from 'state/application/hooks'
-import { useSingleCallResult } from 'state/multicall/hooks'
import { unwrappedToken } from 'utils/unwrappedToken'
import { useV3NFTPositionManagerContract } from './useContract'
diff --git a/src/hooks/useV3Positions.ts b/src/hooks/useV3Positions.ts
index 4f545104aa..b7d4f0415b 100644
--- a/src/hooks/useV3Positions.ts
+++ b/src/hooks/useV3Positions.ts
@@ -1,6 +1,6 @@
import { BigNumber } from '@ethersproject/bignumber'
+import { CallStateResult, useSingleCallResult, useSingleContractMultipleData } from 'lib/hooks/multicall'
import { useMemo } from 'react'
-import { Result, useSingleCallResult, useSingleContractMultipleData } from 'state/multicall/hooks'
import { PositionDetails } from 'types/position'
import { useV3NFTPositionManagerContract } from './useContract'
@@ -22,7 +22,7 @@ function useV3PositionsFromTokenIds(tokenIds: BigNumber[] | undefined): UseV3Pos
if (!loading && !error && tokenIds) {
return results.map((call, i) => {
const tokenId = tokenIds[i]
- const result = call.result as Result
+ const result = call.result as CallStateResult
return {
tokenId,
fee: result.fee,
@@ -90,7 +90,7 @@ export function useV3Positions(account: string | null | undefined): UseV3Positio
if (account) {
return tokenIdResults
.map(({ result }) => result)
- .filter((result): result is Result => !!result)
+ .filter((result): result is CallStateResult => !!result)
.map((result) => BigNumber.from(result[0]))
}
return []
diff --git a/src/hooks/useV3SwapPools.ts b/src/hooks/useV3SwapPools.ts
index 70a4bd5f41..e5040fd581 100644
--- a/src/hooks/useV3SwapPools.ts
+++ b/src/hooks/useV3SwapPools.ts
@@ -1,5 +1,7 @@
import { Currency, Token } from '@uniswap/sdk-core'
import { FeeAmount, Pool } from '@uniswap/v3-sdk'
+import { SupportedChainId } from 'constants/chains'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useMemo } from 'react'
import { useAllCurrencyCombinations } from './useAllCurrencyCombinations'
import { PoolState, usePools } from './usePools'
@@ -16,18 +18,27 @@ export function useV3SwapPools(
pools: Pool[]
loading: boolean
} {
+ const { chainId } = useActiveWeb3React()
+
const allCurrencyCombinations = useAllCurrencyCombinations(currencyIn, currencyOut)
const allCurrencyCombinationsWithAllFees: [Token, Token, FeeAmount][] = useMemo(
() =>
allCurrencyCombinations.reduce<[Token, Token, FeeAmount][]>((list, [tokenA, tokenB]) => {
- return list.concat([
- [tokenA, tokenB, FeeAmount.LOW],
- [tokenA, tokenB, FeeAmount.MEDIUM],
- [tokenA, tokenB, FeeAmount.HIGH],
- ])
+ return chainId === SupportedChainId.MAINNET
+ ? list.concat([
+ [tokenA, tokenB, FeeAmount.LOW],
+ [tokenA, tokenB, FeeAmount.MEDIUM],
+ [tokenA, tokenB, FeeAmount.HIGH],
+ ])
+ : list.concat([
+ [tokenA, tokenB, FeeAmount.LOWEST],
+ [tokenA, tokenB, FeeAmount.LOW],
+ [tokenA, tokenB, FeeAmount.MEDIUM],
+ [tokenA, tokenB, FeeAmount.HIGH],
+ ])
}, []),
- [allCurrencyCombinations]
+ [allCurrencyCombinations, chainId]
)
const pools = usePools(allCurrencyCombinationsWithAllFees)
diff --git a/src/hooks/useWrapCallback.ts b/src/hooks/useWrapCallback.tsx
similarity index 62%
rename from src/hooks/useWrapCallback.ts
rename to src/hooks/useWrapCallback.tsx
index 1ef68c10a1..e0b203f668 100644
--- a/src/hooks/useWrapCallback.ts
+++ b/src/hooks/useWrapCallback.tsx
@@ -1,12 +1,14 @@
+import { Trans } from '@lingui/macro'
import { Currency } from '@uniswap/sdk-core'
+import useActiveWeb3React from 'hooks/useActiveWeb3React'
+import useNativeCurrency from 'lib/hooks/useNativeCurrency'
+import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { useMemo } from 'react'
-import { WETH9_EXTENDED } from '../constants/tokens'
-import { tryParseAmount } from '../state/swap/hooks'
+import { WRAPPED_NATIVE_CURRENCY } from '../constants/tokens'
import { TransactionType } from '../state/transactions/actions'
import { useTransactionAdder } from '../state/transactions/hooks'
import { useCurrencyBalance } from '../state/wallet/hooks'
import { useWETHContract } from './useContract'
-import { useActiveWeb3React } from './web3'
export enum WrapType {
NOT_APPLICABLE,
@@ -15,6 +17,34 @@ export enum WrapType {
}
const NOT_APPLICABLE = { wrapType: WrapType.NOT_APPLICABLE }
+
+enum WrapInputError {
+ NO_ERROR, // must be equal to 0 so all other errors are truthy
+ ENTER_NATIVE_AMOUNT,
+ ENTER_WRAPPED_AMOUNT,
+ INSUFFICIENT_NATIVE_BALANCE,
+ INSUFFICIENT_WRAPPED_BALANCE,
+}
+
+export function WrapErrorText({ wrapInputError }: { wrapInputError: WrapInputError }) {
+ const native = useNativeCurrency()
+ const wrapped = native?.wrapped
+
+ switch (wrapInputError) {
+ case WrapInputError.NO_ERROR:
+ return null
+ case WrapInputError.ENTER_NATIVE_AMOUNT:
+ return Enter {native?.symbol} amount
+ case WrapInputError.ENTER_WRAPPED_AMOUNT:
+ return Enter {wrapped?.symbol} amount
+
+ case WrapInputError.INSUFFICIENT_NATIVE_BALANCE:
+ return Insufficient {native?.symbol} balance
+ case WrapInputError.INSUFFICIENT_WRAPPED_BALANCE:
+ return Insufficient {wrapped?.symbol} balance
+ }
+}
+
/**
* Given the selected input and output currency, return a wrap callback
* @param inputCurrency the selected input currency
@@ -25,17 +55,20 @@ export default function useWrapCallback(
inputCurrency: Currency | undefined | null,
outputCurrency: Currency | undefined | null,
typedValue: string | undefined
-): { wrapType: WrapType; execute?: undefined | (() => Promise); inputError?: string } {
+): { wrapType: WrapType; execute?: undefined | (() => Promise); inputError?: WrapInputError } {
const { chainId, account } = useActiveWeb3React()
const wethContract = useWETHContract()
const balance = useCurrencyBalance(account ?? undefined, inputCurrency ?? undefined)
// we can always parse the amount typed as the input currency, since wrapping is 1:1
- const inputAmount = useMemo(() => tryParseAmount(typedValue, inputCurrency ?? undefined), [inputCurrency, typedValue])
+ const inputAmount = useMemo(
+ () => tryParseCurrencyAmount(typedValue, inputCurrency ?? undefined),
+ [inputCurrency, typedValue]
+ )
const addTransaction = useTransactionAdder()
return useMemo(() => {
if (!wethContract || !chainId || !inputCurrency || !outputCurrency) return NOT_APPLICABLE
- const weth = WETH9_EXTENDED[chainId]
+ const weth = WRAPPED_NATIVE_CURRENCY[chainId]
if (!weth) return NOT_APPLICABLE
const hasInputAmount = Boolean(inputAmount?.greaterThan('0'))
@@ -53,13 +86,18 @@ export default function useWrapCallback(
type: TransactionType.WRAP,
unwrapped: false,
currencyAmountRaw: inputAmount?.quotient.toString(),
+ chainId,
})
} catch (error) {
console.error('Could not deposit', error)
}
}
: undefined,
- inputError: sufficientBalance ? undefined : hasInputAmount ? 'Insufficient ETH balance' : 'Enter ETH amount',
+ inputError: sufficientBalance
+ ? undefined
+ : hasInputAmount
+ ? WrapInputError.INSUFFICIENT_NATIVE_BALANCE
+ : WrapInputError.ENTER_NATIVE_AMOUNT,
}
} else if (weth.equals(inputCurrency) && outputCurrency.isNative) {
return {
@@ -73,13 +111,18 @@ export default function useWrapCallback(
type: TransactionType.WRAP,
unwrapped: true,
currencyAmountRaw: inputAmount?.quotient.toString(),
+ chainId,
})
} catch (error) {
console.error('Could not withdraw', error)
}
}
: undefined,
- inputError: sufficientBalance ? undefined : hasInputAmount ? 'Insufficient WETH balance' : 'Enter WETH amount',
+ inputError: sufficientBalance
+ ? undefined
+ : hasInputAmount
+ ? WrapInputError.INSUFFICIENT_WRAPPED_BALANCE
+ : WrapInputError.ENTER_WRAPPED_AMOUNT,
}
} else {
return NOT_APPLICABLE
diff --git a/src/hooks/web3.ts b/src/hooks/web3.ts
index 0634dae735..c36bd4a824 100644
--- a/src/hooks/web3.ts
+++ b/src/hooks/web3.ts
@@ -1,17 +1,11 @@
-import { Web3Provider } from '@ethersproject/providers'
-import { useWeb3React } from '@web3-react/core'
+import type { EthereumProvider } from 'lib/ethereum'
import { useEffect, useState } from 'react'
+import { useWeb3React } from 'web3-react-core'
import { gnosisSafe, injected } from 'connectors'
-import { IS_IN_IFRAME, NetworkContextName } from '../constants/misc'
+import { IS_IN_IFRAME } from '../constants/misc'
import { isMobile } from '../utils/userAgent'
-export function useActiveWeb3React() {
- const context = useWeb3React()
- const contextNetwork = useWeb3React(NetworkContextName)
- return context.active ? context : contextNetwork
-}
-
export function useEagerConnect() {
const { activate, active } = useWeb3React()
const [tried, setTried] = useState(false)
@@ -74,7 +68,7 @@ export function useInactiveListener(suppress = false) {
const { active, error, activate } = useWeb3React()
useEffect(() => {
- const { ethereum } = window
+ const ethereum = window.ethereum as EthereumProvider | undefined
if (ethereum && ethereum.on && !active && !error && !suppress) {
const handleChainChanged = () => {
diff --git a/src/i18n.tsx b/src/i18n.tsx
index a967acab9b..7eb5396edb 100644
--- a/src/i18n.tsx
+++ b/src/i18n.tsx
@@ -1,110 +1,26 @@
-import { i18n } from '@lingui/core'
-import { I18nProvider } from '@lingui/react'
-import { DEFAULT_LOCALE, DEFAULT_MESSAGES, SupportedLocale } from 'constants/locales'
+import { SupportedLocale } from 'constants/locales'
import { initialLocale, useActiveLocale } from 'hooks/useActiveLocale'
-import {
- af,
- ar,
- ca,
- cs,
- da,
- de,
- el,
- en,
- es,
- fi,
- fr,
- he,
- hu,
- id,
- it,
- ja,
- ko,
- nl,
- no,
- pl,
- PluralCategory,
- pt,
- ro,
- ru,
- sr,
- sv,
- sw,
- tr,
- uk,
- vi,
- zh,
-} from 'make-plural/plurals'
-import { useEffect } from 'react'
-import { ReactNode } from 'react'
+import { dynamicActivate, Provider } from 'lib/i18n'
+import { ReactNode, useCallback } from 'react'
import { useUserLocaleManager } from 'state/user/hooks'
-type LocalePlural = {
- [key in SupportedLocale]: (n: number | string, ord?: boolean) => PluralCategory
-}
-
-const plurals: LocalePlural = {
- 'af-ZA': af,
- 'ar-SA': ar,
- 'ca-ES': ca,
- 'cs-CZ': cs,
- 'da-DK': da,
- 'de-DE': de,
- 'el-GR': el,
- 'en-US': en,
- 'es-ES': es,
- 'fi-FI': fi,
- 'fr-FR': fr,
- 'he-IL': he,
- 'hu-HU': hu,
- 'id-ID': id,
- 'it-IT': it,
- 'ja-JP': ja,
- 'ko-KR': ko,
- 'nl-NL': nl,
- 'no-NO': no,
- 'pl-PL': pl,
- 'pt-BR': pt,
- 'pt-PT': pt,
- 'ro-RO': ro,
- 'ru-RU': ru,
- 'sr-SP': sr,
- 'sv-SE': sv,
- 'sw-TZ': sw,
- 'tr-TR': tr,
- 'uk-UA': uk,
- 'vi-VN': vi,
- 'zh-CN': zh,
- 'zh-TW': zh,
-}
-
-async function dynamicActivate(locale: SupportedLocale) {
- i18n.loadLocaleData(locale, { plurals: () => plurals[locale] })
- const { messages } = locale === DEFAULT_LOCALE ? { messages: DEFAULT_MESSAGES } : await import(`locales/${locale}`)
- i18n.load(locale, messages)
- i18n.activate(locale)
-}
-
dynamicActivate(initialLocale)
export function LanguageProvider({ children }: { children: ReactNode }) {
const locale = useActiveLocale()
const [, setUserLocale] = useUserLocaleManager()
- useEffect(() => {
- dynamicActivate(locale)
- .then(() => {
- document.documentElement.setAttribute('lang', locale)
- setUserLocale(locale) // stores the selected locale to persist across sessions
- })
- .catch((error) => {
- console.error('Failed to activate locale', locale, error)
- })
- }, [locale, setUserLocale])
+ const onActivate = useCallback(
+ (locale: SupportedLocale) => {
+ document.documentElement.setAttribute('lang', locale)
+ setUserLocale(locale) // stores the selected locale to persist across sessions
+ },
+ [setUserLocale]
+ )
return (
-
+
{children}
-
+
)
}
diff --git a/src/index.tsx b/src/index.tsx
index 1338e67706..3a2d8cb8a5 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -2,11 +2,15 @@ import '@reach/dialog/styles.css'
import 'inter-ui'
import 'polyfills'
import 'components/analytics'
-import { createWeb3ReactRoot, Web3ReactProvider } from '@web3-react/core'
+
+import { BlockUpdater } from 'lib/hooks/useBlockNumber'
+import { MulticallUpdater } from 'lib/state/multicall'
import { StrictMode } from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { HashRouter } from 'react-router-dom'
+import { createWeb3ReactRoot, Web3ReactProvider } from 'web3-react-core'
+
import Blocklist from 'components/Blocklist'
import { NetworkContextName } from 'constants/misc'
import { LanguageProvider } from 'i18n'
@@ -16,10 +20,13 @@ import store from 'state'
import ApplicationUpdater from 'state/application/updater'
import ListsUpdater from 'state/lists/updater'
import LogsUpdater from 'state/logs/updater'
-import MulticallUpdater from 'state/multicall/updater'
import TransactionUpdater from 'state/transactions/updater'
-import EnhancedTransactionUpdater from 'state/enhancedTransactions/updater'
import UserUpdater from 'state/user/updater'
+import ThemeProvider, { FixedGlobalStyle, ThemedGlobalStyle } from 'theme'
+import RadialGradientByChainUpdater from 'theme/RadialGradientByChainUpdater'
+import getLibrary from 'utils/getLibrary'
+
+import EnhancedTransactionUpdater from 'state/enhancedTransactions/updater'
import FeesUpdater from 'state/price/updater'
import GasUpdater from 'state/gas/updater'
import SentryUpdater from 'state/sentry/updater'
@@ -31,10 +38,7 @@ import {
UnfillableOrdersUpdater,
} from 'state/orders/updaters'
// import { EventUpdater } from 'state/orders/mocks'
-import ThemeProvider, { FixedGlobalStyle, ThemedGlobalStyle } from 'theme'
-import getLibrary from 'utils/getLibrary'
import AppziButton from 'components/AppziButton'
-import RadialGradientByChainUpdater from 'theme/RadialGradientByChainUpdater'
import { nodeRemoveChildFix } from 'utils/node'
// Node removeChild hackaround
@@ -55,6 +59,7 @@ function Updaters() {
+
@@ -94,6 +99,7 @@ ReactDOM.render(
document.getElementById('root')
)
+// TODO: maybe re-enable service workers?
// if (process.env.REACT_APP_SERVICE_WORKER !== 'false') {
// serviceWorkerRegistration.register()
// }
diff --git a/src/lib/.eslintrc.json b/src/lib/.eslintrc.json
new file mode 100644
index 0000000000..3404f8b782
--- /dev/null
+++ b/src/lib/.eslintrc.json
@@ -0,0 +1,24 @@
+{
+ "extends": ["../../.eslintrc.json"],
+ "plugins": ["better-styled-components"],
+ "rules": {
+ "better-styled-components/sort-declarations-alphabetically": "error",
+ "no-restricted-imports": [
+ "error",
+ {
+ "paths": [
+ {
+ "name": "react-feather",
+ "message": "Please import from lib/icons to ensure performant usage."
+ }
+ ],
+ "patterns": [
+ {
+ "group": ["styled-components"],
+ "message": "Please import styled from lib/theme to get the correct typings."
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/src/lib/assets/fonts.scss b/src/lib/assets/fonts.scss
new file mode 100644
index 0000000000..7088428407
--- /dev/null
+++ b/src/lib/assets/fonts.scss
@@ -0,0 +1,8 @@
+// Use Inter mixin to set font-display: block.
+@use '@fontsource/inter/scss/mixins' as Inter;
+@include Inter.fontFace($fontName: 'Inter', $weight: 400, $display: block);
+@include Inter.fontFace($fontName: 'Inter', $weight: 500, $display: block);
+@include Inter.fontFace($fontName: 'Inter', $weight: 600, $display: block);
+@include Inter.fontFaceVariable($display: block);
+
+@import '~@fontsource/ibm-plex-mono/400.css';
diff --git a/src/lib/assets/svg/auto_router.svg b/src/lib/assets/svg/auto_router.svg
new file mode 100644
index 0000000000..2aa1268848
--- /dev/null
+++ b/src/lib/assets/svg/auto_router.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/assets/svg/check.svg b/src/lib/assets/svg/check.svg
new file mode 100644
index 0000000000..64735211ef
--- /dev/null
+++ b/src/lib/assets/svg/check.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/lib/assets/svg/expando.svg b/src/lib/assets/svg/expando.svg
new file mode 100644
index 0000000000..aef744a93b
--- /dev/null
+++ b/src/lib/assets/svg/expando.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/lib/assets/svg/logo.svg b/src/lib/assets/svg/logo.svg
new file mode 100644
index 0000000000..3bd07e1b17
--- /dev/null
+++ b/src/lib/assets/svg/logo.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/assets/svg/spinner.svg b/src/lib/assets/svg/spinner.svg
new file mode 100644
index 0000000000..e29a12455c
--- /dev/null
+++ b/src/lib/assets/svg/spinner.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/assets/svg/wallet.svg b/src/lib/assets/svg/wallet.svg
new file mode 100644
index 0000000000..d462eb8750
--- /dev/null
+++ b/src/lib/assets/svg/wallet.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/lib/components/ActionButton.tsx b/src/lib/components/ActionButton.tsx
new file mode 100644
index 0000000000..a9f26f1eb5
--- /dev/null
+++ b/src/lib/components/ActionButton.tsx
@@ -0,0 +1,89 @@
+import { AlertTriangle, Icon, LargeIcon } from 'lib/icons'
+import styled, { Color, css, keyframes, ThemedText } from 'lib/theme'
+import { ReactNode, useMemo } from 'react'
+
+import Button from './Button'
+import Row from './Row'
+
+const StyledButton = styled(Button)`
+ border-radius: ${({ theme }) => theme.borderRadius}em;
+ flex-grow: 1;
+ transition: background-color 0.25s ease-out, flex-grow 0.25s ease-out, padding 0.25s ease-out;
+
+ :disabled {
+ margin: -1px;
+ }
+`
+
+const ActionRow = styled(Row)``
+
+const grow = keyframes`
+ from {
+ opacity: 0;
+ width: 0;
+ }
+ to {
+ opacity: 1;
+ width: max-content;
+ }
+`
+
+const actionCss = css`
+ border: 1px solid ${({ theme }) => theme.outline};
+ padding: calc(0.25em - 1px);
+ padding-left: calc(0.75em - 1px);
+
+ ${ActionRow} {
+ animation: ${grow} 0.25s ease-in;
+ white-space: nowrap;
+ }
+
+ ${StyledButton} {
+ border-radius: ${({ theme }) => theme.borderRadius * 0.75}em;
+ flex-grow: 0;
+ padding: 1em;
+ }
+`
+
+export const Overlay = styled(Row)<{ hasAction: boolean }>`
+ border-radius: ${({ theme }) => theme.borderRadius}em;
+ flex-direction: row-reverse;
+ min-height: 3.5em;
+ transition: padding 0.25s ease-out;
+
+ ${({ hasAction }) => hasAction && actionCss}
+`
+
+export interface Action {
+ message: ReactNode
+ icon?: Icon
+ onClick: () => void
+ children: ReactNode
+}
+
+export interface ActionButtonProps {
+ color?: Color
+ disabled?: boolean
+ action?: Action
+ onClick: () => void
+ children: ReactNode
+}
+
+export default function ActionButton({ color = 'accent', disabled, action, onClick, children }: ActionButtonProps) {
+ const textColor = useMemo(() => (color === 'accent' && !disabled ? 'onAccent' : 'currentColor'), [color, disabled])
+ return (
+
+
+
+ {action ? action.children : children}
+
+
+ {action && (
+
+
+ {action?.message}
+
+ )}
+
+ )
+}
diff --git a/src/lib/components/Badge.tsx b/src/lib/components/Badge.tsx
new file mode 100644
index 0000000000..1ad9b0527a
--- /dev/null
+++ b/src/lib/components/Badge.tsx
@@ -0,0 +1,11 @@
+import styled, { Color } from 'lib/theme'
+
+import Row from './Row'
+
+const Badge = styled(Row)<{ borderRadius?: number; padding?: string; color?: Color }>`
+ background-color: ${({ theme, color = 'outline' }) => theme[color]};
+ border-radius: ${({ borderRadius }) => `${borderRadius ?? 0.5}em`};
+ padding: ${({ padding }) => padding ?? '0.25em 0.375em'};
+`
+
+export default Badge
diff --git a/src/lib/components/BrandedFooter.tsx b/src/lib/components/BrandedFooter.tsx
new file mode 100644
index 0000000000..f00e642d8e
--- /dev/null
+++ b/src/lib/components/BrandedFooter.tsx
@@ -0,0 +1,40 @@
+import { Trans } from '@lingui/macro'
+import Row from 'lib/components/Row'
+import { Logo } from 'lib/icons'
+import styled, { brand, ThemedText } from 'lib/theme'
+
+import ExternalLink from './ExternalLink'
+
+const UniswapA = styled(ExternalLink)`
+ color: ${({ theme }) => theme.secondary};
+ cursor: pointer;
+ text-decoration: none;
+
+ ${Logo} {
+ fill: ${({ theme }) => theme.secondary};
+ height: 1em;
+ transition: transform 0.25s ease, fill 0.25s ease;
+ width: 1em;
+ will-change: transform;
+ }
+
+ :hover ${Logo} {
+ fill: ${brand};
+ transform: rotate(-5deg);
+ }
+`
+
+export default function BrandedFooter() {
+ return (
+
+
+
+
+
+ Powered by the Uniswap protocol
+
+
+
+
+ )
+}
diff --git a/src/lib/components/Button.tsx b/src/lib/components/Button.tsx
new file mode 100644
index 0000000000..f38c1270e4
--- /dev/null
+++ b/src/lib/components/Button.tsx
@@ -0,0 +1,66 @@
+import { Icon } from 'lib/icons'
+import styled, { Color } from 'lib/theme'
+import { ComponentProps, forwardRef } from 'react'
+
+export const BaseButton = styled.button`
+ background-color: transparent;
+ border: none;
+ border-radius: 0.5em;
+ color: currentColor;
+ cursor: pointer;
+ font-size: inherit;
+ font-weight: inherit;
+ line-height: inherit;
+ margin: 0;
+ padding: 0;
+
+ :disabled {
+ cursor: initial;
+ filter: saturate(0) opacity(0.4);
+ }
+`
+
+export default styled(BaseButton)<{ color?: Color }>`
+ color: ${({ color = 'interactive', theme }) => color === 'interactive' && theme.onInteractive};
+
+ :enabled {
+ background-color: ${({ color = 'interactive', theme }) => theme[color]};
+ }
+
+ :enabled:hover {
+ background-color: ${({ color = 'interactive', theme }) => theme.onHover(theme[color])};
+ }
+
+ :disabled {
+ border: 1px solid ${({ theme }) => theme.outline};
+ color: ${({ theme }) => theme.secondary};
+ cursor: initial;
+ }
+`
+
+const transparentButton = (defaultColor: Color) => styled(BaseButton)<{ color?: Color }>`
+ color: ${({ color = defaultColor, theme }) => theme[color]};
+
+ :enabled:hover {
+ color: ${({ color = defaultColor, theme }) => theme.onHover(theme[color])};
+ }
+`
+
+export const TextButton = transparentButton('accent')
+
+const SecondaryButton = transparentButton('secondary')
+
+interface IconButtonProps {
+ icon: Icon
+ iconProps?: ComponentProps
+}
+
+export const IconButton = forwardRef>(
+ function IconButton({ icon: Icon, iconProps, ...props }: IconButtonProps & ComponentProps, ref) {
+ return (
+
+
+
+ )
+ }
+)
diff --git a/src/lib/components/Column.tsx b/src/lib/components/Column.tsx
new file mode 100644
index 0000000000..c91d9eb727
--- /dev/null
+++ b/src/lib/components/Column.tsx
@@ -0,0 +1,28 @@
+import styled, { Color, css, Theme } from 'lib/theme'
+
+const Column = styled.div<{
+ align?: string
+ color?: Color
+ justify?: string
+ gap?: number
+ padded?: true
+ flex?: true
+ grow?: true
+ theme: Theme
+ css?: ReturnType
+}>`
+ align-items: ${({ align }) => align ?? 'center'};
+ color: ${({ color, theme }) => color && theme[color]};
+ display: ${({ flex }) => (flex ? 'flex' : 'grid')};
+ flex-direction: column;
+ flex-grow: ${({ grow }) => grow && 1};
+ gap: ${({ gap }) => gap && `${gap}em`};
+ grid-auto-flow: row;
+ grid-template-columns: 1fr;
+ justify-content: ${({ justify }) => justify ?? 'space-between'};
+ padding: ${({ padded }) => padded && '0.75em'};
+
+ ${({ css }) => css}
+`
+
+export default Column
diff --git a/src/lib/components/Dialog.tsx b/src/lib/components/Dialog.tsx
new file mode 100644
index 0000000000..b64d1bf8f1
--- /dev/null
+++ b/src/lib/components/Dialog.tsx
@@ -0,0 +1,115 @@
+import 'wicg-inert'
+
+import useUnmount from 'lib/hooks/useUnmount'
+import { X } from 'lib/icons'
+import styled, { Color, Layer, ThemeProvider } from 'lib/theme'
+import { createContext, ReactElement, ReactNode, useContext, useEffect, useRef, useState } from 'react'
+import { createPortal } from 'react-dom'
+
+import { IconButton } from './Button'
+import Column from './Column'
+import { default as BaseHeader } from './Header'
+import Rule from './Rule'
+
+// Include inert from wicg-inert
+declare global {
+ interface HTMLElement {
+ inert?: boolean
+ }
+}
+
+const Context = createContext({
+ element: null as HTMLElement | null,
+ active: false,
+ setActive: (active: boolean) => undefined as void,
+})
+
+interface ProviderProps {
+ value: HTMLElement | null
+ children: ReactNode
+}
+
+export function Provider({ value, children }: ProviderProps) {
+ // If a Dialog is active, mark the main content inert
+ const ref = useRef(null)
+ const [active, setActive] = useState(false)
+ const context = { element: value, active, setActive }
+ useEffect(() => {
+ if (ref.current) {
+ ref.current.inert = active
+ }
+ }, [active])
+ return (
+
+ {children}
+
+ )
+}
+
+const OnCloseContext = createContext<() => void>(() => void 0)
+
+interface HeaderProps {
+ title?: ReactElement
+ ruled?: boolean
+ children?: ReactNode
+}
+
+export function Header({ title, children, ruled }: HeaderProps) {
+ return (
+ <>
+
+
+ {children}
+
+
+ {ruled && }
+
+ >
+ )
+}
+
+export const Modal = styled.div<{ color: Color }>`
+ background-color: ${({ color, theme }) => theme[color]};
+ border-radius: ${({ theme }) => theme.borderRadius * 0.75}em;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ left: 0;
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+ width: 100%;
+ z-index: ${Layer.DIALOG};
+`
+
+interface DialogProps {
+ color: Color
+ children: ReactNode
+ onClose?: () => void
+}
+
+export default function Dialog({ color, children, onClose = () => void 0 }: DialogProps) {
+ const context = useContext(Context)
+ useEffect(() => {
+ context.setActive(true)
+ return () => context.setActive(false)
+ }, [context])
+ const dialog = useRef(null)
+ useUnmount(dialog)
+ useEffect(() => {
+ const close = (e: KeyboardEvent) => e.key === 'Escape' && onClose?.()
+ document.addEventListener('keydown', close, true)
+ return () => document.removeEventListener('keydown', close, true)
+ }, [onClose])
+ return (
+ context.element &&
+ createPortal(
+
+
+ {children}
+
+ ,
+ context.element
+ )
+ )
+}
diff --git a/src/lib/components/Error/ErrorBoundary.tsx b/src/lib/components/Error/ErrorBoundary.tsx
new file mode 100644
index 0000000000..01fee12f0b
--- /dev/null
+++ b/src/lib/components/Error/ErrorBoundary.tsx
@@ -0,0 +1,46 @@
+import { Trans } from '@lingui/macro'
+import React, { ErrorInfo } from 'react'
+
+import Dialog from '../Dialog'
+import ErrorDialog from './ErrorDialog'
+
+export type ErrorHandler = (error: Error, info: ErrorInfo) => void
+
+interface ErrorBoundaryProps {
+ onError?: ErrorHandler
+}
+
+type ErrorBoundaryState = {
+ error: Error | null
+}
+
+export default class ErrorBoundary extends React.Component {
+ constructor(props: ErrorBoundaryProps) {
+ super(props)
+ this.state = { error: null }
+ }
+
+ static getDerivedStateFromError(error: Error) {
+ return { error }
+ }
+
+ componentDidCatch(error: Error, errorInfo: ErrorInfo) {
+ this.props.onError?.(error, errorInfo)
+ }
+
+ render() {
+ if (this.state.error) {
+ return (
+
+ Something went wrong.}
+ action={Reload the page }
+ onClick={() => window.location.reload()}
+ />
+
+ )
+ }
+ return this.props.children
+ }
+}
diff --git a/src/lib/components/Error/ErrorDialog.tsx b/src/lib/components/Error/ErrorDialog.tsx
new file mode 100644
index 0000000000..86634dc5db
--- /dev/null
+++ b/src/lib/components/Error/ErrorDialog.tsx
@@ -0,0 +1,130 @@
+import { Trans } from '@lingui/macro'
+import useScrollbar from 'lib/hooks/useScrollbar'
+import { AlertTriangle, Expando, Icon, Info, LargeIcon } from 'lib/icons'
+import styled, { Color, ThemedText } from 'lib/theme'
+import { ReactNode, useState } from 'react'
+
+import ActionButton from '../ActionButton'
+import { IconButton } from '../Button'
+import Column from '../Column'
+import Row from '../Row'
+import Rule from '../Rule'
+
+const HeaderIcon = styled(LargeIcon)`
+ flex-grow: 1;
+
+ svg {
+ transition: height 0.25s, width 0.25s;
+ }
+`
+
+interface StatusHeaderProps {
+ icon: Icon
+ iconColor?: Color
+ iconSize?: number
+ children: ReactNode
+}
+
+export function StatusHeader({ icon: Icon, iconColor, iconSize = 4, children }: StatusHeaderProps) {
+ return (
+ <>
+
+
+
+ {children}
+
+
+
+ >
+ )
+}
+
+const ErrorHeader = styled(Column)<{ open: boolean }>`
+ transition: gap 0.25s;
+
+ div:last-child {
+ max-height: ${({ open }) => (open ? 0 : 60 / 14)}em; // 3 * line-height
+ overflow-y: hidden;
+ transition: max-height 0.25s;
+ }
+`
+const ErrorColumn = styled(Column)``
+const ExpandoColumn = styled(Column)<{ open: boolean }>`
+ flex-grow: ${({ open }) => (open ? 2 : 0)};
+ transition: flex-grow 0.25s, gap 0.25s;
+
+ ${Rule} {
+ margin-bottom: ${({ open }) => (open ? 0 : 0.75)}em;
+ transition: margin-bottom 0.25s;
+ }
+
+ ${ErrorColumn} {
+ flex-basis: 0;
+ flex-grow: ${({ open }) => (open ? 1 : 0)};
+ overflow-y: hidden;
+ position: relative;
+ transition: flex-grow 0.25s;
+
+ ${Column} {
+ height: 100%;
+ padding: ${({ open }) => (open ? '0.5em 0' : 0)};
+ transition: padding 0.25s;
+
+ :after {
+ background: linear-gradient(#ffffff00, ${({ theme }) => theme.dialog});
+ bottom: 0;
+ content: '';
+ height: 0.75em;
+ pointer-events: none;
+ position: absolute;
+ width: calc(100% - 1em);
+ }
+ }
+ }
+`
+
+interface ErrorDialogProps {
+ header?: ReactNode
+ error: Error
+ action: ReactNode
+ onClick: () => void
+}
+
+export default function ErrorDialog({ header, error, action, onClick }: ErrorDialogProps) {
+ const [open, setOpen] = useState(false)
+ const [details, setDetails] = useState(null)
+ const scrollbar = useScrollbar(details)
+ return (
+
+
+
+
+ Something went wrong.
+
+ {header}
+
+
+
+
+
+
+ Error details
+
+
+ setOpen(!open)} icon={Expando} iconProps={{ open }} />
+
+
+
+
+
+
+ {error.name}
+ {error.message ? `: ${error.message}` : ''}
+
+
+
+ {action}
+
+
+ )
+}
diff --git a/src/lib/components/Error/WidgetsPropsValidator.tsx b/src/lib/components/Error/WidgetsPropsValidator.tsx
new file mode 100644
index 0000000000..39b43bb852
--- /dev/null
+++ b/src/lib/components/Error/WidgetsPropsValidator.tsx
@@ -0,0 +1,30 @@
+import { SUPPORTED_LOCALES } from 'constants/locales'
+import { WidgetProps } from 'lib/components/Widget'
+import { IntegrationError } from 'lib/errors'
+import { PropsWithChildren, useEffect } from 'react'
+
+export default function WidgetsPropsValidator(props: PropsWithChildren) {
+ const { jsonRpcEndpoint, provider } = props
+
+ useEffect(() => {
+ if (!provider && !jsonRpcEndpoint) {
+ throw new IntegrationError('This widget requires a provider or jsonRpcEndpoint.')
+ }
+ }, [provider, jsonRpcEndpoint])
+
+ const { width } = props
+ useEffect(() => {
+ if (width && width < 300) {
+ throw new IntegrationError(`Set widget width to at least 300px. (You set it to ${width}.)`)
+ }
+ }, [width])
+
+ const { locale } = props
+ useEffect(() => {
+ if (locale && locale !== 'pseudo' && !SUPPORTED_LOCALES.includes(locale)) {
+ console.warn('Unsupported locale: ', locale)
+ }
+ }, [locale])
+
+ return <>{props.children}>
+}
diff --git a/src/lib/components/EtherscanLink.tsx b/src/lib/components/EtherscanLink.tsx
new file mode 100644
index 0000000000..23670797f7
--- /dev/null
+++ b/src/lib/components/EtherscanLink.tsx
@@ -0,0 +1,36 @@
+import { SupportedChainId } from 'constants/chains'
+import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
+import { Link } from 'lib/icons'
+import styled, { Color } from 'lib/theme'
+import { ReactNode, useMemo } from 'react'
+import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
+
+import ExternalLink from './ExternalLink'
+import Row from './Row'
+
+const StyledExternalLink = styled(ExternalLink)<{ color: Color }>`
+ color: ${({ theme, color }) => theme[color]};
+ text-decoration: none;
+`
+
+interface EtherscanLinkProps {
+ type: ExplorerDataType
+ data?: string
+ color?: Color
+ children: ReactNode
+}
+
+export default function EtherscanLink({ data, type, color = 'currentColor', children }: EtherscanLinkProps) {
+ const { chainId } = useActiveWeb3React()
+ const url = useMemo(
+ () => data && getExplorerLink(chainId || SupportedChainId.MAINNET, data, type),
+ [chainId, data, type]
+ )
+ return (
+
+
+ {children}
+
+
+ )
+}
diff --git a/src/lib/components/ExternalLink.tsx b/src/lib/components/ExternalLink.tsx
new file mode 100644
index 0000000000..41c217f692
--- /dev/null
+++ b/src/lib/components/ExternalLink.tsx
@@ -0,0 +1,17 @@
+import { HTMLProps } from 'react'
+
+/**
+ * Outbound link
+ */
+export default function ExternalLink({
+ target = '_blank',
+ href,
+ rel = 'noopener noreferrer',
+ ...rest
+}: Omit, 'as' | 'ref' | 'onClick'> & { href?: string }) {
+ return (
+
+ {rest.children}
+
+ )
+}
diff --git a/src/lib/components/Header.tsx b/src/lib/components/Header.tsx
new file mode 100644
index 0000000000..9b267a5746
--- /dev/null
+++ b/src/lib/components/Header.tsx
@@ -0,0 +1,26 @@
+import { largeIconCss } from 'lib/icons'
+import styled, { ThemedText } from 'lib/theme'
+import { ReactElement, ReactNode } from 'react'
+
+import Row from './Row'
+
+const HeaderRow = styled(Row)`
+ height: 1.75em;
+ margin: 0 0.75em 0.75em;
+ padding-top: 0.5em;
+ ${largeIconCss}
+`
+
+export interface HeaderProps {
+ title?: ReactElement
+ children: ReactNode
+}
+
+export default function Header({ title, children }: HeaderProps) {
+ return (
+
+ {title && {title} }
+ {children}
+
+ )
+}
diff --git a/src/lib/components/Input.tsx b/src/lib/components/Input.tsx
new file mode 100644
index 0000000000..1134f1fc26
--- /dev/null
+++ b/src/lib/components/Input.tsx
@@ -0,0 +1,176 @@
+import JSBI from 'jsbi'
+import styled, { css } from 'lib/theme'
+import { forwardRef, HTMLProps, useCallback, useEffect, useState } from 'react'
+
+const Input = styled.input`
+ -webkit-appearance: textfield;
+ background-color: transparent;
+ border: none;
+ color: currentColor;
+ font-family: inherit;
+ font-size: inherit;
+ font-weight: inherit;
+ line-height: inherit;
+ margin: 0;
+ outline: none;
+ overflow: hidden;
+ padding: 0;
+ text-align: left;
+ text-overflow: ellipsis;
+ width: 100%;
+
+ ::-webkit-search-decoration {
+ -webkit-appearance: none;
+ }
+
+ [type='number'] {
+ -moz-appearance: textfield;
+ }
+
+ ::-webkit-outer-spin-button,
+ ::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ }
+
+ ::placeholder {
+ color: ${({ theme }) => theme.secondary};
+ }
+`
+
+export default Input
+
+interface StringInputProps extends Omit, 'onChange' | 'as' | 'value'> {
+ value: string
+ onChange: (input: string) => void
+}
+
+export const StringInput = forwardRef(function StringInput(
+ { value, onChange, ...props }: StringInputProps,
+ ref
+) {
+ return (
+ onChange(e.target.value)}
+ // universal input options
+ inputMode="text"
+ autoComplete="off"
+ autoCorrect="off"
+ // text-specific options
+ type="text"
+ placeholder={props.placeholder || '-'}
+ minLength={1}
+ spellCheck="false"
+ ref={ref as any}
+ {...props}
+ />
+ )
+})
+
+interface NumericInputProps extends Omit, 'onChange' | 'as' | 'value'> {
+ value: string
+ onChange: (input: string) => void
+}
+
+interface EnforcedNumericInputProps extends NumericInputProps {
+ // Validates nextUserInput; returns stringified value, or null if invalid
+ enforcer: (nextUserInput: string) => string | null
+}
+
+function isNumericallyEqual(a: string, b: string) {
+ const [aInteger, aDecimal] = a.split('.')
+ const [bInteger, bDecimal] = b.split('.')
+ return (
+ JSBI.equal(JSBI.BigInt(aInteger ?? 0), JSBI.BigInt(bInteger ?? 0)) &&
+ JSBI.equal(JSBI.BigInt(aDecimal ?? 0), JSBI.BigInt(bDecimal ?? 0))
+ )
+}
+
+const NumericInput = forwardRef(function NumericInput(
+ { value, onChange, enforcer, pattern, ...props }: EnforcedNumericInputProps,
+ ref
+) {
+ const [state, setState] = useState(value ?? '')
+ useEffect(() => {
+ if (!isNumericallyEqual(state, value)) {
+ setState(value ?? '')
+ }
+ }, [value, state, setState])
+
+ const validateChange = useCallback(
+ (event) => {
+ const nextInput = enforcer(event.target.value.replace(/,/g, '.'))
+ if (nextInput !== null) {
+ setState(nextInput ?? '')
+ if (!isNumericallyEqual(nextInput, value)) {
+ onChange(nextInput)
+ }
+ }
+ },
+ [value, onChange, enforcer]
+ )
+
+ return (
+
+ )
+})
+
+const integerRegexp = /^\d*$/
+const integerEnforcer = (nextUserInput: string) => {
+ if (nextUserInput === '' || integerRegexp.test(nextUserInput)) {
+ const nextInput = parseInt(nextUserInput)
+ return isNaN(nextInput) ? '' : nextInput.toString()
+ }
+ return null
+}
+export const IntegerInput = forwardRef(function IntegerInput(props: NumericInputProps, ref) {
+ return
+})
+
+const decimalRegexp = /^\d*(?:[.])?\d*$/
+const decimalEnforcer = (nextUserInput: string) => {
+ if (nextUserInput === '') {
+ return ''
+ } else if (nextUserInput === '.') {
+ return '0.'
+ } else if (decimalRegexp.test(nextUserInput)) {
+ return nextUserInput
+ }
+ return null
+}
+export const DecimalInput = forwardRef(function DecimalInput(props: NumericInputProps, ref) {
+ return
+})
+
+export const inputCss = css`
+ background-color: ${({ theme }) => theme.container};
+ border: 1px solid ${({ theme }) => theme.container};
+ border-radius: ${({ theme }) => theme.borderRadius}em;
+ cursor: text;
+ padding: calc(0.5em - 1px);
+
+ :hover:not(:focus-within) {
+ background-color: ${({ theme }) => theme.onHover(theme.container)};
+ border-color: ${({ theme }) => theme.onHover(theme.container)};
+ }
+
+ :focus-within {
+ border-color: ${({ theme }) => theme.active};
+ }
+`
diff --git a/src/lib/components/Popover.tsx b/src/lib/components/Popover.tsx
new file mode 100644
index 0000000000..a96068884a
--- /dev/null
+++ b/src/lib/components/Popover.tsx
@@ -0,0 +1,148 @@
+import { Options, Placement } from '@popperjs/core'
+import styled, { Layer } from 'lib/theme'
+import maxSize from 'popper-max-size-modifier'
+import React, { createContext, useContext, useMemo, useRef, useState } from 'react'
+import { createPortal } from 'react-dom'
+import { usePopper } from 'react-popper'
+
+const BoundaryContext = createContext(null)
+
+export const BoundaryProvider = BoundaryContext.Provider
+
+const PopoverContainer = styled.div<{ show: boolean }>`
+ background-color: ${({ theme }) => theme.dialog};
+ border: 1px solid ${({ theme }) => theme.outline};
+ border-radius: 0.5em;
+ opacity: ${(props) => (props.show ? 1 : 0)};
+ padding: 8px;
+ transition: visibility 0.25s linear, opacity 0.25s linear;
+ visibility: ${(props) => (props.show ? 'visible' : 'hidden')};
+ z-index: ${Layer.TOOLTIP};
+`
+
+const Reference = styled.div`
+ display: inline-block;
+`
+
+const Arrow = styled.div`
+ height: 8px;
+ width: 8px;
+ z-index: ${Layer.TOOLTIP};
+
+ ::before {
+ background: ${({ theme }) => theme.dialog};
+ border: 1px solid ${({ theme }) => theme.outline};
+ content: '';
+ height: 8px;
+ position: absolute;
+ transform: rotate(45deg);
+ width: 8px;
+ }
+
+ &.arrow-top {
+ bottom: -4px;
+ ::before {
+ border-radius: 1px;
+ border-left: none;
+ border-top: none;
+ }
+ }
+
+ &.arrow-bottom {
+ top: -5px; // includes -1px from border
+ ::before {
+ border-bottom: none;
+ border-right: none;
+ border-radius: 1px;
+ }
+ }
+
+ &.arrow-left {
+ right: -4px;
+ ::before {
+ border-bottom: none;
+ border-left: none;
+ border-radius: 1px;
+ }
+ }
+
+ &.arrow-right {
+ left: -5px; // includes -1px from border
+ ::before {
+ border-radius: 1px;
+ border-right: none;
+ border-top: none;
+ }
+ }
+`
+
+export interface PopoverProps {
+ content: React.ReactNode
+ show: boolean
+ children: React.ReactNode
+ placement: Placement
+ offset?: number
+ contained?: true
+}
+
+export default function Popover({ content, show, children, placement, offset, contained }: PopoverProps) {
+ const boundary = useContext(BoundaryContext)
+ const reference = useRef