From 7b0c0129bfd23d3df6767888259dce49e9c72dfd Mon Sep 17 00:00:00 2001 From: Adam Carpenter Date: Fri, 2 Jun 2023 12:58:04 -0600 Subject: [PATCH 1/5] Add ability to pass injected wallet url and display URL if wallet is unavailable and url is available --- .../[...4]wallets/[...9]injected/+page.md | 39 +++-- packages/demo/package.json | 2 +- packages/demo/src/App.svelte | 31 +++- packages/injected/README.md | 40 +++-- packages/injected/package.json | 2 +- packages/injected/src/helpers.ts | 9 +- packages/injected/src/index.ts | 139 ++++++++++-------- packages/injected/src/types.ts | 30 +++- packages/injected/src/validation.ts | 8 +- packages/injected/src/wallets.ts | 8 +- 10 files changed, 207 insertions(+), 101 deletions(-) diff --git a/docs/src/routes/docs/[...4]wallets/[...9]injected/+page.md b/docs/src/routes/docs/[...4]wallets/[...9]injected/+page.md index 23cc2b3f3..240372226 100644 --- a/docs/src/routes/docs/[...4]wallets/[...9]injected/+page.md +++ b/docs/src/routes/docs/[...4]wallets/[...9]injected/+page.md @@ -153,7 +153,12 @@ const equal = { provider: window.ethereum }), // A list of platforms that this wallet supports - platforms: ['desktop'] + platforms: ['desktop'], + /** + * A Url to link users to a download page for the wallet + * to be shown if not installed or available on the browser + */ + externalUrl: 'http://www.CoolEqualWalletDownload.com' } const injected = injectedModule({ @@ -172,17 +177,22 @@ You may want to display injected wallets that are not currently available to the ```javascript const injected = injectedModule({ - displayUnavailable: true + // display all unavailable injected wallets + displayUnavailable: true, + || + // display specific unavailable wallets + displayUnavailable: [ProviderLabel.MetaMask, ProviderLabel.Trust] }) ``` -This will render every injected wallet as regardless of whether it has been detected in the window, happy days. -Then the issue of the order of wallets displayed becomes apparent when you have 21 injected wallets at the top of the wallets list. To solve this, all injected wallets are sorted alphabetically by default and there is an additional `sort` parameter which receives the final list of wallets and then returns the list to be rendered. This allows for example setting MetaMask and Coinbase first and then just the rest alphabetically: +This can be set to an array top specify which unavailable injected wallets to show or set to true to display all unavailable injected wallets regardless of whether it has been detected in the window, happy days. +Then the issue of the order of wallets displayed becomes apparent when you have 21 injected wallets at the top of the wallets list. To solve this, all injected wallets are sorted **alphabetically** by default and there is an additional `sort` parameter which receives the final list of wallets and then returns the list to be rendered. This allows for example setting MetaMask and Coinbase first and then just the rest alphabetically: ```javascript const injected = injectedModule({ - // display all wallets even if they are unavailable - displayUnavailable: true, + // display specific injected wallets even if they are unavailable + // can also be set to `true` to display all unavailable injected wallets + displayUnavailable: [ProviderLabel.MetaMask, ProviderLabel.Trust], // do a manual sort of injected wallets so that MetaMask and Coinbase are ordered first sort: (wallets) => { const metaMask = wallets.find(({ label }) => label === ProviderLabel.MetaMask) @@ -203,11 +213,12 @@ const injected = injectedModule({ }) ``` -You may want to display all wallets, but filter out specific wallets based on their availability. For example I may want to display all unavailable wallets except when Binance and Bitski wallet is unavailable, then don't show them, but if they are available, then do show them. To do this, the filters value has been extended to have a new value: `'unavailable'`, as in; remove this wallet if it is unavailable, even though `displayUnavailable` wallets is set: +You may want to display all unavailable injected wallets, but filter out specific wallets based on their availability. For example I may want to display all unavailable wallets except when Binance and Bitski wallet is unavailable, then don't show them, but if they are available, then do show them. To do this, the filters value has been extended to have a new value: `'unavailable'`, as in; remove this wallet if it is unavailable, even though `displayUnavailable` wallets is set: ```javascript const injected = injectedModule({ - // display all wallets even if they are unavailable + // display specific injected wallets even if they are unavailable + // can also be set to `true` to display all unavailable injected wallets displayUnavailable: true, // but only show Binance and Bitski wallet if they are available filter: { @@ -234,15 +245,16 @@ const injected = injectedModule({ }) ``` -If a wallet is selected, but is not available the default error message is: `Please install or enable ${walletName} to continue`. You may want to customise that message, so there is the `walletUnavailableMessage` parameter which is a function that takes the wallet object that is unavailable and returns a string which is the message to display: +If a wallet is selected, but is not available the default error message is: `Oops ${wallet.label} is unavailable! Please install` if a download link is available for that wallet. If there isn't a download link for that wallet the default is: `Please install or enable ${walletName} to continue`. You may want to customize that message, so there is the `walletUnavailableMessage` parameter which is a function that takes the wallet object that is unavailable and returns a string which is the message to display: ```javascript const injected = injectedModule({ custom: [ // include custom (not natively supported) injected wallet modules here ], - // display all wallets even if they are unavailable - displayUnavailable: true, + // display specific injected wallets even if they are unavailable + // can also be set to `true` to display all unavailable injected wallets + displayUnavailable: [ProviderLabel.MetaMask, ProviderLabel.Trust], // but only show Binance and Bitski wallet if they are available filter: { [ProviderLabel.Binance]: 'unavailable', @@ -265,7 +277,10 @@ const injected = injectedModule({ .filter((wallet) => wallet) ) }, - walletUnavailableMessage: (wallet) => `Oops ${wallet.label} is unavailable!` + walletUnavailableMessage: (wallet) => + wallet.externalUrl + ? `Oops ${wallet.label} is unavailable! Please install` + : `Oops ${wallet.label} is unavailable!` }) ``` diff --git a/packages/demo/package.json b/packages/demo/package.json index 14305bacb..47ab5f16d 100644 --- a/packages/demo/package.json +++ b/packages/demo/package.json @@ -35,7 +35,7 @@ "@web3-onboard/gas": "^2.1.7", "@web3-onboard/gnosis": "^2.1.9", "@web3-onboard/infinity-wallet": "^2.0.3", - "@web3-onboard/injected-wallets": "^2.9.0", + "@web3-onboard/injected-wallets": "^2.10.0-alpha.1", "@web3-onboard/keepkey": "^2.3.7", "@web3-onboard/keystone": "^2.3.7", "@web3-onboard/ledger": "^2.4.5", diff --git a/packages/demo/src/App.svelte b/packages/demo/src/App.svelte index 6a3ca1ac2..7c028b567 100644 --- a/packages/demo/src/App.svelte +++ b/packages/demo/src/App.svelte @@ -70,7 +70,7 @@ const injected = injectedModule({ custom: [ // include custom (not natively supported) injected wallet modules here - ] + ], // display all wallets even if they are unavailable // displayUnavailable: true // but only show Binance and Bitski wallet if they are available @@ -101,7 +101,34 @@ // .filter(wallet => wallet) // ) // } - // walletUnavailableMessage: wallet => `Oops ${wallet.label} is unavailable!` + // display all wallets even if they are unavailable + displayUnavailable: [ProviderLabel.MetaMask, ProviderLabel.Trust], + // do a manual sort of injected wallets so that MetaMask and Coinbase are ordered first + // sort: wallets => { + // const metaMask = wallets.find( + // ({ label }) => label === ProviderLabel.MetaMask + // ) + // const coinbase = wallets.find( + // ({ label }) => label === ProviderLabel.Coinbase + // ) + + // return ( + // [ + // metaMask, + // coinbase, + // ...wallets.filter( + // ({ label }) => + // label !== ProviderLabel.MetaMask && label !== ProviderLabel.Coinbase + // ) + // ] + // // remove undefined values + // .filter(wallet => wallet) + // ) + // }, + // walletUnavailableMessage: wallet => + // wallet.externalUrl + // ? `Oops ${wallet.label} is unavailable! Please install` + // : `Oops ${wallet.label} is unavailable!` }) const coinbaseWallet = coinbaseModule() diff --git a/packages/injected/README.md b/packages/injected/README.md index cbc593533..f42e4bc92 100644 --- a/packages/injected/README.md +++ b/packages/injected/README.md @@ -178,7 +178,12 @@ const equal = { provider: window.ethereum }), // A list of platforms that this wallet supports - platforms: ['desktop'] + platforms: ['desktop'], + /** + * A Url to link users to a download page for the wallet + * to be shown if not installed or available on the browser + */ + externalUrl: 'http://www.CoolEqualWalletDownload.com' } const injected = injectedModule({ @@ -197,17 +202,22 @@ You may want to display injected wallets that are not currently available to the ```javascript const injected = injectedModule({ - displayUnavailable: true + // display all unavailable injected wallets + displayUnavailable: true, + || + // display specific unavailable wallets + displayUnavailable: [ProviderLabel.MetaMask, ProviderLabel.Trust] }) ``` -This will render every injected wallet as regardless of whether it has been detected in the window, happy days. -Then the issue of the order of wallets displayed becomes apparent when you have 21 injected wallets at the top of the wallets list. To solve this, all injected wallets are sorted alphabetically by default and there is an additional `sort` parameter which receives the final list of wallets and then returns the list to be rendered. This allows for example setting MetaMask and Coinbase first and then just the rest alphabetically: +This can be set to an array top specify which unavailable injected wallets to show or set to true to display all unavailable injected wallets regardless of whether it has been detected in the window, happy days. +Then the issue of the order of wallets displayed becomes apparent when you have 21 injected wallets at the top of the wallets list. To solve this, all injected wallets are sorted **alphabetically** by default and there is an additional `sort` parameter which receives the final list of wallets and then returns the list to be rendered. This allows for example setting MetaMask and Coinbase first and then just the rest alphabetically: ```javascript const injected = injectedModule({ - // display all wallets even if they are unavailable - displayUnavailable: true, + // display specific injected wallets even if they are unavailable + // can also be set to `true` to display all unavailable injected wallets + displayUnavailable: [ProviderLabel.MetaMask, ProviderLabel.Trust], // do a manual sort of injected wallets so that MetaMask and Coinbase are ordered first sort: wallets => { const metaMask = wallets.find( @@ -233,11 +243,12 @@ const injected = injectedModule({ }) ``` -You may want to display all wallets, but filter out specific wallets based on their availability. For example I may want to display all unavailable wallets except when Binance and Bitski wallet is unavailable, then don't show them, but if they are available, then do show them. To do this, the filters value has been extended to have a new value: `'unavailable'`, as in; remove this wallet if it is unavailable, even though `displayUnavailable` wallets is set: +You may want to display all unavailable injected wallets, but filter out specific wallets based on their availability. For example I may want to display all unavailable wallets except when Binance and Bitski wallet is unavailable, then don't show them, but if they are available, then do show them. To do this, the filters value has been extended to have a new value: `'unavailable'`, as in; remove this wallet if it is unavailable, even though `displayUnavailable` wallets is set: ```javascript const injected = injectedModule({ - // display all wallets even if they are unavailable + // display specific injected wallets even if they are unavailable + // can also be set to `true` to display all unavailable injected wallets displayUnavailable: true, // but only show Binance and Bitski wallet if they are available filter: { @@ -269,15 +280,17 @@ const injected = injectedModule({ }) ``` -If a wallet is selected, but is not available the default error message is: `Please install or enable ${walletName} to continue`. You may want to customise that message, so there is the `walletUnavailableMessage` parameter which is a function that takes the wallet object that is unavailable and returns a string which is the message to display: +If a wallet is selected, but is not available the default error message is: `Oops ${wallet.label} is unavailable! Please install` if a download link is available for that wallet. If there isn't a download link for that wallet the default is: `Please install or enable ${walletName} to continue`. You may want to customize that message, so there is the `walletUnavailableMessage` parameter which is a function that takes the wallet object that is unavailable and returns a string which is the message to display: ```javascript const injected = injectedModule({ custom: [ // include custom (not natively supported) injected wallet modules here ], - // display all wallets even if they are unavailable - displayUnavailable: true, + // display specific injected wallets even if they are unavailable + // can also be set to `true` to display all unavailable injected wallets + displayUnavailable: [ProviderLabel.MetaMask, ProviderLabel.Trust], + // but only show Binance and Bitski wallet if they are available filter: { [ProviderLabel.Binance]: 'unavailable', @@ -305,6 +318,9 @@ const injected = injectedModule({ .filter(wallet => wallet) ) }, - walletUnavailableMessage: wallet => `Oops ${wallet.label} is unavailable!` + walletUnavailableMessage: wallet => + wallet.externalUrl + ? `Oops ${wallet.label} is unavailable! Please install` + : `Oops ${wallet.label} is unavailable!` }) ``` diff --git a/packages/injected/package.json b/packages/injected/package.json index fd994cb20..d1c3e7479 100644 --- a/packages/injected/package.json +++ b/packages/injected/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/injected-wallets", - "version": "2.9.0", + "version": "2.10.0-alpha.1", "description": "Injected wallet module for connecting browser extension and mobile wallets to Web3-Onboard. 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, 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", diff --git a/packages/injected/src/helpers.ts b/packages/injected/src/helpers.ts index 38e48110b..94026390d 100644 --- a/packages/injected/src/helpers.ts +++ b/packages/injected/src/helpers.ts @@ -14,8 +14,13 @@ export class ProviderRpcError extends Error { } } -export const defaultWalletUnavailableMsg = ({ label }: InjectedWalletModule) => - `Please install or enable ${label} to continue` +export const defaultWalletUnavailableMsg = ({ + label, + externalUrl +}: InjectedWalletModule) => + externalUrl + ? `Please install or enable ${label} to continue` + : `Please install or enable ${label} to continue` export const isWalletAvailable = ( provider: InjectedProvider, diff --git a/packages/injected/src/index.ts b/packages/injected/src/index.ts index 974465bf4..6cc33baf7 100644 --- a/packages/injected/src/index.ts +++ b/packages/injected/src/index.ts @@ -41,76 +41,87 @@ function injected(options?: InjectedWalletOptions): WalletInit { ({ label }) => label ) - const wallets = allWallets.reduce((acc, wallet) => { - const { label, platforms, injectedNamespace, checkProviderIdentity } = - wallet - - const walletFilters = filter[label] - const filteredWallet = walletFilters === false - const provider = window[injectedNamespace] as CustomWindow['ethereum'] - - const walletAvailable = isWalletAvailable( - provider, - checkProviderIdentity, - device - ) - - let excludedDevice: boolean = false - - // dev specified platform filters - if ( - Array.isArray(walletFilters) && - (walletFilters.includes(device.type) || - walletFilters.includes(device.os.name)) - ) { - excludedDevice = true - } - - // unavailable filter - if (walletFilters === 'unavailable' && !walletAvailable) { - excludedDevice = true - } - - // wallet specified platform filters - const invalidPlatform = - !platforms.includes('all') && - !platforms.includes(device.type) && - !platforms.includes(device.os.name) - - const supportedWallet = - !filteredWallet && - !excludedDevice && - !invalidPlatform && - (walletAvailable || displayUnavailable) - - if (supportedWallet) { - acc.push( - // modify wallet to display error if unavailable but displayUnavailable is set - displayUnavailable && !walletAvailable - ? { - ...wallet, - getInterface: async () => { - throw new Error( - walletUnavailableMessage - ? walletUnavailableMessage(wallet) - : defaultWalletUnavailableMsg(wallet) - ) - } - } - : // otherwise add wallet to list as is - wallet + const wallets = allWallets.reduce( + (acc: InjectedWalletModule[], wallet: InjectedWalletModule) => { + const { label, platforms, injectedNamespace, checkProviderIdentity } = + wallet + + const walletFilters = filter[label] + const filteredWallet = walletFilters === false + const provider = window[injectedNamespace] as CustomWindow['ethereum'] + + const walletAvailable = isWalletAvailable( + provider, + checkProviderIdentity, + device ) - } - return acc - }, [] as InjectedWalletModule[]) + let excludedDevice: boolean = false + + // dev specified platform filters + if ( + Array.isArray(walletFilters) && + (walletFilters.includes(device.type) || + walletFilters.includes(device.os.name)) + ) { + excludedDevice = true + } + + // unavailable filter + if (walletFilters === 'unavailable' && !walletAvailable) { + excludedDevice = true + } + + // wallet specified platform filters + const invalidPlatform = + !platforms.includes('all') && + !platforms.includes(device.type) && + !platforms.includes(device.os.name) + + const supportedWallet = + !filteredWallet && + !excludedDevice && + !invalidPlatform && + (walletAvailable || + displayUnavailable === true || + (Array.isArray(displayUnavailable) && + displayUnavailable.length && + displayUnavailable.includes(wallet.label))) + + if (supportedWallet) { + acc.push( + // modify wallet to display error if unavailable but displayUnavailable is set + (displayUnavailable === true || + (Array.isArray(displayUnavailable) && + displayUnavailable.length && + displayUnavailable.includes(wallet.label))) && + !walletAvailable + ? { + ...wallet, + getInterface: async () => { + throw new Error( + walletUnavailableMessage + ? walletUnavailableMessage(wallet) + : defaultWalletUnavailableMsg(wallet) + ) + } + } + : // otherwise add wallet to list as is + wallet + ) + } + + return acc + }, + [] as InjectedWalletModule[] + ) if (wallets.length) { const moreThanOneWallet = wallets.length > 1 // if more than one wallet, then remove detected wallet const formattedWallets = wallets - .filter(wallet => { + .filter((wallet: InjectedWalletModule) => { const { label } = wallet return !(label === ProviderLabel.Detected && moreThanOneWallet) }) @@ -121,7 +132,9 @@ function injected(options?: InjectedWalletOptions): WalletInit { getInterface })) // default sort by alphabetical - .sort((a, b) => (a.label < b.label ? -1 : a.label > b.label ? 1 : 0)) + .sort((a, b) => + a.label < b.label ? -1 : a.label > b.label ? 1 : 0 + ) return sort ? sort(formattedWallets) : formattedWallets } diff --git a/packages/injected/src/types.ts b/packages/injected/src/types.ts index af5fdfa25..201fc0d56 100644 --- a/packages/injected/src/types.ts +++ b/packages/injected/src/types.ts @@ -65,6 +65,24 @@ export enum ProviderIdentityFlag { Talisman = 'isTalisman' } + +/** + * The ProviderExternalUrl enum represents the external URLs associated + * with different injected providers. It is used to direct end users who + * do not have a wallet installed to the corresponding wallet installation page. + * For this to be displayed a dapp must set `displayUnavailable` + * to an array (to specify displayed unavailable wallets) or + * true (to display all unavailable wallets) and a user select that wallet. + */ +export enum ProviderExternalUrl { + Binance = 'https://www.bnbchain.org/ru/blog/binance-extension-wallet/', + Coinbase = 'https://www.coinbase.com/wallet/downloads', + MetaMask = 'https://metamask.io/download/', + Phantom = 'https://phantom.app/download', + Talisman = 'https://www.talisman.xyz/', + Trust = 'https://trustwallet.com/download/', +} + export enum ProviderLabel { AlphaWallet = 'AlphaWallet', ApexWallet = 'Apex Wallet', @@ -207,10 +225,13 @@ export interface InjectedWalletOptions { * By default all wallets listed in ./packages/injected/ * are included add them to here to remove them. */ filter?: WalletFilters - /**Will display wallets to be selected even if they + /**If set to true: Will display all unavailable injected wallets even if they * are not currently available to the end user. + * If set to an array of ProviderLabel.walletLabel + * those wallets will be the only unavailable injected wallets shown + * For example [ProviderLabel.MetaMask, ProviderLabel.Trust] */ - displayUnavailable?: boolean + displayUnavailable?: boolean | string[] /**A function that allows for customizing the message to be displayed if the wallet * is unavailable */ @@ -223,4 +244,9 @@ export interface InjectedWalletModule extends WalletModule { injectedNamespace: InjectedNameSpace checkProviderIdentity: (helpers: { provider: any; device: Device }) => boolean platforms: Platform[] + /** + * A Url to link users to a download page for the wallet + * to be shown if not installed or available on the browser + */ + externalUrl?: string } diff --git a/packages/injected/src/validation.ts b/packages/injected/src/validation.ts index 0ac81e039..a7965151c 100644 --- a/packages/injected/src/validation.ts +++ b/packages/injected/src/validation.ts @@ -8,7 +8,8 @@ const walletModule = Joi.object({ getInterface: Joi.function().maxArity(1).required(), injectedNamespace: Joi.string().required(), checkProviderIdentity: Joi.function().arity(1).required(), - platforms: Joi.array().items(Joi.string()) + platforms: Joi.array().items(Joi.string()), + externalUrl: Joi.string() }) const wallets = Joi.array().items(walletModule) @@ -21,9 +22,10 @@ const filter = Joi.object().pattern( const walletOptions = Joi.object({ custom: wallets, filter, - displayUnavailable: Joi.boolean(), + displayUnavailable: [Joi.boolean(), Joi.array().items(Joi.string())], walletUnavailableMessage: Joi.function(), - sort: Joi.function() + sort: Joi.function(), + externalUrl: Joi.string() }) export const validateWalletOptions = ( diff --git a/packages/injected/src/wallets.ts b/packages/injected/src/wallets.ts index 008b8de1c..4d2db711f 100644 --- a/packages/injected/src/wallets.ts +++ b/packages/injected/src/wallets.ts @@ -6,10 +6,11 @@ import type { } from '@web3-onboard/common' import { createEIP1193Provider } from '@web3-onboard/common' -import type { +import { InjectedWalletModule, CustomWindow, - BinanceProvider + BinanceProvider, + ProviderExternalUrl } from './types.js' import { @@ -61,7 +62,8 @@ const metamask: InjectedWalletModule = { !otherProviderFlagsExist(ProviderIdentityFlag.MetaMask, provider), getIcon: async () => (await import('./icons/metamask.js')).default, getInterface: getInjectedInterface(ProviderIdentityFlag.MetaMask, true), - platforms: ['all'] + platforms: ['all'], + externalUrl: ProviderExternalUrl.MetaMask } const infinitywallet: InjectedWalletModule = { From 97f41dca708b56291bd2923f3bffd47edf54fe3f Mon Sep 17 00:00:00 2001 From: Adam Carpenter Date: Fri, 2 Jun 2023 13:33:28 -0600 Subject: [PATCH 2/5] Cleanup demo --- packages/demo/src/App.svelte | 90 ++++++++++++++---------------------- 1 file changed, 34 insertions(+), 56 deletions(-) diff --git a/packages/demo/src/App.svelte b/packages/demo/src/App.svelte index e8b138dab..fe49f6705 100644 --- a/packages/demo/src/App.svelte +++ b/packages/demo/src/App.svelte @@ -71,8 +71,11 @@ custom: [ // include custom (not natively supported) injected wallet modules here ], - // display all wallets even if they are unavailable - // displayUnavailable: true + // display all unavailable injected wallets + // displayUnavailable: true, + // || + // display specific unavailable wallets + displayUnavailable: [ProviderLabel.MetaMask, ProviderLabel.Trust], // but only show Binance and Bitski wallet if they are available // filter: { // [ProviderLabel.Binance]: 'unavailable', @@ -101,34 +104,10 @@ // .filter(wallet => wallet) // ) // } - // display all wallets even if they are unavailable - displayUnavailable: [ProviderLabel.MetaMask, ProviderLabel.Trust], - // do a manual sort of injected wallets so that MetaMask and Coinbase are ordered first - // sort: wallets => { - // const metaMask = wallets.find( - // ({ label }) => label === ProviderLabel.MetaMask - // ) - // const coinbase = wallets.find( - // ({ label }) => label === ProviderLabel.Coinbase - // ) - - // return ( - // [ - // metaMask, - // coinbase, - // ...wallets.filter( - // ({ label }) => - // label !== ProviderLabel.MetaMask && label !== ProviderLabel.Coinbase - // ) - // ] - // // remove undefined values - // .filter(wallet => wallet) - // ) - // }, - // walletUnavailableMessage: wallet => - // wallet.externalUrl - // ? `Oops ${wallet.label} is unavailable! Please install` - // : `Oops ${wallet.label} is unavailable!` + // walletUnavailableMessage: wallet => + // wallet.externalUrl + // ? `Oops ${wallet.label} is unavailable! Please install` + // : `Oops ${wallet.label} is unavailable!` }) const coinbaseWallet = coinbaseModule() @@ -672,31 +651,31 @@ }}>Send Success Notification - - + on:click={() => + onboard.state.actions.customNotification({ + message: + 'This is a custom DApp success notification to use however you want', + autoDismiss: 0, + type: 'pending' + })}>Send Pending Notification + +
-