@safe-global/relay-kit 3.1.0-alpha.1 → 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.
Files changed (28) hide show
  1. package/dist/src/index.d.ts +1 -0
  2. package/dist/src/index.js +6 -0
  3. package/dist/src/index.js.map +1 -1
  4. package/dist/src/packs/safe-4337/Safe4337Pack.d.ts +1 -1
  5. package/dist/src/packs/safe-4337/Safe4337Pack.js +67 -42
  6. package/dist/src/packs/safe-4337/Safe4337Pack.js.map +1 -1
  7. package/dist/src/packs/safe-4337/SafeOperation.d.ts +4 -1
  8. package/dist/src/packs/safe-4337/SafeOperation.js +6 -1
  9. package/dist/src/packs/safe-4337/SafeOperation.js.map +1 -1
  10. package/dist/src/packs/safe-4337/estimators/PimlicoFeeEstimator.js +2 -1
  11. package/dist/src/packs/safe-4337/estimators/PimlicoFeeEstimator.js.map +1 -1
  12. package/dist/src/packs/safe-4337/testing-utils/fixtures.d.ts +1 -1
  13. package/dist/src/packs/safe-4337/testing-utils/fixtures.js +1 -1
  14. package/dist/src/packs/safe-4337/types.d.ts +11 -4
  15. package/dist/tsconfig.build.tsbuildinfo +1 -1
  16. package/package.json +5 -5
  17. package/dist/src/packs/gelato/GelatoRelayPack.test.d.ts +0 -1
  18. package/dist/src/packs/gelato/GelatoRelayPack.test.js +0 -512
  19. package/dist/src/packs/gelato/GelatoRelayPack.test.js.map +0 -1
  20. package/dist/src/packs/safe-4337/Safe4337Pack.test.d.ts +0 -1
  21. package/dist/src/packs/safe-4337/Safe4337Pack.test.js +0 -733
  22. package/dist/src/packs/safe-4337/Safe4337Pack.test.js.map +0 -1
  23. package/dist/src/packs/safe-4337/SafeOperation.test.d.ts +0 -1
  24. package/dist/src/packs/safe-4337/SafeOperation.test.js +0 -129
  25. package/dist/src/packs/safe-4337/SafeOperation.test.js.map +0 -1
  26. package/dist/src/packs/safe-4337/estimators/PimlicoFeeEstimator.test.d.ts +0 -1
  27. package/dist/src/packs/safe-4337/estimators/PimlicoFeeEstimator.test.js +0 -75
  28. package/dist/src/packs/safe-4337/estimators/PimlicoFeeEstimator.test.js.map +0 -1
@@ -1,733 +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
- describe('When using a passkey signer', () => {
277
- const SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS = '0x608Cf2e3412c6BDA14E6D8A0a7D27c4240FeD6F1';
278
- const P256_VERIFIER_ADDRESS = '0xcA89CBa4813D5B40AeC6E57A30d0Eeb500d6531b';
279
- const PASSKEY_PRIVATE_KEY = BigInt(process.env.PASSKEY_PRIVATE_KEY);
280
- let passkey;
281
- beforeAll(async () => {
282
- if (!global.crypto) {
283
- global.crypto = crypto_1.default;
284
- }
285
- const webAuthnCredentials = new webauthnShim_1.WebAuthnCredentials(PASSKEY_PRIVATE_KEY);
286
- passkey = await (0, passkeys_1.createMockPasskey)('chucknorris', webAuthnCredentials);
287
- global.navigator = {
288
- credentials: {
289
- create: jest
290
- .fn()
291
- .mockImplementation(webAuthnCredentials.create.bind(webAuthnCredentials)),
292
- get: jest.fn().mockImplementation(webAuthnCredentials.get.bind(webAuthnCredentials))
293
- }
294
- };
295
- });
296
- it('should include a passkey configuration transaction to SafeWebAuthnSharedSigner contract in a multiSend call', async () => {
297
- const encodeFunctionDataSpy = jest.spyOn(constants.INTERFACES, 'encodeFunctionData');
298
- const safeCreateSpy = jest.spyOn(protocol_kit_1.default, 'init');
299
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
300
- signer: passkey,
301
- options: {
302
- owners: [fixtures.OWNER_1],
303
- threshold: 1
304
- }
305
- });
306
- const provider = safe4337Pack.protocolKit.getSafeProvider().provider;
307
- const safeProvider = await protocolKit.SafeProvider.init(provider, passkey);
308
- const passkeySigner = (await safeProvider.getExternalSigner());
309
- const passkeyOwnerConfiguration = {
310
- ...passkeySigner.coordinates,
311
- verifiers: P256_VERIFIER_ADDRESS
312
- };
313
- const enableModulesData = constants.INTERFACES.encodeFunctionData('enableModules', [
314
- [safe4337ModuleAddress]
315
- ]);
316
- const passkeyConfigureData = constants.INTERFACES.encodeFunctionData('configure', [
317
- passkeyOwnerConfiguration
318
- ]);
319
- const enable4337ModuleTransaction = {
320
- to: addModulesLibAddress,
321
- value: '0',
322
- data: enableModulesData,
323
- operation: safe_core_sdk_types_1.OperationType.DelegateCall
324
- };
325
- const sharedSignerTransaction = {
326
- to: SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS,
327
- value: '0',
328
- data: passkeyConfigureData,
329
- operation: safe_core_sdk_types_1.OperationType.DelegateCall
330
- };
331
- const multiSendData = protocolKit.encodeMultiSendData([
332
- enable4337ModuleTransaction,
333
- sharedSignerTransaction
334
- ]);
335
- expect(encodeFunctionDataSpy).toHaveBeenNthCalledWith(2, 'configure', [
336
- passkeyOwnerConfiguration
337
- ]);
338
- expect(encodeFunctionDataSpy).toHaveBeenNthCalledWith(3, 'multiSend', [multiSendData]);
339
- expect(safeCreateSpy).toHaveBeenCalledWith({
340
- provider: safe4337Pack.protocolKit.getSafeProvider().provider,
341
- signer: passkey,
342
- predictedSafe: {
343
- safeDeploymentConfig: {
344
- safeVersion: constants.DEFAULT_SAFE_VERSION,
345
- saltNonce: undefined
346
- },
347
- safeAccountConfig: {
348
- owners: [fixtures.OWNER_1, SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS],
349
- threshold: 1,
350
- to: await safe4337Pack.protocolKit.getMultiSendAddress(),
351
- data: constants.INTERFACES.encodeFunctionData('multiSend', [multiSendData]),
352
- fallbackHandler: safe4337ModuleAddress,
353
- paymentToken: ethers_1.ethers.ZeroAddress,
354
- payment: 0,
355
- paymentReceiver: ethers_1.ethers.ZeroAddress
356
- }
357
- }
358
- });
359
- });
360
- it('should allow to sign a SafeOperation', async () => {
361
- const transferUSDC = {
362
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
363
- data: (0, helpers_1.generateTransferCallData)(fixtures.SAFE_ADDRESS_4337_PASSKEY, 100000n),
364
- value: '0',
365
- operation: 0
366
- };
367
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
368
- signer: passkey,
369
- options: {
370
- safeAddress: fixtures.SAFE_ADDRESS_4337_PASSKEY
371
- }
372
- });
373
- const safeOperation = await safe4337Pack.createTransaction({
374
- transactions: [transferUSDC]
375
- });
376
- expect(await safe4337Pack.signSafeOperation(safeOperation)).toMatchObject({
377
- signatures: new Map().set(SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS.toLowerCase(), new protocolKit.EthSafeSignature(SAFE_WEBAUTHN_SHARED_SIGNER_ADDRESS, '0x000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0c79e723c4ad6557198f00ab3e8cc1cd3de64b30f6ff44664fc131f37fa1e97fe4dce48568eb0582d34c6adb97a5902b6de0488c10ab3c9f3589b44b98027ac840000000000000000000000000000000000000000000000000000000000000025a24f744b28d73f066bf3203d145765a7bc735e6328168c8b03e476da3ad0d8fe0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e226f726967696e223a2268747470733a2f2f736166652e676c6f62616c22001f', true))
378
- });
379
- });
380
- it('should allow to send an UserOperation to a bundler', async () => {
381
- const transferUSDC = {
382
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
383
- data: (0, helpers_1.generateTransferCallData)(fixtures.SAFE_ADDRESS_4337_PASSKEY, 100000n),
384
- value: '0',
385
- operation: 0
386
- };
387
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
388
- signer: passkey,
389
- options: {
390
- safeAddress: fixtures.SAFE_ADDRESS_4337_PASSKEY
391
- }
392
- });
393
- let safeOperation = await safe4337Pack.createTransaction({
394
- transactions: [transferUSDC]
395
- });
396
- safeOperation = await safe4337Pack.signSafeOperation(safeOperation);
397
- await safe4337Pack.executeTransaction({ executable: safeOperation });
398
- expect(sendMock).toHaveBeenCalledWith(constants.RPC_4337_CALLS.SEND_USER_OPERATION, [
399
- utils.userOperationToHexValues(safeOperation.toUserOperation()),
400
- fixtures.ENTRYPOINTS[0]
401
- ]);
402
- });
403
- });
404
- });
405
- describe('When creating a new SafeOperation', () => {
406
- let safe4337Pack;
407
- let transferUSDC;
408
- beforeAll(async () => {
409
- safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
410
- options: {
411
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
412
- }
413
- });
414
- transferUSDC = {
415
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
416
- data: (0, helpers_1.generateTransferCallData)(fixtures.SAFE_ADDRESS_v1_4_1, 100000n),
417
- value: '0',
418
- operation: 0
419
- };
420
- });
421
- it('should allow to use a transaction batch', async () => {
422
- const transactions = [transferUSDC, transferUSDC];
423
- const safeOperation = await safe4337Pack.createTransaction({
424
- transactions
425
- });
426
- expect(safeOperation).toBeInstanceOf(SafeOperation_1.default);
427
- expect(safeOperation.data).toMatchObject({
428
- safe: fixtures.SAFE_ADDRESS_v1_4_1,
429
- entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
430
- initCode: '0x',
431
- paymasterAndData: '0x',
432
- callData: constants.INTERFACES.encodeFunctionData('executeUserOp', [
433
- await safe4337Pack.protocolKit.getMultiSendAddress(),
434
- '0',
435
- constants.INTERFACES.encodeFunctionData('multiSend', [
436
- protocolKit.encodeMultiSendData(transactions)
437
- ]),
438
- safe_core_sdk_types_1.OperationType.DelegateCall
439
- ]),
440
- nonce: 1n,
441
- callGasLimit: 150000n,
442
- validAfter: 0,
443
- validUntil: 0,
444
- maxFeePerGas: 100000n,
445
- maxPriorityFeePerGas: 200000n,
446
- verificationGasLimit: 400000n,
447
- preVerificationGas: 100000n
448
- });
449
- });
450
- it('should allow to use a single transaction', async () => {
451
- const safeOperation = await safe4337Pack.createTransaction({
452
- transactions: [transferUSDC]
453
- });
454
- expect(safeOperation).toBeInstanceOf(SafeOperation_1.default);
455
- expect(safeOperation.data).toMatchObject({
456
- safe: fixtures.SAFE_ADDRESS_v1_4_1,
457
- entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
458
- initCode: '0x',
459
- paymasterAndData: '0x',
460
- callData: constants.INTERFACES.encodeFunctionData('executeUserOp', [
461
- transferUSDC.to,
462
- transferUSDC.value,
463
- transferUSDC.data,
464
- safe_core_sdk_types_1.OperationType.Call
465
- ]),
466
- nonce: 1n,
467
- callGasLimit: 150000n,
468
- validAfter: 0,
469
- validUntil: 0,
470
- maxFeePerGas: 100000n,
471
- maxPriorityFeePerGas: 200000n,
472
- verificationGasLimit: 400000n,
473
- preVerificationGas: 100000n
474
- });
475
- });
476
- it('should fill the initCode property when the Safe does not exist', async () => {
477
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
478
- options: {
479
- owners: [fixtures.OWNER_1],
480
- threshold: 1
481
- }
482
- });
483
- const getInitCodeSpy = jest.spyOn(safe4337Pack.protocolKit, 'getInitCode');
484
- const safeOperation = await safe4337Pack.createTransaction({
485
- transactions: [transferUSDC]
486
- });
487
- expect(getInitCodeSpy).toHaveBeenCalled();
488
- expect(safeOperation.data.initCode).toBe('0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec671688f0b900000000000000000000000029fcb43b46531bca003ddc8fcb67ffe91900c7620000000000000000000000000000000000000000000000000000000000000060ad27de2a410652abce96ea0fdfc30c2f0fd35952b78f554667111999a28ff33800000000000000000000000000000000000000000000000000000000000001e4b63e800d000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008ecd4ec46d4d2a6b64fe960b3d64e8b94b2234eb0000000000000000000000000000000000000000000000000000000000000140000000000000000000000000a581c4a4db7175302464ff3c06380bc3270b40370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000ffac5578be8ac1b2b9d13b34caf4a074b96b8a1b00000000000000000000000000000000000000000000000000000000000000648d0dc49f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a581c4a4db7175302464ff3c06380bc3270b40370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000');
489
- });
490
- it('should allow to create a sponsored transaction', async () => {
491
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
492
- options: {
493
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
494
- },
495
- paymasterOptions: {
496
- isSponsored: true,
497
- paymasterUrl: fixtures.PAYMASTER_URL,
498
- paymasterAddress: fixtures.PAYMASTER_ADDRESS
499
- }
500
- });
501
- const sponsoredSafeOperation = await safe4337Pack.createTransaction({
502
- transactions: [transferUSDC]
503
- });
504
- expect(sponsoredSafeOperation).toBeInstanceOf(SafeOperation_1.default);
505
- expect(sponsoredSafeOperation.data).toMatchObject({
506
- safe: fixtures.SAFE_ADDRESS_v1_4_1,
507
- entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
508
- initCode: '0x',
509
- paymasterAndData: '0x0000000000325602a77416A16136FDafd04b299f',
510
- callData: constants.INTERFACES.encodeFunctionData('executeUserOp', [
511
- transferUSDC.to,
512
- transferUSDC.value,
513
- transferUSDC.data,
514
- safe_core_sdk_types_1.OperationType.Call
515
- ]),
516
- nonce: 1n,
517
- callGasLimit: 150000n,
518
- validAfter: 0,
519
- validUntil: 0,
520
- maxFeePerGas: 100000n,
521
- maxPriorityFeePerGas: 200000n,
522
- verificationGasLimit: 400000n,
523
- preVerificationGas: 100000n
524
- });
525
- });
526
- it('createTransaction should throw an error if paymasterUrl is not present in sponsored transactions', async () => {
527
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
528
- options: {
529
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
530
- },
531
- paymasterOptions: {
532
- isSponsored: true,
533
- paymasterAddress: fixtures.PAYMASTER_ADDRESS
534
- }
535
- });
536
- await expect(safe4337Pack.createTransaction({
537
- transactions: [transferUSDC]
538
- })).rejects.toThrow('No paymaster url provided for a sponsored transaction');
539
- });
540
- it('should add the approve transaction to the batch when amountToApprove is provided', async () => {
541
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
542
- options: {
543
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
544
- },
545
- paymasterOptions: {
546
- paymasterTokenAddress: fixtures.PAYMASTER_TOKEN_ADDRESS,
547
- paymasterAddress: fixtures.PAYMASTER_ADDRESS
548
- }
549
- });
550
- const amountToApprove = 80000n;
551
- const sponsoredSafeOperation = await safe4337Pack.createTransaction({
552
- transactions: [transferUSDC],
553
- options: {
554
- amountToApprove
555
- }
556
- });
557
- const approveTransaction = {
558
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
559
- data: constants.INTERFACES.encodeFunctionData('approve', [
560
- fixtures.PAYMASTER_ADDRESS,
561
- amountToApprove
562
- ]),
563
- value: '0',
564
- operation: safe_core_sdk_types_1.OperationType.Call // Call for approve
565
- };
566
- const batch = [transferUSDC, approveTransaction];
567
- expect(sponsoredSafeOperation).toBeInstanceOf(SafeOperation_1.default);
568
- expect(sponsoredSafeOperation.data).toMatchObject({
569
- safe: fixtures.SAFE_ADDRESS_v1_4_1,
570
- entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
571
- initCode: '0x',
572
- paymasterAndData: '0x0000000000325602a77416A16136FDafd04b299f',
573
- callData: constants.INTERFACES.encodeFunctionData('executeUserOp', [
574
- await safe4337Pack.protocolKit.getMultiSendAddress(),
575
- '0',
576
- constants.INTERFACES.encodeFunctionData('multiSend', [
577
- protocolKit.encodeMultiSendData(batch)
578
- ]),
579
- safe_core_sdk_types_1.OperationType.DelegateCall
580
- ]),
581
- nonce: 1n,
582
- callGasLimit: 150000n,
583
- validAfter: 0,
584
- validUntil: 0,
585
- maxFeePerGas: 100000n,
586
- maxPriorityFeePerGas: 200000n,
587
- verificationGasLimit: 400000n,
588
- preVerificationGas: 100000n
589
- });
590
- });
591
- });
592
- it('should allow to sign a SafeOperation', async () => {
593
- const transferUSDC = {
594
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
595
- data: (0, helpers_1.generateTransferCallData)(fixtures.SAFE_ADDRESS_v1_4_1, 100000n),
596
- value: '0',
597
- operation: 0
598
- };
599
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
600
- options: {
601
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
602
- }
603
- });
604
- const safeOperation = await safe4337Pack.createTransaction({
605
- transactions: [transferUSDC]
606
- });
607
- expect(await safe4337Pack.signSafeOperation(safeOperation)).toMatchObject({
608
- signatures: new Map().set(fixtures.OWNER_1.toLowerCase(), new protocolKit.EthSafeSignature(fixtures.OWNER_1, '0x40f892ea70b4981af8a4bfcedaf084033f3a6ba9baa79783c3ead7f40d2f042145e735d4c16162a0ee22b5c21631c82bbda9fd63454c496baf59159ab42c98d01f', false))
609
- });
610
- });
611
- it('should allow to sign a SafeOperation using a SafeOperationResponse object from the api to add a signature', async () => {
612
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
613
- options: {
614
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
615
- }
616
- });
617
- expect(await safe4337Pack.signSafeOperation(fixtures.SAFE_OPERATION_RESPONSE)).toMatchObject({
618
- signatures: new Map()
619
- .set(fixtures.OWNER_1.toLowerCase(), new protocolKit.EthSafeSignature(fixtures.OWNER_1, '0x975c7ddab3dc06240918a7bde0f543d1b082a8cadeca19d4bc13c30430367fac46c7ef923d9d0051423d1d59d106e5d199a734cd6a472276d54bb04ec7b3796520', false))
620
- .set(fixtures.OWNER_2.toLowerCase(), new protocolKit.EthSafeSignature(fixtures.OWNER_2, '0xcb28e74375889e400a4d8aca46b8c59e1cf8825e373c26fa99c2fd7c078080e64fe30eaf1125257bdfe0b358b5caef68aa0420478145f52decc8e74c979d43ab1d', false))
621
- });
622
- });
623
- it('should allow to send an UserOperation to a bundler', async () => {
624
- const transferUSDC = {
625
- to: fixtures.PAYMASTER_TOKEN_ADDRESS,
626
- data: (0, helpers_1.generateTransferCallData)(fixtures.SAFE_ADDRESS_v1_4_1, 100000n),
627
- value: '0',
628
- operation: 0
629
- };
630
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
631
- options: {
632
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
633
- }
634
- });
635
- let safeOperation = await safe4337Pack.createTransaction({
636
- transactions: [transferUSDC]
637
- });
638
- safeOperation = await safe4337Pack.signSafeOperation(safeOperation);
639
- await safe4337Pack.executeTransaction({ executable: safeOperation });
640
- expect(sendMock).toHaveBeenCalledWith(constants.RPC_4337_CALLS.SEND_USER_OPERATION, [
641
- utils.userOperationToHexValues(safeOperation.toUserOperation()),
642
- fixtures.ENTRYPOINTS[0]
643
- ]);
644
- });
645
- it('should allow to send a UserOperation to the bundler using a SafeOperationResponse object from the api', async () => {
646
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
647
- options: {
648
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
649
- }
650
- });
651
- await safe4337Pack.executeTransaction({ executable: fixtures.SAFE_OPERATION_RESPONSE });
652
- expect(sendMock).toHaveBeenCalledWith(constants.RPC_4337_CALLS.SEND_USER_OPERATION, [
653
- utils.userOperationToHexValues({
654
- sender: '0xE322e721bCe76cE7FCf3A475f139A9314571ad3D',
655
- nonce: '3',
656
- initCode: '0x',
657
- callData: '0x7bb37428000000000000000000000000e322e721bce76ce7fcf3a475f139a9314571ad3d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
658
- callGasLimit: 122497n,
659
- verificationGasLimit: 123498n,
660
- preVerificationGas: 50705n,
661
- maxFeePerGas: 105183831060n,
662
- maxPriorityFeePerGas: 1380000000n,
663
- paymasterAndData: '0x',
664
- signature: '0x000000000000000000000000cb28e74375889e400a4d8aca46b8c59e1cf8825e373c26fa99c2fd7c078080e64fe30eaf1125257bdfe0b358b5caef68aa0420478145f52decc8e74c979d43ab1d'
665
- }),
666
- fixtures.ENTRYPOINTS[0]
667
- ]);
668
- });
669
- it('should return a UserOperation based on a userOpHash', async () => {
670
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
671
- options: {
672
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
673
- }
674
- });
675
- const { userOperation, entryPoint, transactionHash, blockHash, blockNumber } = await safe4337Pack.getUserOperationByHash('0xee8e07f229d0ebf11c84a3e40f87e1d1b4c7b18eaeaebf3babb4b479424823e6');
676
- expect(userOperation).toMatchObject({
677
- sender: '0x1405B3659a11a16459fc27Fa1925b60388C38Ce1',
678
- nonce: '0x1',
679
- initCode: '0x',
680
- callData: '0x7bb3742800000000000000000000000038869bf66a61cf6bdb996a6ae40d5853fd43b52600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001848d80ff0a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000132001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000d725e11588f040d86c4c49d8236e32a5868549f000000000000000000000000000000000000000000000000000000000000186a0001c7d4b196cb0c7b01d743fbc6116a902379c723800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000d725e11588f040d86c4c49d8236e32a5868549f000000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
681
- callGasLimit: '0x1d7d0',
682
- verificationGasLimit: '0x14470',
683
- preVerificationGas: '0xbdb8',
684
- maxFeePerGas: '0x2d128cfa8c',
685
- maxPriorityFeePerGas: '0x52412100',
686
- paymasterAndData: '0x',
687
- signature: '0x000000000000000000000000a397ca32ee7fb5282256ee3465da0843485930b803d747516aac76e152f834051ac18fd2b3c0565590f9d65085538993c85c9bb189c940d15c15402c7c2885821b'
688
- });
689
- expect(blockHash).toBe('0x65f8249337ffede2067a006a96da47d3d3445ca72492a6a82afa02899f05d2e5');
690
- expect(blockNumber).toBe('0x5378b9');
691
- expect(entryPoint).toBe('0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789');
692
- expect(transactionHash).toBe('0xef262d20f68e4900aa6380b8ac0f66f9c00a7d988179fa177ad9c9758f0e380e');
693
- });
694
- it('should return a UserOperation receipt based on a userOpHash', async () => {
695
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
696
- options: {
697
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
698
- }
699
- });
700
- const userOperationReceipt = await safe4337Pack.getUserOperationReceipt('0xee8e07f229d0ebf11c84a3e40f87e1d1b4c7b18eaeaebf3babb4b479424823e6');
701
- expect(userOperationReceipt?.userOpHash).toBe('0x3cb881d1969036174f38d636d22108d1d032145518b53104fc0b1e1296d2cc9c');
702
- expect(userOperationReceipt?.sender).toBe('0x1405B3659a11a16459fc27Fa1925b60388C38Ce1');
703
- expect(userOperationReceipt?.actualGasUsed).toBe('0x27067');
704
- expect(userOperationReceipt?.actualGasCost).toBe('0x42f29418377167');
705
- expect(userOperationReceipt?.success).toBe(true);
706
- expect(userOperationReceipt?.logs).toStrictEqual([]);
707
- expect(userOperationReceipt?.receipt).toMatchObject({
708
- transactionHash: '0xef262d20f68e4900aa6380b8ac0f66f9c00a7d988179fa177ad9c9758f0e380e',
709
- transactionIndex: '0x63',
710
- blockHash: '0x65f8249337ffede2067a006a96da47d3d3445ca72492a6a82afa02899f05d2e5',
711
- blockNumber: '0x5378b9',
712
- from: '0x4337001Fff419768e088Ce247456c1B892888084',
713
- to: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
714
- cumulativeGasUsed: '0xc1a846',
715
- gasUsed: '0x25e6c',
716
- contractAddress: null,
717
- logs: [],
718
- logsBloom: '0x000000000000900000000000000000000000000000000000080000000002000000080000000000000402000100000000001000000000000080000200000100000000000000000000000000080000000000000000000000000000002000002000000000000a0000000000000000000800000000000000000000000010000200000000000060100000000000000040000000800000000000000008800000000000000000000000000000400000000000000200000000000000000002000000008000000002000100000001000000000000000000000020000000000000000020010040000000000020000010000008000200000000000000000000000000000000',
719
- status: '0x1',
720
- effectiveGasPrice: '0x1b67f3c201'
721
- });
722
- });
723
- it('should return an array of the entryPoint addresses supported by the client', async () => {
724
- const safe4337Pack = await (0, helpers_1.createSafe4337Pack)({
725
- options: {
726
- safeAddress: fixtures.SAFE_ADDRESS_v1_4_1
727
- }
728
- });
729
- const supportedEntryPoints = await safe4337Pack.getSupportedEntryPoints();
730
- expect(supportedEntryPoints).toContain('0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789');
731
- });
732
- });
733
- //# sourceMappingURL=Safe4337Pack.test.js.map