Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/four-tips-push.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion packages/clerk-js/bundlewatch.config.json
Original file line number Diff line number Diff line change
@@ -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" },
Expand Down
57 changes: 57 additions & 0 deletions packages/clerk-js/src/utils/injectedWeb3Providers.ts
Original file line number Diff line number Diff line change
@@ -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<string>; // Standard method for sending requests per EIP-1193
}

interface EIP6963ProviderDetail {
info: EIP6963ProviderInfo;
provider: EIP1193Provider;
}

type EIP6963AnnounceProviderEvent = CustomEvent;

class InjectedWeb3Providers {
#providers: EIP6963ProviderDetail[] = [];
#providerIdMap: Record<Web3Provider, string> = {
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();
28 changes: 12 additions & 16 deletions packages/clerk-js/src/utils/web3.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
// @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]) || '';
}

Expand All @@ -27,15 +24,14 @@ type GenerateWeb3SignatureParams = {
provider: Web3Provider;
};

export async function generateWeb3Signature({ identifier, nonce }: GenerateWeb3SignatureParams): Promise<string> {
// @ts-ignore
if (!global.ethereum) {
// Do nothing when ethereum doesn't exist.
export async function generateWeb3Signature(params: GenerateWeb3SignatureParams): Promise<string> {
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],
});
Expand Down
Loading