Skip to content

Commit b55d46b

Browse files
authored
Merge pull request #430 from pimlicolabs/fix/pm_sponsorUserOperation
Fix/pm sponsor user operation
2 parents 9456b8a + bdd6705 commit b55d46b

File tree

6 files changed

+160
-40
lines changed

6 files changed

+160
-40
lines changed

.changeset/twelve-experts-heal.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pimlico/mock-paymaster": patch
3+
---
4+
5+
Fixed AA34 error in pm_sponsorUserOperation due to signing wrong userOp fields

packages/mock-paymaster/helpers/erc20-utils.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const deployErc20Token = async (
3636
}
3737

3838
export const tokenBalanceOf = async (holder: Address, anvilRpc: string) => {
39-
const publicClient = getPublicClient(anvilRpc)
39+
const publicClient = await getPublicClient(anvilRpc)
4040

4141
const balance = await publicClient.call({
4242
to: erc20Address,
@@ -59,7 +59,10 @@ export const sudoMintTokens = async ({
5959
to: Address
6060
anvilRpc: string
6161
}) => {
62-
const walletClient = getAnvilWalletClient({ addressIndex: 0, anvilRpc })
62+
const walletClient = await getAnvilWalletClient({
63+
addressIndex: 0,
64+
anvilRpc
65+
})
6366

6467
await walletClient.sendTransaction({
6568
to: erc20Address,

packages/mock-paymaster/helpers/utils.ts

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import {
77
type Transport,
88
type WalletClient,
99
createPublicClient,
10-
createWalletClient
10+
createWalletClient,
11+
defineChain
1112
} from "viem"
1213
import { mnemonicToAccount } from "viem/accounts"
13-
import { foundry } from "viem/chains"
1414
import { erc20Address } from "./erc20-utils.js"
1515
import { RpcError, ValidationErrors } from "./schema.js"
1616

@@ -19,9 +19,32 @@ export const maxBigInt = (a: bigint, b: bigint) => {
1919
return a > b ? a : b
2020
}
2121

22-
export const getPublicClient = (
22+
export const getChain = async (rpcUrl: string): Promise<Chain> => {
23+
const tempClient = createPublicClient({
24+
transport: http(rpcUrl)
25+
})
26+
27+
const chainId = await tempClient.getChainId()
28+
29+
return defineChain({
30+
id: chainId,
31+
name: `Chain ${chainId}`,
32+
nativeCurrency: {
33+
name: "ETH",
34+
symbol: "ETH",
35+
decimals: 18
36+
},
37+
rpcUrls: {
38+
default: {
39+
http: [rpcUrl]
40+
}
41+
}
42+
})
43+
}
44+
45+
export const getPublicClient = async (
2346
anvilRpc: string
24-
): PublicClient<Transport, Chain> => {
47+
): Promise<PublicClient<Transport, Chain>> => {
2548
const transport = http(anvilRpc, {
2649
// onFetchRequest: async (req) => {
2750
// console.log(await req.json(), "request")
@@ -31,29 +54,31 @@ export const getPublicClient = (
3154
//}
3255
})
3356

57+
const chain = await getChain(anvilRpc)
58+
3459
return createPublicClient({
35-
chain: foundry,
60+
chain,
3661
transport: transport,
3762
pollingInterval: 100
3863
})
3964
}
4065

41-
export const getAnvilWalletClient = ({
66+
export const getAnvilWalletClient = async ({
4267
addressIndex,
4368
anvilRpc
44-
}: { addressIndex: number; anvilRpc: string }): WalletClient<
45-
Transport,
46-
Chain,
47-
Account
69+
}: { addressIndex: number; anvilRpc: string }): Promise<
70+
WalletClient<Transport, Chain, Account>
4871
> => {
72+
const chain = await getChain(anvilRpc)
73+
4974
return createWalletClient({
5075
account: mnemonicToAccount(
5176
"test test test test test test test test test test test junk",
5277
{
5378
addressIndex
5479
}
5580
),
56-
chain: foundry,
81+
chain,
5782
transport: http(anvilRpc)
5883
})
5984
}

packages/mock-paymaster/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ import Fastify from "fastify"
33
import { defineInstance } from "prool"
44
import { http, createPublicClient } from "viem"
55
import { createBundlerClient } from "viem/account-abstraction"
6-
import { foundry } from "viem/chains"
76
import { deployErc20Token } from "./helpers/erc20-utils.js"
8-
import { getAnvilWalletClient } from "./helpers/utils.js"
7+
import { getAnvilWalletClient, getChain } from "./helpers/utils.js"
98
import { createRpcHandler } from "./relay.js"
109
import { deployPaymasters } from "./singletonPaymasters.js"
1110

@@ -23,16 +22,17 @@ export const paymaster = defineInstance(
2322
port: _port,
2423
name: "mock-paymaster",
2524
start: async ({ port = _port }) => {
26-
const walletClient = getAnvilWalletClient({
25+
const chain = await getChain(anvilRpc)
26+
const walletClient = await getAnvilWalletClient({
2727
anvilRpc,
2828
addressIndex: 1
2929
})
3030
const publicClient = createPublicClient({
3131
transport: http(anvilRpc),
32-
chain: foundry
32+
chain
3333
})
3434
const bundler = createBundlerClient({
35-
chain: foundry,
35+
chain,
3636
transport: http(altoRpc)
3737
})
3838

packages/mock-paymaster/relay.ts

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,34 +69,38 @@ const handlePmSponsor = async ({
6969
}) => {
7070
const is06 = entryPoint === entryPoint06Address
7171

72-
let op = {
72+
let sponsoredUserOp = {
7373
...userOperation,
7474
...getDummyPaymasterData({ is06, paymaster, paymasterMode })
7575
} as UserOperation
7676

77+
// User provided gasLimits
7778
const callGasLimit = userOperation.callGasLimit
7879
const verificationGasLimit = userOperation.verificationGasLimit
7980
const preVerificationGas = userOperation.preVerificationGas
8081

8182
if (estimateGas) {
8283
try {
8384
const gasEstimates = await bundler.estimateUserOperationGas({
84-
...op,
85+
...sponsoredUserOp,
8586
entryPointAddress: entryPoint
8687
})
8788

88-
op = {
89-
...op,
89+
sponsoredUserOp = {
90+
...sponsoredUserOp,
9091
...gasEstimates
9192
} as UserOperation
9293

93-
op.callGasLimit = maxBigInt(op.callGasLimit, callGasLimit)
94-
op.preVerificationGas = maxBigInt(
95-
op.preVerificationGas,
94+
sponsoredUserOp.callGasLimit = maxBigInt(
95+
gasEstimates.callGasLimit,
96+
callGasLimit
97+
)
98+
sponsoredUserOp.preVerificationGas = maxBigInt(
99+
gasEstimates.preVerificationGas,
96100
preVerificationGas
97101
)
98-
op.verificationGasLimit = maxBigInt(
99-
op.verificationGasLimit,
102+
sponsoredUserOp.verificationGasLimit = maxBigInt(
103+
gasEstimates.verificationGasLimit,
100104
verificationGasLimit
101105
)
102106
} catch (e: unknown) {
@@ -116,17 +120,19 @@ const handlePmSponsor = async ({
116120
}
117121

118122
const result = {
119-
preVerificationGas: toHex(op.preVerificationGas),
120-
callGasLimit: toHex(op.callGasLimit),
123+
preVerificationGas: toHex(sponsoredUserOp.preVerificationGas),
124+
callGasLimit: toHex(sponsoredUserOp.callGasLimit),
121125
paymasterVerificationGasLimit: toHex(
122-
op.paymasterVerificationGasLimit || 0
126+
sponsoredUserOp.paymasterVerificationGasLimit || 0
127+
),
128+
paymasterPostOpGasLimit: toHex(
129+
sponsoredUserOp.paymasterPostOpGasLimit || 0
123130
),
124-
paymasterPostOpGasLimit: toHex(op.paymasterPostOpGasLimit || 0),
125-
verificationGasLimit: toHex(op.verificationGasLimit || 0),
131+
verificationGasLimit: toHex(sponsoredUserOp.verificationGasLimit || 0),
126132
...(await getSignedPaymasterData({
127133
publicClient,
128134
signer: paymasterSigner,
129-
userOp: userOperation,
135+
userOp: sponsoredUserOp,
130136
paymaster,
131137
paymasterMode
132138
}))
@@ -277,6 +283,15 @@ const handleMethod = async ({
277283
]
278284
}
279285

286+
if (parsedBody.method === "pimlico_getUserOperationGasPrice") {
287+
return await bundler.request({
288+
// @ts-ignore
289+
method: "pimlico_getUserOperationGasPrice",
290+
// @ts-ignore
291+
params: []
292+
})
293+
}
294+
280295
if (parsedBody.method === "pimlico_getTokenQuotes") {
281296
const params = pimlicoGetTokenQuotesSchema.safeParse(parsedBody.params)
282297

packages/permissionless/actions/pimlico/sponsorUserOperation.test.ts

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import { isHash, zeroAddress } from "viem"
2-
import { entryPoint06Address } from "viem/account-abstraction"
2+
import {
3+
type UserOperation,
4+
entryPoint06Address,
5+
entryPoint07Address,
6+
entryPoint08Address
7+
} from "viem/account-abstraction"
38
import { describe, expect } from "vitest"
49
import { testWithRpc } from "../../../permissionless-test/src/testWithRpc"
510
import {
611
getBundlerClient,
712
getPimlicoClient,
813
getSimpleAccountClient
914
} from "../../../permissionless-test/src/utils"
15+
import { sponsorUserOperation } from "./sponsorUserOperation"
1016

1117
describe("sponsorUserOperation", () => {
1218
testWithRpc("sponsorUserOperation_V06", async ({ rpc }) => {
13-
const { altoRpc } = rpc
19+
const { altoRpc, paymasterRpc } = rpc
1420

1521
const bundlerClient = getPimlicoClient({
1622
entryPointVersion: "0.6",
@@ -30,7 +36,7 @@ describe("sponsorUserOperation", () => {
3036
...rpc
3137
})
3238

33-
const opHash = await simpleAccountClient.sendUserOperation({
39+
const preparedUserOp = await simpleAccountClient.prepareUserOperation({
3440
calls: [
3541
{
3642
to: zeroAddress,
@@ -40,6 +46,28 @@ describe("sponsorUserOperation", () => {
4046
]
4147
})
4248

49+
const paymasterClient = getPimlicoClient({
50+
entryPointVersion: "0.6",
51+
altoRpc: paymasterRpc
52+
})
53+
54+
const sponsorResult = await sponsorUserOperation(paymasterClient, {
55+
userOperation: preparedUserOp,
56+
entryPoint: {
57+
address: entryPoint06Address,
58+
version: "0.6"
59+
}
60+
})
61+
62+
const finalUserOp = {
63+
...preparedUserOp,
64+
...sponsorResult
65+
}
66+
const account = simpleAccountClient.account
67+
finalUserOp.signature = await account.signUserOperation(finalUserOp)
68+
69+
const opHash = await simpleAccountClient.sendUserOperation(finalUserOp)
70+
4371
expect(isHash(opHash)).toBe(true)
4472

4573
const userOperationReceipt =
@@ -61,7 +89,7 @@ describe("sponsorUserOperation", () => {
6189
})
6290

6391
testWithRpc("sponsorUserOperation_V07", async ({ rpc }) => {
64-
const { altoRpc } = rpc
92+
const { altoRpc, paymasterRpc } = rpc
6593

6694
const bundlerClient = getPimlicoClient({
6795
entryPointVersion: "0.7",
@@ -81,7 +109,7 @@ describe("sponsorUserOperation", () => {
81109
...rpc
82110
})
83111

84-
const opHash = await simpleAccountClient.sendUserOperation({
112+
const preparedUserOp = await simpleAccountClient.prepareUserOperation({
85113
calls: [
86114
{
87115
to: zeroAddress,
@@ -91,6 +119,28 @@ describe("sponsorUserOperation", () => {
91119
]
92120
})
93121

122+
const paymasterClient = getPimlicoClient({
123+
entryPointVersion: "0.7",
124+
altoRpc: paymasterRpc
125+
})
126+
127+
const sponsorResult = await sponsorUserOperation(paymasterClient, {
128+
userOperation: preparedUserOp,
129+
entryPoint: {
130+
address: entryPoint07Address,
131+
version: "0.7"
132+
}
133+
})
134+
135+
const finalUserOp = {
136+
...preparedUserOp,
137+
...sponsorResult
138+
}
139+
const account = simpleAccountClient.account
140+
finalUserOp.signature = await account.signUserOperation(finalUserOp)
141+
142+
const opHash = await simpleAccountClient.sendUserOperation(finalUserOp)
143+
94144
expect(isHash(opHash)).toBe(true)
95145

96146
const userOperationReceipt =
@@ -112,7 +162,7 @@ describe("sponsorUserOperation", () => {
112162
})
113163

114164
testWithRpc("sponsorUserOperation_V08", async ({ rpc }) => {
115-
const { altoRpc } = rpc
165+
const { altoRpc, paymasterRpc } = rpc
116166

117167
const bundlerClient = getPimlicoClient({
118168
entryPointVersion: "0.8",
@@ -132,16 +182,38 @@ describe("sponsorUserOperation", () => {
132182
...rpc
133183
})
134184

135-
const opHash = await simpleAccountClient.sendUserOperation({
185+
const preparedUserOp = (await simpleAccountClient.prepareUserOperation({
136186
calls: [
137187
{
138188
to: zeroAddress,
139189
data: "0x",
140190
value: 0n
141191
}
142192
]
193+
})) as UserOperation<"0.8">
194+
195+
const paymasterClient = getPimlicoClient({
196+
entryPointVersion: "0.8",
197+
altoRpc: paymasterRpc
143198
})
144199

200+
const sponsorResult = await sponsorUserOperation(paymasterClient, {
201+
userOperation: preparedUserOp,
202+
entryPoint: {
203+
address: entryPoint08Address,
204+
version: "0.8"
205+
}
206+
})
207+
208+
const finalUserOp = {
209+
...preparedUserOp,
210+
...sponsorResult
211+
}
212+
const account = simpleAccountClient.account
213+
finalUserOp.signature = await account.signUserOperation(finalUserOp)
214+
215+
const opHash = await simpleAccountClient.sendUserOperation(finalUserOp)
216+
145217
expect(isHash(opHash)).toBe(true)
146218

147219
const userOperationReceipt =

0 commit comments

Comments
 (0)