@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,723 @@
|
|
|
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-native.provider.ts
|
|
13
|
+
* @description P2P Native Provider for wallet connections (Nostr-first with P2P upgrade)
|
|
14
|
+
*
|
|
15
|
+
* Implements BaseWalletProvider for P2P connections using the Nostr-first protocol.
|
|
16
|
+
* Enables single-QR scanning for cross-device wallet connections (both mobile and desktop).
|
|
17
|
+
*
|
|
18
|
+
* **Key Features:**
|
|
19
|
+
* - Single QR code scanning (no answer QR needed!)
|
|
20
|
+
* - Nostr-first communication with automatic P2P upgrade
|
|
21
|
+
* - End-to-end encrypted (AES-GCM over Nostr, DTLS over WebRTC)
|
|
22
|
+
* - No intermediary servers required
|
|
23
|
+
* - Works on any network (WiFi, cellular, etc.)
|
|
24
|
+
*
|
|
25
|
+
* **Connection Flow:**
|
|
26
|
+
* 1. dApp generates compact channel invite (hsc:connect?i=...)
|
|
27
|
+
* 2. User scans QR with wallet (or clicks hsc: link on desktop)
|
|
28
|
+
* 3. Wallet connects via Nostr relays
|
|
29
|
+
* 4. User approves session in wallet
|
|
30
|
+
* 5. P2P upgrade happens automatically in background
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { Injectable, NgZone, signal, computed, type Signal } from '@angular/core';
|
|
34
|
+
import { getLogger } from '@hsuite/native-connect-sdk';
|
|
35
|
+
|
|
36
|
+
import type { ConnectionStatus, ProviderMetadata, ProviderType } from '../../models/provider-types';
|
|
37
|
+
import type { ConnectionConfig } from '../../models/provider-types';
|
|
38
|
+
import type { UnifiedAccount } from '../../models/unified-account.model';
|
|
39
|
+
import {
|
|
40
|
+
BaseWalletProvider,
|
|
41
|
+
type SignTransactionOptions,
|
|
42
|
+
type SubmitTransactionOptions,
|
|
43
|
+
type SignResult,
|
|
44
|
+
type SubmitResult,
|
|
45
|
+
type SignMessageOptions,
|
|
46
|
+
type SignMessageResult,
|
|
47
|
+
} from '../base-wallet-provider';
|
|
48
|
+
|
|
49
|
+
import { P2PSessionManager, type P2PSession } from './p2p-session-manager';
|
|
50
|
+
|
|
51
|
+
const logger = getLogger().scoped?.('P2PNativeProvider') ?? getLogger();
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Storage key for mobile session metadata (separate from hsuite-native)
|
|
55
|
+
*/
|
|
56
|
+
const P2P_SESSION_STORAGE_KEY = 'hsuite.p2p.session';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Mobile Session Persistence
|
|
60
|
+
*
|
|
61
|
+
* Stores mobile P2P session metadata to localStorage for dApp reconnection.
|
|
62
|
+
* Uses a separate key from hsuite-native to avoid conflicts.
|
|
63
|
+
*/
|
|
64
|
+
class P2PSessionPersistence {
|
|
65
|
+
/**
|
|
66
|
+
* @param session
|
|
67
|
+
* Persist mobile session to localStorage.
|
|
68
|
+
*/
|
|
69
|
+
save(session: P2PSession): void {
|
|
70
|
+
try {
|
|
71
|
+
if (typeof window === 'undefined') return;
|
|
72
|
+
const payload = {
|
|
73
|
+
...session,
|
|
74
|
+
timestamp: Date.now(),
|
|
75
|
+
};
|
|
76
|
+
window.localStorage.setItem(P2P_SESSION_STORAGE_KEY, JSON.stringify(payload));
|
|
77
|
+
logger.debug('Mobile session persisted', { sessionId: session.sessionId });
|
|
78
|
+
} catch (error) {
|
|
79
|
+
logger.warn('Failed to persist mobile session', { error });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Restore mobile session from localStorage.
|
|
85
|
+
*/
|
|
86
|
+
restore(): (P2PSession & { timestamp: number }) | null {
|
|
87
|
+
try {
|
|
88
|
+
if (typeof window === 'undefined') return null;
|
|
89
|
+
const raw = window.localStorage.getItem(P2P_SESSION_STORAGE_KEY);
|
|
90
|
+
if (!raw) return null;
|
|
91
|
+
const stored = JSON.parse(raw) as P2PSession & { timestamp: number };
|
|
92
|
+
if (!stored?.sessionId) {
|
|
93
|
+
this.clear();
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
logger.info('Restored mobile session metadata', { sessionId: stored.sessionId });
|
|
97
|
+
return stored;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
logger.warn('Failed to restore mobile session', { error });
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Clear persisted mobile session.
|
|
106
|
+
*/
|
|
107
|
+
clear(): void {
|
|
108
|
+
try {
|
|
109
|
+
if (typeof window === 'undefined') return;
|
|
110
|
+
window.localStorage.removeItem(P2P_SESSION_STORAGE_KEY);
|
|
111
|
+
logger.debug('Mobile session cleared');
|
|
112
|
+
} catch (error) {
|
|
113
|
+
logger.warn('Failed to clear mobile session', { error });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if a mobile session is stored.
|
|
119
|
+
*/
|
|
120
|
+
hasStoredSession(): boolean {
|
|
121
|
+
try {
|
|
122
|
+
if (typeof window === 'undefined') return false;
|
|
123
|
+
return !!window.localStorage.getItem(P2P_SESSION_STORAGE_KEY);
|
|
124
|
+
} catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Connection state for the mobile native provider.
|
|
132
|
+
*
|
|
133
|
+
* States:
|
|
134
|
+
* - disconnected: No connection
|
|
135
|
+
* - generating_offer: Creating WebRTC offer for QR display
|
|
136
|
+
* - awaiting_scan: QR displayed, waiting for wallet to scan
|
|
137
|
+
* - connecting: WebRTC handshake in progress
|
|
138
|
+
* - pending_approval: WebRTC connected, waiting for wallet user to approve
|
|
139
|
+
* - connected: Session approved and fully connected
|
|
140
|
+
* - error: An error occurred
|
|
141
|
+
*/
|
|
142
|
+
export type P2PNativeConnectionState =
|
|
143
|
+
| 'disconnected'
|
|
144
|
+
| 'generating_offer'
|
|
145
|
+
| 'awaiting_scan'
|
|
146
|
+
| 'connecting'
|
|
147
|
+
| 'pending_approval'
|
|
148
|
+
| 'connected'
|
|
149
|
+
| 'error';
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Pairing offer result containing QR data
|
|
153
|
+
*/
|
|
154
|
+
export interface PairingOfferResult {
|
|
155
|
+
/** Encoded channel invite for QR code display (hsc:connect?i=...) */
|
|
156
|
+
qrData: string;
|
|
157
|
+
/** Session ID for this connection */
|
|
158
|
+
sessionId?: string;
|
|
159
|
+
/** Expiry timestamp */
|
|
160
|
+
expiresAt: number;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* P2P Native Provider for cross-device wallet connections.
|
|
165
|
+
*
|
|
166
|
+
* @description
|
|
167
|
+
* Enables secure P2P wallet connections via WebRTC for mobile and cross-device
|
|
168
|
+
* scenarios. The dApp generates a QR code containing a channel invite, which
|
|
169
|
+
* the mobile wallet scans to establish a direct encrypted connection.
|
|
170
|
+
*
|
|
171
|
+
* Key Features:
|
|
172
|
+
* - **Single QR Code Flow**: No answer QR needed - just scan and connect
|
|
173
|
+
* - **Nostr-First**: Uses Nostr relays for signaling and fallback
|
|
174
|
+
* - **Automatic P2P Upgrade**: Upgrades to WebRTC for lower latency
|
|
175
|
+
* - **End-to-End Encrypted**: AES-GCM over Nostr, DTLS over WebRTC
|
|
176
|
+
* - **Session Persistence**: Supports reconnection after page reload
|
|
177
|
+
*
|
|
178
|
+
* Transport States (via `transportState` signal):
|
|
179
|
+
* - `'nostr-only'` - Connected via Nostr relay only
|
|
180
|
+
* - `'upgrading'` - P2P upgrade in progress
|
|
181
|
+
* - `'p2p-connected'` - Direct P2P connection established
|
|
182
|
+
* - `'p2p-failed'` - P2P upgrade failed, using Nostr fallback
|
|
183
|
+
*
|
|
184
|
+
* @publicApi
|
|
185
|
+
*/
|
|
186
|
+
@Injectable({ providedIn: 'root' })
|
|
187
|
+
export class P2PNativeProvider extends BaseWalletProvider {
|
|
188
|
+
readonly id = 'mobile-native';
|
|
189
|
+
|
|
190
|
+
readonly metadata: ProviderMetadata = {
|
|
191
|
+
name: 'HSuite Mobile',
|
|
192
|
+
type: 'mobile-native' as ProviderType,
|
|
193
|
+
icon: 'phone-portrait-outline',
|
|
194
|
+
description: 'Connect via QR code (P2P)',
|
|
195
|
+
features: [
|
|
196
|
+
'Direct P2P connection',
|
|
197
|
+
'End-to-end encrypted',
|
|
198
|
+
'No server required',
|
|
199
|
+
'Works on any network',
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// Internal state signals
|
|
204
|
+
private readonly _status = signal<ConnectionStatus>('disconnected');
|
|
205
|
+
private readonly _accounts = signal<UnifiedAccount[]>([]);
|
|
206
|
+
private readonly _error = signal<string | null>(null);
|
|
207
|
+
private readonly _connectionState = signal<P2PNativeConnectionState>('disconnected');
|
|
208
|
+
private readonly _pairingOffer = signal<PairingOfferResult | null>(null);
|
|
209
|
+
|
|
210
|
+
/** Session persistence for page reload recovery */
|
|
211
|
+
private readonly sessionPersistence = new P2PSessionPersistence();
|
|
212
|
+
|
|
213
|
+
// Public readonly signals
|
|
214
|
+
readonly status: Signal<ConnectionStatus> = this._status.asReadonly();
|
|
215
|
+
readonly accounts: Signal<UnifiedAccount[]> = this._accounts.asReadonly();
|
|
216
|
+
readonly error: Signal<string | null> = this._error.asReadonly();
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Current detailed connection state
|
|
220
|
+
*/
|
|
221
|
+
readonly connectionState: Signal<P2PNativeConnectionState> = this._connectionState.asReadonly();
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Current pairing offer (if awaiting scan)
|
|
225
|
+
*/
|
|
226
|
+
readonly pairingOffer: Signal<PairingOfferResult | null> = this._pairingOffer.asReadonly();
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Whether currently awaiting wallet to scan QR
|
|
230
|
+
*/
|
|
231
|
+
readonly isAwaitingScan = computed(() => this._connectionState() === 'awaiting_scan');
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Whether a pairing offer is available
|
|
235
|
+
*/
|
|
236
|
+
readonly hasPairingOffer = computed(() => this._pairingOffer() !== null);
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Whether a persisted session exists
|
|
240
|
+
*/
|
|
241
|
+
readonly hasPersistedSession = computed(() => this.sessionPersistence.hasStoredSession());
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Transport state for the P2P connection.
|
|
245
|
+
* Values: 'nostr-only' | 'upgrading' | 'p2p-connected' | 'p2p-failed'
|
|
246
|
+
*
|
|
247
|
+
* This allows the UI to show the current transport layer being used.
|
|
248
|
+
*/
|
|
249
|
+
readonly transportState = computed(() => this.sessionManager.p2pState());
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @param sessionManager
|
|
253
|
+
* @param ngZone
|
|
254
|
+
*/
|
|
255
|
+
constructor(
|
|
256
|
+
private readonly sessionManager: P2PSessionManager,
|
|
257
|
+
private readonly ngZone: NgZone,
|
|
258
|
+
) {
|
|
259
|
+
super();
|
|
260
|
+
// CRITICAL: Pass NgZone to session manager so signal updates from
|
|
261
|
+
// Nostr/WebSocket callbacks (which fire outside Angular zone) trigger
|
|
262
|
+
// Angular change detection. Without this, walletContext.activeAccount
|
|
263
|
+
// stays null after session approval.
|
|
264
|
+
this.sessionManager.setNgZone(this.ngZone);
|
|
265
|
+
this.setupSessionManagerListeners();
|
|
266
|
+
this.restorePersistedSession();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Restore session from localStorage on page load.
|
|
271
|
+
* Sets accounts and status from persisted data.
|
|
272
|
+
* Note: This only restores metadata - the actual WebRTC connection
|
|
273
|
+
* will need to be re-established via QR pairing.
|
|
274
|
+
*/
|
|
275
|
+
private restorePersistedSession(): void {
|
|
276
|
+
const stored = this.sessionPersistence.restore();
|
|
277
|
+
if (stored) {
|
|
278
|
+
logger.info('Restoring persisted mobile session', {
|
|
279
|
+
sessionId: stored.sessionId,
|
|
280
|
+
accountCount: stored.accounts?.length ?? 0,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Restore accounts with proper providerId
|
|
284
|
+
if (stored.accounts?.length) {
|
|
285
|
+
const accountsWithProvider = this.ensureProviderIdOnAccounts(stored.accounts);
|
|
286
|
+
this._accounts.set(accountsWithProvider);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Note: We don't set connected status because WebRTC needs re-pairing
|
|
290
|
+
// The persisted data is for account info only
|
|
291
|
+
logger.debug('Mobile session metadata restored (requires re-pairing for connection)');
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Creates a pairing offer for QR code display.
|
|
297
|
+
*
|
|
298
|
+
* @description
|
|
299
|
+
* Generates a channel invite encoded for QR display. The mobile wallet
|
|
300
|
+
* scans this QR code to establish a connection via Nostr relays with
|
|
301
|
+
* automatic P2P upgrade.
|
|
302
|
+
*
|
|
303
|
+
* The returned `qrData` is a compact `hsc:connect?i=...` URL that contains:
|
|
304
|
+
* - Channel ID and encryption keys
|
|
305
|
+
* - App metadata (name, ID)
|
|
306
|
+
* - Relay endpoints for communication
|
|
307
|
+
*
|
|
308
|
+
* @param config - Connection configuration specifying ledger and network
|
|
309
|
+
*
|
|
310
|
+
* @returns Promise resolving to pairing offer containing QR data and expiry
|
|
311
|
+
*
|
|
312
|
+
* @throws {Error} If offer generation fails
|
|
313
|
+
*/
|
|
314
|
+
async createPairingOffer(config: ConnectionConfig): Promise<PairingOfferResult> {
|
|
315
|
+
logger.info('Creating pairing offer', { config });
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
this._connectionState.set('generating_offer');
|
|
319
|
+
this._error.set(null);
|
|
320
|
+
|
|
321
|
+
// Extract common properties based on config type
|
|
322
|
+
const appId = 'appId' in config ? (config.appId ?? 'hsuite-dapp') : 'hsuite-dapp';
|
|
323
|
+
const appName = 'appName' in config ? (config.appName ?? 'HSuite dApp') : 'HSuite dApp';
|
|
324
|
+
const ledgerId = 'ledgerId' in config ? (config.ledgerId ?? 'hedera') : 'hedera';
|
|
325
|
+
const networkId = config.networkId ?? 'testnet';
|
|
326
|
+
|
|
327
|
+
const result = await this.sessionManager.createPairingOffer({
|
|
328
|
+
appId,
|
|
329
|
+
appName,
|
|
330
|
+
ledgerId,
|
|
331
|
+
networkId,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const offerResult: PairingOfferResult = {
|
|
335
|
+
qrData: result.qrData,
|
|
336
|
+
sessionId: result.sessionId,
|
|
337
|
+
expiresAt: Date.now() + 120 * 1000, // 2 minutes
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
this._pairingOffer.set(offerResult);
|
|
341
|
+
this._connectionState.set('awaiting_scan');
|
|
342
|
+
this._status.set('connecting');
|
|
343
|
+
|
|
344
|
+
logger.info('Pairing offer created', {
|
|
345
|
+
qrDataLength: result.qrData.length,
|
|
346
|
+
expiresAt: offerResult.expiresAt,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
return offerResult;
|
|
350
|
+
} catch (error) {
|
|
351
|
+
logger.error('Failed to create pairing offer', { error });
|
|
352
|
+
this._connectionState.set('error');
|
|
353
|
+
this._status.set('error');
|
|
354
|
+
this._error.set(error instanceof Error ? error.message : 'Failed to create pairing offer');
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Refresh the pairing offer (e.g., when expired).
|
|
361
|
+
*
|
|
362
|
+
* @param config - Connection configuration
|
|
363
|
+
* @returns Promise resolving to new pairing offer
|
|
364
|
+
*/
|
|
365
|
+
async refreshPairingOffer(config: ConnectionConfig): Promise<PairingOfferResult> {
|
|
366
|
+
logger.debug('Refreshing pairing offer');
|
|
367
|
+
|
|
368
|
+
// Cancel any existing offer
|
|
369
|
+
await this.sessionManager.cancelPairingOffer();
|
|
370
|
+
|
|
371
|
+
return this.createPairingOffer(config);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Register callback for when peer connects.
|
|
376
|
+
*
|
|
377
|
+
* @param callback - Function to call when connection is established
|
|
378
|
+
*/
|
|
379
|
+
onPeerConnected(callback: () => void): void {
|
|
380
|
+
this.sessionManager.onConnected(() => {
|
|
381
|
+
callback();
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Waits for the wallet to connect and approve the session.
|
|
387
|
+
*
|
|
388
|
+
* @description
|
|
389
|
+
* Call this after {@link createPairingOffer} to block until the wallet user
|
|
390
|
+
* completes the connection flow. This is the single-QR flow - no answer
|
|
391
|
+
* scanning is required.
|
|
392
|
+
*
|
|
393
|
+
* The method resolves when all of the following occur:
|
|
394
|
+
* 1. Wallet scans the QR code
|
|
395
|
+
* 2. Wallet connects via Nostr relay
|
|
396
|
+
* 3. User approves the session in the wallet
|
|
397
|
+
*
|
|
398
|
+
* Upon success, the session is automatically persisted for reconnection.
|
|
399
|
+
*
|
|
400
|
+
* @returns Promise resolving to the approved session with account information
|
|
401
|
+
*
|
|
402
|
+
* @throws {Error} If connection fails or is rejected
|
|
403
|
+
* @throws {Error} If no pairing offer was created
|
|
404
|
+
*/
|
|
405
|
+
async waitForSessionApproval(): Promise<P2PSession> {
|
|
406
|
+
logger.info('Waiting for wallet to connect and approve session');
|
|
407
|
+
this._connectionState.set('pending_approval');
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
// This blocks until wallet connects via Nostr and user approves
|
|
411
|
+
const session = await this.sessionManager.waitForSessionApproval();
|
|
412
|
+
|
|
413
|
+
// Connection state will be updated by sessionManager callbacks, but also set here for immediate update
|
|
414
|
+
this._connectionState.set('connected');
|
|
415
|
+
this._status.set('connected');
|
|
416
|
+
|
|
417
|
+
// Ensure accounts have providerId set correctly
|
|
418
|
+
if (session?.accounts) {
|
|
419
|
+
const accountsWithProvider = this.ensureProviderIdOnAccounts(session.accounts);
|
|
420
|
+
this._accounts.set(accountsWithProvider);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Persist session for page reload recovery
|
|
424
|
+
this.sessionPersistence.save(session);
|
|
425
|
+
|
|
426
|
+
return session;
|
|
427
|
+
} catch (error) {
|
|
428
|
+
logger.error('Session approval failed', { error });
|
|
429
|
+
this._connectionState.set('error');
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ========== BaseWalletProvider Implementation ==========
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Connects to a wallet via the P2P pairing flow.
|
|
438
|
+
*
|
|
439
|
+
* @description
|
|
440
|
+
* Initiates the pairing offer flow and waits for connection. For more
|
|
441
|
+
* control over QR display timing, use {@link createPairingOffer} and
|
|
442
|
+
* {@link waitForSessionApproval} separately.
|
|
443
|
+
*
|
|
444
|
+
* This method:
|
|
445
|
+
* 1. Creates a pairing offer
|
|
446
|
+
* 2. Updates `pairingOffer` signal with QR data
|
|
447
|
+
* 3. Waits up to 2 minutes for wallet to connect
|
|
448
|
+
*
|
|
449
|
+
* @param config - Connection configuration
|
|
450
|
+
*
|
|
451
|
+
* @throws {Error} If already connected (no-op)
|
|
452
|
+
* @throws {Error} If connection times out (2 minutes)
|
|
453
|
+
*/
|
|
454
|
+
async connect(config: ConnectionConfig): Promise<void> {
|
|
455
|
+
logger.info('Connect called', { config });
|
|
456
|
+
|
|
457
|
+
// If already connected, no-op
|
|
458
|
+
if (this._status() === 'connected') {
|
|
459
|
+
logger.debug('Already connected');
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Create pairing offer and wait for connection
|
|
464
|
+
await this.createPairingOffer(config);
|
|
465
|
+
|
|
466
|
+
// Wait for connection to complete
|
|
467
|
+
return new Promise((resolve, reject) => {
|
|
468
|
+
const timeout = setTimeout(() => {
|
|
469
|
+
reject(new Error('Connection timeout'));
|
|
470
|
+
}, 120000); // 2 minute timeout
|
|
471
|
+
|
|
472
|
+
this.sessionManager.onConnected(() => {
|
|
473
|
+
clearTimeout(timeout);
|
|
474
|
+
resolve();
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
this.sessionManager.onError((error: Error) => {
|
|
478
|
+
clearTimeout(timeout);
|
|
479
|
+
reject(error);
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Disconnects from the wallet and clears session.
|
|
486
|
+
*
|
|
487
|
+
* @description
|
|
488
|
+
* Terminates the P2P session, clears all state signals, and removes
|
|
489
|
+
* persisted session data. The wallet is notified of the disconnection.
|
|
490
|
+
*/
|
|
491
|
+
async disconnect(): Promise<void> {
|
|
492
|
+
logger.info('Disconnecting');
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
await this.sessionManager.terminateSession();
|
|
496
|
+
} finally {
|
|
497
|
+
this._status.set('disconnected');
|
|
498
|
+
this._connectionState.set('disconnected');
|
|
499
|
+
this._accounts.set([]);
|
|
500
|
+
this._pairingOffer.set(null);
|
|
501
|
+
this._error.set(null);
|
|
502
|
+
|
|
503
|
+
// Clear persisted session
|
|
504
|
+
this.sessionPersistence.clear();
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Signs a transaction without submitting it.
|
|
510
|
+
*
|
|
511
|
+
* @description
|
|
512
|
+
* Sends the transaction to the connected wallet for signing. The wallet
|
|
513
|
+
* displays transaction details and prompts the user for approval.
|
|
514
|
+
*
|
|
515
|
+
* @param options - Transaction signing options
|
|
516
|
+
*
|
|
517
|
+
* @returns Promise resolving to signed payload and signature
|
|
518
|
+
*
|
|
519
|
+
* @throws {Error} If not connected to wallet
|
|
520
|
+
* @throws {Error} If user rejects signing
|
|
521
|
+
*/
|
|
522
|
+
async signTransaction(options: SignTransactionOptions): Promise<SignResult> {
|
|
523
|
+
if (this._status() !== 'connected') {
|
|
524
|
+
throw new Error('Not connected to wallet');
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
logger.info('Signing transaction', { account: options.accountAddress });
|
|
528
|
+
|
|
529
|
+
const result = await this.sessionManager.signTransaction({
|
|
530
|
+
accountAddress: options.accountAddress,
|
|
531
|
+
payload: options.payload,
|
|
532
|
+
ledgerId: options.ledgerId,
|
|
533
|
+
networkId: options.networkId,
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
return {
|
|
537
|
+
signedPayload: result.signedPayload,
|
|
538
|
+
signature: result.signature,
|
|
539
|
+
metadata: result.metadata,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Signs and submits a transaction to the blockchain.
|
|
545
|
+
*
|
|
546
|
+
* @description
|
|
547
|
+
* Performs atomic sign and submit in a single operation. Supports batch
|
|
548
|
+
* transactions when isBatch is true.
|
|
549
|
+
*
|
|
550
|
+
* @param options - Transaction submission options
|
|
551
|
+
*
|
|
552
|
+
* @returns Promise resolving to transaction ID and hash
|
|
553
|
+
*
|
|
554
|
+
* @throws {Error} If not connected to wallet
|
|
555
|
+
* @throws {Error} If user rejects or transaction fails
|
|
556
|
+
*/
|
|
557
|
+
async submitTransaction(options: SubmitTransactionOptions): Promise<SubmitResult> {
|
|
558
|
+
if (this._status() !== 'connected') {
|
|
559
|
+
throw new Error('Not connected to wallet');
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
logger.info('Submitting transaction', { account: options.accountAddress });
|
|
563
|
+
|
|
564
|
+
const result = await this.sessionManager.submitTransaction({
|
|
565
|
+
accountAddress: options.accountAddress,
|
|
566
|
+
payload: options.payload,
|
|
567
|
+
ledgerId: options.ledgerId,
|
|
568
|
+
networkId: options.networkId,
|
|
569
|
+
isBatch: options.isBatch,
|
|
570
|
+
batchKey: options.batchKey,
|
|
571
|
+
innerTransactions: options.innerTransactions,
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
return {
|
|
575
|
+
transactionId: result.transactionId,
|
|
576
|
+
transactionHash: result.transactionHash,
|
|
577
|
+
metadata: result.metadata,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Signs and executes a transaction atomically in a single prompt.
|
|
583
|
+
*
|
|
584
|
+
* @description
|
|
585
|
+
* Delegates to {@link submitTransaction}. This method exists so that
|
|
586
|
+
* {@link UnifiedWalletService.signAndExecuteTransaction} can detect native
|
|
587
|
+
* atomic sign+submit support on the provider (via duck-typing) and avoid
|
|
588
|
+
* its fallback path that chains separate signTransaction + submitTransaction
|
|
589
|
+
* calls. Mirrors the pattern used by HsuiteNativeProvider and the
|
|
590
|
+
* WalletConnect signers.
|
|
591
|
+
*
|
|
592
|
+
* @param options - Transaction submission options
|
|
593
|
+
*
|
|
594
|
+
* @returns Promise resolving to submission result with transaction ID
|
|
595
|
+
*/
|
|
596
|
+
async signAndExecuteTransaction(options: SubmitTransactionOptions): Promise<SubmitResult> {
|
|
597
|
+
return this.submitTransaction(options);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Signs an arbitrary message for authentication or verification.
|
|
602
|
+
*
|
|
603
|
+
* @description
|
|
604
|
+
* Requests the wallet to sign a non-transaction message. Used for
|
|
605
|
+
* authentication challenges, DAO voting, or off-chain signatures.
|
|
606
|
+
*
|
|
607
|
+
* @param options - Message signing options
|
|
608
|
+
*
|
|
609
|
+
* @returns Promise resolving to signature and public key
|
|
610
|
+
*
|
|
611
|
+
* @throws {Error} If not connected to wallet
|
|
612
|
+
* @throws {Error} If user rejects signing
|
|
613
|
+
*/
|
|
614
|
+
async signMessage(options: SignMessageOptions): Promise<SignMessageResult> {
|
|
615
|
+
if (this._status() !== 'connected') {
|
|
616
|
+
throw new Error('Not connected to wallet');
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
logger.info('Signing message', { account: options.accountAddress });
|
|
620
|
+
|
|
621
|
+
const result = await this.sessionManager.signMessage({
|
|
622
|
+
accountAddress: options.accountAddress,
|
|
623
|
+
message: options.message,
|
|
624
|
+
encoding: options.encoding,
|
|
625
|
+
ledgerId: options.ledgerId,
|
|
626
|
+
networkId: options.networkId,
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
return {
|
|
630
|
+
signature: result.signature,
|
|
631
|
+
publicKey: result.publicKey,
|
|
632
|
+
metadata: result.metadata,
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Checks if P2P provider is available in the current environment.
|
|
638
|
+
*
|
|
639
|
+
* @description
|
|
640
|
+
* P2P Native requires WebRTC support (RTCPeerConnection). This is available
|
|
641
|
+
* in all modern browsers and most mobile WebViews.
|
|
642
|
+
*
|
|
643
|
+
* @returns Promise resolving to true if WebRTC is available
|
|
644
|
+
*/
|
|
645
|
+
async isAvailable(): Promise<boolean> {
|
|
646
|
+
// Check for WebRTC support
|
|
647
|
+
return typeof RTCPeerConnection !== 'undefined';
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// ========== Private Methods ==========
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Set up listeners for session manager events
|
|
654
|
+
*/
|
|
655
|
+
private setupSessionManagerListeners(): void {
|
|
656
|
+
// Connection established — wrap in NgZone to ensure Angular change
|
|
657
|
+
// detection picks up signal updates from Nostr/WebSocket callbacks
|
|
658
|
+
this.sessionManager.onConnected((session) => {
|
|
659
|
+
logger.info('Session connected', { sessionId: session?.id });
|
|
660
|
+
this.ngZone.run(() => {
|
|
661
|
+
this._connectionState.set('connected');
|
|
662
|
+
this._status.set('connected');
|
|
663
|
+
this._pairingOffer.set(null);
|
|
664
|
+
|
|
665
|
+
// Update accounts from session - ensure providerId is set
|
|
666
|
+
if (session?.accounts) {
|
|
667
|
+
const accountsWithProvider = this.ensureProviderIdOnAccounts(session.accounts);
|
|
668
|
+
this._accounts.set(accountsWithProvider);
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
// Persist session for page reload recovery
|
|
673
|
+
if (session) {
|
|
674
|
+
this.sessionPersistence.save(session);
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
// Session terminated
|
|
679
|
+
this.sessionManager.onDisconnected(() => {
|
|
680
|
+
logger.info('Session disconnected');
|
|
681
|
+
this.ngZone.run(() => {
|
|
682
|
+
this._connectionState.set('disconnected');
|
|
683
|
+
this._status.set('disconnected');
|
|
684
|
+
this._accounts.set([]);
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
// Clear persisted session
|
|
688
|
+
this.sessionPersistence.clear();
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// Error occurred
|
|
692
|
+
this.sessionManager.onError((error: Error) => {
|
|
693
|
+
logger.error('Session error', { error });
|
|
694
|
+
this.ngZone.run(() => {
|
|
695
|
+
this._connectionState.set('error');
|
|
696
|
+
this._status.set('error');
|
|
697
|
+
this._error.set(error.message);
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
// Accounts updated
|
|
702
|
+
this.sessionManager.onAccountsChanged((accounts) => {
|
|
703
|
+
logger.debug('Accounts updated', { count: accounts.length });
|
|
704
|
+
this.ngZone.run(() => {
|
|
705
|
+
const accountsWithProvider = this.ensureProviderIdOnAccounts(accounts);
|
|
706
|
+
this._accounts.set(accountsWithProvider);
|
|
707
|
+
});
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Ensure all accounts have providerId and providerType set correctly
|
|
713
|
+
* @param accounts
|
|
714
|
+
*/
|
|
715
|
+
private ensureProviderIdOnAccounts(accounts: UnifiedAccount[]): UnifiedAccount[] {
|
|
716
|
+
return accounts.map((account) => ({
|
|
717
|
+
...account,
|
|
718
|
+
id: account.id || `${this.id}-${account.address}`,
|
|
719
|
+
providerId: this.id,
|
|
720
|
+
providerType: this.metadata.type,
|
|
721
|
+
}));
|
|
722
|
+
}
|
|
723
|
+
}
|