Skip to content

Commit 5c4757e

Browse files
committed
feat: insert permit for erc20 paymaster
1 parent f1ea3be commit 5c4757e

File tree

1 file changed

+81
-5
lines changed

1 file changed

+81
-5
lines changed

account-kit/infra/src/middleware/gasManager.ts

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
noopMiddleware,
2121
resolveProperties,
2222
} from "@aa-sdk/core";
23-
import { fromHex, isHex, type Hex } from "viem";
23+
import { fromHex, isHex, toHex, type Hex } from "viem";
2424
import type { AlchemySmartAccountClient } from "../client/smartAccountClient.js";
2525
import type { AlchemyTransport } from "../alchemyTransport.js";
2626
import { alchemyFeeEstimator } from "./feeEstimator.js";
@@ -62,7 +62,10 @@ interface AlchemyGasAndPaymasterAndDataMiddlewareParams {
6262

6363
export type PolicyToken = {
6464
address: string;
65+
maxTokenAmount: bigint;
6566
approvalMode: ApprovalMode;
67+
erc20Name: string;
68+
version: string;
6669
};
6770

6871
export enum ApprovalMode {
@@ -205,6 +208,81 @@ export function alchemyGasAndPaymasterAndDataMiddleware(
205208
: {}),
206209
});
207210

211+
let erc20Context = undefined;
212+
if (
213+
policyToken !== undefined &&
214+
policyToken.approvalMode === ApprovalMode.PERMIT
215+
) {
216+
// get a paymaster address
217+
let paymasterAddress = "0x";
218+
const paymasterData = await (
219+
client as AlchemySmartAccountClient
220+
).request({
221+
method: "pm_getPaymasterStubData",
222+
params: [
223+
userOp,
224+
account.getEntryPoint().address,
225+
toHex(client.chain.id),
226+
{
227+
policyId: Array.isArray(policyId) ? policyId[0] : policyId,
228+
},
229+
],
230+
});
231+
// todo: make a eth_estimateUserOperationGas to avoid client pass
232+
// maxAmountToken
233+
paymasterAddress = paymasterData.paymaster
234+
? paymasterData.paymaster
235+
: paymasterData.paymasterAndData
236+
? paymasterData.paymasterAndData.slice(0, 42)
237+
: "0x";
238+
const typed_permit_data = {
239+
types: {
240+
Permit: [
241+
{
242+
name: "owner",
243+
type: "address",
244+
},
245+
{
246+
name: "spender",
247+
type: "address",
248+
},
249+
{
250+
name: "value",
251+
type: "uint256",
252+
},
253+
{
254+
name: "nonce",
255+
type: "uint256",
256+
},
257+
{
258+
name: "deadline",
259+
type: "uint256",
260+
},
261+
],
262+
},
263+
primaryType: "Permit",
264+
domain: {
265+
name: policyToken.erc20Name,
266+
version: policyToken.version,
267+
chainId: client.chain.id,
268+
verifyingContract: policyToken.address as Hex,
269+
},
270+
message: {
271+
owner: account.address as Hex,
272+
spender: paymasterAddress as Hex,
273+
value: policyToken.maxTokenAmount,
274+
nonce: (await account.getAccountNonce()) + BigInt(1),
275+
deadline: BigInt(0xffffffffffffffffffffffffffffffff),
276+
},
277+
} as const;
278+
279+
erc20Context = {
280+
tokenAddress: policyToken.address,
281+
permit: account.signTypedData(typed_permit_data),
282+
maxTokenAmount: policyToken.maxTokenAmount,
283+
};
284+
}
285+
208286
const result = await (client as AlchemySmartAccountClient).request({
209287
method: "alchemy_requestGasAndPaymasterAndData",
210288
params: [
@@ -214,11 +292,9 @@ export function alchemyGasAndPaymasterAndDataMiddleware(
214292
userOperation: userOp,
215293
dummySignature: await account.getDummySignature(),
216294
overrides,
217-
...(policyToken
295+
...(erc20Context
218296
? {
219-
erc20Context: {
220-
tokenAddress: policyToken.address,
221-
},
297+
erc20Context: erc20Context,
222298
}
223299
: {}),
224300
},

0 commit comments

Comments
 (0)