Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -6,108 +6,143 @@ import type {
PastLogsOptions,
Log,
} from 'web3-core'
import { first } from 'lodash-es'
import { ChainId, EthereumChainDetailed, EthereumMethodType, getRPCConstants } from '@masknet/web3-shared'
import { ChainId, EthereumChainDetailed, EthereumMethodType } from '@masknet/web3-shared'
import { request } from './request'
import type { SendOverrides } from './send'

export async function getGasPrice() {
return request<string>({
method: EthereumMethodType.ETH_GAS_PRICE,
})
export async function getGasPrice(overrides?: SendOverrides) {
return request<string>(
{
method: EthereumMethodType.ETH_GAS_PRICE,
},
overrides,
)
}

export async function getBlockNumber() {
const blockNumber = await request<string>({
method: EthereumMethodType.ETH_BLOCK_NUMBER,
})
export async function getBlockNumber(overrides?: SendOverrides) {
const blockNumber = await request<string>(
{
method: EthereumMethodType.ETH_BLOCK_NUMBER,
},
overrides,
)
return Number.parseInt(blockNumber, 16)
}

export async function getBalance(address: string) {
return request<string>({
method: EthereumMethodType.ETH_GET_BALANCE,
params: [address, 'latest'],
})
export async function getBalance(address: string, overrides?: SendOverrides) {
return request<string>(
{
method: EthereumMethodType.ETH_GET_BALANCE,
params: [address, 'latest'],
},
overrides,
)
}

export async function getTransactionByHash(hash: string) {
return request<Transaction>({
method: EthereumMethodType.ETH_GET_TRANSACTION_BY_HASH,
params: [hash],
})
export async function getTransactionByHash(hash: string, overrides?: SendOverrides) {
return request<Transaction>(
{
method: EthereumMethodType.ETH_GET_TRANSACTION_BY_HASH,
params: [hash],
},
overrides,
)
}

export async function getTransactionReceipt(hash: string) {
return request<TransactionReceipt | null>({
method: EthereumMethodType.ETH_GET_TRANSACTION_RECEIPT,
params: [hash],
})
export async function getTransactionReceipt(hash: string, overrides?: SendOverrides) {
return request<TransactionReceipt | null>(
{
method: EthereumMethodType.ETH_GET_TRANSACTION_RECEIPT,
params: [hash],
},
overrides,
)
}

export async function getTransactionCount(address: string) {
const count = await request<string>({
method: EthereumMethodType.ETH_GET_TRANSACTION_COUNT,
params: [address, 'latest'],
})
export async function getTransactionCount(address: string, overrides?: SendOverrides) {
const count = await request<string>(
{
method: EthereumMethodType.ETH_GET_TRANSACTION_COUNT,
params: [address, 'latest'],
},
overrides,
)
return Number.parseInt(count, 16)
}

export async function estimateGas(config: TransactionConfig) {
const gas = await request<string>({
method: EthereumMethodType.ETH_ESTIMATE_GAS,
params: [config],
})
export async function estimateGas(config: TransactionConfig, overrides?: SendOverrides) {
const gas = await request<string>(
{
method: EthereumMethodType.ETH_ESTIMATE_GAS,
params: [config],
},
overrides,
)
return Number.parseInt(gas, 16)
}

export async function sign(dataToSign: string, address: string) {
return request<string>({
method: EthereumMethodType.ETH_SIGN,
params: [dataToSign, address],
})
export async function sign(dataToSign: string, address: string, overrides?: SendOverrides) {
return request<string>(
{
method: EthereumMethodType.ETH_SIGN,
params: [dataToSign, address],
},
overrides,
)
}

export async function personalSign(dataToSign: string, address: string, password?: string) {
return request<string>({
method: EthereumMethodType.PERSONAL_SIGN,
params: [dataToSign, address, password].filter((x) => typeof x !== 'undefined'),
})
export async function personalSign(dataToSign: string, address: string, password?: string, overrides?: SendOverrides) {
return request<string>(
{
method: EthereumMethodType.PERSONAL_SIGN,
params: [dataToSign, address, password].filter((x) => typeof x !== 'undefined'),
},
overrides,
)
}

export async function addEthereumChain(chainDetailed: EthereumChainDetailed, address?: string) {
return request<boolean>({
method: EthereumMethodType.WALLET_ADD_ETHEREUM_CHAIN,
params: [chainDetailed, address].filter(Boolean),
})
export async function addEthereumChain(
chainDetailed: EthereumChainDetailed,
address?: string,
overrides?: SendOverrides,
) {
return request<boolean>(
{
method: EthereumMethodType.WALLET_ADD_ETHEREUM_CHAIN,
params: [chainDetailed, address].filter(Boolean),
},
overrides,
)
}

export async function switchEthereumChain(chainId: ChainId) {
export async function switchEthereumChain(chainId: ChainId, overrides?: SendOverrides) {
return request<boolean>({
method: EthereumMethodType.WALLET_SWITCH_ETHEREUM_CHAIN,
params: [
{
chainId: `0x${chainId.toString(16)}`,
},
],
overrides,
})
}

export async function signTransaction(config: TransactionConfig) {
return request<SignedTransaction>({
method: EthereumMethodType.ETH_SIGN_TRANSACTION,
params: [config],
})
export async function signTransaction(config: TransactionConfig, overrides?: SendOverrides) {
return request<SignedTransaction>(
{
method: EthereumMethodType.ETH_SIGN_TRANSACTION,
params: [config],
},
overrides,
)
}

export async function getPastLogs(config: PastLogsOptions, chainId: ChainId) {
const { RPC } = getRPCConstants(chainId)
const provderURL = first(RPC)
if (!provderURL) throw new Error('Unknown chain id.')
export async function getPastLogs(config: PastLogsOptions, overrides?: SendOverrides) {
return request<Log[]>(
{
method: EthereumMethodType.ETH_GET_LOGS,
params: [config],
},
provderURL,
overrides,
)
}
Original file line number Diff line number Diff line change
@@ -1,46 +1,52 @@
import Web3 from 'web3'
import type { provider as Provider } from 'web3-core'
import { first } from 'lodash-es'
import { EthereumAddress } from 'wallet.ts'
import createMetaMaskProvider, { MetaMaskInpageProvider } from '@dimensiondev/metamask-extension-provider'
import { ChainId, NetworkType, ProviderType } from '@masknet/web3-shared'
import { updateAccount, updateExoticWalletFromSource } from '../../../../plugins/Wallet/services'
import { currentIsMetamaskLockedSettings, currentProviderSettings } from '../../../../plugins/Wallet/settings'
import { ChainId, ProviderType } from '@masknet/web3-shared'
import { resetAccount, updateAccount } from '../../../../plugins/Wallet/services'
import {
currentChainIdSettings,
currentIsMetamaskLockedSettings,
currentProviderSettings,
} from '../../../../plugins/Wallet/settings'

let provider: MetaMaskInpageProvider | null = null
let web3: Web3 | null = null

async function onAccountsChanged(accounts: string[]) {
await updateWalletInDB(first(accounts) ?? '')
currentIsMetamaskLockedSettings.value = !(await provider!._metamask?.isUnlocked()) && accounts.length === 0
if (currentProviderSettings.value !== ProviderType.MetaMask) return
await updateAccount({
account: first(accounts),
providerType: ProviderType.MetaMask,
chainId: typeof provider?.chainId === 'string' ? Number.parseInt(provider.chainId, 16) : undefined,
networkType: undefined,
})
}

async function onChainIdChanged(id: string) {
// learn more: https://docs.metamask.io/guide/ethereum-provider.html#chain-ids and https://chainid.network/
const chainId_ = Number.parseInt(id, 16)
const chainId = chainId_ === 0 ? ChainId.Mainnet : chainId_
currentIsMetamaskLockedSettings.value = !(await provider!._metamask?.isUnlocked())
if (currentProviderSettings.value !== ProviderType.MetaMask) return

// learn more: https://docs.metamask.io/guide/ethereum-provider.html#chain-ids and https://chainid.network/
const chainId = Number.parseInt(id, 16) || ChainId.Mainnet
if (currentChainIdSettings.value === chainId) return
await updateAccount({
chainId,
networkType: undefined,
})
}

async function onError(error: string) {
if (typeof error !== 'string' || !/Lost Connection to MetaMask/i.test(error)) return
if (currentProviderSettings.value !== ProviderType.MetaMask) return
await updateAccount({
account: '',
networkType: NetworkType.Ethereum,
await resetAccount({
providerType: ProviderType.MetaMask,
})
}

export function createProvider() {
if (provider) {
provider.off('accountsChanged', onAccountsChanged)
provider.off('chainChanged', onChainIdChanged)
provider.off('error', onError)
}
if (provider) return provider
provider = createMetaMaskProvider()
if (!provider) throw new Error('Unable to create in page provider.')
provider.on('accountsChanged', onAccountsChanged as (...args: unknown[]) => void)
Expand All @@ -60,36 +66,10 @@ export function createWeb3() {

export async function requestAccounts() {
const web3 = createWeb3()

// update accounts
const accounts = await web3.eth.requestAccounts()
await updateWalletInDB(first(accounts) ?? '', true)

// update chain id
const chainId = await web3.eth.getChainId()
onChainIdChanged(chainId.toString(16))

return {
chainId,
accounts,
}
}

async function updateWalletInDB(address: string, setAsDefault: boolean = false) {
const providerType = currentProviderSettings.value

// validate address
if (!EthereumAddress.isValid(address)) {
if (providerType === ProviderType.MetaMask) await updateAccount({ account: '' })
return
}

// update wallet in the DB
await updateExoticWalletFromSource(ProviderType.MetaMask, new Map([[address, { address }]]))

// update chain account
await updateAccount({
account: setAsDefault || providerType === ProviderType.MetaMask ? address : undefined,
providerType: setAsDefault ? ProviderType.MetaMask : undefined,
})
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { JsonRpcResponse } from 'web3-core-helpers'
import { first } from 'lodash-es'
import { EthereumAddress } from 'wallet.ts'
import WalletConnect from '@walletconnect/client'
import type { IJsonRpcRequest } from '@walletconnect/types'
import { ProviderType, NetworkType, ChainId } from '@masknet/web3-shared'
import { ProviderType, ChainId } from '@masknet/web3-shared'
import * as Maskbook from '../providers/Maskbook'
import { updateAccount, updateExoticWalletFromSource } from '../../../../plugins/Wallet/services'
import { resetAccount, updateAccount } from '../../../../plugins/Wallet/services'
import { currentChainIdSettings, currentProviderSettings } from '../../../../plugins/Wallet/settings'

let connector: WalletConnect | null = null
Expand Down Expand Up @@ -55,7 +54,7 @@ export async function sendCustomRequest(payload: IJsonRpcRequest) {

// Wrap promise as PromiEvent because WalletConnect returns transaction hash only
// docs: https://docs.walletconnect.org/client-api
export function createWeb3(chainId = currentChainIdSettings.value) {
export function createWeb3({ chainId = currentChainIdSettings.value }: { chainId?: ChainId } = {}) {
return Maskbook.createWeb3({
chainId,
})
Expand Down Expand Up @@ -85,17 +84,11 @@ export async function requestAccounts() {
})
}

const onConnect = async () => {
if (!connector?.accounts.length) return
await updateAccount({
chainId: connector.chainId,
})
await updateWalletInDB(first(connector.accounts) ?? '', connector.peerMeta?.name, true)
}
const onConnect = () => onUpdate(null)

const onUpdate = async (
error: Error | null,
payload: {
payload?: {
params: {
chainId: number
accounts: string[]
Expand All @@ -104,37 +97,21 @@ const onUpdate = async (
) => {
if (error) return
if (!connector?.accounts.length) return
if (currentProviderSettings.value !== ProviderType.WalletConnect) return
await updateAccount({
name: connector.peerMeta?.name,
account: first(connector.accounts),
chainId: connector.chainId,
providerType: ProviderType.WalletConnect,
networkType: undefined,
})
await updateWalletInDB(first(connector.accounts) ?? '', connector.peerMeta?.name, false)
}

const onDisconnect = async (error: Error | null) => {
if (connector?.connected) await connector.killSession()
connector = null
if (currentProviderSettings.value !== ProviderType.WalletConnect) return
await updateAccount({
Comment thread
guanbinrui marked this conversation as resolved.
account: '',
networkType: NetworkType.Ethereum,
})
}

async function updateWalletInDB(address: string, name: string = 'WalletConnect', setAsDefault: boolean = false) {
const providerType = currentProviderSettings.value

// validate address
if (!EthereumAddress.isValid(address)) {
if (providerType === ProviderType.WalletConnect) await updateAccount({ account: '' })
return
}

// update wallet in the DB
await updateExoticWalletFromSource(ProviderType.WalletConnect, new Map([[address, { name, address }]]))

// update chain account
await updateAccount({
account: setAsDefault || providerType === ProviderType.WalletConnect ? address : undefined,
providerType: setAsDefault ? ProviderType.WalletConnect : undefined,
await resetAccount({
providerType: ProviderType.WalletConnect,
})
}
Loading