@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/README.md +201 -40
- package/dist/{chunk-DLM6BVTB.js → chunk-Q4FAYBXS.js} +6 -4
- package/dist/chunk-WGRU2DBF.js +38 -0
- package/dist/index.cjs +211 -93
- package/dist/index.d.cts +47 -151
- package/dist/index.d.ts +47 -151
- package/dist/index.js +125 -41
- package/dist/nostr.cjs +11 -0
- package/dist/nostr.d.cts +17 -4
- package/dist/nostr.d.ts +17 -4
- package/dist/nostr.js +9 -1
- package/dist/repositories/realm/index.cjs +138 -0
- package/dist/repositories/realm/index.d.cts +103 -0
- package/dist/repositories/realm/index.d.ts +103 -0
- package/dist/repositories/realm/index.js +108 -0
- package/dist/repositories/sqlite/index.cjs +163 -0
- package/dist/repositories/sqlite/index.d.cts +63 -0
- package/dist/repositories/sqlite/index.d.ts +63 -0
- package/dist/repositories/sqlite/index.js +138 -0
- package/dist/repository-BwnZ8N62.d.cts +236 -0
- package/dist/repository-BwnZ8N62.d.ts +236 -0
- package/dist/{rfq-DjZlesr4.d.cts → rfq-3jWha5xA.d.cts} +40 -16
- package/dist/{rfq-DjZlesr4.d.ts → rfq-3jWha5xA.d.ts} +40 -16
- package/package.json +24 -4
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-
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
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-
|
|
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 (
|
|
7
|
-
*
|
|
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-
|
|
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 (
|
|
7
|
-
*
|
|
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-
|
|
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 };
|