@arkade-os/swap 0.0.5 → 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 +76 -9
- package/dist/chunk-WGRU2DBF.js +38 -0
- package/dist/index.cjs +56 -0
- package/dist/index.d.cts +46 -231
- package/dist/index.d.ts +46 -231
- package/dist/index.js +59 -34
- package/dist/nostr.d.cts +4 -3
- package/dist/nostr.d.ts +4 -3
- 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-BH2yvo3O.d.cts → rfq-3jWha5xA.d.cts} +4 -3
- package/dist/{rfq-BH2yvo3O.d.ts → rfq-3jWha5xA.d.ts} +4 -3
- package/package.json +24 -4
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Client-side [Arkade Intents](https://arkade.money) asset swaps: discover markets, quote and
|
|
4
4
|
validate, create offers, track them, cancel them, and rebuild the whole record set from chain after
|
|
5
|
-
a wallet restore. Framework-free TypeScript over `@arkade-os/sdk`: the core API and
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
a wallet restore. Framework-free TypeScript over `@arkade-os/sdk`: the core API uses no DOM and no
|
|
6
|
+
Node-specific APIs, so it runs in Node, the browser, and React Native alike. Four storage backends
|
|
7
|
+
ship — in-memory (anywhere, nothing outlives the process), IndexedDB (browser), SQLite and Realm
|
|
8
|
+
(React Native, on subpath entry points) — see "Storage backends" below.
|
|
9
9
|
|
|
10
10
|
## Roles
|
|
11
11
|
|
|
@@ -100,11 +100,78 @@ arkade:BTC|asset` (quote, then take by funding an offer from layer 1).
|
|
|
100
100
|
|
|
101
101
|
Everything the package persists — swap records, the restore-scan cursor, and the markets cache —
|
|
102
102
|
goes through a single `AssetSwapRepository`, following the Arkade repository convention
|
|
103
|
-
(versioned interface, `AsyncDisposable`, one backend per platform).
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
(versioned interface, `AsyncDisposable`, one backend per platform). Construct one and pass it
|
|
104
|
+
wherever the package asks for a repository; `discoverMarkets` also accepts none, for a one-shot
|
|
105
|
+
uncached discovery.
|
|
106
|
+
|
|
107
|
+
## Storage backends
|
|
108
|
+
|
|
109
|
+
| Backend | Import from | For |
|
|
110
|
+
| ------------------------------ | ------------------------------------- | ----------------------------------------------- |
|
|
111
|
+
| `InMemoryAssetSwapRepository` | `@arkade-os/swap` | tests, one-shot scripts — nothing survives exit |
|
|
112
|
+
| `IndexedDbAssetSwapRepository` | `@arkade-os/swap` | the browser (or a polyfilled IndexedDB) |
|
|
113
|
+
| `SQLiteAssetSwapRepository` | `@arkade-os/swap/repositories/sqlite` | React Native, over your SQLite driver |
|
|
114
|
+
| `RealmAssetSwapRepository` | `@arkade-os/swap/repositories/realm` | React Native, over your Realm instance |
|
|
115
|
+
|
|
116
|
+
Neither subpath adds a dependency: they take the SDK's structural `SQLExecutor` / `RealmLike`
|
|
117
|
+
handles, so you pass the database you already opened.
|
|
118
|
+
|
|
119
|
+
**Records are stored whole.** The SQLite and Realm backends serialize each record to **JSON** in a
|
|
120
|
+
`data` column, with only `status` / `createdAt` mapped out for querying — so a field they do not
|
|
121
|
+
know about survives, which is what the `quote`-shaped extension in `MIGRATION.md` relies on. JSON is
|
|
122
|
+
the boundary, though, and it is narrower than IndexedDB's structured clone: a `Date` in a
|
|
123
|
+
consumer-added field comes back an ISO **string**, a `Set` or `Map` comes back empty, and a `bigint`
|
|
124
|
+
makes `saveSwap` **throw**. `AssetSwap` itself is JSON-safe by design (amounts are strings); keep
|
|
125
|
+
your own added fields that way too.
|
|
126
|
+
|
|
127
|
+
### SQLite
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { SQLiteAssetSwapRepository } from "@arkade-os/swap/repositories/sqlite";
|
|
131
|
+
import { SQLiteWalletRepository, type SQLExecutor } from "@arkade-os/sdk/repositories/sqlite";
|
|
132
|
+
|
|
133
|
+
const db = await SQLite.openDatabaseAsync("wallet.db"); // expo-sqlite
|
|
134
|
+
// Build the executor ONCE and hand this same instance to every repository on
|
|
135
|
+
// the database: the SDK serializes transactions in a chain keyed by this
|
|
136
|
+
// object, so a per-repository literal splits the chain and two BEGIN
|
|
137
|
+
// IMMEDIATEs can interleave.
|
|
138
|
+
const executor: SQLExecutor = {
|
|
139
|
+
run: (sql, params) => db.runAsync(sql, params ?? []),
|
|
140
|
+
get: (sql, params) => db.getFirstAsync(sql, params ?? []),
|
|
141
|
+
all: (sql, params) => db.getAllAsync(sql, params ?? []),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const swaps = new SQLiteAssetSwapRepository(executor);
|
|
145
|
+
const wallet = new SQLiteWalletRepository(executor); // same instance
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Sharing the executor is **necessary** for that serialization, not sufficient for atomicity across
|
|
149
|
+
all wallet storage: it disciplines the repositories that enter the chain — this one,
|
|
150
|
+
`SQLiteIntentRepository`, `SQLiteVirtualTxRepository`, and the wallet repository's migration path —
|
|
151
|
+
and nothing else. `SQLiteWalletRepository` and `SQLiteContractRepository` still write raw, so their
|
|
152
|
+
writes can land inside whatever transaction happens to be open.
|
|
153
|
+
|
|
154
|
+
Three tables land in your database, prefixed `arkade_`: `arkade_asset_swaps`,
|
|
155
|
+
`arkade_asset_swap_scanned_txids`, `arkade_asset_swap_markets`. Pass `{ prefix: "myapp_" }` if your
|
|
156
|
+
app already owns those names.
|
|
157
|
+
|
|
158
|
+
### Realm
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import Realm from "realm";
|
|
162
|
+
import { AssetSwapRealmSchemas, RealmAssetSwapRepository } from "@arkade-os/swap/repositories/realm";
|
|
163
|
+
import { ArkRealmSchemas } from "@arkade-os/sdk/repositories/realm";
|
|
164
|
+
|
|
165
|
+
const realm = await Realm.open({
|
|
166
|
+
schema: [...ArkRealmSchemas, ...AssetSwapRealmSchemas, ...yourOwnSchemas],
|
|
167
|
+
schemaVersion: YOUR_VERSION, // these schemas are new: bump yours when adding them
|
|
168
|
+
});
|
|
169
|
+
const swaps = new RealmAssetSwapRepository(realm);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Three classes land in your Realm namespace: `ArkadeAssetSwap`, `ArkadeAssetSwapScannedTxid`,
|
|
173
|
+
`ArkadeAssetSwapMarketsCache`. Unlike SQLite there is no prefix option — a Realm schema name is
|
|
174
|
+
baked into the schema objects you register — so reconcile against your own models by name.
|
|
108
175
|
|
|
109
176
|
## Creating an offer
|
|
110
177
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/repository.ts
|
|
2
|
+
var marketsCacheKey = (network, registry) => `arkade-intents-markets-${network}-${registry}`;
|
|
3
|
+
var InMemoryAssetSwapRepository = class {
|
|
4
|
+
version = 2;
|
|
5
|
+
swaps = /* @__PURE__ */ new Map();
|
|
6
|
+
scanned = /* @__PURE__ */ new Set();
|
|
7
|
+
markets = /* @__PURE__ */ new Map();
|
|
8
|
+
async saveSwap(swap) {
|
|
9
|
+
this.swaps.set(swap.id, swap);
|
|
10
|
+
}
|
|
11
|
+
async getAllSwaps() {
|
|
12
|
+
return [...this.swaps.values()];
|
|
13
|
+
}
|
|
14
|
+
async getScannedTxids() {
|
|
15
|
+
return new Set(this.scanned);
|
|
16
|
+
}
|
|
17
|
+
async markTxidsScanned(txids) {
|
|
18
|
+
for (const txid of txids) this.scanned.add(txid);
|
|
19
|
+
}
|
|
20
|
+
async getCachedMarkets(network, registry) {
|
|
21
|
+
return this.markets.get(marketsCacheKey(network, registry));
|
|
22
|
+
}
|
|
23
|
+
async saveCachedMarkets(network, registry, entry) {
|
|
24
|
+
this.markets.set(marketsCacheKey(network, registry), entry);
|
|
25
|
+
}
|
|
26
|
+
async clear() {
|
|
27
|
+
this.swaps.clear();
|
|
28
|
+
this.scanned.clear();
|
|
29
|
+
this.markets.clear();
|
|
30
|
+
}
|
|
31
|
+
async [Symbol.asyncDispose]() {
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
marketsCacheKey,
|
|
37
|
+
InMemoryAssetSwapRepository
|
|
38
|
+
};
|
package/dist/index.cjs
CHANGED
|
@@ -128,6 +128,7 @@ __export(index_exports, {
|
|
|
128
128
|
senderIdentityForSwapRecord: () => senderIdentityForSwapRecord,
|
|
129
129
|
spendTxidsOf: () => spendTxidsOf,
|
|
130
130
|
spendUpdate: () => spendUpdate,
|
|
131
|
+
swapActivityResolver: () => swapActivityResolver,
|
|
131
132
|
swapPrograms: () => swapPrograms,
|
|
132
133
|
swapSecretsToRecord: () => swapSecretsToRecord,
|
|
133
134
|
unilateralClaimDelay: () => unilateralClaimDelay,
|
|
@@ -3376,6 +3377,60 @@ var outcomeOf = (swap) => {
|
|
|
3376
3377
|
};
|
|
3377
3378
|
var errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
3378
3379
|
var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
|
|
3380
|
+
|
|
3381
|
+
// src/activity.ts
|
|
3382
|
+
var LABELS = {
|
|
3383
|
+
lightning_send: "Lightning send",
|
|
3384
|
+
lightning_receive: "Lightning receive",
|
|
3385
|
+
onchain_send: "Onchain send"
|
|
3386
|
+
};
|
|
3387
|
+
var OUTCOME = {
|
|
3388
|
+
pending: "pending",
|
|
3389
|
+
// `claimable` and `claimed` are both in-progress states with no
|
|
3390
|
+
// user-visible phase distinct from "pending". `needs_counterparty` is
|
|
3391
|
+
// different in kind — the swap is BLOCKED, not merely in flight, since no
|
|
3392
|
+
// unilateral trader move exists (see `RfqSwapState`). Collapsing it into
|
|
3393
|
+
// `pending` here is a deliberate choice the opaque-token design permits —
|
|
3394
|
+
// apps map tokens themselves — but a future reader weighing a `"blocked"`
|
|
3395
|
+
// or `"stuck"` token should know this was already considered.
|
|
3396
|
+
claimable: "pending",
|
|
3397
|
+
claimed: "pending",
|
|
3398
|
+
needs_counterparty: "pending",
|
|
3399
|
+
settled: "settled",
|
|
3400
|
+
refunded: "refunded",
|
|
3401
|
+
failed: "failed"
|
|
3402
|
+
};
|
|
3403
|
+
function swapActivityResolver(deps) {
|
|
3404
|
+
let byTxid = /* @__PURE__ */ new Map();
|
|
3405
|
+
return {
|
|
3406
|
+
id: "arkade:swap",
|
|
3407
|
+
async prepare() {
|
|
3408
|
+
const swaps = await deps.listSwaps();
|
|
3409
|
+
const index = /* @__PURE__ */ new Map();
|
|
3410
|
+
for (const swap of swaps) {
|
|
3411
|
+
for (const txid of swap.txids) {
|
|
3412
|
+
if (txid) index.set(txid, swap);
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
byTxid = index;
|
|
3416
|
+
},
|
|
3417
|
+
resolve(tx) {
|
|
3418
|
+
const key = tx.key.arkTxid || tx.key.commitmentTxid || tx.key.boardingTxid;
|
|
3419
|
+
const swap = key ? byTxid.get(key) : void 0;
|
|
3420
|
+
if (!swap) return void 0;
|
|
3421
|
+
const lostReceive = swap.kind === "lightning_receive" && swap.state === "refunded";
|
|
3422
|
+
return [
|
|
3423
|
+
{
|
|
3424
|
+
groupId: `swap:${swap.rfqId}`,
|
|
3425
|
+
label: LABELS[swap.kind],
|
|
3426
|
+
kind: "swap",
|
|
3427
|
+
outcome: lostReceive ? "lost" : OUTCOME[swap.state],
|
|
3428
|
+
metadata: { rfqId: swap.rfqId, swapKind: swap.kind }
|
|
3429
|
+
}
|
|
3430
|
+
];
|
|
3431
|
+
}
|
|
3432
|
+
};
|
|
3433
|
+
}
|
|
3379
3434
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3380
3435
|
0 && (module.exports = {
|
|
3381
3436
|
ARKADE_ASSET,
|
|
@@ -3476,6 +3531,7 @@ var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
|
|
|
3476
3531
|
senderIdentityForSwapRecord,
|
|
3477
3532
|
spendTxidsOf,
|
|
3478
3533
|
spendUpdate,
|
|
3534
|
+
swapActivityResolver,
|
|
3479
3535
|
swapPrograms,
|
|
3480
3536
|
swapSecretsToRecord,
|
|
3481
3537
|
unilateralClaimDelay,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,233 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/** The sentinel asset id for BTC itself, as opposed to a 68-hex asset id.
|
|
8
|
-
* Lives here with the {@link AssetSwap} fields it describes so the market and
|
|
9
|
-
* restore layers share one spelling instead of re-typing the literal. */
|
|
10
|
-
declare const BTC_ASSET_ID = "btc";
|
|
11
|
-
/**
|
|
12
|
-
* The record fields a wallet-provisioned secret becomes — what
|
|
13
|
-
* {@link swapSecretsToRecord} emits, and what every record type carrying swap
|
|
14
|
-
* secrets embeds.
|
|
15
|
-
*
|
|
16
|
-
* A named type rather than four fields restated per record: the mapper and the
|
|
17
|
-
* records it feeds must agree exactly, and a record that silently omits one of
|
|
18
|
-
* these round-trips a swap whose preimage cannot be re-derived. Embedding makes
|
|
19
|
-
* the omission a compile error instead.
|
|
20
|
-
*
|
|
21
|
-
* **Only `preimageHex` is secret.** `signingDescriptor` and `preimageSaltHex`
|
|
22
|
-
* are public derivation inputs — they must survive a field-mapped backend, but
|
|
23
|
-
* they leak nothing without the seed.
|
|
24
|
-
*/
|
|
25
|
-
interface SwapSecretsProjection {
|
|
26
|
-
/**
|
|
27
|
-
* The wallet descriptor this swap's sender key comes from — a fresh HD
|
|
28
|
-
* child, or a static wallet's `tr(pubkey)`. Public — the signer
|
|
29
|
-
* re-derives from the wallet, so the record carries no key material.
|
|
30
|
-
*/
|
|
31
|
-
signingDescriptor?: string;
|
|
32
|
-
/** P, hex, when it cannot be re-derived from the seed at all: the user
|
|
33
|
-
* supplied it, or the signer cannot sign deterministically. The swap's only
|
|
34
|
-
* claim secret when present. */
|
|
35
|
-
preimageHex?: string;
|
|
36
|
-
/**
|
|
37
|
-
* The salt P derives from, hex, on the salted arm — what a static wallet
|
|
38
|
-
* gets instead of storing P. **Public**, and unlike every other field here
|
|
39
|
-
* it is minted per swap: it is what stops one repeating key from handing
|
|
40
|
-
* every swap the same preimage.
|
|
41
|
-
*/
|
|
42
|
-
preimageSaltHex?: string;
|
|
43
|
-
}
|
|
44
|
-
interface AssetSwap extends SwapSecretsProjection {
|
|
45
|
-
/** Funding txid — the swap's identity. */
|
|
46
|
-
id: string;
|
|
47
|
-
/** 'btc' or a 68-hex asset id. */
|
|
48
|
-
fromAsset: string;
|
|
49
|
-
toAsset: string;
|
|
50
|
-
/** Atomic amounts as strings (bigint is not JSON-safe). */
|
|
51
|
-
fromAmount: string;
|
|
52
|
-
/** The covenant wantAmount — a floor, the fill pays >= this. */
|
|
53
|
-
toAmount: string;
|
|
54
|
-
swapAddress: string;
|
|
55
|
-
/** Hex pkScript of the swap contract — the indexer monitoring key. */
|
|
56
|
-
swapPkScript: string;
|
|
57
|
-
/** TLV offer — needed to rebuild the contract for cancel. */
|
|
58
|
-
offerHex: string;
|
|
59
|
-
fundingTxid: string;
|
|
60
|
-
spentTxid?: string;
|
|
61
|
-
status: AssetSwapStatus;
|
|
62
|
-
createdAt: number;
|
|
63
|
-
completedAt?: number;
|
|
64
|
-
/** RFQ pair string, e.g. `arkade:BTC->onchain:BTC`. */
|
|
65
|
-
pair?: string;
|
|
66
|
-
/** `sha256(P)`, hex. Public, and how a restore confirms a candidate
|
|
67
|
-
* derivation is the right one. */
|
|
68
|
-
paymentHash?: string;
|
|
69
|
-
/** The L1 HTLC's pkScript, hex — the chain-watch key. */
|
|
70
|
-
htlcPkScriptHex?: string;
|
|
71
|
-
htlcLocktime?: number;
|
|
72
|
-
/** The L1 funding txid, once observed. */
|
|
73
|
-
l1Txid?: string;
|
|
74
|
-
}
|
|
75
|
-
/** All swaps, newest-first. Insertion order is not chronological — the restore
|
|
76
|
-
* scan rebuilds records in tx-scan order — so sort at read to keep
|
|
77
|
-
* newest-first canonical for every consumer. */
|
|
78
|
-
declare const getAssetSwapsOrThrow: (repository: AssetSwapRepository) => Promise<AssetSwap[]>;
|
|
79
|
-
/** The consumer read: a broken backend reads as no swaps rather than crashing
|
|
80
|
-
* a history view. Mutations must use {@link getAssetSwapsOrThrow} instead —
|
|
81
|
-
* swallowing the read there would let "the backend is gone" masquerade as "no
|
|
82
|
-
* such swap" and skip the write silently. */
|
|
83
|
-
declare const getAssetSwaps: (repository: AssetSwapRepository) => Promise<AssetSwap[]>;
|
|
84
|
-
/** Add a swap; no-op if the id is already stored. Returns the updated list.
|
|
85
|
-
* THROWS on a failed write — nothing irreversible may happen until this record
|
|
86
|
-
* is durable, so the caller must not fund on a failure. */
|
|
87
|
-
declare const addAssetSwap: (repository: AssetSwapRepository, swap: AssetSwap) => Promise<AssetSwap[]>;
|
|
88
|
-
/** Merge changes into a swap by id. Returns the updated list.
|
|
89
|
-
* THROWS on a failed read or write, like {@link addAssetSwap} — use this for a
|
|
90
|
-
* write that gates something irreversible. Transitions written *after* the
|
|
91
|
-
* irreversible act belong on {@link updateAssetSwapBestEffort}. */
|
|
92
|
-
declare const updateAssetSwap: (repository: AssetSwapRepository, id: string, changes: Partial<Omit<AssetSwap, "id">>) => Promise<AssetSwap[]>;
|
|
93
|
-
/**
|
|
94
|
-
* {@link updateAssetSwap} for transitions that follow an irreversible action (a
|
|
95
|
-
* broadcast claim, a spent lockup): failing the caller there would report as
|
|
96
|
-
* failed a swap whose funds already moved, and a stale status is recoverable —
|
|
97
|
-
* crash recovery re-derives the true state from the chain
|
|
98
|
-
* (`classifyOnchainHtlc`).
|
|
99
|
-
*
|
|
100
|
-
* `persisted` is the part that must not be hidden: a caller that notifies on a
|
|
101
|
-
* change, or treats one as terminal, has to know the store did not agree.
|
|
102
|
-
*/
|
|
103
|
-
declare const updateAssetSwapBestEffort: (repository: AssetSwapRepository, id: string, changes: Partial<Omit<AssetSwap, "id">>) => Promise<{
|
|
104
|
-
swaps: AssetSwap[];
|
|
105
|
-
persisted: boolean;
|
|
106
|
-
}>;
|
|
107
|
-
/**
|
|
108
|
-
* The record fields a wallet-provisioned secret becomes.
|
|
109
|
-
*
|
|
110
|
-
* `signingDescriptor` is public and always stored — it is what recovers the
|
|
111
|
-
* signer. Then at most one of: `preimageHex`, when the wallet says it cannot
|
|
112
|
-
* re-derive P and it becomes the swap's only claim secret; or
|
|
113
|
-
* `preimageSaltHex`, the public input a derivable-but-repeating key needs.
|
|
114
|
-
*/
|
|
115
|
-
declare const swapSecretsToRecord: (secrets: ProvisionedKey | ProvisionedClaimSecret) => SwapSecretsProjection & {
|
|
116
|
-
signingDescriptor: string;
|
|
117
|
-
};
|
|
118
|
-
/** Why a wallet cannot produce a swap's preimage. */
|
|
119
|
-
type PreimageBlockedReason =
|
|
120
|
-
/** The record carries no `signingDescriptor`. */
|
|
121
|
-
"no-secrets"
|
|
122
|
-
/** `preimageHex` or `preimageSaltHex` is present but not 32 bytes of hex. */
|
|
123
|
-
| "malformed-record"
|
|
124
|
-
/**
|
|
125
|
-
* Nothing to derive from: a descriptor that repeats across swaps, with
|
|
126
|
-
* neither a stored preimage nor a salt — or one this wallet holds no key
|
|
127
|
-
* for. Merged deliberately: `contractSigner` reports a key it does not
|
|
128
|
-
* hold as a plain `Error` for static wallets and a `ForeignDescriptorError`
|
|
129
|
-
* for HD ones, so splitting the two here would mean matching on message
|
|
130
|
-
* text, which is the thing this type exists to avoid. The `cause` carries
|
|
131
|
-
* whichever it was.
|
|
132
|
-
*/
|
|
133
|
-
| "not-derivable"
|
|
134
|
-
/** Derived, but it does not hash to the record's `paymentHash`. */
|
|
135
|
-
| "hash-mismatch";
|
|
136
|
-
/**
|
|
137
|
-
* The wallet cannot produce this swap's preimage, and which of the four ways
|
|
138
|
-
* is `reason`.
|
|
139
|
-
*
|
|
140
|
-
* Deliberately **not** {@link RefundNotLocallyPossibleError}: that one means
|
|
141
|
-
* "no local refund is possible", and `RfqSwapManager` acts on it by reporting
|
|
142
|
-
* `needs_counterparty`. A claim-path read failure is a different verdict, and
|
|
143
|
-
* borrowing the refund error would have the manager announce one for the
|
|
144
|
-
* other.
|
|
145
|
-
*/
|
|
146
|
-
declare class PreimageNotRecoverableError extends Error {
|
|
147
|
-
readonly reason: PreimageBlockedReason;
|
|
148
|
-
readonly name = "PreimageNotRecoverableError";
|
|
149
|
-
constructor(reason: PreimageBlockedReason, message: string, options?: {
|
|
150
|
-
cause?: unknown;
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* The preimage a swap record claims with — stored, or re-derived from the
|
|
155
|
-
* wallet.
|
|
156
|
-
*
|
|
157
|
-
* The record-shaped inverse of {@link swapSecretsToRecord}, and the one place
|
|
158
|
-
* that knows which of a record's fields `contractPreimage` needs. Wire claim
|
|
159
|
-
* paths here rather than composing it by hand: a caller that forgets to pass
|
|
160
|
-
* `preimageSaltHex` gets a *wrong* preimage from a wallet that can derive,
|
|
161
|
-
* not an error.
|
|
162
|
-
*
|
|
163
|
-
* Verifies the result against `paymentHash` when the record carries one. The
|
|
164
|
-
* salted arm has two inputs that can be wrong — the key and the salt — where
|
|
165
|
-
* the HD arm had one, and a wrong P otherwise surfaces as an opaque script
|
|
166
|
-
* failure at claim time, long after the mistake.
|
|
167
|
-
*
|
|
168
|
-
* Every refusal is a {@link PreimageNotRecoverableError} carrying a `reason`,
|
|
169
|
-
* so a caller can tell "this record predates the descriptor" from "the salt is
|
|
170
|
-
* corrupt" without reading message text.
|
|
171
|
-
*/
|
|
172
|
-
declare const preimageForSwapRecord: (wallet: IWallet, record: SwapSecretsProjection & {
|
|
173
|
-
paymentHash?: string;
|
|
174
|
-
}) => Promise<Uint8Array>;
|
|
175
|
-
|
|
176
|
-
/** A registry discovery result held for reuse. Refetchable — unlike a swap
|
|
177
|
-
* record, losing it costs one network round trip — but it must survive a cold
|
|
178
|
-
* boot: serving it stale is what keeps quoting alive while a registry is down. */
|
|
179
|
-
interface MarketsCacheEntry {
|
|
180
|
-
markets: DiscoveredMarket[];
|
|
181
|
-
fetchedAt: number;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Everything the package persists, following the monorepo repository
|
|
185
|
-
* convention (versioned interface, AsyncDisposable, one backend per
|
|
186
|
-
* platform — see the Boltz plugin's SwapRepository). Consumers construct
|
|
187
|
-
* exactly one of these; there is no second storage seam.
|
|
188
|
-
*
|
|
189
|
-
* Durable records (swaps) and rebuildable state (the restore scan's txid
|
|
190
|
-
* cursor, the markets cache) live side by side because they share a
|
|
191
|
-
* lifetime: all three belong to one wallet on one device, and a consumer
|
|
192
|
-
* that wipes one wants all three gone.
|
|
193
|
-
*
|
|
194
|
-
* ponytail: no query filters — every consumer reads all swaps and filters
|
|
195
|
-
* in memory; mirror the Boltz plugin's GetSwapsFilter when a consumer needs
|
|
196
|
-
* subset queries.
|
|
197
|
-
*/
|
|
198
|
-
interface AssetSwapRepository extends AsyncDisposable {
|
|
199
|
-
readonly version: 2;
|
|
200
|
-
/** Insert or replace a swap by id. Store the record whole: `preimageHex`
|
|
201
|
-
* and `preimageSaltHex` both leave the swap unclaimable if a field-mapped
|
|
202
|
-
* backend drops them — the first is the only claim secret of a swap whose
|
|
203
|
-
* signer cannot derive, the second the public input every other static
|
|
204
|
-
* wallet's preimage derives from. */
|
|
205
|
-
saveSwap(swap: AssetSwap): Promise<void>;
|
|
206
|
-
/** All stored swaps, in no particular order — `getAssetSwaps` is the
|
|
207
|
-
* canonical newest-first read. */
|
|
208
|
-
getAllSwaps(): Promise<AssetSwap[]>;
|
|
209
|
-
/** Sent txids already checked for offer packets (see restore.ts). */
|
|
210
|
-
getScannedTxids(): Promise<Set<string>>;
|
|
211
|
-
markTxidsScanned(txids: Iterable<string>): Promise<void>;
|
|
212
|
-
/** Cached registry markets, or undefined on a miss. */
|
|
213
|
-
getCachedMarkets(network: string, registry: string): Promise<MarketsCacheEntry | undefined>;
|
|
214
|
-
saveCachedMarkets(network: string, registry: string, entry: MarketsCacheEntry): Promise<void>;
|
|
215
|
-
clear(): Promise<void>;
|
|
216
|
-
}
|
|
217
|
-
declare class InMemoryAssetSwapRepository implements AssetSwapRepository {
|
|
218
|
-
readonly version: 2;
|
|
219
|
-
private readonly swaps;
|
|
220
|
-
private readonly scanned;
|
|
221
|
-
private readonly markets;
|
|
222
|
-
saveSwap(swap: AssetSwap): Promise<void>;
|
|
223
|
-
getAllSwaps(): Promise<AssetSwap[]>;
|
|
224
|
-
getScannedTxids(): Promise<Set<string>>;
|
|
225
|
-
markTxidsScanned(txids: Iterable<string>): Promise<void>;
|
|
226
|
-
getCachedMarkets(network: string, registry: string): Promise<MarketsCacheEntry | undefined>;
|
|
227
|
-
saveCachedMarkets(network: string, registry: string, entry: MarketsCacheEntry): Promise<void>;
|
|
228
|
-
clear(): Promise<void>;
|
|
229
|
-
[Symbol.asyncDispose](): Promise<void>;
|
|
230
|
-
}
|
|
1
|
+
import { asset, IWallet, arkade, RestIndexerProvider, Transaction, IContractManager, RestArkProvider, VHTLC, Identity, ActivityResolver } from '@arkade-os/sdk';
|
|
2
|
+
import { A as AssetSwapRepository, a as AssetSwap, M as MarketsCacheEntry } from './repository-BwnZ8N62.cjs';
|
|
3
|
+
export { b as AssetSwapStatus, B as BTC_ASSET_ID, I as InMemoryAssetSwapRepository, P as PreimageBlockedReason, c as PreimageNotRecoverableError, S as SwapSecretsProjection, d as addAssetSwap, g as getAssetSwaps, e as getAssetSwapsOrThrow, p as preimageForSwapRecord, s as swapSecretsToRecord, u as updateAssetSwap, f as updateAssetSwapBestEffort } from './repository-BwnZ8N62.cjs';
|
|
4
|
+
import { Network, LocalCardInput, DiscoveredMarket, Side, OfferPlan } from '@arkade-os/solver-discovery';
|
|
5
|
+
import { R as RfqStatus, a as RfqTransport, O as OnchainHtlc, C as ChainSource, b as ChainUtxo, c as OnchainHtlcPhase } from './rfq-3jWha5xA.cjs';
|
|
6
|
+
export { A as ARKADE_ASSET, d as ARKADE_BTC, e as AddressMismatch, H as HtlcUtxo, I as InvoiceFacts, L as LIGHTNING_BTC, f as LIGHTNING_RECEIVE_PAIR, g as LIGHTNING_SEND_PAIR, M as MAX_MIN_CONFIRMATIONS, h as MIN_CLAIM_WINDOW_SECONDS, i as MIN_HEADROOM_SECONDS, j as ONCHAIN_BTC, k as ONCHAIN_CLAIM_MARGIN_SECONDS, l as ONCHAIN_DUST_SATS, m as ONCHAIN_ORDER_MARGIN_SECONDS, n as ONCHAIN_RECEIVE_PAIR, o as ONCHAIN_SECONDS_PER_BLOCK, p as ONCHAIN_SEND_PAIR, q as OnchainHtlcParams, r as OnchainNetwork, s as RFQ_TERMINAL_STATES, t as RelaySocket, u as RfqQuote, v as RfqRefusalReason, S as SOLO_REFUND_HEADROOM_SECONDS, w as SwapRefusal, x as arkadeSwapRequest, y as assertFundable, z as assertReceivable, B as awaitOnchainFill, D as buildHtlcClaim, E as buildHtlcRefund, F as claimOnchainFill, G as classifyOnchainHtlc, J as deriveLightningReceive, K as deriveOnchainReceive, N as deriveOnchainSend, P as extractPreimage, Q as httpTransport, T as lightningReceiveRequest, U as lightningSendRequest, V as lightningSendVtxoScript, W as newPreimage, X as newRfqId, Y as offerTermsFromQuote, Z as onchainHtlcScript, _ as onchainReceiveRequest, $ as onchainSendRequest, a0 as paymentHashOf, a1 as receiveVtxoScript, a2 as relayTransport, a3 as requestLightningReceive, a4 as requestLightningSend, a5 as requestOnchainReceive, a6 as requestOnchainSend, a7 as rfqPair, a8 as unilateralClaimDelay, a9 as unilateralRefundDelay, aa as unilateralRefundWithoutReceiverDelay, ab as verifyLockupAddress, ac as verifyReceiveInvoice } from './rfq-3jWha5xA.cjs';
|
|
231
7
|
|
|
232
8
|
/** The contracts — pure data, shared verbatim with any other implementation. */
|
|
233
9
|
declare const swapPrograms: Record<"wantAsset" | "wantBtc", ReturnType<typeof arkade.parseArtifact>>;
|
|
@@ -2010,4 +1786,43 @@ interface RfqSwapOutcome {
|
|
|
2010
1786
|
txid?: string;
|
|
2011
1787
|
}
|
|
2012
1788
|
|
|
2013
|
-
|
|
1789
|
+
/**
|
|
1790
|
+
* One swap, flattened to what grouping needs: an identity, a corridor, an
|
|
1791
|
+
* outcome, and every Arkade transaction that belongs to it.
|
|
1792
|
+
*
|
|
1793
|
+
* Deliberately not a stored swap record itself — resolution should stay
|
|
1794
|
+
* testable with plain data rather than a repository. A correlation helper
|
|
1795
|
+
* that derives these from the record store and the funding lockup's VTXOs
|
|
1796
|
+
* lands separately.
|
|
1797
|
+
*/
|
|
1798
|
+
interface SwapActivityInput {
|
|
1799
|
+
rfqId: string;
|
|
1800
|
+
/**
|
|
1801
|
+
* Literal union rather than `RfqSwapRecord["kind"]` — `RfqSwapRecord` lives
|
|
1802
|
+
* only on the unmerged rfq-persistence branch, not on master. Reconcile
|
|
1803
|
+
* with `RfqSwapRecord["kind"]` once that branch lands.
|
|
1804
|
+
*/
|
|
1805
|
+
kind: "lightning_send" | "lightning_receive" | "onchain_send";
|
|
1806
|
+
state: RfqSwapState;
|
|
1807
|
+
/** Funding, claim and refund txids, in whatever order. */
|
|
1808
|
+
txids: readonly string[];
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* Group each RFQ swap's transactions into one activity carrying its outcome.
|
|
1812
|
+
*
|
|
1813
|
+
* Without this a failed swap renders as two unrelated rows: the send that
|
|
1814
|
+
* funded the lockup, and the receive when the covenant refunds. Grouping by
|
|
1815
|
+
* `rfqId` collapses them into one activity, and the amount comes out correct
|
|
1816
|
+
* by netting, not by `buildActivities`'s same-key change exclusion — that
|
|
1817
|
+
* rule only fires when one txid is both sent and received, and funding and
|
|
1818
|
+
* refund are different txids. Summing the signed amounts
|
|
1819
|
+
* (`-funding + refund ≈ -fees`) is what does the work here.
|
|
1820
|
+
*
|
|
1821
|
+
* `prepare` loads once and `resolve` stays pure and synchronous, as the SDK's
|
|
1822
|
+
* `ActivityResolver` contract requires.
|
|
1823
|
+
*/
|
|
1824
|
+
declare function swapActivityResolver(deps: {
|
|
1825
|
+
listSwaps(): Promise<readonly SwapActivityInput[]>;
|
|
1826
|
+
}): ActivityResolver;
|
|
1827
|
+
|
|
1828
|
+
export { type ArkadeRefundResult, AssetSwap, AssetSwapRepository, ChainSource, ChainUtxo, type ClaimArkProvider, type ClaimPacketInput, type DiscoverMarketsOptions, IndexedDbAssetSwapRepository, type LightningReceiveSwap, type LightningSendSwap, LockupAmountMismatchError, type LockupContractWriter, type LockupFate, LockupNeedsRecoveryError, LockupRegistrationFailed, type LockupSpendIndexer, type LockupVtxo, MarketsCacheEntry, OFFER_PACKET_TYPE, type Offer, type OfferContractRetirer, type OfferSwapWatcher, OnchainHtlc, OnchainHtlcPhase, type OnchainSendAction, type OnchainSendSwap, type PlanError, QUOTE_OPTIONS, REFUND_MTP_LAG_SECONDS, RFQ_RESOLVED_STATES, RFQ_SWAP_TERMINAL_STATES, type RefundArkProvider, type RefundBlockedReason, type RefundIndexer, RefundNotLocallyPossibleError, type RefundOutcome, type RestoreIndexer, RfqStatus, type RfqSwap, type RfqSwapActionName, type RfqSwapLockup, RfqSwapManager, type RfqSwapManagerCallbacks, type RfqSwapManagerConfig, type RfqSwapManagerDeps, type RfqSwapManagerEvents, type RfqSwapOutcome, type RfqSwapState, RfqTransport, SWAP_LOCKUP_CONTRACT_KIND, SWAP_LOCKUP_CONTRACT_LABEL, SWAP_LOCKUP_CONTRACT_TYPE, type SealedClaimPacket, type SpendKind, type SwapActivityInput, type SwapContractRegistry, type Tx, type WatchOfferSwapsParams, awaitLockupFunding, awaitRfqResolution, cancelOffer, claimReceiveLockup, classifyDepositSpend, classifySpend, createOffer, decodeOffer, discoverMarkets, encodeOffer, findLockupVtxos, findMarket, isRfqSwapTerminal, isRfqTerminal, makeCachedFeedFetch, nextOnchainAction, offerVtxoScript, pushClaim, pushRefundWithoutReceiver, readLockupFate, refundIfUnresolved, registerLockupContract, restoreAssetSwaps, retireSettledOfferContracts, sealClaimPacket, senderIdentityForSwapRecord, spendTxidsOf, spendUpdate, swapActivityResolver, swapPrograms, validatePlan, watchOfferSwaps };
|