Skip to content
This repository has been archived by the owner on Jun 24, 2022. It is now read-only.

Commit

Permalink
feat: Add Optimism (#1923)
Browse files Browse the repository at this point in the history
* 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: Lint Action <[email protected]>
Co-authored-by: Moody Salem <[email protected]>
  • Loading branch information
3 people authored Jul 8, 2021
1 parent 0c09688 commit 5ff2cff
Show file tree
Hide file tree
Showing 26 changed files with 212 additions and 79 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"@uniswap/v2-sdk": "^3.0.0-alpha.2",
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.0.0-alpha.9",
"@uniswap/v3-sdk": "^3.2.1",
"@web3-react/core": "^6.0.9",
"@web3-react/fortmatic-connector": "^6.0.9",
"@web3-react/injected-connector": "^6.0.7",
Expand Down
8 changes: 4 additions & 4 deletions src/connectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ const NETWORK_URLS: {
[SupportedChainId.ROPSTEN]: `https://ropsten.infura.io/v3/${INFURA_KEY}`,
[SupportedChainId.GOERLI]: `https://goerli.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_RINKEBY]: `https://rinkeby.arbitrum.io/rpc`,
[SupportedChainId.OPTIMISM]: `https://mainnet.optimism.io`,
[SupportedChainId.OPTIMISTIC_KOVAN]: `https://kovan.optimism.io`,
}

const SUPPORTED_CHAIN_IDS: SupportedChainId[] = [
Expand All @@ -39,10 +39,10 @@ const SUPPORTED_CHAIN_IDS: SupportedChainId[] = [
SupportedChainId.GOERLI,
SupportedChainId.RINKEBY,
SupportedChainId.ROPSTEN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
]

export const network = new NetworkConnector({
Expand Down
20 changes: 17 additions & 3 deletions src/constants/addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ type AddressMap = { [chainId: number]: string }

export const UNI_ADDRESS: AddressMap = constructSameAddressMap('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
export const MULTICALL_ADDRESS: AddressMap = {
...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984'),
[SupportedChainId.ARBITRUM_RINKEBY]: '0xa501c031958F579dB7676fF1CE78AD305794d579',
...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984', [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
]),
[SupportedChainId.ARBITRUM_ONE]: '0xadF885960B47eA2CD9B55E6DAc6B42b7Cb2806dB',
[SupportedChainId.ARBITRUM_RINKEBY]: '0xa501c031958F579dB7676fF1CE78AD305794d579',
}
export const V2_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V2_FACTORY_ADDRESS)
export const V2_ROUTER_ADDRESS: AddressMap = constructSameAddressMap('0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')
Expand All @@ -36,16 +39,25 @@ export const ARGENT_WALLET_DETECTOR_ADDRESS: AddressMap = {
[SupportedChainId.MAINNET]: '0xeca4B0bDBf7c55E9b7925919d03CbF8Dc82537E8',
}
export const V3_CORE_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V3_FACTORY_ADDRESS, [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
])
export const QUOTER_ADDRESSES: AddressMap = constructSameAddressMap('0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6', [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
])
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: AddressMap = constructSameAddressMap(
'0xC36442b4a4522E871399CD717aBDD847Ab11FE88',
[SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_RINKEBY]
[
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
]
)
export const ENS_REGISTRAR_ADDRESSES: AddressMap = {
[SupportedChainId.MAINNET]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
Expand All @@ -57,6 +69,8 @@ export const SOCKS_CONTROLLER_ADDRESSES: AddressMap = {
[SupportedChainId.MAINNET]: '0x65770b5283117639760beA3F867b69b3697a91dd',
}
export const SWAP_ROUTER_ADDRESSES: AddressMap = constructSameAddressMap('0xE592427A0AEce92De3Edee1F18E0157C05861564', [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
])
Expand Down
24 changes: 12 additions & 12 deletions src/constants/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,30 +118,30 @@ export const UNI: { [chainId: number]: Token } = {

export const WETH9_EXTENDED: { [chainId: number]: Token } = {
...WETH9,
[SupportedChainId.ARBITRUM_ONE]: new Token(
SupportedChainId.ARBITRUM_ONE,
'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
[SupportedChainId.OPTIMISM]: new Token(
SupportedChainId.OPTIMISM,
'0x4200000000000000000000000000000000000006',
18,
'WETH',
'Wrapped Ether'
),
[SupportedChainId.ARBITRUM_RINKEBY]: new Token(
SupportedChainId.ARBITRUM_RINKEBY,
'0xB47e6A5f8b33b3F17603C83a0535A9dcD7E32681',
[SupportedChainId.OPTIMISTIC_KOVAN]: new Token(
SupportedChainId.OPTIMISTIC_KOVAN,
'0x4200000000000000000000000000000000000006',
18,
'WETH',
'Wrapped Ether'
),
[SupportedChainId.OPTIMISM]: new Token(
SupportedChainId.OPTIMISM,
'0x4200000000000000000000000000000000000006',
[SupportedChainId.ARBITRUM_ONE]: new Token(
SupportedChainId.ARBITRUM_ONE,
'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
18,
'WETH',
'Wrapped Ether'
),
[SupportedChainId.OPTIMISTIC_KOVAN]: new Token(
SupportedChainId.OPTIMISTIC_KOVAN,
'0x4200000000000000000000000000000000000006',
[SupportedChainId.ARBITRUM_RINKEBY]: new Token(
SupportedChainId.ARBITRUM_RINKEBY,
'0xB47e6A5f8b33b3F17603C83a0535A9dcD7E32681',
18,
'WETH',
'Wrapped Ether'
Expand Down
11 changes: 8 additions & 3 deletions src/hooks/useApproveCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function useApproveCallback(
amountToApprove?: CurrencyAmount<Currency>,
spender?: string
): [ApprovalState, () => Promise<void>] {
const { account } = useActiveWeb3React()
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)
Expand Down Expand Up @@ -51,6 +51,11 @@ export function useApproveCallback(
console.error('approve was called unnecessarily')
return
}
if (!chainId) {
console.error('no chainId')
return
}

if (!token) {
console.error('no token')
return
Expand Down Expand Up @@ -80,7 +85,7 @@ export function useApproveCallback(

return tokenContract
.approve(spender, useExact ? amountToApprove.quotient.toString() : MaxUint256, {
gasLimit: calculateGasMargin(estimatedGas),
gasLimit: calculateGasMargin(chainId, estimatedGas),
})
.then((response: TransactionResponse) => {
addTransaction(response, {
Expand All @@ -92,7 +97,7 @@ export function useApproveCallback(
console.debug('Failed to approve token', error)
throw error
})
}, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])
}, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction, chainId])

return [approvalState, approve]
}
Expand Down
17 changes: 15 additions & 2 deletions src/hooks/useBestV3Trade.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { encodeRouteToPath, Route, Trade } from '@uniswap/v3-sdk'
import { SupportedChainId } from 'constants/chains'
import { BigNumber } from 'ethers'
import { useMemo } from 'react'
import { useSingleContractMultipleData } from '../state/multicall/hooks'
import { useAllV3Routes } from './useAllV3Routes'
import { useV3Quoter } from './useContract'
import { useActiveWeb3React } from './web3'

export enum V3TradeState {
LOADING,
Expand All @@ -14,6 +16,11 @@ export enum V3TradeState {
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
* @param amountIn the amount to swap in
Expand All @@ -23,6 +30,7 @@ export function useBestV3TradeExactIn(
amountIn?: CurrencyAmount<Currency>,
currencyOut?: Currency
): { state: V3TradeState; trade: Trade<Currency, Currency, TradeType.EXACT_INPUT> | null } {
const { chainId } = useActiveWeb3React()
const quoter = useV3Quoter()
const { routes, loading: routesLoading } = useAllV3Routes(amountIn?.currency, currencyOut)

Expand All @@ -33,7 +41,9 @@ export function useBestV3TradeExactIn(
])
}, [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(() => {
if (!amountIn || !currencyOut) {
Expand Down Expand Up @@ -104,6 +114,7 @@ export function useBestV3TradeExactOut(
currencyIn?: Currency,
amountOut?: CurrencyAmount<Currency>
): { state: V3TradeState; trade: Trade<Currency, Currency, TradeType.EXACT_OUTPUT> | null } {
const { chainId } = useActiveWeb3React()
const quoter = useV3Quoter()
const { routes, loading: routesLoading } = useAllV3Routes(currencyIn, amountOut?.currency)

Expand All @@ -114,7 +125,9 @@ export function useBestV3TradeExactOut(
])
}, [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(() => {
if (!amountOut || !currencyIn || quotesResults.some(({ valid }) => !valid)) {
Expand Down
5 changes: 4 additions & 1 deletion src/hooks/usePositionTokenURI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ export function usePositionTokenURI(tokenId: TokenId | undefined): UsePositionTo
() => [tokenId instanceof BigNumber ? tokenId.toHexString() : tokenId?.toString(16)],
[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(() => {
if (error || !valid || !tokenId) {
Expand Down
4 changes: 3 additions & 1 deletion src/hooks/useSwapCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,9 @@ export function useSwapCallback(
to: address,
data: calldata,
// 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 } : {}),
})
.then((response) => {
Expand Down
72 changes: 71 additions & 1 deletion src/pages/AddLiquidity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { BigNumber } from '@ethersproject/bignumber'
import { AddRemoveTabs } from 'components/NavigationTabs'
import HoverInlineText from 'components/HoverInlineText'
import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
import { SupportedChainId } from 'constants/chains'

const DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE = new Percent(50, 10_000)

Expand Down Expand Up @@ -187,6 +188,63 @@ export default function AddLiquidity({
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() {
if (!chainId || !library || !account) return

Expand Down Expand Up @@ -250,7 +308,7 @@ export default function AddLiquidity({
.then((estimate) => {
const newTxn = {
...txn,
gasLimit: calculateGasMargin(estimate),
gasLimit: calculateGasMargin(chainId, estimate),
}

return library
Expand Down Expand Up @@ -385,6 +443,10 @@ export default function AddLiquidity({
const showApprovalB =
!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 (
<>
<ScrollablePage>
Expand Down Expand Up @@ -755,11 +817,19 @@ export default function AddLiquidity({
)}
</RowBetween>
)}
{mustCreateSeparately && (
<ButtonError onClick={onCreate}>
<Text fontWeight={500}>
<Trans>Create</Trans>
</Text>
</ButtonError>
)}
<ButtonError
onClick={() => {
expertMode ? onAdd() : setShowConfirm(true)
}}
disabled={
mustCreateSeparately ||
!isValid ||
(!argentWalletContract && approvalA !== ApprovalState.APPROVED && !depositADisabled) ||
(!argentWalletContract && approvalB !== ApprovalState.APPROVED && !depositBDisabled)
Expand Down
2 changes: 1 addition & 1 deletion src/pages/AddLiquidityV2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export default function AddLiquidity({
.then((estimatedGasLimit) =>
method(...args, {
...(value ? { value } : {}),
gasLimit: calculateGasMargin(estimatedGasLimit),
gasLimit: calculateGasMargin(chainId, estimatedGasLimit),
}).then((response) => {
setAttemptingTxn(false)

Expand Down
Loading

0 comments on commit 5ff2cff

Please sign in to comment.