permissionless 0.2.23 → 0.2.24

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.
@@ -1,7 +1,35 @@
1
- import { type Account, type Address, type Assign, type Chain, type Client, type LocalAccount, type OneOf, type Transport, type WalletClient } from "viem";
2
- import { type SmartAccount, type SmartAccountImplementation, entryPoint06Abi, entryPoint07Abi } from "viem/account-abstraction";
3
- import { type EthereumProvider } from "../../utils/toOwner.js";
1
+ import { type Account, type Address, type Assign, type Chain, type Client, type Transport, type WalletClient } from "viem";
2
+ import { type SmartAccount, type SmartAccountImplementation, type UserOperation, entryPoint06Abi, entryPoint07Abi } from "viem/account-abstraction";
4
3
  export type SafeVersion = "1.4.1";
4
+ export declare const EIP712_SAFE_OPERATION_TYPE_V06: {
5
+ SafeOp: {
6
+ type: string;
7
+ name: string;
8
+ }[];
9
+ };
10
+ export declare const EIP712_SAFE_OPERATION_TYPE_V07: {
11
+ SafeOp: {
12
+ type: string;
13
+ name: string;
14
+ }[];
15
+ };
16
+ export declare function getPaymasterAndData(unpackedUserOperation: UserOperation): `0x${string}`;
17
+ export declare const getDefaultAddresses: (safeVersion: SafeVersion, entryPointVersion: "0.6" | "0.7", { addModuleLibAddress: _addModuleLibAddress, safeModuleSetupAddress: _safeModuleSetupAddress, safe4337ModuleAddress: _safe4337ModuleAddress, safeProxyFactoryAddress: _safeProxyFactoryAddress, safeSingletonAddress: _safeSingletonAddress, multiSendAddress: _multiSendAddress, multiSendCallOnlyAddress: _multiSendCallOnlyAddress }: {
18
+ addModuleLibAddress?: Address;
19
+ safeModuleSetupAddress?: Address;
20
+ safe4337ModuleAddress?: Address;
21
+ safeProxyFactoryAddress?: Address;
22
+ safeSingletonAddress?: Address;
23
+ multiSendAddress?: Address;
24
+ multiSendCallOnlyAddress?: Address;
25
+ }) => {
26
+ safeModuleSetupAddress: `0x${string}`;
27
+ safe4337ModuleAddress: `0x${string}`;
28
+ safeProxyFactoryAddress: `0x${string}`;
29
+ safeSingletonAddress: `0x${string}`;
30
+ multiSendAddress: `0x${string}`;
31
+ multiSendCallOnlyAddress: `0x${string}`;
32
+ };
5
33
  type GetErc7579Params<TErc7579 extends Address | undefined> = TErc7579 extends undefined ? {
6
34
  safeModuleSetupAddress?: Address;
7
35
  multiSendAddress?: Address;
@@ -34,9 +62,8 @@ type GetErc7579Params<TErc7579 extends Address | undefined> = TErc7579 extends u
34
62
  };
35
63
  export type ToSafeSmartAccountParameters<entryPointVersion extends "0.6" | "0.7", TErc7579 extends Address | undefined> = {
36
64
  client: Client;
37
- owners: [
38
- OneOf<EthereumProvider | WalletClient<Transport, Chain | undefined, Account> | LocalAccount>
39
- ];
65
+ owners: (Account | WalletClient<Transport, Chain | undefined, Account>)[];
66
+ threshold?: bigint;
40
67
  version: SafeVersion;
41
68
  entryPoint?: {
42
69
  address: Address;
@@ -1 +1 @@
1
- {"version":3,"file":"toSafeSmartAccount.d.ts","sourceRoot":"","sources":["../../../accounts/safe/toSafeSmartAccount.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"toSafeSmartAccount.d.ts","sourceRoot":"","sources":["../../../accounts/safe/toSafeSmartAccount.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import {
2
+ type SafeSmartAccountImplementation,
3
+ type SafeVersion,
4
+ type ToSafeSmartAccountParameters,
5
+ type ToSafeSmartAccountReturnType,
6
+ toSafeSmartAccount
7
+ } from "./toSafeSmartAccount.js"
8
+
9
+ import { signUserOperation } from "./signUserOperation.js"
10
+
11
+ const SafeSmartAccount = {
12
+ toSafeSmartAccount,
13
+ signUserOperation
14
+ }
15
+
16
+ export {
17
+ type SafeSmartAccountImplementation,
18
+ type SafeVersion,
19
+ type ToSafeSmartAccountParameters,
20
+ type ToSafeSmartAccountReturnType,
21
+ SafeSmartAccount
22
+ }
@@ -0,0 +1,277 @@
1
+ import {
2
+ entryPoint06Address,
3
+ entryPoint07Address
4
+ } from "viem/account-abstraction"
5
+ import {
6
+ generatePrivateKey,
7
+ privateKeyToAccount,
8
+ toAccount
9
+ } from "viem/accounts"
10
+ import { foundry } from "viem/chains"
11
+ import { describe, expect } from "vitest"
12
+ import { testWithRpc } from "../../../permissionless-test/src/testWithRpc"
13
+ import {
14
+ getBundlerClient,
15
+ getSafeClient
16
+ } from "../../../permissionless-test/src/utils"
17
+ import { signUserOperation } from "./signUserOperation"
18
+
19
+ describe("signUserOperation", () => {
20
+ testWithRpc("signUserOperation_V06", async ({ rpc }) => {
21
+ const owners = [
22
+ privateKeyToAccount(generatePrivateKey()),
23
+ privateKeyToAccount(generatePrivateKey()),
24
+ privateKeyToAccount(generatePrivateKey())
25
+ ]
26
+
27
+ const safeAccountClient = getBundlerClient({
28
+ account: await getSafeClient({
29
+ ...rpc,
30
+ entryPoint: {
31
+ version: "0.6"
32
+ },
33
+ owners: owners.map((owner) => toAccount(owner.address))
34
+ }),
35
+ entryPoint: {
36
+ version: "0.6"
37
+ },
38
+ ...rpc
39
+ })
40
+
41
+ const unSignedUserOperation =
42
+ await safeAccountClient.prepareUserOperation({
43
+ calls: [
44
+ {
45
+ to: safeAccountClient.account.address,
46
+ data: "0x"
47
+ }
48
+ ]
49
+ })
50
+
51
+ let partialSignatures = await signUserOperation({
52
+ version: "1.4.1",
53
+ entryPoint: {
54
+ address: entryPoint06Address,
55
+ version: "0.6"
56
+ },
57
+ chainId: foundry.id,
58
+ owners: owners.map((owner) => toAccount(owner.address)),
59
+ account: owners[0],
60
+ ...unSignedUserOperation
61
+ })
62
+
63
+ partialSignatures = await signUserOperation({
64
+ version: "1.4.1",
65
+ entryPoint: {
66
+ address: entryPoint06Address,
67
+ version: "0.6"
68
+ },
69
+ chainId: foundry.id,
70
+ owners: owners.map((owner) => toAccount(owner.address)),
71
+ account: owners[1],
72
+ signatures: partialSignatures,
73
+ ...unSignedUserOperation
74
+ })
75
+
76
+ const finalSignature = await signUserOperation({
77
+ version: "1.4.1",
78
+ entryPoint: {
79
+ address: entryPoint06Address,
80
+ version: "0.6"
81
+ },
82
+ chainId: foundry.id,
83
+ owners: owners.map((owner) => toAccount(owner.address)),
84
+ account: owners[2],
85
+ signatures: partialSignatures,
86
+ ...unSignedUserOperation
87
+ })
88
+
89
+ const userOpHash = safeAccountClient.sendUserOperation({
90
+ ...unSignedUserOperation,
91
+ signature: finalSignature
92
+ })
93
+
94
+ expect(userOpHash).toBeTruthy()
95
+
96
+ const receipt = await safeAccountClient.waitForUserOperationReceipt({
97
+ hash: await userOpHash
98
+ })
99
+
100
+ expect(receipt).toBeTruthy()
101
+ expect(receipt.success).toBeTruthy()
102
+ })
103
+
104
+ testWithRpc("signUserOperation_V07", async ({ rpc }) => {
105
+ const owners = [
106
+ privateKeyToAccount(generatePrivateKey()),
107
+ privateKeyToAccount(generatePrivateKey()),
108
+ privateKeyToAccount(generatePrivateKey())
109
+ ]
110
+
111
+ const safeAccountClient = getBundlerClient({
112
+ account: await getSafeClient({
113
+ ...rpc,
114
+ entryPoint: {
115
+ version: "0.7"
116
+ },
117
+ owners: owners.map((owner) => toAccount(owner.address))
118
+ }),
119
+ entryPoint: {
120
+ version: "0.7"
121
+ },
122
+ ...rpc
123
+ })
124
+
125
+ const unSignedUserOperation =
126
+ await safeAccountClient.prepareUserOperation({
127
+ calls: [
128
+ {
129
+ to: safeAccountClient.account.address,
130
+ data: "0x"
131
+ }
132
+ ]
133
+ })
134
+
135
+ let partialSignatures = await signUserOperation({
136
+ version: "1.4.1",
137
+ entryPoint: {
138
+ address: entryPoint07Address,
139
+ version: "0.7"
140
+ },
141
+ chainId: foundry.id,
142
+ owners: owners.map((owner) => toAccount(owner.address)),
143
+ account: owners[0],
144
+ ...unSignedUserOperation
145
+ })
146
+
147
+ partialSignatures = await signUserOperation({
148
+ version: "1.4.1",
149
+ entryPoint: {
150
+ address: entryPoint07Address,
151
+ version: "0.7"
152
+ },
153
+ chainId: foundry.id,
154
+ owners: owners.map((owner) => toAccount(owner.address)),
155
+ account: owners[1],
156
+ signatures: partialSignatures,
157
+ ...unSignedUserOperation
158
+ })
159
+
160
+ const finalSignature = await signUserOperation({
161
+ version: "1.4.1",
162
+ entryPoint: {
163
+ address: entryPoint07Address,
164
+ version: "0.7"
165
+ },
166
+ chainId: foundry.id,
167
+ owners: owners.map((owner) => toAccount(owner.address)),
168
+ account: owners[2],
169
+ signatures: partialSignatures,
170
+ ...unSignedUserOperation
171
+ })
172
+
173
+ const userOpHash = safeAccountClient.sendUserOperation({
174
+ ...unSignedUserOperation,
175
+ signature: finalSignature
176
+ })
177
+
178
+ expect(userOpHash).toBeTruthy()
179
+
180
+ const receipt = await safeAccountClient.waitForUserOperationReceipt({
181
+ hash: await userOpHash
182
+ })
183
+
184
+ expect(receipt).toBeTruthy()
185
+ expect(receipt.success).toBeTruthy()
186
+ })
187
+
188
+ testWithRpc("signUserOperation_V07 7579", async ({ rpc }) => {
189
+ const { anvilRpc } = rpc
190
+
191
+ const owners = [
192
+ privateKeyToAccount(generatePrivateKey()),
193
+ privateKeyToAccount(generatePrivateKey()),
194
+ privateKeyToAccount(generatePrivateKey())
195
+ ]
196
+
197
+ const safeAccountClient = getBundlerClient({
198
+ account: await getSafeClient({
199
+ ...rpc,
200
+ entryPoint: {
201
+ version: "0.7"
202
+ },
203
+ owners: owners.map((owner) => toAccount(owner.address)),
204
+ erc7579: true
205
+ }),
206
+ entryPoint: {
207
+ version: "0.7"
208
+ },
209
+ ...rpc
210
+ })
211
+
212
+ const unSignedUserOperation =
213
+ await safeAccountClient.prepareUserOperation({
214
+ calls: [
215
+ {
216
+ to: safeAccountClient.account.address,
217
+ data: "0x"
218
+ }
219
+ ]
220
+ })
221
+
222
+ let partialSignatures = await signUserOperation({
223
+ version: "1.4.1",
224
+ entryPoint: {
225
+ address: entryPoint07Address,
226
+ version: "0.7"
227
+ },
228
+ chainId: foundry.id,
229
+ owners: owners.map((owner) => toAccount(owner.address)),
230
+ account: owners[0],
231
+ safe4337ModuleAddress: "0x7579EE8307284F293B1927136486880611F20002",
232
+ ...unSignedUserOperation
233
+ })
234
+
235
+ partialSignatures = await signUserOperation({
236
+ version: "1.4.1",
237
+ entryPoint: {
238
+ address: entryPoint07Address,
239
+ version: "0.7"
240
+ },
241
+ chainId: foundry.id,
242
+ owners: owners.map((owner) => toAccount(owner.address)),
243
+ account: owners[1],
244
+ signatures: partialSignatures,
245
+ safe4337ModuleAddress: "0x7579EE8307284F293B1927136486880611F20002",
246
+ ...unSignedUserOperation
247
+ })
248
+
249
+ const finalSignature = await signUserOperation({
250
+ version: "1.4.1",
251
+ entryPoint: {
252
+ address: entryPoint07Address,
253
+ version: "0.7"
254
+ },
255
+ chainId: foundry.id,
256
+ owners: owners.map((owner) => toAccount(owner.address)),
257
+ account: owners[2],
258
+ signatures: partialSignatures,
259
+ safe4337ModuleAddress: "0x7579EE8307284F293B1927136486880611F20002",
260
+ ...unSignedUserOperation
261
+ })
262
+
263
+ const userOpHash = safeAccountClient.sendUserOperation({
264
+ ...unSignedUserOperation,
265
+ signature: finalSignature
266
+ })
267
+
268
+ expect(userOpHash).toBeTruthy()
269
+
270
+ const receipt = await safeAccountClient.waitForUserOperationReceipt({
271
+ hash: await userOpHash
272
+ })
273
+
274
+ expect(receipt).toBeTruthy()
275
+ expect(receipt.success).toBeTruthy()
276
+ })
277
+ })
@@ -0,0 +1,177 @@
1
+ import {
2
+ type Account,
3
+ type Address,
4
+ type Chain,
5
+ type Hex,
6
+ type LocalAccount,
7
+ type OneOf,
8
+ type Transport,
9
+ type UnionPartialBy,
10
+ type WalletClient,
11
+ concat,
12
+ concatHex,
13
+ decodeAbiParameters,
14
+ encodeAbiParameters,
15
+ encodePacked
16
+ } from "viem"
17
+ import type { UserOperation } from "viem/account-abstraction"
18
+ import { toOwner } from "../../utils/index.js"
19
+ import type { EthereumProvider } from "../../utils/toOwner.js"
20
+ import {
21
+ EIP712_SAFE_OPERATION_TYPE_V06,
22
+ EIP712_SAFE_OPERATION_TYPE_V07,
23
+ type SafeVersion,
24
+ getDefaultAddresses,
25
+ getPaymasterAndData
26
+ } from "./toSafeSmartAccount.js"
27
+
28
+ export async function signUserOperation(
29
+ parameters: UnionPartialBy<UserOperation, "sender"> & {
30
+ version: SafeVersion
31
+ entryPoint: {
32
+ address: Address
33
+ version: "0.6" | "0.7"
34
+ }
35
+ owners: Account[]
36
+ account: OneOf<
37
+ | EthereumProvider
38
+ | WalletClient<Transport, Chain | undefined, Account>
39
+ | LocalAccount
40
+ >
41
+ chainId: number
42
+ signatures?: Hex
43
+ validAfter?: number
44
+ validUntil?: number
45
+ safe4337ModuleAddress?: Address
46
+ }
47
+ ) {
48
+ const {
49
+ chainId,
50
+ entryPoint,
51
+ validAfter = 0,
52
+ validUntil = 0,
53
+ safe4337ModuleAddress: _safe4337ModuleAddress,
54
+ version,
55
+ owners,
56
+ signatures: existingSignatures,
57
+ account,
58
+ ...userOperation
59
+ } = parameters
60
+
61
+ const { safe4337ModuleAddress } = getDefaultAddresses(
62
+ version,
63
+ entryPoint.version,
64
+ {
65
+ safe4337ModuleAddress: _safe4337ModuleAddress
66
+ }
67
+ )
68
+
69
+ const message = {
70
+ safe: userOperation.sender,
71
+ callData: userOperation.callData,
72
+ nonce: userOperation.nonce,
73
+ initCode: userOperation.initCode ?? "0x",
74
+ maxFeePerGas: userOperation.maxFeePerGas,
75
+ maxPriorityFeePerGas: userOperation.maxPriorityFeePerGas,
76
+ preVerificationGas: userOperation.preVerificationGas,
77
+ verificationGasLimit: userOperation.verificationGasLimit,
78
+ callGasLimit: userOperation.callGasLimit,
79
+ paymasterAndData: userOperation.paymasterAndData ?? "0x",
80
+ validAfter: validAfter,
81
+ validUntil: validUntil,
82
+ entryPoint: entryPoint.address
83
+ }
84
+
85
+ if ("initCode" in userOperation) {
86
+ message.paymasterAndData = userOperation.paymasterAndData ?? "0x"
87
+ }
88
+
89
+ if ("factory" in userOperation) {
90
+ if (userOperation.factory && userOperation.factoryData) {
91
+ message.initCode = concatHex([
92
+ userOperation.factory,
93
+ userOperation.factoryData
94
+ ])
95
+ }
96
+ if (!userOperation.sender) {
97
+ throw new Error("Sender is required")
98
+ }
99
+ message.paymasterAndData = getPaymasterAndData({
100
+ ...userOperation,
101
+ sender: userOperation.sender
102
+ })
103
+ }
104
+
105
+ const localOwners = [
106
+ await toOwner({
107
+ owner: account as OneOf<LocalAccount | EthereumProvider>
108
+ })
109
+ ]
110
+
111
+ let unPackedSignatures: readonly { signer: Address; data: Hex }[] = []
112
+
113
+ if (existingSignatures) {
114
+ const decoded = decodeAbiParameters(
115
+ [
116
+ {
117
+ components: [
118
+ { type: "address", name: "signer" },
119
+ { type: "bytes", name: "data" }
120
+ ],
121
+ name: "signatures",
122
+ type: "tuple[]"
123
+ }
124
+ ],
125
+ existingSignatures
126
+ )
127
+
128
+ unPackedSignatures = decoded[0]
129
+ }
130
+
131
+ const signatures: { signer: Address; data: Hex }[] = [
132
+ ...unPackedSignatures,
133
+ ...(await Promise.all(
134
+ localOwners.map(async (localOwner) => ({
135
+ signer: localOwner.address,
136
+ data: await localOwner.signTypedData({
137
+ domain: {
138
+ chainId,
139
+ verifyingContract: safe4337ModuleAddress
140
+ },
141
+ types:
142
+ entryPoint.version === "0.6"
143
+ ? EIP712_SAFE_OPERATION_TYPE_V06
144
+ : EIP712_SAFE_OPERATION_TYPE_V07,
145
+ primaryType: "SafeOp",
146
+ message: message
147
+ })
148
+ }))
149
+ ))
150
+ ]
151
+
152
+ if (signatures.length !== owners.length) {
153
+ return encodeAbiParameters(
154
+ [
155
+ {
156
+ components: [
157
+ { type: "address", name: "signer" },
158
+ { type: "bytes", name: "data" }
159
+ ],
160
+ name: "signatures",
161
+ type: "tuple[]"
162
+ }
163
+ ],
164
+ [signatures]
165
+ )
166
+ }
167
+
168
+ signatures.sort((left, right) =>
169
+ left.signer.toLowerCase().localeCompare(right.signer.toLowerCase())
170
+ )
171
+ const signatureBytes = concat(signatures.map((sig) => sig.data))
172
+
173
+ return encodePacked(
174
+ ["uint48", "uint48", "bytes"],
175
+ [validAfter, validUntil, signatureBytes]
176
+ )
177
+ }