@account-kit/signer 4.43.0 → 4.44.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.
- package/dist/esm/client/base.d.ts +38 -3
- package/dist/esm/client/base.js +142 -3
- package/dist/esm/client/base.js.map +1 -1
- package/dist/esm/client/index.d.ts +5 -1
- package/dist/esm/client/index.js +34 -7
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/types.d.ts +39 -1
- package/dist/esm/client/types.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/types/client/base.d.ts +38 -3
- package/dist/types/client/base.d.ts.map +1 -1
- package/dist/types/client/index.d.ts +5 -1
- package/dist/types/client/index.d.ts.map +1 -1
- package/dist/types/client/types.d.ts +39 -1
- package/dist/types/client/types.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +9 -6
- package/src/client/base.ts +176 -5
- package/src/client/index.ts +37 -7
- package/src/client/types.ts +43 -1
- package/src/version.ts +1 -1
package/src/client/base.ts
CHANGED
|
@@ -2,7 +2,14 @@ import { ConnectionConfigSchema, type ConnectionConfig } from "@aa-sdk/core";
|
|
|
2
2
|
import { TurnkeyClient, type TSignedRequest } from "@turnkey/http";
|
|
3
3
|
import EventEmitter from "eventemitter3";
|
|
4
4
|
import { jwtDecode } from "jwt-decode";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
hexToBytes,
|
|
7
|
+
recoverPublicKey,
|
|
8
|
+
serializeSignature,
|
|
9
|
+
sha256,
|
|
10
|
+
type Address,
|
|
11
|
+
type Hex,
|
|
12
|
+
} from "viem";
|
|
6
13
|
import { NotAuthenticatedError, OAuthProvidersError } from "../errors.js";
|
|
7
14
|
import { getDefaultProviderCustomization } from "../oauth.js";
|
|
8
15
|
import type { OauthMode } from "../signer.js";
|
|
@@ -42,6 +49,8 @@ import type {
|
|
|
42
49
|
AuthMethods,
|
|
43
50
|
} from "./types.js";
|
|
44
51
|
import { VERSION } from "../version.js";
|
|
52
|
+
import { secp256k1 } from "@noble/curves/secp256k1";
|
|
53
|
+
import { Point } from "@noble/secp256k1";
|
|
45
54
|
|
|
46
55
|
export interface BaseSignerClientParams {
|
|
47
56
|
stamper: TurnkeyClient["stamper"];
|
|
@@ -51,8 +60,8 @@ export interface BaseSignerClientParams {
|
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
export type ExportWalletStamper = TurnkeyClient["stamper"] & {
|
|
54
|
-
injectWalletExportBundle(bundle: string): Promise<boolean>;
|
|
55
|
-
injectKeyExportBundle(bundle: string): Promise<boolean>;
|
|
63
|
+
injectWalletExportBundle(bundle: string, orgId: string): Promise<boolean>;
|
|
64
|
+
injectKeyExportBundle(bundle: string, orgId: string): Promise<boolean>;
|
|
56
65
|
publicKey(): string | null;
|
|
57
66
|
};
|
|
58
67
|
|
|
@@ -64,6 +73,8 @@ const MFA_PAYLOAD = {
|
|
|
64
73
|
LIST: "list_mfas",
|
|
65
74
|
} as const;
|
|
66
75
|
|
|
76
|
+
const withHexPrefix = (hex: string) => `0x${hex}` as const;
|
|
77
|
+
|
|
67
78
|
/**
|
|
68
79
|
* Base class for all Alchemy Signer clients
|
|
69
80
|
*/
|
|
@@ -679,6 +690,160 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
|
|
|
679
690
|
return signature;
|
|
680
691
|
};
|
|
681
692
|
|
|
693
|
+
private experimental_createMultiOwnerStamper = () => ({
|
|
694
|
+
stamp: async (
|
|
695
|
+
request: string,
|
|
696
|
+
): Promise<{
|
|
697
|
+
stampHeaderName: string;
|
|
698
|
+
stampHeaderValue: string;
|
|
699
|
+
}> => {
|
|
700
|
+
if (!this.user) {
|
|
701
|
+
throw new NotAuthenticatedError();
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// we need this later to recover the public key from the signature, so we don't let turnkey hash
|
|
705
|
+
// this for us and pass HASH_FUNCTION_NO_OP instead
|
|
706
|
+
const hashed = sha256(new TextEncoder().encode(request));
|
|
707
|
+
|
|
708
|
+
// sign through the user's suborg
|
|
709
|
+
const signature = await this.signRawMessage(hashed, "ETHEREUM");
|
|
710
|
+
|
|
711
|
+
// recover the public key, we can't just use the address
|
|
712
|
+
const recoveredPublicKey = await recoverPublicKey({
|
|
713
|
+
hash: hashed,
|
|
714
|
+
signature,
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
// compute the stamp over the original payload using this signature
|
|
718
|
+
// the format here is important
|
|
719
|
+
const stamp = {
|
|
720
|
+
publicKey: Point.fromHex(hexToBytes(recoveredPublicKey)).toHex(true),
|
|
721
|
+
scheme: "SIGNATURE_SCHEME_TK_API_SECP256K1",
|
|
722
|
+
signature: secp256k1.Signature.fromCompact(
|
|
723
|
+
hexToBytes(signature).slice(0, 64),
|
|
724
|
+
).toDERHex(),
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
return {
|
|
728
|
+
stampHeaderName: "X-Stamp",
|
|
729
|
+
stampHeaderValue: base64UrlEncode(Buffer.from(JSON.stringify(stamp))),
|
|
730
|
+
};
|
|
731
|
+
},
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
private experimental_createMultiOwnerTurnkeyClient = () =>
|
|
735
|
+
new TurnkeyClient(
|
|
736
|
+
{ baseUrl: "https://api.turnkey.com" },
|
|
737
|
+
this.experimental_createMultiOwnerStamper(),
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* This will sign on behalf of the multi-owner org, without doing any transformations on the message.
|
|
742
|
+
* For SignMessage or SignTypedData, the caller should hash the message before calling this method and pass
|
|
743
|
+
* that result here.
|
|
744
|
+
*
|
|
745
|
+
* @param {Hex} msg the hex representation of the bytes to sign
|
|
746
|
+
* @param {string} orgId orgId of the multi-owner org to sign on behalf of
|
|
747
|
+
* @param {string} orgAddress address of the multi-owner org to sign on behalf of
|
|
748
|
+
* @returns {Promise<Hex>} the signature over the raw hex
|
|
749
|
+
*/
|
|
750
|
+
public experimental_multiOwnerSignRawMessage = async (
|
|
751
|
+
msg: Hex,
|
|
752
|
+
orgId: string,
|
|
753
|
+
orgAddress: string,
|
|
754
|
+
) => {
|
|
755
|
+
if (!this.user) {
|
|
756
|
+
throw new NotAuthenticatedError();
|
|
757
|
+
}
|
|
758
|
+
const multiOwnerClient = this.experimental_createMultiOwnerTurnkeyClient();
|
|
759
|
+
|
|
760
|
+
const signatureResult = await multiOwnerClient.signRawPayload({
|
|
761
|
+
organizationId: orgId,
|
|
762
|
+
type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
|
|
763
|
+
timestampMs: Date.now().toString(),
|
|
764
|
+
parameters: {
|
|
765
|
+
encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
|
|
766
|
+
hashFunction: "HASH_FUNCTION_NO_OP",
|
|
767
|
+
payload: msg,
|
|
768
|
+
signWith: orgAddress,
|
|
769
|
+
},
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
const signRawPayloadResult =
|
|
773
|
+
signatureResult.activity.result.signRawPayloadResult;
|
|
774
|
+
if (!signRawPayloadResult) {
|
|
775
|
+
throw new Error("No sign raw payload result");
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return serializeSignature({
|
|
779
|
+
r: withHexPrefix(signRawPayloadResult.r),
|
|
780
|
+
s: withHexPrefix(signRawPayloadResult.s),
|
|
781
|
+
yParity: Number(signRawPayloadResult.v), // this is not actually a legacy v value, it's the y parity bit
|
|
782
|
+
});
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* This will create a multi-sig with the current user and additional specified signers
|
|
787
|
+
*
|
|
788
|
+
* @param {number} quorum multi sig quorum, currently only 1 is supported
|
|
789
|
+
* @param {Address[]} additionalMembers members to add, aside from the currently authenticated user
|
|
790
|
+
* @returns {Promise<SignerResponse<"/v1/multi-sig-create">>} created multi-sig
|
|
791
|
+
*/
|
|
792
|
+
public experimental_createMultiSig = (
|
|
793
|
+
quorum: number,
|
|
794
|
+
additionalMembers: Address[],
|
|
795
|
+
) => {
|
|
796
|
+
if (!this.user) {
|
|
797
|
+
throw new NotAuthenticatedError();
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
return this.request("/v1/multi-sig-create", {
|
|
801
|
+
members: [this.user.address, ...additionalMembers].map(
|
|
802
|
+
(evmSignerAddress) => ({ evmSignerAddress }),
|
|
803
|
+
),
|
|
804
|
+
quorum,
|
|
805
|
+
});
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* This will add additional members to an existing multi-sig account
|
|
810
|
+
*
|
|
811
|
+
* @param {string} orgId orgId of the multi-sig to add members to
|
|
812
|
+
* @param {Address[]} members the addresses of the members to add
|
|
813
|
+
*/
|
|
814
|
+
public experimental_addToMultiOwner = async (
|
|
815
|
+
orgId: string,
|
|
816
|
+
members: Address[],
|
|
817
|
+
) => {
|
|
818
|
+
if (!this.user) {
|
|
819
|
+
throw new NotAuthenticatedError();
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const multiOwnerClient = this.experimental_createMultiOwnerTurnkeyClient();
|
|
823
|
+
|
|
824
|
+
const prepared = await this.request("/v1/multi-sig-prepare-add", {
|
|
825
|
+
members: members.map((evmSignerAddress) => ({ evmSignerAddress })),
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
const stampedRequest = await multiOwnerClient.stampCreateUsers({
|
|
829
|
+
organizationId: orgId,
|
|
830
|
+
type: "ACTIVITY_TYPE_CREATE_USERS_V3",
|
|
831
|
+
timestampMs: Date.now().toString(),
|
|
832
|
+
parameters: prepared,
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
const { updateRootQuorumIntent } = await this.request("/v1/multi-sig-add", {
|
|
836
|
+
stampedRequest,
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
await multiOwnerClient.updateRootQuorum({
|
|
840
|
+
organizationId: orgId,
|
|
841
|
+
type: "ACTIVITY_TYPE_UPDATE_ROOT_QUORUM",
|
|
842
|
+
timestampMs: Date.now().toString(),
|
|
843
|
+
parameters: updateRootQuorumIntent,
|
|
844
|
+
});
|
|
845
|
+
};
|
|
846
|
+
|
|
682
847
|
/**
|
|
683
848
|
* Returns the current user or null if no user is set.
|
|
684
849
|
*
|
|
@@ -936,7 +1101,10 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
|
|
|
936
1101
|
"exportWalletResult",
|
|
937
1102
|
);
|
|
938
1103
|
|
|
939
|
-
const result = await stamper.injectWalletExportBundle(
|
|
1104
|
+
const result = await stamper.injectWalletExportBundle(
|
|
1105
|
+
exportBundle,
|
|
1106
|
+
this.user.orgId,
|
|
1107
|
+
);
|
|
940
1108
|
|
|
941
1109
|
if (!result) {
|
|
942
1110
|
throw new Error("Failed to inject wallet export bundle");
|
|
@@ -966,7 +1134,10 @@ export abstract class BaseSignerClient<TExportWalletParams = unknown> {
|
|
|
966
1134
|
"exportWalletAccountResult",
|
|
967
1135
|
);
|
|
968
1136
|
|
|
969
|
-
const result = await stamper.injectKeyExportBundle(
|
|
1137
|
+
const result = await stamper.injectKeyExportBundle(
|
|
1138
|
+
exportBundle,
|
|
1139
|
+
this.user.orgId,
|
|
1140
|
+
);
|
|
970
1141
|
|
|
971
1142
|
if (!result) {
|
|
972
1143
|
throw new Error("Failed to inject wallet export bundle");
|
package/src/client/index.ts
CHANGED
|
@@ -55,7 +55,10 @@ export class AlchemySignerWebClient extends BaseSignerClient<ExportWalletParams>
|
|
|
55
55
|
private iframeStamper: IframeStamper;
|
|
56
56
|
private webauthnStamper: WebauthnStamper;
|
|
57
57
|
oauthCallbackUrl: string;
|
|
58
|
-
|
|
58
|
+
iframeConfig: {
|
|
59
|
+
iframeElementId: string;
|
|
60
|
+
iframeContainerId: string;
|
|
61
|
+
};
|
|
59
62
|
|
|
60
63
|
/**
|
|
61
64
|
* Initializes a new instance with the given parameters, setting up the connection, iframe configuration, and WebAuthn stamper.
|
|
@@ -97,7 +100,7 @@ export class AlchemySignerWebClient extends BaseSignerClient<ExportWalletParams>
|
|
|
97
100
|
});
|
|
98
101
|
|
|
99
102
|
this.iframeStamper = iframeStamper;
|
|
100
|
-
this.
|
|
103
|
+
this.iframeConfig = iframeConfig;
|
|
101
104
|
|
|
102
105
|
this.webauthnStamper = new WebauthnStamper({
|
|
103
106
|
rpId: rpId ?? window.location.hostname,
|
|
@@ -350,7 +353,19 @@ export class AlchemySignerWebClient extends BaseSignerClient<ExportWalletParams>
|
|
|
350
353
|
public override disconnect = async () => {
|
|
351
354
|
this.user = undefined;
|
|
352
355
|
this.iframeStamper.clear();
|
|
353
|
-
|
|
356
|
+
|
|
357
|
+
// In the latest version of the TK iframe stamper, the
|
|
358
|
+
// IframeStamper instance seems to not be usable after
|
|
359
|
+
// clearing it, so we need to create a new instance.
|
|
360
|
+
const stamper = new IframeStamper({
|
|
361
|
+
iframeContainer: document.getElementById(
|
|
362
|
+
this.iframeConfig.iframeContainerId,
|
|
363
|
+
),
|
|
364
|
+
iframeElementId: this.iframeConfig.iframeElementId,
|
|
365
|
+
iframeUrl: "https://auth.turnkey.com",
|
|
366
|
+
});
|
|
367
|
+
this.iframeStamper = stamper;
|
|
368
|
+
await this.initSessionStamper();
|
|
354
369
|
};
|
|
355
370
|
|
|
356
371
|
/**
|
|
@@ -610,14 +625,29 @@ export class AlchemySignerWebClient extends BaseSignerClient<ExportWalletParams>
|
|
|
610
625
|
return this.request("/v1/prepare-oauth", { nonce });
|
|
611
626
|
};
|
|
612
627
|
|
|
628
|
+
private initSessionStamperPromise: Promise<string> | null = null;
|
|
629
|
+
|
|
613
630
|
protected override async initSessionStamper(): Promise<string> {
|
|
614
|
-
if (
|
|
615
|
-
|
|
631
|
+
if (this.initSessionStamperPromise) {
|
|
632
|
+
return this.initSessionStamperPromise;
|
|
616
633
|
}
|
|
617
634
|
|
|
618
|
-
this.
|
|
635
|
+
this.initSessionStamperPromise = (async () => {
|
|
636
|
+
if (!this.iframeStamper.publicKey()) {
|
|
637
|
+
await this.iframeStamper.init();
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
this.setStamper(this.iframeStamper);
|
|
641
|
+
|
|
642
|
+
return this.iframeStamper.publicKey()!;
|
|
643
|
+
})();
|
|
619
644
|
|
|
620
|
-
|
|
645
|
+
try {
|
|
646
|
+
const result = await this.initSessionStamperPromise;
|
|
647
|
+
return result;
|
|
648
|
+
} finally {
|
|
649
|
+
this.initSessionStamperPromise = null;
|
|
650
|
+
}
|
|
621
651
|
}
|
|
622
652
|
|
|
623
653
|
protected override async initWebauthnStamper(
|
package/src/client/types.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { Address } from "@aa-sdk/core";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
TSignedRequest,
|
|
4
|
+
TurnkeyApiTypes,
|
|
5
|
+
getWebAuthnAttestation,
|
|
6
|
+
} from "@turnkey/http";
|
|
3
7
|
import type { Hex } from "viem";
|
|
4
8
|
import type { AuthParams } from "../signer";
|
|
5
9
|
|
|
@@ -264,6 +268,44 @@ export type SignerEndpoints = [
|
|
|
264
268
|
multiFactors: MfaFactor[];
|
|
265
269
|
};
|
|
266
270
|
},
|
|
271
|
+
{
|
|
272
|
+
Route: "/v1/multi-sig-create";
|
|
273
|
+
Body: {
|
|
274
|
+
quorum: number;
|
|
275
|
+
members: {
|
|
276
|
+
evmSignerAddress: Address;
|
|
277
|
+
}[];
|
|
278
|
+
};
|
|
279
|
+
Response: {
|
|
280
|
+
orgId: string;
|
|
281
|
+
quorum: number;
|
|
282
|
+
evmSignerAddress: Address;
|
|
283
|
+
members: {
|
|
284
|
+
evmSignerAddress: Address;
|
|
285
|
+
}[];
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
Route: "/v1/multi-sig-prepare-add";
|
|
290
|
+
Body: {
|
|
291
|
+
members: {
|
|
292
|
+
evmSignerAddress: Address;
|
|
293
|
+
}[];
|
|
294
|
+
};
|
|
295
|
+
Response: TurnkeyApiTypes["v1CreateUsersIntentV3"];
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
Route: "/v1/multi-sig-add";
|
|
299
|
+
Body: {
|
|
300
|
+
stampedRequest: TSignedRequest;
|
|
301
|
+
};
|
|
302
|
+
Response: {
|
|
303
|
+
members: {
|
|
304
|
+
evmSignerAddress: Address;
|
|
305
|
+
}[];
|
|
306
|
+
updateRootQuorumIntent: TurnkeyApiTypes["v1UpdateRootQuorumIntent"];
|
|
307
|
+
};
|
|
308
|
+
},
|
|
267
309
|
];
|
|
268
310
|
|
|
269
311
|
export type AuthenticatingEventMetadata = {
|
package/src/version.ts
CHANGED