@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,610 @@
|
|
|
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 hedera-signer.ts
|
|
13
|
+
* @description Hedera-specific WalletConnect signer implementation
|
|
14
|
+
*
|
|
15
|
+
* Implements the IWalletConnectSigner interface for Hedera ledger.
|
|
16
|
+
* Handles Hedera-specific JSON-RPC methods and transaction formats.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { getLogger } from '@hsuite/native-connect-sdk';
|
|
20
|
+
|
|
21
|
+
import type { SignResult, SubmitResult } from '../../base-wallet-provider';
|
|
22
|
+
import type { IWalletConnectSigner, SignerOperationParams } from '../core/base-signer.interface';
|
|
23
|
+
|
|
24
|
+
const logger = getLogger().scoped?.('HederaSigner') ?? getLogger();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Extract a meaningful error message from various error shapes.
|
|
28
|
+
* WalletConnect SDK throws errors that are not standard Error instances.
|
|
29
|
+
*
|
|
30
|
+
* Handles:
|
|
31
|
+
* - Standard Error objects
|
|
32
|
+
* - WalletConnect SDK errors: { code, message }
|
|
33
|
+
* - Nested errors: { error: { message } }
|
|
34
|
+
* - PIN_REQUIRED errors from wallet
|
|
35
|
+
* - DOMException objects
|
|
36
|
+
* - Objects that stringify to {} (enumerable property check)
|
|
37
|
+
*
|
|
38
|
+
* @param error - Error from WalletConnect SDK (can be Error, object, or string)
|
|
39
|
+
* @returns Human-readable error message
|
|
40
|
+
*/
|
|
41
|
+
function extractErrorMessage(error: unknown): string {
|
|
42
|
+
// Log the raw error for debugging
|
|
43
|
+
logger.debug('Extracting error message', {
|
|
44
|
+
errorType: typeof error,
|
|
45
|
+
isError: error instanceof Error,
|
|
46
|
+
constructorName: error?.constructor?.name,
|
|
47
|
+
protoName: Object.getPrototypeOf(error)?.constructor?.name,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (error instanceof Error) {
|
|
51
|
+
return error.message;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (typeof error === 'object' && error !== null) {
|
|
55
|
+
const errorObj = error as Record<string, unknown>;
|
|
56
|
+
|
|
57
|
+
// Try to get all properties including non-enumerable and from prototype
|
|
58
|
+
const allProps = Object.getOwnPropertyNames(error).concat(
|
|
59
|
+
Object.getOwnPropertyNames(Object.getPrototypeOf(error) || {}),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Log ALL property values for debugging
|
|
63
|
+
const propDebug: Record<string, string> = {};
|
|
64
|
+
for (const prop of allProps.slice(0, 15)) {
|
|
65
|
+
try {
|
|
66
|
+
const val = (error as any)[prop];
|
|
67
|
+
if (typeof val === 'function') {
|
|
68
|
+
propDebug[prop] = '[fn]';
|
|
69
|
+
} else if (val === undefined) {
|
|
70
|
+
propDebug[prop] = 'undef';
|
|
71
|
+
} else if (val === null) {
|
|
72
|
+
propDebug[prop] = 'null';
|
|
73
|
+
} else if (typeof val === 'object') {
|
|
74
|
+
propDebug[prop] = JSON.stringify(val)?.substring(0, 60) || '[obj]';
|
|
75
|
+
} else {
|
|
76
|
+
propDebug[prop] = String(val).substring(0, 60);
|
|
77
|
+
}
|
|
78
|
+
} catch (_e) {
|
|
79
|
+
propDebug[prop] = '[err]';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
logger.error('WC Error object dump', propDebug);
|
|
83
|
+
|
|
84
|
+
// Check for code property first (common in SDK errors like PIN_REQUIRED)
|
|
85
|
+
if (typeof errorObj['code'] === 'string') {
|
|
86
|
+
const codeMsg = errorObj['message']
|
|
87
|
+
? `${errorObj['code']}: ${errorObj['message']}`
|
|
88
|
+
: errorObj['code'];
|
|
89
|
+
return codeMsg as string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// WalletConnect error format: { code, message }
|
|
93
|
+
if ('message' in errorObj && typeof errorObj['message'] === 'string') {
|
|
94
|
+
return errorObj['message'];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Nested error: { error: { message } }
|
|
98
|
+
if (
|
|
99
|
+
'error' in errorObj &&
|
|
100
|
+
typeof errorObj['error'] === 'object' &&
|
|
101
|
+
errorObj['error'] !== null
|
|
102
|
+
) {
|
|
103
|
+
const nested = errorObj['error'] as Record<string, unknown>;
|
|
104
|
+
if ('message' in nested && typeof nested['message'] === 'string') {
|
|
105
|
+
return nested['message'];
|
|
106
|
+
}
|
|
107
|
+
if ('code' in nested && typeof nested['code'] === 'string') {
|
|
108
|
+
return nested['code'];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Check for DOMException
|
|
113
|
+
if (error instanceof DOMException) {
|
|
114
|
+
return error.message || error.name;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check for reason/cause properties (common in rejected promises)
|
|
118
|
+
if ('reason' in errorObj && typeof errorObj['reason'] === 'string') {
|
|
119
|
+
return errorObj['reason'];
|
|
120
|
+
}
|
|
121
|
+
if ('cause' in errorObj) {
|
|
122
|
+
const cause = errorObj['cause'];
|
|
123
|
+
if (typeof cause === 'string') return cause;
|
|
124
|
+
if (cause instanceof Error) return cause.message;
|
|
125
|
+
if (typeof cause === 'object' && cause !== null && 'message' in (cause as any)) {
|
|
126
|
+
return String((cause as any).message);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// WalletConnect SDK specific error formats
|
|
131
|
+
if ('context' in errorObj && typeof errorObj['context'] === 'string') {
|
|
132
|
+
return errorObj['context'];
|
|
133
|
+
}
|
|
134
|
+
if ('data' in errorObj && typeof errorObj['data'] === 'object' && errorObj['data'] !== null) {
|
|
135
|
+
const data = errorObj['data'] as Record<string, unknown>;
|
|
136
|
+
if ('message' in data && typeof data['message'] === 'string') {
|
|
137
|
+
return data['message'];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// WalletConnect JSON-RPC error format: { code: number, message: string }
|
|
142
|
+
if ('code' in errorObj && 'message' in errorObj) {
|
|
143
|
+
const code = errorObj['code'];
|
|
144
|
+
const msg = errorObj['message'];
|
|
145
|
+
if (typeof msg === 'string') {
|
|
146
|
+
return typeof code === 'number' ? `[${code}] ${msg}` : msg;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Error with 'name' property (common for built-in errors)
|
|
151
|
+
if ('name' in errorObj && typeof errorObj['name'] === 'string') {
|
|
152
|
+
const name = errorObj['name'];
|
|
153
|
+
if (name && name !== 'Error' && name !== 'Object') {
|
|
154
|
+
return name;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Try to stringify, but check if result is empty object
|
|
159
|
+
try {
|
|
160
|
+
const jsonStr = JSON.stringify(error);
|
|
161
|
+
// Don't return empty objects as error messages
|
|
162
|
+
if (jsonStr && jsonStr !== '{}' && jsonStr !== '[]') {
|
|
163
|
+
return jsonStr;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// If stringify returned {}, try Object.keys to get property names
|
|
167
|
+
const keys = Object.keys(errorObj);
|
|
168
|
+
if (keys.length > 0) {
|
|
169
|
+
return `Error object with keys: ${keys.join(', ')}`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Try getting own property names (includes non-enumerable)
|
|
173
|
+
const ownProps = Object.getOwnPropertyNames(errorObj);
|
|
174
|
+
logger.debug('Error object properties', { props: ownProps });
|
|
175
|
+
|
|
176
|
+
if (ownProps.length > 0) {
|
|
177
|
+
// Try to read ALL properties for better debugging
|
|
178
|
+
const propValues: string[] = [];
|
|
179
|
+
for (const k of ownProps) {
|
|
180
|
+
try {
|
|
181
|
+
const val = errorObj[k];
|
|
182
|
+
const valStr = typeof val === 'object' ? JSON.stringify(val) : String(val);
|
|
183
|
+
propValues.push(`${k}=${valStr?.substring(0, 100)}`);
|
|
184
|
+
|
|
185
|
+
// Check if any property contains error info
|
|
186
|
+
if (typeof val === 'object' && val !== null) {
|
|
187
|
+
if ('message' in val) return String((val as any).message);
|
|
188
|
+
if ('error' in val) return String((val as any).error);
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
propValues.push(k);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
logger.debug('Error property values', { values: propValues });
|
|
195
|
+
|
|
196
|
+
// Return a more informative error
|
|
197
|
+
return `Error with properties: ${propValues.slice(0, 5).join(', ')}`;
|
|
198
|
+
}
|
|
199
|
+
} catch (_e) {
|
|
200
|
+
// If stringify fails, try toString
|
|
201
|
+
const str = String(error);
|
|
202
|
+
if (str !== '[object Object]') {
|
|
203
|
+
return str;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Last resort - log what we know about the object
|
|
208
|
+
logger.warn('Could not extract error message', {
|
|
209
|
+
constructor: error?.constructor?.name,
|
|
210
|
+
prototype: Object.getPrototypeOf(error)?.constructor?.name,
|
|
211
|
+
});
|
|
212
|
+
return `Unknown error (type: ${error?.constructor?.name || 'object'})`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (typeof error === 'string') {
|
|
216
|
+
return error;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return 'Unknown error';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Signer implementation for Hedera ledger.
|
|
224
|
+
*
|
|
225
|
+
* Supports Hedera-specific WalletConnect methods:
|
|
226
|
+
* - hedera_signTransaction (sign-only, returns signature map)
|
|
227
|
+
* - hedera_signAndExecuteTransaction (sign + submit, returns transaction ID)
|
|
228
|
+
* - hedera_executeTransaction
|
|
229
|
+
* - hedera_signMessage (arbitrary message signing)
|
|
230
|
+
*
|
|
231
|
+
* Uses HIP-30 account format: hedera:<network>:<accountId>
|
|
232
|
+
*/
|
|
233
|
+
export class HederaSigner implements IWalletConnectSigner {
|
|
234
|
+
readonly ledgerId = 'hedera';
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Build Hedera namespace configuration for WalletConnect.
|
|
238
|
+
* Defines the JSON-RPC methods and chains this signer supports.
|
|
239
|
+
*
|
|
240
|
+
* IMPORTANT: We request ALL Hedera networks (testnet, mainnet)
|
|
241
|
+
* to ensure wallets like Kabila can show accounts from unknown network.
|
|
242
|
+
* The wallet will return all accounts it has across all networks.
|
|
243
|
+
*
|
|
244
|
+
* @param networkId - Network identifier (e.g., 'hedera:testnet', 'hedera:mainnet')
|
|
245
|
+
* @param _networkId
|
|
246
|
+
* @returns Namespace configuration object
|
|
247
|
+
*/
|
|
248
|
+
buildNamespace(_networkId: string): {
|
|
249
|
+
methods: string[];
|
|
250
|
+
chains: string[];
|
|
251
|
+
events: string[];
|
|
252
|
+
} {
|
|
253
|
+
// Request ALL Hedera networks to allow wallets to show all their accounts
|
|
254
|
+
// Wallets like Kabila may have accounts on multiple networks
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
methods: [
|
|
258
|
+
'hedera_signTransaction',
|
|
259
|
+
'hedera_signAndExecuteTransaction',
|
|
260
|
+
'hedera_executeTransaction',
|
|
261
|
+
'hedera_signMessage',
|
|
262
|
+
],
|
|
263
|
+
// Request all Hedera networks so wallet can show all accounts
|
|
264
|
+
chains: ['hedera:testnet', 'hedera:mainnet'],
|
|
265
|
+
events: ['chainChanged', 'accountsChanged'],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Sign a Hedera transaction without submitting to network.
|
|
271
|
+
* Uses hedera_signTransaction method with TransactionList format.
|
|
272
|
+
*
|
|
273
|
+
* The payload comes as TransactionList (base64 frozen transaction bytes).
|
|
274
|
+
* We send the full TransactionList to preserve canonical bytes for signing.
|
|
275
|
+
* The wallet will sign and return the signature map without executing.
|
|
276
|
+
*
|
|
277
|
+
* @param params - Signing parameters (payload is TransactionList base64)
|
|
278
|
+
* @returns Promise resolving to signed transaction with signature map
|
|
279
|
+
*/
|
|
280
|
+
async signTransaction(params: SignerOperationParams): Promise<SignResult> {
|
|
281
|
+
// Extract network name from networkId (mainnet or testnet)
|
|
282
|
+
const network = params.networkId.includes('mainnet') ? 'mainnet' : 'testnet';
|
|
283
|
+
|
|
284
|
+
// Format account ID in HIP-30 format
|
|
285
|
+
const signerAccountId = `hedera:${network}:${params.accountAddress}`;
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
// Send the full TransactionList (frozen transaction bytes) to preserve canonical bytes.
|
|
289
|
+
// The wallet needs the complete transaction structure to sign correctly.
|
|
290
|
+
// Previously we converted to TransactionBody which caused INVALID_SIGNATURE errors
|
|
291
|
+
// because the signature was computed over different bytes than the network expected.
|
|
292
|
+
|
|
293
|
+
// Use 5-minute expiry to give users time to review and approve
|
|
294
|
+
const result = await params.client.request({
|
|
295
|
+
topic: params.topic,
|
|
296
|
+
chainId: `hedera:${network}`,
|
|
297
|
+
request: {
|
|
298
|
+
method: 'hedera_signTransaction',
|
|
299
|
+
params: {
|
|
300
|
+
signerAccountId,
|
|
301
|
+
transactionList: params.payload, // Send full TransactionList for correct signing
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
expiry: 300, // 5 minutes in seconds
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
signedPayload: String(
|
|
309
|
+
(result as { signatureMap?: any }).signatureMap ||
|
|
310
|
+
(result as { signedTransaction?: any }).signedTransaction ||
|
|
311
|
+
JSON.stringify(result),
|
|
312
|
+
),
|
|
313
|
+
metadata: { result },
|
|
314
|
+
};
|
|
315
|
+
} catch (error) {
|
|
316
|
+
// Check if this is an empty error object (WalletConnect SDK bug)
|
|
317
|
+
const isEmptyError =
|
|
318
|
+
typeof error === 'object' &&
|
|
319
|
+
error !== null &&
|
|
320
|
+
Object.keys(error).length === 0 &&
|
|
321
|
+
!(error instanceof Error);
|
|
322
|
+
|
|
323
|
+
if (isEmptyError) {
|
|
324
|
+
logger.warn('WalletConnect SDK empty error - relay subscription may have been stale', {
|
|
325
|
+
topic: params.topic?.substring(0, 16) + '...',
|
|
326
|
+
});
|
|
327
|
+
throw new Error('WalletConnect connection issue - please try again');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const errorMsg = extractErrorMessage(error);
|
|
331
|
+
logger.error('Sign failed', { error: errorMsg });
|
|
332
|
+
throw new Error(`Hedera sign failed: ${errorMsg}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Sign and submit a Hedera transaction to the network.
|
|
338
|
+
* Uses hedera_signAndExecuteTransaction method with TransactionList format.
|
|
339
|
+
*
|
|
340
|
+
* @param params - Submission parameters
|
|
341
|
+
* @returns Promise resolving to transaction ID and metadata
|
|
342
|
+
*/
|
|
343
|
+
async submitTransaction(params: SignerOperationParams): Promise<SubmitResult> {
|
|
344
|
+
// Extract network name from networkId (mainnet or testnet)
|
|
345
|
+
const network = params.networkId.includes('mainnet') ? 'mainnet' : 'testnet';
|
|
346
|
+
|
|
347
|
+
// Format account ID in HIP-30 format
|
|
348
|
+
const signerAccountId = `hedera:${network}:${params.accountAddress}`;
|
|
349
|
+
|
|
350
|
+
logger.debug('Sending hedera_signAndExecuteTransaction request', {
|
|
351
|
+
topic: params.topic?.substring(0, 16) + '...',
|
|
352
|
+
chainId: `hedera:${network}`,
|
|
353
|
+
signerAccountId,
|
|
354
|
+
payloadLength: params.payload?.length,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Create the request promise - keep-alive pings are handled by WalletConnectClientManager
|
|
358
|
+
const requestPromise = params.client.request({
|
|
359
|
+
topic: params.topic,
|
|
360
|
+
chainId: `hedera:${network}`,
|
|
361
|
+
request: {
|
|
362
|
+
method: 'hedera_signAndExecuteTransaction',
|
|
363
|
+
params: {
|
|
364
|
+
signerAccountId,
|
|
365
|
+
transactionList: params.payload, // TransactionList protobuf (base64)
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
expiry: 300, // 5 minutes in seconds
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Create a 5-minute timeout to override any internal SDK timeouts
|
|
372
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
373
|
+
setTimeout(
|
|
374
|
+
() => {
|
|
375
|
+
reject(new Error('Transaction approval timed out after 5 minutes'));
|
|
376
|
+
},
|
|
377
|
+
5 * 60 * 1000,
|
|
378
|
+
); // 5 minutes
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
// Race between the request and our timeout
|
|
383
|
+
const result = await Promise.race([requestPromise, timeoutPromise]);
|
|
384
|
+
|
|
385
|
+
logger.debug('Received hedera_signAndExecuteTransaction response', {
|
|
386
|
+
hasResult: !!result,
|
|
387
|
+
resultKeys: result ? Object.keys(result as object) : [],
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
transactionId:
|
|
392
|
+
(result as { transactionId?: string }).transactionId ||
|
|
393
|
+
(result as { response?: { transactionId?: string } }).response?.transactionId ||
|
|
394
|
+
'',
|
|
395
|
+
transactionHash: (result as { transactionHash?: string }).transactionHash,
|
|
396
|
+
metadata: { result },
|
|
397
|
+
};
|
|
398
|
+
} catch (error) {
|
|
399
|
+
// Log full error for debugging - try to get more info from WalletConnect error
|
|
400
|
+
const wcError = error as any;
|
|
401
|
+
|
|
402
|
+
// Try to extract all available error info
|
|
403
|
+
const errorInfo = {
|
|
404
|
+
errorType: typeof error,
|
|
405
|
+
errorConstructor: error?.constructor?.name,
|
|
406
|
+
errorKeys: error && typeof error === 'object' ? Object.keys(error) : [],
|
|
407
|
+
errorString: String(error),
|
|
408
|
+
// WalletConnect specific error properties
|
|
409
|
+
wcCode: wcError?.code,
|
|
410
|
+
wcMessage: wcError?.message,
|
|
411
|
+
wcContext: wcError?.context,
|
|
412
|
+
wcData: wcError?.data,
|
|
413
|
+
// Try Symbol properties
|
|
414
|
+
symbols:
|
|
415
|
+
error && typeof error === 'object'
|
|
416
|
+
? Object.getOwnPropertySymbols(error).map((s) => s.toString())
|
|
417
|
+
: [],
|
|
418
|
+
// Get all own property names
|
|
419
|
+
ownProps: error && typeof error === 'object' ? Object.getOwnPropertyNames(error) : [],
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
logger.error('Submit failed - full error analysis', errorInfo);
|
|
423
|
+
|
|
424
|
+
// Check if this is an empty error object (WalletConnect SDK bug)
|
|
425
|
+
// This typically happens when relay subscription is stale
|
|
426
|
+
const isEmptyError =
|
|
427
|
+
typeof error === 'object' &&
|
|
428
|
+
error !== null &&
|
|
429
|
+
Object.keys(error).length === 0 &&
|
|
430
|
+
!(error instanceof Error);
|
|
431
|
+
|
|
432
|
+
if (isEmptyError) {
|
|
433
|
+
// Empty error object is a known WalletConnect SDK issue
|
|
434
|
+
// It usually means the relay subscription was stale
|
|
435
|
+
// The SessionHealthManager should have pinged before the request,
|
|
436
|
+
// but if we still get this error, the subscription dropped after the ping
|
|
437
|
+
logger.warn('WalletConnect SDK empty error - relay subscription may have been stale', {
|
|
438
|
+
topic: params.topic?.substring(0, 16) + '...',
|
|
439
|
+
hint: 'This can happen when the relay WebSocket connection drops',
|
|
440
|
+
});
|
|
441
|
+
// NOTE: We can't retry because the wallet may have already executed the transaction.
|
|
442
|
+
// A retry could cause a duplicate transaction on Hedera.
|
|
443
|
+
throw new Error(
|
|
444
|
+
'WalletConnect connection dropped - if you approved the transaction in your wallet, ' +
|
|
445
|
+
'please check your transaction history as it may have been executed successfully.',
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const errorMsg = extractErrorMessage(error);
|
|
450
|
+
logger.error('Submit failed', { error: errorMsg });
|
|
451
|
+
throw new Error(`Hedera submit failed: ${errorMsg}`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Sign and execute a transaction in one call (alias for submitTransaction).
|
|
457
|
+
* Uses hedera_signAndExecuteTransaction WalletConnect RPC method.
|
|
458
|
+
* This prevents double prompts by using the native WalletConnect method.
|
|
459
|
+
*
|
|
460
|
+
* @param params - Transaction parameters
|
|
461
|
+
* @returns Promise resolving to transaction ID and metadata
|
|
462
|
+
*/
|
|
463
|
+
async signAndExecuteTransaction(params: SignerOperationParams): Promise<SubmitResult> {
|
|
464
|
+
// Simply delegate to submitTransaction which already uses hedera_signAndExecuteTransaction
|
|
465
|
+
return this.submitTransaction(params);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Sign an arbitrary message using hedera_signMessage method.
|
|
470
|
+
* Used for DAO votes, authentication proofs, and other non-transaction signing.
|
|
471
|
+
*
|
|
472
|
+
* @param params - Signing parameters with message string
|
|
473
|
+
* @returns Promise resolving to signature and metadata
|
|
474
|
+
*/
|
|
475
|
+
async signMessage(
|
|
476
|
+
params: SignerOperationParams & { message: string; encoding?: 'utf-8' | 'base64' },
|
|
477
|
+
): Promise<import('../../base-wallet-provider').SignMessageResult> {
|
|
478
|
+
// Extract network name from networkId (mainnet or testnet)
|
|
479
|
+
const network = params.networkId.includes('mainnet') ? 'mainnet' : 'testnet';
|
|
480
|
+
|
|
481
|
+
// Format account ID in HIP-30 format
|
|
482
|
+
const signerAccountId = `hedera:${network}:${params.accountAddress}`;
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
// Use 5-minute expiry to give users time to review and approve
|
|
486
|
+
const result = await params.client.request({
|
|
487
|
+
topic: params.topic,
|
|
488
|
+
chainId: `hedera:${network}`,
|
|
489
|
+
request: {
|
|
490
|
+
method: 'hedera_signMessage',
|
|
491
|
+
params: {
|
|
492
|
+
signerAccountId,
|
|
493
|
+
message: params.message, // Can be base64 or utf-8
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
expiry: 300, // 5 minutes in seconds
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
return {
|
|
500
|
+
signature: String(
|
|
501
|
+
(result as { signatureMap?: any }).signatureMap ||
|
|
502
|
+
(result as { signature?: any }).signature ||
|
|
503
|
+
JSON.stringify(result),
|
|
504
|
+
),
|
|
505
|
+
publicKey: (result as { publicKey?: string }).publicKey,
|
|
506
|
+
metadata: { result },
|
|
507
|
+
};
|
|
508
|
+
} catch (error) {
|
|
509
|
+
// Check if this is an empty error object (WalletConnect SDK bug)
|
|
510
|
+
const isEmptyError =
|
|
511
|
+
typeof error === 'object' &&
|
|
512
|
+
error !== null &&
|
|
513
|
+
Object.keys(error).length === 0 &&
|
|
514
|
+
!(error instanceof Error);
|
|
515
|
+
|
|
516
|
+
if (isEmptyError) {
|
|
517
|
+
logger.warn('WalletConnect SDK empty error - relay subscription may have been stale', {
|
|
518
|
+
topic: params.topic?.substring(0, 16) + '...',
|
|
519
|
+
});
|
|
520
|
+
throw new Error('WalletConnect connection issue - please try again');
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const errorMsg = extractErrorMessage(error);
|
|
524
|
+
logger.error('Sign message failed', { error: errorMsg });
|
|
525
|
+
throw new Error(`Hedera sign message failed: ${errorMsg}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Parse accounts from Hedera namespace in WalletConnect session.
|
|
531
|
+
* Extracts account IDs from HIP-30 format: hedera:<network>:<accountId>
|
|
532
|
+
*
|
|
533
|
+
* Optionally filters accounts to only include those matching a target network.
|
|
534
|
+
* This is important for respecting the user's network selection in the dApp,
|
|
535
|
+
* since wallets may return accounts from multiple networks.
|
|
536
|
+
*
|
|
537
|
+
* @param namespace - Hedera namespace object from session
|
|
538
|
+
* @param targetNetworkId - Optional network ID to filter accounts (e.g., 'hedera:mainnet')
|
|
539
|
+
* @returns Array of parsed account objects, filtered by target network if specified
|
|
540
|
+
*/
|
|
541
|
+
parseAccounts(
|
|
542
|
+
namespace: any,
|
|
543
|
+
targetNetworkId?: string,
|
|
544
|
+
): Array<{ address: string; chainId: string }> {
|
|
545
|
+
if (!namespace) {
|
|
546
|
+
logger.warn('Namespace is null or undefined');
|
|
547
|
+
return [];
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (!(namespace as { accounts?: string[] }).accounts) {
|
|
551
|
+
logger.warn('Namespace has no accounts property');
|
|
552
|
+
return [];
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (!Array.isArray((namespace as { accounts?: string[] }).accounts)) {
|
|
556
|
+
logger.warn('Namespace accounts is not an array', {
|
|
557
|
+
type: typeof (namespace as { accounts?: string[] }).accounts,
|
|
558
|
+
});
|
|
559
|
+
return [];
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Parse all accounts from namespace
|
|
563
|
+
const allAccounts = ((namespace as { accounts?: string[] }).accounts || []).map(
|
|
564
|
+
(account: string) => {
|
|
565
|
+
// HIP-30 format: hedera:testnet:0.0.12345
|
|
566
|
+
const parts = account.split(':');
|
|
567
|
+
|
|
568
|
+
if (parts.length < 3) {
|
|
569
|
+
logger.warn('Invalid account format', { account, partsLength: parts.length });
|
|
570
|
+
return { address: account, chainId: 'hedera:testnet' };
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return {
|
|
574
|
+
address: parts[parts.length - 1], // Account ID (0.0.12345)
|
|
575
|
+
chainId: `${parts[0]}:${parts[1]}`, // hedera:testnet
|
|
576
|
+
};
|
|
577
|
+
},
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
// If target network specified, filter accounts to only that network
|
|
581
|
+
if (targetNetworkId) {
|
|
582
|
+
const targetNetwork = targetNetworkId.split(':')[1]?.toLowerCase(); // e.g., 'mainnet'
|
|
583
|
+
|
|
584
|
+
if (targetNetwork) {
|
|
585
|
+
const filteredAccounts = allAccounts.filter((acc) =>
|
|
586
|
+
acc.chainId.toLowerCase().includes(targetNetwork),
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
logger.debug('Filtered accounts by target network', {
|
|
590
|
+
targetNetworkId,
|
|
591
|
+
targetNetwork,
|
|
592
|
+
totalAccounts: allAccounts.length,
|
|
593
|
+
filteredCount: filteredAccounts.length,
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// If no accounts match the target network, log a warning
|
|
597
|
+
if (filteredAccounts.length === 0 && allAccounts.length > 0) {
|
|
598
|
+
logger.warn('No accounts found for target network', {
|
|
599
|
+
targetNetwork,
|
|
600
|
+
availableNetworks: [...new Set(allAccounts.map((a) => a.chainId))],
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return filteredAccounts;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return allAccounts;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file signer-factory.spec.ts
|
|
3
|
+
* @description Unit tests for SignerFactory
|
|
4
|
+
*
|
|
5
|
+
* @compodoc
|
|
6
|
+
* SignerFactory uses static methods for signer management,
|
|
7
|
+
* so tests call static methods directly rather than using DI.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
11
|
+
import { SignerFactory } from './signer-factory';
|
|
12
|
+
|
|
13
|
+
describe('SignerFactory', () => {
|
|
14
|
+
describe('getSigner', () => {
|
|
15
|
+
it('should return a signer for "hedera" ledger ID', () => {
|
|
16
|
+
const signer = SignerFactory.getSigner('hedera');
|
|
17
|
+
|
|
18
|
+
expect(signer).toBeDefined();
|
|
19
|
+
expect(signer.ledgerId).toBe('hedera');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should return a signer for "xrpl" ledger ID', () => {
|
|
23
|
+
const signer = SignerFactory.getSigner('xrpl');
|
|
24
|
+
|
|
25
|
+
expect(signer).toBeDefined();
|
|
26
|
+
expect(signer.ledgerId).toBe('xrpl');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should throw error for unknown ledger ID', () => {
|
|
30
|
+
expect(() => {
|
|
31
|
+
SignerFactory.getSigner('unknown');
|
|
32
|
+
}).toThrow('Unsupported ledger: unknown');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should return same instance on multiple calls (singleton)', () => {
|
|
36
|
+
const signer1 = SignerFactory.getSigner('hedera');
|
|
37
|
+
const signer2 = SignerFactory.getSigner('hedera');
|
|
38
|
+
|
|
39
|
+
expect(signer1).toBe(signer2);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('hasSigner', () => {
|
|
44
|
+
it('should return true for registered ledger', () => {
|
|
45
|
+
expect(SignerFactory.hasSigner('hedera')).toBe(true);
|
|
46
|
+
expect(SignerFactory.hasSigner('xrpl')).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should return false for unregistered ledger', () => {
|
|
50
|
+
expect(SignerFactory.hasSigner('solana')).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('getAvailableLedgers', () => {
|
|
55
|
+
it('should return list of available ledgers', () => {
|
|
56
|
+
const ledgers = SignerFactory.getAvailableLedgers();
|
|
57
|
+
|
|
58
|
+
expect(ledgers).toContain('hedera');
|
|
59
|
+
expect(ledgers).toContain('xrpl');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|