2020-05-17 00:55:22 +03:00
|
|
|
import { Contract } from '@ethersproject/contracts'
|
2020-05-12 01:23:01 +03:00
|
|
|
import { Web3Provider } from '@ethersproject/providers'
|
2019-11-26 01:56:31 +03:00
|
|
|
import { useState, useMemo, useCallback, useEffect, useRef } from 'react'
|
|
|
|
import { useWeb3React as useWeb3ReactCore } from '@web3-react/core'
|
2019-12-05 01:31:56 +03:00
|
|
|
import { isMobile } from 'react-device-detect'
|
2020-03-20 23:03:23 +03:00
|
|
|
import copy from 'copy-to-clipboard'
|
2019-04-25 19:12:47 +03:00
|
|
|
|
2020-05-08 01:52:26 +03:00
|
|
|
import ERC20_ABI from '../constants/abis/erc20.json'
|
2019-11-26 01:56:31 +03:00
|
|
|
import { injected } from '../connectors'
|
2020-03-20 23:03:23 +03:00
|
|
|
import { NetworkContextName } from '../constants'
|
2020-05-05 23:36:22 +03:00
|
|
|
import { getContract, getExchangeContract, isAddress } from '../utils'
|
2019-11-26 01:56:31 +03:00
|
|
|
|
2020-05-19 22:08:58 +03:00
|
|
|
export function useActiveWeb3React() {
|
2020-05-12 01:23:01 +03:00
|
|
|
const context = useWeb3ReactCore<Web3Provider>()
|
|
|
|
const contextNetwork = useWeb3ReactCore<Web3Provider>(NetworkContextName)
|
2019-11-26 01:56:31 +03:00
|
|
|
return context.active ? context : contextNetwork
|
|
|
|
}
|
|
|
|
|
|
|
|
export function useEagerConnect() {
|
|
|
|
const { activate, active } = useWeb3ReactCore() // specifically using useWeb3ReactCore because of what this hook does
|
|
|
|
const [tried, setTried] = useState(false)
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
injected.isAuthorized().then(isAuthorized => {
|
|
|
|
if (isAuthorized) {
|
|
|
|
activate(injected, undefined, true).catch(() => {
|
|
|
|
setTried(true)
|
|
|
|
})
|
|
|
|
} else {
|
2020-05-09 01:32:36 +03:00
|
|
|
if (isMobile && window.ethereum) {
|
2019-12-05 01:31:56 +03:00
|
|
|
activate(injected, undefined, true).catch(() => {
|
|
|
|
setTried(true)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
setTried(true)
|
|
|
|
}
|
2019-11-26 01:56:31 +03:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}, [activate]) // intentionally only running on mount (make sure it's only mounted once :))
|
|
|
|
|
|
|
|
// if the connection worked, wait until we get confirmation of that to flip the flag
|
|
|
|
useEffect(() => {
|
|
|
|
if (active) {
|
|
|
|
setTried(true)
|
|
|
|
}
|
|
|
|
}, [active])
|
|
|
|
|
|
|
|
return tried
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Use for network and injected - logs user in
|
|
|
|
* and out after checking what network theyre on
|
|
|
|
*/
|
|
|
|
export function useInactiveListener(suppress = false) {
|
|
|
|
const { active, error, activate } = useWeb3ReactCore() // specifically using useWeb3React because of what this hook does
|
|
|
|
|
|
|
|
useEffect(() => {
|
2020-05-09 01:32:36 +03:00
|
|
|
const { ethereum } = window
|
2019-11-26 01:56:31 +03:00
|
|
|
|
|
|
|
if (ethereum && ethereum.on && !active && !error && !suppress) {
|
2019-12-05 01:31:56 +03:00
|
|
|
const handleChainChanged = () => {
|
2019-11-26 01:56:31 +03:00
|
|
|
// eat errors
|
2020-05-09 01:32:36 +03:00
|
|
|
activate(injected, undefined, true).catch(error => {
|
|
|
|
console.log(error)
|
|
|
|
})
|
2019-11-26 01:56:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
const handleAccountsChanged = accounts => {
|
|
|
|
if (accounts.length > 0) {
|
|
|
|
// eat errors
|
2020-05-09 01:32:36 +03:00
|
|
|
activate(injected, undefined, true).catch(error => {
|
|
|
|
console.log(error)
|
|
|
|
})
|
2019-11-26 01:56:31 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-05 01:31:56 +03:00
|
|
|
const handleNetworkChanged = () => {
|
|
|
|
// eat errors
|
2020-05-09 01:32:36 +03:00
|
|
|
activate(injected, undefined, true).catch(error => {
|
|
|
|
console.log(error)
|
|
|
|
})
|
2019-12-05 01:31:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ethereum.on('chainChanged', handleChainChanged)
|
2019-11-26 01:56:31 +03:00
|
|
|
ethereum.on('networkChanged', handleNetworkChanged)
|
|
|
|
ethereum.on('accountsChanged', handleAccountsChanged)
|
|
|
|
|
|
|
|
return () => {
|
2019-12-05 01:31:56 +03:00
|
|
|
if (ethereum.removeListener) {
|
|
|
|
ethereum.removeListener('chainChanged', handleChainChanged)
|
|
|
|
ethereum.removeListener('networkChanged', handleNetworkChanged)
|
|
|
|
ethereum.removeListener('accountsChanged', handleAccountsChanged)
|
|
|
|
}
|
2019-11-26 01:56:31 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [active, error, suppress, activate])
|
|
|
|
}
|
2019-04-25 19:12:47 +03:00
|
|
|
|
2019-05-16 20:21:14 +03:00
|
|
|
// modified from https://usehooks.com/useDebounce/
|
2020-05-08 20:48:05 +03:00
|
|
|
export function useDebounce<T>(value: T, delay: number): T {
|
|
|
|
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
2019-05-16 20:21:14 +03:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
// Update debounced value after delay
|
|
|
|
const handler = setTimeout(() => {
|
|
|
|
setDebouncedValue(value)
|
|
|
|
}, delay)
|
|
|
|
|
|
|
|
// Cancel the timeout if value changes (also on delay change or unmount)
|
|
|
|
// This is how we prevent debounced value from updating if value is changed ...
|
|
|
|
// .. within the delay period. Timeout gets cleared and restarted.
|
|
|
|
return () => {
|
|
|
|
clearTimeout(handler)
|
|
|
|
}
|
|
|
|
}, [value, delay])
|
|
|
|
|
|
|
|
return debouncedValue
|
|
|
|
}
|
|
|
|
|
2019-05-03 23:37:59 +03:00
|
|
|
// modified from https://usehooks.com/useKeyPress/
|
|
|
|
export function useBodyKeyDown(targetKey, onKeyDown, suppressOnKeyDown = false) {
|
|
|
|
const downHandler = useCallback(
|
2019-05-30 23:42:25 +03:00
|
|
|
event => {
|
|
|
|
const {
|
|
|
|
target: { tagName },
|
|
|
|
key
|
|
|
|
} = event
|
2019-05-03 23:37:59 +03:00
|
|
|
if (key === targetKey && tagName === 'BODY' && !suppressOnKeyDown) {
|
2019-05-30 23:42:25 +03:00
|
|
|
event.preventDefault()
|
2019-05-03 23:37:59 +03:00
|
|
|
onKeyDown()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[targetKey, onKeyDown, suppressOnKeyDown]
|
|
|
|
)
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
window.addEventListener('keydown', downHandler)
|
|
|
|
return () => {
|
|
|
|
window.removeEventListener('keydown', downHandler)
|
|
|
|
}
|
|
|
|
}, [downHandler])
|
2019-04-25 19:12:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-30 23:42:25 +03:00
|
|
|
export function useENSName(address) {
|
2020-05-19 22:08:58 +03:00
|
|
|
const { library } = useActiveWeb3React()
|
2019-05-30 23:42:25 +03:00
|
|
|
|
2020-05-08 01:52:26 +03:00
|
|
|
const [ENSName, setENSName] = useState<string | null>(null)
|
2019-05-30 23:42:25 +03:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (isAddress(address)) {
|
|
|
|
let stale = false
|
2019-12-31 23:17:10 +03:00
|
|
|
library
|
|
|
|
.lookupAddress(address)
|
|
|
|
.then(name => {
|
2019-05-30 23:42:25 +03:00
|
|
|
if (!stale) {
|
|
|
|
if (name) {
|
2019-10-22 01:30:47 +03:00
|
|
|
setENSName(name)
|
2019-05-30 23:42:25 +03:00
|
|
|
} else {
|
2019-10-22 01:30:47 +03:00
|
|
|
setENSName(null)
|
2019-05-30 23:42:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2019-12-31 23:17:10 +03:00
|
|
|
.catch(() => {
|
|
|
|
if (!stale) {
|
|
|
|
setENSName(null)
|
|
|
|
}
|
|
|
|
})
|
2019-05-30 23:42:25 +03:00
|
|
|
|
|
|
|
return () => {
|
|
|
|
stale = true
|
2020-05-08 01:52:26 +03:00
|
|
|
setENSName(null)
|
2019-05-30 23:42:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [library, address])
|
|
|
|
|
|
|
|
return ENSName
|
|
|
|
}
|
|
|
|
|
2019-05-03 23:37:59 +03:00
|
|
|
// returns null on errors
|
|
|
|
export function useContract(address, ABI, withSignerIfPossible = true) {
|
2020-05-19 22:08:58 +03:00
|
|
|
const { library, account } = useActiveWeb3React()
|
2019-04-25 19:12:47 +03:00
|
|
|
|
|
|
|
return useMemo(() => {
|
|
|
|
try {
|
2019-05-03 23:37:59 +03:00
|
|
|
return getContract(address, ABI, library, withSignerIfPossible ? account : undefined)
|
2019-04-25 19:12:47 +03:00
|
|
|
} catch {
|
|
|
|
return null
|
|
|
|
}
|
2019-05-03 23:37:59 +03:00
|
|
|
}, [address, ABI, library, withSignerIfPossible, account])
|
2019-04-25 19:12:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-03 23:37:59 +03:00
|
|
|
// returns null on errors
|
2020-05-17 00:55:22 +03:00
|
|
|
export function useTokenContract(tokenAddress: string, withSignerIfPossible = true): Contract {
|
2020-05-19 22:08:58 +03:00
|
|
|
const { library, account } = useActiveWeb3React()
|
2019-04-25 19:12:47 +03:00
|
|
|
|
2019-05-03 23:37:59 +03:00
|
|
|
return useMemo(() => {
|
|
|
|
try {
|
|
|
|
return getContract(tokenAddress, ERC20_ABI, library, withSignerIfPossible ? account : undefined)
|
|
|
|
} catch {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
}, [tokenAddress, library, withSignerIfPossible, account])
|
2019-04-25 19:12:47 +03:00
|
|
|
}
|
|
|
|
|
2020-03-26 19:42:06 +03:00
|
|
|
export function usePairContract(pairAddress, withSignerIfPossible = true) {
|
2020-05-19 22:08:58 +03:00
|
|
|
const { library, account } = useActiveWeb3React()
|
2019-05-03 23:37:59 +03:00
|
|
|
|
|
|
|
return useMemo(() => {
|
|
|
|
try {
|
2020-03-26 19:42:06 +03:00
|
|
|
return getExchangeContract(pairAddress, library, withSignerIfPossible ? account : undefined)
|
2019-05-03 23:37:59 +03:00
|
|
|
} catch {
|
|
|
|
return null
|
2019-04-25 19:12:47 +03:00
|
|
|
}
|
2020-03-26 19:42:06 +03:00
|
|
|
}, [pairAddress, library, withSignerIfPossible, account])
|
2019-04-25 19:12:47 +03:00
|
|
|
}
|
2019-05-30 23:42:25 +03:00
|
|
|
|
2020-05-08 18:24:47 +03:00
|
|
|
export function useCopyClipboard(timeout = 500): [boolean, (toCopy: string) => void] {
|
2019-05-30 23:42:25 +03:00
|
|
|
const [isCopied, setIsCopied] = useState(false)
|
|
|
|
|
|
|
|
const staticCopy = useCallback(text => {
|
|
|
|
const didCopy = copy(text)
|
|
|
|
setIsCopied(didCopy)
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (isCopied) {
|
|
|
|
const hide = setTimeout(() => {
|
|
|
|
setIsCopied(false)
|
|
|
|
}, timeout)
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
clearTimeout(hide)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [isCopied, setIsCopied, timeout])
|
|
|
|
|
|
|
|
return [isCopied, staticCopy]
|
|
|
|
}
|
2019-11-26 01:56:31 +03:00
|
|
|
|
|
|
|
// modified from https://usehooks.com/usePrevious/
|
|
|
|
export function usePrevious(value) {
|
|
|
|
// The ref object is a generic container whose current property is mutable ...
|
|
|
|
// ... and can hold any value, similar to an instance property on a class
|
|
|
|
const ref = useRef()
|
|
|
|
|
|
|
|
// Store current value in ref
|
|
|
|
useEffect(() => {
|
|
|
|
ref.current = value
|
|
|
|
}, [value]) // Only re-run if value changes
|
|
|
|
|
|
|
|
// Return previous value (happens before update in useEffect above)
|
|
|
|
return ref.current
|
|
|
|
}
|
2020-03-11 17:44:39 +03:00
|
|
|
|
2020-05-08 01:52:26 +03:00
|
|
|
export function useToggle(initialState = false): [boolean, () => void] {
|
2020-03-11 17:44:39 +03:00
|
|
|
const [state, setState] = useState(initialState)
|
|
|
|
const toggle = useCallback(() => setState(state => !state), [])
|
|
|
|
return [state, toggle]
|
|
|
|
}
|