Commit 5ff2cff8 authored by Noah Zinsmeister's avatar Noah Zinsmeister Committed by GitHub

feat: Add Optimism (#1923)

* preliminary support

* fix multicall abi

add explorer support

* Fix code style issues with ESLint

* fix gas estimation

* fix todo tests

* fix gas estimation comments

* anonymize links to kovan etherscan and poll more frequently on optimistic kovan

* separate pool creation

* remove supported chain id

* remove the blocktag

* do not use the blocktag on optimism only

* give more gas to tokenURI for optimism

* update sdk

* temp fix to the block tag thing (do not update the block number from the fetch block number)

* remove unused import

* gasRequired -> multi-network

* bump quoter gas limit to 6m on optimism

* move the gas required parameter by chain id one level up

* missed a hook

* retry fetching receipts for optimism as many times as arbitrum

* fix duplicate enum, add optimism as well to retry options config

* fix: state not getting updated after a transaction confirmation

* bump sdk version

* update for mainnet optimism (#1998)

* fix for calculateGasMargin on optimism
Co-authored-by: default avatarLint Action <lint-action@samuelmeuli.com>
Co-authored-by: default avatarMoody Salem <moody.salem@gmail.com>
parent 0c096886
...@@ -27,10 +27,10 @@ const NETWORK_URLS: { ...@@ -27,10 +27,10 @@ const NETWORK_URLS: {
[SupportedChainId.ROPSTEN]: `https://ropsten.infura.io/v3/${INFURA_KEY}`, [SupportedChainId.ROPSTEN]: `https://ropsten.infura.io/v3/${INFURA_KEY}`,
[SupportedChainId.GOERLI]: `https://goerli.infura.io/v3/${INFURA_KEY}`, [SupportedChainId.GOERLI]: `https://goerli.infura.io/v3/${INFURA_KEY}`,
[SupportedChainId.KOVAN]: `https://kovan.infura.io/v3/${INFURA_KEY}`, [SupportedChainId.KOVAN]: `https://kovan.infura.io/v3/${INFURA_KEY}`,
[SupportedChainId.OPTIMISM]: `https://optimism-mainnet.infura.io/v3/${INFURA_KEY}`,
[SupportedChainId.OPTIMISTIC_KOVAN]: `https://optimism-kovan.infura.io/v3/${INFURA_KEY}`,
[SupportedChainId.ARBITRUM_ONE]: `https://arb1.arbitrum.io/rpc`, [SupportedChainId.ARBITRUM_ONE]: `https://arb1.arbitrum.io/rpc`,
[SupportedChainId.ARBITRUM_RINKEBY]: `https://rinkeby.arbitrum.io/rpc`, [SupportedChainId.ARBITRUM_RINKEBY]: `https://rinkeby.arbitrum.io/rpc`,
[SupportedChainId.OPTIMISM]: `https://mainnet.optimism.io`,
[SupportedChainId.OPTIMISTIC_KOVAN]: `https://kovan.optimism.io`,
} }
const SUPPORTED_CHAIN_IDS: SupportedChainId[] = [ const SUPPORTED_CHAIN_IDS: SupportedChainId[] = [
...@@ -39,10 +39,10 @@ const SUPPORTED_CHAIN_IDS: SupportedChainId[] = [ ...@@ -39,10 +39,10 @@ const SUPPORTED_CHAIN_IDS: SupportedChainId[] = [
SupportedChainId.GOERLI, SupportedChainId.GOERLI,
SupportedChainId.RINKEBY, SupportedChainId.RINKEBY,
SupportedChainId.ROPSTEN, SupportedChainId.ROPSTEN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN, SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
] ]
export const network = new NetworkConnector({ export const network = new NetworkConnector({
......
...@@ -7,9 +7,12 @@ type AddressMap = { [chainId: number]: string } ...@@ -7,9 +7,12 @@ type AddressMap = { [chainId: number]: string }
export const UNI_ADDRESS: AddressMap = constructSameAddressMap('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984') export const UNI_ADDRESS: AddressMap = constructSameAddressMap('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
export const MULTICALL_ADDRESS: AddressMap = { export const MULTICALL_ADDRESS: AddressMap = {
...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984'), ...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984', [
[SupportedChainId.ARBITRUM_RINKEBY]: '0xa501c031958F579dB7676fF1CE78AD305794d579', SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
]),
[SupportedChainId.ARBITRUM_ONE]: '0xadF885960B47eA2CD9B55E6DAc6B42b7Cb2806dB', [SupportedChainId.ARBITRUM_ONE]: '0xadF885960B47eA2CD9B55E6DAc6B42b7Cb2806dB',
[SupportedChainId.ARBITRUM_RINKEBY]: '0xa501c031958F579dB7676fF1CE78AD305794d579',
} }
export const V2_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V2_FACTORY_ADDRESS) export const V2_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V2_FACTORY_ADDRESS)
export const V2_ROUTER_ADDRESS: AddressMap = constructSameAddressMap('0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D') export const V2_ROUTER_ADDRESS: AddressMap = constructSameAddressMap('0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')
...@@ -36,16 +39,25 @@ export const ARGENT_WALLET_DETECTOR_ADDRESS: AddressMap = { ...@@ -36,16 +39,25 @@ export const ARGENT_WALLET_DETECTOR_ADDRESS: AddressMap = {
[SupportedChainId.MAINNET]: '0xeca4B0bDBf7c55E9b7925919d03CbF8Dc82537E8', [SupportedChainId.MAINNET]: '0xeca4B0bDBf7c55E9b7925919d03CbF8Dc82537E8',
} }
export const V3_CORE_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V3_FACTORY_ADDRESS, [ export const V3_CORE_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V3_FACTORY_ADDRESS, [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY, SupportedChainId.ARBITRUM_RINKEBY,
]) ])
export const QUOTER_ADDRESSES: AddressMap = constructSameAddressMap('0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6', [ export const QUOTER_ADDRESSES: AddressMap = constructSameAddressMap('0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6', [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY, SupportedChainId.ARBITRUM_RINKEBY,
]) ])
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: AddressMap = constructSameAddressMap( export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: AddressMap = constructSameAddressMap(
'0xC36442b4a4522E871399CD717aBDD847Ab11FE88', '0xC36442b4a4522E871399CD717aBDD847Ab11FE88',
[SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_RINKEBY] [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
]
) )
export const ENS_REGISTRAR_ADDRESSES: AddressMap = { export const ENS_REGISTRAR_ADDRESSES: AddressMap = {
[SupportedChainId.MAINNET]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', [SupportedChainId.MAINNET]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
...@@ -57,6 +69,8 @@ export const SOCKS_CONTROLLER_ADDRESSES: AddressMap = { ...@@ -57,6 +69,8 @@ export const SOCKS_CONTROLLER_ADDRESSES: AddressMap = {
[SupportedChainId.MAINNET]: '0x65770b5283117639760beA3F867b69b3697a91dd', [SupportedChainId.MAINNET]: '0x65770b5283117639760beA3F867b69b3697a91dd',
} }
export const SWAP_ROUTER_ADDRESSES: AddressMap = constructSameAddressMap('0xE592427A0AEce92De3Edee1F18E0157C05861564', [ export const SWAP_ROUTER_ADDRESSES: AddressMap = constructSameAddressMap('0xE592427A0AEce92De3Edee1F18E0157C05861564', [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY, SupportedChainId.ARBITRUM_RINKEBY,
]) ])
......
...@@ -118,30 +118,30 @@ export const UNI: { [chainId: number]: Token } = { ...@@ -118,30 +118,30 @@ export const UNI: { [chainId: number]: Token } = {
export const WETH9_EXTENDED: { [chainId: number]: Token } = { export const WETH9_EXTENDED: { [chainId: number]: Token } = {
...WETH9, ...WETH9,
[SupportedChainId.ARBITRUM_ONE]: new Token( [SupportedChainId.OPTIMISM]: new Token(
SupportedChainId.ARBITRUM_ONE, SupportedChainId.OPTIMISM,
'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', '0x4200000000000000000000000000000000000006',
18, 18,
'WETH', 'WETH',
'Wrapped Ether' 'Wrapped Ether'
), ),
[SupportedChainId.ARBITRUM_RINKEBY]: new Token( [SupportedChainId.OPTIMISTIC_KOVAN]: new Token(
SupportedChainId.ARBITRUM_RINKEBY, SupportedChainId.OPTIMISTIC_KOVAN,
'0xB47e6A5f8b33b3F17603C83a0535A9dcD7E32681', '0x4200000000000000000000000000000000000006',
18, 18,
'WETH', 'WETH',
'Wrapped Ether' 'Wrapped Ether'
), ),
[SupportedChainId.OPTIMISM]: new Token( [SupportedChainId.ARBITRUM_ONE]: new Token(
SupportedChainId.OPTIMISM, SupportedChainId.ARBITRUM_ONE,
'0x4200000000000000000000000000000000000006', '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
18, 18,
'WETH', 'WETH',
'Wrapped Ether' 'Wrapped Ether'
), ),
[SupportedChainId.OPTIMISTIC_KOVAN]: new Token( [SupportedChainId.ARBITRUM_RINKEBY]: new Token(
SupportedChainId.OPTIMISTIC_KOVAN, SupportedChainId.ARBITRUM_RINKEBY,
'0x4200000000000000000000000000000000000006', '0xB47e6A5f8b33b3F17603C83a0535A9dcD7E32681',
18, 18,
'WETH', 'WETH',
'Wrapped Ether' 'Wrapped Ether'
......
...@@ -23,7 +23,7 @@ export function useApproveCallback( ...@@ -23,7 +23,7 @@ export function useApproveCallback(
amountToApprove?: CurrencyAmount<Currency>, amountToApprove?: CurrencyAmount<Currency>,
spender?: string spender?: string
): [ApprovalState, () => Promise<void>] { ): [ApprovalState, () => Promise<void>] {
const { account } = useActiveWeb3React() const { account, chainId } = useActiveWeb3React()
const token = amountToApprove?.currency?.isToken ? amountToApprove.currency : undefined const token = amountToApprove?.currency?.isToken ? amountToApprove.currency : undefined
const currentAllowance = useTokenAllowance(token, account ?? undefined, spender) const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
const pendingApproval = useHasPendingApproval(token?.address, spender) const pendingApproval = useHasPendingApproval(token?.address, spender)
...@@ -51,6 +51,11 @@ export function useApproveCallback( ...@@ -51,6 +51,11 @@ export function useApproveCallback(
console.error('approve was called unnecessarily') console.error('approve was called unnecessarily')
return return
} }
if (!chainId) {
console.error('no chainId')
return
}
if (!token) { if (!token) {
console.error('no token') console.error('no token')
return return
...@@ -80,7 +85,7 @@ export function useApproveCallback( ...@@ -80,7 +85,7 @@ export function useApproveCallback(
return tokenContract return tokenContract
.approve(spender, useExact ? amountToApprove.quotient.toString() : MaxUint256, { .approve(spender, useExact ? amountToApprove.quotient.toString() : MaxUint256, {
gasLimit: calculateGasMargin(estimatedGas), gasLimit: calculateGasMargin(chainId, estimatedGas),
}) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
...@@ -92,7 +97,7 @@ export function useApproveCallback( ...@@ -92,7 +97,7 @@ export function useApproveCallback(
console.debug('Failed to approve token', error) console.debug('Failed to approve token', error)
throw error throw error
}) })
}, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction]) }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction, chainId])
return [approvalState, approve] return [approvalState, approve]
} }
......
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { encodeRouteToPath, Route, Trade } from '@uniswap/v3-sdk' import { encodeRouteToPath, Route, Trade } from '@uniswap/v3-sdk'
import { SupportedChainId } from 'constants/chains'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { useMemo } from 'react' import { useMemo } from 'react'
import { useSingleContractMultipleData } from '../state/multicall/hooks' import { useSingleContractMultipleData } from '../state/multicall/hooks'
import { useAllV3Routes } from './useAllV3Routes' import { useAllV3Routes } from './useAllV3Routes'
import { useV3Quoter } from './useContract' import { useV3Quoter } from './useContract'
import { useActiveWeb3React } from './web3'
export enum V3TradeState { export enum V3TradeState {
LOADING, LOADING,
...@@ -14,6 +16,11 @@ export enum V3TradeState { ...@@ -14,6 +16,11 @@ export enum V3TradeState {
SYNCING, SYNCING,
} }
const QUOTE_GAS_OVERRIDES: { [chainId: number]: number } = {
[SupportedChainId.OPTIMISM]: 6_000_000,
[SupportedChainId.OPTIMISTIC_KOVAN]: 6_000_000,
}
/** /**
* Returns the best v3 trade for a desired exact input swap * Returns the best v3 trade for a desired exact input swap
* @param amountIn the amount to swap in * @param amountIn the amount to swap in
...@@ -23,6 +30,7 @@ export function useBestV3TradeExactIn( ...@@ -23,6 +30,7 @@ export function useBestV3TradeExactIn(
amountIn?: CurrencyAmount<Currency>, amountIn?: CurrencyAmount<Currency>,
currencyOut?: Currency currencyOut?: Currency
): { state: V3TradeState; trade: Trade<Currency, Currency, TradeType.EXACT_INPUT> | null } { ): { state: V3TradeState; trade: Trade<Currency, Currency, TradeType.EXACT_INPUT> | null } {
const { chainId } = useActiveWeb3React()
const quoter = useV3Quoter() const quoter = useV3Quoter()
const { routes, loading: routesLoading } = useAllV3Routes(amountIn?.currency, currencyOut) const { routes, loading: routesLoading } = useAllV3Routes(amountIn?.currency, currencyOut)
...@@ -33,7 +41,9 @@ export function useBestV3TradeExactIn( ...@@ -33,7 +41,9 @@ export function useBestV3TradeExactIn(
]) ])
}, [amountIn, routes]) }, [amountIn, routes])
const quotesResults = useSingleContractMultipleData(quoter, 'quoteExactInput', quoteExactInInputs) const quotesResults = useSingleContractMultipleData(quoter, 'quoteExactInput', quoteExactInInputs, {
gasRequired: chainId ? QUOTE_GAS_OVERRIDES[chainId] ?? 1_000_000 : undefined,
})
return useMemo(() => { return useMemo(() => {
if (!amountIn || !currencyOut) { if (!amountIn || !currencyOut) {
...@@ -104,6 +114,7 @@ export function useBestV3TradeExactOut( ...@@ -104,6 +114,7 @@ export function useBestV3TradeExactOut(
currencyIn?: Currency, currencyIn?: Currency,
amountOut?: CurrencyAmount<Currency> amountOut?: CurrencyAmount<Currency>
): { state: V3TradeState; trade: Trade<Currency, Currency, TradeType.EXACT_OUTPUT> | null } { ): { state: V3TradeState; trade: Trade<Currency, Currency, TradeType.EXACT_OUTPUT> | null } {
const { chainId } = useActiveWeb3React()
const quoter = useV3Quoter() const quoter = useV3Quoter()
const { routes, loading: routesLoading } = useAllV3Routes(currencyIn, amountOut?.currency) const { routes, loading: routesLoading } = useAllV3Routes(currencyIn, amountOut?.currency)
...@@ -114,7 +125,9 @@ export function useBestV3TradeExactOut( ...@@ -114,7 +125,9 @@ export function useBestV3TradeExactOut(
]) ])
}, [amountOut, routes]) }, [amountOut, routes])
const quotesResults = useSingleContractMultipleData(quoter, 'quoteExactOutput', quoteExactOutInputs) const quotesResults = useSingleContractMultipleData(quoter, 'quoteExactOutput', quoteExactOutInputs, {
gasRequired: chainId ? QUOTE_GAS_OVERRIDES[chainId] ?? 1_000_000 : undefined,
})
return useMemo(() => { return useMemo(() => {
if (!amountOut || !currencyIn || quotesResults.some(({ valid }) => !valid)) { if (!amountOut || !currencyIn || quotesResults.some(({ valid }) => !valid)) {
......
...@@ -33,7 +33,10 @@ export function usePositionTokenURI(tokenId: TokenId | undefined): UsePositionTo ...@@ -33,7 +33,10 @@ export function usePositionTokenURI(tokenId: TokenId | undefined): UsePositionTo
() => [tokenId instanceof BigNumber ? tokenId.toHexString() : tokenId?.toString(16)], () => [tokenId instanceof BigNumber ? tokenId.toHexString() : tokenId?.toString(16)],
[tokenId] [tokenId]
) )
const { result, error, loading, valid } = useSingleCallResult(contract, 'tokenURI', inputs, NEVER_RELOAD, 3_000_000) const { result, error, loading, valid } = useSingleCallResult(contract, 'tokenURI', inputs, {
...NEVER_RELOAD,
gasRequired: 3_000_000,
})
return useMemo(() => { return useMemo(() => {
if (error || !valid || !tokenId) { if (error || !valid || !tokenId) {
......
...@@ -328,7 +328,9 @@ export function useSwapCallback( ...@@ -328,7 +328,9 @@ export function useSwapCallback(
to: address, to: address,
data: calldata, data: calldata,
// let the wallet try if we can't estimate the gas // let the wallet try if we can't estimate the gas
...('gasEstimate' in bestCallOption ? { gasLimit: calculateGasMargin(bestCallOption.gasEstimate) } : {}), ...('gasEstimate' in bestCallOption
? { gasLimit: calculateGasMargin(chainId, bestCallOption.gasEstimate) }
: {}),
...(value && !isZero(value) ? { value } : {}), ...(value && !isZero(value) ? { value } : {}),
}) })
.then((response) => { .then((response) => {
......
...@@ -56,6 +56,7 @@ import { BigNumber } from '@ethersproject/bignumber' ...@@ -56,6 +56,7 @@ import { BigNumber } from '@ethersproject/bignumber'
import { AddRemoveTabs } from 'components/NavigationTabs' import { AddRemoveTabs } from 'components/NavigationTabs'
import HoverInlineText from 'components/HoverInlineText' import HoverInlineText from 'components/HoverInlineText'
import { SwitchLocaleLink } from 'components/SwitchLocaleLink' import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
import { SupportedChainId } from 'constants/chains'
const DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE = new Percent(50, 10_000) const DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE = new Percent(50, 10_000)
...@@ -187,6 +188,63 @@ export default function AddLiquidity({ ...@@ -187,6 +188,63 @@ export default function AddLiquidity({
outOfRange ? ZERO_PERCENT : DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE outOfRange ? ZERO_PERCENT : DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE
) )
// only called on optimism, atm
async function onCreate() {
if (!chainId || !library) return
if (!positionManager || !currencyA || !currencyB) {
return
}
if (position && account && deadline) {
const { calldata, value } = NonfungiblePositionManager.createCallParameters(position.pool)
const txn: { to: string; data: string; value: string } = {
to: NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[chainId],
data: calldata,
value,
}
setAttemptingTxn(true)
library
.getSigner()
.estimateGas(txn)
.then((estimate) => {
const newTxn = {
...txn,
gasLimit: calculateGasMargin(chainId, estimate),
}
return library
.getSigner()
.sendTransaction(newTxn)
.then((response: TransactionResponse) => {
setAttemptingTxn(false)
addTransaction(response, {
summary: t`Create ${currencyA?.symbol}/${currencyB?.symbol} V3 pool`,
})
setTxHash(response.hash)
ReactGA.event({
category: 'Liquidity',
action: 'Create',
label: [currencies[Field.CURRENCY_A]?.symbol, currencies[Field.CURRENCY_B]?.symbol].join('/'),
})
})
})
.catch((error) => {
console.error('Failed to send transaction', error)
setAttemptingTxn(false)
// we only care if the error is something _other_ than the user rejected the tx
if (error?.code !== 4001) {
console.error(error)
}
})
} else {
return
}
}
async function onAdd() { async function onAdd() {
if (!chainId || !library || !account) return if (!chainId || !library || !account) return
...@@ -250,7 +308,7 @@ export default function AddLiquidity({ ...@@ -250,7 +308,7 @@ export default function AddLiquidity({
.then((estimate) => { .then((estimate) => {
const newTxn = { const newTxn = {
...txn, ...txn,
gasLimit: calculateGasMargin(estimate), gasLimit: calculateGasMargin(chainId, estimate),
} }
return library return library
...@@ -385,6 +443,10 @@ export default function AddLiquidity({ ...@@ -385,6 +443,10 @@ export default function AddLiquidity({
const showApprovalB = const showApprovalB =
!argentWalletContract && approvalB !== ApprovalState.APPROVED && !!parsedAmounts[Field.CURRENCY_B] !argentWalletContract && approvalB !== ApprovalState.APPROVED && !!parsedAmounts[Field.CURRENCY_B]
// flag for whether pool creation must be a separate tx
const mustCreateSeparately =
noLiquidity && (chainId === SupportedChainId.OPTIMISM || chainId === SupportedChainId.OPTIMISTIC_KOVAN)
return ( return (
<> <>
<ScrollablePage> <ScrollablePage>
...@@ -755,11 +817,19 @@ export default function AddLiquidity({ ...@@ -755,11 +817,19 @@ export default function AddLiquidity({
)} )}
</RowBetween> </RowBetween>
)} )}
{mustCreateSeparately && (
<ButtonError onClick={onCreate}>
<Text fontWeight={500}>
<Trans>Create</Trans>
</Text>
</ButtonError>
)}
<ButtonError <ButtonError
onClick={() => { onClick={() => {
expertMode ? onAdd() : setShowConfirm(true) expertMode ? onAdd() : setShowConfirm(true)
}} }}
disabled={ disabled={
mustCreateSeparately ||
!isValid || !isValid ||
(!argentWalletContract && approvalA !== ApprovalState.APPROVED && !depositADisabled) || (!argentWalletContract && approvalA !== ApprovalState.APPROVED && !depositADisabled) ||
(!argentWalletContract && approvalB !== ApprovalState.APPROVED && !depositBDisabled) (!argentWalletContract && approvalB !== ApprovalState.APPROVED && !depositBDisabled)
......
...@@ -184,7 +184,7 @@ export default function AddLiquidity({ ...@@ -184,7 +184,7 @@ export default function AddLiquidity({
.then((estimatedGasLimit) => .then((estimatedGasLimit) =>
method(...args, { method(...args, {
...(value ? { value } : {}), ...(value ? { value } : {}),
gasLimit: calculateGasMargin(estimatedGasLimit), gasLimit: calculateGasMargin(chainId, estimatedGasLimit),
}).then((response) => { }).then((response) => {
setAttemptingTxn(false) setAttemptingTxn(false)
......
...@@ -270,7 +270,8 @@ function V2PairMigration({ ...@@ -270,7 +270,8 @@ function V2PairMigration({
typeof tickLower !== 'number' || typeof tickLower !== 'number' ||
typeof tickUpper !== 'number' || typeof tickUpper !== 'number' ||
!v3Amount0Min || !v3Amount0Min ||
!v3Amount1Min !v3Amount1Min ||
!chainId
) )
return return
...@@ -331,7 +332,7 @@ function V2PairMigration({ ...@@ -331,7 +332,7 @@ function V2PairMigration({
.multicall(data) .multicall(data)
.then((gasEstimate) => { .then((gasEstimate) => {
return migrator return migrator
.multicall(data, { gasLimit: calculateGasMargin(gasEstimate) }) .multicall(data, { gasLimit: calculateGasMargin(chainId, gasEstimate) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
ReactGA.event({ ReactGA.event({
category: 'Migrate', category: 'Migrate',
...@@ -349,6 +350,7 @@ function V2PairMigration({ ...@@ -349,6 +350,7 @@ function V2PairMigration({
setConfirmingMigration(false) setConfirmingMigration(false)
}) })
}, [ }, [
chainId,
isNotUniswap, isNotUniswap,
migrator, migrator,
noLiquidity, noLiquidity,
......
...@@ -384,7 +384,7 @@ export function PositionPage({ ...@@ -384,7 +384,7 @@ export function PositionPage({
.then((estimate) => { .then((estimate) => {
const newTxn = { const newTxn = {
...txn, ...txn,
gasLimit: calculateGasMargin(estimate), gasLimit: calculateGasMargin(chainId, estimate),
} }
return library return library
......
...@@ -136,7 +136,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { ...@@ -136,7 +136,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
.then((estimate) => { .then((estimate) => {
const newTxn = { const newTxn = {
...txn, ...txn,
gasLimit: calculateGasMargin(estimate), gasLimit: calculateGasMargin(chainId, estimate),
} }
return library return library
......
...@@ -240,7 +240,7 @@ export default function RemoveLiquidity({ ...@@ -240,7 +240,7 @@ export default function RemoveLiquidity({
const safeGasEstimates: (BigNumber | undefined)[] = await Promise.all( const safeGasEstimates: (BigNumber | undefined)[] = await Promise.all(
methodNames.map((methodName) => methodNames.map((methodName) =>
router.estimateGas[methodName](...args) router.estimateGas[methodName](...args)
.then(calculateGasMargin) .then((estimateGas) => calculateGasMargin(chainId, estimateGas))
.catch((error) => { .catch((error) => {
console.error(`estimateGas failed`, methodName, args, error) console.error(`estimateGas failed`, methodName, args, error)
return undefined return undefined
......
...@@ -162,7 +162,7 @@ export function useClaimCallback(account: string | null | undefined): { ...@@ -162,7 +162,7 @@ export function useClaimCallback(account: string | null | undefined): {
return distributorContract.estimateGas['claim'](...args, {}).then((estimatedGasLimit) => { return distributorContract.estimateGas['claim'](...args, {}).then((estimatedGasLimit) => {
return distributorContract return distributorContract
.claim(...args, { value: null, gasLimit: calculateGasMargin(estimatedGasLimit) }) .claim(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
summary: `Claimed ${unclaimedAmount?.toSignificant(4)} UNI`, summary: `Claimed ${unclaimedAmount?.toSignificant(4)} UNI`,
......
...@@ -240,7 +240,7 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi ...@@ -240,7 +240,7 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi
if (!uniContract) throw new Error('No UNI Contract!') if (!uniContract) throw new Error('No UNI Contract!')
return uniContract.estimateGas.delegate(...args, {}).then((estimatedGasLimit) => { return uniContract.estimateGas.delegate(...args, {}).then((estimatedGasLimit) => {
return uniContract return uniContract
.delegate(...args, { value: null, gasLimit: calculateGasMargin(estimatedGasLimit) }) .delegate(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
summary: t`Delegated votes`, summary: t`Delegated votes`,
...@@ -256,7 +256,7 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi ...@@ -256,7 +256,7 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi
export function useVoteCallback(): { export function useVoteCallback(): {
voteCallback: (proposalId: string | undefined, support: boolean) => undefined | Promise<string> voteCallback: (proposalId: string | undefined, support: boolean) => undefined | Promise<string>
} { } {
const { account } = useActiveWeb3React() const { account, chainId } = useActiveWeb3React()
const latestGovernanceContract = useLatestGovernanceContract() const latestGovernanceContract = useLatestGovernanceContract()
...@@ -264,11 +264,11 @@ export function useVoteCallback(): { ...@@ -264,11 +264,11 @@ export function useVoteCallback(): {
const voteCallback = useCallback( const voteCallback = useCallback(
(proposalId: string | undefined, support: boolean) => { (proposalId: string | undefined, support: boolean) => {
if (!account || !latestGovernanceContract || !proposalId) return if (!account || !latestGovernanceContract || !proposalId || !chainId) return
const args = [proposalId, support] const args = [proposalId, support]
return latestGovernanceContract.estimateGas.castVote(...args, {}).then((estimatedGasLimit) => { return latestGovernanceContract.estimateGas.castVote(...args, {}).then((estimatedGasLimit) => {
return latestGovernanceContract return latestGovernanceContract
.castVote(...args, { value: null, gasLimit: calculateGasMargin(estimatedGasLimit) }) .castVote(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
summary: `Voted ${support ? 'for ' : 'against'} proposal ${proposalId}`, summary: `Voted ${support ? 'for ' : 'against'} proposal ${proposalId}`,
...@@ -277,7 +277,7 @@ export function useVoteCallback(): { ...@@ -277,7 +277,7 @@ export function useVoteCallback(): {
}) })
}) })
}, },
[account, addTransaction, latestGovernanceContract] [account, addTransaction, latestGovernanceContract, chainId]
) )
return { voteCallback } return { voteCallback }
} }
...@@ -285,14 +285,14 @@ export function useVoteCallback(): { ...@@ -285,14 +285,14 @@ export function useVoteCallback(): {
export function useCreateProposalCallback(): ( export function useCreateProposalCallback(): (
createProposalData: CreateProposalData | undefined createProposalData: CreateProposalData | undefined
) => undefined | Promise<string> { ) => undefined | Promise<string> {
const { account } = useActiveWeb3React() const { account, chainId } = useActiveWeb3React()
const latestGovernanceContract = useLatestGovernanceContract() const latestGovernanceContract = useLatestGovernanceContract()
const addTransaction = useTransactionAdder() const addTransaction = useTransactionAdder()
return useCallback( return useCallback(
(createProposalData: CreateProposalData | undefined) => { (createProposalData: CreateProposalData | undefined) => {
if (!account || !latestGovernanceContract || !createProposalData) return undefined if (!account || !latestGovernanceContract || !createProposalData || !chainId) return undefined
const args = [ const args = [
createProposalData.targets, createProposalData.targets,
...@@ -304,7 +304,7 @@ export function useCreateProposalCallback(): ( ...@@ -304,7 +304,7 @@ export function useCreateProposalCallback(): (
return latestGovernanceContract.estimateGas.propose(...args).then((estimatedGasLimit) => { return latestGovernanceContract.estimateGas.propose(...args).then((estimatedGasLimit) => {
return latestGovernanceContract return latestGovernanceContract
.propose(...args, { gasLimit: calculateGasMargin(estimatedGasLimit) }) .propose(...args, { gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
addTransaction(response, { addTransaction(response, {
summary: t`Submitted new proposal`, summary: t`Submitted new proposal`,
...@@ -313,7 +313,7 @@ export function useCreateProposalCallback(): ( ...@@ -313,7 +313,7 @@ export function useCreateProposalCallback(): (
}) })
}) })
}, },
[account, addTransaction, latestGovernanceContract] [account, addTransaction, latestGovernanceContract, chainId]
) )
} }
......
...@@ -158,11 +158,13 @@ export function useSingleContractMultipleData( ...@@ -158,11 +158,13 @@ export function useSingleContractMultipleData(
contract: Contract | null | undefined, contract: Contract | null | undefined,
methodName: string, methodName: string,
callInputs: OptionalMethodInputs[], callInputs: OptionalMethodInputs[],
options?: ListenerOptions, options: Partial<ListenerOptions> & { gasRequired?: number } = {}
gasRequired?: number
): CallState[] { ): CallState[] {
const fragment = useMemo(() => contract?.interface?.getFunction(methodName), [contract, methodName]) const fragment = useMemo(() => contract?.interface?.getFunction(methodName), [contract, methodName])
const blocksPerFetch = options?.blocksPerFetch
const gasRequired = options?.gasRequired
const calls = useMemo( const calls = useMemo(
() => () =>
contract && fragment && callInputs?.length > 0 && callInputs.every((inputs) => isValidMethodArgs(inputs)) contract && fragment && callInputs?.length > 0 && callInputs.every((inputs) => isValidMethodArgs(inputs))
...@@ -177,7 +179,7 @@ export function useSingleContractMultipleData( ...@@ -177,7 +179,7 @@ export function useSingleContractMultipleData(
[contract, fragment, callInputs, gasRequired] [contract, fragment, callInputs, gasRequired]
) )
const results = useCallsData(calls, options) const results = useCallsData(calls, blocksPerFetch ? { blocksPerFetch } : undefined)
const latestBlockNumber = useBlockNumber() const latestBlockNumber = useBlockNumber()
...@@ -191,10 +193,13 @@ export function useMultipleContractSingleData( ...@@ -191,10 +193,13 @@ export function useMultipleContractSingleData(
contractInterface: Interface, contractInterface: Interface,
methodName: string, methodName: string,
callInputs?: OptionalMethodInputs, callInputs?: OptionalMethodInputs,
options?: ListenerOptions, options?: Partial<ListenerOptions> & { gasRequired?: number }
gasRequired?: number
): CallState[] { ): CallState[] {
const fragment = useMemo(() => contractInterface.getFunction(methodName), [contractInterface, methodName]) const fragment = useMemo(() => contractInterface.getFunction(methodName), [contractInterface, methodName])
const blocksPerFetch = options?.blocksPerFetch
const gasRequired = options?.gasRequired
const callData: string | undefined = useMemo( const callData: string | undefined = useMemo(
() => () =>
fragment && isValidMethodArgs(callInputs) fragment && isValidMethodArgs(callInputs)
...@@ -219,7 +224,7 @@ export function useMultipleContractSingleData( ...@@ -219,7 +224,7 @@ export function useMultipleContractSingleData(
[addresses, callData, fragment, gasRequired] [addresses, callData, fragment, gasRequired]
) )
const results = useCallsData(calls, options) const results = useCallsData(calls, blocksPerFetch ? { blocksPerFetch } : undefined)
const latestBlockNumber = useBlockNumber() const latestBlockNumber = useBlockNumber()
...@@ -232,11 +237,13 @@ export function useSingleCallResult( ...@@ -232,11 +237,13 @@ export function useSingleCallResult(
contract: Contract | null | undefined, contract: Contract | null | undefined,
methodName: string, methodName: string,
inputs?: OptionalMethodInputs, inputs?: OptionalMethodInputs,
options?: ListenerOptions, options?: Partial<ListenerOptions> & { gasRequired?: number }
gasRequired?: number
): CallState { ): CallState {
const fragment = useMemo(() => contract?.interface?.getFunction(methodName), [contract, methodName]) const fragment = useMemo(() => contract?.interface?.getFunction(methodName), [contract, methodName])
const blocksPerFetch = options?.blocksPerFetch
const gasRequired = options?.gasRequired
const calls = useMemo<Call[]>(() => { const calls = useMemo<Call[]>(() => {
return contract && fragment && isValidMethodArgs(inputs) return contract && fragment && isValidMethodArgs(inputs)
? [ ? [
...@@ -249,7 +256,7 @@ export function useSingleCallResult( ...@@ -249,7 +256,7 @@ export function useSingleCallResult(
: [] : []
}, [contract, fragment, inputs, gasRequired]) }, [contract, fragment, inputs, gasRequired])
const result = useCallsData(calls, options)[0] const result = useCallsData(calls, blocksPerFetch ? { blocksPerFetch } : undefined)[0]
const latestBlockNumber = useBlockNumber() const latestBlockNumber = useBlockNumber()
return useMemo(() => { return useMemo(() => {
......
...@@ -4,7 +4,6 @@ import { useMulticall2Contract } from '../../hooks/useContract' ...@@ -4,7 +4,6 @@ import { useMulticall2Contract } from '../../hooks/useContract'
import useDebounce from '../../hooks/useDebounce' import useDebounce from '../../hooks/useDebounce'
import chunkArray from '../../utils/chunkArray' import chunkArray from '../../utils/chunkArray'
import { retry, RetryableError } from '../../utils/retry' import { retry, RetryableError } from '../../utils/retry'
import { updateBlockNumber } from '../application/actions'
import { useBlockNumber } from '../application/hooks' import { useBlockNumber } from '../application/hooks'
import { AppState } from '../index' import { AppState } from '../index'
import { errorFetchingMulticallResults, fetchingMulticallResults, updateMulticallResults } from './actions' import { errorFetchingMulticallResults, fetchingMulticallResults, updateMulticallResults } from './actions'
...@@ -26,6 +25,8 @@ async function fetchChunk( ...@@ -26,6 +25,8 @@ async function fetchChunk(
minBlockNumber: number minBlockNumber: number
): Promise<{ ): Promise<{
results: { success: boolean; returnData: string }[] results: { success: boolean; returnData: string }[]
// note we are not using this returned block number because layer 2 multicalls do not consistently return the L2 block number
// instead we are relying on the blockTag parameter and assume the returned data is at least as new as that block number
blockNumber: number blockNumber: number
}> { }> {
console.debug('Fetching chunk', chunk, minBlockNumber) console.debug('Fetching chunk', chunk, minBlockNumber)
...@@ -189,7 +190,7 @@ export default function Updater(): null { ...@@ -189,7 +190,7 @@ export default function Updater(): null {
maxWait: 2500, maxWait: 2500,
}) })
promise promise
.then(({ results: returnData, blockNumber: fetchBlockNumber }) => { .then(({ results: returnData }) => {
// accumulates the length of all previous indices // accumulates the length of all previous indices
const firstCallKeyIndex = chunkedCalls.slice(0, index).reduce<number>((memo, curr) => memo + curr.length, 0) const firstCallKeyIndex = chunkedCalls.slice(0, index).reduce<number>((memo, curr) => memo + curr.length, 0)
const lastCallKeyIndex = firstCallKeyIndex + returnData.length const lastCallKeyIndex = firstCallKeyIndex + returnData.length
...@@ -218,7 +219,7 @@ export default function Updater(): null { ...@@ -218,7 +219,7 @@ export default function Updater(): null {
updateMulticallResults({ updateMulticallResults({
chainId, chainId,
results, results,
blockNumber: fetchBlockNumber, blockNumber: latestBlockNumber,
}) })
) )
...@@ -229,14 +230,10 @@ export default function Updater(): null { ...@@ -229,14 +230,10 @@ export default function Updater(): null {
errorFetchingMulticallResults({ errorFetchingMulticallResults({
calls: erroredCalls, calls: erroredCalls,
chainId, chainId,
fetchingBlockNumber: fetchBlockNumber, fetchingBlockNumber: latestBlockNumber,
}) })
) )
} }
if (fetchBlockNumber > latestBlockNumber) {
dispatch(updateBlockNumber({ chainId, blockNumber: fetchBlockNumber }))
}
}) })
.catch((error: any) => { .catch((error: any) => {
if (error.isCancelledError) { if (error.isCancelledError) {
......
...@@ -33,8 +33,11 @@ export function shouldCheck(lastBlockNumber: number, tx: TxInterface): boolean { ...@@ -33,8 +33,11 @@ export function shouldCheck(lastBlockNumber: number, tx: TxInterface): boolean {
const RETRY_OPTIONS_BY_CHAIN_ID: { [chainId: number]: RetryOptions } = { const RETRY_OPTIONS_BY_CHAIN_ID: { [chainId: number]: RetryOptions } = {
[SupportedChainId.ARBITRUM_ONE]: { n: 10, minWait: 250, maxWait: 1000 }, [SupportedChainId.ARBITRUM_ONE]: { n: 10, minWait: 250, maxWait: 1000 },
[SupportedChainId.ARBITRUM_RINKEBY]: { n: 10, minWait: 250, maxWait: 1000 },
[SupportedChainId.OPTIMISTIC_KOVAN]: { n: 10, minWait: 250, maxWait: 1000 },
[SupportedChainId.OPTIMISM]: { n: 10, minWait: 250, maxWait: 1000 },
} }
const DEFAULT_RETRY_OPTIONS: RetryOptions = { n: 3, minWait: 1000, maxWait: 3000 } const DEFAULT_RETRY_OPTIONS: RetryOptions = { n: 1, minWait: 0, maxWait: 0 }
export default function Updater(): null { export default function Updater(): null {
const { chainId, library } = useActiveWeb3React() const { chainId, library } = useActiveWeb3React()
......
...@@ -12,6 +12,7 @@ import { useTotalUniEarned } from '../stake/hooks' ...@@ -12,6 +12,7 @@ import { useTotalUniEarned } from '../stake/hooks'
import { Interface } from '@ethersproject/abi' import { Interface } from '@ethersproject/abi'
import ERC20ABI from 'abis/erc20.json' import ERC20ABI from 'abis/erc20.json'
import { Erc20Interface } from 'abis/types/Erc20' import { Erc20Interface } from 'abis/types/Erc20'
import { SupportedChainId } from 'constants/chains'
/** /**
* Returns a map of the given addresses to their eventually consistent ETH balances. * Returns a map of the given addresses to their eventually consistent ETH balances.
*/ */
...@@ -50,6 +51,11 @@ export function useETHBalances(uncheckedAddresses?: (string | undefined)[]): { ...@@ -50,6 +51,11 @@ export function useETHBalances(uncheckedAddresses?: (string | undefined)[]): {
) )
} }
const TOKEN_BALANCE_GAS_OVERRIDE: { [chainId: number]: number } = {
[SupportedChainId.OPTIMISM]: 250_000,
[SupportedChainId.OPTIMISTIC_KOVAN]: 250_000,
}
/** /**
* Returns a map of token addresses to their eventually consistent token balances for a single account. * Returns a map of token addresses to their eventually consistent token balances for a single account.
*/ */
...@@ -62,16 +68,13 @@ export function useTokenBalancesWithLoadingIndicator( ...@@ -62,16 +68,13 @@ export function useTokenBalancesWithLoadingIndicator(
[tokens] [tokens]
) )
const { chainId } = useActiveWeb3React()
const validatedTokenAddresses = useMemo(() => validatedTokens.map((vt) => vt.address), [validatedTokens]) const validatedTokenAddresses = useMemo(() => validatedTokens.map((vt) => vt.address), [validatedTokens])
const ERC20Interface = new Interface(ERC20ABI) as Erc20Interface const ERC20Interface = new Interface(ERC20ABI) as Erc20Interface
const balances = useMultipleContractSingleData( const balances = useMultipleContractSingleData(validatedTokenAddresses, ERC20Interface, 'balanceOf', [address], {
validatedTokenAddresses, gasRequired: (chainId && TOKEN_BALANCE_GAS_OVERRIDE[chainId]) ?? 100_000,
ERC20Interface, })
'balanceOf',
[address],
undefined,
100_000
)
const anyLoading: boolean = useMemo(() => balances.some((callState) => callState.loading), [balances]) const anyLoading: boolean = useMemo(() => balances.some((callState) => callState.loading), [balances])
......
...@@ -4,6 +4,8 @@ const EXPLORER_HOSTNAMES: { [hostname: string]: true } = { ...@@ -4,6 +4,8 @@ const EXPLORER_HOSTNAMES: { [hostname: string]: true } = {
'rinkeby.etherscan.io': true, 'rinkeby.etherscan.io': true,
'kovan.etherscan.io': true, 'kovan.etherscan.io': true,
'goerli.etherscan.io': true, 'goerli.etherscan.io': true,
'optimistic.etherscan.io': true,
'kovan-optimistic.etherscan.io': true,
'rinkeby-explorer.arbitrum.io': true, 'rinkeby-explorer.arbitrum.io': true,
'explorer.arbitrum.io': true, 'explorer.arbitrum.io': true,
} }
......
...@@ -3,7 +3,12 @@ import { calculateGasMargin } from './calculateGasMargin' ...@@ -3,7 +3,12 @@ import { calculateGasMargin } from './calculateGasMargin'
describe('#calculateGasMargin', () => { describe('#calculateGasMargin', () => {
it('adds 20%', () => { it('adds 20%', () => {
expect(calculateGasMargin(BigNumber.from(1000)).toString()).toEqual('1200') expect(calculateGasMargin(1, BigNumber.from(1000)).toString()).toEqual('1200')
expect(calculateGasMargin(BigNumber.from(50)).toString()).toEqual('60') expect(calculateGasMargin(1, BigNumber.from(50)).toString()).toEqual('60')
})
it('optimism - returns exact value', () => {
expect(calculateGasMargin(69, BigNumber.from(1000)).toString()).toEqual('1000')
expect(calculateGasMargin(69, BigNumber.from(50)).toString()).toEqual('50')
}) })
}) })
import { BigNumber } from '@ethersproject/bignumber' import { BigNumber } from '@ethersproject/bignumber'
import { SupportedChainId } from 'constants/chains'
// add 20% // add 20% (except on optimism)
export function calculateGasMargin(value: BigNumber): BigNumber { export function calculateGasMargin(chainId: number, value: BigNumber): BigNumber {
return value.mul(BigNumber.from(10000 + 2000)).div(BigNumber.from(10000)) return chainId === SupportedChainId.OPTIMISM || chainId === SupportedChainId.OPTIMISTIC_KOVAN
? value
: value.mul(BigNumber.from(10000 + 2000)).div(BigNumber.from(10000))
} }
...@@ -6,6 +6,8 @@ const ETHERSCAN_PREFIXES: { [chainId: number]: string } = { ...@@ -6,6 +6,8 @@ const ETHERSCAN_PREFIXES: { [chainId: number]: string } = {
[SupportedChainId.RINKEBY]: 'rinkeby.', [SupportedChainId.RINKEBY]: 'rinkeby.',
[SupportedChainId.GOERLI]: 'goerli.', [SupportedChainId.GOERLI]: 'goerli.',
[SupportedChainId.KOVAN]: 'kovan.', [SupportedChainId.KOVAN]: 'kovan.',
[SupportedChainId.OPTIMISM]: 'optimistic.',
[SupportedChainId.OPTIMISTIC_KOVAN]: 'kovan-optimistic.',
} }
export enum ExplorerDataType { export enum ExplorerDataType {
......
...@@ -4,6 +4,8 @@ import { SupportedChainId } from '../constants/chains' ...@@ -4,6 +4,8 @@ import { SupportedChainId } from '../constants/chains'
const NETWORK_POLLING_INTERVALS: { [chainId: number]: number } = { const NETWORK_POLLING_INTERVALS: { [chainId: number]: number } = {
[SupportedChainId.ARBITRUM_ONE]: 1_000, [SupportedChainId.ARBITRUM_ONE]: 1_000,
[SupportedChainId.ARBITRUM_RINKEBY]: 1_000, [SupportedChainId.ARBITRUM_RINKEBY]: 1_000,
[SupportedChainId.OPTIMISM]: 1_000,
[SupportedChainId.OPTIMISTIC_KOVAN]: 1_000,
} }
export default function getLibrary(provider: any): Web3Provider { export default function getLibrary(provider: any): Web3Provider {
......
...@@ -4147,10 +4147,10 @@ ...@@ -4147,10 +4147,10 @@
base64-sol "1.0.1" base64-sol "1.0.1"
hardhat-watcher "^2.1.1" hardhat-watcher "^2.1.1"
"@uniswap/v3-sdk@^3.0.0-alpha.9": "@uniswap/v3-sdk@^3.2.1":
version "3.0.0-alpha.9" version "3.2.1"
resolved "https://registry.npmjs.org/@uniswap/v3-sdk/-/v3-sdk-3.0.0-alpha.9.tgz" resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-3.2.1.tgz#2da1dab8427aa5e79b266536f9a2f8854b52c3ae"
integrity sha512-mfISrPYixSwpwG2XLzXJXJdmGbO76bycXrpNe7kEpcyPUpByejAwuYOnmAZBwZuvDylXtQZS6DiDDRghzNrc9w== integrity sha512-AqDvYwSguuBwOvZyY2gdV2e15Cfu1/cdxGcjdClZGs4vmYZH/zMCfO99G+efGa+O6K4TtEWJYtgBc77XBQ5lzA==
dependencies: dependencies:
"@ethersproject/abi" "^5.0.12" "@ethersproject/abi" "^5.0.12"
"@ethersproject/solidity" "^5.0.9" "@ethersproject/solidity" "^5.0.9"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment