@arkade-os/sdk 0.5.0-rc.18 → 0.5.0-rc.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.
Files changed (82) hide show
  1. package/README.md +6 -4
  2. package/dist/adapters/expo.cjs +5 -5
  3. package/dist/adapters/expo.d.cts +2 -2
  4. package/dist/adapters/expo.d.ts +2 -2
  5. package/dist/adapters/expo.js +3 -3
  6. package/dist/{asyncStorageTaskQueue-DLgoD3aS.d.ts → asyncStorageTaskQueue-BnITSqqe.d.ts} +2 -2
  7. package/dist/{asyncStorageTaskQueue-lv8FD7R9.d.cts → asyncStorageTaskQueue-D7ZIJkB3.d.cts} +2 -2
  8. package/dist/{chunk-7MZEGU4A.cjs → chunk-B3RA4BDC.cjs} +15 -15
  9. package/dist/{chunk-7MZEGU4A.cjs.map → chunk-B3RA4BDC.cjs.map} +1 -1
  10. package/dist/{chunk-MXGUCMAI.js → chunk-CAFCIQZU.js} +557 -505
  11. package/dist/chunk-CAFCIQZU.js.map +1 -0
  12. package/dist/{chunk-XITXR3G7.js → chunk-JEYJULVX.js} +3 -3
  13. package/dist/{chunk-XITXR3G7.js.map → chunk-JEYJULVX.js.map} +1 -1
  14. package/dist/{chunk-YGFAZDWJ.cjs → chunk-MKFOQED5.cjs} +18 -18
  15. package/dist/{chunk-YGFAZDWJ.cjs.map → chunk-MKFOQED5.cjs.map} +1 -1
  16. package/dist/{chunk-XAPOUVXI.js → chunk-PKJONINU.js} +9 -4
  17. package/dist/chunk-PKJONINU.js.map +1 -0
  18. package/dist/{chunk-YYUF2ELL.cjs → chunk-RAA6RPMR.cjs} +61 -49
  19. package/dist/chunk-RAA6RPMR.cjs.map +1 -0
  20. package/dist/{chunk-RR4V3TNP.cjs → chunk-SE5VZEJ5.cjs} +1045 -994
  21. package/dist/chunk-SE5VZEJ5.cjs.map +1 -0
  22. package/dist/{chunk-YXUGQ2CZ.js → chunk-ULLDTUVX.js} +17 -5
  23. package/dist/chunk-ULLDTUVX.js.map +1 -0
  24. package/dist/{chunk-NUW7KFOO.cjs → chunk-WTDOR7R3.cjs} +9 -3
  25. package/dist/chunk-WTDOR7R3.cjs.map +1 -0
  26. package/dist/{chunk-ABKH2YS4.js → chunk-XRRXQNYO.js} +7 -5
  27. package/dist/chunk-XRRXQNYO.js.map +1 -0
  28. package/dist/{chunk-CCWD47AA.js → chunk-XW3VC2OC.js} +4 -4
  29. package/dist/{chunk-CCWD47AA.js.map → chunk-XW3VC2OC.js.map} +1 -1
  30. package/dist/{chunk-MJH2IHH3.cjs → chunk-YN2RRK23.cjs} +12 -10
  31. package/dist/chunk-YN2RRK23.cjs.map +1 -0
  32. package/dist/contracts/handlers/index.cjs +9 -9
  33. package/dist/contracts/handlers/index.d.cts +3 -3
  34. package/dist/contracts/handlers/index.d.ts +3 -3
  35. package/dist/contracts/handlers/index.js +2 -2
  36. package/dist/{delegate-CPNT3dMM.d.cts → delegate-D1Nn58Mx.d.cts} +1 -1
  37. package/dist/{delegate-BQoqp1-a.d.ts → delegate-d-wWeBdA.d.ts} +1 -1
  38. package/dist/{index-DfZaRZg2.d.cts → index-B1npkDJD.d.cts} +5 -3
  39. package/dist/{index-BhwDsUdz.d.ts → index-C0BZY_PT.d.ts} +5 -3
  40. package/dist/{index-B0pfUvde.d.ts → index-Ca6ynNRI.d.cts} +19 -4
  41. package/dist/{index-B0pfUvde.d.cts → index-Ca6ynNRI.d.ts} +19 -4
  42. package/dist/index.cjs +305 -305
  43. package/dist/index.d.cts +21 -20
  44. package/dist/index.d.ts +21 -20
  45. package/dist/index.js +4 -4
  46. package/dist/repositories/realm/index.cjs +53 -36
  47. package/dist/repositories/realm/index.cjs.map +1 -1
  48. package/dist/repositories/realm/index.d.cts +4 -2
  49. package/dist/repositories/realm/index.d.ts +4 -2
  50. package/dist/repositories/realm/index.js +25 -8
  51. package/dist/repositories/realm/index.js.map +1 -1
  52. package/dist/repositories/sqlite/index.cjs +40 -46
  53. package/dist/repositories/sqlite/index.cjs.map +1 -1
  54. package/dist/repositories/sqlite/index.d.cts +2 -7
  55. package/dist/repositories/sqlite/index.d.ts +2 -7
  56. package/dist/repositories/sqlite/index.js +13 -19
  57. package/dist/repositories/sqlite/index.js.map +1 -1
  58. package/dist/{taskRunner-BGzmSYTA.d.ts → taskRunner-8iBG7wkd.d.ts} +2 -2
  59. package/dist/{taskRunner-Sg-n5VPh.d.cts → taskRunner-CZZhIp0W.d.cts} +2 -2
  60. package/dist/wallet/expo/background.cjs +14 -14
  61. package/dist/wallet/expo/background.d.cts +3 -3
  62. package/dist/wallet/expo/background.d.ts +3 -3
  63. package/dist/wallet/expo/background.js +6 -6
  64. package/dist/wallet/expo/index.cjs +14 -14
  65. package/dist/wallet/expo/index.d.cts +4 -4
  66. package/dist/wallet/expo/index.d.ts +4 -4
  67. package/dist/wallet/expo/index.js +5 -5
  68. package/dist/{wallet-68AYbFR1.d.ts → wallet-Bl-lE_6e.d.ts} +10 -2
  69. package/dist/{wallet-nNEFe6UK.d.cts → wallet-Kw41pLtW.d.cts} +10 -2
  70. package/dist/worker/expo/index.cjs +9 -9
  71. package/dist/worker/expo/index.d.cts +4 -4
  72. package/dist/worker/expo/index.d.ts +4 -4
  73. package/dist/worker/expo/index.js +5 -5
  74. package/package.json +2 -4
  75. package/dist/chunk-ABKH2YS4.js.map +0 -1
  76. package/dist/chunk-MJH2IHH3.cjs.map +0 -1
  77. package/dist/chunk-MXGUCMAI.js.map +0 -1
  78. package/dist/chunk-NUW7KFOO.cjs.map +0 -1
  79. package/dist/chunk-RR4V3TNP.cjs.map +0 -1
  80. package/dist/chunk-XAPOUVXI.js.map +0 -1
  81. package/dist/chunk-YXUGQ2CZ.js.map +0 -1
  82. package/dist/chunk-YYUF2ELL.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
- import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, BufferReader, BufferWriter, AssetRef, RestIndexerProvider } from './chunk-XITXR3G7.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-YXUGQ2CZ.js';
3
- import { VtxoScript, __export, Transaction, requiresForfeit, CSVMultisigTapscript, networks, getNetwork, DEFAULT_NETWORK, resolveEmulatorPubkey, isVtxoSpent, getNormalizedVtxos, setArkPsbtField, PrevArkTxField, ConditionWitness, craftToSpendTx, ArkAddress, isVirtualCoin, getArkPsbtFields, CosignerPublicKey, ServerResponseMismatchError, OP_RETURN_EMPTY_PKSCRIPT, canSweepOnchain, sequenceToTimelock, timelockToSequence, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, VtxoTaprootTree, assertAllowedSighashTypes, baseFetch, Intent, rateGate, DEFAULT_NETWORK_NAME, formatSighash, normalizeVtxo, getSequence, maybeArkError, isArkError, ArkErrorName, toOffchainInputFeeParams, canRecoverOnchain, MultisigTapscript, isEventSourceError, isEventSourceUnavailableError, getAllNormalizedVtxos, isPastExpiry, isRetryableProviderError, DEFAULT_PAGE_SIZE, RestArkProvider, DEFAULT_ARKADE_SERVER_URL, networkFromArkadeInfo, ArkError, toBIP371TapTree, resolveTimeHeight, canSpendOffchain, INFO_FETCH_TIMEOUT_MS, PrevoutTxField, fetchVtxoCreatedAtByTxid, EXPIRY_MIN_PLAUSIBLE_MS, ProviderUnavailableError } from './chunk-XAPOUVXI.js';
1
+ import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, BufferReader, BufferWriter, AssetRef, RestIndexerProvider } from './chunk-JEYJULVX.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-ULLDTUVX.js';
3
+ import { VtxoScript, __export, Transaction, requiresForfeit, CSVMultisigTapscript, networks, getNetwork, DEFAULT_NETWORK, resolveEmulatorPubkey, isVtxoSpent, getNormalizedVtxos, setArkPsbtField, PrevArkTxField, ConditionWitness, craftToSpendTx, ArkAddress, isVirtualCoin, getArkPsbtFields, CosignerPublicKey, ServerResponseMismatchError, OP_RETURN_EMPTY_PKSCRIPT, canSweepOnchain, sequenceToTimelock, sequenceToCanonicalTimelock, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, VtxoTaprootTree, timelockToSequence, assertAllowedSighashTypes, baseFetch, Intent, rateGate, DEFAULT_NETWORK_NAME, formatSighash, normalizeVtxo, getSequence, maybeArkError, isArkError, ArkErrorName, toOffchainInputFeeParams, canRecoverOnchain, MultisigTapscript, isEventSourceError, isEventSourceUnavailableError, getAllNormalizedVtxos, isPastExpiry, isRetryableProviderError, DEFAULT_PAGE_SIZE, RestArkProvider, DEFAULT_ARKADE_SERVER_URL, networkFromArkadeInfo, ArkError, toBIP371TapTree, resolveTimeHeight, canSpendOffchain, INFO_FETCH_TIMEOUT_MS, PrevoutTxField, fetchVtxoCreatedAtByTxid, EXPIRY_MIN_PLAUSIBLE_MS, ProviderUnavailableError } from './chunk-PKJONINU.js';
4
4
  import { sha256, 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$1 } from '@scure/btc-signer';
6
6
  import { hex, base58, base64, bech32m } from '@scure/base';
@@ -1954,15 +1954,10 @@ function findP2AOutput(tx) {
1954
1954
  for (let i = 0; i < tx.outputsLength; i++) {
1955
1955
  const output = tx.getOutput(i);
1956
1956
  if (output.script && hex.encode(output.script) === hexP2Ascript) {
1957
- if (output.amount !== P2A.amount) {
1958
- throw new Error(
1959
- `P2A output has wrong amount, expected ${P2A.amount} got ${output.amount}`
1960
- );
1961
- }
1962
1957
  return {
1963
1958
  txid: tx.id,
1964
1959
  index: i,
1965
- witnessUtxo: P2A
1960
+ witnessUtxo: { script: P2A.script, amount: output.amount ?? 0n }
1966
1961
  };
1967
1962
  }
1968
1963
  }
@@ -1972,7 +1967,8 @@ var CHILD_DUST_AMOUNT = 546;
1972
1967
  function buildAnchorChild(params) {
1973
1968
  const { parent, feeRate, fundingCoins, changeAddress, tapInternalKey, network } = params;
1974
1969
  const child = new Transaction({ version: 3, allowLegacyWitnessUtxo: true });
1975
- child.addInput(findP2AOutput(parent));
1970
+ const anchor = findP2AOutput(parent);
1971
+ child.addInput(anchor);
1976
1972
  const estimator = TxWeightEstimator.create().addP2AInput();
1977
1973
  for (const _ of fundingCoins) {
1978
1974
  estimator.addKeySpendInput(true);
@@ -1990,7 +1986,7 @@ function buildAnchorChild(params) {
1990
1986
  tapInternalKey
1991
1987
  });
1992
1988
  }
1993
- const change = total + P2A.amount - BigInt(fee);
1989
+ const change = total + anchor.witnessUtxo.amount - BigInt(fee);
1994
1990
  if (change < BigInt(CHILD_DUST_AMOUNT)) {
1995
1991
  throw new Error(
1996
1992
  `insufficient funding for anchor child: need change >= ${CHILD_DUST_AMOUNT}, got ${change}`
@@ -3094,7 +3090,9 @@ function buildVirtualTx(inputs, outputs) {
3094
3090
  for (const output of outputs) {
3095
3091
  tx.addOutput(output);
3096
3092
  }
3097
- tx.addOutput(P2A);
3093
+ const fee = inputs.reduce((sum, input) => sum + BigInt(input.value), 0n) - outputs.reduce((sum, output) => sum + (output.amount ?? 0n), 0n);
3094
+ if (fee < 0n) throw new Error("outputs exceed inputs");
3095
+ tx.addOutput({ ...P2A, amount: fee });
3098
3096
  return tx;
3099
3097
  }
3100
3098
  function buildCheckpointTx(vtxo, serverUnrollScript) {
@@ -6007,8 +6005,8 @@ async function clearSyncCursor(repo) {
6007
6005
  };
6008
6006
  });
6009
6007
  }
6010
- function computeSyncWindow(cursor) {
6011
- const after = Math.max(0, cursor - OVERLAP_MS);
6008
+ function computeSyncWindow(cursor, overlapMs = OVERLAP_MS) {
6009
+ const after = Math.max(0, cursor - overlapMs);
6012
6010
  return { after };
6013
6011
  }
6014
6012
  function cursorCutoff(requestStartedAt) {
@@ -7619,215 +7617,13 @@ function createManagedConnection(dbName, version, initDatabase2) {
7619
7617
  };
7620
7618
  }
7621
7619
 
7622
- // src/repositories/legacyVtxoFacts.ts
7623
- function parseLegacyExpiry(batchExpiry) {
7624
- if (typeof batchExpiry !== "number" || !Number.isFinite(batchExpiry) || batchExpiry <= 0) {
7625
- return {};
7626
- }
7627
- if (batchExpiry >= EXPIRY_MIN_PLAUSIBLE_MS) return { expiresAt: new Date(batchExpiry) };
7628
- return { expiresAtHeight: batchExpiry / 1e3 };
7629
- }
7630
- function legacyVtxoFacts(status) {
7631
- let blob = status;
7632
- if (typeof blob === "string") {
7633
- try {
7634
- blob = JSON.parse(blob);
7635
- } catch {
7636
- return void 0;
7637
- }
7638
- }
7639
- if (typeof blob !== "object" || blob === null) return void 0;
7640
- const { state, commitmentTxIds, batchExpiry } = blob;
7641
- if (state === void 0 && commitmentTxIds === void 0 && batchExpiry === void 0) {
7642
- return void 0;
7643
- }
7644
- return {
7645
- isSpent: state === "spent",
7646
- isSwept: state === "swept",
7647
- isPreconfirmed: state === "preconfirmed",
7648
- commitmentTxIds: Array.isArray(commitmentTxIds) ? commitmentTxIds : void 0,
7649
- ...parseLegacyExpiry(batchExpiry)
7650
- };
7651
- }
7652
- function legacyFactsOfRow(row) {
7653
- if (row.isSwept !== void 0 || row.isPreconfirmed !== void 0) return void 0;
7654
- return legacyVtxoFacts(row.virtualStatus);
7655
- }
7656
-
7657
- // src/repositories/indexedDB/schema.ts
7658
- var STORE_VTXOS = "vtxos";
7659
- var STORE_UTXOS = "utxos";
7660
- var STORE_TRANSACTIONS = "transactions";
7661
- var STORE_WALLET_STATE = "walletState";
7662
- var STORE_CONTRACTS = "contracts";
7620
+ // src/repositories/indexedDB/batchesSchema.ts
7621
+ var BATCHES_DB_NAME = "arkade-batches";
7622
+ var BATCHES_DB_VERSION = 1;
7663
7623
  var STORE_INTENTS = "intents";
7664
7624
  var STORE_VIRTUAL_TXS = "virtualTxs";
7665
7625
  var STORE_VTXO_BRANCHES = "vtxoBranches";
7666
- var DB_VERSION = 5;
7667
- var INTENT_DB_VERSION = 5;
7668
- function initDatabase(db, oldVersion, transaction) {
7669
- if (!db.objectStoreNames.contains(STORE_VTXOS)) {
7670
- const vtxosStore = db.createObjectStore(STORE_VTXOS, {
7671
- keyPath: ["address", "txid", "vout"]
7672
- });
7673
- if (!vtxosStore.indexNames.contains("address")) {
7674
- vtxosStore.createIndex("address", "address", {
7675
- unique: false
7676
- });
7677
- }
7678
- if (!vtxosStore.indexNames.contains("txid")) {
7679
- vtxosStore.createIndex("txid", "txid", { unique: false });
7680
- }
7681
- if (!vtxosStore.indexNames.contains("value")) {
7682
- vtxosStore.createIndex("value", "value", { unique: false });
7683
- }
7684
- if (!vtxosStore.indexNames.contains("status")) {
7685
- vtxosStore.createIndex("status", "status", {
7686
- unique: false
7687
- });
7688
- }
7689
- if (!vtxosStore.indexNames.contains("createdAt")) {
7690
- vtxosStore.createIndex("createdAt", "createdAt", {
7691
- unique: false
7692
- });
7693
- }
7694
- if (!vtxosStore.indexNames.contains("isSpent")) {
7695
- vtxosStore.createIndex("isSpent", "isSpent", {
7696
- unique: false
7697
- });
7698
- }
7699
- if (!vtxosStore.indexNames.contains("isUnrolled")) {
7700
- vtxosStore.createIndex("isUnrolled", "isUnrolled", {
7701
- unique: false
7702
- });
7703
- }
7704
- if (!vtxosStore.indexNames.contains("spentBy")) {
7705
- vtxosStore.createIndex("spentBy", "spentBy", {
7706
- unique: false
7707
- });
7708
- }
7709
- if (!vtxosStore.indexNames.contains("settledBy")) {
7710
- vtxosStore.createIndex("settledBy", "settledBy", {
7711
- unique: false
7712
- });
7713
- }
7714
- if (!vtxosStore.indexNames.contains("arkTxId")) {
7715
- vtxosStore.createIndex("arkTxId", "arkTxId", {
7716
- unique: false
7717
- });
7718
- }
7719
- if (!vtxosStore.indexNames.contains("script")) {
7720
- vtxosStore.createIndex("script", "script", {
7721
- unique: false
7722
- });
7723
- }
7724
- if (!vtxosStore.indexNames.contains("scriptUnspent")) {
7725
- vtxosStore.createIndex("scriptUnspent", ["script", "unspent"], { unique: false });
7726
- }
7727
- }
7728
- if (!db.objectStoreNames.contains(STORE_UTXOS)) {
7729
- const utxosStore = db.createObjectStore(STORE_UTXOS, {
7730
- keyPath: ["address", "txid", "vout"]
7731
- });
7732
- if (!utxosStore.indexNames.contains("address")) {
7733
- utxosStore.createIndex("address", "address", {
7734
- unique: false
7735
- });
7736
- }
7737
- if (!utxosStore.indexNames.contains("txid")) {
7738
- utxosStore.createIndex("txid", "txid", { unique: false });
7739
- }
7740
- if (!utxosStore.indexNames.contains("value")) {
7741
- utxosStore.createIndex("value", "value", { unique: false });
7742
- }
7743
- if (!utxosStore.indexNames.contains("status")) {
7744
- utxosStore.createIndex("status", "status", {
7745
- unique: false
7746
- });
7747
- }
7748
- }
7749
- if (!db.objectStoreNames.contains(STORE_TRANSACTIONS)) {
7750
- const transactionsStore = db.createObjectStore(STORE_TRANSACTIONS, {
7751
- keyPath: ["address", "keyBoardingTxid", "keyCommitmentTxid", "keyArkTxid"]
7752
- });
7753
- if (!transactionsStore.indexNames.contains("address")) {
7754
- transactionsStore.createIndex("address", "address", {
7755
- unique: false
7756
- });
7757
- }
7758
- if (!transactionsStore.indexNames.contains("type")) {
7759
- transactionsStore.createIndex("type", "type", {
7760
- unique: false
7761
- });
7762
- }
7763
- if (!transactionsStore.indexNames.contains("amount")) {
7764
- transactionsStore.createIndex("amount", "amount", {
7765
- unique: false
7766
- });
7767
- }
7768
- if (!transactionsStore.indexNames.contains("settled")) {
7769
- transactionsStore.createIndex("settled", "settled", {
7770
- unique: false
7771
- });
7772
- }
7773
- if (!transactionsStore.indexNames.contains("createdAt")) {
7774
- transactionsStore.createIndex("createdAt", "createdAt", {
7775
- unique: false
7776
- });
7777
- }
7778
- if (!transactionsStore.indexNames.contains("addressCreatedAt")) {
7779
- transactionsStore.createIndex("addressCreatedAt", ["address", "createdAt"], {
7780
- unique: false
7781
- });
7782
- }
7783
- if (!transactionsStore.indexNames.contains("arkTxid")) {
7784
- transactionsStore.createIndex("arkTxid", "key.arkTxid", {
7785
- unique: false
7786
- });
7787
- }
7788
- }
7789
- if (!db.objectStoreNames.contains(STORE_WALLET_STATE)) {
7790
- db.createObjectStore(STORE_WALLET_STATE, {
7791
- keyPath: "key"
7792
- });
7793
- }
7794
- if (!db.objectStoreNames.contains(STORE_CONTRACTS)) {
7795
- const contractsStore = db.createObjectStore(STORE_CONTRACTS, {
7796
- keyPath: "script"
7797
- });
7798
- if (!contractsStore.indexNames.contains("type")) {
7799
- contractsStore.createIndex("type", "type", {
7800
- unique: false
7801
- });
7802
- }
7803
- if (!contractsStore.indexNames.contains("state")) {
7804
- contractsStore.createIndex("state", "state", {
7805
- unique: false
7806
- });
7807
- }
7808
- }
7809
- if (oldVersion >= 1 && oldVersion < 4 && transaction) {
7810
- const vtxosStore = transaction.objectStore(STORE_VTXOS);
7811
- if (!vtxosStore.indexNames.contains("script")) {
7812
- vtxosStore.createIndex("script", "script", { unique: false });
7813
- }
7814
- if (!vtxosStore.indexNames.contains("scriptUnspent")) {
7815
- vtxosStore.createIndex("scriptUnspent", ["script", "unspent"], { unique: false });
7816
- }
7817
- backfillVtxoIndexFields(transaction);
7818
- }
7819
- if (oldVersion > 0 && transaction) {
7820
- const transactionsStore = transaction.objectStore(STORE_TRANSACTIONS);
7821
- if (!transactionsStore.indexNames.contains("addressCreatedAt")) {
7822
- transactionsStore.createIndex("addressCreatedAt", ["address", "createdAt"], {
7823
- unique: false
7824
- });
7825
- }
7826
- }
7827
- }
7828
- var unspentFlag = (vtxo) => isVtxoSpent(vtxo) ? {} : { unspent: 1 };
7829
- function initDatabaseWithIntents(db, oldVersion, transaction) {
7830
- initDatabase(db, oldVersion, transaction);
7626
+ function initBatchesDatabase(db, _oldVersion, _transaction) {
7831
7627
  if (!db.objectStoreNames.contains(STORE_INTENTS)) {
7832
7628
  const intentsStore = db.createObjectStore(STORE_INTENTS, {
7833
7629
  keyPath: "intentTxId"
@@ -7845,116 +7641,14 @@ function initDatabaseWithIntents(db, oldVersion, transaction) {
7845
7641
  branchesStore.createIndex("vtxo", ["vtxoTxid", "vtxoVout"], { unique: false });
7846
7642
  branchesStore.createIndex("virtualTxid", "virtualTxid", { unique: false });
7847
7643
  }
7848
- if (oldVersion === 4 && transaction) {
7849
- const intentsStore = transaction.objectStore(STORE_INTENTS);
7850
- if (intentsStore.indexNames.contains("intentId")) {
7851
- intentsStore.deleteIndex("intentId");
7852
- }
7853
- dedupeIntentIds(intentsStore, () => {
7854
- intentsStore.createIndex("intentId", "intentId", { unique: true });
7855
- });
7856
- }
7857
- }
7858
- function dedupeIntentIds(store, onComplete) {
7859
- const seen = /* @__PURE__ */ new Set();
7860
- const cursorRequest = store.openCursor();
7861
- cursorRequest.onsuccess = () => {
7862
- const cursor = cursorRequest.result;
7863
- if (!cursor) {
7864
- onComplete();
7865
- return;
7866
- }
7867
- const id = cursor.value.intentId;
7868
- if (id != null) {
7869
- if (seen.has(id)) {
7870
- cursor.delete();
7871
- } else {
7872
- seen.add(id);
7873
- }
7874
- }
7875
- cursor.continue();
7876
- };
7877
- }
7878
- function backfillVtxoIndexFields(transaction) {
7879
- const store = transaction.objectStore(STORE_VTXOS);
7880
- const pageSize = 1e3;
7881
- const readPage = (after) => {
7882
- const request = store.getAll(after && IDBKeyRange.lowerBound(after, true), pageSize);
7883
- request.onsuccess = () => {
7884
- const rows = request.result;
7885
- for (const value of rows) {
7886
- const legacy = legacyFactsOfRow(value);
7887
- const next = {
7888
- ...value,
7889
- script: value.script || scriptFromArkAddress(value.address),
7890
- ...unspentFlag({ ...value, isSpent: value.isSpent ?? legacy?.isSpent })
7891
- };
7892
- if (next.script !== value.script || next.unspent !== value.unspent) {
7893
- store.put(next);
7894
- }
7895
- }
7896
- const last = rows.at(-1);
7897
- if (rows.length === pageSize && last) readPage([last.address, last.txid, last.vout]);
7898
- };
7899
- };
7900
- readPage();
7901
- }
7902
-
7903
- // src/worker/browser/utils.ts
7904
- var DEFAULT_DB_NAME = "arkade-service-worker";
7905
- var DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS = 1e4;
7906
- function normalizeOptions2(pathOrOptions) {
7907
- const o = typeof pathOrOptions === "string" ? { path: pathOrOptions } : pathOrOptions;
7908
- return {
7909
- path: o.path,
7910
- activationTimeoutMs: o.activationTimeoutMs ?? DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS
7911
- };
7912
- }
7913
- function waitForServiceWorkerReady(timeoutMs) {
7914
- return new Promise((resolve, reject) => {
7915
- const timeoutId = setTimeout(() => {
7916
- reject(new Error(`Service worker activation timed out after ${timeoutMs}ms`));
7917
- }, timeoutMs);
7918
- navigator.serviceWorker.ready.then((registration) => {
7919
- clearTimeout(timeoutId);
7920
- resolve(registration);
7921
- }).catch((error) => {
7922
- clearTimeout(timeoutId);
7923
- reject(error);
7924
- });
7925
- });
7926
- }
7927
- async function setupServiceWorker(pathOrOptions) {
7928
- if (!("serviceWorker" in navigator)) {
7929
- throw new Error("Service workers are not supported in this browser");
7930
- }
7931
- const { path, activationTimeoutMs } = normalizeOptions2(pathOrOptions);
7932
- const registration = await navigator.serviceWorker.register(path);
7933
- await registration.update();
7934
- const serviceWorker = registration.active || registration.waiting || registration.installing;
7935
- if (!serviceWorker) {
7936
- throw new Error("Failed to get service worker instance");
7937
- }
7938
- if (serviceWorker.state === "activated") {
7939
- return serviceWorker;
7940
- }
7941
- const readyRegistration = await waitForServiceWorkerReady(activationTimeoutMs);
7942
- if (!readyRegistration.active) {
7943
- throw new Error("Service worker registration is ready but has no active worker");
7944
- }
7945
- return readyRegistration.active;
7946
7644
  }
7947
7645
 
7948
7646
  // src/repositories/indexedDB/virtualTxRepository.ts
7949
7647
  var IndexedDBVirtualTxRepository = class {
7950
7648
  version = 1;
7951
7649
  connection;
7952
- constructor(dbName = DEFAULT_DB_NAME) {
7953
- this.connection = createManagedConnection(
7954
- dbName,
7955
- INTENT_DB_VERSION,
7956
- initDatabaseWithIntents
7957
- );
7650
+ constructor(dbName = BATCHES_DB_NAME) {
7651
+ this.connection = createManagedConnection(dbName, BATCHES_DB_VERSION, initBatchesDatabase);
7958
7652
  }
7959
7653
  getDB() {
7960
7654
  return this.connection.get();
@@ -8078,12 +7772,8 @@ var IndexedDBVirtualTxRepository = class {
8078
7772
  var IndexedDBIntentRepository = class {
8079
7773
  version = 1;
8080
7774
  connection;
8081
- constructor(dbName = DEFAULT_DB_NAME) {
8082
- this.connection = createManagedConnection(
8083
- dbName,
8084
- INTENT_DB_VERSION,
8085
- initDatabaseWithIntents
8086
- );
7775
+ constructor(dbName = BATCHES_DB_NAME) {
7776
+ this.connection = createManagedConnection(dbName, BATCHES_DB_VERSION, initBatchesDatabase);
8087
7777
  }
8088
7778
  getDB() {
8089
7779
  return this.connection.get();
@@ -8537,7 +8227,8 @@ var ContractWatcher = class {
8537
8227
  async updateContract(contract) {
8538
8228
  const existing = this.contracts.get(contract.script);
8539
8229
  if (!existing) {
8540
- throw new Error(`Contract ${contract.script} not found`);
8230
+ if (isWatchedContract(contract)) await this.addContract(contract);
8231
+ return;
8541
8232
  }
8542
8233
  existing.contract = contract;
8543
8234
  if (this.isWatching) {
@@ -8563,7 +8254,7 @@ var ContractWatcher = class {
8563
8254
  * Feeds both the subscription and the sweep scope. `state` must never narrow it: an Ark
8564
8255
  * receive address can be paid again after the wallet rotated past it, and such a payment
8565
8256
  * would stay invisible. Only an owner's explicit {@link ContractWatchState} narrows it; the
8566
- * row stays in {@link getAllContracts} for reads, annotation and history.
8257
+ * row stays in the repository for reads, annotation and history.
8567
8258
  */
8568
8259
  getWatchedContracts() {
8569
8260
  return this.getAllContracts().filter(isWatchedContract);
@@ -8725,14 +8416,14 @@ var ContractWatcher = class {
8725
8416
  * Handle "this environment has no `EventSource`": warn once and return true so the caller
8726
8417
  * skips reconnecting. A missing global is not a dropped connection; unlimited backoff would
8727
8418
  * retry forever and fire a `connection_reset` (read as "resync, stream coming back") every
8728
- * few seconds. Failsafe polling keeps the watcher correct, just slower.
8419
+ * few seconds.
8729
8420
  */
8730
8421
  reportEventSourceUnavailable(error) {
8731
8422
  if (!isEventSourceUnavailableError(error)) return false;
8732
8423
  if (!this.eventSourceReported) {
8733
8424
  this.eventSourceReported = true;
8734
8425
  console.warn(
8735
- `ContractWatcher: contract events are OFF and will not be retried \u2014 falling back to polling every ${this.config.failsafePollIntervalMs}ms. ` + error.message
8426
+ `ContractWatcher: contract events are OFF and will not be retried \u2014 the wallet's own contracts update only when it syncs (reads, refreshVtxos()); watch-only scripts are still polled. ` + error.message
8736
8427
  );
8737
8428
  }
8738
8429
  return true;
@@ -8757,6 +8448,7 @@ var ContractWatcher = class {
8757
8448
  this.connect();
8758
8449
  }, delay);
8759
8450
  }
8451
+ /** Replays repository state — only what a sync stored; watch-only scripts poll the indexer. */
8760
8452
  startFailsafePolling() {
8761
8453
  if (this.failsafePollIntervalId) {
8762
8454
  clearInterval(this.failsafePollIntervalId);
@@ -9241,6 +8933,11 @@ var SCAN_MAX_INDEX = 1e4;
9241
8933
  var DEFAULT_SCAN_BATCH = 10;
9242
8934
  var CHAIN_TIP_TTL_MS = 3e4;
9243
8935
  var CHAIN_TIP_TIMEOUT_MS = 5e3;
8936
+ function assertNonNegativeMs(name, ms) {
8937
+ if (ms !== void 0 && (!Number.isSafeInteger(ms) || ms < 0)) {
8938
+ throw new Error(`${name} must be a non-negative safe integer`);
8939
+ }
8940
+ }
9244
8941
  var ContractManager = class _ContractManager {
9245
8942
  config;
9246
8943
  watcher;
@@ -9271,7 +8968,9 @@ var ContractManager = class _ContractManager {
9271
8968
  * `dispose()`, so it re-checks this at every await rather than run on a torn-down watcher. */
9272
8969
  disposed = false;
9273
8970
  constructor(config) {
9274
- this.config = config;
8971
+ assertNonNegativeMs("vtxoSyncMaxAgeMs", config.vtxoSyncMaxAgeMs);
8972
+ assertNonNegativeMs("vtxoSyncOverlapMs", config.vtxoSyncOverlapMs);
8973
+ this.config = { ...config };
9275
8974
  this.watcher = new ContractWatcher({
9276
8975
  indexerProvider: config.indexerProvider,
9277
8976
  walletRepository: config.walletRepository,
@@ -9300,8 +8999,14 @@ var ContractManager = class _ContractManager {
9300
8999
  }
9301
9000
  /** @see ContractManagerConfig.vtxoSyncMaxAgeMs — for factory-built managers. */
9302
9001
  setVtxoSyncMaxAge(maxAgeMs) {
9002
+ assertNonNegativeMs("vtxoSyncMaxAgeMs", maxAgeMs);
9303
9003
  this.config.vtxoSyncMaxAgeMs = maxAgeMs;
9304
9004
  }
9005
+ /** @see ContractManagerConfig.vtxoSyncOverlapMs — for factory-built managers. */
9006
+ setVtxoSyncOverlap(overlapMs) {
9007
+ assertNonNegativeMs("vtxoSyncOverlapMs", overlapMs);
9008
+ this.config.vtxoSyncOverlapMs = overlapMs;
9009
+ }
9305
9010
  markSyncOnline() {
9306
9011
  this.lastSyncedAt = Date.now();
9307
9012
  this.syncDegradedReason = this.syncGapReason();
@@ -9355,7 +9060,9 @@ var ContractManager = class _ContractManager {
9355
9060
  return;
9356
9061
  }
9357
9062
  this.disposed = false;
9358
- const contracts = await collectContracts(this.config.contractRepository);
9063
+ const contracts = await collectContracts(this.config.contractRepository, {
9064
+ watch: ["watched", "awaiting-funds"]
9065
+ });
9359
9066
  for (const contract of contracts) {
9360
9067
  await this.watcher.addContract(contract);
9361
9068
  }
@@ -9826,9 +9533,7 @@ var ContractManager = class _ContractManager {
9826
9533
  return await collectContracts(this.config.contractRepository, dbFilter);
9827
9534
  }
9828
9535
  async getContractsWithVtxos(filter, pageSize, options) {
9829
- if (options?.maxSyncAgeMs !== void 0 && (!Number.isSafeInteger(options.maxSyncAgeMs) || options.maxSyncAgeMs < 0)) {
9830
- throw new Error("maxSyncAgeMs must be a non-negative safe integer");
9831
- }
9536
+ assertNonNegativeMs("maxSyncAgeMs", options?.maxSyncAgeMs);
9832
9537
  const contracts = await this.getContracts(filter);
9833
9538
  if (this.syncedWithin(
9834
9539
  contracts,
@@ -10221,7 +9926,7 @@ var ContractManager = class _ContractManager {
10221
9926
  /** Sync virtual outputs for the given contracts (default: the watched set). */
10222
9927
  async syncContracts(options) {
10223
9928
  const cursor = await getSyncCursor(this.config.walletRepository);
10224
- const window2 = options.window ?? computeSyncWindow(cursor);
9929
+ const window2 = options.window ?? computeSyncWindow(cursor, this.config.vtxoSyncOverlapMs);
10225
9930
  const mustUpdateCursor = options.contracts === void 0 && options.window === void 0 && (window2.after ?? 0) <= cursor;
10226
9931
  const contracts = options.contracts ?? (options.includeInactive ? await collectContracts(this.config.contractRepository, {}) : this.watcher.getWatchedContracts());
10227
9932
  const requestStartedAt = Date.now();
@@ -10345,105 +10050,419 @@ var ContractManager = class _ContractManager {
10345
10050
  );
10346
10051
  }
10347
10052
  }
10348
- return result;
10349
- }
10350
- async fetchContractVtxosBulk(contracts, pageSize = DEFAULT_PAGE_SIZE, syncWindow) {
10351
- if (contracts.length === 0) {
10352
- return { vtxosByScript: /* @__PURE__ */ new Map(), promoted: /* @__PURE__ */ new Map() };
10053
+ return result;
10054
+ }
10055
+ async fetchContractVtxosBulk(contracts, pageSize = DEFAULT_PAGE_SIZE, syncWindow) {
10056
+ if (contracts.length === 0) {
10057
+ return { vtxosByScript: /* @__PURE__ */ new Map(), promoted: /* @__PURE__ */ new Map() };
10058
+ }
10059
+ const scriptToContract = new Map(contracts.map((c) => [c.script, c]));
10060
+ const result = new Map(
10061
+ contracts.map((c) => [c.script, []])
10062
+ );
10063
+ const windowOpts = syncWindow ? {
10064
+ ...syncWindow.after !== void 0 && {
10065
+ after: syncWindow.after
10066
+ },
10067
+ ...syncWindow.before !== void 0 && {
10068
+ before: syncWindow.before
10069
+ }
10070
+ } : {};
10071
+ const vtxos = await getAllNormalizedVtxos(
10072
+ this.config.indexerProvider,
10073
+ contracts.map((c) => c.script),
10074
+ { ...windowOpts, pageSize }
10075
+ );
10076
+ const promoted = await this.promoteLookAheadHits(vtxos);
10077
+ for (const [script, contract] of promoted) {
10078
+ scriptToContract.set(script, contract);
10079
+ }
10080
+ const {
10081
+ scripts: annotatable,
10082
+ cache,
10083
+ failures
10084
+ } = annotatableIn(scriptToContract, vtxos, this.tapscriptMemo);
10085
+ this.recordAnnotationFailures(annotatable, failures);
10086
+ const owned = vtxos.filter((v) => annotatable.has(v.script));
10087
+ const annotated = await this.annotateVtxos(owned, cache);
10088
+ for (const vtxo of annotated) {
10089
+ result.get(vtxo.script).push({
10090
+ ...vtxo,
10091
+ contractScript: vtxo.script
10092
+ });
10093
+ }
10094
+ return { vtxosByScript: result, promoted };
10095
+ }
10096
+ /** Stop the watcher, clear callbacks, and mark the manager uninitialized. */
10097
+ dispose() {
10098
+ this.disposed = true;
10099
+ const pendingDrain = this.lookAheadDrain;
10100
+ this.stopWatcherFn?.();
10101
+ this.stopWatcherFn = void 0;
10102
+ this.eventCallbacks.clear();
10103
+ this.lookAheadEntries.clear();
10104
+ if (pendingDrain) {
10105
+ void pendingDrain.catch(() => {
10106
+ }).then(() => {
10107
+ if (this.disposed) this.lookAheadEntries.clear();
10108
+ });
10109
+ }
10110
+ this.initialized = false;
10111
+ }
10112
+ /**
10113
+ * Symbol.dispose implementation for the `using` keyword.
10114
+ * @example
10115
+ * ```typescript
10116
+ * {
10117
+ * using manager = await wallet.getContractManager();
10118
+ * // ... use manager
10119
+ * } // automatically disposed
10120
+ * ```
10121
+ */
10122
+ [Symbol.dispose]() {
10123
+ this.dispose();
10124
+ }
10125
+ };
10126
+
10127
+ // src/repositories/inMemory/contractRepository.ts
10128
+ var InMemoryContractRepository = class {
10129
+ version = 3;
10130
+ contractsByScript = /* @__PURE__ */ new Map();
10131
+ async clear() {
10132
+ this.contractsByScript.clear();
10133
+ }
10134
+ async getContractsPage(filter, page) {
10135
+ assertPageRequest(page);
10136
+ const matches = (value, criterion) => criterion === void 0 || (Array.isArray(criterion) ? criterion.includes(value) : value === criterion);
10137
+ const rows = [...this.contractsByScript.values()].filter((contract) => page.after === void 0 || contract.script > page.after).filter(
10138
+ (contract) => !filter || matches(contract.script, filter.script) && matches(contract.state, filter.state) && matches(contract.type, filter.type) && matches(watchStateOf(contract), filter.watch)
10139
+ ).sort((a, b) => a.script < b.script ? -1 : a.script > b.script ? 1 : 0).slice(0, page.limit + 1);
10140
+ return pageResult(rows, page.limit, (contract) => contract.script);
10141
+ }
10142
+ async saveContract(contract) {
10143
+ this.contractsByScript.set(contract.script, contract);
10144
+ }
10145
+ async deleteContract(script) {
10146
+ this.contractsByScript.delete(script);
10147
+ }
10148
+ async [Symbol.asyncDispose]() {
10149
+ return;
10150
+ }
10151
+ };
10152
+
10153
+ // src/repositories/legacyVtxoFacts.ts
10154
+ function parseLegacyExpiry(batchExpiry) {
10155
+ if (typeof batchExpiry !== "number" || !Number.isFinite(batchExpiry) || batchExpiry <= 0) {
10156
+ return {};
10157
+ }
10158
+ if (batchExpiry >= EXPIRY_MIN_PLAUSIBLE_MS) return { expiresAt: new Date(batchExpiry) };
10159
+ return { expiresAtHeight: batchExpiry / 1e3 };
10160
+ }
10161
+ function legacyVtxoFacts(status) {
10162
+ let blob = status;
10163
+ if (typeof blob === "string") {
10164
+ try {
10165
+ blob = JSON.parse(blob);
10166
+ } catch {
10167
+ return void 0;
10168
+ }
10169
+ }
10170
+ if (typeof blob !== "object" || blob === null) return void 0;
10171
+ const { state, commitmentTxIds, batchExpiry } = blob;
10172
+ if (state === void 0 && commitmentTxIds === void 0 && batchExpiry === void 0) {
10173
+ return void 0;
10174
+ }
10175
+ return {
10176
+ isSpent: state === "spent",
10177
+ isSwept: state === "swept",
10178
+ isPreconfirmed: state === "preconfirmed",
10179
+ commitmentTxIds: Array.isArray(commitmentTxIds) ? commitmentTxIds : void 0,
10180
+ ...parseLegacyExpiry(batchExpiry)
10181
+ };
10182
+ }
10183
+ function legacyFactsOfRow(row) {
10184
+ if (row.isSwept !== void 0 || row.isPreconfirmed !== void 0) return void 0;
10185
+ return legacyVtxoFacts(row.virtualStatus);
10186
+ }
10187
+
10188
+ // src/repositories/indexedDB/schema.ts
10189
+ var STORE_VTXOS = "vtxos";
10190
+ var STORE_UTXOS = "utxos";
10191
+ var STORE_TRANSACTIONS = "transactions";
10192
+ var STORE_WALLET_STATE = "walletState";
10193
+ var STORE_CONTRACTS = "contracts";
10194
+ var DB_VERSION = 6;
10195
+ var CONTRACT_WATCH_INDEX = "watchState";
10196
+ var CONTRACT_TYPE_WATCH_INDEX = "typeWatchState";
10197
+ var contractRow = (contract) => ({
10198
+ ...contract,
10199
+ watchState: watchStateOf(contract)
10200
+ });
10201
+ function initDatabase(db, oldVersion, transaction) {
10202
+ if (!db.objectStoreNames.contains(STORE_VTXOS)) {
10203
+ const vtxosStore = db.createObjectStore(STORE_VTXOS, {
10204
+ keyPath: ["address", "txid", "vout"]
10205
+ });
10206
+ if (!vtxosStore.indexNames.contains("address")) {
10207
+ vtxosStore.createIndex("address", "address", {
10208
+ unique: false
10209
+ });
10210
+ }
10211
+ if (!vtxosStore.indexNames.contains("txid")) {
10212
+ vtxosStore.createIndex("txid", "txid", { unique: false });
10213
+ }
10214
+ if (!vtxosStore.indexNames.contains("value")) {
10215
+ vtxosStore.createIndex("value", "value", { unique: false });
10216
+ }
10217
+ if (!vtxosStore.indexNames.contains("status")) {
10218
+ vtxosStore.createIndex("status", "status", {
10219
+ unique: false
10220
+ });
10221
+ }
10222
+ if (!vtxosStore.indexNames.contains("createdAt")) {
10223
+ vtxosStore.createIndex("createdAt", "createdAt", {
10224
+ unique: false
10225
+ });
10226
+ }
10227
+ if (!vtxosStore.indexNames.contains("isSpent")) {
10228
+ vtxosStore.createIndex("isSpent", "isSpent", {
10229
+ unique: false
10230
+ });
10231
+ }
10232
+ if (!vtxosStore.indexNames.contains("isUnrolled")) {
10233
+ vtxosStore.createIndex("isUnrolled", "isUnrolled", {
10234
+ unique: false
10235
+ });
10236
+ }
10237
+ if (!vtxosStore.indexNames.contains("spentBy")) {
10238
+ vtxosStore.createIndex("spentBy", "spentBy", {
10239
+ unique: false
10240
+ });
10241
+ }
10242
+ if (!vtxosStore.indexNames.contains("settledBy")) {
10243
+ vtxosStore.createIndex("settledBy", "settledBy", {
10244
+ unique: false
10245
+ });
10246
+ }
10247
+ if (!vtxosStore.indexNames.contains("arkTxId")) {
10248
+ vtxosStore.createIndex("arkTxId", "arkTxId", {
10249
+ unique: false
10250
+ });
10251
+ }
10252
+ if (!vtxosStore.indexNames.contains("script")) {
10253
+ vtxosStore.createIndex("script", "script", {
10254
+ unique: false
10255
+ });
10256
+ }
10257
+ if (!vtxosStore.indexNames.contains("scriptUnspent")) {
10258
+ vtxosStore.createIndex("scriptUnspent", ["script", "unspent"], { unique: false });
10259
+ }
10260
+ }
10261
+ if (!db.objectStoreNames.contains(STORE_UTXOS)) {
10262
+ const utxosStore = db.createObjectStore(STORE_UTXOS, {
10263
+ keyPath: ["address", "txid", "vout"]
10264
+ });
10265
+ if (!utxosStore.indexNames.contains("address")) {
10266
+ utxosStore.createIndex("address", "address", {
10267
+ unique: false
10268
+ });
10269
+ }
10270
+ if (!utxosStore.indexNames.contains("txid")) {
10271
+ utxosStore.createIndex("txid", "txid", { unique: false });
10272
+ }
10273
+ if (!utxosStore.indexNames.contains("value")) {
10274
+ utxosStore.createIndex("value", "value", { unique: false });
10275
+ }
10276
+ if (!utxosStore.indexNames.contains("status")) {
10277
+ utxosStore.createIndex("status", "status", {
10278
+ unique: false
10279
+ });
10280
+ }
10281
+ }
10282
+ if (!db.objectStoreNames.contains(STORE_TRANSACTIONS)) {
10283
+ const transactionsStore = db.createObjectStore(STORE_TRANSACTIONS, {
10284
+ keyPath: ["address", "keyBoardingTxid", "keyCommitmentTxid", "keyArkTxid"]
10285
+ });
10286
+ if (!transactionsStore.indexNames.contains("address")) {
10287
+ transactionsStore.createIndex("address", "address", {
10288
+ unique: false
10289
+ });
10290
+ }
10291
+ if (!transactionsStore.indexNames.contains("type")) {
10292
+ transactionsStore.createIndex("type", "type", {
10293
+ unique: false
10294
+ });
10295
+ }
10296
+ if (!transactionsStore.indexNames.contains("amount")) {
10297
+ transactionsStore.createIndex("amount", "amount", {
10298
+ unique: false
10299
+ });
10300
+ }
10301
+ if (!transactionsStore.indexNames.contains("settled")) {
10302
+ transactionsStore.createIndex("settled", "settled", {
10303
+ unique: false
10304
+ });
10305
+ }
10306
+ if (!transactionsStore.indexNames.contains("createdAt")) {
10307
+ transactionsStore.createIndex("createdAt", "createdAt", {
10308
+ unique: false
10309
+ });
10310
+ }
10311
+ if (!transactionsStore.indexNames.contains("addressCreatedAt")) {
10312
+ transactionsStore.createIndex("addressCreatedAt", ["address", "createdAt"], {
10313
+ unique: false
10314
+ });
10315
+ }
10316
+ if (!transactionsStore.indexNames.contains("arkTxid")) {
10317
+ transactionsStore.createIndex("arkTxid", "key.arkTxid", {
10318
+ unique: false
10319
+ });
10320
+ }
10321
+ }
10322
+ if (!db.objectStoreNames.contains(STORE_WALLET_STATE)) {
10323
+ db.createObjectStore(STORE_WALLET_STATE, {
10324
+ keyPath: "key"
10325
+ });
10326
+ }
10327
+ if (!db.objectStoreNames.contains(STORE_CONTRACTS)) {
10328
+ const contractsStore = db.createObjectStore(STORE_CONTRACTS, {
10329
+ keyPath: "script"
10330
+ });
10331
+ if (!contractsStore.indexNames.contains("type")) {
10332
+ contractsStore.createIndex("type", "type", {
10333
+ unique: false
10334
+ });
10335
+ }
10336
+ if (!contractsStore.indexNames.contains("state")) {
10337
+ contractsStore.createIndex("state", "state", {
10338
+ unique: false
10339
+ });
10353
10340
  }
10354
- const scriptToContract = new Map(contracts.map((c) => [c.script, c]));
10355
- const result = new Map(
10356
- contracts.map((c) => [c.script, []])
10357
- );
10358
- const windowOpts = syncWindow ? {
10359
- ...syncWindow.after !== void 0 && {
10360
- after: syncWindow.after
10361
- },
10362
- ...syncWindow.before !== void 0 && {
10363
- before: syncWindow.before
10364
- }
10365
- } : {};
10366
- const vtxos = await getAllNormalizedVtxos(
10367
- this.config.indexerProvider,
10368
- contracts.map((c) => c.script),
10369
- { ...windowOpts, pageSize }
10370
- );
10371
- const promoted = await this.promoteLookAheadHits(vtxos);
10372
- for (const [script, contract] of promoted) {
10373
- scriptToContract.set(script, contract);
10341
+ createContractWatchIndexes(contractsStore);
10342
+ }
10343
+ if (oldVersion >= 1 && oldVersion < 4 && transaction) {
10344
+ const vtxosStore = transaction.objectStore(STORE_VTXOS);
10345
+ if (!vtxosStore.indexNames.contains("script")) {
10346
+ vtxosStore.createIndex("script", "script", { unique: false });
10374
10347
  }
10375
- const {
10376
- scripts: annotatable,
10377
- cache,
10378
- failures
10379
- } = annotatableIn(scriptToContract, vtxos, this.tapscriptMemo);
10380
- this.recordAnnotationFailures(annotatable, failures);
10381
- const owned = vtxos.filter((v) => annotatable.has(v.script));
10382
- const annotated = await this.annotateVtxos(owned, cache);
10383
- for (const vtxo of annotated) {
10384
- result.get(vtxo.script).push({
10385
- ...vtxo,
10386
- contractScript: vtxo.script
10387
- });
10348
+ if (!vtxosStore.indexNames.contains("scriptUnspent")) {
10349
+ vtxosStore.createIndex("scriptUnspent", ["script", "unspent"], { unique: false });
10388
10350
  }
10389
- return { vtxosByScript: result, promoted };
10351
+ backfillVtxoIndexFields(transaction);
10390
10352
  }
10391
- /** Stop the watcher, clear callbacks, and mark the manager uninitialized. */
10392
- dispose() {
10393
- this.disposed = true;
10394
- const pendingDrain = this.lookAheadDrain;
10395
- this.stopWatcherFn?.();
10396
- this.stopWatcherFn = void 0;
10397
- this.eventCallbacks.clear();
10398
- this.lookAheadEntries.clear();
10399
- if (pendingDrain) {
10400
- void pendingDrain.catch(() => {
10401
- }).then(() => {
10402
- if (this.disposed) this.lookAheadEntries.clear();
10353
+ if (oldVersion > 0 && transaction) {
10354
+ const transactionsStore = transaction.objectStore(STORE_TRANSACTIONS);
10355
+ if (!transactionsStore.indexNames.contains("addressCreatedAt")) {
10356
+ transactionsStore.createIndex("addressCreatedAt", ["address", "createdAt"], {
10357
+ unique: false
10403
10358
  });
10404
10359
  }
10405
- this.initialized = false;
10406
10360
  }
10407
- /**
10408
- * Symbol.dispose implementation for the `using` keyword.
10409
- * @example
10410
- * ```typescript
10411
- * {
10412
- * using manager = await wallet.getContractManager();
10413
- * // ... use manager
10414
- * } // automatically disposed
10415
- * ```
10416
- */
10417
- [Symbol.dispose]() {
10418
- this.dispose();
10361
+ if (oldVersion > 0 && oldVersion < 6 && transaction) {
10362
+ const contractsStore = transaction.objectStore(STORE_CONTRACTS);
10363
+ createContractWatchIndexes(contractsStore);
10364
+ backfillContractWatchState(contractsStore);
10419
10365
  }
10420
- };
10421
-
10422
- // src/repositories/inMemory/contractRepository.ts
10423
- var InMemoryContractRepository = class {
10424
- version = 3;
10425
- contractsByScript = /* @__PURE__ */ new Map();
10426
- async clear() {
10427
- this.contractsByScript.clear();
10366
+ }
10367
+ function createContractWatchIndexes(store) {
10368
+ if (!store.indexNames.contains(CONTRACT_WATCH_INDEX)) {
10369
+ store.createIndex(CONTRACT_WATCH_INDEX, ["watchState", "script"], { unique: false });
10428
10370
  }
10429
- async getContractsPage(filter, page) {
10430
- assertPageRequest(page);
10431
- const matches = (value, criterion) => criterion === void 0 || (Array.isArray(criterion) ? criterion.includes(value) : value === criterion);
10432
- const rows = [...this.contractsByScript.values()].filter((contract) => page.after === void 0 || contract.script > page.after).filter(
10433
- (contract) => !filter || matches(contract.script, filter.script) && matches(contract.state, filter.state) && matches(contract.type, filter.type) && matches(watchStateOf(contract), filter.watch)
10434
- ).sort((a, b) => a.script < b.script ? -1 : a.script > b.script ? 1 : 0).slice(0, page.limit + 1);
10435
- return pageResult(rows, page.limit, (contract) => contract.script);
10371
+ if (!store.indexNames.contains(CONTRACT_TYPE_WATCH_INDEX)) {
10372
+ store.createIndex(CONTRACT_TYPE_WATCH_INDEX, ["type", "watchState", "script"], {
10373
+ unique: false
10374
+ });
10436
10375
  }
10437
- async saveContract(contract) {
10438
- this.contractsByScript.set(contract.script, contract);
10376
+ }
10377
+ function backfillContractWatchState(store) {
10378
+ const pageSize = 1e3;
10379
+ const readPage = (after) => {
10380
+ const request = store.getAll(
10381
+ after === void 0 ? void 0 : IDBKeyRange.lowerBound(after, true),
10382
+ pageSize
10383
+ );
10384
+ request.onsuccess = () => {
10385
+ const rows = request.result;
10386
+ for (const row of rows) {
10387
+ const next = contractRow(row);
10388
+ if (next.watchState !== row.watchState) store.put(next);
10389
+ }
10390
+ const last = rows.at(-1);
10391
+ if (rows.length === pageSize && last) readPage(last.script);
10392
+ };
10393
+ };
10394
+ readPage();
10395
+ }
10396
+ var unspentFlag = (vtxo) => isVtxoSpent(vtxo) ? {} : { unspent: 1 };
10397
+ function backfillVtxoIndexFields(transaction) {
10398
+ const store = transaction.objectStore(STORE_VTXOS);
10399
+ const pageSize = 1e3;
10400
+ const readPage = (after) => {
10401
+ const request = store.getAll(after && IDBKeyRange.lowerBound(after, true), pageSize);
10402
+ request.onsuccess = () => {
10403
+ const rows = request.result;
10404
+ for (const value of rows) {
10405
+ const legacy = legacyFactsOfRow(value);
10406
+ const next = {
10407
+ ...value,
10408
+ script: value.script || scriptFromArkAddress(value.address),
10409
+ ...unspentFlag({ ...value, isSpent: value.isSpent ?? legacy?.isSpent })
10410
+ };
10411
+ if (next.script !== value.script || next.unspent !== value.unspent) {
10412
+ store.put(next);
10413
+ }
10414
+ }
10415
+ const last = rows.at(-1);
10416
+ if (rows.length === pageSize && last) readPage([last.address, last.txid, last.vout]);
10417
+ };
10418
+ };
10419
+ readPage();
10420
+ }
10421
+
10422
+ // src/worker/browser/utils.ts
10423
+ var DEFAULT_DB_NAME = "arkade-service-worker";
10424
+ var DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS = 1e4;
10425
+ function normalizeOptions2(pathOrOptions) {
10426
+ const o = typeof pathOrOptions === "string" ? { path: pathOrOptions } : pathOrOptions;
10427
+ return {
10428
+ path: o.path,
10429
+ activationTimeoutMs: o.activationTimeoutMs ?? DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS
10430
+ };
10431
+ }
10432
+ function waitForServiceWorkerReady(timeoutMs) {
10433
+ return new Promise((resolve, reject) => {
10434
+ const timeoutId = setTimeout(() => {
10435
+ reject(new Error(`Service worker activation timed out after ${timeoutMs}ms`));
10436
+ }, timeoutMs);
10437
+ navigator.serviceWorker.ready.then((registration) => {
10438
+ clearTimeout(timeoutId);
10439
+ resolve(registration);
10440
+ }).catch((error) => {
10441
+ clearTimeout(timeoutId);
10442
+ reject(error);
10443
+ });
10444
+ });
10445
+ }
10446
+ async function setupServiceWorker(pathOrOptions) {
10447
+ if (!("serviceWorker" in navigator)) {
10448
+ throw new Error("Service workers are not supported in this browser");
10439
10449
  }
10440
- async deleteContract(script) {
10441
- this.contractsByScript.delete(script);
10450
+ const { path, activationTimeoutMs } = normalizeOptions2(pathOrOptions);
10451
+ const registration = await navigator.serviceWorker.register(path);
10452
+ await registration.update();
10453
+ const serviceWorker = registration.active || registration.waiting || registration.installing;
10454
+ if (!serviceWorker) {
10455
+ throw new Error("Failed to get service worker instance");
10442
10456
  }
10443
- async [Symbol.asyncDispose]() {
10444
- return;
10457
+ if (serviceWorker.state === "activated") {
10458
+ return serviceWorker;
10445
10459
  }
10446
- };
10460
+ const readyRegistration = await waitForServiceWorkerReady(activationTimeoutMs);
10461
+ if (!readyRegistration.active) {
10462
+ throw new Error("Service worker registration is ready but has no active worker");
10463
+ }
10464
+ return readyRegistration.active;
10465
+ }
10447
10466
 
10448
10467
  // src/repositories/indexedDB/contractRepository.ts
10449
10468
  var IndexedDBContractRepository = class {
@@ -10475,38 +10494,43 @@ var IndexedDBContractRepository = class {
10475
10494
  keys.map((script) => promisifyRequest(store.get(script)))
10476
10495
  );
10477
10496
  return pageResult(
10478
- this.applyContractFilter(contracts, normalized),
10497
+ this.applyContractFilter(contracts.map(contractOf), normalized),
10479
10498
  page.limit,
10480
10499
  (contract) => contract.script
10481
10500
  );
10482
10501
  }
10483
- const range = page.after === void 0 ? void 0 : IDBKeyRange.lowerBound(page.after, true);
10484
- const cursorRequest = store.openCursor(range);
10485
- return new Promise((resolve, reject) => {
10486
- const rows = [];
10487
- cursorRequest.onerror = () => reject(cursorRequest.error);
10488
- cursorRequest.onsuccess = () => {
10489
- const cursor = cursorRequest.result;
10490
- if (!cursor) {
10491
- resolve(pageResult(rows, page.limit, (contract) => contract.script));
10492
- return;
10493
- }
10494
- if (this.applyContractFilter([cursor.value], normalized).length) {
10495
- rows.push(cursor.value);
10496
- }
10497
- if (rows.length > page.limit) {
10498
- resolve(pageResult(rows, page.limit, (contract) => contract.script));
10499
- return;
10500
- }
10501
- cursor.continue();
10502
- };
10503
- });
10502
+ const watch = normalized.get("watch");
10503
+ const types = normalized.get("type");
10504
+ const after = page.after ?? "";
10505
+ const reads = watch ? [...new Set(watch)].flatMap(
10506
+ (state) => types ? [...new Set(types)].map((type) => [type, state]) : [[state]]
10507
+ ).map(
10508
+ (prefix) => this.readMatching(
10509
+ store.index(types ? CONTRACT_TYPE_WATCH_INDEX : CONTRACT_WATCH_INDEX),
10510
+ IDBKeyRange.bound(
10511
+ [...prefix, after],
10512
+ [...prefix, []],
10513
+ page.after !== void 0
10514
+ ),
10515
+ normalized,
10516
+ page.limit
10517
+ )
10518
+ ) : [
10519
+ this.readMatching(
10520
+ store,
10521
+ page.after === void 0 ? void 0 : IDBKeyRange.lowerBound(page.after, true),
10522
+ normalized,
10523
+ page.limit
10524
+ )
10525
+ ];
10526
+ const rows = (await Promise.all(reads)).flat().sort((a, b) => a.script < b.script ? -1 : a.script > b.script ? 1 : 0);
10527
+ return pageResult(rows, page.limit, (contract) => contract.script);
10504
10528
  }
10505
10529
  async saveContract(contract) {
10506
10530
  try {
10507
10531
  const db = await this.getDB();
10508
10532
  const transaction = db.transaction([STORE_CONTRACTS], "readwrite");
10509
- transaction.objectStore(STORE_CONTRACTS).put(contract);
10533
+ transaction.objectStore(STORE_CONTRACTS).put(contractRow(contract));
10510
10534
  await awaitTransaction(transaction);
10511
10535
  } catch (error) {
10512
10536
  console.error("Failed to save contract:", error);
@@ -10524,6 +10548,21 @@ var IndexedDBContractRepository = class {
10524
10548
  throw error;
10525
10549
  }
10526
10550
  }
10551
+ /** Up to `limit + 1` rows of `range` that pass `filter`, in key order. */
10552
+ readMatching(source, range, filter, limit) {
10553
+ const request = source.openCursor(range);
10554
+ return new Promise((resolve, reject) => {
10555
+ const rows = [];
10556
+ request.onerror = () => reject(request.error);
10557
+ request.onsuccess = () => {
10558
+ const cursor = request.result;
10559
+ if (!cursor) return resolve(rows);
10560
+ rows.push(...this.applyContractFilter([contractOf(cursor.value)], filter));
10561
+ if (rows.length > limit) return resolve(rows);
10562
+ cursor.continue();
10563
+ };
10564
+ });
10565
+ }
10527
10566
  applyContractFilter(contracts, filter) {
10528
10567
  return contracts.filter((contract) => {
10529
10568
  if (contract === void 0) return false;
@@ -10543,6 +10582,11 @@ var IndexedDBContractRepository = class {
10543
10582
  await this.connection[Symbol.asyncDispose]();
10544
10583
  }
10545
10584
  };
10585
+ var contractOf = (row) => {
10586
+ if (!row) return void 0;
10587
+ const { watchState: _watchState, ...contract } = row;
10588
+ return contract;
10589
+ };
10546
10590
  var FILTER_FIELDS = ["script", "state", "type", "watch"];
10547
10591
  function normalizeFilter(filter) {
10548
10592
  const res = /* @__PURE__ */ new Map();
@@ -11676,9 +11720,19 @@ function hasToReadonly(identity) {
11676
11720
  return typeof identity === "object" && identity !== null && "toReadonly" in identity && typeof identity.toReadonly === "function";
11677
11721
  }
11678
11722
  function filterSnapshotVtxos(snapshot, filter, pendingSpendOutpoints) {
11679
- const f = filter ?? { withRecoverable: true, withUnrolled: false };
11723
+ return filterFlatVtxos(
11724
+ snapshot.flatMap((contract) => contract.vtxos),
11725
+ filter,
11726
+ pendingSpendOutpoints
11727
+ );
11728
+ }
11729
+ function filterFlatVtxos(vtxos, filter, pendingSpendOutpoints) {
11730
+ const f = {
11731
+ withRecoverable: filter?.withRecoverable ?? true,
11732
+ withUnrolled: filter?.withUnrolled ?? false
11733
+ };
11680
11734
  const now = { timestamp: /* @__PURE__ */ new Date() };
11681
- return snapshot.flatMap((_) => _.vtxos).filter((vtxo) => {
11735
+ return vtxos.filter((vtxo) => {
11682
11736
  if (pendingSpendOutpoints.has(`${vtxo.txid}:${vtxo.vout}`)) {
11683
11737
  return false;
11684
11738
  }
@@ -11804,6 +11858,10 @@ var ReadonlyWallet = class _ReadonlyWallet {
11804
11858
  _contractManager;
11805
11859
  _contractManagerInitializing;
11806
11860
  watcherConfig;
11861
+ vtxoSyncOverlapMs;
11862
+ copySyncOverlapTo(wallet) {
11863
+ wallet.vtxoSyncOverlapMs = this.vtxoSyncOverlapMs;
11864
+ }
11807
11865
  /** Opt-in intent-lifecycle repository; `undefined` ⇒ intent persistence is a no-op. */
11808
11866
  intentRepository;
11809
11867
  /**
@@ -12102,19 +12160,13 @@ var ReadonlyWallet = class _ReadonlyWallet {
12102
12160
  walletContractTimelocks
12103
12161
  };
12104
12162
  }
12105
- /**
12106
- * Create a readonly wallet for querying balances, addresses, and history.
12107
- *
12108
- * @param config - Readonly wallet configuration
12109
- * @returns A readonly wallet instance
12110
- */
12111
12163
  static async create(config) {
12112
12164
  const pubkey = await config.identity.xOnlyPublicKey();
12113
12165
  if (!pubkey) {
12114
12166
  throw new Error("Invalid configured public key");
12115
12167
  }
12116
12168
  const setup = await _ReadonlyWallet.setupWalletConfig(config, pubkey);
12117
- const wallet = new _ReadonlyWallet(
12169
+ const wallet = new this(
12118
12170
  config.identity,
12119
12171
  setup.network,
12120
12172
  setup.onchainProvider,
@@ -12130,6 +12182,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
12130
12182
  config.watcherConfig,
12131
12183
  setup.walletContractTimelocks
12132
12184
  );
12185
+ wallet.vtxoSyncOverlapMs = config.vtxoSyncOverlapMs;
12133
12186
  wallet.intentRepository = config.storage?.intentRepository;
12134
12187
  wallet.virtualTxRepository = config.storage?.virtualTxRepository;
12135
12188
  wallet.exitDataCapture = config.storage?.exitDataCapture;
@@ -12790,6 +12843,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
12790
12843
  onVtxosPersisted,
12791
12844
  onVtxosSpent,
12792
12845
  watcherConfig: this.watcherConfig,
12846
+ vtxoSyncOverlapMs: this.vtxoSyncOverlapMs,
12793
12847
  lookAhead: this.lookAheadConfig(),
12794
12848
  // Without it every height-typed CLTV reads as unsatisfied, and seconds-typed timelocks
12795
12849
  // would be judged against the host clock. @see ContractManagerConfig.chainTip
@@ -12889,7 +12943,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
12889
12943
  await this.dispose();
12890
12944
  }
12891
12945
  };
12892
- var Wallet2 = class _Wallet extends ReadonlyWallet {
12946
+ var Wallet2 = class extends ReadonlyWallet {
12893
12947
  constructor(identity, network, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository, delegateProvider, watcherConfig, settlementConfig, walletContractTimelocks, receiveRotator, descriptorProvider, lookAheadWindow, batchExpiryPolicy, checkpointExitDelayPolicy) {
12894
12948
  super(
12895
12949
  identity,
@@ -13618,19 +13672,6 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
13618
13672
  throw rotatorError;
13619
13673
  }
13620
13674
  }
13621
- /**
13622
- * Create a full wallet and initialize its background managers.
13623
- *
13624
- * @param config - Wallet configuration
13625
- * @returns A wallet ready to query balances and send transactions
13626
- * @example
13627
- * ```typescript
13628
- * const wallet = await Wallet.create({
13629
- * identity,
13630
- * arkProvider: new RestArkProvider(),
13631
- * });
13632
- * ```
13633
- */
13634
13675
  static async create(config) {
13635
13676
  if (config.lookAheadWindow !== void 0 && (!Number.isInteger(config.lookAheadWindow) || config.lookAheadWindow <= 0)) {
13636
13677
  throw new Error(
@@ -13654,7 +13695,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
13654
13695
  resolveCheckpointExitDelayPolicy(setup.network, checkpointExitDelayOverrides)
13655
13696
  );
13656
13697
  const boot = await WalletReceiveRotator.resolveBoot(config, setup);
13657
- const wallet = new _Wallet(
13698
+ const wallet = new this(
13658
13699
  config.identity,
13659
13700
  setup.network,
13660
13701
  setup.onchainProvider,
@@ -13710,6 +13751,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
13710
13751
  wallet.setBoardingTapscriptForRotation(resolvedBoarding);
13711
13752
  }
13712
13753
  }
13754
+ wallet.vtxoSyncOverlapMs = config.vtxoSyncOverlapMs;
13713
13755
  wallet.intentRepository = config.storage?.intentRepository;
13714
13756
  wallet.virtualTxRepository = config.storage?.virtualTxRepository;
13715
13757
  wallet.exitDataCapture = config.storage?.exitDataCapture;
@@ -13751,6 +13793,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
13751
13793
  this.watcherConfig,
13752
13794
  this.walletContractTimelocks
13753
13795
  );
13796
+ this.copySyncOverlapTo(readonly);
13754
13797
  readonly._deprecatedSigners = new Map(this._deprecatedSigners);
13755
13798
  return readonly;
13756
13799
  }
@@ -14852,13 +14895,20 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
14852
14895
  const params = asSendParams(args);
14853
14896
  return this._withTxLock(() => this._sendImpl(params));
14854
14897
  }
14855
- async _sendImpl({ recipients: args, selectedVtxos }) {
14898
+ async _sendImpl({
14899
+ recipients: args,
14900
+ selectedVtxos,
14901
+ maxChangeFee
14902
+ }) {
14856
14903
  if (args.length === 0) {
14857
14904
  throw new Error("At least one receiver is required");
14858
14905
  }
14859
14906
  if (selectedVtxos && selectedVtxos.length === 0) {
14860
14907
  throw new Error("send({ selectedVtxos }): no inputs");
14861
14908
  }
14909
+ if (maxChangeFee !== void 0 && (!Number.isSafeInteger(maxChangeFee) || maxChangeFee < 0)) {
14910
+ throw new Error("send({ maxChangeFee }): expected a non-negative safe integer");
14911
+ }
14862
14912
  if (selectedVtxos) {
14863
14913
  void this.logUngatedInputs("send({ selectedVtxos })", selectedVtxos);
14864
14914
  }
@@ -15000,11 +15050,17 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
15000
15050
  `send({ selectedVtxos }): ${changeAmount} sats of change cannot carry ${assetChanges.size} asset change(s), needs ${this.dustAmount}`
15001
15051
  );
15002
15052
  }
15003
- const vtxoMinAmount = changeAmount > 0 || assetChanges.size > 0 ? (await this.arkProvider.getInfo()).vtxoMinAmount ?? 0n : 0n;
15053
+ const info = changeAmount > 0 || assetChanges.size > 0 ? await this.arkProvider.getInfo() : void 0;
15054
+ const vtxoMinAmount = info?.vtxoMinAmount ?? 0n;
15055
+ const canPayChangeAsFee = () => assetChanges.size === 0 && changeAmount > 0 && BigInt(changeAmount) < vtxoMinAmount && (maxChangeFee === void 0 || changeAmount <= maxChangeFee);
15004
15056
  if (selectedVtxos && changeAmount > 0 && BigInt(changeAmount) < vtxoMinAmount) {
15005
- throw new Error(
15006
- `send({ selectedVtxos }): ${changeAmount} sats of change is below the operator minimum of ${vtxoMinAmount} sats`
15007
- );
15057
+ if (canPayChangeAsFee()) {
15058
+ changeAmount = 0;
15059
+ } else {
15060
+ throw new Error(
15061
+ `send({ selectedVtxos }): ${changeAmount} sats of change is below the operator minimum of ${vtxoMinAmount} sats`
15062
+ );
15063
+ }
15008
15064
  }
15009
15065
  const selectedOutpoints = new Set(selectedCoins.map(vtxoOutpoint));
15010
15066
  while (!selectedVtxos && (changeAmount > 0 && BigInt(changeAmount) < vtxoMinAmount || assetChanges.size > 0 && BigInt(changeAmount) < this.dustAmount)) {
@@ -15045,6 +15101,10 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
15045
15101
  break;
15046
15102
  }
15047
15103
  }
15104
+ if (canPayChangeAsFee()) {
15105
+ changeAmount = 0;
15106
+ break;
15107
+ }
15048
15108
  throw new Error(`Cannot form minimum change amount of ${minimumChange} sats`);
15049
15109
  }
15050
15110
  for (const coin of extraCoins) {
@@ -17141,8 +17201,8 @@ var WalletMessageHandler = class {
17141
17201
  }
17142
17202
  case "SEND": {
17143
17203
  const wallet = this.requireWallet();
17144
- const { recipients, selectedVtxos } = message.payload;
17145
- const txid = await (selectedVtxos ? wallet.send({ recipients, selectedVtxos }) : wallet.send(...recipients));
17204
+ const { recipients, selectedVtxos, maxChangeFee } = message.payload;
17205
+ const txid = await (selectedVtxos || maxChangeFee !== void 0 ? wallet.send({ recipients, selectedVtxos, maxChangeFee }) : wallet.send(...recipients));
17146
17206
  return this.tagged({
17147
17207
  id,
17148
17208
  type: "SEND_SUCCESS",
@@ -17590,23 +17650,7 @@ var WalletMessageHandler = class {
17590
17650
  }
17591
17651
  const withUnrolled = message.payload.filter?.withUnrolled ?? false;
17592
17652
  const allVtxos = await this.getVtxosFromRepo({ unspentOnly: !withUnrolled });
17593
- const dustAmount = this.readonlyWallet.dustAmount;
17594
- const includeRecoverable = message.payload.filter?.withRecoverable ?? false;
17595
- return allVtxos.filter((v) => {
17596
- if (v.isUnrolled) {
17597
- return withUnrolled;
17598
- }
17599
- if (isVtxoSpent(v)) {
17600
- return false;
17601
- }
17602
- if (includeRecoverable) {
17603
- return true;
17604
- }
17605
- if (dustAmount != null && isSubdust(v, dustAmount)) {
17606
- return false;
17607
- }
17608
- return canSpendOffchain(v, { timestamp: /* @__PURE__ */ new Date() });
17609
- });
17653
+ return filterFlatVtxos(allVtxos, message.payload.filter, /* @__PURE__ */ new Set());
17610
17654
  }
17611
17655
  /** Tear down handler subscriptions, then delegate the full wipe to the wallet. */
17612
17656
  async clear() {
@@ -19119,14 +19163,22 @@ var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadon
19119
19163
  }
19120
19164
  async send(...args) {
19121
19165
  const [first] = args;
19122
- const { recipients, selectedVtxos } = args.length === 1 && first && "recipients" in first ? first : { recipients: args, selectedVtxos: void 0 };
19166
+ const { recipients, selectedVtxos, maxChangeFee } = args.length === 1 && first && "recipients" in first ? first : {
19167
+ recipients: args,
19168
+ selectedVtxos: void 0,
19169
+ maxChangeFee: void 0
19170
+ };
19123
19171
  const message = {
19124
19172
  tag: this.messageTag,
19125
19173
  type: "SEND",
19126
19174
  id: getRandomId(),
19127
19175
  // Omitted rather than sent as `undefined`, so an older worker sees
19128
19176
  // exactly the payload it saw before.
19129
- payload: selectedVtxos ? { recipients, selectedVtxos } : { recipients }
19177
+ payload: {
19178
+ recipients,
19179
+ ...selectedVtxos ? { selectedVtxos } : {},
19180
+ ...maxChangeFee !== void 0 ? { maxChangeFee } : {}
19181
+ }
19130
19182
  };
19131
19183
  try {
19132
19184
  const response = await this.sendMessage(message);
@@ -22329,9 +22381,9 @@ function providedWitness(witness) {
22329
22381
  });
22330
22382
  }
22331
22383
  function csvTimelock(leafName, operand) {
22332
- if (typeof operand === "string") return { type: "blocks", value: operand };
22333
- const timelock = operand >= 0n && operand < 0x80000000n ? sequenceToTimelock(Number(operand)) : void 0;
22334
- if (!timelock || BigInt(timelockToSequence(timelock)) !== operand) {
22384
+ if (typeof operand === "string") return { type: "sequence", value: operand };
22385
+ const timelock = sequenceToCanonicalTimelock(operand);
22386
+ if (!timelock) {
22335
22387
  fail(`leaf '${leafName}': CSV literal ${operand} is not a canonical BIP68 sequence`);
22336
22388
  }
22337
22389
  return timelock;
@@ -23379,6 +23431,6 @@ function createDefaultPaymentRouter(wallet) {
23379
23431
  }).use(arkRail()).use(arkAssetRail()).use(onchainRail({ feeInfo: walletFeeSource(wallet) }));
23380
23432
  }
23381
23433
 
23382
- 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_PAGE_SIZE, 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, OversizedChangeError, 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, assertHistoryPageFilter, assertIntentIdUnique, assertNoAssets, assertNoSelection, assertPageRequest, assertRecipientArkadeAddress, assertSendableAmount, assertSubmittedArkTxid, assertValidBatchExpiry, assertValidServerUnrollScript, assetMintResolver, assetsOf, attachPrevArkTxs, attachPrevoutTxs, awaitTransaction, boardingResolver, btcTarget, buildForfeitTx, buildOffchainTx, buildPreimageMessage, buildSaltedPreimageMessage, checkSaveVtxosForScript, claimWithPreimageIdentity, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, collabExitResolver, collectContracts, collectIntents, collectPages, collectScriptVtxos, collectTransactionHistory, collectUtxos, collectVtxos, combineTapscriptSigs, compareHistoryCursors, contractFilterMatchesNothing, contractFromArkContract, contractFromArkContractWithAddress, contractPreimage, contractSigner, createAssetPacket, createDefaultActivityRegistry, createDefaultPaymentRouter, createExitChainResolver, createManagedConnection, createdAtToIso, decodeArkContract, defaultBatchExpiryPolicy, defaultCheckpointExitDelayPolicy, deleteByIndex, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, exitObserverFor, extendVirtualCoinForContract, extractArkProviderUrl, gateExclusion, gatedContracts, getAllByIndexValues, getRandomId, hasBoardingTxExpired, hasCandidates, intentMatchesFilter, invoiceTarget, isAddressAllocationCapable, isArkContract, isBatchSignable, isBtcAddress, isContractGenericallySpendable, isContractVtxoEvent, isCooperativelyMigratable, isDeterministicSigner, isDiscoverable, isHDAllocationCapable, isHDDeterministicSignCapable, isHDWalletCapable, isLightningInvoice, isLnurl, isMessageBusInitializingError, isMessageBusNotInitializedError, isPerArtifactDescriptor, isSigningIdentity, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isWatchedContract, iteratePages, legacyVtxoFacts, logExcludedVtxos, makeHandle, matchServerCheckpoints, mergeChainedTxType, notifyExitObserved, onchainRail, openDatabase, outpointExclusion, outpointReasons, pageResult, promisifyRequest, provisionClaimSecret, provisionRefundKey, registerWalletRestoreHook, resolveAssetAmount, resolveBatchExpiryPolicy, resolveCheckpointExitDelayPolicy, resolveDescriptorSigner, resolvePrevTxs, resolveSendAmount, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, selectionOf, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signAndSubmitOffchainTx, signerSetFromInfo, signingDescriptorIndex, strictSigningDescriptorIndex, toXOnlySignerHex, tryResolveSendAmount, validateBatchRecipients, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, walletFeeSource, warnAndFilterVtxosForScript, withPrevTxs };
23383
- //# sourceMappingURL=chunk-MXGUCMAI.js.map
23384
- //# sourceMappingURL=chunk-MXGUCMAI.js.map
23434
+ 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_PAGE_SIZE, 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, OversizedChangeError, 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, assertHistoryPageFilter, assertIntentIdUnique, assertNoAssets, assertNoSelection, assertPageRequest, assertRecipientArkadeAddress, assertSendableAmount, assertSubmittedArkTxid, assertValidBatchExpiry, assertValidServerUnrollScript, assetMintResolver, assetsOf, attachPrevArkTxs, attachPrevoutTxs, awaitTransaction, boardingResolver, btcTarget, buildForfeitTx, buildOffchainTx, buildPreimageMessage, buildSaltedPreimageMessage, checkSaveVtxosForScript, claimWithPreimageIdentity, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, collabExitResolver, collectContracts, collectIntents, collectPages, collectScriptVtxos, collectTransactionHistory, collectUtxos, collectVtxos, combineTapscriptSigs, compareHistoryCursors, contractFilterMatchesNothing, contractFromArkContract, contractFromArkContractWithAddress, contractPreimage, contractSigner, createAssetPacket, createDefaultActivityRegistry, createDefaultPaymentRouter, createExitChainResolver, createManagedConnection, createdAtToIso, decodeArkContract, defaultBatchExpiryPolicy, defaultCheckpointExitDelayPolicy, deleteByIndex, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, exitObserverFor, extendVirtualCoinForContract, extractArkProviderUrl, gateExclusion, gatedContracts, getAllByIndexValues, getRandomId, hasBoardingTxExpired, hasCandidates, intentMatchesFilter, invoiceTarget, isAddressAllocationCapable, isArkContract, isBatchSignable, isBtcAddress, isContractGenericallySpendable, isContractVtxoEvent, isCooperativelyMigratable, isDeterministicSigner, isDiscoverable, isHDAllocationCapable, isHDDeterministicSignCapable, isHDWalletCapable, isLightningInvoice, isLnurl, isMessageBusInitializingError, isMessageBusNotInitializedError, isPerArtifactDescriptor, isSigningIdentity, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, iteratePages, legacyVtxoFacts, logExcludedVtxos, makeHandle, matchServerCheckpoints, mergeChainedTxType, notifyExitObserved, onchainRail, openDatabase, outpointExclusion, outpointReasons, pageResult, promisifyRequest, provisionClaimSecret, provisionRefundKey, registerWalletRestoreHook, resolveAssetAmount, resolveBatchExpiryPolicy, resolveCheckpointExitDelayPolicy, resolveDescriptorSigner, resolvePrevTxs, resolveSendAmount, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, selectionOf, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signAndSubmitOffchainTx, signerSetFromInfo, signingDescriptorIndex, strictSigningDescriptorIndex, toXOnlySignerHex, tryResolveSendAmount, validateBatchRecipients, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, walletFeeSource, warnAndFilterVtxosForScript, withPrevTxs };
23435
+ //# sourceMappingURL=chunk-CAFCIQZU.js.map
23436
+ //# sourceMappingURL=chunk-CAFCIQZU.js.map