@wagmi/core 3.4.1 → 3.4.3

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,478 +1,50 @@
1
- import * as Address from 'ox/Address';
2
- import * as PublicKey from 'ox/PublicKey';
3
- import { KeyAuthorization, SignatureEnvelope } from 'ox/tempo';
4
- import { createClient, defineChain, getAddress, SwitchChainError, } from 'viem';
5
- import { generatePrivateKey, privateKeyToAccount, } from 'viem/accounts';
6
- import { Account, WebAuthnP256, WebCryptoP256, walletNamespaceCompat, } from 'viem/tempo';
1
+ import { numberToHex, SwitchChainError, UserRejectedRequestError, withRetry, } from 'viem';
7
2
  import { createConnector } from '../connectors/createConnector.js';
8
3
  import { ChainNotConfiguredError } from '../errors/config.js';
9
- webAuthn.type = 'webAuthn';
4
+ const tempoWalletIcon = 'data:image/svg+xml,<svg width="269" height="269" viewBox="0 0 269 269" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="269" height="269" fill="black"/><path d="M123.273 190.794H93.445L121.09 105.318H85.7334L93.445 80.2642H191.95L184.238 105.318H150.773L123.273 190.794Z" fill="white"/></svg>';
10
5
  /**
11
- * Connector for a WebAuthn EOA.
6
+ * Connector for the Tempo Wallet dialog.
12
7
  */
13
- export function webAuthn(options) {
14
- let account;
15
- let accessKey;
16
- const defaultAccessKeyOptions = {
17
- expiry: Math.floor((Date.now() + 24 * 60 * 60 * 1000) / 1000),
18
- strict: false,
19
- };
20
- const accessKeyOptions = (() => {
21
- if (typeof options.grantAccessKey === 'object')
22
- return { ...defaultAccessKeyOptions, ...options.grantAccessKey };
23
- if (options.grantAccessKey === true)
24
- return defaultAccessKeyOptions;
25
- return undefined;
26
- })();
27
- return createConnector((config) => ({
28
- id: 'webAuthn',
29
- name: 'EOA (WebAuthn)',
30
- type: 'webAuthn',
31
- async setup() {
32
- const credential = await config.storage?.getItem('webAuthn.activeCredential');
33
- if (!credential)
34
- return;
35
- account = Account.fromWebAuthnP256(credential, {
36
- rpId: options.getOptions?.rpId ?? options.rpId,
37
- });
38
- },
39
- async connect(parameters = {}) {
40
- const capabilities = 'capabilities' in parameters ? (parameters.capabilities ?? {}) : {};
41
- const signHash = 'sign' in capabilities ? capabilities.sign?.hash : undefined;
42
- // Fast path: if a credential is provided directly, use it.
43
- if ('credential' in capabilities && capabilities.credential) {
44
- const credential = capabilities.credential;
45
- config.storage?.setItem('webAuthn.activeCredential', normalizeValue(credential));
46
- config.storage?.setItem('webAuthn.lastActiveCredential', normalizeValue(credential));
47
- account = Account.fromWebAuthnP256(credential, {
48
- rpId: options.getOptions?.rpId ?? options.rpId,
49
- });
50
- const address = getAddress(account.address);
51
- const chainId = parameters.chainId ?? config.chains[0]?.id;
52
- if (!chainId)
53
- throw new ChainNotConfiguredError();
54
- return {
55
- accounts: (parameters.withCapabilities
56
- ? [{ address }]
57
- : [address]),
58
- chainId,
59
- };
60
- }
61
- if (accessKeyOptions?.strict &&
62
- accessKeyOptions.expiry &&
63
- accessKeyOptions.expiry < Date.now() / 1000)
64
- throw new Error(`\`grantAccessKey.expiry = ${accessKeyOptions.expiry}\` is in the past (${new Date(accessKeyOptions.expiry * 1000).toLocaleString()}). Please provide a valid expiry.`);
65
- // We are going to need to find:
66
- // - a WebAuthn `credential` to instantiate an account
67
- // - optionally, a `keyPair` to use as the access key for the account
68
- // - optionally, a signed `keyAuthorization` to provision the access key
69
- const { credential, keyAuthorization, keyPair, signature: signedHash, } = await (async () => {
70
- // If the connection type is of "sign-up", we are going to create a new credential
71
- // and provision an access key (if needed).
72
- if (capabilities.type === 'sign-up') {
73
- // Create credential (sign up)
74
- const createOptions_remote = await options.keyManager.getChallenge?.();
75
- const label = capabilities.label ??
76
- options.createOptions?.label ??
77
- new Date().toISOString();
78
- const rpId = createOptions_remote?.rp?.id ??
79
- options.createOptions?.rpId ??
80
- options.rpId;
81
- const credential = await WebAuthnP256.createCredential({
82
- ...(options.createOptions ?? {}),
83
- label,
84
- rpId,
85
- ...(createOptions_remote ?? {}),
86
- });
87
- await options.keyManager.setPublicKey({
88
- credential: credential.raw,
89
- publicKey: credential.publicKey,
90
- });
91
- // Get key pair (access key) to use for the account.
92
- // Skip if signing a hash — access key provisioning is deferred.
93
- const keyPair = await (async () => {
94
- if (signHash)
95
- return undefined;
96
- if (!accessKeyOptions)
97
- return undefined;
98
- return await WebCryptoP256.createKeyPair();
99
- })();
100
- return { credential, keyPair, signature: undefined };
101
- }
102
- // If we are not selecting an account, we will check if an active credential is present in
103
- // storage and if so, we will use it to instantiate an account.
104
- if (!capabilities.selectAccount) {
105
- const credential = (await config.storage?.getItem('webAuthn.activeCredential'));
106
- if (credential) {
107
- // If signing a hash, skip local keypair checks and return
108
- // the stored credential — the hash will be signed via
109
- // `account.sign` since `createCredential` cannot sign.
110
- if (signHash)
111
- return { credential, keyPair: undefined, signature: undefined };
112
- // Get key pair (access key) to use for the account.
113
- const keyPair = await (async () => {
114
- if (!accessKeyOptions)
115
- return undefined;
116
- const address = Address.fromPublicKey(PublicKey.fromHex(credential.publicKey));
117
- return await idb.get(`accessKey:${address}`);
118
- })();
119
- // If the access key provisioning is not in strict mode, return the credential and key pair (if exists).
120
- if (!accessKeyOptions?.strict)
121
- return { credential, keyPair, signature: undefined };
122
- // If a key pair is found, return the credential and key pair.
123
- if (keyPair)
124
- return { credential, keyPair, signature: undefined };
125
- // If we are reconnecting, throw an error if not found.
126
- if (parameters.isReconnecting)
127
- throw new Error('credential not found.');
128
- // Otherwise, we want to continue to sign up or register against new key pair.
129
- }
130
- }
131
- // Discover credential
132
- {
133
- // Get key pair (access key) to use for the account.
134
- // Skip if signing a hash — access key provisioning is deferred.
135
- const keyPair = await (async () => {
136
- if (signHash)
137
- return undefined;
138
- if (!accessKeyOptions)
139
- return undefined;
140
- return await WebCryptoP256.createKeyPair();
141
- })();
142
- // If we are provisioning an access key, we will need to sign a key authorization.
143
- // We will need the hash (digest) to sign, and the address of the access key to construct the key authorization.
144
- const { hash, keyAuthorization_unsigned } = await (async () => {
145
- const accessKeyAddress = keyPair
146
- ? Address.fromPublicKey(keyPair.publicKey)
147
- : undefined;
148
- if (!accessKeyAddress)
149
- return { keyAuthorization_unsigned: undefined, hash: undefined };
150
- const chainId = parameters.chainId ?? config.chains[0]?.id;
151
- const keyAuthorization_unsigned = KeyAuthorization.from({
152
- address: accessKeyAddress,
153
- chainId: chainId ? BigInt(chainId) : undefined,
154
- expiry: accessKeyOptions?.expiry,
155
- strict: accessKeyOptions?.strict ?? false,
156
- type: 'p256',
157
- });
158
- const hash = KeyAuthorization.getSignPayload(keyAuthorization_unsigned);
159
- return { keyAuthorization_unsigned, hash };
160
- })();
161
- // If no active credential, we will attempt to load the last active credential from storage.
162
- const lastActiveCredential = !capabilities.selectAccount
163
- ? await config.storage?.getItem('webAuthn.lastActiveCredential')
164
- : undefined;
165
- const credential = await WebAuthnP256.getCredential({
166
- ...(options.getOptions ?? {}),
167
- credentialId: lastActiveCredential?.id,
168
- async getPublicKey(credential) {
169
- const publicKey = await options.keyManager.getPublicKey({
170
- credential,
171
- });
172
- if (!publicKey)
173
- throw new Error('publicKey not found.');
174
- return publicKey;
175
- },
176
- hash: signHash ?? hash,
177
- rpId: options.getOptions?.rpId ?? options.rpId,
178
- });
179
- const envelope = SignatureEnvelope.from({
180
- metadata: credential.metadata,
181
- signature: credential.signature,
182
- publicKey: PublicKey.fromHex(credential.publicKey),
183
- type: 'webAuthn',
184
- });
185
- const keyAuthorization = keyAuthorization_unsigned
186
- ? KeyAuthorization.from({
187
- ...keyAuthorization_unsigned,
188
- signature: envelope,
189
- })
190
- : undefined;
191
- const signature = signHash && !keyAuthorization_unsigned
192
- ? SignatureEnvelope.serialize(envelope)
193
- : undefined;
194
- return { credential, keyAuthorization, keyPair, signature };
195
- }
196
- })();
197
- config.storage?.setItem('webAuthn.lastActiveCredential', normalizeValue(credential));
198
- config.storage?.setItem('webAuthn.activeCredential', normalizeValue(credential));
199
- account = Account.fromWebAuthnP256(credential, {
200
- rpId: options.getOptions?.rpId ?? options.rpId,
201
- });
202
- let signature;
203
- if (signHash && !signedHash) {
204
- signature = await account.sign({ hash: signHash });
205
- }
206
- else if (signedHash) {
207
- signature = signedHash;
208
- }
209
- else if (keyPair) {
210
- accessKey = Account.fromWebCryptoP256(keyPair, {
211
- access: account,
212
- });
213
- // If we are reconnecting, check if the access key is expired.
214
- if (parameters.isReconnecting) {
215
- if ('keyAuthorization' in keyPair &&
216
- keyPair.keyAuthorization.expiry &&
217
- keyPair.keyAuthorization.expiry < Date.now() / 1000) {
218
- // remove any pending key authorizations from storage.
219
- await config?.storage?.removeItem(`pendingKeyAuthorization:${account.address.toLowerCase()}`);
220
- const message = `Access key expired (on ${new Date(keyPair.keyAuthorization.expiry * 1000).toLocaleString()}).`;
221
- accessKey = undefined;
222
- // if in strict mode, disconnect and throw an error.
223
- if (accessKeyOptions?.strict) {
224
- await this.disconnect();
225
- throw new Error(message);
226
- }
227
- // otherwise, fall back to the root account.
228
- // biome-ignore lint/suspicious/noConsole: notify
229
- console.warn(`${message} Falling back to passkey.`);
230
- }
231
- }
232
- // If we are not reconnecting, orchestrate the provisioning of the access key.
233
- else {
234
- const keyAuth = keyAuthorization ??
235
- (await account.signKeyAuthorization(accessKey, {
236
- ...accessKeyOptions,
237
- chainId: BigInt(parameters.chainId ?? config.chains[0]?.id ?? 0),
238
- }));
239
- await config?.storage?.setItem(`pendingKeyAuthorization:${account.address.toLowerCase()}`, keyAuth);
240
- await idb.set(`accessKey:${account.address.toLowerCase()}`, {
241
- ...keyPair,
242
- keyAuthorization: keyAuth,
243
- });
244
- }
245
- // If we are granting an access key and it is in strict mode, throw an error if the access key is not provisioned.
246
- }
247
- else if (accessKeyOptions?.strict) {
248
- await config.storage?.removeItem('webAuthn.activeCredential');
249
- throw new Error('access key not found');
250
- }
251
- const address = getAddress(account.address);
252
- const chainId = parameters.chainId ?? config.chains[0]?.id;
253
- if (!chainId)
254
- throw new ChainNotConfiguredError();
255
- return {
256
- accounts: (parameters.withCapabilities
257
- ? [{ address, capabilities: { signature } }]
258
- : [address]),
259
- chainId,
260
- };
261
- },
262
- async disconnect() {
263
- await config.storage?.removeItem('webAuthn.activeCredential');
264
- config.emitter.emit('disconnect');
265
- account = undefined;
266
- },
267
- async getAccounts() {
268
- if (!account)
269
- return [];
270
- return [getAddress(account.address)];
271
- },
272
- async getChainId() {
273
- return config.chains[0]?.id;
274
- },
275
- async isAuthorized() {
276
- try {
277
- const accounts = await this.getAccounts();
278
- return !!accounts.length;
279
- }
280
- catch (error) {
281
- // biome-ignore lint/suspicious/noConsole: notify
282
- console.error('Connector.webAuthn: Failed to check authorization', error);
283
- return false;
284
- }
285
- },
286
- async switchChain({ chainId }) {
287
- const chain = config.chains.find((chain) => chain.id === chainId);
288
- if (!chain)
289
- throw new SwitchChainError(new ChainNotConfiguredError());
290
- return chain;
291
- },
292
- onAccountsChanged() { },
293
- onChainChanged(chain) {
294
- const chainId = Number(chain);
295
- config.emitter.emit('change', { chainId });
296
- },
297
- async onDisconnect() {
298
- config.emitter.emit('disconnect');
299
- account = undefined;
300
- },
301
- async getClient({ chainId } = {}) {
302
- const chain = config.chains.find((x) => x.id === chainId) ?? config.chains[0];
303
- if (!chain)
304
- throw new ChainNotConfiguredError();
305
- const transports = config.transports;
306
- if (!transports)
307
- throw new ChainNotConfiguredError();
308
- const transport = transports[chain.id];
309
- if (!transport)
310
- throw new ChainNotConfiguredError();
311
- const targetAccount = await (async () => {
312
- if (!accessKey)
313
- return account;
314
- if (!account)
315
- throw new Error('account not found.');
316
- const item = await idb.get(`accessKey:${account.address.toLowerCase()}`);
317
- if (item?.keyAuthorization.expiry &&
318
- item.keyAuthorization.expiry < Date.now() / 1000) {
319
- // remove any pending key authorizations from storage.
320
- await config?.storage?.removeItem(`pendingKeyAuthorization:${account.address.toLowerCase()}`);
321
- const message = `Access key expired (on ${new Date(item.keyAuthorization.expiry * 1000).toLocaleString()}).`;
322
- // if in strict mode, disconnect and throw an error.
323
- if (accessKeyOptions?.strict) {
324
- await this.disconnect();
325
- throw new Error(message);
326
- }
327
- // otherwise, fall back to the root account.
328
- // biome-ignore lint/suspicious/noConsole: notify
329
- console.warn(`${message} Falling back to passkey.`);
330
- return account;
331
- }
332
- return accessKey;
333
- })();
334
- if (!targetAccount)
335
- throw new Error('account not found.');
336
- const targetChain = defineChain({
337
- ...chain,
338
- prepareTransactionRequest: [
339
- async (args, { phase }) => {
340
- const keyAuthorization = await (async () => {
341
- {
342
- const keyAuthorization = args.keyAuthorization;
343
- if (keyAuthorization)
344
- return keyAuthorization;
345
- }
346
- const keyAuthorization = await config.storage?.getItem(`pendingKeyAuthorization:${targetAccount?.address.toLowerCase()}`);
347
- await config.storage?.removeItem(`pendingKeyAuthorization:${targetAccount?.address.toLowerCase()}`);
348
- return keyAuthorization;
349
- })();
350
- const [prepareTransactionRequestFn, options] = (() => {
351
- if (!chain.prepareTransactionRequest)
352
- return [undefined, undefined];
353
- if (typeof chain.prepareTransactionRequest === 'function')
354
- return [chain.prepareTransactionRequest, undefined];
355
- return chain.prepareTransactionRequest;
356
- })();
357
- const request = await (async () => {
358
- if (!prepareTransactionRequestFn)
359
- return {};
360
- if (!options || options?.runAt?.includes(phase))
361
- return await prepareTransactionRequestFn(args, { phase });
362
- return {};
363
- })();
364
- return {
365
- ...args,
366
- ...request,
367
- keyAuthorization,
368
- };
369
- },
370
- {
371
- runAt: [
372
- 'afterFillParameters',
373
- 'beforeFillParameters',
374
- 'beforeFillTransaction',
375
- ],
376
- },
377
- ],
8
+ export function tempoWallet(parameters = {}) {
9
+ const { dialog: dialogOption, host, icon = tempoWalletIcon, name, rdns, ...providerParameters } = parameters;
10
+ return _setup({
11
+ createAdapter(accounts) {
12
+ return accounts.dialog({
13
+ dialog: dialogOption,
14
+ host,
15
+ icon,
16
+ name,
17
+ rdns,
378
18
  });
379
- return createClient({
380
- account: targetAccount,
381
- chain: targetChain,
382
- transport: walletNamespaceCompat(transport, {
383
- account: targetAccount,
384
- }),
385
- });
386
- },
387
- async getProvider({ chainId } = {}) {
388
- const { request } = await this.getClient({ chainId });
389
- return { request };
390
19
  },
391
- }));
20
+ icon,
21
+ id: rdns ?? 'xyz.tempo',
22
+ name: name ?? 'Tempo Wallet',
23
+ providerParameters,
24
+ rdns: rdns ?? 'xyz.tempo',
25
+ type: 'injected',
26
+ });
392
27
  }
28
+ webAuthn.type = 'webAuthn';
393
29
  /**
394
- * Normalizes a value into a structured-clone compatible format.
395
- *
396
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone
30
+ * Connector for a WebAuthn EOA.
397
31
  */
398
- function normalizeValue(value) {
399
- if (Array.isArray(value))
400
- return value.map(normalizeValue);
401
- if (typeof value === 'function')
402
- return undefined;
403
- if (typeof value !== 'object' || value === null)
404
- return value;
405
- if (Object.getPrototypeOf(value) !== Object.prototype)
406
- try {
407
- return structuredClone(value);
408
- }
409
- catch {
410
- return undefined;
411
- }
412
- const normalized = {};
413
- for (const [k, v] of Object.entries(value))
414
- normalized[k] = normalizeValue(v);
415
- return normalized;
32
+ export function webAuthn(parameters = {}) {
33
+ const { authUrl, ceremony, icon, name, rdns, ...providerParameters } = parameters;
34
+ return _setup({
35
+ createAdapter(accounts) {
36
+ return ceremony
37
+ ? accounts.webAuthn({ ceremony, icon, name, rdns })
38
+ : accounts.webAuthn({ authUrl, icon, name, rdns });
39
+ },
40
+ icon,
41
+ id: 'webAuthn',
42
+ name: name ?? 'EOA (WebAuthn)',
43
+ providerParameters,
44
+ rdns,
45
+ type: 'webAuthn',
46
+ });
416
47
  }
417
- // Based on `idb-keyval`
418
- // https://github.com/jakearchibald/idb-keyval
419
- let defaultGetStoreFunc;
420
- const idb = {
421
- /**
422
- * Get a value by its key.
423
- *
424
- * @param key
425
- * @param customStore Method to get a custom store. Use with caution (see the docs).
426
- */
427
- get(key) {
428
- return this.defaultGetStore()('readonly', (store) => this.promisifyRequest(store.get(key)));
429
- },
430
- /**
431
- * Set a value with a key.
432
- *
433
- * @param key
434
- * @param value
435
- * @param customStore Method to get a custom store. Use with caution (see the docs).
436
- */
437
- set(key, value) {
438
- return this.defaultGetStore()('readwrite', (store) => {
439
- store.put(value, key);
440
- return this.promisifyRequest(store.transaction);
441
- });
442
- },
443
- defaultGetStore() {
444
- if (!defaultGetStoreFunc)
445
- defaultGetStoreFunc = this.createStore('keyval-store', 'keyval');
446
- return defaultGetStoreFunc;
447
- },
448
- createStore(dbName, storeName) {
449
- let dbp;
450
- const getDB = () => {
451
- if (dbp)
452
- return dbp;
453
- const request = indexedDB.open(dbName);
454
- request.onupgradeneeded = () => request.result.createObjectStore(storeName);
455
- dbp = this.promisifyRequest(request);
456
- dbp.then((db) => {
457
- // It seems like Safari sometimes likes to just close the connection.
458
- // It's supposed to fire this event when that happens. Let's hope it does!
459
- db.onclose = () => {
460
- dbp = undefined;
461
- };
462
- }, () => { });
463
- return dbp;
464
- };
465
- return (txMode, callback) => getDB().then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName)));
466
- },
467
- promisifyRequest(request) {
468
- return new Promise((resolve, reject) => {
469
- // @ts-ignore - file size hacks
470
- request.oncomplete = request.onsuccess = () => resolve(request.result);
471
- // @ts-ignore - file size hacks
472
- request.onabort = request.onerror = () => reject(request.error);
473
- });
474
- },
475
- };
476
48
  dangerous_secp256k1.type = 'dangerous_secp256k1';
477
49
  /**
478
50
  * Connector for a Secp256k1 EOA.
@@ -481,119 +53,255 @@ dangerous_secp256k1.type = 'dangerous_secp256k1';
481
53
  * This connector stores private keys in clear text, and are bound to the session
482
54
  * length of the storage used.
483
55
  */
484
- export function dangerous_secp256k1(options = {}) {
485
- let account;
486
- return createConnector((config) => ({
56
+ export function dangerous_secp256k1(parameters = {}) {
57
+ const { icon, name, privateKey, rdns, ...providerParameters } = parameters;
58
+ return _setup({
59
+ createAdapter(accounts) {
60
+ return accounts.dangerous_secp256k1({ icon, name, privateKey, rdns });
61
+ },
62
+ icon,
487
63
  id: 'secp256k1',
488
- name: 'EOA (Secp256k1)',
64
+ name: name ?? 'EOA (Secp256k1)',
65
+ providerParameters,
66
+ rdns,
489
67
  type: 'secp256k1',
490
- async setup() {
491
- const address = await config.storage?.getItem('secp256k1.activeAddress');
492
- const privateKey = await config.storage?.getItem(`secp256k1.${address}.privateKey`);
493
- if (privateKey)
494
- account = privateKeyToAccount(privateKey);
495
- else if (address &&
496
- options.account &&
497
- Address.isEqual(address, options.account.address))
498
- account = options.account;
499
- },
500
- async connect(parameters = {}) {
501
- const address = await (async () => {
502
- if ('capabilities' in parameters &&
503
- parameters.capabilities?.type === 'sign-up') {
504
- const privateKey = generatePrivateKey();
505
- const account = privateKeyToAccount(privateKey);
506
- const address = account.address;
507
- await config.storage?.setItem(`secp256k1.${address}.privateKey`, privateKey);
508
- await config.storage?.setItem('secp256k1.activeAddress', address);
509
- await config.storage?.setItem('secp256k1.lastActiveAddress', address);
510
- return address;
511
- }
512
- const address = await config.storage?.getItem('secp256k1.lastActiveAddress');
513
- const privateKey = await config.storage?.getItem(`secp256k1.${address}.privateKey`);
514
- if (privateKey)
515
- account = privateKeyToAccount(privateKey);
516
- else if (options.account) {
517
- account = options.account;
518
- await config.storage?.setItem('secp256k1.lastActiveAddress', account.address);
519
- }
520
- if (!account)
521
- throw new Error('account not found.');
522
- await config.storage?.setItem('secp256k1.activeAddress', account.address);
523
- return account.address;
524
- })();
525
- const chainId = parameters.chainId ?? config.chains[0]?.id;
526
- if (!chainId)
527
- throw new ChainNotConfiguredError();
528
- return {
529
- accounts: (parameters.withCapabilities
530
- ? [{ address }]
531
- : [address]),
532
- chainId,
533
- };
534
- },
535
- async disconnect() {
536
- await config.storage?.removeItem('secp256k1.activeAddress');
537
- account = undefined;
68
+ });
69
+ }
70
+ function createAccountsStorage(storage, namespace) {
71
+ const prefix = `accounts.${namespace}`;
72
+ return {
73
+ async getItem(key) {
74
+ return ((await storage.getItem(`${prefix}.${key}`, null)) ??
75
+ null);
538
76
  },
539
- async getAccounts() {
540
- if (!account)
541
- return [];
542
- return [getAddress(account.address)];
77
+ async removeItem(key) {
78
+ await storage.removeItem(`${prefix}.${key}`);
543
79
  },
544
- async getChainId() {
545
- return config.chains[0]?.id;
80
+ async setItem(key, value) {
81
+ await storage.setItem(`${prefix}.${key}`, value);
546
82
  },
547
- async isAuthorized() {
548
- try {
549
- const accounts = await this.getAccounts();
550
- return !!accounts.length;
551
- }
552
- catch (error) {
553
- // biome-ignore lint/suspicious/noConsole: notify
554
- console.error('Connector.secp256k1: Failed to check authorization', error);
555
- return false;
556
- }
557
- },
558
- async switchChain({ chainId }) {
559
- const chain = config.chains.find((chain) => chain.id === chainId);
560
- if (!chain)
561
- throw new SwitchChainError(new ChainNotConfiguredError());
562
- return chain;
83
+ };
84
+ }
85
+ function createMemoryAccountsStorage() {
86
+ const map = new Map();
87
+ return {
88
+ async getItem(key) {
89
+ return (map.get(key) ?? null);
563
90
  },
564
- onAccountsChanged() { },
565
- onChainChanged(chain) {
566
- const chainId = Number(chain);
567
- config.emitter.emit('change', { chainId });
91
+ async removeItem(key) {
92
+ map.delete(key);
568
93
  },
569
- async onDisconnect() {
570
- config.emitter.emit('disconnect');
571
- account = undefined;
94
+ async setItem(key, value) {
95
+ map.set(key, value);
572
96
  },
573
- async getClient({ chainId } = {}) {
574
- const chain = config.chains.find((x) => x.id === chainId) ?? config.chains[0];
575
- if (!chain)
576
- throw new ChainNotConfiguredError();
577
- const transports = config.transports;
578
- if (!transports)
579
- throw new ChainNotConfiguredError();
580
- const transport = transports[chain.id];
581
- if (!transport)
582
- throw new ChainNotConfiguredError();
583
- if (!account)
584
- throw new Error('account not found.');
585
- return createClient({
586
- account,
587
- chain,
588
- transport: walletNamespaceCompat(transport, {
589
- account,
590
- }),
97
+ };
98
+ }
99
+ function _setup(parameters) {
100
+ return createConnector((config) => {
101
+ const chains = config.chains;
102
+ let providerPromise;
103
+ let accountsChanged;
104
+ let chainChanged;
105
+ let connect;
106
+ let disconnect;
107
+ async function getAccountsModule() {
108
+ return await import('accounts').catch(() => {
109
+ throw new Error('dependency "accounts" not found');
591
110
  });
592
- },
593
- async getProvider({ chainId } = {}) {
594
- const { request } = await this.getClient({ chainId });
595
- return { request };
596
- },
597
- }));
111
+ }
112
+ async function getProvider() {
113
+ providerPromise ??= (async () => {
114
+ const accounts = await getAccountsModule();
115
+ return accounts.Provider.create({
116
+ ...parameters.providerParameters,
117
+ adapter: parameters.createAdapter(accounts),
118
+ chains: config.chains,
119
+ storage: parameters.providerParameters.storage ??
120
+ (config.storage
121
+ ? createAccountsStorage(config.storage, parameters.id)
122
+ : createMemoryAccountsStorage()),
123
+ });
124
+ })();
125
+ return await providerPromise;
126
+ }
127
+ return {
128
+ icon: parameters.icon,
129
+ id: parameters.id,
130
+ name: parameters.name,
131
+ rdns: parameters.rdns,
132
+ type: parameters.type,
133
+ async connect(connectParameters = {}) {
134
+ const { chainId, isReconnecting, withCapabilities } = connectParameters;
135
+ const capabilities = 'capabilities' in connectParameters
136
+ ? connectParameters.capabilities
137
+ : undefined;
138
+ let accounts = [];
139
+ let currentChainId;
140
+ if (isReconnecting) {
141
+ accounts = await this.getAccounts()
142
+ .then((accounts) => accounts.map((address) => ({ address, capabilities: {} })))
143
+ .catch(() => []);
144
+ }
145
+ try {
146
+ if (!accounts.length && !isReconnecting) {
147
+ const provider = await getProvider();
148
+ const response = (await provider.request({
149
+ method: 'wallet_connect',
150
+ params: [
151
+ {
152
+ ...(chainId ? { chainId } : {}),
153
+ ...(capabilities ? { capabilities } : {}),
154
+ },
155
+ ],
156
+ }));
157
+ accounts = response.accounts;
158
+ }
159
+ currentChainId ??= await this.getChainId();
160
+ if (!currentChainId)
161
+ throw new ChainNotConfiguredError();
162
+ const provider = await getProvider();
163
+ if (connect) {
164
+ provider.removeListener('connect', connect);
165
+ connect = undefined;
166
+ }
167
+ if (!accountsChanged) {
168
+ accountsChanged = this.onAccountsChanged.bind(this);
169
+ provider.on('accountsChanged', accountsChanged);
170
+ }
171
+ if (!chainChanged) {
172
+ chainChanged = this.onChainChanged.bind(this);
173
+ provider.on('chainChanged', chainChanged);
174
+ }
175
+ if (!disconnect) {
176
+ disconnect = this.onDisconnect.bind(this);
177
+ provider.on('disconnect', disconnect);
178
+ }
179
+ return {
180
+ accounts: (withCapabilities
181
+ ? accounts
182
+ : accounts.map((account) => account.address)),
183
+ chainId: currentChainId,
184
+ };
185
+ }
186
+ catch (error) {
187
+ const rpcError = error;
188
+ if (rpcError.code === UserRejectedRequestError.code)
189
+ throw new UserRejectedRequestError(rpcError);
190
+ throw rpcError;
191
+ }
192
+ },
193
+ async disconnect() {
194
+ const provider = await getProvider();
195
+ if (chainChanged) {
196
+ provider.removeListener('chainChanged', chainChanged);
197
+ chainChanged = undefined;
198
+ }
199
+ if (disconnect) {
200
+ provider.removeListener('disconnect', disconnect);
201
+ disconnect = undefined;
202
+ }
203
+ if (!connect) {
204
+ connect = this.onConnect?.bind(this);
205
+ if (connect)
206
+ provider.on('connect', connect);
207
+ }
208
+ await provider.request({ method: 'wallet_disconnect' });
209
+ },
210
+ async getAccounts() {
211
+ const provider = await getProvider();
212
+ return await provider.request({ method: 'eth_accounts' });
213
+ },
214
+ async getChainId() {
215
+ const provider = await getProvider();
216
+ return Number(await provider.request({ method: 'eth_chainId' }));
217
+ },
218
+ async getClient({ chainId } = {}) {
219
+ const provider = await getProvider();
220
+ return Object.assign(provider.getClient({ chainId }), {
221
+ account: provider.getAccount(),
222
+ });
223
+ },
224
+ async getProvider() {
225
+ return await getProvider();
226
+ },
227
+ async isAuthorized() {
228
+ try {
229
+ const accounts = await withRetry(() => this.getAccounts());
230
+ return !!accounts.length;
231
+ }
232
+ catch {
233
+ return false;
234
+ }
235
+ },
236
+ onAccountsChanged(accounts) {
237
+ config.emitter.emit('change', {
238
+ accounts: accounts,
239
+ });
240
+ },
241
+ onChainChanged(chain) {
242
+ config.emitter.emit('change', { chainId: Number(chain) });
243
+ },
244
+ async onConnect(connectInfo) {
245
+ const accounts = await this.getAccounts();
246
+ if (accounts.length === 0)
247
+ return;
248
+ const chainId = Number(connectInfo.chainId);
249
+ config.emitter.emit('connect', { accounts, chainId });
250
+ const provider = await getProvider();
251
+ if (connect) {
252
+ provider.removeListener('connect', connect);
253
+ connect = undefined;
254
+ }
255
+ if (!accountsChanged) {
256
+ accountsChanged = this.onAccountsChanged.bind(this);
257
+ provider.on('accountsChanged', accountsChanged);
258
+ }
259
+ if (!chainChanged) {
260
+ chainChanged = this.onChainChanged.bind(this);
261
+ provider.on('chainChanged', chainChanged);
262
+ }
263
+ if (!disconnect) {
264
+ disconnect = this.onDisconnect.bind(this);
265
+ provider.on('disconnect', disconnect);
266
+ }
267
+ },
268
+ async onDisconnect(_error) {
269
+ const provider = await getProvider();
270
+ config.emitter.emit('disconnect');
271
+ if (chainChanged) {
272
+ provider.removeListener('chainChanged', chainChanged);
273
+ chainChanged = undefined;
274
+ }
275
+ if (disconnect) {
276
+ provider.removeListener('disconnect', disconnect);
277
+ disconnect = undefined;
278
+ }
279
+ if (!connect) {
280
+ connect = this.onConnect?.bind(this);
281
+ if (connect)
282
+ provider.on('connect', connect);
283
+ }
284
+ },
285
+ async setup() {
286
+ if (!connect) {
287
+ const provider = await getProvider();
288
+ connect = this.onConnect?.bind(this);
289
+ if (connect)
290
+ provider.on('connect', connect);
291
+ }
292
+ },
293
+ async switchChain({ chainId }) {
294
+ const chain = chains.find((chain) => chain.id === chainId);
295
+ if (!chain)
296
+ throw new SwitchChainError(new ChainNotConfiguredError());
297
+ const provider = await getProvider();
298
+ await provider.request({
299
+ method: 'wallet_switchEthereumChain',
300
+ params: [{ chainId: numberToHex(chainId) }],
301
+ });
302
+ return chain;
303
+ },
304
+ };
305
+ });
598
306
  }
599
307
  //# sourceMappingURL=Connectors.js.map