@aptos-labs/wallet-adapter-core 3.6.0 → 3.7.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/src/WalletCore.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { HexString, TxnBuilderTypes, Types, BCS } from "aptos";
1
+ import { TxnBuilderTypes, Types, BCS } from "aptos";
2
2
  import {
3
3
  AnyRawTransaction,
4
4
  AccountAuthenticator,
@@ -7,16 +7,13 @@ import {
7
7
  InputGenerateTransactionOptions,
8
8
  Ed25519Signature,
9
9
  AptosConfig,
10
- generateTransactionPayload,
11
10
  InputSubmitTransactionData,
12
11
  PendingTransactionResponse,
13
- InputEntryFunctionDataWithRemoteABI, Aptos,
12
+ Aptos,
14
13
  } from "@aptos-labs/ts-sdk";
15
14
  import EventEmitter from "eventemitter3";
16
- import nacl from "tweetnacl";
17
- import { Buffer } from "buffer";
18
15
 
19
- import { WalletReadyState } from "./constants";
16
+ import { ChainIdToAnsSupportedNetworkMap, WalletReadyState } from "./constants";
20
17
  import {
21
18
  WalletAccountChangeError,
22
19
  WalletAccountError,
@@ -42,41 +39,84 @@ import {
42
39
  WalletCoreEvents,
43
40
  SignMessageResponse,
44
41
  InputTransactionData,
45
- } from "./types";
42
+ WalletName,
43
+ } from "./LegacyWalletPlugins/types";
46
44
  import {
47
45
  removeLocalStorage,
48
46
  setLocalStorage,
49
47
  scopePollingDetectionStrategy,
50
48
  isRedirectable,
51
49
  generalizedErrorMessage,
52
- areBCSArguments,
53
50
  } from "./utils";
54
- import { getNameByAddress } from "./ans";
51
+ import { convertNetwork } from "./LegacyWalletPlugins/conversion";
52
+ import { WalletCoreV1 } from "./LegacyWalletPlugins/WalletCoreV1";
55
53
  import {
56
- convertNetwork,
57
- convertV2TransactionPayloadToV1BCSPayload,
58
- convertV2PayloadToV1JSONPayload,
59
- } from "./conversion";
60
- import { WalletCoreV1 } from "./WalletCoreV1";
54
+ AptosWallet,
55
+ getAptosWallets,
56
+ AccountInfo as StandardAccountInfo,
57
+ NetworkInfo as StandardNetworkInfo,
58
+ UserResponse,
59
+ UserResponseStatus,
60
+ } from "@aptos-labs/wallet-standard";
61
+ import {
62
+ AptosStandardWallet,
63
+ WalletStandardCore,
64
+ } from "./AIP62StandardWallets/WalletStandard";
65
+
66
+ export type IAptosWallet = AptosStandardWallet & Wallet;
61
67
 
62
68
  export class WalletCore extends EventEmitter<WalletCoreEvents> {
69
+ // Private array to hold legacy wallet adapter plugins
63
70
  private _wallets: ReadonlyArray<Wallet> = [];
71
+
72
+ // Private array to hold compatible AIP-62 standard wallets
73
+ private _standard_wallets: ReadonlyArray<AptosStandardWallet> = [];
74
+
75
+ // Private array to hold all wallets (legacy wallet adapter plugins AND compatible AIP-62 standard wallets)
76
+ // while providing support for legacy and new wallet standard
77
+ private _all_wallets: Array<Wallet> = [];
78
+
79
+ // Current connected wallet
64
80
  private _wallet: Wallet | null = null;
81
+
82
+ // Current connected account
65
83
  private _account: AccountInfo | null = null;
84
+
85
+ // Current connected network
66
86
  private _network: NetworkInfo | null = null;
67
- private readonly waletCoreV1: WalletCoreV1 = new WalletCoreV1();
68
87
 
88
+ // WalletCoreV1 property to interact with wallet adapter v1 (legacy wallet adapter plugins) functionality
89
+ private readonly walletCoreV1: WalletCoreV1 = new WalletCoreV1();
90
+
91
+ // WalletStandardCore property to interact with wallet adapter v2 (compatible AIP-62 standard wallets) functionality
92
+ private readonly walletStandardCore: WalletStandardCore =
93
+ new WalletStandardCore();
94
+
95
+ // Indicates whether the dapp is currently connecting with a wallet
69
96
  private _connecting: boolean = false;
97
+
98
+ // Indicates whether the dapp is connected with a wallet
70
99
  private _connected: boolean = false;
71
100
 
101
+ /**
102
+ * Core functionality constructor.
103
+ * For legacy wallet adapter v1 support we expect the dapp to pass in wallet plugins,
104
+ * since AIP-62 standard support this is optional for dapps.
105
+ *
106
+ * @param plugins legacy wallet adapter v1 wallet plugins
107
+ */
72
108
  constructor(plugins: ReadonlyArray<Wallet>) {
73
109
  super();
74
110
  this._wallets = plugins;
111
+ // Stretegy to detect legacy wallet adapter v1 wallet plugins
75
112
  this.scopePollingDetectionStrategy();
113
+ // Stretegy to detect AIP-62 standard compatible wallets
114
+ this.fetchAptosWallets();
76
115
  }
77
116
 
78
117
  private scopePollingDetectionStrategy() {
79
118
  this._wallets?.forEach((wallet: Wallet) => {
119
+ this._all_wallets.push(wallet);
80
120
  if (!wallet.readyState) {
81
121
  wallet.readyState =
82
122
  typeof window === "undefined" || typeof document === "undefined"
@@ -98,6 +138,117 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
98
138
  });
99
139
  }
100
140
 
141
+ private fetchAptosWallets() {
142
+ let { aptosWallets, on } = getAptosWallets();
143
+ this.setWallets(aptosWallets);
144
+
145
+ if (typeof window === "undefined") return;
146
+ // Adds an event listener for new wallets that get registered after the dapp has been loaded,
147
+ // receiving an unsubscribe function, which it can later use to remove the listener
148
+ const that = this;
149
+ const removeRegisterListener = on("register", function () {
150
+ let { aptosWallets } = getAptosWallets();
151
+ that.setWallets(aptosWallets);
152
+ });
153
+
154
+ const removeUnregisterListener = on("unregister", function () {
155
+ let { aptosWallets } = getAptosWallets();
156
+ that.setWallets(aptosWallets);
157
+ });
158
+ }
159
+
160
+ private setWallets(wallets: readonly AptosWallet[]) {
161
+ const aptosStandardWallets: AptosStandardWallet[] = [];
162
+
163
+ wallets.map((wallet: AptosWallet) => {
164
+ const standardWallet = wallet as AptosStandardWallet;
165
+
166
+ standardWallet.readyState = WalletReadyState.Installed;
167
+ aptosStandardWallets.push(wallet);
168
+ this.standardizeStandardWalletToPluginWalletType(standardWallet);
169
+ });
170
+
171
+ this._standard_wallets = aptosStandardWallets;
172
+ }
173
+
174
+ /**
175
+ * To maintain support for both plugins and AIP-62 standard wallets,
176
+ * without introducing dapps breaking changes, we convert
177
+ * AIP-62 standard compatible wallets to the legacy adapter wallet plugin type.
178
+ *
179
+ * @param standardWallet An AIP-62 standard compatible wallet
180
+ */
181
+ private standardizeStandardWalletToPluginWalletType = (
182
+ standardWallet: AptosStandardWallet
183
+ ) => {
184
+ let standardWalletConvertedToWallet: Wallet = {
185
+ name: standardWallet.name as WalletName,
186
+ url: standardWallet.url,
187
+ icon: standardWallet.icon,
188
+ provider: standardWallet,
189
+ connect: standardWallet.features["aptos:connect"].connect,
190
+ disconnect: standardWallet.features["aptos:disconnect"].disconnect,
191
+ network: standardWallet.features["aptos:network"].network,
192
+ account: standardWallet.features["aptos:account"].account,
193
+ signAndSubmitTransaction:
194
+ standardWallet.features["aptos:signAndSubmitTransaction"]
195
+ ?.signAndSubmitTransaction,
196
+ signMessage: standardWallet.features["aptos:signMessage"].signMessage,
197
+ onAccountChange:
198
+ standardWallet.features["aptos:onAccountChange"].onAccountChange,
199
+ onNetworkChange:
200
+ standardWallet.features["aptos:onNetworkChange"].onNetworkChange,
201
+ signTransaction:
202
+ standardWallet.features["aptos:signTransaction"].signTransaction,
203
+ openInMobileApp:
204
+ standardWallet.features["aptos:openInMobileApp"]?.openInMobileApp,
205
+ readyState: WalletReadyState.Installed,
206
+ isAIP62Standard: true,
207
+ };
208
+
209
+ // Remove optional duplications in the _all_wallets array
210
+ this._all_wallets = this._all_wallets.filter(
211
+ (item) => item.name !== standardWalletConvertedToWallet.name
212
+ );
213
+ this._all_wallets.push(standardWalletConvertedToWallet);
214
+
215
+ this.emit("standardWalletsAdded", standardWalletConvertedToWallet);
216
+ };
217
+
218
+ /**
219
+ * Helper function to ensure wallet exists
220
+ *
221
+ * @param wallet A wallet
222
+ */
223
+ private ensureWalletExists(wallet: Wallet | null): asserts wallet is Wallet {
224
+ if (!wallet) {
225
+ throw new WalletNotConnectedError().name;
226
+ }
227
+ if (
228
+ !(
229
+ wallet.readyState === WalletReadyState.Loadable ||
230
+ wallet.readyState === WalletReadyState.Installed
231
+ )
232
+ )
233
+ throw new WalletNotReadyError("Wallet is not set").name;
234
+ }
235
+
236
+ /**
237
+ * Helper function to ensure account exists
238
+ *
239
+ * @param account An account
240
+ */
241
+ private ensureAccountExists(
242
+ account: AccountInfo | null
243
+ ): asserts account is AccountInfo {
244
+ if (!account) {
245
+ throw new WalletAccountError("Account is not set").name;
246
+ }
247
+ }
248
+
249
+ /**
250
+ * @deprecated use ensureWalletExists
251
+ */
101
252
  private doesWalletExist(): boolean | WalletNotConnectedError {
102
253
  if (!this._connected || this._connecting || !this._wallet)
103
254
  throw new WalletNotConnectedError().name;
@@ -111,7 +262,15 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
111
262
  return true;
112
263
  }
113
264
 
114
- private clearData() {
265
+ /**
266
+ * Function to cleat wallet adapter data.
267
+ *
268
+ * - Removes current connected wallet state
269
+ * - Removes current connected account state
270
+ * - Removes current connected network state
271
+ * - Removes autoconnect local storage value
272
+ */
273
+ private clearData(): void {
115
274
  this._connected = false;
116
275
  this.setWallet(null);
117
276
  this.setAccount(null);
@@ -119,38 +278,153 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
119
278
  removeLocalStorage();
120
279
  }
121
280
 
122
- private async setAnsName() {
281
+ /**
282
+ * Queries and sets ANS name for the current connected wallet account
283
+ */
284
+ private async setAnsName(): Promise<void> {
123
285
  if (this._network?.chainId && this._account) {
124
- const name = await getNameByAddress(
125
- this._network.chainId,
126
- this._account.address
127
- );
286
+ // ANS supports only MAINNET or TESTNET
287
+ if (!ChainIdToAnsSupportedNetworkMap[this._network.chainId]) {
288
+ this._account.ansName = undefined;
289
+ return;
290
+ }
291
+
292
+ const aptosConfig = new AptosConfig({
293
+ network: convertNetwork(this._network),
294
+ });
295
+ const aptos = new Aptos(aptosConfig);
296
+ const name = await aptos.ans.getPrimaryName({
297
+ address: this._account.address,
298
+ });
299
+
128
300
  this._account.ansName = name;
129
301
  }
130
302
  }
131
303
 
132
- setWallet(wallet: Wallet | null) {
304
+ /**
305
+ * Sets the connected wallet
306
+ *
307
+ * @param wallet A wallet
308
+ */
309
+ setWallet(wallet: Wallet | null): void {
133
310
  this._wallet = wallet;
134
311
  }
135
312
 
136
- setAccount(account: AccountInfo | null) {
137
- this._account = account;
313
+ /**
314
+ * Sets the connected account
315
+ *
316
+ * `AccountInfo` type comes from a legacy wallet adapter plugin
317
+ * `StandardAccountInfo` type comes from AIP-62 standard compatible wallet when onAccountChange event is called
318
+ * `UserResponse<StandardAccountInfo>` type comes from AIP-62 standard compatible wallet on wallet connect
319
+ *
320
+ * @param account An account
321
+ */
322
+ setAccount(
323
+ account:
324
+ | AccountInfo
325
+ | StandardAccountInfo
326
+ | UserResponse<StandardAccountInfo>
327
+ | null
328
+ ): void {
329
+ if (account === null) {
330
+ this._account = null;
331
+ return;
332
+ }
333
+
334
+ // Check if wallet is of type AIP-62 standard
335
+ if (this._wallet?.isAIP62Standard) {
336
+ // Check if account is of type UserResponse<StandardAccountInfo> which means the `account`
337
+ // comes from the `connect` method
338
+ if ("status" in account) {
339
+ const connectStandardAccount =
340
+ account as UserResponse<StandardAccountInfo>;
341
+ if (connectStandardAccount.status === UserResponseStatus.REJECTED) {
342
+ this._connecting = false;
343
+ throw new WalletConnectionError("User has rejected the request")
344
+ .message;
345
+ }
346
+ // account is of type
347
+ this._account = {
348
+ address: connectStandardAccount.args.address.toString(),
349
+ publicKey: connectStandardAccount.args.publicKey.toString(),
350
+ ansName: connectStandardAccount.args.ansName,
351
+ };
352
+ return;
353
+ } else {
354
+ // account is of type `StandardAccountInfo` which means it comes from onAccountChange event
355
+ const standardAccount = account as StandardAccountInfo;
356
+ this._account = {
357
+ address: standardAccount.address.toString(),
358
+ publicKey: standardAccount.publicKey.toString(),
359
+ ansName: standardAccount.ansName,
360
+ };
361
+ return;
362
+ }
363
+ }
364
+
365
+ // account is of type `AccountInfo`
366
+ this._account = { ...(account as AccountInfo) };
367
+ return;
138
368
  }
139
369
 
140
- setNetwork(network: NetworkInfo | null) {
141
- this._network = network;
370
+ /**
371
+ * Sets the connected network
372
+ *
373
+ * `NetworkInfo` type comes from a legacy wallet adapter plugin
374
+ * `StandardNetworkInfo` type comes from AIP-62 standard compatible wallet
375
+ *
376
+ * @param network A network
377
+ */
378
+ setNetwork(network: NetworkInfo | StandardNetworkInfo | null): void {
379
+ if (network === null) {
380
+ this._network = null;
381
+ return;
382
+ }
383
+ if (this._wallet?.isAIP62Standard) {
384
+ const standardizeNetwork = network as StandardNetworkInfo;
385
+ this._network = {
386
+ name: standardizeNetwork.name,
387
+ chainId: standardizeNetwork.chainId.toString(),
388
+ url: standardizeNetwork.url,
389
+ };
390
+ return;
391
+ }
392
+ this._network = { ...(network as NetworkInfo) };
142
393
  }
143
394
 
395
+ /**
396
+ * Helper function to detect whether a wallet is connected
397
+ *
398
+ * @returns boolean
399
+ */
144
400
  isConnected(): boolean {
145
401
  return this._connected;
146
402
  }
147
403
 
404
+ /**
405
+ * Getter to fetch all detected wallets
406
+ */
148
407
  get wallets(): ReadonlyArray<Wallet> {
408
+ return this._all_wallets;
409
+ }
410
+
411
+ /**
412
+ * Getter to fetch all detected plugin wallets
413
+ */
414
+ get pluginWallets(): ReadonlyArray<Wallet> {
149
415
  return this._wallets;
150
416
  }
151
417
 
418
+ /**
419
+ * Getter to fetch all detected AIP-62 standard compatible wallets
420
+ */
421
+ get standardWallets(): ReadonlyArray<AptosStandardWallet> {
422
+ return this._standard_wallets;
423
+ }
424
+
152
425
  /**
153
426
  * Getter for the current connected wallet
427
+ *
154
428
  * @return wallet info
155
429
  * @throws WalletNotSelectedError
156
430
  */
@@ -169,6 +443,7 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
169
443
 
170
444
  /**
171
445
  * Getter for the current connected account
446
+ *
172
447
  * @return account info
173
448
  * @throws WalletAccountError
174
449
  */
@@ -182,6 +457,7 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
182
457
 
183
458
  /**
184
459
  * Getter for the current wallet network
460
+ *
185
461
  * @return network info
186
462
  * @throws WalletGetNetworkError
187
463
  */
@@ -194,17 +470,18 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
194
470
  }
195
471
 
196
472
  /**
197
- * We first make sure we can connect a dapp to a wallet.
198
- * If all good, we connect the wallet by calling `this.connectWallet`
199
- * @param walletName. The wallet name we want to connect.
473
+ * Helper function to run some checks before we connect with a wallet.
474
+ *
475
+ * @param walletName. The wallet name we want to connect with.
200
476
  */
201
477
  async connect(walletName: string): Promise<void | string> {
202
- const selectedWallet = this._wallets?.find(
478
+ // Checks the wallet exists in the detected wallets array
479
+ const selectedWallet = this._all_wallets.find(
203
480
  (wallet: Wallet) => wallet.name === walletName
204
481
  );
205
-
206
482
  if (!selectedWallet) return;
207
483
 
484
+ // Check if wallet is already connected
208
485
  if (this._connected) {
209
486
  // if the selected wallet is already connected, we don't need to connect again
210
487
  if (this._wallet?.name === walletName)
@@ -213,16 +490,22 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
213
490
  ).message;
214
491
  }
215
492
 
216
- // check if we are in a redirectable view (i.e on mobile AND not in an in-app browser) and
217
- // since wallet readyState can be NotDetected, we check it before the next check
493
+ // Check if we are in a redirectable view (i.e on mobile AND not in an in-app browser)
218
494
  if (isRedirectable()) {
219
495
  // use wallet deep link
496
+ if (selectedWallet.isAIP62Standard && selectedWallet.openInMobileApp) {
497
+ selectedWallet.openInMobileApp();
498
+ return;
499
+ }
220
500
  if (selectedWallet.deeplinkProvider) {
221
501
  const url = encodeURIComponent(window.location.href);
222
502
  const location = selectedWallet.deeplinkProvider({ url });
223
503
  window.location.href = location;
224
504
  }
505
+ return;
225
506
  }
507
+
508
+ // Check wallet state is Installed or Loadable
226
509
  if (
227
510
  selectedWallet.readyState !== WalletReadyState.Installed &&
228
511
  selectedWallet.readyState !== WalletReadyState.Loadable
@@ -243,21 +526,20 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
243
526
  * @emit emits "connect" event
244
527
  * @throws WalletConnectionError
245
528
  */
246
- async connectWallet(selectedWallet: Wallet) {
529
+ async connectWallet(selectedWallet: Wallet): Promise<void> {
247
530
  try {
248
531
  this._connecting = true;
249
532
  this.setWallet(selectedWallet);
250
533
  const account = await selectedWallet.connect();
251
- this.setAccount({ ...account });
534
+ this.setAccount(account);
252
535
  const network = await selectedWallet.network();
253
- this.setNetwork({ ...network });
536
+ this.setNetwork(network);
254
537
  await this.setAnsName();
255
538
  setLocalStorage(selectedWallet.name);
256
539
  this._connected = true;
257
540
  this.emit("connect", account);
258
541
  } catch (error: any) {
259
542
  this.clearData();
260
-
261
543
  const errMsg = generalizedErrorMessage(error);
262
544
  throw new WalletConnectionError(errMsg).message;
263
545
  } finally {
@@ -266,15 +548,16 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
266
548
  }
267
549
 
268
550
  /**
269
- Disconnect the exisitng wallet. On success, we clear the
270
- current account, current network and LocalStorage data.
271
- @emit emits "disconnect" event
272
- @throws WalletDisconnectionError
273
- */
551
+ * Disconnect the current connected wallet. On success, we clear the
552
+ * current account, current network and LocalStorage data.
553
+ *
554
+ * @emit emits "disconnect" event
555
+ * @throws WalletDisconnectionError
556
+ */
274
557
  async disconnect(): Promise<void> {
275
558
  try {
276
- this.doesWalletExist();
277
- await this._wallet?.disconnect();
559
+ this.ensureWalletExists(this._wallet);
560
+ await this._wallet.disconnect();
278
561
  this.clearData();
279
562
  this.emit("disconnect");
280
563
  } catch (error: any) {
@@ -296,65 +579,57 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
296
579
  { hash: Types.HexEncodedBytes; output?: any } | PendingTransactionResponse
297
580
  > {
298
581
  try {
299
- this.doesWalletExist();
300
-
301
- // wallet supports sdk v2
302
- if (this._wallet?.version === "v2") {
303
- const response = await this._wallet.signAndSubmitTransaction({
304
- ...transactionInput,
305
- sender: transactionInput.sender ?? this._account!.address,
306
- });
307
- // response should be PendingTransactionResponse
308
- return response;
309
- }
582
+ this.ensureWalletExists(this._wallet);
583
+ this.ensureAccountExists(this._account);
310
584
 
311
585
  // get the payload piece from the input
312
586
  const payloadData = transactionInput.data;
587
+ const aptosConfig = new AptosConfig({
588
+ network: convertNetwork(this._network),
589
+ });
313
590
 
314
- // first check if each argument is a BCS serialized argument
315
- if (areBCSArguments(payloadData.functionArguments)) {
316
- const aptosConfig = new AptosConfig({
317
- network: convertNetwork(this._network),
318
- });
319
- const newPayload = await generateTransactionPayload({
320
- ...(payloadData as InputEntryFunctionDataWithRemoteABI),
321
- aptosConfig: aptosConfig,
322
- });
323
- const oldTransactionPayload =
324
- convertV2TransactionPayloadToV1BCSPayload(newPayload);
325
- const response = await this.waletCoreV1.signAndSubmitBCSTransaction(
326
- oldTransactionPayload,
327
- this._wallet!,
328
- {
329
- max_gas_amount: transactionInput.options?.maxGasAmount
330
- ? BigInt(transactionInput.options?.maxGasAmount)
331
- : undefined,
332
- gas_unit_price: transactionInput.options?.gasUnitPrice
333
- ? BigInt(transactionInput.options?.gasUnitPrice)
334
- : undefined,
335
- }
336
- );
337
- const { hash, ...output } = response;
338
- return { hash, output };
339
- }
340
- // if it is not a bcs serialized arguments transaction, convert to the old
341
- // json format
342
- const oldTransactionPayload =
343
- convertV2PayloadToV1JSONPayload(payloadData);
344
- const response = await this.waletCoreV1.signAndSubmitTransaction(
345
- oldTransactionPayload,
346
- this._wallet!,
347
- {
348
- max_gas_amount: transactionInput.options?.maxGasAmount
349
- ? BigInt(transactionInput.options?.maxGasAmount)
350
- : undefined,
351
- gas_unit_price: transactionInput.options?.gasUnitPrice
352
- ? BigInt(transactionInput.options?.gasUnitPrice)
353
- : undefined,
591
+ const aptos = new Aptos(aptosConfig);
592
+
593
+ if (this._wallet.signAndSubmitTransaction) {
594
+ // if wallet is compatible with the AIP-62 standard
595
+ if (this._wallet.isAIP62Standard) {
596
+ const { hash, ...output } =
597
+ await this.walletStandardCore.signAndSubmitTransaction(
598
+ transactionInput,
599
+ aptos,
600
+ this._account,
601
+ this._wallet
602
+ );
603
+ return { hash, output };
604
+ } else {
605
+ // Else use wallet plugin
606
+ const { hash, ...output } =
607
+ await this.walletCoreV1.resolveSignAndSubmitTransaction(
608
+ payloadData,
609
+ this._network,
610
+ this._wallet,
611
+ transactionInput
612
+ );
613
+ return { hash, output };
354
614
  }
355
- );
356
- const { hash, ...output } = response;
357
- return { hash, output };
615
+ }
616
+
617
+ // If wallet does not support signAndSubmitTransaction
618
+ // the adapter will sign and submit it for the dapp.
619
+ // Note: This should happen only for AIP-62 standard compatible wallets since
620
+ // signAndSubmitTransaction is not a required function implementation
621
+ const transaction = await aptos.transaction.build.simple({
622
+ sender: this._account.address,
623
+ data: transactionInput.data,
624
+ options: transactionInput.options,
625
+ });
626
+
627
+ const senderAuthenticator = await this.signTransaction(transaction);
628
+ const response = await this.submitTransaction({
629
+ transaction,
630
+ senderAuthenticator,
631
+ });
632
+ return response;
358
633
  } catch (error: any) {
359
634
  const errMsg = generalizedErrorMessage(error);
360
635
  throw new WalletSignAndSubmitMessageError(errMsg).message;
@@ -377,60 +652,83 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
377
652
  options?: InputGenerateTransactionOptions
378
653
  ): Promise<AccountAuthenticator> {
379
654
  try {
380
- this.doesWalletExist();
381
- // if input is AnyRawTransaction, i.e V2
382
- if ("rawTransaction" in transactionOrPayload) {
383
- if (this._wallet?.version !== "v2") {
384
- throw new WalletNotSupportedMethod(
385
- `Sign Transaction V2 is not supported by ${this.wallet?.name}`
386
- ).message;
655
+ this.ensureWalletExists(this._wallet);
656
+ this.ensureAccountExists(this._account);
657
+
658
+ // Make sure wallet supports signTransaction
659
+ if (this._wallet.signTransaction) {
660
+ // If current connected wallet is AIP-62 standard compatible
661
+ // we want to make sure the transaction input is what the
662
+ // standard expects, i,e new sdk v2 input
663
+ if (this._wallet.isAIP62Standard) {
664
+ // if rawTransaction prop it means transaction input data is
665
+ // compatible with new sdk v2 input
666
+ if ("rawTransaction" in transactionOrPayload) {
667
+ return await this.walletStandardCore.signTransaction(
668
+ transactionOrPayload,
669
+ this._wallet,
670
+ asFeePayer
671
+ );
672
+ } else {
673
+ // else it means dapp passes legacy sdk v1 input data
674
+ // and the current connected wallet does not support it
675
+ throw new WalletNotSupportedMethod(
676
+ `Invalid transaction input data was provided, ${this.wallet?.name} expects AnyRawTransaction type.
677
+ Please upgrade to the latest Aptos TS SDK https://github.com/aptos-labs/aptos-ts-sdk`
678
+ ).message;
679
+ }
387
680
  }
388
- const accountAuthenticator = await (this._wallet as any).signTransaction(
389
- transactionOrPayload,
390
- asFeePayer
391
- );
392
681
 
393
- return accountAuthenticator;
394
- }
682
+ // If current connected wallet is legacy compatible with wallet standard
395
683
 
396
- // check current signTransaction function exists
397
- if (this._wallet && !("signTransaction" in this._wallet)) {
398
- throw new WalletNotSupportedMethod(
399
- `Sign Transaction is not supported by ${this.wallet?.name}`
400
- ).message;
401
- }
684
+ // if input is AnyRawTransaction, i.e new sdk v2 input
685
+ if ("rawTransaction" in transactionOrPayload) {
686
+ const accountAuthenticator = (await this._wallet.signTransaction(
687
+ transactionOrPayload,
688
+ asFeePayer
689
+ )) as AccountAuthenticator;
402
690
 
403
- const response = await this.waletCoreV1.signTransaction(
404
- transactionOrPayload as Types.TransactionPayload,
405
- this._wallet!,
406
- {
407
- max_gas_amount: options?.maxGasAmount
408
- ? BigInt(options?.maxGasAmount)
409
- : undefined,
410
- gas_unit_price: options?.gasUnitPrice
411
- ? BigInt(options?.gasUnitPrice)
412
- : undefined,
413
- }
414
- );
415
- if (!response) {
416
- throw new Error("error");
417
- }
691
+ return accountAuthenticator;
692
+ } else {
693
+ const response = await this.walletCoreV1.signTransaction(
694
+ transactionOrPayload as Types.TransactionPayload,
695
+ this._wallet!,
696
+ {
697
+ max_gas_amount: options?.maxGasAmount
698
+ ? BigInt(options?.maxGasAmount)
699
+ : undefined,
700
+ gas_unit_price: options?.gasUnitPrice
701
+ ? BigInt(options?.gasUnitPrice)
702
+ : undefined,
703
+ }
704
+ );
418
705
 
419
- // Convert retuned bcs serialized SignedTransaction into V2 AccountAuthenticator
420
- const deserializer1 = new BCS.Deserializer(response);
421
- const deserializedSignature =
422
- TxnBuilderTypes.SignedTransaction.deserialize(deserializer1);
423
- const transactionAuthenticator =
424
- deserializedSignature.authenticator as TxnBuilderTypes.TransactionAuthenticatorEd25519;
706
+ if (!response) {
707
+ throw new Error("error");
708
+ }
425
709
 
426
- const publicKey = transactionAuthenticator.public_key.value;
427
- const signature = transactionAuthenticator.signature.value;
710
+ // Convert retuned bcs serialized SignedTransaction into V2 AccountAuthenticator
711
+ const deserializer1 = new BCS.Deserializer(response);
712
+ const deserializedSignature =
713
+ TxnBuilderTypes.SignedTransaction.deserialize(deserializer1);
714
+ const transactionAuthenticator =
715
+ deserializedSignature.authenticator as TxnBuilderTypes.TransactionAuthenticatorEd25519;
716
+
717
+ const publicKey = transactionAuthenticator.public_key.value;
718
+ const signature = transactionAuthenticator.signature.value;
719
+
720
+ const accountAuthenticator = new AccountAuthenticatorEd25519(
721
+ new Ed25519PublicKey(publicKey),
722
+ new Ed25519Signature(signature)
723
+ );
724
+ return accountAuthenticator;
725
+ }
726
+ }
428
727
 
429
- const accountAuthenticator = new AccountAuthenticatorEd25519(
430
- new Ed25519PublicKey(publicKey),
431
- new Ed25519Signature(signature)
432
- );
433
- return accountAuthenticator;
728
+ // If we are here it means this wallet does not support signTransaction
729
+ throw new WalletNotSupportedMethod(
730
+ `Sign Transaction is not supported by ${this.wallet?.name}`
731
+ ).message;
434
732
  } catch (error: any) {
435
733
  const errMsg = generalizedErrorMessage(error);
436
734
  throw new WalletSignTransactionError(errMsg).message;
@@ -438,49 +736,62 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
438
736
  }
439
737
 
440
738
  /**
441
- Sign message (doesnt submit to chain).
442
- @param message
443
- @return response from the wallet's signMessage function
444
- @throws WalletSignMessageError
739
+ * Sign message (doesnt submit to chain).
740
+ *
741
+ * @param message
742
+ * @return response from the wallet's signMessage function
743
+ * @throws WalletSignMessageError
445
744
  */
446
745
  async signMessage(message: SignMessagePayload): Promise<SignMessageResponse> {
447
746
  try {
448
- this.doesWalletExist();
747
+ this.ensureWalletExists(this._wallet);
748
+
749
+ if (this._wallet.isAIP62Standard) {
750
+ return await this.walletStandardCore.signMessage(message, this._wallet);
751
+ }
449
752
  const response = await this._wallet!.signMessage(message);
450
- return response;
753
+ console.log("signMessage response", response);
754
+ return response as SignMessageResponse;
451
755
  } catch (error: any) {
452
756
  const errMsg = generalizedErrorMessage(error);
453
757
  throw new WalletSignMessageError(errMsg).message;
454
758
  }
455
759
  }
456
760
 
761
+ /**
762
+ * Submits transaction to chain
763
+ *
764
+ * @param transaction
765
+ * @returns PendingTransactionResponse
766
+ */
457
767
  async submitTransaction(
458
768
  transaction: InputSubmitTransactionData
459
769
  ): Promise<PendingTransactionResponse> {
460
770
  try {
461
- this.doesWalletExist();
462
- if (this._wallet && !("submitTransaction" in this._wallet)) {
463
- const { additionalSignersAuthenticators } = transaction;
464
- const aptosConfig = new AptosConfig({
465
- network: convertNetwork(this.network),
466
- });
467
- const aptos = new Aptos(aptosConfig);
468
- if (additionalSignersAuthenticators !== undefined) {
469
- const multiAgentTxn = {
470
- ...transaction,
471
- additionalSignersAuthenticators,
472
- };
473
- return aptos.transaction.submit.multiAgent(multiAgentTxn);
474
- } else {
475
- return aptos.transaction.submit.simple(transaction);
476
- }
477
- }
771
+ this.ensureWalletExists(this._wallet);
478
772
 
479
- const pendingTransaction = await (this._wallet as any).submitTransaction(
480
- transaction
481
- );
773
+ // If wallet supports submitTransaction transaction function
774
+ if (this._wallet.submitTransaction) {
775
+ const pendingTransaction =
776
+ await this._wallet.submitTransaction(transaction);
777
+ return pendingTransaction;
778
+ }
482
779
 
483
- return pendingTransaction;
780
+ // Else have the adpater submits the transaction
781
+ const { additionalSignersAuthenticators } = transaction;
782
+ const aptosConfig = new AptosConfig({
783
+ network: convertNetwork(this.network),
784
+ });
785
+ const aptos = new Aptos(aptosConfig);
786
+ if (additionalSignersAuthenticators !== undefined) {
787
+ const multiAgentTxn = {
788
+ ...transaction,
789
+ additionalSignersAuthenticators,
790
+ };
791
+ return aptos.transaction.submit.multiAgent(multiAgentTxn);
792
+ } else {
793
+ return aptos.transaction.submit.simple(transaction);
794
+ }
484
795
  } catch (error: any) {
485
796
  const errMsg = generalizedErrorMessage(error);
486
797
  throw new WalletSignTransactionError(errMsg).message;
@@ -494,12 +805,14 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
494
805
  */
495
806
  async onAccountChange(): Promise<void> {
496
807
  try {
497
- this.doesWalletExist();
498
- await this._wallet?.onAccountChange(async (data: AccountInfo) => {
499
- this.setAccount({ ...data });
500
- await this.setAnsName();
501
- this.emit("accountChange", this._account);
502
- });
808
+ this.ensureWalletExists(this._wallet);
809
+ await this._wallet.onAccountChange(
810
+ async (data: AccountInfo | StandardAccountInfo) => {
811
+ this.setAccount(data);
812
+ await this.setAnsName();
813
+ this.emit("accountChange", this._account);
814
+ }
815
+ );
503
816
  } catch (error: any) {
504
817
  const errMsg = generalizedErrorMessage(error);
505
818
  throw new WalletAccountChangeError(errMsg).message;
@@ -513,12 +826,14 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
513
826
  */
514
827
  async onNetworkChange(): Promise<void> {
515
828
  try {
516
- this.doesWalletExist();
517
- await this._wallet?.onNetworkChange(async (data: NetworkInfo) => {
518
- this.setNetwork({ ...data });
519
- await this.setAnsName();
520
- this.emit("networkChange", this._network);
521
- });
829
+ this.ensureWalletExists(this._wallet);
830
+ await this._wallet.onNetworkChange(
831
+ async (data: NetworkInfo | StandardNetworkInfo) => {
832
+ this.setNetwork(data);
833
+ await this.setAnsName();
834
+ this.emit("networkChange", this._network);
835
+ }
836
+ );
522
837
  } catch (error: any) {
523
838
  const errMsg = generalizedErrorMessage(error);
524
839
  throw new WalletNetworkChangeError(errMsg).message;
@@ -532,67 +847,22 @@ export class WalletCore extends EventEmitter<WalletCoreEvents> {
532
847
  */
533
848
  async signMessageAndVerify(message: SignMessagePayload): Promise<boolean> {
534
849
  try {
535
- this.doesWalletExist();
536
- if (!this._account) throw new Error("No account found!");
537
- const response = await this._wallet?.signMessage(message);
538
- if (!response)
539
- throw new WalletSignMessageAndVerifyError("Failed to sign a message")
540
- .message;
541
- // Verify that the bytes were signed using the private key that matches the known public key
542
- let verified = false;
543
- if (Array.isArray(response.signature)) {
544
- // multi sig wallets
545
- const { fullMessage, signature, bitmap } = response;
546
- if (bitmap) {
547
- const minKeysRequired = this._account.minKeysRequired as number;
548
- if (signature.length < minKeysRequired) {
549
- verified = false;
550
- } else {
551
- // Getting an array which marks the keys signing the message with 1, while marking 0 for the keys not being used.
552
- const bits = Array.from(bitmap).flatMap((n) =>
553
- Array.from({ length: 8 }).map((_, i) => (n >> i) & 1)
554
- );
555
- // Filter out indexes of the keys we need
556
- const index = bits.map((_, i) => i).filter((i) => bits[i]);
557
-
558
- const publicKeys = this._account.publicKey as string[];
559
- const matchedPublicKeys = publicKeys.filter(
560
- (_: string, i: number) => index.includes(i)
561
- );
562
-
563
- verified = true;
564
- for (let i = 0; i < signature.length; i++) {
565
- const isSigVerified = nacl.sign.detached.verify(
566
- Buffer.from(fullMessage),
567
- Buffer.from(signature[i], "hex"),
568
- Buffer.from(matchedPublicKeys[i], "hex")
569
- ); // `isSigVerified` should be `true` for every signature
570
-
571
- if (!isSigVerified) {
572
- verified = false;
573
- break;
574
- }
575
- }
576
- }
577
- } else {
578
- throw new WalletSignMessageAndVerifyError("Failed to get a bitmap")
579
- .message;
580
- }
581
- } else {
582
- // single sig wallets
583
- // support for when address doesnt have hex prefix (0x)
584
- const currentAccountPublicKey = new HexString(
585
- this._account.publicKey as string
586
- );
587
- // support for when address doesnt have hex prefix (0x)
588
- const signature = new HexString(response.signature);
589
- verified = nacl.sign.detached.verify(
590
- Buffer.from(response.fullMessage),
591
- Buffer.from(signature.noPrefix(), "hex"),
592
- Buffer.from(currentAccountPublicKey.noPrefix(), "hex")
850
+ this.ensureWalletExists(this._wallet);
851
+ this.ensureAccountExists(this._account);
852
+
853
+ // If current connected wallet is AIP-62 standard compatible
854
+ if (this._wallet.isAIP62Standard) {
855
+ return this.walletStandardCore.signMessageAndVerify(
856
+ message,
857
+ this._wallet
593
858
  );
594
859
  }
595
- return verified;
860
+
861
+ return await this.walletCoreV1.signMessageAndVerify(
862
+ message,
863
+ this._wallet,
864
+ this._account
865
+ );
596
866
  } catch (error: any) {
597
867
  const errMsg = generalizedErrorMessage(error);
598
868
  throw new WalletSignMessageAndVerifyError(errMsg).message;