import { ActionTree, GetterTree, MutationTree } from 'vuex' import { ChainId, RootState } from '@/types' import { WalletMutation, WalletState } from '@/types/store/wallet' import { getSanctionList } from '@/contracts' import { toChecksumAddress, stringToHex } from '@/utilities' import { ens, getProvider, getWalletProvider } from '@/services' import { CHAINS, errors, METAMASK_LIST, SIGN_MESSAGE, L1_CHAIN_ID } from '@/constants' export const actions: ActionTree = { async setProvider({ commit, dispatch, state }, params) { try { commit(WalletMutation.SET_PROVIDER, params) await dispatch('checkAppNetwork', params.network) } catch (err) { throw new Error(err.message) } }, async checkSanction({ getters }, address) { const contract = getSanctionList(getters.dependencies.l1ChainId) const isSanctioned = await contract.callStatic.isSanctioned(address) if (isSanctioned) { window.onbeforeunload = null // ToDo add type // @ts-expect-error window.location = 'https://twitter.com/TornadoCash/status/1514904975037669386' } }, checkAppNetwork({ commit }, network) { try { // TODO create a selector for active network const filteredChain = L1_CHAIN_ID === ChainId.MAINNET ? ChainId.BSC : ChainId.MAINNET const availableChains = Object.values(ChainId).filter((id) => id !== filteredChain) if (!availableChains.includes(Number(network))) { throw new Error(errors.validation.MISMATCH_NETWORK) } commit(WalletMutation.MISMATCH_NETWORK, false) } catch (err) { console.error(err.message) commit(WalletMutation.MISMATCH_NETWORK, true) } }, async checkNetwork({ dispatch, getters }) { const provider = getWalletProvider(getters.nameProvider || 'METAMASK') const network = await provider.checkNetworkVersion() await dispatch('checkAppNetwork', network) }, async getWalletBalance({ commit, dispatch, getters }) { try { const { provider } = getProvider(getters.chainId) if (!getters.walletAddress || getters.mismatchNetwork) { return } const balance = await provider.getBalance(getters.walletAddress) commit(WalletMutation.SET_WALLET_BALANCE, balance.toString()) } catch (err) { console.log('getWalletBalance has error:', err.message) } }, async setWalletParams({ commit, dispatch }, address) { try { commit(WalletMutation.SET_WALLET_ADDRESS, address) const ensName = await ens.getEnsName(address, ChainId.MAINNET) commit(WalletMutation.SET_ENS_NAME, ensName) dispatch('getWalletBalance') } catch (err) { throw new Error(err.message) } }, async createWalletTransaction({ getters, commit, dispatch }, { calldata, to, transactionInfo, gas = null, amount = '0x0' }) { try { const provider = getWalletProvider(getters.nameProvider) const params = { to, value: amount, data: calldata, from: getters.walletAddress, ...getters.dependencies.txGasParams(getters.chainId, transactionInfo.method), } if (gas) { params.gas = gas } const txHash = await provider.sendRequest({ method: 'eth_sendTransaction', params: [params], }) await dispatch('transaction/transactionWatcher', { txHash, transactionInfo, chainId: getters.chainId }, { root: true }) return txHash } catch (err) { throw new Error(err.message) } }, async signStartMessage({ getters }, { signingAddress }) { try { const callParams = { method: 'personal_sign', params: [stringToHex(SIGN_MESSAGE), signingAddress], } const provider = getWalletProvider(getters.nameProvider) return await provider.sendRequest(callParams) } catch (err) { throw new Error(`Method personal sign has error: ${err.message}`) } }, async changeChain({ getters, dispatch }, chainId) { const provider = getWalletProvider(getters.nameProvider) try { await provider.sendRequest({ method: 'wallet_switchEthereumChain', params: [{ chainId: CHAINS[chainId].hexChainId }], }) } catch (error) { const parsedError = await dispatch( 'application/errorHandler', { errorMessage: error.message, title: 'Change network' }, { root: true }, ) if (parsedError === errors.wallet.TRY_ADDING_THE_CHAIN) { await provider.sendRequest({ method: 'wallet_addEthereumChain', params: [METAMASK_LIST[chainId]], }) } } finally { // TODO check the number of requests for this function dispatch('getWalletBalance') } }, } export const getters: GetterTree = { mismatchNetwork: (state: WalletState) => { return state.provider.mismatchNetwork }, nameProvider: (state: WalletState) => { return state.provider.name }, walletEnsName: (state: WalletState) => { return state.account.ensName }, isConnected: (state: WalletState) => { return state.provider.isConnected }, chainId: (state: WalletState) => { return Number(state.provider.network) }, isL1Chain: (state: WalletState, getters) => { return Number(state.provider.network) === getters.l1ChainId }, isL2Chain: (state: WalletState, getters) => { return Number(state.provider.network) === getters.l2ChainId }, l1ChainId: (state: WalletState) => { return state.provider.l1ChainId }, l2ChainId: (state: WalletState) => { return state.provider.l2ChainId }, chainConfig: (state: WalletState) => { return CHAINS[state.provider.l1ChainId] }, walletAddress: (state: WalletState) => { return state.account.address }, walletBalance: (state: WalletState) => { return state.account.balance }, // another module dependencies dependencies: (state, getters, rootState, rootGetters) => { return { // gasPrice txGasParams: rootGetters['gasPrice/txGasParams'], // wallet l1ChainId: rootGetters['wallet/l1ChainId'], } }, } export const mutations: MutationTree = { [WalletMutation.SET_WALLET_ADDRESS](state, payload) { state.account.address = toChecksumAddress(payload) }, [WalletMutation.SET_ENS_NAME](state, payload) { state.account.ensName = payload }, [WalletMutation.SET_WALLET_BALANCE](state, payload) { state.account.balance = payload }, [WalletMutation.MISMATCH_NETWORK](state, payload) { state.provider.mismatchNetwork = payload }, [WalletMutation.SET_PROVIDER](state, { network, name }) { state.provider = { ...state.provider, name, network, mismatchNetwork: false, } }, [WalletMutation.SET_PROVIDER_CONNECTION](state, isConnected) { // @ts-expect-error this._vm.$set(state.provider, 'isConnected', isConnected) }, [WalletMutation.CLEAR_PROVIDER](state) { state.provider = { name: '', network: L1_CHAIN_ID, l1ChainId: L1_CHAIN_ID, l2ChainId: ChainId.XDAI, isConnected: false, mismatchNetwork: false, } state.account = { address: '', ensName: '', balance: 0, } }, } export const state = () => { return { provider: { name: '', network: L1_CHAIN_ID, l1ChainId: L1_CHAIN_ID, l2ChainId: ChainId.XDAI, isConnected: false, mismatchNetwork: false, }, account: { ensName: '', address: '', balance: 0, }, } }