@arkade-os/swap 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  ONCHAIN_SECONDS_PER_BLOCK,
18
18
  ONCHAIN_SEND_PAIR,
19
19
  RFQ_TERMINAL_STATES,
20
+ SOLO_REFUND_HEADROOM_SECONDS,
20
21
  SWAP_LOCKUP_CONTRACT_KIND,
21
22
  SWAP_LOCKUP_CONTRACT_LABEL,
22
23
  SWAP_LOCKUP_CONTRACT_TYPE,
@@ -58,7 +59,11 @@ import {
58
59
  unilateralRefundWithoutReceiverDelay,
59
60
  verifyLockupAddress,
60
61
  verifyReceiveInvoice
61
- } from "./chunk-DLM6BVTB.js";
62
+ } from "./chunk-Q4FAYBXS.js";
63
+ import {
64
+ InMemoryAssetSwapRepository,
65
+ marketsCacheKey
66
+ } from "./chunk-WGRU2DBF.js";
62
67
 
63
68
  // src/offer.ts
64
69
  import { hex as hex2 } from "@scure/base";
@@ -203,6 +208,8 @@ async function retireSettledOfferContracts(manager, swaps) {
203
208
 
204
209
  // src/store.ts
205
210
  import { hex } from "@scure/base";
211
+ import { sha256 } from "@noble/hashes/sha2.js";
212
+ import { contractPreimage } from "@arkade-os/sdk";
206
213
  var BTC_ASSET_ID = "btc";
207
214
  var byNewest = (a, b) => b.createdAt - a.createdAt;
208
215
  var getAssetSwapsOrThrow = async (repository) => {
@@ -257,8 +264,61 @@ var updateAssetSwapBestEffort = async (repository, id, changes) => {
257
264
  };
258
265
  var swapSecretsToRecord = (secrets) => ({
259
266
  signingDescriptor: secrets.descriptor,
260
- ..."mustPersistPreimage" in secrets && secrets.mustPersistPreimage ? { preimageHex: hex.encode(secrets.preimage) } : {}
267
+ ..."mustPersistPreimage" in secrets && secrets.mustPersistPreimage ? { preimageHex: hex.encode(secrets.preimage) } : {},
268
+ ..."preimageSalt" in secrets && secrets.preimageSalt ? { preimageSaltHex: hex.encode(secrets.preimageSalt) } : {}
261
269
  });
270
+ var decodeHex32 = (value, field) => {
271
+ const bytes = hex.decode(value);
272
+ if (bytes.length !== 32) {
273
+ throw new Error(`${field} must be 32 bytes, got ${bytes.length}`);
274
+ }
275
+ return bytes;
276
+ };
277
+ var PreimageNotRecoverableError = class extends Error {
278
+ constructor(reason, message, options) {
279
+ super(message, options);
280
+ this.reason = reason;
281
+ }
282
+ reason;
283
+ name = "PreimageNotRecoverableError";
284
+ };
285
+ var preimageForSwapRecord = async (wallet, record) => {
286
+ if (!record.signingDescriptor) {
287
+ throw new PreimageNotRecoverableError(
288
+ "no-secrets",
289
+ "this swap record carries no signing descriptor"
290
+ );
291
+ }
292
+ let stored;
293
+ let salt;
294
+ try {
295
+ stored = record.preimageHex ? decodeHex32(record.preimageHex, "preimageHex") : void 0;
296
+ salt = record.preimageSaltHex ? decodeHex32(record.preimageSaltHex, "preimageSaltHex") : void 0;
297
+ } catch (cause) {
298
+ throw new PreimageNotRecoverableError(
299
+ "malformed-record",
300
+ `this swap record's secrets projection is unreadable: ${String(cause)}`,
301
+ { cause }
302
+ );
303
+ }
304
+ let preimage;
305
+ try {
306
+ preimage = await contractPreimage(wallet, record.signingDescriptor, { stored, salt });
307
+ } catch (cause) {
308
+ throw new PreimageNotRecoverableError(
309
+ "not-derivable",
310
+ `this wallet cannot produce the preimage for ${record.signingDescriptor}`,
311
+ { cause }
312
+ );
313
+ }
314
+ if (record.paymentHash && hex.encode(sha256(preimage)) !== record.paymentHash.toLowerCase()) {
315
+ throw new PreimageNotRecoverableError(
316
+ "hash-mismatch",
317
+ "the derived preimage does not match this swap's payment hash: wrong wallet, or a tampered salt"
318
+ );
319
+ }
320
+ return preimage;
321
+ };
262
322
 
263
323
  // src/offer.ts
264
324
  var swapPrograms = {
@@ -634,40 +694,6 @@ var validatePlan = (plan, giveBalance, dust) => {
634
694
  return void 0;
635
695
  };
636
696
 
637
- // src/repository.ts
638
- var marketsCacheKey = (network, registry) => `arkade-intents-markets-${network}-${registry}`;
639
- var InMemoryAssetSwapRepository = class {
640
- version = 1;
641
- swaps = /* @__PURE__ */ new Map();
642
- scanned = /* @__PURE__ */ new Set();
643
- markets = /* @__PURE__ */ new Map();
644
- async saveSwap(swap) {
645
- this.swaps.set(swap.id, swap);
646
- }
647
- async getAllSwaps() {
648
- return [...this.swaps.values()];
649
- }
650
- async getScannedTxids() {
651
- return new Set(this.scanned);
652
- }
653
- async markTxidsScanned(txids) {
654
- for (const txid of txids) this.scanned.add(txid);
655
- }
656
- async getCachedMarkets(network, registry) {
657
- return this.markets.get(marketsCacheKey(network, registry));
658
- }
659
- async saveCachedMarkets(network, registry, entry) {
660
- this.markets.set(marketsCacheKey(network, registry), entry);
661
- }
662
- async clear() {
663
- this.swaps.clear();
664
- this.scanned.clear();
665
- this.markets.clear();
666
- }
667
- async [Symbol.asyncDispose]() {
668
- }
669
- };
670
-
671
697
  // src/indexedDbRepository.ts
672
698
  import { closeDatabase, openDatabase } from "@arkade-os/sdk";
673
699
  var DEFAULT_DB_NAME = "arkade-intents";
@@ -699,7 +725,7 @@ var IndexedDbAssetSwapRepository = class {
699
725
  this.dbName = dbName;
700
726
  }
701
727
  dbName;
702
- version = 1;
728
+ version = 2;
703
729
  // the promise, not the resolved database: openDatabase bumps a refcount on
704
730
  // every call including cache hits, while dispose closes once, so two
705
731
  // concurrent first calls would strand the refcount above zero and leak the
@@ -1025,7 +1051,7 @@ async function watchOfferSwaps({
1025
1051
  // src/claim.ts
1026
1052
  import { hex as hex6 } from "@scure/base";
1027
1053
  import { ripemd160 } from "@noble/hashes/legacy.js";
1028
- import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
1054
+ import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
1029
1055
  import {
1030
1056
  CSVMultisigTapscript as CSVMultisigTapscript2,
1031
1057
  claimWithPreimageIdentity,
@@ -1034,7 +1060,7 @@ import {
1034
1060
 
1035
1061
  // src/refund.ts
1036
1062
  import { base64 as base643, hex as hex5 } from "@scure/base";
1037
- import { sha256 } from "@noble/hashes/sha2.js";
1063
+ import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
1038
1064
  import {
1039
1065
  CSVMultisigTapscript,
1040
1066
  ConditionWitness,
@@ -1115,7 +1141,7 @@ async function findLockupVtxos(indexer, swapPkScript) {
1115
1141
  }
1116
1142
  return out;
1117
1143
  }
1118
- var hashesTo = (candidate, paymentHash) => hex5.encode(sha256(candidate)) === paymentHash;
1144
+ var hashesTo = (candidate, paymentHash) => hex5.encode(sha2562(candidate)) === paymentHash;
1119
1145
  var candidateWitnessItems = (tx, inputIndex) => [
1120
1146
  ...getArkPsbtFields(tx, inputIndex, ConditionWitness).flat(),
1121
1147
  ...tx.getInput(inputIndex).finalScriptWitness ?? []
@@ -1284,7 +1310,7 @@ async function pushClaim(ark, input) {
1284
1310
  }
1285
1311
  }
1286
1312
  const committed = input.script.options.preimageHash;
1287
- if (hex6.encode(ripemd160(sha2562(input.preimage))) !== hex6.encode(committed)) {
1313
+ if (hex6.encode(ripemd160(sha2563(input.preimage))) !== hex6.encode(committed)) {
1288
1314
  throw new Error("preimage does not match the covenant's payment hash");
1289
1315
  }
1290
1316
  const info = await ark.getInfo();
@@ -2151,6 +2177,60 @@ var outcomeOf = (swap) => {
2151
2177
  };
2152
2178
  var errorMessage = (error) => error instanceof Error ? error.message : String(error);
2153
2179
  var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
2180
+
2181
+ // src/activity.ts
2182
+ var LABELS = {
2183
+ lightning_send: "Lightning send",
2184
+ lightning_receive: "Lightning receive",
2185
+ onchain_send: "Onchain send"
2186
+ };
2187
+ var OUTCOME = {
2188
+ pending: "pending",
2189
+ // `claimable` and `claimed` are both in-progress states with no
2190
+ // user-visible phase distinct from "pending". `needs_counterparty` is
2191
+ // different in kind — the swap is BLOCKED, not merely in flight, since no
2192
+ // unilateral trader move exists (see `RfqSwapState`). Collapsing it into
2193
+ // `pending` here is a deliberate choice the opaque-token design permits —
2194
+ // apps map tokens themselves — but a future reader weighing a `"blocked"`
2195
+ // or `"stuck"` token should know this was already considered.
2196
+ claimable: "pending",
2197
+ claimed: "pending",
2198
+ needs_counterparty: "pending",
2199
+ settled: "settled",
2200
+ refunded: "refunded",
2201
+ failed: "failed"
2202
+ };
2203
+ function swapActivityResolver(deps) {
2204
+ let byTxid = /* @__PURE__ */ new Map();
2205
+ return {
2206
+ id: "arkade:swap",
2207
+ async prepare() {
2208
+ const swaps = await deps.listSwaps();
2209
+ const index = /* @__PURE__ */ new Map();
2210
+ for (const swap of swaps) {
2211
+ for (const txid of swap.txids) {
2212
+ if (txid) index.set(txid, swap);
2213
+ }
2214
+ }
2215
+ byTxid = index;
2216
+ },
2217
+ resolve(tx) {
2218
+ const key = tx.key.arkTxid || tx.key.commitmentTxid || tx.key.boardingTxid;
2219
+ const swap = key ? byTxid.get(key) : void 0;
2220
+ if (!swap) return void 0;
2221
+ const lostReceive = swap.kind === "lightning_receive" && swap.state === "refunded";
2222
+ return [
2223
+ {
2224
+ groupId: `swap:${swap.rfqId}`,
2225
+ label: LABELS[swap.kind],
2226
+ kind: "swap",
2227
+ outcome: lostReceive ? "lost" : OUTCOME[swap.state],
2228
+ metadata: { rfqId: swap.rfqId, swapKind: swap.kind }
2229
+ }
2230
+ ];
2231
+ }
2232
+ };
2233
+ }
2154
2234
  export {
2155
2235
  ARKADE_ASSET,
2156
2236
  ARKADE_BTC,
@@ -2175,6 +2255,7 @@ export {
2175
2255
  ONCHAIN_RECEIVE_PAIR,
2176
2256
  ONCHAIN_SECONDS_PER_BLOCK,
2177
2257
  ONCHAIN_SEND_PAIR,
2258
+ PreimageNotRecoverableError,
2178
2259
  QUOTE_OPTIONS,
2179
2260
  REFUND_MTP_LAG_SECONDS,
2180
2261
  RFQ_RESOLVED_STATES,
@@ -2182,6 +2263,7 @@ export {
2182
2263
  RFQ_TERMINAL_STATES,
2183
2264
  RefundNotLocallyPossibleError,
2184
2265
  RfqSwapManager,
2266
+ SOLO_REFUND_HEADROOM_SECONDS,
2185
2267
  SWAP_LOCKUP_CONTRACT_KIND,
2186
2268
  SWAP_LOCKUP_CONTRACT_LABEL,
2187
2269
  SWAP_LOCKUP_CONTRACT_TYPE,
@@ -2229,6 +2311,7 @@ export {
2229
2311
  onchainReceiveRequest,
2230
2312
  onchainSendRequest,
2231
2313
  paymentHashOf,
2314
+ preimageForSwapRecord,
2232
2315
  pushClaim,
2233
2316
  pushRefundWithoutReceiver,
2234
2317
  readLockupFate,
@@ -2247,6 +2330,7 @@ export {
2247
2330
  senderIdentityForSwapRecord,
2248
2331
  spendTxidsOf,
2249
2332
  spendUpdate,
2333
+ swapActivityResolver,
2250
2334
  swapPrograms,
2251
2335
  swapSecretsToRecord,
2252
2336
  unilateralClaimDelay,
package/dist/nostr.cjs CHANGED
@@ -33,6 +33,7 @@ __export(nostr_exports, {
33
33
  RFQ_AD_KIND: () => RFQ_AD_KIND,
34
34
  RFQ_DIRECTED_KIND: () => RFQ_DIRECTED_KIND,
35
35
  RelayUnavailable: () => RelayUnavailable,
36
+ TransportClosed: () => TransportClosed,
36
37
  nostrRfqTransport: () => nostrRfqTransport
37
38
  });
38
39
  module.exports = __toCommonJS(nostr_exports);
@@ -90,6 +91,8 @@ var SwapRefusal = class extends Error {
90
91
  }
91
92
  };
92
93
  var MIN_HEADROOM_SECONDS = 90 * 60;
94
+ var SEQUENCE_GRANULARITY_SECONDS = 512;
95
+ var SOLO_REFUND_HEADROOM_SECONDS = 8 * SEQUENCE_GRANULARITY_SECONDS;
93
96
  var MIN_CLAIM_WINDOW_SECONDS = 30 * 60;
94
97
 
95
98
  // src/nostr.ts
@@ -105,6 +108,12 @@ var RelayUnavailable = class extends Error {
105
108
  this.reasons = reasons;
106
109
  }
107
110
  };
111
+ var TransportClosed = class extends Error {
112
+ constructor() {
113
+ super("transport closed before the solver replied");
114
+ this.name = "TransportClosed";
115
+ }
116
+ };
108
117
  var closeReasons = (raw) => raw.map((entry) => {
109
118
  if (typeof entry === "string") return entry;
110
119
  const { url, reason } = entry ?? {};
@@ -204,6 +213,7 @@ var nostrRfqTransport = (options) => {
204
213
  },
205
214
  async close() {
206
215
  closed = true;
216
+ for (const waiter of waiters.values()) waiter.reject(new TransportClosed());
207
217
  waiters.clear();
208
218
  if (ownsPool) pool.close(relays);
209
219
  else subscription.close();
@@ -215,5 +225,6 @@ var nostrRfqTransport = (options) => {
215
225
  RFQ_AD_KIND,
216
226
  RFQ_DIRECTED_KIND,
217
227
  RelayUnavailable,
228
+ TransportClosed,
218
229
  nostrRfqTransport
219
230
  });
package/dist/nostr.d.cts CHANGED
@@ -1,10 +1,11 @@
1
- import { a as RfqTransport } from './rfq-DjZlesr4.cjs';
1
+ import { a as RfqTransport } from './rfq-3jWha5xA.cjs';
2
2
  import { SimplePool } from 'nostr-tools';
3
3
  import '@arkade-os/sdk';
4
4
 
5
5
  /**
6
- * The Nostr RFQ transport — the PRODUCTION one (docs/rfq-protocol.md § 3.1 in
7
- * arkade-os/lightning-swap-service).
6
+ * The Nostr RFQ transport — the PRODUCTION one (the kind, the NIP-44 framing
7
+ * and the payloads it carries are public at
8
+ * https://docs.arkadeos.com/intents/reference/rfq).
8
9
  *
9
10
  * `rfq.ts` ships two other transports: `httpTransport`, and `relayTransport`
10
11
  * which speaks the dev broker's `{op:"sub"|"event"}` framing. Neither is what a
@@ -70,6 +71,18 @@ declare class RelayUnavailable extends Error {
70
71
  readonly reasons: string[];
71
72
  constructor(reasons: string[]);
72
73
  }
74
+ /**
75
+ * `close()` was called while a negotiation was still waiting on a reply.
76
+ *
77
+ * Distinct from both a timeout and {@link RelayUnavailable}, which describe the
78
+ * wire; this describes a decision on our own side of it. A caller that closed
79
+ * deliberately — a user leaving the screen, a flow abandoning its request — can
80
+ * match on this and stay quiet, rather than reporting a solver failure that
81
+ * never happened.
82
+ */
83
+ declare class TransportClosed extends Error {
84
+ constructor();
85
+ }
73
86
  interface NostrRfqOptions {
74
87
  /** Relay URLs from the solver's card. The rendezvous, not solver endpoints. */
75
88
  relays: string[];
@@ -95,4 +108,4 @@ interface NostrRfqOptions {
95
108
  */
96
109
  declare const nostrRfqTransport: (options: NostrRfqOptions) => RfqTransport;
97
110
 
98
- export { type NostrRfqOptions, RFQ_AD_KIND, RFQ_DIRECTED_KIND, RelayUnavailable, nostrRfqTransport };
111
+ export { type NostrRfqOptions, RFQ_AD_KIND, RFQ_DIRECTED_KIND, RelayUnavailable, TransportClosed, nostrRfqTransport };
package/dist/nostr.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { a as RfqTransport } from './rfq-DjZlesr4.js';
1
+ import { a as RfqTransport } from './rfq-3jWha5xA.js';
2
2
  import { SimplePool } from 'nostr-tools';
3
3
  import '@arkade-os/sdk';
4
4
 
5
5
  /**
6
- * The Nostr RFQ transport — the PRODUCTION one (docs/rfq-protocol.md § 3.1 in
7
- * arkade-os/lightning-swap-service).
6
+ * The Nostr RFQ transport — the PRODUCTION one (the kind, the NIP-44 framing
7
+ * and the payloads it carries are public at
8
+ * https://docs.arkadeos.com/intents/reference/rfq).
8
9
  *
9
10
  * `rfq.ts` ships two other transports: `httpTransport`, and `relayTransport`
10
11
  * which speaks the dev broker's `{op:"sub"|"event"}` framing. Neither is what a
@@ -70,6 +71,18 @@ declare class RelayUnavailable extends Error {
70
71
  readonly reasons: string[];
71
72
  constructor(reasons: string[]);
72
73
  }
74
+ /**
75
+ * `close()` was called while a negotiation was still waiting on a reply.
76
+ *
77
+ * Distinct from both a timeout and {@link RelayUnavailable}, which describe the
78
+ * wire; this describes a decision on our own side of it. A caller that closed
79
+ * deliberately — a user leaving the screen, a flow abandoning its request — can
80
+ * match on this and stay quiet, rather than reporting a solver failure that
81
+ * never happened.
82
+ */
83
+ declare class TransportClosed extends Error {
84
+ constructor();
85
+ }
73
86
  interface NostrRfqOptions {
74
87
  /** Relay URLs from the solver's card. The rendezvous, not solver endpoints. */
75
88
  relays: string[];
@@ -95,4 +108,4 @@ interface NostrRfqOptions {
95
108
  */
96
109
  declare const nostrRfqTransport: (options: NostrRfqOptions) => RfqTransport;
97
110
 
98
- export { type NostrRfqOptions, RFQ_AD_KIND, RFQ_DIRECTED_KIND, RelayUnavailable, nostrRfqTransport };
111
+ export { type NostrRfqOptions, RFQ_AD_KIND, RFQ_DIRECTED_KIND, RelayUnavailable, TransportClosed, nostrRfqTransport };
package/dist/nostr.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SwapRefusal
3
- } from "./chunk-DLM6BVTB.js";
3
+ } from "./chunk-Q4FAYBXS.js";
4
4
 
5
5
  // src/nostr.ts
6
6
  import {
@@ -21,6 +21,12 @@ var RelayUnavailable = class extends Error {
21
21
  this.reasons = reasons;
22
22
  }
23
23
  };
24
+ var TransportClosed = class extends Error {
25
+ constructor() {
26
+ super("transport closed before the solver replied");
27
+ this.name = "TransportClosed";
28
+ }
29
+ };
24
30
  var closeReasons = (raw) => raw.map((entry) => {
25
31
  if (typeof entry === "string") return entry;
26
32
  const { url, reason } = entry ?? {};
@@ -120,6 +126,7 @@ var nostrRfqTransport = (options) => {
120
126
  },
121
127
  async close() {
122
128
  closed = true;
129
+ for (const waiter of waiters.values()) waiter.reject(new TransportClosed());
123
130
  waiters.clear();
124
131
  if (ownsPool) pool.close(relays);
125
132
  else subscription.close();
@@ -130,5 +137,6 @@ export {
130
137
  RFQ_AD_KIND,
131
138
  RFQ_DIRECTED_KIND,
132
139
  RelayUnavailable,
140
+ TransportClosed,
133
141
  nostrRfqTransport
134
142
  };
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/repositories/realm/index.ts
21
+ var realm_exports = {};
22
+ __export(realm_exports, {
23
+ ArkadeAssetSwapMarketsCacheSchema: () => ArkadeAssetSwapMarketsCacheSchema,
24
+ ArkadeAssetSwapScannedTxidSchema: () => ArkadeAssetSwapScannedTxidSchema,
25
+ ArkadeAssetSwapSchema: () => ArkadeAssetSwapSchema,
26
+ AssetSwapRealmSchemas: () => AssetSwapRealmSchemas,
27
+ RealmAssetSwapRepository: () => RealmAssetSwapRepository
28
+ });
29
+ module.exports = __toCommonJS(realm_exports);
30
+
31
+ // src/repository.ts
32
+ var marketsCacheKey = (network, registry) => `arkade-intents-markets-${network}-${registry}`;
33
+
34
+ // src/repositories/realm/repository.ts
35
+ var SWAPS = "ArkadeAssetSwap";
36
+ var SCANNED = "ArkadeAssetSwapScannedTxid";
37
+ var MARKETS = "ArkadeAssetSwapMarketsCache";
38
+ var RealmAssetSwapRepository = class {
39
+ constructor(realm) {
40
+ this.realm = realm;
41
+ }
42
+ realm;
43
+ version = 2;
44
+ async saveSwap(swap) {
45
+ this.realm.write(() => {
46
+ this.realm.create(
47
+ SWAPS,
48
+ {
49
+ id: swap.id,
50
+ status: swap.status,
51
+ createdAt: swap.createdAt,
52
+ data: JSON.stringify(swap)
53
+ },
54
+ "modified"
55
+ );
56
+ });
57
+ }
58
+ async getAllSwaps() {
59
+ return [...this.realm.objects(SWAPS)].map(
60
+ (o) => JSON.parse(o.data)
61
+ );
62
+ }
63
+ async getScannedTxids() {
64
+ return new Set([...this.realm.objects(SCANNED)].map((o) => o.txid));
65
+ }
66
+ async markTxidsScanned(txids) {
67
+ this.realm.write(() => {
68
+ for (const txid of txids) this.realm.create(SCANNED, { txid }, "modified");
69
+ });
70
+ }
71
+ async getCachedMarkets(network, registry) {
72
+ const [row] = [
73
+ ...this.realm.objects(MARKETS).filtered("key == $0", marketsCacheKey(network, registry))
74
+ ];
75
+ return row ? JSON.parse(row.data) : void 0;
76
+ }
77
+ async saveCachedMarkets(network, registry, entry) {
78
+ this.realm.write(() => {
79
+ this.realm.create(
80
+ MARKETS,
81
+ { key: marketsCacheKey(network, registry), data: JSON.stringify(entry) },
82
+ "modified"
83
+ );
84
+ });
85
+ }
86
+ /** All three schemas in one write: clearing swaps but keeping scanned txids
87
+ * would leave the restore scan permanently skipping those funding txs, so
88
+ * a partial clear must not be observable. */
89
+ async clear() {
90
+ this.realm.write(() => {
91
+ for (const name of [SWAPS, SCANNED, MARKETS]) {
92
+ this.realm.delete(this.realm.objects(name));
93
+ }
94
+ });
95
+ }
96
+ async [Symbol.asyncDispose]() {
97
+ }
98
+ };
99
+
100
+ // src/repositories/realm/schemas.ts
101
+ var ArkadeAssetSwapSchema = {
102
+ name: "ArkadeAssetSwap",
103
+ primaryKey: "id",
104
+ properties: {
105
+ id: "string",
106
+ status: "string",
107
+ createdAt: "int",
108
+ data: "string"
109
+ }
110
+ };
111
+ var ArkadeAssetSwapScannedTxidSchema = {
112
+ name: "ArkadeAssetSwapScannedTxid",
113
+ primaryKey: "txid",
114
+ properties: {
115
+ txid: "string"
116
+ }
117
+ };
118
+ var ArkadeAssetSwapMarketsCacheSchema = {
119
+ name: "ArkadeAssetSwapMarketsCache",
120
+ primaryKey: "key",
121
+ properties: {
122
+ key: "string",
123
+ data: "string"
124
+ }
125
+ };
126
+ var AssetSwapRealmSchemas = [
127
+ ArkadeAssetSwapSchema,
128
+ ArkadeAssetSwapScannedTxidSchema,
129
+ ArkadeAssetSwapMarketsCacheSchema
130
+ ];
131
+ // Annotate the CommonJS export names for ESM import in node:
132
+ 0 && (module.exports = {
133
+ ArkadeAssetSwapMarketsCacheSchema,
134
+ ArkadeAssetSwapScannedTxidSchema,
135
+ ArkadeAssetSwapSchema,
136
+ AssetSwapRealmSchemas,
137
+ RealmAssetSwapRepository
138
+ });
@@ -0,0 +1,103 @@
1
+ import { RealmLike } from '@arkade-os/sdk/repositories/realm';
2
+ import { A as AssetSwapRepository, a as AssetSwap, M as MarketsCacheEntry } from '../../repository-BwnZ8N62.cjs';
3
+ import '@arkade-os/solver-discovery';
4
+ import '@arkade-os/sdk';
5
+
6
+ /**
7
+ * Realm backend for React Native.
8
+ *
9
+ * `realm` is not a dependency of this package: consumers open Realm with the
10
+ * schemas from `./schemas.ts` and pass the instance, validated against the
11
+ * shared `RealmLike` shape from `@arkade-os/sdk`.
12
+ *
13
+ * **Records are serialized as JSON**, whole, into a `data` property —
14
+ * `status` and `createdAt` are mapped out for querying only, so no field of a
15
+ * record can be dropped. That holds for JSON-safe values: a consumer-added
16
+ * `Date` comes back a string and a `bigint` throws on save, unlike the
17
+ * IndexedDB backend's structured clone. `AssetSwap` itself is JSON-safe by
18
+ * design.
19
+ *
20
+ * Realm creates the schemas on open, so there is nothing to initialise. The
21
+ * consumer owns the Realm lifecycle — `[Symbol.asyncDispose]` is a no-op.
22
+ */
23
+ declare class RealmAssetSwapRepository implements AssetSwapRepository {
24
+ private readonly realm;
25
+ readonly version: 2;
26
+ constructor(realm: RealmLike);
27
+ saveSwap(swap: AssetSwap): Promise<void>;
28
+ getAllSwaps(): Promise<AssetSwap[]>;
29
+ getScannedTxids(): Promise<Set<string>>;
30
+ markTxidsScanned(txids: Iterable<string>): Promise<void>;
31
+ getCachedMarkets(network: string, registry: string): Promise<MarketsCacheEntry | undefined>;
32
+ saveCachedMarkets(network: string, registry: string, entry: MarketsCacheEntry): Promise<void>;
33
+ /** All three schemas in one write: clearing swaps but keeping scanned txids
34
+ * would leave the restore scan permanently skipping those funding txs, so
35
+ * a partial clear must not be observable. */
36
+ clear(): Promise<void>;
37
+ [Symbol.asyncDispose](): Promise<void>;
38
+ }
39
+
40
+ /**
41
+ * Realm object schemas for the asset-swap repository.
42
+ *
43
+ * The names land in the **consuming application's** schema namespace, next to
44
+ * its own models and the SDK's `Ark*` / the Boltz plugin's `Boltz*`, so they
45
+ * are prefixed with `ArkadeAssetSwap`. Unlike the SQLite backend there is no
46
+ * prefix option: a Realm schema name is baked into the schema objects the
47
+ * consumer registers and into every `realm.objects(…)` call here.
48
+ *
49
+ * Since `realm` is not a dependency of this package, schemas are plain JS
50
+ * objects conforming to Realm's ObjectSchema shape. They are new, so a consumer
51
+ * adds them to its Realm config and bumps its own `schemaVersion`; no migration
52
+ * helper ships here.
53
+ */
54
+ declare const ArkadeAssetSwapSchema: {
55
+ name: string;
56
+ primaryKey: string;
57
+ properties: {
58
+ id: string;
59
+ status: string;
60
+ createdAt: string;
61
+ data: string;
62
+ };
63
+ };
64
+ declare const ArkadeAssetSwapScannedTxidSchema: {
65
+ name: string;
66
+ primaryKey: string;
67
+ properties: {
68
+ txid: string;
69
+ };
70
+ };
71
+ declare const ArkadeAssetSwapMarketsCacheSchema: {
72
+ name: string;
73
+ primaryKey: string;
74
+ properties: {
75
+ key: string;
76
+ data: string;
77
+ };
78
+ };
79
+ declare const AssetSwapRealmSchemas: ({
80
+ name: string;
81
+ primaryKey: string;
82
+ properties: {
83
+ id: string;
84
+ status: string;
85
+ createdAt: string;
86
+ data: string;
87
+ };
88
+ } | {
89
+ name: string;
90
+ primaryKey: string;
91
+ properties: {
92
+ txid: string;
93
+ };
94
+ } | {
95
+ name: string;
96
+ primaryKey: string;
97
+ properties: {
98
+ key: string;
99
+ data: string;
100
+ };
101
+ })[];
102
+
103
+ export { ArkadeAssetSwapMarketsCacheSchema, ArkadeAssetSwapScannedTxidSchema, ArkadeAssetSwapSchema, AssetSwapRealmSchemas, RealmAssetSwapRepository };