@bsv/wallet-toolbox 2.6.5 → 2.7.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 (215) hide show
  1. package/CHANGELOG.md +77 -8
  2. package/README.md +72 -18
  3. package/docs/action-batch-planning.md +80 -37
  4. package/docs/managed-change-liquidity.md +251 -0
  5. package/out/src/Setup.d.ts +9 -0
  6. package/out/src/Setup.d.ts.map +1 -1
  7. package/out/src/Setup.js +2 -0
  8. package/out/src/Setup.js.map +1 -1
  9. package/out/src/SetupClient.d.ts +3 -0
  10. package/out/src/SetupClient.d.ts.map +1 -1
  11. package/out/src/SetupClient.js +1 -0
  12. package/out/src/SetupClient.js.map +1 -1
  13. package/out/src/SetupWallet.d.ts +3 -0
  14. package/out/src/SetupWallet.d.ts.map +1 -1
  15. package/out/src/Wallet.d.ts +4 -3
  16. package/out/src/Wallet.d.ts.map +1 -1
  17. package/out/src/Wallet.js +4 -3
  18. package/out/src/Wallet.js.map +1 -1
  19. package/out/src/WalletPermissionsManager.d.ts.map +1 -1
  20. package/out/src/WalletPermissionsManager.js +5 -8
  21. package/out/src/WalletPermissionsManager.js.map +1 -1
  22. package/out/src/mockchain/MockServices.d.ts.map +1 -1
  23. package/out/src/mockchain/MockServices.js +2 -6
  24. package/out/src/mockchain/MockServices.js.map +1 -1
  25. package/out/src/monitor/Monitor.d.ts.map +1 -1
  26. package/out/src/monitor/Monitor.js +5 -4
  27. package/out/src/monitor/Monitor.js.map +1 -1
  28. package/out/src/monitor/tasks/TaskArcSSE.d.ts +4 -1
  29. package/out/src/monitor/tasks/TaskArcSSE.d.ts.map +1 -1
  30. package/out/src/monitor/tasks/TaskArcSSE.js +92 -65
  31. package/out/src/monitor/tasks/TaskArcSSE.js.map +1 -1
  32. package/out/src/monitor/tasks/TaskReconcilePendingTransactions.d.ts +27 -0
  33. package/out/src/monitor/tasks/TaskReconcilePendingTransactions.d.ts.map +1 -0
  34. package/out/src/monitor/tasks/TaskReconcilePendingTransactions.js +236 -0
  35. package/out/src/monitor/tasks/TaskReconcilePendingTransactions.js.map +1 -0
  36. package/out/src/monitor/tasks/TaskReviewDoubleSpends.d.ts.map +1 -1
  37. package/out/src/monitor/tasks/TaskReviewDoubleSpends.js +15 -3
  38. package/out/src/monitor/tasks/TaskReviewDoubleSpends.js.map +1 -1
  39. package/out/src/monitor/tasks/TaskReviewUtxos.d.ts +29 -2
  40. package/out/src/monitor/tasks/TaskReviewUtxos.d.ts.map +1 -1
  41. package/out/src/monitor/tasks/TaskReviewUtxos.js +155 -7
  42. package/out/src/monitor/tasks/TaskReviewUtxos.js.map +1 -1
  43. package/out/src/monitor/tasks/index.all.d.ts +1 -0
  44. package/out/src/monitor/tasks/index.all.d.ts.map +1 -1
  45. package/out/src/monitor/tasks/index.all.js +1 -0
  46. package/out/src/monitor/tasks/index.all.js.map +1 -1
  47. package/out/src/sdk/ActionBatch.interfaces.d.ts +19 -0
  48. package/out/src/sdk/ActionBatch.interfaces.d.ts.map +1 -1
  49. package/out/src/sdk/WERR_errors.d.ts +23 -0
  50. package/out/src/sdk/WERR_errors.d.ts.map +1 -1
  51. package/out/src/sdk/WERR_errors.js +44 -1
  52. package/out/src/sdk/WERR_errors.js.map +1 -1
  53. package/out/src/sdk/WalletError.d.ts.map +1 -1
  54. package/out/src/sdk/WalletError.js +7 -21
  55. package/out/src/sdk/WalletError.js.map +1 -1
  56. package/out/src/sdk/WalletErrorFromJson.d.ts.map +1 -1
  57. package/out/src/sdk/WalletErrorFromJson.js +9 -1
  58. package/out/src/sdk/WalletErrorFromJson.js.map +1 -1
  59. package/out/src/sdk/WalletServices.interfaces.d.ts +14 -1
  60. package/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
  61. package/out/src/sdk/WalletStorage.interfaces.d.ts +3 -1
  62. package/out/src/sdk/WalletStorage.interfaces.d.ts.map +1 -1
  63. package/out/src/sdk/types.d.ts +3 -2
  64. package/out/src/sdk/types.d.ts.map +1 -1
  65. package/out/src/sdk/types.js +3 -2
  66. package/out/src/sdk/types.js.map +1 -1
  67. package/out/src/services/Services.d.ts.map +1 -1
  68. package/out/src/services/Services.js +73 -15
  69. package/out/src/services/Services.js.map +1 -1
  70. package/out/src/services/classifyOutputUtxo.d.ts +25 -0
  71. package/out/src/services/classifyOutputUtxo.d.ts.map +1 -0
  72. package/out/src/services/classifyOutputUtxo.js +87 -0
  73. package/out/src/services/classifyOutputUtxo.js.map +1 -0
  74. package/out/src/services/createDefaultWalletServicesOptions.d.ts +16 -3
  75. package/out/src/services/createDefaultWalletServicesOptions.d.ts.map +1 -1
  76. package/out/src/services/createDefaultWalletServicesOptions.js +38 -8
  77. package/out/src/services/createDefaultWalletServicesOptions.js.map +1 -1
  78. package/out/src/services/providers/ArcSSEClient.d.ts +13 -4
  79. package/out/src/services/providers/ArcSSEClient.d.ts.map +1 -1
  80. package/out/src/services/providers/ArcSSEClient.js +30 -7
  81. package/out/src/services/providers/ArcSSEClient.js.map +1 -1
  82. package/out/src/services/providers/Arcade.d.ts +10 -1
  83. package/out/src/services/providers/Arcade.d.ts.map +1 -1
  84. package/out/src/services/providers/Arcade.js +63 -0
  85. package/out/src/services/providers/Arcade.js.map +1 -1
  86. package/out/src/services/providers/arcadeStatus.d.ts +18 -0
  87. package/out/src/services/providers/arcadeStatus.d.ts.map +1 -0
  88. package/out/src/services/providers/arcadeStatus.js +56 -0
  89. package/out/src/services/providers/arcadeStatus.js.map +1 -0
  90. package/out/src/signer/actionBatch/ActionBatchPlanner.d.ts.map +1 -1
  91. package/out/src/signer/actionBatch/ActionBatchPlanner.js +25 -3
  92. package/out/src/signer/actionBatch/ActionBatchPlanner.js.map +1 -1
  93. package/out/src/signer/actionBatch/ActionBatchWorkspace.d.ts +2 -0
  94. package/out/src/signer/actionBatch/ActionBatchWorkspace.d.ts.map +1 -1
  95. package/out/src/signer/actionBatch/ActionBatchWorkspace.js +135 -16
  96. package/out/src/signer/actionBatch/ActionBatchWorkspace.js.map +1 -1
  97. package/out/src/storage/StorageIdb.d.ts +2 -0
  98. package/out/src/storage/StorageIdb.d.ts.map +1 -1
  99. package/out/src/storage/StorageIdb.js +24 -1
  100. package/out/src/storage/StorageIdb.js.map +1 -1
  101. package/out/src/storage/StorageKnex.d.ts +1 -1
  102. package/out/src/storage/StorageKnex.d.ts.map +1 -1
  103. package/out/src/storage/StorageKnex.js +4 -4
  104. package/out/src/storage/StorageKnex.js.map +1 -1
  105. package/out/src/storage/StorageProvider.d.ts +18 -2
  106. package/out/src/storage/StorageProvider.d.ts.map +1 -1
  107. package/out/src/storage/StorageProvider.js +45 -14
  108. package/out/src/storage/StorageProvider.js.map +1 -1
  109. package/out/src/storage/StorageReaderWriter.d.ts.map +1 -1
  110. package/out/src/storage/StorageReaderWriter.js +3 -2
  111. package/out/src/storage/StorageReaderWriter.js.map +1 -1
  112. package/out/src/storage/WalletStorageManager.d.ts +12 -0
  113. package/out/src/storage/WalletStorageManager.d.ts.map +1 -1
  114. package/out/src/storage/WalletStorageManager.js +22 -2
  115. package/out/src/storage/WalletStorageManager.js.map +1 -1
  116. package/out/src/storage/adminServer/adminServer.d.ts +2 -0
  117. package/out/src/storage/adminServer/adminServer.d.ts.map +1 -1
  118. package/out/src/storage/adminServer/adminServer.js +25 -8
  119. package/out/src/storage/adminServer/adminServer.js.map +1 -1
  120. package/out/src/storage/adminServer/adminUi.d.ts.map +1 -1
  121. package/out/src/storage/adminServer/adminUi.js +32 -9
  122. package/out/src/storage/adminServer/adminUi.js.map +1 -1
  123. package/out/src/storage/adminServer/reviewUtxos.d.ts +27 -0
  124. package/out/src/storage/adminServer/reviewUtxos.d.ts.map +1 -0
  125. package/out/src/storage/adminServer/reviewUtxos.js +62 -0
  126. package/out/src/storage/adminServer/reviewUtxos.js.map +1 -0
  127. package/out/src/storage/index.all.d.ts +1 -0
  128. package/out/src/storage/index.all.d.ts.map +1 -1
  129. package/out/src/storage/index.all.js +1 -0
  130. package/out/src/storage/index.all.js.map +1 -1
  131. package/out/src/storage/index.client.d.ts +1 -0
  132. package/out/src/storage/index.client.d.ts.map +1 -1
  133. package/out/src/storage/index.client.js +1 -0
  134. package/out/src/storage/index.client.js.map +1 -1
  135. package/out/src/storage/index.mobile.d.ts +1 -0
  136. package/out/src/storage/index.mobile.d.ts.map +1 -1
  137. package/out/src/storage/index.mobile.js +1 -0
  138. package/out/src/storage/index.mobile.js.map +1 -1
  139. package/out/src/storage/methods/ListOutputsSpecOp.d.ts.map +1 -1
  140. package/out/src/storage/methods/ListOutputsSpecOp.js +4 -44
  141. package/out/src/storage/methods/ListOutputsSpecOp.js.map +1 -1
  142. package/out/src/storage/methods/actionBatch.d.ts +9 -2
  143. package/out/src/storage/methods/actionBatch.d.ts.map +1 -1
  144. package/out/src/storage/methods/actionBatch.js +258 -66
  145. package/out/src/storage/methods/actionBatch.js.map +1 -1
  146. package/out/src/storage/methods/actionBatchBlobs.js +7 -4
  147. package/out/src/storage/methods/actionBatchBlobs.js.map +1 -1
  148. package/out/src/storage/methods/attemptToPostReqsToNetwork.d.ts.map +1 -1
  149. package/out/src/storage/methods/attemptToPostReqsToNetwork.js +2 -66
  150. package/out/src/storage/methods/attemptToPostReqsToNetwork.js.map +1 -1
  151. package/out/src/storage/methods/availableManagedChange.d.ts +8 -1
  152. package/out/src/storage/methods/availableManagedChange.d.ts.map +1 -1
  153. package/out/src/storage/methods/availableManagedChange.js.map +1 -1
  154. package/out/src/storage/methods/createAction.d.ts.map +1 -1
  155. package/out/src/storage/methods/createAction.js +176 -58
  156. package/out/src/storage/methods/createAction.js.map +1 -1
  157. package/out/src/storage/methods/generateChange.d.ts +13 -1
  158. package/out/src/storage/methods/generateChange.d.ts.map +1 -1
  159. package/out/src/storage/methods/generateChange.js +169 -12
  160. package/out/src/storage/methods/generateChange.js.map +1 -1
  161. package/out/src/storage/methods/listActionsIdb.d.ts.map +1 -1
  162. package/out/src/storage/methods/listActionsIdb.js +4 -0
  163. package/out/src/storage/methods/listActionsIdb.js.map +1 -1
  164. package/out/src/storage/methods/listActionsKnex.d.ts.map +1 -1
  165. package/out/src/storage/methods/listActionsKnex.js +4 -0
  166. package/out/src/storage/methods/listActionsKnex.js.map +1 -1
  167. package/out/src/storage/methods/listOutputsIdb.d.ts.map +1 -1
  168. package/out/src/storage/methods/listOutputsIdb.js +2 -1
  169. package/out/src/storage/methods/listOutputsIdb.js.map +1 -1
  170. package/out/src/storage/methods/managedChangePolicy.d.ts +44 -0
  171. package/out/src/storage/methods/managedChangePolicy.d.ts.map +1 -0
  172. package/out/src/storage/methods/managedChangePolicy.js +58 -0
  173. package/out/src/storage/methods/managedChangePolicy.js.map +1 -0
  174. package/out/src/storage/methods/reconcileFailedTransactionInputs.d.ts +32 -0
  175. package/out/src/storage/methods/reconcileFailedTransactionInputs.d.ts.map +1 -0
  176. package/out/src/storage/methods/reconcileFailedTransactionInputs.js +142 -0
  177. package/out/src/storage/methods/reconcileFailedTransactionInputs.js.map +1 -0
  178. package/out/src/storage/methods/reviewUtxoOutputs.d.ts +37 -0
  179. package/out/src/storage/methods/reviewUtxoOutputs.d.ts.map +1 -0
  180. package/out/src/storage/methods/reviewUtxoOutputs.js +150 -0
  181. package/out/src/storage/methods/reviewUtxoOutputs.js.map +1 -0
  182. package/out/src/storage/portable/index.d.ts.map +1 -1
  183. package/out/src/storage/portable/index.js +4 -2
  184. package/out/src/storage/portable/index.js.map +1 -1
  185. package/out/src/storage/remoting/StorageClientBase.d.ts +2 -1
  186. package/out/src/storage/remoting/StorageClientBase.d.ts.map +1 -1
  187. package/out/src/storage/remoting/StorageClientBase.js +3 -0
  188. package/out/src/storage/remoting/StorageClientBase.js.map +1 -1
  189. package/out/src/storage/remoting/StorageServer.d.ts.map +1 -1
  190. package/out/src/storage/remoting/StorageServer.js +4 -1
  191. package/out/src/storage/remoting/StorageServer.js.map +1 -1
  192. package/out/src/storage/schema/KnexMigrations.d.ts +1 -0
  193. package/out/src/storage/schema/KnexMigrations.d.ts.map +1 -1
  194. package/out/src/storage/schema/KnexMigrations.js +29 -3
  195. package/out/src/storage/schema/KnexMigrations.js.map +1 -1
  196. package/out/src/storage/schema/entities/EntityOutputBasket.d.ts.map +1 -1
  197. package/out/src/storage/schema/entities/EntityOutputBasket.js +5 -2
  198. package/out/src/storage/schema/entities/EntityOutputBasket.js.map +1 -1
  199. package/out/src/utility/brc153ReferenceLabels.d.ts +21 -0
  200. package/out/src/utility/brc153ReferenceLabels.d.ts.map +1 -0
  201. package/out/src/utility/brc153ReferenceLabels.js +45 -0
  202. package/out/src/utility/brc153ReferenceLabels.js.map +1 -0
  203. package/out/src/utility/index.all.d.ts +1 -0
  204. package/out/src/utility/index.all.d.ts.map +1 -1
  205. package/out/src/utility/index.all.js +1 -0
  206. package/out/src/utility/index.all.js.map +1 -1
  207. package/out/src/utility/index.client.d.ts +1 -0
  208. package/out/src/utility/index.client.d.ts.map +1 -1
  209. package/out/src/utility/index.client.js +1 -0
  210. package/out/src/utility/index.client.js.map +1 -1
  211. package/out/src/utility/utilityHelpers.d.ts +1 -1
  212. package/out/src/utility/utilityHelpers.d.ts.map +1 -1
  213. package/out/src/utility/utilityHelpers.js +2 -1
  214. package/out/src/utility/utilityHelpers.js.map +1 -1
  215. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -6,14 +6,83 @@ attention to changes that materially alter behavior or extend functionality.
6
6
 
7
7
  ## wallet-toolbox (unreleased)
8
8
 
9
- - Allow immediate actions to chain wallet-managed change from transactions
10
- awaiting background broadcast when settled change is insufficient. The child
11
- broadcast recursively includes the delayed parent BEEF, preventing a large
12
- funding output from making the wallet appear temporarily unfunded while
13
- preserving settled-change preference and delayed-broadcast semantics.
14
- - Finish broadcasting durable permission-token grants before resuming the
15
- waiting application request, preventing the grant transaction from briefly
16
- reserving the wallet's funding inputs out from under the resumed action.
9
+ - Age proactive pending-transaction review from immutable request creation
10
+ time, so repeated proof polling cannot keep a rejected transaction below the
11
+ reconciliation threshold forever. A descendant of a locally terminal parent
12
+ now fails from that durable storage evidence; failed-parent outputs remain
13
+ quarantined while unrelated inputs are released for reuse. The shared change
14
+ adds about 1.1 kB to the Vite browser artifact and 3.8 kB to Hermes bytecode;
15
+ the reviewed raw ceilings advance to 1,585,000 and 3,322,000 bytes while all
16
+ compressed ceilings remain unchanged; the esbuild raw ceiling advances to
17
+ 1,238,000 bytes.
18
+ - Keep mainnet and testnet ChainTracks usable from browser and webview wallets:
19
+ browser runtimes temporarily select the CORS-enabled legacy service while
20
+ Node runtimes retain Arcade/go-chaintracks v2. `Services.getHeight` now falls
21
+ back to WhatsOnChain if ChainTracks is unavailable and preserves the original
22
+ ChainTracks error when both providers fail.
23
+ - Route `ttn` wallets through the isolated `teratestnet` overlay preset and
24
+ enable the public TTN Arcade broadcaster/proof provider by default. TTN does
25
+ not register the incompatible legacy ARC BEEF fallback. Other chain defaults
26
+ are unchanged, and an empty Arcade URL explicitly disables Arcade.
27
+ - Reconcile Arcade broadcast outcomes as durable wallet state. Retryable
28
+ locktime and parent conditions remain pending, while validator failures fail
29
+ the request and explicit missing-input or conflict evidence atomically fails
30
+ every local transaction copy and quarantines its wallet-owned inputs without
31
+ depending on WhatsOnChain. Cached accepted/seen labels can no longer revive a
32
+ terminal conflict; only a mined event whose Merkle proof validates through
33
+ the configured chain tracker may repair it. Arcade also participates in the
34
+ shared transaction-status service so double-spend review remains conservative
35
+ on networks with no explorer. A scheduled paged pass also reconciles aged
36
+ pending requests from durable Arcade lifecycle state, including orphan-mempool
37
+ losers whose SSE event was missed; mined/known evidence wins over rejection.
38
+ Inconclusive or absent UTXO providers no longer
39
+ masquerade as a spent-output verdict in invalid-change release, stale-input
40
+ reconciliation, proof recovery, or storage diagnostics. Invalid-change review
41
+ returns conclusive read-only findings plus unknown diagnostics. Direct release
42
+ blocks atomically with machine-readable `WERR_UTXO_REVIEW_INCONCLUSIVE` when
43
+ any result is unknown. Monitor Admin uses bounded, five-second-deadline pages
44
+ and can explicitly release the confirmed-spent subset while retaining unknowns;
45
+ every mutation rechecks ownership/allocation under lock and records audit
46
+ evidence. Monitor Admin scans by default and requires explicit confirmation.
47
+ The
48
+ Arcade SSE cursor advances only after both event storage work and cursor
49
+ persistence succeed; either failure leaves the event queued for ordered retry.
50
+ - Replace 32-satoshi default-basket fragments with a progressive liquidity
51
+ policy targeting 144 useful 5,000-satoshi outputs. New actions create at most
52
+ eight outputs from real surplus and migrate at most four fee-positive legacy
53
+ fragments, while a same-tier compatibility plan guarantees that optional
54
+ shaping cannot refuse an action the former planner could fund. Explicitly
55
+ funded actions materialize change from their existing surplus without
56
+ gathering pool inputs, and SQLite policy migrations use sync-compatible UTC
57
+ ISO timestamps.
58
+ - Prefer completed, then unproven, then sending parents. Plans above 16 inputs
59
+ compare exact transaction-plus-BEEF bytes before accepting pending ancestry;
60
+ pending change remains an unconditional last-resort funding source. Align
61
+ action-batch reservation/planning and add a read-only Monitor liquidity
62
+ report. All work limits are configurable and accept `-1` for explicit
63
+ operator-selected unlimited behavior.
64
+ - Restore delayed broadcast for durable permission-token persistence. Permission
65
+ grants no longer inherit network-broadcast latency; the managed-change policy
66
+ handles queued ancestry without hiding it or preferring it over settled funds.
67
+ - Isolate each in-memory action batch by explicit staged-output or `sendWith`
68
+ membership, so unrelated immediate actions and `noSend` roots cannot be
69
+ captured by or commit a workspace. Add an exact-input resume protocol for
70
+ expired leases, structured lifecycle errors, and a provider-enforced
71
+ cumulative reservation limit that defaults to 256 outputs and can be
72
+ configured, including `-1` for operator-selected unlimited operation.
73
+ - Keep the combined action-batch, managed-liquidity, browser-fallback, and
74
+ fail-safe reconciliation browser/mobile cost bounded by exact packed-artifact
75
+ contracts. Local verification measures 1,557,683 raw / 366,927 gzip / 287,337
76
+ Brotli bytes with Vite, 1,215,983 raw / 333,466 gzip / 268,497 Brotli bytes
77
+ with esbuild, 1,617,544 raw Metro bytes, and 3,271,146 raw / 1,302,110 gzip /
78
+ 1,025,812 Brotli Hermes bytes. Reviewed raw ceilings are 1,561,000 Vite bytes,
79
+ 1,220,000 esbuild bytes, 1,710,000 Metro bytes, and 3,275,000 Hermes bytes;
80
+ compressed ceilings remain independently governed.
81
+ - Fix `WalletStorageManager.getStoreEndpointURL` / `getStores().endpointURL` to
82
+ duck-type provider `endpointUrl` instead of matching
83
+ `constructor.name === 'StorageClient'`. Production minifiers rename classes,
84
+ so the name check left remote stores with `endpointURL: undefined` while
85
+ sync still worked; clients that select a backup by URL (make primary) failed.
17
86
  - Let Storage Server operators select an explicit listener host while retaining
18
87
  the historical omitted-host behavior for existing callers. The official
19
88
  Wallet Infrastructure image uses this to bind direct-mode traffic on IPv4
package/README.md CHANGED
@@ -26,18 +26,16 @@ BSV Desktop and BSV Browser are the BSV Association reference wallet application
26
26
  | **MockChain** | In-memory blockchain for testing — mock mining, UTXO tracking, and merkle proof generation without a network |
27
27
  | **Entropy** | `EntropyCollector` gathers mouse/touch entropy for high-quality randomness in browser environments |
28
28
 
29
- Durable permission grants finish broadcasting their internal token transaction
30
- before the waiting application request resumes. This keeps the grant atomic from
31
- the caller's perspective and makes its funding change immediately reusable by a
32
- following wallet action. If broadcasting is unavailable, the grant rejects and
33
- the application can safely surface the error and retry; ephemeral one-time grants
34
- remain off-chain.
35
-
36
- Immediate actions can fund from wallet-managed change created by a transaction
37
- that is still awaiting background broadcast when settled change is insufficient.
38
- The wallet prefers settled change, then recursively includes the delayed parent
39
- in the child BEEF only when needed, so queued work cannot temporarily strand the
40
- wallet's balance behind a large reserved input.
29
+ Durable permission grants queue their internal token transaction for delayed
30
+ broadcast, so permission approval does not inherit network-broadcast latency.
31
+ The funding planner prefers settled change and uses queued permission ancestry
32
+ only as a last resort, keeping the application path fast without hiding funds.
33
+
34
+ Immediate actions prefer completed, then unproven, then sending change. A
35
+ pathological settled plan is compared with pending alternatives by exact
36
+ serialized BEEF plus transaction bytes; queued ancestry is used only when it is
37
+ necessary or smaller. Pending change is never withheld, so queued
38
+ work cannot strand the balance behind a large reserved input.
41
39
 
42
40
  ### Packages
43
41
 
@@ -49,9 +47,13 @@ The toolbox publishes three npm packages from this repo:
49
47
 
50
48
  ### ChainTracks sources and networks
51
49
 
52
- Wallet services do not require a WhatsOnChain key for ChainTracks. Mainnet,
53
- testnet, and TerraTestNet use the public Arcade/go-chaintracks v2 HTTP and SSE
54
- surfaces by default. Bulk batches still pass through local serialization, hash,
50
+ Wallet services do not require a WhatsOnChain key for ChainTracks. Node
51
+ runtimes on mainnet, testnet, and TerraTestNet use the public
52
+ Arcade/go-chaintracks v2 HTTP and SSE surfaces by default. Browser and webview
53
+ runtimes on mainnet/testnet temporarily select the legacy CORS-enabled service
54
+ until the v2 edge serves CORS and OPTIONS; `Services.getHeight` also falls back
55
+ to WhatsOnChain on those networks if ChainTracks is unavailable. Bulk batches
56
+ still pass through local serialization, hash,
55
57
  continuity, and genesis checks; providers are tried in priority order; and a
56
58
  synchronized tracker can continue serving its last-good checked data during a
57
59
  provider outage. WhatsOnChain remains a mainnet/testnet fallback and anonymous
@@ -66,8 +68,55 @@ go-chaintracks client; existing legacy v1 URLs and explicit clients remain
66
68
  compatible. Browser and mobile distributions expose the same fetch/SSE client
67
69
  without Node `Buffer` or filesystem dependencies.
68
70
 
69
- Arcade is the browser-safe HTTPS/SSE gateway for Teranode-backed header data.
70
- Direct Teranode P2P is not included in browser/mobile artifacts.
71
+ Arcade is the HTTPS/SSE gateway for Teranode-backed header data. Its v2 edge
72
+ must allow browser origins and OPTIONS before browser defaults can use it;
73
+ direct Teranode P2P is not included in browser/mobile artifacts.
74
+
75
+ TTN wallets also register
76
+ `https://arcade-v2-ttn-us-1.bsvblockchain.tech` as their first broadcast and
77
+ Merkle-proof provider. Mainnet and testnet Arcade broadcasting remains opt-in.
78
+ Pass an explicit `arcadeUrl` to override the TTN endpoint or an empty string to
79
+ disable it. TTN overlay lookups use the separate `teratestnet` resolver preset
80
+ and never fall back to testnet discovery.
81
+
82
+ ### Broadcast rejection and monitor reconciliation
83
+
84
+ When Arcade is configured, Wallet Toolbox consumes Arcade's status code and
85
+ validator detail instead of treating every `REJECTED` event alike. Retryable
86
+ parent and locktime conditions stay pending. Terminal validator failures fail
87
+ the request, and explicit missing-input or conflict evidence also quarantines
88
+ every wallet-owned copy of the consumed input in the same storage transaction.
89
+ That quarantine uses Arcade's positive rejection evidence and does not require
90
+ WhatsOnChain or another UTXO explorer.
91
+
92
+ Arcade is also registered as a transaction-status provider, so monitor review
93
+ continues on networks without WhatsOnChain. A scheduled bounded pass revisits
94
+ pending requests after their immutable creation-age threshold and applies
95
+ durable Arcade lifecycle verdicts that may have arrived while SSE was
96
+ disconnected, including `SEEN_IN_ORPHAN_MEMPOOL`. Routine proof checks may
97
+ refresh diagnostic timestamps without postponing that review. A descendant of
98
+ a locally terminal parent is also failed from that storage evidence; the failed
99
+ parent output remains quarantined while unrelated inputs are released for
100
+ reuse. Mined/known evidence takes precedence over a stale rejection. Provider
101
+ absence and provider errors are treated as inconclusive, never as proof that an
102
+ output was spent.
103
+ After an input conflict has been recorded, a later cached accepted/seen label
104
+ cannot restore the failed transaction; recovery requires a mined status and a
105
+ Merkle proof validated by the configured chain tracker. Arcade SSE events are
106
+ acknowledged in order only after their storage update and cursor persistence
107
+ succeed, so a transient storage failure is retried instead of skipped.
108
+
109
+ Invalid-change review applies the same positive-evidence rule. Only an
110
+ explicit successful `isUtxo: false` result is considered spent; a provider
111
+ error, rate limit, timeout, missing provider, missing script, or malformed
112
+ response is unknown. Read-only scans return the conclusive picture plus the
113
+ unknown count. Direct destructive release remains all-or-nothing: any unknown
114
+ throws `WERR_UTXO_REVIEW_INCONCLUSIVE` before mutation. The authenticated
115
+ Monitor Admin tool instead uses 20-output pages (four provider calls in flight,
116
+ five-second per-output review deadline) and may explicitly release the
117
+ positively spent subset while retaining and reporting unknowns. Each confirmed
118
+ spent output is rechecked for ownership and allocation state under the write
119
+ lock, and every release or blocked release records bounded audit evidence.
71
120
 
72
121
  Core ChainTracks factories accept a final source-options argument when an
73
122
  application must override the defaults. Set `disableChaintracks`, `disableCdn`,
@@ -128,6 +177,11 @@ See [Managed change, sweeping, and recovery](./docs/managed-change-policy.md)
128
177
  for the default-basket invariant, automatic funding policy, and supported
129
178
  `internalizeAction` repair paths.
130
179
 
180
+ See [Managed-change liquidity policy](./docs/managed-change-liquidity.md) for
181
+ the 144-output / 5,000-satoshi defaults, gradual legacy-wallet migration,
182
+ pending-parent policy, exact BEEF comparison, operator tuning, action-batch
183
+ alignment, monitoring, and rollout guidance.
184
+
131
185
  See [In-memory action batch planning](./docs/action-batch-planning.md) for
132
186
  capability-negotiated `noSend` planning, compact manifests, compressed binary
133
187
  pack transport, atomic commit, compatibility behavior, and retained benchmarks.
@@ -145,7 +199,7 @@ The planner uses the same exact / least-over / largest-under selection policy
145
199
  as the historical allocator, but proves economic sufficiency before writing a
146
200
  transaction and claims every selected input in one database transaction. Knex
147
201
  storage automatically adds a composite funding-selection index on migration;
148
- IndexedDB schema version 3 adds corresponding user/basket and outpoint indexes
202
+ IndexedDB schema version 4 adds corresponding user/basket and outpoint indexes
149
203
  and resolves transaction-status eligibility in one indexed pass.
150
204
 
151
205
  The retained fragmented-funding benchmark is runnable with:
@@ -17,12 +17,34 @@ Large first actions use compact bootstrap only when the provider also advertises
17
17
  `compactBegin: true`; this keeps new clients compatible with older version-1
18
18
  servers during a rolling deployment.
19
19
 
20
+ One `Wallet` instance owns at most one in-memory action-batch workspace. An
21
+ independent `noSend` graph created while that workspace is open uses the ordinary
22
+ persistent path; it is not captured merely because the calls overlap. Storage
23
+ providers may host many concurrent batches for different wallets or sessions,
24
+ and their output reservations remain disjoint. A future client-side multi-workspace
25
+ router would also need defined semantics for a `sendWith` request or child action
26
+ that joins more than one graph; this version deliberately does not imply those
27
+ semantics.
28
+
20
29
  The original manifest and one-blob-per-request transport remain version 1. A
21
30
  provider can independently advertise the additive `manifestVersion: 2`,
22
- `commitByDigest`, and `packedUploads` capabilities. A new client uses each
23
- optimization only when its provider advertises it. Old clients ignore the extra
24
- fields, new clients retain the version-1 path against old providers, and the two
25
- new storage methods are optional for third-party `WalletStorage` implementations.
31
+ `commitByDigest`, `resume`, `maxReservedOutputs`, and `packedUploads`
32
+ capabilities. A new client uses each optimization only when its provider
33
+ advertises it. Old clients ignore the extra fields, new clients retain the
34
+ version-1 path against old providers, and additive storage methods remain
35
+ optional for third-party `WalletStorage` implementations. The exported capability
36
+ helper does not advertise `resume` by default; built-in providers opt in because
37
+ they implement the optional `resumeActionBatch` method.
38
+
39
+ The retained browser platform contract measures the resumable implementation at
40
+ 1,548,179 raw / 364,550 gzip / 285,697 Brotli bytes with Vite and 1,209,217 raw /
41
+ 331,318 gzip / 267,001 Brotli bytes with esbuild. Every browser size ceiling
42
+ remains unchanged.
43
+
44
+ The mobile contract measures 1,609,633 raw / 405,646 gzip / 315,527 Brotli
45
+ Metro bytes and 3,253,366 raw / 1,293,591 gzip / 1,018,837 Brotli Hermes bytes.
46
+ Only the Hermes raw ceiling advances, by less than 0.15%; every compressed and
47
+ Metro ceiling remains unchanged.
26
48
 
27
49
  The capability and its methods are Wallet Toolbox storage extensions. They do not
28
50
  extend `WalletInterface`, `CreateActionArgs`, `SignActionArgs`, `noSend`,
@@ -36,17 +58,26 @@ extend `WalletInterface`, `CreateActionArgs`, `SignActionArgs`, `noSend`,
36
58
  output-script lengths at bootstrap. Signed transactions and the external
37
59
  proof frontier follow through the binary commit path, so a hexadecimal
38
60
  script is not duplicated into a large JSON request.
39
- 2. The wallet plans, signs, validates, and indexes subsequent actions locally. A
40
- shared BEEF graph avoids retaining repeated ancestry. The signed transaction
41
- is the canonical source for output scripts; format 2 carries script digests
42
- for validation, rather than carrying the same scripts as transaction bytes,
43
- plan strings, metadata strings, and standalone blobs.
61
+ 2. The wallet plans, signs, validates, and indexes a subsequent action locally
62
+ only when it belongs to the same transaction graph: an input or
63
+ `noSendChange` outpoint references a staged output, or `sendWith` references
64
+ a transaction owned by the workspace. Time adjacency, a shared wallet or
65
+ originator, and merely being another `noSend` action do not establish
66
+ membership. Unrelated work continues through the ordinary persistence path.
67
+ A shared BEEF graph avoids retaining repeated ancestry. The signed
68
+ transaction is the canonical source for output scripts; format 2 carries
69
+ script digests for validation rather than duplicating scripts.
44
70
  3. If confirmed funding runs low, the wallet extends its reservation pool using
45
71
  an EWMA estimate and geometrically increasing runway. Forwarded staged change
46
72
  needs no reservation or extension.
47
- 4. `sendWith`, or a normal action created while the workspace is open, validates
48
- and atomically persists every staged action. Only the requested transactions
49
- are sent; earlier actions retain their `nosend` status.
73
+ 4. `sendWith` that names a workspace transaction, or a normal action that
74
+ explicitly depends on a staged output, validates and atomically persists
75
+ every member action. An unrelated normal action neither joins nor commits
76
+ the workspace. Only the requested transactions are sent; earlier actions
77
+ retain their `nosend` status. If a workspace-owned `noSend` action also
78
+ requests only unrelated persisted transactions through `sendWith`, the new
79
+ action remains staged and the explicit broadcasts run through the ordinary
80
+ persistence path; neither request is silently dropped.
50
81
  5. Broadcast occurs after the storage transaction. Delayed mode returns after the
51
82
  batch is durably queued; immediate mode uses the existing aggregate broadcaster
52
83
  and review results.
@@ -62,17 +93,24 @@ can use unreserved outputs normally. A uniqueness constraint prevents one output
62
93
  from belonging to two active batches.
63
94
 
64
95
  The initial reservation includes at most three extra candidates and never more
65
- than eight outputs. Its canonical funding target includes the marginal P2PKH
66
- input fee and enough value to recover an economically viable first change output,
67
- so a low basket minimum cannot repeatedly select inputs that satisfy the nominal
68
- deficit but cost too much to use.
69
-
70
- Extensions add at most 64 outputs per storage call, but there is no cumulative
71
- reservation, workspace-size, action-count, or spend-chain limit. Additional
72
- bounded calls continue for as long as the workspace needs confirmed funding.
73
- They use the same exact, least-over, then largest-under selection policy as
74
- normal funding. Retry targets are incremental shortfalls: the planner credits
75
- the value and count of every unconsumed reserved output before asking for more.
96
+ than eight outputs or the operator's lower cumulative reservation cap. Its
97
+ canonical funding target includes the marginal P2PKH input fee and enough value
98
+ to recover an economically viable first change output, so a low basket minimum
99
+ cannot repeatedly select inputs that satisfy the nominal deficit but cost too
100
+ much to use.
101
+
102
+ Extensions add at most 64 outputs per storage call. Built-in providers also
103
+ enforce and advertise a cumulative maximum of 256 persisted output reservations
104
+ per workspace, including caller-selected persisted inputs and confirmed inputs
105
+ already consumed by uncommitted member transactions. This is a provider resource
106
+ bound, not a transaction or consensus input limit; staged outputs do not consume
107
+ additional persisted reservations. Set `StorageProviderOptions.actionBatchMaxReservedOutputs`
108
+ (or the matching `SetupWalletKnexArgs` option when using
109
+ `Setup.createStorageKnex`) to another positive integer, or to `-1` for
110
+ operator-selected unlimited operation. The provider advertises the effective value. Extensions use the same
111
+ exact, least-over, then largest-under selection policy as normal funding. Retry
112
+ targets are incremental shortfalls: the planner credits the value and count of
113
+ every unconsumed reserved output before asking for more.
76
114
  Confirmed inputs already used by staged transactions remain reserved until
77
115
  commit but are not credited as available funding. Proactive EWMA requests
78
116
  likewise subtract the unconsumed pool, initialize from the first complete
@@ -80,9 +118,14 @@ sample, and increase geometric runway only when the provider fulfills both the
80
118
  requested count and value. Empty or partial extensions therefore cannot
81
119
  compound a target against unchanged wallet state.
82
120
  Leases last 15 minutes with a 60-minute hard lifetime. Long-running workspaces
83
- renew near 80% of the lease; commit may atomically reacquire an expired reservation
84
- when no conflicting spend or reservation occurred. Commit, abort, wallet
85
- destruction, expiry cleanup, and the one-minute monitor task release unused state.
121
+ renew near 80% of the lease. A client and provider advertising `resume` can
122
+ atomically reacquire only the exact persisted outpoints already held by that
123
+ workspace after soft expiry; a conflicting spend or reservation fails that
124
+ connected workflow without capturing unrelated actions. Lifecycle failures use
125
+ `WERR_ACTION_BATCH_STATE` with a machine-readable state rather than message-text
126
+ matching. Version-1 commit retains its compatible expired-input reacquisition
127
+ path. Commit, abort, wallet destruction, expiry cleanup, and the one-minute
128
+ monitor task release unused state.
86
129
 
87
130
  ## Atomic commit and payload transport
88
131
 
@@ -193,15 +236,15 @@ pnpm --filter @bsv/wallet-toolbox bench:action-batch
193
236
  On the same Apple Silicon host with Node.js v25.9.0, a representative cold
194
237
  250-action, 1 KiB-script run reduced planning/signing/validation from
195
238
  19,117.03 ms in legacy mode to 10,907.93 ms in batch mode (42.9%, or 1.75x).
196
- Including atomic commit, total local time was 19,163.87 ms versus 11,462.13 ms
197
- (40.2% lower), while storage calls fell from 501 to 2 and database transactions
198
- from 252 to 3. At 100 ms storage RTT those calls represent 50,100 ms versus
199
- 200 ms of control-path latency, a 99.6% reduction.
200
-
201
- A generic single 4 MiB zero-filled script took 1,258.13 ms through the legacy
202
- path and 208.60 ms through format 2, including upload and atomic commit: 83.4%
203
- less local time. Its instrumented requests fell from 13,982,465 bytes to 8,064
204
- bytes. The one 4,195,234-byte logical pack compressed to 5,233 bytes and was
239
+ Including atomic commit, total local time was 11,131.49 ms versus 5,795.95 ms
240
+ (47.9% lower), while storage calls and database transactions fell from 750 to
241
+ 2 and from 750 to 3, respectively. At 100 ms storage RTT those calls represent
242
+ 75,000 ms versus 200 ms of control-path latency, a 99.7% reduction.
243
+
244
+ A generic single 4 MiB zero-filled script took 1,160.86 ms through the legacy
245
+ path and 288.20 ms through format 2, including upload and atomic commit: 75.2%
246
+ less local time. Its instrumented requests fell from 13,982,465 bytes to 8,067
247
+ bytes. The one 4,195,234-byte logical pack compressed to 5,236 bytes and was
205
248
  uploaded in one call. This synthetic compression ratio represents highly
206
249
  repetitive data; incompressible data falls back to identity encoding without
207
250
  expansion. Absolute times remain host- and database-dependent.
@@ -210,8 +253,8 @@ It records planning, signing and validation, storage RPCs, database transactions
210
253
  request bytes, commit and broadcast time, CPU, and incremental peak heap. Physical
211
254
  SQLite runs compare 1, 10, 50, and 250 action chains at 1 KiB and exercise each
212
255
  larger script size with a real action, including the 4 MiB upload path. Its
213
- complete model covers dependent, independent, explicit-input, and two-step
214
- signing workloads at every action count; 1 KiB, 64 KiB, 1 MiB, and 4 MiB
256
+ complete model covers dependent, isolated-root fallback, explicit-input, and
257
+ two-step signing workloads at every action count; 1 KiB, 64 KiB, 1 MiB, and 4 MiB
215
258
  scripts; and 25, 100, and 250 ms storage RTT. The measured cases deliberately
216
259
  include both many-small-action and few-large-action shapes so transport tuning
217
260
  is not fitted to one contract or graph. Production rollout should additionally
@@ -0,0 +1,251 @@
1
+ # Managed-change liquidity policy
2
+
3
+ Wallet-managed change is both the wallet's balance and its concurrency pool.
4
+ An output that is technically spendable but too small to carry a useful action
5
+ at the current fee rate does not provide useful liquidity. Conversely, creating
6
+ the entire pool in one transaction makes every child carry a large common BEEF
7
+ ancestor and creates unnecessary linkage.
8
+
9
+ This policy keeps `createAction` available to existing callers while gradually
10
+ moving new and existing wallets toward useful, parallel funding units.
11
+
12
+ ## Invariants
13
+
14
+ The implementation treats these as non-negotiable:
15
+
16
+ 1. A policy preference cannot make an action fail if the former planner could
17
+ fund it. Each parent-status tier retries the compatibility funding shape
18
+ before the planner widens to less-preferred ancestry.
19
+ 2. `completed` parents are preferred, then `unproven`, then `sending`.
20
+ Pending outputs are never withheld: they remain a last-resort source and
21
+ are selected when settled liquidity cannot fund the action.
22
+ 3. A large settled-input plan may be compared with pending alternatives, but
23
+ only after the configurable comparison threshold. The comparison uses the
24
+ actual serialized BEEF bytes plus the planned transaction bytes; input
25
+ count and satoshi value are not used as BEEF-size proxies.
26
+ 4. Pool growth consumes only surplus already present after the requested
27
+ action and its exact incremental fee are funded. The wallet does not gather
28
+ another input merely to manufacture change outputs.
29
+ 5. An output below the preferred value is permitted when it is the only
30
+ fundable remainder. The preferred value is not a dust rule and cannot turn
31
+ a valid payment into `WERR_INSUFFICIENT_FUNDS`.
32
+ 6. Legacy fragments migrate only on a caller-authorized `createAction`, only
33
+ when their value exceeds their marginal input fee, and only within the
34
+ configured per-action budget.
35
+ 7. Action-batch workspaces reserve disjoint outputs and use the same change
36
+ values and per-action shaping limits as the legacy `createAction` path.
37
+ 8. Permission-token persistence retains delayed broadcast so a permission
38
+ grant does not inherit network-broadcast latency. The funding policy makes
39
+ queued change available only after preferred alternatives are exhausted.
40
+
41
+ ## Defaults
42
+
43
+ | Setting | Default | Purpose |
44
+ | ---------------------------------- | -------------: | ---------------------------------------------------------------------------------------------------------------- |
45
+ | Default-basket target | 144 outputs | Supports many independently planned actions without requiring one large fanout transaction. |
46
+ | Preferred output value | 5,000 satoshis | Keeps a liquidity unit useful at fee rates materially above the historical 32-satoshi era. |
47
+ | New outputs per action | 8 | Builds the pool progressively and bounds any one transaction's fanout and descendant BEEF footprint. |
48
+ | Legacy migration inputs per action | 4 | Retires old fragments progressively without recreating 178-input permission transactions. |
49
+ | Pending-comparison threshold | 16 inputs | Keeps the common settled path fast; above this point the planner measures alternatives by exact serialized cost. |
50
+
51
+ At the Wallet Toolbox default of 100 satoshis/kB, a 148-byte managed input adds
52
+ about 15 satoshis of fee and a minimal one-input/one-output transaction costs
53
+ about 20 satoshis. A 5,000-satoshi preferred unit is therefore roughly 250
54
+ minimal-spend fees at that rate. Even at 1,000 satoshis/kB it remains roughly
55
+ 26 minimal-spend fees. The value is intentionally a liquidity target, not a
56
+ consensus or economic-dust boundary.
57
+
58
+ A completely filled default pool represents 720,000 satoshis. Wallets with a
59
+ smaller balance do not attempt to manufacture that reserve. They retain fewer
60
+ outputs, and a remainder below 5,000 satoshis is kept when that is the only
61
+ available shape.
62
+
63
+ ## Funding and ancestry selection
64
+
65
+ For each action, storage loads unreserved managed change once and plans in this
66
+ order:
67
+
68
+ 1. completed parents;
69
+ 2. completed plus unproven parents;
70
+ 3. completed, unproven, and sending parents.
71
+
72
+ Within each tier, the new surplus-only shape runs first. If that shape reports
73
+ insufficient funds, the same tier is immediately retried with the former
74
+ funding algorithm and the allocator's economic floor for its first remainder.
75
+ This is deliberately at least as permissive as the historical 32-satoshi
76
+ basket. An inability to create a preferred 5,000-satoshi change output is not
77
+ evidence that settled funds cannot pay the requested output.
78
+
79
+ The first successful plan is accepted immediately when it uses no more than 16
80
+ managed inputs. Above that threshold, later status tiers are also planned and
81
+ the wallet compares:
82
+
83
+ ```text
84
+ serialized cost = planned transaction bytes + exact input BEEF bytes
85
+ ```
86
+
87
+ The smallest measured plan wins. If proof retrieval needed only for comparison
88
+ is unavailable, that alternative receives an infinite comparison cost; the
89
+ already fundable baseline remains available. This optimization can therefore
90
+ improve latency and BEEF size but cannot become a new availability dependency.
91
+
92
+ Using a `sending` parent necessarily extends the unconfirmed BEEF chain and a
93
+ failed ancestor can invalidate its descendants. That is why it is last in the
94
+ normal order. It remains supported because refusing a fundable user action is
95
+ worse than reluctantly extending the chain after all safer liquidity is
96
+ exhausted.
97
+
98
+ ## Progressive pool shaping
99
+
100
+ Explicit or fixed inputs can already cover the requested outputs and fee. In
101
+ that case the planner materializes the first change output directly from that
102
+ existing surplus before considering optional fragment migration. It does not
103
+ call the managed-change allocator merely to create pool outputs. This keeps
104
+ consolidations and externally funded actions on the same shaping policy while
105
+ preserving the invariant that pool growth never gathers compulsory inputs.
106
+ When the surplus cannot pay both the marginal output fee and the economic dust
107
+ floor, the bounded remainder stays in the transaction fee instead of causing a
108
+ compatibility retry to gather another input solely to manufacture change.
109
+
110
+ After compulsory funding succeeds, the planner may consume up to four
111
+ undersized outputs. A fragment is skipped when spending it would cost at least
112
+ its value. Optional migration never supplies a missing satoshi for the caller's
113
+ requested outputs and never runs when the basket is already at its target.
114
+
115
+ The resulting surplus is split into at most eight outputs and only when every
116
+ new output can meet the preferred value after paying the exact added output
117
+ fee. Otherwise the wallet keeps one output. Excess is distributed through the
118
+ existing randomized change algorithm, and ordinary output randomization still
119
+ applies. The policy therefore preserves the existing privacy intent and
120
+ non-uniform values whenever surplus exists instead of replacing it with a
121
+ fixed deterministic denomination scheme. When the available value is exactly
122
+ the sum of the preferred minima, equal minima are unavoidable; the wallet does
123
+ not create a smaller output merely to force cosmetic variance.
124
+
125
+ The target is based on healthy outputs (those at or above the basket's
126
+ preferred value), not raw output count. Consuming a legacy fragment and
127
+ creating a useful output moves the pool forward; merely retaining another
128
+ 32-satoshi fragment does not make the pool appear healthy.
129
+
130
+ ## Existing-wallet migration
131
+
132
+ The SQL and IndexedDB providers recognize only the exact historical untouched
133
+ default:
134
+
135
+ ```text
136
+ basket name = default
137
+ target count = 144
138
+ minimum value = 32
139
+ ```
140
+
141
+ Those baskets advance to a 5,000-satoshi preferred value during the normal
142
+ provider migration. Other basket names, custom target counts, and custom
143
+ minimum values are unchanged. The migration does not consolidate, sign, or
144
+ broadcast a transaction. Future `createAction` calls progressively consume at
145
+ most the configured number of fee-positive fragments and create useful change
146
+ only from real surplus.
147
+
148
+ The SQL data migration is intentionally one-way. Rolling code back does not
149
+ rewrite a migrated preference to 32 or fragment funds. Older code can still
150
+ read and honor the 5,000-satoshi basket value. SQLite writes the migrated
151
+ row's timestamp in UTC ISO form, matching incremental-sync query values and
152
+ keeping that metadata immediately sync-visible. MySQL retains its native
153
+ millisecond timestamp expression.
154
+
155
+ ## Operator configuration
156
+
157
+ The defaults are suitable for a general wallet. Local Knex and IndexedDB
158
+ operators can tune the work budgets without rebuilding the package:
159
+
160
+ ```ts
161
+ const setup = await Setup.createWalletKnex({
162
+ ...args,
163
+ managedChangePolicy: {
164
+ maxOutputsPerAction: 8,
165
+ migrationInputsPerAction: 4,
166
+ pendingComparisonInputs: 16
167
+ }
168
+ })
169
+ ```
170
+
171
+ The same `managedChangePolicy` option is accepted by `StorageKnex`,
172
+ `StorageIdb`, `Setup.createStorageKnex`, and `SetupClient.createStorageIdb`.
173
+ The basket target and preferred value remain user-wallet settings and can be
174
+ changed through `wallet.setWalletChangeParams(count, satoshis)`.
175
+
176
+ Each policy limit accepts `-1`, with deliberately different meanings:
177
+
178
+ - `maxOutputsPerAction: -1` makes the basket target the only fanout bound;
179
+ - `migrationInputsPerAction: -1` permits all fee-positive legacy fragments in
180
+ one authorized action;
181
+ - `pendingComparisonInputs: -1` disables optional pending-plan comparison, but
182
+ pending funds remain available when earlier tiers are insufficient.
183
+
184
+ Unlimited modes can create large transactions or BEEF payloads and should be
185
+ used only by operators that have measured their workload. They do not remove
186
+ the wallet's economic-dust check, transaction validity checks, action-batch
187
+ reservation limit, or available-funding bound.
188
+
189
+ The official `wallet-infra` image exposes the same settings as validated
190
+ environment values:
191
+
192
+ ```dotenv
193
+ WALLET_STORAGE_MANAGED_CHANGE_MAX_OUTPUTS_PER_ACTION=8
194
+ WALLET_STORAGE_MANAGED_CHANGE_MIGRATION_INPUTS_PER_ACTION=4
195
+ WALLET_STORAGE_MANAGED_CHANGE_PENDING_COMPARISON_INPUTS=16
196
+ ```
197
+
198
+ This lets a hosted Wallet Storage provider choose the policy without rebuilding
199
+ the image. Invalid, unsafe-integer, or out-of-range values fail startup instead
200
+ of silently falling back. The API and singleton Monitor roles must use the same
201
+ values so planning, reservation, and operator reporting describe one policy.
202
+
203
+ ## Action batches and concurrent workspaces
204
+
205
+ An action-batch begin response carries the effective output and migration
206
+ limits when the provider supports them. Older and third-party providers can
207
+ omit the optional policy; clients then use the same defaults. Initial and
208
+ extended reservation selection prefers completed parents, then unproven, then
209
+ sending, while still reserving enough last-resort liquidity to avoid wedging a
210
+ workspace.
211
+
212
+ Workspaces remain isolated by explicit transaction-graph membership and their
213
+ reservations remain disjoint. Pool-shaping state is local to each plan; there
214
+ is no global in-memory "current batch" whose change can be consumed by an
215
+ unrelated action.
216
+
217
+ ## Monitoring and rollout
218
+
219
+ `TaskReviewUtxos.reviewManagedChangeByIdentityKey(identityKey)` is a read-only
220
+ operator report. It returns:
221
+
222
+ - total and target managed-change counts;
223
+ - healthy and undersized counts;
224
+ - active action-batch reservations;
225
+ - completed, unproven, and sending parent counts;
226
+ - total satoshis and the preferred minimum.
227
+
228
+ Monitor has no signing authority and does not perform consolidation. This
229
+ keeps migration tied to normal user-authorized actions and makes rollout
230
+ observable without creating surprise transactions. The official Monitor admin
231
+ UI exposes the same report as the **managed-change liquidity (read only)** UTXO
232
+ review mode.
233
+
234
+ Recommended rollout checks are:
235
+
236
+ 1. record the report before upgrade;
237
+ 2. migrate the storage provider and confirm only exact legacy defaults changed;
238
+ 3. exercise a small immediate action, a delayed permission action, and two
239
+ concurrent action-batch workspaces;
240
+ 4. confirm undersized count declines gradually and sending-parent use remains
241
+ exceptional;
242
+ 5. watch action input count, serialized BEEF bytes, fee, and broadcast failure
243
+ rates before changing defaults or selecting an unlimited mode.
244
+
245
+ ## Compatibility surface
246
+
247
+ No BRC-100 method, Wallet Wire method, Storage Server RPC method, or persisted
248
+ transaction format changes. The action-batch policy field is optional. Existing
249
+ custom basket settings remain authoritative. A same-tier compatibility plan,
250
+ followed by the retained pending-parent fallback, ensures the new preferences
251
+ do not add a refusal where the previous wallet could create an action.