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.
- package/CHANGELOG.md +12 -0
- package/_cjs/accounts/biconomy/toBiconomySmartAccount.js +1 -2
- package/_cjs/accounts/biconomy/toBiconomySmartAccount.js.map +1 -1
- package/_cjs/accounts/kernel/toEcdsaKernelSmartAccount.js +2 -2
- package/_cjs/accounts/kernel/toEcdsaKernelSmartAccount.js.map +1 -1
- package/_cjs/accounts/kernel/utils/signMessage.js +1 -2
- package/_cjs/accounts/kernel/utils/signMessage.js.map +1 -1
- package/_cjs/accounts/kernel/utils/signTypedData.js +1 -2
- package/_cjs/accounts/kernel/utils/signTypedData.js.map +1 -1
- package/_cjs/accounts/light/toLightSmartAccount.js +1 -2
- package/_cjs/accounts/light/toLightSmartAccount.js.map +1 -1
- package/_cjs/accounts/safe/toSafeSmartAccount.js +1 -2
- package/_cjs/accounts/safe/toSafeSmartAccount.js.map +1 -1
- package/_cjs/accounts/simple/toSimpleSmartAccount.js +1 -2
- package/_cjs/accounts/simple/toSimpleSmartAccount.js.map +1 -1
- package/_cjs/accounts/trust/toTrustSmartAccount.js +2 -2
- package/_cjs/accounts/trust/toTrustSmartAccount.js.map +1 -1
- package/_cjs/actions/erc7579/accountId.js +1 -2
- package/_cjs/actions/erc7579/accountId.js.map +1 -1
- package/_cjs/actions/erc7579/installModule.js +1 -2
- package/_cjs/actions/erc7579/installModule.js.map +1 -1
- package/_cjs/actions/erc7579/installModules.js +1 -2
- package/_cjs/actions/erc7579/installModules.js.map +1 -1
- package/_cjs/actions/erc7579/isModuleInstalled.js +1 -2
- package/_cjs/actions/erc7579/isModuleInstalled.js.map +1 -1
- package/_cjs/actions/erc7579/supportsExecutionMode.js +2 -3
- package/_cjs/actions/erc7579/supportsExecutionMode.js.map +1 -1
- package/_cjs/actions/erc7579/supportsModule.js +2 -3
- package/_cjs/actions/erc7579/supportsModule.js.map +1 -1
- package/_cjs/actions/erc7579/uninstallModule.js +1 -2
- package/_cjs/actions/erc7579/uninstallModule.js.map +1 -1
- package/_cjs/actions/erc7579/uninstallModules.js +1 -2
- package/_cjs/actions/erc7579/uninstallModules.js.map +1 -1
- package/_cjs/actions/erc7579.js +2 -2
- package/_cjs/actions/erc7579.js.map +1 -1
- package/_cjs/actions/smartAccount/sendTransaction.js +1 -2
- package/_cjs/actions/smartAccount/sendTransaction.js.map +1 -1
- package/_cjs/actions/smartAccount/signMessage.js +1 -2
- package/_cjs/actions/smartAccount/signMessage.js.map +1 -1
- package/_cjs/actions/smartAccount/signTypedData.js +1 -2
- package/_cjs/actions/smartAccount/signTypedData.js.map +1 -1
- package/_cjs/actions/smartAccount/writeContract.js +1 -2
- package/_cjs/actions/smartAccount/writeContract.js.map +1 -1
- package/_cjs/clients/createSmartAccountClient.js +26 -9
- package/_cjs/clients/createSmartAccountClient.js.map +1 -1
- package/_cjs/clients/decorators/smartAccount.js +1 -2
- package/_cjs/clients/decorators/smartAccount.js.map +1 -1
- package/_cjs/clients/pimlico.js +1 -2
- package/_cjs/clients/pimlico.js.map +1 -1
- package/_cjs/experimental/pimlico/index.js +6 -0
- package/_cjs/experimental/pimlico/index.js.map +1 -0
- package/_cjs/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.js +126 -0
- package/_cjs/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.js.map +1 -0
- package/_cjs/utils/decodeNonce.js +1 -2
- package/_cjs/utils/decodeNonce.js.map +1 -1
- package/_cjs/utils/deepHexlify.js +2 -2
- package/_cjs/utils/deepHexlify.js.map +1 -1
- package/_cjs/utils/encode7579Calls.js +1 -2
- package/_cjs/utils/encode7579Calls.js.map +1 -1
- package/_cjs/utils/encodeInstallModule.js +1 -2
- package/_cjs/utils/encodeInstallModule.js.map +1 -1
- package/_cjs/utils/encodeNonce.js +1 -2
- package/_cjs/utils/encodeNonce.js.map +1 -1
- package/_cjs/utils/encodeUninstallModule.js +1 -2
- package/_cjs/utils/encodeUninstallModule.js.map +1 -1
- package/_cjs/utils/getAddressFromInitCodeOrPaymasterAndData.js +1 -2
- package/_cjs/utils/getAddressFromInitCodeOrPaymasterAndData.js.map +1 -1
- package/_cjs/utils/getPackedUserOperation.js +9 -9
- package/_cjs/utils/getPackedUserOperation.js.map +1 -1
- package/_cjs/utils/toOwner.js +1 -2
- package/_cjs/utils/toOwner.js.map +1 -1
- package/_esm/clients/createSmartAccountClient.js +26 -8
- package/_esm/clients/createSmartAccountClient.js.map +1 -1
- package/_esm/experimental/pimlico/index.js +3 -0
- package/_esm/experimental/pimlico/index.js.map +1 -0
- package/_esm/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.js +144 -0
- package/_esm/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.js.map +1 -0
- package/_types/accounts/kernel/utils/encodeCallData.d.ts.map +1 -1
- package/_types/accounts/kernel/utils/getNonceKey.d.ts.map +1 -1
- package/_types/accounts/kernel/utils/isKernelV2.d.ts.map +1 -1
- package/_types/actions/erc7579.d.ts +1 -249
- package/_types/actions/erc7579.d.ts.map +1 -1
- package/_types/actions/pimlico/getTokenQuotes.d.ts.map +1 -1
- package/_types/actions/pimlico/getUserOperationGasPrice.d.ts.map +1 -1
- package/_types/actions/pimlico/getUserOperationStatus.d.ts.map +1 -1
- package/_types/actions/pimlico/sendCompressedUserOperation.d.ts.map +1 -1
- package/_types/actions/pimlico/sponsorUserOperation.d.ts.map +1 -1
- package/_types/actions/pimlico/validateSponsorshipPolicies.d.ts.map +1 -1
- package/_types/actions/public/getAccountNonce.d.ts.map +1 -1
- package/_types/actions/public/getSenderAddress.d.ts.map +1 -1
- package/_types/clients/createSmartAccountClient.d.ts +4 -2
- package/_types/clients/createSmartAccountClient.d.ts.map +1 -1
- package/_types/clients/decorators/pimlico.d.ts.map +1 -1
- package/_types/clients/decorators/smartAccount.d.ts +16 -471
- package/_types/clients/decorators/smartAccount.d.ts.map +1 -1
- package/_types/experimental/pimlico/index.d.ts +3 -0
- package/_types/experimental/pimlico/index.d.ts.map +1 -0
- package/_types/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.d.ts +4 -0
- package/_types/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.d.ts.map +1 -0
- package/_types/utils/getPackedUserOperation.d.ts.map +1 -1
- package/_types/utils/getRequiredPrefund.d.ts.map +1 -1
- package/_types/utils/isSmartAccountDeployed.d.ts.map +1 -1
- package/clients/createSmartAccountClient.ts +53 -21
- package/experimental/pimlico/index.ts +2 -0
- package/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.test.ts +195 -0
- package/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts +270 -0
- 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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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 =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
178
|
+
return client
|
|
179
|
+
.extend(bundlerActions)
|
|
180
|
+
.extend(smartAccountActions()) as SmartAccountClient
|
|
149
181
|
}
|
|
@@ -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.
|
|
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.
|
|
74
|
+
"viem": "^2.21.2"
|
|
75
75
|
}
|
|
76
76
|
}
|