2021-03-17 11:44:26 +01:00
|
|
|
import React, {
|
|
|
|
useContext,
|
|
|
|
useState,
|
|
|
|
useEffect,
|
|
|
|
createContext,
|
|
|
|
ReactElement,
|
|
|
|
ReactNode,
|
|
|
|
useCallback
|
|
|
|
} from 'react'
|
|
|
|
import Web3 from 'web3'
|
2021-05-17 16:12:22 +02:00
|
|
|
import Web3Modal, { getProviderInfo, IProviderInfo } from 'web3modal'
|
2021-03-17 11:44:26 +01:00
|
|
|
import { infuraProjectId as infuraId, portisId } from '../../app.config'
|
|
|
|
import WalletConnectProvider from '@walletconnect/web3-provider'
|
|
|
|
import { Logger } from '@oceanprotocol/lib'
|
2021-10-18 20:44:33 +02:00
|
|
|
import { isBrowser } from '@utils/index'
|
|
|
|
import { getEnsName } from '@utils/ens'
|
|
|
|
import { getOceanBalance } from '@utils/ocean'
|
2021-10-19 14:06:16 +02:00
|
|
|
import useNetworkMetadata, {
|
|
|
|
getNetworkDataById,
|
|
|
|
getNetworkDisplayName
|
|
|
|
} from '@hooks/useNetworkMetadata'
|
2021-03-17 11:44:26 +01:00
|
|
|
|
|
|
|
interface Web3ProviderValue {
|
|
|
|
web3: Web3
|
|
|
|
web3Provider: any
|
|
|
|
web3Modal: Web3Modal
|
2021-05-17 16:12:22 +02:00
|
|
|
web3ProviderInfo: IProviderInfo
|
2021-03-17 11:44:26 +01:00
|
|
|
accountId: string
|
2021-09-20 13:47:15 +02:00
|
|
|
accountEns: string
|
2021-05-31 14:27:04 +02:00
|
|
|
balance: UserBalance
|
2021-03-17 11:44:26 +01:00
|
|
|
networkId: number
|
2021-07-22 15:16:15 +02:00
|
|
|
chainId: number
|
2021-03-17 11:44:26 +01:00
|
|
|
networkDisplayName: string
|
|
|
|
networkData: EthereumListsChain
|
2021-04-13 10:57:59 +02:00
|
|
|
block: number
|
2021-03-17 11:44:26 +01:00
|
|
|
isTestnet: boolean
|
2021-04-13 10:57:59 +02:00
|
|
|
web3Loading: boolean
|
2021-03-17 11:44:26 +01:00
|
|
|
connect: () => Promise<void>
|
|
|
|
logout: () => Promise<void>
|
|
|
|
}
|
|
|
|
|
|
|
|
const web3ModalTheme = {
|
|
|
|
background: 'var(--background-body)',
|
|
|
|
main: 'var(--font-color-heading)',
|
|
|
|
secondary: 'var(--brand-grey-light)',
|
|
|
|
border: 'var(--border-color)',
|
|
|
|
hover: 'var(--background-highlight)'
|
|
|
|
}
|
|
|
|
|
2021-10-27 12:27:14 +02:00
|
|
|
// HEADS UP! We inline-require some packages so the SSR build does not break.
|
2021-03-17 11:44:26 +01:00
|
|
|
// We only need them client-side.
|
|
|
|
const providerOptions = isBrowser
|
|
|
|
? {
|
|
|
|
walletconnect: {
|
|
|
|
package: WalletConnectProvider,
|
|
|
|
options: { infuraId }
|
|
|
|
},
|
|
|
|
portis: {
|
|
|
|
package: require('@portis/web3'),
|
|
|
|
options: {
|
|
|
|
id: portisId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// torus: {
|
|
|
|
// package: require('@toruslabs/torus-embed')
|
|
|
|
// // options: {
|
|
|
|
// // networkParams: {
|
|
|
|
// // host: oceanConfig.url, // optional
|
|
|
|
// // chainId: 1337, // optional
|
|
|
|
// // networkId: 1337 // optional
|
|
|
|
// // }
|
|
|
|
// // }
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
: {}
|
|
|
|
|
|
|
|
export const web3ModalOpts = {
|
|
|
|
cacheProvider: true,
|
|
|
|
providerOptions,
|
|
|
|
theme: web3ModalTheme
|
|
|
|
}
|
|
|
|
|
2021-05-31 14:27:04 +02:00
|
|
|
const refreshInterval = 20000 // 20 sec.
|
|
|
|
|
2021-03-17 11:44:26 +01:00
|
|
|
const Web3Context = createContext({} as Web3ProviderValue)
|
|
|
|
|
|
|
|
function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
2021-06-14 20:04:44 +02:00
|
|
|
const { networksList } = useNetworkMetadata()
|
2021-03-17 11:44:26 +01:00
|
|
|
|
|
|
|
const [web3, setWeb3] = useState<Web3>()
|
|
|
|
const [web3Provider, setWeb3Provider] = useState<any>()
|
|
|
|
const [web3Modal, setWeb3Modal] = useState<Web3Modal>()
|
2021-05-17 16:12:22 +02:00
|
|
|
const [web3ProviderInfo, setWeb3ProviderInfo] = useState<IProviderInfo>()
|
2021-03-17 11:44:26 +01:00
|
|
|
const [networkId, setNetworkId] = useState<number>()
|
2021-07-22 15:16:15 +02:00
|
|
|
const [chainId, setChainId] = useState<number>()
|
2021-03-17 11:44:26 +01:00
|
|
|
const [networkDisplayName, setNetworkDisplayName] = useState<string>()
|
|
|
|
const [networkData, setNetworkData] = useState<EthereumListsChain>()
|
2021-04-13 10:57:59 +02:00
|
|
|
const [block, setBlock] = useState<number>()
|
2021-03-17 11:44:26 +01:00
|
|
|
const [isTestnet, setIsTestnet] = useState<boolean>()
|
|
|
|
const [accountId, setAccountId] = useState<string>()
|
2021-09-20 13:47:15 +02:00
|
|
|
const [accountEns, setAccountEns] = useState<string>()
|
2021-04-15 12:43:12 +02:00
|
|
|
const [web3Loading, setWeb3Loading] = useState<boolean>(true)
|
2021-05-31 14:27:04 +02:00
|
|
|
const [balance, setBalance] = useState<UserBalance>({
|
|
|
|
eth: '0',
|
|
|
|
ocean: '0'
|
|
|
|
})
|
2021-03-17 11:44:26 +01:00
|
|
|
|
2021-05-31 14:27:04 +02:00
|
|
|
// -----------------------------------
|
|
|
|
// Helper: connect to web3
|
|
|
|
// -----------------------------------
|
2021-03-17 11:44:26 +01:00
|
|
|
const connect = useCallback(async () => {
|
2021-04-15 12:43:12 +02:00
|
|
|
if (!web3Modal) {
|
|
|
|
setWeb3Loading(false)
|
|
|
|
return
|
|
|
|
}
|
2021-03-17 11:44:26 +01:00
|
|
|
try {
|
2021-04-13 10:57:59 +02:00
|
|
|
setWeb3Loading(true)
|
2021-03-17 11:44:26 +01:00
|
|
|
Logger.log('[web3] Connecting Web3...')
|
|
|
|
|
|
|
|
const provider = await web3Modal?.connect()
|
|
|
|
setWeb3Provider(provider)
|
|
|
|
|
|
|
|
const web3 = new Web3(provider)
|
|
|
|
setWeb3(web3)
|
|
|
|
Logger.log('[web3] Web3 created.', web3)
|
|
|
|
|
|
|
|
const networkId = await web3.eth.net.getId()
|
|
|
|
setNetworkId(networkId)
|
|
|
|
Logger.log('[web3] network id ', networkId)
|
|
|
|
|
2021-07-22 15:16:15 +02:00
|
|
|
const chainId = await web3.eth.getChainId()
|
|
|
|
setChainId(chainId)
|
|
|
|
Logger.log('[web3] chain id ', chainId)
|
|
|
|
|
2021-03-17 11:44:26 +01:00
|
|
|
const accountId = (await web3.eth.getAccounts())[0]
|
|
|
|
setAccountId(accountId)
|
|
|
|
Logger.log('[web3] account id', accountId)
|
|
|
|
} catch (error) {
|
|
|
|
Logger.error('[web3] Error: ', error.message)
|
2021-04-13 10:57:59 +02:00
|
|
|
} finally {
|
|
|
|
setWeb3Loading(false)
|
2021-03-17 11:44:26 +01:00
|
|
|
}
|
|
|
|
}, [web3Modal])
|
|
|
|
|
2021-05-31 14:27:04 +02:00
|
|
|
// -----------------------------------
|
|
|
|
// Helper: Get user balance
|
|
|
|
// -----------------------------------
|
|
|
|
const getUserBalance = useCallback(async () => {
|
|
|
|
if (!accountId || !networkId || !web3) return
|
|
|
|
|
|
|
|
try {
|
|
|
|
const balance = {
|
|
|
|
eth: web3.utils.fromWei(await web3.eth.getBalance(accountId, 'latest')),
|
|
|
|
ocean: await getOceanBalance(accountId, networkId, web3)
|
|
|
|
}
|
|
|
|
setBalance(balance)
|
|
|
|
Logger.log('[web3] Balance: ', balance)
|
|
|
|
} catch (error) {
|
|
|
|
Logger.error('[web3] Error: ', error.message)
|
|
|
|
}
|
|
|
|
}, [accountId, networkId, web3])
|
|
|
|
|
2021-09-20 13:47:15 +02:00
|
|
|
// -----------------------------------
|
|
|
|
// Helper: Get user ENS name
|
|
|
|
// -----------------------------------
|
|
|
|
const getUserEnsName = useCallback(async () => {
|
|
|
|
if (!accountId) return
|
|
|
|
|
|
|
|
try {
|
|
|
|
// const accountEns = await getEnsNameWithWeb3(
|
|
|
|
// accountId,
|
|
|
|
// web3Provider,
|
|
|
|
// `${networkId}`
|
|
|
|
// )
|
|
|
|
const accountEns = await getEnsName(accountId)
|
|
|
|
setAccountEns(accountEns)
|
|
|
|
accountEns &&
|
|
|
|
Logger.log(`[web3] ENS name found for ${accountId}:`, accountEns)
|
|
|
|
} catch (error) {
|
|
|
|
Logger.error('[web3] Error: ', error.message)
|
|
|
|
}
|
|
|
|
}, [accountId])
|
|
|
|
|
2021-03-17 11:44:26 +01:00
|
|
|
// -----------------------------------
|
|
|
|
// Create initial Web3Modal instance
|
|
|
|
// -----------------------------------
|
|
|
|
useEffect(() => {
|
2021-05-19 10:31:23 +02:00
|
|
|
if (web3Modal) {
|
|
|
|
setWeb3Loading(false)
|
|
|
|
return
|
|
|
|
}
|
2021-03-17 11:44:26 +01:00
|
|
|
|
|
|
|
async function init() {
|
|
|
|
// note: needs artificial await here so the log message is reached and output
|
|
|
|
const web3ModalInstance = await new Web3Modal(web3ModalOpts)
|
|
|
|
setWeb3Modal(web3ModalInstance)
|
|
|
|
Logger.log('[web3] Web3Modal instance created.', web3ModalInstance)
|
|
|
|
}
|
|
|
|
init()
|
|
|
|
}, [connect, web3Modal])
|
|
|
|
|
|
|
|
// -----------------------------------
|
|
|
|
// Reconnect automatically for returning users
|
|
|
|
// -----------------------------------
|
|
|
|
useEffect(() => {
|
|
|
|
if (!web3Modal?.cachedProvider) return
|
|
|
|
|
|
|
|
async function connectCached() {
|
|
|
|
Logger.log(
|
|
|
|
'[web3] Connecting to cached provider: ',
|
|
|
|
web3Modal.cachedProvider
|
|
|
|
)
|
|
|
|
await connect()
|
|
|
|
}
|
|
|
|
connectCached()
|
|
|
|
}, [connect, web3Modal])
|
|
|
|
|
2021-05-31 14:27:04 +02:00
|
|
|
// -----------------------------------
|
|
|
|
// Get and set user balance
|
|
|
|
// -----------------------------------
|
|
|
|
useEffect(() => {
|
|
|
|
getUserBalance()
|
|
|
|
|
|
|
|
// init periodic refresh of wallet balance
|
|
|
|
const balanceInterval = setInterval(() => getUserBalance(), refreshInterval)
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
clearInterval(balanceInterval)
|
|
|
|
}
|
|
|
|
}, [getUserBalance])
|
|
|
|
|
2021-09-20 13:47:15 +02:00
|
|
|
// -----------------------------------
|
|
|
|
// Get and set user ENS name
|
|
|
|
// -----------------------------------
|
|
|
|
useEffect(() => {
|
|
|
|
getUserEnsName()
|
|
|
|
}, [getUserEnsName])
|
|
|
|
|
2021-03-17 11:44:26 +01:00
|
|
|
// -----------------------------------
|
|
|
|
// Get and set network metadata
|
|
|
|
// -----------------------------------
|
|
|
|
useEffect(() => {
|
|
|
|
if (!networkId) return
|
2021-06-09 19:41:12 +02:00
|
|
|
const networkData = getNetworkDataById(networksList, networkId)
|
2021-03-17 11:44:26 +01:00
|
|
|
setNetworkData(networkData)
|
2021-07-13 15:22:19 +02:00
|
|
|
Logger.log(
|
|
|
|
networkData
|
|
|
|
? `[web3] Network metadata found.`
|
|
|
|
: `[web3] No network metadata found.`,
|
|
|
|
networkData
|
|
|
|
)
|
2021-03-17 11:44:26 +01:00
|
|
|
|
|
|
|
// Construct network display name
|
|
|
|
const networkDisplayName = getNetworkDisplayName(networkData, networkId)
|
|
|
|
setNetworkDisplayName(networkDisplayName)
|
|
|
|
|
|
|
|
// Figure out if we're on a chain's testnet, or not
|
2021-10-06 11:30:17 +02:00
|
|
|
setIsTestnet(
|
|
|
|
networkData?.network !== 'mainnet' && networkData.network !== 'moonriver'
|
|
|
|
)
|
2021-03-17 11:44:26 +01:00
|
|
|
|
|
|
|
Logger.log(`[web3] Network display name set to: ${networkDisplayName}`)
|
|
|
|
}, [networkId, networksList])
|
|
|
|
|
2021-04-13 10:57:59 +02:00
|
|
|
// -----------------------------------
|
|
|
|
// Get and set latest head block
|
|
|
|
// -----------------------------------
|
|
|
|
useEffect(() => {
|
|
|
|
if (!web3) return
|
|
|
|
|
|
|
|
async function getBlock() {
|
|
|
|
const block = await web3.eth.getBlockNumber()
|
|
|
|
setBlock(block)
|
|
|
|
Logger.log('[web3] Head block: ', block)
|
|
|
|
}
|
|
|
|
getBlock()
|
|
|
|
}, [web3, networkId])
|
|
|
|
|
2021-05-17 16:12:22 +02:00
|
|
|
// -----------------------------------
|
|
|
|
// Get and set web3 provider info
|
|
|
|
// -----------------------------------
|
|
|
|
// Workaround cause getInjectedProviderName() always returns `MetaMask`
|
|
|
|
// https://github.com/oceanprotocol/market/issues/332
|
|
|
|
useEffect(() => {
|
|
|
|
if (!web3Provider) return
|
|
|
|
|
|
|
|
const providerInfo = getProviderInfo(web3Provider)
|
|
|
|
setWeb3ProviderInfo(providerInfo)
|
|
|
|
}, [web3Provider])
|
|
|
|
|
2021-03-17 11:44:26 +01:00
|
|
|
// -----------------------------------
|
|
|
|
// Logout helper
|
|
|
|
// -----------------------------------
|
|
|
|
async function logout() {
|
2021-07-22 15:16:15 +02:00
|
|
|
if (web3 && web3.currentProvider && (web3.currentProvider as any).close) {
|
|
|
|
await (web3.currentProvider as any).close()
|
|
|
|
}
|
|
|
|
await web3Modal.clearCachedProvider()
|
2021-03-17 11:44:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------
|
|
|
|
// Handle change events
|
|
|
|
// -----------------------------------
|
2021-07-22 15:16:15 +02:00
|
|
|
async function handleChainChanged(chainId: string) {
|
|
|
|
Logger.log('[web3] Chain changed', chainId)
|
|
|
|
const networkId = await web3.eth.net.getId()
|
|
|
|
setChainId(Number(chainId))
|
|
|
|
setNetworkId(Number(networkId))
|
|
|
|
}
|
|
|
|
|
2021-03-17 11:44:26 +01:00
|
|
|
async function handleNetworkChanged(networkId: string) {
|
|
|
|
Logger.log('[web3] Network changed', networkId)
|
2021-07-22 15:16:15 +02:00
|
|
|
const chainId = await web3.eth.getChainId()
|
2021-03-17 11:44:26 +01:00
|
|
|
setNetworkId(Number(networkId))
|
2021-07-22 15:16:15 +02:00
|
|
|
setChainId(Number(chainId))
|
2021-03-17 11:44:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async function handleAccountsChanged(accounts: string[]) {
|
|
|
|
Logger.log('[web3] Account changed', accounts[0])
|
|
|
|
setAccountId(accounts[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (!web3Provider || !web3) return
|
|
|
|
|
2021-07-22 15:16:15 +02:00
|
|
|
web3Provider.on('chainChanged', handleChainChanged)
|
2021-03-17 11:44:26 +01:00
|
|
|
web3Provider.on('networkChanged', handleNetworkChanged)
|
|
|
|
web3Provider.on('accountsChanged', handleAccountsChanged)
|
|
|
|
|
|
|
|
return () => {
|
2021-07-22 15:16:15 +02:00
|
|
|
web3Provider.removeListener('chainChanged', handleChainChanged)
|
|
|
|
web3Provider.removeListener('networkChanged', handleNetworkChanged)
|
|
|
|
web3Provider.removeListener('accountsChanged', handleAccountsChanged)
|
2021-03-17 11:44:26 +01:00
|
|
|
}
|
|
|
|
}, [web3Provider, web3])
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Web3Context.Provider
|
|
|
|
value={{
|
|
|
|
web3,
|
|
|
|
web3Provider,
|
|
|
|
web3Modal,
|
2021-05-17 16:12:22 +02:00
|
|
|
web3ProviderInfo,
|
2021-03-17 11:44:26 +01:00
|
|
|
accountId,
|
2021-09-20 13:47:15 +02:00
|
|
|
accountEns,
|
2021-05-31 14:27:04 +02:00
|
|
|
balance,
|
2021-03-17 11:44:26 +01:00
|
|
|
networkId,
|
2021-07-22 15:16:15 +02:00
|
|
|
chainId,
|
2021-03-17 11:44:26 +01:00
|
|
|
networkDisplayName,
|
|
|
|
networkData,
|
2021-04-13 10:57:59 +02:00
|
|
|
block,
|
2021-03-17 11:44:26 +01:00
|
|
|
isTestnet,
|
2021-04-13 10:57:59 +02:00
|
|
|
web3Loading,
|
2021-03-17 11:44:26 +01:00
|
|
|
connect,
|
|
|
|
logout
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</Web3Context.Provider>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper hook to access the provider values
|
|
|
|
const useWeb3 = (): Web3ProviderValue => useContext(Web3Context)
|
|
|
|
|
2021-10-27 12:27:14 +02:00
|
|
|
export { Web3Provider, useWeb3, Web3Context }
|
2021-03-17 11:44:26 +01:00
|
|
|
export default Web3Provider
|