@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,695 @@
|
|
|
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 p2p-session-manager.ts
|
|
13
|
+
* @description Session management for P2P Native connections (dApp-side)
|
|
14
|
+
*
|
|
15
|
+
* Manages Nostr-first P2P connections for wallet pairing (both mobile and desktop).
|
|
16
|
+
* The dApp creates a session invite QR code that the wallet scans.
|
|
17
|
+
* Communication starts via Nostr relays and upgrades to WebRTC P2P when possible.
|
|
18
|
+
*
|
|
19
|
+
* **Architecture Notes:**
|
|
20
|
+
* - Uses the unified ChannelClient for Nostr/P2P communication
|
|
21
|
+
* - Single QR code scanning (no 2-phase exchange)
|
|
22
|
+
* - Session negotiation via Nostr, then P2P upgrade
|
|
23
|
+
* - RPC communication over best available transport
|
|
24
|
+
* - Session ownership remains with the wallet
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { Injectable, signal, type Signal, NgZone } from '@angular/core';
|
|
28
|
+
import {
|
|
29
|
+
ChannelClient,
|
|
30
|
+
type ChannelInvite,
|
|
31
|
+
type ChannelState,
|
|
32
|
+
type TransportState,
|
|
33
|
+
type ChannelAccount,
|
|
34
|
+
encodeChannelInvite,
|
|
35
|
+
getLogger,
|
|
36
|
+
} from '@hsuite/native-connect-sdk';
|
|
37
|
+
|
|
38
|
+
import type { UnifiedAccount } from '../../models/unified-account.model';
|
|
39
|
+
|
|
40
|
+
const logger = getLogger().scoped?.('P2PSessionManager') ?? getLogger();
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Connection state for P2P native sessions.
|
|
44
|
+
*
|
|
45
|
+
* States:
|
|
46
|
+
* - idle: No connection attempt in progress
|
|
47
|
+
* - creating_offer: Generating session invite for QR code
|
|
48
|
+
* - awaiting_connection: QR displayed, waiting for wallet to scan and connect
|
|
49
|
+
* - pending_approval: Wallet connected, waiting for user to approve session
|
|
50
|
+
* - connected: Session approved and fully connected
|
|
51
|
+
* - disconnected: Previously connected, now disconnected
|
|
52
|
+
* - error: Connection or handshake error occurred
|
|
53
|
+
*/
|
|
54
|
+
export type P2PConnectionState =
|
|
55
|
+
| 'idle'
|
|
56
|
+
| 'creating_offer'
|
|
57
|
+
| 'awaiting_connection'
|
|
58
|
+
| 'pending_approval'
|
|
59
|
+
| 'connected'
|
|
60
|
+
| 'disconnected'
|
|
61
|
+
| 'error';
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Options for creating a pairing offer
|
|
65
|
+
*/
|
|
66
|
+
export interface CreateOfferOptions {
|
|
67
|
+
appId: string;
|
|
68
|
+
appName: string;
|
|
69
|
+
ledgerId: string;
|
|
70
|
+
networkId: string;
|
|
71
|
+
metadata?: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Result of creating a pairing offer
|
|
76
|
+
*/
|
|
77
|
+
export interface CreateOfferResult {
|
|
78
|
+
/** Encoded QR data (nostr+p2p://...) */
|
|
79
|
+
qrData: string;
|
|
80
|
+
/** Channel ID for this connection attempt */
|
|
81
|
+
sessionId: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Managed session data
|
|
86
|
+
*/
|
|
87
|
+
export interface P2PSession {
|
|
88
|
+
id: string;
|
|
89
|
+
sessionId: string;
|
|
90
|
+
appId: string;
|
|
91
|
+
appName: string;
|
|
92
|
+
ledgerId: string;
|
|
93
|
+
networkId: string;
|
|
94
|
+
accounts: UnifiedAccount[];
|
|
95
|
+
permissions: string[];
|
|
96
|
+
connectedAt: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Transaction options for RPC calls
|
|
101
|
+
*/
|
|
102
|
+
export interface TransactionOptions {
|
|
103
|
+
accountAddress: string;
|
|
104
|
+
payload: string;
|
|
105
|
+
ledgerId?: string;
|
|
106
|
+
networkId?: string;
|
|
107
|
+
isBatch?: boolean;
|
|
108
|
+
batchKey?: string;
|
|
109
|
+
innerTransactions?: Array<{ payload: string; description?: string }>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Message signing options
|
|
114
|
+
*/
|
|
115
|
+
export interface MessageSigningOptions {
|
|
116
|
+
accountAddress: string;
|
|
117
|
+
message: string;
|
|
118
|
+
encoding?: 'utf-8' | 'base64';
|
|
119
|
+
ledgerId?: string;
|
|
120
|
+
networkId?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Mobile Native Session Manager
|
|
125
|
+
*
|
|
126
|
+
* Manages Nostr-first P2P connections for mobile wallet pairing.
|
|
127
|
+
* Creates and displays QR codes that mobile wallets can scan
|
|
128
|
+
* to establish a direct, end-to-end encrypted connection.
|
|
129
|
+
*
|
|
130
|
+
* Uses single QR code scanning with Nostr for initial signaling
|
|
131
|
+
* and automatic P2P upgrade when possible.
|
|
132
|
+
*
|
|
133
|
+
* This implementation uses the unified ChannelClient from the new channel protocol.
|
|
134
|
+
*/
|
|
135
|
+
@Injectable({ providedIn: 'root' })
|
|
136
|
+
export class P2PSessionManager {
|
|
137
|
+
// Private state
|
|
138
|
+
private readonly _connectionState = signal<P2PConnectionState>('idle');
|
|
139
|
+
private readonly _currentSession = signal<P2PSession | null>(null);
|
|
140
|
+
private readonly _p2pState = signal<TransportState>('nostr-only');
|
|
141
|
+
|
|
142
|
+
// Channel client
|
|
143
|
+
private client?: ChannelClient;
|
|
144
|
+
private currentInvite?: ChannelInvite;
|
|
145
|
+
|
|
146
|
+
// Callbacks
|
|
147
|
+
private connectedCallbacks: Array<(session: P2PSession) => void> = [];
|
|
148
|
+
private disconnectedCallbacks: Array<() => void> = [];
|
|
149
|
+
private errorCallbacks: Array<(error: Error) => void> = [];
|
|
150
|
+
private accountsChangedCallbacks: Array<(accounts: UnifiedAccount[]) => void> = [];
|
|
151
|
+
|
|
152
|
+
// NgZone for change detection
|
|
153
|
+
private zone?: NgZone;
|
|
154
|
+
|
|
155
|
+
// Session negotiation state
|
|
156
|
+
private sessionApprovalPromise?: {
|
|
157
|
+
resolve: (session: P2PSession) => void;
|
|
158
|
+
reject: (error: Error) => void;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
*
|
|
163
|
+
*/
|
|
164
|
+
constructor() {}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Set NgZone for Angular change detection
|
|
168
|
+
* @param zone
|
|
169
|
+
*/
|
|
170
|
+
setNgZone(zone: NgZone): void {
|
|
171
|
+
this.zone = zone;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Current connection state
|
|
176
|
+
*/
|
|
177
|
+
get connectionState(): Signal<P2PConnectionState> {
|
|
178
|
+
return this._connectionState.asReadonly();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Current session
|
|
183
|
+
*/
|
|
184
|
+
get currentSession(): Signal<P2PSession | null> {
|
|
185
|
+
return this._currentSession.asReadonly();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Current P2P upgrade state
|
|
190
|
+
*/
|
|
191
|
+
get p2pState(): Signal<TransportState> {
|
|
192
|
+
return this._p2pState.asReadonly();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Create a pairing offer for QR code display.
|
|
197
|
+
*
|
|
198
|
+
* Generates a compact session invite encoded for QR code display.
|
|
199
|
+
* The invite contains the channel ID, encryption key, and Nostr relays.
|
|
200
|
+
*
|
|
201
|
+
* @param options - Offer creation options
|
|
202
|
+
* @returns Promise resolving to offer result with QR data
|
|
203
|
+
*/
|
|
204
|
+
async createPairingOffer(options: CreateOfferOptions): Promise<CreateOfferResult> {
|
|
205
|
+
logger.info('Creating pairing offer (Nostr-first)', { options });
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
this.runInZone(() => this._connectionState.set('creating_offer'));
|
|
209
|
+
|
|
210
|
+
// Clean up any existing client
|
|
211
|
+
await this.cleanup(false);
|
|
212
|
+
|
|
213
|
+
// Create new channel client with state/account callbacks.
|
|
214
|
+
// The polling in startStateSync() also reads client.state, but these
|
|
215
|
+
// callbacks ensure immediate handling of state transitions — critical
|
|
216
|
+
// when the wallet approves via Nostr and the approval event arrives
|
|
217
|
+
// between poll ticks.
|
|
218
|
+
this.client = new ChannelClient({
|
|
219
|
+
p2p: { enabled: true },
|
|
220
|
+
onStateChange: (state: ChannelState) => {
|
|
221
|
+
logger.debug('ChannelClient state callback', { state });
|
|
222
|
+
const accounts = this.client?.accounts ?? [];
|
|
223
|
+
this.handleStateChange(state, accounts);
|
|
224
|
+
},
|
|
225
|
+
onAccountsChange: (accounts: ChannelAccount[]) => {
|
|
226
|
+
logger.debug('ChannelClient accounts callback', { count: accounts.length });
|
|
227
|
+
const state = this.client?.state ?? 'idle';
|
|
228
|
+
this.handleStateChange(state, accounts);
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Connect and get invite. Always include the dApp's origin so the
|
|
233
|
+
// wallet can show the user where the request is coming from.
|
|
234
|
+
const resolvedOrigin =
|
|
235
|
+
typeof window !== 'undefined' && window.location ? window.location.origin : undefined;
|
|
236
|
+
|
|
237
|
+
const invite = await this.client.connect({
|
|
238
|
+
type: 'session',
|
|
239
|
+
app: {
|
|
240
|
+
id: options.appId,
|
|
241
|
+
name: options.appName,
|
|
242
|
+
origin: resolvedOrigin,
|
|
243
|
+
},
|
|
244
|
+
context: {
|
|
245
|
+
ledgerId: options.ledgerId,
|
|
246
|
+
networkId: options.networkId,
|
|
247
|
+
},
|
|
248
|
+
permissions: ['sign', 'submit'],
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
this.currentInvite = invite;
|
|
252
|
+
|
|
253
|
+
// Encode invite for QR code
|
|
254
|
+
const qrData = encodeChannelInvite(invite);
|
|
255
|
+
|
|
256
|
+
this.runInZone(() => this._connectionState.set('awaiting_connection'));
|
|
257
|
+
|
|
258
|
+
logger.info('Pairing offer created', {
|
|
259
|
+
sessionId: invite.id.slice(0, 8),
|
|
260
|
+
qrDataLength: qrData.length,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Start polling for state changes
|
|
264
|
+
this.startStateSync();
|
|
265
|
+
|
|
266
|
+
return { qrData, sessionId: invite.id };
|
|
267
|
+
} catch (error) {
|
|
268
|
+
logger.error('Failed to create pairing offer', { error });
|
|
269
|
+
this.runInZone(() => this._connectionState.set('error'));
|
|
270
|
+
this.notifyError(error instanceof Error ? error : new Error('Failed to create offer'));
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Wait for wallet to connect and approve the session.
|
|
277
|
+
*
|
|
278
|
+
* This method blocks until the wallet scans the QR, connects via Nostr,
|
|
279
|
+
* and the user approves the session.
|
|
280
|
+
*
|
|
281
|
+
* @returns Promise resolving to the approved session
|
|
282
|
+
*/
|
|
283
|
+
async waitForSessionApproval(): Promise<P2PSession> {
|
|
284
|
+
if (!this.client || !this.currentInvite) {
|
|
285
|
+
throw new Error('No active pairing offer - call createPairingOffer first');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
logger.info('Waiting for wallet to connect and approve session...');
|
|
289
|
+
|
|
290
|
+
return new Promise<P2PSession>((resolve, reject) => {
|
|
291
|
+
this.sessionApprovalPromise = { resolve, reject };
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Cancel the current pairing offer.
|
|
297
|
+
*
|
|
298
|
+
* Cleans up the transport without fully disconnecting.
|
|
299
|
+
*/
|
|
300
|
+
async cancelPairingOffer(): Promise<void> {
|
|
301
|
+
logger.debug('Cancelling pairing offer');
|
|
302
|
+
|
|
303
|
+
if (this.sessionApprovalPromise) {
|
|
304
|
+
this.sessionApprovalPromise.reject(new Error('Pairing cancelled'));
|
|
305
|
+
this.sessionApprovalPromise = undefined;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
await this.cleanup();
|
|
309
|
+
this.runInZone(() => this._connectionState.set('idle'));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @param callback
|
|
314
|
+
* Register callback for when peer connects
|
|
315
|
+
*/
|
|
316
|
+
onConnected(callback: (session: P2PSession) => void): void {
|
|
317
|
+
this.connectedCallbacks.push(callback);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* @param callback
|
|
322
|
+
* Register callback for disconnection
|
|
323
|
+
*/
|
|
324
|
+
onDisconnected(callback: () => void): void {
|
|
325
|
+
this.disconnectedCallbacks.push(callback);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* @param callback
|
|
330
|
+
* Register callback for errors
|
|
331
|
+
*/
|
|
332
|
+
onError(callback: (error: Error) => void): void {
|
|
333
|
+
this.errorCallbacks.push(callback);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* @param callback
|
|
338
|
+
* Register callback for accounts changes
|
|
339
|
+
*/
|
|
340
|
+
onAccountsChanged(callback: (accounts: UnifiedAccount[]) => void): void {
|
|
341
|
+
this.accountsChangedCallbacks.push(callback);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @param options
|
|
346
|
+
* Sign a transaction via RPC
|
|
347
|
+
*/
|
|
348
|
+
async signTransaction(options: TransactionOptions): Promise<{
|
|
349
|
+
signedPayload: string;
|
|
350
|
+
signature?: string;
|
|
351
|
+
metadata?: Record<string, unknown>;
|
|
352
|
+
}> {
|
|
353
|
+
this.requireSession();
|
|
354
|
+
|
|
355
|
+
logger.info('Signing transaction', { account: options.accountAddress });
|
|
356
|
+
|
|
357
|
+
if (!this.client) {
|
|
358
|
+
throw new Error('Not connected');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const response = await this.client.request<{
|
|
362
|
+
signedPayload?: string;
|
|
363
|
+
signedTransaction?: string;
|
|
364
|
+
signature?: string;
|
|
365
|
+
metadata?: Record<string, unknown>;
|
|
366
|
+
}>('ledger/sign', {
|
|
367
|
+
accountAddress: options.accountAddress,
|
|
368
|
+
payload: options.payload,
|
|
369
|
+
ledgerId: options.ledgerId,
|
|
370
|
+
networkId: options.networkId,
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
signedPayload: response.signedPayload || response.signedTransaction || '',
|
|
375
|
+
signature: response.signature,
|
|
376
|
+
metadata: response.metadata,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* @param options
|
|
382
|
+
* Submit a transaction via RPC
|
|
383
|
+
*/
|
|
384
|
+
async submitTransaction(options: TransactionOptions): Promise<{
|
|
385
|
+
transactionId: string;
|
|
386
|
+
transactionHash?: string;
|
|
387
|
+
metadata?: Record<string, unknown>;
|
|
388
|
+
}> {
|
|
389
|
+
this.requireSession();
|
|
390
|
+
|
|
391
|
+
logger.info('Submitting transaction', { account: options.accountAddress });
|
|
392
|
+
|
|
393
|
+
if (!this.client) {
|
|
394
|
+
throw new Error('Not connected');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const response = await this.client.request<{
|
|
398
|
+
transactionId?: string;
|
|
399
|
+
transactionHash?: string;
|
|
400
|
+
metadata?: Record<string, unknown>;
|
|
401
|
+
}>('ledger/signAndSubmit', {
|
|
402
|
+
accountAddress: options.accountAddress,
|
|
403
|
+
payload: options.payload,
|
|
404
|
+
ledgerId: options.ledgerId,
|
|
405
|
+
networkId: options.networkId,
|
|
406
|
+
isBatch: options.isBatch,
|
|
407
|
+
batchKey: options.batchKey,
|
|
408
|
+
innerTransactions: options.innerTransactions,
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
return {
|
|
412
|
+
transactionId: response.transactionId || '',
|
|
413
|
+
transactionHash: response.transactionHash,
|
|
414
|
+
metadata: response.metadata,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* @param options
|
|
420
|
+
* Sign an arbitrary message via RPC
|
|
421
|
+
*/
|
|
422
|
+
async signMessage(options: MessageSigningOptions): Promise<{
|
|
423
|
+
signature: string;
|
|
424
|
+
publicKey?: string;
|
|
425
|
+
// §21.3 extended shape — passes the adapter's full MessageSignResult
|
|
426
|
+
// through to the dApp. Optional for backwards compatibility with
|
|
427
|
+
// pre-§21.3 wallets that only return signature + publicKey.
|
|
428
|
+
algorithm?: 'ed25519' | 'ecdsa_secp256k1' | 'secp256k1';
|
|
429
|
+
encoding?: 'utf-8' | 'base64';
|
|
430
|
+
caipChainId?: string;
|
|
431
|
+
messageLength?: number;
|
|
432
|
+
metadata?: Record<string, unknown>;
|
|
433
|
+
}> {
|
|
434
|
+
this.requireSession();
|
|
435
|
+
|
|
436
|
+
logger.info('Signing message', { account: options.accountAddress });
|
|
437
|
+
|
|
438
|
+
if (!this.client) {
|
|
439
|
+
throw new Error('Not connected');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const response = await this.client.request<{
|
|
443
|
+
signature?: string;
|
|
444
|
+
publicKey?: string;
|
|
445
|
+
algorithm?: 'ed25519' | 'ecdsa_secp256k1' | 'secp256k1';
|
|
446
|
+
encoding?: 'utf-8' | 'base64';
|
|
447
|
+
caipChainId?: string;
|
|
448
|
+
messageLength?: number;
|
|
449
|
+
metadata?: Record<string, unknown>;
|
|
450
|
+
}>('ledger/signMessage', {
|
|
451
|
+
accountAddress: options.accountAddress,
|
|
452
|
+
message: options.message,
|
|
453
|
+
encoding: options.encoding || 'utf-8',
|
|
454
|
+
ledgerId: options.ledgerId,
|
|
455
|
+
networkId: options.networkId,
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
return {
|
|
459
|
+
signature: response.signature || '',
|
|
460
|
+
publicKey: response.publicKey,
|
|
461
|
+
algorithm: response.algorithm,
|
|
462
|
+
encoding: response.encoding,
|
|
463
|
+
caipChainId: response.caipChainId,
|
|
464
|
+
messageLength: response.messageLength,
|
|
465
|
+
metadata: response.metadata,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Terminate the current session
|
|
471
|
+
*/
|
|
472
|
+
async terminateSession(): Promise<void> {
|
|
473
|
+
logger.info('Terminating session');
|
|
474
|
+
|
|
475
|
+
if (this.client) {
|
|
476
|
+
await this.client.disconnect();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
await this.cleanup();
|
|
480
|
+
this.runInZone(() => {
|
|
481
|
+
this._currentSession.set(null);
|
|
482
|
+
this._connectionState.set('disconnected');
|
|
483
|
+
});
|
|
484
|
+
this.notifyDisconnected();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ========== Private Methods ==========
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Run function in NgZone if available
|
|
491
|
+
* @param fn
|
|
492
|
+
*/
|
|
493
|
+
private runInZone(fn: () => void): void {
|
|
494
|
+
if (this.zone) {
|
|
495
|
+
this.zone.run(fn);
|
|
496
|
+
} else {
|
|
497
|
+
fn();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Start polling for state changes from the channel client
|
|
503
|
+
*/
|
|
504
|
+
private startStateSync(): void {
|
|
505
|
+
const checkState = () => {
|
|
506
|
+
if (!this.client) return;
|
|
507
|
+
|
|
508
|
+
const clientState = this.client.state;
|
|
509
|
+
const clientAccounts = this.client.accounts;
|
|
510
|
+
const clientTransport = this.client.transportState;
|
|
511
|
+
|
|
512
|
+
// Update transport state
|
|
513
|
+
this.runInZone(() => this._p2pState.set(clientTransport));
|
|
514
|
+
|
|
515
|
+
// Map channel state to P2P connection state
|
|
516
|
+
this.handleStateChange(clientState, clientAccounts);
|
|
517
|
+
|
|
518
|
+
// Continue polling while not disconnected
|
|
519
|
+
if (this.client && clientState !== 'disconnected' && clientState !== 'error') {
|
|
520
|
+
setTimeout(checkState, 100);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
setTimeout(checkState, 100);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Handle channel state changes
|
|
529
|
+
* @param state
|
|
530
|
+
* @param accounts
|
|
531
|
+
*/
|
|
532
|
+
private handleStateChange(state: ChannelState, accounts: ChannelAccount[]): void {
|
|
533
|
+
switch (state) {
|
|
534
|
+
case 'pending':
|
|
535
|
+
if (this._connectionState() === 'awaiting_connection') {
|
|
536
|
+
this.runInZone(() => this._connectionState.set('pending_approval'));
|
|
537
|
+
}
|
|
538
|
+
break;
|
|
539
|
+
|
|
540
|
+
case 'active':
|
|
541
|
+
case 'approved':
|
|
542
|
+
if (this._connectionState() !== 'connected') {
|
|
543
|
+
const session = this.createSession(accounts);
|
|
544
|
+
this.runInZone(() => {
|
|
545
|
+
this._currentSession.set(session);
|
|
546
|
+
this._connectionState.set('connected');
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
// Resolve the approval promise
|
|
550
|
+
if (this.sessionApprovalPromise) {
|
|
551
|
+
this.sessionApprovalPromise.resolve(session);
|
|
552
|
+
this.sessionApprovalPromise = undefined;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
this.notifyConnected(session);
|
|
556
|
+
}
|
|
557
|
+
break;
|
|
558
|
+
|
|
559
|
+
case 'disconnected':
|
|
560
|
+
this.handleDisconnection('Wallet disconnected');
|
|
561
|
+
break;
|
|
562
|
+
|
|
563
|
+
case 'error':
|
|
564
|
+
this.runInZone(() => this._connectionState.set('error'));
|
|
565
|
+
if (this.sessionApprovalPromise) {
|
|
566
|
+
this.sessionApprovalPromise.reject(new Error('Connection error'));
|
|
567
|
+
this.sessionApprovalPromise = undefined;
|
|
568
|
+
}
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Create a P2PSession from channel accounts
|
|
575
|
+
* @param accounts
|
|
576
|
+
*/
|
|
577
|
+
private createSession(accounts: ChannelAccount[]): P2PSession {
|
|
578
|
+
const invite = this.currentInvite;
|
|
579
|
+
|
|
580
|
+
return {
|
|
581
|
+
id: crypto.randomUUID(),
|
|
582
|
+
sessionId: invite?.id || crypto.randomUUID(),
|
|
583
|
+
appId: invite?.app.id || 'hsuite-dapp',
|
|
584
|
+
appName: invite?.app.name || 'HSuite dApp',
|
|
585
|
+
ledgerId: invite?.context.ledgerId || 'hedera',
|
|
586
|
+
networkId: invite?.context.networkId || 'testnet',
|
|
587
|
+
accounts: accounts.map((acc, index) => ({
|
|
588
|
+
id: `p2p-${acc.address}`,
|
|
589
|
+
address: acc.address,
|
|
590
|
+
label: acc.alias || `Account ${index + 1}`,
|
|
591
|
+
ledgerId: acc.ledgerId,
|
|
592
|
+
networkId: acc.networkId,
|
|
593
|
+
providerId: 'p2p-native',
|
|
594
|
+
providerType: 'hsuite-native' as const,
|
|
595
|
+
metadata: {
|
|
596
|
+
publicKey: acc.publicKey,
|
|
597
|
+
},
|
|
598
|
+
})),
|
|
599
|
+
permissions: this.client?.permissions || [],
|
|
600
|
+
connectedAt: Date.now(),
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Handle disconnection
|
|
606
|
+
* @param reason
|
|
607
|
+
*/
|
|
608
|
+
private handleDisconnection(reason: string): void {
|
|
609
|
+
logger.info('Handling disconnection', { reason });
|
|
610
|
+
this.cleanup();
|
|
611
|
+
this.runInZone(() => {
|
|
612
|
+
this._currentSession.set(null);
|
|
613
|
+
this._connectionState.set('disconnected');
|
|
614
|
+
});
|
|
615
|
+
this.notifyDisconnected();
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Require active session or throw
|
|
620
|
+
*/
|
|
621
|
+
private requireSession(): P2PSession {
|
|
622
|
+
const session = this._currentSession();
|
|
623
|
+
if (!session) {
|
|
624
|
+
throw new Error('No active session');
|
|
625
|
+
}
|
|
626
|
+
return session;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Clean up resources.
|
|
631
|
+
*
|
|
632
|
+
* @param rejectPending - If true, reject pending promises with error.
|
|
633
|
+
* If false, clear them silently (for new session creation).
|
|
634
|
+
*/
|
|
635
|
+
private async cleanup(rejectPending = true): Promise<void> {
|
|
636
|
+
// Reject pending approval if requested
|
|
637
|
+
if (this.sessionApprovalPromise && rejectPending) {
|
|
638
|
+
this.sessionApprovalPromise.reject(new Error('Connection closed'));
|
|
639
|
+
}
|
|
640
|
+
this.sessionApprovalPromise = undefined;
|
|
641
|
+
|
|
642
|
+
if (this.client) {
|
|
643
|
+
try {
|
|
644
|
+
await this.client.disconnect();
|
|
645
|
+
} catch {
|
|
646
|
+
// Ignore disconnect errors during cleanup
|
|
647
|
+
}
|
|
648
|
+
this.client = undefined;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
this.currentInvite = undefined;
|
|
652
|
+
this.runInZone(() => this._p2pState.set('nostr-only'));
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Notify connected callbacks
|
|
657
|
+
* @param session
|
|
658
|
+
*/
|
|
659
|
+
private notifyConnected(session: P2PSession): void {
|
|
660
|
+
for (const callback of this.connectedCallbacks) {
|
|
661
|
+
try {
|
|
662
|
+
callback(session);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
logger.error('Connected callback error', { error });
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Notify disconnected callbacks
|
|
671
|
+
*/
|
|
672
|
+
private notifyDisconnected(): void {
|
|
673
|
+
for (const callback of this.disconnectedCallbacks) {
|
|
674
|
+
try {
|
|
675
|
+
callback();
|
|
676
|
+
} catch (error) {
|
|
677
|
+
logger.error('Disconnected callback error', { error });
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Notify error callbacks
|
|
684
|
+
* @param error
|
|
685
|
+
*/
|
|
686
|
+
private notifyError(error: Error): void {
|
|
687
|
+
for (const callback of this.errorCallbacks) {
|
|
688
|
+
try {
|
|
689
|
+
callback(error);
|
|
690
|
+
} catch (err) {
|
|
691
|
+
logger.error('Error callback error', { err });
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|