Skip to content

Commit f737d26

Browse files
authored
feat(clerk-js,clerk-react,types): Add Signal support for Web3 APIs (#6840)
1 parent 1e9bd8b commit f737d26

File tree

7 files changed

+173
-3
lines changed

7 files changed

+173
-3
lines changed

.changeset/quick-ghosts-fail.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/clerk-react': minor
4+
'@clerk/types': minor
5+
---
6+
7+
[Experimental] Add Signal support for Web3 APIs

packages/clerk-js/bundlewatch.config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"files": [
33
{ "path": "./dist/clerk.js", "maxSize": "821KB" },
4-
{ "path": "./dist/clerk.browser.js", "maxSize": "79KB" },
5-
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "121KB" },
4+
{ "path": "./dist/clerk.browser.js", "maxSize": "81KB" },
5+
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "123KB" },
66
{ "path": "./dist/clerk.headless*.js", "maxSize": "63KB" },
77
{ "path": "./dist/ui-common*.js", "maxSize": "117.1KB" },
88
{ "path": "./dist/ui-common*.legacy.*.js", "maxSize": "120KB" },

packages/clerk-js/src/core/resources/SignIn.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import type {
4646
SignInFutureSSOParams,
4747
SignInFutureTicketParams,
4848
SignInFutureTOTPVerifyParams,
49+
SignInFutureWeb3Params,
4950
SignInIdentifier,
5051
SignInJSON,
5152
SignInJSONSnapshot,
@@ -871,6 +872,77 @@ class SignInFuture implements SignInFutureResource {
871872
});
872873
}
873874

875+
async web3(params: SignInFutureWeb3Params): Promise<{ error: unknown }> {
876+
const { strategy } = params;
877+
const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider;
878+
879+
return runAsyncResourceTask(this.resource, async () => {
880+
let identifier;
881+
let generateSignature;
882+
switch (provider) {
883+
case 'metamask':
884+
identifier = await getMetamaskIdentifier();
885+
generateSignature = generateSignatureWithMetamask;
886+
break;
887+
case 'coinbase_wallet':
888+
identifier = await getCoinbaseWalletIdentifier();
889+
generateSignature = generateSignatureWithCoinbaseWallet;
890+
break;
891+
case 'base':
892+
identifier = await getBaseIdentifier();
893+
generateSignature = generateSignatureWithBase;
894+
break;
895+
case 'okx_wallet':
896+
identifier = await getOKXWalletIdentifier();
897+
generateSignature = generateSignatureWithOKXWallet;
898+
break;
899+
default:
900+
throw new Error(`Unsupported Web3 provider: ${provider}`);
901+
}
902+
903+
await this.create({ identifier });
904+
905+
const web3FirstFactor = this.resource.supportedFirstFactors?.find(
906+
f => f.strategy === strategy,
907+
) as Web3SignatureFactor;
908+
if (!web3FirstFactor) {
909+
throw new Error('Web3 first factor not found');
910+
}
911+
912+
await this.resource.__internal_basePost({
913+
body: { web3WalletId: web3FirstFactor.web3WalletId, strategy },
914+
action: 'prepare_first_factor',
915+
});
916+
917+
const { message } = this.firstFactorVerification;
918+
if (!message) {
919+
throw new Error('Web3 nonce not found');
920+
}
921+
922+
let signature: string;
923+
try {
924+
signature = await generateSignature({ identifier, nonce: message });
925+
} catch (err) {
926+
// There is a chance that as a user when you try to setup and use the Coinbase Wallet with an existing
927+
// Passkey in order to authenticate, the initial generate signature request to be rejected. For this
928+
// reason we retry the request once more in order for the flow to be able to be completed successfully.
929+
//
930+
// error code 4001 means the user rejected the request
931+
// Reference: https://docs.cdp.coinbase.com/wallet-sdk/docs/errors
932+
if (provider === 'coinbase_wallet' && err.code === 4001) {
933+
signature = await generateSignature({ identifier, nonce: message });
934+
} else {
935+
throw err;
936+
}
937+
}
938+
939+
await this.resource.__internal_basePost({
940+
body: { signature, strategy },
941+
action: 'attempt_first_factor',
942+
});
943+
});
944+
}
945+
874946
async sendMFAPhoneCode(): Promise<{ error: unknown }> {
875947
return runAsyncResourceTask(this.resource, async () => {
876948
const phoneCodeFactor = this.resource.supportedSecondFactors?.find(f => f.strategy === 'phone_code');

packages/clerk-js/src/core/resources/SignUp.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import type {
2727
SignUpFutureSSOParams,
2828
SignUpFutureTicketParams,
2929
SignUpFutureUpdateParams,
30+
SignUpFutureWeb3Params,
3031
SignUpIdentificationField,
3132
SignUpJSON,
3233
SignUpJSONSnapshot,
@@ -713,6 +714,72 @@ class SignUpFuture implements SignUpFutureResource {
713714
});
714715
}
715716

717+
async web3(params: SignUpFutureWeb3Params): Promise<{ error: unknown }> {
718+
const { strategy, unsafeMetadata, legalAccepted } = params;
719+
const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider;
720+
721+
return runAsyncResourceTask(this.resource, async () => {
722+
let identifier;
723+
let generateSignature;
724+
switch (provider) {
725+
case 'metamask':
726+
identifier = await getMetamaskIdentifier();
727+
generateSignature = generateSignatureWithMetamask;
728+
break;
729+
case 'coinbase_wallet':
730+
identifier = await getCoinbaseWalletIdentifier();
731+
generateSignature = generateSignatureWithCoinbaseWallet;
732+
break;
733+
case 'base':
734+
identifier = await getBaseIdentifier();
735+
generateSignature = generateSignatureWithBase;
736+
break;
737+
case 'okx_wallet':
738+
identifier = await getOKXWalletIdentifier();
739+
generateSignature = generateSignatureWithOKXWallet;
740+
break;
741+
default:
742+
throw new Error(`Unsupported Web3 provider: ${provider}`);
743+
}
744+
745+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
746+
const web3Wallet = identifier || this.resource.web3wallet!;
747+
await this.create({ web3Wallet, unsafeMetadata, legalAccepted });
748+
await this.resource.__internal_basePost({
749+
body: { strategy },
750+
action: 'prepare_verification',
751+
});
752+
753+
const { message } = this.resource.verifications.web3Wallet;
754+
if (!message) {
755+
clerkVerifyWeb3WalletCalledBeforeCreate('SignUp');
756+
}
757+
758+
let signature: string;
759+
try {
760+
signature = await generateSignature({ identifier, nonce: message });
761+
} catch (err) {
762+
// There is a chance that as a first time visitor when you try to setup and use the
763+
// Coinbase Wallet from scratch in order to authenticate, the initial generate
764+
// signature request to be rejected. For this reason we retry the request once more
765+
// in order for the flow to be able to be completed successfully.
766+
//
767+
// error code 4001 means the user rejected the request
768+
// Reference: https://docs.cdp.coinbase.com/wallet-sdk/docs/errors
769+
if (provider === 'coinbase_wallet' && err.code === 4001) {
770+
signature = await generateSignature({ identifier, nonce: message });
771+
} else {
772+
throw err;
773+
}
774+
}
775+
776+
await this.resource.__internal_basePost({
777+
body: { signature, strategy },
778+
action: 'attempt_verification',
779+
});
780+
});
781+
}
782+
716783
async ticket(params?: SignUpFutureTicketParams): Promise<{ error: unknown }> {
717784
const ticket = params?.ticket ?? getClerkQueryParam('__clerk_ticket');
718785
return this.create({ ...params, ticket: ticket ?? undefined });

packages/react/src/stateProxy.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export class StateProxy implements State {
9292
'verifyBackupCode',
9393
] as const),
9494
ticket: this.gateMethod(target, 'ticket'),
95+
web3: this.gateMethod(target, 'web3'),
9596
},
9697
};
9798
}
@@ -121,6 +122,7 @@ export class StateProxy implements State {
121122
sso: gateMethod(target, 'sso'),
122123
password: gateMethod(target, 'password'),
123124
ticket: gateMethod(target, 'ticket'),
125+
web3: gateMethod(target, 'web3'),
124126
finalize: gateMethod(target, 'finalize'),
125127

126128
verifications: wrapMethods(() => target().verifications, [

packages/types/src/signInFuture.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { SetActiveNavigate } from './clerk';
22
import type { PhoneCodeChannel } from './phoneCodeChannel';
33
import type { SignInFirstFactor, SignInStatus } from './signInCommon';
4-
import type { OAuthStrategy } from './strategies';
4+
import type { OAuthStrategy, Web3Strategy } from './strategies';
55
import type { VerificationResource } from './verification';
66

77
export interface SignInFutureCreateParams {
@@ -115,6 +115,10 @@ export interface SignInFutureTicketParams {
115115
ticket: string;
116116
}
117117

118+
export interface SignInFutureWeb3Params {
119+
strategy: Web3Strategy;
120+
}
121+
118122
export interface SignInFutureFinalizeParams {
119123
navigate?: SetActiveNavigate;
120124
}
@@ -275,6 +279,11 @@ export interface SignInFutureResource {
275279
*/
276280
ticket: (params?: SignInFutureTicketParams) => Promise<{ error: unknown }>;
277281

282+
/**
283+
* Used to perform a Web3-based sign-in.
284+
*/
285+
web3: (params: SignInFutureWeb3Params) => Promise<{ error: unknown }>;
286+
278287
/**
279288
* Used to convert a sign-in with `status === 'complete'` into an active session. Will cause anything observing the
280289
* session state (such as the `useUser()` hook) to update automatically.

packages/types/src/signUpFuture.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { SetActiveNavigate } from './clerk';
22
import type { PhoneCodeChannel } from './phoneCodeChannel';
33
import type { SignUpIdentificationField, SignUpStatus } from './signUpCommon';
4+
import type { Web3Strategy } from './strategies';
45

56
interface SignUpFutureAdditionalParams {
67
firstName?: string;
@@ -12,6 +13,7 @@ interface SignUpFutureAdditionalParams {
1213
export interface SignUpFutureCreateParams extends SignUpFutureAdditionalParams {
1314
transfer?: boolean;
1415
ticket?: string;
16+
web3Wallet?: string;
1517
}
1618

1719
// This will likely get more properties
@@ -55,6 +57,12 @@ export interface SignUpFutureTicketParams extends SignUpFutureAdditionalParams {
5557
ticket: string;
5658
}
5759

60+
export interface SignUpFutureWeb3Params {
61+
strategy: Web3Strategy;
62+
unsafeMetadata?: SignUpUnsafeMetadata;
63+
legalAccepted?: boolean;
64+
}
65+
5866
export interface SignUpFutureFinalizeParams {
5967
navigate?: SetActiveNavigate;
6068
}
@@ -125,6 +133,11 @@ export interface SignUpFutureResource {
125133
*/
126134
ticket: (params?: SignUpFutureTicketParams) => Promise<{ error: unknown }>;
127135

136+
/**
137+
* Used to perform a Web3-based sign-up.
138+
*/
139+
web3: (params: SignUpFutureWeb3Params) => Promise<{ error: unknown }>;
140+
128141
/**
129142
* Used to convert a sign-up with `status === 'complete'` into an active session. Will cause anything observing the
130143
* session state (such as the `useUser()` hook) to update automatically.

0 commit comments

Comments
 (0)