@arkade-os/sdk 0.4.33 → 0.4.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- 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-DEsDMYGv.d.cts → ark-Dsv5Jq4E.d.cts} +65 -7
- package/dist/{ark-DEsDMYGv.d.ts → ark-Dsv5Jq4E.d.ts} +65 -7
- package/dist/{asyncStorageTaskQueue-CMrTYlKG.d.ts → asyncStorageTaskQueue-BH-zuth5.d.ts} +1 -1
- package/dist/{asyncStorageTaskQueue-D8T1VXEx.d.cts → asyncStorageTaskQueue-D92ch8yI.d.cts} +1 -1
- package/dist/{chunk-L6ZETTX3.js → chunk-5WDBHWX3.js} +4 -4
- package/dist/{chunk-L6ZETTX3.js.map → chunk-5WDBHWX3.js.map} +1 -1
- package/dist/{chunk-5CCRRL5S.cjs → chunk-CCLNFHJ5.cjs} +11 -11
- package/dist/{chunk-5CCRRL5S.cjs.map → chunk-CCLNFHJ5.cjs.map} +1 -1
- package/dist/{chunk-WMIPYZSB.cjs → chunk-CMPJR3HS.cjs} +42 -9
- package/dist/chunk-CMPJR3HS.cjs.map +1 -0
- package/dist/{chunk-AOJUURHM.js → chunk-CUSABEUQ.js} +141 -37
- package/dist/chunk-CUSABEUQ.js.map +1 -0
- package/dist/{chunk-SPDNHPM4.cjs → chunk-FSAXPBGP.cjs} +8 -8
- package/dist/{chunk-SPDNHPM4.cjs.map → chunk-FSAXPBGP.cjs.map} +1 -1
- package/dist/{chunk-E22HEKLN.js → chunk-FXFBPXV3.js} +3 -3
- package/dist/{chunk-E22HEKLN.js.map → chunk-FXFBPXV3.js.map} +1 -1
- package/dist/{chunk-GYSK5R57.cjs → chunk-GUTKJMSF.cjs} +164 -59
- package/dist/chunk-GUTKJMSF.cjs.map +1 -0
- package/dist/{chunk-DSS2GQUG.js → chunk-HFXEUW55.js} +575 -155
- package/dist/chunk-HFXEUW55.js.map +1 -0
- package/dist/{chunk-TU3LVAPX.js → chunk-OUVTG72A.js} +43 -11
- package/dist/chunk-OUVTG72A.js.map +1 -0
- package/dist/{chunk-BU3BU6XK.js → chunk-VVGD3JIP.js} +3 -3
- package/dist/{chunk-BU3BU6XK.js.map → chunk-VVGD3JIP.js.map} +1 -1
- package/dist/{chunk-7K3ROJF6.cjs → chunk-XCHBQVMK.cjs} +718 -298
- package/dist/chunk-XCHBQVMK.cjs.map +1 -0
- package/dist/{chunk-HAVA4XB7.cjs → chunk-ZS3OZHC7.cjs} +7 -7
- package/dist/{chunk-HAVA4XB7.cjs.map → chunk-ZS3OZHC7.cjs.map} +1 -1
- package/dist/contracts/handlers/index.cjs +7 -7
- 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-BJeBNP5a.d.cts → delegate-BaS5SCIW.d.cts} +1 -1
- package/dist/{delegate-EXN2mfkb.d.ts → delegate-Baz_hb83.d.ts} +1 -1
- package/dist/{index-BG2ooYKO.d.ts → index-FwXZveaX.d.ts} +22 -16
- package/dist/{index-DHjEeHEp.d.cts → index-lNZ6qaO3.d.cts} +22 -16
- package/dist/index.cjs +134 -130
- package/dist/index.d.cts +63 -14
- package/dist/index.d.ts +63 -14
- package/dist/index.js +4 -4
- package/dist/repositories/realm/index.cjs +13 -13
- package/dist/repositories/realm/index.d.cts +1 -1
- package/dist/repositories/realm/index.d.ts +1 -1
- package/dist/repositories/realm/index.js +4 -4
- package/dist/repositories/sqlite/index.cjs +13 -13
- package/dist/repositories/sqlite/index.d.cts +1 -1
- package/dist/repositories/sqlite/index.d.ts +1 -1
- package/dist/repositories/sqlite/index.js +4 -4
- package/dist/{taskRunner-B7lBU45X.d.ts → taskRunner-B1NUWyWR.d.ts} +1 -1
- package/dist/{taskRunner-pIGyarFG.d.cts → taskRunner-vFRA3F9b.d.cts} +1 -1
- 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 +13 -13
- 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-C4L_X0i6.d.ts → wallet-By9HIo0Q.d.cts} +160 -5
- package/dist/{wallet-D4Dll5Gu.d.cts → wallet-D6uoBLmS.d.ts} +160 -5
- 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 +4 -4
- package/dist/chunk-7K3ROJF6.cjs.map +0 -1
- package/dist/chunk-AOJUURHM.js.map +0 -1
- package/dist/chunk-DSS2GQUG.js.map +0 -1
- package/dist/chunk-GYSK5R57.cjs.map +0 -1
- package/dist/chunk-TU3LVAPX.js.map +0 -1
- package/dist/chunk-WMIPYZSB.cjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { craftToSpendTx, Transaction, OP_RETURN_EMPTY_PKSCRIPT, Intent, getArkPsbtFields, CosignerPublicKey, setArkPsbtField, VtxoTaprootTree, maybeArkError, AssetRef, AssetId, AssetOutput, AssetGroup, AssetInput, Packet, Metadata, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError, BufferReader } from './chunk-
|
|
2
|
-
import { isMainnetDescriptor, descriptorIsOurs, contractHandlers, DelegateVtxo, WALLET_RECEIVE_SOURCE, deriveDescriptorLeafPubKey, DefaultVtxo, BoardingContractHandler } from './chunk-
|
|
3
|
-
import { VtxoScript, timelockToSequence, DEFAULT_NETWORK, DEFAULT_NETWORK_NAME, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, ArkAddress,
|
|
1
|
+
import { craftToSpendTx, Transaction, OP_RETURN_EMPTY_PKSCRIPT, Intent, getArkPsbtFields, CosignerPublicKey, setArkPsbtField, VtxoTaprootTree, maybeArkError, AssetRef, AssetId, AssetOutput, AssetGroup, AssetInput, Packet, Metadata, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError, BufferReader } from './chunk-FXFBPXV3.js';
|
|
2
|
+
import { isMainnetDescriptor, descriptorIsOurs, contractHandlers, DEFAULT_PAGE_SIZE, DelegateVtxo, WALLET_RECEIVE_SOURCE, deriveDescriptorLeafPubKey, DefaultVtxo, BoardingContractHandler } from './chunk-CUSABEUQ.js';
|
|
3
|
+
import { VtxoScript, timelockToSequence, DEFAULT_NETWORK, DEFAULT_NETWORK_NAME, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, ArkAddress, CSVMultisigTapscript, getSequence, getNetwork, MultisigTapscript, networks as networks$1, DEFAULT_ARKADE_SERVER_URL } from './chunk-OUVTG72A.js';
|
|
4
4
|
import { sha256, hash160, sha256x2, concatBytes, randomPrivateKeyBytes, pubECDSA, pubSchnorr, equalBytes as equalBytes$1 } from '@scure/btc-signer/utils.js';
|
|
5
5
|
import { SigHash, Script, p2tr, RawWitness, Address, OutScript, p2wpkh, TaprootControlBlock, DEFAULT_SEQUENCE, Transaction as Transaction$2 } from '@scure/btc-signer';
|
|
6
6
|
import { hex, base58, base64 } from '@scure/base';
|
|
@@ -1009,7 +1009,7 @@ var ESPLORA_URL = {
|
|
|
1009
1009
|
testnet: "https://mempool.space/testnet/api",
|
|
1010
1010
|
signet: "https://mempool.signet.arkade.sh/api",
|
|
1011
1011
|
mutinynet: "https://mempool.mutinynet.arkade.sh/api",
|
|
1012
|
-
regtest: "http://localhost:3000"
|
|
1012
|
+
regtest: "http://localhost:3000/api"
|
|
1013
1013
|
};
|
|
1014
1014
|
var EsploraProvider = class {
|
|
1015
1015
|
constructor(baseUrl = ESPLORA_URL[DEFAULT_NETWORK_NAME], opts) {
|
|
@@ -1028,6 +1028,9 @@ var EsploraProvider = class {
|
|
|
1028
1028
|
}
|
|
1029
1029
|
async getFeeRate() {
|
|
1030
1030
|
const response = await fetch(`${this.baseUrl}/fee-estimates`);
|
|
1031
|
+
if (response.status === 404) {
|
|
1032
|
+
return void 0;
|
|
1033
|
+
}
|
|
1031
1034
|
if (!response.ok) {
|
|
1032
1035
|
throw new Error(`Failed to fetch fee rate: ${response.statusText}`);
|
|
1033
1036
|
}
|
|
@@ -1153,7 +1156,7 @@ var EsploraProvider = class {
|
|
|
1153
1156
|
return stopFunc;
|
|
1154
1157
|
}
|
|
1155
1158
|
async getChainTip() {
|
|
1156
|
-
const tipBlocks = await fetch(`${this.baseUrl}/blocks
|
|
1159
|
+
const tipBlocks = await fetch(`${this.baseUrl}/blocks`);
|
|
1157
1160
|
if (!tipBlocks.ok) {
|
|
1158
1161
|
throw new Error(`Failed to get chain tip: ${tipBlocks.statusText}`);
|
|
1159
1162
|
}
|
|
@@ -2258,12 +2261,12 @@ var FALLBACK_WALLET_DUST_AMOUNT = 330n;
|
|
|
2258
2261
|
function getDustAmount(wallet) {
|
|
2259
2262
|
return "dustAmount" in wallet ? wallet.dustAmount : FALLBACK_WALLET_DUST_AMOUNT;
|
|
2260
2263
|
}
|
|
2261
|
-
function
|
|
2264
|
+
function extendCoinWithTapscript(boardingTapscript, utxo) {
|
|
2262
2265
|
return {
|
|
2263
2266
|
...utxo,
|
|
2264
|
-
forfeitTapLeafScript:
|
|
2265
|
-
intentTapLeafScript:
|
|
2266
|
-
tapTree:
|
|
2267
|
+
forfeitTapLeafScript: boardingTapscript.forfeit(),
|
|
2268
|
+
intentTapLeafScript: boardingTapscript.forfeit(),
|
|
2269
|
+
tapTree: boardingTapscript.encode()
|
|
2267
2270
|
};
|
|
2268
2271
|
}
|
|
2269
2272
|
function deriveContractTapscripts(contract) {
|
|
@@ -2357,12 +2360,12 @@ function validateRecipients(recipients, dustAmount) {
|
|
|
2357
2360
|
|
|
2358
2361
|
// src/wallet/vtxo-manager.ts
|
|
2359
2362
|
function isSweepCapable(wallet) {
|
|
2360
|
-
return "boardingTapscript" in wallet && "onchainProvider" in wallet && "arkProvider" in wallet && "network" in wallet;
|
|
2363
|
+
return "boardingTapscript" in wallet && "onchainProvider" in wallet && "arkProvider" in wallet && "network" in wallet && "signOnchainBoardingTx" in wallet;
|
|
2361
2364
|
}
|
|
2362
2365
|
function assertSweepCapable(wallet) {
|
|
2363
2366
|
if (!isSweepCapable(wallet)) {
|
|
2364
2367
|
throw new Error(
|
|
2365
|
-
"Boarding UTXO sweep requires a Wallet instance with boardingTapscript, onchainProvider, arkProvider, and
|
|
2368
|
+
"Boarding UTXO sweep requires a Wallet instance with boardingTapscript, onchainProvider, arkProvider, network, and signOnchainBoardingTx"
|
|
2366
2369
|
);
|
|
2367
2370
|
}
|
|
2368
2371
|
}
|
|
@@ -2378,6 +2381,21 @@ async function runWithCrossInstanceLock(name, fn) {
|
|
|
2378
2381
|
await fn();
|
|
2379
2382
|
});
|
|
2380
2383
|
}
|
|
2384
|
+
var MAX_VTXOS_PER_SETTLEMENT = 50;
|
|
2385
|
+
function byValueDescending(vtxos) {
|
|
2386
|
+
return [...vtxos].sort((a, b) => b.value - a.value);
|
|
2387
|
+
}
|
|
2388
|
+
function byExpiryAscending(vtxos) {
|
|
2389
|
+
const expiryKey = (vtxo) => {
|
|
2390
|
+
if (isRecoverable(vtxo)) return -Infinity;
|
|
2391
|
+
const batchExpiry = vtxo.virtualStatus.batchExpiry;
|
|
2392
|
+
if (isExpired(vtxo)) return batchExpiry ?? -Infinity;
|
|
2393
|
+
if (!batchExpiry) return Infinity;
|
|
2394
|
+
if (new Date(batchExpiry).getFullYear() < 2025) return Infinity;
|
|
2395
|
+
return batchExpiry;
|
|
2396
|
+
};
|
|
2397
|
+
return [...vtxos].sort((a, b) => expiryKey(a) - expiryKey(b));
|
|
2398
|
+
}
|
|
2381
2399
|
var DEFAULT_THRESHOLD_SECONDS = 259200;
|
|
2382
2400
|
var DEFAULT_THRESHOLD_MS = DEFAULT_THRESHOLD_SECONDS * 1e3;
|
|
2383
2401
|
var DEFAULT_RENEWAL_CONFIG = {
|
|
@@ -2537,10 +2555,20 @@ var VtxoManager = class _VtxoManager {
|
|
|
2537
2555
|
withUnrolled: false
|
|
2538
2556
|
});
|
|
2539
2557
|
const dustAmount = getDustAmount(this.wallet);
|
|
2540
|
-
|
|
2558
|
+
let { vtxosToRecover, totalAmount } = getRecoverableWithSubdust(allVtxos, dustAmount);
|
|
2541
2559
|
if (vtxosToRecover.length === 0) {
|
|
2542
2560
|
throw new Error("No recoverable VTXOs found");
|
|
2543
2561
|
}
|
|
2562
|
+
if (vtxosToRecover.length > MAX_VTXOS_PER_SETTLEMENT) {
|
|
2563
|
+
const recoverableCount = vtxosToRecover.length;
|
|
2564
|
+
const capped = byValueDescending(vtxosToRecover).slice(0, MAX_VTXOS_PER_SETTLEMENT);
|
|
2565
|
+
({ vtxosToRecover, totalAmount } = getRecoverableWithSubdust(capped, dustAmount));
|
|
2566
|
+
if (vtxosToRecover.length === 0) {
|
|
2567
|
+
throw new Error(
|
|
2568
|
+
`Capped recovery batch (highest-value ${MAX_VTXOS_PER_SETTLEMENT} of ${recoverableCount} recoverable VTXOs) is below the dust threshold ${dustAmount}`
|
|
2569
|
+
);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2544
2572
|
const arkAddress = await this.wallet.getAddress();
|
|
2545
2573
|
return this.wallet.settle(
|
|
2546
2574
|
{
|
|
@@ -2690,6 +2718,9 @@ var VtxoManager = class _VtxoManager {
|
|
|
2690
2718
|
if (vtxos.length === 0) {
|
|
2691
2719
|
throw new Error("No VTXOs available to renew");
|
|
2692
2720
|
}
|
|
2721
|
+
if (vtxos.length > MAX_VTXOS_PER_SETTLEMENT) {
|
|
2722
|
+
vtxos = byExpiryAscending(vtxos).slice(0, MAX_VTXOS_PER_SETTLEMENT);
|
|
2723
|
+
}
|
|
2693
2724
|
const totalAmount = vtxos.reduce((sum, vtxo) => sum + vtxo.value, 0);
|
|
2694
2725
|
const dustAmount = getDustAmount(this.wallet);
|
|
2695
2726
|
if (BigInt(totalAmount) < dustAmount) {
|
|
@@ -2797,7 +2828,6 @@ var VtxoManager = class _VtxoManager {
|
|
|
2797
2828
|
const boardingAddress = await this.wallet.getBoardingAddress();
|
|
2798
2829
|
const feeRate = await this.getOnchainProvider().getFeeRate() ?? 1;
|
|
2799
2830
|
const exitTapLeafScript = this.getBoardingExitLeaf();
|
|
2800
|
-
const sequence = getSequence(exitTapLeafScript);
|
|
2801
2831
|
const leafScript = exitTapLeafScript[1];
|
|
2802
2832
|
const leafScriptSize = leafScript.length - 1;
|
|
2803
2833
|
const controlBlockSize = exitTapLeafScript[0].merklePath.length * 32;
|
|
@@ -2818,19 +2848,28 @@ var VtxoManager = class _VtxoManager {
|
|
|
2818
2848
|
}
|
|
2819
2849
|
const tx = new Transaction();
|
|
2820
2850
|
for (const utxo of expiredUtxos) {
|
|
2851
|
+
const utxoScript = VtxoScript.decode(utxo.tapTree);
|
|
2852
|
+
const utxoExitLeaf = utxoScript.leaves.find(
|
|
2853
|
+
(leaf) => CSVMultisigTapscript.isScriptValid(scriptFromTapLeafScript(leaf)) === true
|
|
2854
|
+
);
|
|
2855
|
+
if (!utxoExitLeaf) {
|
|
2856
|
+
throw new Error(
|
|
2857
|
+
`Boarding sweep: no CSV exit leaf for UTXO ${utxo.txid}:${utxo.vout}`
|
|
2858
|
+
);
|
|
2859
|
+
}
|
|
2821
2860
|
tx.addInput({
|
|
2822
2861
|
txid: utxo.txid,
|
|
2823
2862
|
index: utxo.vout,
|
|
2824
2863
|
witnessUtxo: {
|
|
2825
|
-
script:
|
|
2864
|
+
script: utxoScript.pkScript,
|
|
2826
2865
|
amount: BigInt(utxo.value)
|
|
2827
2866
|
},
|
|
2828
|
-
tapLeafScript: [
|
|
2829
|
-
sequence
|
|
2867
|
+
tapLeafScript: [utxoExitLeaf],
|
|
2868
|
+
sequence: getSequence(utxoExitLeaf)
|
|
2830
2869
|
});
|
|
2831
2870
|
}
|
|
2832
2871
|
tx.addOutputAddress(boardingAddress, outputAmount, this.getNetwork());
|
|
2833
|
-
const signedTx = await this.
|
|
2872
|
+
const signedTx = await this.getSweepWallet().signOnchainBoardingTx(tx);
|
|
2834
2873
|
signedTx.finalize();
|
|
2835
2874
|
const txid = await this.getOnchainProvider().broadcastTransaction(signedTx.hex);
|
|
2836
2875
|
for (const u of expiredUtxos) {
|
|
@@ -2857,10 +2896,6 @@ var VtxoManager = class _VtxoManager {
|
|
|
2857
2896
|
getBoardingExitLeaf() {
|
|
2858
2897
|
return this.getSweepWallet().boardingTapscript.exit();
|
|
2859
2898
|
}
|
|
2860
|
-
/** Returns the pkScript (output script) of the boarding tapscript. */
|
|
2861
|
-
getBoardingOutputScript() {
|
|
2862
|
-
return this.getSweepWallet().boardingTapscript.pkScript;
|
|
2863
|
-
}
|
|
2864
2899
|
/** Returns the onchain provider for fee estimation and broadcasting. */
|
|
2865
2900
|
getOnchainProvider() {
|
|
2866
2901
|
return this.getSweepWallet().onchainProvider;
|
|
@@ -2873,10 +2908,6 @@ var VtxoManager = class _VtxoManager {
|
|
|
2873
2908
|
getNetwork() {
|
|
2874
2909
|
return this.getSweepWallet().network;
|
|
2875
2910
|
}
|
|
2876
|
-
/** Returns the wallet's identity for transaction signing. */
|
|
2877
|
-
getIdentity() {
|
|
2878
|
-
return this.wallet.identity;
|
|
2879
|
-
}
|
|
2880
2911
|
async initializeSubscription() {
|
|
2881
2912
|
if (this.settlementConfig === false) {
|
|
2882
2913
|
return void 0;
|
|
@@ -3157,7 +3188,10 @@ var VtxoManager = class _VtxoManager {
|
|
|
3157
3188
|
totalAmount += BigInt(u.value) - BigInt(inputFee.satoshis);
|
|
3158
3189
|
}
|
|
3159
3190
|
const filteredVtxos = [];
|
|
3160
|
-
for (const v of expiringVtxos) {
|
|
3191
|
+
for (const v of byExpiryAscending(expiringVtxos)) {
|
|
3192
|
+
if (filteredVtxos.length >= MAX_VTXOS_PER_SETTLEMENT) {
|
|
3193
|
+
break;
|
|
3194
|
+
}
|
|
3161
3195
|
const inputFee = estimator.evalOffchainInput({
|
|
3162
3196
|
amount: BigInt(v.value),
|
|
3163
3197
|
type: v.virtualStatus.state === "swept" ? "recoverable" : "vtxo",
|
|
@@ -6515,8 +6549,11 @@ function cursorCutoff(requestStartedAt) {
|
|
|
6515
6549
|
}
|
|
6516
6550
|
|
|
6517
6551
|
// src/contracts/contractManager.ts
|
|
6518
|
-
|
|
6552
|
+
function areCoalescibleContractTypes(a, b) {
|
|
6553
|
+
return a === "default" && b === "boarding" || a === "boarding" && b === "default";
|
|
6554
|
+
}
|
|
6519
6555
|
var SCAN_MAX_INDEX = 1e4;
|
|
6556
|
+
var DEFAULT_SCAN_BATCH = 10;
|
|
6520
6557
|
var ContractManager = class _ContractManager {
|
|
6521
6558
|
config;
|
|
6522
6559
|
watcher;
|
|
@@ -6640,8 +6677,11 @@ var ContractManager = class _ContractManager {
|
|
|
6640
6677
|
const [existing] = await this.getContracts({ script: params.script });
|
|
6641
6678
|
if (existing) {
|
|
6642
6679
|
if (existing.type === params.type) return { contract: existing, persisted: false };
|
|
6680
|
+
if (areCoalescibleContractTypes(existing.type, params.type)) {
|
|
6681
|
+
return { contract: existing, persisted: false };
|
|
6682
|
+
}
|
|
6643
6683
|
throw new Error(
|
|
6644
|
-
`Contract with script ${params.script} already exists with
|
|
6684
|
+
`Contract with script ${params.script} already exists with type ${existing.type}.`
|
|
6645
6685
|
);
|
|
6646
6686
|
}
|
|
6647
6687
|
const contract = {
|
|
@@ -6670,6 +6710,19 @@ var ContractManager = class _ContractManager {
|
|
|
6670
6710
|
* other handler hit it).
|
|
6671
6711
|
* - `persistAndWatchContract` rejecting is operational/fatal and
|
|
6672
6712
|
* propagates (only `discoverAt` is guarded).
|
|
6713
|
+
* - Within an index the handler probes run concurrently (independent
|
|
6714
|
+
* network reads); their hits are persisted sequentially in
|
|
6715
|
+
* `discoverables` order to preserve the first-wins collision tie-break.
|
|
6716
|
+
* - Indices are probed `batchSize` at a time (a second concurrency layer
|
|
6717
|
+
* over the per-index probes), but each window is CAPPED to
|
|
6718
|
+
* `gapLimit - unused` indices — the most a serial scan could still reach
|
|
6719
|
+
* before the gap window is guaranteed to close. So every index probed in
|
|
6720
|
+
* a window is one a one-index-at-a-time scan would also reach: nothing is
|
|
6721
|
+
* over-scanned, nothing is discarded, and `materialize`/`discoverAt` are
|
|
6722
|
+
* invoked on exactly the same index set. The window's hits are still
|
|
6723
|
+
* processed strictly in ascending index order, so the discovered set,
|
|
6724
|
+
* persisted rows, `lastIndexUsed`, and `handlerErrors` are byte-for-byte
|
|
6725
|
+
* identical to the serial path — only the wall-clock differs.
|
|
6673
6726
|
*/
|
|
6674
6727
|
async scanContracts(opts) {
|
|
6675
6728
|
const gapLimit = opts.gapLimit ?? 20;
|
|
@@ -6678,35 +6731,69 @@ var ContractManager = class _ContractManager {
|
|
|
6678
6731
|
`scanContracts: gapLimit must be a positive integer (got ${String(opts.gapLimit)})`
|
|
6679
6732
|
);
|
|
6680
6733
|
}
|
|
6681
|
-
const
|
|
6734
|
+
const batchSize = opts.batchSize ?? DEFAULT_SCAN_BATCH;
|
|
6735
|
+
if (!Number.isInteger(batchSize) || batchSize <= 0) {
|
|
6736
|
+
throw new Error(
|
|
6737
|
+
`scanContracts: batchSize must be a positive integer (got ${String(opts.batchSize)})`
|
|
6738
|
+
);
|
|
6739
|
+
}
|
|
6740
|
+
const registered = contractHandlers.getRegisteredTypes().map((t) => contractHandlers.get(t)).filter(isDiscoverable);
|
|
6741
|
+
const discoverables = [
|
|
6742
|
+
...registered.filter((h) => h.type === "boarding"),
|
|
6743
|
+
...registered.filter((h) => h.type !== "boarding")
|
|
6744
|
+
];
|
|
6682
6745
|
const maxIdx = opts.hd ? SCAN_MAX_INDEX : 0;
|
|
6683
6746
|
const handlerErrors = [];
|
|
6684
6747
|
let lastIndexUsed = -1;
|
|
6685
6748
|
let unused = 0;
|
|
6686
6749
|
let i = 0;
|
|
6750
|
+
const probeIndex = async (index) => {
|
|
6751
|
+
const descriptor = opts.materialize(index);
|
|
6752
|
+
return Promise.all(
|
|
6753
|
+
discoverables.map(async (h) => {
|
|
6754
|
+
try {
|
|
6755
|
+
return {
|
|
6756
|
+
ok: true,
|
|
6757
|
+
found: await h.discoverAt(index, descriptor, opts.deps)
|
|
6758
|
+
};
|
|
6759
|
+
} catch (error) {
|
|
6760
|
+
return { ok: false, error };
|
|
6761
|
+
}
|
|
6762
|
+
})
|
|
6763
|
+
);
|
|
6764
|
+
};
|
|
6687
6765
|
while (i <= maxIdx && unused < gapLimit) {
|
|
6688
|
-
const
|
|
6689
|
-
|
|
6690
|
-
for (
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6766
|
+
const windowEnd = Math.min(maxIdx, i + Math.min(batchSize, gapLimit - unused) - 1);
|
|
6767
|
+
const windowIndices = [];
|
|
6768
|
+
for (let idx = i; idx <= windowEnd; idx++) windowIndices.push(idx);
|
|
6769
|
+
const windowProbes = await Promise.all(windowIndices.map(probeIndex));
|
|
6770
|
+
for (let w = 0; w < windowIndices.length; w++) {
|
|
6771
|
+
const index = windowIndices[w];
|
|
6772
|
+
const probes = windowProbes[w];
|
|
6773
|
+
let hitAtThisIndex = false;
|
|
6774
|
+
for (let h = 0; h < discoverables.length; h++) {
|
|
6775
|
+
const probe = probes[h];
|
|
6776
|
+
if (!probe.ok) {
|
|
6777
|
+
handlerErrors.push({
|
|
6778
|
+
handler: discoverables[h].type,
|
|
6779
|
+
index,
|
|
6780
|
+
error: probe.error
|
|
6781
|
+
});
|
|
6782
|
+
continue;
|
|
6783
|
+
}
|
|
6784
|
+
for (const c of probe.found) {
|
|
6785
|
+
await this.persistAndWatchContract(c);
|
|
6786
|
+
hitAtThisIndex = true;
|
|
6787
|
+
}
|
|
6697
6788
|
}
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6789
|
+
if (hitAtThisIndex) {
|
|
6790
|
+
lastIndexUsed = index;
|
|
6791
|
+
unused = 0;
|
|
6792
|
+
} else {
|
|
6793
|
+
unused += 1;
|
|
6701
6794
|
}
|
|
6702
6795
|
}
|
|
6703
|
-
|
|
6704
|
-
lastIndexUsed = i;
|
|
6705
|
-
unused = 0;
|
|
6706
|
-
} else {
|
|
6707
|
-
unused += 1;
|
|
6708
|
-
}
|
|
6709
|
-
i += 1;
|
|
6796
|
+
i = windowEnd + 1;
|
|
6710
6797
|
}
|
|
6711
6798
|
if (opts.hd && i > maxIdx && unused < gapLimit) {
|
|
6712
6799
|
throw new Error(
|
|
@@ -7412,7 +7499,8 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
|
|
|
7412
7499
|
walletRepository: setup.walletRepository,
|
|
7413
7500
|
contractRepository: setup.contractRepository,
|
|
7414
7501
|
serverPubKey: setup.serverPubKey,
|
|
7415
|
-
expectedContractType
|
|
7502
|
+
expectedContractType,
|
|
7503
|
+
baselineReceivePubKey: setup.offchainTapscript.options.pubKey
|
|
7416
7504
|
};
|
|
7417
7505
|
let boot;
|
|
7418
7506
|
try {
|
|
@@ -7457,14 +7545,17 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
|
|
|
7457
7545
|
receivePubkey: existing.pubKey
|
|
7458
7546
|
};
|
|
7459
7547
|
}
|
|
7460
|
-
|
|
7461
|
-
if (
|
|
7462
|
-
descriptor = await provider.
|
|
7548
|
+
const current = hasPeekableDescriptor(provider) ? await provider.getCurrentSigningDescriptor() : void 0;
|
|
7549
|
+
if (current === void 0) {
|
|
7550
|
+
const descriptor = await provider.getNextSigningDescriptor();
|
|
7551
|
+
return {
|
|
7552
|
+
rotator: new _WalletReceiveRotator(provider, void 0, opts.logger),
|
|
7553
|
+
receivePubkey: deriveLeafPubkey(descriptor)
|
|
7554
|
+
};
|
|
7463
7555
|
}
|
|
7464
|
-
descriptor ??= await provider.getNextSigningDescriptor();
|
|
7465
7556
|
return {
|
|
7466
7557
|
rotator: new _WalletReceiveRotator(provider, void 0, opts.logger),
|
|
7467
|
-
receivePubkey: deriveLeafPubkey(
|
|
7558
|
+
receivePubkey: opts.baselineReceivePubKey ?? deriveLeafPubkey(current)
|
|
7468
7559
|
};
|
|
7469
7560
|
}
|
|
7470
7561
|
/**
|
|
@@ -7697,7 +7788,7 @@ var DescriptorSigningProviderMissingError = class extends Error {
|
|
|
7697
7788
|
};
|
|
7698
7789
|
|
|
7699
7790
|
// src/wallet/inputSignerRouter.ts
|
|
7700
|
-
var DESCRIPTOR_CAPABLE_CONTRACT_TYPES = /* @__PURE__ */ new Set(["default", "delegate"]);
|
|
7791
|
+
var DESCRIPTOR_CAPABLE_CONTRACT_TYPES = /* @__PURE__ */ new Set(["default", "delegate", "boarding"]);
|
|
7701
7792
|
var InputSignerRouter = class {
|
|
7702
7793
|
constructor(deps) {
|
|
7703
7794
|
this.deps = deps;
|
|
@@ -7829,6 +7920,11 @@ function extractArkProviderUrl(provider) {
|
|
|
7829
7920
|
return typeof serverUrl === "string" && serverUrl.length > 0 ? serverUrl : void 0;
|
|
7830
7921
|
}
|
|
7831
7922
|
var MAINNET_UNILATERAL_EXIT_DELAY = 605184n;
|
|
7923
|
+
function toXOnlyPubKey(pubkey) {
|
|
7924
|
+
if (pubkey.length === 33) return pubkey.slice(1);
|
|
7925
|
+
if (pubkey.length === 32) return pubkey;
|
|
7926
|
+
throw new Error(`invalid signer pubkey length: expected 32 or 33, got ${pubkey.length}`);
|
|
7927
|
+
}
|
|
7832
7928
|
function delayToTimelock(delay) {
|
|
7833
7929
|
return {
|
|
7834
7930
|
value: delay,
|
|
@@ -7846,20 +7942,30 @@ function dedupeTimelocks(timelocks) {
|
|
|
7846
7942
|
}
|
|
7847
7943
|
return deduped;
|
|
7848
7944
|
}
|
|
7849
|
-
function areSameScriptBaselineTypesCompatible(existingType, requestedType) {
|
|
7850
|
-
if (existingType === requestedType) return true;
|
|
7851
|
-
return existingType === "default" && requestedType === "boarding" || existingType === "boarding" && requestedType === "default";
|
|
7852
|
-
}
|
|
7853
7945
|
async function ensureWalletContract(manager, params) {
|
|
7854
|
-
const [existing] = await manager.getContracts({ script: params.script });
|
|
7855
|
-
if (existing && existing.type !== params.type && areSameScriptBaselineTypesCompatible(existing.type, params.type)) {
|
|
7856
|
-
if (params.type === "default" && existing.type === "boarding") {
|
|
7857
|
-
await manager.updateContract(params.script, { type: "default" });
|
|
7858
|
-
}
|
|
7859
|
-
return;
|
|
7860
|
-
}
|
|
7861
7946
|
await manager.createContract(params);
|
|
7862
7947
|
}
|
|
7948
|
+
async function resolveBoardingBootTapscript(contractRepository, serverPubKey, baseline) {
|
|
7949
|
+
const serverPubKeyHex = hex.encode(serverPubKey);
|
|
7950
|
+
const candidates = await contractRepository.getContracts({
|
|
7951
|
+
type: ["boarding"],
|
|
7952
|
+
state: "active"
|
|
7953
|
+
});
|
|
7954
|
+
const newest = candidates.filter(
|
|
7955
|
+
(c) => c.params.serverPubKey === serverPubKeyHex && c.metadata?.source === WALLET_RECEIVE_SOURCE
|
|
7956
|
+
).sort((a, b) => {
|
|
7957
|
+
if (b.createdAt !== a.createdAt) return b.createdAt - a.createdAt;
|
|
7958
|
+
return signingDescriptorIndex(b.metadata?.signingDescriptor) - signingDescriptorIndex(a.metadata?.signingDescriptor);
|
|
7959
|
+
})[0];
|
|
7960
|
+
if (!newest?.params.pubKey) return baseline;
|
|
7961
|
+
try {
|
|
7962
|
+
const pubKey = hex.decode(newest.params.pubKey);
|
|
7963
|
+
return new DefaultVtxo.Script({ ...baseline.options, pubKey });
|
|
7964
|
+
} catch (e) {
|
|
7965
|
+
console.warn("Skipping malformed boarding contract at boot", newest.script, e);
|
|
7966
|
+
return baseline;
|
|
7967
|
+
}
|
|
7968
|
+
}
|
|
7863
7969
|
function hasToReadonly(identity) {
|
|
7864
7970
|
return typeof identity === "object" && identity !== null && "toReadonly" in identity && typeof identity.toReadonly === "function";
|
|
7865
7971
|
}
|
|
@@ -7870,7 +7976,6 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
7870
7976
|
this.onchainProvider = onchainProvider;
|
|
7871
7977
|
this.indexerProvider = indexerProvider;
|
|
7872
7978
|
this.arkServerPublicKey = arkServerPublicKey;
|
|
7873
|
-
this.boardingTapscript = boardingTapscript;
|
|
7874
7979
|
this.dustAmount = dustAmount;
|
|
7875
7980
|
this.walletRepository = walletRepository;
|
|
7876
7981
|
this.contractRepository = contractRepository;
|
|
@@ -7886,6 +7991,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
7886
7991
|
}
|
|
7887
7992
|
}
|
|
7888
7993
|
this._offchainTapscript = offchainTapscript;
|
|
7994
|
+
this._boardingTapscript = boardingTapscript;
|
|
7889
7995
|
this.watcherConfig = watcherConfig;
|
|
7890
7996
|
this._assetManager = new ReadonlyAssetManager(this.indexerProvider);
|
|
7891
7997
|
this.walletContractTimelocks = walletContractTimelocks && walletContractTimelocks.length > 0 ? dedupeTimelocks(walletContractTimelocks) : [this.offchainTapscript.options.csvTimelock];
|
|
@@ -7912,6 +8018,17 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
7912
8018
|
* {@link WalletReceiveRotator.rotate} is the sole intended caller of.
|
|
7913
8019
|
*/
|
|
7914
8020
|
_offchainTapscript;
|
|
8021
|
+
/**
|
|
8022
|
+
* Backing field for the current boarding tapscript (the QR / onboarding
|
|
8023
|
+
* target). Read via the public `boardingTapscript` getter; written only
|
|
8024
|
+
* by {@link Wallet.setBoardingTapscriptForRotation}, the sanctioned
|
|
8025
|
+
* boarding-rotation write path (analogue of `_offchainTapscript`). It is
|
|
8026
|
+
* a *current value*, not a fixed setup constant, because per-derivation
|
|
8027
|
+
* boarding rotation (plan §6-II) swaps it when a fresh boarding address
|
|
8028
|
+
* is explicitly allocated. Static / `auto` wallets never rotate it, so
|
|
8029
|
+
* it stays the index-0 baseline for their lifetime.
|
|
8030
|
+
*/
|
|
8031
|
+
_boardingTapscript;
|
|
7915
8032
|
/**
|
|
7916
8033
|
* Currently-active receive tapscript. Read-only from the outside;
|
|
7917
8034
|
* mutated only via {@link Wallet.setOffchainTapscriptForRotation}
|
|
@@ -7920,6 +8037,52 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
7920
8037
|
get offchainTapscript() {
|
|
7921
8038
|
return this._offchainTapscript;
|
|
7922
8039
|
}
|
|
8040
|
+
/**
|
|
8041
|
+
* The wallet's current boarding tapscript (the on-chain onboarding
|
|
8042
|
+
* target). Read-only from the outside; mutated only via
|
|
8043
|
+
* {@link Wallet.setBoardingTapscriptForRotation} when a fresh boarding
|
|
8044
|
+
* address is explicitly allocated. Single-valued for static / `auto`
|
|
8045
|
+
* wallets.
|
|
8046
|
+
*/
|
|
8047
|
+
get boardingTapscript() {
|
|
8048
|
+
return this._boardingTapscript;
|
|
8049
|
+
}
|
|
8050
|
+
/**
|
|
8051
|
+
* Listeners fired after the boarding tapscript rotates to a fresh index
|
|
8052
|
+
* (see {@link Wallet.setBoardingTapscriptForRotation}). A live
|
|
8053
|
+
* {@link notifyIncomingFunds} onchain watcher registers one so it can
|
|
8054
|
+
* re-subscribe to include the newly allocated boarding address within the
|
|
8055
|
+
* same session — without it, a deposit to the fresh address wouldn't fire
|
|
8056
|
+
* a notification until the watcher's next re-init. Always empty for
|
|
8057
|
+
* readonly / static / `auto` wallets, which never rotate boarding.
|
|
8058
|
+
*/
|
|
8059
|
+
_boardingRotationListeners = /* @__PURE__ */ new Set();
|
|
8060
|
+
/**
|
|
8061
|
+
* Register a listener invoked synchronously after each boarding rotation.
|
|
8062
|
+
* Returns an unsubscribe function. Protected: only internal subscribers
|
|
8063
|
+
* (the incoming-funds watcher) participate.
|
|
8064
|
+
*/
|
|
8065
|
+
onBoardingRotation(listener) {
|
|
8066
|
+
this._boardingRotationListeners.add(listener);
|
|
8067
|
+
return () => {
|
|
8068
|
+
this._boardingRotationListeners.delete(listener);
|
|
8069
|
+
};
|
|
8070
|
+
}
|
|
8071
|
+
/**
|
|
8072
|
+
* Notify boarding-rotation listeners. Called by the boarding-rotation
|
|
8073
|
+
* write path ({@link Wallet.setBoardingTapscriptForRotation}) once the new
|
|
8074
|
+
* tapscript is in place. A throwing listener is isolated so it can neither
|
|
8075
|
+
* break the rotation nor starve sibling listeners.
|
|
8076
|
+
*/
|
|
8077
|
+
notifyBoardingRotation() {
|
|
8078
|
+
for (const listener of this._boardingRotationListeners) {
|
|
8079
|
+
try {
|
|
8080
|
+
listener();
|
|
8081
|
+
} catch (e) {
|
|
8082
|
+
console.warn("Boarding-rotation listener failed", e);
|
|
8083
|
+
}
|
|
8084
|
+
}
|
|
8085
|
+
}
|
|
7923
8086
|
/**
|
|
7924
8087
|
* Protected helper to set up shared wallet configuration.
|
|
7925
8088
|
* Extracts common logic used by both ReadonlyWallet.create() and Wallet.create().
|
|
@@ -8160,43 +8323,59 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
8160
8323
|
await clearSyncCursor(this.walletRepository);
|
|
8161
8324
|
}
|
|
8162
8325
|
/**
|
|
8163
|
-
*
|
|
8326
|
+
* The on-chain (P2TR) addresses of every boarding tapscript this wallet
|
|
8327
|
+
* uses — the current address plus any historical rotated boarding
|
|
8328
|
+
* addresses. The aggregating boarding readers (history, notifications) fan
|
|
8329
|
+
* out over this set so deposits at previous boarding addresses are still
|
|
8330
|
+
* surfaced (plan §6-IV); {@link getBoardingAddress} stays single-valued.
|
|
8331
|
+
*/
|
|
8332
|
+
async getBoardingAddresses() {
|
|
8333
|
+
const tapscripts = await this.getBoardingTapscripts();
|
|
8334
|
+
return tapscripts.map((t) => t.onchainAddress(this.network));
|
|
8335
|
+
}
|
|
8336
|
+
/**
|
|
8337
|
+
* Build a transaction history view across the wallet's boarding addresses
|
|
8338
|
+
* (current + historical rotated; plan §6-IV.1).
|
|
8164
8339
|
*/
|
|
8165
8340
|
async getBoardingTxs() {
|
|
8166
8341
|
const utxos = [];
|
|
8167
8342
|
const commitmentsToIgnore = /* @__PURE__ */ new Set();
|
|
8168
|
-
const
|
|
8169
|
-
const txs = await this.onchainProvider.getTransactions(boardingAddress);
|
|
8343
|
+
const tapscripts = await this.getBoardingTapscripts();
|
|
8170
8344
|
const outspendCache = /* @__PURE__ */ new Map();
|
|
8171
|
-
for (const
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8345
|
+
for (const tapscript of tapscripts) {
|
|
8346
|
+
const boardingAddress = tapscript.onchainAddress(this.network);
|
|
8347
|
+
const scriptHex = hex.encode(tapscript.pkScript);
|
|
8348
|
+
const txs = await this.onchainProvider.getTransactions(boardingAddress);
|
|
8349
|
+
for (const tx of txs) {
|
|
8350
|
+
for (let i = 0; i < tx.vout.length; i++) {
|
|
8351
|
+
const vout = tx.vout[i];
|
|
8352
|
+
if (vout.scriptpubkey_address === boardingAddress) {
|
|
8353
|
+
let spentStatuses = outspendCache.get(tx.txid);
|
|
8354
|
+
if (!spentStatuses) {
|
|
8355
|
+
spentStatuses = await this.onchainProvider.getTxOutspends(tx.txid);
|
|
8356
|
+
outspendCache.set(tx.txid, spentStatuses);
|
|
8357
|
+
}
|
|
8358
|
+
const spentStatus = spentStatuses[i];
|
|
8359
|
+
if (spentStatus?.spent) {
|
|
8360
|
+
commitmentsToIgnore.add(spentStatus.txid);
|
|
8361
|
+
}
|
|
8362
|
+
utxos.push({
|
|
8363
|
+
txid: tx.txid,
|
|
8364
|
+
vout: i,
|
|
8365
|
+
value: Number(vout.value),
|
|
8366
|
+
status: {
|
|
8367
|
+
confirmed: tx.status.confirmed,
|
|
8368
|
+
block_time: tx.status.block_time
|
|
8369
|
+
},
|
|
8370
|
+
isUnrolled: true,
|
|
8371
|
+
virtualStatus: {
|
|
8372
|
+
state: spentStatus?.spent ? "spent" : "settled",
|
|
8373
|
+
commitmentTxIds: spentStatus?.spent ? [spentStatus.txid] : void 0
|
|
8374
|
+
},
|
|
8375
|
+
createdAt: tx.status.confirmed ? new Date(tx.status.block_time * 1e3) : /* @__PURE__ */ new Date(0),
|
|
8376
|
+
script: scriptHex
|
|
8377
|
+
});
|
|
8183
8378
|
}
|
|
8184
|
-
utxos.push({
|
|
8185
|
-
txid: tx.txid,
|
|
8186
|
-
vout: i,
|
|
8187
|
-
value: Number(vout.value),
|
|
8188
|
-
status: {
|
|
8189
|
-
confirmed: tx.status.confirmed,
|
|
8190
|
-
block_time: tx.status.block_time
|
|
8191
|
-
},
|
|
8192
|
-
isUnrolled: true,
|
|
8193
|
-
virtualStatus: {
|
|
8194
|
-
state: spentStatus?.spent ? "spent" : "settled",
|
|
8195
|
-
commitmentTxIds: spentStatus?.spent ? [spentStatus.txid] : void 0
|
|
8196
|
-
},
|
|
8197
|
-
createdAt: tx.status.confirmed ? new Date(tx.status.block_time * 1e3) : /* @__PURE__ */ new Date(0),
|
|
8198
|
-
script: hex.encode(this.boardingTapscript.pkScript)
|
|
8199
|
-
});
|
|
8200
8379
|
}
|
|
8201
8380
|
}
|
|
8202
8381
|
}
|
|
@@ -8226,48 +8405,130 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
8226
8405
|
};
|
|
8227
8406
|
}
|
|
8228
8407
|
/**
|
|
8229
|
-
*
|
|
8408
|
+
* The set of boarding tapscripts whose on-chain UTXOs belong to this
|
|
8409
|
+
* wallet — the current display tapscript plus every historical boarding
|
|
8410
|
+
* address it has used. Under per-derivation rotation (plan §6-II) a wallet
|
|
8411
|
+
* can hold unspent boarding UTXOs at several addresses at once, so fund
|
|
8412
|
+
* discovery / spending must enumerate them all, not just the current one
|
|
8413
|
+
* (plan §6-III.1). Deduplicated by scriptPubKey.
|
|
8414
|
+
*
|
|
8415
|
+
* Always includes the index-0 baseline (identity x-only key), which covers
|
|
8416
|
+
* the degenerate equal-delay case where the index-0 boarding row is
|
|
8417
|
+
* coalesced onto a `default` row and so isn't a `boarding`-typed contract.
|
|
8230
8418
|
*/
|
|
8231
|
-
async
|
|
8232
|
-
const
|
|
8233
|
-
const
|
|
8234
|
-
const
|
|
8235
|
-
|
|
8419
|
+
async getBoardingTapscripts() {
|
|
8420
|
+
const byScript = /* @__PURE__ */ new Map();
|
|
8421
|
+
const add = (s) => byScript.set(hex.encode(s.pkScript), s);
|
|
8422
|
+
const boardingCsv = this.boardingTapscript.options.csvTimelock ?? DefaultVtxo.Script.DEFAULT_TIMELOCK;
|
|
8423
|
+
add(
|
|
8424
|
+
new DefaultVtxo.Script({
|
|
8425
|
+
pubKey: await this.identity.xOnlyPublicKey(),
|
|
8426
|
+
serverPubKey: this.boardingTapscript.options.serverPubKey,
|
|
8427
|
+
csvTimelock: boardingCsv
|
|
8428
|
+
})
|
|
8429
|
+
);
|
|
8430
|
+
add(this.boardingTapscript);
|
|
8431
|
+
const serverPubKeyHex = hex.encode(this.boardingTapscript.options.serverPubKey);
|
|
8432
|
+
const boardingContracts = await this.contractRepository.getContracts({
|
|
8433
|
+
type: ["boarding"]
|
|
8236
8434
|
});
|
|
8237
|
-
|
|
8238
|
-
|
|
8435
|
+
for (const c of boardingContracts) {
|
|
8436
|
+
if (c.params.serverPubKey !== serverPubKeyHex) continue;
|
|
8437
|
+
try {
|
|
8438
|
+
add(BoardingContractHandler.createScript(c.params));
|
|
8439
|
+
} catch (e) {
|
|
8440
|
+
console.warn("Skipping malformed boarding contract", c.script, e);
|
|
8441
|
+
}
|
|
8442
|
+
}
|
|
8443
|
+
return [...byScript.values()];
|
|
8444
|
+
}
|
|
8445
|
+
/**
|
|
8446
|
+
* Fetch and cache onchain inputs (UTXOs) received at the wallet's boarding
|
|
8447
|
+
* addresses — the current address plus any historical rotated boarding
|
|
8448
|
+
* addresses that still hold unspent UTXOs (plan §6-III.1). Each UTXO is
|
|
8449
|
+
* annotated with the tapscript of the address it actually sits on, so the
|
|
8450
|
+
* spending path forfeits / exits it with the correct per-index leaves.
|
|
8451
|
+
*/
|
|
8452
|
+
async getBoardingUtxos() {
|
|
8453
|
+
const tapscripts = await this.getBoardingTapscripts();
|
|
8454
|
+
const all = [];
|
|
8455
|
+
for (const tapscript of tapscripts) {
|
|
8456
|
+
const address = tapscript.onchainAddress(this.network);
|
|
8457
|
+
const coins = await this.onchainProvider.getCoins(address);
|
|
8458
|
+
const utxos = coins.map((utxo) => extendCoinWithTapscript(tapscript, utxo));
|
|
8459
|
+
await this.walletRepository.saveUtxos(address, utxos);
|
|
8460
|
+
all.push(...utxos);
|
|
8461
|
+
}
|
|
8462
|
+
return all;
|
|
8239
8463
|
}
|
|
8240
8464
|
/**
|
|
8241
8465
|
* Subscribe to onchain and offchain notifications for newly received funds.
|
|
8242
8466
|
*
|
|
8467
|
+
* The onchain watcher tracks the full boarding-address set (current +
|
|
8468
|
+
* historical rotated). When boarding rotates *after* subscribing — e.g.
|
|
8469
|
+
* rotate-on-board allocates a fresh address via
|
|
8470
|
+
* {@link getNewBoardingAddress} — the watcher automatically re-subscribes
|
|
8471
|
+
* to widen its set, so a deposit to the new address fires a notification
|
|
8472
|
+
* within the same session (no watcher re-init required). The re-subscribe
|
|
8473
|
+
* is driven by {@link onBoardingRotation}; static / `auto` / readonly
|
|
8474
|
+
* wallets never rotate boarding, so it never fires for them.
|
|
8475
|
+
*
|
|
8243
8476
|
* @param eventCallback - Callback invoked when matching funds are detected
|
|
8244
8477
|
* @returns A function that stops the subscriptions
|
|
8245
8478
|
*/
|
|
8246
8479
|
async notifyIncomingFunds(eventCallback) {
|
|
8247
8480
|
const arkAddress = await this.getAddress();
|
|
8248
|
-
const boardingAddress = await this.getBoardingAddress();
|
|
8249
8481
|
let onchainStopFunc;
|
|
8250
8482
|
let indexerStopFunc;
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
(
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8483
|
+
let boardingRotationStopFunc;
|
|
8484
|
+
let stopped = false;
|
|
8485
|
+
let onchainChain = Promise.resolve();
|
|
8486
|
+
const subscribeOnchain = () => {
|
|
8487
|
+
onchainChain = onchainChain.then(async () => {
|
|
8488
|
+
if (stopped || !this.onchainProvider) return;
|
|
8489
|
+
const boardingAddresses = await this.getBoardingAddresses();
|
|
8490
|
+
if (boardingAddresses.length === 0) return;
|
|
8491
|
+
const boardingAddressSet = new Set(boardingAddresses);
|
|
8492
|
+
const previousStop = onchainStopFunc;
|
|
8493
|
+
const stop = await this.onchainProvider.watchAddresses(
|
|
8494
|
+
boardingAddresses,
|
|
8495
|
+
(txs) => {
|
|
8496
|
+
const coins = txs.flatMap((tx) => {
|
|
8497
|
+
const { txid, status } = tx;
|
|
8498
|
+
const matched = [];
|
|
8499
|
+
tx.vout.forEach((v, vout) => {
|
|
8500
|
+
if (boardingAddressSet.has(v.scriptpubkey_address)) {
|
|
8501
|
+
matched.push({
|
|
8502
|
+
txid,
|
|
8503
|
+
vout,
|
|
8504
|
+
value: Number(v.value),
|
|
8505
|
+
status
|
|
8506
|
+
});
|
|
8507
|
+
}
|
|
8508
|
+
});
|
|
8509
|
+
return matched;
|
|
8510
|
+
});
|
|
8511
|
+
eventCallback({
|
|
8512
|
+
type: "utxo",
|
|
8513
|
+
coins
|
|
8514
|
+
});
|
|
8515
|
+
}
|
|
8516
|
+
);
|
|
8517
|
+
if (stopped) {
|
|
8518
|
+
stop();
|
|
8519
|
+
return;
|
|
8268
8520
|
}
|
|
8269
|
-
|
|
8270
|
-
|
|
8521
|
+
onchainStopFunc = stop;
|
|
8522
|
+
previousStop?.();
|
|
8523
|
+
}).catch((e) => {
|
|
8524
|
+
console.warn("Failed to (re)subscribe boarding-funds watcher", e);
|
|
8525
|
+
});
|
|
8526
|
+
return onchainChain;
|
|
8527
|
+
};
|
|
8528
|
+
boardingRotationStopFunc = this.onBoardingRotation(() => {
|
|
8529
|
+
void subscribeOnchain();
|
|
8530
|
+
});
|
|
8531
|
+
await subscribeOnchain();
|
|
8271
8532
|
if (this.indexerProvider && arkAddress) {
|
|
8272
8533
|
const cm = await this.getContractManager();
|
|
8273
8534
|
let annotationQueue = Promise.resolve();
|
|
@@ -8296,7 +8557,10 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
8296
8557
|
});
|
|
8297
8558
|
}
|
|
8298
8559
|
const stopFunc = () => {
|
|
8560
|
+
stopped = true;
|
|
8561
|
+
boardingRotationStopFunc?.();
|
|
8299
8562
|
onchainStopFunc?.();
|
|
8563
|
+
onchainStopFunc = void 0;
|
|
8300
8564
|
indexerStopFunc?.();
|
|
8301
8565
|
};
|
|
8302
8566
|
return stopFunc;
|
|
@@ -8443,17 +8707,21 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
8443
8707
|
});
|
|
8444
8708
|
}
|
|
8445
8709
|
}
|
|
8446
|
-
const boardingScriptHex = hex.encode(this.boardingTapscript.pkScript);
|
|
8447
8710
|
const boardingCsvTimelock = this.boardingTapscript.options.csvTimelock ?? DefaultVtxo.Script.DEFAULT_TIMELOCK;
|
|
8711
|
+
const baselineBoarding = new DefaultVtxo.Script({
|
|
8712
|
+
pubKey: baselinePubkey,
|
|
8713
|
+
serverPubKey: this.boardingTapscript.options.serverPubKey,
|
|
8714
|
+
csvTimelock: boardingCsvTimelock
|
|
8715
|
+
});
|
|
8448
8716
|
await ensureWalletContract(manager, {
|
|
8449
8717
|
type: "boarding",
|
|
8450
8718
|
params: {
|
|
8451
|
-
pubKey: hex.encode(
|
|
8452
|
-
serverPubKey: hex.encode(
|
|
8719
|
+
pubKey: hex.encode(baselineBoarding.options.pubKey),
|
|
8720
|
+
serverPubKey: hex.encode(baselineBoarding.options.serverPubKey),
|
|
8453
8721
|
csvTimelock: timelockToSequence(boardingCsvTimelock).toString()
|
|
8454
8722
|
},
|
|
8455
|
-
script:
|
|
8456
|
-
address:
|
|
8723
|
+
script: hex.encode(baselineBoarding.pkScript),
|
|
8724
|
+
address: baselineBoarding.address(this.network.hrp, this.arkServerPublicKey).encode(),
|
|
8457
8725
|
state: "active"
|
|
8458
8726
|
});
|
|
8459
8727
|
return manager;
|
|
@@ -8553,6 +8821,72 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
8553
8821
|
setOffchainTapscriptForRotation(tapscript) {
|
|
8554
8822
|
this._offchainTapscript = tapscript;
|
|
8555
8823
|
}
|
|
8824
|
+
/**
|
|
8825
|
+
* @internal Sole write path for `boardingTapscript` after construction.
|
|
8826
|
+
* Called by {@link Wallet.getNewBoardingAddress} once the rotated
|
|
8827
|
+
* boarding contract has been persisted. External code must treat
|
|
8828
|
+
* `boardingTapscript` as read-only.
|
|
8829
|
+
*/
|
|
8830
|
+
setBoardingTapscriptForRotation(tapscript) {
|
|
8831
|
+
this._boardingTapscript = tapscript;
|
|
8832
|
+
this.notifyBoardingRotation();
|
|
8833
|
+
}
|
|
8834
|
+
/**
|
|
8835
|
+
* Allocate and return a *fresh* on-chain boarding address, rotating the
|
|
8836
|
+
* wallet's current boarding tapscript to a new HD index.
|
|
8837
|
+
*
|
|
8838
|
+
* This is the explicit boarding allocator — the analogue of dotnet's
|
|
8839
|
+
* `GetNextContract(NextContractPurpose.Boarding)`. Unlike
|
|
8840
|
+
* {@link getBoardingAddress} (a stable read of the current display
|
|
8841
|
+
* address that never burns an index), each call here:
|
|
8842
|
+
*
|
|
8843
|
+
* - allocates the next index from the shared HD stream (so boarding and
|
|
8844
|
+
* L2 receive interleave on one monotonic index);
|
|
8845
|
+
* - builds the boarding tapscript at that index with the boarding-exit
|
|
8846
|
+
* CSV;
|
|
8847
|
+
* - persists an `active` `boarding` contract tagged
|
|
8848
|
+
* {@link WALLET_RECEIVE_SOURCE} (with its `signingDescriptor`) so the
|
|
8849
|
+
* ContractWatcher monitors it, boot can restore it as the current
|
|
8850
|
+
* boarding address, and descriptor-aware signing can recover the
|
|
8851
|
+
* per-index key;
|
|
8852
|
+
* - swaps the wallet's current `boardingTapscript`.
|
|
8853
|
+
*
|
|
8854
|
+
* Gated by `walletMode`: a static / `auto` wallet has no descriptor
|
|
8855
|
+
* provider and keeps a single index-0 boarding address for its lifetime,
|
|
8856
|
+
* so this returns the existing {@link getBoardingAddress} unchanged
|
|
8857
|
+
* (no rotation, no index burned).
|
|
8858
|
+
*/
|
|
8859
|
+
async getNewBoardingAddress() {
|
|
8860
|
+
const provider = this._descriptorProvider;
|
|
8861
|
+
if (!provider) {
|
|
8862
|
+
return this.getBoardingAddress();
|
|
8863
|
+
}
|
|
8864
|
+
const descriptor = await provider.getNextSigningDescriptor();
|
|
8865
|
+
const pubKey = deriveDescriptorLeafPubKey(descriptor);
|
|
8866
|
+
const newBoarding = new DefaultVtxo.Script({
|
|
8867
|
+
...this._boardingTapscript.options,
|
|
8868
|
+
pubKey
|
|
8869
|
+
});
|
|
8870
|
+
const csvTimelock = newBoarding.options.csvTimelock ?? DefaultVtxo.Script.DEFAULT_TIMELOCK;
|
|
8871
|
+
const manager = await this.getContractManager();
|
|
8872
|
+
await manager.createContract({
|
|
8873
|
+
type: "boarding",
|
|
8874
|
+
params: {
|
|
8875
|
+
pubKey: hex.encode(pubKey),
|
|
8876
|
+
serverPubKey: hex.encode(newBoarding.options.serverPubKey),
|
|
8877
|
+
csvTimelock: timelockToSequence(csvTimelock).toString()
|
|
8878
|
+
},
|
|
8879
|
+
script: hex.encode(newBoarding.pkScript),
|
|
8880
|
+
address: newBoarding.address(this.network.hrp, this.arkServerPublicKey).encode(),
|
|
8881
|
+
state: "active",
|
|
8882
|
+
metadata: {
|
|
8883
|
+
source: WALLET_RECEIVE_SOURCE,
|
|
8884
|
+
signingDescriptor: descriptor
|
|
8885
|
+
}
|
|
8886
|
+
});
|
|
8887
|
+
this.setBoardingTapscriptForRotation(newBoarding);
|
|
8888
|
+
return newBoarding.onchainAddress(this.network);
|
|
8889
|
+
}
|
|
8556
8890
|
/**
|
|
8557
8891
|
* Async mutex that serializes all operations submitting VTXOs to the Arkade
|
|
8558
8892
|
* server (`settle`, `send`, `sendBitcoin`). This prevents VtxoManager's
|
|
@@ -8637,12 +8971,25 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
8637
8971
|
const staticDescriptor = hd ? void 0 : `tr(${hex.encode(await this.identity.xOnlyPublicKey())})`;
|
|
8638
8972
|
const materialize = (index) => hd ? provider.materializeDescriptorAt(index) : staticDescriptor;
|
|
8639
8973
|
const delegatePubKey = this.offchainTapscript instanceof DelegateVtxo.Script ? this.offchainTapscript.options.delegatePubKey : void 0;
|
|
8974
|
+
const arkInfo = await this.arkProvider.getInfo();
|
|
8975
|
+
const currentSignerPubKey = toXOnlyPubKey(hex.decode(arkInfo.signerPubkey));
|
|
8976
|
+
const deprecatedSignerPubKeys = arkInfo.deprecatedSigners.map(
|
|
8977
|
+
(s) => toXOnlyPubKey(hex.decode(s.pubkey))
|
|
8978
|
+
);
|
|
8640
8979
|
const deps = {
|
|
8641
8980
|
indexerProvider: this.indexerProvider,
|
|
8642
8981
|
onchainProvider: this.onchainProvider,
|
|
8643
8982
|
network: { hrp: this.network.hrp },
|
|
8644
|
-
|
|
8983
|
+
// Full network for the boarding on-chain (P2TR) probe — the
|
|
8984
|
+
// `{ hrp }` shape above lacks the `bech32` data
|
|
8985
|
+
// `VtxoScript.onchainAddress` needs (plan §6-I.1).
|
|
8986
|
+
onchainNetwork: this.network,
|
|
8987
|
+
serverPubKey: currentSignerPubKey,
|
|
8988
|
+
deprecatedSignerPubKeys,
|
|
8645
8989
|
csvTimelocks: this.walletContractTimelocks,
|
|
8990
|
+
// Boarding-exit CSV so the boarding handler can build its
|
|
8991
|
+
// candidate script (distinct from the unilateral-exit matrix).
|
|
8992
|
+
boardingTimelock: this.boardingTapscript.options.csvTimelock ?? DefaultVtxo.Script.DEFAULT_TIMELOCK,
|
|
8646
8993
|
delegatePubKey
|
|
8647
8994
|
};
|
|
8648
8995
|
const result = await manager.scanContracts({
|
|
@@ -8774,6 +9121,16 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
8774
9121
|
boot?.rotator,
|
|
8775
9122
|
boot?.provider
|
|
8776
9123
|
);
|
|
9124
|
+
if (boot?.provider) {
|
|
9125
|
+
const resolvedBoarding = await resolveBoardingBootTapscript(
|
|
9126
|
+
setup.contractRepository,
|
|
9127
|
+
setup.serverPubKey,
|
|
9128
|
+
setup.boardingTapscript
|
|
9129
|
+
);
|
|
9130
|
+
if (resolvedBoarding !== setup.boardingTapscript) {
|
|
9131
|
+
wallet.setBoardingTapscriptForRotation(resolvedBoarding);
|
|
9132
|
+
}
|
|
9133
|
+
}
|
|
8777
9134
|
await wallet.getVtxoManager();
|
|
8778
9135
|
return wallet;
|
|
8779
9136
|
}
|
|
@@ -8941,7 +9298,10 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
8941
9298
|
}
|
|
8942
9299
|
const vtxos = await this.getVtxos({ withRecoverable: true });
|
|
8943
9300
|
const filteredVtxos = [];
|
|
8944
|
-
for (const vtxo of vtxos) {
|
|
9301
|
+
for (const vtxo of byValueDescending(vtxos)) {
|
|
9302
|
+
if (filteredVtxos.length >= MAX_VTXOS_PER_SETTLEMENT) {
|
|
9303
|
+
break;
|
|
9304
|
+
}
|
|
8945
9305
|
const inputFee = estimator.evalOffchainInput({
|
|
8946
9306
|
amount: BigInt(vtxo.value),
|
|
8947
9307
|
type: vtxo.virtualStatus.state === "swept" ? "recoverable" : "vtxo",
|
|
@@ -9074,6 +9434,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
9074
9434
|
eventCallback: eventCallback ? (event) => Promise.resolve(eventCallback(event)) : void 0
|
|
9075
9435
|
});
|
|
9076
9436
|
await this.updateDbAfterSettle(params.inputs, commitmentTxid);
|
|
9437
|
+
await this.maybeRotateBoardingAfterBoard(params.inputs);
|
|
9077
9438
|
return commitmentTxid;
|
|
9078
9439
|
} catch (error) {
|
|
9079
9440
|
const inputIds = params.inputs.map((i) => `${i.txid}:${i.vout}`).join(",");
|
|
@@ -9091,6 +9452,41 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
9091
9452
|
});
|
|
9092
9453
|
}
|
|
9093
9454
|
}
|
|
9455
|
+
/**
|
|
9456
|
+
* Rotate the boarding address after a board (rotate-on-board trigger).
|
|
9457
|
+
*
|
|
9458
|
+
* Mirrors {@link WalletReceiveRotator}'s L2 rotation, but driven by a
|
|
9459
|
+
* board instead of a `vtxo_received` event: when a settle consumes at
|
|
9460
|
+
* least one boarding (on-chain) UTXO, the current boarding address has
|
|
9461
|
+
* served its purpose, so we allocate a fresh one via
|
|
9462
|
+
* {@link getNewBoardingAddress}. A settle that consumed only VTXOs (a
|
|
9463
|
+
* renewal / offboard) is not a board and leaves the boarding address
|
|
9464
|
+
* untouched.
|
|
9465
|
+
*
|
|
9466
|
+
* Boarding inputs are the non-VTXO coins (no `virtualStatus`), the same
|
|
9467
|
+
* discriminator {@link handleSettlementFinalizationEvent} uses; the
|
|
9468
|
+
* `typeof` guard skips arknote string inputs before the `in` test.
|
|
9469
|
+
*
|
|
9470
|
+
* No-ops for static / `auto` wallets (no descriptor provider — boarding
|
|
9471
|
+
* stays on its fixed index-0 address). Best-effort and non-fatal: the
|
|
9472
|
+
* settle has already committed and its txid must be returned, so a
|
|
9473
|
+
* rotation failure is logged and swallowed rather than thrown. Funds at
|
|
9474
|
+
* the retired boarding address remain discoverable — the old `boarding`
|
|
9475
|
+
* contract stays active and {@link getBoardingUtxos} fans out over the
|
|
9476
|
+
* full historical boarding set.
|
|
9477
|
+
*/
|
|
9478
|
+
async maybeRotateBoardingAfterBoard(inputs) {
|
|
9479
|
+
if (!this._descriptorProvider) return;
|
|
9480
|
+
const consumedBoarding = inputs.some(
|
|
9481
|
+
(input) => typeof input !== "string" && !("virtualStatus" in input)
|
|
9482
|
+
);
|
|
9483
|
+
if (!consumedBoarding) return;
|
|
9484
|
+
try {
|
|
9485
|
+
await this.getNewBoardingAddress();
|
|
9486
|
+
} catch (e) {
|
|
9487
|
+
console.warn("Failed to rotate boarding address after board", e);
|
|
9488
|
+
}
|
|
9489
|
+
}
|
|
9094
9490
|
async handleSettlementFinalizationEvent(event, inputs, forfeitOutputScript, connectorsGraph) {
|
|
9095
9491
|
const signedForfeits = [];
|
|
9096
9492
|
const isVtxo = (input) => "virtualStatus" in input;
|
|
@@ -9301,6 +9697,19 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
9301
9697
|
}
|
|
9302
9698
|
return jobs;
|
|
9303
9699
|
}
|
|
9700
|
+
/**
|
|
9701
|
+
* @internal Sign an on-chain boarding exit / sweep transaction, routing
|
|
9702
|
+
* each input to the correct key by its `witnessUtxo.script`: the identity
|
|
9703
|
+
* for index-0 / static boarding, the per-index descriptor for a rotated
|
|
9704
|
+
* boarding UTXO (plan §6-III.3). Used by
|
|
9705
|
+
* {@link VtxoManager.sweepExpiredBoardingUtxos}; without it, the
|
|
9706
|
+
* unilateral exit of a rotated boarding UTXO would be signed with the
|
|
9707
|
+
* wrong (index-0) key and rejected.
|
|
9708
|
+
*/
|
|
9709
|
+
async signOnchainBoardingTx(tx) {
|
|
9710
|
+
const signed = await this._signerRouter.sign(tx, this.inputSigningJobsFromWitnessUtxos(tx));
|
|
9711
|
+
return signed;
|
|
9712
|
+
}
|
|
9304
9713
|
async safeRegisterIntent(intent, inputs) {
|
|
9305
9714
|
try {
|
|
9306
9715
|
return await this.arkProvider.registerIntent(intent);
|
|
@@ -9867,10 +10276,9 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
9867
10276
|
// mark virtual outputs as spent/settled, remove boarding inputs
|
|
9868
10277
|
async updateDbAfterSettle(inputs, commitmentTxid) {
|
|
9869
10278
|
try {
|
|
9870
|
-
const boardingAddress = await this.getBoardingAddress();
|
|
9871
10279
|
const spentVtxos = [];
|
|
9872
10280
|
const inputArkTxIds = /* @__PURE__ */ new Set();
|
|
9873
|
-
const
|
|
10281
|
+
const boardingRemovalsByAddress = /* @__PURE__ */ new Map();
|
|
9874
10282
|
const isVtxo = (input) => "virtualStatus" in input;
|
|
9875
10283
|
const vtxoInputs = inputs.filter(isVtxo);
|
|
9876
10284
|
const cm = await this.getContractManager();
|
|
@@ -9892,7 +10300,20 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
9892
10300
|
isSpent: true
|
|
9893
10301
|
});
|
|
9894
10302
|
} else {
|
|
9895
|
-
|
|
10303
|
+
let sourceAddress;
|
|
10304
|
+
try {
|
|
10305
|
+
sourceAddress = VtxoScript.decode(input.tapTree).onchainAddress(
|
|
10306
|
+
this.network
|
|
10307
|
+
);
|
|
10308
|
+
} catch {
|
|
10309
|
+
sourceAddress = this.boardingTapscript.onchainAddress(this.network);
|
|
10310
|
+
}
|
|
10311
|
+
let set = boardingRemovalsByAddress.get(sourceAddress);
|
|
10312
|
+
if (!set) {
|
|
10313
|
+
set = /* @__PURE__ */ new Set();
|
|
10314
|
+
boardingRemovalsByAddress.set(sourceAddress, set);
|
|
10315
|
+
}
|
|
10316
|
+
set.add(`${input.txid}:${input.vout}`);
|
|
9896
10317
|
}
|
|
9897
10318
|
}
|
|
9898
10319
|
if (spentVtxos.length > 0) {
|
|
@@ -9924,14 +10345,12 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
9924
10345
|
);
|
|
9925
10346
|
}
|
|
9926
10347
|
}
|
|
9927
|
-
|
|
9928
|
-
const currentUtxos = await this.walletRepository.getUtxos(
|
|
9929
|
-
const filtered = currentUtxos.filter(
|
|
9930
|
-
|
|
9931
|
-
);
|
|
9932
|
-
await this.walletRepository.deleteUtxos(boardingAddress);
|
|
10348
|
+
for (const [address, toRemove] of boardingRemovalsByAddress) {
|
|
10349
|
+
const currentUtxos = await this.walletRepository.getUtxos(address);
|
|
10350
|
+
const filtered = currentUtxos.filter((u) => !toRemove.has(`${u.txid}:${u.vout}`));
|
|
10351
|
+
await this.walletRepository.deleteUtxos(address);
|
|
9933
10352
|
if (filtered.length > 0) {
|
|
9934
|
-
await this.walletRepository.saveUtxos(
|
|
10353
|
+
await this.walletRepository.saveUtxos(address, filtered);
|
|
9935
10354
|
}
|
|
9936
10355
|
}
|
|
9937
10356
|
} catch (e) {
|
|
@@ -11331,9 +11750,7 @@ var WalletMessageHandler = class {
|
|
|
11331
11750
|
);
|
|
11332
11751
|
}
|
|
11333
11752
|
if (funds.type === "utxo") {
|
|
11334
|
-
const utxos =
|
|
11335
|
-
const boardingAddress = await this.readonlyWallet.getBoardingAddress();
|
|
11336
|
-
await this.walletRepository?.saveUtxos(boardingAddress, utxos);
|
|
11753
|
+
const utxos = await this.readonlyWallet.getBoardingUtxos();
|
|
11337
11754
|
this.scheduleForNextTick(
|
|
11338
11755
|
() => this.tagged({
|
|
11339
11756
|
type: "UTXO_UPDATE",
|
|
@@ -11362,13 +11779,16 @@ var WalletMessageHandler = class {
|
|
|
11362
11779
|
return;
|
|
11363
11780
|
}
|
|
11364
11781
|
const vtxos = await this.getVtxosFromRepo();
|
|
11365
|
-
const
|
|
11366
|
-
const
|
|
11367
|
-
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
|
|
11782
|
+
const boardingAddresses = await this.readonlyWallet.getBoardingAddresses();
|
|
11783
|
+
const fresh = await this.readonlyWallet.getBoardingUtxos();
|
|
11784
|
+
const freshKeys = new Set(fresh.map((u) => `${u.txid}:${u.vout}`));
|
|
11785
|
+
for (const addr of boardingAddresses) {
|
|
11786
|
+
const cached = await this.walletRepository.getUtxos(addr);
|
|
11787
|
+
const kept = cached.filter((u) => freshKeys.has(`${u.txid}:${u.vout}`));
|
|
11788
|
+
if (kept.length === cached.length) continue;
|
|
11789
|
+
await this.walletRepository.deleteUtxos(addr);
|
|
11790
|
+
if (kept.length > 0) await this.walletRepository.saveUtxos(addr, kept);
|
|
11791
|
+
}
|
|
11372
11792
|
const address = await this.readonlyWallet.getAddress();
|
|
11373
11793
|
const txs = await this.buildTransactionHistoryFromCache(vtxos);
|
|
11374
11794
|
if (txs) await this.walletRepository.saveTransactions(address, txs);
|
|
@@ -14159,5 +14579,5 @@ function isArkContract(str) {
|
|
|
14159
14579
|
}
|
|
14160
14580
|
|
|
14161
14581
|
export { ArkNote, AssetManager, BIP322, Batch, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EsploraProvider, Estimator, HDDescriptorProvider, InMemoryContractRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBWalletRepository, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MessageBus, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, Unroll, VtxoManager, Wallet2 as Wallet, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, buildForfeitTx, buildOffchainTx, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, decodeArkContract, deserializeAssets, deserializeUtxo, deserializeVtxo, encodeArkContract, extendVirtualCoinForContract, getMigrationStatus, getRandomId, hasBoardingTxExpired, isArkContract, isBatchSignable, isDiscoverable, isExpired, isRecoverable, isSpendable, isSubdust, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, migrateWalletRepository, openDatabase, requiresMigration, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, serializeAssets, serializeUtxo, serializeVtxo, setupServiceWorker, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript };
|
|
14162
|
-
//# sourceMappingURL=chunk-
|
|
14163
|
-
//# sourceMappingURL=chunk-
|
|
14582
|
+
//# sourceMappingURL=chunk-HFXEUW55.js.map
|
|
14583
|
+
//# sourceMappingURL=chunk-HFXEUW55.js.map
|