From 752f10a21a748d5bec3435fb699f9118ba74d58b Mon Sep 17 00:00:00 2001 From: Adam Carpenter Date: Wed, 11 Oct 2023 09:38:48 -0600 Subject: [PATCH 1/4] Remove WC v1 support, cleanup docs and update WC deps --- .../[...26]walletconnect/+page.md | 114 +++--- packages/demo/package.json | 2 +- packages/walletconnect/README.md | 112 ++---- packages/walletconnect/package.json | 4 +- packages/walletconnect/src/index.ts | 81 +--- packages/walletconnect/src/v1.ts | 342 ----------------- packages/walletconnect/src/v2.ts | 357 ------------------ packages/walletconnect/src/validation.ts | 57 +-- 8 files changed, 123 insertions(+), 946 deletions(-) delete mode 100644 packages/walletconnect/src/v1.ts delete mode 100644 packages/walletconnect/src/v2.ts diff --git a/docs/src/routes/docs/[...4]wallets/[...26]walletconnect/+page.md b/docs/src/routes/docs/[...4]wallets/[...26]walletconnect/+page.md index 0da6284c0..e0d0effe9 100644 --- a/docs/src/routes/docs/[...4]wallets/[...26]walletconnect/+page.md +++ b/docs/src/routes/docs/[...4]wallets/[...26]walletconnect/+page.md @@ -4,11 +4,7 @@ title: WalletConnect # {$frontmatter.title} -Wallet module for connecting WalletConnect to web3-onboard, currently supporting both v1 and v2. - -:::admonition type=warning -_Version 1 of WalletConnect has been deprecated by the WC team and the WC bridge is not available. Upgrading to use WalletConnect v2 is recommended. Support will be completely removed from Web3-Onboard in the future_ -::: +Wallet module for connecting WalletConnect to web3-onboard. ## Install @@ -32,71 +28,44 @@ npm install @web3-onboard/walletconnect ```typescript type WalletConnectOptions = { + /** + * Project ID associated with [WalletConnect account](https://cloud.walletconnect.com) + */ + projectId: string + /** + * Defaults to `appMetadata.explore` that is supplied to the web3-onboard init + * Strongly recommended to provide atleast one URL as it is required by some wallets (i.e. MetaMask) + * To connect with walletconnect + */ + dappUrl?: string + /** + * List of Required Chain(s) ID for wallets to support in number format (integer or hex) + * Defaults to [1] - Ethereum + */ + requiredChains?: number[] | undefined + /** + * List of Optional Chain(s) ID for wallets to support in number format (integer or hex) + * Defaults to the chains provided within the web3-onboard init chain property + */ + optionalChains?: number[] | undefined + /** + * `undefined` by default, see https://docs.walletconnect.com/2.0/web/walletConnectModal/options + */ + qrModalOptions?: EthereumProviderOptions['qrModalOptions'] + /** + * Additional required methods to be added to the default list of ['eth_sendTransaction', 'personal_sign'] + * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/advanced/providers/ethereum#required-and-optional-methods + */ + additionalRequiredMethods?: string[] | undefined + /** + * Additional methods to be added to the default list of ['eth_sendTransaction', 'eth_signTransaction', 'personal_sign', 'eth_sign', 'eth_signTypedData', 'eth_signTypedData_v4'] + * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/web/walletConnectModal/options + */ + additionalOptionalMethods?: string[] | undefined /** * Optional function to handle WalletConnect URI when it becomes available */ handleUri?: (uri: string) => Promise -} & ( - | { - /** - * Project ID associated with [WalletConnect account](https://cloud.walletconnect.com) - */ - projectId: string - /** - * Defaults to `appMetadata.explore` that is supplied to the web3-onboard init - * Strongly recommended to provide atleast one URL as it is required by some wallets (i.e. MetaMask) - * To connect with walletconnect - */ - dappUrl?: string - /** - * Defaults to version: 2 - */ - version?: 2 - /** - * List of Required Chain(s) ID for wallets to support in number format (integer or hex) - * Defaults to [1] - Ethereum - */ - requiredChains?: number[] | undefined - /** - * List of Optional Chain(s) ID for wallets to support in number format (integer or hex) - * Defaults to the chains provided within the web3-onboard init chain property - */ - optionalChains?: number[] | undefined - /** - * `undefined` by default, see https://docs.walletconnect.com/2.0/web/walletConnectModal/options - */ - qrModalOptions?: EthereumProviderOptions['qrModalOptions'] - /** - * Additional required methods to be added to the default list of ['eth_sendTransaction', 'personal_sign'] - * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/advanced/providers/ethereum#required-and-optional-methods - */ - additionalRequiredMethods?: string[] | undefined - /** - * Additional methods to be added to the default list of ['eth_sendTransaction', 'eth_signTransaction', 'personal_sign', 'eth_sign', 'eth_signTypedData', 'eth_signTypedData_v4'] - * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/web/walletConnectModal/options - */ - additionalOptionalMethods?: string[] | undefined - } - | { - /** - * @deprecated - * Version 1 of WalletConnect has been deprecated by the WC team and the WC bridge is not available. - * To use version 1 a custom bridge url will need to be provided. - * Support will be completely remove from Web3-Onboard in the future - */ - version: 1 - /** - * Custom URL Bridge must be defined for V1 usage. - * WalletConnect no longer supports a v1 bridge. - * Upgrading to use WalletConnect v2 is recommended. - * A potential bridge can be found here: 'https://derelay.rabby.io' - */ - bridge: string - connectFirstChainId?: boolean - qrcodeModalOptions?: { - mobileLinks: string[] - } - } ) ``` @@ -106,7 +75,7 @@ type WalletConnectOptions = { import Onboard from '@web3-onboard/core' import walletConnectModule from '@web3-onboard/walletconnect' -const wcV2InitOptions = { +const wcInitOptions = { /** * Project ID associated with [WalletConnect account](https://cloud.walletconnect.com) */ @@ -114,7 +83,11 @@ const wcV2InitOptions = { /** * Chains required to be supported by all wallets connecting to your DApp */ - requiredChains: [1, 56], + requiredChains: [1], + /** + * Chains required to be supported by all wallets connecting to your DApp + */ + optionalChains: [42161, 8453, 10, 137, 56], /** * Defaults to `appMetadata.explore` that is supplied to the web3-onboard init * Strongly recommended to provide atleast one URL as it is required by some wallets (i.e. MetaMask) @@ -124,12 +97,9 @@ const wcV2InitOptions = { } // initialize the module with options -// If version isn't set it will default to V2 - V1 support will be completely removed shortly as it is deprecated -const walletConnect = walletConnectModule(wcV2InitOptions) +const walletConnect = walletConnectModule(wcInitOptions) // can also initialize with no options... -// Defaults to V2 - V1 support will be completely removed shortly as it is deprecated -// const walletConnect = walletConnectModule() const onboard = Onboard({ // ... other Onboard options diff --git a/packages/demo/package.json b/packages/demo/package.json index 0bf9428a9..c317eb667 100644 --- a/packages/demo/package.json +++ b/packages/demo/package.json @@ -56,7 +56,7 @@ "@web3-onboard/trust": "^2.0.3", "@web3-onboard/uauth": "^2.1.2", "@web3-onboard/venly": "^2.0.0", - "@web3-onboard/walletconnect": "^2.4.7", + "@web3-onboard/walletconnect": "^2.5.0-alpha.1", "@web3-onboard/web3auth": "^2.2.2", "@web3-onboard/xdefi": "^2.0.5", "@web3-onboard/zeal": "^2.0.3", diff --git a/packages/walletconnect/README.md b/packages/walletconnect/README.md index a6de6cdf6..285361d8a 100644 --- a/packages/walletconnect/README.md +++ b/packages/walletconnect/README.md @@ -6,79 +6,48 @@ `npm i @web3-onboard/core @web3-onboard/walletconnect` -## Version 1 of WalletConnect has been deprecated - -_Version 1 of WalletConnect has been deprecated by the WC team and the WC bridge is not available. If wanting to continue to use WalletConnect V1 a custom bridge URL is required. Support will be completely removed from Web3-Onboard in the future_ - ## Options ```typescript type WalletConnectOptions = { + /** + * Project ID associated with [WalletConnect account](https://cloud.walletconnect.com) + */ + projectId: string + /** + * Defaults to `appMetadata.explore` that is supplied to the web3-onboard init + * Strongly recommended to provide atleast one URL as it is required by some wallets (i.e. MetaMask) + * To connect with walletconnect + */ + dappUrl?: string + /** + * List of Required Chain(s) ID for wallets to support in number format (integer or hex) + * Defaults to [1] - Ethereum + */ + requiredChains?: number[] | undefined + /** + * List of Optional Chain(s) ID for wallets to support in number format (integer or hex) + * Defaults to the chains provided within the web3-onboard init chain property + */ + optionalChains?: number[] | undefined + /** + * `undefined` by default, see https://docs.walletconnect.com/2.0/web/walletConnectModal/options + */ + qrModalOptions?: EthereumProviderOptions['qrModalOptions'] + /** + * Additional required methods to be added to the default list of ['eth_sendTransaction', 'personal_sign'] + * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/advanced/providers/ethereum#required-and-optional-methods + */ + additionalRequiredMethods?: string[] | undefined + /** + * Additional methods to be added to the default list of ['eth_sendTransaction', 'eth_signTransaction', 'personal_sign', 'eth_sign', 'eth_signTypedData', 'eth_signTypedData_v4'] + * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/web/walletConnectModal/options + */ + additionalOptionalMethods?: string[] | undefined /** * Optional function to handle WalletConnect URI when it becomes available */ handleUri?: (uri: string) => Promise -} & ( - | { - /** - * Project ID associated with [WalletConnect account](https://cloud.walletconnect.com) - */ - projectId: string - /** - * Defaults to `appMetadata.explore` that is supplied to the web3-onboard init - * Strongly recommended to provide atleast one URL as it is required by some wallets (i.e. MetaMask) - * To connect with walletconnect - */ - dappUrl?: string - /** - * Defaults to version: 2 - */ - version?: 2 - /** - * List of Required Chain(s) ID for wallets to support in number format (integer or hex) - * Defaults to [1] - Ethereum - */ - requiredChains?: number[] | undefined - /** - * List of Optional Chain(s) ID for wallets to support in number format (integer or hex) - * Defaults to the chains provided within the web3-onboard init chain property - */ - optionalChains?: number[] | undefined - /** - * `undefined` by default, see https://docs.walletconnect.com/2.0/web/walletConnectModal/options - */ - qrModalOptions?: EthereumProviderOptions['qrModalOptions'] - /** - * Additional required methods to be added to the default list of ['eth_sendTransaction', 'personal_sign'] - * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/advanced/providers/ethereum#required-and-optional-methods - */ - additionalRequiredMethods?: string[] | undefined - /** - * Additional methods to be added to the default list of ['eth_sendTransaction', 'eth_signTransaction', 'personal_sign', 'eth_sign', 'eth_signTypedData', 'eth_signTypedData_v4'] - * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/web/walletConnectModal/options - */ - additionalOptionalMethods?: string[] | undefined - } - | { - /** - * @deprecated - * Version 1 of WalletConnect has been deprecated by the WC team and the WC bridge is not available. - * To use version 1 a custom bridge url will need to be provided. - * Support will be completely remove from Web3-Onboard in the future - */ - version: 1 - /** - * Custom URL Bridge must be defined for V1 usage. - * WalletConnect no longer supports a v1 bridge. - * Upgrading to use WalletConnect v2 is recommended. - * A potential bridge can be found here: 'https://derelay.rabby.io' - */ - bridge: string - connectFirstChainId?: boolean - qrcodeModalOptions?: { - mobileLinks: string[] - } - } ) ``` @@ -88,7 +57,7 @@ type WalletConnectOptions = { import Onboard from '@web3-onboard/core' import walletConnectModule from '@web3-onboard/walletconnect' -const wcV2InitOptions = { +const wcInitOptions = { /** * Project ID associated with [WalletConnect account](https://cloud.walletconnect.com) */ @@ -96,7 +65,11 @@ const wcV2InitOptions = { /** * Chains required to be supported by all wallets connecting to your DApp */ - requiredChains: [1, 56], + requiredChains: [1], + /** + * Chains required to be supported by all wallets connecting to your DApp + */ + optionalChains: [42161, 8453, 10, 137, 56], /** * Defaults to `appMetadata.explore` that is supplied to the web3-onboard init * Strongly recommended to provide atleast one URL as it is required by some wallets (i.e. MetaMask) @@ -106,12 +79,9 @@ const wcV2InitOptions = { } // initialize the module with options -// If version isn't set it will default to V2 - V1 support will be completely removed shortly as it is deprecated -const walletConnect = walletConnectModule(wcV2InitOptions) +const walletConnect = walletConnectModule(wcInitOptions) // can also initialize with no options... -// Defaults to V2 - V1 support will be completely removed shortly as it is deprecated -// const walletConnect = walletConnectModule() const onboard = Onboard({ // ... other Onboard options diff --git a/packages/walletconnect/package.json b/packages/walletconnect/package.json index 81fc678e3..30498c919 100644 --- a/packages/walletconnect/package.json +++ b/packages/walletconnect/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/walletconnect", - "version": "2.4.7", + "version": "2.5.0-alpha.1", "description": "WalletConnect SDK module for connecting 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", @@ -60,10 +60,8 @@ }, "dependencies": { "@ethersproject/providers": "5.5.0", - "@walletconnect/client": "^1.8.0", "@walletconnect/ethereum-provider": "^2.10.1", "@walletconnect/modal": "2.6.2", - "@walletconnect/qrcode-modal": "^1.8.0", "@web3-onboard/common": "^2.3.3", "joi": "17.9.1", "rxjs": "^7.5.2" diff --git a/packages/walletconnect/src/index.ts b/packages/walletconnect/src/index.ts index 5326fbf4f..c9bf3b88d 100644 --- a/packages/walletconnect/src/index.ts +++ b/packages/walletconnect/src/index.ts @@ -1,76 +1,8 @@ -import type { WalletInit } from '@web3-onboard/common' -import { EthereumProviderOptions } from '@walletconnect/ethereum-provider/dist/types/EthereumProvider' -import v1 from './v1.js' -import v2 from './v2.js' import { validateWCInitOptions } from './validation.js' +import type { WalletInit } from '@web3-onboard/common' +import walletConnect from './walletConnect.js' -export type WalletConnectOptions = { - /** - * Optional function to handle WalletConnect URI when it becomes available - */ - handleUri?: (uri: string) => Promise -} & ( - | { - /** - * @deprecated - * Version 1 of WalletConnect has been deprecated by the WC team and the WC bridge is not available. - * To use version 1 a custom bridge url will need to be provided. - * Support will be completely remove from Web3-Onboard in the future - */ - version: 1 - /** - * Custom URL Bridge must be defined for V1 usage. - * WalletConnect no longer supports a v1 bridge. - * Upgrading to use WalletConnect v2 is recommended. - * A potential bridge option can be found here: 'https://derelay.rabby.io' - */ - bridge: string - connectFirstChainId?: boolean - qrcodeModalOptions?: { - mobileLinks: string[] - } - } - | { - /** - * Project ID associated with [WalletConnect account](https://cloud.walletconnect.com) - */ - projectId: string - /** - * Defaults to `appMetadata.explore` that is supplied to the web3-onboard init - * Strongly recommended to provide atleast one URL as it is required by some wallets (i.e. MetaMask) - * To connect with walletconnect - */ - dappUrl?: string - /** - * Defaults to version: 2 - */ - version?: 2 - /** - * List of Required Chain(s) ID for wallets to support in number format (integer or hex) - * Defaults to [1] - Ethereum - */ - requiredChains?: number[] | undefined - /** - * List of Optional Chain(s) ID for wallets to support in number format (integer or hex) - * Defaults to the chains provided within the web3-onboard init chain property - */ - optionalChains?: number[] | undefined - /** - * `undefined` by default, see https://docs.walletconnect.com/2.0/web/walletConnectModal/options - */ - qrModalOptions?: EthereumProviderOptions['qrModalOptions'] - /** - * Additional required methods to be added to the default list of ['eth_sendTransaction', 'personal_sign'] - * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/advanced/providers/ethereum#required-and-optional-methods - */ - additionalRequiredMethods?: string[] | undefined - /** - * Additional methods to be added to the default list of ['eth_sendTransaction', 'eth_signTransaction', 'personal_sign', 'eth_sign', 'eth_signTypedData', 'eth_signTypedData_v4'] - * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/web/walletConnectModal/options - */ - additionalOptionalMethods?: string[] | undefined - } -) +import type { WalletConnectOptions } from 'types.js' export const isHexString = (value: string | number) => { if (typeof value !== 'string' || !value.match(/^0x[0-9A-Fa-f]*$/)) { @@ -80,7 +12,7 @@ export const isHexString = (value: string | number) => { return true } -function walletConnect(options: WalletConnectOptions): WalletInit { +function initWalletConnect(options: WalletConnectOptions): WalletInit { if (!options) { throw new Error( `WalletConnect requires an initialization object to be passed - see the official docs for an example: https://onboard.blocknative.com/docs/wallets/walletconnect` @@ -93,8 +25,7 @@ function walletConnect(options: WalletConnectOptions): WalletInit { throw error } } - options.version = options.version || 2 - return options.version === 2 ? v2(options) : v1(options) + return walletConnect(options) } -export default walletConnect +export default initWalletConnect diff --git a/packages/walletconnect/src/v1.ts b/packages/walletconnect/src/v1.ts deleted file mode 100644 index 580a5ddb9..000000000 --- a/packages/walletconnect/src/v1.ts +++ /dev/null @@ -1,342 +0,0 @@ -import type { StaticJsonRpcProvider as StaticJsonRpcProviderType } from '@ethersproject/providers' -import type { - Chain, - ProviderAccounts, - WalletInit, - EIP1193Provider, - ChainId, - AccountAddress -} from '@web3-onboard/common' -import type { WalletConnectOptions } from './index.js' -import { isHexString } from './index.js' - -function walletConnect(options: WalletConnectOptions): WalletInit { - if (options.version !== 1) - throw `WalletConnect version must be set to 1 to initialize - note version 1 has been deprecated by the WalletConnect team` - - const { bridge, qrcodeModalOptions, connectFirstChainId, handleUri } = - options || {} - - console.warn( - `Wallet Connect version 1 support has been deprecated by the WalletConnect team. Please consider using version 2. See docs for more details.` - ) - - if (!bridge) { - throw `WalletConnect version 1 requires a bridge to be passed in. The WalletConnect team has remove support for the bridge. Please upgrade to version 2 of WalletConnect or pass in a custom bridge URL.` - } - - return () => { - return { - label: 'WalletConnect', - getIcon: async () => (await import('./icon.js')).default, - getInterface: async ({ chains, EventEmitter }) => { - const { StaticJsonRpcProvider } = await import( - '@ethersproject/providers' - ) - - const { ProviderRpcError, ProviderRpcErrorCode } = await import( - '@web3-onboard/common' - ) - - const { default: WalletConnect } = await import('@walletconnect/client') - - // This is a cjs module and therefor depending on build tooling - // sometimes it will be nested in the { default } object and - // other times it will be the actual import - // @ts-ignore - It thinks it is missing properties since it expect it to be nested under default - let QRCodeModal: typeof import('@walletconnect/qrcode-modal').default = - await import('@walletconnect/qrcode-modal') - - // @ts-ignore - TS thinks that there is no default property on the `QRCodeModal` but sometimes there is - QRCodeModal = QRCodeModal.default || QRCodeModal - - const { Subject, fromEvent } = await import('rxjs') - const { takeUntil, take } = await import('rxjs/operators') - - const connector = new WalletConnect({ - bridge - }) - - if (handleUri) { - try { - // @ts-ignore - await handleUri(connector.uri || '') - } catch (error) { - throw `An error occurred when handling the URI. Error: ${error}` - } - } - - const emitter = new EventEmitter() - - class EthProvider { - public request: EIP1193Provider['request'] - public connector: InstanceType - public chains: Chain[] - public disconnect: EIP1193Provider['disconnect'] - // @ts-ignore - public emit: typeof EventEmitter['emit'] - // @ts-ignore - public on: typeof EventEmitter['on'] - // @ts-ignore - public removeListener: typeof EventEmitter['removeListener'] - - private disconnected$: InstanceType - private providers: Record - - constructor({ - connector, - chains - }: { - connector: InstanceType - chains: Chain[] - }) { - this.emit = emitter.emit.bind(emitter) - this.on = emitter.on.bind(emitter) - this.removeListener = emitter.removeListener.bind(emitter) - - this.connector = connector - this.chains = chains - this.disconnected$ = new Subject() - this.providers = {} - let activeChain: ChainId - - // listen for session updates - fromEvent(this.connector, 'session_update', (error, payload) => { - if (error) { - throw error - } - return payload - }) - .pipe(takeUntil(this.disconnected$)) - .subscribe({ - next: ({ params }) => { - const [{ accounts, chainId }] = params - const lowerCaseAccounts = accounts.map( - (accountAddress: AccountAddress) => - accountAddress.toLowerCase() - ) - this.emit('accountsChanged', lowerCaseAccounts) - const hexChainId = isHexString(chainId) - ? chainId - : `0x${chainId.toString(16)}` - if (!activeChain || activeChain !== hexChainId) { - this.emit('chainChanged', hexChainId) - activeChain = hexChainId - } - }, - error: console.warn - }) - - // listen for disconnect event - fromEvent(this.connector, 'disconnect', (error, payload) => { - if (error) { - throw error - } - - return payload - }) - .pipe(takeUntil(this.disconnected$)) - .subscribe({ - next: () => { - this.emit('accountsChanged', []) - this.disconnected$.next(true) - typeof localStorage !== 'undefined' && - localStorage.removeItem('walletconnect') - }, - error: console.warn - }) - // @ts-ignore - this.disconnect = () => this.connector.killSession() - - this.request = async ({ method, params }) => { - if (method === 'eth_chainId') { - // @ts-ignore - return isHexString(this.connector.chainId) - ? // @ts-ignore - this.connector.chainId - : // @ts-ignore - `0x${this.connector.chainId.toString(16)}` - } - - if (method === 'eth_requestAccounts') { - return new Promise((resolve, reject) => { - // Subscribe to connection events - fromEvent(this.connector, 'connect', (error, payload) => { - if (error) { - throw error - } - - return payload - }) - .pipe(take(1)) - .subscribe({ - next: ({ params }) => { - const [{ accounts, chainId }] = params - const lowerCaseAccounts = accounts.map( - (accountAddress: AccountAddress) => - accountAddress.toLowerCase() - ) - this.emit('accountsChanged', lowerCaseAccounts) - const hexChainId = isHexString(chainId) - ? chainId - : `0x${chainId.toString(16)}` - if (!activeChain) activeChain = hexChainId - this.emit('chainChanged', hexChainId) - QRCodeModal.close() - resolve(lowerCaseAccounts) - }, - error: reject - }) - - // Check if connection is already established - // @ts-ignore - if (!this.connector.connected) { - // create new session - this.connector - // @ts-ignore - .createSession( - connectFirstChainId - ? { chainId: parseInt(chains[0].id, 16) } - : undefined - ) - .then(() => { - QRCodeModal.open( - // @ts-ignore - this.connector.uri, - () => - reject( - new ProviderRpcError({ - code: 4001, - message: 'User rejected the request.' - }) - ), - qrcodeModalOptions - ) - }) - } else { - // @ts-ignore - const { accounts, chainId } = this.connector.session - const hexChainId = isHexString(chainId) - ? chainId - : `0x${chainId.toString(16)}` - - this.emit('chainChanged', hexChainId) - if (!activeChain) activeChain = hexChainId as ChainId - const lowerCaseAccounts = accounts.map( - (accountAddress: AccountAddress) => - accountAddress.toLowerCase() - ) - return resolve(lowerCaseAccounts) - } - }) - } - - if (method === 'eth_selectAccounts') { - throw new ProviderRpcError({ - code: ProviderRpcErrorCode.UNSUPPORTED_METHOD, - message: `The Provider does not support the requested method: ${method}` - }) - } - - if (method == 'wallet_switchEthereumChain') { - if (!params) { - throw new ProviderRpcError({ - code: ProviderRpcErrorCode.INVALID_PARAMS, - message: `The Provider requires a chainId to be passed in as an argument` - }) - } - const chainIdObj = params[0] as { chainId?: number } - if ( - !chainIdObj.hasOwnProperty('chainId') || - typeof chainIdObj['chainId'] === 'undefined' - ) { - throw new ProviderRpcError({ - code: ProviderRpcErrorCode.INVALID_PARAMS, - message: `The Provider requires a chainId to be passed in as an argument` - }) - } - // @ts-ignore - return this.connector.sendCustomRequest({ - method: 'wallet_switchEthereumChain', - params: [ - { - chainId: chainIdObj.chainId - } - ] - }) - } - - // @ts-ignore - if (method === 'eth_sendTransaction') { - // @ts-ignore - return this.connector.sendTransaction(params[0]) - } - - // @ts-ignore - if (method === 'eth_signTransaction') { - // @ts-ignore - return this.connector.signTransaction(params[0]) - } - - // @ts-ignore - if (method === 'personal_sign') { - // @ts-ignore - return this.connector.signPersonalMessage(params) - } - - // @ts-ignore - if (method === 'eth_sign') { - // @ts-ignore - return this.connector.signMessage(params) - } - - // @ts-ignore - if (method.includes('eth_signTypedData')) { - // @ts-ignore - return this.connector.signTypedData(params) - } - - if (method === 'eth_accounts') { - // @ts-ignore - return this.connector.sendCustomRequest({ - id: 1337, - jsonrpc: '2.0', - method, - params - }) - } - - const chainId = await this.request({ method: 'eth_chainId' }) - - if (!this.providers[chainId]) { - const currentChain = chains.find(({ id }) => id === chainId) - - if (!currentChain) { - throw new ProviderRpcError({ - code: ProviderRpcErrorCode.CHAIN_NOT_ADDED, - message: `The Provider does not have a rpcUrl to make a request for the requested method: ${method}` - }) - } - - this.providers[chainId] = new StaticJsonRpcProvider( - currentChain.rpcUrl - ) - } - return this.providers[chainId].send( - method, - // @ts-ignore - params - ) - } - } - } - - return { - provider: new EthProvider({ chains, connector }) - } - } - } - } -} - -export default walletConnect diff --git a/packages/walletconnect/src/v2.ts b/packages/walletconnect/src/v2.ts deleted file mode 100644 index fe7f5c499..000000000 --- a/packages/walletconnect/src/v2.ts +++ /dev/null @@ -1,357 +0,0 @@ -import { REQUIRED_METHODS } from '@walletconnect/ethereum-provider' -import type { EthereumProviderOptions } from '@walletconnect/ethereum-provider/dist/types/EthereumProvider' -import type { EthereumProvider } from '@walletconnect/ethereum-provider' -import type { CoreTypes } from '@walletconnect/types' - -import type { - Chain, - ProviderAccounts, - WalletInit, - EIP1193Provider -} from '@web3-onboard/common' -import type { WalletConnectOptions } from './index.js' -import type { JQueryStyleEventEmitter } from 'rxjs/internal/observable/fromEvent' -import { isHexString } from './index.js' - -// methods that require user interaction -const methods = [ - 'eth_sendTransaction', - 'eth_signTransaction', - 'personal_sign', - 'eth_sign', - 'eth_signTypedData', - 'eth_signTypedData_v4', - 'wallet_addEthereumChain', - 'wallet_switchEthereumChain' -] - -function walletConnect(options: WalletConnectOptions): WalletInit { - if (options.version !== 2 || !options.projectId) { - throw new Error( - 'WalletConnect requires a projectId. Please visit https://cloud.walletconnect.com to get one.' - ) - } - const { - projectId, - handleUri, - requiredChains, - optionalChains, - qrModalOptions, - additionalRequiredMethods, - additionalOptionalMethods, - dappUrl - } = options - - let instance: unknown - - return () => { - return { - label: 'WalletConnect', - getIcon: async () => (await import('./icon.js')).default, - getInterface: async ({ chains, EventEmitter, appMetadata }) => { - const { ProviderRpcError, ProviderRpcErrorCode } = await import( - '@web3-onboard/common' - ) - - const { default: EthereumProvider } = await import( - '@walletconnect/ethereum-provider' - ) - - const { Subject, fromEvent } = await import('rxjs') - const { takeUntil, take } = await import('rxjs/operators') - - const getMetaData = (): CoreTypes.Metadata | undefined => { - if (!appMetadata) return undefined - const url = dappUrl || appMetadata.explore || '' - - !url && - !url.length && - console.warn( - `It is strongly recommended to supply a dappUrl as it is required by some wallets (i.e. MetaMask) to allow connection.` - ) - const wcMetaData: CoreTypes.Metadata = { - name: appMetadata.name, - description: appMetadata.description || '', - url, - icons: [] - } - - if (appMetadata.icon !== undefined && appMetadata.icon.length) { - wcMetaData.icons = [appMetadata.icon] - } - if (appMetadata.logo !== undefined && appMetadata.logo.length) { - wcMetaData.icons = wcMetaData.icons.length - ? [...wcMetaData.icons, appMetadata.logo] - : [appMetadata.logo] - } - - return wcMetaData - } - - // default to mainnet - const requiredChainsParsed: number[] = - Array.isArray(requiredChains) && - requiredChains.length && - requiredChains.every(num => !isNaN(num)) - ? // @ts-ignore - // Required as WC package does not support hex numbers - requiredChains.map(chainID => parseInt(chainID)) - : [] - - // Defaults to the chains provided within the web3-onboard init chain property - const optionalChainsParsed: number[] = - Array.isArray(optionalChains) && - optionalChains.length && - optionalChains.every(num => !isNaN(num)) - ? // @ts-ignore - // Required as WC package does not support hex numbers - optionalChains.map(chainID => parseInt(chainID)) - : chains.map(({ id }) => parseInt(id, 16)) - - const requiredMethodsSet = new Set( - additionalRequiredMethods && Array.isArray(additionalRequiredMethods) - ? [...additionalRequiredMethods, ...REQUIRED_METHODS] - : REQUIRED_METHODS - ) - const requiredMethods = Array.from(requiredMethodsSet) - - const optionalMethods = - additionalOptionalMethods && Array.isArray(additionalOptionalMethods) - ? [...additionalOptionalMethods, ...methods] - : methods - - const connector = await EthereumProvider.init({ - projectId, - chains: requiredChainsParsed, // default to mainnet - methods: requiredMethods, - optionalChains: optionalChainsParsed, - optionalMethods, - showQrModal: true, - rpcMap: chains - .map(({ id, rpcUrl }) => ({ id, rpcUrl })) - .reduce((rpcMap: Record, { id, rpcUrl }) => { - rpcMap[parseInt(id, 16)] = rpcUrl || '' - return rpcMap - }, {}), - metadata: getMetaData(), - qrModalOptions: qrModalOptions - } as EthereumProviderOptions) - - const emitter = new EventEmitter() - class EthProvider { - public request: EIP1193Provider['request'] - public connector: InstanceType - public chains: Chain[] - public disconnect: EIP1193Provider['disconnect'] - // @ts-ignore - public emit: typeof EventEmitter['emit'] - // @ts-ignore - public on: typeof EventEmitter['on'] - // @ts-ignore - public removeListener: typeof EventEmitter['removeListener'] - - private disconnected$: InstanceType - - constructor({ - connector, - chains - }: { - connector: InstanceType - chains: Chain[] - }) { - this.emit = emitter.emit.bind(emitter) - this.on = emitter.on.bind(emitter) - this.removeListener = emitter.removeListener.bind(emitter) - - this.connector = connector - this.chains = chains - this.disconnected$ = new Subject() - - // listen for accountsChanged - fromEvent(this.connector, 'accountsChanged', payload => payload) - .pipe(takeUntil(this.disconnected$)) - .subscribe({ - next: payload => { - const accounts = Array.isArray(payload) ? payload : [payload] - this.emit('accountsChanged', accounts) - }, - error: console.warn - }) - - // listen for chainChanged - fromEvent( - this.connector as JQueryStyleEventEmitter, - 'chainChanged', - (payload: number) => payload - ) - .pipe(takeUntil(this.disconnected$)) - .subscribe({ - next: chainId => { - const hexChainId = isHexString(chainId) - ? chainId - : `0x${chainId.toString(16)}` - this.emit('chainChanged', hexChainId) - }, - error: console.warn - }) - - // listen for disconnect event - fromEvent( - this.connector as JQueryStyleEventEmitter, - 'session_delete', - (payload: string) => payload - ) - .pipe(takeUntil(this.disconnected$)) - .subscribe({ - next: () => { - this.emit('accountsChanged', []) - this.disconnected$.next(true) - typeof localStorage !== 'undefined' && - localStorage.removeItem('walletconnect') - }, - error: console.warn - }) - - this.disconnect = () => { - if (this.connector.session) { - this.connector.disconnect() - instance = null - } - } - - if (options && handleUri) { - // listen for uri event - fromEvent( - this.connector as JQueryStyleEventEmitter, - 'display_uri', - (payload: string) => payload - ) - .pipe(takeUntil(this.disconnected$)) - .subscribe(async uri => { - try { - handleUri && (await handleUri(uri)) - } catch (error) { - throw `An error occurred when handling the URI. Error: ${error}` - } - }) - } - - const checkForSession = () => { - const session = this.connector.session - instance = session - if (session) { - this.emit('accountsChanged', this.connector.accounts) - this.emit('chainChanged', this.connector.chainId) - } - } - checkForSession() - - this.request = async ({ method, params }) => { - if (method === 'eth_chainId') { - return isHexString(this.connector.chainId) - ? this.connector.chainId - : `0x${this.connector.chainId.toString(16)}` - } - - if (method === 'eth_requestAccounts') { - return new Promise( - async (resolve, reject) => { - // Subscribe to connection events - fromEvent( - this.connector as JQueryStyleEventEmitter< - any, - { chainId: number } - >, - 'connect', - (payload: { chainId: number | string }) => payload - ) - .pipe(take(1)) - .subscribe({ - next: ({ chainId }) => { - this.emit('accountsChanged', this.connector.accounts) - const hexChainId = isHexString(chainId) - ? chainId - : `0x${chainId.toString(16)}` - this.emit('chainChanged', hexChainId) - resolve(this.connector.accounts) - }, - error: reject - }) - - // Check if connection is already established - if (!this.connector.session) { - // create new session - await this.connector.connect().catch(err => { - console.error('err creating new session: ', err) - reject( - new ProviderRpcError({ - code: 4001, - message: 'User rejected the request.' - }) - ) - }) - } else { - // update ethereum provider to load accounts & chainId - const accounts = this.connector.accounts - const chainId = this.connector.chainId - instance = this.connector.session - const hexChainId = `0x${chainId.toString(16)}` - this.emit('chainChanged', hexChainId) - return resolve(accounts) - } - } - ) - } - - if (method === 'eth_selectAccounts') { - throw new ProviderRpcError({ - code: ProviderRpcErrorCode.UNSUPPORTED_METHOD, - message: `The Provider does not support the requested method: ${method}` - }) - } - - if (method == 'wallet_switchEthereumChain') { - if (!params) { - throw new ProviderRpcError({ - code: ProviderRpcErrorCode.INVALID_PARAMS, - message: `The Provider requires a chainId to be passed in as an argument` - }) - } - const chainIdObj = params[0] as { chainId?: number } - if ( - !chainIdObj.hasOwnProperty('chainId') || - typeof chainIdObj['chainId'] === 'undefined' - ) { - throw new ProviderRpcError({ - code: ProviderRpcErrorCode.INVALID_PARAMS, - message: `The Provider requires a chainId to be passed in as an argument` - }) - } - return this.connector.request({ - method: 'wallet_switchEthereumChain', - params: [ - { - chainId: chainIdObj.chainId - } - ] - }) - } - - return this.connector.request>({ - method, - params - }) - } - } - } - - return { - provider: new EthProvider({ chains, connector }), - instance - } - } - } - } -} - -export default walletConnect diff --git a/packages/walletconnect/src/validation.ts b/packages/walletconnect/src/validation.ts index 4094facc3..349cf9e70 100644 --- a/packages/walletconnect/src/validation.ts +++ b/packages/walletconnect/src/validation.ts @@ -1,32 +1,39 @@ import Joi from 'joi' -import type { WalletConnectOptions } from './index.js' +import type { WalletConnectOptions } from './types.js' const wcOptions = Joi.object({ handleUri: Joi.func().optional(), - version: Joi.number().valid(1, 2).optional(), - bridge: Joi.string() - .when('version', { - is: 1, - then: Joi.required(), - otherwise: Joi.forbidden() - }) - .messages({ - 'any.required': `A bridge URL is a required when version is 1 of WalletConnect as the WC team has removed support for their default bridge.` - }), - connectFirstChainId: Joi.boolean().optional(), - qrcodeModalOptions: Joi.object({ - mobileLinks: Joi.array().items(Joi.string()).optional() - }).optional(), - projectId: Joi.string() - .when('version', { - is: 2, - then: Joi.required(), - otherwise: Joi.optional() - }) - .messages({ - 'any.required': `WalletConnect version 2 requires a projectId. Please visit https://cloud.walletconnect.com to get one.` - }), - dappUrl: Joi.string().optional(), + version: Joi.number() + .optional() + .custom((value, helpers) => { + if (value === 1) { + return helpers.message({ + message: `Version 1 of WalletConnect is no longer supported. Please use version 2.`, + type: 'any.custom' + }) + } else if (value !== 2 && value !== undefined) { + return helpers.message({ + message: 'Invalid version number. Please use version 2.', + type: 'any.custom' + }) + } + return value // return the value unchanged if it's valid or not provided + }, 'Custom version validation'), + projectId: Joi.string().messages({ + 'any.required': `WalletConnect version 2 requires a projectId. Please visit https://cloud.walletconnect.com to get one.` + }), + dappUrl: Joi.string() + .optional() + .custom((value, helpers) => { + if (!value) { + return helpers.message({ + message: + 'It is strongly recommended to supply a dappUrl as it is required by some wallets (i.e. MetaMask) to allow connection.', + type: 'any.custom' + }) + } + return value // return the value unchanged if it's provided + }, 'Custom dappUrl validation'), requiredChains: Joi.array().items(Joi.number()).optional(), optionalChains: Joi.array().items(Joi.number()).optional(), qrModalOptions: Joi.object().optional(), From 08c14b7bd73f2a45ec51ecf65926eeba84259a57 Mon Sep 17 00:00:00 2001 From: Adam Carpenter Date: Wed, 11 Oct 2023 09:39:17 -0600 Subject: [PATCH 2/4] Move types to typesfile, rename v2 file to WC as v1 support is removed --- packages/walletconnect/src/types.ts | 42 +++ packages/walletconnect/src/walletConnect.ts | 362 ++++++++++++++++++++ 2 files changed, 404 insertions(+) create mode 100644 packages/walletconnect/src/types.ts create mode 100644 packages/walletconnect/src/walletConnect.ts diff --git a/packages/walletconnect/src/types.ts b/packages/walletconnect/src/types.ts new file mode 100644 index 000000000..3df47bea8 --- /dev/null +++ b/packages/walletconnect/src/types.ts @@ -0,0 +1,42 @@ +import { EthereumProviderOptions } from '@walletconnect/ethereum-provider/dist/types/EthereumProvider' + +export type WalletConnectOptions = { + /** + * Optional function to handle WalletConnect URI when it becomes available + */ + handleUri?: (uri: string) => Promise + /** + * Project ID associated with [WalletConnect account](https://cloud.walletconnect.com) + */ + projectId: string + /** + * Defaults to `appMetadata.explore` that is supplied to the web3-onboard init + * Strongly recommended to provide atleast one URL as it is required by some wallets (i.e. MetaMask) + * To connect with walletconnect + */ + dappUrl?: string + /** + * List of Required Chain(s) ID for wallets to support in number format (integer or hex) + * Defaults to [1] - Ethereum + */ + requiredChains?: number[] | undefined + /** + * List of Optional Chain(s) ID for wallets to support in number format (integer or hex) + * Defaults to the chains provided within the web3-onboard init chain property + */ + optionalChains?: number[] | undefined + /** + * `undefined` by default, see https://docs.walletconnect.com/2.0/web/walletConnectModal/options + */ + qrModalOptions?: EthereumProviderOptions['qrModalOptions'] + /** + * Additional required methods to be added to the default list of ['eth_sendTransaction', 'personal_sign'] + * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/advanced/providers/ethereum#required-and-optional-methods + */ + additionalRequiredMethods?: string[] | undefined + /** + * Additional methods to be added to the default list of ['eth_sendTransaction', 'eth_signTransaction', 'personal_sign', 'eth_sign', 'eth_signTypedData', 'eth_signTypedData_v4'] + * Passed methods to be included along with the defaults methods - see https://docs.walletconnect.com/2.0/web/walletConnectModal/options + */ + additionalOptionalMethods?: string[] | undefined +} diff --git a/packages/walletconnect/src/walletConnect.ts b/packages/walletconnect/src/walletConnect.ts new file mode 100644 index 000000000..9a634fd93 --- /dev/null +++ b/packages/walletconnect/src/walletConnect.ts @@ -0,0 +1,362 @@ +import { REQUIRED_METHODS } from '@walletconnect/ethereum-provider' +import { isHexString } from './index.js' + +import type { EthereumProviderOptions } from '@walletconnect/ethereum-provider/dist/types/EthereumProvider' +import type { JQueryStyleEventEmitter } from 'rxjs/internal/observable/fromEvent' +import type { EthereumProvider } from '@walletconnect/ethereum-provider' +import type { WalletConnectOptions } from './types.js' +import type { CoreTypes } from '@walletconnect/types' +import type { + Chain, + ProviderAccounts, + WalletInit, + EIP1193Provider +} from '@web3-onboard/common' + +// methods that require user interaction +const methods = [ + 'eth_sendTransaction', + 'eth_signTransaction', + 'personal_sign', + 'eth_sign', + 'eth_signTypedData', + 'eth_signTypedData_v4', + 'wallet_addEthereumChain', + 'wallet_switchEthereumChain' +] + +function walletConnect(options: WalletConnectOptions): WalletInit { + if (!options.projectId) { + throw new Error( + 'WalletConnect requires a projectId. Please visit https://cloud.walletconnect.com to get one.' + ) + } + if (!options.dappUrl) { + console.warn( + `It is strongly recommended to supply a dappUrl to the WalletConnect init object as it is required by some wallets (i.e. MetaMask) to allow connection.` + ) + } + const { + projectId, + handleUri, + requiredChains, + optionalChains, + qrModalOptions, + additionalRequiredMethods, + additionalOptionalMethods, + dappUrl + } = options + + let instance: unknown + + return () => { + return { + label: 'WalletConnect', + getIcon: async () => (await import('./icon.js')).default, + getInterface: async ({ chains, EventEmitter, appMetadata }) => { + const { ProviderRpcError, ProviderRpcErrorCode } = await import( + '@web3-onboard/common' + ) + + const { default: EthereumProvider } = await import( + '@walletconnect/ethereum-provider' + ) + + const { Subject, fromEvent } = await import('rxjs') + const { takeUntil, take } = await import('rxjs/operators') + + const getMetaData = (): CoreTypes.Metadata | undefined => { + if (!appMetadata) return undefined + const url = dappUrl || appMetadata.explore || '' + + !url && + !url.length && + console.warn( + `It is strongly recommended to supply a dappUrl as it is required by some wallets (i.e. MetaMask) to allow connection.` + ) + const wcMetaData: CoreTypes.Metadata = { + name: appMetadata.name, + description: appMetadata.description || '', + url, + icons: [] + } + + if (appMetadata.icon !== undefined && appMetadata.icon.length) { + wcMetaData.icons = [appMetadata.icon] + } + if (appMetadata.logo !== undefined && appMetadata.logo.length) { + wcMetaData.icons = wcMetaData.icons.length + ? [...wcMetaData.icons, appMetadata.logo] + : [appMetadata.logo] + } + + return wcMetaData + } + + // default to mainnet + const requiredChainsParsed: number[] = + Array.isArray(requiredChains) && + requiredChains.length && + requiredChains.every(num => !isNaN(num)) + ? // @ts-ignore + // Required as WC package does not support hex numbers + requiredChains.map(chainID => parseInt(chainID)) + : [] + + // Defaults to the chains provided within the web3-onboard init chain property + const optionalChainsParsed: number[] = + Array.isArray(optionalChains) && + optionalChains.length && + optionalChains.every(num => !isNaN(num)) + ? // @ts-ignore + // Required as WC package does not support hex numbers + optionalChains.map(chainID => parseInt(chainID)) + : chains.map(({ id }) => parseInt(id, 16)) + + const requiredMethodsSet = new Set( + additionalRequiredMethods && Array.isArray(additionalRequiredMethods) + ? [...additionalRequiredMethods, ...REQUIRED_METHODS] + : REQUIRED_METHODS + ) + const requiredMethods = Array.from(requiredMethodsSet) + + const optionalMethods = + additionalOptionalMethods && Array.isArray(additionalOptionalMethods) + ? [...additionalOptionalMethods, ...methods] + : methods + + const connector = await EthereumProvider.init({ + projectId, + chains: requiredChainsParsed, // default to mainnet + methods: requiredMethods, + optionalChains: optionalChainsParsed, + optionalMethods, + showQrModal: true, + rpcMap: chains + .map(({ id, rpcUrl }) => ({ id, rpcUrl })) + .reduce((rpcMap: Record, { id, rpcUrl }) => { + rpcMap[parseInt(id, 16)] = rpcUrl || '' + return rpcMap + }, {}), + metadata: getMetaData(), + qrModalOptions: qrModalOptions + } as EthereumProviderOptions) + + const emitter = new EventEmitter() + class EthProvider { + public request: EIP1193Provider['request'] + public connector: InstanceType + public chains: Chain[] + public disconnect: EIP1193Provider['disconnect'] + // @ts-ignore + public emit: typeof EventEmitter['emit'] + // @ts-ignore + public on: typeof EventEmitter['on'] + // @ts-ignore + public removeListener: typeof EventEmitter['removeListener'] + + private disconnected$: InstanceType + + constructor({ + connector, + chains + }: { + connector: InstanceType + chains: Chain[] + }) { + this.emit = emitter.emit.bind(emitter) + this.on = emitter.on.bind(emitter) + this.removeListener = emitter.removeListener.bind(emitter) + + this.connector = connector + this.chains = chains + this.disconnected$ = new Subject() + + // listen for accountsChanged + fromEvent(this.connector, 'accountsChanged', payload => payload) + .pipe(takeUntil(this.disconnected$)) + .subscribe({ + next: payload => { + const accounts = Array.isArray(payload) ? payload : [payload] + this.emit('accountsChanged', accounts) + }, + error: console.warn + }) + + // listen for chainChanged + fromEvent( + this.connector as JQueryStyleEventEmitter, + 'chainChanged', + (payload: number) => payload + ) + .pipe(takeUntil(this.disconnected$)) + .subscribe({ + next: chainId => { + const hexChainId = isHexString(chainId) + ? chainId + : `0x${chainId.toString(16)}` + this.emit('chainChanged', hexChainId) + }, + error: console.warn + }) + + // listen for disconnect event + fromEvent( + this.connector as JQueryStyleEventEmitter, + 'session_delete', + (payload: string) => payload + ) + .pipe(takeUntil(this.disconnected$)) + .subscribe({ + next: () => { + this.emit('accountsChanged', []) + this.disconnected$.next(true) + typeof localStorage !== 'undefined' && + localStorage.removeItem('walletconnect') + }, + error: console.warn + }) + + this.disconnect = () => { + if (this.connector.session) { + this.connector.disconnect() + instance = null + } + } + + if (options && handleUri) { + // listen for uri event + fromEvent( + this.connector as JQueryStyleEventEmitter, + 'display_uri', + (payload: string) => payload + ) + .pipe(takeUntil(this.disconnected$)) + .subscribe(async uri => { + try { + handleUri && (await handleUri(uri)) + } catch (error) { + throw `An error occurred when handling the URI. Error: ${error}` + } + }) + } + + const checkForSession = () => { + const session = this.connector.session + instance = session + if (session) { + this.emit('accountsChanged', this.connector.accounts) + this.emit('chainChanged', this.connector.chainId) + } + } + checkForSession() + + this.request = async ({ method, params }) => { + if (method === 'eth_chainId') { + return isHexString(this.connector.chainId) + ? this.connector.chainId + : `0x${this.connector.chainId.toString(16)}` + } + + if (method === 'eth_requestAccounts') { + return new Promise( + async (resolve, reject) => { + // Subscribe to connection events + fromEvent( + this.connector as JQueryStyleEventEmitter< + any, + { chainId: number } + >, + 'connect', + (payload: { chainId: number | string }) => payload + ) + .pipe(take(1)) + .subscribe({ + next: ({ chainId }) => { + this.emit('accountsChanged', this.connector.accounts) + const hexChainId = isHexString(chainId) + ? chainId + : `0x${chainId.toString(16)}` + this.emit('chainChanged', hexChainId) + resolve(this.connector.accounts) + }, + error: reject + }) + + // Check if connection is already established + if (!this.connector.session) { + // create new session + await this.connector.connect().catch(err => { + console.error('err creating new session: ', err) + reject( + new ProviderRpcError({ + code: 4001, + message: 'User rejected the request.' + }) + ) + }) + } else { + // update ethereum provider to load accounts & chainId + const accounts = this.connector.accounts + const chainId = this.connector.chainId + instance = this.connector.session + const hexChainId = `0x${chainId.toString(16)}` + this.emit('chainChanged', hexChainId) + return resolve(accounts) + } + } + ) + } + + if (method === 'eth_selectAccounts') { + throw new ProviderRpcError({ + code: ProviderRpcErrorCode.UNSUPPORTED_METHOD, + message: `The Provider does not support the requested method: ${method}` + }) + } + + if (method == 'wallet_switchEthereumChain') { + if (!params) { + throw new ProviderRpcError({ + code: ProviderRpcErrorCode.INVALID_PARAMS, + message: `The Provider requires a chainId to be passed in as an argument` + }) + } + const chainIdObj = params[0] as { chainId?: number } + if ( + !chainIdObj.hasOwnProperty('chainId') || + typeof chainIdObj['chainId'] === 'undefined' + ) { + throw new ProviderRpcError({ + code: ProviderRpcErrorCode.INVALID_PARAMS, + message: `The Provider requires a chainId to be passed in as an argument` + }) + } + return this.connector.request({ + method: 'wallet_switchEthereumChain', + params: [ + { + chainId: chainIdObj.chainId + } + ] + }) + } + + return this.connector.request>({ + method, + params + }) + } + } + } + + return { + provider: new EthProvider({ chains, connector }), + instance + } + } + } + } +} + +export default walletConnect From 4146e9c086f76cc1497d5761b64ae462541040b8 Mon Sep 17 00:00:00 2001 From: Adam Carpenter Date: Wed, 11 Oct 2023 09:44:16 -0600 Subject: [PATCH 3/4] Update WC dep version --- packages/walletconnect/package.json | 3 +- yarn.lock | 74 ++++++++++++++--------------- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/packages/walletconnect/package.json b/packages/walletconnect/package.json index 30498c919..7d9feb743 100644 --- a/packages/walletconnect/package.json +++ b/packages/walletconnect/package.json @@ -59,8 +59,7 @@ "@walletconnect/types": "^2.9.0" }, "dependencies": { - "@ethersproject/providers": "5.5.0", - "@walletconnect/ethereum-provider": "^2.10.1", + "@walletconnect/ethereum-provider": "^2.10.2", "@walletconnect/modal": "2.6.2", "@web3-onboard/common": "^2.3.3", "joi": "17.9.1", diff --git a/yarn.lock b/yarn.lock index d61026231..30d405375 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3526,10 +3526,10 @@ "@walletconnect/types" "^1.8.0" "@walletconnect/utils" "^1.8.0" -"@walletconnect/core@2.10.1": - version "2.10.1" - resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.10.1.tgz#d1fb442bd77424666bacdb0f5a07f7708fb3d984" - integrity sha512-WAoXfmj+Zy5q48TnrKUjmHXJCBahzKwbul+noepRZf7JDtUAZ9IOWpUjg+UPRbfK5EiWZ0TF42S6SXidf7EHoQ== +"@walletconnect/core@2.10.2": + version "2.10.2" + resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.10.2.tgz#a1bf6e3e87b33f9df795ce0970d8ddd400fdc8a3" + integrity sha512-JQz/xp3SLEpTeRQctdck2ugSBVEpMxoSE+lFi2voJkZop1hv6P+uqr6E4PzjFluAjeAnKlT1xvra0aFWjPWVcw== dependencies: "@walletconnect/heartbeat" "1.2.1" "@walletconnect/jsonrpc-provider" "1.0.13" @@ -3542,8 +3542,8 @@ "@walletconnect/relay-auth" "^1.0.4" "@walletconnect/safe-json" "^1.0.2" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.10.1" - "@walletconnect/utils" "2.10.1" + "@walletconnect/types" "2.10.2" + "@walletconnect/utils" "2.10.2" events "^3.3.0" lodash.isequal "4.5.0" uint8arrays "^3.1.0" @@ -3625,19 +3625,19 @@ "@walletconnect/utils" "2.9.1" events "^3.3.0" -"@walletconnect/ethereum-provider@^2.10.1": - version "2.10.1" - resolved "https://registry.yarnpkg.com/@walletconnect/ethereum-provider/-/ethereum-provider-2.10.1.tgz#4733a98f0b388cf5ae6c2b269f50da87da432ee5" - integrity sha512-Yhoz8EXkKzxOlBT6G+elphqCx/gkH6RxD9/ZAiy9lLc8Ng5p1gvKCVVP5zsGNE9FbkKmHd+J9JJRzn2Bw2yqtQ== +"@walletconnect/ethereum-provider@^2.10.2": + version "2.10.2" + resolved "https://registry.yarnpkg.com/@walletconnect/ethereum-provider/-/ethereum-provider-2.10.2.tgz#d5aca538fbcbbf7dd771bceb2430de30f06411de" + integrity sha512-QMYFZ6+rVq2CJLdIPdKK0j1Qm66UA27oQU5V2SrL8EVwl7wFfm0Bq7fnL+qAWeDpn612dNeNErpk/ROa1zWlWg== dependencies: "@walletconnect/jsonrpc-http-connection" "^1.0.7" "@walletconnect/jsonrpc-provider" "^1.0.13" "@walletconnect/jsonrpc-types" "^1.0.3" "@walletconnect/jsonrpc-utils" "^1.0.8" - "@walletconnect/sign-client" "2.10.1" - "@walletconnect/types" "2.10.1" - "@walletconnect/universal-provider" "2.10.1" - "@walletconnect/utils" "2.10.1" + "@walletconnect/sign-client" "2.10.2" + "@walletconnect/types" "2.10.2" + "@walletconnect/universal-provider" "2.10.2" + "@walletconnect/utils" "2.10.2" events "^3.3.0" "@walletconnect/events@^1.0.1": @@ -3886,19 +3886,19 @@ dependencies: tslib "1.14.1" -"@walletconnect/sign-client@2.10.1": - version "2.10.1" - resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.10.1.tgz#db60bc9400cd79f0cb2380067343512b21ee4749" - integrity sha512-iG3eJGi1yXeG3xGeVSSMf8wDFyx239B0prLQfy1uYDtYFb2ynnH/09oqAZyKn96W5nfQzUgM2Mz157PVdloH3Q== +"@walletconnect/sign-client@2.10.2": + version "2.10.2" + resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.10.2.tgz#33300a9cfe42487473f66b73c99535f6b26f8c54" + integrity sha512-vviSLV3f92I0bReX+OLr1HmbH0uIzYEQQFd1MzIfDk9PkfFT/LLAHhUnDaIAMkIdippqDcJia+5QEtT4JihL3Q== dependencies: - "@walletconnect/core" "2.10.1" + "@walletconnect/core" "2.10.2" "@walletconnect/events" "^1.0.1" "@walletconnect/heartbeat" "1.2.1" "@walletconnect/jsonrpc-utils" "1.0.8" "@walletconnect/logger" "^2.0.1" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.10.1" - "@walletconnect/utils" "2.10.1" + "@walletconnect/types" "2.10.2" + "@walletconnect/utils" "2.10.2" events "^3.3.0" "@walletconnect/sign-client@2.9.1": @@ -3932,10 +3932,10 @@ dependencies: tslib "1.14.1" -"@walletconnect/types@2.10.1": - version "2.10.1" - resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.10.1.tgz#1355bce236f3eef575716ea3efe4beed98a873ef" - integrity sha512-7pccAhajQdiH2kYywjE1XI64IqRI+4ioyGy0wvz8d0UFQ/DSG3MLKR8jHf5aTOafQQ/HRLz6xvlzN4a7gIVkUQ== +"@walletconnect/types@2.10.2": + version "2.10.2" + resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.10.2.tgz#68e433a29ec2cf42d79d8b50c77bd5c1d91db721" + integrity sha512-luNV+07Wdla4STi9AejseCQY31tzWKQ5a7C3zZZaRK/di+rFaAAb7YW04OP4klE7tw/mJRGPTlekZElmHxO8kQ== dependencies: "@walletconnect/events" "^1.0.1" "@walletconnect/heartbeat" "1.2.1" @@ -3997,19 +3997,19 @@ "@walletconnect/logger" "^2.0.1" events "^3.3.0" -"@walletconnect/universal-provider@2.10.1": - version "2.10.1" - resolved "https://registry.yarnpkg.com/@walletconnect/universal-provider/-/universal-provider-2.10.1.tgz#c4a77bd2eed1a335edae5b2b298636092fff63ef" - integrity sha512-81QxTH/X4dRoYCz0U9iOrBYOcj7N897ONcB57wsGhEkV7Rc9htmWJq2CzeOuxvVZ+pNZkE+/aw9LrhizO1Ltxg== +"@walletconnect/universal-provider@2.10.2": + version "2.10.2" + resolved "https://registry.yarnpkg.com/@walletconnect/universal-provider/-/universal-provider-2.10.2.tgz#85c8da39f65da8fe33f65f62689e703607b5ddc5" + integrity sha512-wFgI0LbQ3D56sgaUMsgOHCM5m8WLxiC71BGuCKQfApgsbNMVKugYVy2zWHyUyi8sqTQHI+uSaVpDev4UHq9LEw== dependencies: "@walletconnect/jsonrpc-http-connection" "^1.0.7" "@walletconnect/jsonrpc-provider" "1.0.13" "@walletconnect/jsonrpc-types" "^1.0.2" "@walletconnect/jsonrpc-utils" "^1.0.7" "@walletconnect/logger" "^2.0.1" - "@walletconnect/sign-client" "2.10.1" - "@walletconnect/types" "2.10.1" - "@walletconnect/utils" "2.10.1" + "@walletconnect/sign-client" "2.10.2" + "@walletconnect/types" "2.10.2" + "@walletconnect/utils" "2.10.2" events "^3.3.0" "@walletconnect/universal-provider@2.9.1": @@ -4027,10 +4027,10 @@ "@walletconnect/utils" "2.9.1" events "^3.3.0" -"@walletconnect/utils@2.10.1": - version "2.10.1" - resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.10.1.tgz#65b37c9800eb0e80a08385b6987471fb46e1e22e" - integrity sha512-DM0dKgm9O58l7VqJEyV2OVv16XRePhDAReI23let6WdW1dSpw/Y/A89Lp99ZJOjLm2FxyblMRF3YRaZtHwBffw== +"@walletconnect/utils@2.10.2": + version "2.10.2" + resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.10.2.tgz#1f2c6a2f1bb95bcc4517b1e94aa7164c9286eb46" + integrity sha512-syxXRpc2yhSknMu3IfiBGobxOY7fLfLTJuw+ppKaeO6WUdZpIit3wfuGOcc0Ms3ZPFCrGfyGOoZsCvgdXtptRg== dependencies: "@stablelib/chacha20poly1305" "1.0.1" "@stablelib/hkdf" "1.0.1" @@ -4040,7 +4040,7 @@ "@walletconnect/relay-api" "^1.0.9" "@walletconnect/safe-json" "^1.0.2" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.10.1" + "@walletconnect/types" "2.10.2" "@walletconnect/window-getters" "^1.0.1" "@walletconnect/window-metadata" "^1.0.1" detect-browser "5.3.0" From c89d9df37f37d2cfb8852d422b66d38600df4358 Mon Sep 17 00:00:00 2001 From: Adam Carpenter Date: Wed, 11 Oct 2023 11:21:33 -0600 Subject: [PATCH 4/4] Refine validation --- packages/walletconnect/src/validation.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/walletconnect/src/validation.ts b/packages/walletconnect/src/validation.ts index 349cf9e70..5d9d15809 100644 --- a/packages/walletconnect/src/validation.ts +++ b/packages/walletconnect/src/validation.ts @@ -7,14 +7,12 @@ const wcOptions = Joi.object({ .optional() .custom((value, helpers) => { if (value === 1) { - return helpers.message({ - message: `Version 1 of WalletConnect is no longer supported. Please use version 2.`, - type: 'any.custom' - }) + console.warn( + 'Version 1 of WalletConnect has been fully deprecated. This version of @web3-onboard/walletconnect only supports version 2' + ) } else if (value !== 2 && value !== undefined) { - return helpers.message({ - message: 'Invalid version number. Please use version 2.', - type: 'any.custom' + return helpers.error('any.invalid', { + message: 'Invalid version number. This version of @web3-onboard/walletconnect only supports version 2' }) } return value // return the value unchanged if it's valid or not provided