diff --git a/.changeset/purple-rice-tell.md b/.changeset/purple-rice-tell.md
new file mode 100644
index 00000000000..3937076b244
--- /dev/null
+++ b/.changeset/purple-rice-tell.md
@@ -0,0 +1,7 @@
+---
+"@clerk/localizations": minor
+"@clerk/clerk-js": minor
+"@clerk/types": minor
+---
+
+Support connecting Coinbase Wallet via
diff --git a/packages/clerk-js/src/core/resources/User.ts b/packages/clerk-js/src/core/resources/User.ts
index 857778d0991..e30b96ecc7f 100644
--- a/packages/clerk-js/src/core/resources/User.ts
+++ b/packages/clerk-js/src/core/resources/User.ts
@@ -392,6 +392,10 @@ export class User extends BaseResource implements UserResource {
return this.externalAccounts.filter(externalAccount => externalAccount.verification?.status != 'verified');
}
+ get verifiedWeb3Wallets() {
+ return this.web3Wallets.filter(web3Wallet => web3Wallet.verification?.status == 'verified');
+ }
+
get hasVerifiedEmailAddress() {
return this.emailAddresses.filter(email => email.verification.status === 'verified').length > 0;
}
diff --git a/packages/clerk-js/src/ui/components/UserProfile/Web3Form.tsx b/packages/clerk-js/src/ui/components/UserProfile/Web3Form.tsx
index b2f8509e40d..3f0178c420e 100644
--- a/packages/clerk-js/src/ui/components/UserProfile/Web3Form.tsx
+++ b/packages/clerk-js/src/ui/components/UserProfile/Web3Form.tsx
@@ -1,7 +1,7 @@
import { useUser } from '@clerk/shared/react';
-import type { Web3Strategy } from '@clerk/types';
+import type { Web3Provider, Web3Strategy } from '@clerk/types';
-import { generateSignatureWithMetamask, getMetamaskIdentifier } from '../../../utils/web3';
+import { generateWeb3Signature, getWeb3Identifier } from '../../../utils/web3';
import { descriptors, Image, localizationKeys } from '../../customizables';
import { ProfileSection, useCardState, withCardStateProvider } from '../../elements';
import { useEnabledThirdPartyProviders } from '../../hooks';
@@ -10,33 +10,33 @@ import { getFieldError, handleError } from '../../utils';
export const AddWeb3WalletActionMenu = withCardStateProvider(() => {
const card = useCardState();
const { user } = useUser();
- const { strategyToDisplayData } = useEnabledThirdPartyProviders();
+ const { strategies, strategyToDisplayData } = useEnabledThirdPartyProviders();
+
+ const enabledStrategies = strategies.filter(s => s.startsWith('web3')) as Web3Strategy[];
+ const connectedStrategies = user?.verifiedWeb3Wallets.map(w => w.verification.strategy) as Web3Strategy[];
+ const unconnectedStrategies = enabledStrategies.filter(strategy => {
+ return !connectedStrategies.includes(strategy);
+ });
- // TODO: This logic is very similar to AddConnectedAccount but only metamask is supported right now
- // const enabledStrategies = strategies.filter(s => s.startsWith('web3')) as Web3Strategy[];
- // const connectedStrategies = user.web3Wallets.map(w => w.web3Wallet) as OAuthStrategy[];
- const unconnectedStrategies: Web3Strategy[] =
- user?.web3Wallets.filter(w => w.verification?.status === 'verified').length === 0
- ? ['web3_metamask_signature']
- : [];
const connect = async (strategy: Web3Strategy) => {
+ const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider;
+
try {
card.setLoading(strategy);
- const identifier = await getMetamaskIdentifier();
+ const identifier = await getWeb3Identifier({ provider });
if (!user) {
throw new Error('user is not defined');
}
let web3Wallet = await user.createWeb3Wallet({ web3Wallet: identifier });
- web3Wallet = await web3Wallet.prepareVerification({ strategy: 'web3_metamask_signature' });
+ web3Wallet = await web3Wallet.prepareVerification({ strategy });
const nonce = web3Wallet.verification.nonce as string;
- const signature = await generateSignatureWithMetamask({ identifier, nonce });
+ const signature = await generateWeb3Signature({ identifier, nonce, provider });
await web3Wallet.attemptVerification({ signature });
card.setIdle();
} catch (err) {
card.setIdle();
- console.log(err);
const fieldError = getFieldError(err);
if (fieldError) {
card.setError(fieldError.longMessage);
@@ -62,7 +62,13 @@ export const AddWeb3WalletActionMenu = withCardStateProvider(() => {
onClick={() => connect(strategy)}
isLoading={card.loadingMetadata === strategy}
isDisabled={card.isLoading}
- localizationKey={`Connect ${strategyToDisplayData[strategy].name} wallet`}
+ localizationKey={localizationKeys('userProfile.web3WalletPage.web3WalletButtonsBlockButton', {
+ provider: strategyToDisplayData[strategy].name,
+ })}
+ sx={t => ({
+ justifyContent: 'start',
+ gap: t.space.$2,
+ })}
leftIcon={
{
+type GetWeb3IdentifierParams = {
+ provider: Web3Provider;
+};
+
+export async function getWeb3Identifier(_: GetWeb3IdentifierParams): Promise {
// @ts-ignore
if (!global.ethereum) {
- // Do nothing when ethereum doesn't exist. We might revise this in the future
- // to offer an Install Metamask prompt to our users.
+ // Do nothing when ethereum doesn't exist.
return '';
}
@@ -16,24 +21,35 @@ export async function getMetamaskIdentifier(): Promise {
return (identifiers && identifiers[0]) || '';
}
-export type GenerateSignatureParams = {
+type GenerateWeb3SignatureParams = {
identifier: string;
nonce: string;
+ provider: Web3Provider;
};
-export async function generateSignatureWithMetamask({ identifier, nonce }: GenerateSignatureParams): Promise {
+export async function generateWeb3Signature({ identifier, nonce }: GenerateWeb3SignatureParams): Promise {
// @ts-ignore
if (!global.ethereum) {
- // Do nothing when ethereum doesn't exist. We might revise this in the future
- // to offer an Install Metamask prompt to our users.
+ // Do nothing when ethereum doesn't exist.
return '';
}
// @ts-ignore
- const signature: string = await global.ethereum.request({
+ return await global.ethereum.request({
method: 'personal_sign',
params: [`0x${toHex(nonce)}`, identifier],
});
+}
+
+export async function getMetamaskIdentifier(): Promise {
+ return await getWeb3Identifier({ provider: 'metamask' });
+}
- return signature;
+type GenerateSignatureParams = {
+ identifier: string;
+ nonce: string;
+};
+
+export async function generateSignatureWithMetamask({ identifier, nonce }: GenerateSignatureParams): Promise {
+ return await generateWeb3Signature({ identifier, nonce, provider: 'metamask' });
}
diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts
index 42aba213888..e9686b12a03 100644
--- a/packages/localizations/src/en-US.ts
+++ b/packages/localizations/src/en-US.ts
@@ -792,7 +792,7 @@ export const enUS: LocalizationResource = {
},
web3WalletsSection: {
destructiveAction: 'Remove wallet',
- primaryButton: 'Web3 wallets',
+ primaryButton: 'Connect wallet',
title: 'Web3 wallets',
},
},
@@ -810,6 +810,7 @@ export const enUS: LocalizationResource = {
},
subtitle__availableWallets: 'Select a web3 wallet to connect to your account.',
subtitle__unavailableWallets: 'There are no available web3 wallets.',
+ web3WalletButtonsBlockButton: '{{provider|titleize}}',
successMessage: 'The wallet has been added to your account.',
title: 'Add web3 wallet',
},
diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts
index 712a68f7615..6c04fdbed95 100644
--- a/packages/types/src/localization.ts
+++ b/packages/types/src/localization.ts
@@ -461,6 +461,7 @@ type _LocalizationResource = {
title: LocalizationValue;
subtitle__availableWallets: LocalizationValue;
subtitle__unavailableWallets: LocalizationValue;
+ web3WalletButtonsBlockButton: LocalizationValue;
successMessage: LocalizationValue;
removeResource: {
title: LocalizationValue;
diff --git a/packages/types/src/user.ts b/packages/types/src/user.ts
index f4d58491408..588efc7f145 100644
--- a/packages/types/src/user.ts
+++ b/packages/types/src/user.ts
@@ -144,6 +144,8 @@ export interface UserResource extends ClerkResource {
get unverifiedExternalAccounts(): ExternalAccountResource[];
+ get verifiedWeb3Wallets(): Web3WalletResource[];
+
get hasVerifiedEmailAddress(): boolean;
get hasVerifiedPhoneNumber(): boolean;