123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- /* eslint-disable no-console */
- import BN from 'bignumber.js'
- import { hexToNumber, numberToHex } from 'web3-utils'
- import { SnackbarProgrammatic as Snackbar, DialogProgrammatic as Dialog } from 'buefy'
- import { PROVIDERS } from '@/constants'
- import networkConfig from '@/networkConfig'
- import { walletConnectConnector } from '@/services'
- import SanctionsListAbi from '@/abis/SanctionsList.abi'
- const { toChecksumAddress } = require('web3-utils')
- const state = () => {
- return {
- netId: 1,
- walletName: '',
- ethBalance: '0',
- ethAccount: null,
- providerConfig: {},
- providerName: null,
- isInitialized: false,
- isReconnecting: false,
- mismatchNetwork: false
- }
- }
- const getters = {
- isWalletConnect(state) {
- return state.providerConfig.name === 'WalletConnect'
- },
- isPartialSupport(state) {
- return state.providerConfig.isPartialSupport
- },
- hasEthAccount(state) {
- return state.ethAccount !== null
- },
- mismatchNetwork(state) {
- return state.mismatchNetwork
- },
- netId(state) {
- return state.netId
- },
- networkName(state) {
- return networkConfig[`netId${state.netId}`].networkName
- },
- currency(state) {
- return networkConfig[`netId${state.netId}`].currencyName
- },
- nativeCurrency(state) {
- return networkConfig[`netId${state.netId}`].nativeCurrency
- },
- networkConfig(state) {
- const conf = networkConfig[`netId${state.netId}`]
- return conf || networkConfig.netId1
- },
- getEthereumProvider: (state, getters) => (netId) => {
- switch (state.providerName) {
- case 'walletConnect':
- return walletConnectConnector(netId || getters.netId)
- case 'metamask':
- case 'trustwallet':
- case 'imtoken':
- case 'alphawallet':
- case 'generic':
- default:
- if (window.ethereum) {
- return window.ethereum
- } else {
- throw new Error(this.app.i18n.t('networkDoesNotHaveEthereumProperty'))
- }
- }
- },
- isLoggedIn: (state, getters) => {
- return !!state.providerName && getters.hasEthAccount
- }
- }
- const mutations = {
- IDENTIFY(state, ethAccount) {
- state.ethAccount = ethAccount
- },
- SET_NET_ID(state, netId) {
- netId = parseInt(netId, 10)
- window.localStorage.setItem('netId', netId)
- state.netId = netId
- },
- SET_RECONNECTING(state, bool) {
- state.isReconnecting = bool
- },
- SET_MISMATCH_NETWORK(state, payload) {
- state.mismatchNetwork = payload
- },
- SAVE_BALANCE(state, ethBalance) {
- state.ethBalance = ethBalance
- },
- SET_WALLET_NAME(state, walletName) {
- state.walletName = walletName
- },
- SET_PROVIDER_NAME(state, providerName) {
- state.providerName = providerName
- state.providerConfig = PROVIDERS[providerName]
- window.localStorage.setItem('provider', providerName)
- },
- CLEAR_PROVIDER(state) {
- state.providerName = null
- state.providerConfig = {}
- },
- SET_INITIALIZED(state, initialized) {
- state.isInitialized = initialized
- }
- }
- const actions = {
- async initialize({ dispatch, commit, getters, rootState, rootGetters }, payload) {
- await dispatch('askPermission', payload)
- dispatch('governance/gov/checkActiveProposals', {}, { root: true })
- },
- onSetInitializeData({ commit, dispatch, state }, isMismatch) {
- if (isMismatch) {
- commit('IDENTIFY', null)
- commit('SET_INITIALIZED', false)
- } else {
- const providerName = window.localStorage.getItem('provider')
- if (providerName && !state.isInitialized) {
- dispatch('initialize', { providerName })
- }
- }
- commit('SET_MISMATCH_NETWORK', isMismatch)
- },
- async checkMismatchNetwork({ dispatch, commit, state, getters }, netId) {
- if (getters.isWalletConnect) {
- const { id } = this.$provider.config
- const isMismatch = Number(netId) !== Number(id)
- await dispatch('onSetInitializeData', isMismatch)
- return
- }
- if (!window.ethereum) {
- return
- }
- const chainId = await window.ethereum.request({ method: 'eth_chainId' })
- const isMismatch = Number(netId) !== hexToNumber(chainId)
- await dispatch('onSetInitializeData', isMismatch)
- },
- async sendTransaction(
- { dispatch, state, rootGetters },
- { method, params, watcherParams, isAwait = true, isSaving = true, eipDisable = false }
- ) {
- try {
- const { ethAccount, netId } = state
- const gasParams = rootGetters['gasPrices/getGasParams']
- const callParams = {
- method,
- params: [
- {
- value: '0x00',
- from: ethAccount,
- ...params,
- ...gasParams
- }
- ]
- }
- dispatch('loading/showConfirmLoader', {}, { root: true })
- const txHash = await this.$provider.sendRequest(callParams)
- dispatch(
- 'loading/changeText',
- { message: this.app.i18n.t('waitUntilTransactionIsMined') },
- { root: true }
- )
- const activeWatcher = () =>
- dispatch(
- 'txHashKeeper/runTxWatcherWithNotifications',
- {
- ...watcherParams,
- txHash,
- isSaving,
- netId
- },
- { root: true }
- )
- if (isAwait) {
- await activeWatcher()
- } else {
- activeWatcher()
- }
- dispatch('loading/disable', {}, { root: true })
- return txHash
- } catch (err) {
- if (err.message.includes('EIP-1559')) {
- return await dispatch('sendTransaction', {
- method,
- params,
- watcherParams,
- isAwait,
- isSaving,
- eipDisable: true
- })
- } else {
- throw new Error(this.app.i18n.t('rejectedRequest', { description: state.walletName }))
- }
- } finally {
- dispatch('loading/disable', {}, { root: true })
- }
- },
- async getEncryptionPublicKey({ state }) {
- try {
- const { ethAccount } = state
- const callParams = {
- method: 'eth_getEncryptionPublicKey',
- params: [ethAccount]
- }
- const key = await this.$provider.sendRequest(callParams)
- return key
- } catch (err) {
- let errorMessage = 'decryptFailed'
- if (err.message.includes('Trezor')) {
- errorMessage = 'trezorNotSupported'
- } else if (err.message.includes('Ledger')) {
- errorMessage = 'ledgerNotSupported'
- }
- const isRejected = err.message.includes(
- 'MetaMask EncryptionPublicKey: User denied message EncryptionPublicKey.'
- )
- if (isRejected) {
- throw new Error(this.app.i18n.t('rejectedRequest', { description: state.walletName }))
- }
- throw new Error(this.app.i18n.t(errorMessage))
- }
- },
- async ethDecrypt({ state }, hexData) {
- try {
- const { ethAccount } = state
- const callParams = {
- method: 'eth_decrypt',
- params: [hexData, ethAccount]
- }
- const encryptedData = await this.$provider.sendRequest(callParams)
- return encryptedData
- } catch (err) {
- throw new Error(`Method ethDecrypt has error: ${err.message}`)
- }
- },
- async onAccountsChanged({ dispatch, commit }, { newAccount }) {
- if (newAccount) {
- const account = toChecksumAddress(newAccount)
- commit('IDENTIFY', account)
- await dispatch('updateAccountBalance')
- } else {
- await dispatch('onLogOut')
- }
- },
- onLogOut({ commit, getters, dispatch }) {
- if (getters.isWalletConnect) {
- const mobileProvider = this.$provider.provider
- if (typeof mobileProvider.close === 'function') {
- mobileProvider.close()
- }
- }
- commit('IDENTIFY', null)
- dispatch('clearProvider')
- commit('SET_INITIALIZED', false)
- },
- async mobileWalletReconnect({ state, dispatch, commit, rootState }, { netId }) {
- try {
- commit('SET_RECONNECTING', true)
- const { providerName } = state
- const { enabled } = rootState.loading
- await dispatch('onLogOut')
- await dispatch('initialize', { providerName, chosenNetId: netId })
- if (enabled) {
- await dispatch('loading/disable', {}, { root: true })
- }
- } catch ({ message }) {
- throw new Error(`Mobile wallet reconnect error: ${message}`)
- } finally {
- commit('SET_RECONNECTING', false)
- }
- },
- async networkChangeHandler({ state, getters, commit, dispatch }, params) {
- try {
- if (getters.isWalletConnect) {
- dispatch('loading/disable', {}, { root: true })
- const networkName = networkConfig[`netId${params.netId}`].networkName
- const { result } = await Dialog.confirm({
- title: this.app.i18n.t('changeNetwork'),
- message: this.app.i18n.t('mobileWallet.reconnect.message', { networkName }),
- cancelText: this.app.i18n.t('cancelButton'),
- confirmText: this.app.i18n.t('mobileWallet.reconnect.action')
- })
- if (result) {
- await dispatch('mobileWalletReconnect', params)
- this.$provider._onNetworkChanged({ id: params.netId })
- }
- } else {
- if (state.isInitialized) {
- await dispatch('switchNetwork', params)
- }
- await dispatch('onNetworkChanged', params)
- }
- } catch (err) {
- console.error('networkChangeHandler', err.message)
- }
- },
- async checkIsSanctioned({ rootGetters }, { address }) {
- const ethProvider = rootGetters['relayer/ethProvider']
- const contract = new ethProvider.eth.Contract(
- SanctionsListAbi,
- '0x40C57923924B5c5c5455c48D93317139ADDaC8fb'
- )
- const isSanctioned = await contract.methods.isSanctioned(address).call()
- if (isSanctioned) {
- window.onbeforeunload = null
- window.location = 'https://twitter.com/TornadoCash/status/1514904975037669386'
- }
- },
- async onNetworkChanged({ state, getters, commit, dispatch }, { netId }) {
- dispatch('checkMismatchNetwork', netId)
- if (netId !== 'loading' && Number(state.netId) !== Number(netId)) {
- try {
- if (!networkConfig[`netId${netId}`]) {
- dispatch('clearProvider')
- Snackbar.open({
- message: this.app.i18n.t('currentNetworkIsNotSupported'),
- type: 'is-primary',
- position: 'is-top',
- actionText: 'OK',
- indefinite: true
- })
- throw new Error(this.app.i18n.t('currentNetworkIsNotSupported'))
- }
- commit('SET_NET_ID', netId)
- await dispatch('application/setNativeCurrency', { netId }, { root: true })
- // TODO what if all rpc failed
- await dispatch('settings/checkCurrentRpc', {}, { root: true })
- dispatch('application/updateSelectEvents', {}, { root: true })
- if (getters.isLoggedIn) {
- await dispatch('updateAccountBalance')
- }
- } catch (e) {
- throw new Error(e.message)
- }
- }
- },
- async updateAccountBalance({ state, commit }, account = '') {
- try {
- const address = account || state.ethAccount
- if (!address) {
- return 0
- }
- const balance = await this.$provider.getBalance({ address })
- commit('SAVE_BALANCE', balance)
- return balance
- } catch (err) {
- console.error(`updateAccountBalance has error ${err.message}`)
- }
- },
- clearProvider({ commit, state }) {
- if (state.providerConfig.storageName) {
- window.localStorage.removeItem(state.providerConfig.storageName)
- }
- commit('CLEAR_PROVIDER')
- window.localStorage.removeItem('provider')
- window.localStorage.removeItem('network')
- },
- async askPermission(
- { commit, dispatch, getters, rootGetters, state, rootState },
- { providerName, chosenNetId }
- ) {
- commit('SET_PROVIDER_NAME', providerName)
- const { name, listener } = state.providerConfig
- commit('SET_WALLET_NAME', name)
- try {
- const provider = await getters.getEthereumProvider(chosenNetId)
- if (providerName === 'walletConnect') {
- await dispatch(listener, { provider })
- }
- const address = await this.$provider.initProvider(provider, {})
- if (!address) {
- throw new Error('lockedMetamask')
- }
- await dispatch('checkIsSanctioned', { address })
- commit('IDENTIFY', address)
- const netId = await dispatch('checkNetworkVersion')
- await dispatch('onNetworkChanged', { netId })
- commit('SET_INITIALIZED', true)
- const { url } = rootGetters['settings/currentRpc']
- this.$provider.initWeb3(url)
- await dispatch('updateAccountBalance', address)
- if (getters.isWalletConnect) {
- if (provider.wc.peerMeta) {
- commit('SET_WALLET_NAME', provider.wc.peerMeta.name)
- }
- }
- this.$provider.on({
- method: 'chainChanged',
- callback: () => {
- dispatch('onNetworkChanged', { netId })
- }
- })
- this.$provider.on({
- method: 'accountsChanged',
- callback: ([newAccount]) => {
- dispatch('onAccountsChanged', { newAccount })
- }
- })
- return { netId, ethAccount: address }
- } catch (err) {
- if (providerName === 'walletConnect') {
- const mobileProvider = this.$provider.provider
- if (typeof mobileProvider.disconnect === 'function') {
- mobileProvider.disconnect()
- }
- await dispatch('onLogOut')
- }
- throw new Error(`method askPermission has error: ${err.message}`)
- }
- },
- walletConnectSocketListener({ state, commit, dispatch, getters, rootState }, { provider }) {
- const { enabled } = rootState.loading
- try {
- provider.wc.on('disconnect', (error, payload) => {
- if (state.isReconnecting) {
- console.warn('Provider reconnect payload', { payload, error, isReconnecting: state.isReconnecting })
- if (enabled) {
- dispatch('loading/disable', {}, { root: true })
- }
- commit('SET_RECONNECTING', false)
- return
- }
- const prevConnection = localStorage.getItem('walletconnectTimeStamp')
- const isPrevConnection = new BN(Date.now()).minus(prevConnection).isGreaterThanOrEqualTo(5000)
- if (isPrevConnection) {
- console.warn('Provider disconnect payload', {
- payload,
- error,
- isReconnecting: state.isReconnecting
- })
- dispatch('onLogOut')
- }
- if (enabled) {
- dispatch('loading/disable', {}, { root: true })
- }
- })
- } catch (err) {
- console.error('WalletConnect listeners error: ', err)
- }
- },
- async switchNetwork({ dispatch }, { netId }) {
- try {
- await this.$provider.sendRequest({
- method: 'wallet_switchEthereumChain',
- params: [{ chainId: numberToHex(netId) }]
- })
- } catch (err) {
- // This error indicates that the chain has not been added to MetaMask.
- if (err.message.includes('wallet_addEthereumChain')) {
- return dispatch('addNetwork', { netId })
- }
- throw new Error(err.message)
- }
- },
- async addNetwork(_, { netId }) {
- const METAMASK_LIST = {
- 56: {
- chainId: '0x38',
- chainName: 'Binance Smart Chain Mainnet',
- rpcUrls: ['https://bsc-dataseed1.ninicoin.io'],
- nativeCurrency: {
- name: 'Binance Chain Native Token',
- symbol: 'BNB',
- decimals: 18
- },
- blockExplorerUrls: ['https://bscscan.com']
- },
- 10: {
- chainId: '0xa',
- chainName: 'Optimism',
- rpcUrls: ['https://mainnet.optimism.io/'],
- nativeCurrency: {
- name: 'Ether',
- symbol: 'ETH',
- decimals: 18
- },
- blockExplorerUrls: ['https://optimistic.etherscan.io']
- },
- 100: {
- chainId: '0x64',
- chainName: 'Gnosis',
- rpcUrls: ['https://rpc.gnosischain.com'],
- nativeCurrency: {
- name: 'xDAI',
- symbol: 'xDAI',
- decimals: 18
- },
- blockExplorerUrls: ['https://blockscout.com/xdai/mainnet']
- },
- 137: {
- chainId: '0x89',
- chainName: 'Polygon Mainnet',
- rpcUrls: ['https://rpc-mainnet.maticvigil.com'],
- nativeCurrency: {
- name: 'MATIC',
- symbol: 'MATIC',
- decimals: 18
- },
- blockExplorerUrls: ['https://polygonscan.com']
- },
- 42161: {
- chainId: '0xA4B1',
- chainName: 'Arbitrum One',
- rpcUrls: ['https://arb1.arbitrum.io/rpc'],
- nativeCurrency: {
- name: 'Ether',
- symbol: 'ETH',
- decimals: 18
- },
- blockExplorerUrls: ['https://arbiscan.io']
- },
- 43114: {
- chainId: '0xA86A',
- chainName: 'Avalanche C-Chain',
- rpcUrls: ['https://api.avax.network/ext/bc/C/rpc'],
- nativeCurrency: {
- name: 'Avalanche',
- symbol: 'AVAX',
- decimals: 18
- },
- blockExplorerUrls: ['https://snowtrace.io']
- }
- }
- if (METAMASK_LIST[netId]) {
- await this.$provider.sendRequest({
- method: 'wallet_addEthereumChain',
- params: [METAMASK_LIST[netId]]
- })
- }
- },
- async checkNetworkVersion() {
- try {
- const id = Number(
- await this.$provider.sendRequest({
- method: 'eth_chainId',
- params: []
- })
- )
- return id
- } catch (err) {
- throw new Error(err.message)
- }
- }
- }
- export default {
- namespaced: true,
- state,
- getters,
- mutations,
- actions
- }
|