@cogcoin/client 0.5.15 → 1.0.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 (174) hide show
  1. package/README.md +80 -25
  2. package/dist/app-paths.d.ts +5 -6
  3. package/dist/app-paths.js +8 -16
  4. package/dist/art/balance.txt +10 -0
  5. package/dist/art/welcome.txt +16 -0
  6. package/dist/bitcoind/bootstrap/controller.d.ts +1 -0
  7. package/dist/bitcoind/bootstrap/controller.js +53 -1
  8. package/dist/bitcoind/client/follow-block-times.d.ts +1 -0
  9. package/dist/bitcoind/client/follow-block-times.js +1 -1
  10. package/dist/bitcoind/client/internal-types.d.ts +7 -3
  11. package/dist/bitcoind/client/managed-client.d.ts +4 -2
  12. package/dist/bitcoind/client/managed-client.js +14 -0
  13. package/dist/bitcoind/client/sync-engine.js +72 -11
  14. package/dist/bitcoind/hash-order.d.ts +4 -0
  15. package/dist/bitcoind/hash-order.js +13 -0
  16. package/dist/bitcoind/indexer-daemon-main.js +11 -3
  17. package/dist/bitcoind/normalize.js +3 -2
  18. package/dist/bitcoind/processing-start-height.d.ts +5 -0
  19. package/dist/bitcoind/processing-start-height.js +7 -0
  20. package/dist/bitcoind/progress/constants.d.ts +4 -0
  21. package/dist/bitcoind/progress/constants.js +4 -0
  22. package/dist/bitcoind/progress/controller.d.ts +2 -1
  23. package/dist/bitcoind/progress/controller.js +3 -3
  24. package/dist/bitcoind/progress/follow-scene.d.ts +6 -2
  25. package/dist/bitcoind/progress/follow-scene.js +29 -6
  26. package/dist/bitcoind/progress/formatting.d.ts +1 -0
  27. package/dist/bitcoind/progress/formatting.js +6 -0
  28. package/dist/bitcoind/progress/train-scene.js +37 -18
  29. package/dist/bitcoind/progress/tty-renderer.d.ts +6 -1
  30. package/dist/bitcoind/progress/tty-renderer.js +8 -4
  31. package/dist/bitcoind/rpc.d.ts +2 -1
  32. package/dist/bitcoind/rpc.js +3 -0
  33. package/dist/bitcoind/types.d.ts +6 -0
  34. package/dist/bytes.d.ts +1 -0
  35. package/dist/bytes.js +3 -0
  36. package/dist/cli/art.d.ts +2 -0
  37. package/dist/cli/art.js +37 -0
  38. package/dist/cli/commands/client-admin.d.ts +2 -0
  39. package/dist/cli/commands/client-admin.js +91 -0
  40. package/dist/cli/commands/follow.js +0 -2
  41. package/dist/cli/commands/mining-admin.js +6 -47
  42. package/dist/cli/commands/mining-read.js +11 -50
  43. package/dist/cli/commands/mining-runtime.js +142 -5
  44. package/dist/cli/commands/service-runtime.js +0 -2
  45. package/dist/cli/commands/status.js +8 -2
  46. package/dist/cli/commands/sync.js +49 -92
  47. package/dist/cli/commands/wallet-admin.js +142 -136
  48. package/dist/cli/commands/wallet-mutation.js +91 -79
  49. package/dist/cli/commands/wallet-read.js +15 -18
  50. package/dist/cli/context.js +5 -14
  51. package/dist/cli/mining-format.d.ts +0 -1
  52. package/dist/cli/mining-format.js +5 -37
  53. package/dist/cli/mining-json.d.ts +0 -18
  54. package/dist/cli/mining-json.js +0 -35
  55. package/dist/cli/mutation-command-groups.d.ts +1 -2
  56. package/dist/cli/mutation-command-groups.js +0 -5
  57. package/dist/cli/mutation-json.d.ts +24 -145
  58. package/dist/cli/mutation-json.js +30 -136
  59. package/dist/cli/mutation-resolved-json.d.ts +0 -7
  60. package/dist/cli/mutation-resolved-json.js +4 -10
  61. package/dist/cli/mutation-success.d.ts +2 -0
  62. package/dist/cli/mutation-success.js +11 -1
  63. package/dist/cli/mutation-text-format.js +1 -3
  64. package/dist/cli/output.d.ts +1 -1
  65. package/dist/cli/output.js +254 -231
  66. package/dist/cli/parse.d.ts +1 -1
  67. package/dist/cli/parse.js +93 -122
  68. package/dist/cli/preview-json.d.ts +17 -120
  69. package/dist/cli/preview-json.js +14 -97
  70. package/dist/cli/prompt.js +8 -13
  71. package/dist/cli/read-json.d.ts +15 -37
  72. package/dist/cli/read-json.js +44 -140
  73. package/dist/cli/runner.js +10 -13
  74. package/dist/cli/sync-progress.d.ts +6 -0
  75. package/dist/cli/sync-progress.js +91 -0
  76. package/dist/cli/types.d.ts +9 -17
  77. package/dist/cli/types.js +0 -2
  78. package/dist/cli/wallet-format.d.ts +1 -0
  79. package/dist/cli/wallet-format.js +208 -144
  80. package/dist/cli/workflow-hints.d.ts +3 -3
  81. package/dist/cli/workflow-hints.js +11 -8
  82. package/dist/client/default-client.d.ts +3 -1
  83. package/dist/client/default-client.js +45 -2
  84. package/dist/client/factory.js +1 -1
  85. package/dist/client/initialization.js +23 -0
  86. package/dist/client/persistence.js +5 -5
  87. package/dist/client/store-adapter.js +1 -0
  88. package/dist/sqlite/checkpoints.d.ts +1 -0
  89. package/dist/sqlite/checkpoints.js +7 -0
  90. package/dist/sqlite/store.js +14 -1
  91. package/dist/types.d.ts +1 -0
  92. package/dist/wallet/coin-control.d.ts +41 -12
  93. package/dist/wallet/coin-control.js +100 -428
  94. package/dist/wallet/descriptor-normalization.d.ts +1 -3
  95. package/dist/wallet/descriptor-normalization.js +0 -16
  96. package/dist/wallet/lifecycle.d.ts +7 -99
  97. package/dist/wallet/lifecycle.js +513 -968
  98. package/dist/wallet/managed-core-wallet.d.ts +13 -0
  99. package/dist/wallet/managed-core-wallet.js +20 -0
  100. package/dist/wallet/mining/constants.d.ts +5 -12
  101. package/dist/wallet/mining/constants.js +5 -12
  102. package/dist/wallet/mining/control.d.ts +1 -13
  103. package/dist/wallet/mining/control.js +45 -349
  104. package/dist/wallet/mining/index.d.ts +4 -5
  105. package/dist/wallet/mining/index.js +2 -3
  106. package/dist/wallet/mining/runner.d.ts +123 -13
  107. package/dist/wallet/mining/runner.js +899 -511
  108. package/dist/wallet/mining/runtime-artifacts.js +23 -3
  109. package/dist/wallet/mining/sentence-protocol.d.ts +44 -0
  110. package/dist/wallet/mining/sentence-protocol.js +123 -0
  111. package/dist/wallet/mining/sentences.d.ts +4 -8
  112. package/dist/wallet/mining/sentences.js +3 -52
  113. package/dist/wallet/mining/state.d.ts +11 -6
  114. package/dist/wallet/mining/state.js +7 -6
  115. package/dist/wallet/mining/types.d.ts +2 -30
  116. package/dist/wallet/mining/visualizer.d.ts +31 -3
  117. package/dist/wallet/mining/visualizer.js +135 -13
  118. package/dist/wallet/read/context.d.ts +0 -2
  119. package/dist/wallet/read/context.js +119 -140
  120. package/dist/wallet/read/filter.js +2 -11
  121. package/dist/wallet/read/index.d.ts +1 -1
  122. package/dist/wallet/read/project.js +24 -77
  123. package/dist/wallet/read/types.d.ts +10 -25
  124. package/dist/wallet/reset.d.ts +0 -1
  125. package/dist/wallet/reset.js +60 -138
  126. package/dist/wallet/root-resolution.d.ts +1 -5
  127. package/dist/wallet/root-resolution.js +0 -18
  128. package/dist/wallet/runtime.d.ts +0 -6
  129. package/dist/wallet/runtime.js +0 -8
  130. package/dist/wallet/state/client-password-agent.js +208 -0
  131. package/dist/wallet/state/client-password.d.ts +65 -0
  132. package/dist/wallet/state/client-password.js +952 -0
  133. package/dist/wallet/state/crypto.d.ts +1 -20
  134. package/dist/wallet/state/crypto.js +0 -63
  135. package/dist/wallet/state/provider.d.ts +23 -11
  136. package/dist/wallet/state/provider.js +248 -290
  137. package/dist/wallet/state/storage.d.ts +2 -2
  138. package/dist/wallet/state/storage.js +48 -16
  139. package/dist/wallet/tx/anchor.d.ts +3 -28
  140. package/dist/wallet/tx/anchor.js +349 -1250
  141. package/dist/wallet/tx/bitcoin-transfer.d.ts +35 -0
  142. package/dist/wallet/tx/bitcoin-transfer.js +200 -0
  143. package/dist/wallet/tx/cog.d.ts +5 -1
  144. package/dist/wallet/tx/cog.js +149 -185
  145. package/dist/wallet/tx/common.d.ts +61 -8
  146. package/dist/wallet/tx/common.js +266 -146
  147. package/dist/wallet/tx/domain-admin.d.ts +3 -1
  148. package/dist/wallet/tx/domain-admin.js +61 -99
  149. package/dist/wallet/tx/domain-market.d.ts +5 -1
  150. package/dist/wallet/tx/domain-market.js +221 -228
  151. package/dist/wallet/tx/field.d.ts +4 -10
  152. package/dist/wallet/tx/field.js +83 -924
  153. package/dist/wallet/tx/identity-selector.d.ts +9 -3
  154. package/dist/wallet/tx/identity-selector.js +17 -35
  155. package/dist/wallet/tx/index.d.ts +3 -1
  156. package/dist/wallet/tx/index.js +2 -1
  157. package/dist/wallet/tx/register.d.ts +3 -1
  158. package/dist/wallet/tx/register.js +62 -220
  159. package/dist/wallet/tx/reputation.d.ts +3 -1
  160. package/dist/wallet/tx/reputation.js +58 -95
  161. package/dist/wallet/types.d.ts +8 -122
  162. package/package.json +5 -5
  163. package/dist/wallet/archive.d.ts +0 -4
  164. package/dist/wallet/archive.js +0 -41
  165. package/dist/wallet/mining/hook-protocol.d.ts +0 -47
  166. package/dist/wallet/mining/hook-protocol.js +0 -161
  167. package/dist/wallet/mining/hook-runner.js +0 -52
  168. package/dist/wallet/mining/hooks.d.ts +0 -38
  169. package/dist/wallet/mining/hooks.js +0 -520
  170. package/dist/wallet/state/explicit-lock.d.ts +0 -4
  171. package/dist/wallet/state/explicit-lock.js +0 -19
  172. package/dist/wallet/state/session.d.ts +0 -12
  173. package/dist/wallet/state/session.js +0 -23
  174. /package/dist/wallet/{mining/hook-runner.d.ts → state/client-password-agent.d.ts} +0 -0
@@ -10,9 +10,8 @@ import { createDefaultWalletSecretProvider, } from "../state/provider.js";
10
10
  import { FIELD_FORMAT_BYTES, serializeDataUpdate, serializeFieldReg, } from "../cogop/index.js";
11
11
  import { validateFieldName } from "../cogop/validate-name.js";
12
12
  import { findDomainField, openWalletReadContext, } from "../read/index.js";
13
- import { assertFixedInputPrefixMatches, assertFundingInputsAfterFixedPrefix, assertWalletMutationContextReady, buildWalletMutationTransactionWithReserveFallback, findSpendableFundingInputsFromTransaction, getDecodedInputScriptPubKeyHex, isAlreadyAcceptedError, isBroadcastUnknownError, outpointKey, pauseMiningForWalletMutation, saveWalletStatePreservingUnlock, unlockTemporaryBuilderLocks, updateMutationRecord, } from "./common.js";
13
+ import { assertWalletMutationContextReady, buildWalletMutationTransactionWithReserveFallback, createBuiltWalletMutationFeeSummary, createWalletMutationFeeMetadata, isAlreadyAcceptedError, isBroadcastUnknownError, mergeFixedWalletInputs, outpointKey, pauseMiningForWalletMutation, resolvePendingMutationReuseDecision, resolveWalletMutationFeeSelection, saveWalletStatePreservingUnlock, unlockTemporaryBuilderLocks, updateMutationRecord, } from "./common.js";
14
14
  import { confirmTypedAcknowledgement as confirmSharedTypedAcknowledgement, confirmYesNo as confirmSharedYesNo, } from "./confirm.js";
15
- import { getCanonicalIdentitySelector } from "./identity-selector.js";
16
15
  import { findPendingMutationByIntent, upsertPendingMutation } from "./journal.js";
17
16
  function createResolvedFieldSenderSummary(sender, selector) {
18
17
  return {
@@ -30,18 +29,6 @@ function createResolvedFieldValueSummary(format, value) {
30
29
  }
31
30
  function createResolvedFieldSummary(options) {
32
31
  if (options.kind === "field-create") {
33
- if (options.family) {
34
- return {
35
- sender: createResolvedFieldSenderSummary(options.sender, options.senderSelector),
36
- path: "field-reg-plus-data-update-family",
37
- value: options.value,
38
- effect: {
39
- kind: "create-and-initialize-field",
40
- tx1BurnCogtoshi: "100",
41
- tx2AdditionalBurnCogtoshi: "1",
42
- },
43
- };
44
- }
45
32
  return {
46
33
  sender: createResolvedFieldSenderSummary(options.sender, options.senderSelector),
47
34
  path: "standalone-field-reg",
@@ -83,8 +70,6 @@ function describeFieldEffect(effect) {
83
70
  switch (effect.kind) {
84
71
  case "create-empty-field":
85
72
  return `burn ${effect.burnCogtoshi} cogtoshi to create an empty field`;
86
- case "create-and-initialize-field":
87
- return `burn ${effect.tx1BurnCogtoshi} cogtoshi in Tx1 and ${effect.tx2AdditionalBurnCogtoshi} additional cogtoshi in Tx2`;
88
73
  case "write-field-value":
89
74
  return `burn ${effect.burnCogtoshi} cogtoshi to write the field value`;
90
75
  case "clear-field-value":
@@ -150,61 +135,12 @@ function isActiveMutationStatus(status) {
150
135
  || status === "live"
151
136
  || status === "repair-required";
152
137
  }
153
- function isActiveFamilyStatus(status) {
154
- return status === "draft"
155
- || status === "broadcasting"
156
- || status === "broadcast-unknown"
157
- || status === "live"
158
- || status === "repair-required";
159
- }
160
- function createFamilyTransactionRecord() {
161
- return {
162
- status: "draft",
163
- attemptedTxid: null,
164
- attemptedWtxid: null,
165
- temporaryBuilderLockedOutpoints: [],
166
- rawHex: null,
167
- };
168
- }
169
- function upsertProactiveFamily(state, family) {
170
- const families = state.proactiveFamilies.slice();
171
- const existingIndex = families.findIndex((entry) => entry.familyId === family.familyId);
172
- if (existingIndex >= 0) {
173
- families[existingIndex] = family;
174
- }
175
- else {
176
- families.push(family);
177
- }
178
- return {
179
- ...state,
180
- proactiveFamilies: families,
181
- };
182
- }
183
- function findFieldFamilyByIntent(state, intentFingerprintHex) {
184
- return state.proactiveFamilies.find((family) => family.type === "field" && family.intentFingerprintHex === intentFingerprintHex) ?? null;
185
- }
186
- function findActiveFieldFamilyByDomain(state, domainName) {
187
- return state.proactiveFamilies.find((family) => family.type === "field"
188
- && family.domainName === domainName
189
- && isActiveFamilyStatus(family.status)) ?? null;
190
- }
191
138
  function findActiveFieldCreateMutationByDomain(state, domainName, intentFingerprintHex) {
192
139
  return (state.pendingMutations ?? []).find((mutation) => mutation.kind === "field-create"
193
140
  && mutation.domainName === domainName
194
141
  && mutation.intentFingerprintHex !== intentFingerprintHex
195
142
  && isActiveMutationStatus(mutation.status)) ?? null;
196
143
  }
197
- function resolveAnchorOutpointForSender(state, sender, errorPrefix) {
198
- const anchoredDomain = state.domains.find((domain) => domain.currentOwnerLocalIndex === sender.index
199
- && domain.canonicalChainStatus === "anchored") ?? null;
200
- if (anchoredDomain?.currentCanonicalAnchorOutpoint === null || anchoredDomain === null) {
201
- throw new Error(`${errorPrefix}_anchor_outpoint_unavailable`);
202
- }
203
- return {
204
- txid: anchoredDomain.currentCanonicalAnchorOutpoint.txid,
205
- vout: anchoredDomain.currentCanonicalAnchorOutpoint.vout,
206
- };
207
- }
208
144
  function resolveAnchoredFieldOperation(context, domainName, errorPrefix) {
209
145
  assertWalletMutationContextReady(context, errorPrefix);
210
146
  const chainDomain = lookupDomain(context.snapshot.state, domainName);
@@ -215,24 +151,19 @@ function resolveAnchoredFieldOperation(context, domainName, errorPrefix) {
215
151
  throw new Error(`${errorPrefix}_domain_not_anchored`);
216
152
  }
217
153
  const ownerHex = Buffer.from(chainDomain.ownerScriptPubKey).toString("hex");
218
- const ownerIdentity = context.model.identities.find((identity) => identity.scriptPubKeyHex === ownerHex) ?? null;
219
- if (ownerIdentity === null || ownerIdentity.address === null) {
154
+ const state = context.localState.state;
155
+ if (ownerHex !== state.funding.scriptPubKeyHex || state.funding.address.trim() === "") {
220
156
  throw new Error(`${errorPrefix}_owner_not_locally_controlled`);
221
157
  }
222
- if (ownerIdentity.readOnly) {
223
- throw new Error(`${errorPrefix}_owner_read_only`);
224
- }
225
158
  return {
226
159
  readContext: context,
227
- state: context.localState.state,
228
- unlockUntilUnixMs: context.localState.unlockUntilUnixMs,
160
+ state,
229
161
  sender: {
230
- localIndex: ownerIdentity.index,
231
- scriptPubKeyHex: ownerIdentity.scriptPubKeyHex,
232
- address: ownerIdentity.address,
162
+ localIndex: 0,
163
+ scriptPubKeyHex: state.funding.scriptPubKeyHex,
164
+ address: state.funding.address,
233
165
  },
234
- senderSelector: getCanonicalIdentitySelector(ownerIdentity),
235
- anchorOutpoint: resolveAnchorOutpointForSender(context.localState.state, ownerIdentity, errorPrefix),
166
+ senderSelector: state.funding.address,
236
167
  chainDomain,
237
168
  };
238
169
  }
@@ -241,98 +172,34 @@ function buildAnchoredFieldPlan(options) {
241
172
  && entry.confirmations >= 1
242
173
  && entry.spendable !== false
243
174
  && entry.safe !== false);
244
- const anchorUtxo = options.allUtxos.find((entry) => entry.txid === options.anchorOutpoint.txid
245
- && entry.vout === options.anchorOutpoint.vout
246
- && entry.scriptPubKey === options.sender.scriptPubKeyHex
247
- && entry.confirmations >= 1
248
- && entry.spendable !== false
249
- && entry.safe !== false);
250
- if (anchorUtxo === undefined) {
251
- throw new Error(`${options.errorPrefix}_anchor_utxo_missing`);
252
- }
253
175
  return {
254
176
  sender: options.sender,
255
177
  changeAddress: options.state.funding.address,
256
- fixedInputs: [
257
- { txid: anchorUtxo.txid, vout: anchorUtxo.vout },
258
- ],
259
- outputs: [
260
- { data: Buffer.from(options.opReturnData).toString("hex") },
261
- { [options.sender.address]: satsToBtcNumber(BigInt(options.state.anchorValueSats)) },
262
- ],
263
- changePosition: 2,
178
+ fixedInputs: [],
179
+ outputs: [{ data: Buffer.from(options.opReturnData).toString("hex") }],
180
+ changePosition: 1,
264
181
  expectedOpReturnScriptHex: encodeOpReturnScript(options.opReturnData),
265
- expectedAnchorScriptHex: options.sender.scriptPubKeyHex,
266
- expectedAnchorValueSats: BigInt(options.state.anchorValueSats),
267
182
  allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
268
183
  eligibleFundingOutpointKeys: new Set(fundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout }))),
269
184
  errorPrefix: options.errorPrefix,
270
185
  };
271
186
  }
272
- function buildFieldFamilyTx2Plan(options) {
273
- const fundingUtxos = options.allUtxos.filter((entry) => entry.scriptPubKey === options.state.funding.scriptPubKeyHex
274
- && entry.confirmations >= 1
275
- && entry.spendable !== false
276
- && entry.safe !== false);
277
- const tx1FundingChangeInputs = findSpendableFundingInputsFromTransaction({
278
- allUtxos: options.allUtxos,
279
- txid: options.tx1Txid,
280
- fundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
281
- minConf: 0,
282
- });
283
- return {
284
- sender: options.sender,
285
- changeAddress: options.state.funding.address,
286
- fixedInputs: [
287
- { txid: options.tx1Txid, vout: 1 },
288
- ...tx1FundingChangeInputs,
289
- ],
290
- outputs: [
291
- { data: Buffer.from(options.opReturnData).toString("hex") },
292
- { [options.sender.address]: satsToBtcNumber(BigInt(options.state.anchorValueSats)) },
293
- ],
294
- changePosition: 2,
295
- expectedOpReturnScriptHex: encodeOpReturnScript(options.opReturnData),
296
- expectedAnchorScriptHex: options.sender.scriptPubKeyHex,
297
- expectedAnchorValueSats: BigInt(options.state.anchorValueSats),
298
- allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
299
- eligibleFundingOutpointKeys: new Set(fundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout }))),
300
- errorPrefix: "wallet_field_create_tx2",
301
- };
302
- }
303
187
  function validateFieldDraft(decoded, funded, plan) {
304
188
  const inputs = decoded.tx.vin;
305
189
  const outputs = decoded.tx.vout;
306
190
  if (inputs.length === 0) {
307
191
  throw new Error(`${plan.errorPrefix}_missing_sender_input`);
308
192
  }
309
- assertFixedInputPrefixMatches(inputs, plan.fixedInputs, `${plan.errorPrefix}_sender_input_mismatch`);
310
- if (getDecodedInputScriptPubKeyHex(decoded, 0) !== plan.sender.scriptPubKeyHex) {
311
- throw new Error(`${plan.errorPrefix}_sender_input_mismatch`);
312
- }
313
- assertFundingInputsAfterFixedPrefix({
314
- decoded,
315
- fixedInputs: plan.fixedInputs,
316
- allowedFundingScriptPubKeyHex: plan.allowedFundingScriptPubKeyHex,
317
- eligibleFundingOutpointKeys: plan.eligibleFundingOutpointKeys,
318
- errorCode: `${plan.errorPrefix}_unexpected_funding_input`,
319
- });
320
193
  if (outputs[0]?.scriptPubKey?.hex !== plan.expectedOpReturnScriptHex) {
321
194
  throw new Error(`${plan.errorPrefix}_opreturn_mismatch`);
322
195
  }
323
- if (outputs[1]?.scriptPubKey?.hex !== plan.expectedAnchorScriptHex) {
324
- throw new Error(`${plan.errorPrefix}_anchor_output_mismatch`);
325
- }
326
- if (valueToSats(outputs[1]?.value ?? 0) !== plan.expectedAnchorValueSats) {
327
- throw new Error(`${plan.errorPrefix}_anchor_value_mismatch`);
328
- }
329
196
  if (funded.changepos === -1) {
330
- if (outputs.length !== 2) {
197
+ if (outputs.length !== 1) {
331
198
  throw new Error(`${plan.errorPrefix}_unexpected_output_count`);
332
199
  }
333
200
  return;
334
201
  }
335
- if (funded.changepos !== plan.changePosition || outputs.length !== 3) {
202
+ if (funded.changepos !== plan.changePosition || outputs.length !== 2) {
336
203
  throw new Error(`${plan.errorPrefix}_change_position_mismatch`);
337
204
  }
338
205
  if (outputs[funded.changepos]?.scriptPubKey?.hex !== plan.allowedFundingScriptPubKeyHex) {
@@ -348,8 +215,8 @@ async function buildFieldTransaction(options) {
348
215
  validateFundedDraft: validateFieldDraft,
349
216
  finalizeErrorCode: `${options.plan.errorPrefix}_finalize_failed`,
350
217
  mempoolRejectPrefix: `${options.plan.errorPrefix}_mempool_rejected`,
218
+ feeRate: options.feeRateSatVb,
351
219
  availableFundingMinConf: options.availableFundingMinConf,
352
- reserveCandidates: options.state.proactiveReserveOutpoints,
353
220
  });
354
221
  }
355
222
  async function saveUpdatedState(options) {
@@ -361,7 +228,6 @@ async function saveUpdatedState(options) {
361
228
  await saveWalletStatePreservingUnlock({
362
229
  state: nextState,
363
230
  provider: options.provider,
364
- unlockUntilUnixMs: options.unlockUntilUnixMs,
365
231
  nowUnixMs: options.nowUnixMs,
366
232
  paths: options.paths,
367
233
  });
@@ -384,6 +250,7 @@ function createStandaloneFieldMutation(options) {
384
250
  lastUpdatedAtUnixMs: options.nowUnixMs,
385
251
  attemptedTxid: null,
386
252
  attemptedWtxid: null,
253
+ ...createWalletMutationFeeMetadata(options.feeSelection),
387
254
  temporaryBuilderLockedOutpoints: [],
388
255
  };
389
256
  }
@@ -405,61 +272,10 @@ function createStandaloneFieldMutation(options) {
405
272
  lastUpdatedAtUnixMs: options.nowUnixMs,
406
273
  attemptedTxid: null,
407
274
  attemptedWtxid: null,
275
+ ...createWalletMutationFeeMetadata(options.feeSelection),
408
276
  temporaryBuilderLockedOutpoints: [],
409
277
  };
410
278
  }
411
- function createFieldFamilyRecord(options) {
412
- if (options.existing !== null && options.existing !== undefined) {
413
- return {
414
- ...options.existing,
415
- type: "field",
416
- status: "draft",
417
- domainName: options.domainName,
418
- domainId: options.domainId,
419
- sourceSenderLocalIndex: options.sender.localIndex,
420
- sourceSenderScriptPubKeyHex: options.sender.scriptPubKeyHex,
421
- fieldName: options.fieldName,
422
- expectedFieldId: options.expectedFieldId,
423
- fieldPermanent: options.permanence,
424
- fieldFormat: options.format,
425
- fieldValueHex: options.valueHex,
426
- currentStep: "tx1",
427
- lastUpdatedAtUnixMs: options.nowUnixMs,
428
- tx1: createFamilyTransactionRecord(),
429
- tx2: createFamilyTransactionRecord(),
430
- };
431
- }
432
- return {
433
- familyId: randomBytes(12).toString("hex"),
434
- type: "field",
435
- status: "draft",
436
- intentFingerprintHex: options.intentFingerprintHex,
437
- createdAtUnixMs: options.nowUnixMs,
438
- lastUpdatedAtUnixMs: options.nowUnixMs,
439
- domainName: options.domainName,
440
- domainId: options.domainId,
441
- sourceSenderLocalIndex: options.sender.localIndex,
442
- sourceSenderScriptPubKeyHex: options.sender.scriptPubKeyHex,
443
- fieldName: options.fieldName,
444
- expectedFieldId: options.expectedFieldId,
445
- fieldPermanent: options.permanence,
446
- fieldFormat: options.format,
447
- fieldValueHex: options.valueHex,
448
- currentStep: "tx1",
449
- tx1: createFamilyTransactionRecord(),
450
- tx2: createFamilyTransactionRecord(),
451
- };
452
- }
453
- function updateFieldFamilyState(options) {
454
- return upsertProactiveFamily(options.state, {
455
- ...options.family,
456
- status: options.status,
457
- currentStep: options.currentStep,
458
- lastUpdatedAtUnixMs: options.nowUnixMs,
459
- tx1: options.tx1 ?? options.family.tx1 ?? createFamilyTransactionRecord(),
460
- tx2: options.tx2 ?? options.family.tx2 ?? createFamilyTransactionRecord(),
461
- });
462
- }
463
279
  function getObservedFieldState(context, domainName, fieldName) {
464
280
  if (context.snapshot === null) {
465
281
  return null;
@@ -542,7 +358,6 @@ async function reconcilePendingFieldMutation(options) {
542
358
  nextState = await saveUpdatedState({
543
359
  state: nextState,
544
360
  provider: options.provider,
545
- unlockUntilUnixMs: options.unlockUntilUnixMs,
546
361
  nowUnixMs: options.nowUnixMs,
547
362
  paths: options.paths,
548
363
  });
@@ -557,7 +372,6 @@ async function reconcilePendingFieldMutation(options) {
557
372
  nextState = await saveUpdatedState({
558
373
  state: nextState,
559
374
  provider: options.provider,
560
- unlockUntilUnixMs: options.unlockUntilUnixMs,
561
375
  nowUnixMs: options.nowUnixMs,
562
376
  paths: options.paths,
563
377
  });
@@ -575,7 +389,6 @@ async function reconcilePendingFieldMutation(options) {
575
389
  nextState = await saveUpdatedState({
576
390
  state: nextState,
577
391
  provider: options.provider,
578
- unlockUntilUnixMs: options.unlockUntilUnixMs,
579
392
  nowUnixMs: options.nowUnixMs,
580
393
  paths: options.paths,
581
394
  });
@@ -592,7 +405,6 @@ async function reconcilePendingFieldMutation(options) {
592
405
  nextState = await saveUpdatedState({
593
406
  state: nextState,
594
407
  provider: options.provider,
595
- unlockUntilUnixMs: options.unlockUntilUnixMs,
596
408
  nowUnixMs: options.nowUnixMs,
597
409
  paths: options.paths,
598
410
  });
@@ -604,180 +416,6 @@ async function reconcilePendingFieldMutation(options) {
604
416
  resolution: "continue",
605
417
  };
606
418
  }
607
- async function reconcileFieldFamily(options) {
608
- const domainName = options.family.domainName ?? "";
609
- const fieldName = options.family.fieldName ?? "";
610
- const observed = getObservedFieldState(options.context, domainName, fieldName);
611
- if (observed !== null) {
612
- if (observed.fieldId === options.family.expectedFieldId
613
- && observed.permanent === options.family.fieldPermanent
614
- && observed.hasValue
615
- && observed.format === options.family.fieldFormat
616
- && observed.rawValueHex === options.family.fieldValueHex) {
617
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
618
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
619
- let nextState = updateFieldFamilyState({
620
- state: options.state,
621
- family: options.family,
622
- status: "confirmed",
623
- currentStep: "tx2",
624
- nowUnixMs: options.nowUnixMs,
625
- tx1: options.family.tx1 == null
626
- ? undefined
627
- : { ...options.family.tx1, status: "confirmed", temporaryBuilderLockedOutpoints: [] },
628
- tx2: options.family.tx2 == null
629
- ? undefined
630
- : { ...options.family.tx2, status: "confirmed", temporaryBuilderLockedOutpoints: [] },
631
- });
632
- nextState = await saveUpdatedState({
633
- state: nextState,
634
- provider: options.provider,
635
- unlockUntilUnixMs: options.unlockUntilUnixMs,
636
- nowUnixMs: options.nowUnixMs,
637
- paths: options.paths,
638
- });
639
- return {
640
- state: nextState,
641
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
642
- ...options.family,
643
- status: "confirmed",
644
- },
645
- resolution: "confirmed",
646
- };
647
- }
648
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
649
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
650
- let nextState = updateFieldFamilyState({
651
- state: options.state,
652
- family: options.family,
653
- status: "repair-required",
654
- currentStep: "tx2",
655
- nowUnixMs: options.nowUnixMs,
656
- tx1: options.family.tx1 == null
657
- ? undefined
658
- : { ...options.family.tx1, temporaryBuilderLockedOutpoints: [] },
659
- tx2: options.family.tx2 == null
660
- ? undefined
661
- : { ...options.family.tx2, temporaryBuilderLockedOutpoints: [] },
662
- });
663
- nextState = await saveUpdatedState({
664
- state: nextState,
665
- provider: options.provider,
666
- unlockUntilUnixMs: options.unlockUntilUnixMs,
667
- nowUnixMs: options.nowUnixMs,
668
- paths: options.paths,
669
- });
670
- return {
671
- state: nextState,
672
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
673
- ...options.family,
674
- status: "repair-required",
675
- },
676
- resolution: "repair-required",
677
- };
678
- }
679
- const tx2Known = options.family.tx2?.attemptedTxid == null
680
- ? false
681
- : await options.rpc.getRawTransaction(options.family.tx2.attemptedTxid, true).then(() => true).catch(() => false);
682
- if (tx2Known) {
683
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
684
- let nextState = updateFieldFamilyState({
685
- state: options.state,
686
- family: options.family,
687
- status: "live",
688
- currentStep: "tx2",
689
- nowUnixMs: options.nowUnixMs,
690
- tx2: options.family.tx2 == null
691
- ? undefined
692
- : { ...options.family.tx2, status: "live", temporaryBuilderLockedOutpoints: [] },
693
- });
694
- nextState = await saveUpdatedState({
695
- state: nextState,
696
- provider: options.provider,
697
- unlockUntilUnixMs: options.unlockUntilUnixMs,
698
- nowUnixMs: options.nowUnixMs,
699
- paths: options.paths,
700
- });
701
- return {
702
- state: nextState,
703
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
704
- ...options.family,
705
- status: "live",
706
- },
707
- resolution: "live",
708
- };
709
- }
710
- const tx1Known = options.family.tx1?.attemptedTxid == null
711
- ? false
712
- : await options.rpc.getRawTransaction(options.family.tx1.attemptedTxid, true).then(() => true).catch(() => false);
713
- if (tx1Known) {
714
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
715
- let nextState = updateFieldFamilyState({
716
- state: options.state,
717
- family: options.family,
718
- status: "live",
719
- currentStep: "tx1",
720
- nowUnixMs: options.nowUnixMs,
721
- tx1: options.family.tx1 == null
722
- ? undefined
723
- : { ...options.family.tx1, status: "live", temporaryBuilderLockedOutpoints: [] },
724
- });
725
- nextState = await saveUpdatedState({
726
- state: nextState,
727
- provider: options.provider,
728
- unlockUntilUnixMs: options.unlockUntilUnixMs,
729
- nowUnixMs: options.nowUnixMs,
730
- paths: options.paths,
731
- });
732
- return {
733
- state: nextState,
734
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
735
- ...options.family,
736
- status: "live",
737
- },
738
- resolution: "ready-for-tx2",
739
- };
740
- }
741
- if (options.family.status === "broadcast-unknown"
742
- || options.family.status === "draft"
743
- || options.family.status === "broadcasting") {
744
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
745
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
746
- let nextState = updateFieldFamilyState({
747
- state: options.state,
748
- family: options.family,
749
- status: "canceled",
750
- currentStep: options.family.currentStep,
751
- nowUnixMs: options.nowUnixMs,
752
- tx1: options.family.tx1 == null
753
- ? undefined
754
- : { ...options.family.tx1, status: options.family.tx1.attemptedTxid === null ? "canceled" : options.family.tx1.status, temporaryBuilderLockedOutpoints: [] },
755
- tx2: options.family.tx2 == null
756
- ? undefined
757
- : { ...options.family.tx2, status: options.family.tx2.attemptedTxid === null ? "canceled" : options.family.tx2.status, temporaryBuilderLockedOutpoints: [] },
758
- });
759
- nextState = await saveUpdatedState({
760
- state: nextState,
761
- provider: options.provider,
762
- unlockUntilUnixMs: options.unlockUntilUnixMs,
763
- nowUnixMs: options.nowUnixMs,
764
- paths: options.paths,
765
- });
766
- return {
767
- state: nextState,
768
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
769
- ...options.family,
770
- status: "canceled",
771
- },
772
- resolution: "not-seen",
773
- };
774
- }
775
- return {
776
- state: options.state,
777
- family: options.family,
778
- resolution: "continue",
779
- };
780
- }
781
419
  async function confirmYesNo(prompter, message, errorCode, options) {
782
420
  await confirmSharedYesNo(prompter, message, {
783
421
  assumeYes: options.assumeYes,
@@ -799,39 +437,10 @@ async function confirmFieldCreate(prompter, options) {
799
437
  const fieldRef = `${options.domainName}:${options.fieldName}`;
800
438
  prompter.writeLine(`Creating field "${fieldRef}" as ${options.permanent ? "permanent" : "mutable"}.`);
801
439
  prompter.writeLine(`Resolved sender: ${options.sender.selector} (${options.sender.address})`);
802
- if (options.value === null) {
803
- prompter.writeLine("Path: standalone-field-reg");
804
- prompter.writeLine(`Effect: ${describeFieldEffect({ kind: "create-empty-field", burnCogtoshi: "100" })}.`);
805
- prompter.writeLine("This publishes a standalone FIELD_REG and burns 0.00000100 COG.");
806
- await confirmYesNo(prompter, "The field will be created empty and the burn is not reversible.", "wallet_field_create_confirmation_rejected", {
807
- assumeYes: options.assumeYes,
808
- requiresTtyErrorCode: "wallet_field_create_requires_tty",
809
- });
810
- return;
811
- }
812
- prompter.writeLine("Path: field-reg-plus-data-update-family");
813
- prompter.writeLine(`Effect: ${describeFieldEffect({
814
- kind: "create-and-initialize-field",
815
- tx1BurnCogtoshi: "100",
816
- tx2AdditionalBurnCogtoshi: "1",
817
- })}.`);
818
- prompter.writeLine(`Value: format ${options.value.format}, ${options.value.value.length} bytes`);
819
- prompter.writeLine(`Initial value format: ${options.value.formatLabel}`);
820
- prompter.writeLine(`Initial value bytes: ${options.value.value.length}`);
821
- prompter.writeLine("Warning: non-clear field values are public in the mempool and on-chain.");
822
- prompter.writeLine("This uses the same-block FIELD_REG -> DATA_UPDATE family.");
823
- prompter.writeLine("Tx1 burns 0.00000100 COG. Tx2 may burn an additional 0.00000001 COG.");
824
- prompter.writeLine("Tx1 may confirm even if Tx2 later fails, is canceled, or needs repair.");
825
- if (options.permanent) {
826
- prompter.writeLine("This is the first non-clear value write to a permanent field.");
827
- await confirmTyped(prompter, fieldRef, `Type ${fieldRef} to continue: `, "wallet_field_create_confirmation_rejected", {
828
- assumeYes: options.assumeYes,
829
- requiresTtyErrorCode: "wallet_field_create_requires_tty",
830
- typedAckRequiredErrorCode: "wallet_field_create_typed_ack_required",
831
- });
832
- return;
833
- }
834
- await confirmYesNo(prompter, "This creates and initializes the field in the same block family.", "wallet_field_create_confirmation_rejected", {
440
+ prompter.writeLine("Path: standalone-field-reg");
441
+ prompter.writeLine(`Effect: ${describeFieldEffect({ kind: "create-empty-field", burnCogtoshi: "100" })}.`);
442
+ prompter.writeLine("This publishes a standalone FIELD_REG and burns 0.00000100 COG.");
443
+ await confirmYesNo(prompter, "The field will be created empty and the burn is not reversible.", "wallet_field_create_confirmation_rejected", {
835
444
  assumeYes: options.assumeYes,
836
445
  requiresTtyErrorCode: "wallet_field_create_requires_tty",
837
446
  });
@@ -981,7 +590,6 @@ async function sendStandaloneMutation(options) {
981
590
  nextState = await saveUpdatedState({
982
591
  state: nextState,
983
592
  provider: options.provider,
984
- unlockUntilUnixMs: options.unlockUntilUnixMs,
985
593
  nowUnixMs: options.nowUnixMs,
986
594
  paths: options.paths,
987
595
  });
@@ -1004,7 +612,6 @@ async function sendStandaloneMutation(options) {
1004
612
  nextState = await saveUpdatedState({
1005
613
  state: nextState,
1006
614
  provider: options.provider,
1007
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1008
615
  nowUnixMs: options.nowUnixMs,
1009
616
  paths: options.paths,
1010
617
  });
@@ -1020,7 +627,6 @@ async function sendStandaloneMutation(options) {
1020
627
  nextState = await saveUpdatedState({
1021
628
  state: nextState,
1022
629
  provider: options.provider,
1023
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1024
630
  nowUnixMs: options.nowUnixMs,
1025
631
  paths: options.paths,
1026
632
  });
@@ -1037,228 +643,11 @@ async function sendStandaloneMutation(options) {
1037
643
  nextState = await saveUpdatedState({
1038
644
  state: nextState,
1039
645
  provider: options.provider,
1040
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1041
646
  nowUnixMs: options.nowUnixMs,
1042
647
  paths: options.paths,
1043
648
  });
1044
649
  return { state: nextState, mutation: live };
1045
650
  }
1046
- async function sendFamilyTx1(options) {
1047
- let nextState = updateFieldFamilyState({
1048
- state: options.state,
1049
- family: options.family,
1050
- status: "broadcasting",
1051
- currentStep: "tx1",
1052
- nowUnixMs: options.nowUnixMs,
1053
- tx1: {
1054
- status: "broadcasting",
1055
- attemptedTxid: options.built.txid,
1056
- attemptedWtxid: options.built.wtxid,
1057
- temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
1058
- rawHex: options.built.rawHex,
1059
- },
1060
- });
1061
- nextState = await saveUpdatedState({
1062
- state: nextState,
1063
- provider: options.provider,
1064
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1065
- nowUnixMs: options.nowUnixMs,
1066
- paths: options.paths,
1067
- });
1068
- if (options.snapshotHeight !== null && options.snapshotHeight !== (await options.rpc.getBlockchainInfo()).blocks) {
1069
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
1070
- throw new Error("wallet_field_create_tx1_tip_mismatch");
1071
- }
1072
- try {
1073
- await options.rpc.sendRawTransaction(options.built.rawHex);
1074
- }
1075
- catch (error) {
1076
- if (!isAlreadyAcceptedError(error)) {
1077
- if (isBroadcastUnknownError(error)) {
1078
- nextState = updateFieldFamilyState({
1079
- state: nextState,
1080
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1081
- status: "broadcast-unknown",
1082
- currentStep: "tx1",
1083
- nowUnixMs: options.nowUnixMs,
1084
- tx1: {
1085
- status: "broadcast-unknown",
1086
- attemptedTxid: options.built.txid,
1087
- attemptedWtxid: options.built.wtxid,
1088
- temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
1089
- rawHex: options.built.rawHex,
1090
- },
1091
- });
1092
- nextState = await saveUpdatedState({
1093
- state: nextState,
1094
- provider: options.provider,
1095
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1096
- nowUnixMs: options.nowUnixMs,
1097
- paths: options.paths,
1098
- });
1099
- throw new Error("wallet_field_create_tx1_broadcast_unknown");
1100
- }
1101
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
1102
- nextState = updateFieldFamilyState({
1103
- state: nextState,
1104
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1105
- status: "canceled",
1106
- currentStep: "tx1",
1107
- nowUnixMs: options.nowUnixMs,
1108
- tx1: {
1109
- status: "canceled",
1110
- attemptedTxid: options.built.txid,
1111
- attemptedWtxid: options.built.wtxid,
1112
- temporaryBuilderLockedOutpoints: [],
1113
- rawHex: options.built.rawHex,
1114
- },
1115
- });
1116
- nextState = await saveUpdatedState({
1117
- state: nextState,
1118
- provider: options.provider,
1119
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1120
- nowUnixMs: options.nowUnixMs,
1121
- paths: options.paths,
1122
- });
1123
- throw error;
1124
- }
1125
- }
1126
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
1127
- nextState = updateFieldFamilyState({
1128
- state: nextState,
1129
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1130
- status: "live",
1131
- currentStep: "tx1",
1132
- nowUnixMs: options.nowUnixMs,
1133
- tx1: {
1134
- status: "live",
1135
- attemptedTxid: options.built.txid,
1136
- attemptedWtxid: options.built.wtxid,
1137
- temporaryBuilderLockedOutpoints: [],
1138
- rawHex: options.built.rawHex,
1139
- },
1140
- });
1141
- nextState = await saveUpdatedState({
1142
- state: nextState,
1143
- provider: options.provider,
1144
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1145
- nowUnixMs: options.nowUnixMs,
1146
- paths: options.paths,
1147
- });
1148
- return {
1149
- state: nextState,
1150
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
1151
- ...options.family,
1152
- status: "live",
1153
- },
1154
- };
1155
- }
1156
- async function sendFamilyTx2(options) {
1157
- let nextState = updateFieldFamilyState({
1158
- state: options.state,
1159
- family: options.family,
1160
- status: "broadcasting",
1161
- currentStep: "tx2",
1162
- nowUnixMs: options.nowUnixMs,
1163
- tx2: {
1164
- status: "broadcasting",
1165
- attemptedTxid: options.built.txid,
1166
- attemptedWtxid: options.built.wtxid,
1167
- temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
1168
- rawHex: options.built.rawHex,
1169
- },
1170
- });
1171
- nextState = await saveUpdatedState({
1172
- state: nextState,
1173
- provider: options.provider,
1174
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1175
- nowUnixMs: options.nowUnixMs,
1176
- paths: options.paths,
1177
- });
1178
- try {
1179
- await options.rpc.sendRawTransaction(options.built.rawHex);
1180
- }
1181
- catch (error) {
1182
- if (!isAlreadyAcceptedError(error)) {
1183
- if (isBroadcastUnknownError(error)) {
1184
- nextState = updateFieldFamilyState({
1185
- state: nextState,
1186
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1187
- status: "broadcast-unknown",
1188
- currentStep: "tx2",
1189
- nowUnixMs: options.nowUnixMs,
1190
- tx2: {
1191
- status: "broadcast-unknown",
1192
- attemptedTxid: options.built.txid,
1193
- attemptedWtxid: options.built.wtxid,
1194
- temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
1195
- rawHex: options.built.rawHex,
1196
- },
1197
- });
1198
- nextState = await saveUpdatedState({
1199
- state: nextState,
1200
- provider: options.provider,
1201
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1202
- nowUnixMs: options.nowUnixMs,
1203
- paths: options.paths,
1204
- });
1205
- throw new Error("wallet_field_create_tx2_broadcast_unknown");
1206
- }
1207
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
1208
- nextState = updateFieldFamilyState({
1209
- state: nextState,
1210
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1211
- status: "live",
1212
- currentStep: "tx1",
1213
- nowUnixMs: options.nowUnixMs,
1214
- tx2: {
1215
- status: "canceled",
1216
- attemptedTxid: options.built.txid,
1217
- attemptedWtxid: options.built.wtxid,
1218
- temporaryBuilderLockedOutpoints: [],
1219
- rawHex: options.built.rawHex,
1220
- },
1221
- });
1222
- nextState = await saveUpdatedState({
1223
- state: nextState,
1224
- provider: options.provider,
1225
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1226
- nowUnixMs: options.nowUnixMs,
1227
- paths: options.paths,
1228
- });
1229
- throw error;
1230
- }
1231
- }
1232
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
1233
- nextState = updateFieldFamilyState({
1234
- state: nextState,
1235
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1236
- status: "live",
1237
- currentStep: "tx2",
1238
- nowUnixMs: options.nowUnixMs,
1239
- tx2: {
1240
- status: "live",
1241
- attemptedTxid: options.built.txid,
1242
- attemptedWtxid: options.built.wtxid,
1243
- temporaryBuilderLockedOutpoints: [],
1244
- rawHex: options.built.rawHex,
1245
- },
1246
- });
1247
- nextState = await saveUpdatedState({
1248
- state: nextState,
1249
- provider: options.provider,
1250
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1251
- nowUnixMs: options.nowUnixMs,
1252
- paths: options.paths,
1253
- });
1254
- return {
1255
- state: nextState,
1256
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
1257
- ...options.family,
1258
- status: "live",
1259
- },
1260
- };
1261
- }
1262
651
  async function submitStandaloneFieldMutation(options) {
1263
652
  if (!options.prompter.isInteractive && options.assumeYes !== true) {
1264
653
  throw new Error(`${options.errorPrefix}_requires_tty`);
@@ -1293,82 +682,91 @@ async function submitStandaloneFieldMutation(options) {
1293
682
  normalizedDomainName,
1294
683
  normalizedFieldName,
1295
684
  ]);
685
+ const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
686
+ dataDir: options.dataDir,
687
+ chain: "main",
688
+ startHeight: 0,
689
+ walletRootId: operation.state.walletRootId,
690
+ });
691
+ const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
692
+ const walletName = operation.state.managedCoreWallet.walletName;
693
+ const feeSelection = await resolveWalletMutationFeeSelection({
694
+ rpc,
695
+ feeRateSatVb: options.feeRateSatVb ?? null,
696
+ });
1296
697
  const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
698
+ let workingState = operation.state;
699
+ let replacementFixedInputs = null;
1297
700
  if (existingMutation !== null) {
1298
- const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
1299
- dataDir: options.dataDir,
1300
- chain: "main",
1301
- startHeight: 0,
1302
- walletRootId: operation.state.walletRootId,
1303
- });
1304
- const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
1305
- const walletName = operation.state.managedCoreWallet.walletName;
1306
701
  const reconciled = await reconcilePendingFieldMutation({
1307
702
  state: operation.state,
1308
703
  mutation: existingMutation,
1309
704
  provider,
1310
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1311
705
  nowUnixMs,
1312
706
  paths,
1313
707
  rpc,
1314
708
  walletName,
1315
709
  context: readContext,
1316
710
  });
711
+ workingState = reconciled.state;
1317
712
  if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
1318
- return {
1319
- kind: options.kind,
1320
- domainName: normalizedDomainName,
1321
- fieldName: normalizedFieldName,
1322
- fieldId: reconciled.mutation.fieldId ?? existingObservedField?.fieldId ?? null,
1323
- txid: reconciled.mutation.attemptedTxid ?? "unknown",
1324
- family: false,
1325
- permanent: reconciled.mutation.fieldPermanent ?? existingObservedField?.permanent ?? null,
1326
- format: reconciled.mutation.fieldFormat ?? existingObservedField?.format ?? null,
1327
- status: reconciled.resolution,
1328
- reusedExisting: true,
1329
- resolved: createResolvedFieldSummary({
1330
- sender: operation.sender,
1331
- senderSelector: operation.senderSelector,
713
+ const reuse = await resolvePendingMutationReuseDecision({
714
+ rpc,
715
+ walletName,
716
+ mutation: reconciled.mutation,
717
+ nextFeeSelection: feeSelection,
718
+ });
719
+ if (reuse.reuseExisting) {
720
+ return {
1332
721
  kind: options.kind,
1333
- family: false,
1334
- value: createResolvedFieldValueFromStoredData(options.kind, reconciled.mutation.fieldFormat ?? existingObservedField?.format ?? null, reconciled.mutation.fieldValueHex),
1335
- }),
1336
- };
722
+ domainName: normalizedDomainName,
723
+ fieldName: normalizedFieldName,
724
+ fieldId: reconciled.mutation.fieldId ?? existingObservedField?.fieldId ?? null,
725
+ txid: reconciled.mutation.attemptedTxid ?? "unknown",
726
+ permanent: reconciled.mutation.fieldPermanent ?? existingObservedField?.permanent ?? null,
727
+ format: reconciled.mutation.fieldFormat ?? existingObservedField?.format ?? null,
728
+ status: reconciled.resolution,
729
+ reusedExisting: true,
730
+ resolved: createResolvedFieldSummary({
731
+ sender: operation.sender,
732
+ senderSelector: operation.senderSelector,
733
+ kind: options.kind,
734
+ value: createResolvedFieldValueFromStoredData(options.kind, reconciled.mutation.fieldFormat ?? existingObservedField?.format ?? null, reconciled.mutation.fieldValueHex),
735
+ }),
736
+ fees: reuse.fees,
737
+ };
738
+ }
739
+ replacementFixedInputs = reuse.replacementFixedInputs;
1337
740
  }
1338
741
  if (reconciled.resolution === "repair-required") {
1339
742
  throw new Error(`${options.errorPrefix}_repair_required`);
1340
743
  }
1341
744
  }
1342
745
  await options.confirm(operation);
1343
- const planned = await options.createMutation(operation, existingMutation);
1344
- let nextState = upsertPendingMutation(operation.state, planned.mutation);
746
+ const planned = await options.createMutation(operation, existingMutation, feeSelection);
747
+ let nextState = upsertPendingMutation(workingState, planned.mutation);
1345
748
  nextState = await saveUpdatedState({
1346
749
  state: nextState,
1347
750
  provider,
1348
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1349
751
  nowUnixMs,
1350
752
  paths,
1351
753
  });
1352
- const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
1353
- dataDir: options.dataDir,
1354
- chain: "main",
1355
- startHeight: 0,
1356
- walletRootId: operation.state.walletRootId,
754
+ const fieldPlan = buildAnchoredFieldPlan({
755
+ state: nextState,
756
+ allUtxos: await rpc.listUnspent(walletName, 1),
757
+ sender: operation.sender,
758
+ opReturnData: planned.opReturnData,
759
+ errorPrefix: options.errorPrefix,
1357
760
  });
1358
- const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
1359
- const walletName = operation.state.managedCoreWallet.walletName;
1360
761
  const built = await buildFieldTransaction({
1361
762
  rpc,
1362
763
  walletName,
1363
764
  state: nextState,
1364
- plan: buildAnchoredFieldPlan({
1365
- state: nextState,
1366
- allUtxos: await rpc.listUnspent(walletName, 1),
1367
- sender: operation.sender,
1368
- anchorOutpoint: operation.anchorOutpoint,
1369
- opReturnData: planned.opReturnData,
1370
- errorPrefix: options.errorPrefix,
1371
- }),
765
+ plan: {
766
+ ...fieldPlan,
767
+ fixedInputs: mergeFixedWalletInputs(fieldPlan.fixedInputs, replacementFixedInputs),
768
+ },
769
+ feeRateSatVb: feeSelection.feeRateSatVb,
1372
770
  });
1373
771
  const final = await sendStandaloneMutation({
1374
772
  rpc,
@@ -1378,7 +776,6 @@ async function submitStandaloneFieldMutation(options) {
1378
776
  mutation: nextState.pendingMutations.find((mutation) => mutation.intentFingerprintHex === planned.mutation.intentFingerprintHex),
1379
777
  state: nextState,
1380
778
  provider,
1381
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1382
779
  nowUnixMs,
1383
780
  paths,
1384
781
  errorPrefix: options.errorPrefix,
@@ -1389,7 +786,6 @@ async function submitStandaloneFieldMutation(options) {
1389
786
  fieldName: normalizedFieldName,
1390
787
  fieldId: final.mutation.fieldId ?? existingObservedField?.fieldId ?? null,
1391
788
  txid: final.mutation.attemptedTxid ?? built.txid,
1392
- family: false,
1393
789
  permanent: final.mutation.fieldPermanent ?? existingObservedField?.permanent ?? null,
1394
790
  format: final.mutation.fieldFormat ?? existingObservedField?.format ?? null,
1395
791
  status: "live",
@@ -1398,238 +794,11 @@ async function submitStandaloneFieldMutation(options) {
1398
794
  sender: operation.sender,
1399
795
  senderSelector: operation.senderSelector,
1400
796
  kind: options.kind,
1401
- family: false,
1402
797
  value: createResolvedFieldValueFromStoredData(options.kind, planned.mutation.fieldFormat ?? existingObservedField?.format ?? null, planned.mutation.fieldValueHex),
1403
798
  }),
1404
- };
1405
- }
1406
- finally {
1407
- await readContext.close();
1408
- await miningPreemption.release();
1409
- }
1410
- }
1411
- finally {
1412
- await controlLock.release();
1413
- }
1414
- }
1415
- async function submitFieldCreateFamily(options) {
1416
- if (!options.prompter.isInteractive && options.assumeYes !== true) {
1417
- throw new Error("wallet_field_create_requires_tty");
1418
- }
1419
- const provider = options.provider ?? createDefaultWalletSecretProvider();
1420
- const nowUnixMs = options.nowUnixMs ?? Date.now();
1421
- const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
1422
- const controlLock = await acquireFileLock(paths.walletControlLockPath, {
1423
- purpose: "wallet_field_create",
1424
- walletRootId: null,
1425
- });
1426
- try {
1427
- const miningPreemption = await pauseMiningForWalletMutation({
1428
- paths,
1429
- reason: "wallet_field_create",
1430
- });
1431
- const readContext = await (options.openReadContext ?? openWalletReadContext)({
1432
- dataDir: options.dataDir,
1433
- databasePath: options.databasePath,
1434
- secretProvider: provider,
1435
- walletControlLockHeld: true,
1436
- paths,
1437
- });
1438
- try {
1439
- const normalizedDomainName = normalizeDomainName(options.domainName);
1440
- const normalizedFieldName = normalizeFieldName(options.fieldName);
1441
- const operation = resolveAnchoredFieldOperation(readContext, normalizedDomainName, "wallet_field_create");
1442
- const existingField = getObservedFieldState(readContext, normalizedDomainName, normalizedFieldName);
1443
- if (existingField !== null) {
1444
- throw new Error("wallet_field_create_field_exists");
1445
- }
1446
- if (operation.chainDomain.nextFieldId === 0xffff_ffff) {
1447
- throw new Error("wallet_field_create_field_id_exhausted");
1448
- }
1449
- if (hex(operation.chainDomain.delegate) !== null) {
1450
- throw new Error("wallet_field_create_delegate_blocks_same_block_family");
1451
- }
1452
- const senderBalance = getBalance(operation.readContext.snapshot.state, Buffer.from(operation.sender.scriptPubKeyHex, "hex"));
1453
- if (senderBalance < 101n) {
1454
- throw new Error("wallet_field_create_insufficient_cog");
1455
- }
1456
- const intentFingerprintHex = createIntentFingerprint([
1457
- "field-create",
1458
- operation.state.walletRootId,
1459
- normalizedDomainName,
1460
- normalizedFieldName,
1461
- options.permanent ? 1 : 0,
1462
- options.value.format,
1463
- options.value.valueHex,
1464
- ]);
1465
- const existingFamily = findFieldFamilyByIntent(operation.state, intentFingerprintHex);
1466
- const conflictingFamily = findActiveFieldFamilyByDomain(operation.state, normalizedDomainName);
1467
- if (conflictingFamily !== null && conflictingFamily.intentFingerprintHex !== intentFingerprintHex) {
1468
- throw new Error("wallet_field_create_family_already_active");
1469
- }
1470
- const conflictingCreate = findActiveFieldCreateMutationByDomain(operation.state, normalizedDomainName, intentFingerprintHex);
1471
- if (conflictingCreate !== null) {
1472
- throw new Error("wallet_field_create_registration_already_pending");
1473
- }
1474
- const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
1475
- dataDir: options.dataDir,
1476
- chain: "main",
1477
- startHeight: 0,
1478
- walletRootId: operation.state.walletRootId,
1479
- });
1480
- const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
1481
- const walletName = operation.state.managedCoreWallet.walletName;
1482
- let workingState = operation.state;
1483
- let resumedFamily = null;
1484
- if (existingFamily !== null) {
1485
- const reconciled = await reconcileFieldFamily({
1486
- state: workingState,
1487
- family: existingFamily,
1488
- provider,
1489
- nowUnixMs,
1490
- paths,
1491
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1492
- rpc,
1493
- walletName,
1494
- context: readContext,
1495
- });
1496
- workingState = reconciled.state;
1497
- if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
1498
- return {
1499
- kind: "field-create",
1500
- domainName: normalizedDomainName,
1501
- fieldName: normalizedFieldName,
1502
- fieldId: reconciled.family.expectedFieldId ?? null,
1503
- txid: reconciled.family.tx2?.attemptedTxid ?? reconciled.family.tx1?.attemptedTxid ?? "unknown",
1504
- tx1Txid: reconciled.family.tx1?.attemptedTxid ?? null,
1505
- tx2Txid: reconciled.family.tx2?.attemptedTxid ?? null,
1506
- family: true,
1507
- permanent: reconciled.family.fieldPermanent ?? null,
1508
- format: reconciled.family.fieldFormat ?? null,
1509
- status: reconciled.resolution,
1510
- reusedExisting: true,
1511
- resolved: createResolvedFieldSummary({
1512
- sender: operation.sender,
1513
- senderSelector: operation.senderSelector,
1514
- kind: "field-create",
1515
- family: true,
1516
- value: createResolvedFieldValueFromStoredData("field-create", reconciled.family.fieldFormat ?? null, reconciled.family.fieldValueHex),
1517
- }),
1518
- };
1519
- }
1520
- if (reconciled.resolution === "repair-required") {
1521
- throw new Error("wallet_field_create_repair_required");
1522
- }
1523
- if (reconciled.resolution === "ready-for-tx2") {
1524
- resumedFamily = reconciled.family;
1525
- }
1526
- }
1527
- if (resumedFamily === null) {
1528
- await confirmFieldCreate(options.prompter, {
1529
- domainName: normalizedDomainName,
1530
- fieldName: normalizedFieldName,
1531
- permanent: options.permanent,
1532
- value: options.value,
1533
- sender: createResolvedFieldSenderSummary(operation.sender, operation.senderSelector),
1534
- assumeYes: options.assumeYes,
1535
- });
1536
- let nextState = upsertProactiveFamily(workingState, createFieldFamilyRecord({
1537
- domainName: normalizedDomainName,
1538
- domainId: operation.chainDomain.domainId,
1539
- fieldName: normalizedFieldName,
1540
- expectedFieldId: operation.chainDomain.nextFieldId,
1541
- sender: operation.sender,
1542
- permanence: options.permanent,
1543
- format: options.value.format,
1544
- valueHex: options.value.valueHex,
1545
- intentFingerprintHex,
1546
- nowUnixMs,
1547
- existing: existingFamily,
1548
- }));
1549
- nextState = await saveUpdatedState({
1550
- state: nextState,
1551
- provider,
1552
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1553
- nowUnixMs,
1554
- paths,
1555
- });
1556
- const family = findFieldFamilyByIntent(nextState, intentFingerprintHex);
1557
- const tx1 = await buildFieldTransaction({
1558
- rpc,
1559
- walletName,
1560
- state: nextState,
1561
- plan: buildAnchoredFieldPlan({
1562
- state: nextState,
1563
- allUtxos: await rpc.listUnspent(walletName, 1),
1564
- sender: operation.sender,
1565
- anchorOutpoint: operation.anchorOutpoint,
1566
- opReturnData: serializeFieldReg(operation.chainDomain.domainId, options.permanent, normalizedFieldName).opReturnData,
1567
- errorPrefix: "wallet_field_create_tx1",
1568
- }),
1569
- });
1570
- const afterTx1 = await sendFamilyTx1({
1571
- rpc,
1572
- walletName,
1573
- snapshotHeight: readContext.snapshot?.tip?.height ?? null,
1574
- built: tx1,
1575
- family,
1576
- state: nextState,
1577
- provider,
1578
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1579
- nowUnixMs,
1580
- paths,
1581
- });
1582
- workingState = afterTx1.state;
1583
- resumedFamily = afterTx1.family;
1584
- }
1585
- const tx1Txid = resumedFamily.tx1?.attemptedTxid;
1586
- if (tx1Txid == null) {
1587
- throw new Error("wallet_field_create_tx1_missing");
1588
- }
1589
- await rpc.getRawTransaction(tx1Txid, true);
1590
- const tx2 = await buildFieldTransaction({
1591
- rpc,
1592
- walletName,
1593
- state: workingState,
1594
- availableFundingMinConf: 0,
1595
- plan: buildFieldFamilyTx2Plan({
1596
- state: workingState,
1597
- allUtxos: await rpc.listUnspent(walletName, 0),
1598
- sender: operation.sender,
1599
- tx1Txid,
1600
- opReturnData: serializeDataUpdate(operation.chainDomain.domainId, resumedFamily.expectedFieldId ?? operation.chainDomain.nextFieldId, options.value.format, options.value.value).opReturnData,
1601
- }),
1602
- });
1603
- const final = await sendFamilyTx2({
1604
- rpc,
1605
- walletName,
1606
- built: tx2,
1607
- family: resumedFamily,
1608
- state: workingState,
1609
- provider,
1610
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1611
- nowUnixMs,
1612
- paths,
1613
- });
1614
- return {
1615
- kind: "field-create",
1616
- domainName: normalizedDomainName,
1617
- fieldName: normalizedFieldName,
1618
- fieldId: final.family.expectedFieldId ?? null,
1619
- txid: final.family.tx2?.attemptedTxid ?? tx2.txid,
1620
- tx1Txid,
1621
- tx2Txid: final.family.tx2?.attemptedTxid ?? tx2.txid,
1622
- family: true,
1623
- permanent: final.family.fieldPermanent ?? null,
1624
- format: final.family.fieldFormat ?? null,
1625
- status: "live",
1626
- reusedExisting: existingFamily !== null,
1627
- resolved: createResolvedFieldSummary({
1628
- sender: operation.sender,
1629
- senderSelector: operation.senderSelector,
1630
- kind: "field-create",
1631
- family: true,
1632
- value: createResolvedFieldValueSummary(options.value.format, options.value.value),
799
+ fees: createBuiltWalletMutationFeeSummary({
800
+ selection: feeSelection,
801
+ built,
1633
802
  }),
1634
803
  };
1635
804
  }
@@ -1643,15 +812,7 @@ async function submitFieldCreateFamily(options) {
1643
812
  }
1644
813
  }
1645
814
  export async function createField(options) {
1646
- const normalizedSource = options.source == null ? null : await loadFieldValue(options.source);
1647
815
  const permanent = options.permanent ?? false;
1648
- if (normalizedSource !== null) {
1649
- return submitFieldCreateFamily({
1650
- ...options,
1651
- permanent,
1652
- value: normalizedSource,
1653
- });
1654
- }
1655
816
  return submitStandaloneFieldMutation({
1656
817
  kind: "field-create",
1657
818
  errorPrefix: "wallet_field_create",
@@ -1667,7 +828,7 @@ export async function createField(options) {
1667
828
  openReadContext: options.openReadContext,
1668
829
  attachService: options.attachService,
1669
830
  rpcFactory: options.rpcFactory,
1670
- async createMutation(operation, existing) {
831
+ async createMutation(operation, existing, feeSelection) {
1671
832
  const existingField = getObservedFieldState(operation.readContext, normalizeDomainName(options.domainName), normalizeFieldName(options.fieldName));
1672
833
  if (existingField !== null) {
1673
834
  throw new Error("wallet_field_create_field_exists");
@@ -1688,10 +849,6 @@ export async function createField(options) {
1688
849
  normalizedFieldName,
1689
850
  permanent ? 1 : 0,
1690
851
  ]);
1691
- const conflictFamily = findActiveFieldFamilyByDomain(operation.state, normalizedDomainName);
1692
- if (conflictFamily !== null && conflictFamily.intentFingerprintHex !== intentFingerprintHex) {
1693
- throw new Error("wallet_field_create_family_already_active");
1694
- }
1695
852
  const conflictCreate = findActiveFieldCreateMutationByDomain(operation.state, normalizedDomainName, intentFingerprintHex);
1696
853
  if (conflictCreate !== null) {
1697
854
  throw new Error("wallet_field_create_registration_already_pending");
@@ -1705,6 +862,7 @@ export async function createField(options) {
1705
862
  sender: operation.sender,
1706
863
  intentFingerprintHex,
1707
864
  nowUnixMs: options.nowUnixMs ?? Date.now(),
865
+ feeSelection,
1708
866
  existing,
1709
867
  fieldId: operation.chainDomain.nextFieldId,
1710
868
  fieldPermanent: permanent,
@@ -1716,7 +874,6 @@ export async function createField(options) {
1716
874
  domainName: normalizeDomainName(options.domainName),
1717
875
  fieldName: normalizeFieldName(options.fieldName),
1718
876
  permanent,
1719
- value: null,
1720
877
  sender: createResolvedFieldSenderSummary(operation.sender, operation.senderSelector),
1721
878
  assumeYes: options.assumeYes,
1722
879
  });
@@ -1740,7 +897,7 @@ export async function setField(options) {
1740
897
  openReadContext: options.openReadContext,
1741
898
  attachService: options.attachService,
1742
899
  rpcFactory: options.rpcFactory,
1743
- async createMutation(operation, existing) {
900
+ async createMutation(operation, existing, feeSelection) {
1744
901
  const normalizedDomainName = normalizeDomainName(options.domainName);
1745
902
  const normalizedFieldName = normalizeFieldName(options.fieldName);
1746
903
  const observedField = getObservedFieldState(operation.readContext, normalizedDomainName, normalizedFieldName);
@@ -1771,6 +928,7 @@ export async function setField(options) {
1771
928
  sender: operation.sender,
1772
929
  intentFingerprintHex,
1773
930
  nowUnixMs: options.nowUnixMs ?? Date.now(),
931
+ feeSelection,
1774
932
  existing,
1775
933
  fieldId: observedField.fieldId,
1776
934
  fieldPermanent: observedField.permanent,
@@ -1814,7 +972,7 @@ export async function clearField(options) {
1814
972
  openReadContext: options.openReadContext,
1815
973
  attachService: options.attachService,
1816
974
  rpcFactory: options.rpcFactory,
1817
- async createMutation(operation, existing) {
975
+ async createMutation(operation, existing, feeSelection) {
1818
976
  const normalizedDomainName = normalizeDomainName(options.domainName);
1819
977
  const normalizedFieldName = normalizeFieldName(options.fieldName);
1820
978
  const observedField = getObservedFieldState(operation.readContext, normalizedDomainName, normalizedFieldName);
@@ -1839,6 +997,7 @@ export async function clearField(options) {
1839
997
  sender: operation.sender,
1840
998
  intentFingerprintHex,
1841
999
  nowUnixMs: options.nowUnixMs ?? Date.now(),
1000
+ feeSelection,
1842
1001
  existing,
1843
1002
  fieldId: observedField.fieldId,
1844
1003
  fieldPermanent: observedField.permanent,