@alchemy/wallet-apis 0.0.0-alpha.12
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/LICENSE +21 -0
- package/dist/esm/actions/formatSign.d.ts +35 -0
- package/dist/esm/actions/formatSign.js +38 -0
- package/dist/esm/actions/formatSign.js.map +1 -0
- package/dist/esm/actions/grantPermissions.d.ts +73 -0
- package/dist/esm/actions/grantPermissions.js +87 -0
- package/dist/esm/actions/grantPermissions.js.map +1 -0
- package/dist/esm/actions/listAccounts.d.ts +40 -0
- package/dist/esm/actions/listAccounts.js +43 -0
- package/dist/esm/actions/listAccounts.js.map +1 -0
- package/dist/esm/actions/prepareCalls.d.ts +44 -0
- package/dist/esm/actions/prepareCalls.js +58 -0
- package/dist/esm/actions/prepareCalls.js.map +1 -0
- package/dist/esm/actions/prepareSign.d.ts +33 -0
- package/dist/esm/actions/prepareSign.js +41 -0
- package/dist/esm/actions/prepareSign.js.map +1 -0
- package/dist/esm/actions/requestAccount.d.ts +33 -0
- package/dist/esm/actions/requestAccount.js +67 -0
- package/dist/esm/actions/requestAccount.js.map +1 -0
- package/dist/esm/actions/sendCalls.d.ts +34 -0
- package/dist/esm/actions/sendCalls.js +49 -0
- package/dist/esm/actions/sendCalls.js.map +1 -0
- package/dist/esm/actions/sendPreparedCalls.d.ts +43 -0
- package/dist/esm/actions/sendPreparedCalls.js +50 -0
- package/dist/esm/actions/sendPreparedCalls.js.map +1 -0
- package/dist/esm/actions/signMessage.d.ts +27 -0
- package/dist/esm/actions/signMessage.js +51 -0
- package/dist/esm/actions/signMessage.js.map +1 -0
- package/dist/esm/actions/signPreparedCalls.d.ts +32 -0
- package/dist/esm/actions/signPreparedCalls.js +88 -0
- package/dist/esm/actions/signPreparedCalls.js.map +1 -0
- package/dist/esm/actions/signSignatureRequest.d.ts +41 -0
- package/dist/esm/actions/signSignatureRequest.js +97 -0
- package/dist/esm/actions/signSignatureRequest.js.map +1 -0
- package/dist/esm/actions/signTypedData.d.ts +40 -0
- package/dist/esm/actions/signTypedData.js +66 -0
- package/dist/esm/actions/signTypedData.js.map +1 -0
- package/dist/esm/client.d.ts +34 -0
- package/dist/esm/client.js +62 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/decorators/smartWalletActions.d.ts +36 -0
- package/dist/esm/decorators/smartWalletActions.js +37 -0
- package/dist/esm/decorators/smartWalletActions.js.map +1 -0
- package/dist/esm/experimental/actions/requestQuoteV0.d.ts +54 -0
- package/dist/esm/experimental/actions/requestQuoteV0.js +62 -0
- package/dist/esm/experimental/actions/requestQuoteV0.js.map +1 -0
- package/dist/esm/experimental/swapActionsDecorator.d.ts +13 -0
- package/dist/esm/experimental/swapActionsDecorator.js +16 -0
- package/dist/esm/experimental/swapActionsDecorator.js.map +1 -0
- package/dist/esm/exports/experimental.d.ts +4 -0
- package/dist/esm/exports/experimental.js +3 -0
- package/dist/esm/exports/experimental.js.map +1 -0
- package/dist/esm/exports/index.d.ts +31 -0
- package/dist/esm/exports/index.js +16 -0
- package/dist/esm/exports/index.js.map +1 -0
- package/dist/esm/exports/internal.d.ts +6 -0
- package/dist/esm/exports/internal.js +4 -0
- package/dist/esm/exports/internal.js.map +1 -0
- package/dist/esm/internal.d.ts +11 -0
- package/dist/esm/internal.js +12 -0
- package/dist/esm/internal.js.map +1 -0
- package/dist/esm/logger.d.ts +2 -0
- package/dist/esm/logger.js +8 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/provider.d.ts +26 -0
- package/dist/esm/provider.js +191 -0
- package/dist/esm/provider.js.map +1 -0
- package/dist/esm/testSetup.d.ts +3 -0
- package/dist/esm/testSetup.js +18 -0
- package/dist/esm/testSetup.js.map +1 -0
- package/dist/esm/types.d.ts +34 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils/assertions.d.ts +18 -0
- package/dist/esm/utils/assertions.js +24 -0
- package/dist/esm/utils/assertions.js.map +1 -0
- package/dist/esm/utils/capabilities.d.ts +10 -0
- package/dist/esm/utils/capabilities.js +19 -0
- package/dist/esm/utils/capabilities.js.map +1 -0
- package/dist/esm/utils/format.d.ts +7 -0
- package/dist/esm/utils/format.js +37 -0
- package/dist/esm/utils/format.js.map +1 -0
- package/dist/esm/utils/viemDecode.d.ts +8 -0
- package/dist/esm/utils/viemDecode.js +231 -0
- package/dist/esm/utils/viemDecode.js.map +1 -0
- package/dist/esm/utils/viemEncode.d.ts +40 -0
- package/dist/esm/utils/viemEncode.js +216 -0
- package/dist/esm/utils/viemEncode.js.map +1 -0
- package/dist/esm/version.d.ts +1 -0
- package/dist/esm/version.js +4 -0
- package/dist/esm/version.js.map +1 -0
- package/dist/types/actions/formatSign.d.ts +36 -0
- package/dist/types/actions/formatSign.d.ts.map +1 -0
- package/dist/types/actions/grantPermissions.d.ts +74 -0
- package/dist/types/actions/grantPermissions.d.ts.map +1 -0
- package/dist/types/actions/listAccounts.d.ts +41 -0
- package/dist/types/actions/listAccounts.d.ts.map +1 -0
- package/dist/types/actions/prepareCalls.d.ts +45 -0
- package/dist/types/actions/prepareCalls.d.ts.map +1 -0
- package/dist/types/actions/prepareSign.d.ts +34 -0
- package/dist/types/actions/prepareSign.d.ts.map +1 -0
- package/dist/types/actions/requestAccount.d.ts +34 -0
- package/dist/types/actions/requestAccount.d.ts.map +1 -0
- package/dist/types/actions/sendCalls.d.ts +35 -0
- package/dist/types/actions/sendCalls.d.ts.map +1 -0
- package/dist/types/actions/sendPreparedCalls.d.ts +44 -0
- package/dist/types/actions/sendPreparedCalls.d.ts.map +1 -0
- package/dist/types/actions/signMessage.d.ts +28 -0
- package/dist/types/actions/signMessage.d.ts.map +1 -0
- package/dist/types/actions/signPreparedCalls.d.ts +33 -0
- package/dist/types/actions/signPreparedCalls.d.ts.map +1 -0
- package/dist/types/actions/signSignatureRequest.d.ts +42 -0
- package/dist/types/actions/signSignatureRequest.d.ts.map +1 -0
- package/dist/types/actions/signTypedData.d.ts +41 -0
- package/dist/types/actions/signTypedData.d.ts.map +1 -0
- package/dist/types/client.d.ts +35 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/decorators/smartWalletActions.d.ts +37 -0
- package/dist/types/decorators/smartWalletActions.d.ts.map +1 -0
- package/dist/types/experimental/actions/requestQuoteV0.d.ts +55 -0
- package/dist/types/experimental/actions/requestQuoteV0.d.ts.map +1 -0
- package/dist/types/experimental/swapActionsDecorator.d.ts +14 -0
- package/dist/types/experimental/swapActionsDecorator.d.ts.map +1 -0
- package/dist/types/exports/experimental.d.ts +5 -0
- package/dist/types/exports/experimental.d.ts.map +1 -0
- package/dist/types/exports/index.d.ts +32 -0
- package/dist/types/exports/index.d.ts.map +1 -0
- package/dist/types/exports/internal.d.ts +7 -0
- package/dist/types/exports/internal.d.ts.map +1 -0
- package/dist/types/internal.d.ts +12 -0
- package/dist/types/internal.d.ts.map +1 -0
- package/dist/types/logger.d.ts +3 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/provider.d.ts +27 -0
- package/dist/types/provider.d.ts.map +1 -0
- package/dist/types/testSetup.d.ts +4 -0
- package/dist/types/testSetup.d.ts.map +1 -0
- package/dist/types/types.d.ts +35 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/utils/assertions.d.ts +19 -0
- package/dist/types/utils/assertions.d.ts.map +1 -0
- package/dist/types/utils/capabilities.d.ts +11 -0
- package/dist/types/utils/capabilities.d.ts.map +1 -0
- package/dist/types/utils/format.d.ts +8 -0
- package/dist/types/utils/format.d.ts.map +1 -0
- package/dist/types/utils/viemDecode.d.ts +9 -0
- package/dist/types/utils/viemDecode.d.ts.map +1 -0
- package/dist/types/utils/viemEncode.d.ts +41 -0
- package/dist/types/utils/viemEncode.d.ts.map +1 -0
- package/dist/types/version.d.ts +2 -0
- package/dist/types/version.d.ts.map +1 -0
- package/package.json +76 -0
- package/src/actions/formatSign.ts +64 -0
- package/src/actions/grantPermissions.ts +125 -0
- package/src/actions/listAccounts.ts +66 -0
- package/src/actions/prepareCalls.ts +85 -0
- package/src/actions/prepareSign.ts +67 -0
- package/src/actions/requestAccount.ts +100 -0
- package/src/actions/sendCalls.ts +67 -0
- package/src/actions/sendPreparedCalls.ts +70 -0
- package/src/actions/signMessage.ts +74 -0
- package/src/actions/signPreparedCalls.ts +120 -0
- package/src/actions/signSignatureRequest.ts +135 -0
- package/src/actions/signTypedData.ts +90 -0
- package/src/client.ts +107 -0
- package/src/decorators/smartWalletActions.ts +123 -0
- package/src/experimental/actions/requestQuoteV0.ts +95 -0
- package/src/experimental/swapActionsDecorator.ts +34 -0
- package/src/exports/experimental.ts +7 -0
- package/src/exports/index.ts +44 -0
- package/src/exports/internal.ts +8 -0
- package/src/internal.ts +25 -0
- package/src/logger.ts +9 -0
- package/src/provider.ts +261 -0
- package/src/testSetup.ts +24 -0
- package/src/types.ts +61 -0
- package/src/utils/assertions.ts +32 -0
- package/src/utils/capabilities.ts +24 -0
- package/src/utils/format.ts +61 -0
- package/src/utils/viemDecode.ts +313 -0
- package/src/utils/viemEncode.ts +345 -0
- package/src/version.ts +3 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { assertNever, BaseError } from "@alchemy/common";
|
|
2
|
+
import type { PrepareCallsResult } from "./prepareCalls.ts";
|
|
3
|
+
import { signSignatureRequest } from "./signSignatureRequest.js";
|
|
4
|
+
import type { Prettify } from "viem";
|
|
5
|
+
import type {
|
|
6
|
+
PreparedCall_Authorization,
|
|
7
|
+
PreparedCall_UserOpV060,
|
|
8
|
+
PreparedCall_UserOpV070,
|
|
9
|
+
} from "@alchemy/wallet-api-types";
|
|
10
|
+
import type { InnerWalletApiClient } from "../types.js";
|
|
11
|
+
import { LOGGER } from "../logger.js";
|
|
12
|
+
import type { SendPreparedCallsParams } from "./sendPreparedCalls.js";
|
|
13
|
+
|
|
14
|
+
export type SignPreparedCallsParams = Prettify<PrepareCallsResult>;
|
|
15
|
+
|
|
16
|
+
export type SignPreparedCallsResult = SendPreparedCallsParams;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Signs prepared calls using the provided signer.
|
|
20
|
+
*
|
|
21
|
+
* @param {InnerWalletApiClient} client - The wallet client to use for signing
|
|
22
|
+
* @param {SignPreparedCallsParams} params - The prepared calls with signature requests
|
|
23
|
+
* @returns {Promise<SignPreparedCallsResult>} A Promise that resolves to the signed calls
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* // Prepare a user operation call.
|
|
28
|
+
* const preparedCalls = await client.prepareCalls({
|
|
29
|
+
* calls: [{
|
|
30
|
+
* to: "0x1234...",
|
|
31
|
+
* data: "0xabcdef...",
|
|
32
|
+
* value: "0x0"
|
|
33
|
+
* }],
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Sign the prepared calls.
|
|
37
|
+
* const signedCalls = await client.signPreparedCalls(preparedCalls);
|
|
38
|
+
*
|
|
39
|
+
* // Send the signed calls.
|
|
40
|
+
* const result = await client.sendPreparedCalls(signedCalls);
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export async function signPreparedCalls(
|
|
44
|
+
client: InnerWalletApiClient,
|
|
45
|
+
params: SignPreparedCallsParams,
|
|
46
|
+
): Promise<SignPreparedCallsResult> {
|
|
47
|
+
LOGGER.debug("signPreparedCalls:start", { type: params.type });
|
|
48
|
+
const signAuthorizationCall = async (call: PreparedCall_Authorization) => {
|
|
49
|
+
const { signatureRequest: _signatureRequest, ...rest } = call;
|
|
50
|
+
const signature = await signSignatureRequest(client, {
|
|
51
|
+
type: "eip7702Auth",
|
|
52
|
+
data: {
|
|
53
|
+
...rest.data,
|
|
54
|
+
chainId: call.chainId,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
...rest,
|
|
59
|
+
signature,
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const signUserOperationCall = async (
|
|
64
|
+
call: PreparedCall_UserOpV060 | PreparedCall_UserOpV070,
|
|
65
|
+
) => {
|
|
66
|
+
const { signatureRequest, ...rest } = call;
|
|
67
|
+
|
|
68
|
+
if (!signatureRequest) {
|
|
69
|
+
LOGGER.warn("signPreparedCalls:missing-signatureRequest", {
|
|
70
|
+
type: call.type,
|
|
71
|
+
});
|
|
72
|
+
throw new BaseError(
|
|
73
|
+
"Signature request is required for signing user operation calls. Ensure `onlyEstimation` is set to `false` when calling `prepareCalls`.",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const signature = await signSignatureRequest(client, signatureRequest);
|
|
78
|
+
const res = {
|
|
79
|
+
...rest,
|
|
80
|
+
signature,
|
|
81
|
+
} as const;
|
|
82
|
+
LOGGER.debug("signPreparedCalls:userOp:ok");
|
|
83
|
+
return res;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
if (params.type === "array") {
|
|
87
|
+
const res = {
|
|
88
|
+
type: "array" as const,
|
|
89
|
+
data: await Promise.all(
|
|
90
|
+
params.data.map((call) =>
|
|
91
|
+
call.type === "authorization"
|
|
92
|
+
? signAuthorizationCall(call)
|
|
93
|
+
: signUserOperationCall(call),
|
|
94
|
+
),
|
|
95
|
+
),
|
|
96
|
+
};
|
|
97
|
+
LOGGER.debug("signPreparedCalls:array:ok", { count: res.data.length });
|
|
98
|
+
return res;
|
|
99
|
+
} else if (
|
|
100
|
+
params.type === "user-operation-v060" ||
|
|
101
|
+
params.type === "user-operation-v070"
|
|
102
|
+
) {
|
|
103
|
+
const res = await signUserOperationCall(params);
|
|
104
|
+
LOGGER.debug("signPreparedCalls:single-userOp:ok");
|
|
105
|
+
return res;
|
|
106
|
+
} else if (params.type === "paymaster-permit") {
|
|
107
|
+
LOGGER.warn("signPreparedCalls:invalid-call-type", { type: params.type });
|
|
108
|
+
throw new BaseError(
|
|
109
|
+
`Invalid call type ${params.type} for signing prepared calls`,
|
|
110
|
+
);
|
|
111
|
+
} else {
|
|
112
|
+
LOGGER.warn("signPreparedCalls:unexpected-call-type", {
|
|
113
|
+
type: (params as { type?: unknown }).type,
|
|
114
|
+
});
|
|
115
|
+
return assertNever(
|
|
116
|
+
params,
|
|
117
|
+
`Unexpected call type in ${params} for signing prepared calls`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { type Hex, hexToNumber, type Prettify, serializeSignature } from "viem";
|
|
2
|
+
import type {
|
|
3
|
+
PersonalSignSignatureRequest,
|
|
4
|
+
TypedDataSignatureRequest,
|
|
5
|
+
AuthorizationSignatureRequest,
|
|
6
|
+
Eip7702UnsignedAuth,
|
|
7
|
+
} from "@alchemy/wallet-api-types";
|
|
8
|
+
import { vToYParity } from "ox/Signature";
|
|
9
|
+
import type { InnerWalletApiClient, WithoutRawPayload } from "../types";
|
|
10
|
+
import { assertNever } from "@alchemy/common";
|
|
11
|
+
import { getAction } from "viem/utils";
|
|
12
|
+
import { signAuthorization, signMessage, signTypedData } from "viem/actions";
|
|
13
|
+
import { LOGGER } from "../logger.js";
|
|
14
|
+
|
|
15
|
+
export type SignSignatureRequestParams = Prettify<
|
|
16
|
+
WithoutRawPayload<
|
|
17
|
+
| PersonalSignSignatureRequest
|
|
18
|
+
| TypedDataSignatureRequest
|
|
19
|
+
| (AuthorizationSignatureRequest & {
|
|
20
|
+
data: Eip7702UnsignedAuth;
|
|
21
|
+
})
|
|
22
|
+
>
|
|
23
|
+
>;
|
|
24
|
+
|
|
25
|
+
export type SignSignatureRequestResult = Prettify<{
|
|
26
|
+
type: "secp256k1";
|
|
27
|
+
data: Hex;
|
|
28
|
+
}>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Signs a signature request using the provided signer.
|
|
32
|
+
* This method handles different types of signature requests including personal_sign, eth_signTypedData_v4, and authorization.
|
|
33
|
+
*
|
|
34
|
+
* @param {SmartAccountSigner} signer - The signer to use for signing the request
|
|
35
|
+
* @param {SignSignatureRequestParams} params - The signature request parameters
|
|
36
|
+
* @param {string} params.type - The type of signature request ('personal_sign', 'eth_signTypedData_v4', or 'signature_with_authorization')
|
|
37
|
+
* @param {SignSignatureRequestParams["data"]} params.data - The data to sign, format depends on the signature type
|
|
38
|
+
* @returns {Promise<SignSignatureRequestResult>} A Promise that resolves to the signature result
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* // Sign a personal message
|
|
43
|
+
* const result = await client.signSignatureRequest({
|
|
44
|
+
* type: 'personal_sign',
|
|
45
|
+
* data: 'Hello, world!'
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* // Sign typed data (EIP-712)
|
|
49
|
+
* const result = await client.signSignatureRequest({
|
|
50
|
+
* type: 'eth_signTypedData_v4',
|
|
51
|
+
* data: {
|
|
52
|
+
* domain: { ... },
|
|
53
|
+
* types: { ... },
|
|
54
|
+
* primaryType: '...',
|
|
55
|
+
* message: { ... }
|
|
56
|
+
* }
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
export async function signSignatureRequest(
|
|
62
|
+
client: InnerWalletApiClient,
|
|
63
|
+
params: SignSignatureRequestParams,
|
|
64
|
+
): Promise<SignSignatureRequestResult> {
|
|
65
|
+
LOGGER.debug("signSignatureRequest:start", { type: params.type });
|
|
66
|
+
const actions = {
|
|
67
|
+
signMessage: getAction(client.owner, signMessage, "signMessage"),
|
|
68
|
+
signTypedData: getAction(client.owner, signTypedData, "signTypedData"),
|
|
69
|
+
signAuthorization: getAction(
|
|
70
|
+
client.owner,
|
|
71
|
+
signAuthorization,
|
|
72
|
+
"signAuthorization",
|
|
73
|
+
),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
switch (params.type) {
|
|
77
|
+
case "personal_sign": {
|
|
78
|
+
const res = {
|
|
79
|
+
type: "secp256k1",
|
|
80
|
+
data: await actions.signMessage({
|
|
81
|
+
message: params.data,
|
|
82
|
+
account: client.owner.account,
|
|
83
|
+
}),
|
|
84
|
+
} as const;
|
|
85
|
+
LOGGER.debug("signSignatureRequest:personal_sign:ok");
|
|
86
|
+
return res;
|
|
87
|
+
}
|
|
88
|
+
case "eth_signTypedData_v4": {
|
|
89
|
+
const res = {
|
|
90
|
+
type: "secp256k1",
|
|
91
|
+
data: await actions.signTypedData({
|
|
92
|
+
...params.data,
|
|
93
|
+
account: client.owner.account,
|
|
94
|
+
}),
|
|
95
|
+
} as const;
|
|
96
|
+
LOGGER.debug("signSignatureRequest:typedData:ok");
|
|
97
|
+
return res;
|
|
98
|
+
}
|
|
99
|
+
case "eip7702Auth": {
|
|
100
|
+
const {
|
|
101
|
+
r,
|
|
102
|
+
s,
|
|
103
|
+
v,
|
|
104
|
+
yParity: _yParity,
|
|
105
|
+
} = await actions.signAuthorization({
|
|
106
|
+
...{
|
|
107
|
+
...params.data,
|
|
108
|
+
chainId: hexToNumber(params.data.chainId),
|
|
109
|
+
nonce: hexToNumber(params.data.nonce),
|
|
110
|
+
},
|
|
111
|
+
account: client.owner.account,
|
|
112
|
+
});
|
|
113
|
+
// yParity *should* already be a number, but some 3rd
|
|
114
|
+
// party signers may mistakenly return it as a bigint.
|
|
115
|
+
const yParity =
|
|
116
|
+
_yParity != null ? Number(_yParity) : vToYParity(Number(v));
|
|
117
|
+
|
|
118
|
+
const res = {
|
|
119
|
+
type: "secp256k1",
|
|
120
|
+
data: serializeSignature({
|
|
121
|
+
r,
|
|
122
|
+
s,
|
|
123
|
+
yParity,
|
|
124
|
+
}),
|
|
125
|
+
} as const;
|
|
126
|
+
LOGGER.debug("signSignatureRequest:eip7702Auth:ok");
|
|
127
|
+
return res;
|
|
128
|
+
}
|
|
129
|
+
default:
|
|
130
|
+
LOGGER.warn("signSignatureRequest:unknown-type", {
|
|
131
|
+
type: (params as { type?: unknown }).type,
|
|
132
|
+
});
|
|
133
|
+
return assertNever(params, `Unexpected signature request type.`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Address,
|
|
3
|
+
type Hex,
|
|
4
|
+
type Prettify,
|
|
5
|
+
type TypedDataDefinition,
|
|
6
|
+
} from "viem";
|
|
7
|
+
import type { InnerWalletApiClient } from "../types.ts";
|
|
8
|
+
import { requestAccount } from "./requestAccount.js";
|
|
9
|
+
import { prepareSign } from "./prepareSign.js";
|
|
10
|
+
import { signSignatureRequest } from "./signSignatureRequest.js";
|
|
11
|
+
import { formatSign } from "./formatSign.js";
|
|
12
|
+
import { typedDataToJsonSafe } from "../utils/format.js";
|
|
13
|
+
import { LOGGER } from "../logger.js";
|
|
14
|
+
|
|
15
|
+
export type SignTypedDataParams = Prettify<
|
|
16
|
+
TypedDataDefinition & {
|
|
17
|
+
account?: Address;
|
|
18
|
+
}
|
|
19
|
+
>;
|
|
20
|
+
|
|
21
|
+
export type SignTypedDataResult = Prettify<Hex>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Signs typed data (EIP-712) using the smart account.
|
|
25
|
+
* This method requests the account associated with the signer and uses it to sign the typed data.
|
|
26
|
+
*
|
|
27
|
+
* @param {InnerWalletApiClient} client - The wallet API client to use for the request
|
|
28
|
+
* @param {SignerClient} signerClient - The wallet client to use for signing
|
|
29
|
+
* @param {TypedDataDefinition} params - The typed data to sign, following EIP-712 format
|
|
30
|
+
* @returns {Promise<SignTypedDataResult>} A Promise that resolves to the signature as a hex string
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* // Sign typed data
|
|
35
|
+
* const signature = await client.signTypedData({
|
|
36
|
+
* domain: {
|
|
37
|
+
* name: 'Example DApp',
|
|
38
|
+
* version: '1',
|
|
39
|
+
* chainId: 1,
|
|
40
|
+
* verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC'
|
|
41
|
+
* },
|
|
42
|
+
* types: {
|
|
43
|
+
* Person: [
|
|
44
|
+
* { name: 'name', type: 'string' },
|
|
45
|
+
* { name: 'wallet', type: 'address' }
|
|
46
|
+
* ]
|
|
47
|
+
* },
|
|
48
|
+
* primaryType: 'Person',
|
|
49
|
+
* message: {
|
|
50
|
+
* name: 'John Doe',
|
|
51
|
+
* wallet: '0xAaAaAaAaAaAaAaAaAaAAAAAAAAaaaAaAaAaaAaAa'
|
|
52
|
+
* }
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
export async function signTypedData(
|
|
58
|
+
client: InnerWalletApiClient,
|
|
59
|
+
params: SignTypedDataParams,
|
|
60
|
+
): Promise<SignTypedDataResult> {
|
|
61
|
+
LOGGER.debug("signTypedData:start", {
|
|
62
|
+
hasExplicitAccount: params.account != null,
|
|
63
|
+
primaryType: params.primaryType,
|
|
64
|
+
});
|
|
65
|
+
const accountAddress = params.account ?? client.account?.address;
|
|
66
|
+
const account = await requestAccount(
|
|
67
|
+
client,
|
|
68
|
+
accountAddress != null ? { accountAddress } : undefined,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const prepared = await prepareSign(client, {
|
|
72
|
+
from: account.address,
|
|
73
|
+
signatureRequest: {
|
|
74
|
+
type: "eth_signTypedData_v4",
|
|
75
|
+
data: typedDataToJsonSafe(params),
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const signed = await signSignatureRequest(client, prepared.signatureRequest);
|
|
80
|
+
|
|
81
|
+
const formatted = await formatSign(client, {
|
|
82
|
+
from: account.address,
|
|
83
|
+
signature: {
|
|
84
|
+
type: "ecdsa",
|
|
85
|
+
data: signed.data,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
LOGGER.debug("signTypedData:done", { from: account.address });
|
|
89
|
+
return formatted.signature;
|
|
90
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createClient,
|
|
3
|
+
type Address,
|
|
4
|
+
type Chain,
|
|
5
|
+
type LocalAccount,
|
|
6
|
+
createWalletClient,
|
|
7
|
+
} from "viem";
|
|
8
|
+
import { smartWalletActions } from "./decorators/smartWalletActions.js";
|
|
9
|
+
import { type AlchemyTransport } from "@alchemy/common";
|
|
10
|
+
import type { SignerClient, SmartWalletClient } from "./types.js";
|
|
11
|
+
import { createInternalState } from "./internal.js";
|
|
12
|
+
import type { CreationOptionsBySignerAddress } from "@alchemy/wallet-api-types";
|
|
13
|
+
|
|
14
|
+
export type CreateSmartWalletClientParams<
|
|
15
|
+
TAccount extends Address | undefined = Address | undefined,
|
|
16
|
+
> = {
|
|
17
|
+
signer: LocalAccount | SignerClient;
|
|
18
|
+
transport: AlchemyTransport;
|
|
19
|
+
chain: Chain;
|
|
20
|
+
account?: TAccount;
|
|
21
|
+
// TODO(v5): Reconsider if the client store store the policyIds, especially as
|
|
22
|
+
// new paymaster fields (i.e. for erc-20 support) are introduced. It might make
|
|
23
|
+
// more sense for them to be stored at a higher level like in the wagmi config
|
|
24
|
+
// or hooks.
|
|
25
|
+
policyId?: string;
|
|
26
|
+
policyIds?: string[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Creates a smart wallet client with wallet API actions.
|
|
31
|
+
*
|
|
32
|
+
* @param {CreateSmartWalletClientParams} params - Parameters for creating the smart wallet client.
|
|
33
|
+
* @param {LocalAccount | JsonRpcAccount} params.account - The account to use for signing.
|
|
34
|
+
* @param {AlchemyTransport} params.transport - The transport to use for RPC calls.
|
|
35
|
+
* @param {Chain} params.chain - The blockchain network to connect to.
|
|
36
|
+
* @param {string[]} params.policyIds - Optional policy IDs for paymaster service.
|
|
37
|
+
* @returns {WalletClient} A wallet client extended with smart wallet actions.
|
|
38
|
+
*/
|
|
39
|
+
export const createSmartWalletClient = <
|
|
40
|
+
TAccount extends Address | undefined = Address | undefined,
|
|
41
|
+
>({
|
|
42
|
+
account,
|
|
43
|
+
transport,
|
|
44
|
+
chain,
|
|
45
|
+
signer,
|
|
46
|
+
policyId,
|
|
47
|
+
policyIds,
|
|
48
|
+
}: CreateSmartWalletClientParams<TAccount>): SmartWalletClient<TAccount> => {
|
|
49
|
+
const _policyIds = [...(policyId ? [policyId] : []), ...(policyIds ?? [])];
|
|
50
|
+
|
|
51
|
+
// If the signer is a `LocalAccount` wrap it inside of a client now so
|
|
52
|
+
// downstream actions can just use `getAction` to get signing actions
|
|
53
|
+
// and `signerClient.account.address` to access the address.
|
|
54
|
+
const signerClient =
|
|
55
|
+
"request" in signer
|
|
56
|
+
? signer
|
|
57
|
+
: createWalletClient({
|
|
58
|
+
account: signer,
|
|
59
|
+
transport,
|
|
60
|
+
chain,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return createClient({
|
|
64
|
+
account,
|
|
65
|
+
transport,
|
|
66
|
+
chain,
|
|
67
|
+
name: "alchemySmartWalletClient",
|
|
68
|
+
})
|
|
69
|
+
.extend(() => ({
|
|
70
|
+
policyIds: _policyIds,
|
|
71
|
+
internal: createInternalState(),
|
|
72
|
+
owner: signerClient,
|
|
73
|
+
}))
|
|
74
|
+
.extend(smartWalletActions<TAccount>);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Creates a smart wallet client and requests an account in a single operation.
|
|
79
|
+
* This is a convenience function that combines client creation with account initialization.
|
|
80
|
+
*
|
|
81
|
+
* @param {CreateSmartWalletClientParams<undefined>} clientParams - Parameters for creating the smart wallet client (without an account).
|
|
82
|
+
* @param {CreationOptionsBySignerAddress | { accountAddress: Address }} accountOptions - Options for requesting the account. Can either be creation options for a new account or an object with an existing account address.
|
|
83
|
+
* @returns {Promise<SmartWalletClient<Address>>} A promise that resolves to a smart wallet client with an initialized account.
|
|
84
|
+
*/
|
|
85
|
+
export const createSmartWalletClientAndRequestAccount = async (
|
|
86
|
+
clientParams: CreateSmartWalletClientParams<undefined>,
|
|
87
|
+
accountOptions:
|
|
88
|
+
| CreationOptionsBySignerAddress
|
|
89
|
+
| { accountAddress: Address } = {},
|
|
90
|
+
): Promise<SmartWalletClient<Address>> => {
|
|
91
|
+
const clientWithoutAccount = createSmartWalletClient(clientParams);
|
|
92
|
+
|
|
93
|
+
const account = await clientWithoutAccount.requestAccount(
|
|
94
|
+
"accountAddress" in accountOptions
|
|
95
|
+
? {
|
|
96
|
+
accountAddress: accountOptions.accountAddress,
|
|
97
|
+
}
|
|
98
|
+
: {
|
|
99
|
+
creationHint: accountOptions,
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return createSmartWalletClient({
|
|
104
|
+
...clientParams,
|
|
105
|
+
account: account.address,
|
|
106
|
+
});
|
|
107
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { type Hex, type Address } from "viem";
|
|
2
|
+
import {
|
|
3
|
+
requestAccount,
|
|
4
|
+
type RequestAccountParams,
|
|
5
|
+
type RequestAccountResult,
|
|
6
|
+
} from "../actions/requestAccount.js";
|
|
7
|
+
import {
|
|
8
|
+
prepareCalls,
|
|
9
|
+
type PrepareCallsParams,
|
|
10
|
+
type PrepareCallsResult,
|
|
11
|
+
} from "../actions/prepareCalls.js";
|
|
12
|
+
import {
|
|
13
|
+
sendPreparedCalls,
|
|
14
|
+
type SendPreparedCallsParams,
|
|
15
|
+
type SendPreparedCallsResult,
|
|
16
|
+
} from "../actions/sendPreparedCalls.js";
|
|
17
|
+
import {
|
|
18
|
+
sendCalls,
|
|
19
|
+
type SendCallsParams,
|
|
20
|
+
type SendCallsResult,
|
|
21
|
+
} from "../actions/sendCalls.js";
|
|
22
|
+
import {
|
|
23
|
+
listAccounts,
|
|
24
|
+
type ListAccountsParams,
|
|
25
|
+
type ListAccountsResult,
|
|
26
|
+
} from "../actions/listAccounts.js";
|
|
27
|
+
import {
|
|
28
|
+
signSignatureRequest,
|
|
29
|
+
type SignSignatureRequestParams,
|
|
30
|
+
type SignSignatureRequestResult,
|
|
31
|
+
} from "../actions/signSignatureRequest.js";
|
|
32
|
+
import {
|
|
33
|
+
signPreparedCalls,
|
|
34
|
+
type SignPreparedCallsParams,
|
|
35
|
+
type SignPreparedCallsResult,
|
|
36
|
+
} from "../actions/signPreparedCalls.js";
|
|
37
|
+
import { signMessage, type SignMessageParams } from "../actions/signMessage.js";
|
|
38
|
+
import {
|
|
39
|
+
signTypedData,
|
|
40
|
+
type SignTypedDataParams,
|
|
41
|
+
} from "../actions/signTypedData.js";
|
|
42
|
+
import {
|
|
43
|
+
grantPermissions,
|
|
44
|
+
type GrantPermissionsParams,
|
|
45
|
+
type GrantPermissionsResult,
|
|
46
|
+
} from "../actions/grantPermissions.js";
|
|
47
|
+
import type { InnerWalletApiClient } from "../types.js";
|
|
48
|
+
import {
|
|
49
|
+
getCallsStatus,
|
|
50
|
+
waitForCallsStatus,
|
|
51
|
+
getCapabilities,
|
|
52
|
+
type GetCallsStatusParameters,
|
|
53
|
+
type GetCallsStatusReturnType,
|
|
54
|
+
type WaitForCallsStatusParameters,
|
|
55
|
+
type WaitForCallsStatusReturnType,
|
|
56
|
+
type GetCapabilitiesParameters,
|
|
57
|
+
type GetCapabilitiesReturnType,
|
|
58
|
+
} from "viem/actions";
|
|
59
|
+
|
|
60
|
+
export type SmartWalletActions<
|
|
61
|
+
TAccount extends Address | undefined = Address | undefined,
|
|
62
|
+
> = {
|
|
63
|
+
requestAccount: (
|
|
64
|
+
params?: RequestAccountParams,
|
|
65
|
+
) => Promise<RequestAccountResult>;
|
|
66
|
+
prepareCalls: (
|
|
67
|
+
params: PrepareCallsParams<TAccount>,
|
|
68
|
+
) => Promise<PrepareCallsResult>;
|
|
69
|
+
sendPreparedCalls: (
|
|
70
|
+
params: SendPreparedCallsParams,
|
|
71
|
+
) => Promise<SendPreparedCallsResult>;
|
|
72
|
+
sendCalls: (params: SendCallsParams<TAccount>) => Promise<SendCallsResult>;
|
|
73
|
+
listAccounts: (params: ListAccountsParams) => Promise<ListAccountsResult>;
|
|
74
|
+
signSignatureRequest: (
|
|
75
|
+
params: SignSignatureRequestParams,
|
|
76
|
+
) => Promise<SignSignatureRequestResult>;
|
|
77
|
+
signPreparedCalls: (
|
|
78
|
+
params: SignPreparedCallsParams,
|
|
79
|
+
) => Promise<SignPreparedCallsResult>;
|
|
80
|
+
signMessage: (params: SignMessageParams) => Promise<Hex>;
|
|
81
|
+
signTypedData: (params: SignTypedDataParams) => Promise<Hex>;
|
|
82
|
+
grantPermissions: (
|
|
83
|
+
params: GrantPermissionsParams<TAccount>,
|
|
84
|
+
) => Promise<GrantPermissionsResult>;
|
|
85
|
+
getCallsStatus: (
|
|
86
|
+
params: GetCallsStatusParameters,
|
|
87
|
+
) => Promise<GetCallsStatusReturnType>;
|
|
88
|
+
waitForCallsStatus: (
|
|
89
|
+
params: WaitForCallsStatusParameters,
|
|
90
|
+
) => Promise<WaitForCallsStatusReturnType>;
|
|
91
|
+
getCapabilities: (
|
|
92
|
+
params?: GetCapabilitiesParameters | undefined,
|
|
93
|
+
) => Promise<GetCapabilitiesReturnType>;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Decorator that adds smart wallet actions to a wallet API client.
|
|
98
|
+
* Provides both Alchemy-specific methods and standard viem wallet actions.
|
|
99
|
+
*
|
|
100
|
+
* @param {InnerWalletApiClient} client The wallet API client instance
|
|
101
|
+
* @returns {SmartWalletActions<TAccount>} An object containing smart wallet action methods
|
|
102
|
+
*/
|
|
103
|
+
export const smartWalletActions = <
|
|
104
|
+
TAccount extends Address | undefined = Address | undefined,
|
|
105
|
+
>(
|
|
106
|
+
client: InnerWalletApiClient,
|
|
107
|
+
): SmartWalletActions<TAccount> => ({
|
|
108
|
+
// Alchemy methods.
|
|
109
|
+
requestAccount: (params) => requestAccount(client, params),
|
|
110
|
+
prepareCalls: (params) => prepareCalls(client, params),
|
|
111
|
+
listAccounts: (params) => listAccounts(client, params),
|
|
112
|
+
sendPreparedCalls: (params) => sendPreparedCalls(client, params),
|
|
113
|
+
sendCalls: (params) => sendCalls(client, params), // TODO(v5): adapt this to fit viem's exact interface?
|
|
114
|
+
signSignatureRequest: (params) => signSignatureRequest(client, params),
|
|
115
|
+
signPreparedCalls: (params) => signPreparedCalls(client, params),
|
|
116
|
+
signMessage: (params) => signMessage(client, params),
|
|
117
|
+
signTypedData: (params) => signTypedData(client, params),
|
|
118
|
+
grantPermissions: (params) => grantPermissions(client, params),
|
|
119
|
+
// Viem methods.
|
|
120
|
+
getCallsStatus: (params) => getCallsStatus(client, params),
|
|
121
|
+
waitForCallsStatus: (params) => waitForCallsStatus(client, params),
|
|
122
|
+
getCapabilities: (params) => getCapabilities(client, params),
|
|
123
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
toHex,
|
|
3
|
+
type Address,
|
|
4
|
+
type IsUndefined,
|
|
5
|
+
type Prettify,
|
|
6
|
+
type UnionOmit,
|
|
7
|
+
} from "viem";
|
|
8
|
+
import type { OptionalChainId, InnerWalletApiClient } from "../../types.ts";
|
|
9
|
+
import type { WalletServerRpcSchemaType } from "@alchemy/wallet-api-types/rpc";
|
|
10
|
+
import { AccountNotFoundError } from "@alchemy/common";
|
|
11
|
+
import { mergeClientCapabilities } from "../../utils/capabilities.js";
|
|
12
|
+
|
|
13
|
+
type RpcSchema = Extract<
|
|
14
|
+
WalletServerRpcSchemaType,
|
|
15
|
+
{
|
|
16
|
+
Request: {
|
|
17
|
+
method: "wallet_requestQuote_v0";
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
export type RequestQuoteV0Params<
|
|
23
|
+
TAccount extends Address | undefined = Address | undefined,
|
|
24
|
+
> = Prettify<
|
|
25
|
+
OptionalChainId<UnionOmit<RpcSchema["Request"]["params"][0], "from">>
|
|
26
|
+
> &
|
|
27
|
+
(IsUndefined<TAccount> extends true ? { from: Address } : { from?: never });
|
|
28
|
+
|
|
29
|
+
export type RequestQuoteV0Result = Prettify<RpcSchema["ReturnType"]>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Requests a quote for a token swap, returning either prepared calls for smart wallets
|
|
33
|
+
* or raw calls for EOA wallets depending on the returnRawCalls parameter.
|
|
34
|
+
*
|
|
35
|
+
* @param {InnerWalletApiClient} client - The wallet API client to use for the request
|
|
36
|
+
* @param {RequestQuoteV0Params<TAccount>} params - Parameters for requesting a swap quote
|
|
37
|
+
* @param {Address} params.fromToken - The address of the token to swap from
|
|
38
|
+
* @param {Address} params.toToken - The address of the token to swap to
|
|
39
|
+
* @param {Hex} [params.fromAmount] - The amount to swap from (mutually exclusive with minimumToAmount)
|
|
40
|
+
* @param {Hex} [params.minimumToAmount] - The minimum amount to receive (mutually exclusive with fromAmount)
|
|
41
|
+
* @param {Address} [params.from] - The address to execute the swap from (required if the client wasn't initialized with an account)
|
|
42
|
+
* @param {Hex} [params.slippage] - The maximum acceptable slippage percentage
|
|
43
|
+
* @param {boolean} [params.returnRawCalls] - Whether to return raw calls for EOA wallets (defaults to false for smart wallets)
|
|
44
|
+
* @param {object} [params.capabilities] - Optional capabilities to include with the request (only available when returnRawCalls is false)
|
|
45
|
+
* @param {Array<{to: Address, data?: Hex, value?: Hex}>} [params.postCalls] - Optional calls to execute after the swap
|
|
46
|
+
* @returns {Promise<RequestQuoteV0Result>} A Promise that resolves to either prepared calls or raw calls depending on returnRawCalls
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts twoslash
|
|
50
|
+
* // Request a quote for smart wallet (prepared calls)
|
|
51
|
+
* const quote = await client.requestQuoteV0({
|
|
52
|
+
* fromToken: "0xA0b86a33E6441e1d6a8E8C7a8E8E8E8E8E8E8E8E",
|
|
53
|
+
* toToken: "0xB0b86a33E6441e1d6a8E8C7a8E8E8E8E8E8E8E8E",
|
|
54
|
+
* fromAmount: "0x1000000000000000000", // 1 ETH
|
|
55
|
+
* capabilities: {
|
|
56
|
+
* paymasterService: { policyId: "your-policy-id" }
|
|
57
|
+
* }
|
|
58
|
+
* });
|
|
59
|
+
*
|
|
60
|
+
* // Request a quote for EOA wallet (raw calls)
|
|
61
|
+
* const rawQuote = await client.requestQuoteV0({
|
|
62
|
+
* fromToken: "0xA0b86a33E6441e1d6a8E8C7a8E8E8E8E8E8E8E8E",
|
|
63
|
+
* toToken: "0xB0b86a33E6441e1d6a8E8C7a8E8E8E8E8E8E8E8E",
|
|
64
|
+
* fromAmount: "0x1000000000000000000",
|
|
65
|
+
* returnRawCalls: true
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export async function requestQuoteV0<
|
|
70
|
+
TAccount extends Address | undefined = Address | undefined,
|
|
71
|
+
>(
|
|
72
|
+
client: InnerWalletApiClient,
|
|
73
|
+
params: RequestQuoteV0Params<TAccount>,
|
|
74
|
+
): Promise<RequestQuoteV0Result> {
|
|
75
|
+
const from = params.from ?? client.account?.address;
|
|
76
|
+
if (!from) {
|
|
77
|
+
throw new AccountNotFoundError();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const capabilities = params.returnRawCalls
|
|
81
|
+
? undefined
|
|
82
|
+
: mergeClientCapabilities(client, params.capabilities);
|
|
83
|
+
|
|
84
|
+
return await client.request({
|
|
85
|
+
method: "wallet_requestQuote_v0",
|
|
86
|
+
params: [
|
|
87
|
+
{
|
|
88
|
+
...params,
|
|
89
|
+
chainId: params.chainId ?? toHex(client.chain.id),
|
|
90
|
+
from,
|
|
91
|
+
...(capabilities && { capabilities }),
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
});
|
|
95
|
+
}
|