@hsuite/native-connect-angular 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/USAGE_EXAMPLES.md +476 -0
- package/assets/wallets/extension.svg +7 -0
- package/assets/wallets/hashpack.svg +6 -0
- package/assets/wallets/hsuite.svg +11 -0
- package/assets/wallets/kabila.svg +11 -0
- package/assets/wallets/walletconnect.svg +13 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/coverage-summary.json +50 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +476 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +476 -0
- package/coverage/lcov-report/lib/components/account-selector/account-actions/account-actions.component.ts.html +868 -0
- package/coverage/lcov-report/lib/components/account-selector/account-actions/index.html +116 -0
- package/coverage/lcov-report/lib/components/account-selector/account-filter/account-filter.component.ts.html +1288 -0
- package/coverage/lcov-report/lib/components/account-selector/account-filter/index.html +116 -0
- package/coverage/lcov-report/lib/components/account-selector/account-formatting.service.ts.html +685 -0
- package/coverage/lcov-report/lib/components/account-selector/account-grouping.service.ts.html +766 -0
- package/coverage/lcov-report/lib/components/account-selector/account-list/account-list.component.ts.html +1495 -0
- package/coverage/lcov-report/lib/components/account-selector/account-list/index.html +116 -0
- package/coverage/lcov-report/lib/components/account-selector/account-selector.component.ts.html +1495 -0
- package/coverage/lcov-report/lib/components/account-selector/account-selector.service.ts.html +1588 -0
- package/coverage/lcov-report/lib/components/account-selector/index.html +161 -0
- package/coverage/lcov-report/lib/components/wallet-account-display/index.html +116 -0
- package/coverage/lcov-report/lib/components/wallet-account-display/wallet-account-display.component.ts.html +505 -0
- package/coverage/lcov-report/lib/components/wallet-connect-button/index.html +116 -0
- package/coverage/lcov-report/lib/components/wallet-connect-button/wallet-connect-button.component.ts.html +805 -0
- package/coverage/lcov-report/lib/components/wallet-connect-prompt/index.html +116 -0
- package/coverage/lcov-report/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts.html +409 -0
- package/coverage/lcov-report/lib/components/wallet-connected-guard/index.html +116 -0
- package/coverage/lcov-report/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts.html +304 -0
- package/coverage/lcov-report/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts.html +436 -0
- package/coverage/lcov-report/lib/components/wallet-connection-modal/connection-method-step/index.html +116 -0
- package/coverage/lcov-report/lib/components/wallet-connection-modal/index.html +116 -0
- package/coverage/lcov-report/lib/components/wallet-connection-modal/qr-pairing-step/index.html +116 -0
- package/coverage/lcov-report/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts.html +2287 -0
- package/coverage/lcov-report/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts.html +2275 -0
- package/coverage/lcov-report/lib/components/wallet-session-display/index.html +116 -0
- package/coverage/lcov-report/lib/components/wallet-session-display/wallet-session-display.component.ts.html +676 -0
- package/coverage/lcov-report/lib/components/wallet-transaction-status/index.html +116 -0
- package/coverage/lcov-report/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts.html +703 -0
- package/coverage/lcov-report/lib/directives/index.html +146 -0
- package/coverage/lcov-report/lib/directives/wallet-connected.directive.ts.html +670 -0
- package/coverage/lcov-report/lib/directives/wallet-context.directive.ts.html +547 -0
- package/coverage/lcov-report/lib/directives/wallet-events.directive.ts.html +781 -0
- package/coverage/lcov-report/lib/hsuite-wallet.module.ts.html +715 -0
- package/coverage/lcov-report/lib/index.html +116 -0
- package/coverage/lcov-report/lib/models/connection-config.model.ts.html +280 -0
- package/coverage/lcov-report/lib/models/index.html +131 -0
- package/coverage/lcov-report/lib/models/provider-types.ts.html +577 -0
- package/coverage/lcov-report/lib/providers/base-wallet-provider.ts.html +1138 -0
- package/coverage/lcov-report/lib/providers/hsuite-native/channel-client.service.ts.html +2671 -0
- package/coverage/lcov-report/lib/providers/hsuite-native/index.html +116 -0
- package/coverage/lcov-report/lib/providers/hsuite-native-provider.ts.html +2347 -0
- package/coverage/lcov-report/lib/providers/index.html +146 -0
- package/coverage/lcov-report/lib/providers/p2p-native/index.html +131 -0
- package/coverage/lcov-report/lib/providers/p2p-native/p2p-native.provider.ts.html +2254 -0
- package/coverage/lcov-report/lib/providers/p2p-native/p2p-session-manager.ts.html +2170 -0
- package/coverage/lcov-report/lib/providers/wallet-error-handler.ts.html +1132 -0
- package/coverage/lcov-report/lib/providers/walletconnect/core/index.html +176 -0
- package/coverage/lcov-report/lib/providers/walletconnect/core/session-health.ts.html +673 -0
- package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-client-manager.ts.html +1177 -0
- package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-provider.ts.html +2563 -0
- package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-session-store.ts.html +904 -0
- package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts.html +982 -0
- package/coverage/lcov-report/lib/providers/walletconnect/signers/hedera-signer.ts.html +1915 -0
- package/coverage/lcov-report/lib/providers/walletconnect/signers/index.html +146 -0
- package/coverage/lcov-report/lib/providers/walletconnect/signers/signer-factory.ts.html +445 -0
- package/coverage/lcov-report/lib/providers/walletconnect/signers/xrpl-signer.ts.html +1519 -0
- package/coverage/lcov-report/lib/services/index.html +191 -0
- package/coverage/lcov-report/lib/services/logger.service.ts.html +463 -0
- package/coverage/lcov-report/lib/services/transaction-builders/base-transaction-builder.service.ts.html +1840 -0
- package/coverage/lcov-report/lib/services/transaction-builders/hedera-amount-utils.ts.html +337 -0
- package/coverage/lcov-report/lib/services/transaction-builders/hedera-transaction-builder.service.ts.html +3940 -0
- package/coverage/lcov-report/lib/services/transaction-builders/index.html +161 -0
- package/coverage/lcov-report/lib/services/transaction-builders/xrpl-transaction-builder.service.ts.html +2581 -0
- package/coverage/lcov-report/lib/services/transaction.service.ts.html +1123 -0
- package/coverage/lcov-report/lib/services/unified-wallet.service.ts.html +2641 -0
- package/coverage/lcov-report/lib/services/wallet-context.service.ts.html +637 -0
- package/coverage/lcov-report/lib/services/wallet-event-bus.service.ts.html +643 -0
- package/coverage/lcov-report/lib/services/wallet-providers.service.ts.html +496 -0
- package/coverage/lcov-report/lib/transports/chrome-extension-transport.ts.html +823 -0
- package/coverage/lcov-report/lib/transports/index.html +116 -0
- package/coverage/lcov-report/lib/utils/index.html +116 -0
- package/coverage/lcov-report/lib/utils/ledger-icons.util.ts.html +319 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +19252 -0
- package/coverage/lib/components/account-selector/account-actions/account-actions.component.ts.html +868 -0
- package/coverage/lib/components/account-selector/account-actions/index.html +116 -0
- package/coverage/lib/components/account-selector/account-filter/account-filter.component.ts.html +1288 -0
- package/coverage/lib/components/account-selector/account-filter/index.html +116 -0
- package/coverage/lib/components/account-selector/account-formatting.service.ts.html +685 -0
- package/coverage/lib/components/account-selector/account-grouping.service.ts.html +766 -0
- package/coverage/lib/components/account-selector/account-list/account-list.component.ts.html +1495 -0
- package/coverage/lib/components/account-selector/account-list/index.html +116 -0
- package/coverage/lib/components/account-selector/account-selector.component.ts.html +1495 -0
- package/coverage/lib/components/account-selector/account-selector.service.ts.html +1588 -0
- package/coverage/lib/components/account-selector/index.html +161 -0
- package/coverage/lib/components/wallet-account-display/index.html +116 -0
- package/coverage/lib/components/wallet-account-display/wallet-account-display.component.ts.html +505 -0
- package/coverage/lib/components/wallet-connect-button/index.html +116 -0
- package/coverage/lib/components/wallet-connect-button/wallet-connect-button.component.ts.html +805 -0
- package/coverage/lib/components/wallet-connect-prompt/index.html +116 -0
- package/coverage/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts.html +409 -0
- package/coverage/lib/components/wallet-connected-guard/index.html +116 -0
- package/coverage/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts.html +304 -0
- package/coverage/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts.html +436 -0
- package/coverage/lib/components/wallet-connection-modal/connection-method-step/index.html +116 -0
- package/coverage/lib/components/wallet-connection-modal/index.html +116 -0
- package/coverage/lib/components/wallet-connection-modal/qr-pairing-step/index.html +116 -0
- package/coverage/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts.html +2287 -0
- package/coverage/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts.html +2275 -0
- package/coverage/lib/components/wallet-session-display/index.html +116 -0
- package/coverage/lib/components/wallet-session-display/wallet-session-display.component.ts.html +676 -0
- package/coverage/lib/components/wallet-transaction-status/index.html +116 -0
- package/coverage/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts.html +703 -0
- package/coverage/lib/directives/index.html +146 -0
- package/coverage/lib/directives/wallet-connected.directive.ts.html +670 -0
- package/coverage/lib/directives/wallet-context.directive.ts.html +547 -0
- package/coverage/lib/directives/wallet-events.directive.ts.html +781 -0
- package/coverage/lib/hsuite-wallet.module.ts.html +715 -0
- package/coverage/lib/index.html +116 -0
- package/coverage/lib/models/connection-config.model.ts.html +280 -0
- package/coverage/lib/models/index.html +131 -0
- package/coverage/lib/models/provider-types.ts.html +577 -0
- package/coverage/lib/providers/base-wallet-provider.ts.html +1138 -0
- package/coverage/lib/providers/hsuite-native/channel-client.service.ts.html +2671 -0
- package/coverage/lib/providers/hsuite-native/index.html +116 -0
- package/coverage/lib/providers/hsuite-native-provider.ts.html +2347 -0
- package/coverage/lib/providers/index.html +146 -0
- package/coverage/lib/providers/p2p-native/index.html +131 -0
- package/coverage/lib/providers/p2p-native/p2p-native.provider.ts.html +2254 -0
- package/coverage/lib/providers/p2p-native/p2p-session-manager.ts.html +2170 -0
- package/coverage/lib/providers/wallet-error-handler.ts.html +1132 -0
- package/coverage/lib/providers/walletconnect/core/index.html +176 -0
- package/coverage/lib/providers/walletconnect/core/session-health.ts.html +673 -0
- package/coverage/lib/providers/walletconnect/core/walletconnect-client-manager.ts.html +1177 -0
- package/coverage/lib/providers/walletconnect/core/walletconnect-provider.ts.html +2563 -0
- package/coverage/lib/providers/walletconnect/core/walletconnect-session-store.ts.html +904 -0
- package/coverage/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts.html +982 -0
- package/coverage/lib/providers/walletconnect/signers/hedera-signer.ts.html +1915 -0
- package/coverage/lib/providers/walletconnect/signers/index.html +146 -0
- package/coverage/lib/providers/walletconnect/signers/signer-factory.ts.html +445 -0
- package/coverage/lib/providers/walletconnect/signers/xrpl-signer.ts.html +1519 -0
- package/coverage/lib/services/index.html +191 -0
- package/coverage/lib/services/logger.service.ts.html +463 -0
- package/coverage/lib/services/transaction-builders/base-transaction-builder.service.ts.html +1840 -0
- package/coverage/lib/services/transaction-builders/hedera-amount-utils.ts.html +337 -0
- package/coverage/lib/services/transaction-builders/hedera-transaction-builder.service.ts.html +3940 -0
- package/coverage/lib/services/transaction-builders/index.html +161 -0
- package/coverage/lib/services/transaction-builders/xrpl-transaction-builder.service.ts.html +2581 -0
- package/coverage/lib/services/transaction.service.ts.html +1123 -0
- package/coverage/lib/services/unified-wallet.service.ts.html +2641 -0
- package/coverage/lib/services/wallet-context.service.ts.html +637 -0
- package/coverage/lib/services/wallet-event-bus.service.ts.html +643 -0
- package/coverage/lib/services/wallet-providers.service.ts.html +496 -0
- package/coverage/lib/transports/chrome-extension-transport.ts.html +823 -0
- package/coverage/lib/transports/index.html +116 -0
- package/coverage/lib/utils/index.html +116 -0
- package/coverage/lib/utils/ledger-icons.util.ts.html +319 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/README.md +48 -0
- package/dist/fesm2022/hsuite-native-connect-angular.mjs +14592 -0
- package/dist/fesm2022/hsuite-native-connect-angular.mjs.map +1 -0
- package/dist/index.d.ts +6949 -0
- package/examples/minimal-connect.ts +178 -0
- package/examples/multi-protocol.ts +495 -0
- package/examples/transaction-signing.ts +361 -0
- package/jest.config.json +45 -0
- package/karma.conf.js +42 -0
- package/ng-package.json +20 -0
- package/package.json +60 -0
- package/src/index.ts +203 -0
- package/src/lib/components/account-selector/account-actions/account-actions.component.ts +261 -0
- package/src/lib/components/account-selector/account-filter/account-filter.component.ts +401 -0
- package/src/lib/components/account-selector/account-formatting.service.ts +200 -0
- package/src/lib/components/account-selector/account-grouping.service.ts +227 -0
- package/src/lib/components/account-selector/account-list/account-list.component.ts +470 -0
- package/src/lib/components/account-selector/account-selector.component.html +135 -0
- package/src/lib/components/account-selector/account-selector.component.scss +2039 -0
- package/src/lib/components/account-selector/account-selector.component.ts +470 -0
- package/src/lib/components/account-selector/account-selector.service.ts +501 -0
- package/src/lib/components/wallet-account-display/wallet-account-display.component.html +34 -0
- package/src/lib/components/wallet-account-display/wallet-account-display.component.scss +99 -0
- package/src/lib/components/wallet-account-display/wallet-account-display.component.ts +140 -0
- package/src/lib/components/wallet-connect-button/wallet-connect-button.component.html +14 -0
- package/src/lib/components/wallet-connect-button/wallet-connect-button.component.scss +272 -0
- package/src/lib/components/wallet-connect-button/wallet-connect-button.component.ts +240 -0
- package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.html +24 -0
- package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.scss +50 -0
- package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts +108 -0
- package/src/lib/components/wallet-connected-guard/wallet-connected-guard.component.html +24 -0
- package/src/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts +73 -0
- package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.html +56 -0
- package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.scss +218 -0
- package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts +117 -0
- package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.html +94 -0
- package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.scss +272 -0
- package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts +734 -0
- package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.html +197 -0
- package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.scss +678 -0
- package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts +730 -0
- package/src/lib/components/wallet-session-display/wallet-session-display.component.html +110 -0
- package/src/lib/components/wallet-session-display/wallet-session-display.component.scss +179 -0
- package/src/lib/components/wallet-session-display/wallet-session-display.component.ts +197 -0
- package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.html +65 -0
- package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.scss +254 -0
- package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts +206 -0
- package/src/lib/directives/wallet-connected.directive.ts +195 -0
- package/src/lib/directives/wallet-context.directive.ts +154 -0
- package/src/lib/directives/wallet-events.directive.ts +232 -0
- package/src/lib/hsuite-wallet.module.ts +210 -0
- package/src/lib/models/connection-config.model.ts +65 -0
- package/src/lib/models/provider-types.ts +164 -0
- package/src/lib/models/unified-account.model.ts +76 -0
- package/src/lib/models/wallet-context.model.ts +121 -0
- package/src/lib/models/wallet-events.model.ts +158 -0
- package/src/lib/providers/base-wallet-provider.ts +351 -0
- package/src/lib/providers/hsuite-native/channel-client.service.spec.ts +73 -0
- package/src/lib/providers/hsuite-native/channel-client.service.ts +862 -0
- package/src/lib/providers/hsuite-native/index.ts +8 -0
- package/src/lib/providers/hsuite-native-provider.ts +754 -0
- package/src/lib/providers/mobile-native/mobile-native.provider.spec.ts +19 -0
- package/src/lib/providers/p2p-native/index.ts +30 -0
- package/src/lib/providers/p2p-native/p2p-native.provider.spec.ts +523 -0
- package/src/lib/providers/p2p-native/p2p-native.provider.ts +723 -0
- package/src/lib/providers/p2p-native/p2p-session-manager.ts +695 -0
- package/src/lib/providers/wallet-error-handler.ts +349 -0
- package/src/lib/providers/walletconnect/core/base-signer.interface.ts +122 -0
- package/src/lib/providers/walletconnect/core/session-health.ts +196 -0
- package/src/lib/providers/walletconnect/core/walletconnect-client-manager.ts +364 -0
- package/src/lib/providers/walletconnect/core/walletconnect-provider.integration.spec.ts +348 -0
- package/src/lib/providers/walletconnect/core/walletconnect-provider.ts +826 -0
- package/src/lib/providers/walletconnect/core/walletconnect-session-store.ts +273 -0
- package/src/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts +299 -0
- package/src/lib/providers/walletconnect/core/walletconnect-types.ts +48 -0
- package/src/lib/providers/walletconnect/index.ts +33 -0
- package/src/lib/providers/walletconnect/signers/hedera-signer.spec.ts +367 -0
- package/src/lib/providers/walletconnect/signers/hedera-signer.ts +610 -0
- package/src/lib/providers/walletconnect/signers/signer-factory.spec.ts +62 -0
- package/src/lib/providers/walletconnect/signers/signer-factory.ts +120 -0
- package/src/lib/providers/walletconnect/signers/xrpl-signer.spec.ts +296 -0
- package/src/lib/providers/walletconnect/signers/xrpl-signer.ts +478 -0
- package/src/lib/services/logger.service.ts +126 -0
- package/src/lib/services/transaction-builders/base-transaction-builder.service.ts +585 -0
- package/src/lib/services/transaction-builders/hedera-amount-utils.ts +84 -0
- package/src/lib/services/transaction-builders/hedera-transaction-builder.service.spec.ts +741 -0
- package/src/lib/services/transaction-builders/hedera-transaction-builder.service.ts +1285 -0
- package/src/lib/services/transaction-builders/index.ts +54 -0
- package/src/lib/services/transaction-builders/xrpl-transaction-builder.service.spec.ts +937 -0
- package/src/lib/services/transaction-builders/xrpl-transaction-builder.service.ts +832 -0
- package/src/lib/services/transaction.service.ts +346 -0
- package/src/lib/services/unified-wallet.service.spec.ts +1382 -0
- package/src/lib/services/unified-wallet.service.ts +852 -0
- package/src/lib/services/wallet-context.service.ts +184 -0
- package/src/lib/services/wallet-event-bus.service.ts +186 -0
- package/src/lib/services/wallet-providers.service.ts +137 -0
- package/src/lib/transports/chrome-extension-transport.ts +246 -0
- package/src/lib/utils/index.ts +14 -0
- package/src/lib/utils/ledger-icons.util.ts +78 -0
- package/test/test-setup.ts +21 -0
- package/test-setup.ts +63 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +29 -0
- package/tsconfig.spec.json +15 -0
- package/vitest.config.ts +48 -0
|
@@ -0,0 +1,826 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HSuite Native Connect
|
|
3
|
+
* Copyright 2024-2025 HSuite (https://hsuite.finance)
|
|
4
|
+
*
|
|
5
|
+
* SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
|
|
6
|
+
*
|
|
7
|
+
* This file is part of HSuite Native Connect. For commercial licensing,
|
|
8
|
+
* visit https://hsuite.finance/licensing
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @file walletconnect-provider.ts
|
|
13
|
+
* @description Refactored WalletConnect v2 provider using Strategy Pattern
|
|
14
|
+
*
|
|
15
|
+
* Multi-chain WalletConnect v2 provider that delegates ledger-specific logic
|
|
16
|
+
* to specialized signer implementations.
|
|
17
|
+
*
|
|
18
|
+
* Architecture:
|
|
19
|
+
* - Provider: Orchestrates connection, session management, and UI
|
|
20
|
+
* - Signers: Handle ledger-specific transaction signing and formatting
|
|
21
|
+
* - Encoders: Convert between SDK formats and WalletConnect formats
|
|
22
|
+
*
|
|
23
|
+
* Benefits:
|
|
24
|
+
* - Clean separation of concerns
|
|
25
|
+
* - Easy to add new ledgers (just implement IWalletConnectSigner)
|
|
26
|
+
* - Testable (mock signers for unit tests)
|
|
27
|
+
* - Maintainable (ledger logic is isolated)
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { Injectable, signal, inject, NgZone } from '@angular/core';
|
|
31
|
+
import { getLogger } from '@hsuite/native-connect-sdk';
|
|
32
|
+
|
|
33
|
+
import type {
|
|
34
|
+
ConnectionStatus,
|
|
35
|
+
ProviderMetadata,
|
|
36
|
+
ConnectionConfig,
|
|
37
|
+
WalletConnectV2Config,
|
|
38
|
+
} from '../../../models/provider-types';
|
|
39
|
+
import type { UnifiedAccount } from '../../../models/unified-account.model';
|
|
40
|
+
import { WalletEventBus } from '../../../services/wallet-event-bus.service';
|
|
41
|
+
import { BaseWalletProvider } from '../../base-wallet-provider';
|
|
42
|
+
import type {
|
|
43
|
+
SignTransactionOptions,
|
|
44
|
+
SubmitTransactionOptions,
|
|
45
|
+
SignResult,
|
|
46
|
+
SubmitResult,
|
|
47
|
+
SignMessageOptions,
|
|
48
|
+
SignMessageResult,
|
|
49
|
+
} from '../../base-wallet-provider';
|
|
50
|
+
import { SignerFactory } from '../signers/signer-factory';
|
|
51
|
+
|
|
52
|
+
import { WalletConnectClientManager } from './walletconnect-client-manager';
|
|
53
|
+
import { WalletConnectSessionStore } from './walletconnect-session-store';
|
|
54
|
+
import { WalletConnectSigningOrchestrator } from './walletconnect-signing-orchestrator';
|
|
55
|
+
|
|
56
|
+
const logger = getLogger().scoped?.('WalletConnectV2Provider') ?? getLogger();
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Storage key prefix for persisting user's network selection per session topic.
|
|
60
|
+
* This is critical for proper session restoration after page reload.
|
|
61
|
+
*/
|
|
62
|
+
const NETWORK_SELECTION_STORAGE_KEY = 'wc_user_network_selection_';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* WalletConnect v2 provider with multi-chain support.
|
|
66
|
+
*
|
|
67
|
+
* Uses Strategy Pattern to delegate ledger-specific operations to
|
|
68
|
+
* specialized signer implementations (HederaSigner, XrplSigner, etc.).
|
|
69
|
+
*
|
|
70
|
+
* Features:
|
|
71
|
+
* - Automatic session restoration from IndexedDB
|
|
72
|
+
* - QR code modal for wallet connection
|
|
73
|
+
* - Support for multiple ledgers (Hedera, XRPL, extensible)
|
|
74
|
+
* - Clean separation of orchestration vs ledger logic
|
|
75
|
+
* - **Multi-session support**: Connect multiple wallets across different ledgers/networks
|
|
76
|
+
*/
|
|
77
|
+
@Injectable({ providedIn: 'root' })
|
|
78
|
+
export class WalletConnectV2Provider extends BaseWalletProvider {
|
|
79
|
+
override readonly id = 'walletconnect-v2';
|
|
80
|
+
|
|
81
|
+
override readonly metadata: ProviderMetadata = {
|
|
82
|
+
id: 'walletconnect-v2',
|
|
83
|
+
name: 'WalletConnect',
|
|
84
|
+
type: 'walletconnect-v2',
|
|
85
|
+
description: 'Connect via QR code to Hedera, XRPL, and other wallets',
|
|
86
|
+
supportsMultipleAccounts: true, // Changed to true for multi-session
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Client manager - handles SignClient lifecycle.
|
|
91
|
+
*/
|
|
92
|
+
private readonly clientManager = new WalletConnectClientManager();
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Session store - handles session Map and account aggregation.
|
|
96
|
+
*/
|
|
97
|
+
private readonly sessionStore = new WalletConnectSessionStore();
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Signing orchestrator - handles transaction signing routing.
|
|
101
|
+
*/
|
|
102
|
+
private signingOrchestrator!: WalletConnectSigningOrchestrator;
|
|
103
|
+
|
|
104
|
+
// Track session_delete listener registration
|
|
105
|
+
private sessionDeleteListenerRegistered = false;
|
|
106
|
+
|
|
107
|
+
// Reactive state
|
|
108
|
+
private _status = signal<ConnectionStatus>('disconnected');
|
|
109
|
+
private _error = signal<string | null>(null);
|
|
110
|
+
|
|
111
|
+
override readonly status = this._status.asReadonly();
|
|
112
|
+
override readonly accounts = this.sessionStore.accounts;
|
|
113
|
+
override readonly error = this._error.asReadonly();
|
|
114
|
+
|
|
115
|
+
// Event bus for emitting wallet events
|
|
116
|
+
private readonly eventBus = inject(WalletEventBus);
|
|
117
|
+
|
|
118
|
+
// NgZone for ensuring change detection runs after WalletConnect events
|
|
119
|
+
private readonly zone = inject(NgZone);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
*
|
|
123
|
+
*/
|
|
124
|
+
constructor() {
|
|
125
|
+
super();
|
|
126
|
+
|
|
127
|
+
// Initialize signing orchestrator
|
|
128
|
+
this.signingOrchestrator = new WalletConnectSigningOrchestrator({
|
|
129
|
+
providerId: this.id,
|
|
130
|
+
sessionStore: this.sessionStore,
|
|
131
|
+
clientManager: this.clientManager,
|
|
132
|
+
eventBus: this.eventBus,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
logger.info('Initializing - will attempt session restoration');
|
|
136
|
+
void this.restoreSession();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Ensure that a previously restored WalletConnect session is present in memory.
|
|
141
|
+
* Useful for UI flows that want to re-select a session after reload without forcing a new pairing flow.
|
|
142
|
+
* @param sessionKey
|
|
143
|
+
*/
|
|
144
|
+
async resumeSession(sessionKey: string): Promise<boolean> {
|
|
145
|
+
if (this.sessionStore.hasSession(sessionKey)) {
|
|
146
|
+
this.sessionStore.updateAggregatedAccounts();
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
await this.restoreSession();
|
|
151
|
+
const exists = this.sessionStore.hasSession(sessionKey);
|
|
152
|
+
if (exists) {
|
|
153
|
+
this.sessionStore.updateAggregatedAccounts();
|
|
154
|
+
}
|
|
155
|
+
return exists;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Restore existing WalletConnect sessions from IndexedDB.
|
|
160
|
+
* Automatically called during provider initialization.
|
|
161
|
+
* Uses clientManager for client lifecycle.
|
|
162
|
+
*/
|
|
163
|
+
private async restoreSession(): Promise<void> {
|
|
164
|
+
try {
|
|
165
|
+
logger.debug('Attempting to restore sessions...');
|
|
166
|
+
|
|
167
|
+
// Restore client from stored projectId
|
|
168
|
+
const client = await this.clientManager.restoreClient({
|
|
169
|
+
name: 'HSuite Demo',
|
|
170
|
+
description: 'Multi-chain wallet connection',
|
|
171
|
+
url: window.location.origin,
|
|
172
|
+
icons: [],
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (!client) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Register session_delete listener
|
|
180
|
+
this.registerSessionDeleteListener();
|
|
181
|
+
|
|
182
|
+
// Get all active sessions
|
|
183
|
+
const activeSessions = this.clientManager.getActiveSessions();
|
|
184
|
+
logger.info('Found active sessions', { count: activeSessions.length });
|
|
185
|
+
|
|
186
|
+
if (activeSessions.length === 0) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Restore each session
|
|
191
|
+
for (const session of activeSessions) {
|
|
192
|
+
try {
|
|
193
|
+
await this.restoreSingleSession(session);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
logger.error('Failed to restore session', {
|
|
196
|
+
topic: session.topic,
|
|
197
|
+
error: error instanceof Error ? error.message : String(error),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Update aggregated accounts
|
|
203
|
+
this.sessionStore.updateAggregatedAccounts();
|
|
204
|
+
|
|
205
|
+
if (this.sessionStore.size > 0) {
|
|
206
|
+
this._status.set('connected');
|
|
207
|
+
logger.info('Restored sessions', { count: this.sessionStore.size });
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
logger.error('Failed to restore sessions', {
|
|
211
|
+
error: error instanceof Error ? error.message : String(error),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Restore a single WalletConnect session.
|
|
218
|
+
* Internal helper for multi-session restoration.
|
|
219
|
+
*
|
|
220
|
+
* IMPORTANT: Reads user's network selection from localStorage to ensure
|
|
221
|
+
* restored sessions use the correct network the user originally selected.
|
|
222
|
+
*
|
|
223
|
+
* @param session - WalletConnect session object from IndexedDB
|
|
224
|
+
*/
|
|
225
|
+
private async restoreSingleSession(session: any): Promise<void> {
|
|
226
|
+
logger.debug('Restoring session', { topic: session.topic });
|
|
227
|
+
logger.debug('Session namespaces', { namespaces: Object.keys(session.namespaces) });
|
|
228
|
+
|
|
229
|
+
// Determine ledger from session namespaces
|
|
230
|
+
let ledgerId: string | undefined;
|
|
231
|
+
let walletReportedNetwork: string | undefined;
|
|
232
|
+
|
|
233
|
+
if (session.namespaces.hedera) {
|
|
234
|
+
ledgerId = 'hedera';
|
|
235
|
+
walletReportedNetwork = session.namespaces.hedera.chains?.[0] || 'hedera:testnet';
|
|
236
|
+
logger.debug('Found Hedera namespace', { chains: session.namespaces.hedera.chains });
|
|
237
|
+
} else if (session.namespaces.xrpl) {
|
|
238
|
+
ledgerId = 'xrpl';
|
|
239
|
+
walletReportedNetwork = session.namespaces.xrpl.chains?.[0] || 'xrpl:testnet';
|
|
240
|
+
logger.debug('Found XRPL namespace', { chains: session.namespaces.xrpl.chains });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!ledgerId || !walletReportedNetwork) {
|
|
244
|
+
logger.warn('Could not determine ledger for session', {
|
|
245
|
+
topic: session.topic,
|
|
246
|
+
availableNamespaces: Object.keys(session.namespaces),
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// CRITICAL: Retrieve user's original network selection from localStorage
|
|
252
|
+
// This ensures we restore the session with the network the user actually selected
|
|
253
|
+
const userSelectedNetwork = this.getUserNetworkSelection(session.topic);
|
|
254
|
+
const networkId = userSelectedNetwork || walletReportedNetwork;
|
|
255
|
+
|
|
256
|
+
if (userSelectedNetwork) {
|
|
257
|
+
logger.debug('Restored user network selection', {
|
|
258
|
+
userSelectedNetwork,
|
|
259
|
+
walletReportedNetwork,
|
|
260
|
+
topic: session.topic.substring(0, 16),
|
|
261
|
+
});
|
|
262
|
+
} else {
|
|
263
|
+
logger.warn('No stored network selection found, using wallet-reported network', {
|
|
264
|
+
walletReportedNetwork,
|
|
265
|
+
topic: session.topic.substring(0, 16),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Get the appropriate signer
|
|
270
|
+
const signer = SignerFactory.getSigner(ledgerId);
|
|
271
|
+
|
|
272
|
+
// Generate session key using the user's selected network
|
|
273
|
+
const sessionKey = this.sessionStore.generateSessionKey(ledgerId, networkId, session.topic);
|
|
274
|
+
|
|
275
|
+
// Parse accounts from session, filtering by user's selected network
|
|
276
|
+
const namespace = session.namespaces[ledgerId];
|
|
277
|
+
logger.debug('Namespace for restoration', {
|
|
278
|
+
ledgerId,
|
|
279
|
+
accountCount: (namespace as { accounts?: any[] })?.accounts?.length || 0,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Pass the user's selected network to filter accounts appropriately
|
|
283
|
+
const parsedAccounts = signer.parseAccounts(namespace, userSelectedNetwork);
|
|
284
|
+
logger.debug('Parsed accounts from restored session', {
|
|
285
|
+
count: parsedAccounts.length,
|
|
286
|
+
userSelectedNetwork,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
if (parsedAccounts.length === 0) {
|
|
290
|
+
logger.warn('Restored session has no accounts for selected network', {
|
|
291
|
+
userSelectedNetwork,
|
|
292
|
+
walletReportedNetwork,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Create UnifiedAccount objects with user's selected network
|
|
297
|
+
const accounts: UnifiedAccount[] = parsedAccounts.map((acc, index) => ({
|
|
298
|
+
id: `${this.id}-${ledgerId}-${acc.address}`,
|
|
299
|
+
address: acc.address,
|
|
300
|
+
label: `Account ${index + 1}`,
|
|
301
|
+
ledgerId: ledgerId as 'hedera' | 'xrpl',
|
|
302
|
+
networkId: networkId, // Use user's selected network
|
|
303
|
+
providerId: this.id,
|
|
304
|
+
providerType: 'walletconnect-v2',
|
|
305
|
+
metadata: {
|
|
306
|
+
topic: session.topic,
|
|
307
|
+
ledger: ledgerId,
|
|
308
|
+
sessionKey,
|
|
309
|
+
peer: session.peer?.metadata,
|
|
310
|
+
walletReportedChainId: acc.chainId, // Keep original for debugging
|
|
311
|
+
userSelectedNetwork: networkId,
|
|
312
|
+
},
|
|
313
|
+
}));
|
|
314
|
+
|
|
315
|
+
// Store session data with user's network selection
|
|
316
|
+
this.sessionStore.setSession(sessionKey, {
|
|
317
|
+
session,
|
|
318
|
+
ledgerId,
|
|
319
|
+
networkId,
|
|
320
|
+
userSelectedNetwork: networkId,
|
|
321
|
+
signer,
|
|
322
|
+
accounts,
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
logger.info('Restored session', {
|
|
326
|
+
sessionKey,
|
|
327
|
+
ledgerId,
|
|
328
|
+
networkId,
|
|
329
|
+
userSelectedNetwork: networkId,
|
|
330
|
+
accountCount: accounts.length,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Emit session restored event
|
|
334
|
+
this.eventBus.sessionRestored.emit({
|
|
335
|
+
providerId: this.id,
|
|
336
|
+
sessionKey,
|
|
337
|
+
accounts,
|
|
338
|
+
metadata: { topic: session.topic, ledgerId, networkId, userSelectedNetwork: networkId },
|
|
339
|
+
timestamp: Date.now(),
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// NOTE: generateSessionKey and updateAggregatedAccounts have been moved to WalletConnectSessionStore
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Store user's network selection in localStorage for session restoration.
|
|
347
|
+
* This ensures the user's network choice persists across page reloads.
|
|
348
|
+
*
|
|
349
|
+
* @param sessionTopic - WalletConnect session topic
|
|
350
|
+
* @param networkId - User's selected network (e.g., 'hedera:mainnet')
|
|
351
|
+
*/
|
|
352
|
+
private storeUserNetworkSelection(sessionTopic: string, networkId: string): void {
|
|
353
|
+
try {
|
|
354
|
+
localStorage.setItem(`${NETWORK_SELECTION_STORAGE_KEY}${sessionTopic}`, networkId);
|
|
355
|
+
logger.debug('Stored user network selection', {
|
|
356
|
+
topic: sessionTopic.substring(0, 16),
|
|
357
|
+
networkId,
|
|
358
|
+
});
|
|
359
|
+
} catch (e) {
|
|
360
|
+
logger.warn('Failed to store network selection', { error: (e as Error).message });
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Retrieve user's network selection from localStorage for session restoration.
|
|
366
|
+
*
|
|
367
|
+
* @param sessionTopic - WalletConnect session topic
|
|
368
|
+
* @returns User's selected network, or undefined if not found
|
|
369
|
+
*/
|
|
370
|
+
private getUserNetworkSelection(sessionTopic: string): string | undefined {
|
|
371
|
+
try {
|
|
372
|
+
return localStorage.getItem(`${NETWORK_SELECTION_STORAGE_KEY}${sessionTopic}`) || undefined;
|
|
373
|
+
} catch (e) {
|
|
374
|
+
logger.warn('Failed to retrieve network selection', { error: (e as Error).message });
|
|
375
|
+
return undefined;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Clean up stored network selection for a session.
|
|
381
|
+
*
|
|
382
|
+
* @param sessionTopic - WalletConnect session topic
|
|
383
|
+
*/
|
|
384
|
+
private clearUserNetworkSelection(sessionTopic: string): void {
|
|
385
|
+
try {
|
|
386
|
+
localStorage.removeItem(`${NETWORK_SELECTION_STORAGE_KEY}${sessionTopic}`);
|
|
387
|
+
} catch (_e) {
|
|
388
|
+
// Ignore cleanup errors
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Connect to a wallet via WalletConnect QR code.
|
|
394
|
+
*
|
|
395
|
+
* Multi-session support:
|
|
396
|
+
* - Each connect() call creates a NEW session
|
|
397
|
+
* - Sessions are tracked separately in the sessions Map
|
|
398
|
+
* - Multiple sessions can coexist for different ledgers/networks
|
|
399
|
+
*
|
|
400
|
+
* Process:
|
|
401
|
+
* 1. Get appropriate signer for the ledger
|
|
402
|
+
* 2. Initialize WalletConnect client and modal
|
|
403
|
+
* 3. Build namespace configuration using signer
|
|
404
|
+
* 4. Generate QR code and wait for wallet approval
|
|
405
|
+
* 5. Store session and update aggregated accounts
|
|
406
|
+
*
|
|
407
|
+
* @param config - Connection configuration including ledger and network
|
|
408
|
+
*/
|
|
409
|
+
async connect(config: ConnectionConfig): Promise<void> {
|
|
410
|
+
const wcConfig = config as WalletConnectV2Config;
|
|
411
|
+
|
|
412
|
+
try {
|
|
413
|
+
this._status.set('connecting');
|
|
414
|
+
this._error.set(null);
|
|
415
|
+
|
|
416
|
+
// Validate configuration
|
|
417
|
+
const projectId = wcConfig.projectId;
|
|
418
|
+
if (!projectId) {
|
|
419
|
+
throw new Error(
|
|
420
|
+
'WalletConnect projectId is required. Get one from https://cloud.walletconnect.com',
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Get the appropriate signer for this ledger (Strategy Pattern)
|
|
425
|
+
const signer = SignerFactory.getSigner(wcConfig.ledgerId);
|
|
426
|
+
logger.debug('Using signer for ledger', { ledgerId: wcConfig.ledgerId });
|
|
427
|
+
|
|
428
|
+
// Initialize client via manager
|
|
429
|
+
await this.clientManager.initialize(projectId, {
|
|
430
|
+
name: wcConfig.appName || 'HSuite Demo',
|
|
431
|
+
description: wcConfig.appDescription || 'Multi-chain wallet connection',
|
|
432
|
+
url: window.location.origin,
|
|
433
|
+
icons: wcConfig.appIconUrl ? [wcConfig.appIconUrl] : [],
|
|
434
|
+
});
|
|
435
|
+
this.registerSessionDeleteListener();
|
|
436
|
+
|
|
437
|
+
// Create modal for QR code display
|
|
438
|
+
const modal = this.clientManager.createModal(projectId, [wcConfig.networkId]);
|
|
439
|
+
|
|
440
|
+
// Build namespace configuration using signer
|
|
441
|
+
const namespaceConfig = signer.buildNamespace(wcConfig.networkId);
|
|
442
|
+
const optionalNamespaces = { [wcConfig.ledgerId]: namespaceConfig };
|
|
443
|
+
logger.debug('Namespace config', { ledgerId: wcConfig.ledgerId });
|
|
444
|
+
|
|
445
|
+
// Build session properties to pass user's preferred network to wallet
|
|
446
|
+
// This tells the wallet which network accounts to show in the picker
|
|
447
|
+
const sessionProperties = {
|
|
448
|
+
preferredNetwork: wcConfig.networkId,
|
|
449
|
+
preferredLedger: wcConfig.ledgerId,
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
logger.info('Connecting with preferred network', {
|
|
453
|
+
preferredNetwork: wcConfig.networkId,
|
|
454
|
+
preferredLedger: wcConfig.ledgerId,
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
// Connect and show QR code
|
|
458
|
+
const { uri, approval } = await this.clientManager.connect(
|
|
459
|
+
optionalNamespaces,
|
|
460
|
+
sessionProperties,
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
if (uri) {
|
|
464
|
+
await modal.openModal({ uri });
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Wait for wallet approval
|
|
468
|
+
logger.info('Waiting for wallet approval...');
|
|
469
|
+
const session = await approval();
|
|
470
|
+
this.clientManager.closeModal();
|
|
471
|
+
|
|
472
|
+
logger.info('[WC:DAPP:APPROVED] Session approved by wallet', {
|
|
473
|
+
topic: session.topic.substring(0, 16) + '...',
|
|
474
|
+
fullTopic: session.topic,
|
|
475
|
+
namespaces: Object.keys(session.namespaces),
|
|
476
|
+
expiry: session.expiry,
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
// Generate session key
|
|
480
|
+
const sessionKey = this.sessionStore.generateSessionKey(
|
|
481
|
+
wcConfig.ledgerId,
|
|
482
|
+
wcConfig.networkId,
|
|
483
|
+
session.topic,
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
// Parse accounts from session
|
|
487
|
+
const sessionNamespace = session.namespaces[wcConfig.ledgerId];
|
|
488
|
+
logger.debug('Session namespace for ledger', {
|
|
489
|
+
ledgerId: wcConfig.ledgerId,
|
|
490
|
+
accountCount: (sessionNamespace as { accounts?: any[] })?.accounts?.length || 0,
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
if (!sessionNamespace) {
|
|
494
|
+
logger.error('No namespace found for ledger', {
|
|
495
|
+
ledgerId: wcConfig.ledgerId,
|
|
496
|
+
availableNamespaces: Object.keys(session.namespaces),
|
|
497
|
+
});
|
|
498
|
+
throw new Error(
|
|
499
|
+
`Wallet did not approve ${wcConfig.ledgerId} namespace. Available: ${Object.keys(session.namespaces).join(', ')}`,
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Parse accounts from session namespace, filtering by user's selected network
|
|
504
|
+
// This ensures we only show accounts that match the network the user selected
|
|
505
|
+
const parsedAccounts = signer.parseAccounts(sessionNamespace, wcConfig.networkId);
|
|
506
|
+
logger.debug('Parsed accounts', {
|
|
507
|
+
count: parsedAccounts.length,
|
|
508
|
+
userSelectedNetwork: wcConfig.networkId,
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
if (parsedAccounts.length === 0) {
|
|
512
|
+
logger.warn('No accounts returned from wallet - possible causes:', {
|
|
513
|
+
reason1: 'Wallet has no accounts for the requested network',
|
|
514
|
+
reason2: 'Wallet denied account sharing',
|
|
515
|
+
reason3: 'Wallet returned accounts in unexpected format',
|
|
516
|
+
userSelectedNetwork: wcConfig.networkId,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Create UnifiedAccount objects
|
|
521
|
+
// IMPORTANT: Use wcConfig.networkId (user's selection) for account networkId
|
|
522
|
+
// This respects user intent rather than relying on wallet-reported chain
|
|
523
|
+
const accounts: UnifiedAccount[] = parsedAccounts.map((acc, index) => ({
|
|
524
|
+
id: `${this.id}-${wcConfig.ledgerId}-${acc.address}`,
|
|
525
|
+
address: acc.address,
|
|
526
|
+
label: `Account ${index + 1}`,
|
|
527
|
+
ledgerId: wcConfig.ledgerId,
|
|
528
|
+
networkId: wcConfig.networkId, // Use user's selected network, not wallet-reported
|
|
529
|
+
providerId: this.id,
|
|
530
|
+
providerType: 'walletconnect-v2',
|
|
531
|
+
metadata: {
|
|
532
|
+
topic: session.topic,
|
|
533
|
+
ledger: wcConfig.ledgerId,
|
|
534
|
+
sessionKey,
|
|
535
|
+
walletReportedChainId: acc.chainId, // Keep original for debugging
|
|
536
|
+
userSelectedNetwork: wcConfig.networkId, // Store for session restoration
|
|
537
|
+
},
|
|
538
|
+
}));
|
|
539
|
+
|
|
540
|
+
// Store session data with user's selected network
|
|
541
|
+
this.sessionStore.setSession(sessionKey, {
|
|
542
|
+
session,
|
|
543
|
+
ledgerId: wcConfig.ledgerId,
|
|
544
|
+
networkId: wcConfig.networkId,
|
|
545
|
+
userSelectedNetwork: wcConfig.networkId, // Store user's selection for restoration
|
|
546
|
+
signer,
|
|
547
|
+
accounts,
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Persist user's network selection for session restoration after page reload
|
|
551
|
+
this.storeUserNetworkSelection(session.topic, wcConfig.networkId);
|
|
552
|
+
|
|
553
|
+
// Verify session exists in WC SDK after storing
|
|
554
|
+
const sdkSession = this.clientManager.getSession(session.topic);
|
|
555
|
+
|
|
556
|
+
logger.info('[WC:DAPP:STORED] Session stored locally', {
|
|
557
|
+
sessionKey,
|
|
558
|
+
topic: session.topic.substring(0, 16) + '...',
|
|
559
|
+
fullTopic: session.topic,
|
|
560
|
+
accountCount: accounts.length,
|
|
561
|
+
totalSessions: this.sessionStore.size,
|
|
562
|
+
existsInSdkSession: !!sdkSession,
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
// Update aggregated accounts
|
|
566
|
+
this.sessionStore.updateAggregatedAccounts();
|
|
567
|
+
this._status.set('connected');
|
|
568
|
+
|
|
569
|
+
// Emit session created event
|
|
570
|
+
this.eventBus.sessionCreated.emit({
|
|
571
|
+
providerId: this.id,
|
|
572
|
+
sessionKey,
|
|
573
|
+
accounts,
|
|
574
|
+
metadata: {
|
|
575
|
+
topic: session.topic,
|
|
576
|
+
ledgerId: wcConfig.ledgerId,
|
|
577
|
+
networkId: wcConfig.networkId,
|
|
578
|
+
},
|
|
579
|
+
timestamp: Date.now(),
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// Register session_delete listener (idempotent)
|
|
583
|
+
this.registerSessionDeleteListener();
|
|
584
|
+
} catch (error) {
|
|
585
|
+
this._status.set('error');
|
|
586
|
+
this._error.set(error instanceof Error ? error.message : 'Connection failed');
|
|
587
|
+
logger.error('Connection failed', {
|
|
588
|
+
error: error instanceof Error ? error.message : String(error),
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
// Emit provider error event
|
|
592
|
+
this.eventBus.providerError.emit({
|
|
593
|
+
providerId: this.id,
|
|
594
|
+
error: error instanceof Error ? error : String(error),
|
|
595
|
+
context: 'connection',
|
|
596
|
+
timestamp: Date.now(),
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
this.clientManager.closeModal();
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Remove a session by its WalletConnect topic.
|
|
606
|
+
* Internal helper for session_delete event handling.
|
|
607
|
+
*
|
|
608
|
+
* @param topic - WalletConnect session topic
|
|
609
|
+
*/
|
|
610
|
+
private removeSessionByTopic(topic: string): void {
|
|
611
|
+
// Find and remove the session using session store
|
|
612
|
+
const removed = this.sessionStore.removeSessionByTopic(topic);
|
|
613
|
+
|
|
614
|
+
// Clean up stored network selection
|
|
615
|
+
this.clearUserNetworkSelection(topic);
|
|
616
|
+
|
|
617
|
+
// Update aggregated accounts
|
|
618
|
+
this.sessionStore.updateAggregatedAccounts();
|
|
619
|
+
|
|
620
|
+
// Update status
|
|
621
|
+
if (this.sessionStore.size === 0) {
|
|
622
|
+
this._status.set('disconnected');
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Emit events if session was found
|
|
626
|
+
if (removed) {
|
|
627
|
+
// Emit session deleted event
|
|
628
|
+
this.eventBus.sessionDeleted.emit({
|
|
629
|
+
providerId: this.id,
|
|
630
|
+
sessionKey: removed.sessionKey,
|
|
631
|
+
accounts: removed.data.accounts,
|
|
632
|
+
metadata: { topic, reason: 'wallet_initiated' },
|
|
633
|
+
timestamp: Date.now(),
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
// Emit disconnection event
|
|
637
|
+
this.eventBus.disconnected.emit({
|
|
638
|
+
providerId: this.id,
|
|
639
|
+
providerType: 'walletconnect-v2',
|
|
640
|
+
sessionKey: removed.sessionKey,
|
|
641
|
+
reason: 'wallet_initiated_disconnect',
|
|
642
|
+
timestamp: Date.now(),
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Register session_delete event listener to handle wallet-initiated disconnects.
|
|
649
|
+
* Idempotent - only registers once even if called multiple times.
|
|
650
|
+
*
|
|
651
|
+
* @private
|
|
652
|
+
*/
|
|
653
|
+
private registerSessionDeleteListener(): void {
|
|
654
|
+
if (!this.clientManager.isInitialized() || this.sessionDeleteListenerRegistered) {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// ✅ CRITICAL: Run inside NgZone to trigger Angular's change detection
|
|
659
|
+
// WalletConnect events arrive outside Angular's zone
|
|
660
|
+
this.clientManager.on('session_delete', (event: any) => {
|
|
661
|
+
this.zone.run(() => {
|
|
662
|
+
logger.info('� Session deleted by wallet (session_delete event)', {
|
|
663
|
+
topic: (event as { topic: string }).topic,
|
|
664
|
+
});
|
|
665
|
+
this.removeSessionByTopic((event as { topic: string }).topic);
|
|
666
|
+
});
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
this.sessionDeleteListenerRegistered = true;
|
|
670
|
+
logger.debug('session_delete listener registered');
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Disconnect from WalletConnect sessions.
|
|
675
|
+
* Supports disconnecting all sessions or a specific session.
|
|
676
|
+
*
|
|
677
|
+
* @param sessionKey - Optional session key to disconnect specific session
|
|
678
|
+
*/
|
|
679
|
+
async disconnect(sessionKey?: string): Promise<void> {
|
|
680
|
+
if (!this.clientManager.isInitialized()) {
|
|
681
|
+
logger.warn('No client to disconnect');
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if (sessionKey) {
|
|
686
|
+
// Disconnect specific session
|
|
687
|
+
const sessionData = this.sessionStore.getSession(sessionKey);
|
|
688
|
+
if (sessionData) {
|
|
689
|
+
try {
|
|
690
|
+
await this.clientManager.disconnect(sessionData.session.topic, {
|
|
691
|
+
code: 6000,
|
|
692
|
+
message: 'User disconnected session',
|
|
693
|
+
});
|
|
694
|
+
logger.info('Disconnected session', { sessionKey });
|
|
695
|
+
} catch (e) {
|
|
696
|
+
logger.error('Disconnect error for session', {
|
|
697
|
+
sessionKey,
|
|
698
|
+
error: e instanceof Error ? e.message : String(e),
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Clean up stored network selection
|
|
703
|
+
this.clearUserNetworkSelection(sessionData.session.topic);
|
|
704
|
+
|
|
705
|
+
this.sessionStore.deleteSession(sessionKey);
|
|
706
|
+
this.sessionStore.updateAggregatedAccounts();
|
|
707
|
+
|
|
708
|
+
if (this.sessionStore.size === 0) {
|
|
709
|
+
this._status.set('disconnected');
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
} else {
|
|
713
|
+
// Disconnect all sessions
|
|
714
|
+
const disconnectPromises: Promise<void>[] = [];
|
|
715
|
+
|
|
716
|
+
for (const [_key, sessionData] of this.sessionStore.entries()) {
|
|
717
|
+
// Clean up stored network selection for each session
|
|
718
|
+
this.clearUserNetworkSelection(sessionData.session.topic);
|
|
719
|
+
|
|
720
|
+
disconnectPromises.push(
|
|
721
|
+
this.clientManager
|
|
722
|
+
.disconnect(sessionData.session.topic, {
|
|
723
|
+
code: 6000,
|
|
724
|
+
message: 'User disconnected all sessions',
|
|
725
|
+
})
|
|
726
|
+
.catch((err: Error) =>
|
|
727
|
+
logger.error('Disconnect error', {
|
|
728
|
+
error: err.message,
|
|
729
|
+
}),
|
|
730
|
+
),
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
await Promise.all(disconnectPromises);
|
|
735
|
+
|
|
736
|
+
this.sessionStore.clear();
|
|
737
|
+
this._status.set('disconnected');
|
|
738
|
+
|
|
739
|
+
logger.info('Disconnected all sessions');
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
this.clientManager.closeModal();
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// NOTE: findSessionForAccount and validateSessionInSdk moved to WalletConnectSigningOrchestrator
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Sign a transaction without submitting (delegates to SigningOrchestrator).
|
|
749
|
+
* @param options
|
|
750
|
+
*/
|
|
751
|
+
async signTransaction(options: SignTransactionOptions): Promise<SignResult> {
|
|
752
|
+
if (!this.clientManager.isInitialized()) {
|
|
753
|
+
throw new Error('Not connected to WalletConnect');
|
|
754
|
+
}
|
|
755
|
+
return this.signingOrchestrator.signTransaction(options);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Sign and submit a transaction (delegates to SigningOrchestrator).
|
|
760
|
+
* @param options
|
|
761
|
+
*/
|
|
762
|
+
async submitTransaction(options: SubmitTransactionOptions): Promise<SubmitResult> {
|
|
763
|
+
if (!this.clientManager.isInitialized()) {
|
|
764
|
+
throw new Error('Not connected to WalletConnect');
|
|
765
|
+
}
|
|
766
|
+
return this.signingOrchestrator.submitTransaction(options);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Sign and execute transaction in one call (delegates to SigningOrchestrator).
|
|
771
|
+
* @param options
|
|
772
|
+
*/
|
|
773
|
+
async signAndExecuteTransaction(options: SubmitTransactionOptions): Promise<SubmitResult> {
|
|
774
|
+
if (!this.clientManager.isInitialized()) {
|
|
775
|
+
throw new Error('Not connected to WalletConnect');
|
|
776
|
+
}
|
|
777
|
+
return this.signingOrchestrator.signAndExecuteTransaction(options);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Sign an arbitrary message (delegates to SigningOrchestrator).
|
|
782
|
+
* @param options
|
|
783
|
+
*/
|
|
784
|
+
async signMessage(options: SignMessageOptions): Promise<SignMessageResult> {
|
|
785
|
+
if (!this.clientManager.isInitialized()) {
|
|
786
|
+
throw new Error('Not connected to WalletConnect');
|
|
787
|
+
}
|
|
788
|
+
return this.signingOrchestrator.signMessage(options);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Check if WalletConnect is available.
|
|
793
|
+
* Always returns true as WalletConnect is browser-based.
|
|
794
|
+
*/
|
|
795
|
+
async isAvailable(): Promise<boolean> {
|
|
796
|
+
return true;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Get all active WalletConnect sessions.
|
|
801
|
+
* Useful for displaying session information in UI and managing multiple connections.
|
|
802
|
+
* Delegates to session store.
|
|
803
|
+
*
|
|
804
|
+
* @returns Array of session information objects
|
|
805
|
+
*/
|
|
806
|
+
getActiveSessions(): Array<{
|
|
807
|
+
sessionKey: string;
|
|
808
|
+
ledgerId: string;
|
|
809
|
+
networkId: string;
|
|
810
|
+
userSelectedNetwork: string;
|
|
811
|
+
accounts: UnifiedAccount[];
|
|
812
|
+
topic: string;
|
|
813
|
+
}> {
|
|
814
|
+
return this.sessionStore.getActiveSessions();
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Disconnect a specific session by session key.
|
|
819
|
+
* Public wrapper for the private disconnect method.
|
|
820
|
+
*
|
|
821
|
+
* @param sessionKey - The session key to disconnect
|
|
822
|
+
*/
|
|
823
|
+
async disconnectSession(sessionKey: string): Promise<void> {
|
|
824
|
+
await this.disconnect(sessionKey);
|
|
825
|
+
}
|
|
826
|
+
}
|