@account-kit/wallet-client 0.1.0-alpha.2 → 0.1.0-alpha.4

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 (119) hide show
  1. package/dist/esm/capabilities/index.d.ts +5 -2
  2. package/dist/esm/capabilities/index.js +1 -1
  3. package/dist/esm/capabilities/index.js.map +1 -1
  4. package/dist/esm/capabilities/overrides.js +8 -8
  5. package/dist/esm/capabilities/overrides.js.map +1 -1
  6. package/dist/esm/capabilities/permissions/index.d.ts +10 -3
  7. package/dist/esm/capabilities/permissions/index.js +11 -2
  8. package/dist/esm/capabilities/permissions/index.js.map +1 -1
  9. package/dist/esm/capabilities/permissions/mav2.d.ts +2 -2
  10. package/dist/esm/capabilities/permissions/mav2.js +9 -1
  11. package/dist/esm/capabilities/permissions/mav2.js.map +1 -1
  12. package/dist/esm/client/actions/grantPermissions.d.ts +57 -3
  13. package/dist/esm/client/actions/grantPermissions.js +1 -0
  14. package/dist/esm/client/actions/grantPermissions.js.map +1 -1
  15. package/dist/esm/client/actions/requestAccount.js +2 -2
  16. package/dist/esm/client/actions/requestAccount.js.map +1 -1
  17. package/dist/esm/client/actions/signSignatureRequest.d.ts +4 -1
  18. package/dist/esm/client/actions/signSignatureRequest.js +22 -1
  19. package/dist/esm/client/actions/signSignatureRequest.js.map +1 -1
  20. package/dist/esm/client/client.e2e-test.js +13 -19
  21. package/dist/esm/client/client.e2e-test.js.map +1 -1
  22. package/dist/esm/client/index.d.ts +4 -3
  23. package/dist/esm/client/index.js +2 -2
  24. package/dist/esm/client/index.js.map +1 -1
  25. package/dist/esm/exports/index.d.ts +2 -2
  26. package/dist/esm/exports/index.js +1 -1
  27. package/dist/esm/exports/index.js.map +1 -1
  28. package/dist/esm/isomorphic/actions/createSession.js +25 -7
  29. package/dist/esm/isomorphic/actions/createSession.js.map +1 -1
  30. package/dist/esm/isomorphic/actions/prepareCalls.js +35 -12
  31. package/dist/esm/isomorphic/actions/prepareCalls.js.map +1 -1
  32. package/dist/esm/isomorphic/actions/sendPreparedCalls.js +26 -6
  33. package/dist/esm/isomorphic/actions/sendPreparedCalls.js.map +1 -1
  34. package/dist/esm/isomorphic/client.d.ts +11 -5
  35. package/dist/esm/isomorphic/utils/7702.d.ts +11 -0
  36. package/dist/esm/isomorphic/utils/7702.js +26 -0
  37. package/dist/esm/isomorphic/utils/7702.js.map +1 -0
  38. package/dist/esm/isomorphic/utils/createAccount.d.ts +2 -1
  39. package/dist/esm/isomorphic/utils/createAccount.js +30 -5
  40. package/dist/esm/isomorphic/utils/createAccount.js.map +1 -1
  41. package/dist/esm/isomorphic/utils/createDummySigner.js +3 -3
  42. package/dist/esm/isomorphic/utils/createDummySigner.js.map +1 -1
  43. package/dist/esm/isomorphic/utils/parsePermissionsContext.d.ts +2 -1
  44. package/dist/esm/isomorphic/utils/parsePermissionsContext.js +17 -5
  45. package/dist/esm/isomorphic/utils/parsePermissionsContext.js.map +1 -1
  46. package/dist/esm/local/client.js +2 -1
  47. package/dist/esm/local/client.js.map +1 -1
  48. package/dist/esm/remote/client.js +6 -2
  49. package/dist/esm/remote/client.js.map +1 -1
  50. package/dist/esm/rpc/examples.d.ts +230 -0
  51. package/dist/esm/rpc/examples.js +314 -0
  52. package/dist/esm/rpc/examples.js.map +1 -0
  53. package/dist/esm/rpc/request.d.ts +48 -17
  54. package/dist/esm/rpc/request.js +53 -14
  55. package/dist/esm/rpc/request.js.map +1 -1
  56. package/dist/esm/rpc/schema.d.ts +42 -11
  57. package/dist/esm/schemas.d.ts +29 -7
  58. package/dist/esm/schemas.js +120 -38
  59. package/dist/esm/schemas.js.map +1 -1
  60. package/dist/types/capabilities/index.d.ts +5 -2
  61. package/dist/types/capabilities/index.d.ts.map +1 -1
  62. package/dist/types/capabilities/overrides.d.ts.map +1 -1
  63. package/dist/types/capabilities/permissions/index.d.ts +10 -3
  64. package/dist/types/capabilities/permissions/index.d.ts.map +1 -1
  65. package/dist/types/capabilities/permissions/mav2.d.ts +2 -2
  66. package/dist/types/capabilities/permissions/mav2.d.ts.map +1 -1
  67. package/dist/types/client/actions/grantPermissions.d.ts +57 -3
  68. package/dist/types/client/actions/grantPermissions.d.ts.map +1 -1
  69. package/dist/types/client/actions/requestAccount.d.ts.map +1 -1
  70. package/dist/types/client/actions/signSignatureRequest.d.ts +4 -1
  71. package/dist/types/client/actions/signSignatureRequest.d.ts.map +1 -1
  72. package/dist/types/client/index.d.ts +4 -3
  73. package/dist/types/client/index.d.ts.map +1 -1
  74. package/dist/types/exports/index.d.ts +2 -2
  75. package/dist/types/exports/index.d.ts.map +1 -1
  76. package/dist/types/isomorphic/actions/createSession.d.ts.map +1 -1
  77. package/dist/types/isomorphic/actions/prepareCalls.d.ts.map +1 -1
  78. package/dist/types/isomorphic/actions/sendPreparedCalls.d.ts.map +1 -1
  79. package/dist/types/isomorphic/client.d.ts +11 -5
  80. package/dist/types/isomorphic/client.d.ts.map +1 -1
  81. package/dist/types/isomorphic/utils/7702.d.ts +12 -0
  82. package/dist/types/isomorphic/utils/7702.d.ts.map +1 -0
  83. package/dist/types/isomorphic/utils/createAccount.d.ts +2 -1
  84. package/dist/types/isomorphic/utils/createAccount.d.ts.map +1 -1
  85. package/dist/types/isomorphic/utils/createDummySigner.d.ts.map +1 -1
  86. package/dist/types/isomorphic/utils/parsePermissionsContext.d.ts +2 -1
  87. package/dist/types/isomorphic/utils/parsePermissionsContext.d.ts.map +1 -1
  88. package/dist/types/remote/client.d.ts.map +1 -1
  89. package/dist/types/rpc/examples.d.ts +231 -0
  90. package/dist/types/rpc/examples.d.ts.map +1 -0
  91. package/dist/types/rpc/request.d.ts +48 -17
  92. package/dist/types/rpc/request.d.ts.map +1 -1
  93. package/dist/types/rpc/schema.d.ts +42 -11
  94. package/dist/types/rpc/schema.d.ts.map +1 -1
  95. package/dist/types/schemas.d.ts +29 -7
  96. package/dist/types/schemas.d.ts.map +1 -1
  97. package/package.json +2 -2
  98. package/src/capabilities/index.ts +5 -8
  99. package/src/capabilities/overrides.ts +23 -8
  100. package/src/capabilities/permissions/index.ts +21 -5
  101. package/src/capabilities/permissions/mav2.ts +12 -2
  102. package/src/client/actions/grantPermissions.ts +57 -3
  103. package/src/client/actions/requestAccount.ts +2 -3
  104. package/src/client/actions/signSignatureRequest.ts +31 -3
  105. package/src/client/client.e2e-test.ts +14 -21
  106. package/src/client/index.ts +12 -10
  107. package/src/exports/index.ts +2 -1
  108. package/src/isomorphic/actions/createSession.ts +28 -7
  109. package/src/isomorphic/actions/prepareCalls.ts +38 -11
  110. package/src/isomorphic/actions/sendPreparedCalls.ts +47 -20
  111. package/src/isomorphic/utils/7702.ts +58 -0
  112. package/src/isomorphic/utils/createAccount.ts +38 -6
  113. package/src/isomorphic/utils/createDummySigner.ts +3 -2
  114. package/src/isomorphic/utils/parsePermissionsContext.ts +23 -7
  115. package/src/local/client.ts +2 -3
  116. package/src/remote/client.ts +12 -2
  117. package/src/rpc/examples.ts +343 -0
  118. package/src/rpc/request.ts +75 -26
  119. package/src/schemas.ts +218 -87
@@ -122,7 +122,7 @@ describe("Client E2E Tests", () => {
122
122
 
123
123
  it("should successfully send a UO with paymaster", async () => {
124
124
  const account = await client.requestAccount();
125
- const preparedUO = await client.prepareCalls({
125
+ const preparedCall = await client.prepareCalls({
126
126
  calls: [{ to: zeroAddress, value: "0x0" }],
127
127
  from: account.address,
128
128
  capabilities: {
@@ -132,20 +132,13 @@ describe("Client E2E Tests", () => {
132
132
  },
133
133
  });
134
134
 
135
- if (preparedUO.signatureRequest.type !== "personal_sign") {
136
- throw new Error("Invalid signature request type");
137
- }
138
-
139
- const signature = await signer.signMessage(
140
- preparedUO.signatureRequest.data,
135
+ const signature = await client.signSignatureRequest(
136
+ preparedCall.signatureRequest,
141
137
  );
142
138
 
143
139
  const result = await client.sendPreparedCalls({
144
- ...preparedUO,
145
- signature: {
146
- type: "ecdsa",
147
- signature,
148
- },
140
+ ...preparedCall,
141
+ signature,
149
142
  });
150
143
 
151
144
  expect(result.preparedCallIds).toBeArrayOfSize(1);
@@ -166,7 +159,7 @@ describe("Client E2E Tests", () => {
166
159
  permissions: [{ type: "root" }],
167
160
  });
168
161
 
169
- const preparedUO = await client.prepareCalls({
162
+ const preparedCall = await client.prepareCalls({
170
163
  calls: [{ to: zeroAddress, value: "0x0" }],
171
164
  from: account.address,
172
165
  capabilities: {
@@ -179,11 +172,11 @@ describe("Client E2E Tests", () => {
179
172
 
180
173
  const signature = await signSignatureRequest(
181
174
  sessionKey,
182
- preparedUO.signatureRequest,
175
+ preparedCall.signatureRequest,
183
176
  );
184
177
 
185
178
  const result = await client.sendPreparedCalls({
186
- ...preparedUO,
179
+ ...preparedCall,
187
180
  signature,
188
181
  capabilities: {
189
182
  permissions,
@@ -302,7 +295,7 @@ describe("Client E2E Tests", () => {
302
295
 
303
296
  it("should successfully send a UO with paymaster", async () => {
304
297
  const account = await client.requestAccount();
305
- const preparedUO = await client.prepareCalls({
298
+ const preparedCall = await client.prepareCalls({
306
299
  calls: [{ to: zeroAddress, value: "0x0" }],
307
300
  from: account.address,
308
301
  capabilities: {
@@ -313,11 +306,11 @@ describe("Client E2E Tests", () => {
313
306
  });
314
307
 
315
308
  const signature = await client.signSignatureRequest(
316
- preparedUO.signatureRequest,
309
+ preparedCall.signatureRequest,
317
310
  );
318
311
 
319
312
  const result = await client.sendPreparedCalls({
320
- ...preparedUO,
313
+ ...preparedCall,
321
314
  signature,
322
315
  });
323
316
 
@@ -339,7 +332,7 @@ describe("Client E2E Tests", () => {
339
332
  permissions: [{ type: "root" }],
340
333
  });
341
334
 
342
- const preparedUO = await client.prepareCalls({
335
+ const preparedCall = await client.prepareCalls({
343
336
  calls: [{ to: zeroAddress, value: "0x0" }],
344
337
  from: account.address,
345
338
  capabilities: {
@@ -352,11 +345,11 @@ describe("Client E2E Tests", () => {
352
345
 
353
346
  const signature = await signSignatureRequest(
354
347
  sessionKey,
355
- preparedUO.signatureRequest,
348
+ preparedCall.signatureRequest,
356
349
  );
357
350
 
358
351
  const result = await client.sendPreparedCalls({
359
- ...preparedUO,
352
+ ...preparedCall,
360
353
  signature,
361
354
  capabilities: {
362
355
  permissions,
@@ -1,4 +1,5 @@
1
1
  import type { SmartAccountSigner } from "@aa-sdk/core";
2
+ import type { AlchemyTransport } from "@account-kit/infra";
2
3
  import type {
3
4
  Address,
4
5
  Chain,
@@ -13,7 +14,6 @@ import {
13
14
  smartWalletClientActions,
14
15
  type SmartWalletActions,
15
16
  } from "./decorator.js";
16
- import type { AlchemyTransport } from "@account-kit/infra";
17
17
 
18
18
  export type SmartWalletClientParams<
19
19
  TAccount extends JsonRpcAccount<Address> | undefined =
@@ -35,19 +35,21 @@ export type SmartWalletClientParams<
35
35
  })
36
36
  >;
37
37
 
38
+ export type SmartWalletClient<
39
+ TAccount extends JsonRpcAccount<Address> | undefined =
40
+ | JsonRpcAccount<Address>
41
+ | undefined,
42
+ > = InnerWalletApiClient<TAccount> & SmartWalletActions<TAccount>;
43
+
38
44
  export function createSmartWalletClient<
39
45
  TAccount extends JsonRpcAccount<Address> | undefined =
40
46
  | JsonRpcAccount<Address>
41
47
  | undefined,
42
- >(
43
- params: SmartWalletClientParams<TAccount>,
44
- ): InnerWalletApiClient<TAccount> & SmartWalletActions<TAccount>;
48
+ >(params: SmartWalletClientParams<TAccount>): SmartWalletClient<TAccount>;
45
49
 
46
50
  export function createSmartWalletClient<
47
51
  TAccount extends JsonRpcAccount<Address> = JsonRpcAccount<Address>,
48
- >(
49
- params: SmartWalletClientParams<TAccount>,
50
- ): InnerWalletApiClient<TAccount> & SmartWalletActions<TAccount>;
52
+ >(params: SmartWalletClientParams<TAccount>): SmartWalletClient<TAccount>;
51
53
 
52
54
  /**
53
55
  * Creates a smart wallet client that can be used to interact with a smart account.
@@ -59,13 +61,13 @@ export function createSmartWalletClient<
59
61
  * @param {"local" | "remote"} params.mode - The client's mode (local or remote).
60
62
  * @param {string} [params.policyId] - The policy ID for gas sponsorship (optional)
61
63
  * @param {Address} [params.account] - The smart account address to use (optional)
62
- * @returns {InnerWalletApiClient & SmartWalletActions} - A viem-compatible client
64
+ * @returns {SmartWalletClient} - A viem-compatible client
63
65
  *
64
66
  * @example
65
67
  * import { LocalAccountSigner } from "@aa-sdk/core";
66
68
  * import { alchemy, arbitrumSepolia } from "@account-kit/infra";
67
69
  * import { generatePrivateKey } from "viem/accounts";
68
- * import { createSmartWalletClient } from "@account-kit/wallet-apis"; // TODO(jh): update this if we change the pkg name to `@account-kit/wallet-client`
70
+ * import { createSmartWalletClient } from "@account-kit/wallet-client";
69
71
  *
70
72
  * const signer = LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey());
71
73
  * const transport = alchemy({
@@ -80,7 +82,7 @@ export function createSmartWalletClient<
80
82
  */
81
83
  export function createSmartWalletClient(
82
84
  params: SmartWalletClientParams,
83
- ): InnerWalletApiClient & SmartWalletActions {
85
+ ): SmartWalletClient {
84
86
  const { transport, chain, policyId, mode, account, signer } = params;
85
87
 
86
88
  const innerClient =
@@ -2,6 +2,7 @@
2
2
  // and we shouldn't export * for the sake of tree-shaking
3
3
  export {
4
4
  createSmartWalletClient,
5
+ type SmartWalletClient,
5
6
  type SmartWalletClientParams,
6
7
  } from "../client/index.js";
7
8
 
@@ -24,10 +25,10 @@ export {
24
25
  // client actions
25
26
  export { createAccount } from "../client/actions/createAccount.js";
26
27
  export { getCallsStatus } from "../client/actions/getCallsStatus.js";
28
+ export { grantPermissions } from "../client/actions/grantPermissions.js";
27
29
  export { listAccounts } from "../client/actions/listAccounts.js";
28
30
  export { prepareCalls } from "../client/actions/prepareCalls.js";
29
31
  export { requestAccount } from "../client/actions/requestAccount.js";
30
32
  export { signMessage } from "../client/actions/signMessage.js";
31
33
  export { signSignatureRequest } from "../client/actions/signSignatureRequest.js";
32
34
  export { signTypedData } from "../client/actions/signTypedData.js";
33
- export { grantPermissions } from "../client/actions/grantPermissions.js";
@@ -24,6 +24,8 @@ import type { wallet_createSession } from "../../rpc/request.js";
24
24
  import type { WalletServerViemRpcSchema } from "../../rpc/schema.js";
25
25
  import { createAccount, isModularAccountV2 } from "../utils/createAccount.js";
26
26
  import { createDummySigner } from "../utils/createDummySigner.js";
27
+ import { createAuthorization } from "../utils/7702.js";
28
+ import { InvalidRequestError } from "ox/RpcResponse";
27
29
 
28
30
  export type CreateSessionParams = Omit<
29
31
  Static<
@@ -57,17 +59,20 @@ export async function createSession(
57
59
  throw new ChainNotFoundError();
58
60
  }
59
61
 
60
- const { counterfactualInfo } = await client.request({
62
+ const { counterfactualInfo, delegation } = await client.request({
61
63
  method: "wallet_requestAccount",
62
64
  params: [
63
65
  {
64
- includeCounterfactualInfo: true,
65
66
  accountAddress: params.account,
67
+ includeCounterfactualInfo: true,
66
68
  },
67
69
  ],
68
70
  });
69
- if (!counterfactualInfo) {
70
- throw new Error("No counterfactual info found.");
71
+ if (!counterfactualInfo && !delegation) {
72
+ throw new InvalidRequestError({
73
+ message:
74
+ "No counterfactual info or delegated implementation address found.",
75
+ });
71
76
  }
72
77
 
73
78
  const account = await createAccount({
@@ -76,10 +81,13 @@ export async function createSession(
76
81
  signer: createDummySigner(params.account),
77
82
  accountAddress: params.account,
78
83
  counterfactualInfo,
84
+ delegation,
79
85
  });
80
86
 
81
87
  if (!isModularAccountV2(account)) {
82
- throw new Error("Sessions are currently only supported by MAv2 accounts.");
88
+ throw new InvalidRequestError({
89
+ message: "Sessions are currently only supported by MAv2 accounts.",
90
+ });
83
91
  }
84
92
 
85
93
  const _client = createSmartAccountClient({
@@ -108,12 +116,25 @@ export async function createSession(
108
116
  })
109
117
  .compileDeferred();
110
118
 
119
+ // If using 7702, we need an Authorization (unless it's already authorized).
120
+ const authorizationRequest = delegation
121
+ ? await createAuthorization(client, {
122
+ address: account.address,
123
+ delegation,
124
+ })
125
+ : undefined;
126
+
127
+ const signatureRequest = {
128
+ type: "eth_signTypedData_v4" as const,
129
+ data: typedData,
130
+ };
131
+
111
132
  return {
112
133
  sessionId: null, // In remote mode, the server will set this later.
113
134
  entityId: toHex(entityId),
114
135
  signatureRequest: {
115
- type: "eth_signTypedData_v4",
116
- data: typedData,
136
+ ...signatureRequest,
137
+ ...(authorizationRequest ? { authorizationRequest } : {}),
117
138
  },
118
139
  fullPreSignatureDeferredActionDigest,
119
140
  };
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  deepHexlify,
3
+ default7702GasEstimator,
3
4
  type SmartAccountClient,
4
5
  type SmartContractAccount,
5
6
  } from "@aa-sdk/core";
@@ -9,7 +10,6 @@ import {
9
10
  custom,
10
11
  fromHex,
11
12
  toHex,
12
- zeroAddress,
13
13
  type Chain,
14
14
  type Transport,
15
15
  } from "viem";
@@ -17,6 +17,8 @@ import type { wallet_prepareCalls } from "../../rpc/request.js";
17
17
  import type { WalletServerViemRpcSchema } from "../../rpc/schema.js";
18
18
  import { createAccount } from "../utils/createAccount.js";
19
19
  import { createDummySigner } from "../utils/createDummySigner.js";
20
+ import { createAuthorization } from "../utils/7702.js";
21
+ import { InvalidRequestError } from "ox/RpcResponse";
20
22
 
21
23
  export type PrepareCallsParams = Omit<
22
24
  Static<
@@ -29,7 +31,6 @@ export type PrepareCallsResult = Static<
29
31
  (typeof wallet_prepareCalls)["properties"]["ReturnType"]
30
32
  >;
31
33
 
32
- // TODO: handle capabilities like permissions and paymaster here
33
34
  export async function prepareCalls(
34
35
  client: SmartAccountClient<
35
36
  Transport,
@@ -45,7 +46,7 @@ export async function prepareCalls(
45
46
  }
46
47
 
47
48
  // in local mode, we probably want some kind of caching for this
48
- const { counterfactualInfo } = await client.request({
49
+ const { counterfactualInfo, delegation } = await client.request({
49
50
  method: "wallet_requestAccount",
50
51
  params: [
51
52
  {
@@ -54,19 +55,37 @@ export async function prepareCalls(
54
55
  },
55
56
  ],
56
57
  });
57
- if (!counterfactualInfo) {
58
- throw new Error("No counterfactual info found.");
58
+
59
+ if (!counterfactualInfo && !delegation) {
60
+ throw new InvalidRequestError({
61
+ message:
62
+ "No counterfactual info or delegated implementation address found.",
63
+ });
59
64
  }
60
65
 
61
66
  const account = await createAccount({
62
67
  chain: client.chain,
63
68
  transport: custom(client.transport),
64
- signer: createDummySigner(zeroAddress),
69
+ signer: createDummySigner(params.from),
65
70
  accountAddress: params.from,
66
71
  counterfactualInfo,
67
72
  capabilities: params.capabilities,
73
+ delegation,
68
74
  });
69
75
 
76
+ // If using 7702, we need an Authorization (unless it's already authorized).
77
+ const authorizationRequest = delegation
78
+ ? await createAuthorization(client, {
79
+ address: account.address,
80
+ delegation,
81
+ })
82
+ : undefined;
83
+
84
+ if (authorizationRequest) {
85
+ // @ts-expect-error - this is available but not typed as public
86
+ client.middleware.gasEstimator = default7702GasEstimator();
87
+ }
88
+
70
89
  // TODO: oops we don't actually support setting the policyId as an override here
71
90
  // if we assume that the the isomorphic client is never used directly, then we can assume that this is handled upstream correctly
72
91
  const builtUo = await client.buildUserOperation({
@@ -79,9 +98,19 @@ export async function prepareCalls(
79
98
  overrides: params.capabilities?.gasParamsOverride,
80
99
  });
81
100
 
101
+ // The eip7702Auth field should never be included in the UO sig
102
+ // request. It's handled by the separate authorization request.
103
+ if ("eip7702Auth" in builtUo) {
104
+ builtUo.eip7702Auth = undefined;
105
+ }
82
106
  const uoRequest = deepHexlify(builtUo);
83
107
 
84
- const hash = account.getEntryPoint().getUserOperationHash(uoRequest);
108
+ const signatureRequest = {
109
+ type: "personal_sign" as const,
110
+ data: {
111
+ raw: account.getEntryPoint().getUserOperationHash(uoRequest),
112
+ },
113
+ };
85
114
 
86
115
  return {
87
116
  type:
@@ -91,10 +120,8 @@ export async function prepareCalls(
91
120
  data: uoRequest,
92
121
  chainId: toHex(client.chain.id),
93
122
  signatureRequest: {
94
- type: "personal_sign",
95
- data: {
96
- raw: hash,
97
- },
123
+ ...signatureRequest,
124
+ ...(authorizationRequest ? { authorizationRequest } : {}),
98
125
  },
99
126
  };
100
127
  }
@@ -6,6 +6,7 @@ import {
6
6
  import type { Static, StaticDecode } from "@sinclair/typebox";
7
7
  import { Value } from "@sinclair/typebox/value";
8
8
  import {
9
+ BaseError,
9
10
  ChainNotFoundError,
10
11
  concat,
11
12
  concatHex,
@@ -18,6 +19,8 @@ import { decodePermissionsContext } from "../../capabilities/permissions/mav2.js
18
19
  import type { wallet_sendPreparedCalls } from "../../rpc/request.js";
19
20
  import type { WalletServerViemRpcSchema } from "../../rpc/schema.js";
20
21
  import { TypeCallId } from "../../schemas.js";
22
+ import { isSupportedImplementationAddress7702 } from "../utils/7702.js";
23
+ import { InvalidRequestError } from "ox/RpcResponse";
21
24
 
22
25
  export type SendPreparedCallsParams = Omit<
23
26
  StaticDecode<
@@ -45,19 +48,30 @@ export async function sendPreparedCalls(
45
48
  throw new ChainNotFoundError();
46
49
  }
47
50
 
51
+ // One last safety check to be sure the UO wasn't modified to include an unsupported 7702 delegation address.
52
+ if (
53
+ params.signedAuthorization &&
54
+ !isSupportedImplementationAddress7702(params.signedAuthorization.address)
55
+ ) {
56
+ throw new InvalidRequestError({
57
+ message: `Unsupported 7702 delegation address: ${params.signedAuthorization.address}`,
58
+ });
59
+ }
60
+
48
61
  const deferredAction: Hex | undefined = (() => {
49
- if (!params.capabilities?.permissions?.context) {
62
+ if (!params.capabilities?.permissions) {
50
63
  return;
51
64
  }
52
65
 
53
66
  const decodedContext = decodePermissionsContext(
54
- params.capabilities.permissions.context,
67
+ params.capabilities.permissions,
55
68
  );
56
69
 
57
70
  if (decodedContext.contextVersion === "REMOTE_MODE_DEFERRED_ACTION") {
58
- throw new Error(
59
- "Remote mode deferred action not supported in isomorphic client",
60
- );
71
+ throw new InvalidRequestError({
72
+ message:
73
+ "Remote mode deferred action not supported in isomorphic client",
74
+ });
61
75
  }
62
76
 
63
77
  return decodedContext.deferredAction;
@@ -68,21 +82,34 @@ export async function sendPreparedCalls(
68
82
  ? getEntryPoint(client.chain, { version: "0.6.0" })
69
83
  : getEntryPoint(client.chain, { version: "0.7.0" });
70
84
 
71
- const hash = await client.sendRawUserOperation(
72
- {
73
- ...params.data,
74
- signature:
75
- deferredAction != null
76
- ? concatHex([
77
- `0x${deferredAction.slice(68)}`, // Cuts off stuff preprended to the digest (nonce, etc. that we had previously).
78
- "0xff",
79
- "0x00",
80
- params.signature.signature,
81
- ])
82
- : concat(["0xFF", "0x00", params.signature.signature]),
83
- },
84
- entryPoint.address,
85
- );
85
+ const hash = await client
86
+ .sendRawUserOperation(
87
+ {
88
+ ...params.data,
89
+ signature:
90
+ deferredAction != null
91
+ ? concatHex([
92
+ `0x${deferredAction.slice(68)}`, // Cuts off stuff preprended to the digest (nonce, etc. that we had previously).
93
+ "0xff",
94
+ "0x00",
95
+ params.signature.signature,
96
+ ])
97
+ : concat(["0xFF", "0x00", params.signature.signature]),
98
+ eip7702Auth: params.signedAuthorization,
99
+ },
100
+ entryPoint.address,
101
+ )
102
+ .catch((err) => {
103
+ if (
104
+ err instanceof BaseError &&
105
+ err.details.endsWith("is not a contract and initCode is empty")
106
+ ) {
107
+ throw new BaseError(
108
+ `${err.details} (If using 7702, be sure you include the 'signedAuthorization' field in the request parameters)`,
109
+ );
110
+ }
111
+ throw err;
112
+ });
86
113
 
87
114
  const callId = Value.Encode(TypeCallId, {
88
115
  chainId: toHex(client.chain.id),
@@ -0,0 +1,58 @@
1
+ import type { SmartAccountClient, SmartContractAccount } from "@aa-sdk/core";
2
+ import {
3
+ concatHex,
4
+ type Authorization,
5
+ type Address,
6
+ type Chain,
7
+ type Transport,
8
+ } from "viem";
9
+ import type { WalletServerViemRpcSchema } from "../../rpc/schema.js";
10
+
11
+ export const createAuthorization = async (
12
+ client: SmartAccountClient<
13
+ Transport,
14
+ Chain,
15
+ SmartContractAccount | undefined,
16
+ Record<string, unknown>,
17
+ WalletServerViemRpcSchema
18
+ >,
19
+ params: { address: Address; delegation: Address },
20
+ ): Promise<Authorization<number, false> | undefined> => {
21
+ const expectedCode = concatHex(["0xef0100", params.delegation]);
22
+ const code = (await client.getCode({ address: params.address })) ?? "0x";
23
+ if (code.toLowerCase() === expectedCode.toLowerCase()) {
24
+ return undefined; // Already authorized.
25
+ }
26
+ return {
27
+ chainId: client.chain.id,
28
+ address: params.delegation,
29
+ nonce: await client.getTransactionCount({
30
+ address: params.address,
31
+ }),
32
+ };
33
+ };
34
+
35
+ type Supported7702AccountType = "ModularAccountV2";
36
+
37
+ const IMPLEMENTATION_ADDRESS_TO_ACCOUNT_TYPE: Record<
38
+ Address,
39
+ Supported7702AccountType
40
+ > = {
41
+ "0x69007702764179f14F51cdce752f4f775d74E139": "ModularAccountV2",
42
+ };
43
+
44
+ export const getAccountTypeForImplementationAddress7702 = (
45
+ address: Address,
46
+ ): Supported7702AccountType | undefined => {
47
+ return IMPLEMENTATION_ADDRESS_TO_ACCOUNT_TYPE[address];
48
+ };
49
+
50
+ const SUPPORTED_IMPLEMENTATION_ADDRESSES = Object.keys(
51
+ IMPLEMENTATION_ADDRESS_TO_ACCOUNT_TYPE,
52
+ );
53
+
54
+ export const isSupportedImplementationAddress7702 = (
55
+ address: string,
56
+ ): boolean => {
57
+ return SUPPORTED_IMPLEMENTATION_ADDRESSES.includes(address);
58
+ };
@@ -9,13 +9,17 @@ import { concatHex, hexToNumber } from "viem";
9
9
  import type { Capabilities } from "../../capabilities/index.js";
10
10
  import type { TypeSerializedInitcode } from "../../schemas.js";
11
11
  import { parsePermissionsContext } from "./parsePermissionsContext.js";
12
+ import { assertNever } from "../../utils.js";
13
+ import { getAccountTypeForImplementationAddress7702 } from "./7702.js";
14
+ import { InternalError } from "ox/RpcResponse";
12
15
 
13
16
  type CreateAccountParams = {
14
17
  chain: Chain;
15
18
  transport: Transport;
16
19
  signer: SmartAccountSigner;
17
20
  accountAddress: Address;
18
- counterfactualInfo: StaticDecode<typeof TypeSerializedInitcode>;
21
+ counterfactualInfo?: StaticDecode<typeof TypeSerializedInitcode>; // undefined for 7702 accounts
22
+ delegation?: Address;
19
23
  capabilities?: StaticDecode<typeof Capabilities>;
20
24
  };
21
25
 
@@ -28,12 +32,38 @@ export async function createAccount(
28
32
  params: CreateAccountParams,
29
33
  ): Promise<SmartContractAccount> {
30
34
  const { counterfactualInfo: ci, ...accountParams } = params;
31
- // TODO: Implement support for other account types.
32
- if (ci.factoryType !== "MAv2.0.0-sma-b") {
33
- throw new Error("Only MAv2 SMA-B accounts are currently supported");
35
+
36
+ const mode = params.delegation ? "7702" : "default";
37
+
38
+ if (mode === "default") {
39
+ if (!ci) {
40
+ throw new InternalError({
41
+ message: "Counterfactual info not found",
42
+ });
43
+ }
44
+ if (ci.factoryType !== "MAv2.0.0-sma-b") {
45
+ throw new InternalError({
46
+ message: `Factory type ${ci.factoryType} is not currently supported.`,
47
+ });
48
+ }
49
+ } else if (mode === "7702") {
50
+ const accountType = getAccountTypeForImplementationAddress7702(
51
+ params.delegation!,
52
+ );
53
+ if (accountType !== "ModularAccountV2") {
54
+ throw new InternalError({
55
+ message: "7702 mode currently only supports ModularAccountV2",
56
+ });
57
+ }
58
+ } else {
59
+ assertNever(mode, "Unexpected mode in createAccount");
34
60
  }
35
61
 
36
- const parsedContext = parsePermissionsContext(params.capabilities, ci);
62
+ const parsedContext = parsePermissionsContext(
63
+ params.capabilities,
64
+ ci,
65
+ params.delegation,
66
+ );
37
67
 
38
68
  const signerEntity =
39
69
  parsedContext?.contextVersion === "NON_DEFERRED_ACTION"
@@ -43,11 +73,13 @@ export async function createAccount(
43
73
  }
44
74
  : undefined;
45
75
 
76
+ // TODO: clean this up to support different account types.
46
77
  return createModularAccountV2({
47
78
  ...accountParams,
48
79
  signerEntity,
49
80
  deferredAction: parsedContext?.deferredAction,
50
- initCode: concatHex([ci.factoryAddress, ci.factoryData]),
81
+ initCode: ci ? concatHex([ci.factoryAddress, ci.factoryData]) : undefined,
82
+ mode,
51
83
  });
52
84
  }
53
85
 
@@ -1,6 +1,7 @@
1
1
  import { type SmartAccountSigner } from "@aa-sdk/core";
2
2
  import type { TypedData } from "abitype";
3
3
  import {
4
+ BaseError,
4
5
  type Address,
5
6
  type Hex,
6
7
  type SignableMessage,
@@ -15,12 +16,12 @@ export const createDummySigner = (address: Address): SmartAccountSigner => ({
15
16
  },
16
17
  // Not supported on the server
17
18
  signMessage: function (_message: SignableMessage): Promise<Hex> {
18
- throw new Error("Function not implemented.");
19
+ throw new BaseError("signMessage not implemented by dummy signer.");
19
20
  },
20
21
  signTypedData: function <
21
22
  const TTypedData extends TypedData | Record<string, unknown>,
22
23
  TPrimaryType extends keyof TTypedData | "EIP712Domain" = keyof TTypedData,
23
24
  >(_params: TypedDataDefinition<TTypedData, TPrimaryType>): Promise<Hex> {
24
- throw new Error("Function not implemented.");
25
+ throw new BaseError("signTypedData not implemented by dummy signer.");
25
26
  },
26
27
  });