@cogcoin/client 1.1.6 → 1.1.8
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 +2 -2
- package/dist/bitcoind/indexer-daemon.js +29 -79
- package/dist/bitcoind/managed-runtime/bitcoind-runtime.d.ts +20 -0
- package/dist/bitcoind/managed-runtime/bitcoind-runtime.js +74 -0
- package/dist/bitcoind/managed-runtime/bitcoind-status.d.ts +11 -0
- package/dist/bitcoind/managed-runtime/bitcoind-status.js +44 -0
- package/dist/bitcoind/managed-runtime/indexer-runtime.d.ts +15 -0
- package/dist/bitcoind/managed-runtime/indexer-runtime.js +82 -0
- package/dist/bitcoind/managed-runtime/types.d.ts +40 -0
- package/dist/bitcoind/node.d.ts +2 -2
- package/dist/bitcoind/node.js +2 -2
- package/dist/bitcoind/rpc.d.ts +2 -1
- package/dist/bitcoind/rpc.js +53 -3
- package/dist/bitcoind/service.js +47 -127
- package/dist/cli/command-registry.d.ts +1 -1
- package/dist/cli/command-registry.js +2 -64
- package/dist/cli/commands/client-admin.js +3 -18
- package/dist/cli/commands/mining-runtime.js +4 -60
- package/dist/cli/commands/wallet-admin.js +6 -6
- package/dist/cli/context.js +1 -3
- package/dist/cli/mining-json.d.ts +1 -22
- package/dist/cli/mining-json.js +0 -23
- package/dist/cli/output.js +16 -2
- package/dist/cli/parse.js +0 -2
- package/dist/cli/preview-json.d.ts +1 -22
- package/dist/cli/preview-json.js +0 -19
- package/dist/cli/types.d.ts +1 -3
- package/dist/cli/wallet-format.js +1 -1
- package/dist/cli/workflow-hints.d.ts +1 -2
- package/dist/cli/workflow-hints.js +5 -8
- package/dist/wallet/lifecycle/context.js +0 -1
- package/dist/wallet/lifecycle/repair-mining.d.ts +1 -5
- package/dist/wallet/lifecycle/repair-mining.js +5 -39
- package/dist/wallet/lifecycle/repair.js +0 -3
- package/dist/wallet/lifecycle/setup.js +10 -8
- package/dist/wallet/lifecycle/types.d.ts +1 -4
- package/dist/wallet/managed-core-wallet.d.ts +2 -0
- package/dist/wallet/managed-core-wallet.js +27 -1
- package/dist/wallet/mining/candidate.d.ts +1 -0
- package/dist/wallet/mining/candidate.js +38 -6
- package/dist/wallet/mining/competitiveness.d.ts +1 -0
- package/dist/wallet/mining/competitiveness.js +6 -0
- package/dist/wallet/mining/cycle.d.ts +2 -0
- package/dist/wallet/mining/cycle.js +14 -4
- package/dist/wallet/mining/engine-state.js +10 -0
- package/dist/wallet/mining/engine-types.d.ts +1 -0
- package/dist/wallet/mining/index.d.ts +1 -1
- package/dist/wallet/mining/index.js +1 -1
- package/dist/wallet/mining/publish.d.ts +3 -0
- package/dist/wallet/mining/publish.js +78 -6
- package/dist/wallet/mining/runner.d.ts +0 -32
- package/dist/wallet/mining/runner.js +59 -104
- package/dist/wallet/mining/stop.d.ts +7 -0
- package/dist/wallet/mining/stop.js +23 -0
- package/dist/wallet/mining/supervisor.d.ts +2 -36
- package/dist/wallet/mining/supervisor.js +139 -246
- package/dist/wallet/mining/visualizer-sync.js +79 -15
- package/dist/wallet/read/context.d.ts +1 -5
- package/dist/wallet/read/context.js +21 -205
- package/dist/wallet/read/managed-services.d.ts +33 -0
- package/dist/wallet/read/managed-services.js +222 -0
- package/dist/wallet/reset/artifacts.d.ts +16 -0
- package/dist/wallet/reset/artifacts.js +141 -0
- package/dist/wallet/reset/execution.d.ts +38 -0
- package/dist/wallet/reset/execution.js +458 -0
- package/dist/wallet/reset/preflight.d.ts +7 -0
- package/dist/wallet/reset/preflight.js +116 -0
- package/dist/wallet/reset/preview.d.ts +2 -0
- package/dist/wallet/reset/preview.js +50 -0
- package/dist/wallet/reset/process-cleanup.d.ts +12 -0
- package/dist/wallet/reset/process-cleanup.js +179 -0
- package/dist/wallet/reset/types.d.ts +189 -0
- package/dist/wallet/reset/types.js +1 -0
- package/dist/wallet/reset.d.ts +4 -119
- package/dist/wallet/reset.js +4 -882
- package/dist/wallet/state/client-password/bootstrap.d.ts +2 -0
- package/dist/wallet/state/client-password/bootstrap.js +3 -0
- package/dist/wallet/state/client-password/context.d.ts +10 -0
- package/dist/wallet/state/client-password/context.js +46 -0
- package/dist/wallet/state/client-password/crypto.d.ts +34 -0
- package/dist/wallet/state/client-password/crypto.js +117 -0
- package/dist/wallet/state/client-password/files.d.ts +10 -0
- package/dist/wallet/state/client-password/files.js +109 -0
- package/dist/wallet/state/client-password/legacy-cleanup.d.ts +11 -0
- package/dist/wallet/state/client-password/legacy-cleanup.js +338 -0
- package/dist/wallet/state/client-password/messages.d.ts +3 -0
- package/dist/wallet/state/client-password/messages.js +9 -0
- package/dist/wallet/state/client-password/migration.d.ts +4 -0
- package/dist/wallet/state/client-password/migration.js +32 -0
- package/dist/wallet/state/client-password/prompts.d.ts +12 -0
- package/dist/wallet/state/client-password/prompts.js +79 -0
- package/dist/wallet/state/client-password/protected-secrets.d.ts +13 -0
- package/dist/wallet/state/client-password/protected-secrets.js +90 -0
- package/dist/wallet/state/client-password/readiness.d.ts +4 -0
- package/dist/wallet/state/client-password/readiness.js +48 -0
- package/dist/wallet/state/client-password/references.d.ts +1 -0
- package/dist/wallet/state/client-password/references.js +56 -0
- package/dist/wallet/state/client-password/rotation.d.ts +6 -0
- package/dist/wallet/state/client-password/rotation.js +98 -0
- package/dist/wallet/state/client-password/session-policy.d.ts +6 -0
- package/dist/wallet/state/client-password/session-policy.js +28 -0
- package/dist/wallet/state/client-password/session.d.ts +19 -0
- package/dist/wallet/state/client-password/session.js +170 -0
- package/dist/wallet/state/client-password/setup.d.ts +8 -0
- package/dist/wallet/state/client-password/setup.js +49 -0
- package/dist/wallet/state/client-password/types.d.ts +82 -0
- package/dist/wallet/state/client-password/types.js +5 -0
- package/dist/wallet/state/client-password.d.ts +7 -38
- package/dist/wallet/state/client-password.js +52 -937
- package/dist/wallet/tx/anchor.js +123 -216
- package/dist/wallet/tx/cog.js +294 -489
- package/dist/wallet/tx/common.d.ts +2 -0
- package/dist/wallet/tx/common.js +2 -0
- package/dist/wallet/tx/domain-admin.js +111 -220
- package/dist/wallet/tx/domain-market.js +401 -681
- package/dist/wallet/tx/executor.d.ts +176 -0
- package/dist/wallet/tx/executor.js +302 -0
- package/dist/wallet/tx/field.js +109 -215
- package/dist/wallet/tx/register.js +158 -269
- package/dist/wallet/tx/reputation.js +120 -227
- package/package.json +1 -1
- package/dist/wallet/mining/worker-main.d.ts +0 -1
- package/dist/wallet/mining/worker-main.js +0 -17
- package/dist/wallet/state/client-password-agent.d.ts +0 -1
- package/dist/wallet/state/client-password-agent.js +0 -211
|
@@ -2,16 +2,16 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
2
2
|
import { getBalance, getListing, lookupDomain } from "@cogcoin/indexer/queries";
|
|
3
3
|
import { attachOrStartManagedBitcoindService } from "../../bitcoind/service.js";
|
|
4
4
|
import { createRpcClient } from "../../bitcoind/node.js";
|
|
5
|
-
import {
|
|
6
|
-
import { resolveWalletRuntimePathsForTesting } from "../runtime.js";
|
|
5
|
+
import {} from "../runtime.js";
|
|
7
6
|
import { reconcilePersistentPolicyLocks as reconcileWalletCoinControlLocks } from "../coin-control.js";
|
|
8
|
-
import {
|
|
7
|
+
import {} from "../state/provider.js";
|
|
9
8
|
import { serializeDomainBuy, serializeDomainSell, serializeDomainTransfer, validateDomainName, } from "../cogop/index.js";
|
|
10
9
|
import { openWalletReadContext } from "../read/index.js";
|
|
11
|
-
import { assertFixedInputPrefixMatches, assertFundingInputsAfterFixedPrefix, assertWalletMutationContextReady, buildWalletMutationTransactionWithReserveFallback,
|
|
10
|
+
import { assertFixedInputPrefixMatches, assertFundingInputsAfterFixedPrefix, assertWalletMutationContextReady, buildWalletMutationTransactionWithReserveFallback, createFundingMutationSender, createWalletMutationFeeMetadata, getDecodedInputScriptPubKeyHex, isLocalWalletScript, mergeFixedWalletInputs, outpointKey, saveWalletStatePreservingUnlock, unlockTemporaryBuilderLocks, updateMutationRecord, } from "./common.js";
|
|
12
11
|
import { confirmTypedAcknowledgement, confirmYesNo } from "./confirm.js";
|
|
12
|
+
import { executeWalletMutationOperation, publishWalletMutation, resolveExistingWalletMutation, } from "./executor.js";
|
|
13
13
|
import { getCanonicalIdentitySelector, resolveIdentityBySelector, } from "./identity-selector.js";
|
|
14
|
-
import {
|
|
14
|
+
import { upsertPendingMutation } from "./journal.js";
|
|
15
15
|
import { normalizeBtcTarget } from "./targets.js";
|
|
16
16
|
async function prepareDomainMarketBuildState(options) {
|
|
17
17
|
if (!options.preflightCoinControl) {
|
|
@@ -567,520 +567,333 @@ async function confirmBuy(prompter, domainName, buyerSelector, buyer, sellerScri
|
|
|
567
567
|
});
|
|
568
568
|
}
|
|
569
569
|
export async function transferDomain(options) {
|
|
570
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
571
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
572
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
573
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
574
|
-
purpose: "wallet-transfer",
|
|
575
|
-
walletRootId: null,
|
|
576
|
-
});
|
|
577
570
|
const normalizedDomainName = normalizeDomainName(options.domainName);
|
|
578
571
|
const recipient = normalizeBtcTarget(options.target);
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
const readContext = await (options.openReadContext ?? openWalletReadContext)({
|
|
585
|
-
dataDir: options.dataDir,
|
|
586
|
-
databasePath: options.databasePath,
|
|
587
|
-
secretProvider: provider,
|
|
588
|
-
walletControlLockHeld: true,
|
|
589
|
-
paths,
|
|
590
|
-
});
|
|
591
|
-
try {
|
|
572
|
+
const execution = await executeWalletMutationOperation({
|
|
573
|
+
...options,
|
|
574
|
+
controlLockPurpose: "wallet-transfer",
|
|
575
|
+
preemptionReason: "wallet-transfer",
|
|
576
|
+
resolveOperation(readContext) {
|
|
592
577
|
const operation = resolveOwnedDomainOperation(readContext, normalizedDomainName, "wallet_transfer");
|
|
593
|
-
const snapshot = readContext.snapshot;
|
|
594
|
-
const model = readContext.model;
|
|
595
578
|
const resolvedSender = createResolvedDomainMarketSenderSummary(operation.sender, operation.senderSelector);
|
|
596
579
|
const resolvedRecipient = createResolvedDomainMarketRecipientSummary(recipient);
|
|
597
|
-
const resolvedEconomicEffect = createTransferEconomicEffectSummary(getListing(snapshot.state, operation.chainDomain.domainId) !== null);
|
|
580
|
+
const resolvedEconomicEffect = createTransferEconomicEffectSummary(getListing(readContext.snapshot.state, operation.chainDomain.domainId) !== null);
|
|
598
581
|
if (operation.sender.scriptPubKeyHex === recipient.scriptPubKeyHex) {
|
|
599
582
|
throw new Error("wallet_transfer_self_transfer");
|
|
600
583
|
}
|
|
601
|
-
|
|
584
|
+
return {
|
|
585
|
+
...operation,
|
|
586
|
+
normalizedDomainName,
|
|
587
|
+
recipient,
|
|
588
|
+
resolvedSender,
|
|
589
|
+
resolvedRecipient,
|
|
590
|
+
resolvedEconomicEffect,
|
|
591
|
+
};
|
|
592
|
+
},
|
|
593
|
+
createIntentFingerprint(operation) {
|
|
594
|
+
return createIntentFingerprint([
|
|
602
595
|
"transfer",
|
|
603
596
|
operation.state.walletRootId,
|
|
604
|
-
normalizedDomainName,
|
|
597
|
+
operation.normalizedDomainName,
|
|
605
598
|
operation.sender.scriptPubKeyHex,
|
|
606
|
-
recipient.scriptPubKeyHex,
|
|
599
|
+
operation.recipient.scriptPubKeyHex,
|
|
607
600
|
]);
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
walletRootId: operation.state.walletRootId,
|
|
613
|
-
});
|
|
614
|
-
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
615
|
-
const walletName = operation.state.managedCoreWallet.walletName;
|
|
616
|
-
const feeSelection = await resolveWalletMutationFeeSelection({
|
|
617
|
-
rpc,
|
|
618
|
-
feeRateSatVb: options.feeRateSatVb ?? null,
|
|
619
|
-
});
|
|
620
|
-
const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
|
|
621
|
-
let workingState = operation.state;
|
|
622
|
-
let replacementFixedInputs = null;
|
|
623
|
-
if (existingMutation !== null) {
|
|
624
|
-
const reconciled = await reconcilePendingMutation({
|
|
625
|
-
state: operation.state,
|
|
626
|
-
mutation: existingMutation,
|
|
627
|
-
provider,
|
|
628
|
-
nowUnixMs,
|
|
629
|
-
paths,
|
|
630
|
-
rpc,
|
|
631
|
-
walletName,
|
|
632
|
-
context: readContext,
|
|
633
|
-
});
|
|
634
|
-
workingState = reconciled.state;
|
|
635
|
-
if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
|
|
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;
|
|
659
|
-
}
|
|
660
|
-
if (reconciled.resolution === "repair-required") {
|
|
661
|
-
throw new Error("wallet_transfer_repair_required");
|
|
662
|
-
}
|
|
601
|
+
},
|
|
602
|
+
async resolveExistingMutation({ operation, existingMutation, execution }) {
|
|
603
|
+
if (existingMutation === null) {
|
|
604
|
+
return { state: operation.state, replacementFixedInputs: null, result: null };
|
|
663
605
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
606
|
+
return resolveExistingWalletMutation({
|
|
607
|
+
existingMutation,
|
|
608
|
+
execution,
|
|
609
|
+
repairRequiredErrorCode: "wallet_transfer_repair_required",
|
|
610
|
+
reconcileExistingMutation: (mutation) => reconcilePendingMutation({
|
|
611
|
+
state: operation.state,
|
|
612
|
+
mutation,
|
|
613
|
+
provider: execution.provider,
|
|
614
|
+
nowUnixMs: execution.nowUnixMs,
|
|
615
|
+
paths: execution.paths,
|
|
616
|
+
rpc: execution.rpc,
|
|
617
|
+
walletName: execution.walletName,
|
|
618
|
+
context: execution.readContext,
|
|
619
|
+
}),
|
|
620
|
+
createReuseResult: ({ mutation, resolution, fees }) => ({
|
|
621
|
+
kind: "transfer",
|
|
622
|
+
domainName: operation.normalizedDomainName,
|
|
623
|
+
txid: mutation.attemptedTxid ?? "unknown",
|
|
624
|
+
status: resolution,
|
|
625
|
+
reusedExisting: true,
|
|
626
|
+
recipientScriptPubKeyHex: operation.recipient.scriptPubKeyHex,
|
|
627
|
+
resolved: {
|
|
628
|
+
sender: operation.resolvedSender,
|
|
629
|
+
recipient: operation.resolvedRecipient,
|
|
630
|
+
economicEffect: operation.resolvedEconomicEffect,
|
|
631
|
+
},
|
|
632
|
+
fees,
|
|
633
|
+
}),
|
|
685
634
|
});
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
635
|
+
},
|
|
636
|
+
confirm({ operation }) {
|
|
637
|
+
return confirmTransfer(options.prompter, operation.normalizedDomainName, operation.resolvedSender, operation.resolvedRecipient, operation.resolvedEconomicEffect, options.assumeYes);
|
|
638
|
+
},
|
|
639
|
+
createDraftMutation({ operation, existingMutation, execution, intentFingerprintHex }) {
|
|
640
|
+
return {
|
|
641
|
+
mutation: createDraftMutation({
|
|
642
|
+
kind: "transfer",
|
|
643
|
+
domainName: operation.normalizedDomainName,
|
|
644
|
+
sender: operation.sender,
|
|
645
|
+
recipientScriptPubKeyHex: operation.recipient.scriptPubKeyHex,
|
|
646
|
+
intentFingerprintHex,
|
|
647
|
+
nowUnixMs: execution.nowUnixMs,
|
|
648
|
+
feeSelection: execution.feeSelection,
|
|
649
|
+
existing: existingMutation,
|
|
650
|
+
}),
|
|
651
|
+
prepared: null,
|
|
652
|
+
};
|
|
653
|
+
},
|
|
654
|
+
async prepareBuildState({ state, execution }) {
|
|
655
|
+
return (await prepareDomainMarketBuildState({
|
|
656
|
+
rpc: execution.rpc,
|
|
657
|
+
walletName: execution.walletName,
|
|
658
|
+
state,
|
|
659
|
+
provider: execution.provider,
|
|
660
|
+
nowUnixMs: execution.nowUnixMs,
|
|
661
|
+
paths: execution.paths,
|
|
693
662
|
preflightCoinControl: false,
|
|
694
|
-
});
|
|
695
|
-
|
|
663
|
+
})).state;
|
|
664
|
+
},
|
|
665
|
+
async build({ operation, state, execution, replacementFixedInputs }) {
|
|
696
666
|
const transferPlan = buildPlanForDomainOperation({
|
|
697
|
-
state
|
|
698
|
-
allUtxos:
|
|
667
|
+
state,
|
|
668
|
+
allUtxos: await execution.rpc.listUnspent(execution.walletName, 1),
|
|
699
669
|
sender: operation.sender,
|
|
700
|
-
opReturnData: serializeDomainTransfer(operation.chainDomain.domainId, Buffer.from(recipient.scriptPubKeyHex, "hex")).opReturnData,
|
|
670
|
+
opReturnData: serializeDomainTransfer(operation.chainDomain.domainId, Buffer.from(operation.recipient.scriptPubKeyHex, "hex")).opReturnData,
|
|
701
671
|
errorPrefix: "wallet_transfer",
|
|
702
672
|
});
|
|
703
|
-
|
|
704
|
-
rpc,
|
|
705
|
-
walletName,
|
|
706
|
-
state
|
|
673
|
+
return buildTransaction({
|
|
674
|
+
rpc: execution.rpc,
|
|
675
|
+
walletName: execution.walletName,
|
|
676
|
+
state,
|
|
707
677
|
plan: {
|
|
708
678
|
...transferPlan,
|
|
709
679
|
fixedInputs: mergeFixedWalletInputs(transferPlan.fixedInputs, replacementFixedInputs),
|
|
710
680
|
},
|
|
711
|
-
feeRateSatVb: feeSelection.feeRateSatVb,
|
|
712
|
-
});
|
|
713
|
-
const broadcasting = updateMutationRecord(nextState.pendingMutations.find((mutation) => mutation.intentFingerprintHex === intentFingerprintHex), "broadcasting", nowUnixMs, {
|
|
714
|
-
attemptedTxid: built.txid,
|
|
715
|
-
attemptedWtxid: built.wtxid,
|
|
716
|
-
temporaryBuilderLockedOutpoints: built.temporaryBuilderLockedOutpoints,
|
|
717
|
-
});
|
|
718
|
-
nextState = {
|
|
719
|
-
...upsertPendingMutation(nextState, broadcasting),
|
|
720
|
-
stateRevision: nextState.stateRevision + 1,
|
|
721
|
-
lastWrittenAtUnixMs: nowUnixMs,
|
|
722
|
-
};
|
|
723
|
-
await saveWalletStatePreservingUnlock({
|
|
724
|
-
state: nextState,
|
|
725
|
-
provider,
|
|
726
|
-
nowUnixMs,
|
|
727
|
-
paths,
|
|
681
|
+
feeRateSatVb: execution.feeSelection.feeRateSatVb,
|
|
728
682
|
});
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
await saveWalletStatePreservingUnlock({
|
|
750
|
-
state: nextState,
|
|
751
|
-
provider,
|
|
752
|
-
nowUnixMs,
|
|
753
|
-
paths,
|
|
754
|
-
});
|
|
755
|
-
throw new Error("wallet_transfer_broadcast_unknown");
|
|
756
|
-
}
|
|
757
|
-
await unlockTemporaryBuilderLocks(rpc, walletName, built.temporaryBuilderLockedOutpoints);
|
|
758
|
-
const canceled = updateMutationRecord(broadcasting, "canceled", nowUnixMs, {
|
|
683
|
+
},
|
|
684
|
+
publish({ operation, state, execution, built, mutation }) {
|
|
685
|
+
return publishWalletMutation({
|
|
686
|
+
rpc: execution.rpc,
|
|
687
|
+
walletName: execution.walletName,
|
|
688
|
+
snapshotHeight: execution.readContext.snapshot?.tip?.height ?? null,
|
|
689
|
+
built,
|
|
690
|
+
mutation,
|
|
691
|
+
state,
|
|
692
|
+
provider: execution.provider,
|
|
693
|
+
nowUnixMs: execution.nowUnixMs,
|
|
694
|
+
paths: execution.paths,
|
|
695
|
+
errorPrefix: "wallet_transfer",
|
|
696
|
+
async afterAccepted({ state: acceptedState, broadcastingMutation, built, nowUnixMs }) {
|
|
697
|
+
const finalStatus = getTransferStatusAfterAcceptance({
|
|
698
|
+
snapshot: execution.readContext.snapshot,
|
|
699
|
+
domainName: operation.normalizedDomainName,
|
|
700
|
+
recipientScriptPubKeyHex: operation.recipient.scriptPubKeyHex,
|
|
701
|
+
});
|
|
702
|
+
const finalMutation = updateMutationRecord(broadcastingMutation, finalStatus, nowUnixMs, {
|
|
759
703
|
attemptedTxid: built.txid,
|
|
760
704
|
attemptedWtxid: built.wtxid,
|
|
761
705
|
temporaryBuilderLockedOutpoints: [],
|
|
762
706
|
});
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
707
|
+
return {
|
|
708
|
+
state: reserveTransferredDomainRecord({
|
|
709
|
+
state: upsertPendingMutation(acceptedState, finalMutation),
|
|
710
|
+
domainName: operation.normalizedDomainName,
|
|
711
|
+
domainId: operation.chainDomain.domainId,
|
|
712
|
+
currentOwnerScriptPubKeyHex: operation.recipient.scriptPubKeyHex,
|
|
713
|
+
nowUnixMs,
|
|
714
|
+
}),
|
|
715
|
+
mutation: finalMutation,
|
|
716
|
+
status: finalStatus,
|
|
767
717
|
};
|
|
768
|
-
|
|
769
|
-
state: nextState,
|
|
770
|
-
provider,
|
|
771
|
-
nowUnixMs,
|
|
772
|
-
paths,
|
|
773
|
-
});
|
|
774
|
-
throw error;
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
await unlockTemporaryBuilderLocks(rpc, walletName, built.temporaryBuilderLockedOutpoints);
|
|
778
|
-
const finalStatus = getTransferStatusAfterAcceptance({
|
|
779
|
-
snapshot: readContext.snapshot,
|
|
780
|
-
domainName: normalizedDomainName,
|
|
781
|
-
recipientScriptPubKeyHex: recipient.scriptPubKeyHex,
|
|
782
|
-
});
|
|
783
|
-
const finalMutation = updateMutationRecord(broadcasting, finalStatus, nowUnixMs, {
|
|
784
|
-
attemptedTxid: built.txid,
|
|
785
|
-
attemptedWtxid: built.wtxid,
|
|
786
|
-
temporaryBuilderLockedOutpoints: [],
|
|
787
|
-
});
|
|
788
|
-
nextState = reserveTransferredDomainRecord({
|
|
789
|
-
state: upsertPendingMutation(nextState, finalMutation),
|
|
790
|
-
domainName: normalizedDomainName,
|
|
791
|
-
domainId: operation.chainDomain.domainId,
|
|
792
|
-
currentOwnerScriptPubKeyHex: recipient.scriptPubKeyHex,
|
|
793
|
-
nowUnixMs,
|
|
794
|
-
});
|
|
795
|
-
nextState = {
|
|
796
|
-
...nextState,
|
|
797
|
-
stateRevision: nextState.stateRevision + 1,
|
|
798
|
-
lastWrittenAtUnixMs: nowUnixMs,
|
|
799
|
-
};
|
|
800
|
-
await saveWalletStatePreservingUnlock({
|
|
801
|
-
state: nextState,
|
|
802
|
-
provider,
|
|
803
|
-
nowUnixMs,
|
|
804
|
-
paths,
|
|
718
|
+
},
|
|
805
719
|
});
|
|
720
|
+
},
|
|
721
|
+
createResult({ operation, mutation, built, status, reusedExisting, fees }) {
|
|
806
722
|
return {
|
|
807
723
|
kind: "transfer",
|
|
808
|
-
domainName: normalizedDomainName,
|
|
809
|
-
txid: built
|
|
810
|
-
status:
|
|
811
|
-
reusedExisting
|
|
812
|
-
recipientScriptPubKeyHex: recipient.scriptPubKeyHex,
|
|
724
|
+
domainName: operation.normalizedDomainName,
|
|
725
|
+
txid: mutation.attemptedTxid ?? built?.txid ?? "unknown",
|
|
726
|
+
status: status,
|
|
727
|
+
reusedExisting,
|
|
728
|
+
recipientScriptPubKeyHex: operation.recipient.scriptPubKeyHex,
|
|
813
729
|
resolved: {
|
|
814
|
-
sender: resolvedSender,
|
|
815
|
-
recipient: resolvedRecipient,
|
|
816
|
-
economicEffect: resolvedEconomicEffect,
|
|
730
|
+
sender: operation.resolvedSender,
|
|
731
|
+
recipient: operation.resolvedRecipient,
|
|
732
|
+
economicEffect: operation.resolvedEconomicEffect,
|
|
817
733
|
},
|
|
818
|
-
fees
|
|
819
|
-
selection: feeSelection,
|
|
820
|
-
built,
|
|
821
|
-
}),
|
|
734
|
+
fees,
|
|
822
735
|
};
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
await miningPreemption.release();
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
finally {
|
|
830
|
-
await controlLock.release();
|
|
831
|
-
}
|
|
736
|
+
},
|
|
737
|
+
});
|
|
738
|
+
return execution.result;
|
|
832
739
|
}
|
|
833
740
|
async function runSellMutation(options) {
|
|
834
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
835
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
836
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
837
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
838
|
-
purpose: "wallet-sell",
|
|
839
|
-
walletRootId: null,
|
|
840
|
-
});
|
|
841
741
|
const normalizedDomainName = normalizeDomainName(options.domainName);
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
const readContext = await (options.openReadContext ?? openWalletReadContext)({
|
|
848
|
-
dataDir: options.dataDir,
|
|
849
|
-
databasePath: options.databasePath,
|
|
850
|
-
secretProvider: provider,
|
|
851
|
-
walletControlLockHeld: true,
|
|
852
|
-
paths,
|
|
853
|
-
});
|
|
854
|
-
try {
|
|
742
|
+
const execution = await executeWalletMutationOperation({
|
|
743
|
+
...options,
|
|
744
|
+
controlLockPurpose: "wallet-sell",
|
|
745
|
+
preemptionReason: "wallet-sell",
|
|
746
|
+
resolveOperation(readContext) {
|
|
855
747
|
const operation = resolveOwnedDomainOperation(readContext, normalizedDomainName, "wallet_sell");
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
748
|
+
return {
|
|
749
|
+
...operation,
|
|
750
|
+
normalizedDomainName,
|
|
751
|
+
listedPriceCogtoshi: options.listedPriceCogtoshi,
|
|
752
|
+
resolvedSender: createResolvedDomainMarketSenderSummary(operation.sender, operation.senderSelector),
|
|
753
|
+
resolvedEconomicEffect: createSellEconomicEffectSummary(options.listedPriceCogtoshi),
|
|
754
|
+
};
|
|
755
|
+
},
|
|
756
|
+
createIntentFingerprint(operation) {
|
|
757
|
+
return createIntentFingerprint([
|
|
860
758
|
"sell",
|
|
861
759
|
operation.state.walletRootId,
|
|
862
|
-
normalizedDomainName,
|
|
760
|
+
operation.normalizedDomainName,
|
|
863
761
|
operation.sender.scriptPubKeyHex,
|
|
864
|
-
|
|
762
|
+
operation.listedPriceCogtoshi.toString(),
|
|
865
763
|
]);
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
walletRootId: operation.state.walletRootId,
|
|
871
|
-
});
|
|
872
|
-
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
873
|
-
const walletName = operation.state.managedCoreWallet.walletName;
|
|
874
|
-
const feeSelection = await resolveWalletMutationFeeSelection({
|
|
875
|
-
rpc,
|
|
876
|
-
feeRateSatVb: options.feeRateSatVb ?? null,
|
|
877
|
-
});
|
|
878
|
-
const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
|
|
879
|
-
let workingState = operation.state;
|
|
880
|
-
let replacementFixedInputs = null;
|
|
881
|
-
if (existingMutation !== null) {
|
|
882
|
-
const reconciled = await reconcilePendingMutation({
|
|
883
|
-
state: operation.state,
|
|
884
|
-
mutation: existingMutation,
|
|
885
|
-
provider,
|
|
886
|
-
nowUnixMs,
|
|
887
|
-
paths,
|
|
888
|
-
rpc,
|
|
889
|
-
walletName,
|
|
890
|
-
context: readContext,
|
|
891
|
-
});
|
|
892
|
-
workingState = reconciled.state;
|
|
893
|
-
if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
|
|
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;
|
|
916
|
-
}
|
|
917
|
-
if (reconciled.resolution === "repair-required") {
|
|
918
|
-
throw new Error("wallet_sell_repair_required");
|
|
919
|
-
}
|
|
764
|
+
},
|
|
765
|
+
async resolveExistingMutation({ operation, existingMutation, execution }) {
|
|
766
|
+
if (existingMutation === null) {
|
|
767
|
+
return { state: operation.state, replacementFixedInputs: null, result: null };
|
|
920
768
|
}
|
|
921
|
-
|
|
922
|
-
|
|
769
|
+
return resolveExistingWalletMutation({
|
|
770
|
+
existingMutation,
|
|
771
|
+
execution,
|
|
772
|
+
repairRequiredErrorCode: "wallet_sell_repair_required",
|
|
773
|
+
reconcileExistingMutation: (mutation) => reconcilePendingMutation({
|
|
774
|
+
state: operation.state,
|
|
775
|
+
mutation,
|
|
776
|
+
provider: execution.provider,
|
|
777
|
+
nowUnixMs: execution.nowUnixMs,
|
|
778
|
+
paths: execution.paths,
|
|
779
|
+
rpc: execution.rpc,
|
|
780
|
+
walletName: execution.walletName,
|
|
781
|
+
context: execution.readContext,
|
|
782
|
+
}),
|
|
783
|
+
createReuseResult: ({ mutation, resolution, fees }) => ({
|
|
784
|
+
kind: "sell",
|
|
785
|
+
domainName: operation.normalizedDomainName,
|
|
786
|
+
txid: mutation.attemptedTxid ?? "unknown",
|
|
787
|
+
status: resolution,
|
|
788
|
+
reusedExisting: true,
|
|
789
|
+
listedPriceCogtoshi: operation.listedPriceCogtoshi,
|
|
790
|
+
resolved: {
|
|
791
|
+
sender: operation.resolvedSender,
|
|
792
|
+
economicEffect: operation.resolvedEconomicEffect,
|
|
793
|
+
},
|
|
794
|
+
fees,
|
|
795
|
+
}),
|
|
796
|
+
});
|
|
797
|
+
},
|
|
798
|
+
async confirm({ operation }) {
|
|
799
|
+
if (operation.listedPriceCogtoshi > 0n) {
|
|
800
|
+
await confirmSell(options.prompter, operation.normalizedDomainName, operation.resolvedSender, operation.listedPriceCogtoshi, options.assumeYes);
|
|
923
801
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
802
|
+
},
|
|
803
|
+
createDraftMutation({ operation, existingMutation, execution, intentFingerprintHex }) {
|
|
804
|
+
return {
|
|
805
|
+
mutation: createDraftMutation({
|
|
806
|
+
kind: "sell",
|
|
807
|
+
domainName: operation.normalizedDomainName,
|
|
808
|
+
sender: operation.sender,
|
|
809
|
+
priceCogtoshi: operation.listedPriceCogtoshi,
|
|
810
|
+
intentFingerprintHex,
|
|
811
|
+
nowUnixMs: execution.nowUnixMs,
|
|
812
|
+
feeSelection: execution.feeSelection,
|
|
813
|
+
existing: existingMutation,
|
|
814
|
+
}),
|
|
815
|
+
prepared: null,
|
|
938
816
|
};
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
state: nextState,
|
|
949
|
-
provider,
|
|
950
|
-
nowUnixMs,
|
|
951
|
-
paths,
|
|
817
|
+
},
|
|
818
|
+
async prepareBuildState({ state, execution }) {
|
|
819
|
+
return (await prepareDomainMarketBuildState({
|
|
820
|
+
rpc: execution.rpc,
|
|
821
|
+
walletName: execution.walletName,
|
|
822
|
+
state,
|
|
823
|
+
provider: execution.provider,
|
|
824
|
+
nowUnixMs: execution.nowUnixMs,
|
|
825
|
+
paths: execution.paths,
|
|
952
826
|
preflightCoinControl: false,
|
|
953
|
-
});
|
|
954
|
-
|
|
827
|
+
})).state;
|
|
828
|
+
},
|
|
829
|
+
async build({ operation, state, execution, replacementFixedInputs }) {
|
|
955
830
|
const sellPlan = buildPlanForDomainOperation({
|
|
956
|
-
state
|
|
957
|
-
allUtxos:
|
|
831
|
+
state,
|
|
832
|
+
allUtxos: await execution.rpc.listUnspent(execution.walletName, 1),
|
|
958
833
|
sender: operation.sender,
|
|
959
|
-
opReturnData: serializeDomainSell(operation.chainDomain.domainId,
|
|
834
|
+
opReturnData: serializeDomainSell(operation.chainDomain.domainId, operation.listedPriceCogtoshi).opReturnData,
|
|
960
835
|
errorPrefix: "wallet_sell",
|
|
961
836
|
});
|
|
962
|
-
|
|
963
|
-
rpc,
|
|
964
|
-
walletName,
|
|
965
|
-
state
|
|
837
|
+
return buildTransaction({
|
|
838
|
+
rpc: execution.rpc,
|
|
839
|
+
walletName: execution.walletName,
|
|
840
|
+
state,
|
|
966
841
|
plan: {
|
|
967
842
|
...sellPlan,
|
|
968
843
|
fixedInputs: mergeFixedWalletInputs(sellPlan.fixedInputs, replacementFixedInputs),
|
|
969
844
|
},
|
|
970
|
-
feeRateSatVb: feeSelection.feeRateSatVb,
|
|
845
|
+
feeRateSatVb: execution.feeSelection.feeRateSatVb,
|
|
971
846
|
});
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
nowUnixMs
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
await rpc.sendRawTransaction(built.rawHex);
|
|
994
|
-
}
|
|
995
|
-
catch (error) {
|
|
996
|
-
if (!isAlreadyAcceptedError(error)) {
|
|
997
|
-
if (isBroadcastUnknownError(error)) {
|
|
998
|
-
const unknown = updateMutationRecord(broadcasting, "broadcast-unknown", nowUnixMs, {
|
|
999
|
-
attemptedTxid: built.txid,
|
|
1000
|
-
attemptedWtxid: built.wtxid,
|
|
1001
|
-
temporaryBuilderLockedOutpoints: built.temporaryBuilderLockedOutpoints,
|
|
1002
|
-
});
|
|
1003
|
-
nextState = {
|
|
1004
|
-
...upsertPendingMutation(nextState, unknown),
|
|
1005
|
-
stateRevision: nextState.stateRevision + 1,
|
|
1006
|
-
lastWrittenAtUnixMs: nowUnixMs,
|
|
1007
|
-
};
|
|
1008
|
-
await saveWalletStatePreservingUnlock({
|
|
1009
|
-
state: nextState,
|
|
1010
|
-
provider,
|
|
1011
|
-
nowUnixMs,
|
|
1012
|
-
paths,
|
|
1013
|
-
});
|
|
1014
|
-
throw new Error("wallet_sell_broadcast_unknown");
|
|
1015
|
-
}
|
|
1016
|
-
await unlockTemporaryBuilderLocks(rpc, walletName, built.temporaryBuilderLockedOutpoints);
|
|
1017
|
-
const canceled = updateMutationRecord(broadcasting, "canceled", nowUnixMs, {
|
|
847
|
+
},
|
|
848
|
+
publish({ operation, state, execution, built, mutation }) {
|
|
849
|
+
return publishWalletMutation({
|
|
850
|
+
rpc: execution.rpc,
|
|
851
|
+
walletName: execution.walletName,
|
|
852
|
+
snapshotHeight: execution.readContext.snapshot?.tip?.height ?? null,
|
|
853
|
+
built,
|
|
854
|
+
mutation,
|
|
855
|
+
state,
|
|
856
|
+
provider: execution.provider,
|
|
857
|
+
nowUnixMs: execution.nowUnixMs,
|
|
858
|
+
paths: execution.paths,
|
|
859
|
+
errorPrefix: "wallet_sell",
|
|
860
|
+
async afterAccepted({ state: acceptedState, broadcastingMutation, built, nowUnixMs }) {
|
|
861
|
+
const finalStatus = getSellStatusAfterAcceptance({
|
|
862
|
+
snapshot: execution.readContext.snapshot,
|
|
863
|
+
domainName: operation.normalizedDomainName,
|
|
864
|
+
senderScriptPubKeyHex: operation.sender.scriptPubKeyHex,
|
|
865
|
+
listedPriceCogtoshi: operation.listedPriceCogtoshi,
|
|
866
|
+
});
|
|
867
|
+
const finalMutation = updateMutationRecord(broadcastingMutation, finalStatus, nowUnixMs, {
|
|
1018
868
|
attemptedTxid: built.txid,
|
|
1019
869
|
attemptedWtxid: built.wtxid,
|
|
1020
870
|
temporaryBuilderLockedOutpoints: [],
|
|
1021
871
|
});
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
872
|
+
return {
|
|
873
|
+
state: upsertPendingMutation(acceptedState, finalMutation),
|
|
874
|
+
mutation: finalMutation,
|
|
875
|
+
status: finalStatus,
|
|
1026
876
|
};
|
|
1027
|
-
|
|
1028
|
-
state: nextState,
|
|
1029
|
-
provider,
|
|
1030
|
-
nowUnixMs,
|
|
1031
|
-
paths,
|
|
1032
|
-
});
|
|
1033
|
-
throw error;
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
await unlockTemporaryBuilderLocks(rpc, walletName, built.temporaryBuilderLockedOutpoints);
|
|
1037
|
-
const finalStatus = getSellStatusAfterAcceptance({
|
|
1038
|
-
snapshot: readContext.snapshot,
|
|
1039
|
-
domainName: normalizedDomainName,
|
|
1040
|
-
senderScriptPubKeyHex: operation.sender.scriptPubKeyHex,
|
|
1041
|
-
listedPriceCogtoshi: options.listedPriceCogtoshi,
|
|
1042
|
-
});
|
|
1043
|
-
const finalMutation = updateMutationRecord(broadcasting, finalStatus, nowUnixMs, {
|
|
1044
|
-
attemptedTxid: built.txid,
|
|
1045
|
-
attemptedWtxid: built.wtxid,
|
|
1046
|
-
temporaryBuilderLockedOutpoints: [],
|
|
1047
|
-
});
|
|
1048
|
-
nextState = {
|
|
1049
|
-
...upsertPendingMutation(nextState, finalMutation),
|
|
1050
|
-
stateRevision: nextState.stateRevision + 1,
|
|
1051
|
-
lastWrittenAtUnixMs: nowUnixMs,
|
|
1052
|
-
};
|
|
1053
|
-
await saveWalletStatePreservingUnlock({
|
|
1054
|
-
state: nextState,
|
|
1055
|
-
provider,
|
|
1056
|
-
nowUnixMs,
|
|
1057
|
-
paths,
|
|
877
|
+
},
|
|
1058
878
|
});
|
|
879
|
+
},
|
|
880
|
+
createResult({ operation, mutation, built, status, reusedExisting, fees }) {
|
|
1059
881
|
return {
|
|
1060
882
|
kind: "sell",
|
|
1061
|
-
domainName: normalizedDomainName,
|
|
1062
|
-
txid: built
|
|
1063
|
-
status:
|
|
1064
|
-
reusedExisting
|
|
1065
|
-
listedPriceCogtoshi:
|
|
883
|
+
domainName: operation.normalizedDomainName,
|
|
884
|
+
txid: mutation.attemptedTxid ?? built?.txid ?? "unknown",
|
|
885
|
+
status: status,
|
|
886
|
+
reusedExisting,
|
|
887
|
+
listedPriceCogtoshi: operation.listedPriceCogtoshi,
|
|
1066
888
|
resolved: {
|
|
1067
|
-
sender: resolvedSender,
|
|
1068
|
-
economicEffect: resolvedEconomicEffect,
|
|
889
|
+
sender: operation.resolvedSender,
|
|
890
|
+
economicEffect: operation.resolvedEconomicEffect,
|
|
1069
891
|
},
|
|
1070
|
-
fees
|
|
1071
|
-
selection: feeSelection,
|
|
1072
|
-
built,
|
|
1073
|
-
}),
|
|
892
|
+
fees,
|
|
1074
893
|
};
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
await miningPreemption.release();
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
finally {
|
|
1082
|
-
await controlLock.release();
|
|
1083
|
-
}
|
|
894
|
+
},
|
|
895
|
+
});
|
|
896
|
+
return execution.result;
|
|
1084
897
|
}
|
|
1085
898
|
export async function sellDomain(options) {
|
|
1086
899
|
if (options.listedPriceCogtoshi < 0n) {
|
|
@@ -1089,270 +902,177 @@ export async function sellDomain(options) {
|
|
|
1089
902
|
return runSellMutation(options);
|
|
1090
903
|
}
|
|
1091
904
|
export async function buyDomain(options) {
|
|
1092
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1093
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1094
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1095
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1096
|
-
purpose: "wallet-buy",
|
|
1097
|
-
walletRootId: null,
|
|
1098
|
-
});
|
|
1099
905
|
const normalizedDomainName = normalizeDomainName(options.domainName);
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
const readContext = await (options.openReadContext ?? openWalletReadContext)({
|
|
1106
|
-
dataDir: options.dataDir,
|
|
1107
|
-
databasePath: options.databasePath,
|
|
1108
|
-
secretProvider: provider,
|
|
1109
|
-
walletControlLockHeld: true,
|
|
1110
|
-
paths,
|
|
1111
|
-
});
|
|
1112
|
-
try {
|
|
906
|
+
const execution = await executeWalletMutationOperation({
|
|
907
|
+
...options,
|
|
908
|
+
controlLockPurpose: "wallet-buy",
|
|
909
|
+
preemptionReason: "wallet-buy",
|
|
910
|
+
resolveOperation(readContext) {
|
|
1113
911
|
const operation = resolveBuyOperation(readContext, normalizedDomainName, options.fromIdentity ?? null);
|
|
1114
|
-
const snapshot = readContext.snapshot;
|
|
1115
912
|
const model = readContext.model;
|
|
1116
913
|
const sellerScriptPubKeyHex = Buffer.from(operation.chainDomain.ownerScriptPubKey).toString("hex");
|
|
1117
914
|
const sellerAddress = sellerScriptPubKeyHex === model.walletScriptPubKeyHex ? model.walletAddress : null;
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
915
|
+
return {
|
|
916
|
+
...operation,
|
|
917
|
+
normalizedDomainName,
|
|
918
|
+
sellerScriptPubKeyHex,
|
|
919
|
+
resolvedBuyer: {
|
|
920
|
+
selector: operation.buyerSelector,
|
|
921
|
+
localIndex: operation.sender.localIndex,
|
|
922
|
+
scriptPubKeyHex: operation.sender.scriptPubKeyHex,
|
|
923
|
+
address: operation.sender.address,
|
|
924
|
+
},
|
|
925
|
+
resolvedSeller: {
|
|
926
|
+
scriptPubKeyHex: sellerScriptPubKeyHex,
|
|
927
|
+
address: sellerAddress,
|
|
928
|
+
},
|
|
1127
929
|
};
|
|
1128
|
-
|
|
930
|
+
},
|
|
931
|
+
createIntentFingerprint(operation) {
|
|
932
|
+
return createIntentFingerprint([
|
|
1129
933
|
"buy",
|
|
1130
934
|
operation.state.walletRootId,
|
|
1131
|
-
normalizedDomainName,
|
|
935
|
+
operation.normalizedDomainName,
|
|
1132
936
|
operation.sender.scriptPubKeyHex,
|
|
1133
937
|
operation.listingPriceCogtoshi.toString(),
|
|
1134
938
|
]);
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
walletRootId: operation.state.walletRootId,
|
|
1140
|
-
});
|
|
1141
|
-
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
1142
|
-
const walletName = operation.state.managedCoreWallet.walletName;
|
|
1143
|
-
const feeSelection = await resolveWalletMutationFeeSelection({
|
|
1144
|
-
rpc,
|
|
1145
|
-
feeRateSatVb: options.feeRateSatVb ?? null,
|
|
1146
|
-
});
|
|
1147
|
-
const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
|
|
1148
|
-
let workingState = operation.state;
|
|
1149
|
-
let replacementFixedInputs = null;
|
|
1150
|
-
if (existingMutation !== null) {
|
|
1151
|
-
const reconciled = await reconcilePendingMutation({
|
|
1152
|
-
state: operation.state,
|
|
1153
|
-
mutation: existingMutation,
|
|
1154
|
-
provider,
|
|
1155
|
-
nowUnixMs,
|
|
1156
|
-
paths,
|
|
1157
|
-
rpc,
|
|
1158
|
-
walletName,
|
|
1159
|
-
context: readContext,
|
|
1160
|
-
});
|
|
1161
|
-
workingState = reconciled.state;
|
|
1162
|
-
if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
|
|
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;
|
|
1183
|
-
}
|
|
1184
|
-
if (reconciled.resolution === "repair-required") {
|
|
1185
|
-
throw new Error("wallet_buy_repair_required");
|
|
1186
|
-
}
|
|
939
|
+
},
|
|
940
|
+
async resolveExistingMutation({ operation, existingMutation, execution }) {
|
|
941
|
+
if (existingMutation === null) {
|
|
942
|
+
return { state: operation.state, replacementFixedInputs: null, result: null };
|
|
1187
943
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
944
|
+
return resolveExistingWalletMutation({
|
|
945
|
+
existingMutation,
|
|
946
|
+
execution,
|
|
947
|
+
repairRequiredErrorCode: "wallet_buy_repair_required",
|
|
948
|
+
reconcileExistingMutation: (mutation) => reconcilePendingMutation({
|
|
949
|
+
state: operation.state,
|
|
950
|
+
mutation,
|
|
951
|
+
provider: execution.provider,
|
|
952
|
+
nowUnixMs: execution.nowUnixMs,
|
|
953
|
+
paths: execution.paths,
|
|
954
|
+
rpc: execution.rpc,
|
|
955
|
+
walletName: execution.walletName,
|
|
956
|
+
context: execution.readContext,
|
|
957
|
+
}),
|
|
958
|
+
createReuseResult: ({ mutation, resolution, fees }) => ({
|
|
959
|
+
kind: "buy",
|
|
960
|
+
domainName: operation.normalizedDomainName,
|
|
961
|
+
txid: mutation.attemptedTxid ?? "unknown",
|
|
962
|
+
status: resolution,
|
|
963
|
+
reusedExisting: true,
|
|
964
|
+
listedPriceCogtoshi: operation.listingPriceCogtoshi,
|
|
965
|
+
resolvedBuyer: operation.resolvedBuyer,
|
|
966
|
+
resolvedSeller: operation.resolvedSeller,
|
|
967
|
+
fees,
|
|
968
|
+
}),
|
|
1209
969
|
});
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
970
|
+
},
|
|
971
|
+
confirm({ operation }) {
|
|
972
|
+
return confirmBuy(options.prompter, operation.normalizedDomainName, operation.buyerSelector, operation.sender, operation.sellerScriptPubKeyHex, operation.resolvedSeller.address, operation.listingPriceCogtoshi, options.assumeYes);
|
|
973
|
+
},
|
|
974
|
+
createDraftMutation({ operation, existingMutation, execution, intentFingerprintHex }) {
|
|
975
|
+
return {
|
|
976
|
+
mutation: createDraftMutation({
|
|
977
|
+
kind: "buy",
|
|
978
|
+
domainName: operation.normalizedDomainName,
|
|
979
|
+
sender: operation.sender,
|
|
980
|
+
priceCogtoshi: operation.listingPriceCogtoshi,
|
|
981
|
+
intentFingerprintHex,
|
|
982
|
+
nowUnixMs: execution.nowUnixMs,
|
|
983
|
+
feeSelection: execution.feeSelection,
|
|
984
|
+
existing: existingMutation,
|
|
985
|
+
}),
|
|
986
|
+
prepared: null,
|
|
987
|
+
};
|
|
988
|
+
},
|
|
989
|
+
async prepareBuildState({ state, execution }) {
|
|
990
|
+
return (await prepareDomainMarketBuildState({
|
|
991
|
+
rpc: execution.rpc,
|
|
992
|
+
walletName: execution.walletName,
|
|
993
|
+
state,
|
|
994
|
+
provider: execution.provider,
|
|
995
|
+
nowUnixMs: execution.nowUnixMs,
|
|
996
|
+
paths: execution.paths,
|
|
1217
997
|
preflightCoinControl: false,
|
|
1218
|
-
});
|
|
1219
|
-
|
|
998
|
+
})).state;
|
|
999
|
+
},
|
|
1000
|
+
async build({ operation, state, execution, replacementFixedInputs }) {
|
|
1220
1001
|
const buyPlan = buildPlanForDomainOperation({
|
|
1221
|
-
state
|
|
1222
|
-
allUtxos:
|
|
1002
|
+
state,
|
|
1003
|
+
allUtxos: await execution.rpc.listUnspent(execution.walletName, 1),
|
|
1223
1004
|
sender: operation.sender,
|
|
1224
1005
|
opReturnData: serializeDomainBuy(operation.chainDomain.domainId, operation.listingPriceCogtoshi).opReturnData,
|
|
1225
1006
|
errorPrefix: "wallet_buy",
|
|
1226
1007
|
});
|
|
1227
|
-
|
|
1228
|
-
rpc,
|
|
1229
|
-
walletName,
|
|
1230
|
-
state
|
|
1008
|
+
return buildTransaction({
|
|
1009
|
+
rpc: execution.rpc,
|
|
1010
|
+
walletName: execution.walletName,
|
|
1011
|
+
state,
|
|
1231
1012
|
plan: {
|
|
1232
1013
|
...buyPlan,
|
|
1233
1014
|
fixedInputs: mergeFixedWalletInputs(buyPlan.fixedInputs, replacementFixedInputs),
|
|
1234
1015
|
},
|
|
1235
|
-
feeRateSatVb: feeSelection.feeRateSatVb,
|
|
1016
|
+
feeRateSatVb: execution.feeSelection.feeRateSatVb,
|
|
1236
1017
|
});
|
|
1018
|
+
},
|
|
1019
|
+
beforePublish({ operation }) {
|
|
1237
1020
|
const currentSellerHex = Buffer.from(operation.chainDomain.ownerScriptPubKey).toString("hex");
|
|
1238
|
-
if (currentSellerHex !== sellerScriptPubKeyHex) {
|
|
1239
|
-
await unlockTemporaryBuilderLocks(rpc, walletName, built.temporaryBuilderLockedOutpoints);
|
|
1021
|
+
if (currentSellerHex !== operation.sellerScriptPubKeyHex) {
|
|
1240
1022
|
throw new Error("wallet_buy_stale_listing_owner");
|
|
1241
1023
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
await rpc.sendRawTransaction(built.rawHex);
|
|
1264
|
-
}
|
|
1265
|
-
catch (error) {
|
|
1266
|
-
if (!isAlreadyAcceptedError(error)) {
|
|
1267
|
-
if (isBroadcastUnknownError(error)) {
|
|
1268
|
-
const unknown = updateMutationRecord(broadcasting, "broadcast-unknown", nowUnixMs, {
|
|
1269
|
-
attemptedTxid: built.txid,
|
|
1270
|
-
attemptedWtxid: built.wtxid,
|
|
1271
|
-
temporaryBuilderLockedOutpoints: built.temporaryBuilderLockedOutpoints,
|
|
1272
|
-
});
|
|
1273
|
-
nextState = {
|
|
1274
|
-
...upsertPendingMutation(nextState, unknown),
|
|
1275
|
-
stateRevision: nextState.stateRevision + 1,
|
|
1276
|
-
lastWrittenAtUnixMs: nowUnixMs,
|
|
1277
|
-
};
|
|
1278
|
-
await saveWalletStatePreservingUnlock({
|
|
1279
|
-
state: nextState,
|
|
1280
|
-
provider,
|
|
1281
|
-
nowUnixMs,
|
|
1282
|
-
paths,
|
|
1283
|
-
});
|
|
1284
|
-
throw new Error("wallet_buy_broadcast_unknown");
|
|
1285
|
-
}
|
|
1286
|
-
await unlockTemporaryBuilderLocks(rpc, walletName, built.temporaryBuilderLockedOutpoints);
|
|
1287
|
-
const canceled = updateMutationRecord(broadcasting, "canceled", nowUnixMs, {
|
|
1024
|
+
return Promise.resolve();
|
|
1025
|
+
},
|
|
1026
|
+
publish({ operation, state, execution, built, mutation }) {
|
|
1027
|
+
return publishWalletMutation({
|
|
1028
|
+
rpc: execution.rpc,
|
|
1029
|
+
walletName: execution.walletName,
|
|
1030
|
+
snapshotHeight: execution.readContext.snapshot?.tip?.height ?? null,
|
|
1031
|
+
built,
|
|
1032
|
+
mutation,
|
|
1033
|
+
state,
|
|
1034
|
+
provider: execution.provider,
|
|
1035
|
+
nowUnixMs: execution.nowUnixMs,
|
|
1036
|
+
paths: execution.paths,
|
|
1037
|
+
errorPrefix: "wallet_buy",
|
|
1038
|
+
async afterAccepted({ state: acceptedState, broadcastingMutation, built, nowUnixMs }) {
|
|
1039
|
+
const finalStatus = getBuyStatusAfterAcceptance({
|
|
1040
|
+
snapshot: execution.readContext.snapshot,
|
|
1041
|
+
domainName: operation.normalizedDomainName,
|
|
1042
|
+
buyerScriptPubKeyHex: operation.sender.scriptPubKeyHex,
|
|
1043
|
+
});
|
|
1044
|
+
const finalMutation = updateMutationRecord(broadcastingMutation, finalStatus, nowUnixMs, {
|
|
1288
1045
|
attemptedTxid: built.txid,
|
|
1289
1046
|
attemptedWtxid: built.wtxid,
|
|
1290
1047
|
temporaryBuilderLockedOutpoints: [],
|
|
1291
1048
|
});
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1049
|
+
return {
|
|
1050
|
+
state: reserveTransferredDomainRecord({
|
|
1051
|
+
state: upsertPendingMutation(acceptedState, finalMutation),
|
|
1052
|
+
domainName: operation.normalizedDomainName,
|
|
1053
|
+
domainId: operation.chainDomain.domainId,
|
|
1054
|
+
currentOwnerScriptPubKeyHex: operation.sender.scriptPubKeyHex,
|
|
1055
|
+
nowUnixMs,
|
|
1056
|
+
}),
|
|
1057
|
+
mutation: finalMutation,
|
|
1058
|
+
status: finalStatus,
|
|
1296
1059
|
};
|
|
1297
|
-
|
|
1298
|
-
state: nextState,
|
|
1299
|
-
provider,
|
|
1300
|
-
nowUnixMs,
|
|
1301
|
-
paths,
|
|
1302
|
-
});
|
|
1303
|
-
throw error;
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
await unlockTemporaryBuilderLocks(rpc, walletName, built.temporaryBuilderLockedOutpoints);
|
|
1307
|
-
const finalStatus = getBuyStatusAfterAcceptance({
|
|
1308
|
-
snapshot: readContext.snapshot,
|
|
1309
|
-
domainName: normalizedDomainName,
|
|
1310
|
-
buyerScriptPubKeyHex: operation.sender.scriptPubKeyHex,
|
|
1311
|
-
});
|
|
1312
|
-
const finalMutation = updateMutationRecord(broadcasting, finalStatus, nowUnixMs, {
|
|
1313
|
-
attemptedTxid: built.txid,
|
|
1314
|
-
attemptedWtxid: built.wtxid,
|
|
1315
|
-
temporaryBuilderLockedOutpoints: [],
|
|
1316
|
-
});
|
|
1317
|
-
nextState = reserveTransferredDomainRecord({
|
|
1318
|
-
state: upsertPendingMutation(nextState, finalMutation),
|
|
1319
|
-
domainName: normalizedDomainName,
|
|
1320
|
-
domainId: operation.chainDomain.domainId,
|
|
1321
|
-
currentOwnerScriptPubKeyHex: operation.sender.scriptPubKeyHex,
|
|
1322
|
-
nowUnixMs,
|
|
1323
|
-
});
|
|
1324
|
-
nextState = {
|
|
1325
|
-
...nextState,
|
|
1326
|
-
stateRevision: nextState.stateRevision + 1,
|
|
1327
|
-
lastWrittenAtUnixMs: nowUnixMs,
|
|
1328
|
-
};
|
|
1329
|
-
await saveWalletStatePreservingUnlock({
|
|
1330
|
-
state: nextState,
|
|
1331
|
-
provider,
|
|
1332
|
-
nowUnixMs,
|
|
1333
|
-
paths,
|
|
1060
|
+
},
|
|
1334
1061
|
});
|
|
1062
|
+
},
|
|
1063
|
+
createResult({ operation, mutation, built, status, reusedExisting, fees }) {
|
|
1335
1064
|
return {
|
|
1336
1065
|
kind: "buy",
|
|
1337
|
-
domainName: normalizedDomainName,
|
|
1338
|
-
txid: built
|
|
1339
|
-
status:
|
|
1340
|
-
reusedExisting
|
|
1066
|
+
domainName: operation.normalizedDomainName,
|
|
1067
|
+
txid: mutation.attemptedTxid ?? built?.txid ?? "unknown",
|
|
1068
|
+
status: status,
|
|
1069
|
+
reusedExisting,
|
|
1341
1070
|
listedPriceCogtoshi: operation.listingPriceCogtoshi,
|
|
1342
|
-
resolvedBuyer,
|
|
1343
|
-
resolvedSeller,
|
|
1344
|
-
fees
|
|
1345
|
-
selection: feeSelection,
|
|
1346
|
-
built,
|
|
1347
|
-
}),
|
|
1071
|
+
resolvedBuyer: operation.resolvedBuyer,
|
|
1072
|
+
resolvedSeller: operation.resolvedSeller,
|
|
1073
|
+
fees,
|
|
1348
1074
|
};
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
await miningPreemption.release();
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
finally {
|
|
1356
|
-
await controlLock.release();
|
|
1357
|
-
}
|
|
1075
|
+
},
|
|
1076
|
+
});
|
|
1077
|
+
return execution.result;
|
|
1358
1078
|
}
|