@arkade-os/sdk 0.4.47 → 0.4.48
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/dist/adapters/expo.cjs +5 -5
- package/dist/adapters/expo.d.cts +2 -2
- package/dist/adapters/expo.d.ts +2 -2
- package/dist/adapters/expo.js +3 -3
- package/dist/adapters/indexedDB.cjs +5 -5
- package/dist/adapters/indexedDB.js +4 -4
- package/dist/{ark-CoJgwTi_.d.cts → ark-CjE0KkGp.d.cts} +405 -250
- package/dist/{ark-CoJgwTi_.d.ts → ark-CjE0KkGp.d.ts} +405 -250
- package/dist/{asyncStorageTaskQueue-Dowyo59a.d.cts → asyncStorageTaskQueue-CeLZhZFU.d.cts} +2 -2
- package/dist/{asyncStorageTaskQueue-Z3aFoXCl.d.ts → asyncStorageTaskQueue-D9SOPMPC.d.ts} +2 -2
- package/dist/{chunk-XN3554FT.js → chunk-4VVTRPH3.js} +4 -4
- package/dist/chunk-4VVTRPH3.js.map +1 -0
- package/dist/{chunk-RQSA7ADZ.cjs → chunk-5XMVTK5S.cjs} +37 -37
- package/dist/chunk-5XMVTK5S.cjs.map +1 -0
- package/dist/{chunk-L5ND2FJJ.cjs → chunk-7TXSPRN5.cjs} +161 -2
- package/dist/chunk-7TXSPRN5.cjs.map +1 -0
- package/dist/{chunk-CDDWXBEM.js → chunk-BHHLRZCS.js} +169 -42
- package/dist/chunk-BHHLRZCS.js.map +1 -0
- package/dist/{chunk-HHV5FPC6.cjs → chunk-EB3ALGTM.cjs} +178 -50
- package/dist/chunk-EB3ALGTM.cjs.map +1 -0
- package/dist/{chunk-JAYOXOLT.cjs → chunk-ENDNQPZA.cjs} +17 -44
- package/dist/chunk-ENDNQPZA.cjs.map +1 -0
- package/dist/{chunk-WTTLIFS7.cjs → chunk-ENZ4YPR2.cjs} +944 -544
- package/dist/chunk-ENZ4YPR2.cjs.map +1 -0
- package/dist/{chunk-3N5I6GT6.js → chunk-G6J6AEMH.js} +5 -4
- package/dist/chunk-G6J6AEMH.js.map +1 -0
- package/dist/{chunk-OCS5S3L3.js → chunk-NYR5H6GR.js} +685 -284
- package/dist/chunk-NYR5H6GR.js.map +1 -0
- package/dist/{chunk-VE67GMGM.cjs → chunk-SYDANBJI.cjs} +9 -8
- package/dist/chunk-SYDANBJI.cjs.map +1 -0
- package/dist/{chunk-AJQ4AOT3.js → chunk-XFMGIFBL.js} +4 -31
- package/dist/chunk-XFMGIFBL.js.map +1 -0
- package/dist/{chunk-C7TXXTMS.js → chunk-ZDDBTFVO.js} +146 -3
- package/dist/chunk-ZDDBTFVO.js.map +1 -0
- package/dist/contracts/handlers/index.cjs +8 -8
- package/dist/contracts/handlers/index.d.cts +3 -3
- package/dist/contracts/handlers/index.d.ts +3 -3
- package/dist/contracts/handlers/index.js +2 -2
- package/dist/{delegate-Dl87_zMA.d.cts → delegate-D3dY-2vS.d.cts} +1 -1
- package/dist/{delegate-CuaDqqQi.d.ts → delegate-DhGZk1r2.d.ts} +1 -1
- package/dist/{index-BC_G0VTh.d.cts → index-D6RQROds.d.cts} +2 -2
- package/dist/{index-CKwpZEpw.d.ts → index-DK4_iWYy.d.ts} +2 -2
- package/dist/index.cjs +211 -183
- package/dist/index.d.cts +61 -11
- package/dist/index.d.ts +61 -11
- package/dist/index.js +4 -4
- package/dist/repositories/realm/index.cjs +19 -19
- package/dist/repositories/realm/index.d.cts +2 -2
- package/dist/repositories/realm/index.d.ts +2 -2
- package/dist/repositories/realm/index.js +5 -5
- package/dist/repositories/sqlite/index.cjs +18 -18
- package/dist/repositories/sqlite/index.d.cts +2 -2
- package/dist/repositories/sqlite/index.d.ts +2 -2
- package/dist/repositories/sqlite/index.js +5 -5
- package/dist/{taskRunner-Dz-TI4Rb.d.ts → taskRunner-B-2MHCYG.d.ts} +2 -2
- package/dist/{taskRunner-Dz47k0lp.d.cts → taskRunner-Brnak0Rc.d.cts} +2 -2
- package/dist/wallet/expo/background.cjs +14 -14
- package/dist/wallet/expo/background.d.cts +3 -3
- package/dist/wallet/expo/background.d.ts +3 -3
- package/dist/wallet/expo/background.js +6 -6
- package/dist/wallet/expo/index.cjs +14 -14
- package/dist/wallet/expo/index.cjs.map +1 -1
- package/dist/wallet/expo/index.d.cts +5 -5
- package/dist/wallet/expo/index.d.ts +5 -5
- package/dist/wallet/expo/index.js +5 -5
- package/dist/wallet/expo/index.js.map +1 -1
- package/dist/{wallet-Oxz_wLxo.d.cts → wallet-B5bwixQa.d.ts} +130 -7
- package/dist/{wallet-x7egmXq6.d.ts → wallet-DiOrtYic.d.cts} +130 -7
- package/dist/worker/expo/index.cjs +9 -9
- package/dist/worker/expo/index.d.cts +4 -4
- package/dist/worker/expo/index.d.ts +4 -4
- package/dist/worker/expo/index.js +5 -5
- package/package.json +3 -2
- package/dist/chunk-3N5I6GT6.js.map +0 -1
- package/dist/chunk-AJQ4AOT3.js.map +0 -1
- package/dist/chunk-C7TXXTMS.js.map +0 -1
- package/dist/chunk-CDDWXBEM.js.map +0 -1
- package/dist/chunk-HHV5FPC6.cjs.map +0 -1
- package/dist/chunk-JAYOXOLT.cjs.map +0 -1
- package/dist/chunk-L5ND2FJJ.cjs.map +0 -1
- package/dist/chunk-OCS5S3L3.js.map +0 -1
- package/dist/chunk-RQSA7ADZ.cjs.map +0 -1
- package/dist/chunk-VE67GMGM.cjs.map +0 -1
- package/dist/chunk-WTTLIFS7.cjs.map +0 -1
- package/dist/chunk-XN3554FT.js.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Transaction, setArkPsbtField, PrevArkTxField, ConditionWitness, AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, craftToSpendTx, getArkPsbtFields, CosignerPublicKey, BufferReader, BufferWriter, OP_RETURN_EMPTY_PKSCRIPT, VtxoTaprootTree, baseFetch, Intent, maybeArkError, isArkError, ArkErrorName, ProviderUnavailableError, FetchError, isFetchTimeoutError, AssetRef, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError } from './chunk-
|
|
2
|
-
import { witnessRefToBytes, validateProgram, toASM, stringifyArtifact, serializeArkadeContractParams, resolveAsm, parseArtifact, getOpcodeValue, getOpcodeName, fromASM, deserializeArkadeContractParams, computeArkadeScriptPublicKey, bytesToASM, asmToBytes, arkadeWitnessHash, arkadeScriptHash, SUPPORTED_PROGRAM_VERSION, OPCODE_VALUES, OPCODE_NAMES, bignum_exports, ArkadeScript, ArkadeProgramScript, ARKADE_OPS, ARKADE_OPCODE_VALUES, ARKADE_OPCODE_NAMES, ARKADE_OPCODES, ARKADE_OP, inputName, contractHandlers, isMainnetDescriptor, descriptorIsOurs, DEFAULT_PAGE_SIZE, DelegateVtxo, WALLET_RECEIVE_SOURCE, deriveDescriptorLeafPubKey,
|
|
3
|
-
import { VtxoScript, CSVMultisigTapscript, DEFAULT_NETWORK, ArkAddress, sequenceToTimelock, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, timelockToSequence, DEFAULT_NETWORK_NAME, getSequence, getNetwork, MultisigTapscript, networks as networks$1, DEFAULT_ARKADE_SERVER_URL } from './chunk-
|
|
1
|
+
import { Transaction, setArkPsbtField, PrevArkTxField, ConditionWitness, AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, craftToSpendTx, getArkPsbtFields, CosignerPublicKey, BufferReader, BufferWriter, OP_RETURN_EMPTY_PKSCRIPT, VtxoTaprootTree, baseFetch, Intent, rateGate, maybeArkError, isArkError, ArkErrorName, ProviderUnavailableError, FetchError, isFetchTimeoutError, AssetRef, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError } from './chunk-BHHLRZCS.js';
|
|
2
|
+
import { witnessRefToBytes, validateProgram, toASM, stringifyArtifact, serializeArkadeContractParams, resolveAsm, parseArtifact, getOpcodeValue, getOpcodeName, fromASM, deserializeArkadeContractParams, computeArkadeScriptPublicKey, bytesToASM, asmToBytes, arkadeWitnessHash, arkadeScriptHash, SUPPORTED_PROGRAM_VERSION, OPCODE_VALUES, OPCODE_NAMES, bignum_exports, ArkadeScript, ArkadeProgramScript, ARKADE_OPS, ARKADE_OPCODE_VALUES, ARKADE_OPCODE_NAMES, ARKADE_OPCODES, ARKADE_OP, inputName, contractHandlers, isMainnetDescriptor, descriptorIsOurs, DefaultVtxo, DEFAULT_PAGE_SIZE, DelegateVtxo, WALLET_RECEIVE_SOURCE, deriveDescriptorLeafPubKey, BoardingContractHandler } from './chunk-4VVTRPH3.js';
|
|
3
|
+
import { VtxoScript, isRecoverable, CSVMultisigTapscript, DEFAULT_NETWORK, hasTerminalSpend, getNormalizedVtxos, isVirtualCoin, ArkAddress, sequenceToTimelock, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, timelockToSequence, DEFAULT_NETWORK_NAME, normalizeVtxo, getSequence, toBatchExpiry, toOffchainInputFeeParams, canRecoverOnchain, getNetwork, MultisigTapscript, canSpendOffchain, toVirtualStatus, resolveTimeHeight, parseLegacyExpiry, networks as networks$1, isExpired, DEFAULT_ARKADE_SERVER_URL, isPastExpiry } from './chunk-ZDDBTFVO.js';
|
|
4
4
|
import { __export } from './chunk-NSBPE2FW.js';
|
|
5
5
|
import { sha256, equalBytes, hash160, sha256x2, concatBytes, randomPrivateKeyBytes, pubECDSA, pubSchnorr } from '@scure/btc-signer/utils.js';
|
|
6
6
|
import { SigHash, Script, OP, OutScript, Address, RawWitness, p2tr, p2wpkh, TaprootControlBlock, DEFAULT_SEQUENCE, Transaction as Transaction$2 } from '@scure/btc-signer';
|
|
7
|
-
import { hex, base58, base64 } from '@scure/base';
|
|
7
|
+
import { hex, base58, base64, bech32m } from '@scure/base';
|
|
8
8
|
import * as musig2 from '@scure/btc-signer/musig2.js';
|
|
9
9
|
import { equalBytes as equalBytes$1, bytesToNumberBE } from '@noble/curves/utils.js';
|
|
10
10
|
import { signAsync, schnorr as schnorr$1, Point } from '@noble/secp256k1';
|
|
@@ -991,7 +991,7 @@ var RestDelegateProvider = class {
|
|
|
991
991
|
*/
|
|
992
992
|
async getDelegateInfo() {
|
|
993
993
|
const url = `${this.url}/v1/delegator/info`;
|
|
994
|
-
const response = await baseFetch(url);
|
|
994
|
+
const response = await rateGate.runHttp(url, () => baseFetch(url));
|
|
995
995
|
if (!response.ok) {
|
|
996
996
|
const errorText2 = await response.text();
|
|
997
997
|
throw new Error(`Failed to get delegate info: ${errorText2}`);
|
|
@@ -2042,20 +2042,6 @@ var TxType = /* @__PURE__ */ ((TxType2) => {
|
|
|
2042
2042
|
TxType2["TxReceived"] = "RECEIVED";
|
|
2043
2043
|
return TxType2;
|
|
2044
2044
|
})(TxType || {});
|
|
2045
|
-
function isSpendable(vtxo) {
|
|
2046
|
-
return !vtxo.isSpent;
|
|
2047
|
-
}
|
|
2048
|
-
function isRecoverable(vtxo) {
|
|
2049
|
-
return vtxo.virtualStatus.state === "swept" && isSpendable(vtxo);
|
|
2050
|
-
}
|
|
2051
|
-
function isExpired(vtxo) {
|
|
2052
|
-
if (vtxo.virtualStatus.state === "swept") return true;
|
|
2053
|
-
const expiry = vtxo.virtualStatus.batchExpiry;
|
|
2054
|
-
if (!expiry) return false;
|
|
2055
|
-
const expireAt = new Date(expiry);
|
|
2056
|
-
if (expireAt.getFullYear() < 2025) return false;
|
|
2057
|
-
return expiry <= Date.now();
|
|
2058
|
-
}
|
|
2059
2045
|
function isSubdust(vtxo, dust) {
|
|
2060
2046
|
if (typeof vtxo === "bigint") return vtxo < dust;
|
|
2061
2047
|
return vtxo.value < dust;
|
|
@@ -2437,6 +2423,15 @@ async function submitOffchainTx(provider, offchainTx, signer, hooks) {
|
|
|
2437
2423
|
await hooks?.afterFinalize?.();
|
|
2438
2424
|
return { arkTxid, signedCheckpointTxs };
|
|
2439
2425
|
}
|
|
2426
|
+
async function signAndSubmitOffchainTx(params) {
|
|
2427
|
+
const offchainTx = buildOffchainTx(params.inputs, params.outputs, params.serverUnrollScript);
|
|
2428
|
+
const signer = {
|
|
2429
|
+
signArkTx: async (arkTx) => ({ arkTx: await params.identity.sign(arkTx) }),
|
|
2430
|
+
signCheckpoint: (checkpoint) => params.identity.sign(checkpoint)
|
|
2431
|
+
};
|
|
2432
|
+
const { arkTxid } = await submitOffchainTx(params.provider, offchainTx, signer);
|
|
2433
|
+
return arkTxid;
|
|
2434
|
+
}
|
|
2440
2435
|
var AmountVariableName = "amount";
|
|
2441
2436
|
var ExpiryVariableName = "expiry";
|
|
2442
2437
|
var BirthVariableName = "birth";
|
|
@@ -2716,7 +2711,7 @@ function selectPendingRecoveryOutpoints(contractsWithVtxos, signerSet, nowSecond
|
|
|
2716
2711
|
continue;
|
|
2717
2712
|
}
|
|
2718
2713
|
for (const v of vtxos) {
|
|
2719
|
-
if (
|
|
2714
|
+
if (!hasTerminalSpend(v) && !v.isSwept) out.add(`${v.txid}:${v.vout}`);
|
|
2720
2715
|
}
|
|
2721
2716
|
}
|
|
2722
2717
|
return out;
|
|
@@ -2744,19 +2739,32 @@ async function runWithCrossInstanceLock(name, fn) {
|
|
|
2744
2739
|
});
|
|
2745
2740
|
}
|
|
2746
2741
|
var MAX_VTXOS_PER_SETTLEMENT = 50;
|
|
2742
|
+
var MAX_INPUTS_PER_INTENT = 20;
|
|
2747
2743
|
function byValueDescending(vtxos) {
|
|
2748
2744
|
return [...vtxos].sort((a, b) => b.value - a.value);
|
|
2749
2745
|
}
|
|
2750
|
-
function
|
|
2746
|
+
async function fetchTimeHeight(wallet) {
|
|
2747
|
+
return resolveTimeHeight(wallet.onchainProvider);
|
|
2748
|
+
}
|
|
2749
|
+
function canMigrateBySend(vtxo, now) {
|
|
2750
|
+
if (!canSpendOffchain(vtxo, now)) return false;
|
|
2751
|
+
return vtxo.expiresAt !== void 0 || vtxo.expiresAtHeight !== void 0;
|
|
2752
|
+
}
|
|
2753
|
+
function byExpiryAscending(vtxos, now) {
|
|
2751
2754
|
const expiryKey = (vtxo) => {
|
|
2752
|
-
if (
|
|
2753
|
-
|
|
2754
|
-
if (
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
return
|
|
2755
|
+
if (vtxo.isSwept) return -Infinity;
|
|
2756
|
+
if (vtxo.expiresAt !== void 0) return vtxo.expiresAt.getTime();
|
|
2757
|
+
if (vtxo.expiresAtHeight !== void 0) {
|
|
2758
|
+
return isPastExpiry(vtxo, now) ? -Infinity : Infinity;
|
|
2759
|
+
}
|
|
2760
|
+
return Infinity;
|
|
2758
2761
|
};
|
|
2759
|
-
return [...vtxos].sort((a, b) =>
|
|
2762
|
+
return [...vtxos].sort((a, b) => {
|
|
2763
|
+
const ka = expiryKey(a);
|
|
2764
|
+
const kb = expiryKey(b);
|
|
2765
|
+
if (ka === kb) return 0;
|
|
2766
|
+
return ka < kb ? -1 : 1;
|
|
2767
|
+
});
|
|
2760
2768
|
}
|
|
2761
2769
|
function capSettlementBatch(sorted, maxAmount) {
|
|
2762
2770
|
const batch = [];
|
|
@@ -2782,22 +2790,19 @@ var DEFAULT_SETTLEMENT_CONFIG = {
|
|
|
2782
2790
|
pollIntervalMs: 6e4,
|
|
2783
2791
|
deprecatedSignerMigration: true
|
|
2784
2792
|
};
|
|
2785
|
-
function getRecoverableVtxos(vtxos, dustAmount) {
|
|
2793
|
+
function getRecoverableVtxos(vtxos, dustAmount, now) {
|
|
2786
2794
|
return vtxos.filter((vtxo) => {
|
|
2787
|
-
if (
|
|
2795
|
+
if (canRecoverOnchain(vtxo, now)) {
|
|
2788
2796
|
return true;
|
|
2789
2797
|
}
|
|
2790
|
-
if (
|
|
2791
|
-
return true;
|
|
2792
|
-
}
|
|
2793
|
-
if (vtxo.virtualStatus.state === "preconfirmed" && isSubdust(vtxo, dustAmount)) {
|
|
2798
|
+
if (canSpendOffchain(vtxo, now) && vtxo.isPreconfirmed && isSubdust(vtxo, dustAmount)) {
|
|
2794
2799
|
return true;
|
|
2795
2800
|
}
|
|
2796
2801
|
return false;
|
|
2797
2802
|
});
|
|
2798
2803
|
}
|
|
2799
|
-
function getRecoverableWithSubdust(vtxos, dustAmount) {
|
|
2800
|
-
const recoverableVtxos = getRecoverableVtxos(vtxos, dustAmount);
|
|
2804
|
+
function getRecoverableWithSubdust(vtxos, dustAmount, now) {
|
|
2805
|
+
const recoverableVtxos = getRecoverableVtxos(vtxos, dustAmount, now);
|
|
2801
2806
|
const subdust = [];
|
|
2802
2807
|
const regular = [];
|
|
2803
2808
|
for (const vtxo of recoverableVtxos) {
|
|
@@ -2821,17 +2826,16 @@ function getRecoverableWithSubdust(vtxos, dustAmount) {
|
|
|
2821
2826
|
}
|
|
2822
2827
|
function isVtxoExpiringSoon(vtxo, thresholdMs) {
|
|
2823
2828
|
const realThresholdMs = thresholdMs <= 100 ? DEFAULT_THRESHOLD_MS : thresholdMs;
|
|
2824
|
-
const
|
|
2825
|
-
if (
|
|
2826
|
-
const expireAt = new Date(batchExpiry);
|
|
2827
|
-
if (expireAt.getFullYear() < 2025) return false;
|
|
2829
|
+
const expiresAt = normalizeVtxo(vtxo).expiresAt;
|
|
2830
|
+
if (expiresAt === void 0) return false;
|
|
2828
2831
|
const now = Date.now();
|
|
2832
|
+
const batchExpiry = expiresAt.getTime();
|
|
2829
2833
|
if (batchExpiry <= now) return false;
|
|
2830
2834
|
return batchExpiry - now <= realThresholdMs;
|
|
2831
2835
|
}
|
|
2832
|
-
function getExpiringAndRecoverableVtxos(vtxos, thresholdMs, dustAmount) {
|
|
2836
|
+
function getExpiringAndRecoverableVtxos(vtxos, thresholdMs, dustAmount, now) {
|
|
2833
2837
|
return vtxos.filter(
|
|
2834
|
-
(vtxo) => isVtxoExpiringSoon(vtxo, thresholdMs) ||
|
|
2838
|
+
(vtxo) => isVtxoExpiringSoon(vtxo, thresholdMs) || canRecoverOnchain(vtxo, now) || isSubdust(vtxo, dustAmount)
|
|
2835
2839
|
);
|
|
2836
2840
|
}
|
|
2837
2841
|
function isMigrationCapable(wallet) {
|
|
@@ -2980,7 +2984,8 @@ var VtxoManager = class _VtxoManager {
|
|
|
2980
2984
|
withUnrolled: false
|
|
2981
2985
|
});
|
|
2982
2986
|
const dustAmount = getDustAmount(this.wallet);
|
|
2983
|
-
|
|
2987
|
+
const now = await fetchTimeHeight(this.wallet);
|
|
2988
|
+
let { vtxosToRecover, totalAmount } = getRecoverableWithSubdust(allVtxos, dustAmount, now);
|
|
2984
2989
|
if (vtxosToRecover.length === 0) {
|
|
2985
2990
|
throw new Error("No recoverable VTXOs found");
|
|
2986
2991
|
}
|
|
@@ -2989,7 +2994,7 @@ var VtxoManager = class _VtxoManager {
|
|
|
2989
2994
|
const capped = capSettlementBatch(byValueDescending(vtxosToRecover), vtxoMaxAmount);
|
|
2990
2995
|
if (capped.length < vtxosToRecover.length) {
|
|
2991
2996
|
const recoverableCount = vtxosToRecover.length;
|
|
2992
|
-
({ vtxosToRecover, totalAmount } = getRecoverableWithSubdust(capped, dustAmount));
|
|
2997
|
+
({ vtxosToRecover, totalAmount } = getRecoverableWithSubdust(capped, dustAmount, now));
|
|
2993
2998
|
if (vtxosToRecover.length === 0) {
|
|
2994
2999
|
throw new Error(
|
|
2995
3000
|
`Capped recovery batch (highest-value subset of ${recoverableCount} recoverable VTXOs within the ${MAX_VTXOS_PER_SETTLEMENT}-input and ${vtxoMaxAmount}-sat limits) is below the dust threshold ${dustAmount}`
|
|
@@ -3041,7 +3046,8 @@ var VtxoManager = class _VtxoManager {
|
|
|
3041
3046
|
const dustAmount = getDustAmount(this.wallet);
|
|
3042
3047
|
const { vtxosToRecover, includesSubdust, totalAmount } = getRecoverableWithSubdust(
|
|
3043
3048
|
allVtxos,
|
|
3044
|
-
dustAmount
|
|
3049
|
+
dustAmount,
|
|
3050
|
+
await fetchTimeHeight(this.wallet)
|
|
3045
3051
|
);
|
|
3046
3052
|
const subdustAmount = vtxosToRecover.filter((v) => BigInt(v.value) < dustAmount).reduce((sum, v) => sum + BigInt(v.value), 0n);
|
|
3047
3053
|
return {
|
|
@@ -3075,6 +3081,16 @@ var VtxoManager = class _VtxoManager {
|
|
|
3075
3081
|
* ```
|
|
3076
3082
|
*/
|
|
3077
3083
|
async getExpiringVtxos(thresholdMs) {
|
|
3084
|
+
return this.selectExpiringVtxos(thresholdMs);
|
|
3085
|
+
}
|
|
3086
|
+
/**
|
|
3087
|
+
* {@link getExpiringVtxos}, against a caller-supplied chain tip.
|
|
3088
|
+
*
|
|
3089
|
+
* The settle paths select, re-select after pre-flight, and sort by expiry within one pass;
|
|
3090
|
+
* each of those judges expiry, so they share one tip rather than fetching (and possibly
|
|
3091
|
+
* disagreeing on) one apiece. Fetches its own when `now` is omitted.
|
|
3092
|
+
*/
|
|
3093
|
+
async selectExpiringVtxos(thresholdMs, now) {
|
|
3078
3094
|
if (this.settlementConfig === false && thresholdMs === void 0) {
|
|
3079
3095
|
return [];
|
|
3080
3096
|
}
|
|
@@ -3087,7 +3103,12 @@ var VtxoManager = class _VtxoManager {
|
|
|
3087
3103
|
} else {
|
|
3088
3104
|
threshold = this.renewalConfig?.thresholdMs ?? DEFAULT_RENEWAL_CONFIG.thresholdMs;
|
|
3089
3105
|
}
|
|
3090
|
-
return getExpiringAndRecoverableVtxos(
|
|
3106
|
+
return getExpiringAndRecoverableVtxos(
|
|
3107
|
+
vtxos,
|
|
3108
|
+
threshold,
|
|
3109
|
+
getDustAmount(this.wallet),
|
|
3110
|
+
now ?? await fetchTimeHeight(this.wallet)
|
|
3111
|
+
);
|
|
3091
3112
|
}
|
|
3092
3113
|
/**
|
|
3093
3114
|
* Renew expiring virtual outputs by settling them back to the wallet's address
|
|
@@ -3140,17 +3161,18 @@ var VtxoManager = class _VtxoManager {
|
|
|
3140
3161
|
} else {
|
|
3141
3162
|
threshold = DEFAULT_RENEWAL_CONFIG.thresholdMs;
|
|
3142
3163
|
}
|
|
3143
|
-
|
|
3164
|
+
const now = await fetchTimeHeight(this.wallet);
|
|
3165
|
+
let vtxos = await this.selectExpiringVtxos(threshold, now);
|
|
3144
3166
|
if (vtxos.length === 0) {
|
|
3145
3167
|
throw new Error("No VTXOs available to renew");
|
|
3146
3168
|
}
|
|
3147
|
-
vtxos = await this.revalidateBeforeSettle(vtxos, threshold);
|
|
3169
|
+
vtxos = await this.revalidateBeforeSettle(vtxos, threshold, now);
|
|
3148
3170
|
if (vtxos.length === 0) {
|
|
3149
3171
|
throw new Error("No VTXOs available to renew");
|
|
3150
3172
|
}
|
|
3151
3173
|
const info = await this.getInfoProvider()?.getInfo();
|
|
3152
3174
|
const vtxoMaxAmount = info?.vtxoMaxAmount ?? -1n;
|
|
3153
|
-
const capped = capSettlementBatch(byExpiryAscending(vtxos), vtxoMaxAmount);
|
|
3175
|
+
const capped = capSettlementBatch(byExpiryAscending(vtxos, now), vtxoMaxAmount);
|
|
3154
3176
|
if (vtxoMaxAmount >= 0n) {
|
|
3155
3177
|
const oversized = vtxos.filter((vtxo) => BigInt(vtxo.value) > vtxoMaxAmount);
|
|
3156
3178
|
if (oversized.length > 0) {
|
|
@@ -3411,6 +3433,7 @@ var VtxoManager = class _VtxoManager {
|
|
|
3411
3433
|
signers: reports
|
|
3412
3434
|
};
|
|
3413
3435
|
if (vtxoMigratable.length > 0) {
|
|
3436
|
+
const now = await fetchTimeHeight(this.wallet);
|
|
3414
3437
|
report.vtxos = await this.runMigrationLeg(
|
|
3415
3438
|
vtxoMigratable,
|
|
3416
3439
|
(c) => c.vtxo.value,
|
|
@@ -3418,7 +3441,11 @@ var VtxoManager = class _VtxoManager {
|
|
|
3418
3441
|
vtxoMaxAmount,
|
|
3419
3442
|
dustAmount,
|
|
3420
3443
|
"VTXO",
|
|
3421
|
-
(capped) => wallet.sendSelectedVtxosToSelf(
|
|
3444
|
+
(capped) => wallet.sendSelectedVtxosToSelf(
|
|
3445
|
+
capped.map((c) => c.vtxo),
|
|
3446
|
+
now
|
|
3447
|
+
),
|
|
3448
|
+
(c) => canMigrateBySend(c.vtxo, now)
|
|
3422
3449
|
);
|
|
3423
3450
|
}
|
|
3424
3451
|
if (boardingMigratable.length > 0) {
|
|
@@ -3458,10 +3485,22 @@ var VtxoManager = class _VtxoManager {
|
|
|
3458
3485
|
* the server ceiling by the cap). The dust floor guards the degenerate cases
|
|
3459
3486
|
* where every input was oversized or the whole holding sums below dust.
|
|
3460
3487
|
*/
|
|
3461
|
-
async runMigrationLeg(candidates, valueOf, toRef, vtxoMaxAmount, dustAmount, legName, submit) {
|
|
3488
|
+
async runMigrationLeg(candidates, valueOf, toRef, vtxoMaxAmount, dustAmount, legName, submit, eligible) {
|
|
3489
|
+
const notSpendableRefs = [];
|
|
3490
|
+
const migratable = eligible ? candidates.filter((c) => {
|
|
3491
|
+
if (eligible(c)) return true;
|
|
3492
|
+
notSpendableRefs.push(toRef(c));
|
|
3493
|
+
return false;
|
|
3494
|
+
}) : candidates;
|
|
3495
|
+
if (notSpendableRefs.length > 0) {
|
|
3496
|
+
console.warn(
|
|
3497
|
+
`Deprecated-signer migration (${legName}): ${notSpendableRefs.length} input(s) are no longer cooperatively spendable at the current chain tip and were excluded; they recover through the sweep/recovery path instead.`
|
|
3498
|
+
);
|
|
3499
|
+
}
|
|
3500
|
+
const notSpendableField = notSpendableRefs.length > 0 ? { notSpendableOffchain: notSpendableRefs } : {};
|
|
3462
3501
|
const oversizedRefs = [];
|
|
3463
3502
|
const sized = [];
|
|
3464
|
-
for (const c of
|
|
3503
|
+
for (const c of migratable) {
|
|
3465
3504
|
if (vtxoMaxAmount >= 0n && BigInt(valueOf(c)) > vtxoMaxAmount) {
|
|
3466
3505
|
oversizedRefs.push(toRef(c));
|
|
3467
3506
|
} else {
|
|
@@ -3481,11 +3520,12 @@ var VtxoManager = class _VtxoManager {
|
|
|
3481
3520
|
const deferred = sized.length - capped.length;
|
|
3482
3521
|
const totalAmount = capped.reduce((sum, c) => sum + BigInt(valueOf(c)), 0n);
|
|
3483
3522
|
if (totalAmount < dustAmount) {
|
|
3484
|
-
const
|
|
3523
|
+
const skipped = sized.length > 0 ? "below-dust" : oversizedRefs.length > 0 ? "oversized-only" : notSpendableRefs.length > 0 ? "not-spendable-only" : "below-dust";
|
|
3485
3524
|
return {
|
|
3486
3525
|
migrated: [],
|
|
3487
|
-
skipped
|
|
3488
|
-
...oversizedField
|
|
3526
|
+
skipped,
|
|
3527
|
+
...oversizedField,
|
|
3528
|
+
...notSpendableField
|
|
3489
3529
|
};
|
|
3490
3530
|
}
|
|
3491
3531
|
try {
|
|
@@ -3494,13 +3534,15 @@ var VtxoManager = class _VtxoManager {
|
|
|
3494
3534
|
txid,
|
|
3495
3535
|
migrated: capped.map(toRef),
|
|
3496
3536
|
...deferred > 0 ? { deferred } : {},
|
|
3497
|
-
...oversizedField
|
|
3537
|
+
...oversizedField,
|
|
3538
|
+
...notSpendableField
|
|
3498
3539
|
};
|
|
3499
3540
|
} catch (e) {
|
|
3500
3541
|
return {
|
|
3501
3542
|
migrated: [],
|
|
3502
3543
|
error: e instanceof Error ? e.message : String(e),
|
|
3503
|
-
...oversizedField
|
|
3544
|
+
...oversizedField,
|
|
3545
|
+
...notSpendableField
|
|
3504
3546
|
};
|
|
3505
3547
|
}
|
|
3506
3548
|
}
|
|
@@ -3528,8 +3570,8 @@ var VtxoManager = class _VtxoManager {
|
|
|
3528
3570
|
if (!serverPubKey) continue;
|
|
3529
3571
|
const cls = classifyAgainstSignerSet(serverPubKey, signerSet, nowSeconds);
|
|
3530
3572
|
if (cls.status === "CURRENT") continue;
|
|
3531
|
-
const recoverable = vtxos.filter((v) =>
|
|
3532
|
-
const spendable = vtxos.filter((v) =>
|
|
3573
|
+
const recoverable = vtxos.filter((v) => v.isSwept && !hasTerminalSpend(v));
|
|
3574
|
+
const spendable = vtxos.filter((v) => !hasTerminalSpend(v) && !v.isSwept);
|
|
3533
3575
|
const value = spendable.reduce((sum, v) => sum + v.value, 0);
|
|
3534
3576
|
let recoverableCount = 0;
|
|
3535
3577
|
let recoverableValue = 0;
|
|
@@ -3542,7 +3584,7 @@ var VtxoManager = class _VtxoManager {
|
|
|
3542
3584
|
awaitingSweepCount = spendable.length;
|
|
3543
3585
|
awaitingSweepValue = value;
|
|
3544
3586
|
for (const v of spendable) {
|
|
3545
|
-
const exp = v
|
|
3587
|
+
const exp = toBatchExpiry(v);
|
|
3546
3588
|
if (exp !== void 0 && (nextSweepEta === void 0 || exp < nextSweepEta)) {
|
|
3547
3589
|
nextSweepEta = exp;
|
|
3548
3590
|
}
|
|
@@ -3578,7 +3620,7 @@ var VtxoManager = class _VtxoManager {
|
|
|
3578
3620
|
}
|
|
3579
3621
|
if (isCooperativelyMigratable(cls.status)) {
|
|
3580
3622
|
for (const v of spendable) {
|
|
3581
|
-
if (
|
|
3623
|
+
if (v.expiresAt === void 0 && v.expiresAtHeight === void 0) continue;
|
|
3582
3624
|
migratable.push({ vtxo: v, classification: cls });
|
|
3583
3625
|
}
|
|
3584
3626
|
} else if (cls.status === "EXPIRED") {
|
|
@@ -3956,7 +3998,7 @@ var VtxoManager = class _VtxoManager {
|
|
|
3956
3998
|
* candidates and lets the post-submit `VTXO_ALREADY_SPENT` recovery
|
|
3957
3999
|
* handle whatever slipped through.
|
|
3958
4000
|
*/
|
|
3959
|
-
async revalidateBeforeSettle(candidates, thresholdMs) {
|
|
4001
|
+
async revalidateBeforeSettle(candidates, thresholdMs, now) {
|
|
3960
4002
|
if (candidates.length === 0) return candidates;
|
|
3961
4003
|
try {
|
|
3962
4004
|
const cm = await this.wallet.getContractManager();
|
|
@@ -3966,7 +4008,7 @@ var VtxoManager = class _VtxoManager {
|
|
|
3966
4008
|
return candidates;
|
|
3967
4009
|
}
|
|
3968
4010
|
try {
|
|
3969
|
-
const refreshed = await this.
|
|
4011
|
+
const refreshed = await this.selectExpiringVtxos(thresholdMs, now);
|
|
3970
4012
|
const candidateKeys = new Set(candidates.map((v) => `${v.txid}:${v.vout}`));
|
|
3971
4013
|
return refreshed.filter((v) => candidateKeys.has(`${v.txid}:${v.vout}`));
|
|
3972
4014
|
} catch (e) {
|
|
@@ -4084,10 +4126,12 @@ var VtxoManager = class _VtxoManager {
|
|
|
4084
4126
|
(u) => u.status.confirmed && !this.knownBoardingUtxos.has(`${u.txid}:${u.vout}`) && !expiredSet.has(`${u.txid}:${u.vout}`)
|
|
4085
4127
|
);
|
|
4086
4128
|
let expiringVtxos = [];
|
|
4129
|
+
let now;
|
|
4087
4130
|
if (!this.renewalInProgress) {
|
|
4088
4131
|
try {
|
|
4089
|
-
|
|
4090
|
-
expiringVtxos = await this.
|
|
4132
|
+
now = await fetchTimeHeight(this.wallet);
|
|
4133
|
+
expiringVtxos = await this.selectExpiringVtxos(void 0, now);
|
|
4134
|
+
expiringVtxos = await this.revalidateBeforeSettle(expiringVtxos, void 0, now);
|
|
4091
4135
|
} catch (e) {
|
|
4092
4136
|
console.error("Error fetching expiring VTXOs:", e);
|
|
4093
4137
|
}
|
|
@@ -4119,17 +4163,11 @@ var VtxoManager = class _VtxoManager {
|
|
|
4119
4163
|
totalAmount += BigInt(u.value) - BigInt(inputFee.satoshis);
|
|
4120
4164
|
}
|
|
4121
4165
|
const filteredVtxos = [];
|
|
4122
|
-
for (const v of byExpiryAscending(expiringVtxos)) {
|
|
4166
|
+
for (const v of byExpiryAscending(expiringVtxos, now ?? { timestamp: /* @__PURE__ */ new Date() })) {
|
|
4123
4167
|
if (filteredVtxos.length >= MAX_VTXOS_PER_SETTLEMENT) {
|
|
4124
4168
|
break;
|
|
4125
4169
|
}
|
|
4126
|
-
const inputFee = estimator.evalOffchainInput(
|
|
4127
|
-
amount: BigInt(v.value),
|
|
4128
|
-
type: v.virtualStatus.state === "swept" ? "recoverable" : "vtxo",
|
|
4129
|
-
weight: 0,
|
|
4130
|
-
birth: v.createdAt,
|
|
4131
|
-
expiry: v.virtualStatus.batchExpiry ? new Date(v.virtualStatus.batchExpiry) : void 0
|
|
4132
|
-
});
|
|
4170
|
+
const inputFee = estimator.evalOffchainInput(toOffchainInputFeeParams(v));
|
|
4133
4171
|
if (inputFee.satoshis >= v.value) {
|
|
4134
4172
|
continue;
|
|
4135
4173
|
}
|
|
@@ -4333,6 +4371,96 @@ function readUInt32BE(array, offset) {
|
|
|
4333
4371
|
function noteTapscript(preimageHash) {
|
|
4334
4372
|
return Script.encode(["SHA256", preimageHash, "EQUAL"]);
|
|
4335
4373
|
}
|
|
4374
|
+
var ArkadeCash = class _ArkadeCash {
|
|
4375
|
+
constructor(privateKey, serverPubKey, csvTimelock, hrp = _ArkadeCash.DefaultHRP) {
|
|
4376
|
+
this.csvTimelock = csvTimelock;
|
|
4377
|
+
this.hrp = hrp;
|
|
4378
|
+
if (privateKey.length !== 32) {
|
|
4379
|
+
throw new Error(
|
|
4380
|
+
`Invalid private key length: expected 32 bytes, got ${privateKey.length}`
|
|
4381
|
+
);
|
|
4382
|
+
}
|
|
4383
|
+
if (serverPubKey.length !== 32) {
|
|
4384
|
+
throw new Error(
|
|
4385
|
+
`Invalid server public key length: expected 32 bytes, got ${serverPubKey.length}`
|
|
4386
|
+
);
|
|
4387
|
+
}
|
|
4388
|
+
this._privateKey = privateKey.slice();
|
|
4389
|
+
this._serverPubKey = serverPubKey.slice();
|
|
4390
|
+
this._publicKey = pubSchnorr(this._privateKey);
|
|
4391
|
+
}
|
|
4392
|
+
csvTimelock;
|
|
4393
|
+
hrp;
|
|
4394
|
+
static DefaultHRP = "arkadecash";
|
|
4395
|
+
static Version = 0;
|
|
4396
|
+
static PayloadLength = 1 + 32 + 32 + 4;
|
|
4397
|
+
// 69 bytes
|
|
4398
|
+
// Keys are held as private copies so a caller mutating the array it passed
|
|
4399
|
+
// in — or one it reads back from a getter — cannot alter this note's
|
|
4400
|
+
// identity or the outputs derived from it.
|
|
4401
|
+
_privateKey;
|
|
4402
|
+
_serverPubKey;
|
|
4403
|
+
_publicKey;
|
|
4404
|
+
/** The note's private key. Returns a fresh copy; mutating it is a no-op. */
|
|
4405
|
+
get privateKey() {
|
|
4406
|
+
return this._privateKey.slice();
|
|
4407
|
+
}
|
|
4408
|
+
/** The server's public key. Returns a fresh copy; mutating it is a no-op. */
|
|
4409
|
+
get serverPubKey() {
|
|
4410
|
+
return this._serverPubKey.slice();
|
|
4411
|
+
}
|
|
4412
|
+
/** The note's public key. Returns a fresh copy; mutating it is a no-op. */
|
|
4413
|
+
get publicKey() {
|
|
4414
|
+
return this._publicKey.slice();
|
|
4415
|
+
}
|
|
4416
|
+
static generate(serverPubKey, csvTimelock, hrp) {
|
|
4417
|
+
return new _ArkadeCash(randomPrivateKeyBytes(), serverPubKey, csvTimelock, hrp);
|
|
4418
|
+
}
|
|
4419
|
+
static fromString(encoded) {
|
|
4420
|
+
const decoded = bech32m.decodeUnsafe(encoded.trim().toLowerCase(), 1023);
|
|
4421
|
+
if (!decoded) {
|
|
4422
|
+
throw new Error("Invalid arkadeCash string: failed to decode bech32m");
|
|
4423
|
+
}
|
|
4424
|
+
const data = new Uint8Array(bech32m.fromWords(decoded.words));
|
|
4425
|
+
if (data.length !== _ArkadeCash.PayloadLength) {
|
|
4426
|
+
throw new Error(
|
|
4427
|
+
`Invalid arkadeCash data length: expected ${_ArkadeCash.PayloadLength} bytes, got ${data.length}`
|
|
4428
|
+
);
|
|
4429
|
+
}
|
|
4430
|
+
const version = data[0];
|
|
4431
|
+
if (version !== _ArkadeCash.Version) {
|
|
4432
|
+
throw new Error(`Unsupported arkadeCash version: ${version}`);
|
|
4433
|
+
}
|
|
4434
|
+
const privateKey = data.slice(1, 33);
|
|
4435
|
+
const serverPubKey = data.slice(33, 65);
|
|
4436
|
+
const sequence = new DataView(data.buffer, data.byteOffset + 65, 4).getUint32(0, false);
|
|
4437
|
+
const csvTimelock = sequenceToTimelock(sequence);
|
|
4438
|
+
return new _ArkadeCash(privateKey, serverPubKey, csvTimelock, decoded.prefix);
|
|
4439
|
+
}
|
|
4440
|
+
toString() {
|
|
4441
|
+
const data = new Uint8Array(_ArkadeCash.PayloadLength);
|
|
4442
|
+
data[0] = _ArkadeCash.Version;
|
|
4443
|
+
data.set(this._privateKey, 1);
|
|
4444
|
+
data.set(this._serverPubKey, 33);
|
|
4445
|
+
const sequence = timelockToSequence(this.csvTimelock);
|
|
4446
|
+
new DataView(data.buffer, data.byteOffset + 65, 4).setUint32(0, sequence, false);
|
|
4447
|
+
const words = bech32m.toWords(data);
|
|
4448
|
+
return bech32m.encode(this.hrp, words, 1023);
|
|
4449
|
+
}
|
|
4450
|
+
get identity() {
|
|
4451
|
+
return SingleKey.fromPrivateKey(this._privateKey);
|
|
4452
|
+
}
|
|
4453
|
+
get vtxoScript() {
|
|
4454
|
+
return new DefaultVtxo.Script({
|
|
4455
|
+
pubKey: this._publicKey,
|
|
4456
|
+
serverPubKey: this._serverPubKey,
|
|
4457
|
+
csvTimelock: this.csvTimelock
|
|
4458
|
+
});
|
|
4459
|
+
}
|
|
4460
|
+
address(addressHrp) {
|
|
4461
|
+
return this.vtxoScript.address(addressHrp, this._serverPubKey);
|
|
4462
|
+
}
|
|
4463
|
+
};
|
|
4336
4464
|
|
|
4337
4465
|
// src/repositories/intentRepository.ts
|
|
4338
4466
|
var ALL_INTENT_STATES = [
|
|
@@ -5397,13 +5525,13 @@ function subtractAssets(spent, change) {
|
|
|
5397
5525
|
return Array.from(map, ([assetId, amount]) => ({ assetId, amount }));
|
|
5398
5526
|
}
|
|
5399
5527
|
async function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnore, getTxCreatedAt) {
|
|
5400
|
-
const fromOldestVtxo =
|
|
5528
|
+
const fromOldestVtxo = vtxos.map(normalizeVtxo).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
5401
5529
|
const unmatchedSettledBoardingTxs = allBoardingTxs.filter(isSettledBoardingReceive).sort((a, b) => a.createdAt - b.createdAt);
|
|
5402
5530
|
const sent = [];
|
|
5403
5531
|
let received = [];
|
|
5404
5532
|
for (const vtxo of fromOldestVtxo) {
|
|
5405
5533
|
if (vtxo.status.isLeaf) {
|
|
5406
|
-
const commitmentTxid = vtxo.
|
|
5534
|
+
const commitmentTxid = vtxo.commitmentTxIds[0];
|
|
5407
5535
|
const vtxoCreatedAt = vtxo.createdAt.getTime();
|
|
5408
5536
|
const ignoredCommitment = commitmentsToIgnore.has(commitmentTxid) || !!vtxo.settledBy && commitmentsToIgnore.has(vtxo.settledBy);
|
|
5409
5537
|
if (ignoredCommitment) {
|
|
@@ -5411,7 +5539,7 @@ async function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnor
|
|
|
5411
5539
|
unmatchedSettledBoardingTxs,
|
|
5412
5540
|
(tx) => tx.createdAt <= vtxoCreatedAt && (tx.key.commitmentTxid === commitmentTxid || tx.key.commitmentTxid === vtxo.settledBy)
|
|
5413
5541
|
);
|
|
5414
|
-
} else if (fromOldestVtxo.filter((v) => v.settledBy === vtxo.
|
|
5542
|
+
} else if (fromOldestVtxo.filter((v) => v.settledBy === vtxo.commitmentTxIds[0]).length === 0) {
|
|
5415
5543
|
const duplicateBoardingReceive = consumeBoardingReceive(
|
|
5416
5544
|
unmatchedSettledBoardingTxs,
|
|
5417
5545
|
(tx) => tx.amount === vtxo.value && tx.createdAt <= vtxoCreatedAt
|
|
@@ -5474,7 +5602,7 @@ async function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnor
|
|
|
5474
5602
|
}
|
|
5475
5603
|
if (vtxo.settledBy && !commitmentsToIgnore.has(vtxo.settledBy) && !sent.some((s) => s.key.commitmentTxid === vtxo.settledBy)) {
|
|
5476
5604
|
const changes = fromOldestVtxo.filter(
|
|
5477
|
-
(v) => v.status.isLeaf && v.
|
|
5605
|
+
(v) => v.status.isLeaf && v.commitmentTxIds.length > 0 && v.commitmentTxIds.every((_) => vtxo.settledBy === _)
|
|
5478
5606
|
);
|
|
5479
5607
|
const forfeitVtxos = fromOldestVtxo.filter((v) => v.settledBy === vtxo.settledBy);
|
|
5480
5608
|
const forfeitAmount = forfeitVtxos.reduce((acc, v) => acc + v.value, 0);
|
|
@@ -5863,7 +5991,7 @@ var DelegateManagerImpl = class {
|
|
|
5863
5991
|
const destinationScript = ArkAddress.decode(destination).pkScript;
|
|
5864
5992
|
const arkInfo = await this.arkInfoProvider.getInfo();
|
|
5865
5993
|
const delegateInfo = await this.delegateProvider.getDelegateInfo();
|
|
5866
|
-
const eligible = vtxos.filter(
|
|
5994
|
+
const eligible = vtxos.map(normalizeVtxo).filter(
|
|
5867
5995
|
(v) => isAnnotated(v) && findDelegateTapLeaf(v, delegateInfo.pubkey) !== void 0
|
|
5868
5996
|
);
|
|
5869
5997
|
if (eligible.length === 0) {
|
|
@@ -5888,14 +6016,15 @@ var DelegateManagerImpl = class {
|
|
|
5888
6016
|
}
|
|
5889
6017
|
return { delegated: eligible, failed: [] };
|
|
5890
6018
|
}
|
|
6019
|
+
const now = { timestamp: /* @__PURE__ */ new Date() };
|
|
5891
6020
|
const groupByExpiry = /* @__PURE__ */ new Map();
|
|
5892
6021
|
let recoverableVtxos = [];
|
|
5893
6022
|
for (const vtxo of eligible) {
|
|
5894
|
-
if (
|
|
6023
|
+
if (canRecoverOnchain(vtxo, now)) {
|
|
5895
6024
|
recoverableVtxos.push(vtxo);
|
|
5896
6025
|
continue;
|
|
5897
6026
|
}
|
|
5898
|
-
const expiry = vtxo
|
|
6027
|
+
const expiry = toBatchExpiry(vtxo);
|
|
5899
6028
|
if (!expiry) continue;
|
|
5900
6029
|
const dayKey = getDayTimestamp(expiry);
|
|
5901
6030
|
groupByExpiry.set(dayKey, [...groupByExpiry.get(dayKey) ?? [], vtxo]);
|
|
@@ -5959,10 +6088,8 @@ async function delegate(identity, delegateProvider, arkInfo, delegateInfo, vtxos
|
|
|
5959
6088
|
throw new Error("unable to delegate: delegate provider not configured");
|
|
5960
6089
|
}
|
|
5961
6090
|
if (!delegateAt) {
|
|
5962
|
-
const
|
|
5963
|
-
|
|
5964
|
-
Number.MAX_SAFE_INTEGER
|
|
5965
|
-
);
|
|
6091
|
+
const now = { timestamp: /* @__PURE__ */ new Date() };
|
|
6092
|
+
const expiryTimestamp = vtxos.filter((coin) => !canRecoverOnchain(coin, now) && toBatchExpiry(coin)).reduce((min, coin) => Math.min(min, toBatchExpiry(coin)), Number.MAX_SAFE_INTEGER);
|
|
5966
6093
|
if (!expiryTimestamp || expiryTimestamp === Number.MAX_SAFE_INTEGER) {
|
|
5967
6094
|
delegateAt = new Date(Date.now() + 1 * 60 * 1e3);
|
|
5968
6095
|
} else {
|
|
@@ -5991,11 +6118,8 @@ async function delegate(identity, delegateProvider, arkInfo, delegateInfo, vtxos
|
|
|
5991
6118
|
let amount = 0n;
|
|
5992
6119
|
for (const coin of vtxos) {
|
|
5993
6120
|
const inputFee = estimator.evalOffchainInput({
|
|
5994
|
-
|
|
5995
|
-
type: "vtxo"
|
|
5996
|
-
weight: 0,
|
|
5997
|
-
birth: coin.createdAt,
|
|
5998
|
-
expiry: coin.virtualStatus.batchExpiry ? new Date(coin.virtualStatus.batchExpiry) : void 0
|
|
6121
|
+
...toOffchainInputFeeParams(coin),
|
|
6122
|
+
type: "vtxo"
|
|
5999
6123
|
});
|
|
6000
6124
|
if (inputFee.value >= coin.value) {
|
|
6001
6125
|
continue;
|
|
@@ -6044,7 +6168,7 @@ async function delegate(identity, delegateProvider, arkInfo, delegateInfo, vtxos
|
|
|
6044
6168
|
Address(getNetwork(network)).decode(forfeitAddress)
|
|
6045
6169
|
);
|
|
6046
6170
|
const forfeits = await Promise.all(
|
|
6047
|
-
vtxos.filter((v) => !
|
|
6171
|
+
vtxos.filter((v) => !canRecoverOnchain(v, { timestamp: /* @__PURE__ */ new Date() })).map(async (coin) => {
|
|
6048
6172
|
const forfeit = await makeDelegateForfeitTx(
|
|
6049
6173
|
coin,
|
|
6050
6174
|
dust,
|
|
@@ -6832,7 +6956,8 @@ function validateVtxosForScript(vtxos, script, context) {
|
|
|
6832
6956
|
);
|
|
6833
6957
|
}
|
|
6834
6958
|
async function getVtxosForContract(repo, contract) {
|
|
6835
|
-
|
|
6959
|
+
const vtxos = repo.getVtxosForScript ? await repo.getVtxosForScript(contract.script) : filterVtxosForScript(await repo.getVtxos(contract.address), contract.script);
|
|
6960
|
+
return vtxos.map(normalizeVtxo);
|
|
6836
6961
|
}
|
|
6837
6962
|
async function saveVtxosForContract(repo, contract, vtxos) {
|
|
6838
6963
|
if (repo.saveVtxosForScript) {
|
|
@@ -7036,7 +7161,7 @@ var deserializeTapLeaf = (t) => {
|
|
|
7036
7161
|
const s = hex.decode(t.s);
|
|
7037
7162
|
return [cb, s];
|
|
7038
7163
|
};
|
|
7039
|
-
var deserializeVtxo = (o) => ({
|
|
7164
|
+
var deserializeVtxo = (o) => normalizeVtxo({
|
|
7040
7165
|
...o,
|
|
7041
7166
|
createdAt: new Date(o.createdAt),
|
|
7042
7167
|
tapTree: hex.decode(o.tapTree),
|
|
@@ -7635,50 +7760,12 @@ function getLifecycleWeight(v) {
|
|
|
7635
7760
|
if (v.arkTxId) weight += 2;
|
|
7636
7761
|
return weight;
|
|
7637
7762
|
}
|
|
7763
|
+
|
|
7764
|
+
// src/repositories/migrations/walletRepositoryImpl.ts
|
|
7638
7765
|
var getVtxosStorageKey = (address) => `vtxos:${address}`;
|
|
7639
7766
|
var getUtxosStorageKey = (address) => `utxos:${address}`;
|
|
7640
7767
|
var getTransactionsStorageKey = (address) => `tx:${address}`;
|
|
7641
7768
|
var walletStateStorageKey = "wallet:state";
|
|
7642
|
-
var serializeVtxo2 = (v) => ({
|
|
7643
|
-
...v,
|
|
7644
|
-
tapTree: hex.encode(v.tapTree),
|
|
7645
|
-
forfeitTapLeafScript: serializeTapLeaf2(v.forfeitTapLeafScript),
|
|
7646
|
-
intentTapLeafScript: serializeTapLeaf2(v.intentTapLeafScript),
|
|
7647
|
-
extraWitness: v.extraWitness?.map(hex.encode),
|
|
7648
|
-
assets: serializeAssets(v.assets)
|
|
7649
|
-
});
|
|
7650
|
-
var serializeUtxo2 = (u) => ({
|
|
7651
|
-
...u,
|
|
7652
|
-
tapTree: hex.encode(u.tapTree),
|
|
7653
|
-
forfeitTapLeafScript: serializeTapLeaf2(u.forfeitTapLeafScript),
|
|
7654
|
-
intentTapLeafScript: serializeTapLeaf2(u.intentTapLeafScript),
|
|
7655
|
-
extraWitness: u.extraWitness?.map(hex.encode)
|
|
7656
|
-
});
|
|
7657
|
-
var deserializeVtxo2 = (o) => ({
|
|
7658
|
-
...o,
|
|
7659
|
-
createdAt: new Date(o.createdAt),
|
|
7660
|
-
tapTree: hex.decode(o.tapTree),
|
|
7661
|
-
forfeitTapLeafScript: deserializeTapLeaf2(o.forfeitTapLeafScript),
|
|
7662
|
-
intentTapLeafScript: deserializeTapLeaf2(o.intentTapLeafScript),
|
|
7663
|
-
extraWitness: o.extraWitness?.map(hex.decode),
|
|
7664
|
-
assets: deserializeAssets(o.assets)
|
|
7665
|
-
});
|
|
7666
|
-
var deserializeUtxo2 = (o) => ({
|
|
7667
|
-
...o,
|
|
7668
|
-
tapTree: hex.decode(o.tapTree),
|
|
7669
|
-
forfeitTapLeafScript: deserializeTapLeaf2(o.forfeitTapLeafScript),
|
|
7670
|
-
intentTapLeafScript: deserializeTapLeaf2(o.intentTapLeafScript),
|
|
7671
|
-
extraWitness: o.extraWitness?.map(hex.decode)
|
|
7672
|
-
});
|
|
7673
|
-
var serializeTapLeaf2 = ([cb, s]) => ({
|
|
7674
|
-
cb: hex.encode(TaprootControlBlock.encode(cb)),
|
|
7675
|
-
s: hex.encode(s)
|
|
7676
|
-
});
|
|
7677
|
-
var deserializeTapLeaf2 = (t) => {
|
|
7678
|
-
const cb = TaprootControlBlock.decode(hex.decode(t.cb));
|
|
7679
|
-
const s = hex.decode(t.s);
|
|
7680
|
-
return [cb, s];
|
|
7681
|
-
};
|
|
7682
7769
|
var WalletRepositoryImpl = class {
|
|
7683
7770
|
version = 1;
|
|
7684
7771
|
storage;
|
|
@@ -7690,7 +7777,7 @@ var WalletRepositoryImpl = class {
|
|
|
7690
7777
|
if (!stored) return [];
|
|
7691
7778
|
try {
|
|
7692
7779
|
const parsed = JSON.parse(stored);
|
|
7693
|
-
return parsed.map(
|
|
7780
|
+
return parsed.map(deserializeVtxo);
|
|
7694
7781
|
} catch (error) {
|
|
7695
7782
|
console.error(`Failed to parse VTXOs for address ${address}:`, error);
|
|
7696
7783
|
return [];
|
|
@@ -7710,7 +7797,7 @@ var WalletRepositoryImpl = class {
|
|
|
7710
7797
|
}
|
|
7711
7798
|
await this.storage.setItem(
|
|
7712
7799
|
getVtxosStorageKey(address),
|
|
7713
|
-
JSON.stringify(storedVtxos.map(
|
|
7800
|
+
JSON.stringify(storedVtxos.map(serializeVtxo))
|
|
7714
7801
|
);
|
|
7715
7802
|
}
|
|
7716
7803
|
async clearVtxos(address) {
|
|
@@ -7724,7 +7811,7 @@ var WalletRepositoryImpl = class {
|
|
|
7724
7811
|
if (!stored) return [];
|
|
7725
7812
|
try {
|
|
7726
7813
|
const parsed = JSON.parse(stored);
|
|
7727
|
-
return parsed.map(
|
|
7814
|
+
return parsed.map(deserializeUtxo);
|
|
7728
7815
|
} catch (error) {
|
|
7729
7816
|
console.error(`Failed to parse UTXOs for address ${address}:`, error);
|
|
7730
7817
|
return [];
|
|
@@ -7744,7 +7831,7 @@ var WalletRepositoryImpl = class {
|
|
|
7744
7831
|
});
|
|
7745
7832
|
await this.storage.setItem(
|
|
7746
7833
|
getUtxosStorageKey(address),
|
|
7747
|
-
JSON.stringify(storedUtxos.map(
|
|
7834
|
+
JSON.stringify(storedUtxos.map(serializeUtxo))
|
|
7748
7835
|
);
|
|
7749
7836
|
}
|
|
7750
7837
|
async clearUtxos(address) {
|
|
@@ -8396,15 +8483,27 @@ var ContractWatcher = class {
|
|
|
8396
8483
|
}
|
|
8397
8484
|
/**
|
|
8398
8485
|
* Handle a subscription update.
|
|
8486
|
+
*
|
|
8487
|
+
* Normalization boundary: `getSubscription` is part of the public `IndexerProvider` interface,
|
|
8488
|
+
* so a consumer implementation may yield legacy-shaped VTXOs. Normalizing on ingest also fixes
|
|
8489
|
+
* the shape of the payloads emitted to external event consumers.
|
|
8399
8490
|
*/
|
|
8400
8491
|
handleSubscriptionUpdate(update) {
|
|
8401
8492
|
if (!this.eventCallback) return;
|
|
8402
8493
|
const timestamp = Date.now();
|
|
8403
8494
|
if (update.newVtxos?.length) {
|
|
8404
|
-
this.processSubscriptionVtxos(
|
|
8495
|
+
this.processSubscriptionVtxos(
|
|
8496
|
+
update.newVtxos.map(normalizeVtxo),
|
|
8497
|
+
"vtxo_received",
|
|
8498
|
+
timestamp
|
|
8499
|
+
);
|
|
8405
8500
|
}
|
|
8406
8501
|
if (update.spentVtxos?.length) {
|
|
8407
|
-
this.processSubscriptionVtxos(
|
|
8502
|
+
this.processSubscriptionVtxos(
|
|
8503
|
+
update.spentVtxos.map(normalizeVtxo),
|
|
8504
|
+
"vtxo_spent",
|
|
8505
|
+
timestamp
|
|
8506
|
+
);
|
|
8408
8507
|
}
|
|
8409
8508
|
}
|
|
8410
8509
|
/**
|
|
@@ -8534,7 +8633,7 @@ async function reconcileIntents(deps) {
|
|
|
8534
8633
|
let vtxosByOutpoint = /* @__PURE__ */ new Map();
|
|
8535
8634
|
if (outpoints.length > 0) {
|
|
8536
8635
|
try {
|
|
8537
|
-
const { vtxos } = await deps.indexerProvider
|
|
8636
|
+
const { vtxos } = await getNormalizedVtxos(deps.indexerProvider, { outpoints });
|
|
8538
8637
|
vtxosByOutpoint = new Map(vtxos.map((v) => [outpointKey(v), v]));
|
|
8539
8638
|
} catch (e) {
|
|
8540
8639
|
console.error("reconcileIntents: failed to fetch input VTXO state", e);
|
|
@@ -8787,9 +8886,10 @@ var ContractManager = class _ContractManager {
|
|
|
8787
8886
|
* Safety-critical invariants (spec §2.C / §4):
|
|
8788
8887
|
* - `opts.materialize(i)` throwing is structural/fatal: it is NOT
|
|
8789
8888
|
* wrapped — it propagates and aborts the scan.
|
|
8790
|
-
* - A `discoverAt` rejection is collected into `handlerErrors` and
|
|
8791
|
-
*
|
|
8792
|
-
*
|
|
8889
|
+
* - A `discoverAt` rejection is collected into `handlerErrors` and makes
|
|
8890
|
+
* its index *indeterminate*: it does NOT advance the gap counter, and
|
|
8891
|
+
* the scan stops verifying there, reporting `truncatedAt`. Only an index
|
|
8892
|
+
* every handler answered for can be a confirmed miss.
|
|
8793
8893
|
* - `persistAndWatchContract` rejecting is operational/fatal and
|
|
8794
8894
|
* propagates (only `discoverAt` is guarded).
|
|
8795
8895
|
* - Within an index the handler probes run concurrently (independent
|
|
@@ -8803,8 +8903,12 @@ var ContractManager = class _ContractManager {
|
|
|
8803
8903
|
* over-scanned, nothing is discarded, and `materialize`/`discoverAt` are
|
|
8804
8904
|
* invoked on exactly the same index set. The window's hits are still
|
|
8805
8905
|
* processed strictly in ascending index order, so the discovered set,
|
|
8806
|
-
* persisted rows, `
|
|
8807
|
-
* identical to the serial path — only the wall-clock
|
|
8906
|
+
* persisted rows, `highestConfirmedUsedIndex`, and `handlerErrors` are
|
|
8907
|
+
* byte-for-byte identical to the serial path — only the wall-clock
|
|
8908
|
+
* differs. Truncation is the one exception: a window's concurrent probes
|
|
8909
|
+
* can surface hits above the failed index that a serial scan would never
|
|
8910
|
+
* have reached, so a truncated batched scan discovers a superset — never
|
|
8911
|
+
* a subset — of the serial one.
|
|
8808
8912
|
*/
|
|
8809
8913
|
async scanContracts(opts) {
|
|
8810
8914
|
const gapLimit = opts.gapLimit ?? 20;
|
|
@@ -8826,7 +8930,8 @@ var ContractManager = class _ContractManager {
|
|
|
8826
8930
|
];
|
|
8827
8931
|
const maxIdx = opts.hd ? SCAN_MAX_INDEX : 0;
|
|
8828
8932
|
const handlerErrors = [];
|
|
8829
|
-
let
|
|
8933
|
+
let highestConfirmedUsedIndex = -1;
|
|
8934
|
+
let truncatedAt;
|
|
8830
8935
|
let unused = 0;
|
|
8831
8936
|
let i = 0;
|
|
8832
8937
|
const probeIndex = async (index) => {
|
|
@@ -8853,6 +8958,7 @@ var ContractManager = class _ContractManager {
|
|
|
8853
8958
|
const index = windowIndices[w];
|
|
8854
8959
|
const probes = windowProbes[w];
|
|
8855
8960
|
let hitAtThisIndex = false;
|
|
8961
|
+
let indeterminate = false;
|
|
8856
8962
|
for (let h = 0; h < discoverables.length; h++) {
|
|
8857
8963
|
const probe = probes[h];
|
|
8858
8964
|
if (!probe.ok) {
|
|
@@ -8861,6 +8967,7 @@ var ContractManager = class _ContractManager {
|
|
|
8861
8967
|
index,
|
|
8862
8968
|
error: probe.error
|
|
8863
8969
|
});
|
|
8970
|
+
indeterminate = true;
|
|
8864
8971
|
continue;
|
|
8865
8972
|
}
|
|
8866
8973
|
for (const c of probe.found) {
|
|
@@ -8868,21 +8975,26 @@ var ContractManager = class _ContractManager {
|
|
|
8868
8975
|
hitAtThisIndex = true;
|
|
8869
8976
|
}
|
|
8870
8977
|
}
|
|
8871
|
-
if (
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
}
|
|
8978
|
+
if (indeterminate && truncatedAt === void 0) truncatedAt = index;
|
|
8979
|
+
if (hitAtThisIndex) highestConfirmedUsedIndex = index;
|
|
8980
|
+
if (truncatedAt !== void 0) continue;
|
|
8981
|
+
if (hitAtThisIndex) unused = 0;
|
|
8982
|
+
else unused += 1;
|
|
8877
8983
|
}
|
|
8984
|
+
if (truncatedAt !== void 0) break;
|
|
8878
8985
|
i = windowEnd + 1;
|
|
8879
8986
|
}
|
|
8880
|
-
if (opts.hd && i > maxIdx && unused < gapLimit) {
|
|
8987
|
+
if (opts.hd && truncatedAt === void 0 && i > maxIdx && unused < gapLimit) {
|
|
8881
8988
|
throw new Error(
|
|
8882
8989
|
`scanContracts: reached SCAN_MAX_INDEX (${SCAN_MAX_INDEX}) without closing the ${gapLimit}-index gap window; a Discoverable handler may be returning unconditional hits`
|
|
8883
8990
|
);
|
|
8884
8991
|
}
|
|
8885
|
-
return {
|
|
8992
|
+
return {
|
|
8993
|
+
lastIndexUsed: highestConfirmedUsedIndex,
|
|
8994
|
+
highestConfirmedUsedIndex,
|
|
8995
|
+
...truncatedAt !== void 0 && { truncatedAt },
|
|
8996
|
+
handlerErrors
|
|
8997
|
+
};
|
|
8886
8998
|
}
|
|
8887
8999
|
/**
|
|
8888
9000
|
* Get contracts with optional filters.
|
|
@@ -8929,7 +9041,9 @@ var ContractManager = class _ContractManager {
|
|
|
8929
9041
|
byScript.set(contract.script, contract);
|
|
8930
9042
|
}
|
|
8931
9043
|
const tapscriptCache = /* @__PURE__ */ new Map();
|
|
8932
|
-
return vtxos.map(
|
|
9044
|
+
return vtxos.map(
|
|
9045
|
+
(vtxo) => extendVirtualCoinForContract(normalizeVtxo(vtxo), byScript, tapscriptCache)
|
|
9046
|
+
);
|
|
8933
9047
|
}
|
|
8934
9048
|
buildContractsDbFilter(filter) {
|
|
8935
9049
|
return {
|
|
@@ -9097,7 +9211,7 @@ var ContractManager = class _ContractManager {
|
|
|
9097
9211
|
}
|
|
9098
9212
|
async refreshOutpoints(outpoints) {
|
|
9099
9213
|
if (outpoints.length === 0) return;
|
|
9100
|
-
const { vtxos } = await this.config.indexerProvider
|
|
9214
|
+
const { vtxos } = await getNormalizedVtxos(this.config.indexerProvider, {
|
|
9101
9215
|
outpoints
|
|
9102
9216
|
});
|
|
9103
9217
|
if (vtxos.length === 0) return;
|
|
@@ -9225,7 +9339,7 @@ var ContractManager = class _ContractManager {
|
|
|
9225
9339
|
async reconcilePendingFrontier(contracts) {
|
|
9226
9340
|
const scripts = contracts.map((c) => c.script);
|
|
9227
9341
|
const scriptToContract = new Map(contracts.map((c) => [c.script, c]));
|
|
9228
|
-
const { vtxos } = await this.config.indexerProvider
|
|
9342
|
+
const { vtxos } = await getNormalizedVtxos(this.config.indexerProvider, {
|
|
9229
9343
|
scripts,
|
|
9230
9344
|
pendingOnly: true
|
|
9231
9345
|
});
|
|
@@ -9316,7 +9430,7 @@ var ContractManager = class _ContractManager {
|
|
|
9316
9430
|
let pageIndex = 0;
|
|
9317
9431
|
let hasMore = true;
|
|
9318
9432
|
while (hasMore) {
|
|
9319
|
-
const { vtxos, page } = await this.config.indexerProvider
|
|
9433
|
+
const { vtxos, page } = await getNormalizedVtxos(this.config.indexerProvider, {
|
|
9320
9434
|
scripts,
|
|
9321
9435
|
...windowOpts,
|
|
9322
9436
|
pageIndex,
|
|
@@ -10127,6 +10241,24 @@ async function spendableVtxosExcludingLocked(vtxos, intentRepository) {
|
|
|
10127
10241
|
}
|
|
10128
10242
|
return excludeLockedOutpoints(vtxos, locked);
|
|
10129
10243
|
}
|
|
10244
|
+
var cashReport = (vtxo, reason) => ({
|
|
10245
|
+
txid: vtxo.txid,
|
|
10246
|
+
vout: vtxo.vout,
|
|
10247
|
+
value: vtxo.value,
|
|
10248
|
+
reason
|
|
10249
|
+
});
|
|
10250
|
+
var ArkadeCashCreateError = class extends Error {
|
|
10251
|
+
constructor(cash, cause) {
|
|
10252
|
+
super(
|
|
10253
|
+
`Failed to create ArkadeCash: send failed after the note may have been submitted. Recover with claimCash using the token on this error's \`cash\` field.`
|
|
10254
|
+
);
|
|
10255
|
+
this.cash = cash;
|
|
10256
|
+
this.cause = cause;
|
|
10257
|
+
this.name = "ArkadeCashCreateError";
|
|
10258
|
+
}
|
|
10259
|
+
cash;
|
|
10260
|
+
cause;
|
|
10261
|
+
};
|
|
10130
10262
|
var ReadonlyWallet = class _ReadonlyWallet {
|
|
10131
10263
|
constructor(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegateProvider, watcherConfig, walletContractTimelocks) {
|
|
10132
10264
|
this.identity = identity;
|
|
@@ -10585,20 +10717,21 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
10585
10717
|
}
|
|
10586
10718
|
let recoverable = 0;
|
|
10587
10719
|
let pendingRecovery = 0;
|
|
10588
|
-
const
|
|
10589
|
-
const
|
|
10590
|
-
(coin) => coin
|
|
10720
|
+
const now = { timestamp: /* @__PURE__ */ new Date() };
|
|
10721
|
+
const settled = vtxos.filter(
|
|
10722
|
+
(coin) => canSpendOffchain(coin, now) && !coin.isPreconfirmed && !isPendingRecovery(coin)
|
|
10591
10723
|
).reduce((sum, coin) => sum + coin.value, 0);
|
|
10592
|
-
const
|
|
10593
|
-
(coin) => (coin
|
|
10724
|
+
const preconfirmed = vtxos.filter(
|
|
10725
|
+
(coin) => canSpendOffchain(coin, now) && coin.isPreconfirmed && !isPendingRecovery(coin)
|
|
10594
10726
|
).reduce((sum, coin) => sum + coin.value, 0);
|
|
10595
|
-
|
|
10727
|
+
const available = unlockedVtxos.filter((coin) => canSpendOffchain(coin, now) && !isPendingRecovery(coin)).reduce((sum, coin) => sum + coin.value, 0);
|
|
10728
|
+
recoverable = vtxos.filter((coin) => canRecoverOnchain(coin, now) && !isPendingRecovery(coin)).reduce((sum, coin) => sum + coin.value, 0);
|
|
10596
10729
|
pendingRecovery = vtxos.filter(isPendingRecovery).reduce((sum, coin) => sum + coin.value, 0);
|
|
10597
10730
|
const totalBoarding = confirmed + unconfirmed;
|
|
10598
10731
|
const totalOffchain = settled + preconfirmed + recoverable + pendingRecovery;
|
|
10599
10732
|
const assetBalances = /* @__PURE__ */ new Map();
|
|
10600
10733
|
for (const vtxo of vtxos) {
|
|
10601
|
-
if (
|
|
10734
|
+
if (hasTerminalSpend(vtxo)) continue;
|
|
10602
10735
|
if (vtxo.assets) {
|
|
10603
10736
|
for (const a of vtxo.assets) {
|
|
10604
10737
|
const current = assetBalances.get(a.assetId) ?? 0n;
|
|
@@ -10634,12 +10767,13 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
10634
10767
|
const f = filter ?? { withRecoverable: true, withUnrolled: false };
|
|
10635
10768
|
const contractManager = await this.getContractManager();
|
|
10636
10769
|
const vtxos = await contractManager.getContractsWithVtxos();
|
|
10770
|
+
const now = { timestamp: /* @__PURE__ */ new Date() };
|
|
10637
10771
|
return vtxos.flatMap((_) => _.vtxos).filter((vtxo) => {
|
|
10638
10772
|
if (this._pendingSpendOutpoints.has(`${vtxo.txid}:${vtxo.vout}`)) {
|
|
10639
10773
|
return false;
|
|
10640
10774
|
}
|
|
10641
|
-
if (
|
|
10642
|
-
if (!f.withRecoverable && (
|
|
10775
|
+
if (!hasTerminalSpend(vtxo)) {
|
|
10776
|
+
if (!f.withRecoverable && canRecoverOnchain(vtxo, now)) {
|
|
10643
10777
|
return false;
|
|
10644
10778
|
}
|
|
10645
10779
|
return true;
|
|
@@ -10672,7 +10806,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
10672
10806
|
const response = await contractManager.getContractsWithVtxos();
|
|
10673
10807
|
const allVtxos = response.flatMap((_) => _.vtxos);
|
|
10674
10808
|
const { boardingTxs, commitmentsToIgnore } = await this.getBoardingTxs();
|
|
10675
|
-
const getTxCreatedAt = (txid) => this.indexerProvider
|
|
10809
|
+
const getTxCreatedAt = (txid) => getNormalizedVtxos(this.indexerProvider, { outpoints: [{ txid, vout: 0 }] }).then((res) => res.vtxos[0]?.createdAt.getTime()).catch((err) => {
|
|
10676
10810
|
if (isRetryableProviderError(err)) return void 0;
|
|
10677
10811
|
throw err;
|
|
10678
10812
|
});
|
|
@@ -10748,6 +10882,12 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
10748
10882
|
if (spent && commitmentTxid) {
|
|
10749
10883
|
commitmentsToIgnore.add(commitmentTxid);
|
|
10750
10884
|
}
|
|
10885
|
+
const boardingFacts = {
|
|
10886
|
+
isSpent: spent,
|
|
10887
|
+
isSwept: false,
|
|
10888
|
+
isPreconfirmed: false,
|
|
10889
|
+
commitmentTxIds: spent && commitmentTxid ? [commitmentTxid] : []
|
|
10890
|
+
};
|
|
10751
10891
|
utxos.push({
|
|
10752
10892
|
txid: tx.txid,
|
|
10753
10893
|
vout: i,
|
|
@@ -10757,10 +10897,9 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
10757
10897
|
block_time: tx.status.block_time
|
|
10758
10898
|
},
|
|
10759
10899
|
isUnrolled: true,
|
|
10760
|
-
|
|
10761
|
-
|
|
10762
|
-
|
|
10763
|
-
},
|
|
10900
|
+
...boardingFacts,
|
|
10901
|
+
virtualStatus: toVirtualStatus(boardingFacts),
|
|
10902
|
+
spentBy: "",
|
|
10764
10903
|
createdAt: tx.status.confirmed ? new Date(tx.status.block_time * 1e3) : /* @__PURE__ */ new Date(0),
|
|
10765
10904
|
script: scriptHex
|
|
10766
10905
|
});
|
|
@@ -10774,12 +10913,12 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
10774
10913
|
const tx = {
|
|
10775
10914
|
key: {
|
|
10776
10915
|
boardingTxid: utxo.txid,
|
|
10777
|
-
commitmentTxid: utxo.
|
|
10916
|
+
commitmentTxid: utxo.commitmentTxIds[0] ?? "",
|
|
10778
10917
|
arkTxid: ""
|
|
10779
10918
|
},
|
|
10780
10919
|
amount: utxo.value,
|
|
10781
10920
|
type: "RECEIVED" /* TxReceived */,
|
|
10782
|
-
settled: utxo.
|
|
10921
|
+
settled: utxo.isSpent,
|
|
10783
10922
|
createdAt: utxo.status.block_time ? new Date(utxo.status.block_time * 1e3).getTime() : 0
|
|
10784
10923
|
};
|
|
10785
10924
|
if (!utxo.status.block_time) {
|
|
@@ -11003,11 +11142,11 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
11003
11142
|
/** Fetch Arkade transaction ids that are still pending final settlement. */
|
|
11004
11143
|
async fetchPendingTxs() {
|
|
11005
11144
|
const scripts = await this.getWalletScripts();
|
|
11006
|
-
let { vtxos } = await this.indexerProvider
|
|
11145
|
+
let { vtxos } = await getNormalizedVtxos(this.indexerProvider, {
|
|
11007
11146
|
scripts
|
|
11008
11147
|
});
|
|
11009
11148
|
return vtxos.filter(
|
|
11010
|
-
(vtxo) => vtxo.
|
|
11149
|
+
(vtxo) => (vtxo.isSpent || vtxo.isPreconfirmed && !vtxo.isSwept) && vtxo.arkTxId !== void 0
|
|
11011
11150
|
).map((_) => _.arkTxId);
|
|
11012
11151
|
}
|
|
11013
11152
|
// ========================================================================
|
|
@@ -11106,7 +11245,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
11106
11245
|
});
|
|
11107
11246
|
onVtxosPersisted = async (_contract, vtxos) => {
|
|
11108
11247
|
for (const v of vtxos) {
|
|
11109
|
-
if (v.
|
|
11248
|
+
if (v.isSpent) continue;
|
|
11110
11249
|
await captureExitBranch({
|
|
11111
11250
|
resolver,
|
|
11112
11251
|
repository: virtualTxRepository,
|
|
@@ -11583,14 +11722,14 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
11583
11722
|
_restoreInFlight;
|
|
11584
11723
|
_addPendingSpends(inputs) {
|
|
11585
11724
|
for (const input of inputs) {
|
|
11586
|
-
if (
|
|
11725
|
+
if (isVirtualCoin(input)) {
|
|
11587
11726
|
this._pendingSpendOutpoints.add(`${input.txid}:${input.vout}`);
|
|
11588
11727
|
}
|
|
11589
11728
|
}
|
|
11590
11729
|
}
|
|
11591
11730
|
_removePendingSpends(inputs) {
|
|
11592
11731
|
for (const input of inputs) {
|
|
11593
|
-
if (
|
|
11732
|
+
if (isVirtualCoin(input)) {
|
|
11594
11733
|
this._pendingSpendOutpoints.delete(`${input.txid}:${input.vout}`);
|
|
11595
11734
|
}
|
|
11596
11735
|
}
|
|
@@ -11678,15 +11817,22 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
11678
11817
|
materialize,
|
|
11679
11818
|
deps
|
|
11680
11819
|
});
|
|
11681
|
-
if (hd && result.
|
|
11682
|
-
await provider.advanceLastIndexUsed(result.
|
|
11820
|
+
if (hd && result.highestConfirmedUsedIndex >= 0) {
|
|
11821
|
+
await provider.advanceLastIndexUsed(result.highestConfirmedUsedIndex);
|
|
11683
11822
|
}
|
|
11684
11823
|
await manager.refreshVtxos({ includeInactive: true });
|
|
11824
|
+
const causes = result.handlerErrors.map(
|
|
11825
|
+
(e) => e.error instanceof Error ? e.error : new Error(String(e.error))
|
|
11826
|
+
);
|
|
11827
|
+
if (result.truncatedAt !== void 0) {
|
|
11828
|
+
throw new AggregateError(
|
|
11829
|
+
causes,
|
|
11830
|
+
`restore: scan truncated at index ${result.truncatedAt}; indices >= ${result.truncatedAt} are unverified (${result.handlerErrors.length} discovery handler failure(s)). Retry is safe (idempotent).`
|
|
11831
|
+
);
|
|
11832
|
+
}
|
|
11685
11833
|
if (result.handlerErrors.length > 0) {
|
|
11686
11834
|
throw new AggregateError(
|
|
11687
|
-
|
|
11688
|
-
(e) => e.error instanceof Error ? e.error : new Error(String(e.error))
|
|
11689
|
-
),
|
|
11835
|
+
causes,
|
|
11690
11836
|
`restore: ${result.handlerErrors.length} discovery handler(s) failed; the gap window may have closed early \u2014 retry is safe (idempotent).`
|
|
11691
11837
|
);
|
|
11692
11838
|
}
|
|
@@ -11998,13 +12144,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
11998
12144
|
if (filteredVtxos.length >= MAX_VTXOS_PER_SETTLEMENT) {
|
|
11999
12145
|
break;
|
|
12000
12146
|
}
|
|
12001
|
-
const inputFee = estimator.evalOffchainInput(
|
|
12002
|
-
amount: BigInt(vtxo.value),
|
|
12003
|
-
type: vtxo.virtualStatus.state === "swept" ? "recoverable" : "vtxo",
|
|
12004
|
-
weight: 0,
|
|
12005
|
-
birth: vtxo.createdAt,
|
|
12006
|
-
expiry: vtxo.virtualStatus.batchExpiry ? new Date(vtxo.virtualStatus.batchExpiry) : void 0
|
|
12007
|
-
});
|
|
12147
|
+
const inputFee = estimator.evalOffchainInput(toOffchainInputFeeParams(vtxo));
|
|
12008
12148
|
if (inputFee.satoshis >= vtxo.value) {
|
|
12009
12149
|
continue;
|
|
12010
12150
|
}
|
|
@@ -12220,9 +12360,9 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12220
12360
|
* renewal / offboard) is not a board and leaves the boarding address
|
|
12221
12361
|
* untouched.
|
|
12222
12362
|
*
|
|
12223
|
-
* Boarding inputs are the non-VTXO coins (no `
|
|
12363
|
+
* Boarding inputs are the non-VTXO coins (no `script`), the same
|
|
12224
12364
|
* discriminator {@link handleSettlementFinalizationEvent} uses; the
|
|
12225
|
-
* `typeof` guard skips arknote
|
|
12365
|
+
* `typeof` guard skips the arknote strings settle also accepts.
|
|
12226
12366
|
*
|
|
12227
12367
|
* No-ops for static / `auto` wallets (no descriptor provider — boarding
|
|
12228
12368
|
* stays on its fixed index-0 address). Best-effort and non-fatal: the
|
|
@@ -12235,7 +12375,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12235
12375
|
async maybeRotateBoardingAfterBoard(inputs) {
|
|
12236
12376
|
if (!this._descriptorProvider) return;
|
|
12237
12377
|
const consumedBoarding = inputs.some(
|
|
12238
|
-
(input) => typeof input !== "string" && !(
|
|
12378
|
+
(input) => typeof input !== "string" && !isVirtualCoin(input)
|
|
12239
12379
|
);
|
|
12240
12380
|
if (!consumedBoarding) return;
|
|
12241
12381
|
try {
|
|
@@ -12246,13 +12386,12 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12246
12386
|
}
|
|
12247
12387
|
async handleSettlementFinalizationEvent(event, inputs, forfeitOutputScript, connectorsGraph) {
|
|
12248
12388
|
const signedForfeits = [];
|
|
12249
|
-
const isVtxo = (input) => "virtualStatus" in input;
|
|
12250
12389
|
let settlementPsbt = Transaction$2.fromPSBT(base64.decode(event.commitmentTx));
|
|
12251
12390
|
let hasBoardingUtxos = false;
|
|
12252
12391
|
let connectorIndex = 0;
|
|
12253
12392
|
const connectorsLeaves = connectorsGraph?.leaves() || [];
|
|
12254
12393
|
for (const input of inputs) {
|
|
12255
|
-
if (!
|
|
12394
|
+
if (!isVirtualCoin(input)) {
|
|
12256
12395
|
for (let i = 0; i < settlementPsbt.inputsLength; i++) {
|
|
12257
12396
|
const settlementInput = settlementPsbt.getInput(i);
|
|
12258
12397
|
if (!settlementInput.txid || settlementInput.index === void 0) {
|
|
@@ -12283,7 +12422,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12283
12422
|
}
|
|
12284
12423
|
continue;
|
|
12285
12424
|
}
|
|
12286
|
-
if (
|
|
12425
|
+
if (canRecoverOnchain(input, { timestamp: /* @__PURE__ */ new Date() }) || isSubdust({ value: input.value }, this.dustAmount)) {
|
|
12287
12426
|
continue;
|
|
12288
12427
|
}
|
|
12289
12428
|
if (connectorsLeaves.length === 0) {
|
|
@@ -12586,18 +12725,17 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12586
12725
|
if (!hasPending) {
|
|
12587
12726
|
return { finalized: [], pending: [] };
|
|
12588
12727
|
}
|
|
12589
|
-
const MAX_INPUTS_PER_INTENT = 20;
|
|
12590
12728
|
if (!vtxos || vtxos.length === 0) {
|
|
12591
12729
|
const scriptMap = await this.getScriptMap();
|
|
12592
12730
|
const allExtended = [];
|
|
12593
12731
|
const allScripts = [...scriptMap.keys()];
|
|
12594
|
-
const { vtxos: fetchedVtxos } = await this.indexerProvider
|
|
12732
|
+
const { vtxos: fetchedVtxos } = await getNormalizedVtxos(this.indexerProvider, {
|
|
12595
12733
|
scripts: allScripts
|
|
12596
12734
|
});
|
|
12597
12735
|
for (const vtxo of fetchedVtxos) {
|
|
12598
12736
|
const vtxoScript = scriptMap.get(vtxo.script);
|
|
12599
12737
|
if (!vtxoScript) continue;
|
|
12600
|
-
if (vtxo.
|
|
12738
|
+
if (!vtxo.isSpent && !(vtxo.isPreconfirmed && !vtxo.isSwept)) {
|
|
12601
12739
|
continue;
|
|
12602
12740
|
}
|
|
12603
12741
|
allExtended.push({
|
|
@@ -12696,6 +12834,271 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12696
12834
|
settings: { ...state.settings, hasPendingTx: value }
|
|
12697
12835
|
}));
|
|
12698
12836
|
}
|
|
12837
|
+
/**
|
|
12838
|
+
* Create an ArkadeCash bearer instrument.
|
|
12839
|
+
*
|
|
12840
|
+
* Generates a fresh keypair, sends the specified amount to a DefaultVtxo
|
|
12841
|
+
* controlled by the new key, and returns the encoded arkadeCash string.
|
|
12842
|
+
* The receiver can claim the funds using `claimCash()` without ever
|
|
12843
|
+
* sharing their address.
|
|
12844
|
+
*
|
|
12845
|
+
* ArkadeCash is a short-lived instrument: it carries a private key, so it is
|
|
12846
|
+
* meant to be handed over and claimed promptly, not held. A note left
|
|
12847
|
+
* unclaimed past its batch expiry is swept by the server and `claimCash`
|
|
12848
|
+
* can only report it, not move it.
|
|
12849
|
+
*
|
|
12850
|
+
* @param amount - Amount in satoshis to send. Must be a whole number of
|
|
12851
|
+
* sats at or above the dust threshold — a below-dust amount would mint an
|
|
12852
|
+
* OP_RETURN output that is unspendable as cash.
|
|
12853
|
+
* @returns The encoded arkadeCash string (e.g., "arkadecash1...")
|
|
12854
|
+
*/
|
|
12855
|
+
async createCash(amount) {
|
|
12856
|
+
if (!Number.isSafeInteger(amount) || amount < Number(this.dustAmount)) {
|
|
12857
|
+
throw new Error(
|
|
12858
|
+
`Invalid ArkadeCash amount ${amount}: must be a whole number of sats >= dust (${this.dustAmount})`
|
|
12859
|
+
);
|
|
12860
|
+
}
|
|
12861
|
+
const cashHrp = this.network.hrp.replace(/ark$/, "arkadecash");
|
|
12862
|
+
const cash = ArkadeCash.generate(
|
|
12863
|
+
this.arkServerPublicKey,
|
|
12864
|
+
this.offchainTapscript.options.csvTimelock,
|
|
12865
|
+
cashHrp
|
|
12866
|
+
);
|
|
12867
|
+
const address = cash.address(this.network.hrp).encode();
|
|
12868
|
+
const cashStr = cash.toString();
|
|
12869
|
+
try {
|
|
12870
|
+
await this.send({ address, amount });
|
|
12871
|
+
} catch (error) {
|
|
12872
|
+
throw new ArkadeCashCreateError(cashStr, error);
|
|
12873
|
+
}
|
|
12874
|
+
return cashStr;
|
|
12875
|
+
}
|
|
12876
|
+
/**
|
|
12877
|
+
* Claim an ArkadeCash bearer instrument: sweep what can be swept, report the
|
|
12878
|
+
* rest.
|
|
12879
|
+
*
|
|
12880
|
+
* Every spendable VTXO at the arkadeCash address is swept to this wallet in
|
|
12881
|
+
* its own offchain transaction, signed with the key carried by the arkadeCash
|
|
12882
|
+
* string. Nothing is ever persisted: no contract is imported, and the
|
|
12883
|
+
* arkadeCash key is not stored. Anything that cannot be swept — server-swept,
|
|
12884
|
+
* subdust, already claimed, or asset-bearing — is returned in `unclaimed`
|
|
12885
|
+
* with a per-VTXO reason and otherwise ignored.
|
|
12886
|
+
*
|
|
12887
|
+
* The arkadeCash string is the recovery token: a claim interrupted between
|
|
12888
|
+
* submit and finalize is completed by simply re-running `claimCash`, which
|
|
12889
|
+
* drains any pending arkadeCash transaction on the server before sweeping.
|
|
12890
|
+
*
|
|
12891
|
+
* @param cashStr - The encoded arkadeCash string (e.g., "arkadecash1...")
|
|
12892
|
+
* @returns The swept total and the report of what was left behind. The
|
|
12893
|
+
* shape is open: further buckets may be added alongside `unclaimed`.
|
|
12894
|
+
*/
|
|
12895
|
+
async claimCash(cashStr) {
|
|
12896
|
+
const cash = ArkadeCash.fromString(cashStr);
|
|
12897
|
+
const cashScript = cash.vtxoScript;
|
|
12898
|
+
const cashPkScript = hex.encode(cashScript.pkScript);
|
|
12899
|
+
const scripts = [cashPkScript];
|
|
12900
|
+
let vtxos = await this.fetchAllVtxos(scripts);
|
|
12901
|
+
if (vtxos.length === 0) {
|
|
12902
|
+
throw new Error("No VTXOs found for this arkadeCash");
|
|
12903
|
+
}
|
|
12904
|
+
const myPkScript = ArkAddress.decode(await this.getAddress()).pkScript;
|
|
12905
|
+
let { spendable, unclaimed } = this.classifyCashVtxos(vtxos);
|
|
12906
|
+
let swept = 0;
|
|
12907
|
+
const drainable = vtxos.filter((vtxo) => !isSubdust(vtxo, this.dustAmount));
|
|
12908
|
+
if (drainable.length > 0) {
|
|
12909
|
+
let drained = { count: 0, swept: 0, claimed: /* @__PURE__ */ new Set() };
|
|
12910
|
+
try {
|
|
12911
|
+
drained = await this.finalizePendingCashTxs(
|
|
12912
|
+
cash,
|
|
12913
|
+
drainable,
|
|
12914
|
+
cashScript,
|
|
12915
|
+
myPkScript
|
|
12916
|
+
);
|
|
12917
|
+
} catch (error) {
|
|
12918
|
+
console.error("Failed to drain pending arkadeCash txs:", error);
|
|
12919
|
+
}
|
|
12920
|
+
if (drained.count > 0) {
|
|
12921
|
+
vtxos = await this.fetchAllVtxos(scripts);
|
|
12922
|
+
({ spendable, unclaimed } = this.classifyCashVtxos(vtxos));
|
|
12923
|
+
swept += drained.swept;
|
|
12924
|
+
unclaimed = unclaimed.filter(
|
|
12925
|
+
(vtxo) => !drained.claimed.has(`${vtxo.txid}:${vtxo.vout}`)
|
|
12926
|
+
);
|
|
12927
|
+
}
|
|
12928
|
+
}
|
|
12929
|
+
if (spendable.length > 0) {
|
|
12930
|
+
const info = await this.arkProvider.getInfo();
|
|
12931
|
+
const serverUnrollScript = CSVMultisigTapscript.decode(
|
|
12932
|
+
hex.decode(info.checkpointTapscript)
|
|
12933
|
+
);
|
|
12934
|
+
for (const vtxo of spendable) {
|
|
12935
|
+
try {
|
|
12936
|
+
await signAndSubmitOffchainTx({
|
|
12937
|
+
identity: cash.identity,
|
|
12938
|
+
provider: this.arkProvider,
|
|
12939
|
+
inputs: [
|
|
12940
|
+
{
|
|
12941
|
+
txid: vtxo.txid,
|
|
12942
|
+
vout: vtxo.vout,
|
|
12943
|
+
value: vtxo.value,
|
|
12944
|
+
tapLeafScript: cashScript.forfeit(),
|
|
12945
|
+
tapTree: cashScript.encode()
|
|
12946
|
+
}
|
|
12947
|
+
],
|
|
12948
|
+
outputs: [{ script: myPkScript, amount: BigInt(vtxo.value) }],
|
|
12949
|
+
serverUnrollScript
|
|
12950
|
+
});
|
|
12951
|
+
swept += vtxo.value;
|
|
12952
|
+
} catch (error) {
|
|
12953
|
+
console.error(
|
|
12954
|
+
`Failed to sweep arkadeCash VTXO ${vtxo.txid}:${vtxo.vout}:`,
|
|
12955
|
+
error
|
|
12956
|
+
);
|
|
12957
|
+
unclaimed.push(cashReport(vtxo, "sweep-failed"));
|
|
12958
|
+
}
|
|
12959
|
+
}
|
|
12960
|
+
}
|
|
12961
|
+
return {
|
|
12962
|
+
swept,
|
|
12963
|
+
unclaimed: {
|
|
12964
|
+
amount: unclaimed.reduce((total, vtxo) => total + vtxo.value, 0),
|
|
12965
|
+
vtxos: unclaimed
|
|
12966
|
+
}
|
|
12967
|
+
};
|
|
12968
|
+
}
|
|
12969
|
+
/**
|
|
12970
|
+
* Split the VTXOs at an arkadeCash address into the ones the thin sweep can
|
|
12971
|
+
* move and the ones it can only report.
|
|
12972
|
+
*/
|
|
12973
|
+
classifyCashVtxos(vtxos) {
|
|
12974
|
+
const spendable = [];
|
|
12975
|
+
const unclaimed = [];
|
|
12976
|
+
for (const vtxo of vtxos) {
|
|
12977
|
+
if (hasTerminalSpend(vtxo)) {
|
|
12978
|
+
unclaimed.push(cashReport(vtxo, "already-spent"));
|
|
12979
|
+
} else if (isSubdust(vtxo, this.dustAmount)) {
|
|
12980
|
+
unclaimed.push(cashReport(vtxo, "subdust"));
|
|
12981
|
+
} else if (vtxo.isSwept) {
|
|
12982
|
+
unclaimed.push(cashReport(vtxo, "swept"));
|
|
12983
|
+
} else if (vtxo.assets && vtxo.assets.length > 0) {
|
|
12984
|
+
unclaimed.push(cashReport(vtxo, "has-assets"));
|
|
12985
|
+
} else {
|
|
12986
|
+
spendable.push(vtxo);
|
|
12987
|
+
}
|
|
12988
|
+
}
|
|
12989
|
+
return { spendable, unclaimed };
|
|
12990
|
+
}
|
|
12991
|
+
/** Read every page of an unfiltered VTXO query for the given scripts. */
|
|
12992
|
+
async fetchAllVtxos(scripts) {
|
|
12993
|
+
const pageSize = 100;
|
|
12994
|
+
const all = [];
|
|
12995
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12996
|
+
for (let pageIndex = 0; ; pageIndex++) {
|
|
12997
|
+
const { vtxos, page } = await getNormalizedVtxos(this.indexerProvider, {
|
|
12998
|
+
scripts,
|
|
12999
|
+
pageIndex,
|
|
13000
|
+
pageSize
|
|
13001
|
+
});
|
|
13002
|
+
for (const vtxo of vtxos) {
|
|
13003
|
+
const outpoint = `${vtxo.txid}:${vtxo.vout}`;
|
|
13004
|
+
if (seen.has(outpoint)) continue;
|
|
13005
|
+
seen.add(outpoint);
|
|
13006
|
+
all.push(vtxo);
|
|
13007
|
+
}
|
|
13008
|
+
if (!page || vtxos.length < pageSize) return all;
|
|
13009
|
+
}
|
|
13010
|
+
}
|
|
13011
|
+
/**
|
|
13012
|
+
* Complete any sweep a previous `claimCash` registered but never finalized.
|
|
13013
|
+
* A thin variant of {@link finalizePendingTxs}, scoped to the arkadeCash key:
|
|
13014
|
+
* the intent is signed with it, and so are the returned checkpoints.
|
|
13015
|
+
*
|
|
13016
|
+
* A drained sweep is not automatically *ours*. Whoever registered it chose
|
|
13017
|
+
* the destination, so a tx a different claimer left half-finished pays that
|
|
13018
|
+
* claimer — finalizing it is still right (the input is spent either way, and
|
|
13019
|
+
* completing it unsticks the funds), but only the value it actually pays to
|
|
13020
|
+
* `myPkScript` may be counted as claimed here.
|
|
13021
|
+
*
|
|
13022
|
+
* @param vtxos - VTXOs at the arkadeCash contract's own pkScript, spent ones
|
|
13023
|
+
* included (a registered sweep marks its input spent). Any other outpoint —
|
|
13024
|
+
* subdust above all — would make the proof invalid, since it describes every
|
|
13025
|
+
* input by that pkScript.
|
|
13026
|
+
* @returns The number of pending txs finalized, the sats among them paid to
|
|
13027
|
+
* this wallet, and the outpoints those txs consumed to pay it.
|
|
13028
|
+
*/
|
|
13029
|
+
async finalizePendingCashTxs(cash, vtxos, cashScript, myPkScript) {
|
|
13030
|
+
const result = { count: 0, swept: 0, claimed: /* @__PURE__ */ new Set() };
|
|
13031
|
+
const inputs = vtxos.map((vtxo) => ({
|
|
13032
|
+
...vtxo,
|
|
13033
|
+
forfeitTapLeafScript: cashScript.forfeit(),
|
|
13034
|
+
intentTapLeafScript: cashScript.forfeit(),
|
|
13035
|
+
tapTree: cashScript.encode()
|
|
13036
|
+
}));
|
|
13037
|
+
if (inputs.length === 0) return result;
|
|
13038
|
+
const identity = cash.identity;
|
|
13039
|
+
const seenTxids = /* @__PURE__ */ new Set();
|
|
13040
|
+
const pendingTxs = [];
|
|
13041
|
+
for (let i = 0; i < inputs.length; i += MAX_INPUTS_PER_INTENT) {
|
|
13042
|
+
const batch = inputs.slice(i, i + MAX_INPUTS_PER_INTENT);
|
|
13043
|
+
const message = { type: "get-pending-tx", expire_at: 0 };
|
|
13044
|
+
const proof = Intent.create(message, batch, []);
|
|
13045
|
+
const signedProof = await identity.sign(
|
|
13046
|
+
proof,
|
|
13047
|
+
Array.from({ length: batch.length + 1 }, (_, j) => j)
|
|
13048
|
+
);
|
|
13049
|
+
const batchPending = await this.arkProvider.getPendingTxs({
|
|
13050
|
+
proof: base64.encode(signedProof.toPSBT()),
|
|
13051
|
+
message
|
|
13052
|
+
});
|
|
13053
|
+
for (const pendingTx of batchPending) {
|
|
13054
|
+
if (seenTxids.has(pendingTx.arkTxid)) continue;
|
|
13055
|
+
seenTxids.add(pendingTx.arkTxid);
|
|
13056
|
+
pendingTxs.push(pendingTx);
|
|
13057
|
+
}
|
|
13058
|
+
}
|
|
13059
|
+
const myPkScriptHex = hex.encode(myPkScript);
|
|
13060
|
+
for (const pendingTx of pendingTxs) {
|
|
13061
|
+
try {
|
|
13062
|
+
const checkpointTxs = pendingTx.signedCheckpointTxs.map(
|
|
13063
|
+
(checkpoint) => Transaction$2.fromPSBT(base64.decode(checkpoint))
|
|
13064
|
+
);
|
|
13065
|
+
const finalCheckpoints = await Promise.all(
|
|
13066
|
+
checkpointTxs.map(
|
|
13067
|
+
async (checkpoint) => base64.encode((await identity.sign(checkpoint)).toPSBT())
|
|
13068
|
+
)
|
|
13069
|
+
);
|
|
13070
|
+
await this.arkProvider.finalizeTx(pendingTx.arkTxid, finalCheckpoints);
|
|
13071
|
+
result.count++;
|
|
13072
|
+
const paidToMe = this.arkTxAmountPaidTo(pendingTx.finalArkTx, myPkScriptHex);
|
|
13073
|
+
if (paidToMe === 0) continue;
|
|
13074
|
+
result.swept += paidToMe;
|
|
13075
|
+
for (const checkpoint of checkpointTxs) {
|
|
13076
|
+
for (let i = 0; i < checkpoint.inputsLength; i++) {
|
|
13077
|
+
const input = checkpoint.getInput(i);
|
|
13078
|
+
if (!input.txid) continue;
|
|
13079
|
+
result.claimed.add(`${hex.encode(input.txid)}:${input.index}`);
|
|
13080
|
+
}
|
|
13081
|
+
}
|
|
13082
|
+
} catch (error) {
|
|
13083
|
+
console.error(
|
|
13084
|
+
`Failed to finalize pending arkadeCash tx ${pendingTx.arkTxid}:`,
|
|
13085
|
+
error
|
|
13086
|
+
);
|
|
13087
|
+
}
|
|
13088
|
+
}
|
|
13089
|
+
return result;
|
|
13090
|
+
}
|
|
13091
|
+
/** Sum the outputs of an ark tx PSBT that pay the given scriptPubKey. */
|
|
13092
|
+
arkTxAmountPaidTo(arkTxPsbt, pkScriptHex) {
|
|
13093
|
+
const arkTx = Transaction$2.fromPSBT(base64.decode(arkTxPsbt));
|
|
13094
|
+
let total = 0;
|
|
13095
|
+
for (let i = 0; i < arkTx.outputsLength; i++) {
|
|
13096
|
+
const output = arkTx.getOutput(i);
|
|
13097
|
+
if (!output.script || output.amount === void 0) continue;
|
|
13098
|
+
if (hex.encode(output.script) === pkScriptHex) total += Number(output.amount);
|
|
13099
|
+
}
|
|
13100
|
+
return total;
|
|
13101
|
+
}
|
|
12699
13102
|
/**
|
|
12700
13103
|
* Send BTC and/or assets to one or more recipients.
|
|
12701
13104
|
*
|
|
@@ -12924,7 +13327,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12924
13327
|
* there is no separate change output. It records no `TxSent` history — the
|
|
12925
13328
|
* funds never leave the wallet.
|
|
12926
13329
|
*/
|
|
12927
|
-
async sendSelectedVtxosToSelf(inputs) {
|
|
13330
|
+
async sendSelectedVtxosToSelf(inputs, now) {
|
|
12928
13331
|
if (inputs.length === 0) {
|
|
12929
13332
|
throw new Error("sendSelectedVtxosToSelf: no inputs");
|
|
12930
13333
|
}
|
|
@@ -12933,26 +13336,28 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12933
13336
|
const serverPubKey = this.arkServerPublicKey;
|
|
12934
13337
|
const serverUnrollScript = this.serverUnrollScript;
|
|
12935
13338
|
const arkAddress = offchainTapscript.address(this.network.hrp, serverPubKey);
|
|
12936
|
-
|
|
12937
|
-
|
|
13339
|
+
const normalizedInputs = inputs.map(normalizeVtxo);
|
|
13340
|
+
const at = now ?? await resolveTimeHeight(this.onchainProvider);
|
|
13341
|
+
for (const input of normalizedInputs) {
|
|
13342
|
+
if (!canSpendOffchain(input, at)) {
|
|
12938
13343
|
throw new Error(
|
|
12939
13344
|
`sendSelectedVtxosToSelf: input ${input.txid}:${input.vout} is not cooperatively spendable`
|
|
12940
13345
|
);
|
|
12941
13346
|
}
|
|
12942
|
-
if (
|
|
13347
|
+
if (input.expiresAt === void 0 && input.expiresAtHeight === void 0) {
|
|
12943
13348
|
throw new Error(
|
|
12944
|
-
`sendSelectedVtxosToSelf: input ${input.txid}:${input.vout} has no
|
|
13349
|
+
`sendSelectedVtxosToSelf: input ${input.txid}:${input.vout} has no batch expiry`
|
|
12945
13350
|
);
|
|
12946
13351
|
}
|
|
12947
13352
|
}
|
|
12948
|
-
const total =
|
|
13353
|
+
const total = normalizedInputs.reduce((sum, c) => sum + BigInt(c.value), 0n);
|
|
12949
13354
|
const outputs = [
|
|
12950
13355
|
{
|
|
12951
13356
|
script: total < this.dustAmount ? arkAddress.subdustPkScript : arkAddress.pkScript,
|
|
12952
13357
|
amount: total
|
|
12953
13358
|
}
|
|
12954
13359
|
];
|
|
12955
|
-
const assetInputs = selectedCoinsToAssetInputs(
|
|
13360
|
+
const assetInputs = selectedCoinsToAssetInputs(normalizedInputs);
|
|
12956
13361
|
let selfAssets;
|
|
12957
13362
|
if (assetInputs.size > 0) {
|
|
12958
13363
|
const totals = /* @__PURE__ */ new Map();
|
|
@@ -12969,7 +13374,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12969
13374
|
const packet = createAssetPacket(assetInputs, [], selfReceiver);
|
|
12970
13375
|
outputs.push(Extension.create([packet]).txOut());
|
|
12971
13376
|
}
|
|
12972
|
-
return this._submitOffchainSpend(
|
|
13377
|
+
return this._submitOffchainSpend(normalizedInputs, outputs, {
|
|
12973
13378
|
sentAmount: 0,
|
|
12974
13379
|
changeAmount: total,
|
|
12975
13380
|
changeVout: 0,
|
|
@@ -13070,43 +13475,42 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
13070
13475
|
const cm = await this.getContractManager();
|
|
13071
13476
|
const annotatedInputs = await cm.annotateVtxos(inputs);
|
|
13072
13477
|
for (const [inputIndex, vtxo] of annotatedInputs.entries()) {
|
|
13478
|
+
const spentFacts = { ...vtxo, isSpent: true };
|
|
13073
13479
|
if (inputIndex < safeLength && signedCheckpointTxs[inputIndex]) {
|
|
13074
13480
|
const checkpoint = Transaction$2.fromPSBT(
|
|
13075
13481
|
base64.decode(signedCheckpointTxs[inputIndex])
|
|
13076
13482
|
);
|
|
13077
13483
|
spentVtxos.push({
|
|
13078
|
-
...
|
|
13079
|
-
virtualStatus:
|
|
13080
|
-
...vtxo.virtualStatus,
|
|
13081
|
-
state: "spent"
|
|
13082
|
-
},
|
|
13484
|
+
...spentFacts,
|
|
13485
|
+
virtualStatus: toVirtualStatus(spentFacts),
|
|
13083
13486
|
spentBy: checkpoint.id,
|
|
13084
|
-
arkTxId: arkTxid
|
|
13085
|
-
isSpent: true
|
|
13487
|
+
arkTxId: arkTxid
|
|
13086
13488
|
});
|
|
13087
13489
|
} else {
|
|
13088
13490
|
spentVtxos.push({
|
|
13089
|
-
...
|
|
13090
|
-
virtualStatus:
|
|
13091
|
-
|
|
13092
|
-
state: "spent"
|
|
13093
|
-
},
|
|
13094
|
-
arkTxId: arkTxid,
|
|
13095
|
-
isSpent: true
|
|
13491
|
+
...spentFacts,
|
|
13492
|
+
virtualStatus: toVirtualStatus(spentFacts),
|
|
13493
|
+
arkTxId: arkTxid
|
|
13096
13494
|
});
|
|
13097
13495
|
}
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
commitmentTxIds.add(id);
|
|
13101
|
-
}
|
|
13496
|
+
for (const id of vtxo.commitmentTxIds) {
|
|
13497
|
+
commitmentTxIds.add(id);
|
|
13102
13498
|
}
|
|
13103
|
-
|
|
13104
|
-
|
|
13499
|
+
const vtxoExpiry = toBatchExpiry(vtxo);
|
|
13500
|
+
if (vtxoExpiry) {
|
|
13501
|
+
batchExpiry = Math.min(batchExpiry, vtxoExpiry);
|
|
13105
13502
|
}
|
|
13106
13503
|
}
|
|
13107
13504
|
const createdAt = Date.now();
|
|
13108
13505
|
let changeVtxo;
|
|
13109
13506
|
if (changeAmount > 0n && batchExpiry !== Number.MAX_SAFE_INTEGER) {
|
|
13507
|
+
const changeFacts = {
|
|
13508
|
+
isSpent: false,
|
|
13509
|
+
isSwept: false,
|
|
13510
|
+
isPreconfirmed: true,
|
|
13511
|
+
commitmentTxIds: Array.from(commitmentTxIds),
|
|
13512
|
+
...parseLegacyExpiry(batchExpiry)
|
|
13513
|
+
};
|
|
13110
13514
|
changeVtxo = {
|
|
13111
13515
|
txid: arkTxid,
|
|
13112
13516
|
vout: changeVout,
|
|
@@ -13114,14 +13518,11 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
13114
13518
|
forfeitTapLeafScript: offchainTapscript.forfeit(),
|
|
13115
13519
|
intentTapLeafScript: offchainTapscript.forfeit(),
|
|
13116
13520
|
isUnrolled: false,
|
|
13117
|
-
isSpent: false,
|
|
13118
13521
|
tapTree: offchainTapscript.encode(),
|
|
13119
13522
|
value: Number(changeAmount),
|
|
13120
|
-
|
|
13121
|
-
|
|
13122
|
-
|
|
13123
|
-
batchExpiry
|
|
13124
|
-
},
|
|
13523
|
+
...changeFacts,
|
|
13524
|
+
virtualStatus: toVirtualStatus(changeFacts),
|
|
13525
|
+
spentBy: "",
|
|
13125
13526
|
status: {
|
|
13126
13527
|
confirmed: false
|
|
13127
13528
|
},
|
|
@@ -13189,25 +13590,25 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
13189
13590
|
const spentVtxos = [];
|
|
13190
13591
|
const inputArkTxIds = /* @__PURE__ */ new Set();
|
|
13191
13592
|
const boardingRemovalsByAddress = /* @__PURE__ */ new Map();
|
|
13192
|
-
const
|
|
13193
|
-
const vtxoInputs = inputs.filter(isVtxo);
|
|
13593
|
+
const vtxoInputs = inputs.filter(isVirtualCoin);
|
|
13194
13594
|
const cm = await this.getContractManager();
|
|
13195
13595
|
const annotatedVtxos = await cm.annotateVtxos(vtxoInputs);
|
|
13196
13596
|
const annotatedByKey = new Map(annotatedVtxos.map((v) => [`${v.txid}:${v.vout}`, v]));
|
|
13197
13597
|
for (const input of inputs) {
|
|
13198
|
-
if (
|
|
13199
|
-
const
|
|
13598
|
+
if (isVirtualCoin(input)) {
|
|
13599
|
+
const outpoint = `${input.txid}:${input.vout}`;
|
|
13600
|
+
const vtxo = annotatedByKey.get(outpoint);
|
|
13601
|
+
if (!vtxo) {
|
|
13602
|
+
throw new Error(`missing annotation for virtual coin ${outpoint}`);
|
|
13603
|
+
}
|
|
13200
13604
|
if (vtxo.arkTxId) {
|
|
13201
13605
|
inputArkTxIds.add(vtxo.arkTxId);
|
|
13202
13606
|
}
|
|
13607
|
+
const settledFacts = { ...vtxo, isSpent: true };
|
|
13203
13608
|
spentVtxos.push({
|
|
13204
|
-
...
|
|
13205
|
-
virtualStatus:
|
|
13206
|
-
|
|
13207
|
-
state: "settled"
|
|
13208
|
-
},
|
|
13209
|
-
settledBy: commitmentTxid,
|
|
13210
|
-
isSpent: true
|
|
13609
|
+
...settledFacts,
|
|
13610
|
+
virtualStatus: toVirtualStatus(settledFacts),
|
|
13611
|
+
settledBy: commitmentTxid
|
|
13211
13612
|
});
|
|
13212
13613
|
} else {
|
|
13213
13614
|
let sourceAddress;
|
|
@@ -13270,9 +13671,9 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
13270
13671
|
}
|
|
13271
13672
|
};
|
|
13272
13673
|
function selectVirtualCoins(coins, targetAmount) {
|
|
13273
|
-
const sortedCoins =
|
|
13274
|
-
const expiryA = a
|
|
13275
|
-
const expiryB = b
|
|
13674
|
+
const sortedCoins = coins.map(normalizeVtxo).sort((a, b) => {
|
|
13675
|
+
const expiryA = toBatchExpiry(a) || Number.MAX_SAFE_INTEGER;
|
|
13676
|
+
const expiryB = toBatchExpiry(b) || Number.MAX_SAFE_INTEGER;
|
|
13276
13677
|
if (expiryA !== expiryB) {
|
|
13277
13678
|
return expiryA - expiryB;
|
|
13278
13679
|
}
|
|
@@ -13966,13 +14367,7 @@ var Ramps = class {
|
|
|
13966
14367
|
const filteredVtxos = [];
|
|
13967
14368
|
let totalAmount = 0n;
|
|
13968
14369
|
for (const vtxo of vtxos) {
|
|
13969
|
-
const inputFee = estimator.evalOffchainInput(
|
|
13970
|
-
amount: BigInt(vtxo.value),
|
|
13971
|
-
type: vtxo.virtualStatus.state === "swept" ? "recoverable" : "vtxo",
|
|
13972
|
-
weight: 0,
|
|
13973
|
-
birth: vtxo.createdAt,
|
|
13974
|
-
expiry: vtxo.virtualStatus.batchExpiry ? new Date(vtxo.virtualStatus.batchExpiry) : void 0
|
|
13975
|
-
});
|
|
14370
|
+
const inputFee = estimator.evalOffchainInput(toOffchainInputFeeParams(vtxo));
|
|
13976
14371
|
if (inputFee.satoshis >= vtxo.value) {
|
|
13977
14372
|
continue;
|
|
13978
14373
|
}
|
|
@@ -14707,30 +15102,30 @@ var WalletMessageHandler = class {
|
|
|
14707
15102
|
unconfirmed += utxo.value;
|
|
14708
15103
|
}
|
|
14709
15104
|
}
|
|
14710
|
-
const
|
|
14711
|
-
const sweptVtxos = allVtxos.filter((vtxo) => vtxo.virtualStatus.state === "swept");
|
|
15105
|
+
const now = { timestamp: /* @__PURE__ */ new Date() };
|
|
14712
15106
|
let settled = 0;
|
|
14713
15107
|
let preconfirmed = 0;
|
|
14714
15108
|
let recoverable = 0;
|
|
14715
15109
|
let pendingRecovery = 0;
|
|
14716
|
-
for (const vtxo of
|
|
15110
|
+
for (const vtxo of allVtxos) {
|
|
15111
|
+
if (hasTerminalSpend(vtxo)) continue;
|
|
14717
15112
|
if (pendingOutpoints.has(`${vtxo.txid}:${vtxo.vout}`)) {
|
|
14718
15113
|
pendingRecovery += vtxo.value;
|
|
14719
|
-
} else if (vtxo
|
|
14720
|
-
settled += vtxo.value;
|
|
14721
|
-
} else if (vtxo.virtualStatus.state === "preconfirmed") {
|
|
14722
|
-
preconfirmed += vtxo.value;
|
|
14723
|
-
}
|
|
14724
|
-
}
|
|
14725
|
-
for (const vtxo of sweptVtxos) {
|
|
14726
|
-
if (isSpendable(vtxo)) {
|
|
15114
|
+
} else if (canRecoverOnchain(vtxo, now)) {
|
|
14727
15115
|
recoverable += vtxo.value;
|
|
15116
|
+
} else if (canSpendOffchain(vtxo, now)) {
|
|
15117
|
+
if (vtxo.isPreconfirmed) {
|
|
15118
|
+
preconfirmed += vtxo.value;
|
|
15119
|
+
} else {
|
|
15120
|
+
settled += vtxo.value;
|
|
15121
|
+
}
|
|
14728
15122
|
}
|
|
14729
15123
|
}
|
|
14730
15124
|
const totalBoarding = confirmed + unconfirmed;
|
|
14731
15125
|
const totalOffchain = settled + preconfirmed + recoverable + pendingRecovery;
|
|
14732
15126
|
const assetBalances = /* @__PURE__ */ new Map();
|
|
14733
|
-
for (const vtxo of
|
|
15127
|
+
for (const vtxo of allVtxos) {
|
|
15128
|
+
if (hasTerminalSpend(vtxo)) continue;
|
|
14734
15129
|
if (vtxo.assets) {
|
|
14735
15130
|
for (const a of vtxo.assets) {
|
|
14736
15131
|
const current = assetBalances.get(a.assetId) ?? 0n;
|
|
@@ -14766,7 +15161,7 @@ var WalletMessageHandler = class {
|
|
|
14766
15161
|
*/
|
|
14767
15162
|
async getSpendableVtxos() {
|
|
14768
15163
|
const vtxos = await this.getVtxosFromRepo();
|
|
14769
|
-
return vtxos.filter(
|
|
15164
|
+
return vtxos.filter((v) => !hasTerminalSpend(v));
|
|
14770
15165
|
}
|
|
14771
15166
|
async onWalletInitialized() {
|
|
14772
15167
|
if (!this.readonlyWallet || !this.arkProvider || !this.indexerProvider || !this.walletRepository) {
|
|
@@ -14778,9 +15173,7 @@ var WalletMessageHandler = class {
|
|
|
14778
15173
|
try {
|
|
14779
15174
|
const vtxos = await this.getVtxosFromRepo();
|
|
14780
15175
|
const { pending, finalized } = await this.wallet.finalizePendingTxs(
|
|
14781
|
-
vtxos.filter(
|
|
14782
|
-
(vtxo) => vtxo.virtualStatus.state !== "swept" && vtxo.virtualStatus.state !== "settled"
|
|
14783
|
-
)
|
|
15176
|
+
vtxos.filter((vtxo) => vtxo.isSpent || vtxo.isPreconfirmed && !vtxo.isSwept)
|
|
14784
15177
|
);
|
|
14785
15178
|
console.info(
|
|
14786
15179
|
`Recovered ${finalized.length}/${pending.length} pending transactions: ${finalized.join(", ")}`
|
|
@@ -15039,8 +15432,12 @@ var WalletMessageHandler = class {
|
|
|
15039
15432
|
`WalletMessageHandler.getVtxosFromRepo: failed to derive script from wallet address ${walletAddress}: ${e instanceof Error ? e.message : String(e)}`
|
|
15040
15433
|
);
|
|
15041
15434
|
}
|
|
15042
|
-
|
|
15043
|
-
|
|
15435
|
+
addVtxos(
|
|
15436
|
+
await getVtxosForContract(this.walletRepository, {
|
|
15437
|
+
script: walletScript,
|
|
15438
|
+
address: walletAddress
|
|
15439
|
+
})
|
|
15440
|
+
);
|
|
15044
15441
|
return allVtxos;
|
|
15045
15442
|
}
|
|
15046
15443
|
/**
|
|
@@ -15072,7 +15469,7 @@ var WalletMessageHandler = class {
|
|
|
15072
15469
|
}));
|
|
15073
15470
|
const BATCH_SIZE = 100;
|
|
15074
15471
|
for (let i = 0; i < outpoints.length; i += BATCH_SIZE) {
|
|
15075
|
-
const res = await this.indexerProvider
|
|
15472
|
+
const res = await getNormalizedVtxos(this.indexerProvider, {
|
|
15076
15473
|
outpoints: outpoints.slice(i, i + BATCH_SIZE)
|
|
15077
15474
|
});
|
|
15078
15475
|
for (const v of res.vtxos) {
|
|
@@ -15774,7 +16171,7 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
|
|
|
15774
16171
|
};
|
|
15775
16172
|
try {
|
|
15776
16173
|
const response = await this.sendMessage(message);
|
|
15777
|
-
return response.payload.vtxos;
|
|
16174
|
+
return response.payload.vtxos.map(normalizeVtxo);
|
|
15778
16175
|
} catch (error) {
|
|
15779
16176
|
throw new Error(`Failed to get vtxos: ${error}`);
|
|
15780
16177
|
}
|
|
@@ -15887,7 +16284,7 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
|
|
|
15887
16284
|
};
|
|
15888
16285
|
try {
|
|
15889
16286
|
const response = await sendContractMessage(message);
|
|
15890
|
-
return response.payload.vtxos;
|
|
16287
|
+
return response.payload.vtxos.map(normalizeVtxo);
|
|
15891
16288
|
} catch (e) {
|
|
15892
16289
|
throw new Error("Failed to annotate vtxos");
|
|
15893
16290
|
}
|
|
@@ -16332,7 +16729,7 @@ var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadon
|
|
|
16332
16729
|
};
|
|
16333
16730
|
try {
|
|
16334
16731
|
const response = await wallet.sendMessage(message);
|
|
16335
|
-
return response.payload.vtxos;
|
|
16732
|
+
return response.payload.vtxos.map(normalizeVtxo);
|
|
16336
16733
|
} catch (e) {
|
|
16337
16734
|
throw new Error(`Failed to get expiring vtxos: ${e}`);
|
|
16338
16735
|
}
|
|
@@ -18072,7 +18469,7 @@ async function resolveFeeRate(opts) {
|
|
|
18072
18469
|
}
|
|
18073
18470
|
async function selectExitVtxos(opts) {
|
|
18074
18471
|
if (!opts.vtxos) return opts.wallet.getVtxos();
|
|
18075
|
-
const res = await opts.wallet.indexerProvider
|
|
18472
|
+
const res = await getNormalizedVtxos(opts.wallet.indexerProvider, { outpoints: opts.vtxos });
|
|
18076
18473
|
const tapTrees = /* @__PURE__ */ new Map();
|
|
18077
18474
|
const out = [];
|
|
18078
18475
|
for (const vtxo of res.vtxos) {
|
|
@@ -18741,7 +19138,7 @@ __export(arkade_exports, {
|
|
|
18741
19138
|
validateProgram: () => validateProgram,
|
|
18742
19139
|
witnessRefToBytes: () => witnessRefToBytes
|
|
18743
19140
|
});
|
|
18744
|
-
var isVtxoCoin = (input) =>
|
|
19141
|
+
var isVtxoCoin = (input) => isVirtualCoin(input);
|
|
18745
19142
|
function createArkadeBatchHandler(intentId, inputs, signer, signedProof, message, session, arkProvider, emulator, network, recipients) {
|
|
18746
19143
|
let batchId;
|
|
18747
19144
|
let sweepTapTreeRoot;
|
|
@@ -18818,7 +19215,11 @@ function createArkadeBatchHandler(intentId, inputs, signer, signedProof, message
|
|
|
18818
19215
|
break;
|
|
18819
19216
|
}
|
|
18820
19217
|
}
|
|
18821
|
-
if (boardingIdx === null)
|
|
19218
|
+
if (boardingIdx === null) {
|
|
19219
|
+
throw new Error(
|
|
19220
|
+
`input ${input.txid}:${input.vout} is neither a virtual output nor an input of the commitment tx; a virtual output must carry its \`script\``
|
|
19221
|
+
);
|
|
19222
|
+
}
|
|
18822
19223
|
commitmentPsbt.updateInput(boardingIdx, {
|
|
18823
19224
|
tapLeafScript: [input.forfeitTapLeafScript]
|
|
18824
19225
|
});
|
|
@@ -19098,13 +19499,13 @@ var ArkadeContract = class _ArkadeContract {
|
|
|
19098
19499
|
const [registered] = await manager.getContracts({ script: scriptHex });
|
|
19099
19500
|
if (registered) {
|
|
19100
19501
|
const [withVtxos] = await manager.getContractsWithVtxos({ script: scriptHex });
|
|
19101
|
-
return (withVtxos?.vtxos ?? []).filter(
|
|
19502
|
+
return (withVtxos?.vtxos ?? []).filter((v) => !hasTerminalSpend(v));
|
|
19102
19503
|
}
|
|
19103
19504
|
}
|
|
19104
19505
|
if (!this.client.indexer) {
|
|
19105
19506
|
throw new Error("ArkadeContract.getUtxos: an indexer is required");
|
|
19106
19507
|
}
|
|
19107
|
-
const { vtxos } = await this.client.indexer
|
|
19508
|
+
const { vtxos } = await getNormalizedVtxos(this.client.indexer, {
|
|
19108
19509
|
scripts: [scriptHex],
|
|
19109
19510
|
spendableOnly: true
|
|
19110
19511
|
});
|
|
@@ -19412,6 +19813,6 @@ function isArkContract(str) {
|
|
|
19412
19813
|
return str.startsWith(ARKCONTRACT_PREFIX + "=");
|
|
19413
19814
|
}
|
|
19414
19815
|
|
|
19415
|
-
export { ARKADE_MAGIC, ActivityRegistry, ArkNote, AssetManager, BIP322, Batch, ChainedTxType, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EmulatorPacket, EsploraProvider, Estimator, Extension, ExtensionNotFoundError, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, RestEmulatorProvider, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnilateralExit, UnknownPacket, Unroll, VtxoManager, Wallet2 as Wallet, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, arkade_exports, assertIntentIdUnique, boardingResolver, buildForfeitTx, buildOffchainTx, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, createAssetPacket, createDefaultActivityRegistry, createExitChainResolver, decodeArkContract, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, extendVirtualCoinForContract, getMigrationStatus, getRandomId, hasBoardingTxExpired, intentMatchesFilter, intentPageBounds, isArkContract, isBatchSignable, isCooperativelyMigratable, isDiscoverable,
|
|
19416
|
-
//# sourceMappingURL=chunk-
|
|
19417
|
-
//# sourceMappingURL=chunk-
|
|
19816
|
+
export { ARKADE_MAGIC, ActivityRegistry, ArkNote, ArkadeCash, ArkadeCashCreateError, AssetManager, BIP322, Batch, ChainedTxType, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EmulatorPacket, EsploraProvider, Estimator, Extension, ExtensionNotFoundError, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, RestEmulatorProvider, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnilateralExit, UnknownPacket, Unroll, VtxoManager, Wallet2 as Wallet, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, arkade_exports, assertIntentIdUnique, boardingResolver, buildForfeitTx, buildOffchainTx, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, createAssetPacket, createDefaultActivityRegistry, createExitChainResolver, decodeArkContract, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, extendVirtualCoinForContract, getMigrationStatus, getRandomId, hasBoardingTxExpired, intentMatchesFilter, intentPageBounds, isArkContract, isBatchSignable, isCooperativelyMigratable, isDiscoverable, isRetryableProviderError, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, mergeChainedTxType, migrateWalletRepository, openDatabase, requiresMigration, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signerSetFromInfo, toXOnlySignerHex, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript };
|
|
19817
|
+
//# sourceMappingURL=chunk-NYR5H6GR.js.map
|
|
19818
|
+
//# sourceMappingURL=chunk-NYR5H6GR.js.map
|