@multiplatform.one/web3 7.24.0 → 7.26.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 (363) hide show
  1. package/README.md +33 -0
  2. package/dist/cjs/bridge/AuthBridge.cjs +124 -0
  3. package/dist/cjs/bridge/AuthBridge.native.js +186 -0
  4. package/dist/cjs/bridge/AuthBridge.native.js.map +1 -0
  5. package/dist/cjs/bridge/BridgeClient.cjs +12 -9
  6. package/dist/cjs/bridge/BridgeClient.native.js +17 -12
  7. package/dist/cjs/bridge/BridgeClient.native.js.map +1 -1
  8. package/dist/cjs/bridge/BridgeHost.cjs +11 -3
  9. package/dist/cjs/bridge/BridgeHost.native.js +11 -3
  10. package/dist/cjs/bridge/BridgeHost.native.js.map +1 -1
  11. package/dist/cjs/bridge/GpgBridge.cjs +61 -0
  12. package/dist/cjs/bridge/GpgBridge.native.js +111 -0
  13. package/dist/cjs/bridge/GpgBridge.native.js.map +1 -0
  14. package/dist/cjs/bridge/SuiBridge.cjs +51 -0
  15. package/dist/cjs/bridge/SuiBridge.native.js +99 -0
  16. package/dist/cjs/bridge/SuiBridge.native.js.map +1 -0
  17. package/dist/cjs/bridge/authTypes.cjs +28 -0
  18. package/dist/cjs/bridge/authTypes.native.js +31 -0
  19. package/dist/cjs/bridge/authTypes.native.js.map +1 -0
  20. package/dist/cjs/bridge/core.cjs +65 -0
  21. package/dist/cjs/bridge/core.native.js +68 -0
  22. package/dist/cjs/bridge/core.native.js.map +1 -0
  23. package/dist/cjs/bridge/hosting.cjs +91 -0
  24. package/dist/cjs/bridge/hosting.native.js +117 -0
  25. package/dist/cjs/bridge/hosting.native.js.map +1 -0
  26. package/dist/cjs/bridge/index.cjs +3 -17
  27. package/dist/cjs/bridge/index.native.js +2 -16
  28. package/dist/cjs/bridge/index.native.js.map +1 -1
  29. package/dist/cjs/bridge/nativeHost.cjs +91 -0
  30. package/dist/cjs/bridge/nativeHost.native.js +118 -0
  31. package/dist/cjs/bridge/nativeHost.native.js.map +1 -0
  32. package/dist/cjs/bridge/protocol.cjs +30 -1
  33. package/dist/cjs/bridge/protocol.native.js +147 -1
  34. package/dist/cjs/bridge/protocol.native.js.map +1 -1
  35. package/dist/cjs/core/family.cjs +88 -0
  36. package/dist/cjs/core/family.native.js +115 -0
  37. package/dist/cjs/core/family.native.js.map +1 -0
  38. package/dist/cjs/core/index.cjs +41 -0
  39. package/dist/cjs/core/index.native.js +44 -0
  40. package/dist/cjs/core/index.native.js.map +1 -0
  41. package/dist/cjs/core/message.cjs +89 -0
  42. package/dist/cjs/core/message.native.js +104 -0
  43. package/dist/cjs/core/message.native.js.map +1 -0
  44. package/dist/cjs/core/names.cjs +51 -0
  45. package/dist/cjs/core/names.native.js +95 -0
  46. package/dist/cjs/core/names.native.js.map +1 -0
  47. package/dist/cjs/core/react.cjs +48 -0
  48. package/dist/cjs/core/react.native.js +52 -0
  49. package/dist/cjs/core/react.native.js.map +1 -0
  50. package/dist/cjs/core/store.cjs +450 -0
  51. package/dist/cjs/core/store.native.js +637 -0
  52. package/dist/cjs/core/store.native.js.map +1 -0
  53. package/dist/cjs/core/types.cjs +18 -0
  54. package/dist/cjs/core/types.native.js +21 -0
  55. package/dist/cjs/core/types.native.js.map +1 -0
  56. package/dist/cjs/index.cjs +3 -1
  57. package/dist/cjs/index.native.js +2 -0
  58. package/dist/cjs/index.native.js.map +1 -1
  59. package/dist/cjs/providers/WalletProvider.cjs +2 -0
  60. package/dist/cjs/providers/WalletProvider.native.js +123 -90
  61. package/dist/cjs/providers/WalletProvider.native.js.map +1 -1
  62. package/dist/cjs/providers/index.cjs +1 -0
  63. package/dist/cjs/providers/index.native.js +1 -0
  64. package/dist/cjs/providers/index.native.js.map +1 -1
  65. package/dist/cjs/providers/native/deepLink.cjs +40 -23
  66. package/dist/cjs/providers/native/deepLink.native.js +43 -25
  67. package/dist/cjs/providers/native/deepLink.native.js.map +1 -1
  68. package/dist/cjs/providers/native/inject.cjs +2 -4
  69. package/dist/cjs/providers/native/inject.native.js +2 -4
  70. package/dist/cjs/providers/native/inject.native.js.map +1 -1
  71. package/dist/cjs/providers/walletBridge.cjs +35 -0
  72. package/dist/cjs/providers/walletBridge.native.js +38 -0
  73. package/dist/cjs/providers/walletBridge.native.js.map +1 -0
  74. package/dist/cjs/providers/walletConfig.native.js.map +1 -1
  75. package/dist/esm/bridge/AuthBridge.mjs +99 -0
  76. package/dist/esm/bridge/AuthBridge.mjs.map +1 -0
  77. package/dist/esm/bridge/AuthBridge.native.js +158 -0
  78. package/dist/esm/bridge/AuthBridge.native.js.map +1 -0
  79. package/dist/esm/bridge/BridgeClient.mjs +13 -10
  80. package/dist/esm/bridge/BridgeClient.mjs.map +1 -1
  81. package/dist/esm/bridge/BridgeClient.native.js +18 -13
  82. package/dist/esm/bridge/BridgeClient.native.js.map +1 -1
  83. package/dist/esm/bridge/BridgeHost.mjs +12 -4
  84. package/dist/esm/bridge/BridgeHost.mjs.map +1 -1
  85. package/dist/esm/bridge/BridgeHost.native.js +12 -4
  86. package/dist/esm/bridge/BridgeHost.native.js.map +1 -1
  87. package/dist/esm/bridge/GpgBridge.mjs +36 -0
  88. package/dist/esm/bridge/GpgBridge.mjs.map +1 -0
  89. package/dist/esm/bridge/GpgBridge.native.js +83 -0
  90. package/dist/esm/bridge/GpgBridge.native.js.map +1 -0
  91. package/dist/esm/bridge/SuiBridge.mjs +26 -0
  92. package/dist/esm/bridge/SuiBridge.mjs.map +1 -0
  93. package/dist/esm/bridge/SuiBridge.native.js +71 -0
  94. package/dist/esm/bridge/SuiBridge.native.js.map +1 -0
  95. package/dist/esm/bridge/authTypes.mjs +3 -0
  96. package/dist/esm/bridge/authTypes.mjs.map +1 -0
  97. package/dist/esm/bridge/authTypes.native.js +3 -0
  98. package/dist/esm/bridge/authTypes.native.js.map +1 -0
  99. package/dist/esm/bridge/core.mjs +17 -0
  100. package/dist/esm/bridge/core.mjs.map +1 -0
  101. package/dist/esm/bridge/core.native.js +17 -0
  102. package/dist/esm/bridge/core.native.js.map +1 -0
  103. package/dist/esm/bridge/hosting.mjs +62 -0
  104. package/dist/esm/bridge/hosting.mjs.map +1 -0
  105. package/dist/esm/bridge/hosting.native.js +85 -0
  106. package/dist/esm/bridge/hosting.native.js.map +1 -0
  107. package/dist/esm/bridge/index.mjs +2 -9
  108. package/dist/esm/bridge/index.mjs.map +1 -1
  109. package/dist/esm/bridge/index.native.js +2 -9
  110. package/dist/esm/bridge/index.native.js.map +1 -1
  111. package/dist/esm/bridge/nativeHost.mjs +65 -0
  112. package/dist/esm/bridge/nativeHost.mjs.map +1 -0
  113. package/dist/esm/bridge/nativeHost.native.js +89 -0
  114. package/dist/esm/bridge/nativeHost.native.js.map +1 -0
  115. package/dist/esm/bridge/protocol.mjs +28 -2
  116. package/dist/esm/bridge/protocol.mjs.map +1 -1
  117. package/dist/esm/bridge/protocol.native.js +145 -2
  118. package/dist/esm/bridge/protocol.native.js.map +1 -1
  119. package/dist/esm/core/family.mjs +62 -0
  120. package/dist/esm/core/family.mjs.map +1 -0
  121. package/dist/esm/core/family.native.js +86 -0
  122. package/dist/esm/core/family.native.js.map +1 -0
  123. package/dist/esm/core/index.mjs +7 -0
  124. package/dist/esm/core/index.mjs.map +1 -0
  125. package/dist/esm/core/index.native.js +7 -0
  126. package/dist/esm/core/index.native.js.map +1 -0
  127. package/dist/esm/core/message.mjs +63 -0
  128. package/dist/esm/core/message.mjs.map +1 -0
  129. package/dist/esm/core/message.native.js +75 -0
  130. package/dist/esm/core/message.native.js.map +1 -0
  131. package/dist/esm/core/names.mjs +25 -0
  132. package/dist/esm/core/names.mjs.map +1 -0
  133. package/dist/esm/core/names.native.js +66 -0
  134. package/dist/esm/core/names.native.js.map +1 -0
  135. package/dist/esm/core/react.mjs +21 -0
  136. package/dist/esm/core/react.mjs.map +1 -0
  137. package/dist/esm/core/react.native.js +22 -0
  138. package/dist/esm/core/react.native.js.map +1 -0
  139. package/dist/esm/core/store.mjs +424 -0
  140. package/dist/esm/core/store.mjs.map +1 -0
  141. package/dist/esm/core/store.native.js +608 -0
  142. package/dist/esm/core/store.native.js.map +1 -0
  143. package/dist/esm/core/types.mjs +2 -0
  144. package/dist/esm/core/types.mjs.map +1 -0
  145. package/dist/esm/core/types.native.js +2 -0
  146. package/dist/esm/core/types.native.js.map +1 -0
  147. package/dist/esm/index.mjs +3 -2
  148. package/dist/esm/index.mjs.map +1 -1
  149. package/dist/esm/index.native.js +3 -2
  150. package/dist/esm/index.native.js.map +1 -1
  151. package/dist/esm/providers/WalletProvider.mjs +2 -1
  152. package/dist/esm/providers/WalletProvider.mjs.map +1 -1
  153. package/dist/esm/providers/WalletProvider.native.js +125 -93
  154. package/dist/esm/providers/WalletProvider.native.js.map +1 -1
  155. package/dist/esm/providers/index.mjs +2 -2
  156. package/dist/esm/providers/index.mjs.map +1 -1
  157. package/dist/esm/providers/index.native.js +2 -2
  158. package/dist/esm/providers/index.native.js.map +1 -1
  159. package/dist/esm/providers/native/deepLink.mjs +36 -24
  160. package/dist/esm/providers/native/deepLink.mjs.map +1 -1
  161. package/dist/esm/providers/native/deepLink.native.js +39 -26
  162. package/dist/esm/providers/native/deepLink.native.js.map +1 -1
  163. package/dist/esm/providers/native/inject.mjs +1 -3
  164. package/dist/esm/providers/native/inject.mjs.map +1 -1
  165. package/dist/esm/providers/native/inject.native.js +1 -3
  166. package/dist/esm/providers/native/inject.native.js.map +1 -1
  167. package/dist/esm/providers/walletBridge.mjs +9 -0
  168. package/dist/esm/providers/walletBridge.mjs.map +1 -0
  169. package/dist/esm/providers/walletBridge.native.js +9 -0
  170. package/dist/esm/providers/walletBridge.native.js.map +1 -0
  171. package/dist/esm/providers/walletConfig.mjs.map +1 -1
  172. package/dist/esm/providers/walletConfig.native.js.map +1 -1
  173. package/dist/jsx/bridge/AuthBridge.mjs +99 -0
  174. package/dist/jsx/bridge/AuthBridge.mjs.map +1 -0
  175. package/dist/jsx/bridge/AuthBridge.native.js +186 -0
  176. package/dist/jsx/bridge/AuthBridge.native.js.map +1 -0
  177. package/dist/jsx/bridge/BridgeClient.mjs +13 -10
  178. package/dist/jsx/bridge/BridgeClient.mjs.map +1 -1
  179. package/dist/jsx/bridge/BridgeClient.native.js +17 -12
  180. package/dist/jsx/bridge/BridgeClient.native.js.map +1 -1
  181. package/dist/jsx/bridge/BridgeHost.mjs +12 -4
  182. package/dist/jsx/bridge/BridgeHost.mjs.map +1 -1
  183. package/dist/jsx/bridge/BridgeHost.native.js +11 -3
  184. package/dist/jsx/bridge/BridgeHost.native.js.map +1 -1
  185. package/dist/jsx/bridge/GpgBridge.mjs +36 -0
  186. package/dist/jsx/bridge/GpgBridge.mjs.map +1 -0
  187. package/dist/jsx/bridge/GpgBridge.native.js +111 -0
  188. package/dist/jsx/bridge/GpgBridge.native.js.map +1 -0
  189. package/dist/jsx/bridge/SuiBridge.mjs +26 -0
  190. package/dist/jsx/bridge/SuiBridge.mjs.map +1 -0
  191. package/dist/jsx/bridge/SuiBridge.native.js +99 -0
  192. package/dist/jsx/bridge/SuiBridge.native.js.map +1 -0
  193. package/dist/jsx/bridge/authTypes.mjs +3 -0
  194. package/dist/jsx/bridge/authTypes.mjs.map +1 -0
  195. package/dist/jsx/bridge/authTypes.native.js +31 -0
  196. package/dist/jsx/bridge/authTypes.native.js.map +1 -0
  197. package/dist/jsx/bridge/core.mjs +17 -0
  198. package/dist/jsx/bridge/core.mjs.map +1 -0
  199. package/dist/jsx/bridge/core.native.js +68 -0
  200. package/dist/jsx/bridge/core.native.js.map +1 -0
  201. package/dist/jsx/bridge/hosting.mjs +62 -0
  202. package/dist/jsx/bridge/hosting.mjs.map +1 -0
  203. package/dist/jsx/bridge/hosting.native.js +117 -0
  204. package/dist/jsx/bridge/hosting.native.js.map +1 -0
  205. package/dist/jsx/bridge/index.mjs +2 -9
  206. package/dist/jsx/bridge/index.mjs.map +1 -1
  207. package/dist/jsx/bridge/index.native.js +2 -16
  208. package/dist/jsx/bridge/index.native.js.map +1 -1
  209. package/dist/jsx/bridge/nativeHost.mjs +65 -0
  210. package/dist/jsx/bridge/nativeHost.mjs.map +1 -0
  211. package/dist/jsx/bridge/nativeHost.native.js +118 -0
  212. package/dist/jsx/bridge/nativeHost.native.js.map +1 -0
  213. package/dist/jsx/bridge/protocol.mjs +28 -2
  214. package/dist/jsx/bridge/protocol.mjs.map +1 -1
  215. package/dist/jsx/bridge/protocol.native.js +147 -1
  216. package/dist/jsx/bridge/protocol.native.js.map +1 -1
  217. package/dist/jsx/core/family.mjs +62 -0
  218. package/dist/jsx/core/family.mjs.map +1 -0
  219. package/dist/jsx/core/family.native.js +115 -0
  220. package/dist/jsx/core/family.native.js.map +1 -0
  221. package/dist/jsx/core/index.mjs +7 -0
  222. package/dist/jsx/core/index.mjs.map +1 -0
  223. package/dist/jsx/core/index.native.js +44 -0
  224. package/dist/jsx/core/index.native.js.map +1 -0
  225. package/dist/jsx/core/message.mjs +63 -0
  226. package/dist/jsx/core/message.mjs.map +1 -0
  227. package/dist/jsx/core/message.native.js +104 -0
  228. package/dist/jsx/core/message.native.js.map +1 -0
  229. package/dist/jsx/core/names.mjs +25 -0
  230. package/dist/jsx/core/names.mjs.map +1 -0
  231. package/dist/jsx/core/names.native.js +95 -0
  232. package/dist/jsx/core/names.native.js.map +1 -0
  233. package/dist/jsx/core/react.mjs +21 -0
  234. package/dist/jsx/core/react.mjs.map +1 -0
  235. package/dist/jsx/core/react.native.js +52 -0
  236. package/dist/jsx/core/react.native.js.map +1 -0
  237. package/dist/jsx/core/store.mjs +424 -0
  238. package/dist/jsx/core/store.mjs.map +1 -0
  239. package/dist/jsx/core/store.native.js +637 -0
  240. package/dist/jsx/core/store.native.js.map +1 -0
  241. package/dist/jsx/core/types.mjs +2 -0
  242. package/dist/jsx/core/types.mjs.map +1 -0
  243. package/dist/jsx/core/types.native.js +21 -0
  244. package/dist/jsx/core/types.native.js.map +1 -0
  245. package/dist/jsx/index.js +3 -2
  246. package/dist/jsx/index.js.map +1 -1
  247. package/dist/jsx/index.mjs +3 -2
  248. package/dist/jsx/index.mjs.map +1 -1
  249. package/dist/jsx/index.native.js +2 -0
  250. package/dist/jsx/index.native.js.map +1 -1
  251. package/dist/jsx/providers/WalletProvider.mjs +2 -1
  252. package/dist/jsx/providers/WalletProvider.mjs.map +1 -1
  253. package/dist/jsx/providers/WalletProvider.native.js +123 -90
  254. package/dist/jsx/providers/WalletProvider.native.js.map +1 -1
  255. package/dist/jsx/providers/index.mjs +2 -2
  256. package/dist/jsx/providers/index.mjs.map +1 -1
  257. package/dist/jsx/providers/index.native.js +1 -0
  258. package/dist/jsx/providers/index.native.js.map +1 -1
  259. package/dist/jsx/providers/native/deepLink.mjs +36 -24
  260. package/dist/jsx/providers/native/deepLink.mjs.map +1 -1
  261. package/dist/jsx/providers/native/deepLink.native.js +43 -25
  262. package/dist/jsx/providers/native/deepLink.native.js.map +1 -1
  263. package/dist/jsx/providers/native/inject.mjs +1 -3
  264. package/dist/jsx/providers/native/inject.mjs.map +1 -1
  265. package/dist/jsx/providers/native/inject.native.js +2 -4
  266. package/dist/jsx/providers/native/inject.native.js.map +1 -1
  267. package/dist/jsx/providers/walletBridge.mjs +9 -0
  268. package/dist/jsx/providers/walletBridge.mjs.map +1 -0
  269. package/dist/jsx/providers/walletBridge.native.js +38 -0
  270. package/dist/jsx/providers/walletBridge.native.js.map +1 -0
  271. package/dist/jsx/providers/walletConfig.mjs.map +1 -1
  272. package/dist/jsx/providers/walletConfig.native.js.map +1 -1
  273. package/docs/bridge-protocol.md +485 -0
  274. package/package.json +14 -6
  275. package/src/bridge/AuthBridge.ts +146 -0
  276. package/src/bridge/BridgeClient.spec.ts +12 -0
  277. package/src/bridge/BridgeClient.ts +15 -10
  278. package/src/bridge/BridgeHost.spec.ts +20 -4
  279. package/src/bridge/BridgeHost.ts +16 -5
  280. package/src/bridge/GpgBridge.ts +49 -0
  281. package/src/bridge/SuiBridge.ts +34 -0
  282. package/src/bridge/authTypes.ts +76 -0
  283. package/src/bridge/core.ts +42 -0
  284. package/src/bridge/hosting.spec.ts +98 -0
  285. package/src/bridge/hosting.ts +99 -0
  286. package/src/bridge/index.ts +1 -8
  287. package/src/bridge/nativeHost.spec.ts +165 -0
  288. package/src/bridge/nativeHost.ts +120 -0
  289. package/src/bridge/protocol.spec.ts +24 -1
  290. package/src/bridge/protocol.ts +44 -1
  291. package/src/core/family.spec.ts +117 -0
  292. package/src/core/family.ts +103 -0
  293. package/src/core/imports.spec.ts +82 -0
  294. package/src/core/index.ts +47 -0
  295. package/src/core/message.spec.ts +178 -0
  296. package/src/core/message.ts +119 -0
  297. package/src/core/names.ts +29 -0
  298. package/src/core/react.spec.tsx +75 -0
  299. package/src/core/react.ts +34 -0
  300. package/src/core/store.spec.ts +890 -0
  301. package/src/core/store.ts +558 -0
  302. package/src/core/types.ts +96 -0
  303. package/src/index.ts +6 -0
  304. package/src/providers/WalletProvider.native.tsx +139 -85
  305. package/src/providers/WalletProvider.tsx +1 -1
  306. package/src/providers/index.ts +2 -0
  307. package/src/providers/native/deepLink.spec.ts +73 -1
  308. package/src/providers/native/deepLink.ts +76 -28
  309. package/src/providers/native/inject.ts +1 -9
  310. package/src/providers/walletBridge.ts +24 -0
  311. package/src/providers/walletConfig.ts +15 -1
  312. package/types/bridge/AuthBridge.d.ts +58 -0
  313. package/types/bridge/AuthBridge.d.ts.map +1 -0
  314. package/types/bridge/BridgeClient.d.ts +3 -1
  315. package/types/bridge/BridgeClient.d.ts.map +1 -1
  316. package/types/bridge/BridgeHost.d.ts +1 -1
  317. package/types/bridge/BridgeHost.d.ts.map +1 -1
  318. package/types/bridge/GpgBridge.d.ts +23 -0
  319. package/types/bridge/GpgBridge.d.ts.map +1 -0
  320. package/types/bridge/SuiBridge.d.ts +18 -0
  321. package/types/bridge/SuiBridge.d.ts.map +1 -0
  322. package/types/bridge/authTypes.d.ts +71 -0
  323. package/types/bridge/authTypes.d.ts.map +1 -0
  324. package/types/bridge/core.d.ts +16 -0
  325. package/types/bridge/core.d.ts.map +1 -0
  326. package/types/bridge/hosting.d.ts +34 -0
  327. package/types/bridge/hosting.d.ts.map +1 -0
  328. package/types/bridge/index.d.ts +1 -8
  329. package/types/bridge/index.d.ts.map +1 -1
  330. package/types/bridge/nativeHost.d.ts +55 -0
  331. package/types/bridge/nativeHost.d.ts.map +1 -0
  332. package/types/bridge/protocol.d.ts +34 -1
  333. package/types/bridge/protocol.d.ts.map +1 -1
  334. package/types/core/family.d.ts +44 -0
  335. package/types/core/family.d.ts.map +1 -0
  336. package/types/core/index.d.ts +7 -0
  337. package/types/core/index.d.ts.map +1 -0
  338. package/types/core/message.d.ts +44 -0
  339. package/types/core/message.d.ts.map +1 -0
  340. package/types/core/names.d.ts +7 -0
  341. package/types/core/names.d.ts.map +1 -0
  342. package/types/core/react.d.ts +14 -0
  343. package/types/core/react.d.ts.map +1 -0
  344. package/types/core/store.d.ts +64 -0
  345. package/types/core/store.d.ts.map +1 -0
  346. package/types/core/types.d.ts +88 -0
  347. package/types/core/types.d.ts.map +1 -0
  348. package/types/index.d.ts +2 -1
  349. package/types/index.d.ts.map +1 -1
  350. package/types/providers/WalletProvider.d.ts +1 -0
  351. package/types/providers/WalletProvider.d.ts.map +1 -1
  352. package/types/providers/WalletProvider.native.d.ts +2 -1
  353. package/types/providers/WalletProvider.native.d.ts.map +1 -1
  354. package/types/providers/index.d.ts +1 -1
  355. package/types/providers/index.d.ts.map +1 -1
  356. package/types/providers/native/deepLink.d.ts +29 -4
  357. package/types/providers/native/deepLink.d.ts.map +1 -1
  358. package/types/providers/native/inject.d.ts +1 -7
  359. package/types/providers/native/inject.d.ts.map +1 -1
  360. package/types/providers/walletBridge.d.ts +17 -0
  361. package/types/providers/walletBridge.d.ts.map +1 -0
  362. package/types/providers/walletConfig.d.ts +18 -1
  363. package/types/providers/walletConfig.d.ts.map +1 -1
@@ -4,25 +4,36 @@ import { createContext, useContext, useEffect, useMemo, useReducer, useRef, useS
4
4
  import { AppState, Linking, StyleSheet, View } from "react-native";
5
5
  import { WebView, type WebViewMessageEvent, type WebViewProps } from "react-native-webview";
6
6
  import { http, WagmiProvider, createConfig } from "wagmi";
7
+ import { AuthBridge } from "../bridge/AuthBridge";
7
8
  import { BridgeClient } from "../bridge/BridgeClient";
9
+ import { GpgBridge } from "../bridge/GpgBridge";
8
10
  import { SolanaBridge } from "../bridge/SolanaBridge";
11
+ import { SuiBridge } from "../bridge/SuiBridge";
9
12
  import { createBridgeConnector } from "../bridge/bridgeConnector";
10
- import { createPostMessageTransport } from "../bridge/postMessageTransport";
11
- import { parseWalletReturn, shouldOpenExternally } from "./native/deepLink";
12
- import { buildConfigInjection, buildInjectScript } from "./native/inject";
13
+ import { queryParams } from "../bridge/hosting";
14
+ import { createNativeBridge } from "../bridge/nativeHost";
15
+ import { createMemoryTransportPair } from "../bridge/transport";
16
+ import {
17
+ decideNavigation,
18
+ decideNewWindow,
19
+ isUnderUrl,
20
+ parseWalletReturn,
21
+ } from "./native/deepLink";
22
+ import { buildConfigInjection } from "./native/inject";
13
23
  import { initialVisibility, nextVisibility } from "./native/visibility";
24
+ import { WalletBridgeContext, type WalletBridgeContextValue } from "./walletBridge";
14
25
  import { type Web3Config, Web3ConfigContext, resolveChains } from "./walletConfig";
15
26
 
16
27
  type WebViewSource = NonNullable<WebViewProps["source"]>;
17
28
 
18
- // The wallet WebView must be loaded over a URL. WKWebView won't render the ~7MB
19
- // bundle inline via source={{html}} (size limit + it breaks the host render), so
20
- // there is no inline fallback: callers pass `webViewSource` or set `config.webViewUrl`
21
- // to a served copy of assets/webview.html.
29
+ // The wallet WebView loads a HOSTED page (config.webViewUrl, https): the
30
+ // sign-in message's domain and the Keycloak nonce cookie belong to the page's
31
+ // host, so a file:// or inline page can never sign in, and WKWebView will not
32
+ // render the multi-MB bundle inline anyway.
22
33
  export interface WalletProviderProps extends PropsWithChildren {
23
34
  config: Web3Config;
24
35
  queryClient?: QueryClient;
25
- /** Override the WebView content (defaults to `config.webViewUrl`). */
36
+ /** Override the WebView content (defaults to `config.webViewUrl`); must be a hosted `{ uri }`. */
26
37
  webViewSource?: WebViewSource;
27
38
  }
28
39
 
@@ -43,6 +54,17 @@ export function useSolanaWallet(): SolanaWalletContextValue {
43
54
 
44
55
  const defaultQueryClient = new QueryClient();
45
56
 
57
+ function pageUrlOf(config: Web3Config, source: WebViewSource | undefined): string | undefined {
58
+ if (!source) return config.webViewUrl || undefined;
59
+ const uri = (source as { uri?: unknown }).uri;
60
+ if (typeof uri !== "string") {
61
+ throw new Error(
62
+ "WalletProvider: webViewSource must be a hosted { uri }; an inline page has no host to sign in for",
63
+ );
64
+ }
65
+ return uri;
66
+ }
67
+
46
68
  export function WalletProvider({
47
69
  children,
48
70
  config: web3Config,
@@ -50,68 +72,91 @@ export function WalletProvider({
50
72
  webViewSource,
51
73
  }: WalletProviderProps) {
52
74
  const webRef = useRef<WebView>(null);
53
- const readyRef = useRef(false);
54
- const outboundQueue = useRef<string[]>([]);
55
-
75
+ const [pageLoad, setPageLoad] = useState(0);
56
76
  const [visibility, dispatchVisibility] = useReducer(nextVisibility, initialVisibility);
57
77
  const [publicKey, setPublicKey] = useState<string | null>(null);
58
78
 
59
- // One bridge for the WebView's lifetime. Outbound messages are queued until the
60
- // WebView signals `sys/ready`, then flushed (the connect request often fires first).
61
- const { bridge, solana, transport } = useMemo(() => {
62
- const t = createPostMessageTransport((raw) => {
63
- if (readyRef.current && webRef.current)
64
- webRef.current.injectJavaScript(buildInjectScript(raw));
65
- else outboundQueue.current.push(raw);
66
- });
67
- const client = new BridgeClient(t);
68
- return { bridge: client, solana: new SolanaBridge(client), transport: t };
69
- }, []);
79
+ // No guessed default: mounting the WebView against an unreachable URL (e.g.
80
+ // http://localhost on a physical device, where localhost is the phone) fires
81
+ // NSURLErrorDomain -1004 at app startup. With no URL the providers still
82
+ // mount and connect flows stay inert until WEB3_WEBVIEW_URL is set.
83
+ const pageUrl = pageUrlOf(web3Config, webViewSource);
84
+
85
+ // Throws on a page that is not hosted, which is the point.
86
+ const host = useMemo(
87
+ () =>
88
+ pageUrl
89
+ ? createNativeBridge({
90
+ pageUrl,
91
+ allowDevHosts: typeof __DEV__ !== "undefined" && __DEV__,
92
+ inject: (script) => webRef.current?.injectJavaScript(script),
93
+ })
94
+ : null,
95
+ [pageUrl],
96
+ );
97
+
98
+ const authDefaults = JSON.stringify(web3Config.auth ?? null);
99
+ const bridge = useMemo<WalletBridgeContextValue>(() => {
100
+ const client = host?.client ?? new BridgeClient(createMemoryTransportPair().a);
101
+ return {
102
+ client,
103
+ solana: new SolanaBridge(client),
104
+ sui: new SuiBridge(client),
105
+ gpg: new GpgBridge(client),
106
+ auth: new AuthBridge(client, {
107
+ openUrl: (url) => Linking.openURL(url),
108
+ defaults: JSON.parse(authDefaults) ?? undefined,
109
+ }),
110
+ };
111
+ }, [host, authDefaults]);
112
+ const { client } = bridge;
70
113
 
71
114
  // React to the WebView's lifecycle + account events.
72
115
  useEffect(() => {
73
- const off = bridge.on((ns, method, params) => {
74
- if (ns === "sys") {
75
- if (method === "ready") {
76
- readyRef.current = true;
77
- const queued = outboundQueue.current.splice(0);
78
- for (const raw of queued) webRef.current?.injectJavaScript(buildInjectScript(raw));
79
- } else if (method === "modalOpen" || method === "modalClose") {
80
- dispatchVisibility(method);
81
- }
116
+ const off = client.on((ns, method, params) => {
117
+ if (ns === "sys" && (method === "modalOpen" || method === "modalClose")) {
118
+ dispatchVisibility(method);
82
119
  } else if (ns === "solana" && method === "accountChanged") {
83
120
  setPublicKey((params as { publicKey: string | null } | undefined)?.publicKey ?? null);
84
121
  }
85
122
  });
86
123
  return off;
87
- }, [bridge]);
124
+ }, [client]);
88
125
 
89
126
  // On app foreground (e.g. return from a wallet deep link) ask the WebView to
90
127
  // re-report account/chain state.
91
128
  useEffect(() => {
92
129
  const sub = AppState.addEventListener("change", (state) => {
93
- if (state === "active" && readyRef.current) {
94
- void bridge.request("sys", "resync").catch(() => undefined);
130
+ if (state === "active" && host?.peer) {
131
+ void client.request("sys", "resync").catch(() => undefined);
95
132
  }
96
133
  });
97
134
  return () => sub.remove();
98
- }, [bridge]);
135
+ }, [client, host]);
99
136
 
100
137
  // Inbound app-link returns from a wallet app: forward the params into the
101
138
  // WebView (the mock-wallet path consumes address/signature; a real wallet
102
- // just needs the resync that AppState already triggers).
139
+ // just needs the resync that AppState already triggers). Only links under
140
+ // walletReturnUrl reach the page, and never Keycloak's onboarding return,
141
+ // whose code belongs to native.
142
+ const walletReturnUrl = web3Config.walletReturnUrl;
143
+ const onboardingReturnUrl = web3Config.auth?.onboarding?.redirectUri;
103
144
  useEffect(() => {
145
+ if (!walletReturnUrl) return;
104
146
  const handle = (url: string | null) => {
105
- if (!url) return;
147
+ if (!url || !isUnderUrl(url, walletReturnUrl)) return;
148
+ if (onboardingReturnUrl && isUnderUrl(url, onboardingReturnUrl)) return;
149
+ const onboarding = bridge.auth.pendingOnboarding;
150
+ if (onboarding && queryParams(url).state === onboarding.state) return;
106
151
  const params = parseWalletReturn(url);
107
- if (params) void bridge.request("sys", "deepLinkReturn", params).catch(() => undefined);
152
+ if (params) void client.request("sys", "deepLinkReturn", params).catch(() => undefined);
108
153
  };
109
154
  const sub = Linking.addEventListener("url", (event) => handle(event.url));
110
155
  void Linking.getInitialURL()
111
156
  .then(handle)
112
157
  .catch(() => undefined);
113
158
  return () => sub.remove();
114
- }, [bridge]);
159
+ }, [bridge, client, walletReturnUrl, onboardingReturnUrl]);
115
160
 
116
161
  const wagmiConfig = useMemo(() => {
117
162
  // Same chain resolution as web (testnet filter + optional localnet dev
@@ -122,12 +167,12 @@ export function WalletProvider({
122
167
  return createConfig({
123
168
  chains,
124
169
  transports,
125
- connectors: [createBridgeConnector(bridge)],
170
+ connectors: [createBridgeConnector(client)],
126
171
  multiInjectedProviderDiscovery: false,
127
172
  });
128
173
  // eslint-disable-next-line react-hooks/exhaustive-deps
129
174
  }, [
130
- bridge,
175
+ client,
131
176
  web3Config.chains,
132
177
  web3Config.enableTestnets,
133
178
  web3Config.localnet,
@@ -135,58 +180,66 @@ export function WalletProvider({
135
180
  ]);
136
181
 
137
182
  const solanaValue = useMemo<SolanaWalletContextValue>(
138
- () => ({ bridge: solana, publicKey, connected: publicKey !== null }),
139
- [solana, publicKey],
183
+ () => ({ bridge: bridge.solana, publicKey, connected: publicKey !== null }),
184
+ [bridge.solana, publicKey],
140
185
  );
141
186
 
142
- const onMessage = (event: WebViewMessageEvent) => transport.receive(event.nativeEvent.data);
187
+ const onMessage = (event: WebViewMessageEvent) =>
188
+ host?.receive(event.nativeEvent.data, event.nativeEvent.url);
143
189
 
144
- // No inline HTML fallback: the bundle is too large for WKWebView source={{html}}.
145
- // And no guessed-URL fallback either: mounting the WebView against an
146
- // unreachable default (e.g. http://localhost:8099 on a physical device, where
147
- // localhost is the phone) fires NSURLErrorDomain -1004 at app startup. When
148
- // neither `webViewSource` nor `config.webViewUrl` resolves, skip the WebView
149
- // entirely — providers still mount so wagmi/bridge consumers render; connect
150
- // flows stay inert until WEB3_WEBVIEW_URL points at a served copy of
151
- // @multiplatform.one/web3/assets/webview.html.
152
- const resolvedSource =
153
- webViewSource ?? (web3Config.webViewUrl ? { uri: web3Config.webViewUrl } : undefined);
190
+ const openExternally = (url: string) => void Linking.openURL(url).catch(() => undefined);
154
191
 
155
192
  return (
156
193
  <Web3ConfigContext.Provider value={web3Config}>
157
194
  <WagmiProvider config={wagmiConfig}>
158
195
  <QueryClientProvider client={queryClient}>
159
- <SolanaWalletContext.Provider value={solanaValue}>
160
- {children}
161
- {resolvedSource && (
162
- <View
163
- style={visibility.sheetOpen ? styles.visible : styles.hidden}
164
- pointerEvents={visibility.sheetOpen ? "auto" : "none"}
165
- >
166
- <WebView
167
- ref={webRef}
168
- source={resolvedSource}
169
- originWhitelist={["*"]}
170
- javaScriptEnabled
171
- domStorageEnabled
172
- onMessage={onMessage}
173
- onShouldStartLoadWithRequest={(req) => {
174
- // Hand wallet deep links to the OS; keep everything else in the WebView.
175
- if (shouldOpenExternally(req.url)) {
176
- void Linking.openURL(req.url).catch(() => undefined);
177
- return false;
178
- }
179
- return true;
180
- }}
181
- injectedJavaScriptBeforeContentLoaded={buildConfigInjection({
182
- ...web3Config,
183
- appUrl: web3Config.appUrl ?? "",
184
- })}
185
- style={styles.webview}
186
- />
187
- </View>
188
- )}
189
- </SolanaWalletContext.Provider>
196
+ <WalletBridgeContext.Provider value={bridge}>
197
+ <SolanaWalletContext.Provider value={solanaValue}>
198
+ {children}
199
+ {host && pageUrl && (
200
+ <View
201
+ style={visibility.sheetOpen ? styles.visible : styles.hidden}
202
+ pointerEvents={visibility.sheetOpen ? "auto" : "none"}
203
+ >
204
+ <WebView
205
+ key={pageLoad}
206
+ ref={webRef}
207
+ source={webViewSource ?? { uri: pageUrl }}
208
+ originWhitelist={["*"]}
209
+ javaScriptEnabled
210
+ domStorageEnabled
211
+ javaScriptCanOpenWindowsAutomatically
212
+ onMessage={onMessage}
213
+ onLoadStart={(event) => {
214
+ if (host.navigated(event.nativeEvent.url)) setPageLoad((n) => n + 1);
215
+ }}
216
+ onShouldStartLoadWithRequest={(req) => {
217
+ const decision = decideNavigation(req.url, host.pageOrigin, req.isTopFrame);
218
+ if (decision === "external") openExternally(req.url);
219
+ return decision === "load";
220
+ }}
221
+ onOpenWindow={(event) => {
222
+ const url = event.nativeEvent.targetUrl;
223
+ if (decideNewWindow(url) === "external") openExternally(url);
224
+ }}
225
+ onContentProcessDidTerminate={() => {
226
+ host.pageGone();
227
+ webRef.current?.reload();
228
+ }}
229
+ onRenderProcessGone={() => {
230
+ host.pageGone();
231
+ webRef.current?.reload();
232
+ }}
233
+ injectedJavaScriptBeforeContentLoaded={buildConfigInjection({
234
+ ...web3Config,
235
+ appUrl: web3Config.appUrl ?? "",
236
+ })}
237
+ style={styles.webview}
238
+ />
239
+ </View>
240
+ )}
241
+ </SolanaWalletContext.Provider>
242
+ </WalletBridgeContext.Provider>
190
243
  </QueryClientProvider>
191
244
  </WagmiProvider>
192
245
  </Web3ConfigContext.Provider>
@@ -201,5 +254,6 @@ const styles = StyleSheet.create({
201
254
  webview: { flex: 1, backgroundColor: "transparent" },
202
255
  });
203
256
 
257
+ export { useWalletBridge, type WalletBridgeContextValue } from "./walletBridge";
204
258
  export { defaultChains } from "./walletConfig";
205
259
  export { useWeb3Config, type Web3Config } from "./walletConfig";
@@ -3,7 +3,6 @@ import { ConnectKitProvider, SIWEProvider, type SIWEConfig, getDefaultConfig } f
3
3
  import { isWeb } from "@multiplatform.one/platform";
4
4
  import type { PropsWithChildren } from "react";
5
5
  import { useMemo } from "react";
6
- import type { Chain } from "viem";
7
6
  import { WagmiProvider, createConfig, http } from "wagmi";
8
7
  import { createMockConnector } from "./mockConnector";
9
8
  import { type Web3Config, Web3ConfigContext, resolveChains } from "./walletConfig";
@@ -108,5 +107,6 @@ export function WalletProvider({
108
107
  );
109
108
  }
110
109
 
110
+ export { useWalletBridge, type WalletBridgeContextValue } from "./walletBridge";
111
111
  export { defaultChains } from "./walletConfig";
112
112
  export { useWeb3Config, type Web3Config } from "./walletConfig";
@@ -2,6 +2,8 @@ export {
2
2
  WalletProvider,
3
3
  useWeb3Config,
4
4
  defaultChains,
5
+ useWalletBridge,
6
+ type WalletBridgeContextValue,
5
7
  type WalletProviderProps,
6
8
  type Web3Config,
7
9
  } from "./WalletProvider";
@@ -1,17 +1,28 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { parseWalletReturn, shouldOpenExternally } from "./deepLink";
2
+ import {
3
+ decideNavigation,
4
+ decideNewWindow,
5
+ isUnderUrl,
6
+ parseWalletReturn,
7
+ shouldOpenExternally,
8
+ } from "./deepLink";
3
9
 
4
10
  describe("shouldOpenExternally", () => {
5
11
  it("opens wallet custom schemes externally", () => {
6
12
  expect(shouldOpenExternally("metamask://connect/mwp?p=abc")).toBe(true);
7
13
  expect(shouldOpenExternally("phantom://v1/connect?app_url=...")).toBe(true);
8
14
  expect(shouldOpenExternally("ethereum:0xabc")).toBe(true);
15
+ expect(shouldOpenExternally("solflare://ul/v1/connect?app_url=x")).toBe(true);
16
+ expect(shouldOpenExternally("backpack://ul/v1/connect?app_url=x")).toBe(true);
9
17
  });
10
18
 
11
19
  it("opens known wallet universal links externally", () => {
12
20
  expect(shouldOpenExternally("https://metamask.app.link/connect/mwp?p=abc")).toBe(true);
13
21
  expect(shouldOpenExternally("https://link.trustwallet.com/open_url?url=...")).toBe(true);
14
22
  expect(shouldOpenExternally("https://phantom.app/ul/v1/connect")).toBe(true);
23
+ expect(shouldOpenExternally("https://solflare.com/ul/v1/signMessage")).toBe(true);
24
+ expect(shouldOpenExternally("https://backpack.app/ul/v1/connect")).toBe(true);
25
+ expect(shouldOpenExternally("HTTPS://Link.Metamask.App.Link/x")).toBe(true);
15
26
  });
16
27
 
17
28
  it("keeps internal / non-wallet navigations in the WebView", () => {
@@ -23,6 +34,53 @@ describe("shouldOpenExternally", () => {
23
34
  });
24
35
  });
25
36
 
37
+ const page = "https://wallet.example.com";
38
+
39
+ describe("decideNavigation", () => {
40
+ it("keeps the hosted page's own origin in the WebView", () => {
41
+ expect(decideNavigation("https://wallet.example.com/webview.html#x", page)).toBe("load");
42
+ expect(decideNavigation("about:blank", page)).toBe("load");
43
+ });
44
+
45
+ it("hands wallet links to the OS from any frame", () => {
46
+ expect(decideNavigation("metamask://connect/mwp?p=abc", page)).toBe("external");
47
+ expect(decideNavigation("https://metamask.app.link/connect", page, false)).toBe("external");
48
+ expect(decideNavigation("solflare://ul/v1/connect", page)).toBe("external");
49
+ });
50
+
51
+ it("never lets the top frame leave the page's origin", () => {
52
+ expect(decideNavigation("https://evil.example/phish", page)).toBe("external");
53
+ expect(decideNavigation("http://wallet.example.com/webview.html", page)).toBe("external");
54
+ expect(decideNavigation("mailto:help@example.com", page)).toBe("external");
55
+ expect(decideNavigation("javascript:alert(1)", page)).toBe("block");
56
+ expect(decideNavigation("file:///etc/hosts", page)).toBe("block");
57
+ expect(decideNavigation("data:text/html,<p>x</p>", page)).toBe("block");
58
+ expect(decideNavigation("intent://x#Intent;end", page)).toBe("block");
59
+ });
60
+
61
+ it("lets frames load what the page asks for", () => {
62
+ expect(decideNavigation("https://relay.example/frame", page, false)).toBe("load");
63
+ expect(decideNavigation("about:srcdoc", page, false)).toBe("load");
64
+ });
65
+ });
66
+
67
+ describe("decideNewWindow", () => {
68
+ it("opens wallet links and web pages outside the app", () => {
69
+ expect(decideNewWindow("https://metamask.app.link/connect/mwp?p=abc")).toBe("external");
70
+ expect(decideNewWindow("phantom://v1/connect")).toBe("external");
71
+ expect(decideNewWindow("backpack://ul/v1/connect")).toBe("external");
72
+ expect(decideNewWindow("https://solflare.com/ul/v1/connect")).toBe("external");
73
+ expect(decideNewWindow("https://metamask.io/download")).toBe("external");
74
+ });
75
+
76
+ it("drops script and local URLs", () => {
77
+ expect(decideNewWindow("javascript:void(0)")).toBe("block");
78
+ expect(decideNewWindow("about:blank")).toBe("block");
79
+ expect(decideNewWindow("file:///sdcard/x")).toBe("block");
80
+ expect(decideNewWindow("")).toBe("block");
81
+ });
82
+ });
83
+
26
84
  describe("parseWalletReturn", () => {
27
85
  it("extracts query params from a return url", () => {
28
86
  expect(parseWalletReturn("mpoapp://wallet-return?address=0x1&signature=0xab")).toEqual({
@@ -39,3 +97,17 @@ describe("parseWalletReturn", () => {
39
97
  expect(parseWalletReturn("not a url at all %%%")).toBeNull();
40
98
  });
41
99
  });
100
+
101
+ describe("isUnderUrl", () => {
102
+ it("matches the base and what sits below it on a boundary", () => {
103
+ const base = "myapp://wallet-return";
104
+ expect(isUnderUrl("myapp://wallet-return", base)).toBe(true);
105
+ expect(isUnderUrl("myapp://wallet-return?address=0x1", base)).toBe(true);
106
+ expect(isUnderUrl("myapp://wallet-return/done", base)).toBe(true);
107
+ expect(isUnderUrl("myapp://wallet-return#x", base)).toBe(true);
108
+ expect(isUnderUrl("myapp://wallet-returned", base)).toBe(false);
109
+ expect(isUnderUrl("myapp://auth?code=c", base)).toBe(false);
110
+ expect(isUnderUrl("myapp://wallet-return/x", "myapp://wallet-return/")).toBe(true);
111
+ expect(isUnderUrl("myapp://anything", "")).toBe(false);
112
+ });
113
+ });
@@ -1,16 +1,21 @@
1
+ import { parseOrigin, queryParams } from "../../bridge/hosting";
2
+
1
3
  /**
2
- * Wallet hand-off URL classification for the native WebView. The WebView loads
3
- * trusted local content (file://); when its ConnectKit flow tries to
4
- * open a wallet app it navigates to a wallet custom-scheme or universal link —
5
- * those must be handed to the OS via Linking.openURL, not loaded in the WebView.
4
+ * Wallet hand-off for the native WebView. The WebView shows the hosted wallet
5
+ * page; when its flow opens a wallet app it navigates to a wallet custom
6
+ * scheme or universal link, or opens one in a new window. Those go to the OS
7
+ * through Linking.openURL and are never loaded in the WebView.
6
8
  */
7
9
 
8
- const WALLET_SCHEMES = new Set([
10
+ /** Custom schemes that open a wallet app. docs/bridge-protocol.md lists the same. */
11
+ export const WALLET_SCHEMES: readonly string[] = [
9
12
  "ethereum",
10
13
  "metamask",
11
14
  "rainbow",
12
15
  "trust",
13
16
  "phantom",
17
+ "solflare",
18
+ "backpack",
14
19
  "solana",
15
20
  "cbwallet",
16
21
  "ledgerlive",
@@ -19,43 +24,86 @@ const WALLET_SCHEMES = new Set([
19
24
  "okx",
20
25
  "imtokenv2",
21
26
  "wallet",
22
- ]);
27
+ ];
23
28
 
24
- const WALLET_UNIVERSAL_HOSTS = [
29
+ /** Hosts (and subdomains) of wallet universal links. docs/bridge-protocol.md lists the same. */
30
+ export const WALLET_UNIVERSAL_HOSTS: readonly string[] = [
25
31
  "metamask.app.link",
26
32
  "link.trustwallet.com",
27
33
  "phantom.app",
34
+ "solflare.com",
35
+ "backpack.app",
28
36
  "rnbwapp.com",
29
37
  "go.cb-w.com",
30
38
  "ledger.com",
31
39
  ];
32
40
 
41
+ const OS_SCHEMES = new Set(["mailto", "tel", "sms"]);
42
+
33
43
  /** True when a WebView navigation should be opened in an external wallet app. */
34
44
  export function shouldOpenExternally(url: string): boolean {
35
- const scheme = url.slice(0, Math.max(0, url.indexOf(":"))).toLowerCase();
36
- if (!scheme) return false;
37
- if (WALLET_SCHEMES.has(scheme)) return true;
38
- if (scheme === "https" || scheme === "http") {
39
- try {
40
- const host = new URL(url).hostname.toLowerCase();
41
- return WALLET_UNIVERSAL_HOSTS.some((h) => host === h || host.endsWith(`.${h}`));
42
- } catch {
43
- return false;
44
- }
45
+ const parsed = parseOrigin(url);
46
+ if (!parsed) return false;
47
+ if (WALLET_SCHEMES.includes(parsed.scheme)) return true;
48
+ if (parsed.scheme !== "https" && parsed.scheme !== "http") return false;
49
+ return WALLET_UNIVERSAL_HOSTS.some(
50
+ (h) => parsed.hostname === h || parsed.hostname.endsWith(`.${h}`),
51
+ );
52
+ }
53
+
54
+ /** `load` in the WebView, hand to the OS (`external`), or drop (`block`). */
55
+ export type NavigationDecision = "load" | "external" | "block";
56
+
57
+ /**
58
+ * Decide a navigation in the wallet WebView (`onShouldStartLoadWithRequest`).
59
+ * The top frame never leaves the hosted page's origin: the bridge would
60
+ * otherwise hand its messages, tokens included, to whatever page loaded next.
61
+ * Other http(s) pages open in the system browser; frames are the page's own
62
+ * business.
63
+ */
64
+ export function decideNavigation(
65
+ url: string,
66
+ pageOrigin: string,
67
+ isTopFrame = true,
68
+ ): NavigationDecision {
69
+ if (shouldOpenExternally(url)) return "external";
70
+ const parsed = parseOrigin(url);
71
+ if (!parsed) return "block";
72
+ if (!isTopFrame) return "load";
73
+ if (parsed.origin === pageOrigin) return "load";
74
+ if (url.trim().toLowerCase() === "about:blank") return "load";
75
+ if (parsed.scheme === "https" || parsed.scheme === "http" || OS_SCHEMES.has(parsed.scheme)) {
76
+ return "external";
77
+ }
78
+ return "block";
79
+ }
80
+
81
+ /**
82
+ * Decide a `window.open` / `target=_blank` navigation (`onOpenWindow`). A new
83
+ * window never opens inside the app: wallet links and web pages go to the OS.
84
+ */
85
+ export function decideNewWindow(url: string): NavigationDecision {
86
+ if (shouldOpenExternally(url)) return "external";
87
+ const parsed = parseOrigin(url);
88
+ if (!parsed) return "block";
89
+ if (parsed.scheme === "https" || parsed.scheme === "http" || OS_SCHEMES.has(parsed.scheme)) {
90
+ return "external";
45
91
  }
46
- return false;
92
+ return "block";
47
93
  }
48
94
 
49
95
  /** Parse the query params of an inbound app-link return (e.g. from a wallet redirect). */
50
96
  export function parseWalletReturn(url: string): Record<string, string> | null {
51
- try {
52
- const parsed = new URL(url);
53
- const out: Record<string, string> = {};
54
- parsed.searchParams.forEach((value, key) => {
55
- out[key] = value;
56
- });
57
- return out;
58
- } catch {
59
- return null;
60
- }
97
+ return parseOrigin(url) ? queryParams(url) : null;
98
+ }
99
+
100
+ /**
101
+ * True when `url` is `base` or below it: `myapp://wallet-return?x=1` and
102
+ * `myapp://wallet-return/done` are under `myapp://wallet-return`,
103
+ * `myapp://wallet-returned` is not.
104
+ */
105
+ export function isUnderUrl(url: string, base: string): boolean {
106
+ if (!base || !url.startsWith(base)) return false;
107
+ if (url.length === base.length || /[/?#]$/.test(base)) return true;
108
+ return "/?#".includes(url[base.length]);
61
109
  }
@@ -1,14 +1,6 @@
1
1
  import type { Web3Config } from "../walletConfig";
2
2
 
3
- /**
4
- * Build the JS the native side runs inside the WebView (via `injectJavaScript`)
5
- * to deliver one inbound bridge message. The raw string is embedded with
6
- * JSON.stringify so quotes/newlines/backslashes survive intact. The trailing
7
- * `true;` is required by react-native-webview's injectJavaScript contract.
8
- */
9
- export function buildInjectScript(raw: string): string {
10
- return `window.__bridgeReceive && window.__bridgeReceive(${JSON.stringify(raw)});true;`;
11
- }
3
+ export { buildInjectScript } from "../../bridge/nativeHost";
12
4
 
13
5
  /**
14
6
  * Build the JS injected *before* the WebView content loads
@@ -0,0 +1,24 @@
1
+ import { createContext, useContext } from "react";
2
+ import type { AuthBridge } from "../bridge/AuthBridge";
3
+ import type { BridgeClient } from "../bridge/BridgeClient";
4
+ import type { GpgBridge } from "../bridge/GpgBridge";
5
+ import type { SolanaBridge } from "../bridge/SolanaBridge";
6
+ import type { SuiBridge } from "../bridge/SuiBridge";
7
+
8
+ /** Every bridge namespace of the native WalletProvider's hosted wallet page. */
9
+ export interface WalletBridgeContextValue {
10
+ client: BridgeClient;
11
+ solana: SolanaBridge;
12
+ sui: SuiBridge;
13
+ gpg: GpgBridge;
14
+ auth: AuthBridge;
15
+ }
16
+
17
+ export const WalletBridgeContext = createContext<WalletBridgeContextValue | null>(null);
18
+
19
+ /** Native only: on web the wallet runs in the page itself and there is no bridge. */
20
+ export function useWalletBridge(): WalletBridgeContextValue {
21
+ const value = useContext(WalletBridgeContext);
22
+ if (!value) throw new Error("useWalletBridge must be used within a native WalletProvider");
23
+ return value;
24
+ }
@@ -1,6 +1,7 @@
1
1
  import { createContext, useContext } from "react";
2
2
  import type { Chain } from "viem";
3
3
  import { arbitrum, base, hardhat, mainnet, optimism, polygon, sepolia } from "wagmi/chains";
4
+ import type { AuthKeycloakConfig, AuthOnboardingConfig } from "../bridge/authTypes";
4
5
 
5
6
  /**
6
7
  * Shared config seam for both platform variants of WalletProvider. Keeping the
@@ -43,8 +44,21 @@ export interface Web3Config {
43
44
  suiRpcUrl?: string;
44
45
  /** E2E only (native): route connect through the in-WebView mock wallet. */
45
46
  mockWallet?: boolean;
46
- /** Native: URL serving the wallet WebView HTML (preferred over the inline string). */
47
+ /**
48
+ * Native: https URL serving `assets/webview.html`. Its host is the sign-in
49
+ * message's domain, so it must be in the realm's `siww.allowedDomains` /
50
+ * `wallet.allowedDomains` and in the client's web origins. Plain http is
51
+ * accepted only on loopback and 10.0.2.2, in development builds.
52
+ */
47
53
  webViewUrl?: string;
54
+ /**
55
+ * Native: the app link wallets send the user back to, e.g.
56
+ * `myapp://wallet-return`. Returns under it reach the page as
57
+ * `sys.deepLinkReturn`; no other app link does.
58
+ */
59
+ walletReturnUrl?: string;
60
+ /** Native: the Keycloak client `useWalletBridge().auth` signs in with when a call names none. */
61
+ auth?: { keycloak?: AuthKeycloakConfig; onboarding?: AuthOnboardingConfig };
48
62
  }
49
63
 
50
64
  /**