@arkade-os/sdk 0.4.26 → 0.4.28

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 (522) hide show
  1. package/README.md +36 -125
  2. package/dist/adapters/asyncStorage.cjs +48 -0
  3. package/dist/adapters/asyncStorage.cjs.map +1 -0
  4. package/dist/adapters/asyncStorage.d.cts +16 -0
  5. package/dist/{types/storage → adapters}/asyncStorage.d.ts +5 -2
  6. package/dist/adapters/asyncStorage.js +46 -0
  7. package/dist/adapters/asyncStorage.js.map +1 -0
  8. package/dist/adapters/expo.cjs +19 -0
  9. package/dist/adapters/expo.cjs.map +1 -0
  10. package/dist/adapters/expo.d.cts +48 -0
  11. package/dist/adapters/expo.d.ts +48 -0
  12. package/dist/adapters/expo.js +6 -0
  13. package/dist/adapters/expo.js.map +1 -0
  14. package/dist/adapters/fileSystem.cjs +116 -0
  15. package/dist/adapters/fileSystem.cjs.map +1 -0
  16. package/dist/adapters/fileSystem.d.cts +17 -0
  17. package/dist/{types/storage → adapters}/fileSystem.d.ts +5 -2
  18. package/dist/adapters/fileSystem.js +93 -0
  19. package/dist/adapters/fileSystem.js.map +1 -0
  20. package/dist/adapters/indexedDB.cjs +103 -0
  21. package/dist/adapters/indexedDB.cjs.map +1 -0
  22. package/dist/adapters/indexedDB.d.cts +18 -0
  23. package/dist/{types/storage → adapters}/indexedDB.d.ts +5 -2
  24. package/dist/adapters/indexedDB.js +101 -0
  25. package/dist/adapters/indexedDB.js.map +1 -0
  26. package/dist/adapters/localStorage.cjs +50 -0
  27. package/dist/adapters/localStorage.cjs.map +1 -0
  28. package/dist/{types/storage/inMemory.d.ts → adapters/localStorage.d.cts} +6 -3
  29. package/dist/{types/storage → adapters}/localStorage.d.ts +5 -2
  30. package/dist/adapters/localStorage.js +48 -0
  31. package/dist/adapters/localStorage.js.map +1 -0
  32. package/dist/ark-TZ1gXAXU.d.cts +3880 -0
  33. package/dist/ark-TZ1gXAXU.d.ts +3880 -0
  34. package/dist/{types/worker/expo/asyncStorageTaskQueue.d.ts → asyncStorageTaskQueue-Cb1F_Z9s.d.ts} +6 -3
  35. package/dist/asyncStorageTaskQueue-EFqSmYTg.d.cts +49 -0
  36. package/dist/chunk-5BLDMQED.cjs +18 -0
  37. package/dist/chunk-5BLDMQED.cjs.map +1 -0
  38. package/dist/chunk-5PG7DV7A.cjs +805 -0
  39. package/dist/chunk-5PG7DV7A.cjs.map +1 -0
  40. package/dist/chunk-A3EMF7RN.js +95 -0
  41. package/dist/chunk-A3EMF7RN.js.map +1 -0
  42. package/dist/chunk-ADV27S4N.cjs +2701 -0
  43. package/dist/chunk-ADV27S4N.cjs.map +1 -0
  44. package/dist/chunk-BQLHADL7.js +13805 -0
  45. package/dist/chunk-BQLHADL7.js.map +1 -0
  46. package/dist/chunk-CFZMTDWI.js +209 -0
  47. package/dist/chunk-CFZMTDWI.js.map +1 -0
  48. package/dist/chunk-FG5ACJJW.cjs +212 -0
  49. package/dist/chunk-FG5ACJJW.cjs.map +1 -0
  50. package/dist/chunk-HW3JJ323.js +768 -0
  51. package/dist/chunk-HW3JJ323.js.map +1 -0
  52. package/dist/chunk-I3DGUUCT.cjs +838 -0
  53. package/dist/chunk-I3DGUUCT.cjs.map +1 -0
  54. package/dist/chunk-IPX2R7FR.cjs +100 -0
  55. package/dist/chunk-IPX2R7FR.cjs.map +1 -0
  56. package/dist/chunk-NSBPE2FW.js +15 -0
  57. package/dist/chunk-NSBPE2FW.js.map +1 -0
  58. package/dist/chunk-T64LAI7L.js +829 -0
  59. package/dist/chunk-T64LAI7L.js.map +1 -0
  60. package/dist/chunk-ZBUDLTBO.js +2671 -0
  61. package/dist/chunk-ZBUDLTBO.js.map +1 -0
  62. package/dist/chunk-ZLO6NETT.cjs +13910 -0
  63. package/dist/chunk-ZLO6NETT.cjs.map +1 -0
  64. package/dist/contracts/handlers/index.cjs +26 -0
  65. package/dist/contracts/handlers/index.cjs.map +1 -0
  66. package/dist/contracts/handlers/index.d.cts +7 -0
  67. package/dist/contracts/handlers/index.d.ts +7 -0
  68. package/dist/contracts/handlers/index.js +5 -0
  69. package/dist/contracts/handlers/index.js.map +1 -0
  70. package/dist/delegate-BFZs69hp.d.cts +84 -0
  71. package/dist/delegate-aaVGfWsV.d.ts +84 -0
  72. package/dist/index-B22cA64m.d.cts +199 -0
  73. package/dist/{types/storage/index.d.ts → index-C0IanN1m.d.cts} +3 -1
  74. package/dist/index-C0IanN1m.d.ts +11 -0
  75. package/dist/index-NDla_UoJ.d.ts +199 -0
  76. package/dist/index.cjs +480 -0
  77. package/dist/index.cjs.map +1 -0
  78. package/dist/index.d.cts +3343 -0
  79. package/dist/index.d.ts +3343 -0
  80. package/dist/index.js +7 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/repositories/realm/index.cjs +513 -0
  83. package/dist/repositories/realm/index.cjs.map +1 -0
  84. package/dist/repositories/realm/index.d.cts +217 -0
  85. package/dist/{types/repositories/realm/schemas.d.ts → repositories/realm/index.d.ts} +80 -112
  86. package/dist/repositories/realm/index.js +507 -0
  87. package/dist/repositories/realm/index.js.map +1 -0
  88. package/dist/repositories/sqlite/index.cjs +588 -0
  89. package/dist/repositories/sqlite/index.cjs.map +1 -0
  90. package/dist/repositories/sqlite/index.d.cts +118 -0
  91. package/dist/{types/repositories/sqlite/walletRepository.d.ts → repositories/sqlite/index.d.ts} +58 -5
  92. package/dist/repositories/sqlite/index.js +585 -0
  93. package/dist/repositories/sqlite/index.js.map +1 -0
  94. package/dist/taskRunner-C6Ff4OaU.d.cts +114 -0
  95. package/dist/taskRunner-yvPN8Z0K.d.ts +114 -0
  96. package/dist/wallet/expo/background.cjs +93 -0
  97. package/dist/wallet/expo/background.cjs.map +1 -0
  98. package/dist/wallet/expo/background.d.cts +84 -0
  99. package/dist/wallet/expo/background.d.ts +84 -0
  100. package/dist/wallet/expo/background.js +68 -0
  101. package/dist/wallet/expo/background.js.map +1 -0
  102. package/dist/wallet/expo/index.cjs +171 -0
  103. package/dist/wallet/expo/index.cjs.map +1 -0
  104. package/dist/wallet/expo/index.d.cts +122 -0
  105. package/dist/{types/wallet/expo/wallet.d.ts → wallet/expo/index.d.ts} +45 -22
  106. package/dist/wallet/expo/index.js +169 -0
  107. package/dist/wallet/expo/index.js.map +1 -0
  108. package/dist/wallet-AF-p-OWj.d.cts +774 -0
  109. package/dist/wallet-D9NBRqvC.d.ts +774 -0
  110. package/dist/worker/expo/index.cjs +140 -0
  111. package/dist/worker/expo/index.cjs.map +1 -0
  112. package/dist/worker/expo/index.d.cts +29 -0
  113. package/dist/worker/expo/index.d.ts +29 -0
  114. package/dist/worker/expo/index.js +121 -0
  115. package/dist/worker/expo/index.js.map +1 -0
  116. package/package.json +110 -76
  117. package/dist/cjs/adapters/asyncStorage.js +0 -5
  118. package/dist/cjs/adapters/expo.js +0 -8
  119. package/dist/cjs/adapters/fileSystem.js +0 -5
  120. package/dist/cjs/adapters/indexedDB.js +0 -5
  121. package/dist/cjs/adapters/localStorage.js +0 -5
  122. package/dist/cjs/arkfee/celenv.js +0 -43
  123. package/dist/cjs/arkfee/estimator.js +0 -143
  124. package/dist/cjs/arkfee/index.js +0 -5
  125. package/dist/cjs/arkfee/types.js +0 -26
  126. package/dist/cjs/arknote/index.js +0 -128
  127. package/dist/cjs/bip322/index.js +0 -270
  128. package/dist/cjs/contracts/arkcontract.js +0 -147
  129. package/dist/cjs/contracts/contractManager.js +0 -629
  130. package/dist/cjs/contracts/contractWatcher.js +0 -598
  131. package/dist/cjs/contracts/handlers/default.js +0 -93
  132. package/dist/cjs/contracts/handlers/delegate.js +0 -90
  133. package/dist/cjs/contracts/handlers/helpers.js +0 -115
  134. package/dist/cjs/contracts/handlers/index.js +0 -19
  135. package/dist/cjs/contracts/handlers/registry.js +0 -89
  136. package/dist/cjs/contracts/handlers/vhtlc.js +0 -194
  137. package/dist/cjs/contracts/index.js +0 -41
  138. package/dist/cjs/contracts/types.js +0 -2
  139. package/dist/cjs/contracts/vtxoOwnership.js +0 -78
  140. package/dist/cjs/extension/asset/assetGroup.js +0 -228
  141. package/dist/cjs/extension/asset/assetId.js +0 -152
  142. package/dist/cjs/extension/asset/assetInput.js +0 -222
  143. package/dist/cjs/extension/asset/assetOutput.js +0 -174
  144. package/dist/cjs/extension/asset/assetRef.js +0 -148
  145. package/dist/cjs/extension/asset/index.js +0 -23
  146. package/dist/cjs/extension/asset/metadata.js +0 -187
  147. package/dist/cjs/extension/asset/packet.js +0 -114
  148. package/dist/cjs/extension/asset/types.js +0 -22
  149. package/dist/cjs/extension/asset/utils.js +0 -105
  150. package/dist/cjs/extension/index.js +0 -254
  151. package/dist/cjs/extension/packet.js +0 -20
  152. package/dist/cjs/forfeit.js +0 -45
  153. package/dist/cjs/identity/descriptor.js +0 -169
  154. package/dist/cjs/identity/descriptorProvider.js +0 -2
  155. package/dist/cjs/identity/hdCapableIdentity.js +0 -2
  156. package/dist/cjs/identity/index.js +0 -38
  157. package/dist/cjs/identity/seedIdentity.js +0 -461
  158. package/dist/cjs/identity/serialize.js +0 -171
  159. package/dist/cjs/identity/singleKey.js +0 -126
  160. package/dist/cjs/identity/staticDescriptorProvider.js +0 -65
  161. package/dist/cjs/index.js +0 -200
  162. package/dist/cjs/intent/index.js +0 -259
  163. package/dist/cjs/musig2/index.js +0 -11
  164. package/dist/cjs/musig2/keys.js +0 -57
  165. package/dist/cjs/musig2/nonces.js +0 -48
  166. package/dist/cjs/musig2/sign.js +0 -102
  167. package/dist/cjs/networks.js +0 -26
  168. package/dist/cjs/package.json +0 -3
  169. package/dist/cjs/providers/ark.js +0 -577
  170. package/dist/cjs/providers/delegator.js +0 -85
  171. package/dist/cjs/providers/electrum.js +0 -869
  172. package/dist/cjs/providers/errors.js +0 -59
  173. package/dist/cjs/providers/expoArk.js +0 -82
  174. package/dist/cjs/providers/expoIndexer.js +0 -111
  175. package/dist/cjs/providers/expoUtils.js +0 -124
  176. package/dist/cjs/providers/indexer.js +0 -630
  177. package/dist/cjs/providers/onchain.js +0 -262
  178. package/dist/cjs/providers/utils.js +0 -121
  179. package/dist/cjs/repositories/contractRepository.js +0 -2
  180. package/dist/cjs/repositories/inMemory/contractRepository.js +0 -55
  181. package/dist/cjs/repositories/inMemory/walletRepository.js +0 -115
  182. package/dist/cjs/repositories/index.js +0 -34
  183. package/dist/cjs/repositories/indexedDB/contractRepository.js +0 -187
  184. package/dist/cjs/repositories/indexedDB/db.js +0 -19
  185. package/dist/cjs/repositories/indexedDB/manager.js +0 -100
  186. package/dist/cjs/repositories/indexedDB/schema.js +0 -204
  187. package/dist/cjs/repositories/indexedDB/walletRepository.js +0 -474
  188. package/dist/cjs/repositories/indexedDB/websqlAdapter.js +0 -144
  189. package/dist/cjs/repositories/migrations/contractRepositoryImpl.js +0 -127
  190. package/dist/cjs/repositories/migrations/fromStorageAdapter.js +0 -66
  191. package/dist/cjs/repositories/migrations/walletRepositoryImpl.js +0 -184
  192. package/dist/cjs/repositories/realm/contractRepository.js +0 -116
  193. package/dist/cjs/repositories/realm/index.js +0 -11
  194. package/dist/cjs/repositories/realm/schemas.js +0 -157
  195. package/dist/cjs/repositories/realm/types.js +0 -7
  196. package/dist/cjs/repositories/realm/walletRepository.js +0 -305
  197. package/dist/cjs/repositories/scriptFromAddress.js +0 -16
  198. package/dist/cjs/repositories/serialization.js +0 -82
  199. package/dist/cjs/repositories/sqlite/contractRepository.js +0 -135
  200. package/dist/cjs/repositories/sqlite/index.js +0 -7
  201. package/dist/cjs/repositories/sqlite/types.js +0 -2
  202. package/dist/cjs/repositories/sqlite/walletRepository.js +0 -441
  203. package/dist/cjs/repositories/walletRepository.js +0 -2
  204. package/dist/cjs/script/address.js +0 -108
  205. package/dist/cjs/script/base.js +0 -185
  206. package/dist/cjs/script/default.js +0 -57
  207. package/dist/cjs/script/delegate.js +0 -53
  208. package/dist/cjs/script/tapscript.js +0 -619
  209. package/dist/cjs/script/vhtlc.js +0 -170
  210. package/dist/cjs/storage/asyncStorage.js +0 -50
  211. package/dist/cjs/storage/fileSystem.js +0 -141
  212. package/dist/cjs/storage/inMemory.js +0 -24
  213. package/dist/cjs/storage/index.js +0 -2
  214. package/dist/cjs/storage/indexedDB.js +0 -101
  215. package/dist/cjs/storage/localStorage.js +0 -51
  216. package/dist/cjs/tree/signingSession.js +0 -229
  217. package/dist/cjs/tree/txTree.js +0 -192
  218. package/dist/cjs/tree/validation.js +0 -107
  219. package/dist/cjs/utils/anchor.js +0 -35
  220. package/dist/cjs/utils/arkTransaction.js +0 -271
  221. package/dist/cjs/utils/bip21.js +0 -127
  222. package/dist/cjs/utils/syncCursors.js +0 -128
  223. package/dist/cjs/utils/timelock.js +0 -59
  224. package/dist/cjs/utils/transaction.js +0 -28
  225. package/dist/cjs/utils/transactionHistory.js +0 -183
  226. package/dist/cjs/utils/txSizeEstimator.js +0 -132
  227. package/dist/cjs/utils/unknownFields.js +0 -174
  228. package/dist/cjs/wallet/asset-manager.js +0 -330
  229. package/dist/cjs/wallet/asset.js +0 -119
  230. package/dist/cjs/wallet/batch.js +0 -183
  231. package/dist/cjs/wallet/delegator.js +0 -302
  232. package/dist/cjs/wallet/expo/background.js +0 -116
  233. package/dist/cjs/wallet/expo/index.js +0 -9
  234. package/dist/cjs/wallet/expo/wallet.js +0 -230
  235. package/dist/cjs/wallet/hdDescriptorProvider.js +0 -159
  236. package/dist/cjs/wallet/index.js +0 -82
  237. package/dist/cjs/wallet/onchain.js +0 -290
  238. package/dist/cjs/wallet/ramps.js +0 -216
  239. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +0 -953
  240. package/dist/cjs/wallet/serviceWorker/wallet.js +0 -1173
  241. package/dist/cjs/wallet/unroll.js +0 -289
  242. package/dist/cjs/wallet/utils.js +0 -111
  243. package/dist/cjs/wallet/validation.js +0 -154
  244. package/dist/cjs/wallet/vtxo-manager.js +0 -1142
  245. package/dist/cjs/wallet/wallet.js +0 -2049
  246. package/dist/cjs/worker/browser/service-worker-manager.js +0 -183
  247. package/dist/cjs/worker/browser/utils.js +0 -67
  248. package/dist/cjs/worker/errors.js +0 -16
  249. package/dist/cjs/worker/expo/asyncStorageTaskQueue.js +0 -78
  250. package/dist/cjs/worker/expo/index.js +0 -13
  251. package/dist/cjs/worker/expo/processors/contractPollProcessor.js +0 -62
  252. package/dist/cjs/worker/expo/processors/index.js +0 -6
  253. package/dist/cjs/worker/expo/taskQueue.js +0 -41
  254. package/dist/cjs/worker/expo/taskRunner.js +0 -73
  255. package/dist/cjs/worker/messageBus.js +0 -473
  256. package/dist/esm/adapters/asyncStorage.js +0 -1
  257. package/dist/esm/adapters/expo.js +0 -3
  258. package/dist/esm/adapters/fileSystem.js +0 -1
  259. package/dist/esm/adapters/indexedDB.js +0 -1
  260. package/dist/esm/adapters/localStorage.js +0 -1
  261. package/dist/esm/arkfee/celenv.js +0 -40
  262. package/dist/esm/arkfee/estimator.js +0 -139
  263. package/dist/esm/arkfee/index.js +0 -1
  264. package/dist/esm/arkfee/types.js +0 -22
  265. package/dist/esm/arknote/index.js +0 -124
  266. package/dist/esm/bip322/index.js +0 -267
  267. package/dist/esm/contracts/arkcontract.js +0 -140
  268. package/dist/esm/contracts/contractManager.js +0 -625
  269. package/dist/esm/contracts/contractWatcher.js +0 -594
  270. package/dist/esm/contracts/handlers/default.js +0 -90
  271. package/dist/esm/contracts/handlers/delegate.js +0 -87
  272. package/dist/esm/contracts/handlers/helpers.js +0 -110
  273. package/dist/esm/contracts/handlers/index.js +0 -12
  274. package/dist/esm/contracts/handlers/registry.js +0 -86
  275. package/dist/esm/contracts/handlers/vhtlc.js +0 -191
  276. package/dist/esm/contracts/index.js +0 -13
  277. package/dist/esm/contracts/types.js +0 -1
  278. package/dist/esm/contracts/vtxoOwnership.js +0 -69
  279. package/dist/esm/extension/asset/assetGroup.js +0 -224
  280. package/dist/esm/extension/asset/assetId.js +0 -148
  281. package/dist/esm/extension/asset/assetInput.js +0 -217
  282. package/dist/esm/extension/asset/assetOutput.js +0 -169
  283. package/dist/esm/extension/asset/assetRef.js +0 -144
  284. package/dist/esm/extension/asset/index.js +0 -8
  285. package/dist/esm/extension/asset/metadata.js +0 -182
  286. package/dist/esm/extension/asset/packet.js +0 -110
  287. package/dist/esm/extension/asset/types.js +0 -19
  288. package/dist/esm/extension/asset/utils.js +0 -99
  289. package/dist/esm/extension/index.js +0 -248
  290. package/dist/esm/extension/packet.js +0 -16
  291. package/dist/esm/forfeit.js +0 -41
  292. package/dist/esm/identity/descriptor.js +0 -161
  293. package/dist/esm/identity/descriptorProvider.js +0 -1
  294. package/dist/esm/identity/hdCapableIdentity.js +0 -1
  295. package/dist/esm/identity/index.js +0 -12
  296. package/dist/esm/identity/seedIdentity.js +0 -453
  297. package/dist/esm/identity/serialize.js +0 -164
  298. package/dist/esm/identity/singleKey.js +0 -121
  299. package/dist/esm/identity/staticDescriptorProvider.js +0 -61
  300. package/dist/esm/index.js +0 -87
  301. package/dist/esm/intent/index.js +0 -255
  302. package/dist/esm/musig2/index.js +0 -3
  303. package/dist/esm/musig2/keys.js +0 -21
  304. package/dist/esm/musig2/nonces.js +0 -11
  305. package/dist/esm/musig2/sign.js +0 -63
  306. package/dist/esm/networks.js +0 -22
  307. package/dist/esm/package.json +0 -3
  308. package/dist/esm/providers/ark.js +0 -572
  309. package/dist/esm/providers/delegator.js +0 -81
  310. package/dist/esm/providers/electrum.js +0 -864
  311. package/dist/esm/providers/errors.js +0 -54
  312. package/dist/esm/providers/expoArk.js +0 -78
  313. package/dist/esm/providers/expoIndexer.js +0 -107
  314. package/dist/esm/providers/expoUtils.js +0 -87
  315. package/dist/esm/providers/indexer.js +0 -626
  316. package/dist/esm/providers/onchain.js +0 -258
  317. package/dist/esm/providers/utils.js +0 -117
  318. package/dist/esm/repositories/contractRepository.js +0 -1
  319. package/dist/esm/repositories/inMemory/contractRepository.js +0 -51
  320. package/dist/esm/repositories/inMemory/walletRepository.js +0 -111
  321. package/dist/esm/repositories/index.js +0 -10
  322. package/dist/esm/repositories/indexedDB/contractRepository.js +0 -183
  323. package/dist/esm/repositories/indexedDB/db.js +0 -4
  324. package/dist/esm/repositories/indexedDB/manager.js +0 -95
  325. package/dist/esm/repositories/indexedDB/schema.js +0 -199
  326. package/dist/esm/repositories/indexedDB/walletRepository.js +0 -470
  327. package/dist/esm/repositories/indexedDB/websqlAdapter.js +0 -138
  328. package/dist/esm/repositories/migrations/contractRepositoryImpl.js +0 -121
  329. package/dist/esm/repositories/migrations/fromStorageAdapter.js +0 -58
  330. package/dist/esm/repositories/migrations/walletRepositoryImpl.js +0 -180
  331. package/dist/esm/repositories/realm/contractRepository.js +0 -112
  332. package/dist/esm/repositories/realm/index.js +0 -3
  333. package/dist/esm/repositories/realm/schemas.js +0 -153
  334. package/dist/esm/repositories/realm/types.js +0 -6
  335. package/dist/esm/repositories/realm/walletRepository.js +0 -301
  336. package/dist/esm/repositories/scriptFromAddress.js +0 -13
  337. package/dist/esm/repositories/serialization.js +0 -67
  338. package/dist/esm/repositories/sqlite/contractRepository.js +0 -131
  339. package/dist/esm/repositories/sqlite/index.js +0 -2
  340. package/dist/esm/repositories/sqlite/types.js +0 -1
  341. package/dist/esm/repositories/sqlite/walletRepository.js +0 -437
  342. package/dist/esm/repositories/walletRepository.js +0 -1
  343. package/dist/esm/script/address.js +0 -104
  344. package/dist/esm/script/base.js +0 -179
  345. package/dist/esm/script/default.js +0 -54
  346. package/dist/esm/script/delegate.js +0 -50
  347. package/dist/esm/script/tapscript.js +0 -615
  348. package/dist/esm/script/vhtlc.js +0 -167
  349. package/dist/esm/storage/asyncStorage.js +0 -46
  350. package/dist/esm/storage/fileSystem.js +0 -104
  351. package/dist/esm/storage/inMemory.js +0 -20
  352. package/dist/esm/storage/index.js +0 -1
  353. package/dist/esm/storage/indexedDB.js +0 -97
  354. package/dist/esm/storage/localStorage.js +0 -47
  355. package/dist/esm/tree/signingSession.js +0 -191
  356. package/dist/esm/tree/txTree.js +0 -188
  357. package/dist/esm/tree/validation.js +0 -101
  358. package/dist/esm/utils/anchor.js +0 -31
  359. package/dist/esm/utils/arkTransaction.js +0 -264
  360. package/dist/esm/utils/bip21.js +0 -123
  361. package/dist/esm/utils/syncCursors.js +0 -119
  362. package/dist/esm/utils/timelock.js +0 -22
  363. package/dist/esm/utils/transaction.js +0 -24
  364. package/dist/esm/utils/transactionHistory.js +0 -180
  365. package/dist/esm/utils/txSizeEstimator.js +0 -128
  366. package/dist/esm/utils/unknownFields.js +0 -169
  367. package/dist/esm/wallet/asset-manager.js +0 -325
  368. package/dist/esm/wallet/asset.js +0 -113
  369. package/dist/esm/wallet/batch.js +0 -180
  370. package/dist/esm/wallet/delegator.js +0 -297
  371. package/dist/esm/wallet/expo/background.js +0 -111
  372. package/dist/esm/wallet/expo/index.js +0 -2
  373. package/dist/esm/wallet/expo/wallet.js +0 -193
  374. package/dist/esm/wallet/hdDescriptorProvider.js +0 -155
  375. package/dist/esm/wallet/index.js +0 -75
  376. package/dist/esm/wallet/onchain.js +0 -285
  377. package/dist/esm/wallet/ramps.js +0 -212
  378. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +0 -946
  379. package/dist/esm/wallet/serviceWorker/wallet.js +0 -1168
  380. package/dist/esm/wallet/unroll.js +0 -285
  381. package/dist/esm/wallet/utils.js +0 -103
  382. package/dist/esm/wallet/validation.js +0 -142
  383. package/dist/esm/wallet/vtxo-manager.js +0 -1136
  384. package/dist/esm/wallet/wallet.js +0 -2041
  385. package/dist/esm/worker/browser/service-worker-manager.js +0 -177
  386. package/dist/esm/worker/browser/utils.js +0 -63
  387. package/dist/esm/worker/errors.js +0 -11
  388. package/dist/esm/worker/expo/asyncStorageTaskQueue.js +0 -74
  389. package/dist/esm/worker/expo/index.js +0 -4
  390. package/dist/esm/worker/expo/processors/contractPollProcessor.js +0 -59
  391. package/dist/esm/worker/expo/processors/index.js +0 -1
  392. package/dist/esm/worker/expo/taskQueue.js +0 -37
  393. package/dist/esm/worker/expo/taskRunner.js +0 -69
  394. package/dist/esm/worker/messageBus.js +0 -469
  395. package/dist/types/adapters/asyncStorage.d.ts +0 -2
  396. package/dist/types/adapters/expo.d.ts +0 -4
  397. package/dist/types/adapters/fileSystem.d.ts +0 -2
  398. package/dist/types/adapters/indexedDB.d.ts +0 -2
  399. package/dist/types/adapters/localStorage.d.ts +0 -2
  400. package/dist/types/arkfee/celenv.d.ts +0 -25
  401. package/dist/types/arkfee/estimator.d.ts +0 -49
  402. package/dist/types/arkfee/index.d.ts +0 -2
  403. package/dist/types/arkfee/types.d.ts +0 -38
  404. package/dist/types/arknote/index.d.ts +0 -84
  405. package/dist/types/bip322/index.d.ts +0 -55
  406. package/dist/types/contracts/arkcontract.d.ts +0 -99
  407. package/dist/types/contracts/contractManager.d.ts +0 -381
  408. package/dist/types/contracts/contractWatcher.d.ts +0 -217
  409. package/dist/types/contracts/handlers/default.d.ts +0 -19
  410. package/dist/types/contracts/handlers/delegate.d.ts +0 -21
  411. package/dist/types/contracts/handlers/helpers.d.ts +0 -19
  412. package/dist/types/contracts/handlers/index.d.ts +0 -7
  413. package/dist/types/contracts/handlers/registry.d.ts +0 -65
  414. package/dist/types/contracts/handlers/vhtlc.d.ts +0 -32
  415. package/dist/types/contracts/index.d.ts +0 -14
  416. package/dist/types/contracts/types.d.ts +0 -233
  417. package/dist/types/contracts/vtxoOwnership.d.ts +0 -33
  418. package/dist/types/extension/asset/assetGroup.d.ts +0 -119
  419. package/dist/types/extension/asset/assetId.d.ts +0 -83
  420. package/dist/types/extension/asset/assetInput.d.ts +0 -64
  421. package/dist/types/extension/asset/assetOutput.d.ts +0 -54
  422. package/dist/types/extension/asset/assetRef.d.ts +0 -91
  423. package/dist/types/extension/asset/index.d.ts +0 -8
  424. package/dist/types/extension/asset/metadata.d.ts +0 -52
  425. package/dist/types/extension/asset/packet.d.ts +0 -41
  426. package/dist/types/extension/asset/types.d.ts +0 -16
  427. package/dist/types/extension/asset/utils.d.ts +0 -21
  428. package/dist/types/extension/index.d.ts +0 -56
  429. package/dist/types/extension/packet.d.ts +0 -21
  430. package/dist/types/forfeit.d.ts +0 -18
  431. package/dist/types/identity/descriptor.d.ts +0 -61
  432. package/dist/types/identity/descriptorProvider.d.ts +0 -35
  433. package/dist/types/identity/hdCapableIdentity.d.ts +0 -44
  434. package/dist/types/identity/index.d.ts +0 -56
  435. package/dist/types/identity/seedIdentity.d.ts +0 -254
  436. package/dist/types/identity/serialize.d.ts +0 -96
  437. package/dist/types/identity/singleKey.d.ts +0 -62
  438. package/dist/types/identity/staticDescriptorProvider.d.ts +0 -18
  439. package/dist/types/index.d.ts +0 -59
  440. package/dist/types/intent/index.d.ts +0 -86
  441. package/dist/types/musig2/index.d.ts +0 -4
  442. package/dist/types/musig2/keys.d.ts +0 -9
  443. package/dist/types/musig2/nonces.d.ts +0 -14
  444. package/dist/types/musig2/sign.d.ts +0 -27
  445. package/dist/types/networks.d.ts +0 -16
  446. package/dist/types/providers/ark.d.ts +0 -369
  447. package/dist/types/providers/delegator.d.ts +0 -82
  448. package/dist/types/providers/electrum.d.ts +0 -312
  449. package/dist/types/providers/errors.d.ts +0 -13
  450. package/dist/types/providers/expoArk.d.ts +0 -22
  451. package/dist/types/providers/expoIndexer.d.ts +0 -18
  452. package/dist/types/providers/expoUtils.d.ts +0 -18
  453. package/dist/types/providers/indexer.d.ts +0 -301
  454. package/dist/types/providers/onchain.d.ts +0 -148
  455. package/dist/types/providers/utils.d.ts +0 -12
  456. package/dist/types/repositories/contractRepository.d.ts +0 -32
  457. package/dist/types/repositories/inMemory/contractRepository.d.ts +0 -17
  458. package/dist/types/repositories/inMemory/walletRepository.d.ts +0 -29
  459. package/dist/types/repositories/index.d.ts +0 -9
  460. package/dist/types/repositories/indexedDB/contractRepository.d.ts +0 -21
  461. package/dist/types/repositories/indexedDB/db.d.ts +0 -4
  462. package/dist/types/repositories/indexedDB/manager.d.ts +0 -25
  463. package/dist/types/repositories/indexedDB/schema.d.ts +0 -9
  464. package/dist/types/repositories/indexedDB/walletRepository.d.ts +0 -28
  465. package/dist/types/repositories/indexedDB/websqlAdapter.d.ts +0 -49
  466. package/dist/types/repositories/migrations/contractRepositoryImpl.d.ts +0 -24
  467. package/dist/types/repositories/migrations/fromStorageAdapter.d.ts +0 -19
  468. package/dist/types/repositories/migrations/walletRepositoryImpl.d.ts +0 -27
  469. package/dist/types/repositories/realm/contractRepository.d.ts +0 -24
  470. package/dist/types/repositories/realm/index.d.ts +0 -4
  471. package/dist/types/repositories/realm/types.d.ts +0 -16
  472. package/dist/types/repositories/realm/walletRepository.d.ts +0 -34
  473. package/dist/types/repositories/scriptFromAddress.d.ts +0 -9
  474. package/dist/types/repositories/serialization.d.ts +0 -65
  475. package/dist/types/repositories/sqlite/contractRepository.d.ts +0 -33
  476. package/dist/types/repositories/sqlite/index.d.ts +0 -3
  477. package/dist/types/repositories/sqlite/types.d.ts +0 -18
  478. package/dist/types/repositories/walletRepository.d.ts +0 -72
  479. package/dist/types/script/address.d.ts +0 -67
  480. package/dist/types/script/base.d.ts +0 -105
  481. package/dist/types/script/default.d.ts +0 -44
  482. package/dist/types/script/delegate.d.ts +0 -40
  483. package/dist/types/script/tapscript.d.ts +0 -169
  484. package/dist/types/script/vhtlc.d.ts +0 -66
  485. package/dist/types/tree/signingSession.d.ts +0 -37
  486. package/dist/types/tree/txTree.d.ts +0 -28
  487. package/dist/types/tree/validation.d.ts +0 -15
  488. package/dist/types/utils/anchor.d.ts +0 -19
  489. package/dist/types/utils/arkTransaction.d.ts +0 -49
  490. package/dist/types/utils/bip21.d.ts +0 -38
  491. package/dist/types/utils/syncCursors.d.ts +0 -60
  492. package/dist/types/utils/timelock.d.ts +0 -9
  493. package/dist/types/utils/transaction.d.ts +0 -13
  494. package/dist/types/utils/transactionHistory.d.ts +0 -15
  495. package/dist/types/utils/txSizeEstimator.d.ts +0 -40
  496. package/dist/types/utils/unknownFields.d.ts +0 -83
  497. package/dist/types/wallet/asset-manager.d.ts +0 -69
  498. package/dist/types/wallet/asset.d.ts +0 -21
  499. package/dist/types/wallet/batch.d.ts +0 -107
  500. package/dist/types/wallet/delegator.d.ts +0 -48
  501. package/dist/types/wallet/expo/background.d.ts +0 -66
  502. package/dist/types/wallet/expo/index.d.ts +0 -4
  503. package/dist/types/wallet/hdDescriptorProvider.d.ts +0 -93
  504. package/dist/types/wallet/index.d.ts +0 -755
  505. package/dist/types/wallet/onchain.d.ts +0 -109
  506. package/dist/types/wallet/ramps.d.ts +0 -64
  507. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +0 -543
  508. package/dist/types/wallet/serviceWorker/wallet.d.ts +0 -238
  509. package/dist/types/wallet/unroll.d.ts +0 -114
  510. package/dist/types/wallet/utils.d.ts +0 -36
  511. package/dist/types/wallet/validation.d.ts +0 -24
  512. package/dist/types/wallet/vtxo-manager.d.ts +0 -476
  513. package/dist/types/wallet/wallet.d.ts +0 -360
  514. package/dist/types/worker/browser/service-worker-manager.d.ts +0 -32
  515. package/dist/types/worker/browser/utils.d.ts +0 -17
  516. package/dist/types/worker/errors.d.ts +0 -7
  517. package/dist/types/worker/expo/index.d.ts +0 -7
  518. package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +0 -19
  519. package/dist/types/worker/expo/processors/index.d.ts +0 -1
  520. package/dist/types/worker/expo/taskQueue.d.ts +0 -50
  521. package/dist/types/worker/expo/taskRunner.d.ts +0 -66
  522. package/dist/types/worker/messageBus.d.ts +0 -188
@@ -1,2049 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Wallet = exports.ReadonlyWallet = exports.getArkadeServerUrl = void 0;
4
- exports.selectVirtualCoins = selectVirtualCoins;
5
- exports.waitForIncomingFunds = waitForIncomingFunds;
6
- const base_1 = require("@scure/base");
7
- const payment_js_1 = require("@scure/btc-signer/payment.js");
8
- const btc_signer_1 = require("@scure/btc-signer");
9
- const utils_js_1 = require("@scure/btc-signer/utils.js");
10
- const address_1 = require("../script/address");
11
- const default_1 = require("../script/default");
12
- const networks_1 = require("../networks");
13
- const onchain_1 = require("../providers/onchain");
14
- const ark_1 = require("../providers/ark");
15
- const forfeit_1 = require("../forfeit");
16
- const validation_1 = require("../tree/validation");
17
- const validation_2 = require("./validation");
18
- const identity_1 = require("../identity");
19
- const _1 = require(".");
20
- const asset_1 = require("./asset");
21
- const base_2 = require("../script/base");
22
- const tapscript_1 = require("../script/tapscript");
23
- const arkTransaction_1 = require("../utils/arkTransaction");
24
- const vtxo_manager_1 = require("./vtxo-manager");
25
- const arknote_1 = require("../arknote");
26
- const intent_1 = require("../intent");
27
- const indexer_1 = require("../providers/indexer");
28
- const utils_1 = require("./utils");
29
- const errors_1 = require("../providers/errors");
30
- const batch_1 = require("./batch");
31
- const arkfee_1 = require("../arkfee");
32
- const transactionHistory_1 = require("../utils/transactionHistory");
33
- const asset_manager_1 = require("./asset-manager");
34
- const extension_1 = require("../extension");
35
- const delegate_1 = require("../script/delegate");
36
- const delegator_1 = require("./delegator");
37
- const repositories_1 = require("../repositories");
38
- const contractManager_1 = require("../contracts/contractManager");
39
- const handlers_1 = require("../contracts/handlers");
40
- const timelock_1 = require("../utils/timelock");
41
- const syncCursors_1 = require("../utils/syncCursors");
42
- const vtxoOwnership_1 = require("../contracts/vtxoOwnership");
43
- const getArkadeServerUrl = ({ arkServerUrl, }) => arkServerUrl || _1.DEFAULT_ARKADE_SERVER_URL;
44
- exports.getArkadeServerUrl = getArkadeServerUrl;
45
- // Historical unilateral exit delay for mainnet (~7 days in seconds).
46
- // Kept so existing wallets can still discover and spend VTXOs sent to the
47
- // legacy address after arkd starts advertising a different delay.
48
- const MAINNET_UNILATERAL_EXIT_DELAY = 605184n;
49
- function delayToTimelock(delay) {
50
- return {
51
- value: delay,
52
- type: delay < 512n ? "blocks" : "seconds",
53
- };
54
- }
55
- function dedupeTimelocks(timelocks) {
56
- const seen = new Set();
57
- const deduped = [];
58
- for (const timelock of timelocks) {
59
- const sequence = (0, timelock_1.timelockToSequence)(timelock).toString();
60
- if (seen.has(sequence))
61
- continue;
62
- seen.add(sequence);
63
- deduped.push(timelock);
64
- }
65
- return deduped;
66
- }
67
- /**
68
- * Type guard function to check if an identity has a toReadonly method.
69
- */
70
- function hasToReadonly(identity) {
71
- return (typeof identity === "object" &&
72
- identity !== null &&
73
- "toReadonly" in identity &&
74
- typeof identity.toReadonly === "function");
75
- }
76
- class ReadonlyWallet {
77
- get assetManager() {
78
- return this._assetManager;
79
- }
80
- constructor(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegatorProvider, watcherConfig, walletContractTimelocks) {
81
- this.identity = identity;
82
- this.network = network;
83
- this.onchainProvider = onchainProvider;
84
- this.indexerProvider = indexerProvider;
85
- this.arkServerPublicKey = arkServerPublicKey;
86
- this.offchainTapscript = offchainTapscript;
87
- this.boardingTapscript = boardingTapscript;
88
- this.dustAmount = dustAmount;
89
- this.walletRepository = walletRepository;
90
- this.contractRepository = contractRepository;
91
- this.delegatorProvider = delegatorProvider;
92
- // Outpoints ("txid:vout") committed to an in-flight settle/send. Filtered
93
- // from getVtxos() so concurrent callers (UI, VtxoManager auto-renewal,
94
- // another send/settle racing the _txLock) can't reselect coins that are
95
- // already on their way out. The set is in-memory only: a process crash
96
- // clears it, and a stale entry only hides a VTXO (never spends one).
97
- this._pendingSpendOutpoints = new Set();
98
- // Guard: detect identity/server network mismatch for descriptor-based identities.
99
- // This duplicates the check in setupWalletConfig() so that subclasses
100
- // bypassing the factory still get the safety net.
101
- if ("descriptor" in identity) {
102
- const descriptor = identity.descriptor;
103
- const identityIsMainnet = !descriptor.includes("tpub");
104
- const serverIsMainnet = network.bech32 === "bc";
105
- if (identityIsMainnet !== serverIsMainnet) {
106
- throw new Error(`Network mismatch: identity uses ${identityIsMainnet ? "mainnet" : "testnet"} derivation ` +
107
- `but wallet network is ${serverIsMainnet ? "mainnet" : "testnet"}. ` +
108
- `Create identity with { isMainnet: ${serverIsMainnet} } to match.`);
109
- }
110
- }
111
- this.watcherConfig = watcherConfig;
112
- this._assetManager = new asset_manager_1.ReadonlyAssetManager(this.indexerProvider);
113
- // Defensive for direct-construction callers; setupWalletConfig already
114
- // passes a deduped list through the public create() factories.
115
- this.walletContractTimelocks =
116
- walletContractTimelocks && walletContractTimelocks.length > 0
117
- ? dedupeTimelocks(walletContractTimelocks)
118
- : [
119
- this.offchainTapscript.options.csvTimelock ??
120
- default_1.DefaultVtxo.Script.DEFAULT_TIMELOCK,
121
- ];
122
- }
123
- /**
124
- * Protected helper to set up shared wallet configuration.
125
- * Extracts common logic used by both ReadonlyWallet.create() and Wallet.create().
126
- */
127
- static async setupWalletConfig(config, pubKey) {
128
- // Use provided arkProvider instance or create a new one from arkServerUrl
129
- const arkProvider = config.arkProvider ||
130
- (() => {
131
- return new ark_1.RestArkProvider((0, exports.getArkadeServerUrl)(config));
132
- })();
133
- // Extract arkServerUrl from provider if not explicitly provided
134
- const arkServerUrl = config.arkServerUrl || arkProvider.serverUrl;
135
- if (!arkServerUrl) {
136
- throw new Error("Could not determine arkServerUrl from provider");
137
- }
138
- // Use provided indexerProvider instance or create a new one
139
- // indexerUrl defaults to arkServerUrl if not provided
140
- const indexerUrl = config.indexerUrl || arkServerUrl;
141
- const indexerProvider = config.indexerProvider || new indexer_1.RestIndexerProvider(indexerUrl);
142
- const info = await arkProvider.getInfo();
143
- const network = (0, networks_1.getNetwork)(info.network);
144
- // Guard: detect identity/server network mismatch for seed-based identities.
145
- // A mainnet descriptor (xpub, coin type 0) connected to a testnet server
146
- // (or vice versa) means wrong derivation path → wrong keys → potential fund loss.
147
- if ("descriptor" in config.identity) {
148
- const descriptor = config.identity.descriptor;
149
- const identityIsMainnet = !descriptor.includes("tpub");
150
- const serverIsMainnet = info.network === "bitcoin";
151
- if (identityIsMainnet && !serverIsMainnet) {
152
- throw new Error(`Network mismatch: identity uses mainnet derivation (coin type 0) ` +
153
- `but the Arkade server is on ${info.network}. ` +
154
- `Create identity with { isMainnet: false } to use testnet derivation.`);
155
- }
156
- if (!identityIsMainnet && serverIsMainnet) {
157
- throw new Error(`Network mismatch: identity uses testnet derivation (coin type 1) ` +
158
- `but the Arkade server is on mainnet. ` +
159
- `Create identity with { isMainnet: true } or omit isMainnet (defaults to mainnet).`);
160
- }
161
- }
162
- // Extract esploraUrl from provider if not explicitly provided
163
- const esploraUrl = config.esploraUrl || onchain_1.ESPLORA_URL[info.network];
164
- // Use provided onchainProvider instance or create a new one
165
- const onchainProvider = config.onchainProvider || new onchain_1.EsploraProvider(esploraUrl);
166
- // validate unilateral exit timelock passed in config if any
167
- if (config.exitTimelock) {
168
- const { value, type } = config.exitTimelock;
169
- if ((value < 512n && type !== "blocks") ||
170
- (value >= 512n && type !== "seconds")) {
171
- throw new Error("invalid exitTimelock");
172
- }
173
- }
174
- const arkdExitTimelock = delayToTimelock(info.unilateralExitDelay);
175
- // create unilateral exit timelock
176
- const exitTimelock = config.exitTimelock ?? arkdExitTimelock;
177
- const walletContractTimelocks = config.exitTimelock
178
- ? [exitTimelock]
179
- : dedupeTimelocks([
180
- arkdExitTimelock,
181
- ...(info.network === "bitcoin"
182
- ? [delayToTimelock(MAINNET_UNILATERAL_EXIT_DELAY)]
183
- : []),
184
- ]);
185
- // validate boarding timelock passed in config if any
186
- if (config.boardingTimelock) {
187
- const { value, type } = config.boardingTimelock;
188
- if ((value < 512n && type !== "blocks") ||
189
- (value >= 512n && type !== "seconds")) {
190
- throw new Error("invalid boardingTimelock");
191
- }
192
- }
193
- // create boarding timelock
194
- const boardingTimelock = config.boardingTimelock ?? {
195
- value: info.boardingExitDelay,
196
- type: info.boardingExitDelay < 512n ? "blocks" : "seconds",
197
- };
198
- // Generate tapscripts for offchain and boarding address
199
- const serverPubKey = base_1.hex.decode(info.signerPubkey).slice(1);
200
- const delegatePubKey = config.delegatorProvider
201
- ? await config.delegatorProvider
202
- .getDelegateInfo()
203
- .then((info) => base_1.hex.decode(info.pubkey).slice(1))
204
- : undefined;
205
- const offchainOptions = {
206
- pubKey,
207
- serverPubKey,
208
- csvTimelock: exitTimelock,
209
- };
210
- const offchainTapscript = !delegatePubKey
211
- ? new default_1.DefaultVtxo.Script(offchainOptions)
212
- : new delegate_1.DelegateVtxo.Script({ ...offchainOptions, delegatePubKey });
213
- const boardingTapscript = new default_1.DefaultVtxo.Script({
214
- ...offchainOptions,
215
- csvTimelock: boardingTimelock,
216
- });
217
- const walletRepository = config.storage?.walletRepository ?? new repositories_1.IndexedDBWalletRepository();
218
- const contractRepository = config.storage?.contractRepository ??
219
- new repositories_1.IndexedDBContractRepository();
220
- return {
221
- arkProvider,
222
- indexerProvider,
223
- onchainProvider,
224
- network,
225
- networkName: info.network,
226
- serverPubKey,
227
- offchainTapscript,
228
- boardingTapscript,
229
- dustAmount: info.dust,
230
- walletRepository,
231
- contractRepository,
232
- info,
233
- delegatorProvider: config.delegatorProvider,
234
- walletContractTimelocks,
235
- };
236
- }
237
- /**
238
- * Create a readonly wallet for querying balances, addresses, and history.
239
- *
240
- * @param config - Readonly wallet configuration
241
- * @returns A readonly wallet instance
242
- */
243
- static async create(config) {
244
- const pubkey = await config.identity.xOnlyPublicKey();
245
- if (!pubkey) {
246
- throw new Error("Invalid configured public key");
247
- }
248
- const setup = await ReadonlyWallet.setupWalletConfig(config, pubkey);
249
- return new ReadonlyWallet(config.identity, setup.network, setup.onchainProvider, setup.indexerProvider, setup.serverPubKey, setup.offchainTapscript, setup.boardingTapscript, setup.dustAmount, setup.walletRepository, setup.contractRepository, setup.delegatorProvider, config.watcherConfig, setup.walletContractTimelocks);
250
- }
251
- get arkAddress() {
252
- return this.offchainTapscript.address(this.network.hrp, this.arkServerPublicKey);
253
- }
254
- /**
255
- * Get the pkScript hex for the wallet's primary offchain address.
256
- * For the full wallet-owned script set registered in ContractManager, use getWalletScripts().
257
- */
258
- get defaultContractScript() {
259
- return base_1.hex.encode(this.offchainTapscript.pkScript);
260
- }
261
- /** Returns the wallet's Arkade address. */
262
- async getAddress() {
263
- return this.arkAddress.encode();
264
- }
265
- /** Returns the onchain boarding address used to move funds into Arkade. */
266
- async getBoardingAddress() {
267
- return this.boardingTapscript.onchainAddress(this.network);
268
- }
269
- /**
270
- * Return the wallet's combined onchain and offchain balances.
271
- */
272
- async getBalance() {
273
- const [boardingUtxos, vtxos] = await Promise.all([
274
- this.getBoardingUtxos(),
275
- this.getVtxos(),
276
- ]);
277
- // boarding
278
- let confirmed = 0;
279
- let unconfirmed = 0;
280
- for (const utxo of boardingUtxos) {
281
- if (utxo.status.confirmed) {
282
- confirmed += utxo.value;
283
- }
284
- else {
285
- unconfirmed += utxo.value;
286
- }
287
- }
288
- // offchain
289
- let settled = 0;
290
- let preconfirmed = 0;
291
- let recoverable = 0;
292
- settled = vtxos
293
- .filter((coin) => coin.virtualStatus.state === "settled")
294
- .reduce((sum, coin) => sum + coin.value, 0);
295
- preconfirmed = vtxos
296
- .filter((coin) => coin.virtualStatus.state === "preconfirmed")
297
- .reduce((sum, coin) => sum + coin.value, 0);
298
- recoverable = vtxos
299
- .filter((coin) => (0, _1.isSpendable)(coin) && coin.virtualStatus.state === "swept")
300
- .reduce((sum, coin) => sum + coin.value, 0);
301
- const totalBoarding = confirmed + unconfirmed;
302
- const totalOffchain = settled + preconfirmed + recoverable;
303
- // aggregate asset balances from spendable virtual outputs
304
- const assetBalances = new Map();
305
- for (const vtxo of vtxos) {
306
- if (!(0, _1.isSpendable)(vtxo))
307
- continue;
308
- if (vtxo.assets) {
309
- for (const a of vtxo.assets) {
310
- const current = assetBalances.get(a.assetId) ?? 0n;
311
- assetBalances.set(a.assetId, current + a.amount);
312
- }
313
- }
314
- }
315
- const assets = Array.from(assetBalances.entries()).map(([assetId, amount]) => ({
316
- assetId,
317
- amount,
318
- }));
319
- return {
320
- boarding: {
321
- confirmed,
322
- unconfirmed,
323
- total: totalBoarding,
324
- },
325
- settled,
326
- preconfirmed,
327
- available: settled + preconfirmed,
328
- recoverable,
329
- total: totalBoarding + totalOffchain,
330
- assets,
331
- };
332
- }
333
- /**
334
- * Return virtual outputs tracked by the wallet.
335
- *
336
- * @param filter - Optional flags controlling whether recoverable or unrolled VTXOs are included
337
- */
338
- async getVtxos(filter) {
339
- const f = filter ?? { withRecoverable: true, withUnrolled: false };
340
- const contractManager = await this.getContractManager();
341
- const vtxos = await contractManager.getContractsWithVtxos();
342
- return vtxos
343
- .flatMap((_) => _.vtxos)
344
- .filter((vtxo) => {
345
- if (this._pendingSpendOutpoints.has(`${vtxo.txid}:${vtxo.vout}`)) {
346
- return false;
347
- }
348
- if ((0, _1.isSpendable)(vtxo)) {
349
- if (!f.withRecoverable &&
350
- ((0, _1.isRecoverable)(vtxo) || (0, _1.isExpired)(vtxo))) {
351
- return false;
352
- }
353
- return true;
354
- }
355
- return !!(f.withUnrolled && vtxo.isUnrolled);
356
- });
357
- }
358
- /**
359
- * Return wallet transaction history derived from Arkade state and boarding transactions.
360
- */
361
- async getTransactionHistory() {
362
- const contractManager = await this.getContractManager();
363
- const response = await contractManager.getContractsWithVtxos();
364
- const allVtxos = response.flatMap((_) => _.vtxos);
365
- const { boardingTxs, commitmentsToIgnore } = await this.getBoardingTxs();
366
- const getTxCreatedAt = (txid) => this.indexerProvider
367
- .getVtxos({ outpoints: [{ txid, vout: 0 }] })
368
- .then((res) => res.vtxos[0]?.createdAt.getTime());
369
- return (0, transactionHistory_1.buildTransactionHistory)(allVtxos, boardingTxs, commitmentsToIgnore, getTxCreatedAt);
370
- }
371
- /**
372
- * Clear the global VTXO sync cursor, forcing a full re-bootstrap on next sync.
373
- * Useful for recovery after indexer reprocessing or debugging.
374
- */
375
- async clearSyncCursor() {
376
- await (0, syncCursors_1.clearSyncCursor)(this.walletRepository);
377
- }
378
- /**
379
- * Build a transaction history view for the wallet's boarding address.
380
- */
381
- async getBoardingTxs() {
382
- const utxos = [];
383
- const commitmentsToIgnore = new Set();
384
- const boardingAddress = await this.getBoardingAddress();
385
- const txs = await this.onchainProvider.getTransactions(boardingAddress);
386
- const outspendCache = new Map();
387
- for (const tx of txs) {
388
- for (let i = 0; i < tx.vout.length; i++) {
389
- const vout = tx.vout[i];
390
- if (vout.scriptpubkey_address === boardingAddress) {
391
- let spentStatuses = outspendCache.get(tx.txid);
392
- if (!spentStatuses) {
393
- spentStatuses =
394
- await this.onchainProvider.getTxOutspends(tx.txid);
395
- outspendCache.set(tx.txid, spentStatuses);
396
- }
397
- const spentStatus = spentStatuses[i];
398
- if (spentStatus?.spent) {
399
- commitmentsToIgnore.add(spentStatus.txid);
400
- }
401
- utxos.push({
402
- txid: tx.txid,
403
- vout: i,
404
- value: Number(vout.value),
405
- status: {
406
- confirmed: tx.status.confirmed,
407
- block_time: tx.status.block_time,
408
- },
409
- isUnrolled: true,
410
- virtualStatus: {
411
- state: spentStatus?.spent ? "spent" : "settled",
412
- commitmentTxIds: spentStatus?.spent
413
- ? [spentStatus.txid]
414
- : undefined,
415
- },
416
- createdAt: tx.status.confirmed
417
- ? new Date(tx.status.block_time * 1000)
418
- : new Date(0),
419
- script: base_1.hex.encode(this.boardingTapscript.pkScript),
420
- });
421
- }
422
- }
423
- }
424
- const unconfirmedTxs = [];
425
- const confirmedTxs = [];
426
- for (const utxo of utxos) {
427
- const tx = {
428
- key: {
429
- boardingTxid: utxo.txid,
430
- commitmentTxid: "",
431
- arkTxid: "",
432
- },
433
- amount: utxo.value,
434
- type: _1.TxType.TxReceived,
435
- settled: utxo.virtualStatus.state === "spent",
436
- createdAt: utxo.status.block_time
437
- ? new Date(utxo.status.block_time * 1000).getTime()
438
- : 0,
439
- };
440
- if (!utxo.status.block_time) {
441
- unconfirmedTxs.push(tx);
442
- }
443
- else {
444
- confirmedTxs.push(tx);
445
- }
446
- }
447
- return {
448
- boardingTxs: [...unconfirmedTxs, ...confirmedTxs],
449
- commitmentsToIgnore,
450
- };
451
- }
452
- /**
453
- * Fetch and cache onchain inputs (UTXOs) received at the boarding address.
454
- */
455
- async getBoardingUtxos() {
456
- const boardingAddress = await this.getBoardingAddress();
457
- const boardingUtxos = await this.onchainProvider.getCoins(boardingAddress);
458
- const utxos = boardingUtxos.map((utxo) => {
459
- return (0, utils_1.extendCoin)(this, utxo);
460
- });
461
- // Save boarding inputs using unified repository
462
- await this.walletRepository.saveUtxos(boardingAddress, utxos);
463
- return utxos;
464
- }
465
- /**
466
- * Subscribe to onchain and offchain notifications for newly received funds.
467
- *
468
- * @param eventCallback - Callback invoked when matching funds are detected
469
- * @returns A function that stops the subscriptions
470
- */
471
- async notifyIncomingFunds(eventCallback) {
472
- const arkAddress = await this.getAddress();
473
- const boardingAddress = await this.getBoardingAddress();
474
- let onchainStopFunc;
475
- let indexerStopFunc;
476
- if (this.onchainProvider && boardingAddress) {
477
- const findVoutOnTx = (tx) => {
478
- return tx.vout.findIndex((v) => v.scriptpubkey_address === boardingAddress);
479
- };
480
- onchainStopFunc = await this.onchainProvider.watchAddresses([boardingAddress], (txs) => {
481
- // find all onchain outputs belonging to our boarding address
482
- const coins = txs
483
- // filter txs where address is in output
484
- .filter((tx) => findVoutOnTx(tx) !== -1)
485
- // return boarding input as Coin
486
- .map((tx) => {
487
- const { txid, status } = tx;
488
- const vout = findVoutOnTx(tx);
489
- const value = Number(tx.vout[vout].value);
490
- return { txid, vout, value, status };
491
- });
492
- // and notify via callback
493
- eventCallback({
494
- type: "utxo",
495
- coins,
496
- });
497
- });
498
- }
499
- if (this.indexerProvider && arkAddress) {
500
- // Share the ContractWatcher's single subscription instead of
501
- // opening a second SSE stream.
502
- const cm = await this.getContractManager();
503
- // Serialize annotation+notification: parallel `annotateVtxos`
504
- // awaits could resolve out of order and deliver eventCallback
505
- // calls in the wrong sequence (e.g. `vtxo_spent` before its
506
- // matching `vtxo_received`).
507
- let annotationQueue = Promise.resolve();
508
- indexerStopFunc = cm.onContractEvent((event) => {
509
- if (event.type !== "vtxo_received" &&
510
- event.type !== "vtxo_spent") {
511
- return;
512
- }
513
- if (event.contract.type !== "default" &&
514
- event.contract.type !== "delegate") {
515
- return;
516
- }
517
- // `event.vtxos` carries placeholder tapscript fields from
518
- // the watcher; `annotateVtxos` fills them in.
519
- annotationQueue = annotationQueue.then(async () => {
520
- try {
521
- const annotated = await cm.annotateVtxos(event.vtxos);
522
- eventCallback({
523
- type: "vtxo",
524
- newVtxos: event.type === "vtxo_received" ? annotated : [],
525
- spentVtxos: event.type === "vtxo_spent" ? annotated : [],
526
- });
527
- }
528
- catch (error) {
529
- console.warn("Dropping subscription update after annotation failed; next sync will reconcile:", error);
530
- }
531
- });
532
- });
533
- }
534
- const stopFunc = () => {
535
- onchainStopFunc?.();
536
- indexerStopFunc?.();
537
- };
538
- return stopFunc;
539
- }
540
- /** Fetch Arkade transaction ids that are still pending final settlement. */
541
- async fetchPendingTxs() {
542
- // get non-swept virtual outputs, rely on the indexer only in case DB doesn't have the right state
543
- const scripts = await this.getWalletScripts();
544
- let { vtxos } = await this.indexerProvider.getVtxos({
545
- scripts,
546
- });
547
- return vtxos
548
- .filter((vtxo) => vtxo.virtualStatus.state !== "swept" &&
549
- vtxo.virtualStatus.state !== "settled" &&
550
- vtxo.arkTxId !== undefined)
551
- .map((_) => _.arkTxId);
552
- }
553
- // ========================================================================
554
- // Multi-script support (default + delegate addresses)
555
- // ========================================================================
556
- /**
557
- * Get all pkScript hex strings for the wallet's own addresses
558
- * (both delegate and non-delegate, current and historical).
559
- */
560
- async getWalletScripts() {
561
- const manager = await this.getContractManager();
562
- const contracts = await manager.getContracts({
563
- type: ["default", "delegate"],
564
- });
565
- return contracts.map((c) => c.script);
566
- }
567
- /**
568
- * Build a map of scriptHex → VtxoScript for all wallet contracts,
569
- * so virtual outputs can be extended with the correct tapscript per contract.
570
- */
571
- async getScriptMap() {
572
- const map = new Map();
573
- const manager = await this.getContractManager();
574
- const contracts = await manager.getContracts({
575
- type: ["default", "delegate"],
576
- });
577
- for (const contract of contracts) {
578
- if (map.has(contract.script))
579
- continue;
580
- const handler = handlers_1.contractHandlers.get(contract.type);
581
- if (handler) {
582
- const script = handler.createScript(contract.params);
583
- map.set(contract.script, script);
584
- }
585
- }
586
- return map;
587
- }
588
- // ========================================================================
589
- // Contract Management
590
- // ========================================================================
591
- /**
592
- * Get the ContractManager for managing contracts including the wallet's default address.
593
- *
594
- * The ContractManager handles:
595
- * - The wallet's default receiving address (as a "default" contract)
596
- * - External contracts (Boltz swaps, HTLCs, etc.)
597
- * - Multi-contract watching with resilient connections
598
- *
599
- * @example
600
- * ```typescript
601
- * const manager = await wallet.getContractManager();
602
- *
603
- * // Create a contract for a Boltz swap
604
- * const contract = await manager.createContract({
605
- * label: "Boltz Swap",
606
- * type: "vhtlc",
607
- * params: { ... },
608
- * script: swapScript,
609
- * address: swapAddress,
610
- * });
611
- *
612
- * // Start watching for events (includes wallet's default address)
613
- * const stop = await manager.onContractEvent((event) => {
614
- * console.log(`${event.type} on ${event.contractScript}`);
615
- * });
616
- * ```
617
- */
618
- async getContractManager() {
619
- // Return existing manager if already initialized
620
- if (this._contractManager) {
621
- return this._contractManager;
622
- }
623
- // If initialization is in progress, wait for it
624
- if (this._contractManagerInitializing) {
625
- return this._contractManagerInitializing;
626
- }
627
- // Start initialization and store the promise
628
- this._contractManagerInitializing = this.initializeContractManager();
629
- try {
630
- const manager = await this._contractManagerInitializing;
631
- this._contractManager = manager;
632
- return manager;
633
- }
634
- catch (error) {
635
- // Clear the initializing promise so subsequent calls can retry
636
- this._contractManagerInitializing = undefined;
637
- throw error;
638
- }
639
- finally {
640
- // Clear the initializing promise after completion
641
- this._contractManagerInitializing = undefined;
642
- }
643
- }
644
- async initializeContractManager() {
645
- const manager = await contractManager_1.ContractManager.create({
646
- indexerProvider: this.indexerProvider,
647
- contractRepository: this.contractRepository,
648
- walletRepository: this.walletRepository,
649
- watcherConfig: this.watcherConfig,
650
- });
651
- for (const csvTimelock of this.walletContractTimelocks) {
652
- const csvTimelockStr = (0, timelock_1.timelockToSequence)(csvTimelock).toString();
653
- const defaultScript = new default_1.DefaultVtxo.Script({
654
- pubKey: this.offchainTapscript.options.pubKey,
655
- serverPubKey: this.offchainTapscript.options.serverPubKey,
656
- csvTimelock,
657
- });
658
- await manager.createContract({
659
- type: "default",
660
- params: {
661
- pubKey: base_1.hex.encode(defaultScript.options.pubKey),
662
- serverPubKey: base_1.hex.encode(defaultScript.options.serverPubKey),
663
- csvTimelock: csvTimelockStr,
664
- },
665
- script: base_1.hex.encode(defaultScript.pkScript),
666
- address: defaultScript
667
- .address(this.network.hrp, this.arkServerPublicKey)
668
- .encode(),
669
- state: "active",
670
- });
671
- if (this.offchainTapscript instanceof delegate_1.DelegateVtxo.Script) {
672
- const delegateScript = new delegate_1.DelegateVtxo.Script({
673
- pubKey: this.offchainTapscript.options.pubKey,
674
- serverPubKey: this.offchainTapscript.options.serverPubKey,
675
- delegatePubKey: this.offchainTapscript.options.delegatePubKey,
676
- csvTimelock,
677
- });
678
- await manager.createContract({
679
- type: "delegate",
680
- params: {
681
- pubKey: base_1.hex.encode(delegateScript.options.pubKey),
682
- serverPubKey: base_1.hex.encode(delegateScript.options.serverPubKey),
683
- delegatePubKey: base_1.hex.encode(delegateScript.options.delegatePubKey),
684
- csvTimelock: csvTimelockStr,
685
- },
686
- script: base_1.hex.encode(delegateScript.pkScript),
687
- address: delegateScript
688
- .address(this.network.hrp, this.arkServerPublicKey)
689
- .encode(),
690
- state: "active",
691
- });
692
- }
693
- }
694
- return manager;
695
- }
696
- /** Dispose wallet-owned managers and release background resources. */
697
- async dispose() {
698
- const manager = this._contractManager ??
699
- (this._contractManagerInitializing
700
- ? await this._contractManagerInitializing.catch(() => undefined)
701
- : undefined);
702
- manager?.dispose();
703
- this._contractManager = undefined;
704
- this._contractManagerInitializing = undefined;
705
- }
706
- /** Async-dispose hook that forwards to `dispose()`. */
707
- async [Symbol.asyncDispose]() {
708
- await this.dispose();
709
- }
710
- }
711
- exports.ReadonlyWallet = ReadonlyWallet;
712
- /**
713
- * Main wallet implementation for Bitcoin transactions with Arkade protocol support.
714
- * The wallet does not store any data locally and relies on Arkade and onchain
715
- * providers to fetch onchain and virtual outputs.
716
- *
717
- * @example
718
- * ```typescript
719
- * // Create a wallet with URL configuration
720
- * const wallet = await Wallet.create({
721
- * identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
722
- * arkServerUrl: 'https://arkade.computer',
723
- * esploraUrl: 'https://mempool.space/api'
724
- * });
725
- *
726
- * // Or with custom provider instances (e.g., for Expo/React Native)
727
- * const wallet = await Wallet.create({
728
- * identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
729
- * arkProvider: new ExpoArkProvider('https://arkade.computer'),
730
- * indexerProvider: new ExpoIndexerProvider('https://arkade.computer'),
731
- * esploraUrl: 'https://mempool.space/api'
732
- * });
733
- *
734
- * // Get addresses
735
- * const arkAddress = await wallet.getAddress();
736
- * const boardingAddress = await wallet.getBoardingAddress();
737
- *
738
- * // Send bitcoin
739
- * const txid = await wallet.send({
740
- * address: 'ark1q...',
741
- * amount: 50000,
742
- * });
743
- * ```
744
- */
745
- class Wallet extends ReadonlyWallet {
746
- _addPendingSpends(inputs) {
747
- for (const input of inputs) {
748
- if ("virtualStatus" in input) {
749
- this._pendingSpendOutpoints.add(`${input.txid}:${input.vout}`);
750
- }
751
- }
752
- }
753
- _removePendingSpends(inputs) {
754
- for (const input of inputs) {
755
- if ("virtualStatus" in input) {
756
- this._pendingSpendOutpoints.delete(`${input.txid}:${input.vout}`);
757
- }
758
- }
759
- }
760
- _withTxLock(fn) {
761
- let release;
762
- const lock = new Promise((r) => (release = r));
763
- const prev = this._txLock;
764
- this._txLock = lock;
765
- return prev.then(async () => {
766
- try {
767
- return await fn();
768
- }
769
- finally {
770
- release();
771
- }
772
- });
773
- }
774
- constructor(identity, network, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository,
775
- /** @deprecated Use settlementConfig */
776
- renewalConfig, delegatorProvider, watcherConfig, settlementConfig, walletContractTimelocks) {
777
- super(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegatorProvider, watcherConfig, walletContractTimelocks);
778
- this.arkProvider = arkProvider;
779
- this.serverUnrollScript = serverUnrollScript;
780
- this.forfeitOutputScript = forfeitOutputScript;
781
- this.forfeitPubkey = forfeitPubkey;
782
- /**
783
- * Async mutex that serializes all operations submitting VTXOs to the Arkade
784
- * server (`settle`, `send`, `sendBitcoin`). This prevents VtxoManager's
785
- * background renewal from racing with user-initiated transactions for the
786
- * same VTXO inputs.
787
- */
788
- this._txLock = Promise.resolve();
789
- this.identity = identity;
790
- // Backwards-compatible: keep renewalConfig populated for any code reading it
791
- this.renewalConfig = {
792
- enabled: renewalConfig?.enabled ?? false,
793
- ...vtxo_manager_1.DEFAULT_RENEWAL_CONFIG,
794
- ...renewalConfig,
795
- };
796
- // Normalize: prefer settlementConfig, fall back to renewalConfig, default to enabled
797
- if (settlementConfig !== undefined) {
798
- this.settlementConfig = settlementConfig;
799
- }
800
- else if (renewalConfig && this.renewalConfig.enabled) {
801
- this.settlementConfig = {
802
- vtxoThreshold: renewalConfig.thresholdMs
803
- ? renewalConfig.thresholdMs / 1000
804
- : undefined,
805
- };
806
- }
807
- else if (renewalConfig) {
808
- // renewalConfig provided but not enabled → disabled
809
- this.settlementConfig = false;
810
- }
811
- else {
812
- // No config at all → enabled by default
813
- this.settlementConfig = { ...vtxo_manager_1.DEFAULT_SETTLEMENT_CONFIG };
814
- }
815
- this._delegatorManager = delegatorProvider
816
- ? new delegator_1.DelegatorManagerImpl(delegatorProvider, arkProvider, identity)
817
- : undefined;
818
- }
819
- get assetManager() {
820
- this._walletAssetManager ?? (this._walletAssetManager = new asset_manager_1.AssetManager(this));
821
- return this._walletAssetManager;
822
- }
823
- async getVtxoManager() {
824
- if (this._vtxoManager) {
825
- return this._vtxoManager;
826
- }
827
- if (this._vtxoManagerInitializing) {
828
- return this._vtxoManagerInitializing;
829
- }
830
- this._vtxoManagerInitializing = Promise.resolve(new vtxo_manager_1.VtxoManager(this, this.renewalConfig, this.settlementConfig));
831
- try {
832
- const manager = await this._vtxoManagerInitializing;
833
- this._vtxoManager = manager;
834
- return manager;
835
- }
836
- catch (error) {
837
- this._vtxoManagerInitializing = undefined;
838
- throw error;
839
- }
840
- finally {
841
- this._vtxoManagerInitializing = undefined;
842
- }
843
- }
844
- async dispose() {
845
- const manager = this._vtxoManager ??
846
- (this._vtxoManagerInitializing
847
- ? await this._vtxoManagerInitializing.catch(() => undefined)
848
- : undefined);
849
- try {
850
- if (manager) {
851
- await manager.dispose();
852
- }
853
- }
854
- catch {
855
- // best-effort teardown; ensure super.dispose() still runs
856
- }
857
- finally {
858
- this._vtxoManager = undefined;
859
- this._vtxoManagerInitializing = undefined;
860
- await super.dispose();
861
- }
862
- }
863
- /**
864
- * Create a full wallet and initialize its background managers.
865
- *
866
- * @param config - Wallet configuration
867
- * @returns A wallet ready to query balances and send transactions
868
- * @example
869
- * ```typescript
870
- * const wallet = await Wallet.create({
871
- * identity,
872
- * arkServerUrl: 'https://arkade.computer',
873
- * });
874
- * ```
875
- */
876
- static async create(config) {
877
- const pubkey = await config.identity.xOnlyPublicKey();
878
- if (!pubkey) {
879
- throw new Error("Invalid configured public key");
880
- }
881
- const setup = await ReadonlyWallet.setupWalletConfig(config, pubkey);
882
- // Compute Wallet-specific forfeit and unroll scripts
883
- // the serverUnrollScript is the one used to create output scripts of the checkpoint transactions
884
- let serverUnrollScript;
885
- try {
886
- const raw = base_1.hex.decode(setup.info.checkpointTapscript);
887
- serverUnrollScript = tapscript_1.CSVMultisigTapscript.decode(raw);
888
- }
889
- catch (e) {
890
- throw new Error("Invalid checkpointTapscript from server");
891
- }
892
- // parse the server forfeit address
893
- // server is expecting funds to be sent to this address
894
- const forfeitPubkey = base_1.hex.decode(setup.info.forfeitPubkey).slice(1);
895
- const forfeitAddress = (0, btc_signer_1.Address)(setup.network).decode(setup.info.forfeitAddress);
896
- const forfeitOutputScript = btc_signer_1.OutScript.encode(forfeitAddress);
897
- const wallet = new Wallet(config.identity, setup.network, setup.onchainProvider, setup.arkProvider, setup.indexerProvider, setup.serverPubKey, setup.offchainTapscript, setup.boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, setup.dustAmount, setup.walletRepository, setup.contractRepository, config.renewalConfig, config.delegatorProvider, config.watcherConfig, config.settlementConfig, setup.walletContractTimelocks);
898
- await wallet.getVtxoManager();
899
- return wallet;
900
- }
901
- /**
902
- * Convert this wallet to a readonly wallet.
903
- *
904
- * @returns A readonly wallet with the same configuration but readonly identity
905
- * @example
906
- * ```typescript
907
- * const wallet = await Wallet.create({ identity: MnemonicIdentity.fromMnemonic('abandon abandon...'), ... });
908
- * const readonlyWallet = await wallet.toReadonly();
909
- *
910
- * // Can query balance and addresses
911
- * const balance = await readonlyWallet.getBalance();
912
- * const address = await readonlyWallet.getAddress();
913
- *
914
- * // But cannot send transactions (type error)
915
- * // readonlyWallet.send(...); // TypeScript error
916
- * ```
917
- */
918
- async toReadonly() {
919
- // Check if the identity has a toReadonly method using type guard
920
- const readonlyIdentity = hasToReadonly(this.identity)
921
- ? await this.identity.toReadonly()
922
- : this.identity; // Identity extends ReadonlyIdentity, so this is safe
923
- return new ReadonlyWallet(readonlyIdentity, this.network, this.onchainProvider, this.indexerProvider, this.arkServerPublicKey, this.offchainTapscript, this.boardingTapscript, this.dustAmount, this.walletRepository, this.contractRepository, this.delegatorProvider, this.watcherConfig, this.walletContractTimelocks);
924
- }
925
- /** Returns the delegator manager when delegation support is configured. */
926
- async getDelegatorManager() {
927
- return this._delegatorManager;
928
- }
929
- /**
930
- * Send bitcoin to an Arkade address.
931
- *
932
- * @deprecated Use `send`.
933
- * @param params - Send parameters
934
- */
935
- async sendBitcoin(params) {
936
- if (params.amount <= 0) {
937
- throw new Error("Amount must be positive");
938
- }
939
- if (!(0, arkTransaction_1.isValidArkAddress)(params.address)) {
940
- throw new Error("Invalid Arkade address " + params.address);
941
- }
942
- if (params.selectedVtxos && params.selectedVtxos.length > 0) {
943
- return this._withTxLock(async () => {
944
- const selectedVtxoSum = params
945
- .selectedVtxos.map((v) => v.value)
946
- .reduce((a, b) => a + b, 0);
947
- if (selectedVtxoSum < params.amount) {
948
- throw new Error("Selected VTXOs do not cover specified amount");
949
- }
950
- const changeAmount = selectedVtxoSum - params.amount;
951
- const selected = {
952
- inputs: params.selectedVtxos,
953
- changeAmount: BigInt(changeAmount),
954
- };
955
- const outputAddress = address_1.ArkAddress.decode(params.address);
956
- const outputScript = BigInt(params.amount) < this.dustAmount
957
- ? outputAddress.subdustPkScript
958
- : outputAddress.pkScript;
959
- const outputs = [
960
- {
961
- script: outputScript,
962
- amount: BigInt(params.amount),
963
- },
964
- ];
965
- // add change output if needed
966
- if (selected.changeAmount > 0n) {
967
- const changeOutputScript = selected.changeAmount < this.dustAmount
968
- ? this.arkAddress.subdustPkScript
969
- : this.arkAddress.pkScript;
970
- outputs.push({
971
- script: changeOutputScript,
972
- amount: BigInt(selected.changeAmount),
973
- });
974
- }
975
- this._addPendingSpends(selected.inputs);
976
- try {
977
- const { arkTxid, signedCheckpointTxs } = await this.buildAndSubmitOffchainTx(selected.inputs, outputs);
978
- await this.updateDbAfterOffchainTx(selected.inputs, arkTxid, signedCheckpointTxs, params.amount, selected.changeAmount, selected.changeAmount > 0n ? outputs.length - 1 : 0);
979
- return arkTxid;
980
- }
981
- finally {
982
- this._removePendingSpends(selected.inputs);
983
- }
984
- });
985
- }
986
- return this.send({
987
- address: params.address,
988
- amount: params.amount,
989
- });
990
- }
991
- /**
992
- * Settle boarding inputs and/or virtual outputs into a finalized mainnet transaction.
993
- *
994
- * @param params - Optional settlement inputs and outputs. When omitted, the wallet settles all eligible funds.
995
- * @param eventCallback - Optional callback invoked for settlement stream events.
996
- * @returns The finalized Arkade transaction id
997
- */
998
- async settle(params, eventCallback) {
999
- return this._withTxLock(() => this._settleImpl(params, eventCallback));
1000
- }
1001
- async _settleImpl(params, eventCallback) {
1002
- if (params?.inputs) {
1003
- for (const input of params.inputs) {
1004
- // validate arknotes inputs
1005
- if (typeof input === "string") {
1006
- try {
1007
- arknote_1.ArkNote.fromString(input);
1008
- }
1009
- catch (e) {
1010
- throw new Error(`Invalid arknote "${input}"`);
1011
- }
1012
- }
1013
- }
1014
- }
1015
- // if no params are provided, use all non-expired boarding inputs and offchain virtual outputs as inputs
1016
- // and send all to the offchain address
1017
- if (!params) {
1018
- const { fees } = await this.arkProvider.getInfo();
1019
- const estimator = new arkfee_1.Estimator(fees.intentFee);
1020
- let amount = 0;
1021
- const exitScript = tapscript_1.CSVMultisigTapscript.decode(base_1.hex.decode(this.boardingTapscript.exitScript));
1022
- const boardingTimelock = exitScript.params.timelock;
1023
- // For block-based timelocks, fetch the chain tip height
1024
- let chainTipHeight;
1025
- if (boardingTimelock.type === "blocks") {
1026
- const tip = await this.onchainProvider.getChainTip();
1027
- chainTipHeight = tip.height;
1028
- }
1029
- const boardingUtxos = (await this.getBoardingUtxos()).filter((utxo) => utxo.status.confirmed &&
1030
- !(0, arkTransaction_1.hasBoardingTxExpired)(utxo, boardingTimelock, chainTipHeight));
1031
- const filteredBoardingUtxos = [];
1032
- for (const utxo of boardingUtxos) {
1033
- const inputFee = estimator.evalOnchainInput({
1034
- amount: BigInt(utxo.value),
1035
- });
1036
- if (inputFee.value >= utxo.value) {
1037
- // skip if fees are greater than the boarding input value
1038
- continue;
1039
- }
1040
- filteredBoardingUtxos.push(utxo);
1041
- amount += utxo.value - inputFee.satoshis;
1042
- }
1043
- const vtxos = await this.getVtxos({ withRecoverable: true });
1044
- const filteredVtxos = [];
1045
- for (const vtxo of vtxos) {
1046
- const inputFee = estimator.evalOffchainInput({
1047
- amount: BigInt(vtxo.value),
1048
- type: vtxo.virtualStatus.state === "swept"
1049
- ? "recoverable"
1050
- : "vtxo",
1051
- weight: 0,
1052
- birth: vtxo.createdAt,
1053
- expiry: vtxo.virtualStatus.batchExpiry
1054
- ? new Date(vtxo.virtualStatus.batchExpiry)
1055
- : undefined,
1056
- });
1057
- if (inputFee.satoshis >= vtxo.value) {
1058
- // skip if fees are greater than the virtual output value
1059
- continue;
1060
- }
1061
- filteredVtxos.push(vtxo);
1062
- amount += vtxo.value - inputFee.satoshis;
1063
- }
1064
- const inputs = [...filteredBoardingUtxos, ...filteredVtxos];
1065
- if (inputs.length === 0) {
1066
- throw new Error("No inputs found");
1067
- }
1068
- const output = {
1069
- address: await this.getAddress(),
1070
- amount: BigInt(amount),
1071
- };
1072
- const outputFee = estimator.evalOffchainOutput({
1073
- amount: output.amount,
1074
- script: base_1.hex.encode(address_1.ArkAddress.decode(output.address).pkScript),
1075
- });
1076
- output.amount -= BigInt(outputFee.satoshis);
1077
- if (output.amount <= this.dustAmount) {
1078
- throw new Error("Output amount is below dust limit");
1079
- }
1080
- params = {
1081
- inputs,
1082
- outputs: [output],
1083
- };
1084
- }
1085
- const onchainOutputIndexes = [];
1086
- const outputs = [];
1087
- let hasOffchainOutputs = false;
1088
- for (const [index, output] of params.outputs.entries()) {
1089
- let script;
1090
- try {
1091
- // offchain
1092
- const addr = address_1.ArkAddress.decode(output.address);
1093
- script = addr.pkScript;
1094
- hasOffchainOutputs = true;
1095
- }
1096
- catch {
1097
- // onchain
1098
- const addr = (0, btc_signer_1.Address)(this.network).decode(output.address);
1099
- script = btc_signer_1.OutScript.encode(addr);
1100
- onchainOutputIndexes.push(index);
1101
- }
1102
- outputs.push({
1103
- amount: output.amount,
1104
- script,
1105
- });
1106
- }
1107
- // if some of the inputs hold assets, build the asset packet and append as output
1108
- // in the intent proof tx, there is a "fake" input at index 0
1109
- // so the real coin indices are offset by +1
1110
- const assetInputs = new Map();
1111
- for (let i = 0; i < params.inputs.length; i++) {
1112
- if ("assets" in params.inputs[i]) {
1113
- const assets = params.inputs[i]
1114
- .assets;
1115
- if (assets && assets.length > 0) {
1116
- assetInputs.set(i + 1, assets);
1117
- }
1118
- }
1119
- }
1120
- let outputAssets;
1121
- const destinationScript = address_1.ArkAddress.decode(await this.getAddress()).pkScript;
1122
- const assetOutputIndex = (0, delegator_1.findDestinationOutputIndex)(outputs, destinationScript);
1123
- if (assetInputs.size > 0) {
1124
- if (assetOutputIndex === -1) {
1125
- throw new Error("Cannot assign assets: no output matches the destination address");
1126
- }
1127
- // collect all input assets and assign them to the destination output
1128
- const allAssets = new Map();
1129
- for (const [, assets] of assetInputs) {
1130
- for (const asset of assets) {
1131
- const existing = allAssets.get(asset.assetId) ?? 0n;
1132
- allAssets.set(asset.assetId, existing + asset.amount);
1133
- }
1134
- }
1135
- outputAssets = [];
1136
- for (const [assetId, amount] of allAssets) {
1137
- outputAssets.push({ assetId, amount });
1138
- }
1139
- }
1140
- const recipients = params.outputs.map((output, i) => ({
1141
- address: output.address,
1142
- amount: Number(output.amount),
1143
- assets: i === assetOutputIndex ? outputAssets : undefined,
1144
- }));
1145
- if (outputAssets && outputAssets.length > 0) {
1146
- const assetPacket = (0, asset_1.createAssetPacket)(assetInputs, recipients);
1147
- outputs.push(extension_1.Extension.create([assetPacket]).txOut());
1148
- }
1149
- // session holds the state of the musig2 signing process of the virtual output tree
1150
- let session;
1151
- const signingPublicKeys = [];
1152
- if (hasOffchainOutputs) {
1153
- session = this.identity.signerSession();
1154
- signingPublicKeys.push(base_1.hex.encode(await session.getPublicKey()));
1155
- }
1156
- const [intent, deleteIntent] = await Promise.all([
1157
- this.makeRegisterIntentSignature(params.inputs, outputs, onchainOutputIndexes, signingPublicKeys),
1158
- this.makeDeleteIntentSignature(params.inputs),
1159
- ]);
1160
- const topics = [
1161
- ...signingPublicKeys,
1162
- ...params.inputs.map((input) => `${input.txid}:${input.vout}`),
1163
- ];
1164
- const abortController = new AbortController();
1165
- let stream;
1166
- // Optimistically hide these inputs from concurrent getVtxos() callers
1167
- // while the settlement is in flight. Set before safeRegisterIntent so
1168
- // there's no window between intent registration and coin-visibility.
1169
- this._addPendingSpends(params.inputs);
1170
- try {
1171
- stream = this.arkProvider.getEventStream(abortController.signal, topics);
1172
- // Prime the iterator so the provider opens the SSE subscription
1173
- // before safeRegisterIntent can trigger server-side batch events.
1174
- const firstNext = stream.next();
1175
- // If settle exits before Batch.join consumes the primed result,
1176
- // keep the orphaned promise from surfacing as an unhandled rejection.
1177
- void firstNext.catch(() => { });
1178
- const primedStream = (async function* () {
1179
- const first = await firstNext;
1180
- if (!first.done) {
1181
- yield first.value;
1182
- }
1183
- yield* stream;
1184
- })();
1185
- const intentId = await this.safeRegisterIntent(intent, params.inputs);
1186
- const handler = this.createBatchHandler(intentId, params.inputs, recipients, session);
1187
- const commitmentTxid = await batch_1.Batch.join(primedStream, handler, {
1188
- abortController,
1189
- skipVtxoTreeSigning: !hasOffchainOutputs,
1190
- eventCallback: eventCallback
1191
- ? (event) => Promise.resolve(eventCallback(event))
1192
- : undefined,
1193
- });
1194
- await this.updateDbAfterSettle(params.inputs, commitmentTxid);
1195
- return commitmentTxid;
1196
- }
1197
- catch (error) {
1198
- // delete the intent to not be stuck in the queue. If deletion fails
1199
- // the intent stays on the server and the next settle will hit
1200
- // "duplicated input" in safeRegisterIntent — surface the failure
1201
- // rather than silently swallowing it.
1202
- const inputIds = params.inputs
1203
- .map((i) => `${i.txid}:${i.vout}`)
1204
- .join(",");
1205
- await this.arkProvider.deleteIntent(deleteIntent).catch((e) => {
1206
- console.warn(`Failed to delete intent after settle failure for inputs [${inputIds}]; intent may linger on server and cause 'duplicated input' on next settle`, e);
1207
- });
1208
- throw error;
1209
- }
1210
- finally {
1211
- // Clear state first so a synchronous handler firing from abort()
1212
- // never observes a stale pending-spend set.
1213
- this._removePendingSpends(params.inputs);
1214
- // close the stream — abort() fires the in-body handler if the
1215
- // generator has started iterating; return() also releases the
1216
- // eager resource if the body is still suspended or never ran
1217
- // (e.g. safeRegisterIntent threw before Batch.join was called).
1218
- abortController.abort();
1219
- await stream?.return?.().catch(() => { });
1220
- }
1221
- }
1222
- async handleSettlementFinalizationEvent(event, inputs, forfeitOutputScript, connectorsGraph) {
1223
- // the signed forfeits transactions to submit
1224
- const signedForfeits = [];
1225
- const isVtxo = (input) => "virtualStatus" in input;
1226
- let settlementPsbt = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(event.commitmentTx));
1227
- let hasBoardingUtxos = false;
1228
- let connectorIndex = 0;
1229
- const connectorsLeaves = connectorsGraph?.leaves() || [];
1230
- for (const input of inputs) {
1231
- // boarding input, we need to sign the settlement tx
1232
- if (!isVtxo(input)) {
1233
- for (let i = 0; i < settlementPsbt.inputsLength; i++) {
1234
- const settlementInput = settlementPsbt.getInput(i);
1235
- if (!settlementInput.txid ||
1236
- settlementInput.index === undefined) {
1237
- throw new Error("The server returned incomplete data. No settlement input found in the PSBT");
1238
- }
1239
- const inputTxId = base_1.hex.encode(settlementInput.txid);
1240
- if (inputTxId !== input.txid)
1241
- continue;
1242
- if (settlementInput.index !== input.vout)
1243
- continue;
1244
- // input found in the settlement tx, sign it
1245
- settlementPsbt.updateInput(i, {
1246
- tapLeafScript: [input.forfeitTapLeafScript],
1247
- });
1248
- settlementPsbt = await this.identity.sign(settlementPsbt, [
1249
- i,
1250
- ]);
1251
- hasBoardingUtxos = true;
1252
- break;
1253
- }
1254
- continue;
1255
- }
1256
- if ((0, _1.isRecoverable)(input) || (0, _1.isSubdust)(input, this.dustAmount)) {
1257
- // recoverable or subdust coin, we don't need to create a forfeit tx
1258
- continue;
1259
- }
1260
- if (connectorsLeaves.length === 0) {
1261
- throw new Error("connectors not received");
1262
- }
1263
- if (connectorIndex >= connectorsLeaves.length) {
1264
- throw new Error("not enough connectors received");
1265
- }
1266
- const connectorLeaf = connectorsLeaves[connectorIndex];
1267
- const connectorTxId = connectorLeaf.id;
1268
- const connectorOutput = connectorLeaf.getOutput(0);
1269
- if (!connectorOutput) {
1270
- throw new Error("connector output not found");
1271
- }
1272
- const connectorAmount = connectorOutput.amount;
1273
- const connectorPkScript = connectorOutput.script;
1274
- if (!connectorAmount || !connectorPkScript) {
1275
- throw new Error("invalid connector output");
1276
- }
1277
- connectorIndex++;
1278
- let forfeitTx = (0, forfeit_1.buildForfeitTx)([
1279
- {
1280
- txid: input.txid,
1281
- index: input.vout,
1282
- witnessUtxo: {
1283
- amount: BigInt(input.value),
1284
- script: base_2.VtxoScript.decode(input.tapTree).pkScript,
1285
- },
1286
- sighashType: btc_signer_1.SigHash.DEFAULT,
1287
- tapLeafScript: [input.forfeitTapLeafScript],
1288
- },
1289
- {
1290
- txid: connectorTxId,
1291
- index: 0,
1292
- witnessUtxo: {
1293
- amount: connectorAmount,
1294
- script: connectorPkScript,
1295
- },
1296
- },
1297
- ], forfeitOutputScript);
1298
- // do not sign the connector input
1299
- forfeitTx = await this.identity.sign(forfeitTx, [0]);
1300
- signedForfeits.push(base_1.base64.encode(forfeitTx.toPSBT()));
1301
- }
1302
- if (signedForfeits.length > 0 || hasBoardingUtxos) {
1303
- await this.arkProvider.submitSignedForfeitTxs(signedForfeits, hasBoardingUtxos
1304
- ? base_1.base64.encode(settlementPsbt.toPSBT())
1305
- : undefined);
1306
- }
1307
- }
1308
- /**
1309
- * Create a batch event handler for settlement flows.
1310
- *
1311
- * @param intentId - The intent ID.
1312
- * @param inputs - Inputs used by the intent.
1313
- * @param expectedRecipients - Expected recipients to validate in the virtual output tree.
1314
- * @param session - Optional musig2 signing session. When omitted, signing steps are skipped.
1315
- */
1316
- createBatchHandler(intentId, inputs, expectedRecipients, session) {
1317
- let sweepTapTreeRoot;
1318
- return {
1319
- onBatchStarted: async (event) => {
1320
- const utf8IntentId = new TextEncoder().encode(intentId);
1321
- const intentIdHash = (0, utils_js_1.sha256)(utf8IntentId);
1322
- const intentIdHashStr = base_1.hex.encode(intentIdHash);
1323
- let skip = true;
1324
- // check if our intent ID hash matches any in the event
1325
- for (const idHash of event.intentIdHashes) {
1326
- if (idHash === intentIdHashStr) {
1327
- if (!this.arkProvider) {
1328
- throw new Error("Arkade provider not configured");
1329
- }
1330
- await this.arkProvider.confirmRegistration(intentId);
1331
- skip = false;
1332
- }
1333
- }
1334
- if (skip) {
1335
- return { skip };
1336
- }
1337
- const sweepTapscript = tapscript_1.CSVMultisigTapscript.encode({
1338
- timelock: {
1339
- value: event.batchExpiry,
1340
- type: event.batchExpiry >= 512n ? "seconds" : "blocks",
1341
- },
1342
- pubkeys: [this.forfeitPubkey],
1343
- }).script;
1344
- sweepTapTreeRoot = (0, payment_js_1.tapLeafHash)(sweepTapscript);
1345
- return { skip: false };
1346
- },
1347
- onTreeSigningStarted: async (event, vtxoTree) => {
1348
- if (!session) {
1349
- return { skip: true };
1350
- }
1351
- if (!sweepTapTreeRoot) {
1352
- throw new Error("Sweep tap tree root not set");
1353
- }
1354
- const xOnlyPublicKeys = event.cosignersPublicKeys.map((k) => k.slice(2));
1355
- const signerPublicKey = await session.getPublicKey();
1356
- const xonlySignerPublicKey = signerPublicKey.subarray(1);
1357
- if (!xOnlyPublicKeys.includes(base_1.hex.encode(xonlySignerPublicKey))) {
1358
- // not a cosigner, skip the signing
1359
- return { skip: true };
1360
- }
1361
- // validate the unsigned virtual output tree
1362
- const commitmentTx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(event.unsignedCommitmentTx));
1363
- (0, validation_1.validateVtxoTxGraph)(vtxoTree, commitmentTx, sweepTapTreeRoot);
1364
- // validate that all expected receivers are in the virtual output tree with correct amounts and assets
1365
- if (expectedRecipients && expectedRecipients.length > 0) {
1366
- (0, validation_2.validateBatchRecipients)(commitmentTx, vtxoTree.leaves(), expectedRecipients, this.network);
1367
- }
1368
- const sharedOutput = commitmentTx.getOutput(0);
1369
- if (!sharedOutput?.amount) {
1370
- throw new Error("Shared output not found");
1371
- }
1372
- await session.init(vtxoTree, sweepTapTreeRoot, sharedOutput.amount);
1373
- const pubkey = base_1.hex.encode(await session.getPublicKey());
1374
- const nonces = await session.getNonces();
1375
- await this.arkProvider.submitTreeNonces(event.id, pubkey, nonces);
1376
- return { skip: false };
1377
- },
1378
- onTreeNonces: async (event) => {
1379
- if (!session) {
1380
- return { fullySigned: true }; // Signing complete (no signing needed)
1381
- }
1382
- const { hasAllNonces } = await session.aggregatedNonces(event.txid, event.nonces);
1383
- // wait to receive and aggregate all nonces before sending signatures
1384
- if (!hasAllNonces)
1385
- return { fullySigned: false };
1386
- const signatures = await session.sign();
1387
- const pubkey = base_1.hex.encode(await session.getPublicKey());
1388
- await this.arkProvider.submitTreeSignatures(event.id, pubkey, signatures);
1389
- return { fullySigned: true };
1390
- },
1391
- onBatchFinalization: async (event, _, connectorTree) => {
1392
- if (!this.forfeitOutputScript) {
1393
- throw new Error("Forfeit output script not set");
1394
- }
1395
- if (connectorTree) {
1396
- (0, validation_1.validateConnectorsTxGraph)(event.commitmentTx, connectorTree);
1397
- }
1398
- await this.handleSettlementFinalizationEvent(event, inputs, this.forfeitOutputScript, connectorTree);
1399
- },
1400
- };
1401
- }
1402
- async safeRegisterIntent(intent, inputs) {
1403
- try {
1404
- return await this.arkProvider.registerIntent(intent);
1405
- }
1406
- catch (error) {
1407
- // catch the "already registered by another intent" error
1408
- if (error instanceof errors_1.ArkError &&
1409
- error.code === 0 &&
1410
- error.message.includes("duplicated input")) {
1411
- // Clear any queued intent spending these exact inputs. The
1412
- // previous implementation signed a proof over getVtxos() only,
1413
- // which misses boarding UTXOs — the most common trigger for
1414
- // "duplicated input" on the auto-settle path. Signing the
1415
- // caller's own inputs keeps the proof surgical and correct
1416
- // regardless of whether the stuck input is a VTXO or boarding.
1417
- const deleteIntent = await this.makeDeleteIntentSignature(inputs);
1418
- await this.arkProvider.deleteIntent(deleteIntent);
1419
- // try again
1420
- return this.arkProvider.registerIntent(intent);
1421
- }
1422
- throw error;
1423
- }
1424
- }
1425
- async makeRegisterIntentSignature(coins, outputs, onchainOutputsIndexes, cosignerPubKeys, validAt) {
1426
- const message = {
1427
- type: "register",
1428
- onchain_output_indexes: onchainOutputsIndexes,
1429
- valid_at: validAt ? Math.floor(validAt) : 0,
1430
- expire_at: 0,
1431
- cosigners_public_keys: cosignerPubKeys,
1432
- };
1433
- const proof = intent_1.Intent.create(message, coins, outputs);
1434
- const signedProof = await this.identity.sign(proof);
1435
- return {
1436
- proof: base_1.base64.encode(signedProof.toPSBT()),
1437
- message,
1438
- };
1439
- }
1440
- async makeDeleteIntentSignature(coins) {
1441
- const message = {
1442
- type: "delete",
1443
- expire_at: 0,
1444
- };
1445
- const proof = intent_1.Intent.create(message, coins, []);
1446
- const signedProof = await this.identity.sign(proof);
1447
- return {
1448
- proof: base_1.base64.encode(signedProof.toPSBT()),
1449
- message,
1450
- };
1451
- }
1452
- async makeGetPendingTxIntentSignature(coins) {
1453
- const message = {
1454
- type: "get-pending-tx",
1455
- expire_at: 0,
1456
- };
1457
- const proof = intent_1.Intent.create(message, coins, []);
1458
- const signedProof = await this.identity.sign(proof);
1459
- return {
1460
- proof: base_1.base64.encode(signedProof.toPSBT()),
1461
- message,
1462
- };
1463
- }
1464
- /**
1465
- * Finalizes pending transactions by retrieving them from the server and finalizing each one.
1466
- * Skips the server check entirely when no send was interrupted (no pending tx flag set).
1467
- * @param vtxos - Optional list of virtual outputs to use instead of retrieving them from the server
1468
- * @returns Array of transaction IDs that were finalized
1469
- */
1470
- async finalizePendingTxs(vtxos) {
1471
- const hasPending = await this.hasPendingTxFlag();
1472
- if (!hasPending) {
1473
- return { finalized: [], pending: [] };
1474
- }
1475
- const MAX_INPUTS_PER_INTENT = 20;
1476
- if (!vtxos || vtxos.length === 0) {
1477
- // Batch all scripts into a single indexer call
1478
- const scriptMap = await this.getScriptMap();
1479
- const allExtended = [];
1480
- const allScripts = [...scriptMap.keys()];
1481
- const { vtxos: fetchedVtxos } = await this.indexerProvider.getVtxos({
1482
- scripts: allScripts,
1483
- });
1484
- for (const vtxo of fetchedVtxos) {
1485
- const vtxoScript = scriptMap.get(vtxo.script);
1486
- if (!vtxoScript)
1487
- continue;
1488
- if (vtxo.virtualStatus.state === "swept" ||
1489
- vtxo.virtualStatus.state === "settled") {
1490
- continue;
1491
- }
1492
- allExtended.push({
1493
- ...vtxo,
1494
- forfeitTapLeafScript: vtxoScript.forfeit(),
1495
- intentTapLeafScript: vtxoScript.forfeit(),
1496
- tapTree: vtxoScript.encode(),
1497
- });
1498
- }
1499
- if (allExtended.length === 0) {
1500
- return { finalized: [], pending: [] };
1501
- }
1502
- vtxos = allExtended;
1503
- }
1504
- const batches = [];
1505
- for (let i = 0; i < vtxos.length; i += MAX_INPUTS_PER_INTENT) {
1506
- batches.push(vtxos.slice(i, i + MAX_INPUTS_PER_INTENT));
1507
- }
1508
- // Track seen arkTxids so parallel batches don't finalize the same tx twice
1509
- const seen = new Set();
1510
- const results = await Promise.all(batches.map(async (batch) => {
1511
- const batchFinalized = [];
1512
- const batchPending = [];
1513
- const intent = await this.makeGetPendingTxIntentSignature(batch);
1514
- const pendingTxs = await this.arkProvider.getPendingTxs(intent);
1515
- for (const pendingTx of pendingTxs) {
1516
- if (seen.has(pendingTx.arkTxid))
1517
- continue;
1518
- seen.add(pendingTx.arkTxid);
1519
- batchPending.push(pendingTx.arkTxid);
1520
- try {
1521
- const finalCheckpoints = await Promise.all(pendingTx.signedCheckpointTxs.map(async (c) => {
1522
- const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(c));
1523
- const signedCheckpoint = await this.identity.sign(tx);
1524
- return base_1.base64.encode(signedCheckpoint.toPSBT());
1525
- }));
1526
- await this.arkProvider.finalizeTx(pendingTx.arkTxid, finalCheckpoints);
1527
- batchFinalized.push(pendingTx.arkTxid);
1528
- }
1529
- catch (error) {
1530
- console.error(`Failed to finalize transaction ${pendingTx.arkTxid}:`, error);
1531
- }
1532
- }
1533
- return {
1534
- finalized: batchFinalized,
1535
- pending: batchPending,
1536
- };
1537
- }));
1538
- const finalized = [];
1539
- const pending = [];
1540
- for (const result of results) {
1541
- finalized.push(...result.finalized);
1542
- pending.push(...result.pending);
1543
- }
1544
- // Only clear the flag if every discovered pending tx was finalized;
1545
- // if any failed, keep it so recovery retries on next startup.
1546
- if (finalized.length === pending.length) {
1547
- await this.setPendingTxFlag(false);
1548
- }
1549
- return { finalized, pending };
1550
- }
1551
- async hasPendingTxFlag() {
1552
- const state = await this.walletRepository.getWalletState();
1553
- return state?.settings?.hasPendingTx === true;
1554
- }
1555
- async setPendingTxFlag(value) {
1556
- await (0, syncCursors_1.updateWalletState)(this.walletRepository, (state) => ({
1557
- ...state,
1558
- settings: { ...state.settings, hasPendingTx: value },
1559
- }));
1560
- }
1561
- /**
1562
- * Send BTC and/or assets to one or more recipients.
1563
- *
1564
- * @param args - Recipients with their addresses, BTC amounts, and assets
1565
- * @returns Promise resolving to the Arkade transaction ID
1566
- *
1567
- * @example
1568
- * ```typescript
1569
- * const txid = await wallet.send({
1570
- * address: 'ark1q...',
1571
- * amount: 1000, // (optional, default to dust) btc amount to send to the output
1572
- * assets: [{ assetId: 'abc123...', amount: 50n }] // (optional) list of assets to send
1573
- * });
1574
- * ```
1575
- */
1576
- async send(...args) {
1577
- return this._withTxLock(() => this._sendImpl(...args));
1578
- }
1579
- async _sendImpl(...args) {
1580
- if (args.length === 0) {
1581
- throw new Error("At least one receiver is required");
1582
- }
1583
- // validate recipients and populate undefined amount with dust amount
1584
- const recipients = (0, utils_1.validateRecipients)(args, Number(this.dustAmount));
1585
- const address = await this.getAddress();
1586
- const outputAddress = address_1.ArkAddress.decode(address);
1587
- const virtualCoins = await this.getVtxos({
1588
- withRecoverable: false,
1589
- });
1590
- // keep track of asset changes
1591
- const assetChanges = new Map();
1592
- let selectedCoins = [];
1593
- let btcAmountToSelect = 0;
1594
- for (const recipient of recipients) {
1595
- btcAmountToSelect += Math.max(recipient.amount, Number(this.dustAmount));
1596
- }
1597
- // select assets
1598
- for (const recipient of recipients) {
1599
- if (!recipient.assets) {
1600
- continue;
1601
- }
1602
- for (const receiverAsset of recipient.assets) {
1603
- let amountToSelect = receiverAsset.amount;
1604
- // check if existing change covers the needed amount
1605
- const existingChange = assetChanges.get(receiverAsset.assetId) ?? 0n;
1606
- if (existingChange >= amountToSelect) {
1607
- assetChanges.set(receiverAsset.assetId, existingChange - amountToSelect);
1608
- if (assetChanges.get(receiverAsset.assetId) === 0n) {
1609
- assetChanges.delete(receiverAsset.assetId);
1610
- }
1611
- continue;
1612
- }
1613
- if (existingChange > 0n) {
1614
- amountToSelect -= existingChange;
1615
- assetChanges.delete(receiverAsset.assetId);
1616
- }
1617
- const availableCoins = virtualCoins.filter((c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout));
1618
- const { selected, totalAssetAmount } = (0, asset_1.selectCoinsWithAsset)(availableCoins, receiverAsset.assetId, amountToSelect);
1619
- for (const coin of selected) {
1620
- selectedCoins.push(coin);
1621
- // asset coins contain btc, subtract from total amount to select
1622
- btcAmountToSelect -= coin.value;
1623
- // coin may contain other assets, add them to asset changes
1624
- if (coin.assets) {
1625
- for (const a of coin.assets) {
1626
- if (a.assetId === receiverAsset.assetId) {
1627
- continue;
1628
- }
1629
- const existing = assetChanges.get(a.assetId) ?? 0n;
1630
- assetChanges.set(a.assetId, existing + a.amount);
1631
- }
1632
- }
1633
- }
1634
- const assetChangeAmount = totalAssetAmount - amountToSelect;
1635
- if (assetChangeAmount > 0n) {
1636
- const existing = assetChanges.get(receiverAsset.assetId) ?? 0n;
1637
- assetChanges.set(receiverAsset.assetId, existing + assetChangeAmount);
1638
- }
1639
- }
1640
- }
1641
- // select remaining btc
1642
- if (btcAmountToSelect > 0) {
1643
- const availableCoins = virtualCoins.filter((c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout));
1644
- const { inputs: btcCoins } = selectVirtualCoins(availableCoins, btcAmountToSelect);
1645
- // some coins may contain assets, add them to asset changes
1646
- for (const coin of btcCoins) {
1647
- if (coin.assets) {
1648
- for (const asset of coin.assets) {
1649
- const existing = assetChanges.get(asset.assetId) ?? 0n;
1650
- assetChanges.set(asset.assetId, existing + asset.amount);
1651
- }
1652
- }
1653
- }
1654
- selectedCoins = [...selectedCoins, ...btcCoins];
1655
- }
1656
- let totalBtcSelected = selectedCoins.reduce((sum, c) => sum + c.value, 0);
1657
- // build tx outputs
1658
- const outputs = recipients.map((recipient) => ({
1659
- script: recipient.script,
1660
- amount: BigInt(recipient.amount),
1661
- }));
1662
- const totalBtcOutput = outputs.reduce((sum, o) => sum + Number(o.amount), 0);
1663
- let changeAmount = totalBtcSelected - totalBtcOutput;
1664
- // enforce minimum change amount when there are asset changes
1665
- if (assetChanges.size > 0 && changeAmount < Number(this.dustAmount)) {
1666
- const availableCoins = virtualCoins.filter((c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout));
1667
- const { inputs: extraCoins } = selectVirtualCoins(availableCoins, Number(this.dustAmount) - changeAmount);
1668
- for (const coin of extraCoins) {
1669
- if (coin.assets) {
1670
- for (const asset of coin.assets) {
1671
- const existing = assetChanges.get(asset.assetId) ?? 0n;
1672
- assetChanges.set(asset.assetId, existing + asset.amount);
1673
- }
1674
- }
1675
- }
1676
- selectedCoins = [...selectedCoins, ...extraCoins];
1677
- totalBtcSelected += extraCoins.reduce((sum, c) => sum + c.value, 0);
1678
- changeAmount = totalBtcSelected - totalBtcOutput;
1679
- }
1680
- // build change receiver with BTC change and all asset changes
1681
- let changeReceiver;
1682
- let changeIndex = 0;
1683
- if (changeAmount > 0) {
1684
- const changeAssets = [];
1685
- for (const [assetId, amount] of assetChanges) {
1686
- if (amount > 0n) {
1687
- changeAssets.push({ assetId, amount });
1688
- }
1689
- }
1690
- changeIndex = outputs.length;
1691
- outputs.push({
1692
- script: BigInt(changeAmount) < this.dustAmount
1693
- ? outputAddress.subdustPkScript
1694
- : outputAddress.pkScript,
1695
- amount: BigInt(changeAmount),
1696
- });
1697
- changeReceiver = {
1698
- address: address,
1699
- amount: changeAmount,
1700
- assets: changeAssets.length > 0 ? changeAssets : undefined,
1701
- };
1702
- }
1703
- // create asset packet only if there are assets involved
1704
- const assetInputs = (0, asset_1.selectedCoinsToAssetInputs)(selectedCoins);
1705
- const hasAssets = assetInputs.size > 0 ||
1706
- recipients.some((r) => r.assets && r.assets.length > 0);
1707
- if (hasAssets) {
1708
- const assetPacket = (0, asset_1.createAssetPacket)(assetInputs, recipients, changeReceiver);
1709
- outputs.push(extension_1.Extension.create([assetPacket]).txOut());
1710
- }
1711
- const sentAmount = recipients.reduce((sum, r) => sum + r.amount, 0);
1712
- // Optimistically hide selected coins from concurrent getVtxos() while
1713
- // the offchain tx is in flight.
1714
- this._addPendingSpends(selectedCoins);
1715
- try {
1716
- const { arkTxid, signedCheckpointTxs } = await this.buildAndSubmitOffchainTx(selectedCoins, outputs);
1717
- await this.updateDbAfterOffchainTx(selectedCoins, arkTxid, signedCheckpointTxs, sentAmount, BigInt(changeAmount), changeReceiver ? changeIndex : 0, changeReceiver?.assets);
1718
- return arkTxid;
1719
- }
1720
- finally {
1721
- this._removePendingSpends(selectedCoins);
1722
- }
1723
- }
1724
- /**
1725
- * Build an offchain transaction from the given inputs and outputs,
1726
- * sign it, submit to the Arkade provider, and finalize.
1727
- * @returns The Arkade transaction id and server-signed checkpoint PSBTs (for bookkeeping)
1728
- */
1729
- async buildAndSubmitOffchainTx(inputs, outputs) {
1730
- const offchainTx = (0, arkTransaction_1.buildOffchainTx)(inputs.map((input) => {
1731
- return {
1732
- ...input,
1733
- tapLeafScript: input.forfeitTapLeafScript,
1734
- };
1735
- }), outputs, this.serverUnrollScript);
1736
- let signedVirtualTx;
1737
- let userSignedCheckpoints;
1738
- if ((0, identity_1.isBatchSignable)(this.identity)) {
1739
- // Batch-sign arkTx + all checkpoints in one wallet popup.
1740
- // Clone so the provider can't mutate originals before submitTx.
1741
- const requests = [
1742
- { tx: offchainTx.arkTx.clone() },
1743
- ...offchainTx.checkpoints.map((c) => ({ tx: c.clone() })),
1744
- ];
1745
- const signed = await this.identity.signMultiple(requests);
1746
- if (signed.length !== requests.length) {
1747
- throw new Error(`signMultiple returned ${signed.length} transactions, expected ${requests.length}`);
1748
- }
1749
- const [firstSignedTx, ...signedCheckpoints] = signed;
1750
- signedVirtualTx = firstSignedTx;
1751
- userSignedCheckpoints = signedCheckpoints;
1752
- }
1753
- else {
1754
- signedVirtualTx = await this.identity.sign(offchainTx.arkTx);
1755
- }
1756
- // Mark pending before submitting — if we crash between submit and
1757
- // finalize, the next init will recover via finalizePendingTxs.
1758
- await this.setPendingTxFlag(true);
1759
- const { arkTxid, signedCheckpointTxs } = await this.arkProvider.submitTx(base_1.base64.encode(signedVirtualTx.toPSBT()), offchainTx.checkpoints.map((c) => base_1.base64.encode(c.toPSBT())));
1760
- let finalCheckpoints;
1761
- if (userSignedCheckpoints) {
1762
- // Merge pre-signed user signatures onto server-signed checkpoints
1763
- finalCheckpoints = signedCheckpointTxs.map((c, i) => {
1764
- const serverSigned = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(c));
1765
- (0, arkTransaction_1.combineTapscriptSigs)(userSignedCheckpoints[i], serverSigned);
1766
- return base_1.base64.encode(serverSigned.toPSBT());
1767
- });
1768
- }
1769
- else {
1770
- // Legacy: sign each checkpoint individually (N popups)
1771
- finalCheckpoints = await Promise.all(signedCheckpointTxs.map(async (c) => {
1772
- const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(c));
1773
- const signedCheckpoint = await this.identity.sign(tx);
1774
- return base_1.base64.encode(signedCheckpoint.toPSBT());
1775
- }));
1776
- }
1777
- await this.arkProvider.finalizeTx(arkTxid, finalCheckpoints);
1778
- try {
1779
- await this.setPendingTxFlag(false);
1780
- }
1781
- catch (error) {
1782
- console.error("Failed to clear pending tx flag:", error);
1783
- }
1784
- return { arkTxid, signedCheckpointTxs };
1785
- }
1786
- // mark virtual outputs as spent, save change outputs if any
1787
- async updateDbAfterOffchainTx(inputs, arkTxid, signedCheckpointTxs, sentAmount, changeAmount, changeVout, changeAssets) {
1788
- try {
1789
- const spentVtxos = [];
1790
- const commitmentTxIds = new Set();
1791
- let batchExpiry = Number.MAX_SAFE_INTEGER;
1792
- if (inputs.length !== signedCheckpointTxs.length) {
1793
- console.warn(`updateDbAfterOffchainTx: inputs length (${inputs.length}) differs from signedCheckpointTxs length (${signedCheckpointTxs.length})`);
1794
- }
1795
- const safeLength = Math.min(inputs.length, signedCheckpointTxs.length);
1796
- const cm = await this.getContractManager();
1797
- const annotatedInputs = await cm.annotateVtxos(inputs);
1798
- for (const [inputIndex, vtxo] of annotatedInputs.entries()) {
1799
- if (inputIndex < safeLength &&
1800
- signedCheckpointTxs[inputIndex]) {
1801
- const checkpoint = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(signedCheckpointTxs[inputIndex]));
1802
- spentVtxos.push({
1803
- ...vtxo,
1804
- virtualStatus: {
1805
- ...vtxo.virtualStatus,
1806
- state: "spent",
1807
- },
1808
- spentBy: checkpoint.id,
1809
- arkTxId: arkTxid,
1810
- isSpent: true,
1811
- });
1812
- }
1813
- else {
1814
- spentVtxos.push({
1815
- ...vtxo,
1816
- virtualStatus: {
1817
- ...vtxo.virtualStatus,
1818
- state: "spent",
1819
- },
1820
- arkTxId: arkTxid,
1821
- isSpent: true,
1822
- });
1823
- }
1824
- if (vtxo.virtualStatus.commitmentTxIds) {
1825
- for (const id of vtxo.virtualStatus.commitmentTxIds) {
1826
- commitmentTxIds.add(id);
1827
- }
1828
- }
1829
- if (vtxo.virtualStatus.batchExpiry) {
1830
- batchExpiry = Math.min(batchExpiry, vtxo.virtualStatus.batchExpiry);
1831
- }
1832
- }
1833
- const createdAt = Date.now();
1834
- const primaryAddr = this.arkAddress.encode();
1835
- // Only save a change virtual output for preconfirmed coins (those with a batchExpiry).
1836
- // Inputs without a batchExpiry are already settled/unrolled and don't need tracking.
1837
- let changeVtxo;
1838
- if (changeAmount > 0n && batchExpiry !== Number.MAX_SAFE_INTEGER) {
1839
- changeVtxo = {
1840
- txid: arkTxid,
1841
- vout: changeVout,
1842
- createdAt: new Date(createdAt),
1843
- forfeitTapLeafScript: this.offchainTapscript.forfeit(),
1844
- intentTapLeafScript: this.offchainTapscript.forfeit(),
1845
- isUnrolled: false,
1846
- isSpent: false,
1847
- tapTree: this.offchainTapscript.encode(),
1848
- value: Number(changeAmount),
1849
- virtualStatus: {
1850
- state: "preconfirmed",
1851
- commitmentTxIds: Array.from(commitmentTxIds),
1852
- batchExpiry,
1853
- },
1854
- status: {
1855
- confirmed: false,
1856
- },
1857
- assets: changeAssets,
1858
- script: base_1.hex.encode(this.offchainTapscript.pkScript),
1859
- };
1860
- }
1861
- // Route spent rows to their owning contract bucket. The wallet's
1862
- // primary contract is registered with the manager at boot, so
1863
- // `addrByScript` already includes it; in a multi-contract spend
1864
- // each input may belong to a different contract.
1865
- const contracts = await cm.getContracts();
1866
- const addrByScript = new Map(contracts.map((c) => [c.script, c.address]));
1867
- const spentByScript = new Map();
1868
- for (const v of spentVtxos) {
1869
- if (!v.script) {
1870
- throw new Error(`Wallet.updateDbAfterOffchainTx: spent VTXO ${v.txid}:${v.vout} has no script`);
1871
- }
1872
- const arr = spentByScript.get(v.script) ?? [];
1873
- arr.push(v);
1874
- spentByScript.set(v.script, arr);
1875
- }
1876
- for (const [script, vtxos] of spentByScript) {
1877
- // User-initiated send path: a wrong-script row here means the
1878
- // wallet is about to record ownership against the wrong
1879
- // contract — fail loudly rather than persist inconsistent state.
1880
- (0, vtxoOwnership_1.validateVtxosForScript)(vtxos, script, "Wallet.updateDbAfterOffchainTx");
1881
- const targetAddr = addrByScript.get(script);
1882
- if (!targetAddr) {
1883
- throw new Error(`Wallet.updateDbAfterOffchainTx: no contract owns script ${script}`);
1884
- }
1885
- await (0, vtxoOwnership_1.saveVtxosForContract)(this.walletRepository, { script, address: targetAddr }, vtxos);
1886
- }
1887
- // Change is always primary-script by construction.
1888
- if (changeVtxo) {
1889
- await (0, vtxoOwnership_1.saveVtxosForContract)(this.walletRepository, { script: changeVtxo.script, address: primaryAddr }, [changeVtxo]);
1890
- }
1891
- await this.walletRepository.saveTransactions(primaryAddr, [
1892
- {
1893
- key: {
1894
- boardingTxid: "",
1895
- commitmentTxid: "",
1896
- arkTxid: arkTxid,
1897
- },
1898
- amount: sentAmount,
1899
- type: _1.TxType.TxSent,
1900
- settled: false,
1901
- createdAt,
1902
- },
1903
- ]);
1904
- }
1905
- catch (e) {
1906
- console.warn("error saving offchain tx to repository", e);
1907
- throw e;
1908
- }
1909
- }
1910
- // mark virtual outputs as spent/settled, remove boarding inputs
1911
- async updateDbAfterSettle(inputs, commitmentTxid) {
1912
- try {
1913
- const boardingAddress = await this.getBoardingAddress();
1914
- const spentVtxos = [];
1915
- const inputArkTxIds = new Set();
1916
- const boardingUtxoToRemove = new Set();
1917
- const isVtxo = (input) => "virtualStatus" in input;
1918
- const vtxoInputs = inputs.filter(isVtxo);
1919
- const cm = await this.getContractManager();
1920
- const annotatedVtxos = await cm.annotateVtxos(vtxoInputs);
1921
- const annotatedByKey = new Map(annotatedVtxos.map((v) => [`${v.txid}:${v.vout}`, v]));
1922
- for (const input of inputs) {
1923
- if (isVtxo(input)) {
1924
- // virtual output = mark it settled
1925
- const vtxo = annotatedByKey.get(`${input.txid}:${input.vout}`);
1926
- if (vtxo.arkTxId) {
1927
- inputArkTxIds.add(vtxo.arkTxId);
1928
- }
1929
- spentVtxos.push({
1930
- ...vtxo,
1931
- virtualStatus: {
1932
- ...vtxo.virtualStatus,
1933
- state: "settled",
1934
- },
1935
- settledBy: commitmentTxid,
1936
- isSpent: true,
1937
- });
1938
- }
1939
- else {
1940
- // boarding input = remove it
1941
- boardingUtxoToRemove.add(`${input.txid}:${input.vout}`);
1942
- }
1943
- }
1944
- if (spentVtxos.length > 0) {
1945
- // Route settled rows to their owning contract bucket. In a
1946
- // multi-contract settle the inputs may belong to several
1947
- // contracts; the wallet's primary contract is registered with
1948
- // the manager at boot, so its address is in `addrByScript`
1949
- // alongside the rest.
1950
- const contracts = await cm.getContracts();
1951
- const addrByScript = new Map(contracts.map((c) => [c.script, c.address]));
1952
- const byScript = new Map();
1953
- for (const v of spentVtxos) {
1954
- if (!v.script) {
1955
- throw new Error(`Wallet.updateDbAfterSettle: spent VTXO ${v.txid}:${v.vout} has no script`);
1956
- }
1957
- const arr = byScript.get(v.script) ?? [];
1958
- arr.push(v);
1959
- byScript.set(v.script, arr);
1960
- }
1961
- for (const [script, vtxos] of byScript) {
1962
- // User-initiated settle path: refuse to record a settle
1963
- // against the wrong script.
1964
- (0, vtxoOwnership_1.validateVtxosForScript)(vtxos, script, "Wallet.updateDbAfterSettle");
1965
- const targetAddr = addrByScript.get(script);
1966
- if (!targetAddr) {
1967
- throw new Error(`Wallet.updateDbAfterSettle: no contract owns script ${script}`);
1968
- }
1969
- await (0, vtxoOwnership_1.saveVtxosForContract)(this.walletRepository, { script, address: targetAddr }, vtxos);
1970
- }
1971
- }
1972
- if (boardingUtxoToRemove.size > 0) {
1973
- const currentUtxos = await this.walletRepository.getUtxos(boardingAddress);
1974
- const filtered = currentUtxos.filter((u) => !boardingUtxoToRemove.has(`${u.txid}:${u.vout}`));
1975
- // Clear and re-save the filtered list
1976
- await this.walletRepository.deleteUtxos(boardingAddress);
1977
- if (filtered.length > 0) {
1978
- await this.walletRepository.saveUtxos(boardingAddress, filtered);
1979
- }
1980
- }
1981
- }
1982
- catch (e) {
1983
- console.warn("error updating repository after settle", e);
1984
- throw e;
1985
- }
1986
- }
1987
- }
1988
- exports.Wallet = Wallet;
1989
- Wallet.MIN_FEE_RATE = 1; // sats/vbyte
1990
- /**
1991
- * Select virtual outputs to reach a target amount, prioritizing those closer to expiry
1992
- * @param coins List of virtual outputs to select from
1993
- * @param targetAmount Target amount to reach in satoshis
1994
- * @returns Selected virtual outputs and change amount
1995
- */
1996
- function selectVirtualCoins(coins, targetAmount) {
1997
- // Sort virtual outputs by expiry (ascending) and amount (descending)
1998
- const sortedCoins = [...coins].sort((a, b) => {
1999
- // First sort by expiry if available
2000
- const expiryA = a.virtualStatus.batchExpiry || Number.MAX_SAFE_INTEGER;
2001
- const expiryB = b.virtualStatus.batchExpiry || Number.MAX_SAFE_INTEGER;
2002
- if (expiryA !== expiryB) {
2003
- return expiryA - expiryB; // Earlier expiry first
2004
- }
2005
- // Then sort by amount
2006
- return b.value - a.value; // Larger amount first
2007
- });
2008
- const selectedCoins = [];
2009
- let selectedAmount = 0;
2010
- // Select coins until we have enough
2011
- for (const coin of sortedCoins) {
2012
- selectedCoins.push(coin);
2013
- selectedAmount += coin.value;
2014
- if (selectedAmount >= targetAmount) {
2015
- break;
2016
- }
2017
- }
2018
- if (selectedAmount === targetAmount) {
2019
- return { inputs: selectedCoins, changeAmount: 0n };
2020
- }
2021
- // Check if we have enough
2022
- if (selectedAmount < targetAmount) {
2023
- throw new Error("Insufficient funds");
2024
- }
2025
- const changeAmount = BigInt(selectedAmount - targetAmount);
2026
- return {
2027
- inputs: selectedCoins,
2028
- changeAmount,
2029
- };
2030
- }
2031
- /**
2032
- * Wait for incoming funds to the wallet
2033
- * @param wallet - The wallet to wait for incoming funds
2034
- * @returns A promise that resolves the next new coins received by the wallet's address
2035
- */
2036
- async function waitForIncomingFunds(wallet) {
2037
- let stopFunc;
2038
- return new Promise((resolve) => {
2039
- wallet
2040
- .notifyIncomingFunds((coins) => {
2041
- resolve(coins);
2042
- if (stopFunc)
2043
- stopFunc();
2044
- })
2045
- .then((stop) => {
2046
- stopFunc = stop;
2047
- });
2048
- });
2049
- }