fix(optimism): Optimism regenesis support (#2703)

* feat(optimism): optimistic kovan local regenesis changes

* use the regenesis version of the sdk

* remove the override no longer necessary

* diff rpc url

* back to kovan url

* lint error

* Optimism mainnet regenesis test (#2695)

* remove the optimism mainnet specific code and point to the mainnet regenesis rpc url

* point at the old mainnet multicall address

* bump the sdk version

* copy the list

* multicall address regenesis change

* revert the gas limit special casing for optimism

* bump the sdk version

* remove a couple other temporary edits

* unused test case

* specific version of v3-sdk
This commit is contained in:
Moody Salem 2021-11-11 15:40:12 -05:00 committed by GitHub
parent 1903a16097
commit ccad45d24e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 67 additions and 153 deletions

@ -66,7 +66,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.4.1",
"@uniswap/v3-sdk": "3.6.3",
"@web3-react/core": "^6.0.9",
"@web3-react/fortmatic-connector": "^6.0.9",
"@web3-react/injected-connector": "^6.0.7",

@ -8,8 +8,10 @@ type AddressMap = { [chainId: number]: string }
export const UNI_ADDRESS: AddressMap = constructSameAddressMap('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
export const MULTICALL_ADDRESS: AddressMap = {
...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984', [SupportedChainId.OPTIMISTIC_KOVAN]),
[SupportedChainId.OPTIMISM]: '0x90f872b3d8f33f305e0250db6A2761B354f7710A',
...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984', [
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.OPTIMISM,
]),
[SupportedChainId.ARBITRUM_ONE]: '0xadF885960B47eA2CD9B55E6DAc6B42b7Cb2806dB',
[SupportedChainId.ARBITRUM_RINKEBY]: '0xa501c031958F579dB7676fF1CE78AD305794d579',
}

@ -87,7 +87,7 @@ export function useApproveCallback(
return tokenContract
.approve(spender, useExact ? amountToApprove.quotient.toString() : MaxUint256, {
gasLimit: calculateGasMargin(chainId, estimatedGas),
gasLimit: calculateGasMargin(estimatedGas),
})
.then((response: TransactionResponse) => {
addTransaction(response, { type: TransactionType.APPROVAL, tokenAddress: token.address, spender })

@ -11,8 +11,6 @@ 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,
}

@ -363,9 +363,7 @@ export function useSwapCallback(
to: address,
data: calldata,
// let the wallet try if we can't estimate the gas
...('gasEstimate' in bestCallOption
? { gasLimit: calculateGasMargin(chainId, bestCallOption.gasEstimate) }
: {}),
...('gasEstimate' in bestCallOption ? { gasLimit: calculateGasMargin(bestCallOption.gasEstimate) } : {}),
...(value && !isZero(value) ? { value } : {}),
})
.then((response) => {

@ -34,7 +34,6 @@ import Row, { AutoRow, RowBetween, RowFixed } from '../../components/Row'
import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from '../../constants/addresses'
import { CHAIN_INFO, SupportedChainId } from '../../constants/chains'
import { ZERO_PERCENT } from '../../constants/misc'
import { WETH9_EXTENDED } from '../../constants/tokens'
import { useCurrency } from '../../hooks/Tokens'
@ -205,60 +204,6 @@ 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 (chainId && library && position && account && deadline && baseCurrency && quoteCurrency && positionManager) {
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, {
type: TransactionType.CREATE_V3_POOL,
baseCurrencyId: currencyId(baseCurrency),
quoteCurrencyId: currencyId(quoteCurrency),
})
// dont set txn hash as we dont want submitted txn screen for create
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
@ -322,7 +267,7 @@ export default function AddLiquidity({
.then((estimate) => {
const newTxn = {
...txn,
gasLimit: calculateGasMargin(chainId, estimate),
gasLimit: calculateGasMargin(estimate),
}
return library
@ -418,22 +363,16 @@ export default function AddLiquidity({
[currencyIdA, currencyIdB, history, onLeftRangeInput, onRightRangeInput]
)
// flag for whether pool creation must be a separate tx
const mustCreateSeparately =
noLiquidity && (chainId === SupportedChainId.OPTIMISM || chainId === SupportedChainId.OPTIMISTIC_KOVAN)
const handleDismissConfirmation = useCallback(() => {
setShowConfirm(false)
// if there was a tx hash, we want to clear the input
if (txHash) {
onFieldAInput('')
// dont jump to pool page if creating
if (!mustCreateSeparately) {
history.push('/pool')
}
history.push('/pool')
}
setTxHash('')
}, [history, mustCreateSeparately, onFieldAInput, txHash])
}, [history, onFieldAInput, txHash])
const addIsUnsupported = useIsSwapUnsupported(currencies?.CURRENCY_A, currencies?.CURRENCY_B)
@ -458,15 +397,11 @@ export default function AddLiquidity({
const showApprovalB =
!argentWalletContract && approvalB !== ApprovalState.APPROVED && !!parsedAmounts[Field.CURRENCY_B]
const pendingText = mustCreateSeparately
? `Creating ${currencies[Field.CURRENCY_A]?.symbol}/${currencies[Field.CURRENCY_B]?.symbol} ${
feeAmount ? feeAmount / 10000 : ''
}% Pool`
: `Supplying ${!depositADisabled ? parsedAmounts[Field.CURRENCY_A]?.toSignificant(6) : ''} ${
!depositADisabled ? currencies[Field.CURRENCY_A]?.symbol : ''
} ${!outOfRange ? 'and' : ''} ${!depositBDisabled ? parsedAmounts[Field.CURRENCY_B]?.toSignificant(6) : ''} ${
!depositBDisabled ? currencies[Field.CURRENCY_B]?.symbol : ''
}`
const pendingText = `Supplying ${!depositADisabled ? parsedAmounts[Field.CURRENCY_A]?.toSignificant(6) : ''} ${
!depositADisabled ? currencies[Field.CURRENCY_A]?.symbol : ''
} ${!outOfRange ? 'and' : ''} ${!depositBDisabled ? parsedAmounts[Field.CURRENCY_B]?.toSignificant(6) : ''} ${
!depositBDisabled ? currencies[Field.CURRENCY_B]?.symbol : ''
}`
const Buttons = () =>
addIsUnsupported ? (
@ -519,32 +454,18 @@ export default function AddLiquidity({
)}
</RowBetween>
)}
{mustCreateSeparately && (
<ButtonError onClick={onCreate} disabled={!isValid || attemptingTxn || !position}>
{attemptingTxn ? (
<Dots>
<Trans>Confirm Create</Trans>
</Dots>
) : (
<Text fontWeight={500}>{errorMessage ? errorMessage : <Trans>Create</Trans>}</Text>
)}
</ButtonError>
)}
<ButtonError
onClick={() => {
expertMode ? onAdd() : setShowConfirm(true)
}}
disabled={
mustCreateSeparately ||
!isValid ||
(!argentWalletContract && approvalA !== ApprovalState.APPROVED && !depositADisabled) ||
(!argentWalletContract && approvalB !== ApprovalState.APPROVED && !depositBDisabled)
}
error={!isValid && !!parsedAmounts[Field.CURRENCY_A] && !!parsedAmounts[Field.CURRENCY_B]}
>
<Text fontWeight={500}>
{mustCreateSeparately ? <Trans>Add</Trans> : errorMessage ? errorMessage : <Trans>Preview</Trans>}
</Text>
<Text fontWeight={500}>{errorMessage ? errorMessage : <Trans>Preview</Trans>}</Text>
</ButtonError>
</AutoColumn>
)
@ -793,19 +714,11 @@ export default function AddLiquidity({
textAlign="left"
color={theme.primaryText1}
>
{mustCreateSeparately ? (
<Trans>
{`This pool must be initialized on ${
chainId && CHAIN_INFO ? CHAIN_INFO[chainId].label : ''
} before you can add liquidity. To initialize, select a starting price for the pool. Then, enter your liquidity price range and deposit amount.`}
</Trans>
) : (
<Trans>
This pool must be initialized before you can add liquidity. To initialize, select a
starting price for the pool. Then, enter your liquidity price range and deposit
amount. Gas fees will be higher than usual due to the initialization transaction.
</Trans>
)}
<Trans>
This pool must be initialized before you can add liquidity. To initialize, select a
starting price for the pool. Then, enter your liquidity price range and deposit
amount. Gas fees will be higher than usual due to the initialization transaction.
</Trans>
</TYPE.body>
</BlueCard>
)}

@ -184,7 +184,7 @@ export default function AddLiquidity({
.then((estimatedGasLimit) =>
method(...args, {
...(value ? { value } : {}),
gasLimit: calculateGasMargin(chainId, estimatedGasLimit),
gasLimit: calculateGasMargin(estimatedGasLimit),
}).then((response) => {
setAttemptingTxn(false)

@ -334,7 +334,7 @@ function V2PairMigration({
.multicall(data)
.then((gasEstimate) => {
return migrator
.multicall(data, { gasLimit: calculateGasMargin(chainId, gasEstimate) })
.multicall(data, { gasLimit: calculateGasMargin(gasEstimate) })
.then((response: TransactionResponse) => {
ReactGA.event({
category: 'Migrate',

@ -14,7 +14,6 @@ import { RowBetween, RowFixed } from 'components/Row'
import { Dots } from 'components/swap/styleds'
import Toggle from 'components/Toggle'
import TransactionConfirmationModal, { ConfirmationModalContent } from 'components/TransactionConfirmationModal'
import { SupportedChainId } from 'constants/chains'
import { useToken } from 'hooks/Tokens'
import { useV3NFTPositionManagerContract } from 'hooks/useContract'
import useIsTickAtLimit from 'hooks/useIsTickAtLimit'
@ -443,7 +442,7 @@ export function PositionPage({
.then((estimate) => {
const newTxn = {
...txn,
gasLimit: calculateGasMargin(chainId, estimate),
gasLimit: calculateGasMargin(estimate),
}
return library
@ -514,15 +513,13 @@ export function PositionPage({
)
}
const onOptimisticChain = chainId && [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)
const showCollectAsWeth = Boolean(
ownsNFT &&
(feeValue0?.greaterThan(0) || feeValue1?.greaterThan(0)) &&
currency0 &&
currency1 &&
(currency0.isNative || currency1.isNative) &&
!collectMigrationHash &&
!onOptimisticChain
!collectMigrationHash
)
return loading || poolState === PoolState.LOADING || !feeAmount ? (

@ -16,7 +16,6 @@ import { AddRemoveTabs } from 'components/NavigationTabs'
import { AutoRow, RowBetween, RowFixed } from 'components/Row'
import Slider from 'components/Slider'
import Toggle from 'components/Toggle'
import { SupportedChainId } from 'constants/chains'
import { useV3NFTPositionManagerContract } from 'hooks/useContract'
import useDebouncedChangeHandler from 'hooks/useDebouncedChangeHandler'
import useTheme from 'hooks/useTheme'
@ -140,7 +139,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
.then((estimate) => {
const newTxn = {
...txn,
gasLimit: calculateGasMargin(chainId, estimate),
gasLimit: calculateGasMargin(estimate),
}
return library
@ -262,10 +261,8 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
)
}
const onOptimisticChain = chainId && [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)
const showCollectAsWeth = Boolean(
!onOptimisticChain &&
liquidityValue0?.currency &&
liquidityValue0?.currency &&
liquidityValue1?.currency &&
(liquidityValue0.currency.isNative ||
liquidityValue1.currency.isNative ||

@ -239,7 +239,7 @@ export default function RemoveLiquidity({
const safeGasEstimates: (BigNumber | undefined)[] = await Promise.all(
methodNames.map((methodName) =>
router.estimateGas[methodName](...args)
.then((estimateGas) => calculateGasMargin(chainId, estimateGas))
.then((estimateGas) => calculateGasMargin(estimateGas))
.catch((error) => {
console.error(`estimateGas failed`, methodName, args, error)
return undefined

@ -164,7 +164,7 @@ export function useClaimCallback(account: string | null | undefined): {
return distributorContract.estimateGas['claim'](...args, {}).then((estimatedGasLimit) => {
return distributorContract
.claim(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.claim(...args, { value: null, gasLimit: calculateGasMargin(estimatedGasLimit) })
.then((response: TransactionResponse) => {
addTransaction(response, {
type: TransactionType.CLAIM,

@ -306,7 +306,7 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi
if (!uniContract) throw new Error('No UNI Contract!')
return uniContract.estimateGas.delegate(...args, {}).then((estimatedGasLimit) => {
return uniContract
.delegate(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.delegate(...args, { value: null, gasLimit: calculateGasMargin(estimatedGasLimit) })
.then((response: TransactionResponse) => {
addTransaction(response, {
type: TransactionType.DELEGATE,
@ -335,7 +335,7 @@ export function useVoteCallback(): {
const args = [proposalId, voteOption === VoteOption.Against ? 0 : voteOption === VoteOption.For ? 1 : 2]
return latestGovernanceContract.estimateGas.castVote(...args, {}).then((estimatedGasLimit) => {
return latestGovernanceContract
.castVote(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.castVote(...args, { value: null, gasLimit: calculateGasMargin(estimatedGasLimit) })
.then((response: TransactionResponse) => {
addTransaction(response, {
type: TransactionType.VOTE,
@ -375,7 +375,7 @@ export function useCreateProposalCallback(): (
return latestGovernanceContract.estimateGas.propose(...args).then((estimatedGasLimit) => {
return latestGovernanceContract
.propose(...args, { gasLimit: calculateGasMargin(chainId, estimatedGasLimit) })
.propose(...args, { gasLimit: calculateGasMargin(estimatedGasLimit) })
.then((response: TransactionResponse) => {
addTransaction(response, {
type: TransactionType.SUBMIT_PROPOSAL,

@ -2,7 +2,6 @@ import { Interface } from '@ethersproject/abi'
import { Currency, CurrencyAmount, Ether, Token } from '@uniswap/sdk-core'
import ERC20ABI from 'abis/erc20.json'
import { Erc20Interface } from 'abis/types/Erc20'
import { SupportedChainId } from 'constants/chains'
import JSBI from 'jsbi'
import { useMemo } from 'react'
@ -52,11 +51,6 @@ 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.
*/
@ -69,12 +63,10 @@ export function useTokenBalancesWithLoadingIndicator(
[tokens]
)
const { chainId } = useActiveWeb3React()
const validatedTokenAddresses = useMemo(() => validatedTokens.map((vt) => vt.address), [validatedTokens])
const ERC20Interface = new Interface(ERC20ABI) as Erc20Interface
const balances = useMultipleContractSingleData(validatedTokenAddresses, ERC20Interface, 'balanceOf', [address], {
gasRequired: (chainId && TOKEN_BALANCE_GAS_OVERRIDE[chainId]) ?? 100_000,
gasRequired: 100_000,
})
const anyLoading: boolean = useMemo(() => balances.some((callState) => callState.loading), [balances])

@ -4,12 +4,7 @@ import { calculateGasMargin } from './calculateGasMargin'
describe('#calculateGasMargin', () => {
it('adds 20%', () => {
expect(calculateGasMargin(1, BigNumber.from(1000)).toString()).toEqual('1200')
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')
expect(calculateGasMargin(BigNumber.from(1000)).toString()).toEqual('1200')
expect(calculateGasMargin(BigNumber.from(50)).toString()).toEqual('60')
})
})

@ -1,9 +1,9 @@
import { BigNumber } from '@ethersproject/bignumber'
import { SupportedChainId } from 'constants/chains'
// add 20% (except on optimism)
export function calculateGasMargin(chainId: number, value: BigNumber): BigNumber {
return chainId === SupportedChainId.OPTIMISM || chainId === SupportedChainId.OPTIMISTIC_KOVAN
? value
: value.mul(BigNumber.from(10000 + 2000)).div(BigNumber.from(10000))
/**
* Returns the gas value plus a margin for unexpected or variable gas costs
* @param value the gas value to pad
*/
export function calculateGasMargin(value: BigNumber): BigNumber {
return value.mul(120).div(100)
}

@ -4642,6 +4642,18 @@
resolved "https://registry.npmjs.org/@uniswap/v3-core/-/v3-core-1.0.0.tgz"
integrity sha512-kSC4djMGKMHj7sLMYVnn61k9nu+lHjMIxgg9CDQT+s2QYLoA56GbSK9Oxr+qJXzzygbkrmuY6cwgP6cW2JXPFA==
"@uniswap/v3-periphery@^1.0.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.2.1.tgz#7775630bea774a2cf989ab87ce3c328ac52e0d50"
integrity sha512-45W8hT8X1j9ZcXa+y3NSVao90hMZtgtoJyDlMOg91wmUGi2idXAiGivOQYdB7+7Lq8Gc6Upv/ggFZknixZrv7g==
dependencies:
"@openzeppelin/contracts" "3.4.1-solc-0.7-2"
"@uniswap/lib" "^4.0.1-alpha"
"@uniswap/v2-core" "1.0.1"
"@uniswap/v3-core" "1.0.0"
base64-sol "1.0.1"
hardhat-watcher "^2.1.1"
"@uniswap/v3-periphery@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.1.1.tgz#be6dfca7b29318ea0d76a7baf15d3b33c3c5e90a"
@ -4654,18 +4666,28 @@
base64-sol "1.0.1"
hardhat-watcher "^2.1.1"
"@uniswap/v3-sdk@^3.4.1":
version "3.4.1"
resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-3.4.1.tgz#2cf9b5f4dd826d6600245254e7bc7d2d4a4282ee"
integrity sha512-P0zcgOgpSqEbI/2DVESm8kf8OydagMEAzZR7qqLOe4JsUyhK1A4u1dy8tYDgWUBW0WeruEXSPNGiEL0pYPy3HQ==
"@uniswap/v3-sdk@3.6.3":
version "3.6.3"
resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-3.6.3.tgz#f2fca86cfde1450976581028195fc0ee4b11036d"
integrity sha512-nepNTZMpM1uwLJAlQaUUEDMFrSujS1sOqyVD739zoPWsVHAUPvqVAKQN0GlgZcLXOqeCMKjO3lteaNHEXef1XA==
dependencies:
"@ethersproject/abi" "^5.0.12"
"@ethersproject/solidity" "^5.0.9"
"@uniswap/sdk-core" "^3.0.1"
"@uniswap/v3-periphery" "^1.1.1"
"@uniswap/v3-staker" "1.0.0"
tiny-invariant "^1.1.0"
tiny-warning "^1.0.3"
"@uniswap/v3-staker@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@uniswap/v3-staker/-/v3-staker-1.0.0.tgz#9a6915ec980852479dfc903f50baf822ff8fa66e"
integrity sha512-JV0Qc46Px5alvg6YWd+UIaGH9lDuYG/Js7ngxPit1SPaIP30AlVer1UYB7BRYeUVVxE+byUyIeN5jeQ7LLDjIw==
dependencies:
"@openzeppelin/contracts" "3.4.1-solc-0.7-2"
"@uniswap/v3-core" "1.0.0"
"@uniswap/v3-periphery" "^1.0.1"
"@vibrant/color@^3.2.1-alpha.1":
version "3.2.1-alpha.1"
resolved "https://registry.yarnpkg.com/@vibrant/color/-/color-3.2.1-alpha.1.tgz#1bcee4545d2276d36f9a1acb42ab3485a9b489ec"