@arkade-os/sdk 0.4.50 → 0.4.51

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 (64) hide show
  1. package/README.md +33 -8
  2. package/dist/adapters/expo.cjs +4 -4
  3. package/dist/adapters/expo.d.cts +2 -2
  4. package/dist/adapters/expo.d.ts +2 -2
  5. package/dist/adapters/expo.js +2 -2
  6. package/dist/adapters/indexedDB.cjs +3 -3
  7. package/dist/adapters/indexedDB.js +2 -2
  8. package/dist/{ark-ApoclKQZ.d.cts → ark-9V45Anrs.d.cts} +105 -1
  9. package/dist/{ark-ApoclKQZ.d.ts → ark-9V45Anrs.d.ts} +105 -1
  10. package/dist/{asyncStorageTaskQueue-CIjgv1co.d.cts → asyncStorageTaskQueue-B6nXJv7m.d.cts} +1 -1
  11. package/dist/{asyncStorageTaskQueue-BCWmdVg8.d.ts → asyncStorageTaskQueue-BZjR843B.d.ts} +1 -1
  12. package/dist/{chunk-AA4Q2J57.js → chunk-3AOTJZQ4.js} +3 -3
  13. package/dist/{chunk-AA4Q2J57.js.map → chunk-3AOTJZQ4.js.map} +1 -1
  14. package/dist/{chunk-Q4K2RSMY.cjs → chunk-3E4GDBYU.cjs} +7 -7
  15. package/dist/{chunk-Q4K2RSMY.cjs.map → chunk-3E4GDBYU.cjs.map} +1 -1
  16. package/dist/{chunk-7WSVP7DK.js → chunk-4KGT3IP2.js} +3 -3
  17. package/dist/{chunk-7WSVP7DK.js.map → chunk-4KGT3IP2.js.map} +1 -1
  18. package/dist/{chunk-TO3SR7TY.cjs → chunk-5AT2NV5G.cjs} +11 -11
  19. package/dist/{chunk-TO3SR7TY.cjs.map → chunk-5AT2NV5G.cjs.map} +1 -1
  20. package/dist/{chunk-ZXRSSBCF.js → chunk-6T5BBPKO.js} +3 -3
  21. package/dist/{chunk-ZXRSSBCF.js.map → chunk-6T5BBPKO.js.map} +1 -1
  22. package/dist/{chunk-6BQTROB4.cjs → chunk-6XF6ZATZ.cjs} +427 -163
  23. package/dist/chunk-6XF6ZATZ.cjs.map +1 -0
  24. package/dist/{chunk-SIAVTHMZ.cjs → chunk-FHDUEBBC.cjs} +3 -3
  25. package/dist/{chunk-SIAVTHMZ.cjs.map → chunk-FHDUEBBC.cjs.map} +1 -1
  26. package/dist/{chunk-SNTSB6M2.js → chunk-TS2MTRP4.js} +323 -59
  27. package/dist/chunk-TS2MTRP4.js.map +1 -0
  28. package/dist/contracts/handlers/index.d.cts +3 -3
  29. package/dist/contracts/handlers/index.d.ts +3 -3
  30. package/dist/{delegate-hJYMZN1D.d.ts → delegate-CJkolr2s.d.ts} +1 -1
  31. package/dist/{delegate-CCHPwQft.d.cts → delegate-tVTPM4fm.d.cts} +1 -1
  32. package/dist/{index-5Y9h9OnO.d.ts → index-B5Fdwzs_.d.ts} +2 -2
  33. package/dist/{index-8jE1XJpT.d.cts → index-BBAffp_8.d.cts} +2 -2
  34. package/dist/index.cjs +145 -145
  35. package/dist/index.d.cts +24 -8
  36. package/dist/index.d.ts +24 -8
  37. package/dist/index.js +2 -2
  38. package/dist/repositories/realm/index.cjs +17 -17
  39. package/dist/repositories/realm/index.d.cts +2 -2
  40. package/dist/repositories/realm/index.d.ts +2 -2
  41. package/dist/repositories/realm/index.js +3 -3
  42. package/dist/repositories/sqlite/index.cjs +16 -16
  43. package/dist/repositories/sqlite/index.d.cts +2 -2
  44. package/dist/repositories/sqlite/index.d.ts +2 -2
  45. package/dist/repositories/sqlite/index.js +3 -3
  46. package/dist/{taskRunner-C3YLaM2x.d.cts → taskRunner-4CrJaWHv.d.cts} +1 -1
  47. package/dist/{taskRunner-ChvV6Xrh.d.ts → taskRunner-TgdjRf_b.d.ts} +1 -1
  48. package/dist/wallet/expo/background.cjs +12 -12
  49. package/dist/wallet/expo/background.d.cts +3 -3
  50. package/dist/wallet/expo/background.d.ts +3 -3
  51. package/dist/wallet/expo/background.js +4 -4
  52. package/dist/wallet/expo/index.cjs +12 -12
  53. package/dist/wallet/expo/index.d.cts +4 -4
  54. package/dist/wallet/expo/index.d.ts +4 -4
  55. package/dist/wallet/expo/index.js +3 -3
  56. package/dist/{wallet-DIH0gVfW.d.ts → wallet-BBfhZx_Z.d.cts} +22 -3
  57. package/dist/{wallet-B260BTJs.d.cts → wallet-CSSTN8Yc.d.ts} +22 -3
  58. package/dist/worker/expo/index.cjs +7 -7
  59. package/dist/worker/expo/index.d.cts +4 -4
  60. package/dist/worker/expo/index.d.ts +4 -4
  61. package/dist/worker/expo/index.js +3 -3
  62. package/package.json +1 -1
  63. package/dist/chunk-6BQTROB4.cjs.map +0 -1
  64. package/dist/chunk-SNTSB6M2.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { Transaction, setArkPsbtField, PrevArkTxField, ConditionWitness, AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, craftToSpendTx, getArkPsbtFields, CosignerPublicKey, BufferReader, BufferWriter, OP_RETURN_EMPTY_PKSCRIPT, VtxoTaprootTree, baseFetch, Intent, rateGate, maybeArkError, isArkError, ArkErrorName, ProviderUnavailableError, FetchError, isFetchTimeoutError, AssetRef, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError } from './chunk-AA4Q2J57.js';
2
- import { witnessRefToBytes, validateProgram, toASM, stringifyArtifact, serializeArkadeContractParams, resolveAsm, parseArtifact, getOpcodeValue, getOpcodeName, fromASM, deserializeArkadeContractParams, computeArkadeScriptPublicKey, bytesToASM, asmToBytes, arkadeWitnessHash, arkadeScriptHash, SUPPORTED_PROGRAM_VERSION, OPCODE_VALUES, OPCODE_NAMES, bignum_exports, ArkadeScript, ArkadeProgramScript, ARKADE_OPS, ARKADE_OPCODE_VALUES, ARKADE_OPCODE_NAMES, ARKADE_OPCODES, ARKADE_OP, inputName, contractHandlers, isMainnetDescriptor, descriptorIsOurs, DefaultVtxo, DelegateVtxo, WALLET_RECEIVE_SOURCE, deriveDescriptorLeafPubKey, BoardingContractHandler } from './chunk-MBSYQ3WX.js';
1
+ import { Transaction, setArkPsbtField, PrevArkTxField, ConditionWitness, AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, craftToSpendTx, getArkPsbtFields, CosignerPublicKey, BufferReader, BufferWriter, OP_RETURN_EMPTY_PKSCRIPT, VtxoTaprootTree, baseFetch, Intent, rateGate, maybeArkError, isArkError, ArkErrorName, ProviderUnavailableError, FetchError, isFetchTimeoutError, AssetRef, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError } from './chunk-3AOTJZQ4.js';
2
+ import { witnessRefToBytes, validateProgram, toASM, stringifyArtifact, serializeArkadeContractParams, resolveAsm, parseArtifact, getOpcodeValue, getOpcodeName, fromASM, deserializeArkadeContractParams, computeArkadeScriptPublicKey, bytesToASM, asmToBytes, arkadeWitnessHash, arkadeScriptHash, SUPPORTED_PROGRAM_VERSION, OPCODE_VALUES, OPCODE_NAMES, bignum_exports, ArkadeScript, ArkadeProgramScript, ARKADE_OPS, ARKADE_OPCODE_VALUES, ARKADE_OPCODE_NAMES, ARKADE_OPCODES, ARKADE_OP, inputName, contractHandlers, isMainnetDescriptor, descriptorIsOurs, DefaultVtxo, DelegateVtxo, deriveDescriptorLeafPubKey, WALLET_RECEIVE_SOURCE, BoardingContractHandler } from './chunk-MBSYQ3WX.js';
3
3
  import { VtxoScript, isRecoverable, CSVMultisigTapscript, DEFAULT_NETWORK, hasTerminalSpend, getNormalizedVtxos, isVirtualCoin, ArkAddress, sequenceToTimelock, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, timelockToSequence, DEFAULT_NETWORK_NAME, normalizeVtxo, getSequence, toBatchExpiry, toOffchainInputFeeParams, canRecoverOnchain, getNetwork, MultisigTapscript, getAllNormalizedVtxos, DEFAULT_PAGE_SIZE, canSpendOffchain, toVirtualStatus, resolveTimeHeight, parseLegacyExpiry, networks as networks$1, isExpired, DEFAULT_ARKADE_SERVER_URL, isPastExpiry } from './chunk-EFNLTS6Q.js';
4
4
  import { __export } from './chunk-NSBPE2FW.js';
5
5
  import { sha256, equalBytes, hash160, sha256x2, concatBytes, randomPrivateKeyBytes, pubECDSA, pubSchnorr } from '@scure/btc-signer/utils.js';
@@ -8713,6 +8713,9 @@ function dedupeOutpoints(outpoints) {
8713
8713
  function areCoalescibleContractTypes(a, b) {
8714
8714
  return a === "default" && b === "boarding" || a === "boarding" && b === "default";
8715
8715
  }
8716
+ function toWatchOnlyContract(params) {
8717
+ return { ...params, state: params.state ?? "active", createdAt: Date.now() };
8718
+ }
8716
8719
  var SCAN_MAX_INDEX = 1e4;
8717
8720
  var DEFAULT_SCAN_BATCH = 10;
8718
8721
  var ContractManager = class _ContractManager {
@@ -8725,6 +8728,12 @@ var ContractManager = class _ContractManager {
8725
8728
  syncDegradedReason;
8726
8729
  /** Epoch-ms of the last successful provider sync, if any. */
8727
8730
  lastSyncedAt;
8731
+ /** Speculative look-ahead scripts, keyed by script. @see LookAheadEntry */
8732
+ lookAheadEntries = /* @__PURE__ */ new Map();
8733
+ /** In-flight look-ahead drain, if any. @see scheduleLookAheadDrain */
8734
+ lookAheadDrain;
8735
+ /** A refill was requested while a drain was running. */
8736
+ lookAheadDirty = false;
8728
8737
  constructor(config) {
8729
8738
  this.config = config;
8730
8739
  this.watcher = new ContractWatcher({
@@ -8775,6 +8784,7 @@ var ContractManager = class _ContractManager {
8775
8784
  for (const contract of contracts) {
8776
8785
  await this.watcher.addContract(contract);
8777
8786
  }
8787
+ await this.scheduleLookAheadDrain();
8778
8788
  try {
8779
8789
  await this.reconcileWatched();
8780
8790
  this.markSyncOnline();
@@ -8826,6 +8836,156 @@ var ContractManager = class _ContractManager {
8826
8836
  console.error("ContractManager: intent reconciliation failed", e);
8827
8837
  }
8828
8838
  }
8839
+ /** @see IContractManager.refillLookAhead */
8840
+ refillLookAhead() {
8841
+ return this.scheduleLookAheadDrain();
8842
+ }
8843
+ /**
8844
+ * Serialized drain of the look-ahead band: concurrent callers join the
8845
+ * active drain and mark it dirty, an idle call starts a new one. Promotion
8846
+ * can uncover more funded indices, and boot / SSE / rotate / reconnect can
8847
+ * all request a refill at once, so the loop coalesces them instead of
8848
+ * recursing.
8849
+ */
8850
+ scheduleLookAheadDrain() {
8851
+ if (!this.config.lookAhead) return Promise.resolve();
8852
+ if (this.lookAheadDrain) {
8853
+ this.lookAheadDirty = true;
8854
+ return this.lookAheadDrain;
8855
+ }
8856
+ const drain = (async () => {
8857
+ do {
8858
+ this.lookAheadDirty = false;
8859
+ await this.ensureLookAhead();
8860
+ } while (this.lookAheadDirty);
8861
+ })().finally(() => {
8862
+ this.lookAheadDrain = void 0;
8863
+ });
8864
+ this.lookAheadDrain = drain;
8865
+ return drain;
8866
+ }
8867
+ /**
8868
+ * Request a drain without awaiting it. Used from inside a sync (promotion),
8869
+ * where awaiting the drain that the sync itself is part of would deadlock.
8870
+ */
8871
+ requestLookAheadDrain() {
8872
+ if (!this.config.lookAhead) return;
8873
+ if (this.lookAheadDrain) {
8874
+ this.lookAheadDirty = true;
8875
+ return;
8876
+ }
8877
+ void this.scheduleLookAheadDrain().catch((err) => {
8878
+ console.error("ContractManager: look-ahead refill failed", err);
8879
+ });
8880
+ }
8881
+ /**
8882
+ * Rebuild the speculative watch band around the allocation watermark.
8883
+ *
8884
+ * NArk needs no analogue because it *is* the address issuer: it persists a
8885
+ * contract row at derivation time, so its watched set is a superset of
8886
+ * every address it ever advertised. This SDK, when a third party issues
8887
+ * addresses from the shared seed, cannot observe those allocations at all —
8888
+ * the window is how a non-issuer compensates.
8889
+ *
8890
+ * Entries are registered with the watcher but NOT persisted: they become
8891
+ * repository rows only once funded (see {@link promoteLookAheadHits}).
8892
+ */
8893
+ async ensureLookAhead() {
8894
+ const lookAhead = this.config.lookAhead;
8895
+ if (!lookAhead) return;
8896
+ const watermark = await lookAhead.currentWatermark();
8897
+ const from = Math.max(0, watermark - lookAhead.size);
8898
+ const to = watermark + lookAhead.size;
8899
+ const band = /* @__PURE__ */ new Map();
8900
+ for (let index = from; index <= to; index++) {
8901
+ const params = lookAhead.materialize(index);
8902
+ band.set(params.script, { index, params });
8903
+ }
8904
+ await this.watcher.withCoalescedSubscription(async () => {
8905
+ const persisted = await this.config.contractRepository.getContracts({
8906
+ script: [...band.keys()]
8907
+ });
8908
+ const persistedScripts = new Set(persisted.map((c) => c.script));
8909
+ for (const [script, { index, params }] of band) {
8910
+ if (persistedScripts.has(script)) {
8911
+ this.lookAheadEntries.delete(script);
8912
+ continue;
8913
+ }
8914
+ if (this.lookAheadEntries.has(script)) continue;
8915
+ const contract = toWatchOnlyContract(params);
8916
+ this.lookAheadEntries.set(script, {
8917
+ index,
8918
+ params,
8919
+ contract,
8920
+ catchUpPending: true
8921
+ });
8922
+ await this.watcher.addContract(contract);
8923
+ }
8924
+ const stale = [...this.lookAheadEntries.keys()].filter((s) => !band.has(s));
8925
+ if (stale.length > 0) {
8926
+ const rows = await this.config.contractRepository.getContracts({ script: stale });
8927
+ const nowPersisted = new Set(rows.map((c) => c.script));
8928
+ for (const script of stale) {
8929
+ this.lookAheadEntries.delete(script);
8930
+ if (!nowPersisted.has(script)) await this.watcher.removeContract(script);
8931
+ }
8932
+ }
8933
+ });
8934
+ await this.runLookAheadCatchUp();
8935
+ }
8936
+ /**
8937
+ * One-time full-history sync for speculative entries the manager has not
8938
+ * successfully caught up on yet.
8939
+ *
8940
+ * Every normal sync uses the cursor-derived delta window and SSE only
8941
+ * delivers events after a script is subscribed, so a script first
8942
+ * registered after its funding time would otherwise be skipped forever
8943
+ * (upgrade migration, or a band that slid over an already-funded index).
8944
+ * The entries are passed as in-memory contracts because `refreshVtxos`
8945
+ * resolves scripts through the repository, where they deliberately do not
8946
+ * exist. Targeted + explicitly windowed, so the global cursor stays put.
8947
+ */
8948
+ async runLookAheadCatchUp() {
8949
+ const pending = [...this.lookAheadEntries.values()].filter((e) => e.catchUpPending);
8950
+ if (pending.length === 0) return;
8951
+ try {
8952
+ await this.syncContracts({
8953
+ contracts: pending.map((e) => e.contract),
8954
+ window: { after: 0 }
8955
+ });
8956
+ for (const entry of pending) entry.catchUpPending = false;
8957
+ } catch (err) {
8958
+ if (!isRetryableProviderError(err)) throw err;
8959
+ this.markSyncDegraded(err);
8960
+ }
8961
+ }
8962
+ /**
8963
+ * Promote every look-ahead entry funded by `vtxos` into a real repository
8964
+ * row, returning the persisted rows keyed by script.
8965
+ *
8966
+ * MUST run on each raw indexer fetch before `annotateVtxos`:
8967
+ * `extendVirtualCoinForContract` throws when a VTXO's script has no
8968
+ * contract row, so a funded window entry has to be persisted before its
8969
+ * VTXOs are annotated and saved. Callers also swap the returned rows into
8970
+ * the local contract maps they built pre-promotion, so `saveVtxosForContract`
8971
+ * and `onVtxosPersisted` never see the synthetic watcher object.
8972
+ */
8973
+ async promoteLookAheadHits(vtxos) {
8974
+ const promoted = /* @__PURE__ */ new Map();
8975
+ if (this.lookAheadEntries.size === 0) return promoted;
8976
+ const hits = /* @__PURE__ */ new Map();
8977
+ for (const vtxo of vtxos) {
8978
+ const entry = this.lookAheadEntries.get(vtxo.script);
8979
+ if (entry) hits.set(vtxo.script, entry);
8980
+ }
8981
+ if (hits.size === 0) return promoted;
8982
+ for (const [script, entry] of hits) {
8983
+ promoted.set(script, await this.persistAndWatchContract(entry.params));
8984
+ await this.config.lookAhead?.onPromoted?.(entry.index);
8985
+ }
8986
+ this.requestLookAheadDrain();
8987
+ return promoted;
8988
+ }
8829
8989
  /**
8830
8990
  * Create and register a new contract.
8831
8991
  *
@@ -8871,6 +9031,11 @@ var ContractManager = class _ContractManager {
8871
9031
  * `persisted` is `true`.
8872
9032
  */
8873
9033
  async upsertContract(params) {
9034
+ const result = await this.upsertContractRow(params);
9035
+ this.lookAheadEntries.delete(params.script);
9036
+ return result;
9037
+ }
9038
+ async upsertContractRow(params) {
8874
9039
  const handler = contractHandlers.get(params.type);
8875
9040
  if (!handler) {
8876
9041
  throw new Error(`No handler registered for contract type '${params.type}'`);
@@ -9360,6 +9525,7 @@ var ContractManager = class _ContractManager {
9360
9525
  }
9361
9526
  break;
9362
9527
  case "connection_reset":
9528
+ await this.scheduleLookAheadDrain();
9363
9529
  await this.reconcileWatched();
9364
9530
  this.markSyncOnline();
9365
9531
  break;
@@ -9419,11 +9585,16 @@ var ContractManager = class _ContractManager {
9419
9585
  contracts.map((c) => c.script),
9420
9586
  { pendingOnly: true }
9421
9587
  );
9588
+ for (const [script, contract] of await this.promoteLookAheadHits(vtxos)) {
9589
+ scriptToContract.set(script, contract);
9590
+ }
9422
9591
  const owned = vtxos.filter((v) => scriptToContract.has(v.script));
9423
9592
  const annotated = await this.annotateVtxos(owned);
9424
9593
  const byContract = /* @__PURE__ */ new Map();
9594
+ const contractByAddress = /* @__PURE__ */ new Map();
9425
9595
  for (const vtxo of annotated) {
9426
9596
  const contract = scriptToContract.get(vtxo.script);
9597
+ contractByAddress.set(contract.address, contract);
9427
9598
  let arr = byContract.get(contract.address);
9428
9599
  if (!arr) {
9429
9600
  arr = [];
@@ -9435,7 +9606,7 @@ var ContractManager = class _ContractManager {
9435
9606
  });
9436
9607
  }
9437
9608
  for (const [addr, contractVtxos] of byContract) {
9438
- const contract = contracts.find((c) => c.address === addr);
9609
+ const contract = contractByAddress.get(addr);
9439
9610
  const filtered = warnAndFilterVtxosForScript(
9440
9611
  contractVtxos,
9441
9612
  contract.script,
@@ -9456,11 +9627,15 @@ var ContractManager = class _ContractManager {
9456
9627
  }
9457
9628
  }
9458
9629
  async fetchContractVxosFromIndexer(contracts, pageSize, syncWindow) {
9459
- const fetched = await this.fetchContractVtxosBulk(contracts, pageSize, syncWindow);
9630
+ const { vtxosByScript, promoted } = await this.fetchContractVtxosBulk(
9631
+ contracts,
9632
+ pageSize,
9633
+ syncWindow
9634
+ );
9460
9635
  const result = /* @__PURE__ */ new Map();
9461
- for (const [contractScript, vtxos] of fetched) {
9636
+ for (const [contractScript, vtxos] of vtxosByScript) {
9462
9637
  result.set(contractScript, vtxos);
9463
- const contract = contracts.find((c) => c.script === contractScript);
9638
+ const contract = promoted.get(contractScript) ?? contracts.find((c) => c.script === contractScript);
9464
9639
  if (contract) {
9465
9640
  const filtered = warnAndFilterVtxosForScript(
9466
9641
  vtxos,
@@ -9488,7 +9663,7 @@ var ContractManager = class _ContractManager {
9488
9663
  }
9489
9664
  async fetchContractVtxosBulk(contracts, pageSize = DEFAULT_PAGE_SIZE, syncWindow) {
9490
9665
  if (contracts.length === 0) {
9491
- return /* @__PURE__ */ new Map();
9666
+ return { vtxosByScript: /* @__PURE__ */ new Map(), promoted: /* @__PURE__ */ new Map() };
9492
9667
  }
9493
9668
  const scriptToContract = new Map(contracts.map((c) => [c.script, c]));
9494
9669
  const result = new Map(
@@ -9507,6 +9682,10 @@ var ContractManager = class _ContractManager {
9507
9682
  contracts.map((c) => c.script),
9508
9683
  { ...windowOpts, pageSize }
9509
9684
  );
9685
+ const promoted = await this.promoteLookAheadHits(vtxos);
9686
+ for (const [script, contract] of promoted) {
9687
+ scriptToContract.set(script, contract);
9688
+ }
9510
9689
  const owned = vtxos.filter((v) => scriptToContract.has(v.script));
9511
9690
  const annotated = await this.annotateVtxos(owned);
9512
9691
  for (const vtxo of annotated) {
@@ -9515,7 +9694,7 @@ var ContractManager = class _ContractManager {
9515
9694
  contractScript: vtxo.script
9516
9695
  });
9517
9696
  }
9518
- return result;
9697
+ return { vtxosByScript: result, promoted };
9519
9698
  }
9520
9699
  /**
9521
9700
  * Dispose of the ContractManager and release all resources.
@@ -9529,6 +9708,7 @@ var ContractManager = class _ContractManager {
9529
9708
  this.stopWatcherFn?.();
9530
9709
  this.stopWatcherFn = void 0;
9531
9710
  this.eventCallbacks.clear();
9711
+ this.lookAheadEntries.clear();
9532
9712
  this.initialized = false;
9533
9713
  }
9534
9714
  /**
@@ -9594,6 +9774,15 @@ var HDDescriptorProvider = class _HDDescriptorProvider {
9594
9774
  if (settings.lastIndexUsed === void 0) return void 0;
9595
9775
  return this.materializeDescriptorAt(settings.lastIndexUsed);
9596
9776
  }
9777
+ /**
9778
+ * The most recently allocated index, or `undefined` when nothing has ever
9779
+ * been allocated on this repo. Read-only peek at the watermark that
9780
+ * {@link getNextSigningDescriptor} advances.
9781
+ */
9782
+ async getLastIndexUsed() {
9783
+ const state = await this.walletRepository.getWalletState();
9784
+ return this.parseSettings(state ?? {}).lastIndexUsed;
9785
+ }
9597
9786
  /**
9598
9787
  * Monotonically advance the allocation watermark so the next
9599
9788
  * `getNextSigningDescriptor()` skips indices discovered by a restore
@@ -9976,58 +10165,26 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
9976
10165
  */
9977
10166
  async rotate(wallet) {
9978
10167
  const descriptor = await this.provider.getNextSigningDescriptor();
9979
- const pubKey = deriveLeafPubkey(descriptor);
9980
- const newTapscript = rebuildTapscript(wallet.offchainTapscript, pubKey);
9981
- const newScript = hex.encode(newTapscript.pkScript);
9982
- const newAddress = newTapscript.address(wallet.network.hrp, wallet.arkServerPublicKey).encode();
10168
+ const { tapscript: newTapscript, params } = buildReceiveContract(
10169
+ wallet.offchainTapscript,
10170
+ descriptor,
10171
+ wallet.network.hrp,
10172
+ true
10173
+ );
10174
+ const newScript = params.script;
9983
10175
  const manager = await wallet.getContractManager();
9984
- const csvTimelock = newTapscript.options.csvTimelock;
9985
- const csvTimelockStr = timelockToSequence(csvTimelock).toString();
9986
- const serverPubKeyHex = hex.encode(newTapscript.options.serverPubKey);
9987
- const baseParams = {
9988
- script: newScript,
9989
- address: newAddress,
9990
- state: "active",
9991
- // Persist the materialized signing descriptor alongside the
9992
- // source tag. The wallet's spending paths read this at sign
9993
- // time to route inputs locked by a rotated pubkey through
9994
- // `DescriptorProvider.signWithDescriptor` instead of the
9995
- // identity's index-0 key. Without it, post-rotation sends
9996
- // produce unsigned PSBTs that the server rejects with
9997
- // `INVALID_PSBT_INPUT (5): missing tapscript spend sig`.
9998
- metadata: {
9999
- source: WALLET_RECEIVE_SOURCE,
10000
- signingDescriptor: descriptor
10001
- }
10002
- };
10003
- if (newTapscript instanceof DelegateVtxo.Script) {
10004
- await manager.createContract({
10005
- ...baseParams,
10006
- type: "delegate",
10007
- params: {
10008
- pubKey: hex.encode(pubKey),
10009
- serverPubKey: serverPubKeyHex,
10010
- delegatePubKey: hex.encode(newTapscript.options.delegatePubKey),
10011
- csvTimelock: csvTimelockStr
10012
- }
10013
- });
10014
- } else {
10015
- await manager.createContract({
10016
- ...baseParams,
10017
- type: "default",
10018
- params: {
10019
- pubKey: hex.encode(pubKey),
10020
- serverPubKey: serverPubKeyHex,
10021
- csvTimelock: csvTimelockStr
10022
- }
10023
- });
10024
- }
10176
+ await manager.createContract(params);
10025
10177
  wallet.setOffchainTapscriptForRotation(newTapscript);
10026
10178
  const previousTagged = this.currentTaggedScript;
10027
10179
  if (previousTagged !== void 0 && previousTagged !== newScript) {
10028
10180
  await manager.setContractState(previousTagged, "inactive");
10029
10181
  }
10030
10182
  this.currentTaggedScript = newScript;
10183
+ try {
10184
+ await manager.refillLookAhead();
10185
+ } catch (err) {
10186
+ this.logger.error("WalletReceiveRotator: look-ahead refill failed", err);
10187
+ }
10031
10188
  }
10032
10189
  };
10033
10190
  function deriveLeafPubkey(descriptor) {
@@ -10040,6 +10197,45 @@ function deriveLeafPubkey(descriptor) {
10040
10197
  );
10041
10198
  }
10042
10199
  }
10200
+ function buildReceiveContract(current, descriptor, hrp, tagSource) {
10201
+ const pubKey = deriveLeafPubkey(descriptor);
10202
+ const tapscript = rebuildTapscript(current, pubKey);
10203
+ const serverPubKey = tapscript.options.serverPubKey;
10204
+ const csvTimelock = timelockToSequence(tapscript.options.csvTimelock).toString();
10205
+ const base = {
10206
+ script: hex.encode(tapscript.pkScript),
10207
+ address: tapscript.address(hrp, serverPubKey).encode(),
10208
+ state: "active",
10209
+ // The materialized signing descriptor is read at sign time to route
10210
+ // inputs locked by a rotated pubkey through
10211
+ // `DescriptorProvider.signWithDescriptor` instead of the identity's
10212
+ // index-0 key. Without it, spends produce unsigned PSBTs that the
10213
+ // server rejects with `INVALID_PSBT_INPUT (5): missing tapscript spend sig`.
10214
+ metadata: {
10215
+ ...tagSource && { source: WALLET_RECEIVE_SOURCE },
10216
+ signingDescriptor: descriptor
10217
+ }
10218
+ };
10219
+ const params = tapscript instanceof DelegateVtxo.Script ? {
10220
+ ...base,
10221
+ type: "delegate",
10222
+ params: {
10223
+ pubKey: hex.encode(pubKey),
10224
+ serverPubKey: hex.encode(serverPubKey),
10225
+ delegatePubKey: hex.encode(tapscript.options.delegatePubKey),
10226
+ csvTimelock
10227
+ }
10228
+ } : {
10229
+ ...base,
10230
+ type: "default",
10231
+ params: {
10232
+ pubKey: hex.encode(pubKey),
10233
+ serverPubKey: hex.encode(serverPubKey),
10234
+ csvTimelock
10235
+ }
10236
+ };
10237
+ return { tapscript, params };
10238
+ }
10043
10239
  function rebuildTapscript(current, pubKey) {
10044
10240
  if (current instanceof DelegateVtxo.Script) {
10045
10241
  return new DelegateVtxo.Script({ ...current.options, pubKey });
@@ -10242,6 +10438,7 @@ function extractArkProviderUrl(provider) {
10242
10438
  const serverUrl = provider.serverUrl;
10243
10439
  return typeof serverUrl === "string" && serverUrl.length > 0 ? serverUrl : void 0;
10244
10440
  }
10441
+ var DEFAULT_LOOK_AHEAD_WINDOW = 20;
10245
10442
  var MAINNET_UNILATERAL_EXIT_DELAY = 605184n;
10246
10443
  function toXOnlyPubKey(pubkey) {
10247
10444
  if (pubkey.length === 33) return pubkey.slice(1);
@@ -11297,6 +11494,14 @@ var ReadonlyWallet = class _ReadonlyWallet {
11297
11494
  this._contractManagerInitializing = void 0;
11298
11495
  }
11299
11496
  }
11497
+ /**
11498
+ * HD look-ahead configuration for the contract manager. Readonly wallets
11499
+ * have no descriptor provider, hence no watermark to look ahead of.
11500
+ * @see Wallet.lookAheadConfig
11501
+ */
11502
+ lookAheadConfig() {
11503
+ return void 0;
11504
+ }
11300
11505
  async initializeContractManager() {
11301
11506
  const virtualTxRepository = this.virtualTxRepository;
11302
11507
  let onVtxosPersisted;
@@ -11331,7 +11536,8 @@ var ReadonlyWallet = class _ReadonlyWallet {
11331
11536
  intentRepository: this.intentRepository,
11332
11537
  onVtxosPersisted,
11333
11538
  onVtxosSpent,
11334
- watcherConfig: this.watcherConfig
11539
+ watcherConfig: this.watcherConfig,
11540
+ lookAhead: this.lookAheadConfig()
11335
11541
  });
11336
11542
  const baselinePubkey = await this.identity.xOnlyPublicKey();
11337
11543
  const delegatePubKey = this.offchainTapscript instanceof DelegateVtxo.Script ? this.offchainTapscript.options.delegatePubKey : void 0;
@@ -11425,7 +11631,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
11425
11631
  }
11426
11632
  };
11427
11633
  var Wallet2 = class _Wallet extends ReadonlyWallet {
11428
- constructor(identity, network, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository, renewalConfig, delegateProvider, watcherConfig, settlementConfig, walletContractTimelocks, receiveRotator, descriptorProvider) {
11634
+ constructor(identity, network, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository, renewalConfig, delegateProvider, watcherConfig, settlementConfig, walletContractTimelocks, receiveRotator, descriptorProvider, lookAheadWindow) {
11429
11635
  super(
11430
11636
  identity,
11431
11637
  network,
@@ -11465,6 +11671,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
11465
11671
  this._serverUnrollScript = serverUnrollScript;
11466
11672
  this._receiveRotator = receiveRotator;
11467
11673
  this._descriptorProvider = descriptorProvider;
11674
+ this._lookAheadWindow = lookAheadWindow ?? DEFAULT_LOOK_AHEAD_WINDOW;
11468
11675
  this._signerRouter = new InputSignerRouter({
11469
11676
  identity,
11470
11677
  contractRepository,
@@ -11537,6 +11744,36 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
11537
11744
  * paths only ever take the identity-sign branch.
11538
11745
  */
11539
11746
  _descriptorProvider;
11747
+ /** Per-side width of the HD look-ahead band. @see WalletConfig.lookAheadWindow */
11748
+ _lookAheadWindow;
11749
+ /**
11750
+ * Watch a band of missing offchain receive scripts around the HD watermark
11751
+ * so payments to an externally-issued address land without a `restore()`.
11752
+ * Only HD wallets have a watermark to look ahead of; static / `auto`
11753
+ * wallets keep a single index-0 receive address, so the feature stays off.
11754
+ *
11755
+ * `materialize` reads `this.offchainTapscript` at call time, so a refill
11756
+ * after {@link rotateServerSigner} rebuilds the band under the active
11757
+ * signer (persisted old-signer rows stay watched through the repository).
11758
+ */
11759
+ lookAheadConfig() {
11760
+ const provider = this._descriptorProvider;
11761
+ if (!(provider instanceof HDDescriptorProvider)) return void 0;
11762
+ return {
11763
+ size: this._lookAheadWindow,
11764
+ currentWatermark: async () => await provider.getLastIndexUsed() ?? -1,
11765
+ materialize: (index) => buildReceiveContract(
11766
+ this.offchainTapscript,
11767
+ provider.materializeDescriptorAt(index),
11768
+ this.network.hrp,
11769
+ // Never tagged: a promoted entry was generated by someone
11770
+ // else, and the tag would make the next boot advertise a
11771
+ // third party's invoice address as our own.
11772
+ false
11773
+ ).params,
11774
+ onPromoted: (index) => provider.advanceLastIndexUsed(index)
11775
+ };
11776
+ }
11540
11777
  _signerRouter;
11541
11778
  /**
11542
11779
  * @internal Sole write path for `offchainTapscript` after construction.
@@ -11649,6 +11886,11 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
11649
11886
  }
11650
11887
  });
11651
11888
  this.setBoardingTapscriptForRotation(newBoarding);
11889
+ try {
11890
+ await manager.refillLookAhead();
11891
+ } catch (e) {
11892
+ console.warn("look-ahead refill after boarding allocation failed", e);
11893
+ }
11652
11894
  return newBoarding.onchainAddress(this.network);
11653
11895
  }
11654
11896
  /**
@@ -11770,6 +12012,11 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
11770
12012
  this.setBoardingTapscriptForRotation(newBoarding);
11771
12013
  this.setArkServerPublicKeyForRotation(xonly);
11772
12014
  this.setServerUnrollScriptForRotation(newServerUnrollScript);
12015
+ try {
12016
+ await manager.refillLookAhead();
12017
+ } catch (e) {
12018
+ console.warn("look-ahead refill after server-signer rotation failed", e);
12019
+ }
11773
12020
  }
11774
12021
  /**
11775
12022
  * Async mutex that serializes all operations submitting VTXOs to the Arkade
@@ -11885,6 +12132,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
11885
12132
  if (hd && result.highestConfirmedUsedIndex >= 0) {
11886
12133
  await provider.advanceLastIndexUsed(result.highestConfirmedUsedIndex);
11887
12134
  }
12135
+ await manager.refillLookAhead();
11888
12136
  await manager.refreshVtxos({ includeInactive: true, after: 0 });
11889
12137
  const causes = result.handlerErrors.map(
11890
12138
  (e) => e.error instanceof Error ? e.error : new Error(String(e.error))
@@ -11976,6 +12224,11 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
11976
12224
  * ```
11977
12225
  */
11978
12226
  static async create(config) {
12227
+ if (config.lookAheadWindow !== void 0 && (!Number.isInteger(config.lookAheadWindow) || config.lookAheadWindow <= 0)) {
12228
+ throw new Error(
12229
+ `lookAheadWindow must be a positive integer (got ${String(config.lookAheadWindow)})`
12230
+ );
12231
+ }
11979
12232
  const pubkey = await config.identity.xOnlyPublicKey();
11980
12233
  if (!pubkey) {
11981
12234
  throw new Error("Invalid configured public key");
@@ -12013,7 +12266,8 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
12013
12266
  config.settlementConfig,
12014
12267
  setup.walletContractTimelocks,
12015
12268
  boot?.rotator,
12016
- boot?.provider
12269
+ boot?.provider,
12270
+ config.lookAheadWindow
12017
12271
  );
12018
12272
  wallet._serverInfoSource = setup.serverInfoSource;
12019
12273
  if (setup.serverInfoSource === "live") {
@@ -14007,7 +14261,8 @@ var MessageBus = class {
14007
14261
  delegateProvider,
14008
14262
  settlementConfig: config.settlementConfig,
14009
14263
  walletMode: config.walletMode,
14010
- watcherConfig: config.watcherConfig
14264
+ watcherConfig: config.watcherConfig,
14265
+ lookAheadWindow: config.lookAheadWindow
14011
14266
  });
14012
14267
  return { wallet, arkProvider, readonlyWallet: wallet };
14013
14268
  }
@@ -16472,6 +16727,9 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
16472
16727
  )
16473
16728
  );
16474
16729
  },
16730
+ refillLookAhead() {
16731
+ return Promise.resolve();
16732
+ },
16475
16733
  async isWatching() {
16476
16734
  const message = {
16477
16735
  type: "IS_CONTRACT_MANAGER_WATCHING",
@@ -16521,6 +16779,11 @@ var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadon
16521
16779
  return serializeSigningIdentity(this.identity);
16522
16780
  }
16523
16781
  static async create(options) {
16782
+ if (options.lookAheadWindow !== void 0 && (!Number.isInteger(options.lookAheadWindow) || options.lookAheadWindow <= 0)) {
16783
+ throw new Error(
16784
+ `lookAheadWindow must be a positive integer (got ${String(options.lookAheadWindow)})`
16785
+ );
16786
+ }
16524
16787
  const walletRepository = options.storage?.walletRepository ?? new IndexedDBWalletRepository();
16525
16788
  const contractRepository = options.storage?.contractRepository ?? new IndexedDBContractRepository();
16526
16789
  if (!isSigningCapable(options.identity)) {
@@ -16570,6 +16833,7 @@ var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadon
16570
16833
  settlementConfig: options.settlementConfig,
16571
16834
  walletMode: options.walletMode,
16572
16835
  watcherConfig: options.watcherConfig,
16836
+ lookAheadWindow: options.lookAheadWindow,
16573
16837
  messageTimeouts
16574
16838
  };
16575
16839
  await initializeMessageBus(
@@ -19878,5 +20142,5 @@ function isArkContract(str) {
19878
20142
  }
19879
20143
 
19880
20144
  export { ARKADE_MAGIC, ActivityRegistry, ArkNote, ArkadeCash, ArkadeCashCreateError, AssetManager, BIP322, Batch, ChainedTxType, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EmulatorPacket, EsploraProvider, Estimator, Extension, ExtensionNotFoundError, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, RestEmulatorProvider, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnilateralExit, UnknownPacket, Unroll, VtxoManager, Wallet2 as Wallet, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, arkade_exports, assertIntentIdUnique, boardingResolver, buildForfeitTx, buildOffchainTx, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, createAssetPacket, createDefaultActivityRegistry, createExitChainResolver, decodeArkContract, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, extendVirtualCoinForContract, getMigrationStatus, getRandomId, hasBoardingTxExpired, intentMatchesFilter, intentPageBounds, isArkContract, isBatchSignable, isCooperativelyMigratable, isDiscoverable, isRetryableProviderError, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, mergeChainedTxType, migrateWalletRepository, openDatabase, requiresMigration, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signerSetFromInfo, toXOnlySignerHex, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript };
19881
- //# sourceMappingURL=chunk-SNTSB6M2.js.map
19882
- //# sourceMappingURL=chunk-SNTSB6M2.js.map
20145
+ //# sourceMappingURL=chunk-TS2MTRP4.js.map
20146
+ //# sourceMappingURL=chunk-TS2MTRP4.js.map