@account-kit/signer 4.0.0-alpha.0 → 4.0.0-alpha.10

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 (45) hide show
  1. package/dist/cjs/base.js.map +1 -1
  2. package/dist/cjs/client/base.d.ts +1 -1
  3. package/dist/cjs/client/base.js.map +1 -1
  4. package/dist/cjs/client/index.js.map +1 -1
  5. package/dist/cjs/index.d.ts +5 -4
  6. package/dist/cjs/index.js +9 -7
  7. package/dist/cjs/index.js.map +1 -1
  8. package/dist/cjs/signer.d.ts +1 -1
  9. package/dist/cjs/signer.js +5 -5
  10. package/dist/cjs/signer.js.map +1 -1
  11. package/dist/cjs/version.d.ts +1 -1
  12. package/dist/cjs/version.js +1 -1
  13. package/dist/cjs/version.js.map +1 -1
  14. package/dist/esm/base.js.map +1 -1
  15. package/dist/esm/client/base.d.ts +1 -1
  16. package/dist/esm/client/base.js.map +1 -1
  17. package/dist/esm/client/index.js.map +1 -1
  18. package/dist/esm/index.d.ts +5 -4
  19. package/dist/esm/index.js +3 -2
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/esm/signer.d.ts +1 -1
  22. package/dist/esm/signer.js +5 -5
  23. package/dist/esm/signer.js.map +1 -1
  24. package/dist/esm/version.d.ts +1 -1
  25. package/dist/esm/version.js +1 -1
  26. package/dist/esm/version.js.map +1 -1
  27. package/dist/types/base.d.ts +248 -14
  28. package/dist/types/base.d.ts.map +1 -1
  29. package/dist/types/client/base.d.ts +60 -6
  30. package/dist/types/client/base.d.ts.map +1 -1
  31. package/dist/types/client/index.d.ts +158 -0
  32. package/dist/types/client/index.d.ts.map +1 -1
  33. package/dist/types/index.d.ts +5 -4
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/types/signer.d.ts +22 -1
  36. package/dist/types/signer.d.ts.map +1 -1
  37. package/dist/types/version.d.ts +1 -1
  38. package/dist/types/version.d.ts.map +1 -1
  39. package/package.json +5 -4
  40. package/src/base.ts +250 -16
  41. package/src/client/base.ts +61 -6
  42. package/src/client/index.ts +159 -1
  43. package/src/index.ts +5 -6
  44. package/src/signer.ts +27 -6
  45. package/src/version.ts +1 -1
package/src/base.ts CHANGED
@@ -44,6 +44,10 @@ type InternalStore = Mutate<
44
44
  [["zustand/subscribeWithSelector", never]]
45
45
  >;
46
46
 
47
+ /**
48
+ * Base abstract class for Alchemy Signer, providing authentication and session management for smart accounts.
49
+ * Implements the `SmartAccountAuthenticator` interface and handles various signer events.
50
+ */
47
51
  export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
48
52
  implements SmartAccountAuthenticator<AuthParams, User, TClient>
49
53
  {
@@ -52,6 +56,15 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
52
56
  private sessionManager: SessionManager;
53
57
  private store: InternalStore;
54
58
 
59
+ /**
60
+ * Initializes an instance with the provided client and session configuration.
61
+ * This function sets up the internal store, initializes the session manager,
62
+ * registers listeners and initializes the session manager to manage session state.
63
+ *
64
+ * @param {BaseAlchemySignerParams<TClient>} param0 Object containing the client and session configuration
65
+ * @param {TClient} param0.client The client instance to be used internally
66
+ * @param {SessionConfig} param0.sessionConfig Configuration for managing sessions
67
+ */
55
68
  constructor({ client, sessionConfig }: BaseAlchemySignerParams<TClient>) {
56
69
  this.inner = client;
57
70
  this.store = createStore(
@@ -88,9 +101,9 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
88
101
  /**
89
102
  * Allows you to subscribe to events emitted by the signer
90
103
  *
91
- * @param event the event to subscribe to
92
- * @param listener the function to run when the event is emitted
93
- * @returns a function to remove the listener
104
+ * @param {AlchemySignerEvent} event the event to subscribe to
105
+ * @param {AlchemySignerEvents[AlchemySignerEvent]} listener the function to run when the event is emitted
106
+ * @returns {() => void} a function to remove the listener
94
107
  */
95
108
  on = <E extends AlchemySignerEvent>(
96
109
  event: E,
@@ -132,8 +145,29 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
132
145
  /**
133
146
  * Authenticate a user with either an email or a passkey and create a session for that user
134
147
  *
135
- * @param params - undefined if passkey login, otherwise an object with email and bundle to resolve
136
- * @returns the user that was authenticated
148
+ * @example
149
+ * ```ts
150
+ * import { AlchemyWebSigner } from "@account-kit/signer";
151
+ *
152
+ * const signer = new AlchemyWebSigner({
153
+ * client: {
154
+ * connection: {
155
+ * rpcUrl: "/api/rpc",
156
+ * },
157
+ * iframeConfig: {
158
+ * iframeContainerId: "alchemy-signer-iframe-container",
159
+ * },
160
+ * },
161
+ * });
162
+ *
163
+ * const result = await signer.authenticate({
164
+ * type: "email",
165
+ * email: "foo@mail.com",
166
+ * });
167
+ * ```
168
+ *
169
+ * @param {AuthParams} params - undefined if passkey login, otherwise an object with email and bundle to resolve
170
+ * @returns {Promise<User>} the user that was authenticated
137
171
  */
138
172
  authenticate: (params: AuthParams) => Promise<User> = async (params) => {
139
173
  if (params.type === "email") {
@@ -144,7 +178,27 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
144
178
  };
145
179
 
146
180
  /**
147
- * NOTE: right now this only clears the session locally.
181
+ * Clear a user session and log them out
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * import { AlchemyWebSigner } from "@account-kit/signer";
186
+ *
187
+ * const signer = new AlchemyWebSigner({
188
+ * client: {
189
+ * connection: {
190
+ * rpcUrl: "/api/rpc",
191
+ * },
192
+ * iframeConfig: {
193
+ * iframeContainerId: "alchemy-signer-iframe-container",
194
+ * },
195
+ * },
196
+ * });
197
+ *
198
+ * await signer.disconnect();
199
+ * ```
200
+ *
201
+ * @returns {Promise<void>} a promise that resolves when the user is logged out
148
202
  */
149
203
  disconnect: () => Promise<void> = async () => {
150
204
  await this.inner.disconnect();
@@ -155,10 +209,29 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
155
209
  * If a user has an ongoing session, it will use that session and
156
210
  * try to authenticate
157
211
  *
212
+ * @example
213
+ * ```ts
214
+ * import { AlchemyWebSigner } from "@account-kit/signer";
215
+ *
216
+ * const signer = new AlchemyWebSigner({
217
+ * client: {
218
+ * connection: {
219
+ * rpcUrl: "/api/rpc",
220
+ * },
221
+ * iframeConfig: {
222
+ * iframeContainerId: "alchemy-signer-iframe-container",
223
+ * },
224
+ * },
225
+ * });
226
+ *
227
+ * // throws if not logged in
228
+ * const user = await signer.getAuthDetails();
229
+ * ```
230
+ *
158
231
  * @throws if there is no user logged in
159
- * @returns the current user
232
+ * @returns {Promise<User>} the current user
160
233
  */
161
- getAuthDetails: () => Promise<User> = async () => {
234
+ getAuthDetails = async (): Promise<User> => {
162
235
  const sessionUser = await this.sessionManager.getSessionUser();
163
236
  if (sessionUser != null) {
164
237
  return sessionUser;
@@ -167,12 +240,41 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
167
240
  return this.inner.whoami();
168
241
  };
169
242
 
243
+ /**
244
+ * Retrieves the address of the current user by calling the `whoami` method on `this.inner`.
245
+ *
246
+ * @returns {Promise<string>} A promise that resolves to the address of the current user.
247
+ */
170
248
  getAddress: () => Promise<`0x${string}`> = async () => {
171
249
  const { address } = await this.inner.whoami();
172
250
 
173
251
  return address;
174
252
  };
175
253
 
254
+ /**
255
+ * Signs a raw message after hashing it.
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * import { AlchemyWebSigner } from "@account-kit/signer";
260
+ *
261
+ * const signer = new AlchemyWebSigner({
262
+ * client: {
263
+ * connection: {
264
+ * rpcUrl: "/api/rpc",
265
+ * },
266
+ * iframeConfig: {
267
+ * iframeContainerId: "alchemy-signer-iframe-container",
268
+ * },
269
+ * },
270
+ * });
271
+ *
272
+ * const signature = await signer.signMessage("Hello, world!");
273
+ * ```
274
+ *
275
+ * @param {string} msg the message to be hashed and then signed
276
+ * @returns {Promise<string>} a promise that resolves to the signed message
277
+ */
176
278
  signMessage: (msg: SignableMessage) => Promise<`0x${string}`> = async (
177
279
  msg
178
280
  ) => {
@@ -181,6 +283,35 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
181
283
  return this.inner.signRawMessage(messageHash);
182
284
  };
183
285
 
286
+ /**
287
+ * Signs a typed message by first hashing it and then signing the hashed message using the `signRawMessage` method.
288
+ *
289
+ * @example
290
+ * ```ts
291
+ * import { AlchemyWebSigner } from "@account-kit/signer";
292
+ *
293
+ * const signer = new AlchemyWebSigner({
294
+ * client: {
295
+ * connection: {
296
+ * rpcUrl: "/api/rpc",
297
+ * },
298
+ * iframeConfig: {
299
+ * iframeContainerId: "alchemy-signer-iframe-container",
300
+ * },
301
+ * },
302
+ * });
303
+ *
304
+ * const signature = await signer.signTypedData({
305
+ * domain: {},
306
+ * types: {},
307
+ * primaryType: "",
308
+ * message: {},
309
+ * });
310
+ * ```
311
+ *
312
+ * @param {TypedDataDefinition<TTypedData, TPrimaryType>} params The parameters for the typed message to be hashed and signed
313
+ * @returns {Promise<any>} A promise that resolves to the signed message
314
+ */
184
315
  signTypedData: <
185
316
  const TTypedData extends TypedData | { [key: string]: unknown },
186
317
  TPrimaryType extends keyof TTypedData | "EIP712Domain" = keyof TTypedData
@@ -192,6 +323,36 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
192
323
  return this.inner.signRawMessage(messageHash);
193
324
  };
194
325
 
326
+ /**
327
+ * Serializes a transaction, signs it with a raw message, and then returns the serialized transaction with the signature.
328
+ *
329
+ * @example
330
+ * ```ts
331
+ * import { AlchemyWebSigner } from "@account-kit/signer";
332
+ *
333
+ * const signer = new AlchemyWebSigner({
334
+ * client: {
335
+ * connection: {
336
+ * rpcUrl: "/api/rpc",
337
+ * },
338
+ * iframeConfig: {
339
+ * iframeContainerId: "alchemy-signer-iframe-container",
340
+ * },
341
+ * },
342
+ * });
343
+ *
344
+ * const tx = await signer.signTransaction({
345
+ * to: "0x1234",
346
+ * value: "0x1234",
347
+ * data: "0x1234",
348
+ * });
349
+ * ```
350
+ *
351
+ * @param {Transaction} tx the transaction to be serialized and signed
352
+ * @param {{serializer?: SerializeTransactionFn}} args options for serialization
353
+ * @param {() => Hex} [args.serializer] an optional serializer function. If not provided, the default `serializeTransaction` function will be used
354
+ * @returns {Promise<string>} a promise that resolves to the serialized transaction with the signature
355
+ */
195
356
  signTransaction: CustomSource["signTransaction"] = async (tx, args) => {
196
357
  const serializeFn = args?.serializer ?? serializeTransaction;
197
358
  const serializedTx = serializeFn(tx);
@@ -211,8 +372,26 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
211
372
  /**
212
373
  * Unauthenticated call to look up a user's organizationId by email
213
374
  *
214
- * @param email the email to lookup
215
- * @returns the organization id for the user if they exist
375
+ * @example
376
+ * ```ts
377
+ * import { AlchemyWebSigner } from "@account-kit/signer";
378
+ *
379
+ * const signer = new AlchemyWebSigner({
380
+ * client: {
381
+ * connection: {
382
+ * rpcUrl: "/api/rpc",
383
+ * },
384
+ * iframeConfig: {
385
+ * iframeContainerId: "alchemy-signer-iframe-container",
386
+ * },
387
+ * },
388
+ * });
389
+ *
390
+ * const result = await signer.getUser("foo@mail.com");
391
+ * ```
392
+ *
393
+ * @param {string} email the email to lookup
394
+ * @returns {Promise<{orgId: string}>} the organization id for the user if they exist
216
395
  */
217
396
  getUser: (email: string) => Promise<{ orgId: string } | null> = async (
218
397
  email
@@ -231,8 +410,26 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
231
410
  /**
232
411
  * Adds a passkey to the user's account
233
412
  *
234
- * @param params optional parameters for the passkey creation
235
- * @returns an array of the authenticator ids added to the user
413
+ * @example
414
+ * ```ts
415
+ * import { AlchemyWebSigner } from "@account-kit/signer";
416
+ *
417
+ * const signer = new AlchemyWebSigner({
418
+ * client: {
419
+ * connection: {
420
+ * rpcUrl: "/api/rpc",
421
+ * },
422
+ * iframeConfig: {
423
+ * iframeContainerId: "alchemy-signer-iframe-container",
424
+ * },
425
+ * },
426
+ * });
427
+ *
428
+ * const result = await signer.addPasskey()
429
+ * ```
430
+ *
431
+ * @param {CredentialCreationOptions | undefined} params optional parameters for the passkey creation
432
+ * @returns {Promise<string[]>} an array of the authenticator ids added to the user
236
433
  */
237
434
  addPasskey: (params?: CredentialCreationOptions) => Promise<string[]> =
238
435
  async (params) => {
@@ -244,8 +441,27 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
244
441
  * If the user is authenticated with an Email, this will return a seed phrase
245
442
  * If the user is authenticated with a Passkey, this will return a private key
246
443
  *
247
- * @param params export wallet parameters
248
- * @returns true if the wallet was exported successfully
444
+ * @example
445
+ * ```ts
446
+ * import { AlchemyWebSigner } from "@account-kit/signer";
447
+ *
448
+ * const signer = new AlchemyWebSigner({
449
+ * client: {
450
+ * connection: {
451
+ * rpcUrl: "/api/rpc",
452
+ * },
453
+ * iframeConfig: {
454
+ * iframeContainerId: "alchemy-signer-iframe-container",
455
+ * },
456
+ * },
457
+ * });
458
+ *
459
+ * // the params passed to this are different based on the specific signer
460
+ * const result = signer.exportWallet()
461
+ * ```
462
+ *
463
+ * @param {unknown} params export wallet parameters
464
+ * @returns {boolean} true if the wallet was exported successfully
249
465
  */
250
466
  exportWallet: (
251
467
  params: Parameters<(typeof this.inner)["exportWallet"]>[0]
@@ -257,10 +473,28 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
257
473
  * This method lets you adapt your AlchemySigner to a viem LocalAccount, which
258
474
  * will let you use the signer as an EOA directly.
259
475
  *
476
+ * @example
477
+ * ```ts
478
+ * import { AlchemyWebSigner } from "@account-kit/signer";
479
+ *
480
+ * const signer = new AlchemyWebSigner({
481
+ * client: {
482
+ * connection: {
483
+ * rpcUrl: "/api/rpc",
484
+ * },
485
+ * iframeConfig: {
486
+ * iframeContainerId: "alchemy-signer-iframe-container",
487
+ * },
488
+ * },
489
+ * });
490
+ *
491
+ * const account = signer.toViemAccount();
492
+ * ```
493
+ *
260
494
  * @throws if your signer is not authenticated
261
- * @returns a LocalAccount object that can be used with viem's wallet client
495
+ * @returns {LocalAccount} a LocalAccount object that can be used with viem's wallet client
262
496
  */
263
- toViemAccount: () => LocalAccount = () => {
497
+ toViemAccount = (): LocalAccount => {
264
498
  // if we want this method to be synchronous, then we need to do this check here
265
499
  // otherwise we can use the sessionManager to get the user
266
500
  if (!this.inner.getUser()) {
@@ -30,6 +30,9 @@ export type ExportWalletStamper = TurnkeyClient["stamper"] & {
30
30
  publicKey(): string | null;
31
31
  };
32
32
 
33
+ /**
34
+ * Base class for all Alchemy Signer clients
35
+ */
33
36
  export abstract class BaseSignerClient<TExportWalletParams = unknown> {
34
37
  private _user: User | undefined;
35
38
  private connectionConfig: ConnectionConfig;
@@ -37,6 +40,11 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
37
40
  protected rootOrg: string;
38
41
  protected eventEmitter: EventEmitter<AlchemySignerClientEvents>;
39
42
 
43
+ /**
44
+ * Create a new instance of the Alchemy Signer client
45
+ *
46
+ * @param {BaseSignerClientParams} params the parameters required to create the client
47
+ */
40
48
  constructor(params: BaseSignerClientParams) {
41
49
  const { stamper, connection, rootOrgId } = params;
42
50
 
@@ -63,10 +71,23 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
63
71
  this._user = user;
64
72
  }
65
73
 
74
+ /**
75
+ * Sets the stamper of the TurnkeyClient.
76
+ *
77
+ * @param {TurnkeyClient["stamper"]} stamper the stamper function to set for the TurnkeyClient
78
+ */
66
79
  protected setStamper(stamper: TurnkeyClient["stamper"]) {
67
80
  this.turnkeyClient.stamper = stamper;
68
81
  }
69
82
 
83
+ /**
84
+ * Exports wallet credentials based on the specified type, either as a SEED_PHRASE or PRIVATE_KEY.
85
+ *
86
+ * @param {object} params The parameters for exporting the wallet
87
+ * @param {ExportWalletStamper} params.exportStamper The stamper used for exporting the wallet
88
+ * @param {"SEED_PHRASE" | "PRIVATE_KEY"} params.exportAs Specifies the format for exporting the wallet, either as a SEED_PHRASE or PRIVATE_KEY
89
+ * @returns {Promise<boolean>} A promise that resolves to true if the export is successful
90
+ */
70
91
  protected exportWalletInner(params: {
71
92
  exportStamper: ExportWalletStamper;
72
93
  exportAs: "SEED_PHRASE" | "PRIVATE_KEY";
@@ -112,9 +133,9 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
112
133
  /**
113
134
  * Listen to events emitted by the client
114
135
  *
115
- * @param event the event you want to listen to
116
- * @param listener the callback function to execute when an event is fired
117
- * @returns a function that will remove the listener when called
136
+ * @param {AlchemySignerClientEvent} event the event you want to listen to
137
+ * @param {AlchemySignerClientEvents[AlchemySignerClientEvent]} listener the callback function to execute when an event is fired
138
+ * @returns {() => void} a function that will remove the listener when called
118
139
  */
119
140
  public on = <E extends AlchemySignerClientEvent>(
120
141
  event: E,
@@ -125,6 +146,13 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
125
146
  return () => this.eventEmitter.removeListener(event, listener as any);
126
147
  };
127
148
 
149
+ /**
150
+ * Handles the creation of authenticators using WebAuthn attestation and the provided options. Requires the user to be authenticated.
151
+ *
152
+ * @param {CredentialCreationOptions} options The options used to create the WebAuthn attestation
153
+ * @returns {Promise<string[]>} A promise that resolves to an array of authenticator IDs
154
+ * @throws {NotAuthenticatedError} If the user is not authenticated
155
+ */
128
156
  public addPasskey = async (options: CredentialCreationOptions) => {
129
157
  if (!this.user) {
130
158
  throw new NotAuthenticatedError();
@@ -158,6 +186,13 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
158
186
  return authenticatorIds;
159
187
  };
160
188
 
189
+ /**
190
+ * Retrieves the current user or fetches the user information if not already available.
191
+ *
192
+ * @param {string} [orgId] optional organization ID, defaults to the user's organization ID
193
+ * @returns {Promise<User>} A promise that resolves to the user object
194
+ * @throws {Error} if no organization ID is provided when there is no current user
195
+ */
161
196
  public whoami = async (orgId = this.user?.orgId): Promise<User> => {
162
197
  if (this.user) {
163
198
  return this.user;
@@ -192,6 +227,12 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
192
227
  return this.user;
193
228
  };
194
229
 
230
+ /**
231
+ * Looks up information based on an email address.
232
+ *
233
+ * @param {string} email the email address to look up
234
+ * @returns {Promise<any>} the result of the lookup request
235
+ */
195
236
  public lookupUserByEmail = async (email: string) => {
196
237
  return this.request("/v1/lookup", { email });
197
238
  };
@@ -201,10 +242,10 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
201
242
  * For SignMessage or SignTypedData, the caller should hash the message before calling this method and pass
202
243
  * that result here.
203
244
  *
204
- * @param msg the hex representation of the bytes to sign
205
- * @returns the signature over the raw hex
245
+ * @param {Hex} msg the hex representation of the bytes to sign
246
+ * @returns {Promise<Hex>} the signature over the raw hex
206
247
  */
207
- public signRawMessage = async (msg: Hex) => {
248
+ public signRawMessage = async (msg: Hex): Promise<Hex> => {
208
249
  if (!this.user) {
209
250
  throw new NotAuthenticatedError();
210
251
  }
@@ -228,10 +269,23 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
228
269
  return signature;
229
270
  };
230
271
 
272
+ /**
273
+ * Returns the current user or null if no user is set.
274
+ *
275
+ * @returns {User | null} the current user object or null if no user is available
276
+ */
231
277
  public getUser = (): User | null => {
232
278
  return this.user ?? null;
233
279
  };
234
280
 
281
+ /**
282
+ * Sends a POST request to the given signer route with the specified body and returns the response.
283
+ * Not intended to be used directly, use the specific methods instead on the client instead.
284
+ *
285
+ * @param {SignerRoutes} route The route to which the request should be sent
286
+ * @param {SignerBody<R>} body The request body containing the data to be sent
287
+ * @returns {Promise<SignerResponse<R>>} A promise that resolves to the response from the signer
288
+ */
235
289
  public request = async <R extends SignerRoutes>(
236
290
  route: R,
237
291
  body: SignerBody<R>
@@ -348,6 +402,7 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
348
402
  return result;
349
403
  };
350
404
 
405
+ // eslint-disable-next-line eslint-rules/require-jsdoc-on-reexported-functions
351
406
  protected pollActivityCompletion = async <
352
407
  T extends keyof Awaited<
353
408
  ReturnType<(typeof this.turnkeyClient)["getActivity"]>