@cogcoin/client 0.5.14 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) 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 +16 -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 +38 -3
  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 +51 -4
  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 +4 -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/types.d.ts +8 -17
  75. package/dist/cli/types.js +0 -2
  76. package/dist/cli/wallet-format.d.ts +1 -0
  77. package/dist/cli/wallet-format.js +205 -144
  78. package/dist/cli/workflow-hints.d.ts +3 -3
  79. package/dist/cli/workflow-hints.js +11 -8
  80. package/dist/client/default-client.d.ts +3 -1
  81. package/dist/client/default-client.js +45 -2
  82. package/dist/client/factory.js +1 -1
  83. package/dist/client/initialization.js +23 -0
  84. package/dist/client/persistence.js +5 -5
  85. package/dist/client/store-adapter.js +1 -0
  86. package/dist/sqlite/checkpoints.d.ts +1 -0
  87. package/dist/sqlite/checkpoints.js +7 -0
  88. package/dist/sqlite/store.js +14 -1
  89. package/dist/types.d.ts +1 -0
  90. package/dist/wallet/coin-control.d.ts +41 -11
  91. package/dist/wallet/coin-control.js +100 -357
  92. package/dist/wallet/descriptor-normalization.d.ts +1 -3
  93. package/dist/wallet/descriptor-normalization.js +0 -16
  94. package/dist/wallet/lifecycle.d.ts +7 -99
  95. package/dist/wallet/lifecycle.js +513 -968
  96. package/dist/wallet/managed-core-wallet.d.ts +13 -0
  97. package/dist/wallet/managed-core-wallet.js +20 -0
  98. package/dist/wallet/mining/constants.d.ts +5 -12
  99. package/dist/wallet/mining/constants.js +5 -12
  100. package/dist/wallet/mining/control.d.ts +1 -13
  101. package/dist/wallet/mining/control.js +45 -349
  102. package/dist/wallet/mining/index.d.ts +3 -4
  103. package/dist/wallet/mining/index.js +1 -2
  104. package/dist/wallet/mining/runner.d.ts +179 -6
  105. package/dist/wallet/mining/runner.js +891 -501
  106. package/dist/wallet/mining/runtime-artifacts.js +23 -3
  107. package/dist/wallet/mining/sentence-protocol.d.ts +44 -0
  108. package/dist/wallet/mining/sentence-protocol.js +123 -0
  109. package/dist/wallet/mining/sentences.d.ts +4 -8
  110. package/dist/wallet/mining/sentences.js +3 -52
  111. package/dist/wallet/mining/state.d.ts +11 -6
  112. package/dist/wallet/mining/state.js +7 -6
  113. package/dist/wallet/mining/types.d.ts +2 -30
  114. package/dist/wallet/mining/visualizer.d.ts +31 -3
  115. package/dist/wallet/mining/visualizer.js +135 -13
  116. package/dist/wallet/read/context.d.ts +0 -2
  117. package/dist/wallet/read/context.js +119 -140
  118. package/dist/wallet/read/filter.js +2 -11
  119. package/dist/wallet/read/index.d.ts +1 -1
  120. package/dist/wallet/read/project.js +24 -77
  121. package/dist/wallet/read/types.d.ts +10 -25
  122. package/dist/wallet/reset.d.ts +0 -1
  123. package/dist/wallet/reset.js +60 -138
  124. package/dist/wallet/root-resolution.d.ts +1 -5
  125. package/dist/wallet/root-resolution.js +0 -18
  126. package/dist/wallet/runtime.d.ts +0 -6
  127. package/dist/wallet/runtime.js +0 -8
  128. package/dist/wallet/state/client-password-agent.js +208 -0
  129. package/dist/wallet/state/client-password.d.ts +65 -0
  130. package/dist/wallet/state/client-password.js +952 -0
  131. package/dist/wallet/state/crypto.d.ts +1 -20
  132. package/dist/wallet/state/crypto.js +0 -63
  133. package/dist/wallet/state/provider.d.ts +23 -11
  134. package/dist/wallet/state/provider.js +248 -290
  135. package/dist/wallet/state/storage.d.ts +2 -2
  136. package/dist/wallet/state/storage.js +48 -16
  137. package/dist/wallet/tx/anchor.d.ts +3 -28
  138. package/dist/wallet/tx/anchor.js +349 -1240
  139. package/dist/wallet/tx/bitcoin-transfer.d.ts +35 -0
  140. package/dist/wallet/tx/bitcoin-transfer.js +200 -0
  141. package/dist/wallet/tx/cog.d.ts +5 -1
  142. package/dist/wallet/tx/cog.js +149 -185
  143. package/dist/wallet/tx/common.d.ts +74 -10
  144. package/dist/wallet/tx/common.js +315 -138
  145. package/dist/wallet/tx/domain-admin.d.ts +3 -1
  146. package/dist/wallet/tx/domain-admin.js +61 -99
  147. package/dist/wallet/tx/domain-market.d.ts +5 -1
  148. package/dist/wallet/tx/domain-market.js +221 -228
  149. package/dist/wallet/tx/field.d.ts +4 -10
  150. package/dist/wallet/tx/field.js +84 -914
  151. package/dist/wallet/tx/identity-selector.d.ts +9 -3
  152. package/dist/wallet/tx/identity-selector.js +17 -35
  153. package/dist/wallet/tx/index.d.ts +3 -1
  154. package/dist/wallet/tx/index.js +2 -1
  155. package/dist/wallet/tx/register.d.ts +3 -1
  156. package/dist/wallet/tx/register.js +62 -220
  157. package/dist/wallet/tx/reputation.d.ts +3 -1
  158. package/dist/wallet/tx/reputation.js +58 -95
  159. package/dist/wallet/types.d.ts +8 -122
  160. package/package.json +5 -5
  161. package/dist/wallet/archive.d.ts +0 -4
  162. package/dist/wallet/archive.js +0 -41
  163. package/dist/wallet/mining/hook-protocol.d.ts +0 -47
  164. package/dist/wallet/mining/hook-protocol.js +0 -161
  165. package/dist/wallet/mining/hook-runner.js +0 -52
  166. package/dist/wallet/mining/hooks.d.ts +0 -38
  167. package/dist/wallet/mining/hooks.js +0 -520
  168. package/dist/wallet/state/explicit-lock.d.ts +0 -4
  169. package/dist/wallet/state/explicit-lock.js +0 -19
  170. package/dist/wallet/state/session.d.ts +0 -12
  171. package/dist/wallet/state/session.js +0 -23
  172. /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, 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,89 +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
- return {
278
- sender: options.sender,
279
- changeAddress: options.state.funding.address,
280
- fixedInputs: [{ txid: options.tx1Txid, vout: 1 }],
281
- outputs: [
282
- { data: Buffer.from(options.opReturnData).toString("hex") },
283
- { [options.sender.address]: satsToBtcNumber(BigInt(options.state.anchorValueSats)) },
284
- ],
285
- changePosition: 2,
286
- expectedOpReturnScriptHex: encodeOpReturnScript(options.opReturnData),
287
- expectedAnchorScriptHex: options.sender.scriptPubKeyHex,
288
- expectedAnchorValueSats: BigInt(options.state.anchorValueSats),
289
- allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
290
- eligibleFundingOutpointKeys: new Set(fundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout }))),
291
- errorPrefix: "wallet_field_create_tx2",
292
- };
293
- }
294
187
  function validateFieldDraft(decoded, funded, plan) {
295
188
  const inputs = decoded.tx.vin;
296
189
  const outputs = decoded.tx.vout;
297
190
  if (inputs.length === 0) {
298
191
  throw new Error(`${plan.errorPrefix}_missing_sender_input`);
299
192
  }
300
- assertFixedInputPrefixMatches(inputs, plan.fixedInputs, `${plan.errorPrefix}_sender_input_mismatch`);
301
- if (inputs[0]?.prevout?.scriptPubKey?.hex !== plan.sender.scriptPubKeyHex) {
302
- throw new Error(`${plan.errorPrefix}_sender_input_mismatch`);
303
- }
304
- assertFundingInputsAfterFixedPrefix({
305
- inputs,
306
- fixedInputs: plan.fixedInputs,
307
- allowedFundingScriptPubKeyHex: plan.allowedFundingScriptPubKeyHex,
308
- eligibleFundingOutpointKeys: plan.eligibleFundingOutpointKeys,
309
- errorCode: `${plan.errorPrefix}_unexpected_funding_input`,
310
- });
311
193
  if (outputs[0]?.scriptPubKey?.hex !== plan.expectedOpReturnScriptHex) {
312
194
  throw new Error(`${plan.errorPrefix}_opreturn_mismatch`);
313
195
  }
314
- if (outputs[1]?.scriptPubKey?.hex !== plan.expectedAnchorScriptHex) {
315
- throw new Error(`${plan.errorPrefix}_anchor_output_mismatch`);
316
- }
317
- if (valueToSats(outputs[1]?.value ?? 0) !== plan.expectedAnchorValueSats) {
318
- throw new Error(`${plan.errorPrefix}_anchor_value_mismatch`);
319
- }
320
196
  if (funded.changepos === -1) {
321
- if (outputs.length !== 2) {
197
+ if (outputs.length !== 1) {
322
198
  throw new Error(`${plan.errorPrefix}_unexpected_output_count`);
323
199
  }
324
200
  return;
325
201
  }
326
- if (funded.changepos !== plan.changePosition || outputs.length !== 3) {
202
+ if (funded.changepos !== plan.changePosition || outputs.length !== 2) {
327
203
  throw new Error(`${plan.errorPrefix}_change_position_mismatch`);
328
204
  }
329
205
  if (outputs[funded.changepos]?.scriptPubKey?.hex !== plan.allowedFundingScriptPubKeyHex) {
@@ -339,7 +215,8 @@ async function buildFieldTransaction(options) {
339
215
  validateFundedDraft: validateFieldDraft,
340
216
  finalizeErrorCode: `${options.plan.errorPrefix}_finalize_failed`,
341
217
  mempoolRejectPrefix: `${options.plan.errorPrefix}_mempool_rejected`,
342
- reserveCandidates: options.state.proactiveReserveOutpoints,
218
+ feeRate: options.feeRateSatVb,
219
+ availableFundingMinConf: options.availableFundingMinConf,
343
220
  });
344
221
  }
345
222
  async function saveUpdatedState(options) {
@@ -351,7 +228,6 @@ async function saveUpdatedState(options) {
351
228
  await saveWalletStatePreservingUnlock({
352
229
  state: nextState,
353
230
  provider: options.provider,
354
- unlockUntilUnixMs: options.unlockUntilUnixMs,
355
231
  nowUnixMs: options.nowUnixMs,
356
232
  paths: options.paths,
357
233
  });
@@ -374,6 +250,7 @@ function createStandaloneFieldMutation(options) {
374
250
  lastUpdatedAtUnixMs: options.nowUnixMs,
375
251
  attemptedTxid: null,
376
252
  attemptedWtxid: null,
253
+ ...createWalletMutationFeeMetadata(options.feeSelection),
377
254
  temporaryBuilderLockedOutpoints: [],
378
255
  };
379
256
  }
@@ -395,61 +272,10 @@ function createStandaloneFieldMutation(options) {
395
272
  lastUpdatedAtUnixMs: options.nowUnixMs,
396
273
  attemptedTxid: null,
397
274
  attemptedWtxid: null,
275
+ ...createWalletMutationFeeMetadata(options.feeSelection),
398
276
  temporaryBuilderLockedOutpoints: [],
399
277
  };
400
278
  }
401
- function createFieldFamilyRecord(options) {
402
- if (options.existing !== null && options.existing !== undefined) {
403
- return {
404
- ...options.existing,
405
- type: "field",
406
- status: "draft",
407
- domainName: options.domainName,
408
- domainId: options.domainId,
409
- sourceSenderLocalIndex: options.sender.localIndex,
410
- sourceSenderScriptPubKeyHex: options.sender.scriptPubKeyHex,
411
- fieldName: options.fieldName,
412
- expectedFieldId: options.expectedFieldId,
413
- fieldPermanent: options.permanence,
414
- fieldFormat: options.format,
415
- fieldValueHex: options.valueHex,
416
- currentStep: "tx1",
417
- lastUpdatedAtUnixMs: options.nowUnixMs,
418
- tx1: createFamilyTransactionRecord(),
419
- tx2: createFamilyTransactionRecord(),
420
- };
421
- }
422
- return {
423
- familyId: randomBytes(12).toString("hex"),
424
- type: "field",
425
- status: "draft",
426
- intentFingerprintHex: options.intentFingerprintHex,
427
- createdAtUnixMs: options.nowUnixMs,
428
- lastUpdatedAtUnixMs: options.nowUnixMs,
429
- domainName: options.domainName,
430
- domainId: options.domainId,
431
- sourceSenderLocalIndex: options.sender.localIndex,
432
- sourceSenderScriptPubKeyHex: options.sender.scriptPubKeyHex,
433
- fieldName: options.fieldName,
434
- expectedFieldId: options.expectedFieldId,
435
- fieldPermanent: options.permanence,
436
- fieldFormat: options.format,
437
- fieldValueHex: options.valueHex,
438
- currentStep: "tx1",
439
- tx1: createFamilyTransactionRecord(),
440
- tx2: createFamilyTransactionRecord(),
441
- };
442
- }
443
- function updateFieldFamilyState(options) {
444
- return upsertProactiveFamily(options.state, {
445
- ...options.family,
446
- status: options.status,
447
- currentStep: options.currentStep,
448
- lastUpdatedAtUnixMs: options.nowUnixMs,
449
- tx1: options.tx1 ?? options.family.tx1 ?? createFamilyTransactionRecord(),
450
- tx2: options.tx2 ?? options.family.tx2 ?? createFamilyTransactionRecord(),
451
- });
452
- }
453
279
  function getObservedFieldState(context, domainName, fieldName) {
454
280
  if (context.snapshot === null) {
455
281
  return null;
@@ -532,7 +358,6 @@ async function reconcilePendingFieldMutation(options) {
532
358
  nextState = await saveUpdatedState({
533
359
  state: nextState,
534
360
  provider: options.provider,
535
- unlockUntilUnixMs: options.unlockUntilUnixMs,
536
361
  nowUnixMs: options.nowUnixMs,
537
362
  paths: options.paths,
538
363
  });
@@ -547,7 +372,6 @@ async function reconcilePendingFieldMutation(options) {
547
372
  nextState = await saveUpdatedState({
548
373
  state: nextState,
549
374
  provider: options.provider,
550
- unlockUntilUnixMs: options.unlockUntilUnixMs,
551
375
  nowUnixMs: options.nowUnixMs,
552
376
  paths: options.paths,
553
377
  });
@@ -565,7 +389,6 @@ async function reconcilePendingFieldMutation(options) {
565
389
  nextState = await saveUpdatedState({
566
390
  state: nextState,
567
391
  provider: options.provider,
568
- unlockUntilUnixMs: options.unlockUntilUnixMs,
569
392
  nowUnixMs: options.nowUnixMs,
570
393
  paths: options.paths,
571
394
  });
@@ -582,7 +405,6 @@ async function reconcilePendingFieldMutation(options) {
582
405
  nextState = await saveUpdatedState({
583
406
  state: nextState,
584
407
  provider: options.provider,
585
- unlockUntilUnixMs: options.unlockUntilUnixMs,
586
408
  nowUnixMs: options.nowUnixMs,
587
409
  paths: options.paths,
588
410
  });
@@ -594,180 +416,6 @@ async function reconcilePendingFieldMutation(options) {
594
416
  resolution: "continue",
595
417
  };
596
418
  }
597
- async function reconcileFieldFamily(options) {
598
- const domainName = options.family.domainName ?? "";
599
- const fieldName = options.family.fieldName ?? "";
600
- const observed = getObservedFieldState(options.context, domainName, fieldName);
601
- if (observed !== null) {
602
- if (observed.fieldId === options.family.expectedFieldId
603
- && observed.permanent === options.family.fieldPermanent
604
- && observed.hasValue
605
- && observed.format === options.family.fieldFormat
606
- && observed.rawValueHex === options.family.fieldValueHex) {
607
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
608
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
609
- let nextState = updateFieldFamilyState({
610
- state: options.state,
611
- family: options.family,
612
- status: "confirmed",
613
- currentStep: "tx2",
614
- nowUnixMs: options.nowUnixMs,
615
- tx1: options.family.tx1 == null
616
- ? undefined
617
- : { ...options.family.tx1, status: "confirmed", temporaryBuilderLockedOutpoints: [] },
618
- tx2: options.family.tx2 == null
619
- ? undefined
620
- : { ...options.family.tx2, status: "confirmed", temporaryBuilderLockedOutpoints: [] },
621
- });
622
- nextState = await saveUpdatedState({
623
- state: nextState,
624
- provider: options.provider,
625
- unlockUntilUnixMs: options.unlockUntilUnixMs,
626
- nowUnixMs: options.nowUnixMs,
627
- paths: options.paths,
628
- });
629
- return {
630
- state: nextState,
631
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
632
- ...options.family,
633
- status: "confirmed",
634
- },
635
- resolution: "confirmed",
636
- };
637
- }
638
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
639
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
640
- let nextState = updateFieldFamilyState({
641
- state: options.state,
642
- family: options.family,
643
- status: "repair-required",
644
- currentStep: "tx2",
645
- nowUnixMs: options.nowUnixMs,
646
- tx1: options.family.tx1 == null
647
- ? undefined
648
- : { ...options.family.tx1, temporaryBuilderLockedOutpoints: [] },
649
- tx2: options.family.tx2 == null
650
- ? undefined
651
- : { ...options.family.tx2, temporaryBuilderLockedOutpoints: [] },
652
- });
653
- nextState = await saveUpdatedState({
654
- state: nextState,
655
- provider: options.provider,
656
- unlockUntilUnixMs: options.unlockUntilUnixMs,
657
- nowUnixMs: options.nowUnixMs,
658
- paths: options.paths,
659
- });
660
- return {
661
- state: nextState,
662
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
663
- ...options.family,
664
- status: "repair-required",
665
- },
666
- resolution: "repair-required",
667
- };
668
- }
669
- const tx2Known = options.family.tx2?.attemptedTxid == null
670
- ? false
671
- : await options.rpc.getRawTransaction(options.family.tx2.attemptedTxid, true).then(() => true).catch(() => false);
672
- if (tx2Known) {
673
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
674
- let nextState = updateFieldFamilyState({
675
- state: options.state,
676
- family: options.family,
677
- status: "live",
678
- currentStep: "tx2",
679
- nowUnixMs: options.nowUnixMs,
680
- tx2: options.family.tx2 == null
681
- ? undefined
682
- : { ...options.family.tx2, status: "live", temporaryBuilderLockedOutpoints: [] },
683
- });
684
- nextState = await saveUpdatedState({
685
- state: nextState,
686
- provider: options.provider,
687
- unlockUntilUnixMs: options.unlockUntilUnixMs,
688
- nowUnixMs: options.nowUnixMs,
689
- paths: options.paths,
690
- });
691
- return {
692
- state: nextState,
693
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
694
- ...options.family,
695
- status: "live",
696
- },
697
- resolution: "live",
698
- };
699
- }
700
- const tx1Known = options.family.tx1?.attemptedTxid == null
701
- ? false
702
- : await options.rpc.getRawTransaction(options.family.tx1.attemptedTxid, true).then(() => true).catch(() => false);
703
- if (tx1Known) {
704
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
705
- let nextState = updateFieldFamilyState({
706
- state: options.state,
707
- family: options.family,
708
- status: "live",
709
- currentStep: "tx1",
710
- nowUnixMs: options.nowUnixMs,
711
- tx1: options.family.tx1 == null
712
- ? undefined
713
- : { ...options.family.tx1, status: "live", temporaryBuilderLockedOutpoints: [] },
714
- });
715
- nextState = await saveUpdatedState({
716
- state: nextState,
717
- provider: options.provider,
718
- unlockUntilUnixMs: options.unlockUntilUnixMs,
719
- nowUnixMs: options.nowUnixMs,
720
- paths: options.paths,
721
- });
722
- return {
723
- state: nextState,
724
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
725
- ...options.family,
726
- status: "live",
727
- },
728
- resolution: "ready-for-tx2",
729
- };
730
- }
731
- if (options.family.status === "broadcast-unknown"
732
- || options.family.status === "draft"
733
- || options.family.status === "broadcasting") {
734
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
735
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
736
- let nextState = updateFieldFamilyState({
737
- state: options.state,
738
- family: options.family,
739
- status: "canceled",
740
- currentStep: options.family.currentStep,
741
- nowUnixMs: options.nowUnixMs,
742
- tx1: options.family.tx1 == null
743
- ? undefined
744
- : { ...options.family.tx1, status: options.family.tx1.attemptedTxid === null ? "canceled" : options.family.tx1.status, temporaryBuilderLockedOutpoints: [] },
745
- tx2: options.family.tx2 == null
746
- ? undefined
747
- : { ...options.family.tx2, status: options.family.tx2.attemptedTxid === null ? "canceled" : options.family.tx2.status, temporaryBuilderLockedOutpoints: [] },
748
- });
749
- nextState = await saveUpdatedState({
750
- state: nextState,
751
- provider: options.provider,
752
- unlockUntilUnixMs: options.unlockUntilUnixMs,
753
- nowUnixMs: options.nowUnixMs,
754
- paths: options.paths,
755
- });
756
- return {
757
- state: nextState,
758
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
759
- ...options.family,
760
- status: "canceled",
761
- },
762
- resolution: "not-seen",
763
- };
764
- }
765
- return {
766
- state: options.state,
767
- family: options.family,
768
- resolution: "continue",
769
- };
770
- }
771
419
  async function confirmYesNo(prompter, message, errorCode, options) {
772
420
  await confirmSharedYesNo(prompter, message, {
773
421
  assumeYes: options.assumeYes,
@@ -789,39 +437,10 @@ async function confirmFieldCreate(prompter, options) {
789
437
  const fieldRef = `${options.domainName}:${options.fieldName}`;
790
438
  prompter.writeLine(`Creating field "${fieldRef}" as ${options.permanent ? "permanent" : "mutable"}.`);
791
439
  prompter.writeLine(`Resolved sender: ${options.sender.selector} (${options.sender.address})`);
792
- if (options.value === null) {
793
- prompter.writeLine("Path: standalone-field-reg");
794
- prompter.writeLine(`Effect: ${describeFieldEffect({ kind: "create-empty-field", burnCogtoshi: "100" })}.`);
795
- prompter.writeLine("This publishes a standalone FIELD_REG and burns 0.00000100 COG.");
796
- await confirmYesNo(prompter, "The field will be created empty and the burn is not reversible.", "wallet_field_create_confirmation_rejected", {
797
- assumeYes: options.assumeYes,
798
- requiresTtyErrorCode: "wallet_field_create_requires_tty",
799
- });
800
- return;
801
- }
802
- prompter.writeLine("Path: field-reg-plus-data-update-family");
803
- prompter.writeLine(`Effect: ${describeFieldEffect({
804
- kind: "create-and-initialize-field",
805
- tx1BurnCogtoshi: "100",
806
- tx2AdditionalBurnCogtoshi: "1",
807
- })}.`);
808
- prompter.writeLine(`Value: format ${options.value.format}, ${options.value.value.length} bytes`);
809
- prompter.writeLine(`Initial value format: ${options.value.formatLabel}`);
810
- prompter.writeLine(`Initial value bytes: ${options.value.value.length}`);
811
- prompter.writeLine("Warning: non-clear field values are public in the mempool and on-chain.");
812
- prompter.writeLine("This uses the same-block FIELD_REG -> DATA_UPDATE family.");
813
- prompter.writeLine("Tx1 burns 0.00000100 COG. Tx2 may burn an additional 0.00000001 COG.");
814
- prompter.writeLine("Tx1 may confirm even if Tx2 later fails, is canceled, or needs repair.");
815
- if (options.permanent) {
816
- prompter.writeLine("This is the first non-clear value write to a permanent field.");
817
- await confirmTyped(prompter, fieldRef, `Type ${fieldRef} to continue: `, "wallet_field_create_confirmation_rejected", {
818
- assumeYes: options.assumeYes,
819
- requiresTtyErrorCode: "wallet_field_create_requires_tty",
820
- typedAckRequiredErrorCode: "wallet_field_create_typed_ack_required",
821
- });
822
- return;
823
- }
824
- 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", {
825
444
  assumeYes: options.assumeYes,
826
445
  requiresTtyErrorCode: "wallet_field_create_requires_tty",
827
446
  });
@@ -971,7 +590,6 @@ async function sendStandaloneMutation(options) {
971
590
  nextState = await saveUpdatedState({
972
591
  state: nextState,
973
592
  provider: options.provider,
974
- unlockUntilUnixMs: options.unlockUntilUnixMs,
975
593
  nowUnixMs: options.nowUnixMs,
976
594
  paths: options.paths,
977
595
  });
@@ -994,7 +612,6 @@ async function sendStandaloneMutation(options) {
994
612
  nextState = await saveUpdatedState({
995
613
  state: nextState,
996
614
  provider: options.provider,
997
- unlockUntilUnixMs: options.unlockUntilUnixMs,
998
615
  nowUnixMs: options.nowUnixMs,
999
616
  paths: options.paths,
1000
617
  });
@@ -1010,7 +627,6 @@ async function sendStandaloneMutation(options) {
1010
627
  nextState = await saveUpdatedState({
1011
628
  state: nextState,
1012
629
  provider: options.provider,
1013
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1014
630
  nowUnixMs: options.nowUnixMs,
1015
631
  paths: options.paths,
1016
632
  });
@@ -1027,228 +643,11 @@ async function sendStandaloneMutation(options) {
1027
643
  nextState = await saveUpdatedState({
1028
644
  state: nextState,
1029
645
  provider: options.provider,
1030
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1031
646
  nowUnixMs: options.nowUnixMs,
1032
647
  paths: options.paths,
1033
648
  });
1034
649
  return { state: nextState, mutation: live };
1035
650
  }
1036
- async function sendFamilyTx1(options) {
1037
- let nextState = updateFieldFamilyState({
1038
- state: options.state,
1039
- family: options.family,
1040
- status: "broadcasting",
1041
- currentStep: "tx1",
1042
- nowUnixMs: options.nowUnixMs,
1043
- tx1: {
1044
- status: "broadcasting",
1045
- attemptedTxid: options.built.txid,
1046
- attemptedWtxid: options.built.wtxid,
1047
- temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
1048
- rawHex: options.built.rawHex,
1049
- },
1050
- });
1051
- nextState = await saveUpdatedState({
1052
- state: nextState,
1053
- provider: options.provider,
1054
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1055
- nowUnixMs: options.nowUnixMs,
1056
- paths: options.paths,
1057
- });
1058
- if (options.snapshotHeight !== null && options.snapshotHeight !== (await options.rpc.getBlockchainInfo()).blocks) {
1059
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
1060
- throw new Error("wallet_field_create_tx1_tip_mismatch");
1061
- }
1062
- try {
1063
- await options.rpc.sendRawTransaction(options.built.rawHex);
1064
- }
1065
- catch (error) {
1066
- if (!isAlreadyAcceptedError(error)) {
1067
- if (isBroadcastUnknownError(error)) {
1068
- nextState = updateFieldFamilyState({
1069
- state: nextState,
1070
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1071
- status: "broadcast-unknown",
1072
- currentStep: "tx1",
1073
- nowUnixMs: options.nowUnixMs,
1074
- tx1: {
1075
- status: "broadcast-unknown",
1076
- attemptedTxid: options.built.txid,
1077
- attemptedWtxid: options.built.wtxid,
1078
- temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
1079
- rawHex: options.built.rawHex,
1080
- },
1081
- });
1082
- nextState = await saveUpdatedState({
1083
- state: nextState,
1084
- provider: options.provider,
1085
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1086
- nowUnixMs: options.nowUnixMs,
1087
- paths: options.paths,
1088
- });
1089
- throw new Error("wallet_field_create_tx1_broadcast_unknown");
1090
- }
1091
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
1092
- nextState = updateFieldFamilyState({
1093
- state: nextState,
1094
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1095
- status: "canceled",
1096
- currentStep: "tx1",
1097
- nowUnixMs: options.nowUnixMs,
1098
- tx1: {
1099
- status: "canceled",
1100
- attemptedTxid: options.built.txid,
1101
- attemptedWtxid: options.built.wtxid,
1102
- temporaryBuilderLockedOutpoints: [],
1103
- rawHex: options.built.rawHex,
1104
- },
1105
- });
1106
- nextState = await saveUpdatedState({
1107
- state: nextState,
1108
- provider: options.provider,
1109
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1110
- nowUnixMs: options.nowUnixMs,
1111
- paths: options.paths,
1112
- });
1113
- throw error;
1114
- }
1115
- }
1116
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
1117
- nextState = updateFieldFamilyState({
1118
- state: nextState,
1119
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1120
- status: "live",
1121
- currentStep: "tx1",
1122
- nowUnixMs: options.nowUnixMs,
1123
- tx1: {
1124
- status: "live",
1125
- attemptedTxid: options.built.txid,
1126
- attemptedWtxid: options.built.wtxid,
1127
- temporaryBuilderLockedOutpoints: [],
1128
- rawHex: options.built.rawHex,
1129
- },
1130
- });
1131
- nextState = await saveUpdatedState({
1132
- state: nextState,
1133
- provider: options.provider,
1134
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1135
- nowUnixMs: options.nowUnixMs,
1136
- paths: options.paths,
1137
- });
1138
- return {
1139
- state: nextState,
1140
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
1141
- ...options.family,
1142
- status: "live",
1143
- },
1144
- };
1145
- }
1146
- async function sendFamilyTx2(options) {
1147
- let nextState = updateFieldFamilyState({
1148
- state: options.state,
1149
- family: options.family,
1150
- status: "broadcasting",
1151
- currentStep: "tx2",
1152
- nowUnixMs: options.nowUnixMs,
1153
- tx2: {
1154
- status: "broadcasting",
1155
- attemptedTxid: options.built.txid,
1156
- attemptedWtxid: options.built.wtxid,
1157
- temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
1158
- rawHex: options.built.rawHex,
1159
- },
1160
- });
1161
- nextState = await saveUpdatedState({
1162
- state: nextState,
1163
- provider: options.provider,
1164
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1165
- nowUnixMs: options.nowUnixMs,
1166
- paths: options.paths,
1167
- });
1168
- try {
1169
- await options.rpc.sendRawTransaction(options.built.rawHex);
1170
- }
1171
- catch (error) {
1172
- if (!isAlreadyAcceptedError(error)) {
1173
- if (isBroadcastUnknownError(error)) {
1174
- nextState = updateFieldFamilyState({
1175
- state: nextState,
1176
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1177
- status: "broadcast-unknown",
1178
- currentStep: "tx2",
1179
- nowUnixMs: options.nowUnixMs,
1180
- tx2: {
1181
- status: "broadcast-unknown",
1182
- attemptedTxid: options.built.txid,
1183
- attemptedWtxid: options.built.wtxid,
1184
- temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
1185
- rawHex: options.built.rawHex,
1186
- },
1187
- });
1188
- nextState = await saveUpdatedState({
1189
- state: nextState,
1190
- provider: options.provider,
1191
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1192
- nowUnixMs: options.nowUnixMs,
1193
- paths: options.paths,
1194
- });
1195
- throw new Error("wallet_field_create_tx2_broadcast_unknown");
1196
- }
1197
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
1198
- nextState = updateFieldFamilyState({
1199
- state: nextState,
1200
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1201
- status: "live",
1202
- currentStep: "tx1",
1203
- nowUnixMs: options.nowUnixMs,
1204
- tx2: {
1205
- status: "canceled",
1206
- attemptedTxid: options.built.txid,
1207
- attemptedWtxid: options.built.wtxid,
1208
- temporaryBuilderLockedOutpoints: [],
1209
- rawHex: options.built.rawHex,
1210
- },
1211
- });
1212
- nextState = await saveUpdatedState({
1213
- state: nextState,
1214
- provider: options.provider,
1215
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1216
- nowUnixMs: options.nowUnixMs,
1217
- paths: options.paths,
1218
- });
1219
- throw error;
1220
- }
1221
- }
1222
- await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
1223
- nextState = updateFieldFamilyState({
1224
- state: nextState,
1225
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
1226
- status: "live",
1227
- currentStep: "tx2",
1228
- nowUnixMs: options.nowUnixMs,
1229
- tx2: {
1230
- status: "live",
1231
- attemptedTxid: options.built.txid,
1232
- attemptedWtxid: options.built.wtxid,
1233
- temporaryBuilderLockedOutpoints: [],
1234
- rawHex: options.built.rawHex,
1235
- },
1236
- });
1237
- nextState = await saveUpdatedState({
1238
- state: nextState,
1239
- provider: options.provider,
1240
- unlockUntilUnixMs: options.unlockUntilUnixMs,
1241
- nowUnixMs: options.nowUnixMs,
1242
- paths: options.paths,
1243
- });
1244
- return {
1245
- state: nextState,
1246
- family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
1247
- ...options.family,
1248
- status: "live",
1249
- },
1250
- };
1251
- }
1252
651
  async function submitStandaloneFieldMutation(options) {
1253
652
  if (!options.prompter.isInteractive && options.assumeYes !== true) {
1254
653
  throw new Error(`${options.errorPrefix}_requires_tty`);
@@ -1283,82 +682,91 @@ async function submitStandaloneFieldMutation(options) {
1283
682
  normalizedDomainName,
1284
683
  normalizedFieldName,
1285
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
+ });
1286
697
  const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
698
+ let workingState = operation.state;
699
+ let replacementFixedInputs = null;
1287
700
  if (existingMutation !== null) {
1288
- const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
1289
- dataDir: options.dataDir,
1290
- chain: "main",
1291
- startHeight: 0,
1292
- walletRootId: operation.state.walletRootId,
1293
- });
1294
- const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
1295
- const walletName = operation.state.managedCoreWallet.walletName;
1296
701
  const reconciled = await reconcilePendingFieldMutation({
1297
702
  state: operation.state,
1298
703
  mutation: existingMutation,
1299
704
  provider,
1300
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1301
705
  nowUnixMs,
1302
706
  paths,
1303
707
  rpc,
1304
708
  walletName,
1305
709
  context: readContext,
1306
710
  });
711
+ workingState = reconciled.state;
1307
712
  if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
1308
- return {
1309
- kind: options.kind,
1310
- domainName: normalizedDomainName,
1311
- fieldName: normalizedFieldName,
1312
- fieldId: reconciled.mutation.fieldId ?? existingObservedField?.fieldId ?? null,
1313
- txid: reconciled.mutation.attemptedTxid ?? "unknown",
1314
- family: false,
1315
- permanent: reconciled.mutation.fieldPermanent ?? existingObservedField?.permanent ?? null,
1316
- format: reconciled.mutation.fieldFormat ?? existingObservedField?.format ?? null,
1317
- status: reconciled.resolution,
1318
- reusedExisting: true,
1319
- resolved: createResolvedFieldSummary({
1320
- sender: operation.sender,
1321
- 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 {
1322
721
  kind: options.kind,
1323
- family: false,
1324
- value: createResolvedFieldValueFromStoredData(options.kind, reconciled.mutation.fieldFormat ?? existingObservedField?.format ?? null, reconciled.mutation.fieldValueHex),
1325
- }),
1326
- };
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;
1327
740
  }
1328
741
  if (reconciled.resolution === "repair-required") {
1329
742
  throw new Error(`${options.errorPrefix}_repair_required`);
1330
743
  }
1331
744
  }
1332
745
  await options.confirm(operation);
1333
- const planned = await options.createMutation(operation, existingMutation);
1334
- let nextState = upsertPendingMutation(operation.state, planned.mutation);
746
+ const planned = await options.createMutation(operation, existingMutation, feeSelection);
747
+ let nextState = upsertPendingMutation(workingState, planned.mutation);
1335
748
  nextState = await saveUpdatedState({
1336
749
  state: nextState,
1337
750
  provider,
1338
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1339
751
  nowUnixMs,
1340
752
  paths,
1341
753
  });
1342
- const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
1343
- dataDir: options.dataDir,
1344
- chain: "main",
1345
- startHeight: 0,
1346
- 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,
1347
760
  });
1348
- const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
1349
- const walletName = operation.state.managedCoreWallet.walletName;
1350
761
  const built = await buildFieldTransaction({
1351
762
  rpc,
1352
763
  walletName,
1353
764
  state: nextState,
1354
- plan: buildAnchoredFieldPlan({
1355
- state: nextState,
1356
- allUtxos: await rpc.listUnspent(walletName, 1),
1357
- sender: operation.sender,
1358
- anchorOutpoint: operation.anchorOutpoint,
1359
- opReturnData: planned.opReturnData,
1360
- errorPrefix: options.errorPrefix,
1361
- }),
765
+ plan: {
766
+ ...fieldPlan,
767
+ fixedInputs: mergeFixedWalletInputs(fieldPlan.fixedInputs, replacementFixedInputs),
768
+ },
769
+ feeRateSatVb: feeSelection.feeRateSatVb,
1362
770
  });
1363
771
  const final = await sendStandaloneMutation({
1364
772
  rpc,
@@ -1368,7 +776,6 @@ async function submitStandaloneFieldMutation(options) {
1368
776
  mutation: nextState.pendingMutations.find((mutation) => mutation.intentFingerprintHex === planned.mutation.intentFingerprintHex),
1369
777
  state: nextState,
1370
778
  provider,
1371
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1372
779
  nowUnixMs,
1373
780
  paths,
1374
781
  errorPrefix: options.errorPrefix,
@@ -1379,7 +786,6 @@ async function submitStandaloneFieldMutation(options) {
1379
786
  fieldName: normalizedFieldName,
1380
787
  fieldId: final.mutation.fieldId ?? existingObservedField?.fieldId ?? null,
1381
788
  txid: final.mutation.attemptedTxid ?? built.txid,
1382
- family: false,
1383
789
  permanent: final.mutation.fieldPermanent ?? existingObservedField?.permanent ?? null,
1384
790
  format: final.mutation.fieldFormat ?? existingObservedField?.format ?? null,
1385
791
  status: "live",
@@ -1388,237 +794,11 @@ async function submitStandaloneFieldMutation(options) {
1388
794
  sender: operation.sender,
1389
795
  senderSelector: operation.senderSelector,
1390
796
  kind: options.kind,
1391
- family: false,
1392
797
  value: createResolvedFieldValueFromStoredData(options.kind, planned.mutation.fieldFormat ?? existingObservedField?.format ?? null, planned.mutation.fieldValueHex),
1393
798
  }),
1394
- };
1395
- }
1396
- finally {
1397
- await readContext.close();
1398
- await miningPreemption.release();
1399
- }
1400
- }
1401
- finally {
1402
- await controlLock.release();
1403
- }
1404
- }
1405
- async function submitFieldCreateFamily(options) {
1406
- if (!options.prompter.isInteractive && options.assumeYes !== true) {
1407
- throw new Error("wallet_field_create_requires_tty");
1408
- }
1409
- const provider = options.provider ?? createDefaultWalletSecretProvider();
1410
- const nowUnixMs = options.nowUnixMs ?? Date.now();
1411
- const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
1412
- const controlLock = await acquireFileLock(paths.walletControlLockPath, {
1413
- purpose: "wallet_field_create",
1414
- walletRootId: null,
1415
- });
1416
- try {
1417
- const miningPreemption = await pauseMiningForWalletMutation({
1418
- paths,
1419
- reason: "wallet_field_create",
1420
- });
1421
- const readContext = await (options.openReadContext ?? openWalletReadContext)({
1422
- dataDir: options.dataDir,
1423
- databasePath: options.databasePath,
1424
- secretProvider: provider,
1425
- walletControlLockHeld: true,
1426
- paths,
1427
- });
1428
- try {
1429
- const normalizedDomainName = normalizeDomainName(options.domainName);
1430
- const normalizedFieldName = normalizeFieldName(options.fieldName);
1431
- const operation = resolveAnchoredFieldOperation(readContext, normalizedDomainName, "wallet_field_create");
1432
- const existingField = getObservedFieldState(readContext, normalizedDomainName, normalizedFieldName);
1433
- if (existingField !== null) {
1434
- throw new Error("wallet_field_create_field_exists");
1435
- }
1436
- if (operation.chainDomain.nextFieldId === 0xffff_ffff) {
1437
- throw new Error("wallet_field_create_field_id_exhausted");
1438
- }
1439
- if (hex(operation.chainDomain.delegate) !== null) {
1440
- throw new Error("wallet_field_create_delegate_blocks_same_block_family");
1441
- }
1442
- const senderBalance = getBalance(operation.readContext.snapshot.state, Buffer.from(operation.sender.scriptPubKeyHex, "hex"));
1443
- if (senderBalance < 101n) {
1444
- throw new Error("wallet_field_create_insufficient_cog");
1445
- }
1446
- const intentFingerprintHex = createIntentFingerprint([
1447
- "field-create",
1448
- operation.state.walletRootId,
1449
- normalizedDomainName,
1450
- normalizedFieldName,
1451
- options.permanent ? 1 : 0,
1452
- options.value.format,
1453
- options.value.valueHex,
1454
- ]);
1455
- const existingFamily = findFieldFamilyByIntent(operation.state, intentFingerprintHex);
1456
- const conflictingFamily = findActiveFieldFamilyByDomain(operation.state, normalizedDomainName);
1457
- if (conflictingFamily !== null && conflictingFamily.intentFingerprintHex !== intentFingerprintHex) {
1458
- throw new Error("wallet_field_create_family_already_active");
1459
- }
1460
- const conflictingCreate = findActiveFieldCreateMutationByDomain(operation.state, normalizedDomainName, intentFingerprintHex);
1461
- if (conflictingCreate !== null) {
1462
- throw new Error("wallet_field_create_registration_already_pending");
1463
- }
1464
- const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
1465
- dataDir: options.dataDir,
1466
- chain: "main",
1467
- startHeight: 0,
1468
- walletRootId: operation.state.walletRootId,
1469
- });
1470
- const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
1471
- const walletName = operation.state.managedCoreWallet.walletName;
1472
- let workingState = operation.state;
1473
- let resumedFamily = null;
1474
- if (existingFamily !== null) {
1475
- const reconciled = await reconcileFieldFamily({
1476
- state: workingState,
1477
- family: existingFamily,
1478
- provider,
1479
- nowUnixMs,
1480
- paths,
1481
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1482
- rpc,
1483
- walletName,
1484
- context: readContext,
1485
- });
1486
- workingState = reconciled.state;
1487
- if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
1488
- return {
1489
- kind: "field-create",
1490
- domainName: normalizedDomainName,
1491
- fieldName: normalizedFieldName,
1492
- fieldId: reconciled.family.expectedFieldId ?? null,
1493
- txid: reconciled.family.tx2?.attemptedTxid ?? reconciled.family.tx1?.attemptedTxid ?? "unknown",
1494
- tx1Txid: reconciled.family.tx1?.attemptedTxid ?? null,
1495
- tx2Txid: reconciled.family.tx2?.attemptedTxid ?? null,
1496
- family: true,
1497
- permanent: reconciled.family.fieldPermanent ?? null,
1498
- format: reconciled.family.fieldFormat ?? null,
1499
- status: reconciled.resolution,
1500
- reusedExisting: true,
1501
- resolved: createResolvedFieldSummary({
1502
- sender: operation.sender,
1503
- senderSelector: operation.senderSelector,
1504
- kind: "field-create",
1505
- family: true,
1506
- value: createResolvedFieldValueFromStoredData("field-create", reconciled.family.fieldFormat ?? null, reconciled.family.fieldValueHex),
1507
- }),
1508
- };
1509
- }
1510
- if (reconciled.resolution === "repair-required") {
1511
- throw new Error("wallet_field_create_repair_required");
1512
- }
1513
- if (reconciled.resolution === "ready-for-tx2") {
1514
- resumedFamily = reconciled.family;
1515
- }
1516
- }
1517
- if (resumedFamily === null) {
1518
- await confirmFieldCreate(options.prompter, {
1519
- domainName: normalizedDomainName,
1520
- fieldName: normalizedFieldName,
1521
- permanent: options.permanent,
1522
- value: options.value,
1523
- sender: createResolvedFieldSenderSummary(operation.sender, operation.senderSelector),
1524
- assumeYes: options.assumeYes,
1525
- });
1526
- let nextState = upsertProactiveFamily(workingState, createFieldFamilyRecord({
1527
- domainName: normalizedDomainName,
1528
- domainId: operation.chainDomain.domainId,
1529
- fieldName: normalizedFieldName,
1530
- expectedFieldId: operation.chainDomain.nextFieldId,
1531
- sender: operation.sender,
1532
- permanence: options.permanent,
1533
- format: options.value.format,
1534
- valueHex: options.value.valueHex,
1535
- intentFingerprintHex,
1536
- nowUnixMs,
1537
- existing: existingFamily,
1538
- }));
1539
- nextState = await saveUpdatedState({
1540
- state: nextState,
1541
- provider,
1542
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1543
- nowUnixMs,
1544
- paths,
1545
- });
1546
- const family = findFieldFamilyByIntent(nextState, intentFingerprintHex);
1547
- const tx1 = await buildFieldTransaction({
1548
- rpc,
1549
- walletName,
1550
- state: nextState,
1551
- plan: buildAnchoredFieldPlan({
1552
- state: nextState,
1553
- allUtxos: await rpc.listUnspent(walletName, 1),
1554
- sender: operation.sender,
1555
- anchorOutpoint: operation.anchorOutpoint,
1556
- opReturnData: serializeFieldReg(operation.chainDomain.domainId, options.permanent, normalizedFieldName).opReturnData,
1557
- errorPrefix: "wallet_field_create_tx1",
1558
- }),
1559
- });
1560
- const afterTx1 = await sendFamilyTx1({
1561
- rpc,
1562
- walletName,
1563
- snapshotHeight: readContext.snapshot?.tip?.height ?? null,
1564
- built: tx1,
1565
- family,
1566
- state: nextState,
1567
- provider,
1568
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1569
- nowUnixMs,
1570
- paths,
1571
- });
1572
- workingState = afterTx1.state;
1573
- resumedFamily = afterTx1.family;
1574
- }
1575
- const tx1Txid = resumedFamily.tx1?.attemptedTxid;
1576
- if (tx1Txid == null) {
1577
- throw new Error("wallet_field_create_tx1_missing");
1578
- }
1579
- await rpc.getRawTransaction(tx1Txid, true);
1580
- const tx2 = await buildFieldTransaction({
1581
- rpc,
1582
- walletName,
1583
- state: workingState,
1584
- plan: buildFieldFamilyTx2Plan({
1585
- state: workingState,
1586
- allUtxos: await rpc.listUnspent(walletName, 1),
1587
- sender: operation.sender,
1588
- tx1Txid,
1589
- opReturnData: serializeDataUpdate(operation.chainDomain.domainId, resumedFamily.expectedFieldId ?? operation.chainDomain.nextFieldId, options.value.format, options.value.value).opReturnData,
1590
- }),
1591
- });
1592
- const final = await sendFamilyTx2({
1593
- rpc,
1594
- walletName,
1595
- built: tx2,
1596
- family: resumedFamily,
1597
- state: workingState,
1598
- provider,
1599
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
1600
- nowUnixMs,
1601
- paths,
1602
- });
1603
- return {
1604
- kind: "field-create",
1605
- domainName: normalizedDomainName,
1606
- fieldName: normalizedFieldName,
1607
- fieldId: final.family.expectedFieldId ?? null,
1608
- txid: final.family.tx2?.attemptedTxid ?? tx2.txid,
1609
- tx1Txid,
1610
- tx2Txid: final.family.tx2?.attemptedTxid ?? tx2.txid,
1611
- family: true,
1612
- permanent: final.family.fieldPermanent ?? null,
1613
- format: final.family.fieldFormat ?? null,
1614
- status: "live",
1615
- reusedExisting: existingFamily !== null,
1616
- resolved: createResolvedFieldSummary({
1617
- sender: operation.sender,
1618
- senderSelector: operation.senderSelector,
1619
- kind: "field-create",
1620
- family: true,
1621
- value: createResolvedFieldValueSummary(options.value.format, options.value.value),
799
+ fees: createBuiltWalletMutationFeeSummary({
800
+ selection: feeSelection,
801
+ built,
1622
802
  }),
1623
803
  };
1624
804
  }
@@ -1632,15 +812,7 @@ async function submitFieldCreateFamily(options) {
1632
812
  }
1633
813
  }
1634
814
  export async function createField(options) {
1635
- const normalizedSource = options.source == null ? null : await loadFieldValue(options.source);
1636
815
  const permanent = options.permanent ?? false;
1637
- if (normalizedSource !== null) {
1638
- return submitFieldCreateFamily({
1639
- ...options,
1640
- permanent,
1641
- value: normalizedSource,
1642
- });
1643
- }
1644
816
  return submitStandaloneFieldMutation({
1645
817
  kind: "field-create",
1646
818
  errorPrefix: "wallet_field_create",
@@ -1656,7 +828,7 @@ export async function createField(options) {
1656
828
  openReadContext: options.openReadContext,
1657
829
  attachService: options.attachService,
1658
830
  rpcFactory: options.rpcFactory,
1659
- async createMutation(operation, existing) {
831
+ async createMutation(operation, existing, feeSelection) {
1660
832
  const existingField = getObservedFieldState(operation.readContext, normalizeDomainName(options.domainName), normalizeFieldName(options.fieldName));
1661
833
  if (existingField !== null) {
1662
834
  throw new Error("wallet_field_create_field_exists");
@@ -1677,10 +849,6 @@ export async function createField(options) {
1677
849
  normalizedFieldName,
1678
850
  permanent ? 1 : 0,
1679
851
  ]);
1680
- const conflictFamily = findActiveFieldFamilyByDomain(operation.state, normalizedDomainName);
1681
- if (conflictFamily !== null && conflictFamily.intentFingerprintHex !== intentFingerprintHex) {
1682
- throw new Error("wallet_field_create_family_already_active");
1683
- }
1684
852
  const conflictCreate = findActiveFieldCreateMutationByDomain(operation.state, normalizedDomainName, intentFingerprintHex);
1685
853
  if (conflictCreate !== null) {
1686
854
  throw new Error("wallet_field_create_registration_already_pending");
@@ -1694,6 +862,7 @@ export async function createField(options) {
1694
862
  sender: operation.sender,
1695
863
  intentFingerprintHex,
1696
864
  nowUnixMs: options.nowUnixMs ?? Date.now(),
865
+ feeSelection,
1697
866
  existing,
1698
867
  fieldId: operation.chainDomain.nextFieldId,
1699
868
  fieldPermanent: permanent,
@@ -1705,7 +874,6 @@ export async function createField(options) {
1705
874
  domainName: normalizeDomainName(options.domainName),
1706
875
  fieldName: normalizeFieldName(options.fieldName),
1707
876
  permanent,
1708
- value: null,
1709
877
  sender: createResolvedFieldSenderSummary(operation.sender, operation.senderSelector),
1710
878
  assumeYes: options.assumeYes,
1711
879
  });
@@ -1729,7 +897,7 @@ export async function setField(options) {
1729
897
  openReadContext: options.openReadContext,
1730
898
  attachService: options.attachService,
1731
899
  rpcFactory: options.rpcFactory,
1732
- async createMutation(operation, existing) {
900
+ async createMutation(operation, existing, feeSelection) {
1733
901
  const normalizedDomainName = normalizeDomainName(options.domainName);
1734
902
  const normalizedFieldName = normalizeFieldName(options.fieldName);
1735
903
  const observedField = getObservedFieldState(operation.readContext, normalizedDomainName, normalizedFieldName);
@@ -1760,6 +928,7 @@ export async function setField(options) {
1760
928
  sender: operation.sender,
1761
929
  intentFingerprintHex,
1762
930
  nowUnixMs: options.nowUnixMs ?? Date.now(),
931
+ feeSelection,
1763
932
  existing,
1764
933
  fieldId: observedField.fieldId,
1765
934
  fieldPermanent: observedField.permanent,
@@ -1803,7 +972,7 @@ export async function clearField(options) {
1803
972
  openReadContext: options.openReadContext,
1804
973
  attachService: options.attachService,
1805
974
  rpcFactory: options.rpcFactory,
1806
- async createMutation(operation, existing) {
975
+ async createMutation(operation, existing, feeSelection) {
1807
976
  const normalizedDomainName = normalizeDomainName(options.domainName);
1808
977
  const normalizedFieldName = normalizeFieldName(options.fieldName);
1809
978
  const observedField = getObservedFieldState(operation.readContext, normalizedDomainName, normalizedFieldName);
@@ -1828,6 +997,7 @@ export async function clearField(options) {
1828
997
  sender: operation.sender,
1829
998
  intentFingerprintHex,
1830
999
  nowUnixMs: options.nowUnixMs ?? Date.now(),
1000
+ feeSelection,
1831
1001
  existing,
1832
1002
  fieldId: observedField.fieldId,
1833
1003
  fieldPermanent: observedField.permanent,