@account-kit/signer 4.15.2 → 4.16.0

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 (41) hide show
  1. package/dist/esm/base.d.ts +27 -1
  2. package/dist/esm/base.js +37 -1
  3. package/dist/esm/base.js.map +1 -1
  4. package/dist/esm/client/base.d.ts +3 -1
  5. package/dist/esm/client/base.js +12 -5
  6. package/dist/esm/client/base.js.map +1 -1
  7. package/dist/esm/client/index.d.ts +22 -0
  8. package/dist/esm/client/index.js +29 -0
  9. package/dist/esm/client/index.js.map +1 -1
  10. package/dist/esm/client/types.d.ts +1 -0
  11. package/dist/esm/client/types.js.map +1 -1
  12. package/dist/esm/index.d.ts +2 -1
  13. package/dist/esm/index.js +1 -1
  14. package/dist/esm/index.js.map +1 -1
  15. package/dist/esm/solanaSigner.d.ts +12 -0
  16. package/dist/esm/solanaSigner.js +67 -0
  17. package/dist/esm/solanaSigner.js.map +1 -0
  18. package/dist/esm/version.d.ts +1 -1
  19. package/dist/esm/version.js +1 -1
  20. package/dist/esm/version.js.map +1 -1
  21. package/dist/types/base.d.ts +27 -1
  22. package/dist/types/base.d.ts.map +1 -1
  23. package/dist/types/client/base.d.ts +3 -1
  24. package/dist/types/client/base.d.ts.map +1 -1
  25. package/dist/types/client/index.d.ts +22 -0
  26. package/dist/types/client/index.d.ts.map +1 -1
  27. package/dist/types/client/types.d.ts +1 -0
  28. package/dist/types/client/types.d.ts.map +1 -1
  29. package/dist/types/index.d.ts +2 -1
  30. package/dist/types/index.d.ts.map +1 -1
  31. package/dist/types/solanaSigner.d.ts +13 -0
  32. package/dist/types/solanaSigner.d.ts.map +1 -0
  33. package/dist/types/version.d.ts +1 -1
  34. package/package.json +5 -4
  35. package/src/base.ts +35 -2
  36. package/src/client/base.ts +21 -6
  37. package/src/client/index.ts +25 -0
  38. package/src/client/types.ts +1 -0
  39. package/src/index.ts +2 -1
  40. package/src/solanaSigner.ts +83 -0
  41. package/src/version.ts +1 -1
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "4.15.2";
1
+ export declare const VERSION = "4.16.0";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@account-kit/signer",
3
- "version": "4.15.2",
3
+ "version": "4.16.0",
4
4
  "description": "Core interfaces and clients for interfacing with the Alchemy Signer API",
5
5
  "author": "Alchemy",
6
6
  "license": "MIT",
@@ -49,8 +49,9 @@
49
49
  "vitest": "^2.0.4"
50
50
  },
51
51
  "dependencies": {
52
- "@aa-sdk/core": "^4.15.2",
53
- "@account-kit/logging": "^4.15.2",
52
+ "@aa-sdk/core": "^4.16.0",
53
+ "@account-kit/logging": "^4.16.0",
54
+ "@solana/web3.js": "^1.98.0",
54
55
  "@turnkey/http": "^2.6.2",
55
56
  "@turnkey/iframe-stamper": "^1.0.0",
56
57
  "@turnkey/viem": "^0.4.8",
@@ -73,5 +74,5 @@
73
74
  "url": "https://github.com/alchemyplatform/aa-sdk/issues"
74
75
  },
75
76
  "homepage": "https://github.com/alchemyplatform/aa-sdk#readme",
76
- "gitHead": "c69762a97a77be62706accec8b9af908c836ccd5"
77
+ "gitHead": "d0a8f15fb7d10812c29b98f334d9705d46a31e7f"
77
78
  }
package/src/base.ts CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  type TypedDataDefinition,
17
17
  } from "viem";
18
18
  import { toAccount } from "viem/accounts";
19
+ import { hashAuthorization, type Authorization } from "viem/experimental";
19
20
  import type { Mutate, StoreApi } from "zustand";
20
21
  import { subscribeWithSelector } from "zustand/middleware";
21
22
  import { createStore } from "zustand/vanilla";
@@ -27,7 +28,9 @@ import {
27
28
  SessionManager,
28
29
  type SessionManagerParams,
29
30
  } from "./session/manager.js";
31
+ import type { SessionManagerEvents } from "./session/types";
30
32
  import type { AuthParams } from "./signer";
33
+ import { SolanaSigner } from "./solanaSigner.js";
31
34
  import {
32
35
  AlchemySignerStatus,
33
36
  type AlchemySignerEvent,
@@ -35,8 +38,6 @@ import {
35
38
  type ErrorInfo,
36
39
  } from "./types.js";
37
40
  import { assertNever } from "./utils/typeAssertions.js";
38
- import type { SessionManagerEvents } from "./session/types";
39
- import { hashAuthorization, type Authorization } from "viem/experimental";
40
41
 
41
42
  export interface BaseAlchemySignerParams<TClient extends BaseSignerClient> {
42
43
  client: TClient;
@@ -733,6 +734,38 @@ export abstract class BaseAlchemySigner<TClient extends BaseSignerClient>
733
734
  });
734
735
  };
735
736
 
737
+ /**
738
+ * Creates a new instance of `SolanaSigner` using the provided inner value.
739
+ * This requires the signer to be authenticated first
740
+ *
741
+ * @example
742
+ * ```ts
743
+ * import { AlchemyWebSigner } from "@account-kit/signer";
744
+ *
745
+ * const signer = new AlchemyWebSigner({
746
+ * client: {
747
+ * connection: {
748
+ * rpcUrl: "/api/rpc",
749
+ * },
750
+ * iframeConfig: {
751
+ * iframeContainerId: "alchemy-signer-iframe-container",
752
+ * },
753
+ * },
754
+ * });
755
+ *
756
+ * const solanaSigner = signer.toSolanaSigner();
757
+ * ```
758
+ *
759
+ * @returns {SolanaSigner} A new instance of `SolanaSigner`
760
+ */
761
+ experimental_toSolanaSigner = (): SolanaSigner => {
762
+ if (!this.inner.getUser()) {
763
+ throw new NotAuthenticatedError();
764
+ }
765
+
766
+ return new SolanaSigner(this.inner);
767
+ };
768
+
736
769
  private authenticateWithEmail = async (
737
770
  params: Extract<AuthParams, { type: "email" }>
738
771
  ): Promise<User> => {
@@ -4,9 +4,11 @@ import EventEmitter from "eventemitter3";
4
4
  import { jwtDecode } from "jwt-decode";
5
5
  import { sha256, type Hex } from "viem";
6
6
  import { NotAuthenticatedError, OAuthProvidersError } from "../errors.js";
7
+ import { addOpenIdIfAbsent, getDefaultScopeAndClaims } from "../oauth.js";
8
+ import type { OauthMode } from "../signer.js";
7
9
  import { base64UrlEncode } from "../utils/base64UrlEncode.js";
8
- import { assertNever } from "../utils/typeAssertions.js";
9
10
  import { resolveRelativeUrl } from "../utils/resolveRelativeUrl.js";
11
+ import { assertNever } from "../utils/typeAssertions.js";
10
12
  import type {
11
13
  AlchemySignerClientEvent,
12
14
  AlchemySignerClientEvents,
@@ -25,8 +27,6 @@ import type {
25
27
  SignupResponse,
26
28
  User,
27
29
  } from "./types.js";
28
- import type { OauthMode } from "../signer.js";
29
- import { addOpenIdIfAbsent, getDefaultScopeAndClaims } from "../oauth.js";
30
30
 
31
31
  export interface BaseSignerClientParams {
32
32
  stamper: TurnkeyClient["stamper"];
@@ -159,6 +159,8 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
159
159
 
160
160
  public abstract lookupUserWithPasskey(user?: User): Promise<User>;
161
161
 
162
+ public abstract targetPublicKey(): Promise<string>;
163
+
162
164
  protected abstract getOauthConfig(): Promise<OauthConfig>;
163
165
 
164
166
  protected abstract getWebAuthnAttestation(
@@ -315,22 +317,35 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
315
317
  * that result here.
316
318
  *
317
319
  * @param {Hex} msg the hex representation of the bytes to sign
320
+ * @param {string} mode specify if signing should happen for solana or ethereum
318
321
  * @returns {Promise<Hex>} the signature over the raw hex
319
322
  */
320
- public signRawMessage = async (msg: Hex): Promise<Hex> => {
323
+ public signRawMessage = async (
324
+ msg: Hex,
325
+ mode: "SOLANA" | "ETHEREUM" = "ETHEREUM"
326
+ ): Promise<Hex> => {
321
327
  if (!this.user) {
322
328
  throw new NotAuthenticatedError();
323
329
  }
324
330
 
331
+ if (!this.user.solanaAddress && mode === "SOLANA") {
332
+ // TODO: we need to add backwards compatibility for users who signed up before we added Solana support
333
+ throw new Error("No Solana address available for the user");
334
+ }
335
+
325
336
  const stampedRequest = await this.turnkeyClient.stampSignRawPayload({
326
337
  organizationId: this.user.orgId,
327
338
  type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
328
339
  timestampMs: Date.now().toString(),
329
340
  parameters: {
330
341
  encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
331
- hashFunction: "HASH_FUNCTION_NO_OP",
342
+ hashFunction:
343
+ mode === "ETHEREUM"
344
+ ? "HASH_FUNCTION_NO_OP"
345
+ : "HASH_FUNCTION_NOT_APPLICABLE",
332
346
  payload: msg,
333
- signWith: this.user.address,
347
+ signWith:
348
+ mode === "ETHEREUM" ? this.user.address : this.user.solanaAddress!,
334
349
  },
335
350
  });
336
351
 
@@ -557,6 +557,31 @@ export class AlchemySignerWebClient extends BaseSignerClient<ExportWalletParams>
557
557
  });
558
558
  };
559
559
 
560
+ /**
561
+ * Initializes the iframe stamper and returns its public key.
562
+ *
563
+ * @example
564
+ * ```ts twoslash
565
+ * import { AlchemySignerWebClient } from "@account-kit/signer";
566
+ *
567
+ * const client = new AlchemySignerWebClient({
568
+ * connection: {
569
+ * apiKey: "your-api-key",
570
+ * },
571
+ * iframeConfig: {
572
+ * iframeContainerId: "signer-iframe-container",
573
+ * },
574
+ * });
575
+ *
576
+ * const publicKey = await client.targetPublicKey();
577
+ * ```
578
+ *
579
+ * @returns {Promise<string>} A promise that resolves with the target public key when the iframe stamper is successfully initialized, or throws an error if the target public key is not supported.
580
+ */
581
+ public override targetPublicKey = async (): Promise<string> => {
582
+ return this.initIframeStamper();
583
+ };
584
+
560
585
  private initIframeStamper = async () => {
561
586
  if (!this.iframeStamper.publicKey()) {
562
587
  await this.iframeStamper.init();
@@ -13,6 +13,7 @@ export type User = {
13
13
  orgId: string;
14
14
  userId: string;
15
15
  address: Address;
16
+ solanaAddress?: string;
16
17
  credentialId?: string;
17
18
  idToken?: string;
18
19
  claims?: Record<string, unknown>;
package/src/index.ts CHANGED
@@ -6,12 +6,13 @@ export {
6
6
  OauthFailedError,
7
7
  } from "./client/index.js";
8
8
  export type * from "./client/types.js";
9
+ export { NotAuthenticatedError, OAuthProvidersError } from "./errors.js";
9
10
  export {
10
11
  DEFAULT_SESSION_MS,
11
12
  SessionManagerParamsSchema,
12
13
  } from "./session/manager.js";
13
14
  export type * from "./signer.js";
14
15
  export { AlchemyWebSigner } from "./signer.js";
16
+ export type * from "./solanaSigner.js";
15
17
  export type * from "./types.js";
16
18
  export { AlchemySignerStatus } from "./types.js";
17
- export { OAuthProvidersError, NotAuthenticatedError } from "./errors.js";
@@ -0,0 +1,83 @@
1
+ import {
2
+ PublicKey,
3
+ Transaction,
4
+ type VersionedTransaction,
5
+ } from "@solana/web3.js";
6
+ import { size, slice, toBytes, toHex, type ByteArray, type Hex } from "viem";
7
+ import type { BaseSignerClient } from "./client/base";
8
+ import { NotAuthenticatedError } from "./errors.js";
9
+
10
+ // TODO: I don't want this to be a class so that the flow is closer to how we do this for `toViemAccount`
11
+ export class SolanaSigner {
12
+ private alchemyClient: BaseSignerClient;
13
+ public address: string;
14
+
15
+ constructor(client: BaseSignerClient) {
16
+ this.alchemyClient = client;
17
+ if (!client.getUser()) throw new Error("Must be authenticated!");
18
+
19
+ // TODO: also throw here
20
+ this.address = client.getUser()!.solanaAddress!;
21
+ }
22
+
23
+ async addSignature(
24
+ tx: Transaction | VersionedTransaction
25
+ ): Promise<Transaction | VersionedTransaction> {
26
+ const user = this.alchemyClient.getUser();
27
+ if (!user) {
28
+ throw new NotAuthenticatedError();
29
+ }
30
+
31
+ if (!user.solanaAddress) {
32
+ throw new Error("no solana address");
33
+ }
34
+
35
+ const fromKey = new PublicKey(user.solanaAddress);
36
+ const messageToSign = this.getMessageToSign(tx);
37
+ const signature = await this.alchemyClient.signRawMessage(
38
+ messageToSign,
39
+ "SOLANA"
40
+ );
41
+
42
+ tx.addSignature(
43
+ fromKey,
44
+ Buffer.from(toBytes(this.formatSignatureForSolana(signature)))
45
+ );
46
+ return tx;
47
+ }
48
+
49
+ async signMessage(message: Uint8Array): Promise<ByteArray> {
50
+ const user = this.alchemyClient.getUser();
51
+ if (!user) {
52
+ throw new NotAuthenticatedError();
53
+ }
54
+
55
+ if (!user.solanaAddress) {
56
+ throw new Error("no solana address");
57
+ }
58
+
59
+ const messageToSign = toHex(message);
60
+ const signature = await this.alchemyClient.signRawMessage(
61
+ messageToSign,
62
+ "SOLANA"
63
+ );
64
+
65
+ return toBytes(this.formatSignatureForSolana(signature));
66
+ }
67
+
68
+ private formatSignatureForSolana(signature: Hex): Hex {
69
+ if (size(signature) === 64) return signature;
70
+
71
+ return slice(signature, 0, 64);
72
+ }
73
+
74
+ private getMessageToSign(tx: Transaction | VersionedTransaction): Hex {
75
+ let messageToSign;
76
+ if (tx instanceof Transaction) {
77
+ messageToSign = tx.serializeMessage();
78
+ } else {
79
+ messageToSign = Buffer.from(tx.message.serialize());
80
+ }
81
+ return toHex(messageToSign);
82
+ }
83
+ }
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // This file is autogenerated by inject-version.ts. Any changes will be
2
2
  // overwritten on commit!
3
- export const VERSION = "4.15.2";
3
+ export const VERSION = "4.16.0";