@account-kit/wallet-client 0.1.0-alpha.5 → 0.1.0-alpha.7

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 (121) hide show
  1. package/dist/esm/capabilities/eip7702Auth.js +3 -1
  2. package/dist/esm/capabilities/eip7702Auth.js.map +1 -1
  3. package/dist/esm/capabilities/overrides.js +8 -8
  4. package/dist/esm/capabilities/overrides.js.map +1 -1
  5. package/dist/esm/capabilities/permissions/index.d.ts +1 -1
  6. package/dist/esm/capabilities/permissions/index.js +6 -3
  7. package/dist/esm/capabilities/permissions/index.js.map +1 -1
  8. package/dist/esm/client/actions/grantPermissions.d.ts +5 -60
  9. package/dist/esm/client/actions/grantPermissions.js +5 -58
  10. package/dist/esm/client/actions/grantPermissions.js.map +1 -1
  11. package/dist/esm/client/actions/requestAccount.js +5 -2
  12. package/dist/esm/client/actions/requestAccount.js.map +1 -1
  13. package/dist/esm/client/actions/sendPreparedCalls.d.ts +6 -14
  14. package/dist/esm/client/actions/sendPreparedCalls.js +11 -12
  15. package/dist/esm/client/actions/sendPreparedCalls.js.map +1 -1
  16. package/dist/esm/client/actions/signPreparedCalls.d.ts +14 -0
  17. package/dist/esm/client/actions/signPreparedCalls.js +54 -0
  18. package/dist/esm/client/actions/signPreparedCalls.js.map +1 -0
  19. package/dist/esm/client/actions/signSignatureRequest.d.ts +8 -7
  20. package/dist/esm/client/actions/signSignatureRequest.js +69 -31
  21. package/dist/esm/client/actions/signSignatureRequest.js.map +1 -1
  22. package/dist/esm/client/client.e2e-test.js +80 -24
  23. package/dist/esm/client/client.e2e-test.js.map +1 -1
  24. package/dist/esm/client/decorator.d.ts +3 -1
  25. package/dist/esm/client/decorator.js +3 -1
  26. package/dist/esm/client/decorator.js.map +1 -1
  27. package/dist/esm/exports/index.d.ts +2 -1
  28. package/dist/esm/exports/index.js +2 -1
  29. package/dist/esm/exports/index.js.map +1 -1
  30. package/dist/esm/isomorphic/actions/createSession.js +18 -32
  31. package/dist/esm/isomorphic/actions/createSession.js.map +1 -1
  32. package/dist/esm/isomorphic/actions/prepareCalls.js +22 -16
  33. package/dist/esm/isomorphic/actions/prepareCalls.js.map +1 -1
  34. package/dist/esm/isomorphic/actions/sendPreparedCalls.d.ts +3 -3
  35. package/dist/esm/isomorphic/actions/sendPreparedCalls.js +76 -36
  36. package/dist/esm/isomorphic/actions/sendPreparedCalls.js.map +1 -1
  37. package/dist/esm/isomorphic/client.d.ts +139 -13
  38. package/dist/esm/isomorphic/utils/7702.d.ts +4 -3
  39. package/dist/esm/isomorphic/utils/7702.js +14 -6
  40. package/dist/esm/isomorphic/utils/7702.js.map +1 -1
  41. package/dist/esm/isomorphic/utils/decodeSignature.d.ts +3 -0
  42. package/dist/esm/isomorphic/utils/decodeSignature.js +13 -0
  43. package/dist/esm/isomorphic/utils/decodeSignature.js.map +1 -0
  44. package/dist/esm/local/client.js +2 -3
  45. package/dist/esm/local/client.js.map +1 -1
  46. package/dist/esm/remote/client.js +3 -7
  47. package/dist/esm/remote/client.js.map +1 -1
  48. package/dist/esm/rpc/examples.d.ts +3 -3
  49. package/dist/esm/rpc/examples.js +3 -3
  50. package/dist/esm/rpc/examples.js.map +1 -1
  51. package/dist/esm/rpc/request.d.ts +357 -49
  52. package/dist/esm/rpc/request.js +26 -53
  53. package/dist/esm/rpc/request.js.map +1 -1
  54. package/dist/esm/rpc/schema.d.ts +323 -57
  55. package/dist/esm/rpc/schema.js.map +1 -1
  56. package/dist/esm/schemas.d.ts +451 -13
  57. package/dist/esm/schemas.js +165 -52
  58. package/dist/esm/schemas.js.map +1 -1
  59. package/dist/esm/types.d.ts +3 -0
  60. package/dist/esm/types.js.map +1 -1
  61. package/dist/types/capabilities/eip7702Auth.d.ts.map +1 -1
  62. package/dist/types/capabilities/permissions/index.d.ts +1 -1
  63. package/dist/types/capabilities/permissions/index.d.ts.map +1 -1
  64. package/dist/types/client/actions/grantPermissions.d.ts +5 -60
  65. package/dist/types/client/actions/grantPermissions.d.ts.map +1 -1
  66. package/dist/types/client/actions/requestAccount.d.ts.map +1 -1
  67. package/dist/types/client/actions/sendPreparedCalls.d.ts +6 -14
  68. package/dist/types/client/actions/sendPreparedCalls.d.ts.map +1 -1
  69. package/dist/types/client/actions/signPreparedCalls.d.ts +15 -0
  70. package/dist/types/client/actions/signPreparedCalls.d.ts.map +1 -0
  71. package/dist/types/client/actions/signSignatureRequest.d.ts +8 -7
  72. package/dist/types/client/actions/signSignatureRequest.d.ts.map +1 -1
  73. package/dist/types/client/decorator.d.ts +3 -1
  74. package/dist/types/client/decorator.d.ts.map +1 -1
  75. package/dist/types/exports/index.d.ts +2 -1
  76. package/dist/types/exports/index.d.ts.map +1 -1
  77. package/dist/types/isomorphic/actions/createSession.d.ts.map +1 -1
  78. package/dist/types/isomorphic/actions/prepareCalls.d.ts.map +1 -1
  79. package/dist/types/isomorphic/actions/sendPreparedCalls.d.ts +3 -3
  80. package/dist/types/isomorphic/actions/sendPreparedCalls.d.ts.map +1 -1
  81. package/dist/types/isomorphic/client.d.ts +139 -13
  82. package/dist/types/isomorphic/client.d.ts.map +1 -1
  83. package/dist/types/isomorphic/utils/7702.d.ts +4 -3
  84. package/dist/types/isomorphic/utils/7702.d.ts.map +1 -1
  85. package/dist/types/isomorphic/utils/decodeSignature.d.ts +4 -0
  86. package/dist/types/isomorphic/utils/decodeSignature.d.ts.map +1 -0
  87. package/dist/types/remote/client.d.ts.map +1 -1
  88. package/dist/types/rpc/examples.d.ts +3 -3
  89. package/dist/types/rpc/examples.d.ts.map +1 -1
  90. package/dist/types/rpc/request.d.ts +357 -49
  91. package/dist/types/rpc/request.d.ts.map +1 -1
  92. package/dist/types/rpc/schema.d.ts +323 -57
  93. package/dist/types/rpc/schema.d.ts.map +1 -1
  94. package/dist/types/schemas.d.ts +451 -13
  95. package/dist/types/schemas.d.ts.map +1 -1
  96. package/dist/types/types.d.ts +3 -0
  97. package/dist/types/types.d.ts.map +1 -1
  98. package/package.json +5 -5
  99. package/src/capabilities/eip7702Auth.ts +17 -10
  100. package/src/capabilities/overrides.ts +8 -8
  101. package/src/capabilities/permissions/index.ts +8 -3
  102. package/src/client/actions/grantPermissions.ts +5 -60
  103. package/src/client/actions/requestAccount.ts +10 -8
  104. package/src/client/actions/sendPreparedCalls.ts +17 -17
  105. package/src/client/actions/signPreparedCalls.ts +72 -0
  106. package/src/client/actions/signSignatureRequest.ts +50 -46
  107. package/src/client/client.e2e-test.ts +101 -33
  108. package/src/client/decorator.ts +14 -5
  109. package/src/exports/index.ts +2 -1
  110. package/src/isomorphic/actions/createSession.ts +20 -42
  111. package/src/isomorphic/actions/prepareCalls.ts +25 -17
  112. package/src/isomorphic/actions/sendPreparedCalls.ts +104 -54
  113. package/src/isomorphic/utils/7702.ts +18 -8
  114. package/src/isomorphic/utils/decodeSignature.ts +18 -0
  115. package/src/local/client.ts +2 -6
  116. package/src/remote/client.ts +3 -10
  117. package/src/rpc/examples.ts +3 -4
  118. package/src/rpc/request.ts +41 -63
  119. package/src/rpc/schema.ts +2 -2
  120. package/src/schemas.ts +227 -53
  121. package/src/types.ts +4 -0
@@ -2,7 +2,6 @@ import { LocalAccountSigner } from "@aa-sdk/core";
2
2
  import { alchemy, arbitrumSepolia } from "@account-kit/infra";
3
3
  import { describe, expect, it } from "bun:test";
4
4
  import { createPublicClient, zeroAddress, type Address } from "viem";
5
- import { signSignatureRequest } from "./actions/signSignatureRequest.js";
6
5
  import { createSmartWalletClient } from "./index.js";
7
6
 
8
7
  describe("Client E2E Tests", () => {
@@ -120,9 +119,44 @@ describe("Client E2E Tests", () => {
120
119
  expect(isValid).toBeTrue();
121
120
  });
122
121
 
122
+ it("should successfully send a UO with paymaster using 7702", async () => {
123
+ const _signer = LocalAccountSigner.privateKeyToAccountSigner(
124
+ "0x49daf21e92c997093e9ffdf7e7ddbf8970e9eadc5d4847d0f690db25d5128e1f",
125
+ );
126
+
127
+ const _client = createSmartWalletClient({
128
+ transport,
129
+ chain: arbitrumSepolia,
130
+ mode: "local",
131
+ signer: _signer,
132
+ });
133
+
134
+ const account = await _client.requestAccount({
135
+ creationHint: {
136
+ accountType: "7702",
137
+ },
138
+ });
139
+
140
+ const preparedCalls = await _client.prepareCalls({
141
+ calls: [{ to: zeroAddress, value: "0x0" }],
142
+ from: account.address,
143
+ capabilities: {
144
+ paymasterService: {
145
+ policyId: process.env.TEST_PAYMASTER_POLICY_ID!,
146
+ },
147
+ },
148
+ });
149
+
150
+ const signedCalls = await _client.signPreparedCalls(preparedCalls);
151
+
152
+ const result = await _client.sendPreparedCalls(signedCalls);
153
+
154
+ expect(result.preparedCallIds).toBeArrayOfSize(1);
155
+ });
156
+
123
157
  it("should successfully send a UO with paymaster", async () => {
124
158
  const account = await client.requestAccount();
125
- const preparedCall = await client.prepareCalls({
159
+ const preparedCalls = await client.prepareCalls({
126
160
  calls: [{ to: zeroAddress, value: "0x0" }],
127
161
  from: account.address,
128
162
  capabilities: {
@@ -132,14 +166,9 @@ describe("Client E2E Tests", () => {
132
166
  },
133
167
  });
134
168
 
135
- const signature = await client.signSignatureRequest(
136
- preparedCall.signatureRequest,
137
- );
169
+ const signedCalls = await client.signPreparedCalls(preparedCalls);
138
170
 
139
- const result = await client.sendPreparedCalls({
140
- ...preparedCall,
141
- signature,
142
- });
171
+ const result = await client.sendPreparedCalls(signedCalls);
143
172
 
144
173
  expect(result.preparedCallIds).toBeArrayOfSize(1);
145
174
  });
@@ -151,7 +180,7 @@ describe("Client E2E Tests", () => {
151
180
 
152
181
  const permissions = await client.grantPermissions({
153
182
  account: account.address,
154
- expiry: Math.floor(Date.now() / 1000) + 60 * 60,
183
+ expirySec: Math.floor(Date.now() / 1000) + 60 * 60,
155
184
  key: {
156
185
  publicKey: await sessionKey.getAddress(),
157
186
  type: "secp256k1",
@@ -159,7 +188,14 @@ describe("Client E2E Tests", () => {
159
188
  permissions: [{ type: "root" }],
160
189
  });
161
190
 
162
- const preparedCall = await client.prepareCalls({
191
+ const sessionKeyClient = createSmartWalletClient({
192
+ transport,
193
+ chain: arbitrumSepolia,
194
+ mode: "local",
195
+ signer: sessionKey,
196
+ });
197
+
198
+ const preparedCalls = await sessionKeyClient.prepareCalls({
163
199
  calls: [{ to: zeroAddress, value: "0x0" }],
164
200
  from: account.address,
165
201
  capabilities: {
@@ -170,14 +206,11 @@ describe("Client E2E Tests", () => {
170
206
  },
171
207
  });
172
208
 
173
- const signature = await signSignatureRequest(
174
- sessionKey,
175
- preparedCall.signatureRequest,
176
- );
209
+ const signedCalls =
210
+ await sessionKeyClient.signPreparedCalls(preparedCalls);
177
211
 
178
- const result = await client.sendPreparedCalls({
179
- ...preparedCall,
180
- signature,
212
+ const result = await sessionKeyClient.sendPreparedCalls({
213
+ ...signedCalls,
181
214
  capabilities: {
182
215
  permissions,
183
216
  },
@@ -295,7 +328,7 @@ describe("Client E2E Tests", () => {
295
328
 
296
329
  it("should successfully send a UO with paymaster", async () => {
297
330
  const account = await client.requestAccount();
298
- const preparedCall = await client.prepareCalls({
331
+ const preparedCalls = await client.prepareCalls({
299
332
  calls: [{ to: zeroAddress, value: "0x0" }],
300
333
  from: account.address,
301
334
  capabilities: {
@@ -305,15 +338,46 @@ describe("Client E2E Tests", () => {
305
338
  },
306
339
  });
307
340
 
308
- const signature = await client.signSignatureRequest(
309
- preparedCall.signatureRequest,
341
+ const signedCalls = await client.signPreparedCalls(preparedCalls);
342
+
343
+ const result = await client.sendPreparedCalls(signedCalls);
344
+
345
+ expect(result.preparedCallIds).toBeArrayOfSize(1);
346
+ });
347
+
348
+ it("should successfully send a UO with paymaster using 7702", async () => {
349
+ const _signer = LocalAccountSigner.privateKeyToAccountSigner(
350
+ "0x00d35c6d307b5cddeb70aeed96ee27a551fee58bf1a43858477e6c11f9172ba8",
310
351
  );
311
352
 
312
- const result = await client.sendPreparedCalls({
313
- ...preparedCall,
314
- signature,
353
+ const _client = createSmartWalletClient({
354
+ transport,
355
+ chain: arbitrumSepolia,
356
+ mode: "remote",
357
+ signer: _signer,
358
+ });
359
+
360
+ const account = await _client.requestAccount({
361
+ creationHint: {
362
+ accountType: "7702",
363
+ },
364
+ });
365
+ expect(account.address).toBe(await _signer.getAddress());
366
+
367
+ const preparedCalls = await _client.prepareCalls({
368
+ calls: [{ to: zeroAddress, value: "0x0" }],
369
+ from: account.address,
370
+ capabilities: {
371
+ paymasterService: {
372
+ policyId: process.env.TEST_PAYMASTER_POLICY_ID!,
373
+ },
374
+ },
315
375
  });
316
376
 
377
+ const signedCalls = await _client.signPreparedCalls(preparedCalls);
378
+
379
+ const result = await _client.sendPreparedCalls(signedCalls);
380
+
317
381
  expect(result.preparedCallIds).toBeArrayOfSize(1);
318
382
  });
319
383
 
@@ -324,7 +388,7 @@ describe("Client E2E Tests", () => {
324
388
 
325
389
  const permissions = await client.grantPermissions({
326
390
  account: account.address,
327
- expiry: Math.floor(Date.now() / 1000) + 60 * 60,
391
+ expirySec: Math.floor(Date.now() / 1000) + 60 * 60,
328
392
  key: {
329
393
  publicKey: await sessionKey.getAddress(),
330
394
  type: "secp256k1",
@@ -332,7 +396,14 @@ describe("Client E2E Tests", () => {
332
396
  permissions: [{ type: "root" }],
333
397
  });
334
398
 
335
- const preparedCall = await client.prepareCalls({
399
+ const sessionKeyClient = createSmartWalletClient({
400
+ transport,
401
+ chain: arbitrumSepolia,
402
+ mode: "remote",
403
+ signer: sessionKey,
404
+ });
405
+
406
+ const preparedCalls = await sessionKeyClient.prepareCalls({
336
407
  calls: [{ to: zeroAddress, value: "0x0" }],
337
408
  from: account.address,
338
409
  capabilities: {
@@ -343,14 +414,11 @@ describe("Client E2E Tests", () => {
343
414
  },
344
415
  });
345
416
 
346
- const signature = await signSignatureRequest(
347
- sessionKey,
348
- preparedCall.signatureRequest,
349
- );
417
+ const signedCalls =
418
+ await sessionKeyClient.signPreparedCalls(preparedCalls);
350
419
 
351
- const result = await client.sendPreparedCalls({
352
- ...preparedCall,
353
- signature,
420
+ const result = await sessionKeyClient.sendPreparedCalls({
421
+ ...signedCalls,
354
422
  capabilities: {
355
423
  permissions,
356
424
  },
@@ -1,9 +1,5 @@
1
1
  import type { SmartAccountSigner } from "@aa-sdk/core";
2
2
  import type { Address, Hex } from "viem";
3
- import type {
4
- SendPreparedCallsParams,
5
- SendPreparedCallsResult,
6
- } from "../isomorphic/actions/sendPreparedCalls.ts";
7
3
  import type { InnerWalletApiClient } from "../types.ts";
8
4
  import {
9
5
  createAccount,
@@ -35,7 +31,11 @@ import {
35
31
  type RequestAccountParams,
36
32
  type RequestAccountResult,
37
33
  } from "./actions/requestAccount.js";
38
- import { sendPreparedCalls } from "./actions/sendPreparedCalls.js";
34
+ import {
35
+ sendPreparedCalls,
36
+ type SendPreparedCallsParams,
37
+ type SendPreparedCallsResult,
38
+ } from "./actions/sendPreparedCalls.js";
39
39
  import { signMessage, type SignMessageParams } from "./actions/signMessage.js";
40
40
  import {
41
41
  signSignatureRequest,
@@ -46,6 +46,11 @@ import {
46
46
  signTypedData,
47
47
  type SignTypedDataParams,
48
48
  } from "./actions/signTypedData.js";
49
+ import {
50
+ signPreparedCalls,
51
+ type SignPreparedCallsParams,
52
+ type SignPreparedCallsResult,
53
+ } from "./actions/signPreparedCalls.js";
49
54
 
50
55
  export type SmartWalletActions<
51
56
  TAccount extends Address | undefined = Address | undefined,
@@ -67,6 +72,9 @@ export type SmartWalletActions<
67
72
  signSignatureRequest: (
68
73
  params: SignSignatureRequestParams,
69
74
  ) => Promise<SignSignatureRequestResult>;
75
+ signPreparedCalls: (
76
+ params: SignPreparedCallsParams,
77
+ ) => Promise<SignPreparedCallsResult>;
70
78
  signMessage: (params: SignMessageParams) => Promise<Hex>;
71
79
  signTypedData: (params: SignTypedDataParams) => Promise<Hex>;
72
80
  grantPermissions: (
@@ -88,6 +96,7 @@ export function smartWalletClientActions<
88
96
  sendPreparedCalls: (params) => sendPreparedCalls(client, params),
89
97
  getCallsStatus: (params) => getCallsStatus(client, params),
90
98
  signSignatureRequest: (params) => signSignatureRequest(signer, params),
99
+ signPreparedCalls: (params) => signPreparedCalls(signer, params),
91
100
  signMessage: (params) => signMessage(client, signer, params),
92
101
  signTypedData: (params) => signTypedData(client, signer, params),
93
102
  grantPermissions: (params) => grantPermissions(client, signer, params),
@@ -29,6 +29,7 @@ export { grantPermissions } from "../client/actions/grantPermissions.js";
29
29
  export { listAccounts } from "../client/actions/listAccounts.js";
30
30
  export { prepareCalls } from "../client/actions/prepareCalls.js";
31
31
  export { requestAccount } from "../client/actions/requestAccount.js";
32
- export { signMessage } from "../client/actions/signMessage.js";
33
32
  export { signSignatureRequest } from "../client/actions/signSignatureRequest.js";
33
+ export { signPreparedCalls } from "../client/actions/signPreparedCalls.js";
34
+ export { signMessage } from "../client/actions/signMessage.js";
34
35
  export { signTypedData } from "../client/actions/signTypedData.js";
@@ -24,12 +24,7 @@ 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
27
  import { InvalidRequestError } from "ox/RpcResponse";
29
- import {
30
- parseDelegation,
31
- assertValid7702AccountAddress,
32
- } from "../utils/7702.js";
33
28
 
34
29
  export type CreateSessionParams = Omit<
35
30
  Static<
@@ -63,30 +58,26 @@ export async function createSession(
63
58
  throw new ChainNotFoundError();
64
59
  }
65
60
 
66
- assertValid7702AccountAddress(
67
- params.account,
68
- params.capabilities?.eip7702Auth,
69
- );
70
-
71
- const { counterfactualInfo, delegation } = params.capabilities?.eip7702Auth
72
- ? {
73
- counterfactualInfo: undefined,
74
- delegation: parseDelegation(params.capabilities.eip7702Auth),
75
- }
76
- : await client.request({
77
- method: "wallet_requestAccount",
78
- params: [
79
- {
80
- accountAddress: params.account,
81
- includeCounterfactualInfo: true,
82
- },
83
- ],
84
- });
61
+ const { counterfactualInfo, delegation } = await client.request({
62
+ method: "wallet_requestAccount",
63
+ params: [
64
+ {
65
+ accountAddress: params.account,
66
+ includeCounterfactualInfo: true,
67
+ },
68
+ ],
69
+ });
85
70
 
86
- if (!counterfactualInfo && !delegation) {
71
+ if (delegation) {
87
72
  throw new InvalidRequestError({
88
73
  message:
89
- "No counterfactual info or delegated implementation address found.",
74
+ "'wallet_createSession' does not currently support 7702 accounts.",
75
+ });
76
+ }
77
+
78
+ if (!counterfactualInfo) {
79
+ throw new InvalidRequestError({
80
+ message: "No counterfactual info found.",
90
81
  });
91
82
  }
92
83
 
@@ -126,7 +117,7 @@ export async function createSession(
126
117
  },
127
118
  entityId,
128
119
  nonce,
129
- deadline: params.expiry,
120
+ deadline: params.expirySec,
130
121
  })
131
122
  .addPermissions({
132
123
  permissions: params.permissions.map((permission) =>
@@ -135,25 +126,12 @@ export async function createSession(
135
126
  })
136
127
  .compileDeferred();
137
128
 
138
- // If using 7702, we need an Authorization (unless it's already authorized).
139
- const authorizationRequest = delegation
140
- ? await createAuthorization(client, {
141
- address: account.address,
142
- delegation,
143
- })
144
- : undefined;
145
-
146
- const signatureRequest = {
147
- type: "eth_signTypedData_v4" as const,
148
- data: typedData,
149
- };
150
-
151
129
  return {
152
130
  sessionId: null, // In remote mode, the server will set this later.
153
131
  entityId: toHex(entityId),
154
132
  signatureRequest: {
155
- ...signatureRequest,
156
- ...(authorizationRequest ? { authorizationRequest } : {}),
133
+ type: "eth_signTypedData_v4" as const,
134
+ data: typedData,
157
135
  },
158
136
  fullPreSignatureDeferredActionDigest,
159
137
  };
@@ -17,8 +17,9 @@ 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, parseDelegation } from "../utils/7702.js";
20
+ import { createAuthorizationRequest, parseDelegation } from "../utils/7702.js";
21
21
  import { InvalidRequestError } from "ox/RpcResponse";
22
+ import { assertNever } from "../../utils.js";
22
23
  import { assertValid7702AccountAddress } from "../utils/7702.js";
23
24
 
24
25
  export type PrepareCallsParams = Omit<
@@ -83,7 +84,7 @@ export async function prepareCalls(
83
84
 
84
85
  // If using 7702, we need an Authorization (unless it's already authorized).
85
86
  const authorizationRequest = delegation
86
- ? await createAuthorization(client, {
87
+ ? await createAuthorizationRequest(client, {
87
88
  address: account.address,
88
89
  delegation,
89
90
  })
@@ -107,29 +108,36 @@ export async function prepareCalls(
107
108
  });
108
109
 
109
110
  // The eip7702Auth field should never be included in the UO sig
110
- // request. It's handled by the separate authorization request.
111
+ // request. It's handled by a separate authorization request.
111
112
  if ("eip7702Auth" in builtUo) {
112
113
  builtUo.eip7702Auth = undefined;
113
114
  }
114
- const uoRequest = deepHexlify(builtUo);
115
115
 
116
- const signatureRequest = {
117
- type: "personal_sign" as const,
118
- data: {
119
- raw: account.getEntryPoint().getUserOperationHash(uoRequest),
120
- },
121
- };
116
+ const hexlifiedUo = deepHexlify(builtUo);
117
+
118
+ const ep = account.getEntryPoint();
122
119
 
123
- return {
120
+ const uoRequest = {
124
121
  type:
125
- account.getEntryPoint().version === "0.7.0"
126
- ? "user-operation-v070"
127
- : "user-operation-v060",
128
- data: uoRequest,
122
+ ep.version === "0.7.0"
123
+ ? ("user-operation-v070" as const)
124
+ : ep.version === "0.6.0"
125
+ ? ("user-operation-v060" as const)
126
+ : assertNever(ep.version, "Unexpected entry point version"),
127
+ data: hexlifiedUo,
129
128
  chainId: toHex(client.chain.id),
130
129
  signatureRequest: {
131
- ...signatureRequest,
132
- ...(authorizationRequest ? { authorizationRequest } : {}),
130
+ type: "personal_sign" as const,
131
+ data: {
132
+ raw: ep.getUserOperationHash(hexlifiedUo),
133
+ },
133
134
  },
134
135
  };
136
+
137
+ return authorizationRequest
138
+ ? {
139
+ type: "array" as const,
140
+ data: [authorizationRequest, uoRequest],
141
+ }
142
+ : uoRequest;
135
143
  }
@@ -3,31 +3,33 @@ import {
3
3
  type SmartAccountClient,
4
4
  type SmartContractAccount,
5
5
  } from "@aa-sdk/core";
6
- import type { Static, StaticDecode } from "@sinclair/typebox";
7
6
  import { Value } from "@sinclair/typebox/value";
8
7
  import {
9
8
  BaseError,
10
9
  ChainNotFoundError,
11
10
  concat,
12
11
  concatHex,
12
+ numberToHex,
13
+ parseSignature,
13
14
  toHex,
15
+ type Address,
14
16
  type Chain,
15
17
  type Hex,
16
18
  type Transport,
17
19
  } from "viem";
18
20
  import { decodePermissionsContext } from "../../capabilities/permissions/mav2.js";
19
- import type { wallet_sendPreparedCalls } from "../../rpc/request.js";
21
+ import { type wallet_sendPreparedCalls } from "../../rpc/request.js";
20
22
  import type { WalletServerViemRpcSchema } from "../../rpc/schema.js";
21
23
  import { TypeCallId } from "../../schemas.js";
22
24
  import { isSupportedDelegationAddress7702 } from "../utils/7702.js";
23
25
  import { InvalidRequestError } from "ox/RpcResponse";
26
+ import { assertNever } from "../../utils.js";
27
+ import type { Static } from "@sinclair/typebox";
28
+ import { decodeSignature } from "../utils/decodeSignature.js";
24
29
 
25
- export type SendPreparedCallsParams = Omit<
26
- StaticDecode<
27
- (typeof wallet_sendPreparedCalls)["properties"]["Request"]["properties"]["params"]
28
- >[0],
29
- "chainId"
30
- >;
30
+ export type SendPreparedCallsParams = Static<
31
+ (typeof wallet_sendPreparedCalls)["properties"]["Request"]["properties"]["params"]
32
+ >[0];
31
33
 
32
34
  export type SendPreparedCallsResult = Static<
33
35
  (typeof wallet_sendPreparedCalls)["properties"]["ReturnType"]
@@ -48,16 +50,6 @@ export async function sendPreparedCalls(
48
50
  throw new ChainNotFoundError();
49
51
  }
50
52
 
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
- !isSupportedDelegationAddress7702(params.signedAuthorization.address)
55
- ) {
56
- throw new InvalidRequestError({
57
- message: `Unsupported 7702 delegation address: ${params.signedAuthorization.address}`,
58
- });
59
- }
60
-
61
53
  const deferredAction: Hex | undefined = (() => {
62
54
  if (!params.capabilities?.permissions) {
63
55
  return;
@@ -77,46 +69,104 @@ export async function sendPreparedCalls(
77
69
  return decodedContext.deferredAction;
78
70
  })();
79
71
 
80
- const entryPoint =
81
- params.type === "user-operation-v060"
82
- ? getEntryPoint(client.chain, { version: "0.6.0" })
83
- : getEntryPoint(client.chain, { version: "0.7.0" });
72
+ const userOps =
73
+ params.type === "array"
74
+ ? params.data.filter((it) => {
75
+ const isUserOp =
76
+ it.type === "user-operation-v060" ||
77
+ it.type === "user-operation-v070";
78
+ if (isUserOp && it.chainId !== toHex(client.chain.id)) {
79
+ throw new InvalidRequestError({
80
+ message:
81
+ "Multiple chain IDs in a single request are not currently supported.",
82
+ });
83
+ }
84
+ return isUserOp;
85
+ })
86
+ : [params];
87
+
88
+ const authorizations =
89
+ params.type === "array"
90
+ ? params.data.filter((it) => it.type === "authorization")
91
+ : [];
84
92
 
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;
93
+ if (authorizations.length > 1) {
94
+ throw new InvalidRequestError({
95
+ message:
96
+ "Multiple authorizations in a single request are not currently supported",
112
97
  });
98
+ }
99
+ const [authorization] = authorizations;
100
+
101
+ // One last safety check to be sure the UO wasn't modified to include an unsupported 7702 delegation address.
102
+ if (
103
+ authorization &&
104
+ !isSupportedDelegationAddress7702(authorization.data.address)
105
+ ) {
106
+ throw new InvalidRequestError({
107
+ message: `Unsupported 7702 delegation address: ${authorization.data.address}`,
108
+ });
109
+ }
113
110
 
114
- const callId = Value.Encode(TypeCallId, {
115
- chainId: toHex(client.chain.id),
116
- hash,
117
- });
111
+ const hashes = await Promise.all(
112
+ userOps.map(async (userOp, idx) => {
113
+ const ep: { address: Address } =
114
+ userOp.type === "user-operation-v060"
115
+ ? getEntryPoint(client.chain, { version: "0.6.0" })
116
+ : userOp.type === "user-operation-v070"
117
+ ? getEntryPoint(client.chain, { version: "0.7.0" })
118
+ : assertNever(userOp, "Unexpected user op type");
119
+ const authSig = authorization
120
+ ? parseSignature(decodeSignature(authorization.signature).data)
121
+ : undefined;
122
+ const uoSigHex = decodeSignature(userOp.signature).data;
123
+ return client
124
+ .sendRawUserOperation(
125
+ {
126
+ ...userOp.data,
127
+ signature:
128
+ deferredAction != null
129
+ ? concatHex([
130
+ `0x${deferredAction.slice(68)}`, // Cuts off stuff prepended to the digest (nonce, etc. that we had previously).
131
+ "0xff",
132
+ "0x00",
133
+ uoSigHex,
134
+ ])
135
+ : concat(["0xFF", "0x00", uoSigHex]),
136
+ eip7702Auth:
137
+ idx === 0 && authorization && authSig
138
+ ? {
139
+ ...authorization.data,
140
+ chainId: authorization.chainId,
141
+ ...{
142
+ ...authSig,
143
+ yParity: numberToHex(authSig.yParity),
144
+ },
145
+ }
146
+ : undefined,
147
+ },
148
+ ep.address,
149
+ )
150
+ .catch((err) => {
151
+ if (
152
+ err instanceof BaseError &&
153
+ err.details.endsWith("is not a contract and initCode is empty")
154
+ ) {
155
+ throw new BaseError(
156
+ `${err.details} (If using 7702, be sure you include the signed authorization in the request parameters)`,
157
+ );
158
+ }
159
+ throw err;
160
+ });
161
+ }),
162
+ );
118
163
 
119
164
  return {
120
- preparedCallIds: [callId],
165
+ preparedCallIds: hashes.map((hash) =>
166
+ Value.Encode(TypeCallId, {
167
+ chainId: toHex(client.chain.id),
168
+ hash,
169
+ }),
170
+ ),
121
171
  };
122
172
  }