@crossmint/client-sdk-smart-wallet 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/index.cjs +4 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +254 -119
  4. package/dist/index.d.ts +254 -119
  5. package/dist/index.js +4 -1
  6. package/dist/index.js.map +1 -1
  7. package/package.json +5 -13
  8. package/src/SmartWalletSDK.test.ts +12 -13
  9. package/src/SmartWalletSDK.ts +33 -26
  10. package/src/api/APIErrorService.ts +10 -7
  11. package/src/api/BaseCrossmintService.ts +3 -4
  12. package/src/api/CrossmintWalletService.test.ts +9 -9
  13. package/src/api/CrossmintWalletService.ts +39 -16
  14. package/src/blockchain/chains.ts +25 -2
  15. package/src/blockchain/transfer.ts +1 -1
  16. package/src/blockchain/wallets/EVMSmartWallet.ts +49 -42
  17. package/src/blockchain/wallets/account/config.ts +60 -0
  18. package/src/blockchain/wallets/account/creator.ts +36 -0
  19. package/src/blockchain/wallets/account/eoa.ts +50 -0
  20. package/src/blockchain/wallets/{passkey.ts → account/passkey.ts} +32 -32
  21. package/src/blockchain/wallets/account/signer.ts +44 -0
  22. package/src/blockchain/wallets/account/strategy.ts +5 -0
  23. package/src/blockchain/wallets/clientDecorator.ts +6 -6
  24. package/src/blockchain/wallets/paymaster.ts +12 -15
  25. package/src/blockchain/wallets/service.ts +38 -143
  26. package/src/error/index.ts +25 -117
  27. package/src/error/processor.ts +5 -6
  28. package/src/index.ts +16 -12
  29. package/src/services/logging/ConsoleProvider.ts +3 -12
  30. package/src/services/logging/DatadogProvider.ts +1 -1
  31. package/src/types/internal.ts +41 -20
  32. package/src/types/{Config.ts → params.ts} +0 -5
  33. package/src/types/schema.ts +63 -0
  34. package/src/types/service.ts +31 -0
  35. package/src/utils/api.ts +39 -0
  36. package/src/utils/constants.ts +2 -0
  37. package/src/utils/log.ts +1 -109
  38. package/src/utils/signer.ts +14 -16
  39. package/src/blockchain/wallets/eoa.ts +0 -49
  40. package/src/types/API.ts +0 -40
  41. package/src/utils/log.test.ts +0 -76
  42. /package/src/types/{Tokens.ts → token.ts} +0 -0
@@ -1,7 +1,3 @@
1
- import type { CrossmintWalletService } from "@/api/CrossmintWalletService";
2
- import { type PasskeySignerData, displayPasskey } from "@/types/API";
3
- import type { PasskeySigner, UserParams, WalletParams } from "@/types/Config";
4
- import type { AccountAndSigner, PasskeyValidatorSerializedData, WalletCreationParams } from "@/types/internal";
5
1
  import { PasskeyValidatorContractVersion, WebAuthnMode, toPasskeyValidator } from "@zerodev/passkey-validator";
6
2
  import { type KernelSmartAccount, type KernelValidator, createKernelAccount } from "@zerodev/sdk";
7
3
  import { type WebAuthnKey, toWebAuthnKey } from "@zerodev/webauthn-key";
@@ -13,40 +9,44 @@ import {
13
9
  PasskeyMismatchError,
14
10
  PasskeyPromptError,
15
11
  PasskeyRegistrationError,
16
- } from "../../error";
17
-
18
- export interface PasskeyWalletParams extends WalletCreationParams {
19
- walletParams: WalletParams & { signer: PasskeySigner };
20
- }
21
-
22
- export function isPasskeyParams(params: WalletCreationParams): params is PasskeyWalletParams {
23
- return (params.walletParams.signer as PasskeySigner).type === "PASSKEY";
24
- }
12
+ } from "../../../error";
13
+ import type { AccountAndSigner, PasskeyCreationParams } from "../../../types/internal";
14
+ import type { UserParams } from "../../../types/params";
15
+ import type { PasskeySignerData, PasskeyValidatorSerializedData } from "../../../types/service";
16
+ import { PasskeySignerConfig } from "./signer";
17
+ import { AccountCreationStrategy } from "./strategy";
25
18
 
26
19
  type PasskeyValidator = KernelValidator<EntryPoint, "WebAuthnValidator"> & {
27
20
  getSerializedData: () => string;
28
21
  };
29
- export class PasskeyAccountService {
30
- constructor(private readonly crossmintService: CrossmintWalletService) {}
31
-
32
- public async get(
33
- { user, publicClient, walletParams, entryPoint, kernelVersion }: PasskeyWalletParams,
34
- existingSignerConfig?: PasskeySignerData
35
- ): Promise<AccountAndSigner> {
22
+ export class PasskeyCreationStrategy implements AccountCreationStrategy {
23
+ constructor(private readonly passkeyServerUrl: string, private readonly apiKey: string) {}
24
+
25
+ public async create({
26
+ user,
27
+ publicClient,
28
+ walletParams,
29
+ entryPoint,
30
+ kernelVersion,
31
+ existingSignerConfig,
32
+ }: PasskeyCreationParams): Promise<AccountAndSigner> {
36
33
  const inputPasskeyName = walletParams.signer.passkeyName ?? user.id;
37
- if (existingSignerConfig != null && existingSignerConfig.passkeyName !== inputPasskeyName) {
34
+ if (existingSignerConfig != null && existingSignerConfig.data.passkeyName !== inputPasskeyName) {
38
35
  throw new PasskeyMismatchError(
39
- `User '${user.id}' has an existing wallet created with a passkey named '${existingSignerConfig.passkeyName}', this does match input passkey name '${inputPasskeyName}'.`,
40
- displayPasskey(existingSignerConfig)
36
+ `User '${user.id}' has an existing wallet created with a passkey named '${existingSignerConfig.data.passkeyName}', this does match input passkey name '${inputPasskeyName}'.`,
37
+ existingSignerConfig.display()
41
38
  );
42
39
  }
43
40
 
44
41
  try {
45
- const passkey = await this.getPasskey(user, inputPasskeyName, existingSignerConfig);
42
+ const passkey = await this.getPasskey(user, inputPasskeyName, existingSignerConfig?.data);
46
43
 
47
44
  const latestValidatorVersion = PasskeyValidatorContractVersion.V0_0_2;
48
45
  const validatorContractVersion =
49
- existingSignerConfig == null ? latestValidatorVersion : existingSignerConfig.validatorContractVersion;
46
+ existingSignerConfig == null
47
+ ? latestValidatorVersion
48
+ : existingSignerConfig.data.validatorContractVersion;
49
+
50
50
  const validator = await toPasskeyValidator(publicClient, {
51
51
  webAuthnKey: passkey,
52
52
  entryPoint: entryPoint.address,
@@ -61,7 +61,7 @@ export class PasskeyAccountService {
61
61
  });
62
62
 
63
63
  return {
64
- signerData: this.getSignerData(validator, validatorContractVersion, inputPasskeyName),
64
+ signerConfig: this.getSignerConfig(validator, validatorContractVersion, inputPasskeyName),
65
65
  account: this.decorate(kernelAccount, inputPasskeyName),
66
66
  };
67
67
  } catch (error) {
@@ -85,29 +85,29 @@ export class PasskeyAccountService {
85
85
 
86
86
  return toWebAuthnKey({
87
87
  passkeyName,
88
- passkeyServerUrl: this.crossmintService.getPasskeyServerUrl(),
88
+ passkeyServerUrl: this.passkeyServerUrl,
89
89
  mode: WebAuthnMode.Register,
90
90
  passkeyServerHeaders: this.createPasskeysServerHeaders(user),
91
91
  });
92
92
  }
93
93
 
94
- private getSignerData(
94
+ private getSignerConfig(
95
95
  validator: PasskeyValidator,
96
96
  validatorContractVersion: PasskeyValidatorContractVersion,
97
97
  passkeyName: string
98
- ): PasskeySignerData {
99
- return {
98
+ ): PasskeySignerConfig {
99
+ return new PasskeySignerConfig({
100
100
  ...deserializePasskeyValidatorData(validator.getSerializedData()),
101
101
  passkeyName,
102
102
  validatorContractVersion,
103
103
  domain: window.location.hostname,
104
104
  type: "passkeys",
105
- };
105
+ });
106
106
  }
107
107
 
108
108
  private createPasskeysServerHeaders(user: UserParams) {
109
109
  return {
110
- "x-api-key": this.crossmintService.crossmintAPIHeaders["x-api-key"],
110
+ "x-api-key": this.apiKey,
111
111
  Authorization: `Bearer ${user.jwt}`,
112
112
  };
113
113
  }
@@ -0,0 +1,44 @@
1
+ import type {
2
+ EOASignerData,
3
+ PasskeyDisplay,
4
+ PasskeySignerData,
5
+ SignerData,
6
+ SignerDisplay,
7
+ } from "../../../types/service";
8
+
9
+ export interface SignerConfig {
10
+ readonly type: "passkeys" | "eoa";
11
+ display(): SignerDisplay;
12
+ readonly data: SignerData;
13
+ }
14
+
15
+ export class PasskeySignerConfig implements SignerConfig {
16
+ public readonly data: PasskeySignerData;
17
+ public readonly type = "passkeys";
18
+
19
+ constructor(data: PasskeySignerData) {
20
+ this.data = data;
21
+ }
22
+
23
+ public display(): PasskeyDisplay {
24
+ return {
25
+ pubKeyX: this.data.pubKeyX,
26
+ pubKeyY: this.data.pubKeyY,
27
+ passkeyName: this.data.passkeyName,
28
+ type: this.type,
29
+ };
30
+ }
31
+ }
32
+
33
+ export class EOASignerConfig implements SignerConfig {
34
+ public readonly data: EOASignerData;
35
+ public readonly type = "eoa";
36
+
37
+ constructor(data: EOASignerData) {
38
+ this.data = data;
39
+ }
40
+
41
+ public display() {
42
+ return this.data;
43
+ }
44
+ }
@@ -0,0 +1,5 @@
1
+ import { AccountAndSigner, WalletCreationParams } from "../../../types/internal";
2
+
3
+ export interface AccountCreationStrategy {
4
+ create(params: WalletCreationParams): Promise<AccountAndSigner>;
5
+ }
@@ -1,12 +1,12 @@
1
- import { SmartWalletSDKError } from "@/error";
2
- import { ErrorProcessor } from "@/error/processor";
3
- import { logInfo } from "@/services/logging";
4
- import { usesGelatoBundler } from "@/utils/blockchain";
5
- import { logPerformance } from "@/utils/log";
6
1
  import type { SmartAccountClient } from "permissionless";
7
2
  import type { EntryPoint } from "permissionless/types/entrypoint";
8
3
  import { stringify } from "viem";
9
4
 
5
+ import { SmartWalletError } from "../../error";
6
+ import { ErrorProcessor } from "../../error/processor";
7
+ import { logInfo } from "../../services/logging";
8
+ import { usesGelatoBundler } from "../../utils/blockchain";
9
+ import { logPerformance } from "../../utils/log";
10
10
  import { SmartWalletChain } from "../chains";
11
11
 
12
12
  const transactionMethods = [
@@ -83,7 +83,7 @@ export class ClientDecorator {
83
83
  const description = isTxnMethod(prop) ? "signing" : "sending transaction";
84
84
  throw this.errorProcessor.map(
85
85
  error,
86
- new SmartWalletSDKError(`Error ${description}: ${error.message}`, stringify(error))
86
+ new SmartWalletError(`Error ${description}: ${error.message}`, stringify(error))
87
87
  );
88
88
  }
89
89
  }
@@ -1,39 +1,36 @@
1
- import { PAYMASTER_RPC } from "@/utils/constants";
2
- import { createZeroDevPaymasterClient } from "@zerodev/sdk";
1
+ import { CrossmintWalletService } from "@/api/CrossmintWalletService";
3
2
  import { Middleware } from "permissionless/actions/smartAccount";
4
3
  import { EntryPoint } from "permissionless/types/entrypoint";
5
- import { http } from "viem";
6
4
 
5
+ import { UserParams } from "../../types/params";
7
6
  import { usesGelatoBundler } from "../../utils/blockchain";
8
- import { SmartWalletChain, viemNetworks, zerodevProjects } from "../chains";
7
+ import { SmartWalletChain } from "../chains";
9
8
 
10
9
  export function usePaymaster(chain: SmartWalletChain) {
11
10
  return !usesGelatoBundler(chain);
12
11
  }
13
12
 
14
- const getPaymasterRPC = (chain: SmartWalletChain) => {
15
- return PAYMASTER_RPC + zerodevProjects[chain];
16
- };
17
-
18
13
  export function paymasterMiddleware({
19
14
  entryPoint,
20
15
  chain,
16
+ walletService,
17
+ user,
21
18
  }: {
22
19
  entryPoint: EntryPoint;
23
20
  chain: SmartWalletChain;
21
+ walletService: CrossmintWalletService;
22
+ user: UserParams;
24
23
  }): Middleware<EntryPoint> {
25
24
  return {
26
25
  middleware: {
27
26
  sponsorUserOperation: async ({ userOperation }) => {
28
- const paymasterClient = createZeroDevPaymasterClient({
29
- chain: viemNetworks[chain],
30
- transport: http(getPaymasterRPC(chain)),
31
- entryPoint,
32
- });
33
- return paymasterClient.sponsorUserOperation({
27
+ const { sponsorUserOpParams } = await walletService.sponsorUserOperation(
28
+ user,
34
29
  userOperation,
35
30
  entryPoint,
36
- });
31
+ chain
32
+ );
33
+ return sponsorUserOpParams;
37
34
  },
38
35
  },
39
36
  };
@@ -1,45 +1,28 @@
1
- import { type SignerData, displayPasskey } from "@/types/API";
2
- import { equalsIgnoreCase } from "@/utils/helpers";
3
- import { type KernelSmartAccount, createKernelAccountClient } from "@zerodev/sdk";
1
+ import { createKernelAccountClient } from "@zerodev/sdk";
4
2
  import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "permissionless";
5
- import type { EntryPoint } from "permissionless/types/entrypoint";
6
- import { Address, type HttpTransport, createPublicClient, getAddress, http } from "viem";
3
+ import { createPublicClient, http } from "viem";
7
4
 
8
5
  import { blockchainToChainId } from "@crossmint/common-sdk-base";
9
6
 
10
7
  import type { CrossmintWalletService } from "../../api/CrossmintWalletService";
11
- import {
12
- AdminMismatchError,
13
- CrossmintServiceError,
14
- SmartWalletSDKError,
15
- UserWalletAlreadyCreatedError,
16
- } from "../../error";
17
- import type { EntryPointDetails, UserParams, WalletParams } from "../../types/Config";
18
- import {
19
- SUPPORTED_ENTRYPOINT_VERSIONS,
20
- SUPPORTED_KERNEL_VERSIONS,
21
- SmartWalletClient,
22
- type SupportedKernelVersion,
23
- type WalletCreationParams,
24
- isSupportedEntryPointVersion,
25
- isSupportedKernelVersion,
26
- } from "../../types/internal";
8
+ import { UserWalletAlreadyCreatedError } from "../../error";
9
+ import type { SmartWalletClient } from "../../types/internal";
10
+ import type { UserParams, WalletParams } from "../../types/params";
27
11
  import { CURRENT_VERSION, ZERO_DEV_TYPE } from "../../utils/constants";
28
- import { SmartWalletChain, getBundlerRPC, viemNetworks } from "../chains";
12
+ import { equalsIgnoreCase } from "../../utils/helpers";
13
+ import { type SmartWalletChain, getBundlerRPC, viemNetworks } from "../chains";
29
14
  import { EVMSmartWallet } from "./EVMSmartWallet";
30
- import { ClientDecorator } from "./clientDecorator";
31
- import { EOAAccountService, type EOAWalletParams } from "./eoa";
32
- import { PasskeyAccountService, isPasskeyParams } from "./passkey";
15
+ import type { AccountConfigFacade } from "./account/config";
16
+ import type { AccountCreator } from "./account/creator";
17
+ import type { ClientDecorator } from "./clientDecorator";
33
18
  import { paymasterMiddleware, usePaymaster } from "./paymaster";
34
19
 
35
20
  export class SmartWalletService {
36
21
  constructor(
37
- private readonly crossmintWalletService: CrossmintWalletService,
38
- private readonly clientDecorator: ClientDecorator,
39
- private readonly accountFactory = new AccountFactory(
40
- new EOAAccountService(),
41
- new PasskeyAccountService(crossmintWalletService)
42
- )
22
+ private readonly crossmintService: CrossmintWalletService,
23
+ private readonly accountConfigFacade: AccountConfigFacade,
24
+ private readonly accountCreator: AccountCreator,
25
+ private readonly clientDecorator: ClientDecorator
43
26
  ) {}
44
27
 
45
28
  public async getOrCreate(
@@ -47,35 +30,36 @@ export class SmartWalletService {
47
30
  chain: SmartWalletChain,
48
31
  walletParams: WalletParams
49
32
  ): Promise<EVMSmartWallet> {
50
- const { entryPoint, kernelVersion, existingSignerConfig, smartContractWalletAddress, userId } =
51
- await this.fetchConfig(user, chain);
33
+ const { entryPointVersion, kernelVersion, existingSignerConfig, smartContractWalletAddress, userId } =
34
+ await this.accountConfigFacade.get(user, chain);
52
35
  const publicClient = createPublicClient({ transport: http(getBundlerRPC(chain)) });
53
36
 
54
- const { account, signerData } = await this.accountFactory.get(
55
- {
56
- chain,
57
- walletParams,
58
- publicClient,
59
- user: { ...user, id: userId },
60
- entryPoint,
61
- kernelVersion,
37
+ const { account, signerConfig } = await this.accountCreator.get({
38
+ chain,
39
+ walletParams,
40
+ publicClient,
41
+ user: { ...user, id: userId },
42
+ entryPoint: {
43
+ version: entryPointVersion,
44
+ address: entryPointVersion === "v0.6" ? ENTRYPOINT_ADDRESS_V06 : ENTRYPOINT_ADDRESS_V07,
62
45
  },
63
- existingSignerConfig
64
- );
46
+ kernelVersion,
47
+ existingSignerConfig,
48
+ });
65
49
 
66
50
  if (smartContractWalletAddress != null && !equalsIgnoreCase(smartContractWalletAddress, account.address)) {
67
51
  throw new UserWalletAlreadyCreatedError(userId);
68
52
  }
69
53
 
70
54
  if (existingSignerConfig == null) {
71
- await this.crossmintWalletService.idempotentCreateSmartWallet(user, {
55
+ await this.crossmintService.idempotentCreateSmartWallet(user, {
72
56
  type: ZERO_DEV_TYPE,
73
57
  smartContractWalletAddress: account.address,
74
- signerData: signerData,
58
+ signerData: signerConfig.data,
75
59
  version: CURRENT_VERSION,
76
60
  baseLayer: "evm",
77
61
  chainId: blockchainToChainId(chain),
78
- entryPointVersion: entryPoint.version,
62
+ entryPointVersion,
79
63
  kernelVersion,
80
64
  });
81
65
  }
@@ -85,7 +69,13 @@ export class SmartWalletService {
85
69
  chain: viemNetworks[chain],
86
70
  entryPoint: account.entryPoint,
87
71
  bundlerTransport: http(getBundlerRPC(chain)),
88
- ...(usePaymaster(chain) && paymasterMiddleware({ entryPoint: account.entryPoint, chain })),
72
+ ...(usePaymaster(chain) &&
73
+ paymasterMiddleware({
74
+ entryPoint: account.entryPoint,
75
+ chain,
76
+ walletService: this.crossmintService,
77
+ user,
78
+ })),
89
79
  });
90
80
 
91
81
  const smartAccountClient = this.clientDecorator.decorate({
@@ -93,101 +83,6 @@ export class SmartWalletService {
93
83
  smartAccountClient: kernelAccountClient,
94
84
  });
95
85
 
96
- return new EVMSmartWallet(this.crossmintWalletService, smartAccountClient, publicClient, chain);
97
- }
98
-
99
- private async fetchConfig(
100
- user: UserParams,
101
- chain: SmartWalletChain
102
- ): Promise<{
103
- entryPoint: EntryPointDetails;
104
- kernelVersion: SupportedKernelVersion;
105
- userId: string;
106
- existingSignerConfig?: SignerData;
107
- smartContractWalletAddress?: Address;
108
- }> {
109
- const { entryPointVersion, kernelVersion, signers, smartContractWalletAddress, userId } =
110
- await this.crossmintWalletService.getSmartWalletConfig(user, chain);
111
-
112
- if (!isSupportedKernelVersion(kernelVersion)) {
113
- throw new SmartWalletSDKError(
114
- `Unsupported kernel version. Supported versions: ${SUPPORTED_KERNEL_VERSIONS.join(
115
- ", "
116
- )}. Version used: ${kernelVersion}, Please contact support`
117
- );
118
- }
119
-
120
- if (!isSupportedEntryPointVersion(entryPointVersion)) {
121
- throw new SmartWalletSDKError(
122
- `Unsupported entry point version. Supported versions: ${SUPPORTED_ENTRYPOINT_VERSIONS.join(
123
- ", "
124
- )}. Version used: ${entryPointVersion}. Please contact support`
125
- );
126
- }
127
-
128
- if (
129
- (entryPointVersion === "v0.7" && kernelVersion.startsWith("0.2")) ||
130
- (entryPointVersion === "v0.6" && kernelVersion.startsWith("0.3"))
131
- ) {
132
- throw new SmartWalletSDKError(
133
- `Unsupported combination: entryPoint ${entryPointVersion} and kernel version ${kernelVersion}. Please contact support`
134
- );
135
- }
136
-
137
- return {
138
- entryPoint: {
139
- version: entryPointVersion,
140
- address: entryPointVersion === "v0.6" ? ENTRYPOINT_ADDRESS_V06 : ENTRYPOINT_ADDRESS_V07,
141
- },
142
- kernelVersion,
143
- userId,
144
- existingSignerConfig: this.getSigner(signers),
145
- smartContractWalletAddress:
146
- smartContractWalletAddress != null ? getAddress(smartContractWalletAddress) : undefined,
147
- };
148
- }
149
-
150
- private getSigner(signers: any[]): SignerData | undefined {
151
- if (signers.length === 0) {
152
- return undefined;
153
- }
154
-
155
- if (signers.length > 1) {
156
- throw new CrossmintServiceError("Invalid wallet signer configuration. Please contact support");
157
- }
158
-
159
- return signers[0].signerData;
160
- }
161
- }
162
-
163
- class AccountFactory {
164
- constructor(private readonly eoa: EOAAccountService, private readonly passkey: PasskeyAccountService) {}
165
-
166
- public get(
167
- params: WalletCreationParams,
168
- existingSignerConfig?: SignerData
169
- ): Promise<{
170
- signerData: SignerData;
171
- account: KernelSmartAccount<EntryPoint, HttpTransport>;
172
- }> {
173
- if (isPasskeyParams(params)) {
174
- if (existingSignerConfig != null && existingSignerConfig?.type !== "passkeys") {
175
- throw new AdminMismatchError(
176
- `Cannot create wallet with passkey signer for user '${params.user.id}', they have an existing wallet with eoa signer '${existingSignerConfig.eoaAddress}.'`,
177
- existingSignerConfig
178
- );
179
- }
180
-
181
- return this.passkey.get(params, existingSignerConfig);
182
- }
183
-
184
- if (existingSignerConfig != null && existingSignerConfig?.type !== "eoa") {
185
- throw new AdminMismatchError(
186
- `Cannot create wallet with eoa signer for user '${params.user.id}', they already have a wallet with a passkey named '${existingSignerConfig.passkeyName}' as it's signer.`,
187
- displayPasskey(existingSignerConfig)
188
- );
189
- }
190
-
191
- return this.eoa.get(params as EOAWalletParams, existingSignerConfig);
86
+ return new EVMSmartWallet(this.crossmintService, smartAccountClient, publicClient, chain);
192
87
  }
193
88
  }