Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Commit

Permalink
refactor: rely on Onboard for provider watching (#3364)
Browse files Browse the repository at this point in the history
* refactor: rely on onboard for provider watching

* chore: remove key check

* fix: ENS support + notifications after full load

* feat: use fallthroughs + remove console error

* fix: reset web3 on wallet disconnection

* fix: wallet is loaded when one flag is true

* fix: default fallthrough logic

* chore: cleanup code structure

* fix: always dispatch wallet store actions

* fix: track wallet once + clear on disconnect

* fix: chain-dependant ENS resolution

* fix: don't reset web3 object

* fix: disconnection logic

* fix: clarify provider actions

* fix: adjust `onDisconnect` logic

* fix: remove unnecessary key

* fix: call Onboard popup in correct order

* fix: drill chain id throughout Onboard

* fix: test `rpcUrl`

* refactor: remove unnecessary boolean conversion

* fix: lookup ENS domains when intialising form (#3465)

* fix: checksum address before ens validation

* fix: checksum `verifiedAddress`

* refactor: lookup ENS w/ form init

* fix: use field-level owner validation (#3470)

* fix: use field-level owner validation

* fix: test

* fix: forward compare previous owners

* fix: checksum address from Onboard

* fix: keep store logic in reducer

* fix: reduce infura requests (#3479)

* fix: increase block polling interval

* fix: patch balance `stateSyncer` out of WC

* fix: upgrade `bnc-onboard` + patch `web3-provider-engine`

* fix: cleanup patch

* fix: cleanup patch

* fix: exchange patch for custom WC module instead
  • Loading branch information
iamacook committed Feb 17, 2022
1 parent f7f1a32 commit 74e980f
Show file tree
Hide file tree
Showing 31 changed files with 565 additions and 478 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
"abi-decoder": "^2.4.0",
"axios": "0.21.4",
"bignumber.js": "9.0.1",
"bnc-onboard": "~1.35.3",
"bnc-onboard": "^1.37.3",
"classnames": "^2.2.6",
"currency-flags": "3.2.1",
"date-fns": "^2.20.2",
Expand Down
9 changes: 3 additions & 6 deletions src/components/AppLayout/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useSelector } from 'react-redux'

import Layout from './components/Layout'
import ConnectDetails from './components/ProviderDetails/ConnectDetails'
Expand All @@ -14,9 +14,7 @@ import {
userAccountSelector,
userEnsSelector,
} from 'src/logic/wallets/store/selectors'
import { removeProvider } from 'src/logic/wallets/store/actions'
import onboard from 'src/logic/wallets/onboard'
import { loadLastUsedProvider } from 'src/logic/wallets/store/middlewares/providerWatcher'
import onboard, { loadLastUsedProvider } from 'src/logic/wallets/onboard'

const HeaderComponent = (): React.ReactElement => {
const provider = useSelector(providerNameSelector)
Expand All @@ -25,7 +23,6 @@ const HeaderComponent = (): React.ReactElement => {
const ensName = useSelector(userEnsSelector)
const loaded = useSelector(loadedSelector)
const available = useSelector(availableSelector)
const dispatch = useDispatch()

useEffect(() => {
const tryToConnectToLastUsedProvider = async () => {
Expand All @@ -44,7 +41,7 @@ const HeaderComponent = (): React.ReactElement => {
}

const onDisconnect = () => {
dispatch(removeProvider())
onboard().walletReset()
}

const getProviderInfoBased = () => {
Expand Down
1 change: 0 additions & 1 deletion src/components/forms/AddressInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ const AddressInput = ({
// A crypto domain name
if (isValidEnsName(address) || isValidCryptoDomainName(address)) {
setResolutions((prev) => ({ ...prev, [rawVal]: '' }))

getAddressFromDomain(address)
.then((resolverAddr) => {
const formattedAddress = checksumAddress(resolverAddr)
Expand Down
2 changes: 1 addition & 1 deletion src/components/forms/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const minMaxDecimalsLength =
return minMaxLengthErrMsg ? `Should be ${minLen} to ${maxLen} decimals` : undefined
}

export const ADDRESS_REPEATED_ERROR = 'Address already introduced'
export const ADDRESS_REPEATED_ERROR = 'Address already added'
export const OWNER_ADDRESS_IS_SAFE_ADDRESS_ERROR = 'Cannot use Safe itself as owner.'
export const THRESHOLD_ERROR = 'You cannot set more confirmations than owners'

Expand Down
3 changes: 1 addition & 2 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ const formatRpcServiceUrl = ({ authentication, value }: RpcUri, TOKEN: string):
return needsToken ? `${value}${TOKEN}` : value
}

export const getRpcServiceUrl = (): string => {
const { rpcUri } = getChainInfo()
export const getRpcServiceUrl = (rpcUri = getChainInfo().rpcUri): string => {
return formatRpcServiceUrl(rpcUri, INFURA_TOKEN)
}

Expand Down
38 changes: 5 additions & 33 deletions src/logic/wallets/getWeb3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { namehash } from '@ethersproject/hash'
import Safe, { Web3Adapter } from '@gnosis.pm/safe-core-sdk'
import { FEATURES } from '@gnosis.pm/safe-react-gateway-sdk'

import { sameAddress, ZERO_ADDRESS } from './ethAddresses'
import { ZERO_ADDRESS } from './ethAddresses'
import { EMPTY_DATA } from './ethTransactions'
import { ProviderProps } from './store/model/provider'
import { getRpcServiceUrl, _getChainId } from 'src/config'
import { CHAIN_ID, ChainId } from 'src/config/chain.d'
import { isValidCryptoDomainName } from 'src/logic/wallets/ethAddresses'
import { getAddressFromUnstoppableDomain } from './utils/unstoppableDomains'
import { hasFeature } from 'src/logic/safe/utils/safeVersion'
import { checksumAddress } from 'src/utils/checksumAddress'
import { isValidAddress } from 'src/utils/isValidAddress'

// This providers have direct relation with name assigned in bnc-onboard configuration
export enum WALLET_PROVIDER {
Expand Down Expand Up @@ -67,18 +68,10 @@ export const resetWeb3 = (): void => {
web3 = web3ReadOnly[_getChainId()]
}

export const getAccountFrom = async (web3Provider: Web3): Promise<string | null> => {
const accounts = await web3Provider.eth.getAccounts()
return accounts && accounts.length > 0 ? accounts[0] : null
}

export const getChainIdFrom = (web3Provider: Web3): Promise<number> => {
return web3Provider.eth.getChainId()
}

const isHardwareWallet = (walletName: string) =>
sameAddress(WALLET_PROVIDER.LEDGER, walletName) || sameAddress(WALLET_PROVIDER.TREZOR, walletName)

export const isSmartContractWallet = async (account: string): Promise<boolean> => {
if (!account) {
return false
Expand All @@ -91,27 +84,6 @@ export const isSmartContractWallet = async (account: string): Promise<boolean> =
}
return !!contractCode && contractCode.replace(EMPTY_DATA, '').replace(/0/g, '') !== ''
}

export const getProviderInfo = async (web3Instance: Web3, providerName = 'Wallet'): Promise<ProviderProps> => {
const account = (await getAccountFrom(web3Instance)) || ''
const ensDomain = account ? await reverseENSLookup(account) : ''
const network = await getChainIdFrom(web3Instance)
const smartContractWallet = await isSmartContractWallet(account)
const hardwareWallet = isHardwareWallet(providerName)
const available = Boolean(account)

return {
name: providerName,
available,
loaded: true,
account,
ensDomain,
network: network.toString() as ChainId,
smartContractWallet,
hardwareWallet,
}
}

export const getAddressFromDomain = (name: string): Promise<string> => {
if (isValidCryptoDomainName(name)) {
return getAddressFromUnstoppableDomain(name)
Expand All @@ -120,7 +92,7 @@ export const getAddressFromDomain = (name: string): Promise<string> => {
}

export const reverseENSLookup = async (address: string): Promise<string> => {
if (!address || !hasFeature(FEATURES.DOMAIN_LOOKUP)) {
if (!address || !hasFeature(FEATURES.DOMAIN_LOOKUP) || !isValidAddress(address)) {
return ''
}

Expand All @@ -144,7 +116,7 @@ export const reverseENSLookup = async (address: string): Promise<string> => {
return ''
}

return verifiedAddress === address ? name : ''
return checksumAddress(verifiedAddress) === checksumAddress(address) ? name : ''
}

export const getContentFromENS = (name: string): Promise<ContentHash> => web3.eth.ens.getContenthash(name)
Expand Down
104 changes: 72 additions & 32 deletions src/logic/wallets/onboard.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,94 @@
import Onboard from 'bnc-onboard'
import { API, Wallet } from 'bnc-onboard/dist/src/interfaces'
import { store } from 'src/store'
import { API, Initialization } from 'bnc-onboard/dist/src/interfaces'
import { FEATURES } from '@gnosis.pm/safe-react-gateway-sdk'

import { _getChainId, getChainName } from 'src/config'
import { setWeb3 } from './getWeb3'
import { fetchProvider, removeProvider } from './store/actions'
import { setWeb3, isSmartContractWallet, resetWeb3 } from 'src/logic/wallets/getWeb3'
import transactionDataCheck from './transactionDataCheck'
import { getSupportedWallets } from './utils/walletList'
import { ChainId, CHAIN_ID } from 'src/config/chain.d'
import { instantiateSafeContracts } from 'src/logic/contracts/safeContracts'
import { loadFromStorage, removeFromStorage, saveToStorage } from 'src/utils/storage'
import { store } from 'src/store'
import updateProviderWallet from 'src/logic/wallets/store/actions/updateProviderWallet'
import updateProviderAccount from 'src/logic/wallets/store/actions/updateProviderAccount'
import updateProviderNetwork from 'src/logic/wallets/store/actions/updateProviderNetwork'
import updateProviderEns from 'src/logic/wallets/store/actions/updateProviderEns'
import closeSnackbar from '../notifications/store/actions/closeSnackbar'
import { getChains } from 'src/config/cache/chains'

const LAST_USED_PROVIDER_KEY = 'LAST_USED_PROVIDER'

const NETWORK_NAMES: Record<ChainId, string> = {
[CHAIN_ID.ETHEREUM]: 'mainnet',
export const loadLastUsedProvider = (): string | undefined => {
return loadFromStorage<string>(LAST_USED_PROVIDER_KEY)
}

const getOnboardConfiguration = () => {
let lastUsedAddress = ''
let providerName: string | null = null
let lastNetworkId = ''
const getNetworkName = (chainId: ChainId) => {
// 'mainnet' is hardcoded in onboard v1
const NETWORK_NAMES: Record<ChainId, string> = {
[CHAIN_ID.ETHEREUM]: 'mainnet',
}

// Ledger requires lowercase names
return NETWORK_NAMES[chainId] || getChainName().toLowerCase()
}

return {
networkId: parseInt(_getChainId(), 10),
// Is it mandatory for Ledger to work to send network name in lowercase
// @FIXME: Move to CGW
networkName: NETWORK_NAMES[_getChainId()] || getChainName().toLowerCase(),
const hasENSSupport = (chainId: ChainId): boolean => {
return getChains().some((chain) => chain.chainId === chainId && chain.features.includes(FEATURES.DOMAIN_LOOKUP))
}

let prevAddress = ''

const getOnboard = (chainId: ChainId): API => {
const config: Initialization = {
networkId: parseInt(chainId, 10),
networkName: getNetworkName(chainId),
subscriptions: {
wallet: (wallet: Wallet) => {
wallet: async (wallet) => {
if (wallet.provider) {
// this function will intialize web3 and store it somewhere available throughout the dapp and
// can also instantiate your contracts with the web3 instance
setWeb3(wallet.provider)
providerName = wallet.name
instantiateSafeContracts()
}

if (wallet.name) {
saveToStorage(LAST_USED_PROVIDER_KEY, wallet.name)
}

store.dispatch(
updateProviderWallet({
name: wallet.name || '',
hardwareWallet: wallet.type === 'hardware',
smartContractWallet: await isSmartContractWallet(onboard().getState().address),
}),
)
},
address: (address: string) => {
const networkId = _getChainId()
// Non-checksummed address
address: (address) => {
store.dispatch(updateProviderAccount(address))

if (!lastUsedAddress && address && providerName) {
lastUsedAddress = address
lastNetworkId = networkId
store.dispatch(fetchProvider(providerName))
if (address) {
prevAddress = address
}

// we don't have an unsubscribe event so we rely on this
if (!address && lastUsedAddress) {
lastUsedAddress = ''
providerName = null
store.dispatch(removeProvider({ keepStorageKey: lastNetworkId !== networkId }))
// Wallet disconnected
if (!address && prevAddress) {
resetWeb3()
removeFromStorage(LAST_USED_PROVIDER_KEY)
}
},
network: (networkId) => {
store.dispatch(updateProviderNetwork(networkId?.toString() || ''))
store.dispatch(closeSnackbar({ dismissAll: true }))
},
ens: hasENSSupport(chainId)
? (ens) => {
store.dispatch(updateProviderEns(ens?.name || ''))
}
: undefined,
},
walletSelect: {
description: 'Please select a wallet to connect to Gnosis Safe',
wallets: getSupportedWallets(),
wallets: getSupportedWallets(chainId),
},
walletCheck: [
{ checkName: 'derivationPath' },
Expand All @@ -60,13 +98,15 @@ const getOnboardConfiguration = () => {
transactionDataCheck(),
],
}

return Onboard(config)
}

let currentOnboardInstance: API
export const onboard = (): API => {
const chainId = _getChainId()
if (!currentOnboardInstance || currentOnboardInstance.getState().appNetworkId.toString() !== chainId) {
currentOnboardInstance = Onboard(getOnboardConfiguration())
currentOnboardInstance = getOnboard(chainId)
}

return currentOnboardInstance
Expand Down
Loading

0 comments on commit 74e980f

Please sign in to comment.