chunk multicall by gas cost
add useAllV3Ticks
This commit is contained in:
parent
edf4c47451
commit
a037595e6e
@ -49,7 +49,7 @@
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v2-sdk": "^1.0.6",
|
||||
"@uniswap/v3-core": "^1.0.0-rc.0",
|
||||
"@uniswap/v3-periphery": "^1.0.0-beta.11",
|
||||
"@uniswap/v3-periphery": "^1.0.0-beta.12",
|
||||
"@web3-react/core": "^6.0.9",
|
||||
"@web3-react/fortmatic-connector": "^6.0.9",
|
||||
"@web3-react/injected-connector": "^6.0.7",
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { ChainId } from '@uniswap/sdk-core'
|
||||
import MULTICALL_ABI from './abi.json'
|
||||
|
||||
const MULTICALL_NETWORKS: { [chainId in ChainId]: string } = {
|
||||
const MULTICALL_NETWORKS: { [chainId in ChainId | 1337]: string } = {
|
||||
[ChainId.MAINNET]: '0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441',
|
||||
[ChainId.ROPSTEN]: '0x53C43764255c17BD724F74c4eF150724AC50a3ed',
|
||||
[ChainId.KOVAN]: '0x2cc8688C5f75E365aaEEb4ea8D6a480405A48D2A',
|
||||
[ChainId.RINKEBY]: '0x42Ad527de7d4e9d9d011aC45B31D8551f8Fe9821',
|
||||
[ChainId.GÖRLI]: '0x77dCa2C955b15e9dE4dbBCf1246B4B85b651e50e',
|
||||
[1337]: '0xFeabCc62240297F1e4b238937D68e7516f0918D7',
|
||||
}
|
||||
|
||||
export { MULTICALL_ABI, MULTICALL_NETWORKS }
|
||||
|
@ -1,12 +1,30 @@
|
||||
import { ChainId } from '@uniswap/sdk-core'
|
||||
|
||||
export const FACTORY_ADDRESSES: { [chainId in ChainId | 1337]: string } = {
|
||||
[ChainId.MAINNET]: '',
|
||||
[ChainId.ROPSTEN]: '',
|
||||
[ChainId.RINKEBY]: '',
|
||||
[ChainId.GÖRLI]: '',
|
||||
[ChainId.KOVAN]: '',
|
||||
[1337]: '0x49A6d0854B0fF95065f0dA247b8a2d440D92D2c7',
|
||||
}
|
||||
|
||||
export const TICK_LENS_ADDRESSES: { [chainId in ChainId | 1337]: string } = {
|
||||
[ChainId.MAINNET]: '',
|
||||
[ChainId.ROPSTEN]: '',
|
||||
[ChainId.RINKEBY]: '',
|
||||
[ChainId.GÖRLI]: '',
|
||||
[ChainId.KOVAN]: '',
|
||||
[1337]: '0xe0507a63E40Ce227CbF2ed7273a01066bAFE667B',
|
||||
}
|
||||
|
||||
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: { [chainId in ChainId | 1337]: string } = {
|
||||
[ChainId.MAINNET]: '',
|
||||
[ChainId.ROPSTEN]: '',
|
||||
[ChainId.RINKEBY]: '',
|
||||
[ChainId.GÖRLI]: '',
|
||||
[ChainId.KOVAN]: '',
|
||||
[1337]: '0xee9e30637f84Bbf929042A9118c6E20023dab833',
|
||||
[1337]: '0x273Edaa13C845F605b5886Dd66C89AB497A6B17b',
|
||||
}
|
||||
|
||||
export const NONFUNGIBLE_TOKEN_POSITION_DESCRIPTOR_ADDRESSES: { [chainId in ChainId | 1337]: string } = {
|
||||
@ -15,7 +33,7 @@ export const NONFUNGIBLE_TOKEN_POSITION_DESCRIPTOR_ADDRESSES: { [chainId in Chai
|
||||
[ChainId.RINKEBY]: '',
|
||||
[ChainId.GÖRLI]: '',
|
||||
[ChainId.KOVAN]: '',
|
||||
[1337]: '0x3431b9Ed12e3204bC6f7039e1c576417B70fdD67',
|
||||
[1337]: '0x3d137e860008BaF6d1c063158e5ec0baBbcFefF8',
|
||||
}
|
||||
|
||||
export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId | 1337]: string } = {
|
||||
@ -24,5 +42,5 @@ export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId | 1337]: string } = {
|
||||
[ChainId.RINKEBY]: '',
|
||||
[ChainId.GÖRLI]: '',
|
||||
[ChainId.KOVAN]: '',
|
||||
[1337]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f',
|
||||
[1337]: '0x80AacDBEe92DC1c2Fbaa261Fb369696AF1AD9f98',
|
||||
}
|
||||
|
48
src/hooks/useAllV3Ticks.ts
Normal file
48
src/hooks/useAllV3Ticks.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { Result, useSingleContractMultipleData } from 'state/multicall/hooks'
|
||||
import { useTickLens } from './useContract'
|
||||
|
||||
// the following should probably all be from the sdk, just mocking it for now
|
||||
function MIN_TICK(tickSpacing: number) {
|
||||
return Math.ceil(-887272 / tickSpacing) * tickSpacing
|
||||
}
|
||||
|
||||
function MAX_TICK(tickSpacing: number) {
|
||||
return Math.floor(887272 / tickSpacing) * tickSpacing
|
||||
}
|
||||
|
||||
function bitmapIndex(tick: number, tickSpacing: number) {
|
||||
return (tick / tickSpacing) >> 8
|
||||
}
|
||||
|
||||
// todo this hook needs some tlc around return values
|
||||
export function useAllV3Ticks(poolAddress: string, tickSpacing: number): Result[] | null {
|
||||
const tickLens = useTickLens()
|
||||
|
||||
const min = MIN_TICK(tickSpacing)
|
||||
const max = MAX_TICK(tickSpacing)
|
||||
const minIndex = bitmapIndex(min, tickSpacing)
|
||||
const maxIndex = bitmapIndex(max, tickSpacing)
|
||||
|
||||
const tickLensArgs = new Array(maxIndex - minIndex + 1)
|
||||
.fill(0)
|
||||
.map((_, i) => i + minIndex)
|
||||
.map((wordIndex) => [poolAddress, wordIndex])
|
||||
|
||||
const callStates = useSingleContractMultipleData(
|
||||
tickLens,
|
||||
'getPopulatedTicksInWord',
|
||||
tickLensArgs,
|
||||
undefined,
|
||||
2_000_000
|
||||
)
|
||||
|
||||
const canReturn = callStates.every(
|
||||
(callState) => !callState.error && !callState.loading && !callState.syncing && callState.valid && callState.result
|
||||
)
|
||||
|
||||
return canReturn
|
||||
? callStates
|
||||
.map(({ result }) => (result as Result).populatedTicks)
|
||||
.reduce((accumulator, current) => [...accumulator, ...current], [])
|
||||
: null
|
||||
}
|
@ -6,6 +6,9 @@ import { abi as MERKLE_DISTRIBUTOR_ABI } from '@uniswap/merkle-distributor/build
|
||||
import { ChainId, WETH9 } from '@uniswap/sdk-core'
|
||||
import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
|
||||
import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
|
||||
import { abi as V3FactoryABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json'
|
||||
import { abi as TickLensABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/TickLens.sol/TickLens.json'
|
||||
|
||||
import ARGENT_WALLET_DETECTOR_ABI from 'abis/argent-wallet-detector.json'
|
||||
import ENS_PUBLIC_RESOLVER_ABI from 'abis/ens-public-resolver.json'
|
||||
import ENS_ABI from 'abis/ens-registrar.json'
|
||||
@ -24,8 +27,9 @@ import {
|
||||
} from 'constants/index'
|
||||
import { MULTICALL_ABI, MULTICALL_NETWORKS } from 'constants/multicall'
|
||||
import { V1_EXCHANGE_ABI, V1_FACTORY_ABI, V1_FACTORY_ADDRESSES } from 'constants/v1'
|
||||
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from 'constants/v3'
|
||||
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, FACTORY_ADDRESSES, TICK_LENS_ADDRESSES } from 'constants/v3'
|
||||
import { useMemo } from 'react'
|
||||
import { TickLens, UniswapV3Factory } from 'types/v3'
|
||||
import { NonfungiblePositionManager } from 'types/v3/NonfungiblePositionManager'
|
||||
import { getContract } from 'utils'
|
||||
import { useActiveWeb3React } from './index'
|
||||
@ -141,3 +145,15 @@ export function useV3NFTPositionManagerContract(): NonfungiblePositionManager |
|
||||
const address = chainId ? NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[chainId] : undefined
|
||||
return useContract(address, NFTPositionManagerABI) as NonfungiblePositionManager | null
|
||||
}
|
||||
|
||||
export function useV3Factory(): UniswapV3Factory | null {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const address = chainId ? FACTORY_ADDRESSES[chainId] : undefined
|
||||
return useContract(address, V3FactoryABI) as UniswapV3Factory | null
|
||||
}
|
||||
|
||||
export function useTickLens(): TickLens | null {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const address = chainId ? TICK_LENS_ADDRESSES[chainId] : undefined
|
||||
return useContract(address, TickLensABI) as TickLens | null
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { createAction } from '@reduxjs/toolkit'
|
||||
export interface Call {
|
||||
address: string
|
||||
callData: string
|
||||
gasRequired?: number
|
||||
}
|
||||
|
||||
const ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/
|
||||
@ -14,17 +15,25 @@ export function toCallKey(call: Call): string {
|
||||
if (!LOWER_HEX_REGEX.test(call.callData)) {
|
||||
throw new Error(`Invalid hex: ${call.callData}`)
|
||||
}
|
||||
return `${call.address}-${call.callData}`
|
||||
let key = `${call.address}-${call.callData}`
|
||||
if (call.gasRequired) {
|
||||
if (!Number.isSafeInteger(call.gasRequired)) {
|
||||
throw new Error(`Invalid number: ${call.gasRequired}`)
|
||||
}
|
||||
key += `-${call.gasRequired}`
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
export function parseCallKey(callKey: string): Call {
|
||||
const pcs = callKey.split('-')
|
||||
if (pcs.length !== 2) {
|
||||
if (![2, 3].includes(pcs.length)) {
|
||||
throw new Error(`Invalid call key: ${callKey}`)
|
||||
}
|
||||
return {
|
||||
address: pcs[0],
|
||||
callData: pcs[1],
|
||||
...(pcs[2] ? { gasRequired: Number.parseInt(pcs[2]) } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,7 +164,8 @@ export function useSingleContractMultipleData(
|
||||
contract: Contract | null | undefined,
|
||||
methodName: string,
|
||||
callInputs: OptionalMethodInputs[],
|
||||
options?: ListenerOptions
|
||||
options?: ListenerOptions,
|
||||
gasRequired?: number
|
||||
): CallState[] {
|
||||
const fragment = useMemo(() => contract?.interface?.getFunction(methodName), [contract, methodName])
|
||||
|
||||
@ -175,10 +176,11 @@ export function useSingleContractMultipleData(
|
||||
return {
|
||||
address: contract.address,
|
||||
callData: contract.interface.encodeFunctionData(fragment, inputs),
|
||||
...(gasRequired ? { gasRequired } : {}),
|
||||
}
|
||||
})
|
||||
: [],
|
||||
[callInputs, contract, fragment]
|
||||
[callInputs, contract, fragment, gasRequired]
|
||||
)
|
||||
|
||||
const results = useCallsData(calls, options)
|
||||
@ -195,7 +197,8 @@ export function useMultipleContractSingleData(
|
||||
contractInterface: Interface,
|
||||
methodName: string,
|
||||
callInputs?: OptionalMethodInputs,
|
||||
options?: ListenerOptions
|
||||
options?: ListenerOptions,
|
||||
gasRequired?: number
|
||||
): CallState[] {
|
||||
const fragment = useMemo(() => contractInterface.getFunction(methodName), [contractInterface, methodName])
|
||||
const callData: string | undefined = useMemo(
|
||||
@ -214,11 +217,12 @@ export function useMultipleContractSingleData(
|
||||
? {
|
||||
address,
|
||||
callData,
|
||||
...(gasRequired ? { gasRequired } : {}),
|
||||
}
|
||||
: undefined
|
||||
})
|
||||
: [],
|
||||
[addresses, callData, fragment]
|
||||
[addresses, callData, fragment, gasRequired]
|
||||
)
|
||||
|
||||
const results = useCallsData(calls, options)
|
||||
@ -234,7 +238,8 @@ export function useSingleCallResult(
|
||||
contract: Contract | null | undefined,
|
||||
methodName: string,
|
||||
inputs?: OptionalMethodInputs,
|
||||
options?: ListenerOptions
|
||||
options?: ListenerOptions,
|
||||
gasRequired?: number
|
||||
): CallState {
|
||||
const fragment = useMemo(() => contract?.interface?.getFunction(methodName), [contract, methodName])
|
||||
|
||||
@ -244,10 +249,11 @@ export function useSingleCallResult(
|
||||
{
|
||||
address: contract.address,
|
||||
callData: contract.interface.encodeFunctionData(fragment, inputs),
|
||||
...(gasRequired ? { gasRequired } : {}),
|
||||
},
|
||||
]
|
||||
: []
|
||||
}, [contract, fragment, inputs])
|
||||
}, [contract, fragment, inputs, gasRequired])
|
||||
|
||||
const result = useCallsData(calls, options)[0]
|
||||
const latestBlockNumber = useBlockNumber()
|
||||
|
@ -16,9 +16,6 @@ import {
|
||||
updateMulticallResults,
|
||||
} from './actions'
|
||||
|
||||
// chunk calls so we do not exceed the gas limit
|
||||
const CALL_CHUNK_SIZE = 500
|
||||
|
||||
/**
|
||||
* Fetches a chunk of calls, enforcing a minimum block number constraint
|
||||
* @param multicallContract multicall contract to fetch against
|
||||
@ -141,7 +138,7 @@ export default function Updater(): null {
|
||||
if (outdatedCallKeys.length === 0) return
|
||||
const calls = outdatedCallKeys.map((key) => parseCallKey(key))
|
||||
|
||||
const chunkedCalls = chunkArray(calls, CALL_CHUNK_SIZE)
|
||||
const chunkedCalls = chunkArray(calls)
|
||||
|
||||
if (cancellations.current?.blockNumber !== latestBlockNumber) {
|
||||
cancellations.current?.cancellations?.forEach((c) => c())
|
||||
|
@ -1,32 +1,17 @@
|
||||
import chunkArray from './chunkArray'
|
||||
import chunkArray, { DEFAULT_GAS_REQUIRED } from './chunkArray'
|
||||
|
||||
describe('#chunkArray', () => {
|
||||
it('size 1', () => {
|
||||
expect(chunkArray([1, 2, 3], 1)).toEqual([[1], [2], [3]])
|
||||
expect(chunkArray([1, 2, 3], DEFAULT_GAS_REQUIRED)).toEqual([[1], [2], [3]])
|
||||
})
|
||||
it('size 0 throws', () => {
|
||||
expect(() => chunkArray([1, 2, 3], 0)).toThrow('maxChunkSize must be gte 1')
|
||||
})
|
||||
it('size gte items', () => {
|
||||
expect(chunkArray([1, 2, 3], 3)).toEqual([[1, 2, 3]])
|
||||
expect(chunkArray([1, 2, 3], 4)).toEqual([[1, 2, 3]])
|
||||
it('size gt items', () => {
|
||||
expect(chunkArray([1, 2, 3], DEFAULT_GAS_REQUIRED * 3 + 1)).toEqual([[1, 2, 3]])
|
||||
})
|
||||
it('size exact half', () => {
|
||||
expect(chunkArray([1, 2, 3, 4], 2)).toEqual([
|
||||
expect(chunkArray([1, 2, 3, 4], DEFAULT_GAS_REQUIRED * 2 + 1)).toEqual([
|
||||
[1, 2],
|
||||
[3, 4],
|
||||
])
|
||||
})
|
||||
it('evenly distributes', () => {
|
||||
const chunked = chunkArray([...Array(100).keys()], 40)
|
||||
|
||||
expect(chunked).toEqual([
|
||||
[...Array(34).keys()],
|
||||
[...Array(34).keys()].map((i) => i + 34),
|
||||
[...Array(32).keys()].map((i) => i + 68),
|
||||
])
|
||||
|
||||
expect(chunked[0][0]).toEqual(0)
|
||||
expect(chunked[2][31]).toEqual(99)
|
||||
})
|
||||
})
|
||||
|
@ -1,11 +1,32 @@
|
||||
// chunks array into chunks of maximum size
|
||||
const CONSERVATIVE_BLOCK_GAS_LIMIT = 10_000_000 // conservative, hard-coded estimate of the current block gas limit
|
||||
export const DEFAULT_GAS_REQUIRED = 200_000 // the default value for calls that don't specify gasRequired
|
||||
|
||||
// chunks array into chunks
|
||||
// evenly distributes items among the chunks
|
||||
export default function chunkArray<T>(items: T[], maxChunkSize: number): T[][] {
|
||||
if (maxChunkSize < 1) throw new Error('maxChunkSize must be gte 1')
|
||||
if (items.length <= maxChunkSize) return [items]
|
||||
export default function chunkArray<T>(items: T[], gasLimit = CONSERVATIVE_BLOCK_GAS_LIMIT * 10): T[][] {
|
||||
const chunks: T[][] = []
|
||||
let currentChunk: T[] = []
|
||||
let currentChunkCumulativeGas = 0
|
||||
|
||||
const numChunks: number = Math.ceil(items.length / maxChunkSize)
|
||||
const chunkSize = Math.ceil(items.length / numChunks)
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i]
|
||||
|
||||
return [...Array(numChunks).keys()].map((ix) => items.slice(ix * chunkSize, ix * chunkSize + chunkSize))
|
||||
// calculate the gas required by the current item
|
||||
const gasRequired = (item as { gasRequired?: number })?.gasRequired ?? DEFAULT_GAS_REQUIRED
|
||||
|
||||
// if the current chunk is empty, or the current item wouldn't push it over the gas limit,
|
||||
// append the current item and increment the cumulative gas
|
||||
if (currentChunk.length === 0 || currentChunkCumulativeGas + gasRequired < gasLimit) {
|
||||
currentChunk.push(item)
|
||||
currentChunkCumulativeGas += gasRequired
|
||||
} else {
|
||||
// otherwise, push the current chunk and create a new chunk
|
||||
chunks.push(currentChunk)
|
||||
currentChunk = [item]
|
||||
currentChunkCumulativeGas = gasRequired
|
||||
}
|
||||
}
|
||||
if (currentChunk.length > 0) chunks.push(currentChunk)
|
||||
|
||||
return chunks
|
||||
}
|
||||
|
27
yarn.lock
27
yarn.lock
@ -4085,6 +4085,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/lib/-/lib-1.1.1.tgz#0afd29601846c16e5d082866cbb24a9e0758e6bc"
|
||||
integrity sha512-2yK7sLpKIT91TiS5sewHtOa7YuM8IuBXVl4GZv2jZFys4D2sY7K5vZh6MqD25TPA95Od+0YzCVq6cTF2IKrOmg==
|
||||
|
||||
"@uniswap/lib@^4.0.1-alpha":
|
||||
version "4.0.1-alpha"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/lib/-/lib-4.0.1-alpha.tgz#2881008e55f075344675b3bca93f020b028fbd02"
|
||||
integrity sha512-f6UIliwBbRsgVLxIaBANF6w09tYqc6Y/qXdsrbEmXHyFA7ILiKrIwRFXe1yOg8M3cksgVsO9N7yuL2DdCGQKBA==
|
||||
|
||||
"@uniswap/liquidity-staker@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/liquidity-staker/-/liquidity-staker-1.0.2.tgz#afed49d51c5f97411432e891a59a24817728dad6"
|
||||
@ -4120,6 +4125,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/v2-core/-/v2-core-1.0.0.tgz#e0fab91a7d53e8cafb5326ae4ca18351116b0844"
|
||||
integrity sha512-BJiXrBGnN8mti7saW49MXwxDBRFiWemGetE58q8zgfnPPzQKq55ADltEILqOt6VFZ22kVeVKbF8gVd8aY3l7pA==
|
||||
|
||||
"@uniswap/v2-core@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/v2-core/-/v2-core-1.0.1.tgz#af8f508bf183204779938969e2e54043e147d425"
|
||||
integrity sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q==
|
||||
|
||||
"@uniswap/v2-periphery@^1.1.0-beta.0":
|
||||
version "1.1.0-beta.0"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/v2-periphery/-/v2-periphery-1.1.0-beta.0.tgz#20a4ccfca22f1a45402303aedb5717b6918ebe6d"
|
||||
@ -4139,18 +4149,25 @@
|
||||
tiny-invariant "^1.1.0"
|
||||
tiny-warning "^1.0.3"
|
||||
|
||||
"@uniswap/v3-core@1.0.0-rc.1":
|
||||
version "1.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.0-rc.1.tgz#f2bbc483451364a951fba06eb2d978c6e8bdd58f"
|
||||
integrity sha512-4ET2H0a8p7nVBGFWfio9SHc+RA6UIXEvlTRIJNsDwjQvfs8Jq9lfJ+eSOUTGmiB8Vp8V5dWarLDBU/rDE159pQ==
|
||||
|
||||
"@uniswap/v3-core@^1.0.0-rc.0":
|
||||
version "1.0.0-rc.0"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.0-rc.0.tgz#eee325b11ab423b5e77af1ae6e9aec89fa51d7c5"
|
||||
integrity sha512-nMmAtXtU1B3pOsP1DMMBO4/CUwPlatVhPKoos1l8KvVKCO9wP+trMR/heYmcwILJVF4Em5EqUBvx6Jzd8oagAg==
|
||||
|
||||
"@uniswap/v3-periphery@^1.0.0-beta.11":
|
||||
version "1.0.0-beta.11"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.0.0-beta.11.tgz#5068eed1cafa591ebb69ee4e990b4ca38de602be"
|
||||
integrity sha512-/jUNxj+/oH9TDbXv4acJc4G4xLydYJnsFc5W+fYvPTMR89PT3RLXWYK8o8FaEr/2uDxnycjXfovHb1tgB/kgtg==
|
||||
"@uniswap/v3-periphery@^1.0.0-beta.12":
|
||||
version "1.0.0-beta.12"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.0.0-beta.12.tgz#f8c8010ec92582950127a068414e7d6682b247bc"
|
||||
integrity sha512-c6qUcr5V+EQ/2SqlyGax1N0drRkULfOwrnJcM+98MBzVbJe2q4PfRk9dnsfijqoN9hb4GMqnb/8Ahhs0u48fEA==
|
||||
dependencies:
|
||||
"@openzeppelin/contracts" "3.4.1-solc-0.7-2"
|
||||
"@uniswap/v3-core" "^1.0.0-rc.0"
|
||||
"@uniswap/lib" "^4.0.1-alpha"
|
||||
"@uniswap/v2-core" "1.0.1"
|
||||
"@uniswap/v3-core" "1.0.0-rc.1"
|
||||
|
||||
"@walletconnect/client@^1.1.1-alpha.0":
|
||||
version "1.3.6"
|
||||
|
Loading…
Reference in New Issue
Block a user