@solana-mobile/wallet-adapter-mobile 0.9.4 → 0.9.6

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.
@@ -2,11 +2,11 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ var mobileWalletAdapterProtocolWeb3js = require('@solana-mobile/mobile-wallet-adapter-protocol-web3js');
5
6
  var walletAdapterBase = require('@solana/wallet-adapter-base');
6
7
  var web3_js = require('@solana/web3.js');
7
- var mobileWalletAdapterProtocolWeb3js = require('@solana-mobile/mobile-wallet-adapter-protocol-web3js');
8
- var reactNative = require('react-native');
9
8
  var jsBase64 = require('js-base64');
9
+ var reactNative = require('react-native');
10
10
  var AsyncStorage = require('@react-native-async-storage/async-storage');
11
11
 
12
12
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -42,12 +42,15 @@ function getIsSupported() {
42
42
  return reactNative.Platform.OS === 'android';
43
43
  }
44
44
 
45
- const SolanaMobileWalletAdapterWalletName = 'Default wallet app';
45
+ const SolanaMobileWalletAdapterWalletName = 'Mobile Wallet Adapter';
46
46
  const SIGNATURE_LENGTH_IN_BYTES = 64;
47
47
  function getPublicKeyFromAddress(address) {
48
48
  const publicKeyByteArray = jsBase64.toUint8Array(address);
49
49
  return new web3_js.PublicKey(publicKeyByteArray);
50
50
  }
51
+ function isVersionedTransaction(transaction) {
52
+ return 'version' in transaction;
53
+ }
51
54
  class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalletAdapter {
52
55
  constructor(config) {
53
56
  super();
@@ -55,14 +58,21 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
55
58
  // FIXME(#244): We can't actually know what versions are supported until we know which wallet we're talking to.
56
59
  ['legacy', 0]);
57
60
  this.name = SolanaMobileWalletAdapterWalletName;
58
- this.url = 'https://solanamobile.com';
61
+ this.url = 'https://solanamobile.com/wallets';
59
62
  this.icon = 'data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI4IiB3aWR0aD0iMjgiIHZpZXdCb3g9Ii0zIDAgMjggMjgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0iI0RDQjhGRiI+PHBhdGggZD0iTTE3LjQgMTcuNEgxNXYyLjRoMi40di0yLjRabTEuMi05LjZoLTIuNHYyLjRoMi40VjcuOFoiLz48cGF0aCBkPSJNMjEuNiAzVjBoLTIuNHYzaC0zLjZWMGgtMi40djNoLTIuNHY2LjZINC41YTIuMSAyLjEgMCAxIDEgMC00LjJoMi43VjNINC41QTQuNSA0LjUgMCAwIDAgMCA3LjVWMjRoMjEuNnYtNi42aC0yLjR2NC4ySDIuNFYxMS41Yy41LjMgMS4yLjQgMS44LjVoNy41QTYuNiA2LjYgMCAwIDAgMjQgOVYzaC0yLjRabTAgNS43YTQuMiA0LjIgMCAxIDEtOC40IDBWNS40aDguNHYzLjNaIi8+PC9nPjwvc3ZnPg==';
60
63
  this._connecting = false;
64
+ /**
65
+ * Every time the connection is recycled in some way (eg. `disconnect()` is called)
66
+ * increment this and use it to make sure that `transact` calls from the previous
67
+ * 'generation' don't continue to do work and throw exceptions.
68
+ */
69
+ this._connectionGeneration = 0;
61
70
  this._readyState = getIsSupported() ? walletAdapterBase.WalletReadyState.Loadable : walletAdapterBase.WalletReadyState.Unsupported;
62
71
  this._authorizationResultCache = config.authorizationResultCache;
63
72
  this._addressSelector = config.addressSelector;
64
73
  this._appIdentity = config.appIdentity;
65
74
  this._cluster = config.cluster;
75
+ this._onWalletNotFound = config.onWalletNotFound;
66
76
  if (this._readyState !== walletAdapterBase.WalletReadyState.Unsupported) {
67
77
  this._authorizationResultCache.get().then((authorizationResult) => {
68
78
  if (authorizationResult) {
@@ -220,6 +230,8 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
220
230
  disconnect() {
221
231
  return __awaiter(this, void 0, void 0, function* () {
222
232
  this._authorizationResultCache.clear(); // TODO: Evaluate whether there's any threat to not `awaiting` this expression
233
+ this._connecting = false;
234
+ this._connectionGeneration++;
223
235
  delete this._authorizationResult;
224
236
  delete this._publicKey;
225
237
  delete this._selectedAddress;
@@ -231,7 +243,21 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
231
243
  return __awaiter(this, void 0, void 0, function* () {
232
244
  const walletUriBase = (_a = this._authorizationResult) === null || _a === void 0 ? void 0 : _a.wallet_uri_base;
233
245
  const config = walletUriBase ? { baseUri: walletUriBase } : undefined;
234
- return yield mobileWalletAdapterProtocolWeb3js.transact(callback, config);
246
+ const currentConnectionGeneration = this._connectionGeneration;
247
+ try {
248
+ return yield mobileWalletAdapterProtocolWeb3js.transact(callback, config);
249
+ }
250
+ catch (e) {
251
+ if (this._connectionGeneration !== currentConnectionGeneration) {
252
+ yield new Promise(() => { }); // Never resolve.
253
+ }
254
+ if (e instanceof Error &&
255
+ e.name === 'SolanaMobileWalletAdapterError' &&
256
+ e.code === 'ERROR_WALLET_NOT_FOUND') {
257
+ yield this._onWalletNotFound(this);
258
+ }
259
+ throw e;
260
+ }
235
261
  });
236
262
  }
237
263
  assertIsAuthorized() {
@@ -266,9 +292,43 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
266
292
  const minContextSlot = options === null || options === void 0 ? void 0 : options.minContextSlot;
267
293
  try {
268
294
  return yield this.transact((wallet) => __awaiter(this, void 0, void 0, function* () {
269
- yield Promise.all([
295
+ function getTargetCommitment() {
296
+ let targetCommitment;
297
+ switch (connection.commitment) {
298
+ case 'confirmed':
299
+ case 'finalized':
300
+ case 'processed':
301
+ targetCommitment = connection.commitment;
302
+ break;
303
+ default:
304
+ targetCommitment = 'finalized';
305
+ }
306
+ let targetPreflightCommitment;
307
+ switch (options === null || options === void 0 ? void 0 : options.preflightCommitment) {
308
+ case 'confirmed':
309
+ case 'finalized':
310
+ case 'processed':
311
+ targetPreflightCommitment = options.preflightCommitment;
312
+ break;
313
+ case undefined:
314
+ targetPreflightCommitment = targetCommitment;
315
+ default:
316
+ targetPreflightCommitment = 'finalized';
317
+ }
318
+ const preflightCommitmentScore = targetPreflightCommitment === 'finalized'
319
+ ? 2
320
+ : targetPreflightCommitment === 'confirmed'
321
+ ? 1
322
+ : 0;
323
+ const targetCommitmentScore = targetCommitment === 'finalized' ? 2 : targetCommitment === 'confirmed' ? 1 : 0;
324
+ return preflightCommitmentScore < targetCommitmentScore
325
+ ? targetPreflightCommitment
326
+ : targetCommitment;
327
+ }
328
+ const [capabilities, _1, _2] = yield Promise.all([
329
+ wallet.getCapabilities(),
270
330
  this.performReauthorization(wallet, authToken),
271
- 'version' in transaction
331
+ isVersionedTransaction(transaction)
272
332
  ? null
273
333
  : /**
274
334
  * Unlike versioned transactions, legacy `Transaction` objects
@@ -279,52 +339,32 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
279
339
  var _a;
280
340
  transaction.feePayer || (transaction.feePayer = (_a = this.publicKey) !== null && _a !== void 0 ? _a : undefined);
281
341
  if (transaction.recentBlockhash == null) {
282
- let targetCommitment;
283
- switch (connection.commitment) {
284
- case 'confirmed':
285
- case 'finalized':
286
- case 'processed':
287
- targetCommitment = connection.commitment;
288
- break;
289
- default:
290
- targetCommitment = 'finalized';
291
- }
292
- let targetPreflightCommitment;
293
- switch (options === null || options === void 0 ? void 0 : options.preflightCommitment) {
294
- case 'confirmed':
295
- case 'finalized':
296
- case 'processed':
297
- targetPreflightCommitment = options.preflightCommitment;
298
- break;
299
- case undefined:
300
- targetPreflightCommitment = targetCommitment;
301
- default:
302
- targetPreflightCommitment = 'finalized';
303
- }
304
- const preflightCommitmentScore = targetPreflightCommitment === 'finalized'
305
- ? 2
306
- : targetPreflightCommitment === 'confirmed'
307
- ? 1
308
- : 0;
309
- const targetCommitmentScore = targetCommitment === 'finalized'
310
- ? 2
311
- : targetCommitment === 'confirmed'
312
- ? 1
313
- : 0;
314
342
  const { blockhash } = yield connection.getLatestBlockhash({
315
- commitment: preflightCommitmentScore < targetCommitmentScore
316
- ? targetPreflightCommitment
317
- : targetCommitment,
343
+ commitment: getTargetCommitment(),
318
344
  });
319
345
  transaction.recentBlockhash = blockhash;
320
346
  }
321
347
  }))(),
322
348
  ]);
323
- const signatures = yield wallet.signAndSendTransactions({
324
- minContextSlot,
325
- transactions: [transaction],
326
- });
327
- return signatures[0];
349
+ if (capabilities.supports_sign_and_send_transactions) {
350
+ const signatures = yield wallet.signAndSendTransactions({
351
+ minContextSlot,
352
+ transactions: [transaction],
353
+ });
354
+ return signatures[0];
355
+ }
356
+ else {
357
+ const [signedTransaction] = yield wallet.signTransactions({
358
+ transactions: [transaction],
359
+ });
360
+ if (isVersionedTransaction(signedTransaction)) {
361
+ return yield connection.sendTransaction(signedTransaction);
362
+ }
363
+ else {
364
+ const serializedTransaction = signedTransaction.serialize();
365
+ return yield connection.sendRawTransaction(serializedTransaction, Object.assign(Object.assign({}, options), { preflightCommitment: getTargetCommitment() }));
366
+ }
367
+ }
328
368
  }));
329
369
  }
330
370
  catch (error) {
@@ -415,7 +455,19 @@ function createDefaultAuthorizationResultCache() {
415
455
  };
416
456
  }
417
457
 
458
+ function defaultWalletNotFoundHandler(mobileWalletAdapter) {
459
+ return __awaiter(this, void 0, void 0, function* () {
460
+ if (typeof window !== 'undefined') {
461
+ window.location.assign(mobileWalletAdapter.url);
462
+ }
463
+ });
464
+ }
465
+ function createDefaultWalletNotFoundHandler() {
466
+ return defaultWalletNotFoundHandler;
467
+ }
468
+
418
469
  exports.SolanaMobileWalletAdapter = SolanaMobileWalletAdapter;
419
470
  exports.SolanaMobileWalletAdapterWalletName = SolanaMobileWalletAdapterWalletName;
420
471
  exports.createDefaultAddressSelector = createDefaultAddressSelector;
421
472
  exports.createDefaultAuthorizationResultCache = createDefaultAuthorizationResultCache;
473
+ exports.createDefaultWalletNotFoundHandler = createDefaultWalletNotFoundHandler;
@@ -1,6 +1,6 @@
1
+ import { transact } from '@solana-mobile/mobile-wallet-adapter-protocol-web3js';
1
2
  import { BaseMessageSignerWalletAdapter, WalletReadyState, WalletPublicKeyError, WalletNotReadyError, WalletConnectionError, WalletDisconnectedError, WalletNotConnectedError, WalletSignTransactionError, WalletSendTransactionError, WalletSignMessageError } from '@solana/wallet-adapter-base';
2
3
  import { PublicKey } from '@solana/web3.js';
3
- import { transact } from '@solana-mobile/mobile-wallet-adapter-protocol-web3js';
4
4
 
5
5
  /*! *****************************************************************************
6
6
  Copyright (c) Microsoft Corporation.
@@ -27,13 +27,6 @@ function __awaiter(thisArg, _arguments, P, generator) {
27
27
  });
28
28
  }
29
29
 
30
- function getIsSupported() {
31
- return (typeof window !== 'undefined' &&
32
- window.isSecureContext &&
33
- typeof document !== 'undefined' &&
34
- /android/i.test(navigator.userAgent));
35
- }
36
-
37
30
  function toUint8Array(base64EncodedByteArray) {
38
31
  return new Uint8Array(window
39
32
  .atob(base64EncodedByteArray)
@@ -41,12 +34,22 @@ function toUint8Array(base64EncodedByteArray) {
41
34
  .map((c) => c.charCodeAt(0)));
42
35
  }
43
36
 
44
- const SolanaMobileWalletAdapterWalletName = 'Default wallet app';
37
+ function getIsSupported() {
38
+ return (typeof window !== 'undefined' &&
39
+ window.isSecureContext &&
40
+ typeof document !== 'undefined' &&
41
+ /android/i.test(navigator.userAgent));
42
+ }
43
+
44
+ const SolanaMobileWalletAdapterWalletName = 'Mobile Wallet Adapter';
45
45
  const SIGNATURE_LENGTH_IN_BYTES = 64;
46
46
  function getPublicKeyFromAddress(address) {
47
47
  const publicKeyByteArray = toUint8Array(address);
48
48
  return new PublicKey(publicKeyByteArray);
49
49
  }
50
+ function isVersionedTransaction(transaction) {
51
+ return 'version' in transaction;
52
+ }
50
53
  class SolanaMobileWalletAdapter extends BaseMessageSignerWalletAdapter {
51
54
  constructor(config) {
52
55
  super();
@@ -54,14 +57,21 @@ class SolanaMobileWalletAdapter extends BaseMessageSignerWalletAdapter {
54
57
  // FIXME(#244): We can't actually know what versions are supported until we know which wallet we're talking to.
55
58
  ['legacy', 0]);
56
59
  this.name = SolanaMobileWalletAdapterWalletName;
57
- this.url = 'https://solanamobile.com';
60
+ this.url = 'https://solanamobile.com/wallets';
58
61
  this.icon = 'data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI4IiB3aWR0aD0iMjgiIHZpZXdCb3g9Ii0zIDAgMjggMjgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0iI0RDQjhGRiI+PHBhdGggZD0iTTE3LjQgMTcuNEgxNXYyLjRoMi40di0yLjRabTEuMi05LjZoLTIuNHYyLjRoMi40VjcuOFoiLz48cGF0aCBkPSJNMjEuNiAzVjBoLTIuNHYzaC0zLjZWMGgtMi40djNoLTIuNHY2LjZINC41YTIuMSAyLjEgMCAxIDEgMC00LjJoMi43VjNINC41QTQuNSA0LjUgMCAwIDAgMCA3LjVWMjRoMjEuNnYtNi42aC0yLjR2NC4ySDIuNFYxMS41Yy41LjMgMS4yLjQgMS44LjVoNy41QTYuNiA2LjYgMCAwIDAgMjQgOVYzaC0yLjRabTAgNS43YTQuMiA0LjIgMCAxIDEtOC40IDBWNS40aDguNHYzLjNaIi8+PC9nPjwvc3ZnPg==';
59
62
  this._connecting = false;
63
+ /**
64
+ * Every time the connection is recycled in some way (eg. `disconnect()` is called)
65
+ * increment this and use it to make sure that `transact` calls from the previous
66
+ * 'generation' don't continue to do work and throw exceptions.
67
+ */
68
+ this._connectionGeneration = 0;
60
69
  this._readyState = getIsSupported() ? WalletReadyState.Loadable : WalletReadyState.Unsupported;
61
70
  this._authorizationResultCache = config.authorizationResultCache;
62
71
  this._addressSelector = config.addressSelector;
63
72
  this._appIdentity = config.appIdentity;
64
73
  this._cluster = config.cluster;
74
+ this._onWalletNotFound = config.onWalletNotFound;
65
75
  if (this._readyState !== WalletReadyState.Unsupported) {
66
76
  this._authorizationResultCache.get().then((authorizationResult) => {
67
77
  if (authorizationResult) {
@@ -219,6 +229,8 @@ class SolanaMobileWalletAdapter extends BaseMessageSignerWalletAdapter {
219
229
  disconnect() {
220
230
  return __awaiter(this, void 0, void 0, function* () {
221
231
  this._authorizationResultCache.clear(); // TODO: Evaluate whether there's any threat to not `awaiting` this expression
232
+ this._connecting = false;
233
+ this._connectionGeneration++;
222
234
  delete this._authorizationResult;
223
235
  delete this._publicKey;
224
236
  delete this._selectedAddress;
@@ -230,7 +242,21 @@ class SolanaMobileWalletAdapter extends BaseMessageSignerWalletAdapter {
230
242
  return __awaiter(this, void 0, void 0, function* () {
231
243
  const walletUriBase = (_a = this._authorizationResult) === null || _a === void 0 ? void 0 : _a.wallet_uri_base;
232
244
  const config = walletUriBase ? { baseUri: walletUriBase } : undefined;
233
- return yield transact(callback, config);
245
+ const currentConnectionGeneration = this._connectionGeneration;
246
+ try {
247
+ return yield transact(callback, config);
248
+ }
249
+ catch (e) {
250
+ if (this._connectionGeneration !== currentConnectionGeneration) {
251
+ yield new Promise(() => { }); // Never resolve.
252
+ }
253
+ if (e instanceof Error &&
254
+ e.name === 'SolanaMobileWalletAdapterError' &&
255
+ e.code === 'ERROR_WALLET_NOT_FOUND') {
256
+ yield this._onWalletNotFound(this);
257
+ }
258
+ throw e;
259
+ }
234
260
  });
235
261
  }
236
262
  assertIsAuthorized() {
@@ -265,9 +291,43 @@ class SolanaMobileWalletAdapter extends BaseMessageSignerWalletAdapter {
265
291
  const minContextSlot = options === null || options === void 0 ? void 0 : options.minContextSlot;
266
292
  try {
267
293
  return yield this.transact((wallet) => __awaiter(this, void 0, void 0, function* () {
268
- yield Promise.all([
294
+ function getTargetCommitment() {
295
+ let targetCommitment;
296
+ switch (connection.commitment) {
297
+ case 'confirmed':
298
+ case 'finalized':
299
+ case 'processed':
300
+ targetCommitment = connection.commitment;
301
+ break;
302
+ default:
303
+ targetCommitment = 'finalized';
304
+ }
305
+ let targetPreflightCommitment;
306
+ switch (options === null || options === void 0 ? void 0 : options.preflightCommitment) {
307
+ case 'confirmed':
308
+ case 'finalized':
309
+ case 'processed':
310
+ targetPreflightCommitment = options.preflightCommitment;
311
+ break;
312
+ case undefined:
313
+ targetPreflightCommitment = targetCommitment;
314
+ default:
315
+ targetPreflightCommitment = 'finalized';
316
+ }
317
+ const preflightCommitmentScore = targetPreflightCommitment === 'finalized'
318
+ ? 2
319
+ : targetPreflightCommitment === 'confirmed'
320
+ ? 1
321
+ : 0;
322
+ const targetCommitmentScore = targetCommitment === 'finalized' ? 2 : targetCommitment === 'confirmed' ? 1 : 0;
323
+ return preflightCommitmentScore < targetCommitmentScore
324
+ ? targetPreflightCommitment
325
+ : targetCommitment;
326
+ }
327
+ const [capabilities, _1, _2] = yield Promise.all([
328
+ wallet.getCapabilities(),
269
329
  this.performReauthorization(wallet, authToken),
270
- 'version' in transaction
330
+ isVersionedTransaction(transaction)
271
331
  ? null
272
332
  : /**
273
333
  * Unlike versioned transactions, legacy `Transaction` objects
@@ -278,52 +338,32 @@ class SolanaMobileWalletAdapter extends BaseMessageSignerWalletAdapter {
278
338
  var _a;
279
339
  transaction.feePayer || (transaction.feePayer = (_a = this.publicKey) !== null && _a !== void 0 ? _a : undefined);
280
340
  if (transaction.recentBlockhash == null) {
281
- let targetCommitment;
282
- switch (connection.commitment) {
283
- case 'confirmed':
284
- case 'finalized':
285
- case 'processed':
286
- targetCommitment = connection.commitment;
287
- break;
288
- default:
289
- targetCommitment = 'finalized';
290
- }
291
- let targetPreflightCommitment;
292
- switch (options === null || options === void 0 ? void 0 : options.preflightCommitment) {
293
- case 'confirmed':
294
- case 'finalized':
295
- case 'processed':
296
- targetPreflightCommitment = options.preflightCommitment;
297
- break;
298
- case undefined:
299
- targetPreflightCommitment = targetCommitment;
300
- default:
301
- targetPreflightCommitment = 'finalized';
302
- }
303
- const preflightCommitmentScore = targetPreflightCommitment === 'finalized'
304
- ? 2
305
- : targetPreflightCommitment === 'confirmed'
306
- ? 1
307
- : 0;
308
- const targetCommitmentScore = targetCommitment === 'finalized'
309
- ? 2
310
- : targetCommitment === 'confirmed'
311
- ? 1
312
- : 0;
313
341
  const { blockhash } = yield connection.getLatestBlockhash({
314
- commitment: preflightCommitmentScore < targetCommitmentScore
315
- ? targetPreflightCommitment
316
- : targetCommitment,
342
+ commitment: getTargetCommitment(),
317
343
  });
318
344
  transaction.recentBlockhash = blockhash;
319
345
  }
320
346
  }))(),
321
347
  ]);
322
- const signatures = yield wallet.signAndSendTransactions({
323
- minContextSlot,
324
- transactions: [transaction],
325
- });
326
- return signatures[0];
348
+ if (capabilities.supports_sign_and_send_transactions) {
349
+ const signatures = yield wallet.signAndSendTransactions({
350
+ minContextSlot,
351
+ transactions: [transaction],
352
+ });
353
+ return signatures[0];
354
+ }
355
+ else {
356
+ const [signedTransaction] = yield wallet.signTransactions({
357
+ transactions: [transaction],
358
+ });
359
+ if (isVersionedTransaction(signedTransaction)) {
360
+ return yield connection.sendTransaction(signedTransaction);
361
+ }
362
+ else {
363
+ const serializedTransaction = signedTransaction.serialize();
364
+ return yield connection.sendRawTransaction(serializedTransaction, Object.assign(Object.assign({}, options), { preflightCommitment: getTargetCommitment() }));
365
+ }
366
+ }
327
367
  }));
328
368
  }
329
369
  catch (error) {
@@ -429,4 +469,15 @@ function createDefaultAuthorizationResultCache() {
429
469
  };
430
470
  }
431
471
 
432
- export { SolanaMobileWalletAdapter, SolanaMobileWalletAdapterWalletName, createDefaultAddressSelector, createDefaultAuthorizationResultCache };
472
+ function defaultWalletNotFoundHandler(mobileWalletAdapter) {
473
+ return __awaiter(this, void 0, void 0, function* () {
474
+ if (typeof window !== 'undefined') {
475
+ window.location.assign(mobileWalletAdapter.url);
476
+ }
477
+ });
478
+ }
479
+ function createDefaultWalletNotFoundHandler() {
480
+ return defaultWalletNotFoundHandler;
481
+ }
482
+
483
+ export { SolanaMobileWalletAdapter, SolanaMobileWalletAdapterWalletName, createDefaultAddressSelector, createDefaultAuthorizationResultCache, createDefaultWalletNotFoundHandler };