@arkade-os/sdk 0.4.17 → 0.4.19
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 +16 -6
- package/dist/cjs/contracts/arkcontract.js +0 -2
- package/dist/cjs/contracts/contractManager.js +111 -215
- package/dist/cjs/contracts/contractWatcher.js +86 -115
- package/dist/cjs/providers/ark.js +36 -33
- package/dist/cjs/repositories/indexedDB/manager.js +6 -3
- package/dist/cjs/repositories/indexedDB/schema.js +47 -2
- package/dist/cjs/repositories/indexedDB/walletRepository.js +21 -2
- package/dist/cjs/repositories/realm/contractRepository.js +0 -4
- package/dist/cjs/repositories/realm/index.js +3 -1
- package/dist/cjs/repositories/realm/schemas.js +50 -1
- package/dist/cjs/repositories/realm/walletRepository.js +8 -4
- package/dist/cjs/repositories/scriptFromAddress.js +16 -0
- package/dist/cjs/repositories/sqlite/contractRepository.js +2 -6
- package/dist/cjs/repositories/sqlite/walletRepository.js +121 -33
- package/dist/cjs/utils/syncCursors.js +48 -56
- package/dist/cjs/wallet/expo/background.js +0 -13
- package/dist/cjs/wallet/expo/wallet.js +1 -6
- package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +16 -7
- package/dist/cjs/wallet/serviceWorker/wallet.js +19 -0
- package/dist/cjs/wallet/utils.js +41 -10
- package/dist/cjs/wallet/vtxo-manager.js +222 -40
- package/dist/cjs/wallet/wallet.js +149 -211
- package/dist/cjs/worker/expo/processors/contractPollProcessor.js +9 -13
- package/dist/cjs/worker/expo/taskRunner.js +2 -11
- package/dist/esm/contracts/arkcontract.js +0 -2
- package/dist/esm/contracts/contractManager.js +113 -217
- package/dist/esm/contracts/contractWatcher.js +86 -115
- package/dist/esm/providers/ark.js +36 -33
- package/dist/esm/repositories/indexedDB/manager.js +6 -3
- package/dist/esm/repositories/indexedDB/schema.js +46 -2
- package/dist/esm/repositories/indexedDB/walletRepository.js +21 -2
- package/dist/esm/repositories/realm/contractRepository.js +0 -4
- package/dist/esm/repositories/realm/index.js +1 -1
- package/dist/esm/repositories/realm/schemas.js +48 -0
- package/dist/esm/repositories/realm/walletRepository.js +8 -4
- package/dist/esm/repositories/scriptFromAddress.js +13 -0
- package/dist/esm/repositories/sqlite/contractRepository.js +2 -6
- package/dist/esm/repositories/sqlite/walletRepository.js +121 -33
- package/dist/esm/utils/syncCursors.js +47 -53
- package/dist/esm/wallet/expo/background.js +0 -13
- package/dist/esm/wallet/expo/wallet.js +2 -7
- package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +17 -8
- package/dist/esm/wallet/serviceWorker/wallet.js +19 -0
- package/dist/esm/wallet/utils.js +41 -9
- package/dist/esm/wallet/vtxo-manager.js +222 -40
- package/dist/esm/wallet/wallet.js +152 -214
- package/dist/esm/worker/expo/processors/contractPollProcessor.js +9 -13
- package/dist/esm/worker/expo/taskRunner.js +3 -12
- package/dist/types/contracts/arkcontract.d.ts +0 -2
- package/dist/types/contracts/contractManager.d.ts +38 -9
- package/dist/types/contracts/contractWatcher.d.ts +22 -21
- package/dist/types/contracts/types.d.ts +0 -7
- package/dist/types/repositories/indexedDB/manager.d.ts +5 -2
- package/dist/types/repositories/indexedDB/schema.d.ts +3 -2
- package/dist/types/repositories/realm/index.d.ts +1 -1
- package/dist/types/repositories/realm/schemas.d.ts +41 -0
- package/dist/types/repositories/scriptFromAddress.d.ts +9 -0
- package/dist/types/repositories/serialization.d.ts +1 -1
- package/dist/types/repositories/sqlite/walletRepository.d.ts +22 -0
- package/dist/types/repositories/walletRepository.d.ts +10 -2
- package/dist/types/utils/syncCursors.d.ts +25 -23
- package/dist/types/wallet/index.d.ts +1 -1
- package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +15 -3
- package/dist/types/wallet/utils.d.ts +20 -4
- package/dist/types/wallet/vtxo-manager.d.ts +29 -6
- package/dist/types/wallet/wallet.d.ts +8 -17
- package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +9 -4
- package/dist/types/worker/expo/taskRunner.d.ts +6 -3
- package/package.json +1 -1
|
@@ -27,6 +27,38 @@ function assertSweepCapable(wallet) {
|
|
|
27
27
|
throw new Error("Boarding UTXO sweep requires a Wallet instance with boardingTapscript, onchainProvider, and network");
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Web Locks name used to serialize boarding-poll work across same-origin
|
|
32
|
+
* browser contexts (tabs, service worker). Static because the goal is to
|
|
33
|
+
* deduplicate polls for the *same* wallet — two distinct wallets on the
|
|
34
|
+
* same origin will take turns, which is acceptable.
|
|
35
|
+
*/
|
|
36
|
+
const BOARDING_POLL_LOCK_NAME = "arkade-boarding-poll";
|
|
37
|
+
/**
|
|
38
|
+
* Run `fn` under an exclusive Web Lock when the runtime provides one
|
|
39
|
+
* (browser main thread, service worker). In environments without
|
|
40
|
+
* `navigator.locks` (Node, React Native) the callback runs immediately
|
|
41
|
+
* with no coordination.
|
|
42
|
+
*
|
|
43
|
+
* Uses `ifAvailable: true`: if another context already holds the lock,
|
|
44
|
+
* skip this cycle entirely rather than queueing — the other context will
|
|
45
|
+
* do the work and the next poll will re-check.
|
|
46
|
+
*/
|
|
47
|
+
async function runWithCrossInstanceLock(name, fn) {
|
|
48
|
+
const locks = typeof globalThis !== "undefined" &&
|
|
49
|
+
typeof globalThis.navigator !== "undefined"
|
|
50
|
+
? globalThis.navigator.locks
|
|
51
|
+
: undefined;
|
|
52
|
+
if (!locks) {
|
|
53
|
+
await fn();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
await locks.request(name, { ifAvailable: true, mode: "exclusive" }, async (lock) => {
|
|
57
|
+
if (lock === null)
|
|
58
|
+
return;
|
|
59
|
+
await fn();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
30
62
|
/** Default renewal threshold in seconds (3 days). */
|
|
31
63
|
export const DEFAULT_THRESHOLD_SECONDS = 3 * 24 * 60 * 60;
|
|
32
64
|
/**
|
|
@@ -186,6 +218,20 @@ export class VtxoManager {
|
|
|
186
218
|
// server emits new VTXOs → vtxo_received → renewVtxos() again → infinite loop.
|
|
187
219
|
this.renewalInProgress = false;
|
|
188
220
|
this.lastRenewalTimestamp = 0;
|
|
221
|
+
// Guards against a retry treadmill on the periodic-settle path: a failing
|
|
222
|
+
// settle would otherwise re-submit identical intents on every 60s poll,
|
|
223
|
+
// producing per-minute DeleteIntent RPCs forever. Mirrors the renewal
|
|
224
|
+
// cooldown but with exponential backoff on consecutive failures, so a
|
|
225
|
+
// persistently broken input eventually drops to the backoff cap instead
|
|
226
|
+
// of hammering the server. Shared across boarding + expiring-VTXO work
|
|
227
|
+
// because they now ride on the same settle intent.
|
|
228
|
+
this.lastPeriodicSettleTimestamp = 0;
|
|
229
|
+
this.consecutivePeriodicSettleFailures = 0;
|
|
230
|
+
// Throttle for the VTXO_ALREADY_SPENT -> refreshVtxos() reconciliation.
|
|
231
|
+
// The server's authoritative view says our local cache is stale, so we
|
|
232
|
+
// trigger a full refresh to advance the global sync cursor. Rate-limit
|
|
233
|
+
// to guard against a buggy indexer cycling us into a refresh storm.
|
|
234
|
+
this.lastVtxoSpentRefreshTimestamp = 0;
|
|
189
235
|
// Normalize: prefer settlementConfig, fall back to renewalConfig, default to enabled
|
|
190
236
|
if (settlementConfig !== undefined) {
|
|
191
237
|
this.settlementConfig = settlementConfig;
|
|
@@ -407,10 +453,15 @@ export class VtxoManager {
|
|
|
407
453
|
},
|
|
408
454
|
],
|
|
409
455
|
}, eventCallback);
|
|
410
|
-
this.lastRenewalTimestamp = Date.now();
|
|
411
456
|
return txid;
|
|
412
457
|
}
|
|
413
458
|
finally {
|
|
459
|
+
// Update cooldown on EVERY attempt (success or failure) so transient
|
|
460
|
+
// settle failures (stream close, connector mismatch, duplicated input)
|
|
461
|
+
// don't allow the next vtxo_received event to re-enter renewal
|
|
462
|
+
// immediately. Without this, a failed settle leaves lastRenewalTimestamp
|
|
463
|
+
// at its previous value and the cooldown check becomes a no-op.
|
|
464
|
+
this.lastRenewalTimestamp = Date.now();
|
|
414
465
|
this.renewalInProgress = false;
|
|
415
466
|
}
|
|
416
467
|
}
|
|
@@ -616,7 +667,6 @@ export class VtxoManager {
|
|
|
616
667
|
return;
|
|
617
668
|
}
|
|
618
669
|
if (e.message.includes("VTXO_ALREADY_REGISTERED") ||
|
|
619
|
-
e.message.includes("VTXO_ALREADY_SPENT") ||
|
|
620
670
|
e.message.includes("duplicated input")) {
|
|
621
671
|
// Virtual output is already being used in a concurrent
|
|
622
672
|
// user-initiated operation. Skip silently — the
|
|
@@ -624,6 +674,14 @@ export class VtxoManager {
|
|
|
624
674
|
// renewal will retry on the next cycle.
|
|
625
675
|
return;
|
|
626
676
|
}
|
|
677
|
+
if (e.message.includes("VTXO_ALREADY_SPENT")) {
|
|
678
|
+
// Our local VTXO cache is stale vs. the
|
|
679
|
+
// server's authoritative view. Trigger a
|
|
680
|
+
// throttled refresh to reconcile, then skip
|
|
681
|
+
// — the next cycle will see fresh data.
|
|
682
|
+
void this.maybeRefreshAfterVtxoSpent();
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
627
685
|
}
|
|
628
686
|
console.error("Error renewing VTXOs:", e);
|
|
629
687
|
});
|
|
@@ -641,6 +699,39 @@ export class VtxoManager {
|
|
|
641
699
|
return undefined;
|
|
642
700
|
}
|
|
643
701
|
}
|
|
702
|
+
/**
|
|
703
|
+
* VTXO_ALREADY_SPENT means the server's authoritative view of VTXO state
|
|
704
|
+
* is ahead of ours — cross-instance race, pre-lock snapshot drift, or an
|
|
705
|
+
* SSE gap left stale data in the local cache. Silent-swallowing guarantees
|
|
706
|
+
* the same error on the next cycle because nothing reconciles the cache,
|
|
707
|
+
* so instead we trigger a full refreshVtxos() to advance the global sync
|
|
708
|
+
* cursor. Throttled to prevent a buggy indexer from causing a refresh
|
|
709
|
+
* storm.
|
|
710
|
+
*/
|
|
711
|
+
maybeRefreshAfterVtxoSpent() {
|
|
712
|
+
if (this.vtxoSpentRefreshPromise) {
|
|
713
|
+
return this.vtxoSpentRefreshPromise;
|
|
714
|
+
}
|
|
715
|
+
const now = Date.now();
|
|
716
|
+
if (now - this.lastVtxoSpentRefreshTimestamp <
|
|
717
|
+
VtxoManager.VTXO_SPENT_REFRESH_COOLDOWN_MS) {
|
|
718
|
+
return Promise.resolve();
|
|
719
|
+
}
|
|
720
|
+
this.lastVtxoSpentRefreshTimestamp = now;
|
|
721
|
+
this.vtxoSpentRefreshPromise = (async () => {
|
|
722
|
+
try {
|
|
723
|
+
const contractManager = await this.wallet.getContractManager();
|
|
724
|
+
await contractManager.refreshVtxos();
|
|
725
|
+
}
|
|
726
|
+
catch (e) {
|
|
727
|
+
console.error("Error refreshing VTXOs after VTXO_ALREADY_SPENT:", e);
|
|
728
|
+
}
|
|
729
|
+
finally {
|
|
730
|
+
this.vtxoSpentRefreshPromise = undefined;
|
|
731
|
+
}
|
|
732
|
+
})();
|
|
733
|
+
return this.vtxoSpentRefreshPromise;
|
|
734
|
+
}
|
|
644
735
|
/** Computes the next poll delay, applying exponential backoff on failures. */
|
|
645
736
|
getNextPollDelay() {
|
|
646
737
|
if (this.settlementConfig === false)
|
|
@@ -688,33 +779,42 @@ export class VtxoManager {
|
|
|
688
779
|
this.pollDone = { promise, resolve: resolve };
|
|
689
780
|
let hadError = false;
|
|
690
781
|
try {
|
|
691
|
-
//
|
|
692
|
-
//
|
|
693
|
-
|
|
694
|
-
//
|
|
695
|
-
//
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
(this.settlementConfig?.boardingUtxoSweep ??
|
|
705
|
-
DEFAULT_SETTLEMENT_CONFIG.boardingUtxoSweep);
|
|
706
|
-
if (sweepEnabled) {
|
|
782
|
+
// Cross-instance guard: in browser / service worker environments,
|
|
783
|
+
// serialize the poll body across tabs and SW contexts so only one
|
|
784
|
+
// of them registers intents per interval. Without this, every tab
|
|
785
|
+
// submits a parallel RegisterIntent for the same boarding input
|
|
786
|
+
// and N-1 of them collide on the server's duplicated-input check,
|
|
787
|
+
// each producing a DeleteIntent RPC. No-op outside the browser.
|
|
788
|
+
await runWithCrossInstanceLock(BOARDING_POLL_LOCK_NAME, async () => {
|
|
789
|
+
// Fetch boarding inputs once for the entire poll cycle so that
|
|
790
|
+
// settle and sweep don't each hit the network independently.
|
|
791
|
+
const boardingUtxos = await this.wallet.getBoardingUtxos();
|
|
792
|
+
// Settle new (unexpired) boarding inputs + any near-expiry
|
|
793
|
+
// VTXOs in a single intent, then sweep expired boarding
|
|
794
|
+
// inputs. Sequential to avoid racing for the same inputs.
|
|
707
795
|
try {
|
|
708
|
-
await this.
|
|
796
|
+
await this.runPeriodicSettle(boardingUtxos);
|
|
709
797
|
}
|
|
710
798
|
catch (e) {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
799
|
+
hadError = true;
|
|
800
|
+
console.error("Error during periodic settle:", e);
|
|
801
|
+
}
|
|
802
|
+
const sweepEnabled = this.settlementConfig !== false &&
|
|
803
|
+
(this.settlementConfig?.boardingUtxoSweep ??
|
|
804
|
+
DEFAULT_SETTLEMENT_CONFIG.boardingUtxoSweep);
|
|
805
|
+
if (sweepEnabled) {
|
|
806
|
+
try {
|
|
807
|
+
await this.sweepExpiredBoardingUtxos(boardingUtxos);
|
|
808
|
+
}
|
|
809
|
+
catch (e) {
|
|
810
|
+
if (!(e instanceof Error) ||
|
|
811
|
+
!e.message.includes("No expired boarding UTXOs")) {
|
|
812
|
+
hadError = true;
|
|
813
|
+
console.error("Error auto-sweeping boarding UTXOs:", e);
|
|
814
|
+
}
|
|
715
815
|
}
|
|
716
816
|
}
|
|
717
|
-
}
|
|
817
|
+
});
|
|
718
818
|
}
|
|
719
819
|
catch (e) {
|
|
720
820
|
hadError = true;
|
|
@@ -734,13 +834,19 @@ export class VtxoManager {
|
|
|
734
834
|
}
|
|
735
835
|
}
|
|
736
836
|
/**
|
|
737
|
-
* Auto-settle new (unexpired) boarding inputs into
|
|
738
|
-
* Skips UTXOs that are already expired
|
|
739
|
-
*
|
|
740
|
-
*
|
|
741
|
-
*
|
|
837
|
+
* Auto-settle new (unexpired) boarding inputs AND near-expiry VTXOs into
|
|
838
|
+
* Arkade in a single intent. Skips boarding UTXOs that are already expired
|
|
839
|
+
* (those are handled by sweep) and those already in-flight (tracked in
|
|
840
|
+
* knownBoardingUtxos). If the event-driven renewal path is currently
|
|
841
|
+
* running, VTXOs are omitted from this cycle to avoid double-spending.
|
|
842
|
+
*
|
|
843
|
+
* Failure bookkeeping: after every settle *attempt*, lastPeriodicSettleTimestamp
|
|
844
|
+
* is armed and consecutive failures are counted so the next attempt is
|
|
845
|
+
* blocked by an exponentially growing cooldown (capped). This stops a
|
|
846
|
+
* persistently failing input from producing identical RegisterIntent +
|
|
847
|
+
* DeleteIntent retries on every 60s poll.
|
|
742
848
|
*/
|
|
743
|
-
async
|
|
849
|
+
async runPeriodicSettle(boardingUtxos) {
|
|
744
850
|
// Exclude expired boarding inputs — those should be swept, not settled.
|
|
745
851
|
// If we can't determine expired status, bail out entirely to avoid
|
|
746
852
|
// accidentally settling expired inputs (which would conflict with sweep).
|
|
@@ -758,22 +864,95 @@ export class VtxoManager {
|
|
|
758
864
|
catch (e) {
|
|
759
865
|
throw e instanceof Error ? e : new Error(String(e));
|
|
760
866
|
}
|
|
761
|
-
const
|
|
867
|
+
const unsettledBoarding = boardingUtxos.filter((u) => u.status.confirmed &&
|
|
868
|
+
!this.knownBoardingUtxos.has(`${u.txid}:${u.vout}`) &&
|
|
762
869
|
!expiredSet.has(`${u.txid}:${u.vout}`));
|
|
763
|
-
|
|
870
|
+
// Collect near-expiry VTXOs unless the event-driven path is mid-renewal.
|
|
871
|
+
// Skipping when renewalInProgress avoids double-submitting the same VTXOs.
|
|
872
|
+
let expiringVtxos = [];
|
|
873
|
+
if (!this.renewalInProgress) {
|
|
874
|
+
try {
|
|
875
|
+
expiringVtxos = await this.getExpiringVtxos();
|
|
876
|
+
}
|
|
877
|
+
catch (e) {
|
|
878
|
+
// Non-fatal: fall back to boarding-only settle.
|
|
879
|
+
console.error("Error fetching expiring VTXOs:", e);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
if (unsettledBoarding.length === 0 && expiringVtxos.length === 0) {
|
|
764
883
|
return;
|
|
884
|
+
}
|
|
885
|
+
// Respect the cooldown armed by the previous attempt. Cooldown grows
|
|
886
|
+
// exponentially with consecutive failures and is capped by
|
|
887
|
+
// PERIODIC_SETTLE_MAX_BACKOFF_MS.
|
|
888
|
+
const cooldownMs = Math.min(VtxoManager.PERIODIC_SETTLE_COOLDOWN_MS *
|
|
889
|
+
Math.pow(2, this.consecutivePeriodicSettleFailures), VtxoManager.PERIODIC_SETTLE_MAX_BACKOFF_MS);
|
|
890
|
+
if (Date.now() - this.lastPeriodicSettleTimestamp < cooldownMs) {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
765
893
|
const dustAmount = getDustAmount(this.wallet);
|
|
766
|
-
const
|
|
894
|
+
const boardingTotal = unsettledBoarding.reduce((sum, u) => sum + BigInt(u.value), 0n);
|
|
895
|
+
const vtxoTotal = expiringVtxos.reduce((sum, v) => sum + BigInt(v.value), 0n);
|
|
896
|
+
const totalAmount = boardingTotal + vtxoTotal;
|
|
767
897
|
if (totalAmount < dustAmount)
|
|
768
898
|
return;
|
|
769
899
|
const arkAddress = await this.wallet.getAddress();
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
900
|
+
const includesVtxos = expiringVtxos.length > 0;
|
|
901
|
+
// Block the event-driven renewal path while this settle is in flight
|
|
902
|
+
// when VTXOs are part of the intent. Mirrors renewVtxos()'s guard so
|
|
903
|
+
// the two paths can't race on the same VTXO inputs.
|
|
904
|
+
if (includesVtxos) {
|
|
905
|
+
this.renewalInProgress = true;
|
|
906
|
+
}
|
|
907
|
+
let success = false;
|
|
908
|
+
let staleCacheSkip = false;
|
|
909
|
+
try {
|
|
910
|
+
try {
|
|
911
|
+
await this.wallet.settle({
|
|
912
|
+
inputs: [...unsettledBoarding, ...expiringVtxos],
|
|
913
|
+
outputs: [{ address: arkAddress, amount: totalAmount }],
|
|
914
|
+
});
|
|
915
|
+
// Mark boarding inputs as known only after successful settle.
|
|
916
|
+
for (const u of unsettledBoarding) {
|
|
917
|
+
this.knownBoardingUtxos.add(`${u.txid}:${u.vout}`);
|
|
918
|
+
}
|
|
919
|
+
success = true;
|
|
920
|
+
}
|
|
921
|
+
catch (e) {
|
|
922
|
+
if (e instanceof Error &&
|
|
923
|
+
e.message.includes("VTXO_ALREADY_SPENT")) {
|
|
924
|
+
// Local VTXO cache is stale vs. the server's
|
|
925
|
+
// authoritative view — not a transient failure.
|
|
926
|
+
// Trigger a throttled refresh and skip this cycle
|
|
927
|
+
// without bumping the failure counter, so the next
|
|
928
|
+
// poll can retry once the cache reconciles.
|
|
929
|
+
staleCacheSkip = true;
|
|
930
|
+
void this.maybeRefreshAfterVtxoSpent();
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
933
|
+
throw e;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
finally {
|
|
938
|
+
this.lastPeriodicSettleTimestamp = Date.now();
|
|
939
|
+
if (includesVtxos) {
|
|
940
|
+
// Match event-path semantics: bump the renewal cooldown
|
|
941
|
+
// whether we succeeded or failed so a failed periodic settle
|
|
942
|
+
// doesn't let the next vtxo_received event re-enter renewal
|
|
943
|
+
// immediately.
|
|
944
|
+
this.lastRenewalTimestamp = Date.now();
|
|
945
|
+
this.renewalInProgress = false;
|
|
946
|
+
}
|
|
947
|
+
if (success) {
|
|
948
|
+
this.consecutivePeriodicSettleFailures = 0;
|
|
949
|
+
}
|
|
950
|
+
else if (!staleCacheSkip) {
|
|
951
|
+
// Don't bump on stale-cache skip: it's not a transient
|
|
952
|
+
// failure, and the next cycle should try immediately
|
|
953
|
+
// after the refresh lands.
|
|
954
|
+
this.consecutivePeriodicSettleFailures++;
|
|
955
|
+
}
|
|
777
956
|
}
|
|
778
957
|
}
|
|
779
958
|
async dispose() {
|
|
@@ -806,3 +985,6 @@ export class VtxoManager {
|
|
|
806
985
|
}
|
|
807
986
|
VtxoManager.MAX_BACKOFF_MS = 5 * 60 * 1000; // 5 minutes
|
|
808
987
|
VtxoManager.RENEWAL_COOLDOWN_MS = 30000; // 30 seconds
|
|
988
|
+
VtxoManager.PERIODIC_SETTLE_COOLDOWN_MS = 30000;
|
|
989
|
+
VtxoManager.PERIODIC_SETTLE_MAX_BACKOFF_MS = 5 * 60 * 1000;
|
|
990
|
+
VtxoManager.VTXO_SPENT_REFRESH_COOLDOWN_MS = 30000;
|