diff --git a/.changeset/four-tips-push.md b/.changeset/four-tips-push.md new file mode 100644 index 00000000000..425cf20db76 --- /dev/null +++ b/.changeset/four-tips-push.md @@ -0,0 +1,5 @@ +--- +"@clerk/clerk-js": patch +--- + +Bug fix: Use the EIP-6963 standard to get a Web3 provider when more than one provider is injected. diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index 9e711db89c8..9701311a5ef 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -1,6 +1,6 @@ { "files": [ - { "path": "./dist/clerk.browser.js", "maxSize": "63kB" }, + { "path": "./dist/clerk.browser.js", "maxSize": "64kB" }, { "path": "./dist/clerk.headless.js", "maxSize": "43kB" }, { "path": "./dist/ui-common*.js", "maxSize": "85KB" }, { "path": "./dist/vendors*.js", "maxSize": "70KB" }, diff --git a/packages/clerk-js/src/utils/injectedWeb3Providers.ts b/packages/clerk-js/src/utils/injectedWeb3Providers.ts new file mode 100644 index 00000000000..7ce4591af6c --- /dev/null +++ b/packages/clerk-js/src/utils/injectedWeb3Providers.ts @@ -0,0 +1,57 @@ +import type { Web3Provider } from '@clerk/types'; +//https://eips.ethereum.org/EIPS/eip-6963 + +interface EIP6963ProviderInfo { + walletId: string; + uuid: string; + name: string; + icon: string; +} + +interface EIP1193Provider { + isStatus?: boolean; + host?: string; + path?: string; + sendAsync?: ( + request: { method: string; params?: [] }, + callback: (error: Error | null, response: unknown) => void, + ) => void; // For sending asynchronous requests + send?: (request: { method: string; params?: [] }, callback: (error: Error | null, response: unknown) => void) => void; // For sending synchronous requests + request: (request: { method: string; params?: string[] }) => Promise; // Standard method for sending requests per EIP-1193 +} + +interface EIP6963ProviderDetail { + info: EIP6963ProviderInfo; + provider: EIP1193Provider; +} + +type EIP6963AnnounceProviderEvent = CustomEvent; + +class InjectedWeb3Providers { + #providers: EIP6963ProviderDetail[] = []; + #providerIdMap: Record = { + coinbase: 'Coinbase Wallet', + metamask: 'MetaMask', + } as const; + + constructor() { + if (typeof window === 'undefined') { + return; + } + window.addEventListener('eip6963:announceProvider', this.#onAnnouncement as EventListener); + window.dispatchEvent(new Event('eip6963:requestProvider')); + } + + get = (provider: Web3Provider) => { + return this.#providers.find(p => p.info.name === this.#providerIdMap[provider])?.provider; + }; + + #onAnnouncement = (event: EIP6963AnnounceProviderEvent) => { + if (this.#providers.some(p => p.info.uuid === event.detail.info.uuid)) { + return; + } + this.#providers.push(event.detail); + }; +} + +export const injectedWeb3Providers = new InjectedWeb3Providers(); diff --git a/packages/clerk-js/src/utils/web3.ts b/packages/clerk-js/src/utils/web3.ts index 3dd3f6ade26..e7b3fca2fbb 100644 --- a/packages/clerk-js/src/utils/web3.ts +++ b/packages/clerk-js/src/utils/web3.ts @@ -1,23 +1,20 @@ import type { Web3Provider } from '@clerk/types'; import { toHex } from './hex'; - +import { injectedWeb3Providers } from './injectedWeb3Providers'; type GetWeb3IdentifierParams = { provider: Web3Provider; }; -export async function getWeb3Identifier(_: GetWeb3IdentifierParams): Promise { - // @ts-ignore - if (!global.ethereum) { - // Do nothing when ethereum doesn't exist. +export async function getWeb3Identifier(params: GetWeb3IdentifierParams) { + const injectedProvider = injectedWeb3Providers.get(params.provider); + if (!injectedProvider) { + // If a plugin for the requested provider is not found, + // the flow will fail as it has been the expected behavior so far. return ''; } - // @ts-ignore - const identifiers = await global.ethereum.request({ - method: 'eth_requestAccounts', - }); - + const identifiers = await injectedProvider.request({ method: 'eth_requestAccounts' }); return (identifiers && identifiers[0]) || ''; } @@ -27,15 +24,14 @@ type GenerateWeb3SignatureParams = { provider: Web3Provider; }; -export async function generateWeb3Signature({ identifier, nonce }: GenerateWeb3SignatureParams): Promise { - // @ts-ignore - if (!global.ethereum) { - // Do nothing when ethereum doesn't exist. +export async function generateWeb3Signature(params: GenerateWeb3SignatureParams): Promise { + const { identifier, nonce, provider } = params; + const injectedProvider = injectedWeb3Providers.get(provider); + if (!injectedProvider) { return ''; } - // @ts-ignore - return await global.ethereum.request({ + return await injectedProvider.request({ method: 'personal_sign', params: [`0x${toHex(nonce)}`, identifier], });