@cogcoin/client 0.5.15 → 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.
- package/README.md +80 -25
- package/dist/app-paths.d.ts +5 -6
- package/dist/app-paths.js +8 -16
- package/dist/art/balance.txt +10 -0
- package/dist/art/welcome.txt +16 -0
- package/dist/bitcoind/bootstrap/controller.d.ts +1 -0
- package/dist/bitcoind/bootstrap/controller.js +53 -1
- package/dist/bitcoind/client/follow-block-times.d.ts +1 -0
- package/dist/bitcoind/client/follow-block-times.js +1 -1
- package/dist/bitcoind/client/internal-types.d.ts +7 -3
- package/dist/bitcoind/client/managed-client.d.ts +4 -2
- package/dist/bitcoind/client/managed-client.js +14 -0
- package/dist/bitcoind/client/sync-engine.js +72 -11
- package/dist/bitcoind/hash-order.d.ts +4 -0
- package/dist/bitcoind/hash-order.js +13 -0
- package/dist/bitcoind/indexer-daemon-main.js +11 -3
- package/dist/bitcoind/normalize.js +3 -2
- package/dist/bitcoind/processing-start-height.d.ts +5 -0
- package/dist/bitcoind/processing-start-height.js +7 -0
- package/dist/bitcoind/progress/constants.d.ts +4 -0
- package/dist/bitcoind/progress/constants.js +4 -0
- package/dist/bitcoind/progress/controller.d.ts +2 -1
- package/dist/bitcoind/progress/controller.js +3 -3
- package/dist/bitcoind/progress/follow-scene.d.ts +6 -2
- package/dist/bitcoind/progress/follow-scene.js +29 -6
- package/dist/bitcoind/progress/formatting.d.ts +1 -0
- package/dist/bitcoind/progress/formatting.js +6 -0
- package/dist/bitcoind/progress/train-scene.js +37 -18
- package/dist/bitcoind/progress/tty-renderer.d.ts +6 -1
- package/dist/bitcoind/progress/tty-renderer.js +8 -4
- package/dist/bitcoind/rpc.d.ts +2 -1
- package/dist/bitcoind/rpc.js +3 -0
- package/dist/bitcoind/types.d.ts +6 -0
- package/dist/bytes.d.ts +1 -0
- package/dist/bytes.js +3 -0
- package/dist/cli/art.d.ts +2 -0
- package/dist/cli/art.js +37 -0
- package/dist/cli/commands/client-admin.d.ts +2 -0
- package/dist/cli/commands/client-admin.js +91 -0
- package/dist/cli/commands/follow.js +0 -2
- package/dist/cli/commands/mining-admin.js +6 -47
- package/dist/cli/commands/mining-read.js +11 -50
- package/dist/cli/commands/mining-runtime.js +38 -3
- package/dist/cli/commands/service-runtime.js +0 -2
- package/dist/cli/commands/status.js +8 -2
- package/dist/cli/commands/sync.js +51 -4
- package/dist/cli/commands/wallet-admin.js +142 -136
- package/dist/cli/commands/wallet-mutation.js +91 -79
- package/dist/cli/commands/wallet-read.js +15 -18
- package/dist/cli/context.js +4 -14
- package/dist/cli/mining-format.d.ts +0 -1
- package/dist/cli/mining-format.js +5 -37
- package/dist/cli/mining-json.d.ts +0 -18
- package/dist/cli/mining-json.js +0 -35
- package/dist/cli/mutation-command-groups.d.ts +1 -2
- package/dist/cli/mutation-command-groups.js +0 -5
- package/dist/cli/mutation-json.d.ts +24 -145
- package/dist/cli/mutation-json.js +30 -136
- package/dist/cli/mutation-resolved-json.d.ts +0 -7
- package/dist/cli/mutation-resolved-json.js +4 -10
- package/dist/cli/mutation-success.d.ts +2 -0
- package/dist/cli/mutation-success.js +11 -1
- package/dist/cli/mutation-text-format.js +1 -3
- package/dist/cli/output.d.ts +1 -1
- package/dist/cli/output.js +254 -231
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +93 -122
- package/dist/cli/preview-json.d.ts +17 -120
- package/dist/cli/preview-json.js +14 -97
- package/dist/cli/prompt.js +8 -13
- package/dist/cli/read-json.d.ts +15 -37
- package/dist/cli/read-json.js +44 -140
- package/dist/cli/runner.js +10 -13
- package/dist/cli/types.d.ts +8 -17
- package/dist/cli/types.js +0 -2
- package/dist/cli/wallet-format.d.ts +1 -0
- package/dist/cli/wallet-format.js +205 -144
- package/dist/cli/workflow-hints.d.ts +3 -3
- package/dist/cli/workflow-hints.js +11 -8
- package/dist/client/default-client.d.ts +3 -1
- package/dist/client/default-client.js +45 -2
- package/dist/client/factory.js +1 -1
- package/dist/client/initialization.js +23 -0
- package/dist/client/persistence.js +5 -5
- package/dist/client/store-adapter.js +1 -0
- package/dist/sqlite/checkpoints.d.ts +1 -0
- package/dist/sqlite/checkpoints.js +7 -0
- package/dist/sqlite/store.js +14 -1
- package/dist/types.d.ts +1 -0
- package/dist/wallet/coin-control.d.ts +41 -12
- package/dist/wallet/coin-control.js +100 -428
- package/dist/wallet/descriptor-normalization.d.ts +1 -3
- package/dist/wallet/descriptor-normalization.js +0 -16
- package/dist/wallet/lifecycle.d.ts +7 -99
- package/dist/wallet/lifecycle.js +513 -968
- package/dist/wallet/managed-core-wallet.d.ts +13 -0
- package/dist/wallet/managed-core-wallet.js +20 -0
- package/dist/wallet/mining/constants.d.ts +5 -12
- package/dist/wallet/mining/constants.js +5 -12
- package/dist/wallet/mining/control.d.ts +1 -13
- package/dist/wallet/mining/control.js +45 -349
- package/dist/wallet/mining/index.d.ts +3 -4
- package/dist/wallet/mining/index.js +1 -2
- package/dist/wallet/mining/runner.d.ts +116 -13
- package/dist/wallet/mining/runner.js +885 -501
- package/dist/wallet/mining/runtime-artifacts.js +23 -3
- package/dist/wallet/mining/sentence-protocol.d.ts +44 -0
- package/dist/wallet/mining/sentence-protocol.js +123 -0
- package/dist/wallet/mining/sentences.d.ts +4 -8
- package/dist/wallet/mining/sentences.js +3 -52
- package/dist/wallet/mining/state.d.ts +11 -6
- package/dist/wallet/mining/state.js +7 -6
- package/dist/wallet/mining/types.d.ts +2 -30
- package/dist/wallet/mining/visualizer.d.ts +31 -3
- package/dist/wallet/mining/visualizer.js +135 -13
- package/dist/wallet/read/context.d.ts +0 -2
- package/dist/wallet/read/context.js +119 -140
- package/dist/wallet/read/filter.js +2 -11
- package/dist/wallet/read/index.d.ts +1 -1
- package/dist/wallet/read/project.js +24 -77
- package/dist/wallet/read/types.d.ts +10 -25
- package/dist/wallet/reset.d.ts +0 -1
- package/dist/wallet/reset.js +60 -138
- package/dist/wallet/root-resolution.d.ts +1 -5
- package/dist/wallet/root-resolution.js +0 -18
- package/dist/wallet/runtime.d.ts +0 -6
- package/dist/wallet/runtime.js +0 -8
- package/dist/wallet/state/client-password-agent.js +208 -0
- package/dist/wallet/state/client-password.d.ts +65 -0
- package/dist/wallet/state/client-password.js +952 -0
- package/dist/wallet/state/crypto.d.ts +1 -20
- package/dist/wallet/state/crypto.js +0 -63
- package/dist/wallet/state/provider.d.ts +23 -11
- package/dist/wallet/state/provider.js +248 -290
- package/dist/wallet/state/storage.d.ts +2 -2
- package/dist/wallet/state/storage.js +48 -16
- package/dist/wallet/tx/anchor.d.ts +3 -28
- package/dist/wallet/tx/anchor.js +349 -1250
- package/dist/wallet/tx/bitcoin-transfer.d.ts +35 -0
- package/dist/wallet/tx/bitcoin-transfer.js +200 -0
- package/dist/wallet/tx/cog.d.ts +5 -1
- package/dist/wallet/tx/cog.js +149 -185
- package/dist/wallet/tx/common.d.ts +61 -8
- package/dist/wallet/tx/common.js +266 -146
- package/dist/wallet/tx/domain-admin.d.ts +3 -1
- package/dist/wallet/tx/domain-admin.js +61 -99
- package/dist/wallet/tx/domain-market.d.ts +5 -1
- package/dist/wallet/tx/domain-market.js +221 -228
- package/dist/wallet/tx/field.d.ts +4 -10
- package/dist/wallet/tx/field.js +83 -924
- package/dist/wallet/tx/identity-selector.d.ts +9 -3
- package/dist/wallet/tx/identity-selector.js +17 -35
- package/dist/wallet/tx/index.d.ts +3 -1
- package/dist/wallet/tx/index.js +2 -1
- package/dist/wallet/tx/register.d.ts +3 -1
- package/dist/wallet/tx/register.js +62 -220
- package/dist/wallet/tx/reputation.d.ts +3 -1
- package/dist/wallet/tx/reputation.js +58 -95
- package/dist/wallet/types.d.ts +8 -122
- package/package.json +5 -5
- package/dist/wallet/archive.d.ts +0 -4
- package/dist/wallet/archive.js +0 -41
- package/dist/wallet/mining/hook-protocol.d.ts +0 -47
- package/dist/wallet/mining/hook-protocol.js +0 -161
- package/dist/wallet/mining/hook-runner.js +0 -52
- package/dist/wallet/mining/hooks.d.ts +0 -38
- package/dist/wallet/mining/hooks.js +0 -520
- package/dist/wallet/state/explicit-lock.d.ts +0 -4
- package/dist/wallet/state/explicit-lock.js +0 -19
- package/dist/wallet/state/session.d.ts +0 -12
- package/dist/wallet/state/session.js +0 -23
- /package/dist/wallet/{mining/hook-runner.d.ts → state/client-password-agent.d.ts} +0 -0
|
@@ -4,14 +4,47 @@ import { attachOrStartManagedBitcoindService } from "../../bitcoind/service.js";
|
|
|
4
4
|
import { createRpcClient } from "../../bitcoind/node.js";
|
|
5
5
|
import { acquireFileLock } from "../fs/lock.js";
|
|
6
6
|
import { resolveWalletRuntimePathsForTesting } from "../runtime.js";
|
|
7
|
+
import { reconcilePersistentPolicyLocks as reconcileWalletCoinControlLocks } from "../coin-control.js";
|
|
7
8
|
import { createDefaultWalletSecretProvider, } from "../state/provider.js";
|
|
8
9
|
import { serializeDomainBuy, serializeDomainSell, serializeDomainTransfer, validateDomainName, } from "../cogop/index.js";
|
|
9
10
|
import { openWalletReadContext } from "../read/index.js";
|
|
10
|
-
import { assertFixedInputPrefixMatches, assertFundingInputsAfterFixedPrefix, assertWalletMutationContextReady, buildWalletMutationTransactionWithReserveFallback, getDecodedInputScriptPubKeyHex, isAlreadyAcceptedError, isBroadcastUnknownError, outpointKey, pauseMiningForWalletMutation, saveWalletStatePreservingUnlock, unlockTemporaryBuilderLocks, updateMutationRecord, } from "./common.js";
|
|
11
|
+
import { assertFixedInputPrefixMatches, assertFundingInputsAfterFixedPrefix, assertWalletMutationContextReady, buildWalletMutationTransactionWithReserveFallback, createBuiltWalletMutationFeeSummary, createFundingMutationSender, createWalletMutationFeeMetadata, getDecodedInputScriptPubKeyHex, isLocalWalletScript, isAlreadyAcceptedError, isBroadcastUnknownError, mergeFixedWalletInputs, outpointKey, pauseMiningForWalletMutation, resolvePendingMutationReuseDecision, resolveWalletMutationFeeSelection, saveWalletStatePreservingUnlock, unlockTemporaryBuilderLocks, updateMutationRecord, } from "./common.js";
|
|
11
12
|
import { confirmTypedAcknowledgement, confirmYesNo } from "./confirm.js";
|
|
12
13
|
import { getCanonicalIdentitySelector, resolveIdentityBySelector, } from "./identity-selector.js";
|
|
13
14
|
import { findPendingMutationByIntent, upsertPendingMutation } from "./journal.js";
|
|
14
15
|
import { normalizeBtcTarget } from "./targets.js";
|
|
16
|
+
async function prepareDomainMarketBuildState(options) {
|
|
17
|
+
if (!options.preflightCoinControl) {
|
|
18
|
+
return {
|
|
19
|
+
state: options.state,
|
|
20
|
+
allUtxos: (await options.rpc.listUnspent(options.walletName, 1)).slice(),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const reconciled = await reconcileWalletCoinControlLocks({
|
|
24
|
+
rpc: options.rpc,
|
|
25
|
+
walletName: options.walletName,
|
|
26
|
+
state: options.state,
|
|
27
|
+
});
|
|
28
|
+
const nextState = reconciled.changed
|
|
29
|
+
? {
|
|
30
|
+
...reconciled.state,
|
|
31
|
+
stateRevision: reconciled.state.stateRevision + 1,
|
|
32
|
+
lastWrittenAtUnixMs: options.nowUnixMs,
|
|
33
|
+
}
|
|
34
|
+
: reconciled.state;
|
|
35
|
+
if (reconciled.changed) {
|
|
36
|
+
await saveWalletStatePreservingUnlock({
|
|
37
|
+
state: nextState,
|
|
38
|
+
provider: options.provider,
|
|
39
|
+
nowUnixMs: options.nowUnixMs,
|
|
40
|
+
paths: options.paths,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
state: nextState,
|
|
45
|
+
allUtxos: (await options.rpc.listUnspent(options.walletName, 1)).slice(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
15
48
|
function normalizeDomainName(domainName) {
|
|
16
49
|
const normalized = domainName.trim().toLowerCase();
|
|
17
50
|
if (normalized.length === 0) {
|
|
@@ -61,12 +94,6 @@ function createIntentFingerprint(parts) {
|
|
|
61
94
|
.update(parts.join("\n"))
|
|
62
95
|
.digest("hex");
|
|
63
96
|
}
|
|
64
|
-
function replaceAssignedDomainNames(identity, nextAssignedDomainNames) {
|
|
65
|
-
return {
|
|
66
|
-
...identity,
|
|
67
|
-
assignedDomainNames: nextAssignedDomainNames.slice().sort((left, right) => left.localeCompare(right)),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
97
|
function reserveTransferredDomainRecord(options) {
|
|
71
98
|
const existing = options.state.domains.find((domain) => domain.name === options.domainName) ?? null;
|
|
72
99
|
const domains = options.state.domains.some((domain) => domain.name === options.domainName)
|
|
@@ -78,9 +105,7 @@ function reserveTransferredDomainRecord(options) {
|
|
|
78
105
|
...domain,
|
|
79
106
|
domainId: options.domainId ?? domain.domainId,
|
|
80
107
|
currentOwnerScriptPubKeyHex: options.currentOwnerScriptPubKeyHex,
|
|
81
|
-
currentOwnerLocalIndex: options.currentOwnerLocalIndex,
|
|
82
108
|
canonicalChainStatus: "registered-unanchored",
|
|
83
|
-
currentCanonicalAnchorOutpoint: null,
|
|
84
109
|
birthTime: domain.birthTime ?? Math.floor(options.nowUnixMs / 1000),
|
|
85
110
|
};
|
|
86
111
|
})
|
|
@@ -89,27 +114,15 @@ function reserveTransferredDomainRecord(options) {
|
|
|
89
114
|
{
|
|
90
115
|
name: options.domainName,
|
|
91
116
|
domainId: options.domainId,
|
|
92
|
-
dedicatedIndex: null,
|
|
93
117
|
currentOwnerScriptPubKeyHex: options.currentOwnerScriptPubKeyHex,
|
|
94
|
-
currentOwnerLocalIndex: options.currentOwnerLocalIndex,
|
|
95
118
|
canonicalChainStatus: "registered-unanchored",
|
|
96
|
-
localAnchorIntent: "none",
|
|
97
|
-
currentCanonicalAnchorOutpoint: null,
|
|
98
119
|
foundingMessageText: existing?.foundingMessageText ?? null,
|
|
99
120
|
birthTime: Math.floor(options.nowUnixMs / 1000),
|
|
100
121
|
},
|
|
101
122
|
];
|
|
102
|
-
const identities = options.state.identities.map((identity) => {
|
|
103
|
-
const filtered = identity.assignedDomainNames.filter((domainName) => domainName !== options.domainName);
|
|
104
|
-
if (identity.index === options.currentOwnerLocalIndex) {
|
|
105
|
-
return replaceAssignedDomainNames(identity, [...filtered, options.domainName]);
|
|
106
|
-
}
|
|
107
|
-
return replaceAssignedDomainNames(identity, filtered);
|
|
108
|
-
});
|
|
109
123
|
return {
|
|
110
124
|
...options.state,
|
|
111
125
|
domains,
|
|
112
|
-
identities,
|
|
113
126
|
};
|
|
114
127
|
}
|
|
115
128
|
function createResolvedDomainMarketSenderSummary(sender, selector) {
|
|
@@ -145,21 +158,6 @@ function createSellEconomicEffectSummary(listedPriceCogtoshi) {
|
|
|
145
158
|
listedPriceCogtoshi: listedPriceCogtoshi.toString(),
|
|
146
159
|
};
|
|
147
160
|
}
|
|
148
|
-
function resolveAnchorOutpointForSender(state, sender, errorPrefix) {
|
|
149
|
-
const anchoredDomains = state.domains.filter((domain) => domain.currentOwnerLocalIndex === sender.index
|
|
150
|
-
&& domain.canonicalChainStatus === "anchored");
|
|
151
|
-
if (anchoredDomains.length === 0) {
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
const anchoredDomain = anchoredDomains[0];
|
|
155
|
-
if (anchoredDomain.currentCanonicalAnchorOutpoint === null) {
|
|
156
|
-
throw new Error(`${errorPrefix}_anchor_outpoint_unavailable`);
|
|
157
|
-
}
|
|
158
|
-
return {
|
|
159
|
-
txid: anchoredDomain.currentCanonicalAnchorOutpoint.txid,
|
|
160
|
-
vout: anchoredDomain.currentCanonicalAnchorOutpoint.vout,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
161
|
function resolveOwnedDomainOperation(context, domainName, errorPrefix) {
|
|
164
162
|
assertWalletMutationContextReady(context, errorPrefix);
|
|
165
163
|
const chainDomain = lookupDomain(context.snapshot.state, domainName);
|
|
@@ -170,24 +168,14 @@ function resolveOwnedDomainOperation(context, domainName, errorPrefix) {
|
|
|
170
168
|
throw new Error(`${errorPrefix}_domain_anchored`);
|
|
171
169
|
}
|
|
172
170
|
const ownerHex = Buffer.from(chainDomain.ownerScriptPubKey).toString("hex");
|
|
173
|
-
|
|
174
|
-
if (senderIdentity === null || senderIdentity.address === null) {
|
|
171
|
+
if (ownerHex !== context.localState.state.funding.scriptPubKeyHex || context.model.walletAddress == null) {
|
|
175
172
|
throw new Error(`${errorPrefix}_owner_not_locally_controlled`);
|
|
176
173
|
}
|
|
177
|
-
if (senderIdentity.readOnly) {
|
|
178
|
-
throw new Error(`${errorPrefix}_owner_read_only`);
|
|
179
|
-
}
|
|
180
174
|
return {
|
|
181
175
|
readContext: context,
|
|
182
176
|
state: context.localState.state,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
localIndex: senderIdentity.index,
|
|
186
|
-
scriptPubKeyHex: senderIdentity.scriptPubKeyHex,
|
|
187
|
-
address: senderIdentity.address,
|
|
188
|
-
},
|
|
189
|
-
senderSelector: getCanonicalIdentitySelector(senderIdentity),
|
|
190
|
-
anchorOutpoint: resolveAnchorOutpointForSender(context.localState.state, senderIdentity, errorPrefix),
|
|
177
|
+
sender: createFundingMutationSender(context.localState.state),
|
|
178
|
+
senderSelector: context.model.walletAddress,
|
|
191
179
|
chainDomain,
|
|
192
180
|
};
|
|
193
181
|
}
|
|
@@ -204,41 +192,24 @@ function resolveBuyOperation(context, domainName, fromIdentity = null) {
|
|
|
204
192
|
if (listing === null) {
|
|
205
193
|
throw new Error("wallet_buy_domain_not_listed");
|
|
206
194
|
}
|
|
207
|
-
|
|
208
|
-
? context.model.fundingIdentity
|
|
209
|
-
: resolveIdentityBySelector(context, fromIdentity, "wallet_buy");
|
|
210
|
-
if (selectedIdentity === null) {
|
|
195
|
+
if (context.model.walletAddress === null) {
|
|
211
196
|
throw new Error("wallet_buy_funding_identity_unavailable");
|
|
212
197
|
}
|
|
213
|
-
if (selectedIdentity.address === null) {
|
|
214
|
-
throw new Error(fromIdentity === null
|
|
215
|
-
? "wallet_buy_funding_identity_unavailable"
|
|
216
|
-
: "wallet_buy_sender_address_unavailable");
|
|
217
|
-
}
|
|
218
|
-
if (selectedIdentity.readOnly) {
|
|
219
|
-
throw new Error("wallet_buy_sender_read_only");
|
|
220
|
-
}
|
|
221
198
|
const ownerHex = Buffer.from(chainDomain.ownerScriptPubKey).toString("hex");
|
|
222
|
-
if (ownerHex ===
|
|
199
|
+
if (ownerHex === context.localState.state.funding.scriptPubKeyHex) {
|
|
223
200
|
throw new Error("wallet_buy_already_owner");
|
|
224
201
|
}
|
|
225
|
-
if (getBalance(context.snapshot.state,
|
|
202
|
+
if (getBalance(context.snapshot.state, context.localState.state.funding.scriptPubKeyHex) < listing.priceCogtoshi) {
|
|
226
203
|
throw new Error("wallet_buy_insufficient_cog_balance");
|
|
227
204
|
}
|
|
228
205
|
return {
|
|
229
206
|
readContext: context,
|
|
230
207
|
state: context.localState.state,
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
localIndex: selectedIdentity.index,
|
|
234
|
-
scriptPubKeyHex: selectedIdentity.scriptPubKeyHex,
|
|
235
|
-
address: selectedIdentity.address,
|
|
236
|
-
},
|
|
237
|
-
senderSelector: getCanonicalIdentitySelector(selectedIdentity),
|
|
238
|
-
anchorOutpoint: resolveAnchorOutpointForSender(context.localState.state, selectedIdentity, "wallet_buy"),
|
|
208
|
+
sender: createFundingMutationSender(context.localState.state),
|
|
209
|
+
senderSelector: context.model.walletAddress,
|
|
239
210
|
chainDomain,
|
|
240
211
|
listingPriceCogtoshi: listing.priceCogtoshi,
|
|
241
|
-
buyerSelector:
|
|
212
|
+
buyerSelector: context.model.walletAddress,
|
|
242
213
|
};
|
|
243
214
|
}
|
|
244
215
|
function buildPlanForDomainOperation(options) {
|
|
@@ -246,45 +217,13 @@ function buildPlanForDomainOperation(options) {
|
|
|
246
217
|
&& entry.confirmations >= 1
|
|
247
218
|
&& entry.spendable !== false
|
|
248
219
|
&& entry.safe !== false);
|
|
249
|
-
const outputs = [{ data: Buffer.from(options.opReturnData).toString("hex") }];
|
|
250
|
-
if (options.anchorOutpoint === null) {
|
|
251
|
-
return {
|
|
252
|
-
sender: options.sender,
|
|
253
|
-
changeAddress: options.state.funding.address,
|
|
254
|
-
fixedInputs: [],
|
|
255
|
-
outputs,
|
|
256
|
-
changePosition: 1,
|
|
257
|
-
expectedOpReturnScriptHex: encodeOpReturnScript(options.opReturnData),
|
|
258
|
-
expectedAnchorScriptHex: null,
|
|
259
|
-
expectedAnchorValueSats: null,
|
|
260
|
-
allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
|
|
261
|
-
eligibleFundingOutpointKeys: new Set(fundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout }))),
|
|
262
|
-
errorPrefix: options.errorPrefix,
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
const anchorUtxo = options.allUtxos.find((entry) => entry.txid === options.anchorOutpoint?.txid
|
|
266
|
-
&& entry.vout === options.anchorOutpoint.vout
|
|
267
|
-
&& entry.scriptPubKey === options.sender.scriptPubKeyHex
|
|
268
|
-
&& entry.confirmations >= 1
|
|
269
|
-
&& entry.spendable !== false
|
|
270
|
-
&& entry.safe !== false);
|
|
271
|
-
if (anchorUtxo === undefined) {
|
|
272
|
-
throw new Error(`${options.errorPrefix}_anchor_utxo_missing`);
|
|
273
|
-
}
|
|
274
|
-
outputs.push({
|
|
275
|
-
[options.sender.address]: satsToBtcNumber(options.anchorValueSats),
|
|
276
|
-
});
|
|
277
220
|
return {
|
|
278
221
|
sender: options.sender,
|
|
279
222
|
changeAddress: options.state.funding.address,
|
|
280
|
-
fixedInputs: [
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
outputs,
|
|
284
|
-
changePosition: 2,
|
|
223
|
+
fixedInputs: [],
|
|
224
|
+
outputs: [{ data: Buffer.from(options.opReturnData).toString("hex") }],
|
|
225
|
+
changePosition: 1,
|
|
285
226
|
expectedOpReturnScriptHex: encodeOpReturnScript(options.opReturnData),
|
|
286
|
-
expectedAnchorScriptHex: options.sender.scriptPubKeyHex,
|
|
287
|
-
expectedAnchorValueSats: options.anchorValueSats,
|
|
288
227
|
allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
|
|
289
228
|
eligibleFundingOutpointKeys: new Set(fundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout }))),
|
|
290
229
|
errorPrefix: options.errorPrefix,
|
|
@@ -296,29 +235,10 @@ function validateFundedDraft(decoded, funded, plan) {
|
|
|
296
235
|
if (inputs.length === 0) {
|
|
297
236
|
throw new Error(`${plan.errorPrefix}_missing_sender_input`);
|
|
298
237
|
}
|
|
299
|
-
assertFixedInputPrefixMatches(inputs, plan.fixedInputs, `${plan.errorPrefix}_sender_input_mismatch`);
|
|
300
|
-
if (getDecodedInputScriptPubKeyHex(decoded, 0) !== plan.sender.scriptPubKeyHex) {
|
|
301
|
-
throw new Error(`${plan.errorPrefix}_sender_input_mismatch`);
|
|
302
|
-
}
|
|
303
|
-
assertFundingInputsAfterFixedPrefix({
|
|
304
|
-
decoded,
|
|
305
|
-
fixedInputs: plan.fixedInputs,
|
|
306
|
-
allowedFundingScriptPubKeyHex: plan.allowedFundingScriptPubKeyHex,
|
|
307
|
-
eligibleFundingOutpointKeys: plan.eligibleFundingOutpointKeys,
|
|
308
|
-
errorCode: `${plan.errorPrefix}_unexpected_funding_input`,
|
|
309
|
-
});
|
|
310
238
|
if (outputs[0]?.scriptPubKey?.hex !== plan.expectedOpReturnScriptHex) {
|
|
311
239
|
throw new Error(`${plan.errorPrefix}_opreturn_mismatch`);
|
|
312
240
|
}
|
|
313
|
-
|
|
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 ?? 0n)) {
|
|
318
|
-
throw new Error(`${plan.errorPrefix}_anchor_value_mismatch`);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
const expectedWithoutChange = plan.expectedAnchorScriptHex === null ? 1 : 2;
|
|
241
|
+
const expectedWithoutChange = 1;
|
|
322
242
|
if (funded.changepos === -1) {
|
|
323
243
|
if (outputs.length !== expectedWithoutChange) {
|
|
324
244
|
throw new Error(`${plan.errorPrefix}_unexpected_output_count`);
|
|
@@ -341,7 +261,7 @@ async function buildTransaction(options) {
|
|
|
341
261
|
validateFundedDraft,
|
|
342
262
|
finalizeErrorCode: `${options.plan.errorPrefix}_finalize_failed`,
|
|
343
263
|
mempoolRejectPrefix: `${options.plan.errorPrefix}_mempool_rejected`,
|
|
344
|
-
|
|
264
|
+
feeRate: options.feeRateSatVb,
|
|
345
265
|
});
|
|
346
266
|
}
|
|
347
267
|
function createDraftMutation(options) {
|
|
@@ -358,6 +278,7 @@ function createDraftMutation(options) {
|
|
|
358
278
|
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
359
279
|
attemptedTxid: null,
|
|
360
280
|
attemptedWtxid: null,
|
|
281
|
+
...createWalletMutationFeeMetadata(options.feeSelection),
|
|
361
282
|
temporaryBuilderLockedOutpoints: [],
|
|
362
283
|
};
|
|
363
284
|
}
|
|
@@ -376,6 +297,7 @@ function createDraftMutation(options) {
|
|
|
376
297
|
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
377
298
|
attemptedTxid: null,
|
|
378
299
|
attemptedWtxid: null,
|
|
300
|
+
...createWalletMutationFeeMetadata(options.feeSelection),
|
|
379
301
|
temporaryBuilderLockedOutpoints: [],
|
|
380
302
|
};
|
|
381
303
|
}
|
|
@@ -436,13 +358,11 @@ async function reconcilePendingMutation(options) {
|
|
|
436
358
|
domainName: options.mutation.domainName,
|
|
437
359
|
domainId: chainDomain.domainId,
|
|
438
360
|
currentOwnerScriptPubKeyHex: ownerHex,
|
|
439
|
-
currentOwnerLocalIndex: options.context.model?.identities.find((identity) => identity.scriptPubKeyHex === ownerHex)?.index ?? null,
|
|
440
361
|
nowUnixMs: options.nowUnixMs,
|
|
441
362
|
});
|
|
442
363
|
await saveWalletStatePreservingUnlock({
|
|
443
364
|
state: nextState,
|
|
444
365
|
provider: options.provider,
|
|
445
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
446
366
|
nowUnixMs: options.nowUnixMs,
|
|
447
367
|
paths: options.paths,
|
|
448
368
|
});
|
|
@@ -456,7 +376,6 @@ async function reconcilePendingMutation(options) {
|
|
|
456
376
|
await saveWalletStatePreservingUnlock({
|
|
457
377
|
state: nextState,
|
|
458
378
|
provider: options.provider,
|
|
459
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
460
379
|
nowUnixMs: options.nowUnixMs,
|
|
461
380
|
paths: options.paths,
|
|
462
381
|
});
|
|
@@ -475,7 +394,6 @@ async function reconcilePendingMutation(options) {
|
|
|
475
394
|
await saveWalletStatePreservingUnlock({
|
|
476
395
|
state: nextState,
|
|
477
396
|
provider: options.provider,
|
|
478
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
479
397
|
nowUnixMs: options.nowUnixMs,
|
|
480
398
|
paths: options.paths,
|
|
481
399
|
});
|
|
@@ -490,7 +408,6 @@ async function reconcilePendingMutation(options) {
|
|
|
490
408
|
await saveWalletStatePreservingUnlock({
|
|
491
409
|
state: nextState,
|
|
492
410
|
provider: options.provider,
|
|
493
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
494
411
|
nowUnixMs: options.nowUnixMs,
|
|
495
412
|
paths: options.paths,
|
|
496
413
|
});
|
|
@@ -505,7 +422,6 @@ async function reconcilePendingMutation(options) {
|
|
|
505
422
|
await saveWalletStatePreservingUnlock({
|
|
506
423
|
state: nextState,
|
|
507
424
|
provider: options.provider,
|
|
508
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
509
425
|
nowUnixMs: options.nowUnixMs,
|
|
510
426
|
paths: options.paths,
|
|
511
427
|
});
|
|
@@ -523,13 +439,11 @@ async function reconcilePendingMutation(options) {
|
|
|
523
439
|
domainName: options.mutation.domainName,
|
|
524
440
|
domainId: chainDomain.domainId,
|
|
525
441
|
currentOwnerScriptPubKeyHex: ownerHex,
|
|
526
|
-
currentOwnerLocalIndex: options.context.model?.identities.find((identity) => identity.scriptPubKeyHex === ownerHex)?.index ?? null,
|
|
527
442
|
nowUnixMs: options.nowUnixMs,
|
|
528
443
|
});
|
|
529
444
|
await saveWalletStatePreservingUnlock({
|
|
530
445
|
state: nextState,
|
|
531
446
|
provider: options.provider,
|
|
532
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
533
447
|
nowUnixMs: options.nowUnixMs,
|
|
534
448
|
paths: options.paths,
|
|
535
449
|
});
|
|
@@ -543,7 +457,6 @@ async function reconcilePendingMutation(options) {
|
|
|
543
457
|
await saveWalletStatePreservingUnlock({
|
|
544
458
|
state: nextState,
|
|
545
459
|
provider: options.provider,
|
|
546
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
547
460
|
nowUnixMs: options.nowUnixMs,
|
|
548
461
|
paths: options.paths,
|
|
549
462
|
});
|
|
@@ -567,16 +480,12 @@ async function reconcilePendingMutation(options) {
|
|
|
567
480
|
currentOwnerScriptPubKeyHex: live.kind === "transfer"
|
|
568
481
|
? (live.recipientScriptPubKeyHex ?? live.senderScriptPubKeyHex)
|
|
569
482
|
: live.senderScriptPubKeyHex,
|
|
570
|
-
currentOwnerLocalIndex: live.kind === "transfer"
|
|
571
|
-
? options.context.model?.identities.find((identity) => identity.scriptPubKeyHex === live.recipientScriptPubKeyHex)?.index ?? null
|
|
572
|
-
: live.senderLocalIndex,
|
|
573
483
|
nowUnixMs: options.nowUnixMs,
|
|
574
484
|
});
|
|
575
485
|
}
|
|
576
486
|
await saveWalletStatePreservingUnlock({
|
|
577
487
|
state: nextState,
|
|
578
488
|
provider: options.provider,
|
|
579
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
580
489
|
nowUnixMs: options.nowUnixMs,
|
|
581
490
|
paths: options.paths,
|
|
582
491
|
});
|
|
@@ -595,7 +504,6 @@ async function reconcilePendingMutation(options) {
|
|
|
595
504
|
await saveWalletStatePreservingUnlock({
|
|
596
505
|
state: nextState,
|
|
597
506
|
provider: options.provider,
|
|
598
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
599
507
|
nowUnixMs: options.nowUnixMs,
|
|
600
508
|
paths: options.paths,
|
|
601
509
|
});
|
|
@@ -705,46 +613,63 @@ export async function transferDomain(options) {
|
|
|
705
613
|
});
|
|
706
614
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
707
615
|
const walletName = operation.state.managedCoreWallet.walletName;
|
|
616
|
+
const feeSelection = await resolveWalletMutationFeeSelection({
|
|
617
|
+
rpc,
|
|
618
|
+
feeRateSatVb: options.feeRateSatVb ?? null,
|
|
619
|
+
});
|
|
708
620
|
const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
|
|
621
|
+
let workingState = operation.state;
|
|
622
|
+
let replacementFixedInputs = null;
|
|
709
623
|
if (existingMutation !== null) {
|
|
710
624
|
const reconciled = await reconcilePendingMutation({
|
|
711
625
|
state: operation.state,
|
|
712
626
|
mutation: existingMutation,
|
|
713
627
|
provider,
|
|
714
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
715
628
|
nowUnixMs,
|
|
716
629
|
paths,
|
|
717
630
|
rpc,
|
|
718
631
|
walletName,
|
|
719
632
|
context: readContext,
|
|
720
633
|
});
|
|
634
|
+
workingState = reconciled.state;
|
|
721
635
|
if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
636
|
+
const reuse = await resolvePendingMutationReuseDecision({
|
|
637
|
+
rpc,
|
|
638
|
+
walletName,
|
|
639
|
+
mutation: reconciled.mutation,
|
|
640
|
+
nextFeeSelection: feeSelection,
|
|
641
|
+
});
|
|
642
|
+
if (reuse.reuseExisting) {
|
|
643
|
+
return {
|
|
644
|
+
kind: "transfer",
|
|
645
|
+
domainName: normalizedDomainName,
|
|
646
|
+
txid: reconciled.mutation.attemptedTxid ?? "unknown",
|
|
647
|
+
status: reconciled.resolution,
|
|
648
|
+
reusedExisting: true,
|
|
649
|
+
recipientScriptPubKeyHex: recipient.scriptPubKeyHex,
|
|
650
|
+
resolved: {
|
|
651
|
+
sender: resolvedSender,
|
|
652
|
+
recipient: resolvedRecipient,
|
|
653
|
+
economicEffect: resolvedEconomicEffect,
|
|
654
|
+
},
|
|
655
|
+
fees: reuse.fees,
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
replacementFixedInputs = reuse.replacementFixedInputs;
|
|
735
659
|
}
|
|
736
660
|
if (reconciled.resolution === "repair-required") {
|
|
737
661
|
throw new Error("wallet_transfer_repair_required");
|
|
738
662
|
}
|
|
739
663
|
}
|
|
740
664
|
await confirmTransfer(options.prompter, normalizedDomainName, resolvedSender, resolvedRecipient, resolvedEconomicEffect, options.assumeYes);
|
|
741
|
-
let nextState = upsertPendingMutation(
|
|
665
|
+
let nextState = upsertPendingMutation(workingState, createDraftMutation({
|
|
742
666
|
kind: "transfer",
|
|
743
667
|
domainName: normalizedDomainName,
|
|
744
668
|
sender: operation.sender,
|
|
745
669
|
recipientScriptPubKeyHex: recipient.scriptPubKeyHex,
|
|
746
670
|
intentFingerprintHex,
|
|
747
671
|
nowUnixMs,
|
|
672
|
+
feeSelection,
|
|
748
673
|
existing: existingMutation,
|
|
749
674
|
}));
|
|
750
675
|
nextState = {
|
|
@@ -755,23 +680,35 @@ export async function transferDomain(options) {
|
|
|
755
680
|
await saveWalletStatePreservingUnlock({
|
|
756
681
|
state: nextState,
|
|
757
682
|
provider,
|
|
758
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
759
683
|
nowUnixMs,
|
|
760
684
|
paths,
|
|
761
685
|
});
|
|
686
|
+
const buildPreparation = await prepareDomainMarketBuildState({
|
|
687
|
+
rpc,
|
|
688
|
+
walletName,
|
|
689
|
+
state: nextState,
|
|
690
|
+
provider,
|
|
691
|
+
nowUnixMs,
|
|
692
|
+
paths,
|
|
693
|
+
preflightCoinControl: false,
|
|
694
|
+
});
|
|
695
|
+
nextState = buildPreparation.state;
|
|
696
|
+
const transferPlan = buildPlanForDomainOperation({
|
|
697
|
+
state: nextState,
|
|
698
|
+
allUtxos: buildPreparation.allUtxos,
|
|
699
|
+
sender: operation.sender,
|
|
700
|
+
opReturnData: serializeDomainTransfer(operation.chainDomain.domainId, Buffer.from(recipient.scriptPubKeyHex, "hex")).opReturnData,
|
|
701
|
+
errorPrefix: "wallet_transfer",
|
|
702
|
+
});
|
|
762
703
|
const built = await buildTransaction({
|
|
763
704
|
rpc,
|
|
764
705
|
walletName,
|
|
765
706
|
state: nextState,
|
|
766
|
-
plan:
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
opReturnData: serializeDomainTransfer(operation.chainDomain.domainId, Buffer.from(recipient.scriptPubKeyHex, "hex")).opReturnData,
|
|
772
|
-
anchorValueSats: BigInt(nextState.anchorValueSats),
|
|
773
|
-
errorPrefix: "wallet_transfer",
|
|
774
|
-
}),
|
|
707
|
+
plan: {
|
|
708
|
+
...transferPlan,
|
|
709
|
+
fixedInputs: mergeFixedWalletInputs(transferPlan.fixedInputs, replacementFixedInputs),
|
|
710
|
+
},
|
|
711
|
+
feeRateSatVb: feeSelection.feeRateSatVb,
|
|
775
712
|
});
|
|
776
713
|
const broadcasting = updateMutationRecord(nextState.pendingMutations.find((mutation) => mutation.intentFingerprintHex === intentFingerprintHex), "broadcasting", nowUnixMs, {
|
|
777
714
|
attemptedTxid: built.txid,
|
|
@@ -786,7 +723,6 @@ export async function transferDomain(options) {
|
|
|
786
723
|
await saveWalletStatePreservingUnlock({
|
|
787
724
|
state: nextState,
|
|
788
725
|
provider,
|
|
789
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
790
726
|
nowUnixMs,
|
|
791
727
|
paths,
|
|
792
728
|
});
|
|
@@ -813,7 +749,6 @@ export async function transferDomain(options) {
|
|
|
813
749
|
await saveWalletStatePreservingUnlock({
|
|
814
750
|
state: nextState,
|
|
815
751
|
provider,
|
|
816
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
817
752
|
nowUnixMs,
|
|
818
753
|
paths,
|
|
819
754
|
});
|
|
@@ -833,7 +768,6 @@ export async function transferDomain(options) {
|
|
|
833
768
|
await saveWalletStatePreservingUnlock({
|
|
834
769
|
state: nextState,
|
|
835
770
|
provider,
|
|
836
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
837
771
|
nowUnixMs,
|
|
838
772
|
paths,
|
|
839
773
|
});
|
|
@@ -856,7 +790,6 @@ export async function transferDomain(options) {
|
|
|
856
790
|
domainName: normalizedDomainName,
|
|
857
791
|
domainId: operation.chainDomain.domainId,
|
|
858
792
|
currentOwnerScriptPubKeyHex: recipient.scriptPubKeyHex,
|
|
859
|
-
currentOwnerLocalIndex: model.identities.find((identity) => identity.scriptPubKeyHex === recipient.scriptPubKeyHex)?.index ?? null,
|
|
860
793
|
nowUnixMs,
|
|
861
794
|
});
|
|
862
795
|
nextState = {
|
|
@@ -867,7 +800,6 @@ export async function transferDomain(options) {
|
|
|
867
800
|
await saveWalletStatePreservingUnlock({
|
|
868
801
|
state: nextState,
|
|
869
802
|
provider,
|
|
870
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
871
803
|
nowUnixMs,
|
|
872
804
|
paths,
|
|
873
805
|
});
|
|
@@ -883,6 +815,10 @@ export async function transferDomain(options) {
|
|
|
883
815
|
recipient: resolvedRecipient,
|
|
884
816
|
economicEffect: resolvedEconomicEffect,
|
|
885
817
|
},
|
|
818
|
+
fees: createBuiltWalletMutationFeeSummary({
|
|
819
|
+
selection: feeSelection,
|
|
820
|
+
built,
|
|
821
|
+
}),
|
|
886
822
|
};
|
|
887
823
|
}
|
|
888
824
|
finally {
|
|
@@ -935,32 +871,48 @@ async function runSellMutation(options) {
|
|
|
935
871
|
});
|
|
936
872
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
937
873
|
const walletName = operation.state.managedCoreWallet.walletName;
|
|
874
|
+
const feeSelection = await resolveWalletMutationFeeSelection({
|
|
875
|
+
rpc,
|
|
876
|
+
feeRateSatVb: options.feeRateSatVb ?? null,
|
|
877
|
+
});
|
|
938
878
|
const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
|
|
879
|
+
let workingState = operation.state;
|
|
880
|
+
let replacementFixedInputs = null;
|
|
939
881
|
if (existingMutation !== null) {
|
|
940
882
|
const reconciled = await reconcilePendingMutation({
|
|
941
883
|
state: operation.state,
|
|
942
884
|
mutation: existingMutation,
|
|
943
885
|
provider,
|
|
944
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
945
886
|
nowUnixMs,
|
|
946
887
|
paths,
|
|
947
888
|
rpc,
|
|
948
889
|
walletName,
|
|
949
890
|
context: readContext,
|
|
950
891
|
});
|
|
892
|
+
workingState = reconciled.state;
|
|
951
893
|
if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
894
|
+
const reuse = await resolvePendingMutationReuseDecision({
|
|
895
|
+
rpc,
|
|
896
|
+
walletName,
|
|
897
|
+
mutation: reconciled.mutation,
|
|
898
|
+
nextFeeSelection: feeSelection,
|
|
899
|
+
});
|
|
900
|
+
if (reuse.reuseExisting) {
|
|
901
|
+
return {
|
|
902
|
+
kind: "sell",
|
|
903
|
+
domainName: normalizedDomainName,
|
|
904
|
+
txid: reconciled.mutation.attemptedTxid ?? "unknown",
|
|
905
|
+
status: reconciled.resolution,
|
|
906
|
+
reusedExisting: true,
|
|
907
|
+
listedPriceCogtoshi: options.listedPriceCogtoshi,
|
|
908
|
+
resolved: {
|
|
909
|
+
sender: resolvedSender,
|
|
910
|
+
economicEffect: resolvedEconomicEffect,
|
|
911
|
+
},
|
|
912
|
+
fees: reuse.fees,
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
replacementFixedInputs = reuse.replacementFixedInputs;
|
|
964
916
|
}
|
|
965
917
|
if (reconciled.resolution === "repair-required") {
|
|
966
918
|
throw new Error("wallet_sell_repair_required");
|
|
@@ -969,13 +921,14 @@ async function runSellMutation(options) {
|
|
|
969
921
|
if (options.listedPriceCogtoshi > 0n) {
|
|
970
922
|
await confirmSell(options.prompter, normalizedDomainName, resolvedSender, options.listedPriceCogtoshi, options.assumeYes);
|
|
971
923
|
}
|
|
972
|
-
let nextState = upsertPendingMutation(
|
|
924
|
+
let nextState = upsertPendingMutation(workingState, createDraftMutation({
|
|
973
925
|
kind: "sell",
|
|
974
926
|
domainName: normalizedDomainName,
|
|
975
927
|
sender: operation.sender,
|
|
976
928
|
priceCogtoshi: options.listedPriceCogtoshi,
|
|
977
929
|
intentFingerprintHex,
|
|
978
930
|
nowUnixMs,
|
|
931
|
+
feeSelection,
|
|
979
932
|
existing: existingMutation,
|
|
980
933
|
}));
|
|
981
934
|
nextState = {
|
|
@@ -986,23 +939,35 @@ async function runSellMutation(options) {
|
|
|
986
939
|
await saveWalletStatePreservingUnlock({
|
|
987
940
|
state: nextState,
|
|
988
941
|
provider,
|
|
989
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
990
942
|
nowUnixMs,
|
|
991
943
|
paths,
|
|
992
944
|
});
|
|
945
|
+
const buildPreparation = await prepareDomainMarketBuildState({
|
|
946
|
+
rpc,
|
|
947
|
+
walletName,
|
|
948
|
+
state: nextState,
|
|
949
|
+
provider,
|
|
950
|
+
nowUnixMs,
|
|
951
|
+
paths,
|
|
952
|
+
preflightCoinControl: false,
|
|
953
|
+
});
|
|
954
|
+
nextState = buildPreparation.state;
|
|
955
|
+
const sellPlan = buildPlanForDomainOperation({
|
|
956
|
+
state: nextState,
|
|
957
|
+
allUtxos: buildPreparation.allUtxos,
|
|
958
|
+
sender: operation.sender,
|
|
959
|
+
opReturnData: serializeDomainSell(operation.chainDomain.domainId, options.listedPriceCogtoshi).opReturnData,
|
|
960
|
+
errorPrefix: "wallet_sell",
|
|
961
|
+
});
|
|
993
962
|
const built = await buildTransaction({
|
|
994
963
|
rpc,
|
|
995
964
|
walletName,
|
|
996
965
|
state: nextState,
|
|
997
|
-
plan:
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
opReturnData: serializeDomainSell(operation.chainDomain.domainId, options.listedPriceCogtoshi).opReturnData,
|
|
1003
|
-
anchorValueSats: BigInt(nextState.anchorValueSats),
|
|
1004
|
-
errorPrefix: "wallet_sell",
|
|
1005
|
-
}),
|
|
966
|
+
plan: {
|
|
967
|
+
...sellPlan,
|
|
968
|
+
fixedInputs: mergeFixedWalletInputs(sellPlan.fixedInputs, replacementFixedInputs),
|
|
969
|
+
},
|
|
970
|
+
feeRateSatVb: feeSelection.feeRateSatVb,
|
|
1006
971
|
});
|
|
1007
972
|
const broadcasting = updateMutationRecord(nextState.pendingMutations.find((mutation) => mutation.intentFingerprintHex === intentFingerprintHex), "broadcasting", nowUnixMs, {
|
|
1008
973
|
attemptedTxid: built.txid,
|
|
@@ -1017,7 +982,6 @@ async function runSellMutation(options) {
|
|
|
1017
982
|
await saveWalletStatePreservingUnlock({
|
|
1018
983
|
state: nextState,
|
|
1019
984
|
provider,
|
|
1020
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1021
985
|
nowUnixMs,
|
|
1022
986
|
paths,
|
|
1023
987
|
});
|
|
@@ -1044,7 +1008,6 @@ async function runSellMutation(options) {
|
|
|
1044
1008
|
await saveWalletStatePreservingUnlock({
|
|
1045
1009
|
state: nextState,
|
|
1046
1010
|
provider,
|
|
1047
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1048
1011
|
nowUnixMs,
|
|
1049
1012
|
paths,
|
|
1050
1013
|
});
|
|
@@ -1064,7 +1027,6 @@ async function runSellMutation(options) {
|
|
|
1064
1027
|
await saveWalletStatePreservingUnlock({
|
|
1065
1028
|
state: nextState,
|
|
1066
1029
|
provider,
|
|
1067
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1068
1030
|
nowUnixMs,
|
|
1069
1031
|
paths,
|
|
1070
1032
|
});
|
|
@@ -1091,7 +1053,6 @@ async function runSellMutation(options) {
|
|
|
1091
1053
|
await saveWalletStatePreservingUnlock({
|
|
1092
1054
|
state: nextState,
|
|
1093
1055
|
provider,
|
|
1094
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1095
1056
|
nowUnixMs,
|
|
1096
1057
|
paths,
|
|
1097
1058
|
});
|
|
@@ -1106,6 +1067,10 @@ async function runSellMutation(options) {
|
|
|
1106
1067
|
sender: resolvedSender,
|
|
1107
1068
|
economicEffect: resolvedEconomicEffect,
|
|
1108
1069
|
},
|
|
1070
|
+
fees: createBuiltWalletMutationFeeSummary({
|
|
1071
|
+
selection: feeSelection,
|
|
1072
|
+
built,
|
|
1073
|
+
}),
|
|
1109
1074
|
};
|
|
1110
1075
|
}
|
|
1111
1076
|
finally {
|
|
@@ -1149,7 +1114,7 @@ export async function buyDomain(options) {
|
|
|
1149
1114
|
const snapshot = readContext.snapshot;
|
|
1150
1115
|
const model = readContext.model;
|
|
1151
1116
|
const sellerScriptPubKeyHex = Buffer.from(operation.chainDomain.ownerScriptPubKey).toString("hex");
|
|
1152
|
-
const sellerAddress = model.
|
|
1117
|
+
const sellerAddress = sellerScriptPubKeyHex === model.walletScriptPubKeyHex ? model.walletAddress : null;
|
|
1153
1118
|
const resolvedBuyer = {
|
|
1154
1119
|
selector: operation.buyerSelector,
|
|
1155
1120
|
localIndex: operation.sender.localIndex,
|
|
@@ -1175,43 +1140,60 @@ export async function buyDomain(options) {
|
|
|
1175
1140
|
});
|
|
1176
1141
|
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
1177
1142
|
const walletName = operation.state.managedCoreWallet.walletName;
|
|
1143
|
+
const feeSelection = await resolveWalletMutationFeeSelection({
|
|
1144
|
+
rpc,
|
|
1145
|
+
feeRateSatVb: options.feeRateSatVb ?? null,
|
|
1146
|
+
});
|
|
1178
1147
|
const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
|
|
1148
|
+
let workingState = operation.state;
|
|
1149
|
+
let replacementFixedInputs = null;
|
|
1179
1150
|
if (existingMutation !== null) {
|
|
1180
1151
|
const reconciled = await reconcilePendingMutation({
|
|
1181
1152
|
state: operation.state,
|
|
1182
1153
|
mutation: existingMutation,
|
|
1183
1154
|
provider,
|
|
1184
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1185
1155
|
nowUnixMs,
|
|
1186
1156
|
paths,
|
|
1187
1157
|
rpc,
|
|
1188
1158
|
walletName,
|
|
1189
1159
|
context: readContext,
|
|
1190
1160
|
});
|
|
1161
|
+
workingState = reconciled.state;
|
|
1191
1162
|
if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1163
|
+
const reuse = await resolvePendingMutationReuseDecision({
|
|
1164
|
+
rpc,
|
|
1165
|
+
walletName,
|
|
1166
|
+
mutation: reconciled.mutation,
|
|
1167
|
+
nextFeeSelection: feeSelection,
|
|
1168
|
+
});
|
|
1169
|
+
if (reuse.reuseExisting) {
|
|
1170
|
+
return {
|
|
1171
|
+
kind: "buy",
|
|
1172
|
+
domainName: normalizedDomainName,
|
|
1173
|
+
txid: reconciled.mutation.attemptedTxid ?? "unknown",
|
|
1174
|
+
status: reconciled.resolution,
|
|
1175
|
+
reusedExisting: true,
|
|
1176
|
+
listedPriceCogtoshi: operation.listingPriceCogtoshi,
|
|
1177
|
+
resolvedBuyer,
|
|
1178
|
+
resolvedSeller,
|
|
1179
|
+
fees: reuse.fees,
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
replacementFixedInputs = reuse.replacementFixedInputs;
|
|
1202
1183
|
}
|
|
1203
1184
|
if (reconciled.resolution === "repair-required") {
|
|
1204
1185
|
throw new Error("wallet_buy_repair_required");
|
|
1205
1186
|
}
|
|
1206
1187
|
}
|
|
1207
1188
|
await confirmBuy(options.prompter, normalizedDomainName, operation.buyerSelector, operation.sender, sellerScriptPubKeyHex, sellerAddress, operation.listingPriceCogtoshi, options.assumeYes);
|
|
1208
|
-
let nextState = upsertPendingMutation(
|
|
1189
|
+
let nextState = upsertPendingMutation(workingState, createDraftMutation({
|
|
1209
1190
|
kind: "buy",
|
|
1210
1191
|
domainName: normalizedDomainName,
|
|
1211
1192
|
sender: operation.sender,
|
|
1212
1193
|
priceCogtoshi: operation.listingPriceCogtoshi,
|
|
1213
1194
|
intentFingerprintHex,
|
|
1214
1195
|
nowUnixMs,
|
|
1196
|
+
feeSelection,
|
|
1215
1197
|
existing: existingMutation,
|
|
1216
1198
|
}));
|
|
1217
1199
|
nextState = {
|
|
@@ -1222,23 +1204,35 @@ export async function buyDomain(options) {
|
|
|
1222
1204
|
await saveWalletStatePreservingUnlock({
|
|
1223
1205
|
state: nextState,
|
|
1224
1206
|
provider,
|
|
1225
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1226
1207
|
nowUnixMs,
|
|
1227
1208
|
paths,
|
|
1228
1209
|
});
|
|
1210
|
+
const buildPreparation = await prepareDomainMarketBuildState({
|
|
1211
|
+
rpc,
|
|
1212
|
+
walletName,
|
|
1213
|
+
state: nextState,
|
|
1214
|
+
provider,
|
|
1215
|
+
nowUnixMs,
|
|
1216
|
+
paths,
|
|
1217
|
+
preflightCoinControl: false,
|
|
1218
|
+
});
|
|
1219
|
+
nextState = buildPreparation.state;
|
|
1220
|
+
const buyPlan = buildPlanForDomainOperation({
|
|
1221
|
+
state: nextState,
|
|
1222
|
+
allUtxos: buildPreparation.allUtxos,
|
|
1223
|
+
sender: operation.sender,
|
|
1224
|
+
opReturnData: serializeDomainBuy(operation.chainDomain.domainId, operation.listingPriceCogtoshi).opReturnData,
|
|
1225
|
+
errorPrefix: "wallet_buy",
|
|
1226
|
+
});
|
|
1229
1227
|
const built = await buildTransaction({
|
|
1230
1228
|
rpc,
|
|
1231
1229
|
walletName,
|
|
1232
1230
|
state: nextState,
|
|
1233
|
-
plan:
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
opReturnData: serializeDomainBuy(operation.chainDomain.domainId, operation.listingPriceCogtoshi).opReturnData,
|
|
1239
|
-
anchorValueSats: BigInt(nextState.anchorValueSats),
|
|
1240
|
-
errorPrefix: "wallet_buy",
|
|
1241
|
-
}),
|
|
1231
|
+
plan: {
|
|
1232
|
+
...buyPlan,
|
|
1233
|
+
fixedInputs: mergeFixedWalletInputs(buyPlan.fixedInputs, replacementFixedInputs),
|
|
1234
|
+
},
|
|
1235
|
+
feeRateSatVb: feeSelection.feeRateSatVb,
|
|
1242
1236
|
});
|
|
1243
1237
|
const currentSellerHex = Buffer.from(operation.chainDomain.ownerScriptPubKey).toString("hex");
|
|
1244
1238
|
if (currentSellerHex !== sellerScriptPubKeyHex) {
|
|
@@ -1258,7 +1252,6 @@ export async function buyDomain(options) {
|
|
|
1258
1252
|
await saveWalletStatePreservingUnlock({
|
|
1259
1253
|
state: nextState,
|
|
1260
1254
|
provider,
|
|
1261
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1262
1255
|
nowUnixMs,
|
|
1263
1256
|
paths,
|
|
1264
1257
|
});
|
|
@@ -1285,7 +1278,6 @@ export async function buyDomain(options) {
|
|
|
1285
1278
|
await saveWalletStatePreservingUnlock({
|
|
1286
1279
|
state: nextState,
|
|
1287
1280
|
provider,
|
|
1288
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1289
1281
|
nowUnixMs,
|
|
1290
1282
|
paths,
|
|
1291
1283
|
});
|
|
@@ -1305,7 +1297,6 @@ export async function buyDomain(options) {
|
|
|
1305
1297
|
await saveWalletStatePreservingUnlock({
|
|
1306
1298
|
state: nextState,
|
|
1307
1299
|
provider,
|
|
1308
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1309
1300
|
nowUnixMs,
|
|
1310
1301
|
paths,
|
|
1311
1302
|
});
|
|
@@ -1328,7 +1319,6 @@ export async function buyDomain(options) {
|
|
|
1328
1319
|
domainName: normalizedDomainName,
|
|
1329
1320
|
domainId: operation.chainDomain.domainId,
|
|
1330
1321
|
currentOwnerScriptPubKeyHex: operation.sender.scriptPubKeyHex,
|
|
1331
|
-
currentOwnerLocalIndex: operation.sender.localIndex,
|
|
1332
1322
|
nowUnixMs,
|
|
1333
1323
|
});
|
|
1334
1324
|
nextState = {
|
|
@@ -1339,7 +1329,6 @@ export async function buyDomain(options) {
|
|
|
1339
1329
|
await saveWalletStatePreservingUnlock({
|
|
1340
1330
|
state: nextState,
|
|
1341
1331
|
provider,
|
|
1342
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1343
1332
|
nowUnixMs,
|
|
1344
1333
|
paths,
|
|
1345
1334
|
});
|
|
@@ -1352,6 +1341,10 @@ export async function buyDomain(options) {
|
|
|
1352
1341
|
listedPriceCogtoshi: operation.listingPriceCogtoshi,
|
|
1353
1342
|
resolvedBuyer,
|
|
1354
1343
|
resolvedSeller,
|
|
1344
|
+
fees: createBuiltWalletMutationFeeSummary({
|
|
1345
|
+
selection: feeSelection,
|
|
1346
|
+
built,
|
|
1347
|
+
}),
|
|
1355
1348
|
};
|
|
1356
1349
|
}
|
|
1357
1350
|
finally {
|