@hashgraph/hedera-wallet-connect 1.3.6-canary.67ec2cf.0 → 1.3.7-canary.813a6d2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  import { Signer, AccountBalance, AccountId, AccountInfo, Executable, Key, LedgerId, SignerSignature, Transaction, TransactionRecord } from '@hashgraph/sdk';
2
- import type { ISignClient } from '@walletconnect/types';
2
+ import type { CoreTypes, ISignClient } from '@walletconnect/types';
3
3
  export declare class DAppSigner implements Signer {
4
4
  private readonly accountId;
5
5
  private readonly signClient;
@@ -24,6 +24,7 @@ export declare class DAppSigner implements Signer {
24
24
  getAccountBalance(): Promise<AccountBalance>;
25
25
  getAccountInfo(): Promise<AccountInfo>;
26
26
  getAccountRecords(): Promise<TransactionRecord[]>;
27
+ getMetadata(): CoreTypes.Metadata;
27
28
  sign(data: Uint8Array[], signOptions?: Record<string, any>): Promise<SignerSignature[]>;
28
29
  checkTransaction<T extends Transaction>(transaction: T): Promise<T>;
29
30
  populateTransaction<T extends Transaction>(transaction: T): Promise<T>;
@@ -49,8 +49,9 @@ export class DAppSigner {
49
49
  return allNodes.slice(0, numberOfNodes);
50
50
  }
51
51
  request(request) {
52
- if (this.extensionId)
52
+ if (this.extensionId) {
53
53
  extensionOpen(this.extensionId);
54
+ }
54
55
  return this.signClient.request({
55
56
  topic: this.topic,
56
57
  request,
@@ -81,6 +82,9 @@ export class DAppSigner {
81
82
  getAccountRecords() {
82
83
  return this.call(new AccountRecordsQuery().setAccountId(this.accountId));
83
84
  }
85
+ getMetadata() {
86
+ return this.signClient.metadata;
87
+ }
84
88
  async sign(data, signOptions) {
85
89
  const { signatureMap } = await this.request({
86
90
  method: HederaJsonRpcMethod.SignMessage,
@@ -7,6 +7,7 @@ import { DAppSigner } from './DAppSigner';
7
7
  export * from './DAppSigner';
8
8
  type BaseLogger = 'error' | 'warn' | 'info' | 'debug' | 'trace' | 'fatal';
9
9
  export declare class DAppConnector {
10
+ private logger;
10
11
  dAppMetadata: SignClientTypes.Metadata;
11
12
  network: LedgerId;
12
13
  projectId: string;
@@ -27,8 +28,14 @@ export declare class DAppConnector {
27
28
  * @param methods - Array of supported methods for the DApp (optional).
28
29
  * @param events - Array of supported events for the DApp (optional).
29
30
  * @param chains - Array of supported chains for the DApp (optional).
31
+ * @param logLevel - Logging level for the DAppConnector (optional).
30
32
  */
31
- constructor(metadata: SignClientTypes.Metadata, network: LedgerId, projectId: string, methods?: string[], events?: string[], chains?: string[]);
33
+ constructor(metadata: SignClientTypes.Metadata, network: LedgerId, projectId: string, methods?: string[], events?: string[], chains?: string[], logLevel?: 'error' | 'warn' | 'info' | 'debug');
34
+ /**
35
+ * Sets the logging level for the DAppConnector
36
+ * @param level - The logging level to set
37
+ */
38
+ setLogLevel(level: 'error' | 'warn' | 'info' | 'debug'): void;
32
39
  /**
33
40
  * Initializes the DAppConnector instance.
34
41
  * @param logger - `BaseLogger` for logging purposes (optional).
@@ -71,6 +78,16 @@ export declare class DAppConnector {
71
78
  * @returns A Promise that resolves when the connection process is complete.
72
79
  */
73
80
  connectExtension(extensionId: string, pairingTopic?: string): Promise<SessionTypes.Struct>;
81
+ /**
82
+ * Validates the session by checking if the session exists.
83
+ * @param topic - The topic of the session to validate.
84
+ * @returns {boolean} - True if the session exists, false otherwise.
85
+ */
86
+ private validateSession;
87
+ /**
88
+ * Validates the session and refreshes the signers by removing the invalid ones.
89
+ */
90
+ private validateAndRefreshSigners;
74
91
  /**
75
92
  * Initiates the WallecConnect connection if the wallet in iframe mode is detected.
76
93
  */
@@ -81,7 +98,7 @@ export declare class DAppConnector {
81
98
  * @param topic - The topic of the session to disconnect.
82
99
  * @returns A Promise that resolves when the session is disconnected.
83
100
  */
84
- disconnect(topic: string): Promise<void>;
101
+ disconnect(topic: string): Promise<boolean>;
85
102
  /**
86
103
  * Disconnects all active sessions and pairings.
87
104
  *
@@ -127,7 +144,7 @@ export declare class DAppConnector {
127
144
  * @example
128
145
  * ```ts
129
146
  * const params = {
130
- * signerAccountId: '0.0.12345',
147
+ * signerAccountId: 'hedera:testnet:0.0.12345',
131
148
  * message: 'Hello World!'
132
149
  * }
133
150
  *
@@ -22,6 +22,7 @@ import QRCodeModal from '@walletconnect/qrcode-modal';
22
22
  import { WalletConnectModal } from '@walletconnect/modal';
23
23
  import SignClient from '@walletconnect/sign-client';
24
24
  import { getSdkError } from '@walletconnect/utils';
25
+ import { DefaultLogger } from '../shared/logger';
25
26
  import { HederaJsonRpcMethod, accountAndLedgerFromSession, networkNamespaces, extensionConnect, findExtensions, } from '../shared';
26
27
  import { DAppSigner } from './DAppSigner';
27
28
  export * from './DAppSigner';
@@ -34,8 +35,9 @@ export class DAppConnector {
34
35
  * @param methods - Array of supported methods for the DApp (optional).
35
36
  * @param events - Array of supported events for the DApp (optional).
36
37
  * @param chains - Array of supported chains for the DApp (optional).
38
+ * @param logLevel - Logging level for the DAppConnector (optional).
37
39
  */
38
- constructor(metadata, network, projectId, methods, events, chains) {
40
+ constructor(metadata, network, projectId, methods, events, chains, logLevel = 'debug') {
39
41
  this.network = LedgerId.TESTNET;
40
42
  this.supportedMethods = [];
41
43
  this.supportedEvents = [];
@@ -62,6 +64,7 @@ export class DAppConnector {
62
64
  }
63
65
  });
64
66
  };
67
+ this.logger = new DefaultLogger(logLevel);
65
68
  this.dAppMetadata = metadata;
66
69
  this.network = network;
67
70
  this.projectId = projectId;
@@ -77,6 +80,15 @@ export class DAppConnector {
77
80
  this.extensions.push(Object.assign(Object.assign({}, metadata), { available: true, availableInIframe: isIframe }));
78
81
  });
79
82
  }
83
+ /**
84
+ * Sets the logging level for the DAppConnector
85
+ * @param level - The logging level to set
86
+ */
87
+ setLogLevel(level) {
88
+ if (this.logger instanceof DefaultLogger) {
89
+ this.logger.setLogLevel(level);
90
+ }
91
+ }
80
92
  /**
81
93
  * Initializes the DAppConnector instance.
82
94
  * @param logger - `BaseLogger` for logging purposes (optional).
@@ -87,20 +99,24 @@ export class DAppConnector {
87
99
  if (!this.projectId) {
88
100
  throw new Error('Project ID is not defined');
89
101
  }
90
- this.walletConnectClient = await SignClient.init({
102
+ const signClient = await SignClient.init({
91
103
  logger,
92
104
  relayUrl: 'wss://relay.walletconnect.com',
93
105
  projectId: this.projectId,
94
106
  metadata: this.dAppMetadata,
95
107
  });
108
+ this.walletConnectClient = signClient;
96
109
  const existingSessions = this.walletConnectClient.session.getAll();
97
- if (existingSessions.length > 0)
110
+ if (existingSessions.length > 0) {
98
111
  this.signers = existingSessions.flatMap((session) => this.createSigners(session));
99
- else
100
- this.checkIframeConnect();
112
+ }
113
+ else {
114
+ await this.checkIframeConnect();
115
+ }
101
116
  this.walletConnectClient.on('session_event', (event) => {
102
117
  // Handle session events, such as "chainChanged", "accountsChanged", etc.
103
- console.log(event);
118
+ this.logger.debug('Session event received:', event);
119
+ this.validateAndRefreshSigners();
104
120
  });
105
121
  this.walletConnectClient.on('session_update', ({ topic, params }) => {
106
122
  // Handle session update
@@ -109,33 +125,37 @@ export class DAppConnector {
109
125
  // Overwrite the `namespaces` of the existing session with the incoming one.
110
126
  const updatedSession = Object.assign(Object.assign({}, _session), { namespaces });
111
127
  // Integrate the updated session state into your dapp state.
112
- console.log(updatedSession);
128
+ this.logger.info('Session updated:', updatedSession);
129
+ this.signers = this.signers.filter((signer) => signer.topic !== topic);
130
+ this.signers.push(...this.createSigners(updatedSession));
113
131
  });
114
132
  this.walletConnectClient.on('session_delete', (pairing) => {
115
- console.log(pairing);
133
+ this.logger.info('Session deleted:', pairing);
116
134
  this.signers = this.signers.filter((signer) => signer.topic !== pairing.topic);
117
135
  // Session was deleted -> reset the dapp state, clean up from user session, etc.
118
136
  try {
119
137
  this.disconnect(pairing.topic);
120
138
  }
121
139
  catch (e) {
122
- console.error(e);
140
+ this.logger.error('Error disconnecting session:', e);
123
141
  }
124
- console.log('Dapp: Session deleted by wallet!');
142
+ this.logger.info('Session deleted by wallet');
125
143
  });
126
144
  this.walletConnectClient.core.pairing.events.on('pairing_delete', (pairing) => {
127
- console.log(pairing);
145
+ this.logger.info('Pairing deleted:', pairing);
128
146
  this.signers = this.signers.filter((signer) => signer.topic !== pairing.topic);
129
147
  try {
130
148
  this.disconnect(pairing.topic);
131
149
  }
132
150
  catch (e) {
133
- console.error(e);
151
+ this.logger.error('Error disconnecting pairing:', e);
134
152
  }
135
- console.log(`Dapp: Pairing deleted by wallet!`);
136
- // clean up after the pairing for `topic` was deleted.
153
+ this.logger.info('Pairing deleted by wallet');
137
154
  });
138
155
  }
156
+ catch (e) {
157
+ this.logger.error('Error initializing DAppConnector:', e);
158
+ }
139
159
  finally {
140
160
  this.isInitializing = false;
141
161
  }
@@ -148,6 +168,9 @@ export class DAppConnector {
148
168
  * @throws {Error} - If no signer is found for the provided account ID.
149
169
  */
150
170
  getSigner(accountId) {
171
+ if (this.isInitializing) {
172
+ throw new Error('DAppConnector is not initialized yet. Try again later.');
173
+ }
151
174
  const signer = this.signers.find((signer) => signer.getAccountId().equals(accountId));
152
175
  if (!signer)
153
176
  throw new Error('Signer is not found for this accountId');
@@ -231,6 +254,32 @@ export class DAppConnector {
231
254
  extensionConnect(extension.id, extension.availableInIframe, uri);
232
255
  }, pairingTopic, extension.availableInIframe ? undefined : extensionId);
233
256
  }
257
+ /**
258
+ * Validates the session by checking if the session exists.
259
+ * @param topic - The topic of the session to validate.
260
+ * @returns {boolean} - True if the session exists, false otherwise.
261
+ */
262
+ validateSession(topic) {
263
+ try {
264
+ if (!this.walletConnectClient) {
265
+ return false;
266
+ }
267
+ const session = this.walletConnectClient.session.get(topic);
268
+ if (!session) {
269
+ return false;
270
+ }
271
+ return true;
272
+ }
273
+ catch (_a) {
274
+ return false;
275
+ }
276
+ }
277
+ /**
278
+ * Validates the session and refreshes the signers by removing the invalid ones.
279
+ */
280
+ validateAndRefreshSigners() {
281
+ this.signers = this.signers.filter((signer) => this.validateSession(signer.topic));
282
+ }
234
283
  /**
235
284
  * Initiates the WallecConnect connection if the wallet in iframe mode is detected.
236
285
  */
@@ -248,10 +297,20 @@ export class DAppConnector {
248
297
  * @returns A Promise that resolves when the session is disconnected.
249
298
  */
250
299
  async disconnect(topic) {
251
- await this.walletConnectClient.disconnect({
252
- topic: topic,
253
- reason: getSdkError('USER_DISCONNECTED'),
254
- });
300
+ try {
301
+ if (!this.walletConnectClient) {
302
+ throw new Error('WalletConnect is not initialized');
303
+ }
304
+ await this.walletConnectClient.disconnect({
305
+ topic: topic,
306
+ reason: getSdkError('USER_DISCONNECTED'),
307
+ });
308
+ return true;
309
+ }
310
+ catch (e) {
311
+ this.logger.error('Either the session was already disconnected or the topic is invalid', e);
312
+ return false;
313
+ }
255
314
  }
256
315
  /**
257
316
  * Disconnects all active sessions and pairings.
@@ -271,7 +330,7 @@ export class DAppConnector {
271
330
  const disconnectionPromises = [];
272
331
  // disconnect sessions
273
332
  for (const session of this.walletConnectClient.session.getAll()) {
274
- console.log(`Disconnecting from session: ${session}`);
333
+ this.logger.info(`Disconnecting from session: ${session}`);
275
334
  const promise = this.disconnect(session.topic);
276
335
  disconnectionPromises.push(promise);
277
336
  }
@@ -292,7 +351,34 @@ export class DAppConnector {
292
351
  });
293
352
  }
294
353
  async onSessionConnected(session) {
295
- this.signers.push(...this.createSigners(session));
354
+ const newSigners = this.createSigners(session);
355
+ // Filter out any existing signers with duplicate AccountIds
356
+ for (const newSigner of newSigners) {
357
+ // We check if any signers have the same account, extension + metadata name.
358
+ const existingSigners = this.signers.filter((currentSigner) => {
359
+ var _a, _b;
360
+ const matchingAccountId = ((_a = currentSigner === null || currentSigner === void 0 ? void 0 : currentSigner.getAccountId()) === null || _a === void 0 ? void 0 : _a.toString()) === ((_b = newSigner === null || newSigner === void 0 ? void 0 : newSigner.getAccountId()) === null || _b === void 0 ? void 0 : _b.toString());
361
+ const matchingExtensionId = newSigner.extensionId === currentSigner.extensionId;
362
+ const newSignerMetadata = newSigner.getMetadata();
363
+ const existingSignerMetadata = currentSigner.getMetadata();
364
+ const metadataNameMatch = (newSignerMetadata === null || newSignerMetadata === void 0 ? void 0 : newSignerMetadata.name) === (existingSignerMetadata === null || existingSignerMetadata === void 0 ? void 0 : existingSignerMetadata.name);
365
+ if (currentSigner.topic === newSigner.topic) {
366
+ this.logger.error('The topic was already connected. This is a weird error. Please report it.', newSigner.getAccountId().toString());
367
+ }
368
+ return matchingAccountId && matchingExtensionId && metadataNameMatch;
369
+ });
370
+ // Any dupes get disconnected + removed from the signers array.
371
+ for (const existingSigner of existingSigners) {
372
+ this.logger.debug(`Disconnecting duplicate signer for account ${existingSigner.getAccountId().toString()}`);
373
+ await this.disconnect(existingSigner.topic);
374
+ this.signers = this.signers.filter((s) => s.topic !== existingSigner.topic);
375
+ }
376
+ }
377
+ // Add new signers after all duplicates have been cleaned up
378
+ this.signers.push(...newSigners);
379
+ this.logger.debug(`Current signers after connection: ${this.signers
380
+ .map((s) => `${s.getAccountId().toString()}:${s.topic}`)
381
+ .join(', ')}`);
296
382
  }
297
383
  async connectURI(pairingTopic) {
298
384
  if (!this.walletConnectClient) {
@@ -304,10 +390,25 @@ export class DAppConnector {
304
390
  });
305
391
  }
306
392
  async request({ method, params, }) {
307
- const signer = this.signers[this.signers.length - 1];
393
+ var _a, _b, _c;
394
+ let signer;
395
+ this.logger.debug(`Requesting method: ${method} with params: ${JSON.stringify(params)}`);
396
+ if (params === null || params === void 0 ? void 0 : params.signerAccountId) {
397
+ // Extract the actual account ID from the hedera:<network>:<address> format
398
+ const actualAccountId = (_b = (_a = params === null || params === void 0 ? void 0 : params.signerAccountId) === null || _a === void 0 ? void 0 : _a.split(':')) === null || _b === void 0 ? void 0 : _b.pop();
399
+ signer = this.signers.find((s) => { var _a; return ((_a = s === null || s === void 0 ? void 0 : s.getAccountId()) === null || _a === void 0 ? void 0 : _a.toString()) === actualAccountId; });
400
+ this.logger.debug(`Found signer: ${(_c = signer === null || signer === void 0 ? void 0 : signer.getAccountId()) === null || _c === void 0 ? void 0 : _c.toString()}`);
401
+ if (!signer) {
402
+ throw new Error(`Signer not found for account ID: ${params === null || params === void 0 ? void 0 : params.signerAccountId}. Did you use the correct format? e.g hedera:<network>:<address> `);
403
+ }
404
+ }
405
+ else {
406
+ signer = this.signers[this.signers.length - 1];
407
+ }
308
408
  if (!signer) {
309
409
  throw new Error('There is no active session. Connect to the wallet at first.');
310
410
  }
411
+ this.logger.debug(`Using signer: ${signer.getAccountId().toString()}: ${signer.topic} - about to request.`);
311
412
  return await signer.request({
312
413
  method: method,
313
414
  params: params,
@@ -357,7 +458,7 @@ export class DAppConnector {
357
458
  * @example
358
459
  * ```ts
359
460
  * const params = {
360
- * signerAccountId: '0.0.12345',
461
+ * signerAccountId: 'hedera:testnet:0.0.12345',
361
462
  * message: 'Hello World!'
362
463
  * }
363
464
  *
@@ -0,0 +1,15 @@
1
+ export interface ILogger {
2
+ error(message: string, ...args: any[]): void;
3
+ warn(message: string, ...args: any[]): void;
4
+ info(message: string, ...args: any[]): void;
5
+ debug(message: string, ...args: any[]): void;
6
+ }
7
+ export declare class DefaultLogger implements ILogger {
8
+ private logLevel;
9
+ constructor(logLevel?: 'error' | 'warn' | 'info' | 'debug');
10
+ setLogLevel(level: 'error' | 'warn' | 'info' | 'debug'): void;
11
+ error(message: string, ...args: any[]): void;
12
+ warn(message: string, ...args: any[]): void;
13
+ info(message: string, ...args: any[]): void;
14
+ debug(message: string, ...args: any[]): void;
15
+ }
@@ -0,0 +1,29 @@
1
+ export class DefaultLogger {
2
+ constructor(logLevel = 'info') {
3
+ this.logLevel = 'info';
4
+ this.logLevel = logLevel;
5
+ }
6
+ setLogLevel(level) {
7
+ this.logLevel = level;
8
+ }
9
+ error(message, ...args) {
10
+ if (['error', 'warn', 'info', 'debug'].includes(this.logLevel)) {
11
+ console.error(`[ERROR] ${message}`, ...args);
12
+ }
13
+ }
14
+ warn(message, ...args) {
15
+ if (['warn', 'info', 'debug'].includes(this.logLevel)) {
16
+ console.warn(`[WARN] ${message}`, ...args);
17
+ }
18
+ }
19
+ info(message, ...args) {
20
+ if (['info', 'debug'].includes(this.logLevel)) {
21
+ console.info(`[INFO] ${message}`, ...args);
22
+ }
23
+ }
24
+ debug(message, ...args) {
25
+ if (this.logLevel === 'debug') {
26
+ console.debug(`[DEBUG] ${message}`, ...args);
27
+ }
28
+ }
29
+ }
@@ -49,8 +49,8 @@ export declare function base64StringToTransaction<T extends Transaction>(transac
49
49
  * @param transaction - a base64 encoded string of proto.TransactionBody.encode().finish()
50
50
  * @returns `string`
51
51
  * */
52
- export declare function transactionToTransactionBody<T extends Transaction>(transaction: T, nodeAccountId: AccountId): Uint8Array | null | undefined;
53
- export declare function transactionBodyToBase64String(transactionBody: Uint8Array): string;
52
+ export declare function transactionToTransactionBody<T extends Transaction>(transaction: T, nodeAccountId: AccountId): any;
53
+ export declare function transactionBodyToBase64String(transactionBody: proto.ITransactionBody): string;
54
54
  /**
55
55
  * @param transactionList - a proto.TransactionList object
56
56
  * @returns `string`
@@ -86,10 +86,10 @@ export function base64StringToTransaction(transactionBytes) {
86
86
  export function transactionToTransactionBody(transaction, nodeAccountId) {
87
87
  // This is a private function, though provides the capabilities to construct a proto.TransactionBody
88
88
  //@ts-ignore
89
- return transaction._signedTransactions.current.bodyBytes;
89
+ return transaction._makeTransactionBody(nodeAccountId);
90
90
  }
91
91
  export function transactionBodyToBase64String(transactionBody) {
92
- return Uint8ArrayToBase64String(transactionBody);
92
+ return Uint8ArrayToBase64String(proto.TransactionBody.encode(transactionBody).finish());
93
93
  }
94
94
  /**
95
95
  * @param transactionList - a proto.TransactionList object
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashgraph/hedera-wallet-connect",
3
- "version": "1.3.6-canary.67ec2cf.0",
3
+ "version": "1.3.7-canary.813a6d2.0",
4
4
  "description": "A library to facilitate integrating Hedera with WalletConnect",
5
5
  "repository": {
6
6
  "type": "git",