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.
- package/CHANGELOG.md +6 -0
- package/_cjs/accounts/safe/index.js +11 -0
- package/_cjs/accounts/safe/index.js.map +1 -0
- package/_cjs/accounts/safe/signUserOperation.js +97 -0
- package/_cjs/accounts/safe/signUserOperation.js.map +1 -0
- package/_cjs/accounts/safe/toSafeSmartAccount.js +111 -89
- package/_cjs/accounts/safe/toSafeSmartAccount.js.map +1 -1
- package/_esm/accounts/safe/index.js +8 -0
- package/_esm/accounts/safe/index.js.map +1 -0
- package/_esm/accounts/safe/signUserOperation.js +94 -0
- package/_esm/accounts/safe/signUserOperation.js.map +1 -0
- package/_esm/accounts/safe/toSafeSmartAccount.js +112 -92
- package/_esm/accounts/safe/toSafeSmartAccount.js.map +1 -1
- package/_types/accounts/safe/index.d.ts +8 -0
- package/_types/accounts/safe/index.d.ts.map +1 -0
- package/_types/accounts/safe/signUserOperation.d.ts +19 -0
- package/_types/accounts/safe/signUserOperation.d.ts.map +1 -0
- package/_types/accounts/safe/toSafeSmartAccount.d.ts +33 -6
- package/_types/accounts/safe/toSafeSmartAccount.d.ts.map +1 -1
- package/accounts/safe/index.ts +22 -0
- package/accounts/safe/signUserOperation.test.ts +277 -0
- package/accounts/safe/signUserOperation.ts +177 -0
- package/accounts/safe/toSafeSmartAccount.ts +177 -116
- package/actions/smartAccount/signMessage.test.ts +27 -7
- package/actions/smartAccount/signTypedData.test.ts +4 -1
- package/package.json +6 -1
|
@@ -1,7 +1,35 @@
|
|
|
1
|
-
import { type Account, type Address, type Assign, type Chain, type Client, type
|
|
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
|
-
|
|
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":"AAAA,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,OAAO,EACZ,KAAK,MAAM,EACX,KAAK,KAAK,EACV,KAAK,MAAM,
|
|
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
|
+
}
|