@pimlico/mock-paymaster 0.0.2

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 (53) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/_cjs/helpers/abi.js +1049 -0
  3. package/_cjs/helpers/abi.js.map +1 -0
  4. package/_cjs/helpers/erc20-utils.js +52 -0
  5. package/_cjs/helpers/erc20-utils.js.map +1 -0
  6. package/_cjs/helpers/schema.js +263 -0
  7. package/_cjs/helpers/schema.js.map +1 -0
  8. package/_cjs/helpers/utils.js +38 -0
  9. package/_cjs/helpers/utils.js.map +1 -0
  10. package/_cjs/index.js +55 -0
  11. package/_cjs/index.js.map +1 -0
  12. package/_cjs/package.json +1 -0
  13. package/_cjs/relay.js +250 -0
  14. package/_cjs/relay.js.map +1 -0
  15. package/_cjs/singletonPaymasters.js +266 -0
  16. package/_cjs/singletonPaymasters.js.map +1 -0
  17. package/_esm/helpers/abi.js +1046 -0
  18. package/_esm/helpers/abi.js.map +1 -0
  19. package/_esm/helpers/erc20-utils.js +46 -0
  20. package/_esm/helpers/erc20-utils.js.map +1 -0
  21. package/_esm/helpers/schema.js +260 -0
  22. package/_esm/helpers/schema.js.map +1 -0
  23. package/_esm/helpers/utils.js +39 -0
  24. package/_esm/helpers/utils.js.map +1 -0
  25. package/_esm/index.js +52 -0
  26. package/_esm/index.js.map +1 -0
  27. package/_esm/package.json +1 -0
  28. package/_esm/relay.js +249 -0
  29. package/_esm/relay.js.map +1 -0
  30. package/_esm/singletonPaymasters.js +263 -0
  31. package/_esm/singletonPaymasters.js.map +1 -0
  32. package/_types/helpers/abi.d.ts +977 -0
  33. package/_types/helpers/abi.d.ts.map +1 -0
  34. package/_types/helpers/erc20-utils.d.ts +10 -0
  35. package/_types/helpers/erc20-utils.d.ts.map +1 -0
  36. package/_types/helpers/schema.d.ts +1091 -0
  37. package/_types/helpers/schema.d.ts.map +1 -0
  38. package/_types/helpers/utils.d.ts +15 -0
  39. package/_types/helpers/utils.d.ts.map +1 -0
  40. package/_types/index.d.ts +6 -0
  41. package/_types/index.d.ts.map +1 -0
  42. package/_types/relay.d.ts +86 -0
  43. package/_types/relay.d.ts.map +1 -0
  44. package/_types/singletonPaymasters.d.ts +93267 -0
  45. package/_types/singletonPaymasters.d.ts.map +1 -0
  46. package/helpers/abi.ts +1046 -0
  47. package/helpers/erc20-utils.ts +80 -0
  48. package/helpers/schema.ts +272 -0
  49. package/helpers/utils.ts +77 -0
  50. package/index.ts +78 -0
  51. package/package.json +38 -0
  52. package/relay.ts +430 -0
  53. package/singletonPaymasters.ts +381 -0
package/relay.ts ADDED
@@ -0,0 +1,430 @@
1
+ import * as util from "node:util"
2
+ import type { FastifyReply, FastifyRequest } from "fastify"
3
+ import {
4
+ type Address,
5
+ BaseError,
6
+ type RpcRequestError,
7
+ getAddress,
8
+ toHex
9
+ } from "viem"
10
+ import {
11
+ type BundlerClient,
12
+ type UserOperation,
13
+ entryPoint06Address,
14
+ entryPoint07Address
15
+ } from "viem/account-abstraction"
16
+ import { fromZodError } from "zod-validation-error"
17
+ import { ERC20_ADDRESS } from "./helpers/erc20-utils.js"
18
+ import {
19
+ InternalBundlerError,
20
+ type JsonRpcSchema,
21
+ RpcError,
22
+ ValidationErrors,
23
+ jsonRpcSchema,
24
+ pimlicoGetTokenQuotesSchema,
25
+ pmGetPaymasterData,
26
+ pmGetPaymasterStubDataParamsSchema,
27
+ pmSponsorUserOperationParamsSchema
28
+ } from "./helpers/schema.js"
29
+ import {
30
+ type PaymasterMode,
31
+ isTokenSupported,
32
+ maxBigInt
33
+ } from "./helpers/utils.js"
34
+ import {
35
+ type SingletonPaymasterV06,
36
+ type SingletonPaymasterV07,
37
+ getDummyPaymasterData
38
+ } from "./singletonPaymasters.js"
39
+
40
+ const handleMethodV06 = async (
41
+ userOperation: UserOperation<"0.6">,
42
+ paymasterMode: PaymasterMode,
43
+ bundler: BundlerClient,
44
+ singletonPaymasterV06: SingletonPaymasterV06,
45
+ estimateGas: boolean
46
+ ) => {
47
+ let op: UserOperation<"0.6"> = {
48
+ ...userOperation,
49
+ ...getDummyPaymasterData(
50
+ true,
51
+ singletonPaymasterV06.singletonPaymaster.address,
52
+ paymasterMode
53
+ )
54
+ }
55
+
56
+ const callGasLimit = userOperation.callGasLimit
57
+ const verificationGasLimit = userOperation.verificationGasLimit
58
+ const preVerificationGas = userOperation.preVerificationGas
59
+
60
+ if (estimateGas) {
61
+ try {
62
+ const gasEstimates = await bundler.estimateUserOperationGas({
63
+ ...op
64
+ })
65
+ op = {
66
+ ...op,
67
+ ...gasEstimates
68
+ }
69
+
70
+ op.callGasLimit = maxBigInt(op.callGasLimit, callGasLimit)
71
+ op.preVerificationGas = maxBigInt(
72
+ op.preVerificationGas,
73
+ preVerificationGas
74
+ )
75
+ op.verificationGasLimit = maxBigInt(
76
+ op.verificationGasLimit,
77
+ verificationGasLimit
78
+ )
79
+ } catch (e: unknown) {
80
+ if (!(e instanceof BaseError)) throw new InternalBundlerError()
81
+ const err = e.walk() as RpcRequestError
82
+ throw err
83
+ }
84
+ } else if (
85
+ userOperation.preVerificationGas === 1n ||
86
+ userOperation.verificationGasLimit === 1n ||
87
+ userOperation.callGasLimit === 1n
88
+ ) {
89
+ throw new RpcError(
90
+ "Gas Limit values (preVerificationGas, verificationGasLimit, callGasLimit) must be set",
91
+ ValidationErrors.InvalidFields
92
+ )
93
+ }
94
+
95
+ const result = {
96
+ preVerificationGas: toHex(op.preVerificationGas),
97
+ callGasLimit: toHex(op.callGasLimit),
98
+ verificationGasLimit: toHex(op.verificationGasLimit || 0),
99
+ ...(await singletonPaymasterV06.encodePaymasterData(op, paymasterMode))
100
+ }
101
+
102
+ return result
103
+ }
104
+
105
+ const handleMethodV07 = async (
106
+ userOperation: UserOperation<"0.7">,
107
+ paymasterMode: PaymasterMode,
108
+ bundler: BundlerClient,
109
+ singletonPaymasterV07: SingletonPaymasterV07,
110
+ estimateGas: boolean
111
+ ) => {
112
+ let op = {
113
+ ...userOperation,
114
+ ...singletonPaymasterV07.getDummyPaymasterData(paymasterMode)
115
+ }
116
+
117
+ const callGasLimit = userOperation.callGasLimit
118
+ const verificationGasLimit = userOperation.verificationGasLimit
119
+ const preVerificationGas = userOperation.preVerificationGas
120
+
121
+ if (estimateGas) {
122
+ try {
123
+ const gasEstimates = await bundler.estimateUserOperationGas({
124
+ ...op
125
+ })
126
+
127
+ op = {
128
+ ...op,
129
+ ...gasEstimates
130
+ }
131
+
132
+ op.callGasLimit = maxBigInt(op.callGasLimit, callGasLimit)
133
+ op.preVerificationGas = maxBigInt(
134
+ op.preVerificationGas,
135
+ preVerificationGas
136
+ )
137
+ op.verificationGasLimit = maxBigInt(
138
+ op.verificationGasLimit,
139
+ verificationGasLimit
140
+ )
141
+ } catch (e: unknown) {
142
+ if (!(e instanceof BaseError)) throw new InternalBundlerError()
143
+ const err = e.walk() as RpcRequestError
144
+ throw err
145
+ }
146
+ } else if (
147
+ userOperation.preVerificationGas === 1n ||
148
+ userOperation.verificationGasLimit === 1n ||
149
+ userOperation.callGasLimit === 1n
150
+ ) {
151
+ throw new RpcError(
152
+ "Gas Limit values (preVerificationGas, verificationGasLimit, callGasLimit) must be set",
153
+ ValidationErrors.InvalidFields
154
+ )
155
+ }
156
+
157
+ const result = {
158
+ preVerificationGas: toHex(op.preVerificationGas),
159
+ callGasLimit: toHex(op.callGasLimit),
160
+ paymasterVerificationGasLimit: toHex(
161
+ op.paymasterVerificationGasLimit || 0
162
+ ),
163
+ paymasterPostOpGasLimit: toHex(op.paymasterPostOpGasLimit || 0),
164
+ verificationGasLimit: toHex(op.verificationGasLimit || 0),
165
+ ...(await singletonPaymasterV07.encodePaymasterData(op, paymasterMode))
166
+ }
167
+
168
+ return result
169
+ }
170
+
171
+ const handleMethod = async (
172
+ bundler: BundlerClient,
173
+ singletonPaymasterV07: SingletonPaymasterV07,
174
+ singletonPaymasterV06: SingletonPaymasterV06,
175
+ parsedBody: JsonRpcSchema
176
+ ) => {
177
+ if (parsedBody.method === "pm_sponsorUserOperation") {
178
+ const params = pmSponsorUserOperationParamsSchema.safeParse(
179
+ parsedBody.params
180
+ )
181
+
182
+ if (!params.success) {
183
+ throw new RpcError(
184
+ fromZodError(params.error).message,
185
+ ValidationErrors.InvalidFields
186
+ )
187
+ }
188
+
189
+ const [userOperation, entryPoint] = params.data
190
+
191
+ if (entryPoint === entryPoint07Address) {
192
+ return await handleMethodV07(
193
+ userOperation,
194
+ { mode: "verifying" },
195
+ bundler,
196
+ singletonPaymasterV07,
197
+ true
198
+ )
199
+ }
200
+
201
+ if (entryPoint === entryPoint06Address) {
202
+ return await handleMethodV06(
203
+ userOperation,
204
+ { mode: "verifying" },
205
+ bundler,
206
+ singletonPaymasterV06,
207
+ true
208
+ )
209
+ }
210
+
211
+ throw new RpcError(
212
+ "EntryPoint not supported",
213
+ ValidationErrors.InvalidFields
214
+ )
215
+ }
216
+
217
+ if (parsedBody.method === "pm_getPaymasterStubData") {
218
+ const params = pmGetPaymasterStubDataParamsSchema.safeParse(
219
+ parsedBody.params
220
+ )
221
+
222
+ if (!params.success) {
223
+ throw new RpcError(
224
+ fromZodError(params.error).message,
225
+ ValidationErrors.InvalidFields
226
+ )
227
+ }
228
+
229
+ const [, entryPoint, , data] = params.data
230
+
231
+ const paymasterMode = getPaymasterMode(data)
232
+
233
+ const sponsorData = {
234
+ name: "Pimlico",
235
+ icon: "data:image/jpeg;base64,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"
236
+ }
237
+
238
+ if (entryPoint === entryPoint07Address) {
239
+ return {
240
+ ...singletonPaymasterV07.getDummyPaymasterData(paymasterMode),
241
+ paymasterVerificationGasLimit: toHex(50_000n),
242
+ paymasterPostOpGasLimit: toHex(100_000n),
243
+ sponsor: sponsorData,
244
+ isFinal: false
245
+ }
246
+ }
247
+
248
+ if (entryPoint === entryPoint06Address) {
249
+ return {
250
+ ...singletonPaymasterV06.getDummyPaymasterData(paymasterMode),
251
+ sponsor: sponsorData,
252
+ isFinal: false
253
+ }
254
+ }
255
+
256
+ throw new RpcError(
257
+ "EntryPoint not supported",
258
+ ValidationErrors.InvalidFields
259
+ )
260
+ }
261
+
262
+ if (parsedBody.method === "pm_getPaymasterData") {
263
+ const params = pmGetPaymasterData.safeParse(parsedBody.params)
264
+
265
+ if (!params.success) {
266
+ throw new RpcError(
267
+ fromZodError(params.error).message,
268
+ ValidationErrors.InvalidFields
269
+ )
270
+ }
271
+
272
+ const [userOperation, entryPoint, , data] = params.data
273
+ const paymasterMode = getPaymasterMode(data)
274
+
275
+ if (entryPoint === entryPoint07Address) {
276
+ const { paymaster, paymasterData } = await handleMethodV07(
277
+ userOperation as UserOperation<"0.7">,
278
+ paymasterMode,
279
+ bundler,
280
+ singletonPaymasterV07,
281
+ false
282
+ )
283
+
284
+ return { paymaster, paymasterData }
285
+ }
286
+
287
+ if (entryPoint === entryPoint06Address) {
288
+ const { paymasterAndData } = await handleMethodV06(
289
+ userOperation,
290
+ paymasterMode,
291
+ bundler,
292
+ singletonPaymasterV06,
293
+ false
294
+ )
295
+
296
+ return { paymasterAndData }
297
+ }
298
+
299
+ throw new RpcError(
300
+ "EntryPoint not supported",
301
+ ValidationErrors.InvalidFields
302
+ )
303
+ }
304
+
305
+ if (parsedBody.method === "pm_validateSponsorshipPolicies") {
306
+ return [
307
+ {
308
+ sponsorshipPolicyId: "sp_crazy_kangaroo",
309
+ data: {
310
+ name: "Free ops for devs",
311
+ author: "foo",
312
+ icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
313
+ description: "Free userOps :)"
314
+ }
315
+ }
316
+ ]
317
+ }
318
+
319
+ if (parsedBody.method === "pimlico_getTokenQuotes") {
320
+ const params = pimlicoGetTokenQuotesSchema.safeParse(parsedBody.params)
321
+
322
+ if (!params.success) {
323
+ throw new RpcError(
324
+ fromZodError(params.error).message,
325
+ ValidationErrors.InvalidFields
326
+ )
327
+ }
328
+
329
+ const [context, entryPoint] = params.data
330
+ const { tokens } = context
331
+
332
+ const quotes = {
333
+ [getAddress("0xffffffffffffffffffffffffffffffffffffffff")]: {
334
+ exchangeRateNativeToUsd: "0x1a2b3c4d5e6f7890abcdef",
335
+ exchangeRate: "0x3a7b9c8d6e5f4321",
336
+ balanceSlot: "0x0",
337
+ allowanceSlot: "0x1",
338
+ postOpGas: "0x1a2b3c"
339
+ },
340
+ [ERC20_ADDRESS]: {
341
+ exchangeRateNativeToUsd: "0x5cc717fbb3450c0000000",
342
+ exchangeRate: "0x5cc717fbb3450c0000",
343
+ balanceSlot: "0x5",
344
+ allowanceSlot: "0x0",
345
+ postOpGas: "0xc350"
346
+ }
347
+ }
348
+
349
+ let paymaster: Address
350
+ if (entryPoint === entryPoint07Address) {
351
+ paymaster = singletonPaymasterV07.singletonPaymaster.address
352
+ } else {
353
+ paymaster = singletonPaymasterV06.singletonPaymaster.address
354
+ }
355
+
356
+ return {
357
+ quotes: tokens
358
+ .filter((t) => quotes[t]) // Filter out unrecongized tokens
359
+ .map((token) => ({
360
+ ...quotes[token],
361
+ paymaster,
362
+ token
363
+ }))
364
+ }
365
+ }
366
+
367
+ throw new RpcError(
368
+ `Attempted to call an unknown method ${parsedBody.method}`,
369
+ ValidationErrors.InvalidFields
370
+ )
371
+ }
372
+
373
+ export const createRpcHandler = (
374
+ bundler: BundlerClient,
375
+ singletonPaymasterV07: SingletonPaymasterV07,
376
+ singletonPaymasterV06: SingletonPaymasterV06
377
+ ) => {
378
+ return async (request: FastifyRequest, _reply: FastifyReply) => {
379
+ const body = request.body
380
+ const parsedBody = jsonRpcSchema.safeParse(body)
381
+ if (!parsedBody.success) {
382
+ throw new RpcError(
383
+ fromZodError(parsedBody.error).message,
384
+ ValidationErrors.InvalidFields
385
+ )
386
+ }
387
+
388
+ try {
389
+ const result = await handleMethod(
390
+ bundler,
391
+ singletonPaymasterV07,
392
+ singletonPaymasterV06,
393
+ parsedBody.data
394
+ )
395
+
396
+ return {
397
+ jsonrpc: "2.0",
398
+ id: parsedBody.data.id,
399
+ result
400
+ }
401
+ } catch (err: unknown) {
402
+ console.log(`JSON.stringify(err): ${util.inspect(err)}`)
403
+
404
+ const error = {
405
+ // biome-ignore lint/suspicious/noExplicitAny:
406
+ message: (err as any).message,
407
+ // biome-ignore lint/suspicious/noExplicitAny:
408
+ data: (err as any).data,
409
+ // biome-ignore lint/suspicious/noExplicitAny:
410
+ code: (err as any).code ?? -32603
411
+ }
412
+
413
+ return {
414
+ jsonrpc: "2.0",
415
+ id: parsedBody.data.id,
416
+ error
417
+ }
418
+ }
419
+ }
420
+ }
421
+
422
+ const getPaymasterMode = (data: any): PaymasterMode => {
423
+ if (data !== null && "token" in data) {
424
+ isTokenSupported(data.token)
425
+
426
+ return { mode: "erc20", token: data.token }
427
+ }
428
+
429
+ return { mode: "verifying" }
430
+ }