@arkade-os/sdk 0.5.0-rc.4 → 0.5.0-rc.6

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.
Files changed (77) hide show
  1. package/dist/adapters/expo.cjs +5 -5
  2. package/dist/adapters/expo.d.cts +2 -2
  3. package/dist/adapters/expo.d.ts +2 -2
  4. package/dist/adapters/expo.js +3 -3
  5. package/dist/{asyncStorageTaskQueue-CTIwKrt5.d.cts → asyncStorageTaskQueue-BHOhjFiF.d.cts} +2 -2
  6. package/dist/{asyncStorageTaskQueue-BCiOgs5x.d.ts → asyncStorageTaskQueue-BedBOEpT.d.ts} +2 -2
  7. package/dist/{chunk-GLZNJFAT.js → chunk-2P75JIT5.js} +30 -5
  8. package/dist/chunk-2P75JIT5.js.map +1 -0
  9. package/dist/{chunk-5NLJUXXQ.cjs → chunk-3LGOKP52.cjs} +10 -10
  10. package/dist/{chunk-5NLJUXXQ.cjs.map → chunk-3LGOKP52.cjs.map} +1 -1
  11. package/dist/{chunk-ZHLYFRYY.cjs → chunk-4I6KPFCB.cjs} +811 -495
  12. package/dist/chunk-4I6KPFCB.cjs.map +1 -0
  13. package/dist/{chunk-CIBGIZUW.js → chunk-7PW4GV2Q.js} +4 -4
  14. package/dist/{chunk-CIBGIZUW.js.map → chunk-7PW4GV2Q.js.map} +1 -1
  15. package/dist/{chunk-GPJSGLWM.cjs → chunk-BMYEJA3W.cjs} +21 -17
  16. package/dist/chunk-BMYEJA3W.cjs.map +1 -0
  17. package/dist/{chunk-MOWSLXGO.js → chunk-BWHKCHJN.js} +8 -4
  18. package/dist/chunk-BWHKCHJN.js.map +1 -0
  19. package/dist/{chunk-XHASFSDU.js → chunk-IRXEWL2A.js} +37 -11
  20. package/dist/chunk-IRXEWL2A.js.map +1 -0
  21. package/dist/{chunk-PYTAH2NY.js → chunk-ISA3HWFN.js} +388 -73
  22. package/dist/chunk-ISA3HWFN.js.map +1 -0
  23. package/dist/{chunk-4EZOMPAO.js → chunk-QKHXUTXM.js} +3 -3
  24. package/dist/{chunk-4EZOMPAO.js.map → chunk-QKHXUTXM.js.map} +1 -1
  25. package/dist/{chunk-SYRQJS5N.cjs → chunk-TVG3GDEO.cjs} +52 -26
  26. package/dist/chunk-TVG3GDEO.cjs.map +1 -0
  27. package/dist/{chunk-C5Y2R63Z.cjs → chunk-XR23C6TF.cjs} +30 -4
  28. package/dist/chunk-XR23C6TF.cjs.map +1 -0
  29. package/dist/{chunk-F7HJO5GT.cjs → chunk-ZF52B7BV.cjs} +50 -50
  30. package/dist/{chunk-F7HJO5GT.cjs.map → chunk-ZF52B7BV.cjs.map} +1 -1
  31. package/dist/contracts/handlers/index.cjs +9 -9
  32. package/dist/contracts/handlers/index.d.cts +3 -3
  33. package/dist/contracts/handlers/index.d.ts +3 -3
  34. package/dist/contracts/handlers/index.js +2 -2
  35. package/dist/{delegate-CpQIgTs7.d.cts → delegate-Dv8aEvSp.d.cts} +1 -1
  36. package/dist/{delegate-Dv-us16c.d.ts → delegate-Hl92NVgS.d.ts} +1 -1
  37. package/dist/{hdWalletCapable-BC4Fmmht.d.ts → hdWalletCapable-Bqcb-XtC.d.ts} +2 -2
  38. package/dist/{hdWalletCapable-DotEa5rZ.d.cts → hdWalletCapable-QiCK1B9d.d.cts} +2 -2
  39. package/dist/{index-DUkvXxpt.d.ts → index-BwEO0Yb-.d.ts} +2 -2
  40. package/dist/{index-DdJt5fRc.d.cts → index-CeC9FiJt.d.cts} +2 -2
  41. package/dist/{index-Czz9HSqE.d.cts → index-F-X4BWxi.d.cts} +136 -5
  42. package/dist/{index-Czz9HSqE.d.ts → index-F-X4BWxi.d.ts} +136 -5
  43. package/dist/index.cjs +296 -288
  44. package/dist/index.d.cts +83 -12
  45. package/dist/index.d.ts +83 -12
  46. package/dist/index.js +4 -4
  47. package/dist/repositories/realm/index.cjs +20 -20
  48. package/dist/repositories/realm/index.d.cts +2 -2
  49. package/dist/repositories/realm/index.d.ts +2 -2
  50. package/dist/repositories/realm/index.js +5 -5
  51. package/dist/repositories/sqlite/index.cjs +19 -19
  52. package/dist/repositories/sqlite/index.d.cts +2 -2
  53. package/dist/repositories/sqlite/index.d.ts +2 -2
  54. package/dist/repositories/sqlite/index.js +5 -5
  55. package/dist/{taskRunner-DUId1UJ8.d.ts → taskRunner-BdU_d__J.d.ts} +2 -2
  56. package/dist/{taskRunner-7E_Rx8bD.d.cts → taskRunner-CDCz7MTW.d.cts} +2 -2
  57. package/dist/wallet/expo/background.cjs +14 -14
  58. package/dist/wallet/expo/background.d.cts +3 -3
  59. package/dist/wallet/expo/background.d.ts +3 -3
  60. package/dist/wallet/expo/background.js +6 -6
  61. package/dist/wallet/expo/index.cjs +14 -14
  62. package/dist/wallet/expo/index.d.cts +4 -4
  63. package/dist/wallet/expo/index.d.ts +4 -4
  64. package/dist/wallet/expo/index.js +5 -5
  65. package/dist/worker/expo/index.cjs +9 -9
  66. package/dist/worker/expo/index.d.cts +4 -4
  67. package/dist/worker/expo/index.d.ts +4 -4
  68. package/dist/worker/expo/index.js +5 -5
  69. package/package.json +1 -1
  70. package/dist/chunk-C5Y2R63Z.cjs.map +0 -1
  71. package/dist/chunk-GLZNJFAT.js.map +0 -1
  72. package/dist/chunk-GPJSGLWM.cjs.map +0 -1
  73. package/dist/chunk-MOWSLXGO.js.map +0 -1
  74. package/dist/chunk-PYTAH2NY.js.map +0 -1
  75. package/dist/chunk-SYRQJS5N.cjs.map +0 -1
  76. package/dist/chunk-XHASFSDU.js.map +0 -1
  77. package/dist/chunk-ZHLYFRYY.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
- import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, BufferReader, BufferWriter, AssetRef, RestIndexerProvider } from './chunk-XHASFSDU.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, toXOnly, inputName, contractHandlers, isMainnetDescriptor, descriptorIsOurs, deriveDescriptorLeafPubKey, deriveDescriptorLeafCompressedPubKey, parseHDDescriptor, identityDescriptor, DefaultVtxo, speculativeReceiveMetadata, DelegateVtxo, WALLET_RECEIVE_SOURCE, BoardingContractHandler } from './chunk-4EZOMPAO.js';
3
- import { VtxoScript, __export, Transaction, isVirtualCoin, canRecoverOnchain, CSVMultisigTapscript, DEFAULT_NETWORK, resolveEmulatorPubkey, hasTerminalSpend, getNormalizedVtxos, setArkPsbtField, PrevArkTxField, ConditionWitness, craftToSpendTx, ArkAddress, ServerResponseMismatchError, getArkPsbtFields, CosignerPublicKey, OP_RETURN_EMPTY_PKSCRIPT, canSweepOnchain, sequenceToTimelock, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, VtxoTaprootTree, timelockToSequence, normalizeVtxo, assertAllowedSighashTypes, baseFetch, Intent, rateGate, DEFAULT_NETWORK_NAME, formatSighash, getSequence, maybeArkError, isArkError, ArkErrorName, toOffchainInputFeeParams, getNetwork, MultisigTapscript, isEventSourceError, isEventSourceUnavailableError, isRetryableProviderError, getAllNormalizedVtxos, DEFAULT_PAGE_SIZE, RestArkProvider, DEFAULT_ARKADE_SERVER_URL, networkFromArkadeInfo, ArkError, TapTreeCoder, resolveTimeHeight, canSpendOffchain, INFO_FETCH_TIMEOUT_MS, PrevoutTxField, EXPIRY_MIN_PLAUSIBLE_MS, ProviderUnavailableError, networks as networks$1, isPastExpiry, fetchVtxoCreatedAtByTxid } from './chunk-GLZNJFAT.js';
1
+ import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, BufferReader, BufferWriter, AssetRef, RestIndexerProvider } from './chunk-IRXEWL2A.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, toXOnly, inputName, contractHandlers, isMainnetDescriptor, descriptorIsOurs, deriveDescriptorLeafPubKey, deriveDescriptorLeafCompressedPubKey, parseHDDescriptor, identityDescriptor, DefaultVtxo, speculativeReceiveMetadata, DelegateVtxo, WALLET_RECEIVE_SOURCE, BoardingContractHandler } from './chunk-QKHXUTXM.js';
3
+ import { VtxoScript, __export, Transaction, isVirtualCoin, canRecoverOnchain, CSVMultisigTapscript, DEFAULT_NETWORK, resolveEmulatorPubkey, hasTerminalSpend, getNormalizedVtxos, setArkPsbtField, PrevArkTxField, ConditionWitness, craftToSpendTx, ArkAddress, ServerResponseMismatchError, getArkPsbtFields, CosignerPublicKey, OP_RETURN_EMPTY_PKSCRIPT, canSweepOnchain, sequenceToTimelock, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, VtxoTaprootTree, timelockToSequence, normalizeVtxo, assertAllowedSighashTypes, baseFetch, Intent, rateGate, DEFAULT_NETWORK_NAME, formatSighash, getSequence, maybeArkError, isArkError, ArkErrorName, toOffchainInputFeeParams, getNetwork, MultisigTapscript, isEventSourceError, isEventSourceUnavailableError, getAllNormalizedVtxos, isPastExpiry, isRetryableProviderError, DEFAULT_PAGE_SIZE, RestArkProvider, DEFAULT_ARKADE_SERVER_URL, networkFromArkadeInfo, ArkError, TapTreeCoder, resolveTimeHeight, canSpendOffchain, INFO_FETCH_TIMEOUT_MS, PrevoutTxField, fetchVtxoCreatedAtByTxid, EXPIRY_MIN_PLAUSIBLE_MS, ProviderUnavailableError, networks as networks$1 } from './chunk-2P75JIT5.js';
4
4
  import { sha256 as sha256$1, equalBytes, hash160, sha256x2, concatBytes, randomPrivateKeyBytes, pubECDSA, pubSchnorr } from '@scure/btc-signer/utils.js';
5
5
  import { SigHash, Script, OP, OutScript, Address, RawWitness, p2tr, p2wpkh, TaprootControlBlock, DEFAULT_SEQUENCE, Transaction as Transaction$2 } from '@scure/btc-signer';
6
6
  import { hex, base58, base64, bech32m } from '@scure/base';
@@ -3577,6 +3577,26 @@ function claimWithPreimageIdentity(identity, preimage) {
3577
3577
  }
3578
3578
  };
3579
3579
  }
3580
+
3581
+ // src/contracts/types.ts
3582
+ function watchStateOf(contract) {
3583
+ return contract.watch ?? "watched";
3584
+ }
3585
+ function isWatchedContract(contract) {
3586
+ return watchStateOf(contract) !== "retained";
3587
+ }
3588
+ function hasCandidates(handler) {
3589
+ return !!handler && typeof handler.candidatesAt === "function";
3590
+ }
3591
+ function isDiscoverable(handler) {
3592
+ return !!handler && typeof handler.discoverAt === "function";
3593
+ }
3594
+ function isTapscriptDeriving(handler) {
3595
+ return !!handler && typeof handler.deriveTapscripts === "function";
3596
+ }
3597
+ function isContractVtxoEvent(event) {
3598
+ return "contract" in event && event.contract !== void 0;
3599
+ }
3580
3600
  var AmountVariableName = "amount";
3581
3601
  var ExpiryVariableName = "expiry";
3582
3602
  var BirthVariableName = "birth";
@@ -3732,23 +3752,6 @@ function parseProgram(text, env) {
3732
3752
  }
3733
3753
  return { program, text };
3734
3754
  }
3735
-
3736
- // src/contracts/types.ts
3737
- function watchStateOf(contract) {
3738
- return contract.watch ?? "watched";
3739
- }
3740
- function isWatchedContract(contract) {
3741
- return watchStateOf(contract) !== "retained";
3742
- }
3743
- function hasCandidates(handler) {
3744
- return !!handler && typeof handler.candidatesAt === "function";
3745
- }
3746
- function isDiscoverable(handler) {
3747
- return !!handler && typeof handler.discoverAt === "function";
3748
- }
3749
- function isTapscriptDeriving(handler) {
3750
- return !!handler && typeof handler.deriveTapscripts === "function";
3751
- }
3752
3755
  var DUST_AMOUNT = 546;
3753
3756
  var FALLBACK_WALLET_DUST_AMOUNT = 330n;
3754
3757
  function getDustAmount(wallet) {
@@ -3913,9 +3916,18 @@ function gatedContracts(contracts) {
3913
3916
  }
3914
3917
  return gated;
3915
3918
  }
3919
+ function gatedFrom(snapshot) {
3920
+ return gatedContracts(snapshot.map((entry) => entry.contract));
3921
+ }
3922
+ function gatedTypeOf(vtxo, gated) {
3923
+ return vtxo.script === void 0 ? void 0 : gated.get(vtxo.script);
3924
+ }
3925
+ function isGatedVtxo(vtxo, gated) {
3926
+ return gatedTypeOf(vtxo, gated) !== void 0;
3927
+ }
3916
3928
  function gateExclusion(gated) {
3917
3929
  return (vtxo) => {
3918
- const type = vtxo.script === void 0 ? void 0 : gated.get(vtxo.script);
3930
+ const type = gatedTypeOf(vtxo, gated);
3919
3931
  if (type === void 0) return void 0;
3920
3932
  if (!contractHandlers.has(type)) {
3921
3933
  return `at ${vtxo.script} (contract type '${type}') has no handler registered in this build`;
@@ -5282,7 +5294,7 @@ var VtxoManager = class _VtxoManager {
5282
5294
  this.wallet.getAddress()
5283
5295
  ]);
5284
5296
  const stopWatching = contractManager.onContractEvent((event) => {
5285
- if (event.type !== "vtxo_received") {
5297
+ if (event.type !== "vtxo_received" || !isContractVtxoEvent(event)) {
5286
5298
  return;
5287
5299
  }
5288
5300
  const msSinceLastRenewal = Date.now() - this.lastRenewalTimestamp;
@@ -6877,6 +6889,24 @@ var Batch;
6877
6889
  })(Batch || (Batch = {}));
6878
6890
 
6879
6891
  // src/utils/transactionHistory.ts
6892
+ function localCreatedAtByTxid(vtxos) {
6893
+ const createdAt = /* @__PURE__ */ new Map();
6894
+ for (const vtxo of vtxos) {
6895
+ if (vtxo.txid && !createdAt.has(vtxo.txid)) {
6896
+ createdAt.set(vtxo.txid, vtxo.createdAt.getTime());
6897
+ }
6898
+ }
6899
+ return createdAt;
6900
+ }
6901
+ function gatedTouchpoints(gated) {
6902
+ const paidIn = /* @__PURE__ */ new Set();
6903
+ const paidOut = /* @__PURE__ */ new Set();
6904
+ for (const vtxo of gated) {
6905
+ if (vtxo.txid) paidIn.add(vtxo.txid);
6906
+ if (vtxo.isSpent && vtxo.arkTxId) paidOut.add(vtxo.arkTxId);
6907
+ }
6908
+ return { paidIn, paidOut };
6909
+ }
6880
6910
  var txKey = {
6881
6911
  commitmentTxid: "",
6882
6912
  boardingTxid: "",
@@ -6928,24 +6958,27 @@ function subtractAssets(spent, change) {
6928
6958
  if (map.size === 0) return void 0;
6929
6959
  return Array.from(map, ([assetId, amount]) => ({ assetId, amount }));
6930
6960
  }
6931
- function collectArkTxidsNeedingCreatedAt(vtxos) {
6932
- const ownTxids = new Set(vtxos.map((v) => v.txid));
6961
+ function collectArkTxidsNeedingCreatedAt(vtxos, localCreatedAt) {
6933
6962
  const txids = /* @__PURE__ */ new Set();
6934
6963
  for (const vtxo of vtxos) {
6935
- if (vtxo.isSpent && vtxo.arkTxId && !ownTxids.has(vtxo.arkTxId)) {
6964
+ if (vtxo.isSpent && vtxo.arkTxId && !localCreatedAt.has(vtxo.arkTxId)) {
6936
6965
  txids.add(vtxo.arkTxId);
6937
6966
  }
6938
6967
  }
6939
6968
  return [...txids];
6940
6969
  }
6941
- async function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnore, resolveTxCreatedAt) {
6942
- const fromOldestVtxo = vtxos.map(normalizeVtxo).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
6943
- const txidsNeedingCreatedAt = resolveTxCreatedAt ? collectArkTxidsNeedingCreatedAt(fromOldestVtxo) : [];
6970
+ async function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnore, resolveTxCreatedAt, gatedScripts = /* @__PURE__ */ new Map()) {
6971
+ const normalized = vtxos.map(normalizeVtxo).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
6972
+ const localCreatedAt = localCreatedAtByTxid(normalized);
6973
+ const gatedVtxos = normalized.filter((vtxo) => isGatedVtxo(vtxo, gatedScripts));
6974
+ const ownVtxos = normalized.filter((vtxo) => !isGatedVtxo(vtxo, gatedScripts));
6975
+ const { paidIn: paidIntoGated, paidOut: paidOutOfGated } = gatedTouchpoints(gatedVtxos);
6976
+ const txidsNeedingCreatedAt = resolveTxCreatedAt ? collectArkTxidsNeedingCreatedAt(ownVtxos, localCreatedAt) : [];
6944
6977
  const resolvedCreatedAt = resolveTxCreatedAt && txidsNeedingCreatedAt.length > 0 ? await resolveTxCreatedAt(txidsNeedingCreatedAt) : /* @__PURE__ */ new Map();
6945
6978
  const unmatchedSettledBoardingTxs = allBoardingTxs.filter(isSettledBoardingReceive).sort((a, b) => a.createdAt - b.createdAt);
6946
6979
  const sent = [];
6947
6980
  let received = [];
6948
- for (const vtxo of fromOldestVtxo) {
6981
+ for (const vtxo of ownVtxos) {
6949
6982
  if (vtxo.status.isLeaf) {
6950
6983
  const commitmentTxid = vtxo.commitmentTxIds[0];
6951
6984
  const vtxoCreatedAt = vtxo.createdAt.getTime();
@@ -6955,7 +6988,7 @@ async function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnor
6955
6988
  unmatchedSettledBoardingTxs,
6956
6989
  (tx) => tx.createdAt <= vtxoCreatedAt && (tx.key.commitmentTxid === commitmentTxid || tx.key.commitmentTxid === vtxo.settledBy)
6957
6990
  );
6958
- } else if (fromOldestVtxo.filter((v) => v.settledBy === vtxo.commitmentTxIds[0]).length === 0) {
6991
+ } else if (ownVtxos.filter((v) => v.settledBy === vtxo.commitmentTxIds[0]).length === 0) {
6959
6992
  const duplicateBoardingReceive = consumeBoardingReceive(
6960
6993
  unmatchedSettledBoardingTxs,
6961
6994
  (tx) => tx.amount === vtxo.value && tx.createdAt <= vtxoCreatedAt
@@ -6976,11 +7009,14 @@ async function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnor
6976
7009
  });
6977
7010
  }
6978
7011
  }
6979
- } else if (fromOldestVtxo.filter((v) => v.arkTxId === vtxo.txid).length === 0) {
7012
+ } else if (ownVtxos.filter((v) => v.arkTxId === vtxo.txid).length === 0) {
6980
7013
  const assets = collectAssets([vtxo]);
6981
7014
  received.push({
6982
7015
  key: { ...txKey, arkTxid: vtxo.txid },
6983
- tag: "offchain",
7016
+ // The escrow paying out: a swap fill or a cancel spends the
7017
+ // gated coin and lands the proceeds here. Nothing else about
7018
+ // the row changes — the tag only names the counterparty.
7019
+ tag: paidOutOfGated.has(vtxo.txid) ? "gated" : "offchain",
6984
7020
  type: "RECEIVED" /* TxReceived */,
6985
7021
  amount: vtxo.value,
6986
7022
  settled: vtxo.status.isLeaf || vtxo.isSpent,
@@ -6990,8 +7026,8 @@ async function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnor
6990
7026
  }
6991
7027
  if (vtxo.isSpent) {
6992
7028
  if (vtxo.arkTxId && !sent.some((s) => s.key.arkTxid === vtxo.arkTxId)) {
6993
- const changes = fromOldestVtxo.filter((_) => _.txid === vtxo.arkTxId);
6994
- const allSpent = fromOldestVtxo.filter((v) => v.arkTxId === vtxo.arkTxId);
7029
+ const changes = ownVtxos.filter((_) => _.txid === vtxo.arkTxId);
7030
+ const allSpent = ownVtxos.filter((v) => v.arkTxId === vtxo.arkTxId);
6995
7031
  const spentAmount = allSpent.reduce((acc, v) => acc + v.value, 0);
6996
7032
  let txAmount = 0;
6997
7033
  let txTime = 0;
@@ -7001,13 +7037,16 @@ async function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnor
7001
7037
  txTime = changes[0].createdAt.getTime();
7002
7038
  } else {
7003
7039
  txAmount = spentAmount;
7004
- txTime = resolvedCreatedAt.get(vtxo.arkTxId) ?? vtxo.createdAt.getTime() + 1;
7040
+ txTime = localCreatedAt.get(vtxo.arkTxId) ?? resolvedCreatedAt.get(vtxo.arkTxId) ?? vtxo.createdAt.getTime() + 1;
7005
7041
  }
7006
7042
  const assets = subtractAssets(allSpent, changes);
7007
7043
  if (txAmount !== 0 || assets) {
7008
7044
  sent.push({
7009
7045
  key: { ...txKey, arkTxid: vtxo.arkTxId },
7010
- tag: "offchain",
7046
+ // Funding the escrow: an output of this tx landed on a
7047
+ // gated contract of the wallet's, so the send is to the
7048
+ // user's own covenant rather than to a stranger.
7049
+ tag: paidIntoGated.has(vtxo.arkTxId) ? "gated" : "offchain",
7011
7050
  type: "SENT" /* TxSent */,
7012
7051
  amount: txAmount,
7013
7052
  settled: true,
@@ -7017,10 +7056,10 @@ async function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnor
7017
7056
  }
7018
7057
  }
7019
7058
  if (vtxo.settledBy && !commitmentsToIgnore.has(vtxo.settledBy) && !sent.some((s) => s.key.commitmentTxid === vtxo.settledBy)) {
7020
- const changes = fromOldestVtxo.filter(
7059
+ const changes = ownVtxos.filter(
7021
7060
  (v) => v.status.isLeaf && v.commitmentTxIds.length > 0 && v.commitmentTxIds.every((_) => vtxo.settledBy === _)
7022
7061
  );
7023
- const forfeitVtxos = fromOldestVtxo.filter((v) => v.settledBy === vtxo.settledBy);
7062
+ const forfeitVtxos = ownVtxos.filter((v) => v.settledBy === vtxo.settledBy);
7024
7063
  const forfeitAmount = forfeitVtxos.reduce((acc, v) => acc + v.value, 0);
7025
7064
  if (changes.length > 0) {
7026
7065
  const settledAmount = changes.reduce((acc, v) => acc + v.value, 0);
@@ -8645,6 +8684,8 @@ var DEFAULT_CONTRACT_WATCHER_CONFIG = {
8645
8684
  var ContractWatcher = class {
8646
8685
  config;
8647
8686
  contracts = /* @__PURE__ */ new Map();
8687
+ /** In-memory and process-lifetime; never persisted. */
8688
+ watchedScripts = /* @__PURE__ */ new Map();
8648
8689
  subscriptionId;
8649
8690
  abortController;
8650
8691
  isWatching = false;
@@ -8765,6 +8806,47 @@ var ContractWatcher = class {
8765
8806
  getWatchedContracts() {
8766
8807
  return this.getAllContracts().filter(isWatchedContract);
8767
8808
  }
8809
+ /**
8810
+ * Every script the subscription must carry. Wider than
8811
+ * {@link getWatchedContracts}, which stays the sync scope.
8812
+ */
8813
+ getSubscribedScripts() {
8814
+ const scripts = new Set(this.getWatchedContracts().map((c) => c.script));
8815
+ for (const script of this.watchedScripts.keys()) {
8816
+ scripts.add(script);
8817
+ }
8818
+ return Array.from(scripts);
8819
+ }
8820
+ /** @see IContractManager.watchScript for the delivery and filter contract. */
8821
+ async addWatchedScript(script, options) {
8822
+ const existing = this.watchedScripts.get(script);
8823
+ if (existing) {
8824
+ if (options?.label !== void 0) existing.label = options.label;
8825
+ return;
8826
+ }
8827
+ this.watchedScripts.set(script, {
8828
+ label: options?.label,
8829
+ lastKnownVtxos: /* @__PURE__ */ new Map()
8830
+ });
8831
+ if (this.isWatching) {
8832
+ await this.pollWatchedScripts([script]);
8833
+ await this.tryUpdateSubscription();
8834
+ }
8835
+ }
8836
+ /** Stop watching a script added by {@link addWatchedScript}. */
8837
+ async removeWatchedScript(script) {
8838
+ if (!this.watchedScripts.delete(script)) return;
8839
+ if (this.isWatching) {
8840
+ await this.tryUpdateSubscription();
8841
+ }
8842
+ }
8843
+ /** Every script registered via {@link addWatchedScript}. */
8844
+ getWatchedScripts() {
8845
+ return Array.from(this.watchedScripts.entries()).map(([script, state]) => ({
8846
+ script,
8847
+ label: state.label
8848
+ }));
8849
+ }
8768
8850
  /**
8769
8851
  * Get virtual outputs for contracts, grouped by contract script.
8770
8852
  * @see WalletRepository for `repo`
@@ -8956,8 +9038,72 @@ var ContractWatcher = class {
8956
9038
  }
8957
9039
  async pollAllContracts() {
8958
9040
  const scripts = this.getWatchedContracts().map((c) => c.script);
9041
+ if (scripts.length > 0) {
9042
+ await this.pollContracts(scripts);
9043
+ }
9044
+ await this.pollWatchedScripts(Array.from(this.watchedScripts.keys()));
9045
+ }
9046
+ /**
9047
+ * Poll watch-only scripts against the indexer and emit the delta.
9048
+ *
9049
+ * Separate from {@link pollContracts} because that one diffs the wallet
9050
+ * repository, which is permanently empty for a script this wallet does not
9051
+ * own — reusing it would report every live output as spent on every tick.
9052
+ * Not a second poll loop: same timer and callers, different source.
9053
+ */
9054
+ async pollWatchedScripts(candidates) {
9055
+ if (!this.eventCallback) return;
9056
+ const scripts = candidates.filter((s) => {
9057
+ const state = this.contracts.get(s);
9058
+ return this.watchedScripts.has(s) && (state === void 0 || !isWatchedContract(state.contract));
9059
+ });
8959
9060
  if (scripts.length === 0) return;
8960
- await this.pollContracts(scripts);
9061
+ const now = Date.now();
9062
+ let current;
9063
+ try {
9064
+ current = await getAllNormalizedVtxos(this.config.indexerProvider, scripts, {
9065
+ spendableOnly: true
9066
+ });
9067
+ } catch (error) {
9068
+ console.error("ContractWatcher watch-only poll failed:", error);
9069
+ return;
9070
+ }
9071
+ const byScript = /* @__PURE__ */ new Map();
9072
+ for (const vtxo of current) {
9073
+ let bucket = byScript.get(vtxo.script);
9074
+ if (!bucket) {
9075
+ bucket = [];
9076
+ byScript.set(vtxo.script, bucket);
9077
+ }
9078
+ bucket.push(vtxo);
9079
+ }
9080
+ for (const script of scripts) {
9081
+ const state = this.watchedScripts.get(script);
9082
+ if (!state) continue;
9083
+ const currentVtxos = byScript.get(script) || [];
9084
+ const currentKeys = new Set(currentVtxos.map((v) => `${v.txid}:${v.vout}`));
9085
+ const newVtxos = [];
9086
+ for (const vtxo of currentVtxos) {
9087
+ const key = `${vtxo.txid}:${vtxo.vout}`;
9088
+ if (!state.lastKnownVtxos.has(key)) {
9089
+ newVtxos.push(vtxo);
9090
+ state.lastKnownVtxos.set(key, vtxo);
9091
+ }
9092
+ }
9093
+ const spentVtxos = [];
9094
+ for (const [key, vtxo] of state.lastKnownVtxos) {
9095
+ if (!currentKeys.has(key)) {
9096
+ spentVtxos.push(vtxo);
9097
+ state.lastKnownVtxos.delete(key);
9098
+ }
9099
+ }
9100
+ if (newVtxos.length > 0) {
9101
+ this.emitWatchedScriptEvent(script, newVtxos, "vtxo_received", now);
9102
+ }
9103
+ if (spentVtxos.length > 0) {
9104
+ this.emitWatchedScriptEvent(script, spentVtxos, "vtxo_spent", now);
9105
+ }
9106
+ }
8961
9107
  }
8962
9108
  /**
8963
9109
  * Poll specific contracts and emit events for changes.
@@ -9058,10 +9204,10 @@ var ContractWatcher = class {
9058
9204
  /**
9059
9205
  * Update the subscription with scripts that should be watched.
9060
9206
  *
9061
- * @see getWatchedContracts
9207
+ * @see getSubscribedScripts
9062
9208
  */
9063
9209
  async updateSubscription() {
9064
- const scriptsToWatch = this.getWatchedContracts().map((c) => c.script);
9210
+ const scriptsToWatch = this.getSubscribedScripts();
9065
9211
  if (scriptsToWatch.length === 0) {
9066
9212
  if (this.subscriptionId) {
9067
9213
  try {
@@ -9145,16 +9291,26 @@ var ContractWatcher = class {
9145
9291
  */
9146
9292
  processSubscriptionVtxos(vtxos, eventType, timestamp) {
9147
9293
  const byContract = /* @__PURE__ */ new Map();
9294
+ const byWatchedScript = /* @__PURE__ */ new Map();
9148
9295
  let unknownScript = 0;
9296
+ const now = { timestamp: /* @__PURE__ */ new Date() };
9149
9297
  for (const vtxo of vtxos) {
9150
- if (!this.contracts.has(vtxo.script)) {
9298
+ const state = this.contracts.get(vtxo.script);
9299
+ const watchOnly = this.watchedScripts.has(vtxo.script);
9300
+ const preferContract = state !== void 0 && (!watchOnly || isWatchedContract(state.contract));
9301
+ const target = preferContract ? byContract : watchOnly ? byWatchedScript : void 0;
9302
+ if (!target) {
9151
9303
  unknownScript++;
9152
9304
  continue;
9153
9305
  }
9154
- let bucket = byContract.get(vtxo.script);
9306
+ if (target === byWatchedScript && eventType === "vtxo_received") {
9307
+ const n = normalizeVtxo(vtxo);
9308
+ if (hasTerminalSpend(n) || n.isSwept || isPastExpiry(n, now)) continue;
9309
+ }
9310
+ let bucket = target.get(vtxo.script);
9155
9311
  if (!bucket) {
9156
9312
  bucket = [];
9157
- byContract.set(vtxo.script, bucket);
9313
+ target.set(vtxo.script, bucket);
9158
9314
  }
9159
9315
  bucket.push(vtxo);
9160
9316
  }
@@ -9177,6 +9333,29 @@ var ContractWatcher = class {
9177
9333
  }
9178
9334
  this.emitVtxoEvent(contractScript, bucketVtxos, eventType, timestamp);
9179
9335
  }
9336
+ for (const [script, bucketVtxos] of byWatchedScript) {
9337
+ const state = this.watchedScripts.get(script);
9338
+ if (state) {
9339
+ for (const vtxo of bucketVtxos) {
9340
+ const key = `${vtxo.txid}:${vtxo.vout}`;
9341
+ if (eventType === "vtxo_received") {
9342
+ state.lastKnownVtxos.set(key, vtxo);
9343
+ } else {
9344
+ state.lastKnownVtxos.delete(key);
9345
+ }
9346
+ }
9347
+ }
9348
+ this.emitWatchedScriptEvent(script, bucketVtxos, eventType, timestamp);
9349
+ }
9350
+ }
9351
+ /** As {@link emitVtxoEvent}, less the `contract` and the annotation. */
9352
+ emitWatchedScriptEvent(script, vtxos, eventType, timestamp) {
9353
+ if (!this.eventCallback) return;
9354
+ if (eventType === "vtxo_received") {
9355
+ this.eventCallback({ type: "vtxo_received", contractScript: script, vtxos, timestamp });
9356
+ return;
9357
+ }
9358
+ this.eventCallback({ type: "vtxo_spent", contractScript: script, vtxos, timestamp });
9180
9359
  }
9181
9360
  /**
9182
9361
  * Emit a virtual output event for a contract.
@@ -9348,6 +9527,7 @@ var ContractManager = class _ContractManager {
9348
9527
  syncDegradedReason;
9349
9528
  /** Epoch-ms of the last successful provider sync, if any. */
9350
9529
  lastSyncedAt;
9530
+ syncedAtByScript = /* @__PURE__ */ new Map();
9351
9531
  /** Last chain tip read, with the epoch-ms it was read at. @see currentChainTip */
9352
9532
  chainTipCache;
9353
9533
  /** In-flight chain tip read, so concurrent cache misses share one. */
@@ -9401,6 +9581,10 @@ var ContractManager = class _ContractManager {
9401
9581
  lastSyncedAt: this.lastSyncedAt
9402
9582
  };
9403
9583
  }
9584
+ /** @see ContractManagerConfig.vtxoSyncMaxAgeMs — for factory-built managers. */
9585
+ setVtxoSyncMaxAge(maxAgeMs) {
9586
+ this.config.vtxoSyncMaxAgeMs = maxAgeMs;
9587
+ }
9404
9588
  markSyncOnline() {
9405
9589
  this.lastSyncedAt = Date.now();
9406
9590
  this.syncDegradedReason = this.annotationDegradedReason();
@@ -9963,12 +10147,16 @@ var ContractManager = class _ContractManager {
9963
10147
  }
9964
10148
  async getContractsWithVtxos(filter, pageSize) {
9965
10149
  const contracts = await this.getContracts(filter);
9966
- try {
9967
- await this.syncContracts({ contracts, pageSize });
9968
- this.markSyncOnline();
9969
- } catch (err) {
9970
- if (!isRetryableProviderError(err)) throw err;
9971
- this.markSyncDegraded(err);
10150
+ if (this.syncedWithin(contracts, this.config.vtxoSyncMaxAgeMs ?? 0)) {
10151
+ await this.demoteFundedAwaitingContracts(contracts);
10152
+ } else {
10153
+ try {
10154
+ await this.syncContracts({ contracts, pageSize });
10155
+ this.markSyncOnline();
10156
+ } catch (err) {
10157
+ if (!isRetryableProviderError(err)) throw err;
10158
+ this.markSyncDegraded(err);
10159
+ }
9972
10160
  }
9973
10161
  const vtxos = await this.getVtxosForContracts(contracts);
9974
10162
  return contracts.map((contract) => ({
@@ -10330,6 +10518,18 @@ var ContractManager = class _ContractManager {
10330
10518
  async isWatching() {
10331
10519
  return this.watcher.isCurrentlyWatching();
10332
10520
  }
10521
+ /** @see IContractManager.watchScript */
10522
+ async watchScript(script, options) {
10523
+ await this.watcher.addWatchedScript(script, options);
10524
+ }
10525
+ /** @see IContractManager.unwatchScript */
10526
+ async unwatchScript(script) {
10527
+ await this.watcher.removeWatchedScript(script);
10528
+ }
10529
+ /** @see IContractManager.getWatchedScripts */
10530
+ async getWatchedScripts() {
10531
+ return this.watcher.getWatchedScripts();
10532
+ }
10333
10533
  /**
10334
10534
  * Emit an event to all registered callbacks.
10335
10535
  */
@@ -10350,11 +10550,15 @@ var ContractManager = class _ContractManager {
10350
10550
  try {
10351
10551
  switch (event.type) {
10352
10552
  // Delta-sync only the changed virtual outputs for this contract.
10553
+ // The guard below is the ownership boundary: a watch-only script
10554
+ // reports these same types, so it must precede `syncContracts`.
10353
10555
  case "vtxo_received":
10556
+ if (!isContractVtxoEvent(event)) break;
10354
10557
  await this.syncContracts({ contracts: [event.contract] });
10355
10558
  this.markSyncOnline();
10356
10559
  break;
10357
10560
  case "vtxo_spent":
10561
+ if (!isContractVtxoEvent(event)) break;
10358
10562
  await this.syncContracts({ contracts: [event.contract] });
10359
10563
  this.markSyncOnline();
10360
10564
  if (this.config.onVtxosSpent) {
@@ -10367,6 +10571,7 @@ var ContractManager = class _ContractManager {
10367
10571
  }
10368
10572
  break;
10369
10573
  case "connection_reset":
10574
+ this.syncedAtByScript.clear();
10370
10575
  await this.scheduleLookAheadDrain();
10371
10576
  await this.reconcileWatched();
10372
10577
  this.markSyncOnline();
@@ -10413,9 +10618,21 @@ var ContractManager = class _ContractManager {
10413
10618
  const cutoff = cursorCutoff(requestStartedAt);
10414
10619
  await advanceSyncCursor(this.config.walletRepository, cutoff);
10415
10620
  }
10621
+ if (options.window === void 0) {
10622
+ for (const contract of contracts) {
10623
+ this.syncedAtByScript.set(contract.script, requestStartedAt);
10624
+ }
10625
+ }
10416
10626
  await this.demoteFundedAwaitingContracts(contracts);
10417
10627
  return result;
10418
10628
  }
10629
+ syncedWithin(contracts, maxAgeMs) {
10630
+ if (maxAgeMs <= 0) return false;
10631
+ const floor = Date.now() - maxAgeMs;
10632
+ return contracts.every(
10633
+ (contract) => (this.syncedAtByScript.get(contract.script) ?? 0) >= floor
10634
+ );
10635
+ }
10419
10636
  /**
10420
10637
  * Demote every `awaiting-funds` contract in `contracts` that has been
10421
10638
  * funded — the automatic half of {@link ContractWatchState}.
@@ -12596,7 +12813,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
12596
12813
  const vtxos = filterSnapshotVtxos(snapshot, filter, this._pendingSpendOutpoints);
12597
12814
  const { gated, pendingRecovery } = this.spendabilityView(snapshot);
12598
12815
  const selectable = vtxos.filter(
12599
- (vtxo) => !gated.has(vtxo.script) && !pendingRecovery.has(`${vtxo.txid}:${vtxo.vout}`)
12816
+ (vtxo) => !isGatedVtxo(vtxo, gated) && !pendingRecovery.has(`${vtxo.txid}:${vtxo.vout}`)
12600
12817
  );
12601
12818
  const unlocked = await spendableVtxosExcludingLocked(selectable, this.intentRepository);
12602
12819
  logExcludedVtxos("getSpendableVtxos", vtxos, [
@@ -12694,7 +12911,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
12694
12911
  */
12695
12912
  spendabilityView(snapshot) {
12696
12913
  return {
12697
- gated: gatedContracts(snapshot.map((_) => _.contract)),
12914
+ gated: gatedFrom(snapshot),
12698
12915
  pendingRecovery: this.selectPendingRecovery(snapshot)
12699
12916
  };
12700
12917
  }
@@ -12709,7 +12926,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
12709
12926
  return {
12710
12927
  now: { timestamp: /* @__PURE__ */ new Date() },
12711
12928
  isPendingRecovery: (vtxo) => pendingRecovery.has(`${vtxo.txid}:${vtxo.vout}`),
12712
- isGenericallySpendable: (vtxo) => !gated.has(vtxo.script),
12929
+ isGenericallySpendable: (vtxo) => !isGatedVtxo(vtxo, gated),
12713
12930
  isUnlocked: (vtxo) => unlocked.has(`${vtxo.txid}:${vtxo.vout}`)
12714
12931
  };
12715
12932
  }
@@ -12749,16 +12966,18 @@ var ReadonlyWallet = class _ReadonlyWallet {
12749
12966
  * Return wallet transaction history derived from Arkade state and boarding transactions.
12750
12967
  */
12751
12968
  async getTransactionHistory() {
12752
- const contractManager = await this.getContractManager();
12753
- const response = await contractManager.getContractsWithVtxos();
12754
- const allVtxos = response.flatMap((_) => _.vtxos);
12755
- const { boardingTxs, commitmentsToIgnore } = await this.getBoardingTxs();
12969
+ const [snapshot, { boardingTxs, commitmentsToIgnore }] = await Promise.all([
12970
+ this.contractSnapshot(),
12971
+ this.getBoardingTxs()
12972
+ ]);
12973
+ const allVtxos = snapshot.flatMap((_) => _.vtxos);
12756
12974
  const resolveTxCreatedAt = (txids) => fetchVtxoCreatedAtByTxid(this.indexerProvider, txids);
12757
12975
  return buildTransactionHistory(
12758
12976
  allVtxos,
12759
12977
  boardingTxs,
12760
12978
  commitmentsToIgnore,
12761
- resolveTxCreatedAt
12979
+ resolveTxCreatedAt,
12980
+ gatedFrom(snapshot)
12762
12981
  );
12763
12982
  }
12764
12983
  /**
@@ -13055,7 +13274,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
13055
13274
  const cm = await this.getContractManager();
13056
13275
  let annotationQueue = Promise.resolve();
13057
13276
  indexerStopFunc = cm.onContractEvent((event) => {
13058
- if (event.type !== "vtxo_received" && event.type !== "vtxo_spent") {
13277
+ if (!isContractVtxoEvent(event)) {
13059
13278
  return;
13060
13279
  }
13061
13280
  if (event.contract.type !== "default" && event.contract.type !== "delegate") {
@@ -17054,6 +17273,9 @@ var DelegateNotConfiguredError = class extends Error {
17054
17273
  }
17055
17274
  };
17056
17275
  var DEFAULT_MESSAGE_TAG = "WALLET_UPDATER";
17276
+ function unsupportedByManager(method) {
17277
+ return new Error(`Contract manager does not support ${method}`);
17278
+ }
17057
17279
  var serializeMigrationVtxoRef = (ref) => ({
17058
17280
  txid: ref.txid,
17059
17281
  vout: ref.vout,
@@ -17397,8 +17619,7 @@ var WalletMessageHandler = class {
17397
17619
  });
17398
17620
  }
17399
17621
  case "GET_TRANSACTION_HISTORY": {
17400
- const allVtxos = await this.getVtxosFromRepo();
17401
- const transactions = await this.buildTransactionHistoryFromCache(allVtxos) ?? [];
17622
+ const transactions = await this.buildTransactionHistoryFromCache() ?? [];
17402
17623
  return this.tagged({
17403
17624
  id,
17404
17625
  type: "TRANSACTION_HISTORY",
@@ -17474,6 +17695,39 @@ var WalletMessageHandler = class {
17474
17695
  payload: { contracts }
17475
17696
  });
17476
17697
  }
17698
+ case "WATCH_SCRIPT": {
17699
+ const manager = await this.readonlyWallet.getContractManager();
17700
+ if (!manager.watchScript) throw unsupportedByManager("watchScript");
17701
+ await manager.watchScript(message.payload.script, {
17702
+ label: message.payload.label
17703
+ });
17704
+ return this.tagged({
17705
+ id,
17706
+ type: "SCRIPT_WATCHED",
17707
+ payload: { script: message.payload.script }
17708
+ });
17709
+ }
17710
+ case "UNWATCH_SCRIPT": {
17711
+ const manager = await this.readonlyWallet.getContractManager();
17712
+ if (!manager.unwatchScript) throw unsupportedByManager("unwatchScript");
17713
+ await manager.unwatchScript(message.payload.script);
17714
+ return this.tagged({
17715
+ id,
17716
+ type: "SCRIPT_UNWATCHED",
17717
+ payload: { script: message.payload.script }
17718
+ });
17719
+ }
17720
+ case "GET_WATCHED_SCRIPTS": {
17721
+ const manager = await this.readonlyWallet.getContractManager();
17722
+ if (!manager.getWatchedScripts) {
17723
+ throw unsupportedByManager("getWatchedScripts");
17724
+ }
17725
+ return this.tagged({
17726
+ id,
17727
+ type: "WATCHED_SCRIPTS",
17728
+ payload: { scripts: await manager.getWatchedScripts() }
17729
+ });
17730
+ }
17477
17731
  case "ANNOTATE_VTXOS": {
17478
17732
  const manager = await this.readonlyWallet.getContractManager();
17479
17733
  const annotated = await manager.annotateVtxos(message.payload.vtxos);
@@ -17848,7 +18102,7 @@ var WalletMessageHandler = class {
17848
18102
  unconfirmed += utxo.value;
17849
18103
  }
17850
18104
  }
17851
- const gated = gatedContracts(snapshot.map((_) => _.contract));
18105
+ const gated = gatedFrom(snapshot);
17852
18106
  const unlocked = new Set(
17853
18107
  (await spendableVtxosExcludingLocked(allVtxos, this.readonlyWallet?.intentRepository)).map((vtxo) => `${vtxo.txid}:${vtxo.vout}`)
17854
18108
  );
@@ -17856,7 +18110,7 @@ var WalletMessageHandler = class {
17856
18110
  const offchain = computeOffchainBalance(allVtxos, {
17857
18111
  now: { timestamp: /* @__PURE__ */ new Date() },
17858
18112
  isPendingRecovery: (vtxo) => pendingOutpoints.has(`${vtxo.txid}:${vtxo.vout}`),
17859
- isGenericallySpendable: (vtxo) => !gated.has(vtxo.script),
18113
+ isGenericallySpendable: (vtxo) => !isGatedVtxo(vtxo, gated),
17860
18114
  isUnlocked: (vtxo) => unlocked.has(`${vtxo.txid}:${vtxo.vout}`)
17861
18115
  });
17862
18116
  return {
@@ -17996,7 +18250,6 @@ var WalletMessageHandler = class {
17996
18250
  if (!this.readonlyWallet || !this.walletRepository) {
17997
18251
  return;
17998
18252
  }
17999
- const vtxos = await this.getVtxosFromRepo();
18000
18253
  const boardingAddresses = await this.readonlyWallet.getBoardingAddresses();
18001
18254
  const fresh = await this.readonlyWallet.getBoardingUtxos();
18002
18255
  const freshKeys = new Set(fresh.map((u) => `${u.txid}:${u.vout}`));
@@ -18008,7 +18261,7 @@ var WalletMessageHandler = class {
18008
18261
  if (kept.length > 0) await this.walletRepository.saveUtxos(addr, kept);
18009
18262
  }
18010
18263
  const address = await this.readonlyWallet.getAddress();
18011
- const txs = await this.buildTransactionHistoryFromCache(vtxos);
18264
+ const txs = await this.buildTransactionHistoryFromCache();
18012
18265
  if (txs) await this.walletRepository.saveTransactions(address, txs);
18013
18266
  }
18014
18267
  /**
@@ -18199,13 +18452,29 @@ var WalletMessageHandler = class {
18199
18452
  /**
18200
18453
  * Build transaction history from cached virtual outputs, hitting the indexer only for
18201
18454
  * uncached timestamps. Best-effort, like the plain Wallet path.
18202
- */
18203
- async buildTransactionHistoryFromCache(vtxos) {
18455
+ *
18456
+ * Takes its own {@link repoSnapshot} rather than a caller-supplied VTXO
18457
+ * list: the coins and the gate that judges them have to come off one read
18458
+ * or they answer about different instants, and being the worker's only
18459
+ * history builder means neither caller can supply one without the other.
18460
+ * The snapshot and the boarding read are independent, so they run together
18461
+ * — the same pairing `handleGetBalance` makes.
18462
+ */
18463
+ async buildTransactionHistoryFromCache() {
18204
18464
  if (!this.readonlyWallet) return null;
18205
- const { boardingTxs, commitmentsToIgnore } = await this.readonlyWallet.getBoardingTxs();
18465
+ const [{ snapshot, vtxos }, { boardingTxs, commitmentsToIgnore }] = await Promise.all([
18466
+ this.repoSnapshot(),
18467
+ this.readonlyWallet.getBoardingTxs()
18468
+ ]);
18206
18469
  const indexerProvider = this.indexerProvider;
18207
18470
  const resolveTxCreatedAt = indexerProvider ? (txids) => fetchVtxoCreatedAtByTxid(indexerProvider, txids) : void 0;
18208
- return buildTransactionHistory(vtxos, boardingTxs, commitmentsToIgnore, resolveTxCreatedAt);
18471
+ return buildTransactionHistory(
18472
+ vtxos,
18473
+ boardingTxs,
18474
+ commitmentsToIgnore,
18475
+ resolveTxCreatedAt,
18476
+ gatedFrom(snapshot)
18477
+ );
18209
18478
  }
18210
18479
  async ensureContractEventBroadcasting() {
18211
18480
  const wallet = this.readonlyWallet;
@@ -18276,6 +18545,7 @@ var DEFAULT_MESSAGE_TIMEOUTS = {
18276
18545
  GET_TRANSACTION_HISTORY: 2e4,
18277
18546
  GET_CONTRACTS: 2e4,
18278
18547
  GET_CONTRACTS_WITH_VTXOS: 2e4,
18548
+ GET_WATCHED_SCRIPTS: 1e4,
18279
18549
  ANNOTATE_VTXOS: 2e4,
18280
18550
  GET_SPENDABLE_PATHS: 2e4,
18281
18551
  GET_ALL_SPENDING_PATHS: 2e4,
@@ -18315,6 +18585,10 @@ var DEFAULT_MESSAGE_TIMEOUTS = {
18315
18585
  SIGN_TRANSACTION: 3e4,
18316
18586
  CREATE_CONTRACT: 3e4,
18317
18587
  UPDATE_CONTRACT: 3e4,
18588
+ // Registering a watch is an in-memory map write plus one subscription
18589
+ // update — no indexer round trip on the request path.
18590
+ WATCH_SCRIPT: 1e4,
18591
+ UNWATCH_SCRIPT: 1e4,
18318
18592
  DELETE_CONTRACT: 1e4,
18319
18593
  REFRESH_VTXOS: 3e4,
18320
18594
  REFRESH_OUTPOINTS: 3e4
@@ -18338,6 +18612,7 @@ var DEDUPABLE_REQUEST_TYPES = /* @__PURE__ */ new Set([
18338
18612
  "GET_SPENDABLE_VTXOS",
18339
18613
  "GET_CONTRACTS",
18340
18614
  "GET_CONTRACTS_WITH_VTXOS",
18615
+ "GET_WATCHED_SCRIPTS",
18341
18616
  "ANNOTATE_VTXOS",
18342
18617
  "GET_SPENDABLE_PATHS",
18343
18618
  "GET_ALL_SPENDING_PATHS",
@@ -19077,6 +19352,46 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
19077
19352
  throw new Error("Failed to get contracts with vtxos");
19078
19353
  }
19079
19354
  },
19355
+ async watchScript(script, options) {
19356
+ const message = {
19357
+ type: "WATCH_SCRIPT",
19358
+ id: getRandomId(),
19359
+ tag: messageTag,
19360
+ payload: { script, label: options?.label }
19361
+ };
19362
+ try {
19363
+ await sendContractMessage(message);
19364
+ } catch (e) {
19365
+ throw new Error("Failed to watch script");
19366
+ }
19367
+ },
19368
+ async unwatchScript(script) {
19369
+ const message = {
19370
+ type: "UNWATCH_SCRIPT",
19371
+ id: getRandomId(),
19372
+ tag: messageTag,
19373
+ payload: { script }
19374
+ };
19375
+ try {
19376
+ await sendContractMessage(message);
19377
+ } catch (e) {
19378
+ throw new Error("Failed to unwatch script");
19379
+ }
19380
+ },
19381
+ async getWatchedScripts() {
19382
+ const message = {
19383
+ type: "GET_WATCHED_SCRIPTS",
19384
+ id: getRandomId(),
19385
+ tag: messageTag,
19386
+ payload: {}
19387
+ };
19388
+ try {
19389
+ const response = await sendContractMessage(message);
19390
+ return response.payload.scripts;
19391
+ } catch (e) {
19392
+ throw new Error("Failed to get watched scripts");
19393
+ }
19394
+ },
19080
19395
  getSyncState() {
19081
19396
  return syncState;
19082
19397
  },
@@ -21162,7 +21477,7 @@ var RestEmulatorProvider = class {
21162
21477
  serverUrl;
21163
21478
  async getInfo() {
21164
21479
  const url = `${this.serverUrl}/v1/info`;
21165
- const response = await fetch(url);
21480
+ const response = await baseFetch(url);
21166
21481
  if (!response.ok) {
21167
21482
  const errorText2 = await response.text();
21168
21483
  throw new Error(`Failed to get emulator info: ${errorText2}`);
@@ -23602,6 +23917,6 @@ function createDefaultPaymentRouter(wallet) {
23602
23917
  (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
23603
23918
  */
23604
23919
 
23605
- export { ARKADE_MAGIC, ARKADE_SALTED_PREIMAGE_TAG, ARKADE_SWAP_PREIMAGE_TAG, ASSET_CARRIER_SATS, AbortError, ActivityRegistry, AmbiguousRouteError, ArkNote, ArkadeCash, ArkadeCashCreateError, AssetManager, BIP21, BIP21Error, BIP322, BLOCKED_UPGRADE_TIMEOUT_MS, Batch, CachingArkProvider, ChainedTxType, ConnectionDisposedError, ContractManager, ContractWatcher, DEFAULT_MESSAGE_TIMEOUTS, DEFAULT_MIN_BATCH_EXPIRY_SECONDS, DEFAULT_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, DatabaseUpgradeBlockedError, DelegateManagerImpl, DelegateNotConfiguredError, DescriptorIdentity, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EmulatorPacket, EsploraProvider, Estimator, Extension, ExtensionNotFoundError, ForeignDescriptorError, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MAX_USED_SIGNING_DESCRIPTORS_LOOK_AHEAD, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MUTINYNET_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, PaymentRouter, PrevTxUnavailableError, REGTEST_MIN_BATCH_EXPIRY_SECONDS, REGTEST_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestEmulatorProvider, SIGNET_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnannotatableInputError, UnilateralExit, UnknownPacket, Unroll, VtxoManager, Wallet2 as Wallet, WalletCannotAllocateAddressError, WalletCannotSignError, WalletMessageHandler, WalletNotInitializedError, WsElectrumChainSource, adoptContractDescriptor, arkAssetRail, arkRail, arkTarget, arkade_exports, assertFinalCommitmentMatchesValidated, assertIntentIdUnique, assertNoAssets, assertRecipientArkadeAddress, assertSendableAmount, assertSubmittedArkTxid, assertValidBatchExpiry, assertValidServerUnrollScript, assetMintResolver, assetsOf, attachPrevArkTxs, attachPrevoutTxs, awaitTransaction, boardingResolver, btcTarget, buildForfeitTx, buildOffchainTx, buildPreimageMessage, buildSaltedPreimageMessage, claimWithPreimageIdentity, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, collabExitResolver, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, contractPreimage, contractSigner, createAssetPacket, createDefaultActivityRegistry, createDefaultPaymentRouter, createExitChainResolver, createManagedConnection, decodeArkContract, defaultBatchExpiryPolicy, defaultCheckpointExitDelayPolicy, deleteByIndex, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, exitObserverFor, extendVirtualCoinForContract, extractArkProviderUrl, gateExclusion, gatedContracts, getAllByIndexValues, getRandomId, hasBoardingTxExpired, hasCandidates, intentMatchesFilter, intentPageBounds, invoiceTarget, isAddressAllocationCapable, isArkContract, isBatchSignable, isBtcAddress, isContractGenericallySpendable, isCooperativelyMigratable, isDeterministicSigner, isDiscoverable, isHDAllocationCapable, isHDDeterministicSignCapable, isHDWalletCapable, isLightningInvoice, isLnurl, isMessageBusInitializingError, isMessageBusNotInitializedError, isPerArtifactDescriptor, isSigningIdentity, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, isWatchedContract, legacyVtxoFacts, logExcludedVtxos, makeHandle, matchServerCheckpoints, mergeChainedTxType, notifyExitObserved, onchainRail, openDatabase, outpointExclusion, outpointReasons, promisifyRequest, provisionClaimSecret, provisionRefundKey, resolveAssetAmount, resolveBatchExpiryPolicy, resolveCheckpointExitDelayPolicy, resolveDescriptorSigner, resolvePrevTxs, resolveSendAmount, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signAndSubmitOffchainTx, signerSetFromInfo, signingDescriptorIndex, strictSigningDescriptorIndex, toXOnlySignerHex, tryResolveSendAmount, validateBatchRecipients, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, walletFeeSource, warnAndFilterVtxosForScript, withPrevTxs };
23606
- //# sourceMappingURL=chunk-PYTAH2NY.js.map
23607
- //# sourceMappingURL=chunk-PYTAH2NY.js.map
23920
+ export { ARKADE_MAGIC, ARKADE_SALTED_PREIMAGE_TAG, ARKADE_SWAP_PREIMAGE_TAG, ASSET_CARRIER_SATS, AbortError, ActivityRegistry, AmbiguousRouteError, ArkNote, ArkadeCash, ArkadeCashCreateError, AssetManager, BIP21, BIP21Error, BIP322, BLOCKED_UPGRADE_TIMEOUT_MS, Batch, CachingArkProvider, ChainedTxType, ConnectionDisposedError, ContractManager, ContractWatcher, DEFAULT_MESSAGE_TIMEOUTS, DEFAULT_MIN_BATCH_EXPIRY_SECONDS, DEFAULT_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, DatabaseUpgradeBlockedError, DelegateManagerImpl, DelegateNotConfiguredError, DescriptorIdentity, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EmulatorPacket, EsploraProvider, Estimator, Extension, ExtensionNotFoundError, ForeignDescriptorError, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MAX_USED_SIGNING_DESCRIPTORS_LOOK_AHEAD, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MUTINYNET_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, PaymentRouter, PrevTxUnavailableError, REGTEST_MIN_BATCH_EXPIRY_SECONDS, REGTEST_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestEmulatorProvider, SIGNET_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnannotatableInputError, UnilateralExit, UnknownPacket, Unroll, VtxoManager, Wallet2 as Wallet, WalletCannotAllocateAddressError, WalletCannotSignError, WalletMessageHandler, WalletNotInitializedError, WsElectrumChainSource, adoptContractDescriptor, arkAssetRail, arkRail, arkTarget, arkade_exports, assertFinalCommitmentMatchesValidated, assertIntentIdUnique, assertNoAssets, assertRecipientArkadeAddress, assertSendableAmount, assertSubmittedArkTxid, assertValidBatchExpiry, assertValidServerUnrollScript, assetMintResolver, assetsOf, attachPrevArkTxs, attachPrevoutTxs, awaitTransaction, boardingResolver, btcTarget, buildForfeitTx, buildOffchainTx, buildPreimageMessage, buildSaltedPreimageMessage, claimWithPreimageIdentity, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, collabExitResolver, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, contractPreimage, contractSigner, createAssetPacket, createDefaultActivityRegistry, createDefaultPaymentRouter, createExitChainResolver, createManagedConnection, decodeArkContract, defaultBatchExpiryPolicy, defaultCheckpointExitDelayPolicy, deleteByIndex, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, exitObserverFor, extendVirtualCoinForContract, extractArkProviderUrl, gateExclusion, gatedContracts, getAllByIndexValues, getRandomId, hasBoardingTxExpired, hasCandidates, intentMatchesFilter, intentPageBounds, invoiceTarget, isAddressAllocationCapable, isArkContract, isBatchSignable, isBtcAddress, isContractGenericallySpendable, isContractVtxoEvent, isCooperativelyMigratable, isDeterministicSigner, isDiscoverable, isHDAllocationCapable, isHDDeterministicSignCapable, isHDWalletCapable, isLightningInvoice, isLnurl, isMessageBusInitializingError, isMessageBusNotInitializedError, isPerArtifactDescriptor, isSigningIdentity, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, isWatchedContract, legacyVtxoFacts, logExcludedVtxos, makeHandle, matchServerCheckpoints, mergeChainedTxType, notifyExitObserved, onchainRail, openDatabase, outpointExclusion, outpointReasons, promisifyRequest, provisionClaimSecret, provisionRefundKey, resolveAssetAmount, resolveBatchExpiryPolicy, resolveCheckpointExitDelayPolicy, resolveDescriptorSigner, resolvePrevTxs, resolveSendAmount, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signAndSubmitOffchainTx, signerSetFromInfo, signingDescriptorIndex, strictSigningDescriptorIndex, toXOnlySignerHex, tryResolveSendAmount, validateBatchRecipients, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, walletFeeSource, warnAndFilterVtxosForScript, withPrevTxs };
23921
+ //# sourceMappingURL=chunk-ISA3HWFN.js.map
23922
+ //# sourceMappingURL=chunk-ISA3HWFN.js.map