@account-kit/infra 4.30.0 → 4.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/esm/actions/types.d.ts +5 -0
  2. package/dist/esm/actions/types.js.map +1 -1
  3. package/dist/esm/chains.d.ts +2 -0
  4. package/dist/esm/chains.js +46 -0
  5. package/dist/esm/chains.js.map +1 -1
  6. package/dist/esm/client/smartAccountClient.d.ts +8 -1
  7. package/dist/esm/client/smartAccountClient.js +1 -0
  8. package/dist/esm/client/smartAccountClient.js.map +1 -1
  9. package/dist/esm/gas-manager.d.ts +46 -1
  10. package/dist/esm/gas-manager.js +18 -0
  11. package/dist/esm/gas-manager.js.map +1 -1
  12. package/dist/esm/index.d.ts +1 -1
  13. package/dist/esm/index.js +1 -1
  14. package/dist/esm/index.js.map +1 -1
  15. package/dist/esm/middleware/gasManager.d.ts +14 -5
  16. package/dist/esm/middleware/gasManager.js +133 -10
  17. package/dist/esm/middleware/gasManager.js.map +1 -1
  18. package/dist/esm/version.d.ts +1 -1
  19. package/dist/esm/version.js +1 -1
  20. package/dist/esm/version.js.map +1 -1
  21. package/dist/types/actions/types.d.ts +5 -0
  22. package/dist/types/actions/types.d.ts.map +1 -1
  23. package/dist/types/chains.d.ts +2 -0
  24. package/dist/types/chains.d.ts.map +1 -1
  25. package/dist/types/client/smartAccountClient.d.ts +8 -1
  26. package/dist/types/client/smartAccountClient.d.ts.map +1 -1
  27. package/dist/types/gas-manager.d.ts +46 -1
  28. package/dist/types/gas-manager.d.ts.map +1 -1
  29. package/dist/types/index.d.ts +1 -1
  30. package/dist/types/index.d.ts.map +1 -1
  31. package/dist/types/middleware/gasManager.d.ts +14 -5
  32. package/dist/types/middleware/gasManager.d.ts.map +1 -1
  33. package/dist/types/version.d.ts +1 -1
  34. package/package.json +4 -4
  35. package/src/actions/types.ts +5 -0
  36. package/src/chains.ts +48 -0
  37. package/src/client/smartAccountClient.ts +9 -1
  38. package/src/gas-manager.ts +36 -1
  39. package/src/index.ts +2 -0
  40. package/src/middleware/gasManager.ts +224 -15
  41. package/src/version.ts +1 -1
@@ -12,7 +12,7 @@ import {
12
12
  type SmartContractAccountWithSigner,
13
13
  type UserOperationContext,
14
14
  } from "@aa-sdk/core";
15
- import { type Chain } from "viem";
15
+ import { type Address, type Chain } from "viem";
16
16
  import {
17
17
  alchemy,
18
18
  convertHeadersToObject,
@@ -48,6 +48,13 @@ export type AlchemySmartAccountClientConfig<
48
48
  account?: account;
49
49
  useSimulation?: boolean;
50
50
  policyId?: string | string[];
51
+ policyToken?: {
52
+ address: Address;
53
+ approvalMode?: "NONE" | "PERMIT";
54
+ maxTokenAmount?: bigint;
55
+ erc20Name?: string;
56
+ version?: string;
57
+ };
51
58
  } & Pick<
52
59
  SmartAccountClientConfig<AlchemyTransport, chain, account, context>,
53
60
  | "customMiddleware"
@@ -163,6 +170,7 @@ export function createAlchemySmartAccountClient(
163
170
  ...(config.policyId
164
171
  ? alchemyGasAndPaymasterAndDataMiddleware({
165
172
  policyId: config.policyId,
173
+ policyToken: config.policyToken,
166
174
  transport: config.transport,
167
175
  gasEstimatorOverride: config.gasEstimator,
168
176
  feeEstimatorOverride: config.feeEstimator,
@@ -1,4 +1,4 @@
1
- import type { Address, Chain } from "viem";
1
+ import type { Address, Chain, Hex } from "viem";
2
2
  import {
3
3
  arbitrum,
4
4
  arbitrumSepolia,
@@ -66,3 +66,38 @@ export const getAlchemyPaymasterAddress = (chain: Chain): Address => {
66
66
  throw new Error(`Unsupported chain: ${chain}`);
67
67
  }
68
68
  };
69
+
70
+ export const PermitTypes = {
71
+ EIP712Domain: [
72
+ { name: "name", type: "string" },
73
+ { name: "version", type: "string" },
74
+ { name: "chainId", type: "uint256" },
75
+ { name: "verifyingContract", type: "address" },
76
+ ],
77
+ Permit: [
78
+ { name: "owner", type: "address" },
79
+ { name: "spender", type: "address" },
80
+ { name: "value", type: "uint256" },
81
+ { name: "nonce", type: "uint256" },
82
+ { name: "deadline", type: "uint256" },
83
+ ],
84
+ } as const;
85
+
86
+ export const EIP712NoncesAbi = [
87
+ "function nonces(address owner) external view returns (uint)",
88
+ ] as const;
89
+
90
+ export type PermitMessage = {
91
+ owner: Hex;
92
+ spender: Hex;
93
+ value: bigint;
94
+ nonce: bigint;
95
+ deadline: bigint;
96
+ };
97
+
98
+ export type PermitDomain = {
99
+ name: string;
100
+ version: string;
101
+ chainId: bigint;
102
+ verifyingContract: Hex;
103
+ };
package/src/index.ts CHANGED
@@ -44,6 +44,8 @@ export {
44
44
  openlootSepolia,
45
45
  gensynTestnet,
46
46
  riseTestnet,
47
+ storyMainnet,
48
+ storyAeneid,
47
49
  } from "./chains.js";
48
50
  export type * from "./client/decorators/alchemyEnhancedApis.js";
49
51
  export { alchemyEnhancedApiActions } from "./client/decorators/alchemyEnhancedApis.js";
@@ -1,8 +1,11 @@
1
1
  import type {
2
+ Address,
2
3
  ClientMiddlewareConfig,
3
4
  ClientMiddlewareFn,
4
5
  EntryPointVersion,
6
+ Erc7677Client,
5
7
  Multiplier,
8
+ SmartContractAccount,
6
9
  UserOperationFeeOptions,
7
10
  UserOperationOverrides,
8
11
  UserOperationRequest,
@@ -20,11 +23,33 @@ import {
20
23
  noopMiddleware,
21
24
  resolveProperties,
22
25
  } from "@aa-sdk/core";
23
- import { fromHex, isHex, type Hex } from "viem";
26
+ import {
27
+ fromHex,
28
+ isHex,
29
+ toHex,
30
+ type Hex,
31
+ encodeAbiParameters,
32
+ encodeFunctionData,
33
+ parseAbi,
34
+ maxUint256,
35
+ sliceHex,
36
+ } from "viem";
24
37
  import type { AlchemySmartAccountClient } from "../client/smartAccountClient.js";
25
38
  import type { AlchemyTransport } from "../alchemyTransport.js";
26
39
  import { alchemyFeeEstimator } from "./feeEstimator.js";
40
+ import type { RequestGasAndPaymasterAndDataRequest } from "../actions/types.js";
41
+ import { PermitTypes, EIP712NoncesAbi } from "../gas-manager.js";
42
+ import type { PermitMessage, PermitDomain } from "../gas-manager.js";
43
+ import type { MiddlewareClient } from "../../../../aa-sdk/core/dist/types/middleware/actions.js";
27
44
 
45
+ type Context = {
46
+ policyId: string | string[];
47
+ erc20Context?: {
48
+ tokenAddress: Address;
49
+ maxTokenAmount?: BigInt;
50
+ permit?: Hex;
51
+ };
52
+ };
28
53
  /**
29
54
  * Paymaster middleware factory that uses Alchemy's Gas Manager for sponsoring
30
55
  * transactions. Adheres to the ERC-7677 standardized communication protocol.
@@ -41,24 +66,80 @@ import { alchemyFeeEstimator } from "./feeEstimator.js";
41
66
  * });
42
67
  * ```
43
68
  *
44
- * @param {string | string[]} policyId the policyId (or list of policyIds) for Alchemy's gas manager
45
- * @returns {Pick<ClientMiddlewareConfig, "dummyPaymasterAndData" | "paymasterAndData">} partial client middleware configuration containing `dummyPaymasterAndData` and `paymasterAndData`
69
+ * @param {string | string[]} policyId - The policyId (or list of policyIds) for Alchemy's gas manager
70
+ * @param {PolicyToken | undefined} policyToken - The policy token configuration
71
+ * @returns {Pick<ClientMiddlewareConfig, "dummyPaymasterAndData" | "paymasterAndData">} Partial client middleware configuration containing `dummyPaymasterAndData` and `paymasterAndData`
46
72
  */
47
73
  export function alchemyGasManagerMiddleware(
48
- policyId: string | string[]
49
- ): Pick<ClientMiddlewareConfig, "dummyPaymasterAndData" | "paymasterAndData"> {
50
- return erc7677Middleware<{ policyId: string | string[] }>({
51
- context: { policyId: policyId },
52
- });
74
+ policyId: string | string[],
75
+ policyToken?: PolicyToken
76
+ ): Required<
77
+ Pick<ClientMiddlewareConfig, "dummyPaymasterAndData" | "paymasterAndData">
78
+ > {
79
+ const buildContext = async (
80
+ uo: Parameters<ClientMiddlewareFn>[0],
81
+ args: Parameters<ClientMiddlewareFn>[1]
82
+ ): Promise<Context> => {
83
+ const context: Context = { policyId };
84
+
85
+ const { account, client } = args;
86
+ if (!client.chain) {
87
+ throw new ChainNotFoundError();
88
+ }
89
+
90
+ if (policyToken !== undefined) {
91
+ const userOp = await deepHexlify(await resolveProperties(uo));
92
+ context.erc20Context = {
93
+ tokenAddress: policyToken.address,
94
+ ...(policyToken.maxTokenAmount
95
+ ? { maxTokenAmount: policyToken.maxTokenAmount }
96
+ : {}),
97
+ };
98
+
99
+ if (policyToken.approvalMode === "PERMIT") {
100
+ context.erc20Context.permit = await generateSignedPermit(
101
+ userOp,
102
+ client as AlchemySmartAccountClient,
103
+ account,
104
+ policyId,
105
+ policyToken
106
+ );
107
+ }
108
+ }
109
+
110
+ return context;
111
+ };
112
+ return {
113
+ dummyPaymasterAndData: async (uo, args) => {
114
+ const context = await buildContext(uo, args);
115
+ const baseMiddleware = erc7677Middleware({ context });
116
+ return baseMiddleware.dummyPaymasterAndData(uo, args);
117
+ },
118
+
119
+ paymasterAndData: async (uo, args) => {
120
+ const context = await buildContext(uo, args);
121
+ const baseMiddleware = erc7677Middleware({ context });
122
+ return baseMiddleware.paymasterAndData(uo, args);
123
+ },
124
+ };
53
125
  }
54
126
 
55
127
  interface AlchemyGasAndPaymasterAndDataMiddlewareParams {
56
128
  policyId: string | string[];
129
+ policyToken?: PolicyToken;
57
130
  transport: AlchemyTransport;
58
131
  gasEstimatorOverride?: ClientMiddlewareFn;
59
132
  feeEstimatorOverride?: ClientMiddlewareFn;
60
133
  }
61
134
 
135
+ export type PolicyToken = {
136
+ address: Address;
137
+ maxTokenAmount?: bigint;
138
+ approvalMode?: "NONE" | "PERMIT";
139
+ erc20Name?: string;
140
+ version?: string;
141
+ };
142
+
62
143
  /**
63
144
  * Paymaster middleware factory that uses Alchemy's Gas Manager for sponsoring
64
145
  * transactions. Uses Alchemy's custom `alchemy_requestGasAndPaymasterAndData`
@@ -86,7 +167,7 @@ interface AlchemyGasAndPaymasterAndDataMiddlewareParams {
86
167
  * @param {AlchemyGasAndPaymasterAndDataMiddlewareParams.transport} params.transport fallback transport to use for fee estimation when not using the paymaster
87
168
  * @param {AlchemyGasAndPaymasterAndDataMiddlewareParams.gasEstimatorOverride} params.gasEstimatorOverride custom gas estimator middleware
88
169
  * @param {AlchemyGasAndPaymasterAndDataMiddlewareParams.feeEstimatorOverride} params.feeEstimatorOverride custom fee estimator middleware
89
- * @returns {Pick<ClientMiddlewareConfig, "dummyPaymasterAndData" | "paymasterAndData">} partial client middleware configuration containing `dummyPaymasterAndData` and `paymasterAndData`
170
+ * @returns {Pick<ClientMiddlewareConfig, "dummyPaymasterAndData" | "feeEstimator" | "gasEstimator" | "paymasterAndData">} partial client middleware configuration containing `dummyPaymasterAndData`, `feeEstimator`, `gasEstimator`, and `paymasterAndData`
90
171
  */
91
172
  export function alchemyGasAndPaymasterAndDataMiddleware(
92
173
  params: AlchemyGasAndPaymasterAndDataMiddlewareParams
@@ -94,8 +175,13 @@ export function alchemyGasAndPaymasterAndDataMiddleware(
94
175
  ClientMiddlewareConfig,
95
176
  "dummyPaymasterAndData" | "feeEstimator" | "gasEstimator" | "paymasterAndData"
96
177
  > {
97
- const { policyId, transport, gasEstimatorOverride, feeEstimatorOverride } =
98
- params;
178
+ const {
179
+ policyId,
180
+ policyToken,
181
+ transport,
182
+ gasEstimatorOverride,
183
+ feeEstimatorOverride,
184
+ } = params;
99
185
  return {
100
186
  dummyPaymasterAndData: async (uo, args) => {
101
187
  if (
@@ -109,10 +195,10 @@ export function alchemyGasAndPaymasterAndDataMiddleware(
109
195
  }
110
196
 
111
197
  // Fall back to the default 7677 dummyPaymasterAndData middleware.
112
- return alchemyGasManagerMiddleware(policyId).dummyPaymasterAndData!(
113
- uo,
114
- args
115
- );
198
+ return alchemyGasManagerMiddleware(
199
+ policyId,
200
+ policyToken
201
+ ).dummyPaymasterAndData(uo, args);
116
202
  },
117
203
  feeEstimator: (uo, args) => {
118
204
  return feeEstimatorOverride
@@ -188,6 +274,26 @@ export function alchemyGasAndPaymasterAndDataMiddleware(
188
274
  : {}),
189
275
  });
190
276
 
277
+ let erc20Context: RequestGasAndPaymasterAndDataRequest[0]["erc20Context"] =
278
+ undefined;
279
+ if (policyToken !== undefined) {
280
+ erc20Context = {
281
+ tokenAddress: policyToken.address,
282
+ ...(policyToken.maxTokenAmount
283
+ ? { maxTokenAmount: policyToken.maxTokenAmount }
284
+ : {}),
285
+ };
286
+ if (policyToken.approvalMode === "PERMIT") {
287
+ erc20Context.permit = await generateSignedPermit(
288
+ userOp,
289
+ client,
290
+ account,
291
+ policyId,
292
+ policyToken
293
+ );
294
+ }
295
+ }
296
+
191
297
  const result = await (client as AlchemySmartAccountClient).request({
192
298
  method: "alchemy_requestGasAndPaymasterAndData",
193
299
  params: [
@@ -197,6 +303,11 @@ export function alchemyGasAndPaymasterAndDataMiddleware(
197
303
  userOperation: userOp,
198
304
  dummySignature: await account.getDummySignature(),
199
305
  overrides,
306
+ ...(erc20Context
307
+ ? {
308
+ erc20Context,
309
+ }
310
+ : {}),
200
311
  },
201
312
  ],
202
313
  });
@@ -256,3 +367,101 @@ const overrideField = <
256
367
  }
257
368
  return undefined;
258
369
  };
370
+
371
+ /**
372
+ * Utility function to generate a signed Permit for erc20 transaction
373
+ *
374
+ * @param {UserOperationRequest<TEntryPointVersion>} userOp - The user operation request
375
+ * @param {MiddlewareClient} client - The Alchemy smart account client
376
+ * @param {TAccount} account - The smart account instance
377
+ * @param {string | string[]} policyId - The policy ID or array of policy IDs
378
+ * @param {PolicyToken} policyToken - The policy token configuration
379
+ * @param {Address} policyToken.address - ERC20 contract addressya
380
+ * @param {bigint} [policyToken.maxTokenAmount] - Optional ERC20 token limit
381
+ * @param {"NONE" | "PERMIT"} [policyToken.approvalMode] - ERC20 approve mode
382
+ * @param {string} [policyToken.erc20Name] - EIP2612 specified ERC20 contract name
383
+ * @param {string} [policyToken.version] - EIP2612 specified ERC20 contract version
384
+ * @returns {Promise<Hex>} Returns a Promise containing the signed EIP2612 permit
385
+ */
386
+ const generateSignedPermit = async <
387
+ TAccount extends SmartContractAccount,
388
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion
389
+ >(
390
+ userOp: UserOperationRequest<TEntryPointVersion>,
391
+ client: MiddlewareClient,
392
+ account: TAccount,
393
+ policyId: string | string[],
394
+ policyToken: {
395
+ address: Address;
396
+ maxTokenAmount?: bigint;
397
+ approvalMode?: "NONE" | "PERMIT";
398
+ erc20Name?: string;
399
+ version?: string;
400
+ }
401
+ ): Promise<Hex> => {
402
+ if (!client.chain) {
403
+ throw new ChainNotFoundError();
404
+ }
405
+ if (!policyToken.erc20Name || !policyToken.version) {
406
+ throw new Error("erc20Name or version is missing");
407
+ }
408
+ // get a paymaster address
409
+ const maxAmountToken = policyToken.maxTokenAmount || maxUint256;
410
+ const paymasterData = await (client as Erc7677Client).request({
411
+ method: "pm_getPaymasterStubData",
412
+ params: [
413
+ userOp,
414
+ account.getEntryPoint().address,
415
+ toHex(client.chain.id),
416
+ {
417
+ policyId: Array.isArray(policyId) ? policyId[0] : policyId,
418
+ },
419
+ ],
420
+ });
421
+
422
+ const paymasterAddress = paymasterData.paymaster
423
+ ? paymasterData.paymaster
424
+ : paymasterData.paymasterAndData
425
+ ? sliceHex(paymasterData.paymasterAndData, 0, 20)
426
+ : undefined;
427
+
428
+ if (paymasterAddress === undefined || paymasterAddress === "0x") {
429
+ throw new Error("no paymaster contract address available");
430
+ }
431
+ const deadline = maxUint256;
432
+ const { data } = await client.call({
433
+ to: policyToken.address,
434
+ data: encodeFunctionData({
435
+ abi: parseAbi(EIP712NoncesAbi),
436
+ functionName: "nonces",
437
+ args: [account.address],
438
+ }),
439
+ });
440
+ if (!data) {
441
+ throw new Error("No nonces returned from erc20 contract call");
442
+ }
443
+
444
+ const typedPermitData = {
445
+ types: PermitTypes,
446
+ primaryType: "Permit" as const,
447
+ domain: {
448
+ name: policyToken.erc20Name ?? "",
449
+ version: policyToken.version ?? "",
450
+ chainId: BigInt(client.chain.id),
451
+ verifyingContract: policyToken.address,
452
+ } satisfies PermitDomain,
453
+ message: {
454
+ owner: account.address,
455
+ spender: paymasterAddress,
456
+ value: maxAmountToken,
457
+ nonce: BigInt(data),
458
+ deadline,
459
+ } satisfies PermitMessage,
460
+ } as const;
461
+
462
+ const signedPermit = await account.signTypedData(typedPermitData);
463
+ return encodeAbiParameters(
464
+ [{ type: "uint256" }, { type: "uint256" }, { type: "bytes" }],
465
+ [maxAmountToken, deadline, signedPermit]
466
+ );
467
+ };
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // This file is autogenerated by inject-version.ts. Any changes will be
2
2
  // overwritten on commit!
3
- export const VERSION = "4.30.0";
3
+ export const VERSION = "4.31.0";