@dynamic-labs/ethereum-aa 4.11.2 → 4.12.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.
@@ -0,0 +1,526 @@
1
+ 'use client'
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ var _tslib = require('../_virtual/_tslib.cjs');
7
+ var viem = require('viem');
8
+ var zksync = require('viem/zksync');
9
+ var passkey = require('zksync-sso/client/passkey');
10
+ var utils$1 = require('zksync-sso/utils');
11
+ var client = require('zksync-sso/client');
12
+ var ecdsa = require('zksync-sso/client/ecdsa');
13
+ var ethereumCore = require('@dynamic-labs/ethereum-core');
14
+ var utils = require('@dynamic-labs/utils');
15
+ var AccountAbstractionConnector = require('./AccountAbstractionConnector.cjs');
16
+ require('./utils/logger.cjs');
17
+ require('@zerodev/sdk/accounts');
18
+ require('@zerodev/sdk/clients');
19
+ require('@zerodev/sdk/actions');
20
+ require('@zerodev/ecdsa-validator');
21
+ require('@zerodev/sdk/constants');
22
+ require('@zerodev/multi-chain-ecdsa-validator');
23
+ require('@dynamic-labs/sdk-api-core');
24
+ var getSalt = require('./utils/zksync/getSalt.cjs');
25
+ var passkeys = require('./utils/zksync/passkeys.cjs');
26
+ var deployment = require('./utils/zksync/deployment.cjs');
27
+ var network = require('./utils/zksync/network.cjs');
28
+
29
+ class ZKsyncConnector extends AccountAbstractionConnector.AccountAbstractionConnector {
30
+ constructor(props) {
31
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
32
+ super(props);
33
+ // Core wallet properties
34
+ this.ChainWallet = ethereumCore.EthereumWallet;
35
+ this.name = 'ZKsync';
36
+ this.overrideKey = 'zksync';
37
+ // Chain configuration
38
+ this.connectedChain = 'EVM';
39
+ this.evmNetworks = [];
40
+ this.supportedChains = ['ETH', 'EVM'];
41
+ this._isSmartAccountDeployed = false;
42
+ this.enablePasskeys = true;
43
+ this.enableEIP7702Mode = false;
44
+ this.walletFallback = {
45
+ brand: {
46
+ alt: 'Smart Wallet',
47
+ spriteId: 'smartwallet',
48
+ },
49
+ name: 'ZKsync',
50
+ };
51
+ this._walletUiUtils = props.walletUiUtils;
52
+ this.providersConfig = (_a = props.providersConfig) !== null && _a !== void 0 ? _a : {};
53
+ this.chainId = (_b = props.apiProviders.zksync) === null || _b === void 0 ? void 0 : _b.defaultChainId;
54
+ this.evmNetworks = utils.parseEvmNetworks(props.evmNetworks).filter((network) => network.chainId === this.chainId);
55
+ this.factoryAddress = (_c = props.apiProviders.zksync) === null || _c === void 0 ? void 0 : _c.factoryAddress; // Fallback if not provided
56
+ this.paymasterAddress = (_d = props.apiProviders.zksync) === null || _d === void 0 ? void 0 : _d.paymasterAddress; // Fallback if not provided
57
+ this.passkeyAddress = (_e = props.apiProviders.zksync) === null || _e === void 0 ? void 0 : _e.passkeyAddress; // Fallback if not provided
58
+ this.sessionKeyAddress = (_f = props.apiProviders.zksync) === null || _f === void 0 ? void 0 : _f.sessionAddress; // Fallback if not provided
59
+ this.saltText = (_g = props.apiProviders.zksync) === null || _g === void 0 ? void 0 : _g.salt;
60
+ this.enablePasskeys =
61
+ (_j = (_h = props.settings.sdk.accountAbstraction) === null || _h === void 0 ? void 0 : _h.enablePasskeys) !== null && _j !== void 0 ? _j : false;
62
+ }
63
+ getEnabledNetworks() {
64
+ return this.evmNetworks;
65
+ }
66
+ getConnectedAccounts() {
67
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
68
+ var _a;
69
+ yield ((_a = this.initializedDeferredPromise) === null || _a === void 0 ? void 0 : _a.promise);
70
+ if (!this.smartAccountAddress) {
71
+ throw new Error('Smart account address is not initialized');
72
+ }
73
+ return [this.smartAccountAddress];
74
+ });
75
+ }
76
+ getNetwork() {
77
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
78
+ var _a;
79
+ yield ((_a = this.initializedDeferredPromise) === null || _a === void 0 ? void 0 : _a.promise);
80
+ if (!this.smartAccountAddress) {
81
+ throw new Error('Smart account address is not initialized');
82
+ }
83
+ return this.chainId;
84
+ });
85
+ }
86
+ getPublicClient() {
87
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
88
+ return viem.createPublicClient({
89
+ chain: ethereumCore.getOrMapViemChain(this.evmNetworks[0]),
90
+ transport: viem.http(),
91
+ });
92
+ });
93
+ }
94
+ canSponsorTransactionGas() {
95
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
96
+ return false;
97
+ });
98
+ }
99
+ getAccountAbstractionProvider() {
100
+ return this.smartAccount;
101
+ }
102
+ getWalletClient(chainId) {
103
+ const smartAccountClient = this.smartAccount;
104
+ if (!smartAccountClient) {
105
+ return undefined;
106
+ }
107
+ const transport = this.getWalletClientTransport(smartAccountClient);
108
+ return viem.createWalletClient({
109
+ account: smartAccountClient.account,
110
+ chain: chainId ? ethereumCore.chainsMap[chainId] : smartAccountClient.chain,
111
+ transport,
112
+ });
113
+ }
114
+ getWalletClientTransport(smartAccountClient) {
115
+ return viem.custom({
116
+ request: (_a) => _tslib.__awaiter(this, [_a], void 0, function* ({ method, params, }) {
117
+ const handleRequest = () => _tslib.__awaiter(this, void 0, void 0, function* () {
118
+ var _b;
119
+ switch (method) {
120
+ case 'personal_sign':
121
+ // Delegate signing to the smart account client instance
122
+ return smartAccountClient.signMessage({ message: params[0] });
123
+ case 'eth_signTypedData_v4': {
124
+ // Delegate typed data signing to the smart account client instance
125
+ // Note: ZKsync usually expects JSON string for EIP712, but viem standard passes object.
126
+ // The smartAccountClient's signTypedData should handle the expected format internally.
127
+ const signTypedDataParams = params[1]; // Assuming standard viem format
128
+ return smartAccountClient.signTypedData(signTypedDataParams);
129
+ }
130
+ case 'eth_sendTransaction':
131
+ // Delegate transaction sending to the smart account client instance.
132
+ // This ensures deployment checks, paymaster logic, etc., are handled.
133
+ return smartAccountClient.sendTransaction(params[0]);
134
+ case 'eth_accounts':
135
+ case 'eth_requestAccounts':
136
+ // Return the smart account address
137
+ return ((_b = smartAccountClient.account) === null || _b === void 0 ? void 0 : _b.address)
138
+ ? [smartAccountClient.account.address]
139
+ : [];
140
+ case 'eth_chainId': {
141
+ // Return chainId from the smart account client
142
+ const chainId = yield smartAccountClient.getChainId();
143
+ return viem.toHex(chainId);
144
+ }
145
+ default: {
146
+ // For other read-only methods, delegate to a basic public client transport.
147
+ // Avoid creating a new client/transport on every call if possible,
148
+ // but for simplicity here, we create it transiently.
149
+ // Consider caching the provided client if performance becomes an issue.
150
+ return smartAccountClient.request({ method, params });
151
+ }
152
+ }
153
+ });
154
+ // The internal confirmationTransport used by smartAccountClient via its 'owner'
155
+ // should handle the UI prompts. We don't need to add extra prompts here.
156
+ return handleRequest();
157
+ }),
158
+ });
159
+ }
160
+ getTransport(provider) {
161
+ const transport = ethereumCore.confirmationTransport({
162
+ getAccounts: () => _tslib.__awaiter(this, void 0, void 0, function* () { return [provider.account.address]; }),
163
+ onPersonalSign: (_a) => _tslib.__awaiter(this, [_a], void 0, function* ({ message }) {
164
+ this._walletUiUtils.disabledConfirmationOnce();
165
+ if (!this._isSmartAccountDeployed) {
166
+ yield this.deploySmartAccount({
167
+ withPaymaster: true,
168
+ });
169
+ }
170
+ this._walletUiUtils.disabledConfirmationOnce();
171
+ return provider.signMessage({
172
+ message,
173
+ });
174
+ }),
175
+ onSignTypedData: (_b) => _tslib.__awaiter(this, [_b], void 0, function* ({ message }) {
176
+ this._walletUiUtils.disabledConfirmationOnce();
177
+ const signTypedData = JSON.parse(message);
178
+ if (!this._isSmartAccountDeployed) {
179
+ yield this.deploySmartAccount({
180
+ withPaymaster: true,
181
+ });
182
+ }
183
+ this._walletUiUtils.disabledConfirmationOnce();
184
+ return provider.signTypedData({
185
+ domain: signTypedData.domain,
186
+ message: signTypedData.message,
187
+ primaryType: signTypedData.primaryType,
188
+ types: signTypedData.types,
189
+ });
190
+ }),
191
+ provider: provider.extend(viem.publicActions),
192
+ transport: viem.custom(provider, this.providersConfig.httpTransportConfig),
193
+ walletConnector: this,
194
+ walletUiUtils: this._walletUiUtils,
195
+ });
196
+ return transport;
197
+ }
198
+ createEcdsaClient(eoaConnector) {
199
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
200
+ const eoaSigner = yield (eoaConnector === null || eoaConnector === void 0 ? void 0 : eoaConnector.getSigner());
201
+ if (!eoaSigner) {
202
+ throw new Error('EOA wallet client not found');
203
+ }
204
+ if (!this.smartAccountAddress) {
205
+ throw new Error('Smart account address is not initialized');
206
+ }
207
+ const transport = this.getTransport(eoaSigner);
208
+ const ownerWithTransport = viem.createWalletClient(Object.assign(Object.assign({}, eoaSigner), { transport }));
209
+ const walletClient = yield ecdsa.createZksyncEcdsaClient({
210
+ address: this.smartAccountAddress,
211
+ chain: ethereumCore.getOrMapViemChain(this.evmNetworks[0]),
212
+ contracts: {
213
+ accountFactory: this.factoryAddress,
214
+ session: this.sessionKeyAddress,
215
+ },
216
+ owner: ownerWithTransport,
217
+ transport: viem.custom(ownerWithTransport),
218
+ });
219
+ return walletClient;
220
+ });
221
+ }
222
+ /**
223
+ * Implementation of the interface method required by IAccountAbstractionWalletConnector
224
+ * @param eoaConnector - The EOA connector to use for signing transactions
225
+ * @param smartAccountAddress - The address of the smart account
226
+ */
227
+ initialize(_a) {
228
+ return _tslib.__awaiter(this, arguments, void 0, function* ({ eoaConnector, smartWalletAddress, }) {
229
+ this.initializedDeferredPromise = this.initializedDeferredPromise
230
+ ? this.initializedDeferredPromise
231
+ : new utils.DeferredPromise();
232
+ this.eoaConnector = eoaConnector;
233
+ this.eoaAddress = (yield eoaConnector.getAddress());
234
+ this.smartAccountAddress = smartWalletAddress;
235
+ this.smartAccount = (yield this.createEcdsaClient(eoaConnector));
236
+ yield this.checkIsDeployed();
237
+ this.initializedDeferredPromise.resolve();
238
+ });
239
+ }
240
+ createSessionClient(sessionKey, sessionConfig) {
241
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
242
+ if (!this.smartAccountAddress) {
243
+ throw new Error('Smart account address is not initialized');
244
+ }
245
+ if (!this.sessionKeyAddress) {
246
+ throw new Error('Session key address is not initialized');
247
+ }
248
+ return client.createZksyncSessionClient({
249
+ address: this.smartAccountAddress,
250
+ chain: ethereumCore.getOrMapViemChain(this.evmNetworks[0]),
251
+ contracts: {
252
+ session: this.sessionKeyAddress,
253
+ },
254
+ sessionConfig,
255
+ sessionKey,
256
+ transport: viem.http(),
257
+ });
258
+ });
259
+ }
260
+ /**
261
+ * Creates a passkey client for ZKsync authentication
262
+ *
263
+ * This method initializes a passkey client using stored credentials and
264
+ * configures it with the necessary contract addresses and chain information.
265
+ *
266
+ */
267
+ createPasskeyClient() {
268
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
269
+ if (!this.smartAccountAddress) {
270
+ throw new Error('Smart account address is not initialized');
271
+ }
272
+ if (!this.passkeyAddress) {
273
+ throw new Error('Passkey address is not initialized');
274
+ }
275
+ const storedCredentialPublicKey = utils.StorageService.getItem('credentialPublicKey');
276
+ if (!storedCredentialPublicKey) {
277
+ throw new Error('Credential public key not found');
278
+ }
279
+ const credentialPublicKey = new Uint8Array(Object.values(storedCredentialPublicKey));
280
+ const publicClient = yield this.getTypedPublicClient();
281
+ const { chain } = publicClient;
282
+ if (!chain) {
283
+ throw new Error('Chain information not available');
284
+ }
285
+ const contractAddresses = {
286
+ accountFactory: this.factoryAddress,
287
+ passkey: this.passkeyAddress,
288
+ session: this.sessionKeyAddress,
289
+ };
290
+ this.passkeyClient = passkey.createZksyncPasskeyClient({
291
+ address: this.smartAccountAddress,
292
+ chain,
293
+ contracts: contractAddresses,
294
+ credentialPublicKey,
295
+ transport: viem.http(),
296
+ userDisplayName: this.smartAccountAddress,
297
+ userName: this.smartAccountAddress,
298
+ });
299
+ });
300
+ }
301
+ registerPasskeyAndDeploy() {
302
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
303
+ if (!this.smartAccountAddress) {
304
+ throw new Error('Smart account address is not initialized');
305
+ }
306
+ const { credentialPublicKey, credentialId } = yield passkey.registerNewPasskey({
307
+ attestationType: 'none',
308
+ authenticatorSelection: {
309
+ authenticatorAttachment: 'platform',
310
+ requireResidentKey: false,
311
+ userVerification: 'required',
312
+ },
313
+ userDisplayName: this.smartAccountAddress,
314
+ userName: this.smartAccountAddress,
315
+ });
316
+ utils.StorageService.setItem('credentialPublicKey', credentialPublicKey);
317
+ utils.StorageService.setItem('credentialId', credentialId);
318
+ yield this.deploySmartAccount({
319
+ withPaymaster: true,
320
+ });
321
+ });
322
+ }
323
+ deploySmartAccount(_a) {
324
+ return _tslib.__awaiter(this, arguments, void 0, function* ({ withPaymaster, }) {
325
+ if (!this.chainId) {
326
+ throw new Error('Chain ID is not initialized');
327
+ }
328
+ if (!this.eoaConnector) {
329
+ throw new Error('EOA connector is not initialized');
330
+ }
331
+ yield network.ensureEoaConnectorNetwork(this.eoaConnector, this.chainId);
332
+ if (withPaymaster && this.paymasterAddress) {
333
+ yield this.deploySmartAccountWithPaymaster();
334
+ }
335
+ else {
336
+ yield this.deploySmartAccountWithoutPaymaster();
337
+ }
338
+ });
339
+ }
340
+ deploySmartAccountWithPaymaster() {
341
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
342
+ if (!this.paymasterAddress) {
343
+ throw new Error('Paymaster address is not initialized');
344
+ }
345
+ if (!this.smartAccountAddress) {
346
+ throw new Error('Smart account address is not initialized');
347
+ }
348
+ if (!this.eoaAddress) {
349
+ throw new Error('EOA address is not initialized');
350
+ }
351
+ const paymaster = this.paymasterAddress;
352
+ const publicClient = yield this.getTypedPublicClient();
353
+ const walletClient = yield this.getTypedWalletClient();
354
+ const deploymentParams = yield deployment.getDeploymentParameters({
355
+ enablePasskeys: this.enablePasskeys,
356
+ eoaAddress: this.eoaAddress,
357
+ passkeyAddress: this.passkeyAddress,
358
+ saltText: this.saltText,
359
+ sessionKeyAddress: this.sessionKeyAddress,
360
+ });
361
+ const { request } = yield deployment.simulateAccountDeployment(publicClient, walletClient, deploymentParams, this.factoryAddress, false);
362
+ const paymasterInput = zksync.getGeneralPaymasterInput({
363
+ innerInput: new Uint8Array(),
364
+ });
365
+ const requestWithPaymaster = Object.assign(Object.assign({}, request), { paymaster,
366
+ paymasterInput });
367
+ yield deployment.executeAccountDeployment(walletClient, publicClient, requestWithPaymaster, this.smartAccountAddress);
368
+ this._isSmartAccountDeployed = true;
369
+ });
370
+ }
371
+ /**
372
+ * Deploys a new smart account contract
373
+ * @throws {ZkSyncConnectorError} If deployment fails
374
+ * @testable This method can be mocked in tests
375
+ */
376
+ deploySmartAccountWithoutPaymaster() {
377
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
378
+ if (!this.eoaConnector) {
379
+ throw new Error('EOA connector is not initialized');
380
+ }
381
+ if (!this.smartAccountAddress) {
382
+ throw new Error('Smart account address is not initialized');
383
+ }
384
+ const walletClient = yield this.getTypedWalletClient();
385
+ const publicClient = yield this.getTypedPublicClient();
386
+ const deploymentParams = yield this.getDeploymentParameters();
387
+ // Simulate the contract deployment first
388
+ const { request } = yield deployment.simulateAccountDeployment(publicClient, walletClient, deploymentParams, this.factoryAddress, false);
389
+ // Execute the deployment transaction
390
+ yield deployment.executeAccountDeployment(walletClient, publicClient, request, this.smartAccountAddress);
391
+ });
392
+ }
393
+ /**
394
+ * Gets typed public client from EOA connector
395
+ * @throws {ZkSyncConnectorError} If public client cannot be obtained
396
+ * @testable This method can be mocked in tests
397
+ */
398
+ getTypedPublicClient() {
399
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
400
+ if (!this.eoaConnector) {
401
+ throw new Error('EOA connector is not initialized');
402
+ }
403
+ const publicClient = yield this.eoaConnector.getPublicClient();
404
+ if (!publicClient) {
405
+ throw new Error('Failed to get public client from EOA connector');
406
+ }
407
+ return publicClient;
408
+ });
409
+ }
410
+ /**
411
+ * Gets typed wallet client from EOA connector on the correct chain
412
+ * @throws {ZkSyncConnectorError} If wallet client cannot be obtained
413
+ * @testable This method can be mocked in tests
414
+ */
415
+ getTypedWalletClient() {
416
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
417
+ if (!this.eoaConnector) {
418
+ throw new Error('EOA connector is not initialized');
419
+ }
420
+ const walletClient = yield this.eoaConnector.getWalletClient();
421
+ if (!walletClient) {
422
+ throw new Error('Failed to get wallet client from EOA connector');
423
+ }
424
+ return walletClient
425
+ .extend(viem.publicActions)
426
+ .extend(viem.walletActions)
427
+ .extend(zksync.eip712WalletActions())
428
+ .extend(ecdsa.zksyncSsoEcdsaWalletActions);
429
+ });
430
+ }
431
+ /**
432
+ * Returns the parameters needed for deploying the smart account
433
+ * @returns Deployment parameters object
434
+ * @testable This method can be overridden in tests
435
+ */
436
+ getDeploymentParameters() {
437
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
438
+ if (!this.eoaAddress) {
439
+ throw new Error('EOA address is not initialized');
440
+ }
441
+ const initialValidators = [];
442
+ const initialK1Owners = [this.eoaAddress];
443
+ // TODO: revisit this logic, maybe we should always add the passkey module data,
444
+ // otherwise if people active passkeys later, old accounts will not be able to use passkey module
445
+ if (this.enablePasskeys && this.passkeyAddress) {
446
+ const encodedPasskeyModuleData = yield passkeys.getEncodedPasskeyModuleData({
447
+ passkeyAddress: this.passkeyAddress,
448
+ });
449
+ initialValidators.push(encodedPasskeyModuleData);
450
+ }
451
+ if (this.sessionKeyAddress) {
452
+ const encodedSessionKeyModuleData = utils$1.encodeModuleData({
453
+ address: this.sessionKeyAddress,
454
+ parameters: '0x',
455
+ });
456
+ initialValidators.push(encodedSessionKeyModuleData);
457
+ }
458
+ return {
459
+ initialK1Owners,
460
+ initialValidators,
461
+ salt: getSalt.getSalt(this.saltText),
462
+ };
463
+ });
464
+ }
465
+ signMessage(message) {
466
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
467
+ const walletClient = this.getWalletClient();
468
+ if (!walletClient) {
469
+ throw new Error('Error fetching signer');
470
+ }
471
+ return walletClient.signMessage({
472
+ account: walletClient.account,
473
+ message,
474
+ });
475
+ });
476
+ }
477
+ /**
478
+ * Checks if a smart account is deployed
479
+ * @throws {ZkSyncConnectorError} If the check fails
480
+ * @returns Promise that resolves when the check is complete
481
+ */
482
+ checkIsDeployed() {
483
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
484
+ if (!this.smartAccountAddress) {
485
+ throw new Error('Smart account address is not initialized');
486
+ }
487
+ const client = yield this.getPublicClient();
488
+ if (!client) {
489
+ throw new Error('Failed to get public client from EOA connector');
490
+ }
491
+ const code = yield client.getCode({
492
+ address: this.smartAccountAddress,
493
+ });
494
+ const isDeployed = Boolean(code);
495
+ this._isSmartAccountDeployed = isDeployed;
496
+ });
497
+ }
498
+ getAddress() {
499
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
500
+ var _a;
501
+ yield ((_a = this.initializedDeferredPromise) === null || _a === void 0 ? void 0 : _a.promise);
502
+ if (!this.smartAccount) {
503
+ throw new Error('Smart account is not initialized');
504
+ }
505
+ return this.smartAccountAddress;
506
+ });
507
+ }
508
+ validateActiveWallet(expectedAddress) {
509
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
510
+ const currentAddress = yield this.getAddress();
511
+ if (currentAddress !== expectedAddress) {
512
+ throw new utils.DynamicError('Invalid active wallet');
513
+ }
514
+ });
515
+ }
516
+ isSmartAccountDeployed() {
517
+ return _tslib.__awaiter(this, arguments, void 0, function* (refetch = false) {
518
+ if (refetch) {
519
+ yield this.checkIsDeployed();
520
+ }
521
+ return this._isSmartAccountDeployed;
522
+ });
523
+ }
524
+ }
525
+
526
+ exports.ZKsyncConnector = ZKsyncConnector;