@hashgraph/hedera-wallet-connect 1.3.3 → 1.3.7-canary.519a0db.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -52,245 +52,19 @@ _The relay and this library have different intentions and serve different purpos
52
52
  native Hedera integration vs. Ethereum compatability layers to ease developer onboarding for
53
53
  those more familiar with the Ethereum ecosystem._
54
54
 
55
- ## Set up
55
+ # Documentation
56
56
 
57
- To start using WalletConnect, sign up for an account at <https://cloud.walletconnect.com>. You
58
- will use your project id when initializing client libraries.
57
+ WalletConnect <> Hedera docs are fully hosted on [https://hwc-docs.hgraph.app/](https://hwc-docs.hgraph.app/)
59
58
 
60
- It is important to understand core WalletConnect concepts when integrating this library. Please
61
- reference the [WalletConnect documentation](https://docs.walletconnect.com/2.0/).
59
+ - [Installation](/docs/docs/installation.md)
60
+ - [dApp Guide](/docs/docs/dapp-guide.md)
61
+ - [Wallet Guide](/docs/docs/wallet-guide.md)
62
+ - [Signing Messages](/docs/docs/sign-messages.md)
63
+ - [Demos](/docs/docs/demos.md)
62
64
 
63
- ## Usage
65
+ # Accessing the docs locally
64
66
 
65
- Upon successfully configuring your dApp and/or wallet to manage WalletConnect sessions, you can
66
- use this library’s functions to easily create and handle requests for the Hedera network.
67
-
68
- ### Installation
69
-
70
- `npm i --save @hashgraph/hedera-wallet-connect`
71
-
72
- ### Example code
73
-
74
- - [Typescript dApp example code](demos/typescript/dapp/main.ts)
75
- - [Typescript Wallet example code](demos/typescript/wallet/main.ts)
76
- - [React dApp example code](demos/react-dapp)
77
-
78
-
79
- ### DApps
80
-
81
- #### Signer
82
-
83
- This library provides a `DAppSigner` class that implements the `@hashgraph/sdk`'s `Signer`
84
- interface. You may use the `DAppSigner` class to sign and execute transactions on the Hedera
85
- network.
86
-
87
- ##### Get Signer
88
-
89
- After you have paired your wallet with your dApp, you can get the signer from the
90
- `DAppConnector` instance:
91
-
92
- ```javascript
93
- const signer = dAppConnector.signers[0] // DAppSigner
94
- ```
95
-
96
- Or, if multiple signers are available, you can find the signer by account ID:
97
-
98
- ```javascript
99
- const signer = dAppConnector.signers.find(
100
- (signer_) => signer_.getAccountId().toString() === '0.0.100',
101
- ) // DAppSigner
102
- ```
103
-
104
- ##### Sign Transactions
105
-
106
- ```javascript
107
- const transaction = new TransferTransaction()
108
- .addHbarTransfer('0.0.100', new Hbar(-1))
109
- .addHbarTransfer('0.0.101', new Hbar(1))
110
-
111
- await transaction.freezeWithSigner(signer)
112
- const signedTransaction = await signer.signTransaction(transaction)
113
- ```
114
-
115
- ##### Sign and Execute Transactions
116
-
117
- ```javascript
118
- const transaction = new TransferTransaction()
119
- .addHbarTransfer('0.0.100', new Hbar(-1))
120
- .addHbarTransfer('0.0.101', new Hbar(1))
121
-
122
- await transaction.freezeWithSigner(signer)
123
- const transactionResponse = await transaction.executeWithSigner(signer)
124
- ```
125
-
126
- ##### Sign and verify messages
127
-
128
- ```javascript
129
- const text = 'Example message to sign'
130
- const base64String = btoa(text)
131
-
132
- const sigMaps = await signer.sign([base64StringToUint8Array(base64String)]) // import { base64StringToUint8Array } from '@hashgraph/hedera-wallet-connect'
133
-
134
- // sigMaps[0].publicKey also contains the public key of the signer, but you should obtain a PublicKey you can trust from a mirror node.
135
- const verifiedResult = verifySignerSignature(base64String, sigMaps[0], publicKey) // import { verifySignerSignature } from '@hashgraph/hedera-wallet-connect'
136
- ```
137
-
138
- ### Wallet
139
-
140
- This library provides a Wallet class that extends the
141
- [ Web3Wallet ](https://github.com/WalletConnect/walletconnect-monorepo/tree/v2.0/packages/web3wallet)
142
- class provided by WalletConnect class
143
-
144
- #### Event Listeners
145
-
146
- WalletConnect emits various events during a session. Listen to these events to synchronize the
147
- state of your application:
148
-
149
- ```javascript
150
- // Handle pairing proposals
151
- signClient.on('session_proposal', (event) => {
152
- // Display session proposal to the user and decide to approve or reject
153
- })
154
-
155
- // Handle session requests, like signing transactions or messages
156
- signClient.on('session_request', (event) => {
157
- // Process the session request
158
- })
159
-
160
- // Handle session deletions
161
- signClient.on('session_delete', (event) => {
162
- // React to session termination
163
- })
164
- ```
165
-
166
- #### Pairing with dApps
167
-
168
- Pairing establishes a connection between the wallet and a dApp. Once paired, the dApp can send
169
- session requests to the wallet.
170
-
171
- ##### a. Pairing via URI
172
-
173
- If a dApp shares a URI for pairing:
174
-
175
- ```javascript
176
- await signClient.core.pairing.pair({ uri: 'RECEIVED_URI' })
177
- ```
178
-
179
- Upon successful pairing, the `session_proposal` event will be triggered.
180
-
181
- ##### b. Pairing via QR Codes
182
-
183
- For a better user experience, dApps often share QR codes that wallets can scan to establish a
184
- pairing. Use a QR code scanning library to scan and obtain the URI, then proceed with pairing:
185
-
186
- ```javascript
187
- const scannedUri = '...' // URI obtained from scanning the QR code
188
- await signClient.core.pairing.pair({ uri: scannedUri })
189
- ```
190
-
191
- #### Handling Session Proposals
192
-
193
- Upon receiving a `session_proposal` event, display the proposal details to the user. Allow them
194
- to approve or reject the session:
195
-
196
- ##### Handling Session Requests
197
-
198
- Upon receiving a `session_request` event, process the request. For instance, if the dApp
199
- requests a transaction to be signed:
200
-
201
- #### Extension popup
202
-
203
- By default, it is not possible to directly pop up an extension with Wallet Connect. However, to
204
- allow this possibility, the dAppConnector look for extensions. If you create the AppConnector,
205
- it will automatically send a message to the extension to detect if it is installed. In case the
206
- extension is installed, it will be added to the available extensions and its data can be found
207
- at the extensions property of dAppConnector.
208
-
209
- To connect an available extension, use the method `connectExtension(<extensionId>)`. This will
210
- link the extension to the signer and session. Whenever you use the signer created for this
211
- session, the extension will automatically open. You can find out if the extension is available
212
- by checking the `extensions` property.
213
-
214
- ```javascript
215
- const dAppConnector = new DAppConnector(
216
- dAppMetadata,
217
- LedgerId.TESTNET,
218
- projectId,
219
- Object.values(HederaJsonRpcMethod),
220
- [HederaSessionEvent.ChainChanged, HederaSessionEvent.AccountsChanged],
221
- [HederaChainId.Testnet]
222
- )
223
-
224
- [...]
225
-
226
- dAppConnector?.extensions?.forEach((extension) => {
227
- console.log(extension)
228
- })
229
-
230
- const extension = dAppConnector?.extensions?.find((extension) => extension.name === '<Extension name>')
231
- if (extension.available) {
232
- await dAppConnector!.connectExtension(extension.id);
233
- const signer = dAppConnector.getSigner(AccountId.fromString('0.0.12345'))
234
-
235
- // This request will open the extension
236
- const response = await signer.signAndExecuteTransaction(transaction)
237
- }
238
- ```
239
-
240
- Wallets that are compatible should be able to receive and respond to the following messages:
241
-
242
- - `"hedera-extension-query"`: The extension is required to respond with
243
- `"hedera-extension-response"` and provide the next set of data in the metadata property.
244
- ```javascript
245
- let metadata = {
246
- id: '<extesnionId>',
247
- name: '<Wallet name>',
248
- url: '<Wallet url>',
249
- icon: '<Wallet con>',
250
- description: '<Wallet url>',
251
- }
252
- ```
253
- - `"hedera-extension-open-<extensionId>"`: The extension needs to listen to this message and
254
- automatically open.
255
- - `"hedera-extension-connect-<extensionId>"`: The extension must listen to this message and
256
- utilize the `pairingString` property in order to establish a connection.
257
-
258
- This communication protocol between the wallet and web dApps requires an intermediate script to
259
- use the Chrome API. Refer to the
260
- [Chrome Extensions documentation](https://developer.chrome.com/docs/extensions/develop/concepts/messaging)
261
-
262
- To enable communication between the extension and a web dApp embedded in an iframe, the wallet
263
- must support the following messages:
264
-
265
- - `"hedera-iframe-query"`:The extension is required to respond with `"hedera-iframe-response"`
266
- and provide the next set of data in the metadata property.
267
- ```javascript
268
- let metadata = {
269
- id: '<Wallet extension id>',
270
- name: '<Wallet name>',
271
- url: '<Wallet url>',
272
- icon: '<Wallet icon>',
273
- description: '<Wallet description>',
274
- }
275
- ```
276
- - `"hedera-iframe-connect"`: The extension must listen to this message and utilize the
277
- `pairingString` property in order to establish a connection.
278
-
279
- The dAppConnector is designed to automatically initiate pairing without any need for user
280
- action, in case no sessions are noticed and an iframe extension is detected. To capture this
281
- event and the newly established session, you can utilize the `onSessionIframeCreated` function.
282
-
283
- ## Demo & docs
284
-
285
- This repository includes a vanilla html/css/javascript implementation with a dApp and wallet
286
- example useful for testing and development while integrating WalletConnect and Hedera.
287
-
288
- The docs site utilizes [Typedoc](https://typedoc.org) to generate a library documentation site
289
- at <https://wc.hgraph.app/docs/>
290
-
291
- The demo source code lives in `./demos/typescript` and is available at
292
- <https://wc.hgraph.app>
293
-
294
- ## Passing tests
295
-
296
- - `git commit -Ss "the commit message"`
67
+ - `cd docs`
68
+ - `npm install`
69
+ - `npm run docs`
70
+ - Navigating to `localhost:3000`
@@ -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,24 +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
- this.disconnect(pairing.topic);
118
135
  // Session was deleted -> reset the dapp state, clean up from user session, etc.
119
- console.log('Dapp: Session deleted by wallet!');
136
+ try {
137
+ this.disconnect(pairing.topic);
138
+ }
139
+ catch (e) {
140
+ this.logger.error('Error disconnecting session:', e);
141
+ }
142
+ this.logger.info('Session deleted by wallet');
120
143
  });
121
144
  this.walletConnectClient.core.pairing.events.on('pairing_delete', (pairing) => {
122
- // Session was deleted
123
- console.log(pairing);
145
+ this.logger.info('Pairing deleted:', pairing);
124
146
  this.signers = this.signers.filter((signer) => signer.topic !== pairing.topic);
125
- this.disconnect(pairing.topic);
126
- console.log(`Dapp: Pairing deleted by wallet!`);
127
- // clean up after the pairing for `topic` was deleted.
147
+ try {
148
+ this.disconnect(pairing.topic);
149
+ }
150
+ catch (e) {
151
+ this.logger.error('Error disconnecting pairing:', e);
152
+ }
153
+ this.logger.info('Pairing deleted by wallet');
128
154
  });
129
155
  }
156
+ catch (e) {
157
+ this.logger.error('Error initializing DAppConnector:', e);
158
+ }
130
159
  finally {
131
160
  this.isInitializing = false;
132
161
  }
@@ -139,6 +168,9 @@ export class DAppConnector {
139
168
  * @throws {Error} - If no signer is found for the provided account ID.
140
169
  */
141
170
  getSigner(accountId) {
171
+ if (this.isInitializing) {
172
+ throw new Error('DAppConnector is not initialized yet. Try again later.');
173
+ }
142
174
  const signer = this.signers.find((signer) => signer.getAccountId().equals(accountId));
143
175
  if (!signer)
144
176
  throw new Error('Signer is not found for this accountId');
@@ -222,6 +254,32 @@ export class DAppConnector {
222
254
  extensionConnect(extension.id, extension.availableInIframe, uri);
223
255
  }, pairingTopic, extension.availableInIframe ? undefined : extensionId);
224
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
+ }
225
283
  /**
226
284
  * Initiates the WallecConnect connection if the wallet in iframe mode is detected.
227
285
  */
@@ -239,10 +297,20 @@ export class DAppConnector {
239
297
  * @returns A Promise that resolves when the session is disconnected.
240
298
  */
241
299
  async disconnect(topic) {
242
- await this.walletConnectClient.disconnect({
243
- topic: topic,
244
- reason: getSdkError('USER_DISCONNECTED'),
245
- });
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
+ }
246
314
  }
247
315
  /**
248
316
  * Disconnects all active sessions and pairings.
@@ -262,7 +330,7 @@ export class DAppConnector {
262
330
  const disconnectionPromises = [];
263
331
  // disconnect sessions
264
332
  for (const session of this.walletConnectClient.session.getAll()) {
265
- console.log(`Disconnecting from session: ${session}`);
333
+ this.logger.info(`Disconnecting from session: ${session}`);
266
334
  const promise = this.disconnect(session.topic);
267
335
  disconnectionPromises.push(promise);
268
336
  }
@@ -283,7 +351,34 @@ export class DAppConnector {
283
351
  });
284
352
  }
285
353
  async onSessionConnected(session) {
286
- 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(async (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(', ')}`);
287
382
  }
288
383
  async connectURI(pairingTopic) {
289
384
  if (!this.walletConnectClient) {
@@ -295,10 +390,25 @@ export class DAppConnector {
295
390
  });
296
391
  }
297
392
  async request({ method, params, }) {
298
- 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
+ }
299
408
  if (!signer) {
300
409
  throw new Error('There is no active session. Connect to the wallet at first.');
301
410
  }
411
+ this.logger.debug(`Using signer: ${signer.getAccountId().toString()}: ${signer.topic} - about to request.`);
302
412
  return await signer.request({
303
413
  method: method,
304
414
  params: params,
@@ -348,7 +458,7 @@ export class DAppConnector {
348
458
  * @example
349
459
  * ```ts
350
460
  * const params = {
351
- * signerAccountId: '0.0.12345',
461
+ * signerAccountId: 'hedera:testnet:0.0.12345',
352
462
  * message: 'Hello World!'
353
463
  * }
354
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.3",
3
+ "version": "1.3.7-canary.519a0db.0",
4
4
  "description": "A library to facilitate integrating Hedera with WalletConnect",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,7 +16,9 @@
16
16
  ],
17
17
  "license": "Apache-2.0",
18
18
  "devDependencies": {
19
- "@hashgraph/hedera-wallet-connect": "^1.3.1",
19
+ "@hashgraph/hedera-wallet-connect": "^1.3.4",
20
+ "@swc/core": "^1.7.40",
21
+ "@swc/jest": "^0.2.36",
20
22
  "@types/jest": "^29.5.3",
21
23
  "@types/node": "^22.5.0",
22
24
  "@types/react-dom": "^18.2.21",
@@ -36,7 +38,6 @@
36
38
  "react": "^18.2.0",
37
39
  "react-dom": "^18.2.0",
38
40
  "rimraf": "^5.0.5",
39
- "ts-jest": "^29.1.2",
40
41
  "ts-node": "^10.9.2",
41
42
  "typedoc": "^0.26.3",
42
43
  "typedoc-theme-hierarchy": "^4.1.2",
@@ -49,6 +50,7 @@
49
50
  "build:docs": "typedoc --options typedoc.json",
50
51
  "watch": "nodemon --watch src/lib/ --ext ts --exec \"npm run build\"",
51
52
  "dev": "npm run dev:ts-demo",
53
+ "dev:docs": "cd docs && npm run start",
52
54
  "dev:ts-demo": "rimraf dist && npm run build && concurrently --raw \"npm run watch\" \"node scripts/demos/typescript/dev.mjs\"",
53
55
  "dev:react-demo": "rimraf dist && npm run build && concurrently --raw \"npm run watch\" \"node scripts/demos/react/dev.mjs\"",
54
56
  "test": "jest",
@@ -58,6 +60,7 @@
58
60
  "prepare": "husky install",
59
61
  "prettier:check": "prettier --check ./src/",
60
62
  "prettier:fix": "prettier --write ./src/",
63
+ "prod:docs-docker": "sh docker-run.sh",
61
64
  "test:sigMap": "jest --testMatch '**/SignatureMapHelpers.test.ts' --verbose"
62
65
  },
63
66
  "peerDependencies": {