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