@cogcoin/client 0.5.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 (289) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +136 -0
  3. package/dist/app-paths.d.ts +38 -0
  4. package/dist/app-paths.js +121 -0
  5. package/dist/art/banner.txt +13 -0
  6. package/dist/art/scroll.txt +13 -0
  7. package/dist/art/train-car.txt +6 -0
  8. package/dist/art/train-smoke.txt +6 -0
  9. package/dist/art/train.txt +6 -0
  10. package/dist/bitcoind/bootstrap/chainstate.d.ts +4 -0
  11. package/dist/bitcoind/bootstrap/chainstate.js +13 -0
  12. package/dist/bitcoind/bootstrap/constants.d.ts +7 -0
  13. package/dist/bitcoind/bootstrap/constants.js +12 -0
  14. package/dist/bitcoind/bootstrap/controller.d.ts +29 -0
  15. package/dist/bitcoind/bootstrap/controller.js +101 -0
  16. package/dist/bitcoind/bootstrap/download.d.ts +2 -0
  17. package/dist/bitcoind/bootstrap/download.js +196 -0
  18. package/dist/bitcoind/bootstrap/headers.d.ts +13 -0
  19. package/dist/bitcoind/bootstrap/headers.js +61 -0
  20. package/dist/bitcoind/bootstrap/paths.d.ts +4 -0
  21. package/dist/bitcoind/bootstrap/paths.js +15 -0
  22. package/dist/bitcoind/bootstrap/snapshot-file.d.ts +7 -0
  23. package/dist/bitcoind/bootstrap/snapshot-file.js +42 -0
  24. package/dist/bitcoind/bootstrap/state.d.ts +40 -0
  25. package/dist/bitcoind/bootstrap/state.js +70 -0
  26. package/dist/bitcoind/bootstrap/types.d.ts +28 -0
  27. package/dist/bitcoind/bootstrap/types.js +1 -0
  28. package/dist/bitcoind/bootstrap.d.ts +8 -0
  29. package/dist/bitcoind/bootstrap.js +7 -0
  30. package/dist/bitcoind/client/factory.d.ts +3 -0
  31. package/dist/bitcoind/client/factory.js +57 -0
  32. package/dist/bitcoind/client/follow-block-times.d.ts +8 -0
  33. package/dist/bitcoind/client/follow-block-times.js +25 -0
  34. package/dist/bitcoind/client/follow-loop.d.ts +10 -0
  35. package/dist/bitcoind/client/follow-loop.js +57 -0
  36. package/dist/bitcoind/client/internal-types.d.ts +63 -0
  37. package/dist/bitcoind/client/internal-types.js +18 -0
  38. package/dist/bitcoind/client/managed-client.d.ts +20 -0
  39. package/dist/bitcoind/client/managed-client.js +197 -0
  40. package/dist/bitcoind/client/rate-tracker.d.ts +2 -0
  41. package/dist/bitcoind/client/rate-tracker.js +24 -0
  42. package/dist/bitcoind/client/sync-engine.d.ts +3 -0
  43. package/dist/bitcoind/client/sync-engine.js +143 -0
  44. package/dist/bitcoind/client.d.ts +1 -0
  45. package/dist/bitcoind/client.js +1 -0
  46. package/dist/bitcoind/errors.d.ts +1 -0
  47. package/dist/bitcoind/errors.js +49 -0
  48. package/dist/bitcoind/index.d.ts +2 -0
  49. package/dist/bitcoind/index.js +1 -0
  50. package/dist/bitcoind/indexer-daemon-main.d.ts +1 -0
  51. package/dist/bitcoind/indexer-daemon-main.js +472 -0
  52. package/dist/bitcoind/indexer-daemon.d.ts +107 -0
  53. package/dist/bitcoind/indexer-daemon.js +391 -0
  54. package/dist/bitcoind/node.d.ts +8 -0
  55. package/dist/bitcoind/node.js +219 -0
  56. package/dist/bitcoind/normalize.d.ts +3 -0
  57. package/dist/bitcoind/normalize.js +47 -0
  58. package/dist/bitcoind/progress/assets.d.ts +10 -0
  59. package/dist/bitcoind/progress/assets.js +90 -0
  60. package/dist/bitcoind/progress/constants.d.ts +48 -0
  61. package/dist/bitcoind/progress/constants.js +53 -0
  62. package/dist/bitcoind/progress/controller.d.ts +28 -0
  63. package/dist/bitcoind/progress/controller.js +188 -0
  64. package/dist/bitcoind/progress/follow-scene.d.ts +40 -0
  65. package/dist/bitcoind/progress/follow-scene.js +367 -0
  66. package/dist/bitcoind/progress/formatting.d.ts +23 -0
  67. package/dist/bitcoind/progress/formatting.js +227 -0
  68. package/dist/bitcoind/progress/quote-scene.d.ts +4 -0
  69. package/dist/bitcoind/progress/quote-scene.js +137 -0
  70. package/dist/bitcoind/progress/train-scene.d.ts +9 -0
  71. package/dist/bitcoind/progress/train-scene.js +92 -0
  72. package/dist/bitcoind/progress/tty-renderer.d.ts +18 -0
  73. package/dist/bitcoind/progress/tty-renderer.js +150 -0
  74. package/dist/bitcoind/progress.d.ts +7 -0
  75. package/dist/bitcoind/progress.js +7 -0
  76. package/dist/bitcoind/quotes.d.ts +24 -0
  77. package/dist/bitcoind/quotes.js +195 -0
  78. package/dist/bitcoind/rpc.d.ts +71 -0
  79. package/dist/bitcoind/rpc.js +322 -0
  80. package/dist/bitcoind/service-paths.d.ts +19 -0
  81. package/dist/bitcoind/service-paths.js +49 -0
  82. package/dist/bitcoind/service.d.ts +40 -0
  83. package/dist/bitcoind/service.js +735 -0
  84. package/dist/bitcoind/testing.d.ts +9 -0
  85. package/dist/bitcoind/testing.js +9 -0
  86. package/dist/bitcoind/types.d.ts +396 -0
  87. package/dist/bitcoind/types.js +3 -0
  88. package/dist/bytes.d.ts +9 -0
  89. package/dist/bytes.js +36 -0
  90. package/dist/cli/commands/follow.d.ts +2 -0
  91. package/dist/cli/commands/follow.js +43 -0
  92. package/dist/cli/commands/mining-admin.d.ts +2 -0
  93. package/dist/cli/commands/mining-admin.js +92 -0
  94. package/dist/cli/commands/mining-read.d.ts +2 -0
  95. package/dist/cli/commands/mining-read.js +173 -0
  96. package/dist/cli/commands/mining-runtime.d.ts +2 -0
  97. package/dist/cli/commands/mining-runtime.js +108 -0
  98. package/dist/cli/commands/status.d.ts +2 -0
  99. package/dist/cli/commands/status.js +31 -0
  100. package/dist/cli/commands/sync.d.ts +2 -0
  101. package/dist/cli/commands/sync.js +52 -0
  102. package/dist/cli/commands/wallet-admin.d.ts +2 -0
  103. package/dist/cli/commands/wallet-admin.js +175 -0
  104. package/dist/cli/commands/wallet-mutation.d.ts +2 -0
  105. package/dist/cli/commands/wallet-mutation.js +681 -0
  106. package/dist/cli/commands/wallet-read.d.ts +2 -0
  107. package/dist/cli/commands/wallet-read.js +265 -0
  108. package/dist/cli/context.d.ts +3 -0
  109. package/dist/cli/context.js +75 -0
  110. package/dist/cli/io.d.ts +3 -0
  111. package/dist/cli/io.js +12 -0
  112. package/dist/cli/mining-format.d.ts +5 -0
  113. package/dist/cli/mining-format.js +156 -0
  114. package/dist/cli/mining-json.d.ts +49 -0
  115. package/dist/cli/mining-json.js +89 -0
  116. package/dist/cli/mutation-command-groups.d.ts +15 -0
  117. package/dist/cli/mutation-command-groups.js +71 -0
  118. package/dist/cli/mutation-json.d.ts +430 -0
  119. package/dist/cli/mutation-json.js +311 -0
  120. package/dist/cli/mutation-resolved-json.d.ts +124 -0
  121. package/dist/cli/mutation-resolved-json.js +129 -0
  122. package/dist/cli/mutation-success.d.ts +20 -0
  123. package/dist/cli/mutation-success.js +47 -0
  124. package/dist/cli/mutation-text-format.d.ts +22 -0
  125. package/dist/cli/mutation-text-format.js +171 -0
  126. package/dist/cli/mutation-text-write.d.ts +13 -0
  127. package/dist/cli/mutation-text-write.js +16 -0
  128. package/dist/cli/output.d.ts +185 -0
  129. package/dist/cli/output.js +1085 -0
  130. package/dist/cli/parse.d.ts +3 -0
  131. package/dist/cli/parse.js +971 -0
  132. package/dist/cli/preview-json.d.ts +416 -0
  133. package/dist/cli/preview-json.js +293 -0
  134. package/dist/cli/prompt.d.ts +3 -0
  135. package/dist/cli/prompt.js +33 -0
  136. package/dist/cli/read-json.d.ts +187 -0
  137. package/dist/cli/read-json.js +675 -0
  138. package/dist/cli/runner.d.ts +2 -0
  139. package/dist/cli/runner.js +129 -0
  140. package/dist/cli/signals.d.ts +3 -0
  141. package/dist/cli/signals.js +63 -0
  142. package/dist/cli/status-format.d.ts +2 -0
  143. package/dist/cli/status-format.js +48 -0
  144. package/dist/cli/types.d.ts +148 -0
  145. package/dist/cli/types.js +2 -0
  146. package/dist/cli/wallet-format.d.ts +29 -0
  147. package/dist/cli/wallet-format.js +637 -0
  148. package/dist/cli/workflow-hints.d.ts +13 -0
  149. package/dist/cli/workflow-hints.js +94 -0
  150. package/dist/cli-runner.d.ts +3 -0
  151. package/dist/cli-runner.js +3 -0
  152. package/dist/cli.d.ts +2 -0
  153. package/dist/cli.js +6 -0
  154. package/dist/client/default-client.d.ts +11 -0
  155. package/dist/client/default-client.js +118 -0
  156. package/dist/client/factory.d.ts +2 -0
  157. package/dist/client/factory.js +15 -0
  158. package/dist/client/initialization.d.ts +6 -0
  159. package/dist/client/initialization.js +30 -0
  160. package/dist/client/persistence.d.ts +5 -0
  161. package/dist/client/persistence.js +28 -0
  162. package/dist/client/store-adapter.d.ts +3 -0
  163. package/dist/client/store-adapter.js +20 -0
  164. package/dist/client.d.ts +2 -0
  165. package/dist/client.js +2 -0
  166. package/dist/index.d.ts +2 -0
  167. package/dist/index.js +1 -0
  168. package/dist/passive-status.d.ts +36 -0
  169. package/dist/passive-status.js +100 -0
  170. package/dist/sqlite/better-sqlite3.d.ts +26 -0
  171. package/dist/sqlite/better-sqlite3.js +4 -0
  172. package/dist/sqlite/checkpoints.d.ts +11 -0
  173. package/dist/sqlite/checkpoints.js +27 -0
  174. package/dist/sqlite/driver.d.ts +17 -0
  175. package/dist/sqlite/driver.js +98 -0
  176. package/dist/sqlite/index.d.ts +4 -0
  177. package/dist/sqlite/index.js +9 -0
  178. package/dist/sqlite/migrate.d.ts +2 -0
  179. package/dist/sqlite/migrate.js +37 -0
  180. package/dist/sqlite/store.d.ts +3 -0
  181. package/dist/sqlite/store.js +122 -0
  182. package/dist/sqlite/tip-meta.d.ts +26 -0
  183. package/dist/sqlite/tip-meta.js +97 -0
  184. package/dist/sqlite/types.d.ts +10 -0
  185. package/dist/sqlite/types.js +1 -0
  186. package/dist/types.d.ts +55 -0
  187. package/dist/types.js +1 -0
  188. package/dist/wallet/archive.d.ts +4 -0
  189. package/dist/wallet/archive.js +39 -0
  190. package/dist/wallet/cogop/constants.d.ts +32 -0
  191. package/dist/wallet/cogop/constants.js +32 -0
  192. package/dist/wallet/cogop/index.d.ts +32 -0
  193. package/dist/wallet/cogop/index.js +213 -0
  194. package/dist/wallet/cogop/numeric.d.ts +3 -0
  195. package/dist/wallet/cogop/numeric.js +24 -0
  196. package/dist/wallet/cogop/scriptpubkey.d.ts +2 -0
  197. package/dist/wallet/cogop/scriptpubkey.js +13 -0
  198. package/dist/wallet/cogop/validate-name.d.ts +2 -0
  199. package/dist/wallet/cogop/validate-name.js +18 -0
  200. package/dist/wallet/fs/atomic.d.ts +6 -0
  201. package/dist/wallet/fs/atomic.js +46 -0
  202. package/dist/wallet/fs/lock.d.ts +19 -0
  203. package/dist/wallet/fs/lock.js +61 -0
  204. package/dist/wallet/fs/status-file.d.ts +1 -0
  205. package/dist/wallet/fs/status-file.js +4 -0
  206. package/dist/wallet/lifecycle.d.ts +193 -0
  207. package/dist/wallet/lifecycle.js +1475 -0
  208. package/dist/wallet/material.d.ts +45 -0
  209. package/dist/wallet/material.js +118 -0
  210. package/dist/wallet/mining/config.d.ts +18 -0
  211. package/dist/wallet/mining/config.js +44 -0
  212. package/dist/wallet/mining/constants.d.ts +24 -0
  213. package/dist/wallet/mining/constants.js +24 -0
  214. package/dist/wallet/mining/control.d.ts +53 -0
  215. package/dist/wallet/mining/control.js +758 -0
  216. package/dist/wallet/mining/coordination.d.ts +40 -0
  217. package/dist/wallet/mining/coordination.js +121 -0
  218. package/dist/wallet/mining/hook-protocol.d.ts +47 -0
  219. package/dist/wallet/mining/hook-protocol.js +161 -0
  220. package/dist/wallet/mining/hook-runner.d.ts +1 -0
  221. package/dist/wallet/mining/hook-runner.js +52 -0
  222. package/dist/wallet/mining/hooks.d.ts +38 -0
  223. package/dist/wallet/mining/hooks.js +520 -0
  224. package/dist/wallet/mining/index.d.ts +8 -0
  225. package/dist/wallet/mining/index.js +6 -0
  226. package/dist/wallet/mining/runner.d.ts +155 -0
  227. package/dist/wallet/mining/runner.js +2574 -0
  228. package/dist/wallet/mining/runtime-artifacts.d.ts +17 -0
  229. package/dist/wallet/mining/runtime-artifacts.js +166 -0
  230. package/dist/wallet/mining/sentences.d.ts +23 -0
  231. package/dist/wallet/mining/sentences.js +281 -0
  232. package/dist/wallet/mining/state.d.ts +9 -0
  233. package/dist/wallet/mining/state.js +75 -0
  234. package/dist/wallet/mining/types.d.ts +141 -0
  235. package/dist/wallet/mining/types.js +1 -0
  236. package/dist/wallet/mining/visualizer.d.ts +19 -0
  237. package/dist/wallet/mining/visualizer.js +134 -0
  238. package/dist/wallet/mining/worker-main.d.ts +1 -0
  239. package/dist/wallet/mining/worker-main.js +17 -0
  240. package/dist/wallet/read/context.d.ts +20 -0
  241. package/dist/wallet/read/context.js +532 -0
  242. package/dist/wallet/read/filter.d.ts +9 -0
  243. package/dist/wallet/read/filter.js +42 -0
  244. package/dist/wallet/read/index.d.ts +4 -0
  245. package/dist/wallet/read/index.js +3 -0
  246. package/dist/wallet/read/project.d.ts +11 -0
  247. package/dist/wallet/read/project.js +300 -0
  248. package/dist/wallet/read/types.d.ts +144 -0
  249. package/dist/wallet/read/types.js +1 -0
  250. package/dist/wallet/runtime.d.ts +26 -0
  251. package/dist/wallet/runtime.js +28 -0
  252. package/dist/wallet/state/crypto.d.ts +31 -0
  253. package/dist/wallet/state/crypto.js +127 -0
  254. package/dist/wallet/state/provider.d.ts +37 -0
  255. package/dist/wallet/state/provider.js +312 -0
  256. package/dist/wallet/state/session.d.ts +12 -0
  257. package/dist/wallet/state/session.js +23 -0
  258. package/dist/wallet/state/storage.d.ts +19 -0
  259. package/dist/wallet/state/storage.js +55 -0
  260. package/dist/wallet/tx/anchor.d.ts +40 -0
  261. package/dist/wallet/tx/anchor.js +1210 -0
  262. package/dist/wallet/tx/cog.d.ts +92 -0
  263. package/dist/wallet/tx/cog.js +1055 -0
  264. package/dist/wallet/tx/common.d.ts +89 -0
  265. package/dist/wallet/tx/common.js +156 -0
  266. package/dist/wallet/tx/confirm.d.ts +15 -0
  267. package/dist/wallet/tx/confirm.js +24 -0
  268. package/dist/wallet/tx/domain-admin.d.ts +105 -0
  269. package/dist/wallet/tx/domain-admin.js +869 -0
  270. package/dist/wallet/tx/domain-market.d.ts +112 -0
  271. package/dist/wallet/tx/domain-market.js +1365 -0
  272. package/dist/wallet/tx/field.d.ts +101 -0
  273. package/dist/wallet/tx/field.js +1853 -0
  274. package/dist/wallet/tx/identity-selector.d.ts +12 -0
  275. package/dist/wallet/tx/identity-selector.js +52 -0
  276. package/dist/wallet/tx/index.d.ts +7 -0
  277. package/dist/wallet/tx/index.js +7 -0
  278. package/dist/wallet/tx/journal.d.ts +5 -0
  279. package/dist/wallet/tx/journal.js +31 -0
  280. package/dist/wallet/tx/register.d.ts +68 -0
  281. package/dist/wallet/tx/register.js +952 -0
  282. package/dist/wallet/tx/reputation.d.ts +72 -0
  283. package/dist/wallet/tx/reputation.js +693 -0
  284. package/dist/wallet/tx/targets.d.ts +7 -0
  285. package/dist/wallet/tx/targets.js +122 -0
  286. package/dist/wallet/types.d.ts +249 -0
  287. package/dist/wallet/types.js +1 -0
  288. package/dist/writing_quotes.json +1654 -0
  289. package/package.json +78 -0
@@ -0,0 +1,869 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ import { resolve as resolvePath } from "node:path";
4
+ import { lookupDomain, resolveCanonical } from "@cogcoin/indexer/queries";
5
+ import { attachOrStartManagedBitcoindService } from "../../bitcoind/service.js";
6
+ import { createRpcClient } from "../../bitcoind/node.js";
7
+ import { acquireFileLock } from "../fs/lock.js";
8
+ import { resolveWalletRuntimePathsForTesting } from "../runtime.js";
9
+ import { createDefaultWalletSecretProvider, } from "../state/provider.js";
10
+ import { serializeSetCanonical, serializeSetDelegate, serializeSetEndpoint, serializeSetMiner, validateDomainName, } from "../cogop/index.js";
11
+ import { openWalletReadContext } from "../read/index.js";
12
+ import { assertWalletMutationContextReady, buildWalletMutationTransaction, isAlreadyAcceptedError, isBroadcastUnknownError, pauseMiningForWalletMutation, saveWalletStatePreservingUnlock, unlockTemporaryBuilderLocks, updateMutationRecord, } from "./common.js";
13
+ import { confirmYesNo } from "./confirm.js";
14
+ import { getCanonicalIdentitySelector } from "./identity-selector.js";
15
+ import { findPendingMutationByIntent, upsertPendingMutation } from "./journal.js";
16
+ import { normalizeBtcTarget } from "./targets.js";
17
+ function normalizeDomainName(domainName) {
18
+ const normalized = domainName.trim().toLowerCase();
19
+ if (normalized.length === 0) {
20
+ throw new Error("wallet_domain_admin_missing_domain");
21
+ }
22
+ validateDomainName(normalized);
23
+ return normalized;
24
+ }
25
+ function bytesToHex(value) {
26
+ return Buffer.from(value ?? new Uint8Array()).toString("hex");
27
+ }
28
+ function encodeOpReturnScript(payload) {
29
+ if (payload.length <= 75) {
30
+ return Buffer.concat([
31
+ Buffer.from([0x6a, payload.length]),
32
+ Buffer.from(payload),
33
+ ]).toString("hex");
34
+ }
35
+ return Buffer.concat([
36
+ Buffer.from([0x6a, 0x4c, payload.length]),
37
+ Buffer.from(payload),
38
+ ]).toString("hex");
39
+ }
40
+ function satsToBtcNumber(value) {
41
+ return Number(value) / 100_000_000;
42
+ }
43
+ function valueToSats(value) {
44
+ const text = typeof value === "number" ? value.toFixed(8) : value;
45
+ const match = /^(-?)(\d+)(?:\.(\d{0,8}))?$/.exec(text.trim());
46
+ if (match == null) {
47
+ throw new Error(`wallet_domain_admin_invalid_amount_${text}`);
48
+ }
49
+ const sign = match[1] === "-" ? -1n : 1n;
50
+ const whole = BigInt(match[2] ?? "0");
51
+ const fraction = BigInt((match[3] ?? "").padEnd(8, "0"));
52
+ return sign * ((whole * 100000000n) + fraction);
53
+ }
54
+ function createResolvedDomainAdminSenderSummary(sender, selector) {
55
+ return {
56
+ selector,
57
+ localIndex: sender.localIndex,
58
+ scriptPubKeyHex: sender.scriptPubKeyHex,
59
+ address: sender.address,
60
+ };
61
+ }
62
+ function createResolvedDomainAdminTargetSummary(target) {
63
+ return {
64
+ scriptPubKeyHex: target.scriptPubKeyHex,
65
+ address: target.address,
66
+ opaque: target.opaque,
67
+ };
68
+ }
69
+ function createIntentFingerprint(parts) {
70
+ return createHash("sha256")
71
+ .update(parts.map((part) => String(part)).join("\n"))
72
+ .digest("hex");
73
+ }
74
+ function resolveAnchorOutpointForSender(state, sender, errorPrefix) {
75
+ const anchoredDomain = state.domains.find((domain) => domain.currentOwnerLocalIndex === sender.index
76
+ && domain.canonicalChainStatus === "anchored") ?? null;
77
+ if (anchoredDomain?.currentCanonicalAnchorOutpoint === null || anchoredDomain === null) {
78
+ throw new Error(`${errorPrefix}_anchor_outpoint_unavailable`);
79
+ }
80
+ return {
81
+ txid: anchoredDomain.currentCanonicalAnchorOutpoint.txid,
82
+ vout: anchoredDomain.currentCanonicalAnchorOutpoint.vout,
83
+ };
84
+ }
85
+ function resolveAnchoredDomainOperation(context, domainName, errorPrefix, options = {}) {
86
+ assertWalletMutationContextReady(context, errorPrefix);
87
+ const chainDomain = lookupDomain(context.snapshot.state, domainName);
88
+ if (chainDomain === null) {
89
+ throw new Error(`${errorPrefix}_domain_not_found`);
90
+ }
91
+ if (!chainDomain.anchored) {
92
+ throw new Error(`${errorPrefix}_domain_not_anchored`);
93
+ }
94
+ if (options.requireRoot && domainName.includes("-")) {
95
+ throw new Error(`${errorPrefix}_root_domain_required`);
96
+ }
97
+ const ownerHex = Buffer.from(chainDomain.ownerScriptPubKey).toString("hex");
98
+ const ownerIdentity = context.model.identities.find((identity) => identity.scriptPubKeyHex === ownerHex) ?? null;
99
+ if (ownerIdentity === null || ownerIdentity.address === null) {
100
+ throw new Error(`${errorPrefix}_owner_not_locally_controlled`);
101
+ }
102
+ if ((options.rejectReadOnly ?? true) && ownerIdentity.readOnly) {
103
+ throw new Error(`${errorPrefix}_owner_read_only`);
104
+ }
105
+ return {
106
+ readContext: context,
107
+ state: context.localState.state,
108
+ unlockUntilUnixMs: context.localState.unlockUntilUnixMs,
109
+ sender: {
110
+ localIndex: ownerIdentity.index,
111
+ scriptPubKeyHex: ownerIdentity.scriptPubKeyHex,
112
+ address: ownerIdentity.address,
113
+ },
114
+ senderSelector: getCanonicalIdentitySelector(ownerIdentity),
115
+ anchorOutpoint: resolveAnchorOutpointForSender(context.localState.state, ownerIdentity, errorPrefix),
116
+ chainDomain,
117
+ };
118
+ }
119
+ function buildPlanForDomainAdminOperation(options) {
120
+ const fundingUtxos = options.allUtxos.filter((entry) => entry.scriptPubKey === options.state.funding.scriptPubKeyHex
121
+ && entry.confirmations >= 1
122
+ && entry.spendable !== false
123
+ && entry.safe !== false);
124
+ const anchorUtxo = options.allUtxos.find((entry) => entry.txid === options.anchorOutpoint.txid
125
+ && entry.vout === options.anchorOutpoint.vout
126
+ && entry.scriptPubKey === options.sender.scriptPubKeyHex
127
+ && entry.confirmations >= 1
128
+ && entry.spendable !== false
129
+ && entry.safe !== false);
130
+ if (anchorUtxo === undefined) {
131
+ throw new Error(`${options.errorPrefix}_anchor_utxo_missing`);
132
+ }
133
+ return {
134
+ sender: options.sender,
135
+ changeAddress: options.state.funding.address,
136
+ inputs: [
137
+ { txid: anchorUtxo.txid, vout: anchorUtxo.vout },
138
+ ...fundingUtxos.map((entry) => ({ txid: entry.txid, vout: entry.vout })),
139
+ ],
140
+ outputs: [
141
+ { data: Buffer.from(options.opReturnData).toString("hex") },
142
+ { [options.sender.address]: satsToBtcNumber(BigInt(options.state.anchorValueSats)) },
143
+ ],
144
+ changePosition: 2,
145
+ expectedOpReturnScriptHex: encodeOpReturnScript(options.opReturnData),
146
+ expectedAnchorScriptHex: options.sender.scriptPubKeyHex,
147
+ expectedAnchorValueSats: BigInt(options.state.anchorValueSats),
148
+ allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
149
+ errorPrefix: options.errorPrefix,
150
+ };
151
+ }
152
+ function validateFundedDraft(decoded, funded, plan) {
153
+ const inputs = decoded.tx.vin;
154
+ const outputs = decoded.tx.vout;
155
+ if (inputs.length === 0) {
156
+ throw new Error(`${plan.errorPrefix}_missing_sender_input`);
157
+ }
158
+ if (inputs[0]?.prevout?.scriptPubKey?.hex !== plan.sender.scriptPubKeyHex) {
159
+ throw new Error(`${plan.errorPrefix}_sender_input_mismatch`);
160
+ }
161
+ for (let index = 1; index < inputs.length; index += 1) {
162
+ if (inputs[index]?.prevout?.scriptPubKey?.hex !== plan.allowedFundingScriptPubKeyHex) {
163
+ throw new Error(`${plan.errorPrefix}_unexpected_funding_input`);
164
+ }
165
+ }
166
+ if (outputs[0]?.scriptPubKey?.hex !== plan.expectedOpReturnScriptHex) {
167
+ throw new Error(`${plan.errorPrefix}_opreturn_mismatch`);
168
+ }
169
+ if (outputs[1]?.scriptPubKey?.hex !== plan.expectedAnchorScriptHex) {
170
+ throw new Error(`${plan.errorPrefix}_anchor_output_mismatch`);
171
+ }
172
+ if (valueToSats(outputs[1]?.value ?? 0) !== plan.expectedAnchorValueSats) {
173
+ throw new Error(`${plan.errorPrefix}_anchor_value_mismatch`);
174
+ }
175
+ if (funded.changepos === -1) {
176
+ if (outputs.length !== 2) {
177
+ throw new Error(`${plan.errorPrefix}_unexpected_output_count`);
178
+ }
179
+ return;
180
+ }
181
+ if (funded.changepos !== plan.changePosition || outputs.length !== 3) {
182
+ throw new Error(`${plan.errorPrefix}_change_position_mismatch`);
183
+ }
184
+ if (outputs[funded.changepos]?.scriptPubKey?.hex !== plan.allowedFundingScriptPubKeyHex) {
185
+ throw new Error(`${plan.errorPrefix}_change_output_mismatch`);
186
+ }
187
+ }
188
+ async function buildTransaction(options) {
189
+ return buildWalletMutationTransaction({
190
+ rpc: options.rpc,
191
+ walletName: options.walletName,
192
+ plan: options.plan,
193
+ validateFundedDraft,
194
+ finalizeErrorCode: `${options.plan.errorPrefix}_finalize_failed`,
195
+ mempoolRejectPrefix: `${options.plan.errorPrefix}_mempool_rejected`,
196
+ });
197
+ }
198
+ function createDraftMutation(options) {
199
+ if (options.existing !== null && options.existing !== undefined) {
200
+ return {
201
+ ...options.existing,
202
+ kind: options.kind,
203
+ domainName: options.domainName,
204
+ senderScriptPubKeyHex: options.sender.scriptPubKeyHex,
205
+ senderLocalIndex: options.sender.localIndex,
206
+ recipientScriptPubKeyHex: options.recipientScriptPubKeyHex ?? null,
207
+ endpointValueHex: options.endpointValueHex ?? null,
208
+ status: "draft",
209
+ lastUpdatedAtUnixMs: options.nowUnixMs,
210
+ attemptedTxid: null,
211
+ attemptedWtxid: null,
212
+ temporaryBuilderLockedOutpoints: [],
213
+ };
214
+ }
215
+ return {
216
+ mutationId: randomBytes(12).toString("hex"),
217
+ kind: options.kind,
218
+ domainName: options.domainName,
219
+ parentDomainName: null,
220
+ senderScriptPubKeyHex: options.sender.scriptPubKeyHex,
221
+ senderLocalIndex: options.sender.localIndex,
222
+ recipientScriptPubKeyHex: options.recipientScriptPubKeyHex ?? null,
223
+ endpointValueHex: options.endpointValueHex ?? null,
224
+ intentFingerprintHex: options.intentFingerprintHex,
225
+ status: "draft",
226
+ createdAtUnixMs: options.nowUnixMs,
227
+ lastUpdatedAtUnixMs: options.nowUnixMs,
228
+ attemptedTxid: null,
229
+ attemptedWtxid: null,
230
+ temporaryBuilderLockedOutpoints: [],
231
+ };
232
+ }
233
+ async function saveUpdatedMutationState(options) {
234
+ const nextState = {
235
+ ...options.state,
236
+ stateRevision: options.state.stateRevision + 1,
237
+ lastWrittenAtUnixMs: options.nowUnixMs,
238
+ };
239
+ await saveWalletStatePreservingUnlock({
240
+ state: nextState,
241
+ provider: options.provider,
242
+ unlockUntilUnixMs: options.unlockUntilUnixMs,
243
+ nowUnixMs: options.nowUnixMs,
244
+ paths: options.paths,
245
+ });
246
+ return nextState;
247
+ }
248
+ function mutationConfirmedOnChain(mutation, context) {
249
+ if (context.snapshot === null) {
250
+ return false;
251
+ }
252
+ const chainDomain = lookupDomain(context.snapshot.state, mutation.domainName);
253
+ if (chainDomain === null || !chainDomain.anchored) {
254
+ return false;
255
+ }
256
+ const ownerHex = Buffer.from(chainDomain.ownerScriptPubKey).toString("hex");
257
+ if (ownerHex !== mutation.senderScriptPubKeyHex) {
258
+ return false;
259
+ }
260
+ if (mutation.kind === "endpoint") {
261
+ return bytesToHex(chainDomain.endpoint) === (mutation.endpointValueHex ?? "");
262
+ }
263
+ if (mutation.kind === "delegate") {
264
+ return bytesToHex(chainDomain.delegate) === (mutation.recipientScriptPubKeyHex ?? "");
265
+ }
266
+ if (mutation.kind === "miner") {
267
+ return bytesToHex(chainDomain.miner) === (mutation.recipientScriptPubKeyHex ?? "");
268
+ }
269
+ if (chainDomain.domainId === null) {
270
+ return false;
271
+ }
272
+ return resolveCanonical(context.snapshot.state, Buffer.from(mutation.senderScriptPubKeyHex, "hex")) === chainDomain.domainId;
273
+ }
274
+ function mutationNeedsRepair(mutation, context) {
275
+ if (context.snapshot === null) {
276
+ return false;
277
+ }
278
+ const chainDomain = lookupDomain(context.snapshot.state, mutation.domainName);
279
+ if (chainDomain === null) {
280
+ return false;
281
+ }
282
+ return !chainDomain.anchored || Buffer.from(chainDomain.ownerScriptPubKey).toString("hex") !== mutation.senderScriptPubKeyHex;
283
+ }
284
+ async function reconcilePendingAdminMutation(options) {
285
+ if (options.mutation.status === "confirmed" || options.mutation.status === "live") {
286
+ return {
287
+ state: options.state,
288
+ mutation: options.mutation,
289
+ resolution: options.mutation.status,
290
+ };
291
+ }
292
+ if (options.mutation.status === "repair-required") {
293
+ return {
294
+ state: options.state,
295
+ mutation: options.mutation,
296
+ resolution: "repair-required",
297
+ };
298
+ }
299
+ if (mutationConfirmedOnChain(options.mutation, options.context)) {
300
+ await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.mutation.temporaryBuilderLockedOutpoints);
301
+ const confirmed = updateMutationRecord(options.mutation, "confirmed", options.nowUnixMs, {
302
+ temporaryBuilderLockedOutpoints: [],
303
+ });
304
+ let nextState = upsertPendingMutation(options.state, confirmed);
305
+ nextState = await saveUpdatedMutationState({
306
+ state: nextState,
307
+ provider: options.provider,
308
+ unlockUntilUnixMs: options.unlockUntilUnixMs,
309
+ nowUnixMs: options.nowUnixMs,
310
+ paths: options.paths,
311
+ });
312
+ return { state: nextState, mutation: confirmed, resolution: "confirmed" };
313
+ }
314
+ if (mutationNeedsRepair(options.mutation, options.context)) {
315
+ await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.mutation.temporaryBuilderLockedOutpoints);
316
+ const repair = updateMutationRecord(options.mutation, "repair-required", options.nowUnixMs, {
317
+ temporaryBuilderLockedOutpoints: [],
318
+ });
319
+ let nextState = upsertPendingMutation(options.state, repair);
320
+ nextState = await saveUpdatedMutationState({
321
+ state: nextState,
322
+ provider: options.provider,
323
+ unlockUntilUnixMs: options.unlockUntilUnixMs,
324
+ nowUnixMs: options.nowUnixMs,
325
+ paths: options.paths,
326
+ });
327
+ return { state: nextState, mutation: repair, resolution: "repair-required" };
328
+ }
329
+ const known = options.mutation.attemptedTxid === null
330
+ ? false
331
+ : await options.rpc.getRawTransaction(options.mutation.attemptedTxid, true).then(() => true).catch(() => false);
332
+ if (known) {
333
+ await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.mutation.temporaryBuilderLockedOutpoints);
334
+ const live = updateMutationRecord(options.mutation, "live", options.nowUnixMs, {
335
+ temporaryBuilderLockedOutpoints: [],
336
+ });
337
+ let nextState = upsertPendingMutation(options.state, live);
338
+ nextState = await saveUpdatedMutationState({
339
+ state: nextState,
340
+ provider: options.provider,
341
+ unlockUntilUnixMs: options.unlockUntilUnixMs,
342
+ nowUnixMs: options.nowUnixMs,
343
+ paths: options.paths,
344
+ });
345
+ return { state: nextState, mutation: live, resolution: "live" };
346
+ }
347
+ if (options.mutation.status === "broadcast-unknown"
348
+ || options.mutation.status === "draft"
349
+ || options.mutation.status === "broadcasting") {
350
+ await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.mutation.temporaryBuilderLockedOutpoints);
351
+ const canceled = updateMutationRecord(options.mutation, "canceled", options.nowUnixMs, {
352
+ temporaryBuilderLockedOutpoints: [],
353
+ });
354
+ let nextState = upsertPendingMutation(options.state, canceled);
355
+ nextState = await saveUpdatedMutationState({
356
+ state: nextState,
357
+ provider: options.provider,
358
+ unlockUntilUnixMs: options.unlockUntilUnixMs,
359
+ nowUnixMs: options.nowUnixMs,
360
+ paths: options.paths,
361
+ });
362
+ return { state: nextState, mutation: canceled, resolution: "not-seen" };
363
+ }
364
+ return {
365
+ state: options.state,
366
+ mutation: options.mutation,
367
+ resolution: "continue",
368
+ };
369
+ }
370
+ async function confirmEndpointMutation(prompter, domainName, payload, options) {
371
+ prompter.writeLine(`${options.clear ? "Clearing" : "Updating"} endpoint for "${domainName}".`);
372
+ prompter.writeLine(`Resolved sender: ${options.sender.selector} (${options.sender.address})`);
373
+ prompter.writeLine(options.clear
374
+ ? "Effect: clear the endpoint payload."
375
+ : `Effect: set the endpoint payload to ${payload.length} bytes.`);
376
+ if (!options.clear) {
377
+ prompter.writeLine(`Payload bytes: ${payload.length}`);
378
+ if (options.sourceKind !== undefined) {
379
+ prompter.writeLine(`Payload source: ${options.sourceKind}`);
380
+ }
381
+ prompter.writeLine("Warning: endpoint data is public in the mempool and on-chain.");
382
+ }
383
+ await confirmYesNo(prompter, options.clear
384
+ ? "This publishes a standalone anchored endpoint clear."
385
+ : "This publishes a standalone anchored endpoint update.", {
386
+ assumeYes: options.assumeYes,
387
+ errorCode: "wallet_domain_endpoint_confirmation_rejected",
388
+ requiresTtyErrorCode: "wallet_domain_endpoint_requires_tty",
389
+ });
390
+ }
391
+ async function confirmTargetMutation(prompter, options) {
392
+ prompter.writeLine(`${options.target === null ? "Clearing" : "Updating"} ${options.kind} for "${options.domainName}".`);
393
+ prompter.writeLine(`Resolved sender: ${options.sender.selector} (${options.sender.address})`);
394
+ if (options.target === null) {
395
+ prompter.writeLine(`Effect: clear the ${options.kind === "delegate" ? "delegate" : "designated miner"} target.`);
396
+ await confirmYesNo(prompter, `This clears the current ${options.kind} target.`, {
397
+ assumeYes: options.assumeYes,
398
+ errorCode: `wallet_domain_${options.kind}_confirmation_rejected`,
399
+ requiresTtyErrorCode: `wallet_domain_${options.kind}_requires_tty`,
400
+ });
401
+ return;
402
+ }
403
+ prompter.writeLine(`Resolved target: ${options.target.address ?? `spk:${options.target.scriptPubKeyHex}`}`);
404
+ prompter.writeLine(`Effect: set the ${options.kind === "delegate" ? "delegate" : "designated miner"} target.`);
405
+ if (options.kind === "miner" && options.target.scriptPubKeyHex === options.sender.scriptPubKeyHex) {
406
+ prompter.writeLine("Warning: setting the designated miner to the current owner is usually redundant.");
407
+ }
408
+ await confirmYesNo(prompter, options.kind === "delegate"
409
+ ? "This changes who may act for the domain as delegate."
410
+ : "This changes who may mine for the domain as designated miner.", {
411
+ assumeYes: options.assumeYes,
412
+ errorCode: `wallet_domain_${options.kind}_confirmation_rejected`,
413
+ requiresTtyErrorCode: `wallet_domain_${options.kind}_requires_tty`,
414
+ });
415
+ }
416
+ async function confirmCanonical(prompter, domainName, sender, assumeYes = false) {
417
+ prompter.writeLine(`Canonicalizing "${domainName}" as the anchored owner.`);
418
+ prompter.writeLine(`Resolved sender: ${sender.selector} (${sender.address})`);
419
+ prompter.writeLine("Effect: canonicalize the current anchored owner.");
420
+ await confirmYesNo(prompter, "This publishes a standalone SET_CANONICAL operation.", {
421
+ assumeYes,
422
+ errorCode: "wallet_domain_canonical_confirmation_rejected",
423
+ requiresTtyErrorCode: "wallet_domain_canonical_requires_tty",
424
+ });
425
+ }
426
+ async function loadEndpointPayload(source) {
427
+ if (source.kind === "text") {
428
+ const value = source.value;
429
+ if (value.length === 0) {
430
+ throw new Error("wallet_domain_endpoint_payload_missing");
431
+ }
432
+ return new TextEncoder().encode(value);
433
+ }
434
+ if (source.kind === "json") {
435
+ const value = source.value.trim();
436
+ if (value.length === 0) {
437
+ throw new Error("wallet_domain_endpoint_payload_missing");
438
+ }
439
+ try {
440
+ JSON.parse(value);
441
+ }
442
+ catch {
443
+ throw new Error("wallet_domain_endpoint_invalid_json");
444
+ }
445
+ return new TextEncoder().encode(value);
446
+ }
447
+ if (source.value.startsWith("hex:")) {
448
+ const hex = source.value.slice(4);
449
+ if (!/^[0-9a-f]+$/.test(hex) || hex.length % 2 !== 0) {
450
+ throw new Error("wallet_domain_endpoint_invalid_bytes");
451
+ }
452
+ if (hex.length === 0) {
453
+ throw new Error("wallet_domain_endpoint_payload_missing");
454
+ }
455
+ return Buffer.from(hex, "hex");
456
+ }
457
+ if (!source.value.startsWith("@")) {
458
+ throw new Error("wallet_domain_endpoint_invalid_bytes");
459
+ }
460
+ const filePath = source.value.slice(1);
461
+ if (filePath.trim() === "") {
462
+ throw new Error("wallet_domain_endpoint_invalid_bytes");
463
+ }
464
+ const payload = await readFile(resolvePath(process.cwd(), filePath));
465
+ if (payload.length === 0) {
466
+ throw new Error("wallet_domain_endpoint_payload_missing");
467
+ }
468
+ return payload;
469
+ }
470
+ async function sendBuiltTransaction(options) {
471
+ let nextState = options.state;
472
+ const broadcasting = updateMutationRecord(options.mutation, "broadcasting", options.nowUnixMs, {
473
+ attemptedTxid: options.built.txid,
474
+ attemptedWtxid: options.built.wtxid,
475
+ temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
476
+ });
477
+ nextState = upsertPendingMutation(nextState, broadcasting);
478
+ nextState = await saveUpdatedMutationState({
479
+ state: nextState,
480
+ provider: options.provider,
481
+ unlockUntilUnixMs: options.unlockUntilUnixMs,
482
+ nowUnixMs: options.nowUnixMs,
483
+ paths: options.paths,
484
+ });
485
+ if (options.snapshotHeight !== null && options.snapshotHeight !== (await options.rpc.getBlockchainInfo()).blocks) {
486
+ await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
487
+ throw new Error(`${options.errorPrefix}_tip_mismatch`);
488
+ }
489
+ try {
490
+ await options.rpc.sendRawTransaction(options.built.rawHex);
491
+ }
492
+ catch (error) {
493
+ if (!isAlreadyAcceptedError(error)) {
494
+ if (isBroadcastUnknownError(error)) {
495
+ const unknown = updateMutationRecord(broadcasting, "broadcast-unknown", options.nowUnixMs, {
496
+ attemptedTxid: options.built.txid,
497
+ attemptedWtxid: options.built.wtxid,
498
+ temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
499
+ });
500
+ nextState = upsertPendingMutation(nextState, unknown);
501
+ nextState = await saveUpdatedMutationState({
502
+ state: nextState,
503
+ provider: options.provider,
504
+ unlockUntilUnixMs: options.unlockUntilUnixMs,
505
+ nowUnixMs: options.nowUnixMs,
506
+ paths: options.paths,
507
+ });
508
+ throw new Error(`${options.errorPrefix}_broadcast_unknown`);
509
+ }
510
+ await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
511
+ const canceled = updateMutationRecord(broadcasting, "canceled", options.nowUnixMs, {
512
+ attemptedTxid: options.built.txid,
513
+ attemptedWtxid: options.built.wtxid,
514
+ temporaryBuilderLockedOutpoints: [],
515
+ });
516
+ nextState = upsertPendingMutation(nextState, canceled);
517
+ nextState = await saveUpdatedMutationState({
518
+ state: nextState,
519
+ provider: options.provider,
520
+ unlockUntilUnixMs: options.unlockUntilUnixMs,
521
+ nowUnixMs: options.nowUnixMs,
522
+ paths: options.paths,
523
+ });
524
+ throw error;
525
+ }
526
+ }
527
+ await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
528
+ const live = updateMutationRecord(broadcasting, "live", options.nowUnixMs, {
529
+ attemptedTxid: options.built.txid,
530
+ attemptedWtxid: options.built.wtxid,
531
+ temporaryBuilderLockedOutpoints: [],
532
+ });
533
+ nextState = upsertPendingMutation(nextState, live);
534
+ nextState = await saveUpdatedMutationState({
535
+ state: nextState,
536
+ provider: options.provider,
537
+ unlockUntilUnixMs: options.unlockUntilUnixMs,
538
+ nowUnixMs: options.nowUnixMs,
539
+ paths: options.paths,
540
+ });
541
+ return { state: nextState, mutation: live };
542
+ }
543
+ async function submitDomainAdminMutation(options) {
544
+ const provider = options.provider ?? createDefaultWalletSecretProvider();
545
+ const nowUnixMs = options.nowUnixMs ?? Date.now();
546
+ const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
547
+ const controlLock = await acquireFileLock(paths.walletControlLockPath, {
548
+ purpose: options.errorPrefix,
549
+ walletRootId: null,
550
+ });
551
+ try {
552
+ const miningPreemption = await pauseMiningForWalletMutation({
553
+ paths,
554
+ reason: options.errorPrefix,
555
+ });
556
+ const readContext = await (options.openReadContext ?? openWalletReadContext)({
557
+ dataDir: options.dataDir,
558
+ databasePath: options.databasePath,
559
+ secretProvider: provider,
560
+ paths,
561
+ });
562
+ try {
563
+ const normalizedDomainName = normalizeDomainName(options.domainName);
564
+ const operation = resolveAnchoredDomainOperation(readContext, normalizedDomainName, options.errorPrefix, { requireRoot: options.requireRoot });
565
+ const resolvedSender = createResolvedDomainAdminSenderSummary(operation.sender, operation.senderSelector);
566
+ const payload = await options.createPayload(operation);
567
+ const intentFingerprintHex = createIntentFingerprint([
568
+ options.kind,
569
+ operation.state.walletRootId,
570
+ ...options.intentParts(operation),
571
+ ]);
572
+ const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
573
+ dataDir: options.dataDir,
574
+ chain: "main",
575
+ startHeight: 0,
576
+ walletRootId: operation.state.walletRootId,
577
+ });
578
+ const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
579
+ const walletName = operation.state.managedCoreWallet.walletName;
580
+ const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
581
+ if (existingMutation !== null) {
582
+ const reconciled = await reconcilePendingAdminMutation({
583
+ state: operation.state,
584
+ mutation: existingMutation,
585
+ provider,
586
+ unlockUntilUnixMs: operation.unlockUntilUnixMs,
587
+ nowUnixMs,
588
+ paths,
589
+ rpc,
590
+ walletName,
591
+ context: readContext,
592
+ });
593
+ if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
594
+ return {
595
+ kind: options.kind,
596
+ domainName: normalizedDomainName,
597
+ txid: reconciled.mutation.attemptedTxid ?? "unknown",
598
+ status: reconciled.resolution,
599
+ reusedExisting: true,
600
+ recipientScriptPubKeyHex: payload.recipientScriptPubKeyHex ?? null,
601
+ endpointValueHex: payload.endpointValueHex ?? null,
602
+ resolved: {
603
+ sender: resolvedSender,
604
+ target: payload.resolvedTarget,
605
+ effect: payload.resolvedEffect,
606
+ },
607
+ };
608
+ }
609
+ if (reconciled.resolution === "repair-required") {
610
+ throw new Error(`${options.errorPrefix}_repair_required`);
611
+ }
612
+ }
613
+ await options.confirm(operation);
614
+ let nextState = upsertPendingMutation(operation.state, createDraftMutation({
615
+ kind: options.kind,
616
+ domainName: normalizedDomainName,
617
+ sender: operation.sender,
618
+ intentFingerprintHex,
619
+ nowUnixMs,
620
+ recipientScriptPubKeyHex: payload.recipientScriptPubKeyHex ?? null,
621
+ endpointValueHex: payload.endpointValueHex ?? null,
622
+ existing: existingMutation,
623
+ }));
624
+ nextState = await saveUpdatedMutationState({
625
+ state: nextState,
626
+ provider,
627
+ unlockUntilUnixMs: operation.unlockUntilUnixMs,
628
+ nowUnixMs,
629
+ paths,
630
+ });
631
+ const built = await buildTransaction({
632
+ rpc,
633
+ walletName,
634
+ plan: buildPlanForDomainAdminOperation({
635
+ state: nextState,
636
+ allUtxos: await rpc.listUnspent(walletName, 1),
637
+ sender: operation.sender,
638
+ anchorOutpoint: operation.anchorOutpoint,
639
+ opReturnData: payload.opReturnData,
640
+ errorPrefix: options.errorPrefix,
641
+ }),
642
+ });
643
+ const final = await sendBuiltTransaction({
644
+ rpc,
645
+ walletName,
646
+ snapshotHeight: readContext.snapshot?.tip?.height ?? null,
647
+ built,
648
+ mutation: nextState.pendingMutations.find((mutation) => mutation.intentFingerprintHex === intentFingerprintHex),
649
+ state: nextState,
650
+ provider,
651
+ unlockUntilUnixMs: operation.unlockUntilUnixMs,
652
+ nowUnixMs,
653
+ paths,
654
+ errorPrefix: options.errorPrefix,
655
+ });
656
+ return {
657
+ kind: options.kind,
658
+ domainName: normalizedDomainName,
659
+ txid: final.mutation.attemptedTxid ?? built.txid,
660
+ status: "live",
661
+ reusedExisting: false,
662
+ recipientScriptPubKeyHex: payload.recipientScriptPubKeyHex ?? null,
663
+ endpointValueHex: payload.endpointValueHex ?? null,
664
+ resolved: {
665
+ sender: resolvedSender,
666
+ target: payload.resolvedTarget,
667
+ effect: payload.resolvedEffect,
668
+ },
669
+ };
670
+ }
671
+ finally {
672
+ await readContext.close();
673
+ await miningPreemption.release();
674
+ }
675
+ }
676
+ finally {
677
+ await controlLock.release();
678
+ }
679
+ }
680
+ export async function setDomainEndpoint(options) {
681
+ const payloadBytes = await loadEndpointPayload(options.source);
682
+ return submitDomainAdminMutation({
683
+ ...options,
684
+ kind: "endpoint",
685
+ errorPrefix: "wallet_domain_endpoint",
686
+ intentParts(operation) {
687
+ return [operation.chainDomain.name, Buffer.from(payloadBytes).toString("hex")];
688
+ },
689
+ async createPayload(operation) {
690
+ return {
691
+ opReturnData: serializeSetEndpoint(operation.chainDomain.domainId, payloadBytes).opReturnData,
692
+ endpointValueHex: Buffer.from(payloadBytes).toString("hex"),
693
+ resolvedTarget: null,
694
+ resolvedEffect: {
695
+ kind: "endpoint-set",
696
+ byteLength: payloadBytes.length,
697
+ },
698
+ };
699
+ },
700
+ async confirm(operation) {
701
+ await confirmEndpointMutation(options.prompter, operation.chainDomain.name, payloadBytes, {
702
+ clear: false,
703
+ sender: createResolvedDomainAdminSenderSummary(operation.sender, operation.senderSelector),
704
+ sourceKind: options.source.kind,
705
+ assumeYes: options.assumeYes,
706
+ });
707
+ },
708
+ });
709
+ }
710
+ export async function clearDomainEndpoint(options) {
711
+ return submitDomainAdminMutation({
712
+ ...options,
713
+ kind: "endpoint",
714
+ errorPrefix: "wallet_domain_endpoint",
715
+ intentParts(operation) {
716
+ return [operation.chainDomain.name, "clear"];
717
+ },
718
+ async createPayload(operation) {
719
+ return {
720
+ opReturnData: serializeSetEndpoint(operation.chainDomain.domainId).opReturnData,
721
+ endpointValueHex: "",
722
+ resolvedTarget: null,
723
+ resolvedEffect: { kind: "endpoint-clear" },
724
+ };
725
+ },
726
+ async confirm(operation) {
727
+ await confirmEndpointMutation(options.prompter, operation.chainDomain.name, new Uint8Array(), {
728
+ clear: true,
729
+ sender: createResolvedDomainAdminSenderSummary(operation.sender, operation.senderSelector),
730
+ assumeYes: options.assumeYes,
731
+ });
732
+ },
733
+ });
734
+ }
735
+ export async function setDomainDelegate(options) {
736
+ const target = normalizeBtcTarget(options.target);
737
+ return submitDomainAdminMutation({
738
+ ...options,
739
+ kind: "delegate",
740
+ errorPrefix: "wallet_domain_delegate",
741
+ intentParts(operation) {
742
+ return [operation.chainDomain.name, target.scriptPubKeyHex];
743
+ },
744
+ async createPayload(operation) {
745
+ if (target.scriptPubKeyHex === operation.sender.scriptPubKeyHex) {
746
+ throw new Error("wallet_domain_delegate_self_target");
747
+ }
748
+ return {
749
+ opReturnData: serializeSetDelegate(operation.chainDomain.domainId, Buffer.from(target.scriptPubKeyHex, "hex")).opReturnData,
750
+ recipientScriptPubKeyHex: target.scriptPubKeyHex,
751
+ resolvedTarget: createResolvedDomainAdminTargetSummary(target),
752
+ resolvedEffect: { kind: "delegate-set" },
753
+ };
754
+ },
755
+ async confirm(operation) {
756
+ await confirmTargetMutation(options.prompter, {
757
+ kind: "delegate",
758
+ domainName: operation.chainDomain.name,
759
+ target,
760
+ sender: createResolvedDomainAdminSenderSummary(operation.sender, operation.senderSelector),
761
+ assumeYes: options.assumeYes,
762
+ });
763
+ },
764
+ });
765
+ }
766
+ export async function clearDomainDelegate(options) {
767
+ return submitDomainAdminMutation({
768
+ ...options,
769
+ kind: "delegate",
770
+ errorPrefix: "wallet_domain_delegate",
771
+ intentParts(operation) {
772
+ return [operation.chainDomain.name, "clear"];
773
+ },
774
+ async createPayload(operation) {
775
+ return {
776
+ opReturnData: serializeSetDelegate(operation.chainDomain.domainId).opReturnData,
777
+ recipientScriptPubKeyHex: null,
778
+ resolvedTarget: null,
779
+ resolvedEffect: { kind: "delegate-clear" },
780
+ };
781
+ },
782
+ async confirm(operation) {
783
+ await confirmTargetMutation(options.prompter, {
784
+ kind: "delegate",
785
+ domainName: operation.chainDomain.name,
786
+ target: null,
787
+ sender: createResolvedDomainAdminSenderSummary(operation.sender, operation.senderSelector),
788
+ assumeYes: options.assumeYes,
789
+ });
790
+ },
791
+ });
792
+ }
793
+ export async function setDomainMiner(options) {
794
+ const target = normalizeBtcTarget(options.target);
795
+ return submitDomainAdminMutation({
796
+ ...options,
797
+ kind: "miner",
798
+ errorPrefix: "wallet_domain_miner",
799
+ requireRoot: true,
800
+ intentParts(operation) {
801
+ return [operation.chainDomain.name, target.scriptPubKeyHex];
802
+ },
803
+ async createPayload(operation) {
804
+ return {
805
+ opReturnData: serializeSetMiner(operation.chainDomain.domainId, Buffer.from(target.scriptPubKeyHex, "hex")).opReturnData,
806
+ recipientScriptPubKeyHex: target.scriptPubKeyHex,
807
+ resolvedTarget: createResolvedDomainAdminTargetSummary(target),
808
+ resolvedEffect: { kind: "miner-set" },
809
+ };
810
+ },
811
+ async confirm(operation) {
812
+ await confirmTargetMutation(options.prompter, {
813
+ kind: "miner",
814
+ domainName: operation.chainDomain.name,
815
+ target,
816
+ sender: createResolvedDomainAdminSenderSummary(operation.sender, operation.senderSelector),
817
+ assumeYes: options.assumeYes,
818
+ });
819
+ },
820
+ });
821
+ }
822
+ export async function clearDomainMiner(options) {
823
+ return submitDomainAdminMutation({
824
+ ...options,
825
+ kind: "miner",
826
+ errorPrefix: "wallet_domain_miner",
827
+ requireRoot: true,
828
+ intentParts(operation) {
829
+ return [operation.chainDomain.name, "clear"];
830
+ },
831
+ async createPayload(operation) {
832
+ return {
833
+ opReturnData: serializeSetMiner(operation.chainDomain.domainId).opReturnData,
834
+ recipientScriptPubKeyHex: null,
835
+ resolvedTarget: null,
836
+ resolvedEffect: { kind: "miner-clear" },
837
+ };
838
+ },
839
+ async confirm(operation) {
840
+ await confirmTargetMutation(options.prompter, {
841
+ kind: "miner",
842
+ domainName: operation.chainDomain.name,
843
+ target: null,
844
+ sender: createResolvedDomainAdminSenderSummary(operation.sender, operation.senderSelector),
845
+ assumeYes: options.assumeYes,
846
+ });
847
+ },
848
+ });
849
+ }
850
+ export async function setDomainCanonical(options) {
851
+ return submitDomainAdminMutation({
852
+ ...options,
853
+ kind: "canonical",
854
+ errorPrefix: "wallet_domain_canonical",
855
+ intentParts(operation) {
856
+ return [operation.chainDomain.name, operation.sender.scriptPubKeyHex];
857
+ },
858
+ async createPayload(operation) {
859
+ return {
860
+ opReturnData: serializeSetCanonical(operation.chainDomain.domainId).opReturnData,
861
+ resolvedTarget: null,
862
+ resolvedEffect: { kind: "canonicalize-owner" },
863
+ };
864
+ },
865
+ async confirm(operation) {
866
+ await confirmCanonical(options.prompter, operation.chainDomain.name, createResolvedDomainAdminSenderSummary(operation.sender, operation.senderSelector), options.assumeYes);
867
+ },
868
+ });
869
+ }