Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
06b934c
feat(clerk-js,clerk-react,shared,types): Add sign in with Solana feat…
kduprey Dec 10, 2025
0c1878d
feat(clerk-js,localizations,types): Add ability for users to connect …
kduprey Dec 12, 2025
5323789
fix(bundlewatch): Correct maxSize for vendors*.js to 143KB
kduprey Dec 15, 2025
9ac851d
chore: `pnpm dedupe`
kduprey Dec 15, 2025
d4dba5a
fix(ui): Improve bundle chunking
dstaley Dec 16, 2025
dca9fc0
fix(clerk-js): Fix signup bundle
dstaley Dec 17, 2025
646f881
fix(WalletInitialIcon): Ensure safe access to value for uppercase tra…
kduprey Dec 17, 2025
766e150
chore: sort imports
kduprey Dec 17, 2025
b2d2c62
fix: Update signUpContinueUrl for Solana wallet navigation
kduprey Dec 17, 2025
fc7717b
fix(Web3SelectSolanaWalletScreen): Improve error handling and ensure …
kduprey Dec 17, 2025
956a603
fix(Web3SolanaWalletButtons): Add aria-label for accessibility on Wal…
kduprey Dec 17, 2025
31a164a
docs: add JSDoc for authenticateWithSolana method
kduprey Dec 17, 2025
c61fb7d
fix(localization): correct German subtitle for Solana wallet registra…
kduprey Dec 17, 2025
26178c7
fix(InjectedWeb3SolanaProviders): prevent re-initialization by adding…
kduprey Dec 17, 2025
17a3bec
style: clean up import paths to use alias
kduprey Dec 17, 2025
1fab9a4
style: update import path to use path alias
kduprey Dec 17, 2025
94802ea
style: update authenticateWithSolana methods to destructre the params…
kduprey Dec 17, 2025
e0c16da
refactor: update chunk naming for solana wallet libs
kduprey Dec 17, 2025
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
7 changes: 7 additions & 0 deletions .changeset/afraid-apes-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@clerk/localizations': minor
'@clerk/clerk-js': minor
'@clerk/shared': minor
---

Add Web3 Solana support to `<UserProfile />`
9 changes: 9 additions & 0 deletions .changeset/legal-jokes-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@clerk/localizations': minor
'@clerk/clerk-js': minor
'@clerk/shared': minor
'@clerk/react': minor
'@clerk/ui': minor
---

Add support for Sign in with Solana.
4 changes: 2 additions & 2 deletions packages/clerk-js/bundle-check.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import path from 'node:path';
import { pipeline } from 'node:stream';
import zlib from 'node:zlib';

import { chromium } from 'playwright';
import { chromium } from '@playwright/test';

/**
* This script generates a CLI report detailing the gzipped size of JavaScript resources loaded by `clerk-js` for a
Expand Down Expand Up @@ -212,7 +212,7 @@ function report(url, responses) {

/**
* Loads the given `url` in `browser`, capturing all HTTP requests that occur.
* @param {import('playwright').Browser} browser
* @param {import('@playwright/test').Browser} browser
* @param {string} url
*/
async function getResponseSizes(browser, url) {
Expand Down
4 changes: 4 additions & 0 deletions packages/clerk-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,13 @@
"@base-org/account": "catalog:module-manager",
"@clerk/shared": "workspace:^",
"@coinbase/wallet-sdk": "catalog:module-manager",
"@solana/wallet-adapter-base": "catalog:module-manager",
"@solana/wallet-adapter-react": "catalog:module-manager",
"@solana/wallet-standard": "catalog:module-manager",
"@stripe/stripe-js": "5.6.0",
"@swc/helpers": "^0.5.17",
"@tanstack/query-core": "5.87.4",
"@wallet-standard/core": "catalog:module-manager",
"@zxcvbn-ts/core": "catalog:module-manager",
"@zxcvbn-ts/language-common": "catalog:module-manager",
"alien-signals": "2.0.6",
Expand Down
15 changes: 14 additions & 1 deletion packages/clerk-js/rspack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,20 @@ const common = ({ mode, variant, disableRHC = false }) => {
},
defaultVendors: {
minChunks: 1,
test: /[\\/]node_modules[\\/]/,
test: module => {
if (!(module instanceof rspack.NormalModule) || !module.resource) {
return false;
}
// Exclude Solana packages and their known transitive dependencies
if (
/[\\/]node_modules[\\/](@solana|@solana-mobile|@wallet-standard|bn\.js|borsh|buffer|superstruct|bs58|jayson|rpc-websockets|qrcode)[\\/]/.test(
module.resource,
)
) {
return false;
}
return /[\\/]node_modules[\\/]/.test(module.resource);
},
name: 'vendors',
priority: -10,
},
Expand Down
26 changes: 23 additions & 3 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import type {
AuthenticateWithGoogleOneTapParams,
AuthenticateWithMetamaskParams,
AuthenticateWithOKXWalletParams,
AuthenticateWithSolanaParams,
BillingNamespace,
CheckoutSignalValue,
Clerk as ClerkInterface,
Expand All @@ -74,7 +75,7 @@ import type {
EnvironmentJSON,
EnvironmentJSONSnapshot,
EnvironmentResource,
GenerateSignatureParams,
GenerateSignature,
GoogleOneTapProps,
HandleEmailLinkVerificationParams,
HandleOAuthCallbackParams,
Expand Down Expand Up @@ -2338,6 +2339,13 @@ export class Clerk implements ClerkInterface {
});
};

public authenticateWithSolana = async (props: AuthenticateWithSolanaParams): Promise<void> => {
await this.authenticateWithWeb3({
...props,
strategy: 'web3_solana_signature',
});
};

public authenticateWithWeb3 = async ({
redirectUrl,
signUpContinueUrl,
Expand All @@ -2346,6 +2354,7 @@ export class Clerk implements ClerkInterface {
strategy,
legalAccepted,
secondFactorUrl,
walletName,
}: ClerkAuthenticateWithWeb3Params): Promise<void> => {
if (!this.client || !this.environment) {
return;
Expand All @@ -2354,8 +2363,8 @@ export class Clerk implements ClerkInterface {
const { displayConfig } = this.environment;

const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider;
const identifier = await web3().getWeb3Identifier({ provider });
let generateSignature: (params: GenerateSignatureParams) => Promise<string>;
const identifier = await web3().getWeb3Identifier({ provider, walletName });
let generateSignature: GenerateSignature;
switch (provider) {
case 'metamask':
generateSignature = web3().generateSignatureWithMetamask;
Expand All @@ -2366,6 +2375,15 @@ export class Clerk implements ClerkInterface {
case 'coinbase_wallet':
generateSignature = web3().generateSignatureWithCoinbaseWallet;
break;
case 'solana':
if (!walletName) {
throw new ClerkRuntimeError('Wallet name is required for Solana authentication.', {
code: 'web3_solana_wallet_name_required',
});
}
// Solana requires walletName; bind it into the helper
generateSignature = params => web3().generateSignatureWithSolana({ ...params, walletName });
break;
default:
generateSignature = web3().generateSignatureWithOKXWallet;
break;
Expand Down Expand Up @@ -2395,6 +2413,7 @@ export class Clerk implements ClerkInterface {
identifier,
generateSignature,
strategy,
walletName,
});
} catch (err) {
if (isError(err, ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND)) {
Expand All @@ -2404,6 +2423,7 @@ export class Clerk implements ClerkInterface {
unsafeMetadata,
strategy,
legalAccepted,
walletName,
});

if (
Expand Down
63 changes: 57 additions & 6 deletions packages/clerk-js/src/core/resources/SignIn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type {
EmailLinkConfig,
EmailLinkFactor,
EnterpriseSSOConfig,
GenerateSignature,
PassKeyConfig,
PasskeyFactor,
PhoneCodeConfig,
Expand All @@ -32,6 +33,7 @@ import type {
ResetPasswordEmailCodeFactorConfig,
ResetPasswordParams,
ResetPasswordPhoneCodeFactorConfig,
SignInAuthenticateWithSolanaParams,
SignInCreateParams,
SignInFirstFactor,
SignInFutureBackupCodeVerifyParams,
Expand Down Expand Up @@ -202,6 +204,7 @@ export class SignIn extends BaseResource implements SignInResource {
case 'web3_base_signature':
case 'web3_coinbase_wallet_signature':
case 'web3_okx_wallet_signature':
case 'web3_solana_signature':
config = { web3WalletId: params.web3WalletId } as Web3SignatureConfig;
break;
case 'reset_password_phone_code':
Expand Down Expand Up @@ -363,13 +366,17 @@ export class SignIn extends BaseResource implements SignInResource {
};

public authenticateWithWeb3 = async (params: AuthenticateWithWeb3Params): Promise<SignInResource> => {
const { identifier, generateSignature, strategy = 'web3_metamask_signature' } = params || {};
const { identifier, generateSignature, strategy = 'web3_metamask_signature', walletName } = params || {};
const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider;

if (!(typeof generateSignature === 'function')) {
clerkMissingOptionError('generateSignature');
}

if (provider === 'solana' && !walletName) {
clerkMissingOptionError('walletName');
}

await this.create({ identifier });

const web3FirstFactor = this.supportedFirstFactors?.find(f => f.strategy === strategy) as Web3SignatureFactor;
Expand All @@ -387,7 +394,7 @@ export class SignIn extends BaseResource implements SignInResource {

let signature: string;
try {
signature = await generateSignature({ identifier, nonce: message, provider });
signature = await generateSignature({ identifier, nonce: message, walletName, provider });
} catch (err) {
// There is a chance that as a user when you try to setup and use the Coinbase Wallet with an existing
// Passkey in order to authenticate, the initial generate signature request to be rejected. For this
Expand All @@ -396,7 +403,7 @@ export class SignIn extends BaseResource implements SignInResource {
// error code 4001 means the user rejected the request
// Reference: https://docs.cdp.coinbase.com/wallet-sdk/docs/errors
if (provider === 'coinbase_wallet' && err.code === 4001) {
signature = await generateSignature({ identifier, nonce: message, provider });
signature = await generateSignature({ identifier, nonce: message, provider, walletName });
} else {
throw err;
}
Expand Down Expand Up @@ -444,6 +451,31 @@ export class SignIn extends BaseResource implements SignInResource {
});
};

/**
* Authenticates a user using a Solana Web3 wallet during sign-in.
*
* @param params - Configuration for Solana authentication
* @param params.walletName - The name of the Solana wallet to use for authentication
* @returns A promise that resolves to the updated SignIn resource
* @throws {ClerkRuntimeError} If walletName is not provided or wallet connection fails
*
* @example
* ```typescript
* await signIn.authenticateWithSolana({ walletName: 'phantom' });
* ```
*/
public authenticateWithSolana = async ({
walletName,
}: SignInAuthenticateWithSolanaParams): Promise<SignInResource> => {
const identifier = await web3().getSolanaIdentifier(walletName);
return this.authenticateWithWeb3({
identifier,
generateSignature: p => web3().generateSignatureWithSolana({ ...p, walletName: walletName }),
strategy: 'web3_solana_signature',
walletName: walletName,
});
};

public authenticateWithPasskey = async (params?: AuthenticateWithPasskeyParams): Promise<SignInResource> => {
const { flow } = params || {};

Expand Down Expand Up @@ -961,7 +993,7 @@ class SignInFuture implements SignInFutureResource {

return runAsyncResourceTask(this.#resource, async () => {
let identifier;
let generateSignature;
let generateSignature: GenerateSignature;
switch (provider) {
case 'metamask':
identifier = await web3().getMetamaskIdentifier();
Expand All @@ -979,6 +1011,16 @@ class SignInFuture implements SignInFutureResource {
identifier = await web3().getOKXWalletIdentifier();
generateSignature = web3().generateSignatureWithOKXWallet;
break;
case 'solana':
if (!params.walletName) {
throw new ClerkRuntimeError('Wallet name is required for Solana authentication.', {
code: 'web3_solana_wallet_name_required',
});
}
identifier = await web3().getSolanaIdentifier(params.walletName);
generateSignature = p =>
web3().generateSignatureWithSolana({ ...p, walletName: params.walletName as string });
break;
default:
throw new Error(`Unsupported Web3 provider: ${provider}`);
}
Expand All @@ -1004,7 +1046,12 @@ class SignInFuture implements SignInFutureResource {

let signature: string;
try {
signature = await generateSignature({ identifier, nonce: message });
signature = await generateSignature({
identifier,
nonce: message,
walletName: params?.walletName,
provider,
});
} catch (err) {
// There is a chance that as a user when you try to setup and use the Coinbase Wallet with an existing
// Passkey in order to authenticate, the initial generate signature request to be rejected. For this
Expand All @@ -1013,7 +1060,11 @@ class SignInFuture implements SignInFutureResource {
// error code 4001 means the user rejected the request
// Reference: https://docs.cdp.coinbase.com/wallet-sdk/docs/errors
if (provider === 'coinbase_wallet' && err.code === 4001) {
signature = await generateSignature({ identifier, nonce: message });
signature = await generateSignature({
identifier,
nonce: message,
provider,
});
} else {
throw err;
}
Expand Down
Loading