@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.
Files changed (276) hide show
  1. package/README.md +48 -0
  2. package/USAGE_EXAMPLES.md +476 -0
  3. package/assets/wallets/extension.svg +7 -0
  4. package/assets/wallets/hashpack.svg +6 -0
  5. package/assets/wallets/hsuite.svg +11 -0
  6. package/assets/wallets/kabila.svg +11 -0
  7. package/assets/wallets/walletconnect.svg +13 -0
  8. package/coverage/base.css +224 -0
  9. package/coverage/block-navigation.js +87 -0
  10. package/coverage/coverage-summary.json +50 -0
  11. package/coverage/favicon.png +0 -0
  12. package/coverage/index.html +476 -0
  13. package/coverage/lcov-report/base.css +224 -0
  14. package/coverage/lcov-report/block-navigation.js +87 -0
  15. package/coverage/lcov-report/favicon.png +0 -0
  16. package/coverage/lcov-report/index.html +476 -0
  17. package/coverage/lcov-report/lib/components/account-selector/account-actions/account-actions.component.ts.html +868 -0
  18. package/coverage/lcov-report/lib/components/account-selector/account-actions/index.html +116 -0
  19. package/coverage/lcov-report/lib/components/account-selector/account-filter/account-filter.component.ts.html +1288 -0
  20. package/coverage/lcov-report/lib/components/account-selector/account-filter/index.html +116 -0
  21. package/coverage/lcov-report/lib/components/account-selector/account-formatting.service.ts.html +685 -0
  22. package/coverage/lcov-report/lib/components/account-selector/account-grouping.service.ts.html +766 -0
  23. package/coverage/lcov-report/lib/components/account-selector/account-list/account-list.component.ts.html +1495 -0
  24. package/coverage/lcov-report/lib/components/account-selector/account-list/index.html +116 -0
  25. package/coverage/lcov-report/lib/components/account-selector/account-selector.component.ts.html +1495 -0
  26. package/coverage/lcov-report/lib/components/account-selector/account-selector.service.ts.html +1588 -0
  27. package/coverage/lcov-report/lib/components/account-selector/index.html +161 -0
  28. package/coverage/lcov-report/lib/components/wallet-account-display/index.html +116 -0
  29. package/coverage/lcov-report/lib/components/wallet-account-display/wallet-account-display.component.ts.html +505 -0
  30. package/coverage/lcov-report/lib/components/wallet-connect-button/index.html +116 -0
  31. package/coverage/lcov-report/lib/components/wallet-connect-button/wallet-connect-button.component.ts.html +805 -0
  32. package/coverage/lcov-report/lib/components/wallet-connect-prompt/index.html +116 -0
  33. package/coverage/lcov-report/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts.html +409 -0
  34. package/coverage/lcov-report/lib/components/wallet-connected-guard/index.html +116 -0
  35. package/coverage/lcov-report/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts.html +304 -0
  36. package/coverage/lcov-report/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts.html +436 -0
  37. package/coverage/lcov-report/lib/components/wallet-connection-modal/connection-method-step/index.html +116 -0
  38. package/coverage/lcov-report/lib/components/wallet-connection-modal/index.html +116 -0
  39. package/coverage/lcov-report/lib/components/wallet-connection-modal/qr-pairing-step/index.html +116 -0
  40. package/coverage/lcov-report/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts.html +2287 -0
  41. package/coverage/lcov-report/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts.html +2275 -0
  42. package/coverage/lcov-report/lib/components/wallet-session-display/index.html +116 -0
  43. package/coverage/lcov-report/lib/components/wallet-session-display/wallet-session-display.component.ts.html +676 -0
  44. package/coverage/lcov-report/lib/components/wallet-transaction-status/index.html +116 -0
  45. package/coverage/lcov-report/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts.html +703 -0
  46. package/coverage/lcov-report/lib/directives/index.html +146 -0
  47. package/coverage/lcov-report/lib/directives/wallet-connected.directive.ts.html +670 -0
  48. package/coverage/lcov-report/lib/directives/wallet-context.directive.ts.html +547 -0
  49. package/coverage/lcov-report/lib/directives/wallet-events.directive.ts.html +781 -0
  50. package/coverage/lcov-report/lib/hsuite-wallet.module.ts.html +715 -0
  51. package/coverage/lcov-report/lib/index.html +116 -0
  52. package/coverage/lcov-report/lib/models/connection-config.model.ts.html +280 -0
  53. package/coverage/lcov-report/lib/models/index.html +131 -0
  54. package/coverage/lcov-report/lib/models/provider-types.ts.html +577 -0
  55. package/coverage/lcov-report/lib/providers/base-wallet-provider.ts.html +1138 -0
  56. package/coverage/lcov-report/lib/providers/hsuite-native/channel-client.service.ts.html +2671 -0
  57. package/coverage/lcov-report/lib/providers/hsuite-native/index.html +116 -0
  58. package/coverage/lcov-report/lib/providers/hsuite-native-provider.ts.html +2347 -0
  59. package/coverage/lcov-report/lib/providers/index.html +146 -0
  60. package/coverage/lcov-report/lib/providers/p2p-native/index.html +131 -0
  61. package/coverage/lcov-report/lib/providers/p2p-native/p2p-native.provider.ts.html +2254 -0
  62. package/coverage/lcov-report/lib/providers/p2p-native/p2p-session-manager.ts.html +2170 -0
  63. package/coverage/lcov-report/lib/providers/wallet-error-handler.ts.html +1132 -0
  64. package/coverage/lcov-report/lib/providers/walletconnect/core/index.html +176 -0
  65. package/coverage/lcov-report/lib/providers/walletconnect/core/session-health.ts.html +673 -0
  66. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-client-manager.ts.html +1177 -0
  67. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-provider.ts.html +2563 -0
  68. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-session-store.ts.html +904 -0
  69. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts.html +982 -0
  70. package/coverage/lcov-report/lib/providers/walletconnect/signers/hedera-signer.ts.html +1915 -0
  71. package/coverage/lcov-report/lib/providers/walletconnect/signers/index.html +146 -0
  72. package/coverage/lcov-report/lib/providers/walletconnect/signers/signer-factory.ts.html +445 -0
  73. package/coverage/lcov-report/lib/providers/walletconnect/signers/xrpl-signer.ts.html +1519 -0
  74. package/coverage/lcov-report/lib/services/index.html +191 -0
  75. package/coverage/lcov-report/lib/services/logger.service.ts.html +463 -0
  76. package/coverage/lcov-report/lib/services/transaction-builders/base-transaction-builder.service.ts.html +1840 -0
  77. package/coverage/lcov-report/lib/services/transaction-builders/hedera-amount-utils.ts.html +337 -0
  78. package/coverage/lcov-report/lib/services/transaction-builders/hedera-transaction-builder.service.ts.html +3940 -0
  79. package/coverage/lcov-report/lib/services/transaction-builders/index.html +161 -0
  80. package/coverage/lcov-report/lib/services/transaction-builders/xrpl-transaction-builder.service.ts.html +2581 -0
  81. package/coverage/lcov-report/lib/services/transaction.service.ts.html +1123 -0
  82. package/coverage/lcov-report/lib/services/unified-wallet.service.ts.html +2641 -0
  83. package/coverage/lcov-report/lib/services/wallet-context.service.ts.html +637 -0
  84. package/coverage/lcov-report/lib/services/wallet-event-bus.service.ts.html +643 -0
  85. package/coverage/lcov-report/lib/services/wallet-providers.service.ts.html +496 -0
  86. package/coverage/lcov-report/lib/transports/chrome-extension-transport.ts.html +823 -0
  87. package/coverage/lcov-report/lib/transports/index.html +116 -0
  88. package/coverage/lcov-report/lib/utils/index.html +116 -0
  89. package/coverage/lcov-report/lib/utils/ledger-icons.util.ts.html +319 -0
  90. package/coverage/lcov-report/prettify.css +1 -0
  91. package/coverage/lcov-report/prettify.js +2 -0
  92. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  93. package/coverage/lcov-report/sorter.js +210 -0
  94. package/coverage/lcov.info +19252 -0
  95. package/coverage/lib/components/account-selector/account-actions/account-actions.component.ts.html +868 -0
  96. package/coverage/lib/components/account-selector/account-actions/index.html +116 -0
  97. package/coverage/lib/components/account-selector/account-filter/account-filter.component.ts.html +1288 -0
  98. package/coverage/lib/components/account-selector/account-filter/index.html +116 -0
  99. package/coverage/lib/components/account-selector/account-formatting.service.ts.html +685 -0
  100. package/coverage/lib/components/account-selector/account-grouping.service.ts.html +766 -0
  101. package/coverage/lib/components/account-selector/account-list/account-list.component.ts.html +1495 -0
  102. package/coverage/lib/components/account-selector/account-list/index.html +116 -0
  103. package/coverage/lib/components/account-selector/account-selector.component.ts.html +1495 -0
  104. package/coverage/lib/components/account-selector/account-selector.service.ts.html +1588 -0
  105. package/coverage/lib/components/account-selector/index.html +161 -0
  106. package/coverage/lib/components/wallet-account-display/index.html +116 -0
  107. package/coverage/lib/components/wallet-account-display/wallet-account-display.component.ts.html +505 -0
  108. package/coverage/lib/components/wallet-connect-button/index.html +116 -0
  109. package/coverage/lib/components/wallet-connect-button/wallet-connect-button.component.ts.html +805 -0
  110. package/coverage/lib/components/wallet-connect-prompt/index.html +116 -0
  111. package/coverage/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts.html +409 -0
  112. package/coverage/lib/components/wallet-connected-guard/index.html +116 -0
  113. package/coverage/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts.html +304 -0
  114. package/coverage/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts.html +436 -0
  115. package/coverage/lib/components/wallet-connection-modal/connection-method-step/index.html +116 -0
  116. package/coverage/lib/components/wallet-connection-modal/index.html +116 -0
  117. package/coverage/lib/components/wallet-connection-modal/qr-pairing-step/index.html +116 -0
  118. package/coverage/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts.html +2287 -0
  119. package/coverage/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts.html +2275 -0
  120. package/coverage/lib/components/wallet-session-display/index.html +116 -0
  121. package/coverage/lib/components/wallet-session-display/wallet-session-display.component.ts.html +676 -0
  122. package/coverage/lib/components/wallet-transaction-status/index.html +116 -0
  123. package/coverage/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts.html +703 -0
  124. package/coverage/lib/directives/index.html +146 -0
  125. package/coverage/lib/directives/wallet-connected.directive.ts.html +670 -0
  126. package/coverage/lib/directives/wallet-context.directive.ts.html +547 -0
  127. package/coverage/lib/directives/wallet-events.directive.ts.html +781 -0
  128. package/coverage/lib/hsuite-wallet.module.ts.html +715 -0
  129. package/coverage/lib/index.html +116 -0
  130. package/coverage/lib/models/connection-config.model.ts.html +280 -0
  131. package/coverage/lib/models/index.html +131 -0
  132. package/coverage/lib/models/provider-types.ts.html +577 -0
  133. package/coverage/lib/providers/base-wallet-provider.ts.html +1138 -0
  134. package/coverage/lib/providers/hsuite-native/channel-client.service.ts.html +2671 -0
  135. package/coverage/lib/providers/hsuite-native/index.html +116 -0
  136. package/coverage/lib/providers/hsuite-native-provider.ts.html +2347 -0
  137. package/coverage/lib/providers/index.html +146 -0
  138. package/coverage/lib/providers/p2p-native/index.html +131 -0
  139. package/coverage/lib/providers/p2p-native/p2p-native.provider.ts.html +2254 -0
  140. package/coverage/lib/providers/p2p-native/p2p-session-manager.ts.html +2170 -0
  141. package/coverage/lib/providers/wallet-error-handler.ts.html +1132 -0
  142. package/coverage/lib/providers/walletconnect/core/index.html +176 -0
  143. package/coverage/lib/providers/walletconnect/core/session-health.ts.html +673 -0
  144. package/coverage/lib/providers/walletconnect/core/walletconnect-client-manager.ts.html +1177 -0
  145. package/coverage/lib/providers/walletconnect/core/walletconnect-provider.ts.html +2563 -0
  146. package/coverage/lib/providers/walletconnect/core/walletconnect-session-store.ts.html +904 -0
  147. package/coverage/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts.html +982 -0
  148. package/coverage/lib/providers/walletconnect/signers/hedera-signer.ts.html +1915 -0
  149. package/coverage/lib/providers/walletconnect/signers/index.html +146 -0
  150. package/coverage/lib/providers/walletconnect/signers/signer-factory.ts.html +445 -0
  151. package/coverage/lib/providers/walletconnect/signers/xrpl-signer.ts.html +1519 -0
  152. package/coverage/lib/services/index.html +191 -0
  153. package/coverage/lib/services/logger.service.ts.html +463 -0
  154. package/coverage/lib/services/transaction-builders/base-transaction-builder.service.ts.html +1840 -0
  155. package/coverage/lib/services/transaction-builders/hedera-amount-utils.ts.html +337 -0
  156. package/coverage/lib/services/transaction-builders/hedera-transaction-builder.service.ts.html +3940 -0
  157. package/coverage/lib/services/transaction-builders/index.html +161 -0
  158. package/coverage/lib/services/transaction-builders/xrpl-transaction-builder.service.ts.html +2581 -0
  159. package/coverage/lib/services/transaction.service.ts.html +1123 -0
  160. package/coverage/lib/services/unified-wallet.service.ts.html +2641 -0
  161. package/coverage/lib/services/wallet-context.service.ts.html +637 -0
  162. package/coverage/lib/services/wallet-event-bus.service.ts.html +643 -0
  163. package/coverage/lib/services/wallet-providers.service.ts.html +496 -0
  164. package/coverage/lib/transports/chrome-extension-transport.ts.html +823 -0
  165. package/coverage/lib/transports/index.html +116 -0
  166. package/coverage/lib/utils/index.html +116 -0
  167. package/coverage/lib/utils/ledger-icons.util.ts.html +319 -0
  168. package/coverage/prettify.css +1 -0
  169. package/coverage/prettify.js +2 -0
  170. package/coverage/sort-arrow-sprite.png +0 -0
  171. package/coverage/sorter.js +210 -0
  172. package/dist/README.md +48 -0
  173. package/dist/fesm2022/hsuite-native-connect-angular.mjs +14592 -0
  174. package/dist/fesm2022/hsuite-native-connect-angular.mjs.map +1 -0
  175. package/dist/index.d.ts +6949 -0
  176. package/examples/minimal-connect.ts +178 -0
  177. package/examples/multi-protocol.ts +495 -0
  178. package/examples/transaction-signing.ts +361 -0
  179. package/jest.config.json +45 -0
  180. package/karma.conf.js +42 -0
  181. package/ng-package.json +20 -0
  182. package/package.json +60 -0
  183. package/src/index.ts +203 -0
  184. package/src/lib/components/account-selector/account-actions/account-actions.component.ts +261 -0
  185. package/src/lib/components/account-selector/account-filter/account-filter.component.ts +401 -0
  186. package/src/lib/components/account-selector/account-formatting.service.ts +200 -0
  187. package/src/lib/components/account-selector/account-grouping.service.ts +227 -0
  188. package/src/lib/components/account-selector/account-list/account-list.component.ts +470 -0
  189. package/src/lib/components/account-selector/account-selector.component.html +135 -0
  190. package/src/lib/components/account-selector/account-selector.component.scss +2039 -0
  191. package/src/lib/components/account-selector/account-selector.component.ts +470 -0
  192. package/src/lib/components/account-selector/account-selector.service.ts +501 -0
  193. package/src/lib/components/wallet-account-display/wallet-account-display.component.html +34 -0
  194. package/src/lib/components/wallet-account-display/wallet-account-display.component.scss +99 -0
  195. package/src/lib/components/wallet-account-display/wallet-account-display.component.ts +140 -0
  196. package/src/lib/components/wallet-connect-button/wallet-connect-button.component.html +14 -0
  197. package/src/lib/components/wallet-connect-button/wallet-connect-button.component.scss +272 -0
  198. package/src/lib/components/wallet-connect-button/wallet-connect-button.component.ts +240 -0
  199. package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.html +24 -0
  200. package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.scss +50 -0
  201. package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts +108 -0
  202. package/src/lib/components/wallet-connected-guard/wallet-connected-guard.component.html +24 -0
  203. package/src/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts +73 -0
  204. package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.html +56 -0
  205. package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.scss +218 -0
  206. package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts +117 -0
  207. package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.html +94 -0
  208. package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.scss +272 -0
  209. package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts +734 -0
  210. package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.html +197 -0
  211. package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.scss +678 -0
  212. package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts +730 -0
  213. package/src/lib/components/wallet-session-display/wallet-session-display.component.html +110 -0
  214. package/src/lib/components/wallet-session-display/wallet-session-display.component.scss +179 -0
  215. package/src/lib/components/wallet-session-display/wallet-session-display.component.ts +197 -0
  216. package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.html +65 -0
  217. package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.scss +254 -0
  218. package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts +206 -0
  219. package/src/lib/directives/wallet-connected.directive.ts +195 -0
  220. package/src/lib/directives/wallet-context.directive.ts +154 -0
  221. package/src/lib/directives/wallet-events.directive.ts +232 -0
  222. package/src/lib/hsuite-wallet.module.ts +210 -0
  223. package/src/lib/models/connection-config.model.ts +65 -0
  224. package/src/lib/models/provider-types.ts +164 -0
  225. package/src/lib/models/unified-account.model.ts +76 -0
  226. package/src/lib/models/wallet-context.model.ts +121 -0
  227. package/src/lib/models/wallet-events.model.ts +158 -0
  228. package/src/lib/providers/base-wallet-provider.ts +351 -0
  229. package/src/lib/providers/hsuite-native/channel-client.service.spec.ts +73 -0
  230. package/src/lib/providers/hsuite-native/channel-client.service.ts +862 -0
  231. package/src/lib/providers/hsuite-native/index.ts +8 -0
  232. package/src/lib/providers/hsuite-native-provider.ts +754 -0
  233. package/src/lib/providers/mobile-native/mobile-native.provider.spec.ts +19 -0
  234. package/src/lib/providers/p2p-native/index.ts +30 -0
  235. package/src/lib/providers/p2p-native/p2p-native.provider.spec.ts +523 -0
  236. package/src/lib/providers/p2p-native/p2p-native.provider.ts +723 -0
  237. package/src/lib/providers/p2p-native/p2p-session-manager.ts +695 -0
  238. package/src/lib/providers/wallet-error-handler.ts +349 -0
  239. package/src/lib/providers/walletconnect/core/base-signer.interface.ts +122 -0
  240. package/src/lib/providers/walletconnect/core/session-health.ts +196 -0
  241. package/src/lib/providers/walletconnect/core/walletconnect-client-manager.ts +364 -0
  242. package/src/lib/providers/walletconnect/core/walletconnect-provider.integration.spec.ts +348 -0
  243. package/src/lib/providers/walletconnect/core/walletconnect-provider.ts +826 -0
  244. package/src/lib/providers/walletconnect/core/walletconnect-session-store.ts +273 -0
  245. package/src/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts +299 -0
  246. package/src/lib/providers/walletconnect/core/walletconnect-types.ts +48 -0
  247. package/src/lib/providers/walletconnect/index.ts +33 -0
  248. package/src/lib/providers/walletconnect/signers/hedera-signer.spec.ts +367 -0
  249. package/src/lib/providers/walletconnect/signers/hedera-signer.ts +610 -0
  250. package/src/lib/providers/walletconnect/signers/signer-factory.spec.ts +62 -0
  251. package/src/lib/providers/walletconnect/signers/signer-factory.ts +120 -0
  252. package/src/lib/providers/walletconnect/signers/xrpl-signer.spec.ts +296 -0
  253. package/src/lib/providers/walletconnect/signers/xrpl-signer.ts +478 -0
  254. package/src/lib/services/logger.service.ts +126 -0
  255. package/src/lib/services/transaction-builders/base-transaction-builder.service.ts +585 -0
  256. package/src/lib/services/transaction-builders/hedera-amount-utils.ts +84 -0
  257. package/src/lib/services/transaction-builders/hedera-transaction-builder.service.spec.ts +741 -0
  258. package/src/lib/services/transaction-builders/hedera-transaction-builder.service.ts +1285 -0
  259. package/src/lib/services/transaction-builders/index.ts +54 -0
  260. package/src/lib/services/transaction-builders/xrpl-transaction-builder.service.spec.ts +937 -0
  261. package/src/lib/services/transaction-builders/xrpl-transaction-builder.service.ts +832 -0
  262. package/src/lib/services/transaction.service.ts +346 -0
  263. package/src/lib/services/unified-wallet.service.spec.ts +1382 -0
  264. package/src/lib/services/unified-wallet.service.ts +852 -0
  265. package/src/lib/services/wallet-context.service.ts +184 -0
  266. package/src/lib/services/wallet-event-bus.service.ts +186 -0
  267. package/src/lib/services/wallet-providers.service.ts +137 -0
  268. package/src/lib/transports/chrome-extension-transport.ts +246 -0
  269. package/src/lib/utils/index.ts +14 -0
  270. package/src/lib/utils/ledger-icons.util.ts +78 -0
  271. package/test/test-setup.ts +21 -0
  272. package/test-setup.ts +63 -0
  273. package/tsconfig.build.json +11 -0
  274. package/tsconfig.json +29 -0
  275. package/tsconfig.spec.json +15 -0
  276. 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
+ }