@hashgraph/hedera-wallet-connect 1.3.8-canary.5eb145b.0 → 1.3.8-canary.6c8147c.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.
@@ -1,5 +1,6 @@
1
1
  import { Signer, AccountBalance, AccountId, AccountInfo, Executable, Key, LedgerId, SignerSignature, Transaction, TransactionRecord } from '@hashgraph/sdk';
2
2
  import type { CoreTypes, ISignClient } from '@walletconnect/types';
3
+ import { LogLevel } from '../shared/logger';
3
4
  export declare class DAppSigner implements Signer {
4
5
  private readonly accountId;
5
6
  private readonly signClient;
@@ -7,12 +8,12 @@ export declare class DAppSigner implements Signer {
7
8
  private readonly ledgerId;
8
9
  readonly extensionId?: string | undefined;
9
10
  private logger;
10
- constructor(accountId: AccountId, signClient: ISignClient, topic: string, ledgerId?: LedgerId, extensionId?: string | undefined, logLevel?: 'error' | 'warn' | 'info' | 'debug');
11
+ constructor(accountId: AccountId, signClient: ISignClient, topic: string, ledgerId?: LedgerId, extensionId?: string | undefined, logLevel?: LogLevel);
11
12
  /**
12
13
  * Sets the logging level for the DAppSigner
13
14
  * @param level - The logging level to set
14
15
  */
15
- setLogLevel(level: 'error' | 'warn' | 'info' | 'debug'): void;
16
+ setLogLevel(level: LogLevel): void;
16
17
  private _getHederaClient;
17
18
  private get _signerAccountId();
18
19
  private _getRandomNodes;
@@ -31,7 +32,9 @@ export declare class DAppSigner implements Signer {
31
32
  getAccountInfo(): Promise<AccountInfo>;
32
33
  getAccountRecords(): Promise<TransactionRecord[]>;
33
34
  getMetadata(): CoreTypes.Metadata;
34
- sign(data: Uint8Array[], signOptions?: Record<string, any>): Promise<SignerSignature[]>;
35
+ sign(data: Uint8Array[], signOptions?: {
36
+ encoding?: 'utf-8' | 'base64';
37
+ }): Promise<SignerSignature[]>;
35
38
  checkTransaction<T extends Transaction>(transaction: T): Promise<T>;
36
39
  populateTransaction<T extends Transaction>(transaction: T): Promise<T>;
37
40
  /**
@@ -19,8 +19,9 @@
19
19
  */
20
20
  import { AccountBalance, AccountId, AccountInfo, LedgerId, SignerSignature, Transaction, TransactionRecord, Client, PublicKey, TransactionId, TransactionResponse, Query, AccountRecordsQuery, AccountInfoQuery, AccountBalanceQuery, TransactionReceiptQuery, TransactionReceipt, TransactionRecordQuery, } from '@hashgraph/sdk';
21
21
  import { proto } from '@hashgraph/proto';
22
- import { HederaJsonRpcMethod, Uint8ArrayToBase64String, base64StringToSignatureMap, base64StringToUint8Array, ledgerIdToCAIPChainId, queryToBase64String, transactionBodyToBase64String, transactionToBase64String, transactionToTransactionBody, extensionOpen, } from '../shared';
22
+ import { HederaJsonRpcMethod, base64StringToSignatureMap, base64StringToUint8Array, ledgerIdToCAIPChainId, queryToBase64String, transactionBodyToBase64String, transactionToBase64String, transactionToTransactionBody, extensionOpen, Uint8ArrayToBase64String, Uint8ArrayToString, } from '../shared';
23
23
  import { DefaultLogger } from '../shared/logger';
24
+ import { SessionNotFoundError } from './SessionNotFoundError';
24
25
  const clients = {};
25
26
  export class DAppSigner {
26
27
  constructor(accountId, signClient, topic, ledgerId = LedgerId.MAINNET, extensionId, logLevel = 'debug') {
@@ -60,6 +61,12 @@ export class DAppSigner {
60
61
  return allNodes.slice(0, numberOfNodes);
61
62
  }
62
63
  request(request) {
64
+ var _a, _b;
65
+ // Avoid a wallet call if the session is no longer valid
66
+ if (!((_b = (_a = this === null || this === void 0 ? void 0 : this.signClient) === null || _a === void 0 ? void 0 : _a.session) === null || _b === void 0 ? void 0 : _b.get(this.topic))) {
67
+ this.logger.error('Session no longer exists. Please reconnect to the wallet.');
68
+ throw new SessionNotFoundError('Session no longer exists. Please reconnect to the wallet.');
69
+ }
63
70
  if (this.extensionId)
64
71
  extensionOpen(this.extensionId);
65
72
  return this.signClient.request({
@@ -95,14 +102,18 @@ export class DAppSigner {
95
102
  getMetadata() {
96
103
  return this.signClient.metadata;
97
104
  }
98
- async sign(data, signOptions) {
99
- this.logger.debug('Signing data');
105
+ async sign(data, signOptions = {
106
+ encoding: 'utf-8',
107
+ }) {
100
108
  try {
109
+ const messageToSign = signOptions.encoding === 'base64'
110
+ ? Uint8ArrayToBase64String(data[0])
111
+ : Uint8ArrayToString(data[0]);
101
112
  const { signatureMap } = await this.request({
102
113
  method: HederaJsonRpcMethod.SignMessage,
103
114
  params: {
104
115
  signerAccountId: this._signerAccountId,
105
- message: Uint8ArrayToBase64String(data[0]),
116
+ message: messageToSign,
106
117
  },
107
118
  });
108
119
  const sigmap = base64StringToSignatureMap(signatureMap);
@@ -160,6 +171,10 @@ export class DAppSigner {
160
171
  }
161
172
  async _tryExecuteTransactionRequest(request) {
162
173
  try {
174
+ // Verify session is still valid before proceeding
175
+ if (!this.signClient.session.get(this.topic)) {
176
+ throw new Error('Session no longer exists. Please reconnect to the wallet.');
177
+ }
163
178
  const requestToBytes = request.toBytes();
164
179
  this.logger.debug('Creating transaction from bytes', requestToBytes, request);
165
180
  const transaction = Transaction.fromBytes(requestToBytes);
@@ -175,7 +190,22 @@ export class DAppSigner {
175
190
  return { result: TransactionResponse.fromJSON(result) };
176
191
  }
177
192
  catch (error) {
178
- this.logger.error('Error executing transaction request:', error);
193
+ // Check if error is due to session being deleted
194
+ if (error instanceof SessionNotFoundError) {
195
+ this.logger.error('Session was deleted, removing signer');
196
+ // Notify DAppConnector to remove this signer
197
+ this.signClient.emit({
198
+ topic: this.topic,
199
+ event: {
200
+ name: 'session_delete',
201
+ data: { topic: this.topic },
202
+ },
203
+ chainId: ledgerIdToCAIPChainId(this.ledgerId),
204
+ });
205
+ }
206
+ else {
207
+ this.logger.error('Error executing transaction request:', error);
208
+ }
179
209
  return { error };
180
210
  }
181
211
  }
@@ -230,9 +260,7 @@ export class DAppSigner {
230
260
  if (!(result === null || result === void 0 ? void 0 : result.error)) {
231
261
  return { result: result.result };
232
262
  }
233
- else {
234
- this.logger.error('Error executing free receipt query. Sending to wallet.', result.error);
235
- }
263
+ this.logger.error('Error executing free receipt query. Sending to wallet.', result.error);
236
264
  }
237
265
  /**
238
266
  * Note, should we be converting these to specific query types?
@@ -253,6 +281,7 @@ export class DAppSigner {
253
281
  return { result: this._parseQueryResponse(query, result.response) };
254
282
  }
255
283
  catch (error) {
284
+ this.logger.error('Error executing query request:', error);
256
285
  return { error };
257
286
  }
258
287
  }
@@ -0,0 +1,3 @@
1
+ export declare class SessionNotFoundError extends Error {
2
+ constructor(message: string);
3
+ }
@@ -0,0 +1,6 @@
1
+ export class SessionNotFoundError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = 'SessionNotFoundError';
5
+ }
6
+ }
@@ -2,6 +2,7 @@ import { AccountId, LedgerId } from '@hashgraph/sdk';
2
2
  import { SessionTypes, SignClientTypes } from '@walletconnect/types';
3
3
  import { WalletConnectModal } from '@walletconnect/modal';
4
4
  import SignClient from '@walletconnect/sign-client';
5
+ import { LogLevel } from '../shared/logger';
5
6
  import { GetNodeAddressesResult, ExecuteTransactionParams, ExecuteTransactionResult, SignMessageParams, SignMessageResult, SignAndExecuteQueryResult, SignAndExecuteQueryParams, SignAndExecuteTransactionParams, SignAndExecuteTransactionResult, SignTransactionParams, SignTransactionResult, ExtensionData } from '../shared';
6
7
  import { DAppSigner } from './DAppSigner';
7
8
  export * from './DAppSigner';
@@ -30,12 +31,12 @@ export declare class DAppConnector {
30
31
  * @param chains - Array of supported chains for the DApp (optional).
31
32
  * @param logLevel - Logging level for the DAppConnector (optional).
32
33
  */
33
- constructor(metadata: SignClientTypes.Metadata, network: LedgerId, projectId: string, methods?: string[], events?: string[], chains?: string[], logLevel?: 'error' | 'warn' | 'info' | 'debug');
34
+ constructor(metadata: SignClientTypes.Metadata, network: LedgerId, projectId: string, methods?: string[], events?: string[], chains?: string[], logLevel?: LogLevel);
34
35
  /**
35
36
  * Sets the logging level for the DAppConnector
36
37
  * @param level - The logging level to set
37
38
  */
38
- setLogLevel(level: 'error' | 'warn' | 'info' | 'debug'): void;
39
+ setLogLevel(level: LogLevel): void;
39
40
  /**
40
41
  * Initializes the DAppConnector instance.
41
42
  * @param logger - `BaseLogger` for logging purposes (optional).
@@ -79,9 +80,10 @@ export declare class DAppConnector {
79
80
  */
80
81
  connectExtension(extensionId: string, pairingTopic?: string): Promise<SessionTypes.Struct>;
81
82
  /**
82
- * Validates the session by checking if the session exists.
83
+ * Validates the session by checking if the session exists and is valid.
84
+ * Also ensures the signer exists for the session.
83
85
  * @param topic - The topic of the session to validate.
84
- * @returns {boolean} - True if the session exists, false otherwise.
86
+ * @returns {boolean} - True if the session exists and has a valid signer, false otherwise.
85
87
  */
86
88
  private validateSession;
87
89
  /**
@@ -113,6 +113,8 @@ export class DAppConnector {
113
113
  this.walletConnectClient.on('session_event', this.handleSessionEvent.bind(this));
114
114
  this.walletConnectClient.on('session_update', this.handleSessionUpdate.bind(this));
115
115
  this.walletConnectClient.on('session_delete', this.handleSessionDelete.bind(this));
116
+ // Listen for custom session_delete events from DAppSigner
117
+ this.walletConnectClient.core.events.on('session_delete', this.handleSessionDelete.bind(this));
116
118
  this.walletConnectClient.core.pairing.events.on('pairing_delete', this.handlePairingDelete.bind(this));
117
119
  }
118
120
  catch (e) {
@@ -217,9 +219,10 @@ export class DAppConnector {
217
219
  }, pairingTopic, extension.availableInIframe ? undefined : extensionId);
218
220
  }
219
221
  /**
220
- * Validates the session by checking if the session exists.
222
+ * Validates the session by checking if the session exists and is valid.
223
+ * Also ensures the signer exists for the session.
221
224
  * @param topic - The topic of the session to validate.
222
- * @returns {boolean} - True if the session exists, false otherwise.
225
+ * @returns {boolean} - True if the session exists and has a valid signer, false otherwise.
223
226
  */
224
227
  validateSession(topic) {
225
228
  try {
@@ -228,11 +231,23 @@ export class DAppConnector {
228
231
  }
229
232
  const session = this.walletConnectClient.session.get(topic);
230
233
  if (!session) {
234
+ // If session doesn't exist but we have a signer for it, clean up
235
+ const hasSigner = this.signers.some((signer) => signer.topic === topic);
236
+ if (hasSigner) {
237
+ this.handleSessionDelete({ topic });
238
+ }
239
+ return false;
240
+ }
241
+ // Verify we have a signer for this session
242
+ const hasSigner = this.signers.some((signer) => signer.topic === topic);
243
+ if (!hasSigner) {
244
+ this.logger.warn(`Session exists but no signer found for topic: ${topic}`);
231
245
  return false;
232
246
  }
233
247
  return true;
234
248
  }
235
- catch (_a) {
249
+ catch (e) {
250
+ this.logger.error('Error validating session:', e);
236
251
  return false;
237
252
  }
238
253
  }
@@ -521,14 +536,24 @@ export class DAppConnector {
521
536
  }
522
537
  handleSessionDelete(event) {
523
538
  this.logger.info('Session deleted:', event);
524
- this.signers = this.signers.filter((signer) => signer.topic !== event.topic);
525
- try {
526
- this.disconnect(event.topic);
527
- }
528
- catch (e) {
529
- this.logger.error('Error disconnecting session:', e);
539
+ let deletedSigner = false;
540
+ this.signers = this.signers.filter((signer) => {
541
+ if (signer.topic !== event.topic) {
542
+ return true;
543
+ }
544
+ deletedSigner = true;
545
+ return false;
546
+ });
547
+ // prevent emitting disconnected event if signers is untouched.
548
+ if (deletedSigner) {
549
+ try {
550
+ this.disconnect(event.topic);
551
+ }
552
+ catch (e) {
553
+ this.logger.error('Error disconnecting session:', e);
554
+ }
555
+ this.logger.info('Session deleted and signer removed');
530
556
  }
531
- this.logger.info('Session deleted by wallet');
532
557
  }
533
558
  handlePairingDelete(event) {
534
559
  this.logger.info('Pairing deleted:', event);
@@ -4,11 +4,12 @@ export interface ILogger {
4
4
  info(message: string, ...args: any[]): void;
5
5
  debug(message: string, ...args: any[]): void;
6
6
  }
7
+ export type LogLevel = 'error' | 'warn' | 'info' | 'debug' | 'off';
7
8
  export declare class DefaultLogger implements ILogger {
8
9
  private logLevel;
9
- constructor(logLevel?: 'error' | 'warn' | 'info' | 'debug');
10
- setLogLevel(level: 'error' | 'warn' | 'info' | 'debug'): void;
11
- getLogLevel(): 'error' | 'warn' | 'info' | 'debug';
10
+ constructor(logLevel?: LogLevel);
11
+ setLogLevel(level: LogLevel): void;
12
+ getLogLevel(): LogLevel;
12
13
  error(message: string, ...args: any[]): void;
13
14
  warn(message: string, ...args: any[]): void;
14
15
  info(message: string, ...args: any[]): void;
@@ -85,6 +85,12 @@ export declare function base64StringToSignatureMap(base64string: string): proto.
85
85
  * @returns Base64-encoded string representation of the input `Uint8Array`
86
86
  */
87
87
  export declare function Uint8ArrayToBase64String(binary: Uint8Array): string;
88
+ /**
89
+ * Encodes the binary data represented by the `Uint8Array` to a UTF-8 string.
90
+ * @param binary - The `Uint8Array` containing binary data to be converted
91
+ * @returns UTF-8 string representation of the input `Uint8Array`
92
+ */
93
+ export declare function Uint8ArrayToString(binary: Uint8Array): string;
88
94
  /**
89
95
  * Converts a Base64-encoded string to a `Uint8Array`.
90
96
  * @param base64string - Base64-encoded string to be converted
@@ -139,6 +139,14 @@ export function base64StringToSignatureMap(base64string) {
139
139
  export function Uint8ArrayToBase64String(binary) {
140
140
  return Buffer.from(binary).toString('base64');
141
141
  }
142
+ /**
143
+ * Encodes the binary data represented by the `Uint8Array` to a UTF-8 string.
144
+ * @param binary - The `Uint8Array` containing binary data to be converted
145
+ * @returns UTF-8 string representation of the input `Uint8Array`
146
+ */
147
+ export function Uint8ArrayToString(binary) {
148
+ return Buffer.from(binary).toString('utf-8');
149
+ }
142
150
  /**
143
151
  * Converts a Base64-encoded string to a `Uint8Array`.
144
152
  * @param base64string - Base64-encoded string to be converted
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashgraph/hedera-wallet-connect",
3
- "version": "1.3.8-canary.5eb145b.0",
3
+ "version": "1.3.8-canary.6c8147c.0",
4
4
  "description": "A library to facilitate integrating Hedera with WalletConnect",
5
5
  "repository": {
6
6
  "type": "git",
@@ -55,6 +55,7 @@
55
55
  "dev:ts-demo": "rimraf dist && npm run build && concurrently --raw \"npm run watch\" \"node scripts/demos/typescript/dev.mjs\"",
56
56
  "dev:react-demo": "rimraf dist && npm run build && concurrently --raw \"npm run watch\" \"node scripts/demos/react/dev.mjs\"",
57
57
  "test": "jest",
58
+ "test:watch": "jest --watch",
58
59
  "test:connect": "jest --testMatch '**/DAppConnector.test.ts' --verbose",
59
60
  "test:signer": "jest --testMatch '**/DAppSigner.test.ts' --verbose",
60
61
  "prepublishOnly": "rm -Rf dist && npm run build",