@account-kit/signer 4.63.1 → 4.64.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 (52) hide show
  1. package/dist/esm/base.d.ts +0 -1
  2. package/dist/esm/base.js +8 -16
  3. package/dist/esm/base.js.map +1 -1
  4. package/dist/esm/client/base.d.ts +14 -0
  5. package/dist/esm/client/base.js +21 -1
  6. package/dist/esm/client/base.js.map +1 -1
  7. package/dist/esm/client/server.d.ts +61 -0
  8. package/dist/esm/client/server.js +193 -0
  9. package/dist/esm/client/server.js.map +1 -0
  10. package/dist/esm/client/types.d.ts +15 -1
  11. package/dist/esm/client/types.js.map +1 -1
  12. package/dist/esm/errors.d.ts +4 -0
  13. package/dist/esm/errors.js +11 -0
  14. package/dist/esm/errors.js.map +1 -1
  15. package/dist/esm/index.d.ts +2 -0
  16. package/dist/esm/index.js +2 -0
  17. package/dist/esm/index.js.map +1 -1
  18. package/dist/esm/serverSigner.d.ts +99 -0
  19. package/dist/esm/serverSigner.js +139 -0
  20. package/dist/esm/serverSigner.js.map +1 -0
  21. package/dist/esm/signer.d.ts +7 -1
  22. package/dist/esm/signer.js.map +1 -1
  23. package/dist/esm/version.d.ts +1 -1
  24. package/dist/esm/version.js +1 -1
  25. package/dist/esm/version.js.map +1 -1
  26. package/dist/types/base.d.ts +0 -1
  27. package/dist/types/base.d.ts.map +1 -1
  28. package/dist/types/client/base.d.ts +14 -0
  29. package/dist/types/client/base.d.ts.map +1 -1
  30. package/dist/types/client/server.d.ts +62 -0
  31. package/dist/types/client/server.d.ts.map +1 -0
  32. package/dist/types/client/types.d.ts +15 -1
  33. package/dist/types/client/types.d.ts.map +1 -1
  34. package/dist/types/errors.d.ts +4 -0
  35. package/dist/types/errors.d.ts.map +1 -1
  36. package/dist/types/index.d.ts +2 -0
  37. package/dist/types/index.d.ts.map +1 -1
  38. package/dist/types/serverSigner.d.ts +100 -0
  39. package/dist/types/serverSigner.d.ts.map +1 -0
  40. package/dist/types/signer.d.ts +7 -1
  41. package/dist/types/signer.d.ts.map +1 -1
  42. package/dist/types/version.d.ts +1 -1
  43. package/package.json +4 -4
  44. package/src/base.ts +8 -20
  45. package/src/client/base.ts +29 -1
  46. package/src/client/server.ts +149 -0
  47. package/src/client/types.ts +18 -1
  48. package/src/errors.ts +7 -0
  49. package/src/index.ts +6 -0
  50. package/src/serverSigner.ts +171 -0
  51. package/src/signer.ts +7 -1
  52. package/src/version.ts +1 -1
@@ -0,0 +1,149 @@
1
+ import type { ConnectionConfig } from "@aa-sdk/core";
2
+ import { ApiKeyStamper } from "@turnkey/api-key-stamper";
3
+ import type { CreateAccountParams, SignupResponse, User } from "./types";
4
+ import { BaseSignerClient } from "./base.js";
5
+ import { NotAuthenticatedError, UnsupportedFeatureError } from "../errors.js";
6
+ import { TurnkeyClient } from "@turnkey/http";
7
+ import type { AuthParams } from "../signer";
8
+ import { p256 } from "@noble/curves/p256";
9
+ import { bytesToHex } from "@noble/curves/utils";
10
+
11
+ export interface ServerSignerClientParams {
12
+ connection: ConnectionConfig;
13
+ }
14
+
15
+ const unimplementedFunction = (feature: string) => {
16
+ return () => {
17
+ throw new UnsupportedFeatureError(feature);
18
+ };
19
+ };
20
+
21
+ const createDummyStamper = () => {
22
+ return {
23
+ stamp: () => {
24
+ throw new NotAuthenticatedError();
25
+ },
26
+ };
27
+ };
28
+
29
+ /**
30
+ * ServerSignerClient is a client for signing messages using an access key.
31
+ * It extends the BaseSignerClient and uses the ApiKeyStamper for signing.
32
+ * Primarily intended to be used server-side.
33
+ */
34
+ export class ServerSignerClient extends BaseSignerClient<undefined> {
35
+ /**
36
+ * Creates an instance of ServerSignerClient.
37
+ *
38
+ * @param {ServerSignerClientParams} params The parameters for the client, including the access key and connection configuration
39
+ * @param {ConnectionConfig} params.connection The connection configuration for the client
40
+ * @param {string} params.accessKey The access key to be used for authentication
41
+ * @param {string | undefined} params.accountId An optional ID to identify the account. Only required when using a single access key for multiple signers.
42
+ */
43
+ constructor({ connection }: ServerSignerClientParams) {
44
+ // we will re-recreate the turnkey client (including the stamper) when we authenticate
45
+ const stamper = createDummyStamper();
46
+
47
+ super({ connection, stamper });
48
+ }
49
+
50
+ /**
51
+ * Unsets the user for the client
52
+ */
53
+ public override disconnect = async (): Promise<void> => {
54
+ this.user = undefined;
55
+ };
56
+
57
+ /**
58
+ * Creates a new user with the given parameters.
59
+ *
60
+ * @param {CreateAccountParams} params The parameters for creating the account
61
+ * @returns {Promise<SignupResponse>} A promise that resolves to the signup response
62
+ */
63
+ public override createAccount = async (
64
+ params: CreateAccountParams,
65
+ ): Promise<SignupResponse> => {
66
+ if (params.type !== "accessKey") {
67
+ throw new Error(
68
+ "ServerSignerClient only supports account creation via access key",
69
+ );
70
+ }
71
+
72
+ return this.request("/v1/signup", {
73
+ accessKey: {
74
+ publicKey: params.publicKey,
75
+ accountId: params.accountId,
76
+ },
77
+ });
78
+ };
79
+
80
+ /**
81
+ * Authenticates the user with an access key.
82
+ *
83
+ * @param {Extract<AuthParams, { type: "accessKey" }>} params The parameters for the authentication
84
+ * @returns {Promise<User>} A promise that resolves to the user
85
+ */
86
+ public authenticateWithAccessKey = async ({
87
+ accessKey,
88
+ accountId,
89
+ }: Extract<AuthParams, { type: "accessKey" }>): Promise<User> => {
90
+ const publicKey = bytesToHex(p256.getPublicKey(accessKey));
91
+
92
+ const user = await this.lookupUserByAccessKey({
93
+ publicKey,
94
+ accountId,
95
+ });
96
+
97
+ const orgId =
98
+ user?.orgId ??
99
+ (
100
+ await this.createAccount({
101
+ type: "accessKey",
102
+ publicKey,
103
+ accountId,
104
+ })
105
+ )?.orgId;
106
+
107
+ return this.completeAuthWithApiKey(
108
+ { publicKey, privateKey: accessKey },
109
+ orgId,
110
+ );
111
+ };
112
+
113
+ private completeAuthWithApiKey = async (
114
+ apiKey: { publicKey: string; privateKey: string },
115
+ subOrgId: string,
116
+ ) => {
117
+ this.turnkeyClient = new TurnkeyClient(
118
+ { baseUrl: "https://api.turnkey.com" },
119
+ new ApiKeyStamper({
120
+ apiPrivateKey: apiKey.privateKey,
121
+ apiPublicKey: apiKey.publicKey,
122
+ }),
123
+ );
124
+
125
+ const user = await this.whoami(subOrgId);
126
+ this.user = user;
127
+ return user;
128
+ };
129
+
130
+ /**
131
+ * Unimplemented functions for server signer.
132
+ * Required to extend the BaseSignerClient class.
133
+ */
134
+ public initSmsAuth = unimplementedFunction("Sms auth");
135
+ public submitJwt = unimplementedFunction("Jwt");
136
+ protected initSessionStamper = unimplementedFunction("Sessions");
137
+ protected initWebauthnStamper = unimplementedFunction("Webauthn");
138
+ override initEmailAuth = unimplementedFunction("Email auth");
139
+ public override submitOtpCode = unimplementedFunction("Otp code submission");
140
+ override completeAuthWithBundle = unimplementedFunction("Auth with bundle");
141
+ override oauthWithRedirect = unimplementedFunction("OAuth redirect");
142
+ override oauthWithPopup = unimplementedFunction("OAuth popup");
143
+ override exportWallet = unimplementedFunction("Wallet export");
144
+ override targetPublicKey = unimplementedFunction("Target public key");
145
+ override getWebAuthnAttestation = unimplementedFunction(
146
+ "WebAuthn attestation",
147
+ );
148
+ override getOauthConfig = unimplementedFunction("OAuth config");
149
+ }
@@ -64,6 +64,11 @@ export type CreateAccountParams =
64
64
  type: "passkey";
65
65
  username: string;
66
66
  creationOpts?: CredentialCreationOptionOverrides;
67
+ }
68
+ | {
69
+ type: "accessKey";
70
+ publicKey: string;
71
+ accountId?: string;
67
72
  };
68
73
 
69
74
  export type EmailType = "magicLink" | "otp";
@@ -83,6 +88,13 @@ export type SmsAuthParams = {
83
88
  targetPublicKey: string;
84
89
  };
85
90
 
91
+ export type AccessKeyAuthParamsPublicKeyOnly = {
92
+ accessKey: {
93
+ publicKey: string;
94
+ accountId?: string;
95
+ };
96
+ };
97
+
86
98
  export type OauthParams = Extract<AuthParams, { type: "oauth" }> & {
87
99
  expirationSeconds?: number;
88
100
  fetchIdTokenOnly?: boolean;
@@ -173,7 +185,8 @@ export type SignerEndpoints = [
173
185
  challenge: string;
174
186
  attestation: Awaited<ReturnType<typeof getWebAuthnAttestation>>;
175
187
  };
176
- };
188
+ }
189
+ | AccessKeyAuthParamsPublicKeyOnly;
177
190
  Response: SignupResponse;
178
191
  },
179
192
  {
@@ -202,6 +215,10 @@ export type SignerEndpoints = [
202
215
  Body: {
203
216
  email?: string;
204
217
  phone?: string;
218
+ accessKey?: {
219
+ publicKey: string;
220
+ accountId?: string;
221
+ };
205
222
  };
206
223
  Response: {
207
224
  orgId: string | null;
package/src/errors.ts CHANGED
@@ -33,3 +33,10 @@ export class MfaRequiredError extends BaseError {
33
33
  this.multiFactors = multiFactors;
34
34
  }
35
35
  }
36
+
37
+ export class UnsupportedFeatureError extends BaseError {
38
+ override name = "UnsupportedFeatureError";
39
+ constructor(feature: string) {
40
+ super(`${feature} not supported by this signer`);
41
+ }
42
+ }
package/src/index.ts CHANGED
@@ -1,10 +1,16 @@
1
1
  export { BaseAlchemySigner } from "./base.js";
2
+ export {
3
+ AlchemyServerSigner,
4
+ createServerSigner,
5
+ generateAccessKey,
6
+ } from "./serverSigner.js";
2
7
  export { BaseSignerClient } from "./client/base.js";
3
8
  export {
4
9
  AlchemySignerWebClient,
5
10
  OauthCancelledError,
6
11
  OauthFailedError,
7
12
  } from "./client/index.js";
13
+ export { ServerSignerClient } from "./client/server.js";
8
14
  export type * from "./client/types.js";
9
15
  export {
10
16
  NotAuthenticatedError,
@@ -0,0 +1,171 @@
1
+ import {
2
+ type AuthorizationRequest,
3
+ type SmartAccountSigner,
4
+ unpackSignRawMessageBytes,
5
+ } from "@aa-sdk/core";
6
+ import {
7
+ ServerSignerClient,
8
+ type ServerSignerClientParams,
9
+ } from "./client/server.js";
10
+ import {
11
+ hashMessage,
12
+ hashTypedData,
13
+ type Address,
14
+ type Hex,
15
+ type SignableMessage,
16
+ type SignedAuthorization,
17
+ type TypedData,
18
+ type TypedDataDefinition,
19
+ } from "viem";
20
+ import { hashAuthorization } from "viem/utils";
21
+ import type { AccessKeyAuthParams } from "./signer.js";
22
+ import { SolanaSigner } from "./solanaSigner.js";
23
+ import { p256 } from "@noble/curves/p256";
24
+ import { bytesToHex } from "@noble/curves/utils";
25
+
26
+ /**
27
+ * AlchemyServerSigner is a signer that can sign messages and typed data using an access key.
28
+ * It extends the SmartAccountSigner interface and uses the ServerSignerClient to sign requests.
29
+ * Primarily intended to be used server-side.
30
+ */
31
+ export class AlchemyServerSigner implements SmartAccountSigner {
32
+ inner: ServerSignerClient;
33
+ signerType = "alchemy-server-signer";
34
+
35
+ /**
36
+ * Creates an instance of AlchemyServerSigner.
37
+ *
38
+ * @param {ServerSignerClient} client The underlying signer client
39
+ */
40
+ constructor(client: ServerSignerClient) {
41
+ this.inner = client;
42
+ }
43
+
44
+ /**
45
+ * Gets the address of the user from the signer client.
46
+ *
47
+ * @returns {Promise<Address>} The address of the user
48
+ * @throws {Error} If the user cannot be retrieved from the signer client
49
+ */
50
+ async getAddress(): Promise<Address> {
51
+ const { address } = await this.inner.whoami();
52
+ return address;
53
+ }
54
+
55
+ /**
56
+ * Signs a message using the inner client.
57
+ *
58
+ * @param {SignableMessage} msg The message to sign
59
+ * @returns {Promise<Hex>} The signed message
60
+ */
61
+ async signMessage(msg: SignableMessage): Promise<Hex> {
62
+ const messageHash = hashMessage(msg);
63
+ return this.inner.signRawMessage(messageHash);
64
+ }
65
+
66
+ /**
67
+ * Signs typed data using the inner client.
68
+ *
69
+ * @param {TypedDataDefinition<TTypedData, TPrimaryType>} params The typed data to sign
70
+ * @returns {Promise<Hex>} The signed typed data
71
+ */
72
+ async signTypedData<
73
+ const TTypedData extends TypedData | Record<string, unknown>,
74
+ TPrimaryType extends keyof TTypedData | "EIP712Domain" = keyof TTypedData,
75
+ >(params: TypedDataDefinition<TTypedData, TPrimaryType>): Promise<Hex> {
76
+ const messageHash = hashTypedData(params);
77
+ return this.inner.signRawMessage(messageHash);
78
+ }
79
+
80
+ /**
81
+ * Signs an authorization using the inner client.
82
+ *
83
+ * @param {Authorization<number, false>} unsignedAuthorization The unsigned authorization to sign
84
+ * @returns {Promise<Authorization<number, true>>} The signed authorization
85
+ */
86
+ async signAuthorization(
87
+ unsignedAuthorization: AuthorizationRequest<number>,
88
+ ): Promise<SignedAuthorization<number>> {
89
+ const hashedAuthorization = hashAuthorization(unsignedAuthorization);
90
+ const signedAuthorizationHex =
91
+ await this.inner.signRawMessage(hashedAuthorization);
92
+ const signature = unpackSignRawMessageBytes(signedAuthorizationHex);
93
+ const { address, contractAddress, ...unsignedAuthorizationRest } =
94
+ unsignedAuthorization;
95
+
96
+ return {
97
+ ...unsignedAuthorizationRest,
98
+ ...signature,
99
+ address: address ?? contractAddress,
100
+ };
101
+ }
102
+
103
+ /**
104
+ * Creates a new instance of `SolanaSigner` using the inner client.
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * import { AlchemyServerSigner } from "@account-kit/signer";
109
+ *
110
+ * const signer = await createServerSigner({
111
+ * auth: { accessKey },
112
+ * connection: {
113
+ * apiKey: "alchemy-api-key",
114
+ * },
115
+ * });
116
+ *
117
+ * const solanaSigner = signer.toSolanaSigner();
118
+ * ```
119
+ *
120
+ * @returns {SolanaSigner} A new instance of `SolanaSigner`
121
+ */
122
+ toSolanaSigner(): SolanaSigner {
123
+ return new SolanaSigner(this.inner);
124
+ }
125
+ }
126
+
127
+ type CreateServerSignerParams = ServerSignerClientParams & {
128
+ auth: AccessKeyAuthParams;
129
+ };
130
+
131
+ /**
132
+ * Creates a new server signer.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * const signer = await createServerSigner({
137
+ * auth: { accessKey },
138
+ * connection: {
139
+ * apiKey: "alchemy-api-key",
140
+ * }
141
+ * });
142
+ *
143
+ * console.log("Signer address:", await signer.getAddress());
144
+ * ```
145
+ *
146
+ * @param {CreateServerSignerParams} params Parameters
147
+ * @param {AccessKeyAuthParams} params.auth Authentication config for the server signer
148
+ * @param {ConnectionConfig} params.connection Connection config for the server signer
149
+ * @returns {Promise<AlchemyServerSigner>} A promise that resolves to a server signer
150
+ */
151
+ export const createServerSigner = async (
152
+ params: CreateServerSignerParams,
153
+ ): Promise<AlchemyServerSigner> => {
154
+ const client = new ServerSignerClient(params);
155
+ const signer = new AlchemyServerSigner(client);
156
+
157
+ await signer.inner.authenticateWithAccessKey({
158
+ ...params.auth,
159
+ type: "accessKey",
160
+ });
161
+
162
+ return signer;
163
+ };
164
+
165
+ /**
166
+ * Generates a new access key for use in the server signer
167
+ *
168
+ * @returns {Hex} A randomly generated access key
169
+ */
170
+ export const generateAccessKey = () =>
171
+ bytesToHex(p256.utils.randomPrivateKey());
package/src/signer.ts CHANGED
@@ -11,6 +11,11 @@ import type {
11
11
  } from "./client/types.js";
12
12
  import { SessionManagerParamsSchema } from "./session/manager.js";
13
13
 
14
+ export type AccessKeyAuthParams = {
15
+ accessKey: string;
16
+ accountId?: string;
17
+ };
18
+
14
19
  export type AuthParams =
15
20
  | {
16
21
  type: "email";
@@ -64,7 +69,8 @@ export type AuthParams =
64
69
  type: "otp";
65
70
  otpCode: string;
66
71
  multiFactors?: VerifyMfaParams[];
67
- };
72
+ }
73
+ | ({ type: "accessKey" } & AccessKeyAuthParams);
68
74
 
69
75
  export type OauthProviderConfig =
70
76
  | {
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.63.1";
3
+ export const VERSION = "4.64.0";