@safe-global/relay-kit 3.1.0-alpha.2 → 3.1.0

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,740 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- const crypto_1 = __importDefault(require("crypto"));
30
- const dotenv_1 = __importDefault(require("dotenv"));
31
- const ethers_1 = require("ethers");
32
- const protocol_kit_1 = __importStar(require("@safe-global/protocol-kit")), protocolKit = protocol_kit_1;
33
- const webauthnShim_1 = require("@safe-global/protocol-kit/tests/e2e/utils/webauthnShim");
34
- const passkeys_1 = require("@safe-global/protocol-kit/tests/e2e/utils/passkeys");
35
- const safe_modules_deployments_1 = require("@safe-global/safe-modules-deployments");
36
- const safe_core_sdk_types_1 = require("@safe-global/safe-core-sdk-types");
37
- const Safe4337Pack_1 = require("./Safe4337Pack");
38
- const SafeOperation_1 = __importDefault(require("./SafeOperation"));
39
- const constants = __importStar(require("./constants"));
40
- const fixtures = __importStar(require("./testing-utils/fixtures"));
41
- const helpers_1 = require("./testing-utils/helpers");
42
- const utils = __importStar(require("./utils"));
43
- dotenv_1.default.config();
44
- const requestResponseMap = {
45
- [constants.RPC_4337_CALLS.SUPPORTED_ENTRY_POINTS]: fixtures.ENTRYPOINTS,
46
- [constants.RPC_4337_CALLS.CHAIN_ID]: fixtures.CHAIN_ID,
47
- [constants.RPC_4337_CALLS.SEND_USER_OPERATION]: fixtures.USER_OPERATION_HASH,
48
- [constants.RPC_4337_CALLS.ESTIMATE_USER_OPERATION_GAS]: fixtures.GAS_ESTIMATION,
49
- [constants.RPC_4337_CALLS.GET_USER_OPERATION_BY_HASH]: fixtures.USER_OPERATION_BY_HASH,
50
- [constants.RPC_4337_CALLS.GET_USER_OPERATION_RECEIPT]: fixtures.USER_OPERATION_RECEIPT,
51
- ['pimlico_getUserOperationGasPrice']: fixtures.USER_OPERATION_GAS_PRICE
52
- };
53
- const sendMock = jest.fn(async (method) => {
54
- return requestResponseMap[method];
55
- });
56
- jest.mock('./utils', () => ({
57
- ...jest.requireActual('./utils'),
58
- getEip4337BundlerProvider: jest.fn(() => ({ send: sendMock }))
59
- }));
60
- let safe4337ModuleAddress;
61
- let addModulesLibAddress;
62
- describe('Safe4337Pack', () => {
63
- beforeAll(async () => {
64
- const network = parseInt(fixtures.CHAIN_ID).toString();
65
- safe4337ModuleAddress = (0, safe_modules_deployments_1.getSafe4337ModuleDeployment)({
66
- released: true,
67
- version: '0.2.0',
68
- network
69
- })?.networkAddresses[network];
70
- addModulesLibAddress = (0, safe_modules_deployments_1.getAddModulesLibDeployment)({
71
- released: true,
72
- version: '0.2.0',
73
- network
74
- })?.networkAddresses[network];
75
- });
76
- afterEach(() => {
77
- jest.clearAllMocks();
78
- });
79
- describe('4337 Safe validation', () => {
80
- it('should throw an error if the Safe version is not greater than 1.4.1', async () => {
81
- await expect((0, helpers_1.createSafe4337Pack)({ options: { safeAddress: fixtures.SAFE_ADDRESS_v1_3_0 } })).rejects.toThrow('Incompatibility detected: The current Safe Account version (1.3.0) is not supported. EIP-4337 requires the Safe to use at least v1.4.1.');
82
- });
83
- it('should throw an error if the 4337 Module is not enabled in the Safe account', async () => {
84
- await expect((0, helpers_1.createSafe4337Pack)({
85
- options: { safeAddress: fixtures.SAFE_ADDRESS_4337_MODULE_NOT_ENABLED }
86
- })).rejects.toThrow('Incompatibility detected: The EIP-4337 module is not enabled in the provided Safe Account. Enable this module (address: 0xa581c4A4DB7175302464fF3C06380BC3270b4037) to add compatibility.');
87
- });
88
- it('should throw an error if the 4337 fallbackhandler is not attached to the Safe account', async () => {
89
- await expect((0, helpers_1.createSafe4337Pack)({
90
- options: { safeAddress: fixtures.SAFE_ADDRESS_4337_FALLBACKHANDLER_NOT_ENABLED }
91
- })).rejects.toThrow('Incompatibility detected: The EIP-4337 fallbackhandler is not attached to the Safe Account. Attach this fallbackhandler (address: 0xa581c4A4DB7175302464fF3C06380BC3270b4037) to ensure compatibility.');
92
- });
93
- it('should throw an error if the Safe Modules do not match the supported version', async () => {
94
- await expect((0, helpers_1.createSafe4337Pack)({
95
- safeModulesVersion: fixtures.SAFE_MODULES_V0_3_0
96
- })).rejects.toThrow('Incompatibility detected: Safe modules version 0.3.0 is not supported. The SDK can use 0.2.0 only.');
97
- });
98
- });
99
- describe('When using existing Safe Accounts with version 1.4.1 or greater', () => {
100
- it('should throw an error if the version of the entrypoint used is incompatible', async () => {
101
- await expect((0, helpers_1.createSafe4337Pack)({
102
- options: { safeAddress: fixtures.SAFE_ADDRESS_v1_4_1 },
103
- customContracts: { entryPointAddress: fixtures.ENTRYPOINTS[1] }
104
- })).rejects.toThrow(`The selected entrypoint ${fixtures.ENTRYPOINTS[1]} is not compatible with version 0.2.0 of Safe modules`);
105
- });
106
- it('should throw an error if no supported entrypoints are available', async () => {
107
- const overridenMap = Object.assign({}, requestResponseMap, {
108
- [constants.RPC_4337_CALLS.SUPPORTED_ENTRY_POINTS]: [fixtures.ENTRYPOINTS[1]]
109
- });
110
- const mockedUtils = jest.requireMock('./utils');
111
- mockedUtils.getEip4337BundlerProvider.mockImplementationOnce(() => ({
112
- send: jest.fn(async (method) => overridenMap[method])
113
- }));
114
- await expect((0, helpers_1.createSafe4337Pack)({
115
- options: { safeAddress: fixtures.SAFE_ADDRESS_v1_4_1 }
116
- })).rejects.toThrow(`Incompatibility detected: None of the entrypoints provided by the bundler is compatible with the Safe modules version 0.2.0`);
117
- });
118
- it('should be able to instantiate the pack using a existing Safe', async () => {
119
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
120
- options: { safeAddress: fixtures.SAFE_ADDRESS_v1_4_1 }
121
- });
122
- expect(safe4337Pack).toBeInstanceOf(Safe4337Pack_1.Safe4337Pack);
123
- expect(safe4337Pack.protocolKit).toBeInstanceOf(protocol_kit_1.default);
124
- expect(await safe4337Pack.protocolKit.getAddress()).toBe(fixtures.SAFE_ADDRESS_v1_4_1);
125
- expect(await safe4337Pack.getChainId()).toBe(fixtures.CHAIN_ID);
126
- });
127
- it('should have the 4337 module enabled', async () => {
128
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
129
- options: { safeAddress: fixtures.SAFE_ADDRESS_v1_4_1 }
130
- });
131
- expect(await safe4337Pack.protocolKit.getModules()).toEqual([safe4337ModuleAddress]);
132
- });
133
- it('should detect if a custom 4337 module is not enabled in the Safe', async () => {
134
- await expect((0, helpers_1.createSafe4337Pack)({
135
- customContracts: {
136
- safe4337ModuleAddress: '0xCustomModule'
137
- },
138
- options: {
139
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
140
- }
141
- })).rejects.toThrow('Incompatibility detected: The EIP-4337 module is not enabled in the provided Safe Account. Enable this module (address: 0xCustomModule) to add compatibility.');
142
- });
143
- it('should use the 4337 module as the fallback handler', async () => {
144
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
145
- options: {
146
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
147
- }
148
- });
149
- expect(await safe4337Pack.protocolKit.getFallbackHandler()).toEqual(safe4337ModuleAddress);
150
- });
151
- });
152
- describe('When the Safe Account does not exists', () => {
153
- it('should be able to instantiate the pack using a predicted Safe', async () => {
154
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
155
- options: {
156
- owners: [fixtures.OWNER_1],
157
- threshold: 1
158
- }
159
- });
160
- expect(await safe4337Pack.protocolKit.getAddress()).toBe(fixtures.PREDICTED_SAFE_ADDRESS);
161
- });
162
- it('should throw an error if the entrypoint is not compatible with the safe modules version', async () => {
163
- await expect((0, helpers_1.createSafe4337Pack)({
164
- options: {
165
- owners: [fixtures.OWNER_1],
166
- threshold: 1
167
- },
168
- customContracts: { entryPointAddress: fixtures.ENTRYPOINTS[1] }
169
- })).rejects.toThrow(`The selected entrypoint ${fixtures.ENTRYPOINTS[1]} is not compatible with version 0.2.0 of Safe modules`);
170
- });
171
- it('should throw an error if the owners or threshold are not specified', async () => {
172
- await expect((0, helpers_1.createSafe4337Pack)({
173
- // @ts-expect-error - An error will be thrown
174
- options: {
175
- threshold: 1
176
- }
177
- })).rejects.toThrow('Owners and threshold are required to deploy a new Safe');
178
- await expect((0, helpers_1.createSafe4337Pack)({
179
- // @ts-expect-error - An error will be thrown
180
- options: {
181
- owners: [fixtures.OWNER_1]
182
- }
183
- })).rejects.toThrow('Owners and threshold are required to deploy a new Safe');
184
- });
185
- it('should encode the enableModules transaction as deployment data', async () => {
186
- const encodeFunctionDataSpy = jest.spyOn(constants.INTERFACES, 'encodeFunctionData');
187
- const safeCreateSpy = jest.spyOn(protocol_kit_1.default, 'init');
188
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
189
- options: {
190
- owners: [fixtures.OWNER_1, fixtures.OWNER_2],
191
- threshold: 1
192
- }
193
- });
194
- expect(encodeFunctionDataSpy).toHaveBeenCalledWith('enableModules', [[safe4337ModuleAddress]]);
195
- expect(safeCreateSpy).toHaveBeenCalledWith({
196
- provider: safe4337Pack.protocolKit.getSafeProvider().provider,
197
- signer: safe4337Pack.protocolKit.getSafeProvider().signer,
198
- predictedSafe: {
199
- safeDeploymentConfig: {
200
- safeVersion: constants.DEFAULT_SAFE_VERSION,
201
- saltNonce: undefined
202
- },
203
- safeAccountConfig: {
204
- owners: [fixtures.OWNER_1, fixtures.OWNER_2],
205
- threshold: 1,
206
- to: addModulesLibAddress,
207
- data: constants.INTERFACES.encodeFunctionData('enableModules', [
208
- [safe4337ModuleAddress]
209
- ]),
210
- fallbackHandler: safe4337ModuleAddress,
211
- paymentToken: ethers_1.ethers.ZeroAddress,
212
- payment: 0,
213
- paymentReceiver: ethers_1.ethers.ZeroAddress
214
- }
215
- }
216
- });
217
- });
218
- it('should encode the enablesModule transaction together with a specific token approval in a multiSend call when trying to use a paymaster', async () => {
219
- const encodeFunctionDataSpy = jest.spyOn(constants.INTERFACES, 'encodeFunctionData');
220
- const safeCreateSpy = jest.spyOn(protocol_kit_1.default, 'init');
221
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
222
- options: {
223
- owners: [fixtures.OWNER_1],
224
- threshold: 1
225
- },
226
- paymasterOptions: {
227
- paymasterAddress: fixtures.PAYMASTER_ADDRESS,
228
- paymasterTokenAddress: fixtures.PAYMASTER_TOKEN_ADDRESS
229
- }
230
- });
231
- const enableModulesData = constants.INTERFACES.encodeFunctionData('enableModules', [
232
- [safe4337ModuleAddress]
233
- ]);
234
- const approveData = constants.INTERFACES.encodeFunctionData('approve', [
235
- fixtures.PAYMASTER_ADDRESS,
236
- 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn
237
- ]);
238
- const enable4337ModuleTransaction = {
239
- to: addModulesLibAddress,
240
- value: '0',
241
- data: enableModulesData,
242
- operation: safe_core_sdk_types_1.OperationType.DelegateCall
243
- };
244
- const approveToPaymasterTransaction = {
245
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
246
- value: '0',
247
- data: approveData,
248
- operation: safe_core_sdk_types_1.OperationType.Call
249
- };
250
- const multiSendData = protocolKit.encodeMultiSendData([
251
- enable4337ModuleTransaction,
252
- approveToPaymasterTransaction
253
- ]);
254
- expect(encodeFunctionDataSpy).toHaveBeenNthCalledWith(3, 'multiSend', [multiSendData]);
255
- expect(safeCreateSpy).toHaveBeenCalledWith({
256
- provider: safe4337Pack.protocolKit.getSafeProvider().provider,
257
- signer: safe4337Pack.protocolKit.getSafeProvider().signer,
258
- predictedSafe: {
259
- safeDeploymentConfig: {
260
- safeVersion: constants.DEFAULT_SAFE_VERSION,
261
- saltNonce: undefined
262
- },
263
- safeAccountConfig: {
264
- owners: [fixtures.OWNER_1],
265
- threshold: 1,
266
- to: await safe4337Pack.protocolKit.getMultiSendAddress(),
267
- data: constants.INTERFACES.encodeFunctionData('multiSend', [multiSendData]),
268
- fallbackHandler: safe4337ModuleAddress,
269
- paymentToken: ethers_1.ethers.ZeroAddress,
270
- payment: 0,
271
- paymentReceiver: ethers_1.ethers.ZeroAddress
272
- }
273
- }
274
- });
275
- });
276
- });
277
- describe('When creating a new SafeOperation', () => {
278
- let safe4337Pack;
279
- let transferUSDC;
280
- beforeAll(async () => {
281
- safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
282
- options: {
283
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
284
- }
285
- });
286
- transferUSDC = {
287
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
288
- data: (0, helpers_1.generateTransferCallData)(fixtures.SAFE_ADDRESS_v1_4_1, 100000n),
289
- value: '0',
290
- operation: 0
291
- };
292
- });
293
- it('should allow to use a transaction batch', async () => {
294
- const transactions = [transferUSDC, transferUSDC];
295
- const safeOperation = await safe4337Pack.createTransaction({
296
- transactions
297
- });
298
- expect(safeOperation).toBeInstanceOf(SafeOperation_1.default);
299
- expect(safeOperation.data).toMatchObject({
300
- safe: fixtures.SAFE_ADDRESS_v1_4_1,
301
- entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
302
- initCode: '0x',
303
- paymasterAndData: '0x',
304
- callData: constants.INTERFACES.encodeFunctionData('executeUserOp', [
305
- await safe4337Pack.protocolKit.getMultiSendAddress(),
306
- '0',
307
- constants.INTERFACES.encodeFunctionData('multiSend', [
308
- protocolKit.encodeMultiSendData(transactions)
309
- ]),
310
- safe_core_sdk_types_1.OperationType.DelegateCall
311
- ]),
312
- nonce: 1n,
313
- callGasLimit: 150000n,
314
- validAfter: 0,
315
- validUntil: 0,
316
- maxFeePerGas: 100000n,
317
- maxPriorityFeePerGas: 200000n,
318
- verificationGasLimit: 400000n,
319
- preVerificationGas: 105000n
320
- });
321
- });
322
- it('should allow to use a single transaction', async () => {
323
- const safeOperation = await safe4337Pack.createTransaction({
324
- transactions: [transferUSDC]
325
- });
326
- expect(safeOperation).toBeInstanceOf(SafeOperation_1.default);
327
- expect(safeOperation.data).toMatchObject({
328
- safe: fixtures.SAFE_ADDRESS_v1_4_1,
329
- entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
330
- initCode: '0x',
331
- paymasterAndData: '0x',
332
- callData: constants.INTERFACES.encodeFunctionData('executeUserOp', [
333
- transferUSDC.to,
334
- transferUSDC.value,
335
- transferUSDC.data,
336
- safe_core_sdk_types_1.OperationType.Call
337
- ]),
338
- nonce: 1n,
339
- callGasLimit: 150000n,
340
- validAfter: 0,
341
- validUntil: 0,
342
- maxFeePerGas: 100000n,
343
- maxPriorityFeePerGas: 200000n,
344
- verificationGasLimit: 400000n,
345
- preVerificationGas: 105000n
346
- });
347
- });
348
- it('should fill the initCode property when the Safe does not exist', async () => {
349
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
350
- options: {
351
- owners: [fixtures.OWNER_1],
352
- threshold: 1
353
- }
354
- });
355
- const getInitCodeSpy = jest.spyOn(safe4337Pack.protocolKit, 'getInitCode');
356
- const safeOperation = await safe4337Pack.createTransaction({
357
- transactions: [transferUSDC]
358
- });
359
- expect(getInitCodeSpy).toHaveBeenCalled();
360
- expect(safeOperation.data.initCode).toBe('0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec671688f0b900000000000000000000000029fcb43b46531bca003ddc8fcb67ffe91900c7620000000000000000000000000000000000000000000000000000000000000060ad27de2a410652abce96ea0fdfc30c2f0fd35952b78f554667111999a28ff33800000000000000000000000000000000000000000000000000000000000001e4b63e800d000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008ecd4ec46d4d2a6b64fe960b3d64e8b94b2234eb0000000000000000000000000000000000000000000000000000000000000140000000000000000000000000a581c4a4db7175302464ff3c06380bc3270b40370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ffac5578be8ac1b2b9d13b34caf4a074b96b8a1b00000000000000000000000000000000000000000000000000000000000000648d0dc49f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a581c4a4db7175302464ff3c06380bc3270b40370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000');
361
- });
362
- it('should allow to create a sponsored transaction', async () => {
363
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
364
- options: {
365
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
366
- },
367
- paymasterOptions: {
368
- isSponsored: true,
369
- paymasterUrl: fixtures.PAYMASTER_URL
370
- }
371
- });
372
- const sponsoredSafeOperation = await safe4337Pack.createTransaction({
373
- transactions: [transferUSDC]
374
- });
375
- expect(sponsoredSafeOperation).toBeInstanceOf(SafeOperation_1.default);
376
- expect(sponsoredSafeOperation.data).toMatchObject({
377
- safe: fixtures.SAFE_ADDRESS_v1_4_1,
378
- entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
379
- initCode: '0x',
380
- paymasterAndData: '0x',
381
- callData: constants.INTERFACES.encodeFunctionData('executeUserOp', [
382
- transferUSDC.to,
383
- transferUSDC.value,
384
- transferUSDC.data,
385
- safe_core_sdk_types_1.OperationType.Call
386
- ]),
387
- nonce: 1n,
388
- callGasLimit: 150000n,
389
- validAfter: 0,
390
- validUntil: 0,
391
- maxFeePerGas: 100000n,
392
- maxPriorityFeePerGas: 200000n,
393
- verificationGasLimit: 400000n,
394
- preVerificationGas: 105000n
395
- });
396
- });
397
- it('createTransaction should throw an error if paymasterUrl is not present in sponsored transactions', async () => {
398
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
399
- options: {
400
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
401
- },
402
- // @ts-expect-error - An error will be thrown
403
- paymasterOptions: {
404
- isSponsored: true
405
- }
406
- });
407
- await expect(safe4337Pack.createTransaction({
408
- transactions: [transferUSDC]
409
- })).rejects.toThrow('No paymaster url provided for a sponsored transaction');
410
- });
411
- it('should add the approve transaction to the batch when amountToApprove is provided', async () => {
412
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
413
- options: {
414
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
415
- },
416
- paymasterOptions: {
417
- paymasterTokenAddress: fixtures.PAYMASTER_TOKEN_ADDRESS,
418
- paymasterAddress: fixtures.PAYMASTER_ADDRESS
419
- }
420
- });
421
- const amountToApprove = 80000n;
422
- const sponsoredSafeOperation = await safe4337Pack.createTransaction({
423
- transactions: [transferUSDC],
424
- options: {
425
- amountToApprove
426
- }
427
- });
428
- const approveTransaction = {
429
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
430
- data: constants.INTERFACES.encodeFunctionData('approve', [
431
- fixtures.PAYMASTER_ADDRESS,
432
- amountToApprove
433
- ]),
434
- value: '0',
435
- operation: safe_core_sdk_types_1.OperationType.Call // Call for approve
436
- };
437
- const batch = [transferUSDC, approveTransaction];
438
- expect(sponsoredSafeOperation).toBeInstanceOf(SafeOperation_1.default);
439
- expect(sponsoredSafeOperation.data).toMatchObject({
440
- safe: fixtures.SAFE_ADDRESS_v1_4_1,
441
- entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
442
- initCode: '0x',
443
- paymasterAndData: '0x0000000000325602a77416A16136FDafd04b299f',
444
- callData: constants.INTERFACES.encodeFunctionData('executeUserOp', [
445
- await safe4337Pack.protocolKit.getMultiSendAddress(),
446
- '0',
447
- constants.INTERFACES.encodeFunctionData('multiSend', [
448
- protocolKit.encodeMultiSendData(batch)
449
- ]),
450
- safe_core_sdk_types_1.OperationType.DelegateCall
451
- ]),
452
- nonce: 1n,
453
- callGasLimit: 150000n,
454
- validAfter: 0,
455
- validUntil: 0,
456
- maxFeePerGas: 100000n,
457
- maxPriorityFeePerGas: 200000n,
458
- verificationGasLimit: 400000n,
459
- preVerificationGas: 105000n
460
- });
461
- });
462
- });
463
- describe('When using a passkey signer', () => {
464
- const SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS = '0x608Cf2e3412c6BDA14E6D8A0a7D27c4240FeD6F1';
465
- const CUSTOM_P256_VERIFIER_ADDRESS = '0xcA89CBa4813D5B40AeC6E57A30d0Eeb500d6531b';
466
- const PASSKEY_PRIVATE_KEY = BigInt(process.env.PASSKEY_PRIVATE_KEY);
467
- jest.setTimeout(120000);
468
- let passkey;
469
- beforeAll(async () => {
470
- if (!global.crypto) {
471
- global.crypto = crypto_1.default;
472
- }
473
- const webAuthnCredentials = new webauthnShim_1.WebAuthnCredentials(PASSKEY_PRIVATE_KEY);
474
- passkey = await (0, passkeys_1.createMockPasskey)('chucknorris', webAuthnCredentials);
475
- passkey.customVerifierAddress = CUSTOM_P256_VERIFIER_ADDRESS;
476
- Object.defineProperty(global, 'navigator', {
477
- value: {
478
- credentials: {
479
- create: jest
480
- .fn()
481
- .mockImplementation(webAuthnCredentials.create.bind(webAuthnCredentials)),
482
- get: jest.fn().mockImplementation(webAuthnCredentials.get.bind(webAuthnCredentials))
483
- }
484
- },
485
- writable: true
486
- });
487
- });
488
- it('should include a passkey configuration transaction to SafeWebAuthnSharedSigner contract in a multiSend call', async () => {
489
- const encodeFunctionDataSpy = jest.spyOn(constants.INTERFACES, 'encodeFunctionData');
490
- const safeCreateSpy = jest.spyOn(protocol_kit_1.default, 'init');
491
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
492
- signer: passkey,
493
- options: {
494
- owners: [fixtures.OWNER_1],
495
- threshold: 1
496
- }
497
- });
498
- const provider = safe4337Pack.protocolKit.getSafeProvider().provider;
499
- const safeProvider = await protocolKit.SafeProvider.init(provider, passkey);
500
- const passkeySigner = (await safeProvider.getExternalSigner());
501
- const passkeyOwnerConfiguration = {
502
- ...passkeySigner.coordinates,
503
- verifiers: CUSTOM_P256_VERIFIER_ADDRESS
504
- };
505
- const enableModulesData = constants.INTERFACES.encodeFunctionData('enableModules', [
506
- [safe4337ModuleAddress]
507
- ]);
508
- const passkeyConfigureData = constants.INTERFACES.encodeFunctionData('configure', [
509
- passkeyOwnerConfiguration
510
- ]);
511
- const enable4337ModuleTransaction = {
512
- to: addModulesLibAddress,
513
- value: '0',
514
- data: enableModulesData,
515
- operation: safe_core_sdk_types_1.OperationType.DelegateCall
516
- };
517
- const sharedSignerTransaction = {
518
- to: SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS,
519
- value: '0',
520
- data: passkeyConfigureData,
521
- operation: safe_core_sdk_types_1.OperationType.DelegateCall
522
- };
523
- const multiSendData = protocolKit.encodeMultiSendData([
524
- enable4337ModuleTransaction,
525
- sharedSignerTransaction
526
- ]);
527
- expect(encodeFunctionDataSpy).toHaveBeenNthCalledWith(2, 'configure', [
528
- passkeyOwnerConfiguration
529
- ]);
530
- expect(encodeFunctionDataSpy).toHaveBeenNthCalledWith(3, 'multiSend', [multiSendData]);
531
- expect(safeCreateSpy).toHaveBeenCalledWith({
532
- provider: safe4337Pack.protocolKit.getSafeProvider().provider,
533
- signer: passkey,
534
- predictedSafe: {
535
- safeDeploymentConfig: {
536
- safeVersion: constants.DEFAULT_SAFE_VERSION,
537
- saltNonce: undefined
538
- },
539
- safeAccountConfig: {
540
- owners: [fixtures.OWNER_1, SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS],
541
- threshold: 1,
542
- to: await safe4337Pack.protocolKit.getMultiSendAddress(),
543
- data: constants.INTERFACES.encodeFunctionData('multiSend', [multiSendData]),
544
- fallbackHandler: safe4337ModuleAddress,
545
- paymentToken: ethers_1.ethers.ZeroAddress,
546
- payment: 0,
547
- paymentReceiver: ethers_1.ethers.ZeroAddress
548
- }
549
- }
550
- });
551
- });
552
- it('should allow to sign a SafeOperation', async () => {
553
- const transferUSDC = {
554
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
555
- data: (0, helpers_1.generateTransferCallData)(fixtures.SAFE_ADDRESS_4337_PASSKEY, 100000n),
556
- value: '0',
557
- operation: 0
558
- };
559
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
560
- signer: passkey,
561
- options: {
562
- owners: [],
563
- threshold: 1
564
- }
565
- });
566
- const safeOperation = await safe4337Pack.createTransaction({
567
- transactions: [transferUSDC]
568
- });
569
- const safeOpHash = utils.calculateSafeUserOperationHash(safeOperation.data, BigInt(fixtures.CHAIN_ID), fixtures.MODULE_ADDRESS);
570
- const passkeySignature = await safe4337Pack.protocolKit.signHash(safeOpHash);
571
- expect(await safe4337Pack.signSafeOperation(safeOperation)).toMatchObject({
572
- signatures: new Map().set(SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS.toLowerCase(), new protocolKit.EthSafeSignature(SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS, passkeySignature.data, true))
573
- });
574
- });
575
- it('should allow to send an UserOperation to a bundler', async () => {
576
- const transferUSDC = {
577
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
578
- data: (0, helpers_1.generateTransferCallData)(fixtures.SAFE_ADDRESS_4337_PASSKEY, 100000n),
579
- value: '0',
580
- operation: 0
581
- };
582
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
583
- signer: passkey,
584
- options: {
585
- safeAddress: fixtures.SAFE_ADDRESS_4337_PASSKEY
586
- }
587
- });
588
- let safeOperation = await safe4337Pack.createTransaction({
589
- transactions: [transferUSDC]
590
- });
591
- safeOperation = await safe4337Pack.signSafeOperation(safeOperation);
592
- await safe4337Pack.executeTransaction({ executable: safeOperation });
593
- expect(sendMock).toHaveBeenCalledWith(constants.RPC_4337_CALLS.SEND_USER_OPERATION, [
594
- utils.userOperationToHexValues(safeOperation.toUserOperation()),
595
- fixtures.ENTRYPOINTS[0]
596
- ]);
597
- });
598
- });
599
- it('should allow to sign a SafeOperation', async () => {
600
- const transferUSDC = {
601
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
602
- data: (0, helpers_1.generateTransferCallData)(fixtures.SAFE_ADDRESS_v1_4_1, 100000n),
603
- value: '0',
604
- operation: 0
605
- };
606
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
607
- options: {
608
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
609
- }
610
- });
611
- const safeOperation = await safe4337Pack.createTransaction({
612
- transactions: [transferUSDC]
613
- });
614
- expect(await safe4337Pack.signSafeOperation(safeOperation)).toMatchObject({
615
- signatures: new Map().set(fixtures.OWNER_1.toLowerCase(), new protocolKit.EthSafeSignature(fixtures.OWNER_1, '0xda808d1e84e6aac5eb50fda331469a108bfdce442fd41501fefaa5b5d648ade406d08a1ca2ca9a5f0ba1a079da001dbee6990189a2cdb054e6c388d5afbd2d9b20', false))
616
- });
617
- });
618
- it('should allow to sign a SafeOperation using a SafeOperationResponse object from the api to add a signature', async () => {
619
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
620
- options: {
621
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
622
- }
623
- });
624
- expect(await safe4337Pack.signSafeOperation(fixtures.SAFE_OPERATION_RESPONSE)).toMatchObject({
625
- signatures: new Map()
626
- .set(fixtures.OWNER_1.toLowerCase(), new protocolKit.EthSafeSignature(fixtures.OWNER_1, '0x975c7ddab3dc06240918a7bde0f543d1b082a8cadeca19d4bc13c30430367fac46c7ef923d9d0051423d1d59d106e5d199a734cd6a472276d54bb04ec7b3796520', false))
627
- .set(fixtures.OWNER_2.toLowerCase(), new protocolKit.EthSafeSignature(fixtures.OWNER_2, '0xcb28e74375889e400a4d8aca46b8c59e1cf8825e373c26fa99c2fd7c078080e64fe30eaf1125257bdfe0b358b5caef68aa0420478145f52decc8e74c979d43ab1d', false))
628
- });
629
- });
630
- it('should allow to send an UserOperation to a bundler', async () => {
631
- const transferUSDC = {
632
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
633
- data: (0, helpers_1.generateTransferCallData)(fixtures.SAFE_ADDRESS_v1_4_1, 100000n),
634
- value: '0',
635
- operation: 0
636
- };
637
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
638
- options: {
639
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
640
- }
641
- });
642
- let safeOperation = await safe4337Pack.createTransaction({
643
- transactions: [transferUSDC]
644
- });
645
- safeOperation = await safe4337Pack.signSafeOperation(safeOperation);
646
- await safe4337Pack.executeTransaction({ executable: safeOperation });
647
- expect(sendMock).toHaveBeenCalledWith(constants.RPC_4337_CALLS.SEND_USER_OPERATION, [
648
- utils.userOperationToHexValues(safeOperation.toUserOperation()),
649
- fixtures.ENTRYPOINTS[0]
650
- ]);
651
- });
652
- it('should allow to send a UserOperation to the bundler using a SafeOperationResponse object from the api', async () => {
653
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
654
- options: {
655
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
656
- }
657
- });
658
- await safe4337Pack.executeTransaction({ executable: fixtures.SAFE_OPERATION_RESPONSE });
659
- expect(sendMock).toHaveBeenCalledWith(constants.RPC_4337_CALLS.SEND_USER_OPERATION, [
660
- utils.userOperationToHexValues({
661
- sender: '0xE322e721bCe76cE7FCf3A475f139A9314571ad3D',
662
- nonce: '3',
663
- initCode: '0x',
664
- callData: '0x7bb37428000000000000000000000000e322e721bce76ce7fcf3a475f139a9314571ad3d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
665
- callGasLimit: 122497n,
666
- verificationGasLimit: 123498n,
667
- preVerificationGas: 50705n,
668
- maxFeePerGas: 105183831060n,
669
- maxPriorityFeePerGas: 1380000000n,
670
- paymasterAndData: '0x',
671
- signature: '0x000000000000000000000000cb28e74375889e400a4d8aca46b8c59e1cf8825e373c26fa99c2fd7c078080e64fe30eaf1125257bdfe0b358b5caef68aa0420478145f52decc8e74c979d43ab1d'
672
- }),
673
- fixtures.ENTRYPOINTS[0]
674
- ]);
675
- });
676
- it('should return a UserOperation based on a userOpHash', async () => {
677
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
678
- options: {
679
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
680
- }
681
- });
682
- const { userOperation, entryPoint, transactionHash, blockHash, blockNumber } = await safe4337Pack.getUserOperationByHash('0xee8e07f229d0ebf11c84a3e40f87e1d1b4c7b18eaeaebf3babb4b479424823e6');
683
- expect(userOperation).toMatchObject({
684
- sender: '0x1405B3659a11a16459fc27Fa1925b60388C38Ce1',
685
- nonce: '0x1',
686
- initCode: '0x',
687
- callData: '0x7bb3742800000000000000000000000038869bf66a61cf6bdb996a6ae40d5853fd43b52600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001848d80ff0a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000132001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000d725e11588f040d86c4c49d8236e32a5868549f000000000000000000000000000000000000000000000000000000000000186a0001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000d725e11588f040d86c4c49d8236e32a5868549f000000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
688
- callGasLimit: '0x1d7d0',
689
- verificationGasLimit: '0x14470',
690
- preVerificationGas: '0xbdb8',
691
- maxFeePerGas: '0x2d128cfa8c',
692
- maxPriorityFeePerGas: '0x52412100',
693
- paymasterAndData: '0x',
694
- signature: '0x000000000000000000000000a397ca32ee7fb5282256ee3465da0843485930b803d747516aac76e152f834051ac18fd2b3c0565590f9d65085538993c85c9bb189c940d15c15402c7c2885821b'
695
- });
696
- expect(blockHash).toBe('0x65f8249337ffede2067a006a96da47d3d3445ca72492a6a82afa02899f05d2e5');
697
- expect(blockNumber).toBe('0x5378b9');
698
- expect(entryPoint).toBe('0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789');
699
- expect(transactionHash).toBe('0xef262d20f68e4900aa6380b8ac0f66f9c00a7d988179fa177ad9c9758f0e380e');
700
- });
701
- it('should return a UserOperation receipt based on a userOpHash', async () => {
702
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
703
- options: {
704
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
705
- }
706
- });
707
- const userOperationReceipt = await safe4337Pack.getUserOperationReceipt('0xee8e07f229d0ebf11c84a3e40f87e1d1b4c7b18eaeaebf3babb4b479424823e6');
708
- expect(userOperationReceipt?.userOpHash).toBe('0x3cb881d1969036174f38d636d22108d1d032145518b53104fc0b1e1296d2cc9c');
709
- expect(userOperationReceipt?.sender).toBe('0x1405B3659a11a16459fc27Fa1925b60388C38Ce1');
710
- expect(userOperationReceipt?.actualGasUsed).toBe('0x27067');
711
- expect(userOperationReceipt?.actualGasCost).toBe('0x42f29418377167');
712
- expect(userOperationReceipt?.success).toBe(true);
713
- expect(userOperationReceipt?.logs).toStrictEqual([]);
714
- expect(userOperationReceipt?.receipt).toMatchObject({
715
- transactionHash: '0xef262d20f68e4900aa6380b8ac0f66f9c00a7d988179fa177ad9c9758f0e380e',
716
- transactionIndex: '0x63',
717
- blockHash: '0x65f8249337ffede2067a006a96da47d3d3445ca72492a6a82afa02899f05d2e5',
718
- blockNumber: '0x5378b9',
719
- from: '0x4337001Fff419768e088Ce247456c1B892888084',
720
- to: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
721
- cumulativeGasUsed: '0xc1a846',
722
- gasUsed: '0x25e6c',
723
- contractAddress: null,
724
- logs: [],
725
- logsBloom: '0x000000000000900000000000000000000000000000000000080000000002000000080000000000000402000100000000001000000000000080000200000100000000000000000000000000080000000000000000000000000000002000002000000000000a0000000000000000000800000000000000000000000010000200000000000060100000000000000040000000800000000000000008800000000000000000000000000000400000000000000200000000000000000002000000008000000002000100000001000000000000000000000020000000000000000020010040000000000020000010000008000200000000000000000000000000000000',
726
- status: '0x1',
727
- effectiveGasPrice: '0x1b67f3c201'
728
- });
729
- });
730
- it('should return an array of the entryPoint addresses supported by the client', async () => {
731
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
732
- options: {
733
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
734
- }
735
- });
736
- const supportedEntryPoints = await safe4337Pack.getSupportedEntryPoints();
737
- expect(supportedEntryPoints).toContain('0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789');
738
- });
739
- });
740
- //# sourceMappingURL=Safe4337Pack.test.js.map