UNPKG

idquia

Version:

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, mul

477 lines (413 loc) 12.2 kB
import connectWallet from './connect.js' import disconnectWallet from './disconnect.js' import setChain from './chain.js' 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 { ConnectModalOptions, InitOptions, Notify, Theme } 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, setLocalStore } from './utils.js' import { preflightNotifications } from './preflight-notifications.js' import { validateInitOptions, validateNotify, validateNotifyOptions } from './validation.js' import { addChains, updateAccountCenter, updateNotify, customNotification, setLocale, setPrimaryWallet, setWalletModules, updateConnectModal, updateTheme, updateAppMetadata } from './store/actions.js' import type { PatchedEIP1193Provider } from '@web3-onboard/transaction-preview' import { getBlocknativeSdk } from './services.js' const API = { connectWallet, disconnectWallet, setChain, state: { get: state.get, select: state.select, actions: { setWalletModules, setLocale, updateNotify, customNotification, preflightNotifications, updateBalances, updateAccountCenter, setPrimaryWallet, updateTheme, updateAppMetadata } } } export type OnboardAPI = typeof API export type { InitOptions, ConnectOptions, DisconnectOptions, WalletState, ConnectedChain, AccountCenter, AppState, CustomNotification, Notification, Notify, UpdateNotification, PreflightNotificationsOptions, Theme } from './types.js' export type { EIP1193Provider } from '@web3-onboard/common' function init(options: InitOptions): OnboardAPI { if (typeof window === 'undefined') return API if (options) { const error = validateInitOptions(options) if (error) { throw error } } const { wallets, chains, appMetadata, i18n, accountCenter, apiKey, notify, gas, connect, containerElements, transactionPreview, theme, disableFontDownload, unstoppableResolution } = options if (containerElements) updateConfiguration({ containerElements }) const { device, svelteInstance } = configuration if (svelteInstance) { // if already initialized, need to cleanup old instance console.warn('Re-initializing Onboard and resetting back to initial state') reset$.next() } initI18N(i18n) addChains(chainIdToHex(chains)) if (typeof connect !== undefined) { updateConnectModal(connect) } // update accountCenter if (typeof accountCenter !== 'undefined') { let accountCenterUpdate const { hideTransactionProtectionBtn, transactionProtectionInfoLink } = accountCenter if (device.type === 'mobile') { accountCenterUpdate = { ...APP_INITIAL_STATE.accountCenter, hideTransactionProtectionBtn, transactionProtectionInfoLink, ...(accountCenter.mobile ? accountCenter.mobile : {}) } } else if (accountCenter.desktop) { accountCenterUpdate = { ...APP_INITIAL_STATE.accountCenter, hideTransactionProtectionBtn, transactionProtectionInfoLink, ...accountCenter.desktop } } updateAccountCenter(accountCenterUpdate) } // update notify if (typeof notify !== 'undefined') { if ('desktop' in notify || 'mobile' in notify) { const error = validateNotifyOptions(notify) if (error) { throw error } if ( (!notify.desktop || (notify.desktop && !notify.desktop.position)) && accountCenter && accountCenter.desktop && accountCenter.desktop.position ) { notify.desktop.position = accountCenter.desktop.position } if ( (!notify.mobile || (notify.mobile && !notify.mobile.position)) && accountCenter && accountCenter.mobile && accountCenter.mobile.position ) { notify.mobile.position = accountCenter.mobile.position } let notifyUpdate: Partial<Notify> if (device.type === 'mobile' && notify.mobile) { notifyUpdate = { ...APP_INITIAL_STATE.notify, ...notify.mobile } } else if (notify.desktop) { notifyUpdate = { ...APP_INITIAL_STATE.notify, ...notify.desktop } } updateNotify(notifyUpdate) } else { const error = validateNotify(notify as Notify) if (error) { throw error } const notifyUpdate: Partial<Notify> = { ...APP_INITIAL_STATE.notify, ...notify } updateNotify(notifyUpdate) } } else { const notifyUpdate: Partial<Notify> = APP_INITIAL_STATE.notify updateNotify(notifyUpdate) } const app = svelteInstance || mountApp(theme, disableFontDownload) updateConfiguration({ svelteInstance: app, apiKey, initialWalletInit: wallets, gas, transactionPreview, unstoppableResolution }) appMetadata && updateAppMetadata(appMetadata) if (apiKey && transactionPreview) { const getBnSDK = async () => { transactionPreview.init({ containerElement: '#w3o-transaction-preview-container', sdk: await getBlocknativeSdk(), apiKey }) wallets$.subscribe(wallets => { wallets.forEach(({ provider }) => { transactionPreview.patchProvider(provider as PatchedEIP1193Provider) }) }) } getBnSDK() } theme && updateTheme(theme) // handle auto connection of last wallet if ( connect && (connect.autoConnectLastWallet || connect.autoConnectAllPreviousWallet) ) { const lastConnectedWallets = getLocalStore( STORAGE_KEYS.LAST_CONNECTED_WALLET ) 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 fontFamilyExternallyDefined = ( theme: Theme, disableFontDownload: boolean ): boolean => { if (disableFontDownload) return true if ( document.body && (getComputedStyle(document.body).getPropertyValue( '--onboard-font-family-normal' ) || getComputedStyle(document.body).getPropertyValue('--w3o-font-family')) ) return true if (!theme) return false if (typeof theme === 'object' && theme['--w3o-font-family']) return true return false } const importInterFont = async (): Promise<void> => { const { InterVar } = await import('@web3-onboard/common') // Add Fonts to main page const styleEl = document.createElement('style') styleEl.innerHTML = ` ${InterVar} ` document.body.appendChild(styleEl) } 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(theme: Theme, disableFontDownload: boolean) { class Onboard extends HTMLElement { constructor() { super() } } if (!customElements.get('onboard-v2')) { customElements.define('onboard-v2', Onboard) } if (!fontFamilyExternallyDefined(theme, disableFontDownload)) { importInterFont() } // add to DOM const onboard = document.createElement('onboard-v2') const target = onboard.attachShadow({ mode: 'open' }) onboard.style.all = 'initial' target.innerHTML = ` <style> :host { /* COLORS */ --white: white; --black: black; --primary-1: #2F80ED; --primary-100: #eff1fc; --primary-200: #d0d4f7; --primary-300: #b1b8f2; --primary-400: #929bed; --primary-500: #6370e5; --primary-600: #454ea0; --primary-700: #323873; --gray-100: #ebebed; --gray-200: #c2c4c9; --gray-300: #999ca5; --gray-400: #707481; --gray-500: #33394b; --gray-600: #242835; --gray-700: #1a1d26; --success-100: #d1fae3; --success-200: #baf7d5; --success-300: #a4f4c6; --success-400: #8df2b8; --success-500: #5aec99; --success-600: #18ce66; --success-700: #129b4d; --danger-100: #ffe5e6; --danger-200: #ffcccc; --danger-300: #ffb3b3; --danger-400: #ff8080; --danger-500: #ff4f4f; --danger-600: #cc0000; --danger-700: #660000; --warning-100: #ffefcc; --warning-200: #ffe7b3; --warning-300: #ffd780; --warning-400: #ffc74c; --warning-500: #ffaf00; --warning-600: #cc8c00; --warning-700: #664600; /* FONTS */ --font-family-normal: var(--w3o-font-family, Inter, sans-serif); --font-size-1: 3rem; --font-size-2: 2.25rem; --font-size-3: 1.5rem; --font-size-4: 1.25rem; --font-size-5: 1rem; --font-size-6: .875rem; --font-size-7: .75rem; --font-line-height-1: 24px; --font-line-height-2: 20px; --font-line-height-3: 16px; --font-line-height-4: 12px; /* SPACING */ --spacing-1: 3rem; --spacing-2: 2rem; --spacing-3: 1.5rem; --spacing-4: 1rem; --spacing-5: 0.5rem; --spacing-6: 0.25rem; --spacing-7: 0.125rem; /* BORDER RADIUS */ --border-radius-1: 24px; --border-radius-2: 20px; --border-radius-3: 16px; --border-radius-4: 12px; --border-radius-5: 8px; /* SHADOWS */ --shadow-0: none; --shadow-1: 0px 4px 12px rgba(0, 0, 0, 0.1); --shadow-2: inset 0px -1px 0px rgba(0, 0, 0, 0.1); --shadow-3: 0px 4px 16px rgba(0, 0, 0, 0.2); /* MODAL POSITIONING */ --modal-z-index: 10; --modal-top: unset; --modal-right: unset; --modal-bottom: unset; --modal-left: unset; /* MODAL STYLES */ --modal-backdrop: rgba(0, 0, 0, 0.6); } </style> ` const connectModalContEl = configuration.containerElements.connectModal const containerElementQuery = connectModalContEl || state.get().accountCenter.containerElement || 'body' const containerElement = document.querySelector(containerElementQuery) if (!containerElement) { throw new Error( `Element with query ${containerElementQuery} does not exist.` ) } containerElement.appendChild(onboard) const app = new App({ target }) return app } export default init