@arkade-os/sdk 0.3.13 → 0.4.0-next.1

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 (270) hide show
  1. package/README.md +586 -54
  2. package/dist/cjs/asset/assetGroup.js +141 -0
  3. package/dist/cjs/asset/assetId.js +88 -0
  4. package/dist/cjs/asset/assetInput.js +204 -0
  5. package/dist/cjs/asset/assetOutput.js +159 -0
  6. package/dist/cjs/asset/assetRef.js +82 -0
  7. package/dist/cjs/asset/index.js +24 -0
  8. package/dist/cjs/asset/metadata.js +172 -0
  9. package/dist/cjs/asset/packet.js +164 -0
  10. package/dist/cjs/asset/types.js +25 -0
  11. package/dist/cjs/asset/utils.js +105 -0
  12. package/dist/cjs/bip322/index.js +270 -0
  13. package/dist/cjs/contracts/arkcontract.js +148 -0
  14. package/dist/cjs/contracts/contractManager.js +436 -0
  15. package/dist/cjs/contracts/contractWatcher.js +567 -0
  16. package/dist/cjs/contracts/handlers/default.js +85 -0
  17. package/dist/cjs/contracts/handlers/delegate.js +89 -0
  18. package/dist/cjs/contracts/handlers/helpers.js +105 -0
  19. package/dist/cjs/contracts/handlers/index.js +19 -0
  20. package/dist/cjs/contracts/handlers/registry.js +89 -0
  21. package/dist/cjs/contracts/handlers/vhtlc.js +193 -0
  22. package/dist/cjs/contracts/index.js +41 -0
  23. package/dist/cjs/contracts/types.js +2 -0
  24. package/dist/cjs/forfeit.js +12 -8
  25. package/dist/cjs/identity/index.js +1 -0
  26. package/dist/cjs/identity/seedIdentity.js +255 -0
  27. package/dist/cjs/index.js +72 -14
  28. package/dist/cjs/intent/index.js +47 -11
  29. package/dist/cjs/providers/ark.js +7 -0
  30. package/dist/cjs/providers/delegator.js +66 -0
  31. package/dist/cjs/providers/expoIndexer.js +5 -0
  32. package/dist/cjs/providers/indexer.js +68 -1
  33. package/dist/cjs/providers/utils.js +1 -0
  34. package/dist/cjs/repositories/contractRepository.js +0 -103
  35. package/dist/cjs/repositories/inMemory/contractRepository.js +55 -0
  36. package/dist/cjs/repositories/inMemory/walletRepository.js +80 -0
  37. package/dist/cjs/repositories/index.js +16 -0
  38. package/dist/cjs/repositories/indexedDB/contractRepository.js +187 -0
  39. package/dist/cjs/repositories/indexedDB/db.js +19 -0
  40. package/dist/cjs/repositories/indexedDB/manager.js +97 -0
  41. package/dist/cjs/repositories/indexedDB/schema.js +159 -0
  42. package/dist/cjs/repositories/indexedDB/walletRepository.js +338 -0
  43. package/dist/cjs/repositories/indexedDB/websqlAdapter.js +144 -0
  44. package/dist/cjs/repositories/migrations/contractRepositoryImpl.js +127 -0
  45. package/dist/cjs/repositories/migrations/fromStorageAdapter.js +66 -0
  46. package/dist/cjs/repositories/migrations/walletRepositoryImpl.js +180 -0
  47. package/dist/cjs/repositories/realm/contractRepository.js +120 -0
  48. package/dist/cjs/repositories/realm/index.js +9 -0
  49. package/dist/cjs/repositories/realm/schemas.js +108 -0
  50. package/dist/cjs/repositories/realm/types.js +7 -0
  51. package/dist/cjs/repositories/realm/walletRepository.js +273 -0
  52. package/dist/cjs/repositories/serialization.js +49 -0
  53. package/dist/cjs/repositories/sqlite/contractRepository.js +139 -0
  54. package/dist/cjs/repositories/sqlite/index.js +7 -0
  55. package/dist/cjs/repositories/sqlite/types.js +2 -0
  56. package/dist/cjs/repositories/sqlite/walletRepository.js +328 -0
  57. package/dist/cjs/repositories/walletRepository.js +0 -169
  58. package/dist/cjs/script/base.js +54 -0
  59. package/dist/cjs/script/delegate.js +49 -0
  60. package/dist/cjs/storage/asyncStorage.js +4 -1
  61. package/dist/cjs/storage/fileSystem.js +3 -0
  62. package/dist/cjs/storage/inMemory.js +3 -0
  63. package/dist/cjs/storage/indexedDB.js +5 -1
  64. package/dist/cjs/storage/localStorage.js +3 -0
  65. package/dist/cjs/utils/arkTransaction.js +16 -0
  66. package/dist/cjs/utils/transactionHistory.js +50 -0
  67. package/dist/cjs/wallet/asset-manager.js +338 -0
  68. package/dist/cjs/wallet/asset.js +117 -0
  69. package/dist/cjs/wallet/batch.js +1 -1
  70. package/dist/cjs/wallet/delegator.js +235 -0
  71. package/dist/cjs/wallet/expo/background.js +133 -0
  72. package/dist/cjs/wallet/expo/index.js +9 -0
  73. package/dist/cjs/wallet/expo/wallet.js +231 -0
  74. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +568 -0
  75. package/dist/cjs/wallet/serviceWorker/wallet.js +383 -102
  76. package/dist/cjs/wallet/utils.js +58 -0
  77. package/dist/cjs/wallet/validation.js +151 -0
  78. package/dist/cjs/wallet/vtxo-manager.js +8 -1
  79. package/dist/cjs/wallet/wallet.js +702 -260
  80. package/dist/cjs/worker/browser/service-worker-manager.js +82 -0
  81. package/dist/cjs/{wallet/serviceWorker → worker/browser}/utils.js +2 -1
  82. package/dist/cjs/worker/expo/asyncStorageTaskQueue.js +78 -0
  83. package/dist/cjs/worker/expo/index.js +12 -0
  84. package/dist/cjs/worker/expo/processors/contractPollProcessor.js +61 -0
  85. package/dist/cjs/worker/expo/processors/index.js +6 -0
  86. package/dist/cjs/worker/expo/taskQueue.js +41 -0
  87. package/dist/cjs/worker/expo/taskRunner.js +57 -0
  88. package/dist/cjs/worker/messageBus.js +252 -0
  89. package/dist/esm/asset/assetGroup.js +137 -0
  90. package/dist/esm/asset/assetId.js +84 -0
  91. package/dist/esm/asset/assetInput.js +199 -0
  92. package/dist/esm/asset/assetOutput.js +154 -0
  93. package/dist/esm/asset/assetRef.js +78 -0
  94. package/dist/esm/asset/index.js +8 -0
  95. package/dist/esm/asset/metadata.js +167 -0
  96. package/dist/esm/asset/packet.js +159 -0
  97. package/dist/esm/asset/types.js +22 -0
  98. package/dist/esm/asset/utils.js +99 -0
  99. package/dist/esm/bip322/index.js +267 -0
  100. package/dist/esm/contracts/arkcontract.js +141 -0
  101. package/dist/esm/contracts/contractManager.js +432 -0
  102. package/dist/esm/contracts/contractWatcher.js +563 -0
  103. package/dist/esm/contracts/handlers/default.js +82 -0
  104. package/dist/esm/contracts/handlers/delegate.js +86 -0
  105. package/dist/esm/contracts/handlers/helpers.js +66 -0
  106. package/dist/esm/contracts/handlers/index.js +12 -0
  107. package/dist/esm/contracts/handlers/registry.js +86 -0
  108. package/dist/esm/contracts/handlers/vhtlc.js +190 -0
  109. package/dist/esm/contracts/index.js +13 -0
  110. package/dist/esm/contracts/types.js +1 -0
  111. package/dist/esm/forfeit.js +11 -8
  112. package/dist/esm/identity/index.js +1 -0
  113. package/dist/esm/identity/seedIdentity.js +249 -0
  114. package/dist/esm/index.js +28 -15
  115. package/dist/esm/intent/index.js +44 -9
  116. package/dist/esm/providers/ark.js +7 -0
  117. package/dist/esm/providers/delegator.js +62 -0
  118. package/dist/esm/providers/expoIndexer.js +5 -0
  119. package/dist/esm/providers/indexer.js +68 -1
  120. package/dist/esm/providers/utils.js +1 -0
  121. package/dist/esm/repositories/contractRepository.js +1 -101
  122. package/dist/esm/repositories/inMemory/contractRepository.js +51 -0
  123. package/dist/esm/repositories/inMemory/walletRepository.js +76 -0
  124. package/dist/esm/repositories/index.js +8 -0
  125. package/dist/esm/repositories/indexedDB/contractRepository.js +183 -0
  126. package/dist/esm/repositories/indexedDB/db.js +4 -0
  127. package/dist/esm/repositories/indexedDB/manager.js +92 -0
  128. package/dist/esm/repositories/indexedDB/schema.js +155 -0
  129. package/dist/esm/repositories/indexedDB/walletRepository.js +334 -0
  130. package/dist/esm/repositories/indexedDB/websqlAdapter.js +138 -0
  131. package/dist/esm/repositories/migrations/contractRepositoryImpl.js +121 -0
  132. package/dist/esm/repositories/migrations/fromStorageAdapter.js +58 -0
  133. package/dist/esm/repositories/migrations/walletRepositoryImpl.js +176 -0
  134. package/dist/esm/repositories/realm/contractRepository.js +116 -0
  135. package/dist/esm/repositories/realm/index.js +3 -0
  136. package/dist/esm/repositories/realm/schemas.js +105 -0
  137. package/dist/esm/repositories/realm/types.js +6 -0
  138. package/dist/esm/repositories/realm/walletRepository.js +269 -0
  139. package/dist/esm/repositories/serialization.js +40 -0
  140. package/dist/esm/repositories/sqlite/contractRepository.js +135 -0
  141. package/dist/esm/repositories/sqlite/index.js +2 -0
  142. package/dist/esm/repositories/sqlite/types.js +1 -0
  143. package/dist/esm/repositories/sqlite/walletRepository.js +324 -0
  144. package/dist/esm/repositories/walletRepository.js +1 -167
  145. package/dist/esm/script/base.js +21 -1
  146. package/dist/esm/script/delegate.js +46 -0
  147. package/dist/esm/storage/asyncStorage.js +4 -1
  148. package/dist/esm/storage/fileSystem.js +3 -0
  149. package/dist/esm/storage/inMemory.js +3 -0
  150. package/dist/esm/storage/indexedDB.js +5 -1
  151. package/dist/esm/storage/localStorage.js +3 -0
  152. package/dist/esm/utils/arkTransaction.js +15 -0
  153. package/dist/esm/utils/transactionHistory.js +50 -0
  154. package/dist/esm/wallet/asset-manager.js +333 -0
  155. package/dist/esm/wallet/asset.js +111 -0
  156. package/dist/esm/wallet/batch.js +1 -1
  157. package/dist/esm/wallet/delegator.js +231 -0
  158. package/dist/esm/wallet/expo/background.js +128 -0
  159. package/dist/esm/wallet/expo/index.js +2 -0
  160. package/dist/esm/wallet/expo/wallet.js +194 -0
  161. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +564 -0
  162. package/dist/esm/wallet/serviceWorker/wallet.js +382 -101
  163. package/dist/esm/wallet/utils.js +54 -0
  164. package/dist/esm/wallet/validation.js +139 -0
  165. package/dist/esm/wallet/vtxo-manager.js +8 -1
  166. package/dist/esm/wallet/wallet.js +704 -229
  167. package/dist/esm/worker/browser/service-worker-manager.js +76 -0
  168. package/dist/esm/{wallet/serviceWorker → worker/browser}/utils.js +2 -1
  169. package/dist/esm/worker/expo/asyncStorageTaskQueue.js +74 -0
  170. package/dist/esm/worker/expo/index.js +4 -0
  171. package/dist/esm/worker/expo/processors/contractPollProcessor.js +58 -0
  172. package/dist/esm/worker/expo/processors/index.js +1 -0
  173. package/dist/esm/worker/expo/taskQueue.js +37 -0
  174. package/dist/esm/worker/expo/taskRunner.js +54 -0
  175. package/dist/esm/worker/messageBus.js +248 -0
  176. package/dist/types/asset/assetGroup.d.ts +28 -0
  177. package/dist/types/asset/assetId.d.ts +19 -0
  178. package/dist/types/asset/assetInput.d.ts +46 -0
  179. package/dist/types/asset/assetOutput.d.ts +39 -0
  180. package/dist/types/asset/assetRef.d.ts +25 -0
  181. package/dist/types/asset/index.d.ts +8 -0
  182. package/dist/types/asset/metadata.d.ts +37 -0
  183. package/dist/types/asset/packet.d.ts +27 -0
  184. package/dist/types/asset/types.d.ts +18 -0
  185. package/dist/types/asset/utils.d.ts +21 -0
  186. package/dist/types/bip322/index.d.ts +55 -0
  187. package/dist/types/contracts/arkcontract.d.ts +101 -0
  188. package/dist/types/contracts/contractManager.d.ts +331 -0
  189. package/dist/types/contracts/contractWatcher.d.ts +192 -0
  190. package/dist/types/contracts/handlers/default.d.ts +19 -0
  191. package/dist/types/contracts/handlers/delegate.d.ts +21 -0
  192. package/dist/types/contracts/handlers/helpers.d.ts +18 -0
  193. package/dist/types/contracts/handlers/index.d.ts +7 -0
  194. package/dist/types/contracts/handlers/registry.d.ts +65 -0
  195. package/dist/types/contracts/handlers/vhtlc.d.ts +32 -0
  196. package/dist/types/contracts/index.d.ts +14 -0
  197. package/dist/types/contracts/types.d.ts +222 -0
  198. package/dist/types/forfeit.d.ts +2 -1
  199. package/dist/types/identity/index.d.ts +1 -0
  200. package/dist/types/identity/seedIdentity.d.ts +128 -0
  201. package/dist/types/index.d.ts +22 -12
  202. package/dist/types/intent/index.d.ts +15 -1
  203. package/dist/types/providers/ark.d.ts +11 -2
  204. package/dist/types/providers/delegator.d.ts +29 -0
  205. package/dist/types/providers/indexer.d.ts +11 -1
  206. package/dist/types/repositories/contractRepository.d.ts +30 -19
  207. package/dist/types/repositories/inMemory/contractRepository.d.ts +17 -0
  208. package/dist/types/repositories/inMemory/walletRepository.d.ts +26 -0
  209. package/dist/types/repositories/index.d.ts +7 -0
  210. package/dist/types/repositories/indexedDB/contractRepository.d.ts +21 -0
  211. package/dist/types/repositories/indexedDB/db.d.ts +4 -0
  212. package/dist/types/repositories/indexedDB/manager.d.ts +22 -0
  213. package/dist/types/repositories/indexedDB/schema.d.ts +8 -0
  214. package/dist/types/repositories/indexedDB/walletRepository.d.ts +25 -0
  215. package/dist/types/repositories/indexedDB/websqlAdapter.d.ts +49 -0
  216. package/dist/types/repositories/migrations/contractRepositoryImpl.d.ts +24 -0
  217. package/dist/types/repositories/migrations/fromStorageAdapter.d.ts +19 -0
  218. package/dist/types/repositories/migrations/walletRepositoryImpl.d.ts +27 -0
  219. package/dist/types/repositories/realm/contractRepository.d.ts +24 -0
  220. package/dist/types/repositories/realm/index.d.ts +4 -0
  221. package/dist/types/repositories/realm/schemas.d.ts +208 -0
  222. package/dist/types/repositories/realm/types.d.ts +16 -0
  223. package/dist/types/repositories/realm/walletRepository.d.ts +31 -0
  224. package/dist/types/repositories/serialization.d.ts +40 -0
  225. package/dist/types/repositories/sqlite/contractRepository.d.ts +33 -0
  226. package/dist/types/repositories/sqlite/index.d.ts +3 -0
  227. package/dist/types/repositories/sqlite/types.d.ts +18 -0
  228. package/dist/types/repositories/sqlite/walletRepository.d.ts +40 -0
  229. package/dist/types/repositories/walletRepository.d.ts +13 -24
  230. package/dist/types/script/base.d.ts +1 -0
  231. package/dist/types/script/delegate.d.ts +36 -0
  232. package/dist/types/storage/asyncStorage.d.ts +4 -0
  233. package/dist/types/storage/fileSystem.d.ts +3 -0
  234. package/dist/types/storage/inMemory.d.ts +3 -0
  235. package/dist/types/storage/index.d.ts +3 -0
  236. package/dist/types/storage/indexedDB.d.ts +3 -0
  237. package/dist/types/storage/localStorage.d.ts +3 -0
  238. package/dist/types/utils/arkTransaction.d.ts +6 -0
  239. package/dist/types/wallet/asset-manager.d.ts +78 -0
  240. package/dist/types/wallet/asset.d.ts +21 -0
  241. package/dist/types/wallet/batch.d.ts +1 -1
  242. package/dist/types/wallet/delegator.d.ts +24 -0
  243. package/dist/types/wallet/expo/background.d.ts +66 -0
  244. package/dist/types/wallet/expo/index.d.ts +4 -0
  245. package/dist/types/wallet/expo/wallet.d.ts +97 -0
  246. package/dist/types/wallet/index.d.ts +75 -2
  247. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +366 -0
  248. package/dist/types/wallet/serviceWorker/wallet.d.ts +20 -11
  249. package/dist/types/wallet/utils.d.ts +12 -1
  250. package/dist/types/wallet/validation.d.ts +24 -0
  251. package/dist/types/wallet/wallet.d.ts +111 -17
  252. package/dist/types/worker/browser/service-worker-manager.d.ts +21 -0
  253. package/dist/types/{wallet/serviceWorker → worker/browser}/utils.d.ts +2 -1
  254. package/dist/types/worker/expo/asyncStorageTaskQueue.d.ts +46 -0
  255. package/dist/types/worker/expo/index.d.ts +7 -0
  256. package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +14 -0
  257. package/dist/types/worker/expo/processors/index.d.ts +1 -0
  258. package/dist/types/worker/expo/taskQueue.d.ts +50 -0
  259. package/dist/types/worker/expo/taskRunner.d.ts +42 -0
  260. package/dist/types/worker/messageBus.d.ts +109 -0
  261. package/package.json +69 -11
  262. package/dist/cjs/wallet/serviceWorker/request.js +0 -78
  263. package/dist/cjs/wallet/serviceWorker/response.js +0 -222
  264. package/dist/cjs/wallet/serviceWorker/worker.js +0 -655
  265. package/dist/esm/wallet/serviceWorker/request.js +0 -75
  266. package/dist/esm/wallet/serviceWorker/response.js +0 -219
  267. package/dist/esm/wallet/serviceWorker/worker.js +0 -651
  268. package/dist/types/wallet/serviceWorker/request.d.ts +0 -74
  269. package/dist/types/wallet/serviceWorker/response.d.ts +0 -123
  270. package/dist/types/wallet/serviceWorker/worker.d.ts +0 -53
package/README.md CHANGED
@@ -16,10 +16,18 @@ npm install @arkade-os/sdk
16
16
  ### Creating a Wallet
17
17
 
18
18
  ```typescript
19
- import { SingleKey, Wallet } from '@arkade-os/sdk'
19
+ import {
20
+ MnemonicIdentity,
21
+ Wallet,
22
+ IndexedDBWalletRepository,
23
+ IndexedDBContractRepository
24
+ } from '@arkade-os/sdk'
25
+ import { generateMnemonic } from '@scure/bip39'
26
+ import { wordlist } from '@scure/bip39/wordlists/english.js'
20
27
 
21
- // Create a new in-memory key (or use an external signer)
22
- const identity = SingleKey.fromHex('your_private_key_hex')
28
+ // Generate a new mnemonic or use an existing one
29
+ const mnemonic = generateMnemonic(wordlist)
30
+ const identity = MnemonicIdentity.fromMnemonic(mnemonic, { isMainnet: false })
23
31
 
24
32
  // Create a wallet with Ark support
25
33
  const wallet = await Wallet.create({
@@ -27,8 +35,11 @@ const wallet = await Wallet.create({
27
35
  // Esplora API, can be left empty - mempool.space API will be used
28
36
  esploraUrl: 'https://mutinynet.com/api',
29
37
  arkServerUrl: 'https://mutinynet.arkade.sh',
30
- // Optional: specify storage adapter (defaults to InMemoryStorageAdapter)
31
- // storage: new LocalStorageAdapter() // for browser persistence
38
+ // Optional: provide repositories for persistence (defaults to IndexedDB)
39
+ // storage: {
40
+ // walletRepository: new IndexedDBWalletRepository('my-wallet-db'),
41
+ // contractRepository: new IndexedDBContractRepository('my-wallet-db')
42
+ // }
32
43
  })
33
44
  ```
34
45
 
@@ -103,11 +114,88 @@ const readonlyWallet = await ReadonlyWallet.create({
103
114
  })
104
115
  ```
105
116
 
106
- **Benefits:**
107
- - ✅ Type-safe: Transaction methods don't exist on readonly types
108
- - Secure: Private keys never leave the signing environment
109
- - ✅ Flexible: Convert between full and readonly wallets as needed
110
- - Same API: Query operations work identically on both wallet types
117
+ ### Seed & Mnemonic Identity (Recommended)
118
+
119
+ The SDK supports key derivation from BIP39 mnemonic phrases or raw seeds using BIP86 (Taproot) output descriptors. This is the recommended identity type for new integrations — it uses standard derivation paths that are interoperable with other wallets and HD-ready for future multi-address support.
120
+
121
+ > **Note:** Prefer `MnemonicIdentity` or `SeedIdentity` over `SingleKey` for new applications. `SingleKey` exists for backward compatibility with raw private keys.
122
+
123
+ #### Creating from Mnemonic
124
+
125
+ ```typescript
126
+ import { MnemonicIdentity, Wallet } from '@arkade-os/sdk'
127
+ import { generateMnemonic } from '@scure/bip39'
128
+ import { wordlist } from '@scure/bip39/wordlists/english.js'
129
+
130
+ // Generate a new 12-word mnemonic
131
+ const mnemonic = generateMnemonic(wordlist)
132
+
133
+ // Create identity from a 12 or 24 word mnemonic
134
+ const identity = MnemonicIdentity.fromMnemonic(mnemonic, { isMainnet: true })
135
+
136
+ // With optional passphrase for additional security
137
+ const identityWithPassphrase = MnemonicIdentity.fromMnemonic(mnemonic, {
138
+ isMainnet: true,
139
+ passphrase: 'my secret passphrase'
140
+ })
141
+
142
+ // Create wallet as usual
143
+ const wallet = await Wallet.create({
144
+ identity,
145
+ arkServerUrl: 'https://mutinynet.arkade.sh'
146
+ })
147
+ ```
148
+
149
+ #### Creating from Raw Seed
150
+
151
+ ```typescript
152
+ import { SeedIdentity } from '@arkade-os/sdk'
153
+ import { mnemonicToSeedSync } from '@scure/bip39'
154
+
155
+ // If you already have a 64-byte seed
156
+ const seed = mnemonicToSeedSync(mnemonic)
157
+ const identity = SeedIdentity.fromSeed(seed, { isMainnet: true })
158
+
159
+ // Or with a custom output descriptor
160
+ const identity2 = SeedIdentity.fromSeed(seed, { descriptor })
161
+
162
+ // Or with a custom descriptor and passphrase (MnemonicIdentity)
163
+ const identity3 = MnemonicIdentity.fromMnemonic(mnemonic, {
164
+ descriptor,
165
+ passphrase: 'my secret passphrase'
166
+ })
167
+ ```
168
+
169
+ #### Watch-Only with ReadonlyDescriptorIdentity
170
+
171
+ Create watch-only wallets from an output descriptor:
172
+
173
+ ```typescript
174
+ import { ReadonlyDescriptorIdentity, ReadonlyWallet } from '@arkade-os/sdk'
175
+
176
+ // From a full identity
177
+ const readonly = await identity.toReadonly()
178
+
179
+ // Or directly from a descriptor (e.g., from another wallet)
180
+ const descriptor = "tr([12345678/86'/0'/0']xpub.../0/0)"
181
+ const readonlyFromDescriptor = ReadonlyDescriptorIdentity.fromDescriptor(descriptor)
182
+
183
+ // Use in a watch-only wallet
184
+ const readonlyWallet = await ReadonlyWallet.create({
185
+ identity: readonly,
186
+ arkServerUrl: 'https://mutinynet.arkade.sh'
187
+ })
188
+
189
+ // Can query but not sign
190
+ const balance = await readonlyWallet.getBalance()
191
+ ```
192
+
193
+ **Derivation Path:** `m/86'/{coinType}'/0'/0/0`
194
+ - BIP86 (Taproot) purpose
195
+ - Coin type 0 for mainnet, 1 for testnet
196
+ - Account 0, external chain, first address
197
+
198
+ The descriptor format (`tr([fingerprint/path']xpub.../0/0)`) is HD-ready — future versions will support deriving multiple addresses and change outputs from the same seed.
111
199
 
112
200
  ### Receiving Bitcoin
113
201
 
@@ -169,6 +257,42 @@ const txid = await wallet.sendBitcoin({
169
257
  })
170
258
  ```
171
259
 
260
+ ### Assets (Issue, Reissue, Burn, Send)
261
+
262
+ The wallet's `assetManager` lets you create and manage assets on Ark. `send` method supports sending assets.
263
+
264
+ ```typescript
265
+ // Issue a new asset (non-reissuable by default)
266
+ const controlAssetIssuance = await wallet.assetManager.issue({
267
+ amount: 1000,
268
+ metadata: { name: 'My Token', ticker: 'MTK', decimals: 8 },
269
+ })
270
+
271
+ // Issue a new asset using the control asset as reference
272
+ const assetIssuance = await wallet.assetManager.issue({
273
+ amount: 500,
274
+ controlAssetId: controlAssetIssuance.assetId,
275
+ })
276
+
277
+ // Reissue more supply of the asset, need ownership of the control asset
278
+ const reissuanceTxid = await wallet.assetManager.reissue({
279
+ assetId: assetIssuance.assetId,
280
+ amount: 500,
281
+ })
282
+
283
+ // Burn some of the asset
284
+ const burnTxid = await wallet.assetManager.burn({
285
+ assetId: assetIssuance.assetId,
286
+ amount: 200,
287
+ })
288
+
289
+ // Send asset to another Ark address
290
+ const sendTxid = await wallet.send({
291
+ address: 'ark1qq4...',
292
+ assets: [{ assetId: assetIssuance.assetId, amount: 100 }],
293
+ })
294
+ ```
295
+
172
296
  ### Batch Settlements
173
297
 
174
298
  This can be used to move preconfirmed balances into finalized balances and to manually convert UTXOs and VTXOs.
@@ -233,6 +357,103 @@ const balance = await manager.getRecoverableBalance()
233
357
  ```
234
358
 
235
359
 
360
+ ### VTXO Delegation
361
+
362
+ Delegation allows you to outsource VTXO renewal to a third-party delegator service. Instead of renewing VTXOs yourself, the delegator will automatically settle them before they expire, sending the funds back to your wallet address (minus a service fee). This is useful for wallets that cannot be online 24/7.
363
+
364
+ When a `delegatorProvider` is configured, the wallet address includes an extra tapscript path that authorizes the delegator to co-sign renewals alongside the Ark server.
365
+
366
+ #### Setting Up a Wallet with Delegation
367
+
368
+ ```typescript
369
+ import { Wallet, SingleKey, RestDelegatorProvider } from '@arkade-os/sdk'
370
+
371
+ const identity = SingleKey.fromHex('your_private_key_hex')
372
+
373
+ const wallet = await Wallet.create({
374
+ identity,
375
+ arkServerUrl: 'https://mutinynet.arkade.sh',
376
+ delegatorProvider: new RestDelegatorProvider('https://delegator.example.com'),
377
+ })
378
+ ```
379
+
380
+ > **Note:** Adding a `delegatorProvider` changes your wallet address because the offchain tapscript includes an additional delegation path. Funds sent to an address without delegation cannot be delegated, and vice versa.
381
+
382
+ #### Delegating VTXOs
383
+
384
+ Once the wallet is configured with a delegator, use `wallet.delegatorManager` to delegate your VTXOs:
385
+
386
+ ```typescript
387
+ // Get spendable VTXOs
388
+ const vtxos = (await wallet.getVtxos({ withRecoverable: true }))
389
+ .filter(v => v.virtualStatus.type === 'confirmed')
390
+
391
+ // Delegate all VTXOs — the delegator will renew them before expiry
392
+ const myAddress = await wallet.getAddress()
393
+ const result = await wallet.delegatorManager.delegate(vtxos, myAddress)
394
+
395
+ console.log('Delegated:', result.delegated.length)
396
+ console.log('Failed:', result.failed.length)
397
+ ```
398
+
399
+ The `delegate` method groups VTXOs by expiry date and submits them to the delegator service. By default, delegation is scheduled at 90% of each VTXO's remaining lifetime. You can override this with an explicit date:
400
+
401
+ ```typescript
402
+ // Delegate with a specific renewal time
403
+ const delegateAt = new Date(Date.now() + 12 * 60 * 60 * 1000) // 12 hours from now
404
+ await wallet.delegatorManager.delegate(vtxos, myAddress, delegateAt)
405
+ ```
406
+
407
+ #### Service Worker Integration
408
+
409
+ When using a service worker wallet, pass the `delegatorUrl` option. The service worker will automatically delegate VTXOs after each VTXO update:
410
+
411
+ ```typescript
412
+ import { ServiceWorkerWallet, SingleKey } from '@arkade-os/sdk'
413
+
414
+ const wallet = await ServiceWorkerWallet.setup({
415
+ serviceWorkerPath: '/service-worker.js',
416
+ arkServerUrl: 'https://mutinynet.arkade.sh',
417
+ identity: SingleKey.fromHex('your_private_key_hex'),
418
+ delegatorUrl: 'https://delegator.example.com',
419
+ })
420
+ ```
421
+
422
+ #### Querying Delegator Info
423
+
424
+ You can query the delegator service directly to inspect its public key, fee, and payment address:
425
+
426
+ ```typescript
427
+ import { RestDelegatorProvider } from '@arkade-os/sdk'
428
+
429
+ const provider = new RestDelegatorProvider('https://delegator.example.com')
430
+ const info = await provider.getDelegateInfo()
431
+
432
+ console.log('Delegator public key:', info.pubkey)
433
+ console.log('Service fee (sats):', info.fee)
434
+ console.log('Fee address:', info.delegatorAddress)
435
+ ```
436
+
437
+ ### BIP-322 Message Signing
438
+
439
+ Sign and verify messages using [BIP-322](https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki). Supports P2TR (Taproot) signing, and verification for P2TR, P2WPKH, and legacy P2PKH addresses.
440
+
441
+ ```typescript
442
+ import { BIP322, SingleKey } from '@arkade-os/sdk'
443
+
444
+ const identity = SingleKey.fromHex('your_private_key_hex')
445
+
446
+ // Sign a message (P2TR key-spend)
447
+ const signature = await BIP322.sign('Hello Bitcoin!', identity)
448
+
449
+ // Verify against a P2TR address
450
+ const valid = BIP322.verify('Hello Bitcoin!', signature, 'bc1p...')
451
+
452
+ // Also works with P2WPKH and legacy P2PKH addresses
453
+ BIP322.verify('Hello Bitcoin!', sig, 'bc1q...') // P2WPKH
454
+ BIP322.verify('Hello Bitcoin!', sig, '1A1zP1...') // legacy P2PKH
455
+ ```
456
+
236
457
  ### Transaction History
237
458
 
238
459
  ```typescript
@@ -329,81 +550,285 @@ await Unroll.completeUnroll(
329
550
 
330
551
  ### Running the wallet in a service worker
331
552
 
332
- **Ultra-simplified setup!** We handle all the complex service worker registration and identity management for you:
553
+ The SDK provides a `MessageBus` orchestrator that runs inside a service worker
554
+ and routes messages to pluggable `MessageHandler`s. The built-in
555
+ `WalletMessageHandler` exposes all wallet operations over this message bus, and
556
+ `ServiceWorkerWallet` is a client-side proxy that communicates with it
557
+ transparently.
558
+
559
+ #### Service worker file
560
+
561
+ ```javascript
562
+ // service-worker.js
563
+ import {
564
+ MessageBus,
565
+ WalletMessageHandler,
566
+ IndexedDBWalletRepository,
567
+ IndexedDBContractRepository,
568
+ } from '@arkade-os/sdk'
569
+
570
+ const walletRepo = new IndexedDBWalletRepository()
571
+ const contractRepo = new IndexedDBContractRepository()
572
+
573
+ const bus = new MessageBus(walletRepo, contractRepo, {
574
+ messageHandlers: [new WalletMessageHandler()],
575
+ tickIntervalMs: 10_000, // default 10s
576
+ })
577
+
578
+ bus.start()
579
+ ```
580
+
581
+ #### Client-side usage
333
582
 
334
583
  ```typescript
335
- // SIMPLE SETUP with identity! 🎉
336
- import { ServiceWorkerWallet, SingleKey } from '@arkade-os/sdk';
584
+ // app.ts
585
+ import { ServiceWorkerWallet, SingleKey } from '@arkade-os/sdk'
337
586
 
338
- // Create your identity
339
- const identity = SingleKey.fromHex('your_private_key_hex');
340
- // Or generate a new one:
341
- // const identity = SingleKey.fromRandomBytes();
587
+ const identity = SingleKey.fromHex('your_private_key_hex')
342
588
 
589
+ // One-liner: registers the SW, initializes the MessageBus, and creates the wallet
343
590
  const wallet = await ServiceWorkerWallet.setup({
344
591
  serviceWorkerPath: '/service-worker.js',
345
592
  arkServerUrl: 'https://mutinynet.arkade.sh',
346
- identity
347
- });
593
+ identity,
594
+ })
348
595
 
349
- // That's it! Ready to use immediately:
350
- const address = await wallet.getAddress();
351
- const balance = await wallet.getBalance();
596
+ // Use like any other wallet — calls are proxied to the service worker
597
+ const address = await wallet.getAddress()
598
+ const balance = await wallet.getBalance()
352
599
  ```
353
600
 
354
- You'll also need to create a service worker file:
601
+ For watch-only wallets, use `ServiceWorkerReadonlyWallet` with a
602
+ `ReadonlySingleKey` identity instead.
603
+
604
+ ### Worker Architecture
605
+
606
+ The _worker_ captures the background processing infrastructure for the SDK.
607
+ Two platform-specific implementations share common patterns (pluggable
608
+ handlers, periodic scheduling, repository/provider dependency injection) but
609
+ differ in orchestration and communication.
610
+
611
+ | Platform | Directory | Orchestrator | Communication |
612
+ |----------|----------------------------------------------|-------------|---------------|
613
+ | **Browser** | [`browser/`](./src/worker/browser/README.md) | `MessageBus` inside a Service Worker | `postMessage` between SW and window clients |
614
+ | **Expo/React Native** | [`expo/`](./src/worker/expo/README.md) | `runTasks()` called from foreground interval and OS background wake | `AsyncStorageTaskQueue` inbox/outbox |
615
+
616
+ See the platform READMEs for architecture details, runtime flow, and usage
617
+ examples.
618
+
619
+
620
+
621
+ ### Repositories (Storage)
622
+
623
+ The `StorageAdapter` API is deprecated. Use repositories instead. If you omit
624
+ `storage`, the SDK uses IndexedDB repositories with the default database name.
625
+
626
+ #### Migration from v1 StorageAdapter
627
+
628
+ > [!WARNING]
629
+ > If you previously used the v1 `StorageAdapter`-based repositories, migrate
630
+ > data into the new IndexedDB repositories before use:
631
+ >
632
+ > ```typescript
633
+ > import {
634
+ > IndexedDBWalletRepository,
635
+ > IndexedDBContractRepository,
636
+ > getMigrationStatus,
637
+ > migrateWalletRepository,
638
+ > rollbackMigration,
639
+ > } from '@arkade-os/sdk'
640
+ > import { IndexedDBStorageAdapter } from '@arkade-os/sdk/adapters/indexedDB'
641
+ >
642
+ > const oldStorage = new IndexedDBStorageAdapter('legacy-wallet', 1)
643
+ > const newDbName = 'my-app-db'
644
+ > const walletRepository = new IndexedDBWalletRepository(newDbName)
645
+ >
646
+ > // Check migration status before running
647
+ > const status = await getMigrationStatus('wallet', oldStorage)
648
+ > // status: "not-needed" | "pending" | "in-progress" | "done"
649
+ >
650
+ > if (status === 'pending' || status === 'in-progress') {
651
+ > try {
652
+ > await migrateWalletRepository(oldStorage, walletRepository, {
653
+ > onchain: [ 'address-1', 'address-2' ],
654
+ > offchain: [ 'onboarding-address-1' ],
655
+ > })
656
+ > } catch (err) {
657
+ > // Reset migration flag so the next attempt starts clean
658
+ > await rollbackMigration('wallet', oldStorage)
659
+ > throw err
660
+ > }
661
+ > }
662
+ > ```
663
+ >
664
+ > **Migration status helpers:**
665
+ >
666
+ > | Helper | Description |
667
+ > |--------|-------------|
668
+ > | `getMigrationStatus(repoType, adapter)` | Returns `"not-needed"` (no legacy DB), `"pending"`, `"in-progress"` (interrupted), or `"done"` |
669
+ > | `requiresMigration(repoType, adapter)` | Returns `true` if status is `"pending"` or `"in-progress"` |
670
+ > | `rollbackMigration(repoType, adapter)` | Removes the migration flag so migration can re-run from scratch |
671
+ > | `MIGRATION_KEY(repoType)` | Returns the storage key used for the migration flag |
672
+ >
673
+ > `migrateWalletRepository` sets an `"in-progress"` flag before copying data.
674
+ > If the process crashes mid-way, the flag remains as `"in-progress"` so the
675
+ > next call to `getMigrationStatus` can detect the partial migration. Old data
676
+ > is never deleted — re-running migration after a rollback is safe.
677
+ >
678
+ > Anything related to contract repository migration must be handled by the package which created them. The SDK doesn't manage contracts in V1. Data remains untouched and persisted in the same old location.
679
+ >
680
+ > If you persisted custom data in the ContractRepository via its `setContractData` method,
681
+ > or a custom collection via `saveToContractCollection`, you'll need to migrate it manually:
682
+ >
683
+ > ```typescript
684
+ > // Custom data stored in the ContractRepository
685
+ > const oldStorage = new IndexedDBStorageAdapter('legacy-wallet', 1)
686
+ > const oldRepo = new ContractRepositoryImpl(storageAdapter)
687
+ > const customContract = await oldRepo.getContractData('my-contract', 'status')
688
+ > await contractRepository.setContractData('my-contract', 'status', customData)
689
+ > const customCollection = await oldRepo.getContractCollection('swaps')
690
+ > await contractRepository.saveToContractCollection('swaps', customCollection)
691
+ > ```
692
+
693
+ #### Repository Versioning
694
+
695
+ `WalletRepository`, `ContractRepository`, and `SwapRepository` (in
696
+ `@arkade-os/boltz-swap`) each declare a `readonly version` field with a literal
697
+ type. All built-in implementations set this to the current version. If you
698
+ maintain a custom repository implementation, TypeScript will produce a compile
699
+ error when the version is bumped, signaling that a semantic update is required:
355
700
 
356
701
  ```typescript
357
- // service-worker.js
358
- import { Worker } from '@arkade-os/sdk'
702
+ import { WalletRepository } from '@arkade-os/sdk'
359
703
 
360
- // Worker handles communication between the main thread and service worker
361
- new Worker().start()
704
+ class MyWalletRepository implements WalletRepository {
705
+ readonly version = 1 // must match the interface's literal type
706
+ // ...
707
+ }
362
708
  ```
363
709
 
364
- ### Storage Adapters
710
+ #### SQLite Repository (Node.js / React Native)
365
711
 
366
- Choose the appropriate storage adapter for your environment:
712
+ For Node.js or React Native environments, use the SQLite repository with any
713
+ SQLite driver. The SDK accepts a `SQLExecutor` interface — you provide the
714
+ driver, the SDK handles the schema.
715
+
716
+ See [examples/node/multiple-wallets.ts](examples/node/multiple-wallets.ts) for
717
+ a full working example using `better-sqlite3`.
367
718
 
368
719
  ```typescript
369
- import {
370
- SingleKey,
371
- Wallet,
372
- InMemoryStorageAdapter, // Works everywhere, data lost on restart
373
- } from '@arkade-os/sdk'
720
+ import { SingleKey, Wallet } from '@arkade-os/sdk'
721
+ import { SQLiteWalletRepository, SQLiteContractRepository, SQLExecutor } from '@arkade-os/sdk/repositories/sqlite'
722
+ import Database from 'better-sqlite3'
374
723
 
375
- // Import additional storage adapters as needed:
376
- import { LocalStorageAdapter } from '@arkade-os/sdk/adapters/localStorage' // Browser/PWA persistent storage
377
- import { IndexedDBStorageAdapter } from '@arkade-os/sdk/adapters/indexedDB' // Browser/PWA/Service Worker advanced storage
378
- import { AsyncStorageAdapter } from '@arkade-os/sdk/adapters/asyncStorage' // React Native persistent storage
379
- import { FileSystemStorageAdapter } from '@arkade-os/sdk/adapters/fileSystem' // Node.js file-based storage
724
+ const db = new Database('my-wallet.sqlite')
725
+ db.pragma('journal_mode = WAL')
380
726
 
381
- // Node.js
382
- const storage = new FileSystemStorageAdapter('./wallet-data')
727
+ const executor: SQLExecutor = {
728
+ run: async (sql, params) => { db.prepare(sql).run(...(params ?? [])) },
729
+ get: async (sql, params) => db.prepare(sql).get(...(params ?? [])) as any,
730
+ all: async (sql, params) => db.prepare(sql).all(...(params ?? [])) as any,
731
+ }
383
732
 
384
- // Browser/PWA
385
- const storage = new LocalStorageAdapter()
386
- // or for advanced features:
387
- const storage = new IndexedDBStorageAdapter('my-app', 1)
733
+ const wallet = await Wallet.create({
734
+ identity: SingleKey.fromHex('your_private_key_hex'),
735
+ arkServerUrl: 'https://mutinynet.arkade.sh',
736
+ storage: {
737
+ walletRepository: new SQLiteWalletRepository(executor),
738
+ contractRepository: new SQLiteContractRepository(executor),
739
+ },
740
+ })
741
+ ```
388
742
 
389
- // React Native
390
- const storage = new AsyncStorageAdapter()
743
+ #### Realm Repository (React Native)
391
744
 
392
- // Service Worker
393
- const storage = new IndexedDBStorageAdapter('service-worker-wallet', 1)
745
+ For React Native apps using Realm, pass your Realm instance directly:
394
746
 
395
- // Load identity from storage (simple pattern everywhere)
396
- const privateKeyHex = await storage.getItem('private-key')
397
- const identity = SingleKey.fromHex(privateKeyHex)
747
+ ```typescript
748
+ import { RealmWalletRepository, RealmContractRepository, ArkRealmSchemas } from '@arkade-os/sdk/repositories/realm'
398
749
 
399
- // Create wallet (same API everywhere)
750
+ const realm = await Realm.open({ schema: [...ArkRealmSchemas, ...yourSchemas] })
400
751
  const wallet = await Wallet.create({
401
752
  identity,
402
753
  arkServerUrl: 'https://mutinynet.arkade.sh',
403
- storage // optional
754
+ storage: {
755
+ walletRepository: new RealmWalletRepository(realm),
756
+ contractRepository: new RealmContractRepository(realm),
757
+ },
758
+ })
759
+ ```
760
+
761
+ #### IndexedDB Repository (Browser)
762
+
763
+ In the browser, the SDK defaults to IndexedDB repositories when no `storage`
764
+ is provided:
765
+
766
+ ```typescript
767
+ import { SingleKey, Wallet } from '@arkade-os/sdk'
768
+
769
+ const wallet = await Wallet.create({
770
+ identity: SingleKey.fromHex('your_private_key_hex'),
771
+ arkServerUrl: 'https://mutinynet.arkade.sh',
772
+ // Uses IndexedDB by default in the browser
404
773
  })
405
774
  ```
406
775
 
776
+ If you want a custom database name or a different repository implementation,
777
+ pass `storage` explicitly.
778
+
779
+ For ephemeral storage (no persistence), pass the in-memory repositories:
780
+
781
+ ```typescript
782
+ import {
783
+ InMemoryWalletRepository,
784
+ InMemoryContractRepository,
785
+ Wallet
786
+ } from '@arkade-os/sdk'
787
+
788
+ const wallet = await Wallet.create({
789
+ identity,
790
+ arkServerUrl: 'https://mutinynet.arkade.sh',
791
+ storage: {
792
+ walletRepository: new InMemoryWalletRepository(),
793
+ contractRepository: new InMemoryContractRepository()
794
+ }
795
+ })
796
+ ```
797
+
798
+ ### Using with Node.js
799
+
800
+ Node.js does not provide a global `EventSource` implementation. The SDK relies on `EventSource` for Server-Sent Events during settlement (onboarding/offboarding) and contract watching. You must polyfill it before using the SDK:
801
+
802
+ ```bash
803
+ npm install eventsource
804
+ ```
805
+
806
+ ```typescript
807
+ import { EventSource } from "eventsource";
808
+ (globalThis as any).EventSource = EventSource;
809
+
810
+ // Use dynamic import so the polyfill is set before the SDK evaluates
811
+ const { Wallet, SingleKey, Ramps } = await import("@arkade-os/sdk");
812
+ ```
813
+
814
+ If you also need IndexedDB persistence (e.g. for `WalletRepository`), set up the shim before any SDK import:
815
+
816
+ ```typescript
817
+ // Must define `self` BEFORE calling setGlobalVars
818
+ if (typeof self === "undefined") {
819
+ (globalThis as any).self = globalThis;
820
+ }
821
+ import setGlobalVars from "indexeddbshim/src/node-UnicodeIdentifiers";
822
+ (globalThis as any).window = globalThis;
823
+ setGlobalVars(null, { checkOrigin: false });
824
+ ```
825
+
826
+ > **Note:** `eventsource` and `indexeddbshim` are optional peer dependencies.
827
+ > Without the `EventSource` polyfill, settlement operations will fail with
828
+ > `ReferenceError: EventSource is not defined`.
829
+
830
+ See [`examples/node/multiple-wallets.ts`](examples/node/multiple-wallets.ts) for a complete working example.
831
+
407
832
  ### Using with Expo/React Native
408
833
 
409
834
  For React Native and Expo applications where standard EventSource and fetch streaming may not work properly, use the Expo-compatible providers:
@@ -432,6 +857,31 @@ Both ExpoArkProvider and ExpoIndexerProvider are available as adapters following
432
857
  - **ExpoArkProvider**: Handles settlement events and transaction streaming using expo/fetch for Server-Sent Events
433
858
  - **ExpoIndexerProvider**: Handles address subscriptions and VTXO updates using expo/fetch for JSON streaming
434
859
 
860
+ For persistence in Expo/React Native, use the SQLite repository with `expo-sqlite`:
861
+
862
+ ```typescript
863
+ import { SQLiteWalletRepository, SQLiteContractRepository } from '@arkade-os/sdk/repositories/sqlite'
864
+ import * as SQLite from 'expo-sqlite'
865
+
866
+ const db = SQLite.openDatabaseSync('my-wallet.db')
867
+ const executor = {
868
+ run: (sql, params) => db.runAsync(sql, params ?? []),
869
+ get: (sql, params) => db.getFirstAsync(sql, params ?? []),
870
+ all: (sql, params) => db.getAllAsync(sql, params ?? []),
871
+ }
872
+
873
+ const wallet = await Wallet.create({
874
+ identity,
875
+ arkServerUrl: 'https://mutinynet.arkade.sh',
876
+ arkProvider: new ExpoArkProvider('https://mutinynet.arkade.sh'),
877
+ indexerProvider: new ExpoIndexerProvider('https://mutinynet.arkade.sh'),
878
+ storage: {
879
+ walletRepository: new SQLiteWalletRepository(executor),
880
+ contractRepository: new SQLiteContractRepository(executor),
881
+ },
882
+ })
883
+ ```
884
+
435
885
  #### Crypto Polyfill Requirement
436
886
 
437
887
  Install `expo-crypto` and polyfill `crypto.getRandomValues()` at the top of your app entry point:
@@ -453,6 +903,88 @@ import { ExpoArkProvider, ExpoIndexerProvider } from '@arkade-os/sdk/adapters/ex
453
903
 
454
904
  This is required for MuSig2 settlements and cryptographic operations.
455
905
 
906
+ ### Contract Management
907
+
908
+ Both `Wallet` and `ServiceWorkerWallet` use a `ContractManager` internally to watch for VTXOs. This provides resilient connection handling with automatic reconnection and failsafe polling - for your wallet's default address and any external contracts you register (Boltz swaps, HTLCs, etc.).
909
+
910
+ When you call `wallet.notifyIncomingFunds()` or use `waitForIncomingFunds()`, it uses the ContractManager under the hood, giving you automatic reconnection and failsafe polling for free - no code changes needed.
911
+
912
+ For advanced use cases, you can access the ContractManager directly to register external contracts:
913
+
914
+ ```typescript
915
+ // Get the contract manager (wallet's default address is already registered)
916
+ const manager = await wallet.getContractManager()
917
+
918
+ // Register a VHTLC contract (e.g., for a Lightning swap)
919
+ const contract = await manager.createContract({
920
+ type: 'vhtlc',
921
+ params: {
922
+ sender: alicePubKey,
923
+ receiver: bobPubKey,
924
+ server: serverPubKey,
925
+ hash: paymentHash,
926
+ refundLocktime: '800000',
927
+ claimDelay: '100',
928
+ refundDelay: '102',
929
+ refundNoReceiverDelay: '103',
930
+ },
931
+ script: swapScript,
932
+ address: swapAddress,
933
+ })
934
+
935
+ // Listen for all contracts events (wallet address + external contracts)
936
+ const unsubscribe = await manager.onContractEvent((event) => {
937
+ switch (event.type) {
938
+ case 'vtxo_received':
939
+ console.log(`Received ${event.vtxos.length} VTXOs on ${event.contractScript}`)
940
+ break
941
+ case 'vtxo_spent':
942
+ console.log(`Spent VTXOs on ${event.contractScript}`)
943
+ break
944
+ case 'contract_expired':
945
+ console.log(`Contract ${event.contractScript} expired`)
946
+ break
947
+ }
948
+ })
949
+
950
+ // Update contract data (e.g., set preimage when revealed)
951
+ await manager.updateContractParams(contract.script, { preimage: revealedPreimage })
952
+
953
+ // Check spendable paths (requires a specific VTXO)
954
+ const [withVtxos] = await manager.getContractsWithVtxos({ script: contract.script })
955
+ const vtxo = withVtxos.vtxos[0]
956
+ const paths = manager.getSpendablePaths({
957
+ contractScript: contract.script,
958
+ vtxo,
959
+ collaborative: true,
960
+ walletPubKey: myPubKey,
961
+ })
962
+ if (paths.length > 0) {
963
+ console.log('Contract is spendable via:', paths[0].leaf)
964
+ }
965
+
966
+ // Or list all possible paths for the current context (no spendability checks)
967
+ const allPaths = manager.getAllSpendingPaths({
968
+ contractScript: contract.script,
969
+ collaborative: true,
970
+ walletPubKey: myPubKey,
971
+ })
972
+
973
+ // Get balances across all contracts
974
+ const balances = await manager.getAllBalances()
975
+
976
+ // Manually sweep all eligible contracts
977
+ const sweepResults = await manager.sweepAll()
978
+
979
+ // Stop watching
980
+ unsubscribe()
981
+ ```
982
+
983
+ The watcher features:
984
+ - **Automatic reconnection** with exponential backoff (1s → 30s max)
985
+ - **Failsafe polling** every 60 seconds to catch missed events
986
+ - **Immediate sync** on connection and after failures
987
+
456
988
  ### Repository Pattern
457
989
 
458
990
  Access low-level data management through repositories: