@solana-mobile/wallet-adapter-mobile 0.0.1-alpha.6 → 0.9.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.
package/README.md CHANGED
@@ -10,6 +10,7 @@ Create an instance of the mobile wallet adapter like this.
10
10
 
11
11
  ```typescript
12
12
  new SolanaMobileWalletAdapter({
13
+ addressSelector: createDefaultAddressSelector(),
13
14
  appIdentity: {
14
15
  name: 'My app',
15
16
  uri: 'https://myapp.io',
@@ -24,6 +25,7 @@ Use that adapter instance alongside the other adapters used by your app.
24
25
  ```typescript
25
26
  const wallets = useMemo(() => [
26
27
  new SolanaMobileWalletAdapter({
28
+ addressSelector: createDefaultAddressSelector(),
27
29
  appIdentity: {
28
30
  name: 'My app',
29
31
  uri: 'https://myapp.io',
@@ -31,6 +31,13 @@ function __awaiter(thisArg, _arguments, P, generator) {
31
31
  });
32
32
  }
33
33
 
34
+ function toUint8Array(base64EncodedByteArray) {
35
+ return new Uint8Array(window
36
+ .atob(base64EncodedByteArray)
37
+ .split('')
38
+ .map((c) => c.charCodeAt(0)));
39
+ }
40
+
34
41
  function getIsSupported() {
35
42
  return (typeof window !== 'undefined' &&
36
43
  window.isSecureContext &&
@@ -40,6 +47,10 @@ function getIsSupported() {
40
47
 
41
48
  const SolanaMobileWalletAdapterWalletName = 'Default wallet app';
42
49
  const SIGNATURE_LENGTH_IN_BYTES = 64;
50
+ function getPublicKeyFromAddress(address) {
51
+ const publicKeyByteArray = toUint8Array(address);
52
+ return new web3_js.PublicKey(publicKeyByteArray);
53
+ }
43
54
  class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalletAdapter {
44
55
  constructor(config) {
45
56
  super();
@@ -49,7 +60,9 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
49
60
  this._connecting = false;
50
61
  this._readyState = getIsSupported() ? walletAdapterBase.WalletReadyState.Loadable : walletAdapterBase.WalletReadyState.Unsupported;
51
62
  this._authorizationResultCache = config.authorizationResultCache;
63
+ this._addressSelector = config.addressSelector;
52
64
  this._appIdentity = config.appIdentity;
65
+ this._cluster = config.cluster;
53
66
  if (this._readyState !== walletAdapterBase.WalletReadyState.Unsupported) {
54
67
  this._authorizationResultCache.get().then((authorizationResult) => {
55
68
  if (authorizationResult) {
@@ -62,8 +75,13 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
62
75
  }
63
76
  }
64
77
  get publicKey() {
65
- if (this._publicKey == null && this._authorizationResult != null) {
66
- this._publicKey = new web3_js.PublicKey(this._authorizationResult.pub_key);
78
+ if (this._publicKey == null && this._selectedAddress != null) {
79
+ try {
80
+ this._publicKey = getPublicKeyFromAddress(this._selectedAddress);
81
+ }
82
+ catch (e) {
83
+ throw new walletAdapterBase.WalletPublicKeyError((e instanceof Error && (e === null || e === void 0 ? void 0 : e.message)) || 'Unknown error', e);
84
+ }
67
85
  }
68
86
  return this._publicKey ? this._publicKey : null;
69
87
  }
@@ -76,71 +94,88 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
76
94
  get readyState() {
77
95
  return this._readyState;
78
96
  }
79
- connect() {
97
+ runWithGuard(callback) {
80
98
  return __awaiter(this, void 0, void 0, function* () {
81
- if (this._readyState !== walletAdapterBase.WalletReadyState.Installed && this._readyState !== walletAdapterBase.WalletReadyState.Loadable) {
82
- const err = new walletAdapterBase.WalletNotReadyError();
83
- this.emit('error', err);
84
- throw err;
99
+ try {
100
+ return yield callback();
85
101
  }
86
- this._connecting = true;
87
- const cachedAuthorizationResult = yield this._authorizationResultCache.get();
88
- if (cachedAuthorizationResult) {
89
- this._authorizationResult = cachedAuthorizationResult;
90
- this._connecting = false;
91
- if (this._readyState !== walletAdapterBase.WalletReadyState.Installed) {
92
- this.emit('readyStateChange', (this._readyState = walletAdapterBase.WalletReadyState.Installed));
93
- }
94
- this.emit('connect',
95
- // Having just set an `authorizationResult`, `this.publicKey` is definitely non-null
96
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
97
- this.publicKey);
98
- return;
102
+ catch (e) {
103
+ this.emit('error', e);
104
+ throw e;
99
105
  }
100
- try {
101
- yield this.transact((wallet) => __awaiter(this, void 0, void 0, function* () {
102
- const { auth_token, pub_key: base58PublicKey, wallet_uri_base, } = yield wallet.authorize({ identity: this._appIdentity });
103
- try {
104
- this._publicKey = new web3_js.PublicKey(base58PublicKey);
105
- }
106
- catch (e) {
107
- throw new walletAdapterBase.WalletPublicKeyError((e instanceof Error && (e === null || e === void 0 ? void 0 : e.message)) || 'Unknown error', e);
106
+ });
107
+ }
108
+ connect() {
109
+ return __awaiter(this, void 0, void 0, function* () {
110
+ return yield this.runWithGuard(() => __awaiter(this, void 0, void 0, function* () {
111
+ if (this._readyState !== walletAdapterBase.WalletReadyState.Installed && this._readyState !== walletAdapterBase.WalletReadyState.Loadable) {
112
+ throw new walletAdapterBase.WalletNotReadyError();
113
+ }
114
+ this._connecting = true;
115
+ const cachedAuthorizationResult = yield this._authorizationResultCache.get();
116
+ if (cachedAuthorizationResult) {
117
+ this._authorizationResult = cachedAuthorizationResult;
118
+ this._connecting = false;
119
+ if (this._readyState !== walletAdapterBase.WalletReadyState.Installed) {
120
+ this.emit('readyStateChange', (this._readyState = walletAdapterBase.WalletReadyState.Installed));
108
121
  }
109
- this.handleAuthorizationResult({
110
- auth_token,
111
- pub_key: base58PublicKey,
112
- wallet_uri_base: wallet_uri_base,
113
- }); // TODO: Evaluate whether there's any threat to not `awaiting` this expression
122
+ this._selectedAddress = yield this._addressSelector.select(cachedAuthorizationResult.accounts.map(({ address }) => address));
114
123
  this.emit('connect',
115
- // Having just set an `authorizationResult`, `this.publicKey` is definitely non-null
124
+ // Having just set `this._selectedAddress`, `this.publicKey` is definitely non-null
116
125
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
117
126
  this.publicKey);
118
- }));
119
- }
120
- catch (e) {
121
- throw new walletAdapterBase.WalletConnectionError((e instanceof Error && e.message) || 'Unknown error', e);
122
- }
123
- finally {
124
- this._connecting = false;
125
- }
127
+ return;
128
+ }
129
+ try {
130
+ yield this.transact((wallet) => __awaiter(this, void 0, void 0, function* () {
131
+ const authorizationResult = yield wallet.authorize({
132
+ cluster: this._cluster,
133
+ identity: this._appIdentity,
134
+ });
135
+ this.handleAuthorizationResult(authorizationResult); // TODO: Evaluate whether there's any threat to not `awaiting` this expression
136
+ }));
137
+ }
138
+ catch (e) {
139
+ throw new walletAdapterBase.WalletConnectionError((e instanceof Error && e.message) || 'Unknown error', e);
140
+ }
141
+ finally {
142
+ this._connecting = false;
143
+ }
144
+ }));
126
145
  });
127
146
  }
128
147
  handleAuthorizationResult(authorizationResult) {
148
+ var _a;
129
149
  return __awaiter(this, void 0, void 0, function* () {
150
+ const didPublicKeysChange =
151
+ // Case 1: We started from having no authorization.
152
+ this._authorizationResult == null ||
153
+ // Case 2: The number of authorized accounts changed.
154
+ ((_a = this._authorizationResult) === null || _a === void 0 ? void 0 : _a.accounts.length) !== authorizationResult.accounts.length ||
155
+ // Case 3: The new list of addresses isn't exactly the same as the old list, in the same order.
156
+ this._authorizationResult.accounts.some((account, ii) => account.address !== authorizationResult.accounts[ii].address);
130
157
  this._authorizationResult = authorizationResult;
158
+ if (didPublicKeysChange) {
159
+ const nextSelectedAddress = yield this._addressSelector.select(authorizationResult.accounts.map(({ address }) => address));
160
+ if (nextSelectedAddress !== this._selectedAddress) {
161
+ this._selectedAddress = nextSelectedAddress;
162
+ delete this._publicKey;
163
+ this.emit('connect',
164
+ // Having just set `this._selectedAddress`, `this.publicKey` is definitely non-null
165
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
166
+ this.publicKey);
167
+ }
168
+ }
131
169
  yield this._authorizationResultCache.set(authorizationResult);
132
170
  });
133
171
  }
134
- performReauthorization(wallet, currentAuthorizationResult) {
172
+ performReauthorization(wallet, authToken) {
135
173
  return __awaiter(this, void 0, void 0, function* () {
136
174
  try {
137
- const { auth_token } = yield wallet.reauthorize({
138
- auth_token: currentAuthorizationResult.auth_token,
175
+ const authorizationResult = yield wallet.reauthorize({
176
+ auth_token: authToken,
139
177
  });
140
- if (currentAuthorizationResult.auth_token !== auth_token) {
141
- this.handleAuthorizationResult(Object.assign(Object.assign({}, currentAuthorizationResult), { auth_token })); // TODO: Evaluate whether there's any threat to not `awaiting` this expression
142
- }
143
- return auth_token;
178
+ this.handleAuthorizationResult(authorizationResult); // TODO: Evaluate whether there's any threat to not `awaiting` this expression
144
179
  }
145
180
  catch (e) {
146
181
  this.disconnect();
@@ -153,6 +188,7 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
153
188
  this._authorizationResultCache.clear(); // TODO: Evaluate whether there's any threat to not `awaiting` this expression
154
189
  delete this._authorizationResult;
155
190
  delete this._publicKey;
191
+ delete this._selectedAddress;
156
192
  this.emit('disconnect');
157
193
  });
158
194
  }
@@ -165,46 +201,82 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
165
201
  });
166
202
  }
167
203
  assertIsAuthorized() {
168
- const authorizationResult = this._authorizationResult;
169
- if (!authorizationResult)
204
+ if (!this._authorizationResult || !this._selectedAddress)
170
205
  throw new walletAdapterBase.WalletNotConnectedError();
171
- return authorizationResult;
206
+ return {
207
+ authToken: this._authorizationResult.auth_token,
208
+ selectedAddress: this._selectedAddress,
209
+ };
172
210
  }
173
211
  performSignTransactions(transactions) {
174
212
  return __awaiter(this, void 0, void 0, function* () {
213
+ const { authToken } = this.assertIsAuthorized();
175
214
  try {
176
- const authorizationResult = this.assertIsAuthorized();
177
- try {
178
- return yield this.transact((wallet) => __awaiter(this, void 0, void 0, function* () {
179
- const freshAuthToken = yield this.performReauthorization(wallet, authorizationResult);
180
- const signedTransactions = yield wallet.signTransaction({
181
- auth_token: freshAuthToken,
182
- transactions,
183
- });
184
- return signedTransactions;
185
- }));
186
- }
187
- catch (error) {
188
- throw new walletAdapterBase.WalletSignTransactionError(error === null || error === void 0 ? void 0 : error.message, error);
189
- }
215
+ return yield this.transact((wallet) => __awaiter(this, void 0, void 0, function* () {
216
+ yield this.performReauthorization(wallet, authToken);
217
+ const signedTransactions = yield wallet.signTransactions({
218
+ transactions,
219
+ });
220
+ return signedTransactions;
221
+ }));
190
222
  }
191
223
  catch (error) {
192
- this.emit('error', error);
193
- throw error;
224
+ throw new walletAdapterBase.WalletSignTransactionError(error === null || error === void 0 ? void 0 : error.message, error);
194
225
  }
195
226
  });
196
227
  }
197
- sendTransaction(transaction, connection, _options) {
228
+ sendTransaction(transaction, connection, options) {
198
229
  return __awaiter(this, void 0, void 0, function* () {
199
- try {
200
- const authorizationResult = this.assertIsAuthorized();
230
+ return yield this.runWithGuard(() => __awaiter(this, void 0, void 0, function* () {
231
+ const { authToken } = this.assertIsAuthorized();
232
+ const minContextSlot = options === null || options === void 0 ? void 0 : options.minContextSlot;
201
233
  try {
202
234
  return yield this.transact((wallet) => __awaiter(this, void 0, void 0, function* () {
203
- const freshAuthToken = yield this.performReauthorization(wallet, authorizationResult);
204
- const signatures = yield wallet.signAndSendTransaction({
205
- auth_token: freshAuthToken,
206
- fee_payer: this.publicKey || undefined,
207
- connection,
235
+ var _a;
236
+ let targetCommitment;
237
+ switch (connection.commitment) {
238
+ case 'confirmed':
239
+ case 'finalized':
240
+ case 'processed':
241
+ targetCommitment = connection.commitment;
242
+ break;
243
+ default:
244
+ targetCommitment = 'finalized';
245
+ }
246
+ let targetPreflightCommitment;
247
+ switch (options === null || options === void 0 ? void 0 : options.preflightCommitment) {
248
+ case 'confirmed':
249
+ case 'finalized':
250
+ case 'processed':
251
+ targetPreflightCommitment = options.preflightCommitment;
252
+ break;
253
+ case undefined:
254
+ targetPreflightCommitment = targetCommitment;
255
+ default:
256
+ targetPreflightCommitment = 'finalized';
257
+ }
258
+ yield Promise.all([
259
+ this.performReauthorization(wallet, authToken),
260
+ (() => __awaiter(this, void 0, void 0, function* () {
261
+ if (transaction.recentBlockhash == null) {
262
+ const preflightCommitmentScore = targetPreflightCommitment === 'finalized'
263
+ ? 2
264
+ : targetPreflightCommitment === 'confirmed'
265
+ ? 1
266
+ : 0;
267
+ const targetCommitmentScore = targetCommitment === 'finalized' ? 2 : targetCommitment === 'confirmed' ? 1 : 0;
268
+ const { blockhash } = yield connection.getLatestBlockhash({
269
+ commitment: preflightCommitmentScore < targetCommitmentScore
270
+ ? targetPreflightCommitment
271
+ : targetCommitment,
272
+ });
273
+ transaction.recentBlockhash = blockhash;
274
+ }
275
+ }))(),
276
+ ]);
277
+ transaction.feePayer || (transaction.feePayer = (_a = this.publicKey) !== null && _a !== void 0 ? _a : undefined);
278
+ const signatures = yield wallet.signAndSendTransactions({
279
+ minContextSlot,
208
280
  transactions: [transaction],
209
281
  });
210
282
  return signatures[0];
@@ -213,34 +285,34 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
213
285
  catch (error) {
214
286
  throw new walletAdapterBase.WalletSendTransactionError(error === null || error === void 0 ? void 0 : error.message, error);
215
287
  }
216
- }
217
- catch (error) {
218
- this.emit('error', error);
219
- throw error;
220
- }
288
+ }));
221
289
  });
222
290
  }
223
291
  signTransaction(transaction) {
224
292
  return __awaiter(this, void 0, void 0, function* () {
225
- const [signedTransaction] = yield this.performSignTransactions([transaction]);
226
- return signedTransaction;
293
+ return yield this.runWithGuard(() => __awaiter(this, void 0, void 0, function* () {
294
+ const [signedTransaction] = yield this.performSignTransactions([transaction]);
295
+ return signedTransaction;
296
+ }));
227
297
  });
228
298
  }
229
299
  signAllTransactions(transactions) {
230
300
  return __awaiter(this, void 0, void 0, function* () {
231
- const signedTransactions = yield this.performSignTransactions(transactions);
232
- return signedTransactions;
301
+ return yield this.runWithGuard(() => __awaiter(this, void 0, void 0, function* () {
302
+ const signedTransactions = yield this.performSignTransactions(transactions);
303
+ return signedTransactions;
304
+ }));
233
305
  });
234
306
  }
235
307
  signMessage(message) {
236
308
  return __awaiter(this, void 0, void 0, function* () {
237
- try {
238
- const authorizationResult = this.assertIsAuthorized();
309
+ return yield this.runWithGuard(() => __awaiter(this, void 0, void 0, function* () {
310
+ const { authToken, selectedAddress } = this.assertIsAuthorized();
239
311
  try {
240
312
  return yield this.transact((wallet) => __awaiter(this, void 0, void 0, function* () {
241
- const freshAuthToken = yield this.performReauthorization(wallet, authorizationResult);
242
- const [signedMessage] = yield wallet.signMessage({
243
- auth_token: freshAuthToken,
313
+ yield this.performReauthorization(wallet, authToken);
314
+ const [signedMessage] = yield wallet.signMessages({
315
+ addresses: [selectedAddress],
244
316
  payloads: [message],
245
317
  });
246
318
  const signature = signedMessage.slice(-SIGNATURE_LENGTH_IN_BYTES);
@@ -250,15 +322,21 @@ class SolanaMobileWalletAdapter extends walletAdapterBase.BaseMessageSignerWalle
250
322
  catch (error) {
251
323
  throw new walletAdapterBase.WalletSignMessageError(error === null || error === void 0 ? void 0 : error.message, error);
252
324
  }
253
- }
254
- catch (error) {
255
- this.emit('error', error);
256
- throw error;
257
- }
325
+ }));
258
326
  });
259
327
  }
260
328
  }
261
329
 
330
+ function createDefaultAddressSelector() {
331
+ return {
332
+ select(addresses) {
333
+ return __awaiter(this, void 0, void 0, function* () {
334
+ return addresses[0];
335
+ });
336
+ },
337
+ };
338
+ }
339
+
262
340
  const CACHE_KEY = 'SolanaMobileWalletAdapterDefaultAuthorizationCache';
263
341
  function createDefaultAuthorizationResultCache() {
264
342
  let storage;
@@ -309,4 +387,5 @@ function createDefaultAuthorizationResultCache() {
309
387
 
310
388
  exports.SolanaMobileWalletAdapter = SolanaMobileWalletAdapter;
311
389
  exports.SolanaMobileWalletAdapterWalletName = SolanaMobileWalletAdapterWalletName;
390
+ exports.createDefaultAddressSelector = createDefaultAddressSelector;
312
391
  exports.createDefaultAuthorizationResultCache = createDefaultAuthorizationResultCache;