permissionless 0.2.4 → 0.2.6

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 (107) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/_cjs/accounts/biconomy/toBiconomySmartAccount.js +1 -2
  3. package/_cjs/accounts/biconomy/toBiconomySmartAccount.js.map +1 -1
  4. package/_cjs/accounts/kernel/toEcdsaKernelSmartAccount.js +2 -2
  5. package/_cjs/accounts/kernel/toEcdsaKernelSmartAccount.js.map +1 -1
  6. package/_cjs/accounts/kernel/utils/signMessage.js +1 -2
  7. package/_cjs/accounts/kernel/utils/signMessage.js.map +1 -1
  8. package/_cjs/accounts/kernel/utils/signTypedData.js +1 -2
  9. package/_cjs/accounts/kernel/utils/signTypedData.js.map +1 -1
  10. package/_cjs/accounts/light/toLightSmartAccount.js +1 -2
  11. package/_cjs/accounts/light/toLightSmartAccount.js.map +1 -1
  12. package/_cjs/accounts/safe/toSafeSmartAccount.js +1 -2
  13. package/_cjs/accounts/safe/toSafeSmartAccount.js.map +1 -1
  14. package/_cjs/accounts/simple/toSimpleSmartAccount.js +1 -2
  15. package/_cjs/accounts/simple/toSimpleSmartAccount.js.map +1 -1
  16. package/_cjs/accounts/trust/toTrustSmartAccount.js +2 -2
  17. package/_cjs/accounts/trust/toTrustSmartAccount.js.map +1 -1
  18. package/_cjs/actions/erc7579/accountId.js +1 -2
  19. package/_cjs/actions/erc7579/accountId.js.map +1 -1
  20. package/_cjs/actions/erc7579/installModule.js +1 -2
  21. package/_cjs/actions/erc7579/installModule.js.map +1 -1
  22. package/_cjs/actions/erc7579/installModules.js +1 -2
  23. package/_cjs/actions/erc7579/installModules.js.map +1 -1
  24. package/_cjs/actions/erc7579/isModuleInstalled.js +1 -2
  25. package/_cjs/actions/erc7579/isModuleInstalled.js.map +1 -1
  26. package/_cjs/actions/erc7579/supportsExecutionMode.js +2 -3
  27. package/_cjs/actions/erc7579/supportsExecutionMode.js.map +1 -1
  28. package/_cjs/actions/erc7579/supportsModule.js +2 -3
  29. package/_cjs/actions/erc7579/supportsModule.js.map +1 -1
  30. package/_cjs/actions/erc7579/uninstallModule.js +1 -2
  31. package/_cjs/actions/erc7579/uninstallModule.js.map +1 -1
  32. package/_cjs/actions/erc7579/uninstallModules.js +1 -2
  33. package/_cjs/actions/erc7579/uninstallModules.js.map +1 -1
  34. package/_cjs/actions/erc7579.js +2 -2
  35. package/_cjs/actions/erc7579.js.map +1 -1
  36. package/_cjs/actions/smartAccount/sendTransaction.js +1 -2
  37. package/_cjs/actions/smartAccount/sendTransaction.js.map +1 -1
  38. package/_cjs/actions/smartAccount/signMessage.js +1 -2
  39. package/_cjs/actions/smartAccount/signMessage.js.map +1 -1
  40. package/_cjs/actions/smartAccount/signTypedData.js +1 -2
  41. package/_cjs/actions/smartAccount/signTypedData.js.map +1 -1
  42. package/_cjs/actions/smartAccount/writeContract.js +1 -2
  43. package/_cjs/actions/smartAccount/writeContract.js.map +1 -1
  44. package/_cjs/clients/createSmartAccountClient.js +26 -9
  45. package/_cjs/clients/createSmartAccountClient.js.map +1 -1
  46. package/_cjs/clients/decorators/smartAccount.js +1 -2
  47. package/_cjs/clients/decorators/smartAccount.js.map +1 -1
  48. package/_cjs/clients/pimlico.js +1 -2
  49. package/_cjs/clients/pimlico.js.map +1 -1
  50. package/_cjs/experimental/pimlico/index.js +6 -0
  51. package/_cjs/experimental/pimlico/index.js.map +1 -0
  52. package/_cjs/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.js +126 -0
  53. package/_cjs/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.js.map +1 -0
  54. package/_cjs/utils/decodeNonce.js +1 -2
  55. package/_cjs/utils/decodeNonce.js.map +1 -1
  56. package/_cjs/utils/deepHexlify.js +2 -2
  57. package/_cjs/utils/deepHexlify.js.map +1 -1
  58. package/_cjs/utils/encode7579Calls.js +1 -2
  59. package/_cjs/utils/encode7579Calls.js.map +1 -1
  60. package/_cjs/utils/encodeInstallModule.js +1 -2
  61. package/_cjs/utils/encodeInstallModule.js.map +1 -1
  62. package/_cjs/utils/encodeNonce.js +1 -2
  63. package/_cjs/utils/encodeNonce.js.map +1 -1
  64. package/_cjs/utils/encodeUninstallModule.js +1 -2
  65. package/_cjs/utils/encodeUninstallModule.js.map +1 -1
  66. package/_cjs/utils/getAddressFromInitCodeOrPaymasterAndData.js +1 -2
  67. package/_cjs/utils/getAddressFromInitCodeOrPaymasterAndData.js.map +1 -1
  68. package/_cjs/utils/getPackedUserOperation.js +9 -9
  69. package/_cjs/utils/getPackedUserOperation.js.map +1 -1
  70. package/_cjs/utils/toOwner.js +1 -2
  71. package/_cjs/utils/toOwner.js.map +1 -1
  72. package/_esm/clients/createSmartAccountClient.js +26 -8
  73. package/_esm/clients/createSmartAccountClient.js.map +1 -1
  74. package/_esm/experimental/pimlico/index.js +3 -0
  75. package/_esm/experimental/pimlico/index.js.map +1 -0
  76. package/_esm/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.js +144 -0
  77. package/_esm/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.js.map +1 -0
  78. package/_types/accounts/kernel/utils/encodeCallData.d.ts.map +1 -1
  79. package/_types/accounts/kernel/utils/getNonceKey.d.ts.map +1 -1
  80. package/_types/accounts/kernel/utils/isKernelV2.d.ts.map +1 -1
  81. package/_types/actions/erc7579.d.ts +1 -249
  82. package/_types/actions/erc7579.d.ts.map +1 -1
  83. package/_types/actions/pimlico/getTokenQuotes.d.ts.map +1 -1
  84. package/_types/actions/pimlico/getUserOperationGasPrice.d.ts.map +1 -1
  85. package/_types/actions/pimlico/getUserOperationStatus.d.ts.map +1 -1
  86. package/_types/actions/pimlico/sendCompressedUserOperation.d.ts.map +1 -1
  87. package/_types/actions/pimlico/sponsorUserOperation.d.ts.map +1 -1
  88. package/_types/actions/pimlico/validateSponsorshipPolicies.d.ts.map +1 -1
  89. package/_types/actions/public/getAccountNonce.d.ts.map +1 -1
  90. package/_types/actions/public/getSenderAddress.d.ts.map +1 -1
  91. package/_types/clients/createSmartAccountClient.d.ts +4 -2
  92. package/_types/clients/createSmartAccountClient.d.ts.map +1 -1
  93. package/_types/clients/decorators/pimlico.d.ts.map +1 -1
  94. package/_types/clients/decorators/smartAccount.d.ts +16 -471
  95. package/_types/clients/decorators/smartAccount.d.ts.map +1 -1
  96. package/_types/experimental/pimlico/index.d.ts +3 -0
  97. package/_types/experimental/pimlico/index.d.ts.map +1 -0
  98. package/_types/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.d.ts +4 -0
  99. package/_types/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.d.ts.map +1 -0
  100. package/_types/utils/getPackedUserOperation.d.ts.map +1 -1
  101. package/_types/utils/getRequiredPrefund.d.ts.map +1 -1
  102. package/_types/utils/isSmartAccountDeployed.d.ts.map +1 -1
  103. package/clients/createSmartAccountClient.ts +53 -21
  104. package/experimental/pimlico/index.ts +2 -0
  105. package/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.test.ts +195 -0
  106. package/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts +270 -0
  107. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"getRequiredPrefund.d.ts","sourceRoot":"","sources":["../../utils/getRequiredPrefund.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAE7D,MAAM,MAAM,4BAA4B,CACpC,iBAAiB,SAAS,KAAK,GAAG,KAAK,IACvC;IACA,aAAa,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAA;IAC/C,iBAAiB,EAAE,iBAAiB,CAAA;CACvC,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,sIAGsB,MA2BpD,CAAA"}
1
+ {"version":3,"file":"getRequiredPrefund.d.ts","sourceRoot":"","sources":["../../utils/getRequiredPrefund.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAE7D,MAAM,MAAM,4BAA4B,CACpC,iBAAiB,SAAS,KAAK,GAAG,KAAK,IACvC;IACA,aAAa,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAA;IAC/C,iBAAiB,EAAE,iBAAiB,CAAA;CACvC,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,GAAI,iBAAiB,SAAS,KAAK,GAAG,KAAK,wCAGvE,4BAA4B,CAAC,iBAAiB,CAAC,KAAG,MA2BpD,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"isSmartAccountDeployed.d.ts","sourceRoot":"","sources":["../../utils/isSmartAccountDeployed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAG3C,eAAO,MAAM,sBAAsB,WACvB,MAAM,WACL,OAAO,KACjB,QAAQ,OAAO,CAMjB,CAAA"}
1
+ {"version":3,"file":"isSmartAccountDeployed.d.ts","sourceRoot":"","sources":["../../utils/isSmartAccountDeployed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAG3C,eAAO,MAAM,sBAAsB,WACvB,MAAM,WACL,OAAO,KACjB,OAAO,CAAC,OAAO,CAMjB,CAAA"}
@@ -1,20 +1,23 @@
1
- import type {
2
- BundlerRpcSchema,
3
- Chain,
4
- Client,
5
- ClientConfig,
6
- EstimateFeesPerGasReturnType,
7
- Prettify,
8
- RpcSchema,
9
- Transport
1
+ import {
2
+ type BundlerRpcSchema,
3
+ type Chain,
4
+ type Client,
5
+ type ClientConfig,
6
+ type EstimateFeesPerGasReturnType,
7
+ type Prettify,
8
+ type RpcSchema,
9
+ type Transport,
10
+ createClient
10
11
  } from "viem"
11
12
  import {
12
13
  type BundlerActions,
13
14
  type BundlerClientConfig,
14
15
  type PaymasterActions,
16
+ type PrepareUserOperationParameters,
15
17
  type SmartAccount,
16
18
  type UserOperationRequest,
17
- createBundlerClient
19
+ bundlerActions,
20
+ type prepareUserOperation as viemPrepareUserOperation
18
21
  } from "viem/account-abstraction"
19
22
  import {
20
23
  type SmartAccountActions,
@@ -101,6 +104,8 @@ export type SmartAccountClientConfig<
101
104
  userOperation: UserOperationRequest
102
105
  }) => Promise<EstimateFeesPerGasReturnType<"eip1559">>)
103
106
  | undefined
107
+ /** Prepare User Operation configuration. */
108
+ prepareUserOperation?: typeof viemPrepareUserOperation | undefined
104
109
  }
105
110
  | undefined
106
111
  }
@@ -134,16 +139,43 @@ export function createSmartAccountClient(
134
139
  userOperation
135
140
  } = parameters
136
141
 
137
- const client = createBundlerClient({
138
- ...parameters,
139
- chain: parameters.chain ?? client_?.chain,
140
- key,
141
- name,
142
- transport: bundlerTransport,
143
- paymaster,
144
- paymasterContext,
145
- userOperation
146
- })
142
+ const client = Object.assign(
143
+ createClient({
144
+ ...parameters,
145
+ chain: parameters.chain ?? client_?.chain,
146
+ transport: bundlerTransport,
147
+ key,
148
+ name,
149
+ type: "bundlerClient" // TODO: is this okay?
150
+ }),
151
+ { client: client_, paymaster, paymasterContext, userOperation }
152
+ )
153
+
154
+ if (parameters.userOperation?.prepareUserOperation) {
155
+ const customPrepareUserOp =
156
+ parameters.userOperation.prepareUserOperation
157
+
158
+ return client
159
+ .extend(bundlerActions)
160
+ .extend((client) => ({
161
+ prepareUserOperation: (
162
+ args: PrepareUserOperationParameters
163
+ ) => {
164
+ return customPrepareUserOp(client, args)
165
+ }
166
+ }))
167
+ .extend(bundlerActions)
168
+ .extend((client) => ({
169
+ prepareUserOperation: (
170
+ args: PrepareUserOperationParameters
171
+ ) => {
172
+ return customPrepareUserOp(client, args)
173
+ }
174
+ }))
175
+ .extend(smartAccountActions()) as SmartAccountClient
176
+ }
147
177
 
148
- return client.extend(smartAccountActions()) as unknown as SmartAccountClient
178
+ return client
179
+ .extend(bundlerActions)
180
+ .extend(smartAccountActions()) as SmartAccountClient
149
181
  }
@@ -0,0 +1,2 @@
1
+ import { prepareUserOperationForErc20Paymaster } from "./utils/prepareUserOperationForErc20Paymaster"
2
+ export { prepareUserOperationForErc20Paymaster }
@@ -0,0 +1,195 @@
1
+ import { http, parseEther, zeroAddress } from "viem"
2
+ import {
3
+ entryPoint06Address,
4
+ entryPoint07Address
5
+ } from "viem/account-abstraction"
6
+ import { foundry } from "viem/chains"
7
+ import { describe, expect } from "vitest"
8
+ import {
9
+ ERC20_ADDRESS,
10
+ sudoMintTokens,
11
+ tokenBalanceOf
12
+ } from "../../../../permissionless-test/src/erc20-utils.ts"
13
+ import { testWithRpc } from "../../../../permissionless-test/src/testWithRpc.ts"
14
+ import {
15
+ getCoreSmartAccounts,
16
+ getPublicClient
17
+ } from "../../../../permissionless-test/src/utils"
18
+ import { createSmartAccountClient } from "../../../clients/createSmartAccountClient.ts"
19
+ import { createPimlicoClient } from "../../../clients/pimlico.ts"
20
+ import { prepareUserOperationForErc20Paymaster } from "./prepareUserOperationForErc20Paymaster.ts"
21
+
22
+ describe.each(getCoreSmartAccounts())(
23
+ "prepareUserOperationForErc20Paymaster $name",
24
+ ({
25
+ getSmartAccountClient,
26
+ supportsEntryPointV06,
27
+ supportsEntryPointV07
28
+ }) => {
29
+ testWithRpc.skipIf(!supportsEntryPointV06)(
30
+ "prepareUserOperationForErc20Paymaster_v06",
31
+ async ({ rpc }) => {
32
+ const { anvilRpc } = rpc
33
+
34
+ const account = (
35
+ await getSmartAccountClient({
36
+ entryPoint: {
37
+ version: "0.6"
38
+ },
39
+ ...rpc
40
+ })
41
+ ).account
42
+
43
+ const pimlicoClient = createPimlicoClient({
44
+ transport: http(rpc.paymasterRpc),
45
+ entryPoint: {
46
+ address: entryPoint06Address,
47
+ version: "0.6"
48
+ }
49
+ })
50
+
51
+ const publicClient = getPublicClient(anvilRpc)
52
+
53
+ const smartAccountClient = createSmartAccountClient({
54
+ // @ts-ignore
55
+ client: getPublicClient(anvilRpc),
56
+ account,
57
+ paymaster: pimlicoClient,
58
+ chain: foundry,
59
+ userOperation: {
60
+ prepareUserOperation:
61
+ prepareUserOperationForErc20Paymaster(pimlicoClient)
62
+ },
63
+ bundlerTransport: http(rpc.altoRpc)
64
+ })
65
+
66
+ const INITIAL_TOKEN_BALANCE = parseEther("100")
67
+ const INTIAL_ETH_BALANCE = await publicClient.getBalance({
68
+ address: smartAccountClient.account.address
69
+ })
70
+
71
+ sudoMintTokens({
72
+ amount: INITIAL_TOKEN_BALANCE,
73
+ to: smartAccountClient.account.address,
74
+ anvilRpc
75
+ })
76
+
77
+ const opHash = await smartAccountClient.sendUserOperation({
78
+ calls: [
79
+ {
80
+ to: zeroAddress,
81
+ data: "0x",
82
+ value: 0n
83
+ }
84
+ ],
85
+ paymasterContext: {
86
+ token: ERC20_ADDRESS
87
+ }
88
+ })
89
+
90
+ const receipt =
91
+ await smartAccountClient.waitForUserOperationReceipt({
92
+ hash: opHash
93
+ })
94
+
95
+ expect(receipt).toBeTruthy()
96
+ expect(receipt).toBeTruthy()
97
+ expect(receipt.success).toBeTruthy()
98
+
99
+ const FINAL_TOKEN_BALANCE = await tokenBalanceOf(
100
+ smartAccountClient.account.address,
101
+ rpc.anvilRpc
102
+ )
103
+ const FINAL_ETH_BALANCE = await publicClient.getBalance({
104
+ address: smartAccountClient.account.address
105
+ })
106
+
107
+ expect(FINAL_TOKEN_BALANCE).toBeLessThan(INITIAL_TOKEN_BALANCE) // Token balance should be deducted
108
+ expect(FINAL_ETH_BALANCE).toEqual(INTIAL_ETH_BALANCE) // There should be no ETH balance change
109
+ }
110
+ )
111
+
112
+ testWithRpc.skipIf(!supportsEntryPointV07)(
113
+ "prepareUserOperationForErc20Paymaster_v07",
114
+ async ({ rpc }) => {
115
+ const { anvilRpc } = rpc
116
+
117
+ const account = (
118
+ await getSmartAccountClient({
119
+ entryPoint: {
120
+ version: "0.7"
121
+ },
122
+ ...rpc
123
+ })
124
+ ).account
125
+
126
+ const publicClient = getPublicClient(anvilRpc)
127
+
128
+ const pimlicoClient = createPimlicoClient({
129
+ transport: http(rpc.paymasterRpc),
130
+ entryPoint: {
131
+ address: entryPoint07Address,
132
+ version: "0.7"
133
+ }
134
+ })
135
+
136
+ const smartAccountClient = createSmartAccountClient({
137
+ // @ts-ignore
138
+ client: getPublicClient(anvilRpc),
139
+ account,
140
+ paymaster: pimlicoClient,
141
+ chain: foundry,
142
+ userOperation: {
143
+ prepareUserOperation:
144
+ prepareUserOperationForErc20Paymaster(pimlicoClient)
145
+ },
146
+ bundlerTransport: http(rpc.altoRpc)
147
+ })
148
+
149
+ const INITIAL_TOKEN_BALANCE = parseEther("100")
150
+ const INTIAL_ETH_BALANCE = await publicClient.getBalance({
151
+ address: smartAccountClient.account.address
152
+ })
153
+
154
+ sudoMintTokens({
155
+ amount: INITIAL_TOKEN_BALANCE,
156
+ to: smartAccountClient.account.address,
157
+ anvilRpc
158
+ })
159
+
160
+ const opHash = await smartAccountClient.sendUserOperation({
161
+ calls: [
162
+ {
163
+ to: zeroAddress,
164
+ data: "0x",
165
+ value: 0n
166
+ }
167
+ ],
168
+ paymasterContext: {
169
+ token: ERC20_ADDRESS
170
+ }
171
+ })
172
+
173
+ const receipt =
174
+ await smartAccountClient.waitForUserOperationReceipt({
175
+ hash: opHash
176
+ })
177
+
178
+ expect(receipt).toBeTruthy()
179
+ expect(receipt).toBeTruthy()
180
+ expect(receipt.success).toBeTruthy()
181
+
182
+ const FINAL_TOKEN_BALANCE = await tokenBalanceOf(
183
+ smartAccountClient.account.address,
184
+ rpc.anvilRpc
185
+ )
186
+ const FINAL_ETH_BALANCE = await publicClient.getBalance({
187
+ address: smartAccountClient.account.address
188
+ })
189
+
190
+ expect(FINAL_TOKEN_BALANCE).toBeLessThan(INITIAL_TOKEN_BALANCE) // Token balance should be deducted
191
+ expect(FINAL_ETH_BALANCE).toEqual(INTIAL_ETH_BALANCE) // There should be no ETH balance change
192
+ }
193
+ )
194
+ }
195
+ )
@@ -0,0 +1,270 @@
1
+ import {
2
+ type Address,
3
+ type Chain,
4
+ type Client,
5
+ type ContractFunctionParameters,
6
+ type Transport,
7
+ encodeFunctionData,
8
+ erc20Abi,
9
+ getAddress,
10
+ maxUint256
11
+ } from "viem"
12
+ import {
13
+ type BundlerClient,
14
+ type PrepareUserOperationParameters,
15
+ type PrepareUserOperationRequest,
16
+ type PrepareUserOperationReturnType,
17
+ type SmartAccount,
18
+ type UserOperation,
19
+ type UserOperationCall,
20
+ getPaymasterData as getPaymasterData_,
21
+ prepareUserOperation
22
+ } from "viem/account-abstraction"
23
+ import { getChainId as getChainId_ } from "viem/actions"
24
+ import { readContract } from "viem/actions"
25
+ import { getAction, parseAccount } from "viem/utils"
26
+ import { getTokenQuotes } from "../../../actions/pimlico"
27
+
28
+ export const prepareUserOperationForErc20Paymaster =
29
+ (pimlicoClient: Client) =>
30
+ async <
31
+ account extends SmartAccount | undefined,
32
+ const calls extends readonly unknown[],
33
+ const request extends PrepareUserOperationRequest<
34
+ account,
35
+ accountOverride,
36
+ calls
37
+ >,
38
+ accountOverride extends SmartAccount | undefined = undefined
39
+ >(
40
+ client: Client<Transport, Chain | undefined, account>,
41
+ parameters_: PrepareUserOperationParameters<
42
+ account,
43
+ accountOverride,
44
+ calls,
45
+ request
46
+ >
47
+ ): Promise<
48
+ PrepareUserOperationReturnType<account, accountOverride, calls, request>
49
+ > => {
50
+ const parameters = parameters_ as PrepareUserOperationParameters
51
+ const account_ = client.account
52
+
53
+ if (!account_) throw new Error("Account not found")
54
+ const account = parseAccount(account_)
55
+
56
+ const bundlerClient = client as unknown as BundlerClient
57
+
58
+ const paymasterContext = parameters.paymasterContext
59
+ ? parameters.paymasterContext
60
+ : bundlerClient?.paymasterContext
61
+
62
+ if (
63
+ typeof paymasterContext === "object" &&
64
+ paymasterContext !== null &&
65
+ "token" in paymasterContext &&
66
+ typeof paymasterContext.token === "string"
67
+ ) {
68
+ ////////////////////////////////////////////////////////////////////////////////
69
+ // Inject custom approval before calling prepareUserOperation
70
+ ////////////////////////////////////////////////////////////////////////////////
71
+
72
+ const token = getAddress(paymasterContext.token)
73
+
74
+ let chainId: number | undefined
75
+ async function getChainId(): Promise<number> {
76
+ if (chainId) return chainId
77
+ if (client.chain) return client.chain.id
78
+ const chainId_ = await getAction(
79
+ client,
80
+ getChainId_,
81
+ "getChainId"
82
+ )({})
83
+ chainId = chainId_
84
+ return chainId
85
+ }
86
+
87
+ const quotes = await getAction(
88
+ pimlicoClient,
89
+ getTokenQuotes,
90
+ "getTokenQuotes"
91
+ )({
92
+ tokens: [token],
93
+ chain:
94
+ pimlicoClient.chain ?? client.chain ?? account.client.chain,
95
+ entryPointAddress: account.entryPoint.address
96
+ })
97
+
98
+ const {
99
+ postOpGas,
100
+ exchangeRate,
101
+ paymaster: paymasterERC20Address
102
+ } = quotes[0]
103
+
104
+ const callsWithDummyApproval = [
105
+ {
106
+ abi: erc20Abi,
107
+ functionName: "approve",
108
+ args: [paymasterERC20Address, maxUint256], // dummy approval to ensure simulation passes
109
+ to: paymasterContext.token
110
+ },
111
+ ...(parameters.calls ? parameters.calls : [])
112
+ ]
113
+
114
+ if (parameters.callData) {
115
+ throw new Error(
116
+ "parameter callData is not supported with prepareUserOperationForErc20Paymaster"
117
+ )
118
+ }
119
+
120
+ ////////////////////////////////////////////////////////////////////////////////
121
+ // Call prepareUserOperation
122
+ ////////////////////////////////////////////////////////////////////////////////
123
+
124
+ const userOperation = await getAction(
125
+ client,
126
+ prepareUserOperation,
127
+ "prepareUserOperation"
128
+ )({
129
+ ...parameters,
130
+ calls: callsWithDummyApproval
131
+ } as unknown as PrepareUserOperationParameters)
132
+
133
+ ////////////////////////////////////////////////////////////////////////////////
134
+ // Call pimlico_getTokenQuotes and calculate the approval amount needed for op
135
+ ////////////////////////////////////////////////////////////////////////////////
136
+
137
+ const maxFeePerGas = userOperation.maxFeePerGas
138
+
139
+ const userOperationMaxGas =
140
+ userOperation.preVerificationGas +
141
+ userOperation.callGasLimit +
142
+ userOperation.verificationGasLimit +
143
+ (userOperation.paymasterPostOpGasLimit || 0n) +
144
+ (userOperation.paymasterVerificationGasLimit || 0n)
145
+
146
+ const userOperationMaxCost = userOperationMaxGas * maxFeePerGas
147
+
148
+ // using formula here https://github.com/pimlicolabs/singleton-paymaster/blob/main/src/base/BaseSingletonPaymaster.sol#L334-L341
149
+ const maxCostInToken =
150
+ ((userOperationMaxCost + postOpGas * maxFeePerGas) *
151
+ exchangeRate) /
152
+ BigInt(1e18)
153
+
154
+ ////////////////////////////////////////////////////////////////////////////////
155
+ // Check if we need to approve the token
156
+ // If the user has existing approval that is sufficient, skip approval injection
157
+ ////////////////////////////////////////////////////////////////////////////////
158
+
159
+ const publicClient = account.client
160
+
161
+ const allowance = await getAction(
162
+ publicClient,
163
+ readContract,
164
+ "readContract"
165
+ )({
166
+ abi: erc20Abi,
167
+ functionName: "allowance",
168
+ args: [account.address, paymasterERC20Address],
169
+ address: token
170
+ })
171
+
172
+ const hasSufficientApproval = allowance >= maxCostInToken
173
+
174
+ const finalCalls = hasSufficientApproval
175
+ ? parameters.calls
176
+ : [
177
+ {
178
+ abi: erc20Abi,
179
+ functionName: "approve",
180
+ args: [paymasterERC20Address, maxCostInToken],
181
+ to: paymasterContext.token
182
+ },
183
+ ...parameters.calls
184
+ ]
185
+
186
+ userOperation.callData = await account.encodeCalls(
187
+ finalCalls.map((call_) => {
188
+ const call = call_ as
189
+ | UserOperationCall
190
+ | (ContractFunctionParameters & {
191
+ to: Address
192
+ value: bigint
193
+ })
194
+ if ("abi" in call)
195
+ return {
196
+ data: encodeFunctionData(call),
197
+ to: call.to,
198
+ value: call.value
199
+ } as UserOperationCall
200
+ return call as UserOperationCall
201
+ })
202
+ )
203
+ parameters.calls = finalCalls
204
+
205
+ ////////////////////////////////////////////////////////////////////////////////
206
+ // Declare Paymaster properties. (taken from viem)
207
+ ////////////////////////////////////////////////////////////////////////////////
208
+
209
+ const paymaster = parameters.paymaster ?? bundlerClient?.paymaster
210
+ const { getPaymasterData } = (() => {
211
+ // If `paymaster: true`, we will assume the Bundler Client supports Paymaster Actions.
212
+ if (paymaster === true)
213
+ return {
214
+ getPaymasterData: (parameters: any) =>
215
+ getAction(
216
+ bundlerClient,
217
+ getPaymasterData_,
218
+ "getPaymasterData"
219
+ )(parameters)
220
+ }
221
+
222
+ // If Actions are passed to `paymaster` (via Paymaster Client or directly), we will use them.
223
+ if (
224
+ typeof paymaster === "object" &&
225
+ paymaster.getPaymasterData
226
+ ) {
227
+ const { getPaymasterData } = paymaster
228
+ return {
229
+ getPaymasterData
230
+ }
231
+ }
232
+
233
+ throw new Error(
234
+ "Expected paymaster: cannot sponsor ERC-20 without paymaster"
235
+ )
236
+ })()
237
+
238
+ ////////////////////////////////////////////////////////////////////////////////
239
+ // Re-calculate Paymaster data fields.
240
+ ////////////////////////////////////////////////////////////////////////////////
241
+
242
+ const paymasterData = await getPaymasterData({
243
+ chainId: await getChainId(),
244
+ entryPointAddress: account.entryPoint.address,
245
+ context: paymasterContext,
246
+ ...(userOperation as UserOperation)
247
+ })
248
+
249
+ return {
250
+ ...userOperation,
251
+ ...paymasterData
252
+ } as unknown as PrepareUserOperationReturnType<
253
+ account,
254
+ accountOverride,
255
+ calls,
256
+ request
257
+ >
258
+ }
259
+
260
+ return (await getAction(
261
+ client,
262
+ prepareUserOperation,
263
+ "prepareUserOperation"
264
+ )(parameters)) as unknown as PrepareUserOperationReturnType<
265
+ account,
266
+ accountOverride,
267
+ calls,
268
+ request
269
+ >
270
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "permissionless",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "author": "Pimlico",
5
5
  "homepage": "https://docs.pimlico.io/permissionless",
6
6
  "repository": "github:pimlicolabs/permissionless.js",
@@ -64,13 +64,13 @@
64
64
  "import": "./_esm/errors/index.js",
65
65
  "default": "./_cjs/errors/index.js"
66
66
  },
67
- "./experimental": {
68
- "types": "./_types/experimental/index.d.ts",
69
- "import": "./_esm/experimental/index.js",
70
- "default": "./_cjs/experimental/index.js"
67
+ "./experimental/pimlico": {
68
+ "types": "./_types/experimental/pimlico/index.d.ts",
69
+ "import": "./_esm/experimental/pimlico/index.js",
70
+ "default": "./_cjs/experimental/pimlico/index.js"
71
71
  }
72
72
  },
73
73
  "peerDependencies": {
74
- "viem": "^2.20.0"
74
+ "viem": "^2.21.2"
75
75
  }
76
76
  }