Skip to content

Commit

Permalink
feat(types,clerk-js): Enhance Web3 wallet resource with relevant oper…
Browse files Browse the repository at this point in the history
…ations

Enhance Web3Wallet resource with new operations. Those are create() which creates a new web3 wallet prepareVerification() to prepare the relevant web3 wallet verification, attemptVerification() to verify the web3 wallet and finally destroy() to delete it.
  • Loading branch information
chanioxaris committed May 17, 2022
1 parent acd7160 commit a166716
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 20 deletions.
20 changes: 16 additions & 4 deletions packages/clerk-js/src/core/resources/SignUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,12 @@ export class SignUp extends BaseResource implements SignUpResource {
};

attemptWeb3WalletVerification = async (params: AttemptWeb3WalletVerificationParams): Promise<SignUpResource> => {
const { generateSignature } = params || {};
const { signature, generateSignature } = params || {};

if (signature) {
return this.attemptVerification({ signature, strategy: 'web3_metamask_signature' });
}

if (!(typeof generateSignature === 'function')) {
clerkMissingOptionError('generateSignature');
}
Expand All @@ -148,16 +153,23 @@ export class SignUp extends BaseResource implements SignUpResource {
clerkVerifyWeb3WalletCalledBeforeCreate('SignUp');
}

const signature = await generateSignature({ identifier: this.web3wallet!, nonce });
return this.attemptVerification({ signature, strategy: 'web3_metamask_signature' });
const generatedSignature = await generateSignature({ identifier: this.web3wallet!, nonce });
return this.attemptVerification({ signature: generatedSignature, strategy: 'web3_metamask_signature' });
};

public authenticateWithWeb3 = async (params: AuthenticateWithWeb3Params): Promise<SignUpResource> => {
const { generateSignature, identifier } = params || {};
const web3Wallet = identifier || this.web3wallet!;
await this.create({ web3Wallet });
await this.prepareWeb3WalletVerification();
return this.attemptWeb3WalletVerification({ generateSignature });

const { nonce } = this.verifications.web3Wallet;
if (!nonce) {
clerkVerifyWeb3WalletCalledBeforeCreate('SignUp');
}

const signature = await generateSignature({ identifier, nonce });
return this.attemptWeb3WalletVerification({ signature });
};

public authenticateWithMetamask = async (): Promise<SignUpResource> => {
Expand Down
44 changes: 34 additions & 10 deletions packages/clerk-js/src/core/resources/User.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ExternalAccountJSON, UserJSON, VerificationJSON } from '@clerk/types';
import { ExternalAccountJSON, UserJSON, VerificationJSON, Web3WalletJSON } from '@clerk/types';
import { BaseResource, ExternalAccount } from 'core/resources/internal';

import { User } from './User';

describe('User', () => {

it('creates an external account', async () => {
const externalAccountJSON = {
object: 'external_account',
Expand All @@ -15,9 +14,7 @@ describe('User', () => {
};

// @ts-ignore
BaseResource._fetch = jest.fn().mockReturnValue(
Promise.resolve({ response: externalAccountJSON }),
);
BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: externalAccountJSON }));

const user = new User({
email_addresses: [],
Expand All @@ -32,12 +29,39 @@ describe('User', () => {
expect(BaseResource._fetch).toHaveBeenCalledWith({
method: 'POST',
path: '/me/external_accounts',
body:
{
redirect_url: 'https://www.example.com',
strategy: 'oauth_dropbox',
}
body: {
redirect_url: 'https://www.example.com',
strategy: 'oauth_dropbox',
},
});
});

it('creates a web3 wallet', async () => {
const targetWeb3Wallet = '0x0000000000000000000000000000000000000000';
const web3WalletJSON = {
object: 'web3_wallet',
web3_wallet: targetWeb3Wallet,
};

// @ts-ignore
BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON }));

const user = new User({
email_addresses: [],
phone_numbers: [],
web3_wallets: [],
external_accounts: [],
} as unknown as UserJSON);

await user.createWeb3Wallet({ web3Wallet: targetWeb3Wallet });

// @ts-ignore
expect(BaseResource._fetch).toHaveBeenCalledWith({
method: 'POST',
path: '/me/web3_wallets/',
body: {
web3_wallet: targetWeb3Wallet,
},
});
});
});
11 changes: 11 additions & 0 deletions packages/clerk-js/src/core/resources/User.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {
CreateEmailAddressParams,
CreatePhoneNumberParams,
CreateWeb3WalletParams,
EmailAddressResource,
ExternalAccountJSON,
ExternalAccountResource,
Expand Down Expand Up @@ -117,6 +118,16 @@ export class User extends BaseResource implements UserResource {
).create();
};

createWeb3Wallet = (params: CreateWeb3WalletParams): Promise<Web3WalletResource> => {
const { web3Wallet } = params || {};
return new Web3Wallet(
{
web3_wallet: web3Wallet,
},
this.path() + '/web3_wallets/',
).create();
};

createExternalAccount = async ({
strategy,
redirect_url,
Expand Down
94 changes: 94 additions & 0 deletions packages/clerk-js/src/core/resources/Web3Wallet.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Web3WalletJSON } from '@clerk/types';
import { BaseResource, Web3Wallet } from 'core/resources/internal';

describe('Web3 wallet', () => {
it('create', async () => {
const web3WalletJSON = {
object: 'web3_wallet',
web3_wallet: '0x0000000000000000000000000000000000000000',
} as Web3WalletJSON;

// @ts-ignore
BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON }));

const web3Wallet = new Web3Wallet(web3WalletJSON, '/me/web3_wallets');
await web3Wallet.create();

// @ts-ignore
expect(BaseResource._fetch).toHaveBeenCalledWith({
method: 'POST',
path: '/me/web3_wallets',
body: {
web3_wallet: web3WalletJSON.web3_wallet,
},
});
});

it('prepareVerification', async () => {
const web3WalletJSON = {
id: 'test-id',
object: 'web3_wallet',
web3_wallet: '0x0000000000000000000000000000000000000000',
} as Web3WalletJSON;

// @ts-ignore
BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON }));

const web3Wallet = new Web3Wallet(web3WalletJSON, '/me/web3_wallets');
await web3Wallet.prepareVerification({ strategy: 'web3_metamask_signature' });

// @ts-ignore
expect(BaseResource._fetch).toHaveBeenCalledWith({
method: 'POST',
path: `/me/web3_wallets/${web3WalletJSON.id}/prepare_verification`,
body: {
strategy: 'web3_metamask_signature',
},
});
});

it('attemptVerification', async () => {
const web3WalletJSON = {
id: 'test-id',
object: 'web3_wallet',
web3_wallet: '0x0000000000000000000000000000000000000000',
} as Web3WalletJSON;

// @ts-ignore
BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: web3WalletJSON }));

const web3Wallet = new Web3Wallet(web3WalletJSON, '/me/web3_wallets');
await web3Wallet.attemptVerification({ signature: 'mock-signature' });

// @ts-ignore
expect(BaseResource._fetch).toHaveBeenCalledWith({
method: 'POST',
path: `/me/web3_wallets/${web3WalletJSON.id}/attempt_verification`,
body: {
signature: 'mock-signature',
},
});
});

it('destroy', async () => {
const targetId = 'test_id';

const deletedObjectJSON = {
object: 'web3_wallet',
id: targetId,
deleted: true,
};

// @ts-ignore
BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: deletedObjectJSON }));

const web3Wallet = new Web3Wallet({ id: targetId }, '/me/web3_wallets');
await web3Wallet.destroy();

// @ts-ignore
expect(BaseResource._fetch).toHaveBeenCalledWith({
method: 'DELETE',
path: `/me/web3_wallets/${targetId}`,
});
});
});
61 changes: 60 additions & 1 deletion packages/clerk-js/src/core/resources/Web3Wallet.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import type { VerificationResource, Web3WalletJSON, Web3WalletResource } from '@clerk/types';
import type {
AttemptWeb3WalletVerificationParams,
PrepareWeb3WalletVerificationParams,
VerificationResource,
Web3WalletJSON,
Web3WalletResource,
} from '@clerk/types';
import { clerkMissingOptionError, clerkVerifyWeb3WalletCalledBeforeCreate } from 'core/errors';

import { BaseResource, Verification } from './internal';

Expand All @@ -7,12 +14,64 @@ export class Web3Wallet extends BaseResource implements Web3WalletResource {
web3Wallet = '';
verification!: VerificationResource;

public constructor(data: Partial<Web3WalletJSON>, pathRoot: string);
public constructor(data: Web3WalletJSON, pathRoot: string) {
super();
this.pathRoot = pathRoot;
this.fromJSON(data);
}

create(): Promise<this> {
return this._basePost({
body: { web3_wallet: this.web3Wallet },
});
}

prepareVerification = (params: PrepareWeb3WalletVerificationParams): Promise<this> => {
return this._basePost<Web3WalletJSON>({
action: 'prepare_verification',
body: { ...params },
});
};

attemptVerification = (params: AttemptWeb3WalletVerificationParams): Promise<this> => {
const { signature, generateSignature } = params || {};

if (signature) {
return this._basePost<Web3WalletJSON>({
action: 'attempt_verification',
body: { signature },
});
}

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

const generateSignatureForNonce = async (): Promise<this> => {
if (!(typeof generateSignature === 'function')) {
clerkMissingOptionError('generateSignature');
}

const { nonce } = this.verification;
if (!nonce) {
clerkVerifyWeb3WalletCalledBeforeCreate('SignUp');
}

const generatedSignature = await generateSignature({ identifier: this.web3Wallet, nonce });
return this._basePost<Web3WalletJSON>({
action: 'attempt_verification',
body: { signature: generatedSignature },
});
};

return generateSignatureForNonce();
};

destroy(): Promise<void> {
return this._baseDelete();
}

toString(): string {
return this.web3Wallet;
}
Expand Down
6 changes: 1 addition & 5 deletions packages/types/src/signUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
} from './strategies';
import type { SnakeToCamel } from './utils';
import { CreateMagicLinkFlowReturn, StartMagicLinkFlowParams, VerificationResource } from './verification';
import { AuthenticateWithWeb3Params, GenerateSignature } from './web3Wallet';
import { AttemptWeb3WalletVerificationParams, AuthenticateWithWeb3Params } from './web3Wallet';

export interface SignUpResource extends ClerkResource {
status: SignUpStatus | null;
Expand Down Expand Up @@ -110,10 +110,6 @@ export type AttemptVerificationParams =
signature: string;
};

export type AttemptWeb3WalletVerificationParams = {
generateSignature: GenerateSignature;
};

export type SignUpAttributeField =
| FirstNameAttribute
| LastNameAttribute
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface UserResource extends ClerkResource {
update: (params: UpdateUserParams) => Promise<UserResource>;
createEmailAddress: (params: CreateEmailAddressParams) => Promise<EmailAddressResource>;
createPhoneNumber: (params: CreatePhoneNumberParams) => Promise<PhoneNumberResource>;
createWeb3Wallet: (params: CreateWeb3WalletParams) => Promise<Web3WalletResource>;
twoFactorEnabled: () => boolean;
isPrimaryIdentification: (ident: EmailAddressResource | PhoneNumberResource) => boolean;
getSessions: () => Promise<SessionWithActivitiesResource[]>;
Expand All @@ -76,6 +77,7 @@ export interface UserResource extends ClerkResource {

export type CreateEmailAddressParams = { email: string };
export type CreatePhoneNumberParams = { phoneNumber: string };
export type CreateWeb3WalletParams = { web3Wallet: string };
export type SetProfileImageParams = { file: Blob | File };

type UpdateUserJSON = Pick<
Expand Down
15 changes: 15 additions & 0 deletions packages/types/src/web3Wallet.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
import { ClerkResource } from './resource';
import { Web3Strategy } from './strategies';
import { VerificationResource } from './verification';

export type PrepareWeb3WalletVerificationParams = {
strategy: Web3Strategy;
};

export type AttemptWeb3WalletVerificationParams = {
signature?: string;
/** @deprecated Use signature field instead */
generateSignature?: GenerateSignature;
};

export interface Web3WalletResource extends ClerkResource {
id: string;
web3Wallet: string;
verification: VerificationResource;
toString: () => string;
prepareVerification: (params: PrepareWeb3WalletVerificationParams) => Promise<Web3WalletResource>;
attemptVerification: (params: AttemptWeb3WalletVerificationParams) => Promise<Web3WalletResource>;
destroy: () => Promise<void>;
}

export type GenerateSignature = (opts: GenerateSignatureParams) => Promise<string>;
Expand Down

0 comments on commit a166716

Please sign in to comment.