Skip to content

Commit

Permalink
[core, react, vue]: Feature - Connect all previously connected wallets (
Browse files Browse the repository at this point in the history
#1603)

* add validation, parameterize, backwards compatibility and testing

* Update docs for new prop

* Merge in dev and handle conflicts

* Add length check

* Try block

* Add second try block for parsing local storage

* Added final syntax check

* Add docs for chain coverage
  • Loading branch information
Adamj1232 authored Mar 24, 2023
1 parent ef3970c commit 494dd26
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 41 deletions.
6 changes: 5 additions & 1 deletion docs/src/routes/docs/[...1]overview/[...1]introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ Web3-Onboard is the quickest and easiest way to add multi-wallet and multi-chain

web3-onboard supports all EVM networks. Supporting a new network is simply a matter of adding its details in the Chains section upon initialization. For more information see [initialization options](https://onboard.blocknative.com/docs/modules/core#options).

- Ethereum
- Arbitrum
- Optimism
- Avalanche
- BNB Chain
- Celo
Expand All @@ -44,8 +46,10 @@ web3-onboard supports all EVM networks. Supporting a new network is simply a mat
- Gnosis Chain
- Harmony One
- Moonriver
- Optimism
- Polygon
- Goerli
- Sepolia
- Core Goerli
- Any other EVM network

### [Optional] Use an API key to fetch real time transaction data, balances & gas
Expand Down
34 changes: 23 additions & 11 deletions docs/src/routes/docs/[...3]modules/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ npm install @web3-onboard/core
</Tabs>

#### All Wallet Modules

If you would like to support all wallets, then you can install all of the wallet modules:

<Tabs values={['yarn', 'npm']}>
Expand Down Expand Up @@ -96,12 +97,13 @@ type InitOptions {
}

```

---

#### wallets

An array of wallet modules that you would like to be presented to the user to select from when connecting a wallet. A wallet module is an abstraction that allows for easy interaction without needing to know the specifics of how that wallet works and are separate packages that can be included.
These modules are separate @web3-onboard packages such as `@web3-onboard/injected-wallets` or `@web3-onboard/ledger`.
An array of wallet modules that you would like to be presented to the user to select from when connecting a wallet. A wallet module is an abstraction that allows for easy interaction without needing to know the specifics of how that wallet works and are separate packages that can be included.
These modules are separate @web3-onboard packages such as `@web3-onboard/injected-wallets` or `@web3-onboard/ledger`.
For a full list click [here](#all-wallet-modules).

---
Expand All @@ -113,11 +115,11 @@ An array of Chains that your app supports:
```ts
type Chain = {
id: ChainId // hex encoded string, eg '0x1' for Ethereum Mainnet
namespace?: 'evm' // string indicating chain namespace. Defaults to 'evm' but will allow other chain namespaces in the future
// PLEASE NOTE: Some wallets require an rpcUrl, label, and token for actions such as adding a new chain.
// It is recommended to include rpcUrl, label, and token for full functionality.
rpcUrl?: string // Recommended to include. Used for network requests.
label?: string // Recommended to include. Used for display, eg Ethereum Mainnet.
namespace?: 'evm' // string indicating chain namespace. Defaults to 'evm' but will allow other chain namespaces in the future
// PLEASE NOTE: Some wallets require an rpcUrl, label, and token for actions such as adding a new chain.
// It is recommended to include rpcUrl, label, and token for full functionality.
rpcUrl?: string // Recommended to include. Used for network requests.
label?: string // Recommended to include. Used for display, eg Ethereum Mainnet.
token?: TokenSymbol // Recommended to include. The native token symbol, eg ETH, BNB, MATIC.
color?: string // the color used to represent the chain and will be used as a background for the icon
icon?: string // the icon to represent the chain
Expand Down Expand Up @@ -181,18 +183,28 @@ An object that allows for customizing the connect modal layout and behavior
```typescript copy
type ConnectModalOptions = {
/**
* Display the connect modal sidebar - only applies to desktop views
*/
showSidebar?: boolean
/**
* Disabled close of the connect modal with background click and
* hides the close button forcing an action from the connect modal
* Defaults to false
*/
disableClose?: boolean
/**If set to true, the last connected wallet will store in local storage.
* Then on init, onboard will try to reconnect to that wallet with
* no modals displayed
/**
* If set to true, the most recently connected wallet will store in
* local storage. Then on init, onboard will try to reconnect to
* that wallet with no modals displayed
*/
autoConnectLastWallet?: boolean
/**
* If set to true, all previously connected wallets will store in
* local storage. Then on init, onboard will try to reconnect to
* each wallet with no modals displayed
*/
autoConnectLastWallet?: boolean // defaults to false
autoConnectAllPreviousWallet?: boolean
/**
* Customize the link for the `I don't have a wallet` flow shown on the
* select wallet modal.
Expand Down
21 changes: 16 additions & 5 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,28 @@ An object that allows for customization of the Connect Modal and accepts the typ
```typescript
type ConnectModalOptions = {
/**
* Display the connect modal sidebar - only applies to desktop views
*/
showSidebar?: boolean
/**
* Disabled close of the connect modal with background click and
* hides the close button forcing an action from the connect modal
* Defaults to false
*/
disableClose?: boolean // defaults to false
/**If set to true, the last connected wallet will store in local storage.
* Then on init, onboard will try to reconnect to that wallet with
* no modals displayed
disableClose?: boolean
/**
* If set to true, the most recently connected wallet will store in
* local storage. Then on init, onboard will try to reconnect to
* that wallet with no modals displayed
*/
autoConnectLastWallet?: boolean
/**
* If set to true, all previously connected wallets will store in
* local storage. Then on init, onboard will try to reconnect to
* each wallet with no modals displayed
*/
autoConnectLastWallet?: boolean // defaults to false
autoConnectAllPreviousWallet?: boolean
/**
* Customize the link for the `I don't have a wallet` flow shown on the
* select wallet modal.
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@web3-onboard/core",
"version": "2.15.6-alpha.4",
"version": "2.15.6-alpha.5",
"description": "Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardized spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.",
"keywords": [
"Ethereum",
Expand Down
12 changes: 10 additions & 2 deletions packages/core/src/disconnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { removeWallet } from './store/actions.js'
import { disconnectWallet$ } from './streams.js'
import type { DisconnectOptions, WalletState } from './types.js'
import { validateDisconnectOptions } from './validation.js'
import { delLocalStore, getLocalStore } from './utils'
import { delLocalStore, getLocalStore, setLocalStore } from './utils'
import { STORAGE_KEYS } from './constants'

async function disconnect(options: DisconnectOptions): Promise<WalletState[]> {
Expand Down Expand Up @@ -35,7 +35,15 @@ async function disconnect(options: DisconnectOptions): Promise<WalletState[]> {
disconnectWallet$.next(label)
removeWallet(label)

if (getLocalStore(STORAGE_KEYS.LAST_CONNECTED_WALLET) === label) {
const labels = JSON.parse(getLocalStore(STORAGE_KEYS.LAST_CONNECTED_WALLET))

if (Array.isArray(labels) && labels.indexOf(label) >= 0) {
setLocalStore(
STORAGE_KEYS.LAST_CONNECTED_WALLET,
JSON.stringify(labels.filter(walletLabel => walletLabel !== label))
)
}
if (typeof labels === 'string' && labels === label) {
delLocalStore(STORAGE_KEYS.LAST_CONNECTED_WALLET)
}

Expand Down
67 changes: 58 additions & 9 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { state } from './store/index.js'
import { reset$, wallets$ } from './streams.js'
import initI18N from './i18n/index.js'
import App from './views/Index.svelte'
import type { InitOptions, Notify } from './types.js'
import type { ConnectModalOptions, InitOptions, Notify } from './types.js'
import { APP_INITIAL_STATE, STORAGE_KEYS } from './constants.js'
import { configuration, updateConfiguration } from './configuration.js'
import updateBalances from './update-balances.js'
import { chainIdToHex, getLocalStore } from './utils.js'
import { chainIdToHex, getLocalStore, setLocalStore } from './utils.js'
import { preflightNotifications } from './preflight-notifications.js'

import {
Expand Down Expand Up @@ -227,20 +227,69 @@ function init(options: InitOptions): OnboardAPI {
theme && updateTheme(theme)

// handle auto connection of last wallet
if (connect && connect.autoConnectLastWallet) {
const lastConnectedWallet = getLocalStore(
if (
connect &&
(connect.autoConnectLastWallet || connect.autoConnectAllPreviousWallet)
) {
const lastConnectedWallets = getLocalStore(
STORAGE_KEYS.LAST_CONNECTED_WALLET
)

lastConnectedWallet &&
API.connectWallet({
autoSelect: { label: lastConnectedWallet, disableModals: true }
})
try {
const lastConnectedWalletsParsed = JSON.parse(lastConnectedWallets)
if (
lastConnectedWalletsParsed &&
Array.isArray(lastConnectedWalletsParsed) &&
lastConnectedWalletsParsed.length
) {
connectAllPreviousWallets(lastConnectedWalletsParsed, connect)
}
} catch (err) {
// Handle for legacy single wallet approach
// Above try will throw syntax error is local storage is not json
if (err instanceof SyntaxError && lastConnectedWallets) {
API.connectWallet({
autoSelect: {
label: lastConnectedWallets,
disableModals: true
}
})
}
}
}

return API
}

const connectAllPreviousWallets = async (
lastConnectedWallets: Array<string>,
connect: ConnectModalOptions
): Promise<void> => {
const activeWalletsList = []
const parsedWalletList = lastConnectedWallets

if (!connect.autoConnectAllPreviousWallet) {
API.connectWallet({
autoSelect: { label: parsedWalletList[0], disableModals: true }
})
activeWalletsList.push(parsedWalletList[0])
} else {
// Loop in reverse to maintain wallet order
for (let i = parsedWalletList.length; i--; ) {
const walletConnectionPromise = await API.connectWallet({
autoSelect: { label: parsedWalletList[i], disableModals: true }
})
// Update localStorage list for available wallets
if (walletConnectionPromise.some(r => r.label === parsedWalletList[i])) {
activeWalletsList.unshift(parsedWalletList[i])
}
}
}
setLocalStore(
STORAGE_KEYS.LAST_CONNECTED_WALLET,
JSON.stringify(activeWalletsList)
)
}

function mountApp() {
class Onboard extends HTMLElement {
constructor() {
Expand Down
13 changes: 10 additions & 3 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,18 @@ export type ConnectModalOptions = {
* Defaults to false
*/
disableClose?: boolean
/**If set to true, the last connected wallet will store in local storage.
* Then on init, onboard will try to reconnect to that wallet with
* no modals displayed
/**
* If set to true, the most recently connected wallet will store in
* local storage. Then on init, onboard will try to reconnect to
* that wallet with no modals displayed
*/
autoConnectLastWallet?: boolean
/**
* If set to true, all previously connected wallets will store in
* local storage. Then on init, onboard will try to reconnect to
* each wallet with no modals displayed
*/
autoConnectAllPreviousWallet?: boolean
/**
* Customize the link for the `I don't have a wallet` flow shown on the
* select wallet modal.
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ const connectModalOptions = Joi.object({
showSidebar: Joi.boolean(),
disableClose: Joi.boolean(),
autoConnectLastWallet: Joi.boolean(),
autoConnectAllPreviousWallet: Joi.boolean(),
iDontHaveAWalletLink: Joi.string(),
disableUDResolution: Joi.boolean()
})
Expand Down
41 changes: 38 additions & 3 deletions packages/core/src/views/connect/Index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
import { state } from '../../store/index.js'
import { connectWallet$, onDestroy$ } from '../../streams.js'
import { addWallet, updateAccount } from '../../store/actions.js'
import { validEnsChain, isSVG, setLocalStore } from '../../utils.js'
import {
validEnsChain,
isSVG,
setLocalStore,
getLocalStore
} from '../../utils.js'
import CloseButton from '../shared/CloseButton.svelte'
import Modal from '../shared/Modal.svelte'
import Agreement from './Agreement.svelte'
Expand Down Expand Up @@ -207,8 +212,38 @@
}
// store last connected wallet
if (state.get().connect.autoConnectLastWallet) {
setLocalStore(STORAGE_KEYS.LAST_CONNECTED_WALLET, label)
if (
state.get().connect.autoConnectLastWallet ||
state.get().connect.autoConnectAllPreviousWallet
) {
let labelsList: string | Array<String> = getLocalStore(
STORAGE_KEYS.LAST_CONNECTED_WALLET
)
try {
let labelsListParsed: Array<String> = JSON.parse(labelsList)
if (labelsListParsed && Array.isArray(labelsListParsed)) {
const tempLabels = labelsListParsed
labelsList = [...new Set([label, ...tempLabels])]
}
} catch (err) {
if (
err instanceof SyntaxError &&
labelsList &&
typeof labelsList === 'string'
) {
const tempLabel = labelsList
labelsList = [tempLabel]
} else {
throw new Error(err as string)
}
}
if (!labelsList) labelsList = [label]
setLocalStore(
STORAGE_KEYS.LAST_CONNECTED_WALLET,
JSON.stringify(labelsList)
)
}
const chain = await getChainId(provider)
Expand Down
2 changes: 1 addition & 1 deletion packages/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"webpack-dev-server": "4.7.4"
},
"dependencies": {
"@web3-onboard/core": "^2.15.6-alpha.4",
"@web3-onboard/core": "^2.15.6-alpha.5",
"@web3-onboard/coinbase": "^2.2.1-alpha.1",
"@web3-onboard/transaction-preview": "^2.0.5-alpha.1",
"@web3-onboard/dcent": "^2.2.4-alpha.1",
Expand Down
3 changes: 2 additions & 1 deletion packages/demo/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@
connect: {
// disableClose: true,
// disableUDResolution: true,
autoConnectLastWallet: true
autoConnectLastWallet: true,
autoConnectAllPreviousWallet: true
},
appMetadata: {
name: 'Blocknative',
Expand Down
4 changes: 2 additions & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@web3-onboard/react",
"version": "2.6.7-alpha.4",
"version": "2.6.7-alpha.5",
"description": "A collection of React hooks for integrating Web3-Onboard in to React and Next.js projects. Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardised spec compliant web3 providers for all supported wallets, modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.",
"keywords": [
"Ethereum",
Expand Down Expand Up @@ -62,7 +62,7 @@
"typescript": "^4.5.5"
},
"dependencies": {
"@web3-onboard/core": "^2.15.6-alpha.4",
"@web3-onboard/core": "^2.15.6-alpha.5",
"@web3-onboard/common": "^2.2.4-alpha.1",
"use-sync-external-store": "1.0.0"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/vue/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@web3-onboard/vue",
"version": "2.5.7-alpha.4",
"version": "2.5.7-alpha.5",
"description": "A collection of Vue Composables for integrating Web3-Onboard in to a Vue or Nuxt project. Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardized spec compliant web3 providers for all supported wallets, modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.",
"keywords": [
"Ethereum",
Expand Down Expand Up @@ -63,7 +63,7 @@
"@vueuse/core": "^8.4.2",
"@vueuse/rxjs": "^8.2.0",
"@web3-onboard/common": "^2.2.4-alpha.1",
"@web3-onboard/core": "^2.15.6-alpha.4",
"@web3-onboard/core": "^2.15.6-alpha.5",
"vue-demi": "^0.12.4"
},
"peerDependencies": {
Expand Down

0 comments on commit 494dd26

Please sign in to comment.