import { useState, useMemo, useCallback, useEffect, useRef } from 'react' import { useWeb3React as useWeb3ReactCore } from '@web3-react/core' import { isMobile } from 'react-device-detect' import copy from 'copy-to-clipboard' import ERC20_ABI from '../constants/abis/erc20.json' import { injected } from '../connectors' import { NetworkContextName } from '../constants' import { getContract, getExchangeContract, isAddress } from '../utils' export function useWeb3React() { const context = useWeb3ReactCore() const contextNetwork = useWeb3ReactCore(NetworkContextName) 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 { if (isMobile && (window as any).ethereum) { activate(injected, undefined, true).catch(() => { setTried(true) }) } else { setTried(true) } } }) }, [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(() => { const { ethereum } = window as any if (ethereum && ethereum.on && !active && !error && !suppress) { const handleChainChanged = () => { // eat errors activate(injected, undefined, true).catch(() => {}) } const handleAccountsChanged = accounts => { if (accounts.length > 0) { // eat errors activate(injected, undefined, true).catch(() => {}) } } const handleNetworkChanged = () => { // eat errors activate(injected, undefined, true).catch(() => {}) } ethereum.on('chainChanged', handleChainChanged) ethereum.on('networkChanged', handleNetworkChanged) ethereum.on('accountsChanged', handleAccountsChanged) return () => { if (ethereum.removeListener) { ethereum.removeListener('chainChanged', handleChainChanged) ethereum.removeListener('networkChanged', handleNetworkChanged) ethereum.removeListener('accountsChanged', handleAccountsChanged) } } } return () => {} }, [active, error, suppress, activate]) } // modified from https://usehooks.com/useDebounce/ export function useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] = useState(value) 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 } // modified from https://usehooks.com/useKeyPress/ export function useBodyKeyDown(targetKey, onKeyDown, suppressOnKeyDown = false) { const downHandler = useCallback( event => { const { target: { tagName }, key } = event if (key === targetKey && tagName === 'BODY' && !suppressOnKeyDown) { event.preventDefault() onKeyDown() } }, [targetKey, onKeyDown, suppressOnKeyDown] ) useEffect(() => { window.addEventListener('keydown', downHandler) return () => { window.removeEventListener('keydown', downHandler) } }, [downHandler]) } export function useENSName(address) { const { library } = useWeb3React() const [ENSName, setENSName] = useState(null) useEffect(() => { if (isAddress(address)) { let stale = false library .lookupAddress(address) .then(name => { if (!stale) { if (name) { setENSName(name) } else { setENSName(null) } } }) .catch(() => { if (!stale) { setENSName(null) } }) return () => { stale = true setENSName(null) } } }, [library, address]) return ENSName } // returns null on errors export function useContract(address, ABI, withSignerIfPossible = true) { const { library, account } = useWeb3React() return useMemo(() => { try { return getContract(address, ABI, library, withSignerIfPossible ? account : undefined) } catch { return null } }, [address, ABI, library, withSignerIfPossible, account]) } // returns null on errors export function useTokenContract(tokenAddress, withSignerIfPossible = true) { const { library, account } = useWeb3React() return useMemo(() => { try { return getContract(tokenAddress, ERC20_ABI, library, withSignerIfPossible ? account : undefined) } catch { return null } }, [tokenAddress, library, withSignerIfPossible, account]) } export function usePairContract(pairAddress, withSignerIfPossible = true) { const { library, account } = useWeb3React() return useMemo(() => { try { return getExchangeContract(pairAddress, library, withSignerIfPossible ? account : undefined) } catch { return null } }, [pairAddress, library, withSignerIfPossible, account]) } export function useCopyClipboard(timeout = 500) { 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] } // 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 } export function useToggle(initialState = false): [boolean, () => void] { const [state, setState] = useState(initialState) const toggle = useCallback(() => setState(state => !state), []) return [state, toggle] }