@arkade-os/swap 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1102 -1101
- package/dist/{chunk-IRDBAAJZ.js → chunk-D7GDHZCG.js} +1040 -96
- package/dist/index.cjs +223 -38
- package/dist/index.d.cts +16 -8
- package/dist/index.d.ts +16 -8
- package/dist/index.js +156 -862
- package/dist/nostr.cjs +108 -3
- package/dist/nostr.d.cts +1 -1
- package/dist/nostr.d.ts +1 -1
- package/dist/nostr.js +1 -1
- package/dist/repositories/realm/index.d.cts +2 -2
- package/dist/repositories/realm/index.d.ts +2 -2
- package/dist/repositories/sqlite/index.d.cts +2 -2
- package/dist/repositories/sqlite/index.d.ts +2 -2
- package/dist/{repository-DH5zjdw4.d.ts → repository-B6VPDGHC.d.ts} +4 -1
- package/dist/{repository-rwU1Sv2-.d.cts → repository-BXIcmIIC.d.cts} +4 -1
- package/dist/{rfq-DirJn_4k.d.ts → rfq-D96aLvCA.d.cts} +123 -10
- package/dist/{rfq-DirJn_4k.d.cts → rfq-D96aLvCA.d.ts} +123 -10
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ARKADE_ASSET,
|
|
3
3
|
ARKADE_BTC,
|
|
4
|
+
ASSET_CARRIER_SATS,
|
|
4
5
|
AddressMismatch,
|
|
6
|
+
BTC_ASSET_ID,
|
|
5
7
|
CLAIM_PACKET_TYPE,
|
|
6
8
|
L1_NETWORKS,
|
|
7
9
|
LIGHTNING_BTC,
|
|
@@ -13,6 +15,8 @@ import {
|
|
|
13
15
|
MAX_MIN_CONFIRMATIONS,
|
|
14
16
|
MIN_CLAIM_WINDOW_SECONDS,
|
|
15
17
|
MIN_HEADROOM_SECONDS,
|
|
18
|
+
OFFER_CONTRACT_KIND,
|
|
19
|
+
OFFER_PACKET_TYPE,
|
|
16
20
|
ONCHAIN_BTC,
|
|
17
21
|
ONCHAIN_CLAIM_MARGIN_SECONDS,
|
|
18
22
|
ONCHAIN_DUST_SATS,
|
|
@@ -20,6 +24,8 @@ import {
|
|
|
20
24
|
ONCHAIN_RECEIVE_PAIR,
|
|
21
25
|
ONCHAIN_SECONDS_PER_BLOCK,
|
|
22
26
|
ONCHAIN_SEND_PAIR,
|
|
27
|
+
PreimageNotRecoverableError,
|
|
28
|
+
RETIRABLE,
|
|
23
29
|
RFQ_REFUSAL_ERROR_CODES,
|
|
24
30
|
RFQ_TERMINAL_STATES,
|
|
25
31
|
SEALED_CIPHERTEXT_LENGTH,
|
|
@@ -28,21 +34,31 @@ import {
|
|
|
28
34
|
SWAP_LOCKUP_CONTRACT_LABEL,
|
|
29
35
|
SWAP_LOCKUP_CONTRACT_TYPE,
|
|
30
36
|
SwapRefusal,
|
|
37
|
+
addAssetSwap,
|
|
31
38
|
appendArkadeScript,
|
|
32
39
|
arkadeAssetLeg,
|
|
33
40
|
arkadeSwapRequest,
|
|
41
|
+
assertArkadeFundable,
|
|
34
42
|
assertFundable,
|
|
35
43
|
assertReceivable,
|
|
36
44
|
awaitOnchainFill,
|
|
37
45
|
buildHtlcClaim,
|
|
38
46
|
buildHtlcRefund,
|
|
47
|
+
cancelOffer,
|
|
48
|
+
canonicalAssetAmount,
|
|
39
49
|
claimOnchainFill,
|
|
40
50
|
claimPacketShape,
|
|
41
51
|
classifyOnchainHtlc,
|
|
52
|
+
createOffer,
|
|
53
|
+
decodeOffer,
|
|
42
54
|
deriveLightningReceive,
|
|
43
55
|
deriveOnchainReceive,
|
|
44
56
|
deriveOnchainSend,
|
|
57
|
+
encodeOffer,
|
|
45
58
|
extractPreimage,
|
|
59
|
+
fillOffer,
|
|
60
|
+
getAssetSwaps,
|
|
61
|
+
getAssetSwapsOrThrow,
|
|
46
62
|
httpTransport,
|
|
47
63
|
isRfqRefusalErrorCode,
|
|
48
64
|
l1ScriptForAddress,
|
|
@@ -52,806 +68,51 @@ import {
|
|
|
52
68
|
lockupContractParams,
|
|
53
69
|
newPreimage,
|
|
54
70
|
newRfqId,
|
|
71
|
+
normalizeMakerPkScript,
|
|
72
|
+
normalizeMakerPublicKey,
|
|
55
73
|
offerTermsFromQuote,
|
|
74
|
+
offerVtxoScript,
|
|
56
75
|
onchainHtlcScript,
|
|
57
76
|
onchainReceiveRequest,
|
|
58
77
|
onchainSendRequest,
|
|
59
78
|
paymentHashOf,
|
|
79
|
+
preimageForSwapRecord,
|
|
60
80
|
receiveVtxoScript,
|
|
61
81
|
registerLockupContract,
|
|
62
82
|
relayTransport,
|
|
83
|
+
requestArkadeSwap,
|
|
63
84
|
requestLightningReceive,
|
|
64
85
|
requestLightningSend,
|
|
65
86
|
requestOnchainReceive,
|
|
66
87
|
requestOnchainSend,
|
|
88
|
+
restoreOfferCoverage,
|
|
89
|
+
retireOfferContract,
|
|
90
|
+
retireSettledOfferContracts,
|
|
67
91
|
rfqPair,
|
|
68
92
|
sealClaimPacket,
|
|
93
|
+
swapPrograms,
|
|
94
|
+
swapSecretsToRecord,
|
|
69
95
|
unilateralClaimDelay,
|
|
70
96
|
unilateralRefundDelay,
|
|
71
97
|
unilateralRefundWithoutReceiverDelay,
|
|
98
|
+
updateAssetSwap,
|
|
99
|
+
updateAssetSwapBestEffort,
|
|
72
100
|
verifyLockupAddress,
|
|
101
|
+
verifyOfferAddress,
|
|
73
102
|
verifyReceiveInvoice
|
|
74
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-D7GDHZCG.js";
|
|
75
104
|
import {
|
|
76
105
|
InMemoryAssetSwapRepository,
|
|
77
106
|
marketsCacheKey
|
|
78
107
|
} from "./chunk-6ZUS47GA.js";
|
|
79
108
|
|
|
80
|
-
// src/offer.ts
|
|
81
|
-
import { hex as hex2 } from "@scure/base";
|
|
82
|
-
import { concatBytes } from "@scure/btc-signer/utils.js";
|
|
83
|
-
import {
|
|
84
|
-
ASSET_CARRIER_SATS as SDK_ASSET_CARRIER_SATS,
|
|
85
|
-
ArkAddress,
|
|
86
|
-
RestArkProvider,
|
|
87
|
-
RestEmulatorProvider,
|
|
88
|
-
RestIndexerProvider,
|
|
89
|
-
arkade,
|
|
90
|
-
asset,
|
|
91
|
-
getNetwork,
|
|
92
|
-
resolveEmulatorPubkey,
|
|
93
|
-
toXOnlySignerHex
|
|
94
|
-
} from "@arkade-os/sdk";
|
|
95
|
-
|
|
96
|
-
// src/swap-want-asset.program.json
|
|
97
|
-
var swap_want_asset_program_default = {
|
|
98
|
-
version: 0,
|
|
99
|
-
name: "banco-btc-to-asset",
|
|
100
|
-
params: [
|
|
101
|
-
{ name: "makerWP", type: "pubkey" },
|
|
102
|
-
{ name: "wantAmount", type: "int" },
|
|
103
|
-
{ name: "wantAssetTxid", type: "hash" },
|
|
104
|
-
{ name: "wantAssetGroupIndex", type: "int" },
|
|
105
|
-
{ name: "server", type: "pubkey" },
|
|
106
|
-
{ name: "user", type: "pubkey" }
|
|
107
|
-
],
|
|
108
|
-
functions: {
|
|
109
|
-
fulfill: {
|
|
110
|
-
tapscript: { signers: ["$server"] },
|
|
111
|
-
arkadeScript: {
|
|
112
|
-
asm: [
|
|
113
|
-
0,
|
|
114
|
-
"$wantAssetTxid",
|
|
115
|
-
"$wantAssetGroupIndex",
|
|
116
|
-
"INSPECTOUTASSETLOOKUP",
|
|
117
|
-
"VERIFY",
|
|
118
|
-
"$wantAmount",
|
|
119
|
-
"GREATERTHANOREQUAL",
|
|
120
|
-
"VERIFY",
|
|
121
|
-
0,
|
|
122
|
-
"INSPECTOUTPUTSCRIPTPUBKEY",
|
|
123
|
-
1,
|
|
124
|
-
"EQUALVERIFY",
|
|
125
|
-
"$makerWP",
|
|
126
|
-
"EQUAL"
|
|
127
|
-
]
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
cancel: {
|
|
131
|
-
tapscript: { signers: ["$user", "$server"] }
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// src/swap-want-btc.program.json
|
|
137
|
-
var swap_want_btc_program_default = {
|
|
138
|
-
version: 0,
|
|
139
|
-
name: "banco-asset-to-btc",
|
|
140
|
-
params: [
|
|
141
|
-
{ name: "makerWP", type: "pubkey" },
|
|
142
|
-
{ name: "wantAmount", type: "int" },
|
|
143
|
-
{ name: "server", type: "pubkey" },
|
|
144
|
-
{ name: "user", type: "pubkey" }
|
|
145
|
-
],
|
|
146
|
-
functions: {
|
|
147
|
-
fulfill: {
|
|
148
|
-
tapscript: { signers: ["$server"] },
|
|
149
|
-
arkadeScript: {
|
|
150
|
-
asm: [
|
|
151
|
-
0,
|
|
152
|
-
"INSPECTOUTPUTVALUE",
|
|
153
|
-
"$wantAmount",
|
|
154
|
-
"GREATERTHANOREQUAL",
|
|
155
|
-
"VERIFY",
|
|
156
|
-
0,
|
|
157
|
-
"INSPECTOUTPUTSCRIPTPUBKEY",
|
|
158
|
-
1,
|
|
159
|
-
"EQUALVERIFY",
|
|
160
|
-
"$makerWP",
|
|
161
|
-
"EQUAL"
|
|
162
|
-
]
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
cancel: {
|
|
166
|
-
tapscript: { signers: ["$user", "$server"] }
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
// src/coverage.ts
|
|
172
|
-
var RETIRABLE = ["fulfilled", "cancelled"];
|
|
173
|
-
var issuedAt = /* @__PURE__ */ new Map();
|
|
174
|
-
var inFlight = /* @__PURE__ */ new Map();
|
|
175
|
-
async function serialize(script, task) {
|
|
176
|
-
const previous = inFlight.get(script) ?? Promise.resolve();
|
|
177
|
-
const result = previous.then(task, task);
|
|
178
|
-
const settled = result.then(
|
|
179
|
-
() => {
|
|
180
|
-
},
|
|
181
|
-
() => {
|
|
182
|
-
}
|
|
183
|
-
);
|
|
184
|
-
inFlight.set(script, settled);
|
|
185
|
-
void settled.then(() => {
|
|
186
|
-
if (inFlight.get(script) === settled) inFlight.delete(script);
|
|
187
|
-
});
|
|
188
|
-
return result;
|
|
189
|
-
}
|
|
190
|
-
function addressOutstanding(swaps, script) {
|
|
191
|
-
const issued = issuedAt.get(script);
|
|
192
|
-
if (issued === void 0) return false;
|
|
193
|
-
if (swaps.some((s) => s.swapPkScript === script && s.createdAt >= issued)) {
|
|
194
|
-
issuedAt.delete(script);
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
return true;
|
|
198
|
-
}
|
|
199
|
-
async function promoteOfferContract(manager, script, issued = Date.now()) {
|
|
200
|
-
await serialize(script, async () => {
|
|
201
|
-
await manager.setContractWatchState(script, "watched");
|
|
202
|
-
issuedAt.set(script, issued);
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
async function retireOfferContract(manager, swaps, script) {
|
|
206
|
-
await serialize(script, async () => {
|
|
207
|
-
if (swaps.some((s) => s.swapPkScript === script && !RETIRABLE.includes(s.status))) return;
|
|
208
|
-
if (addressOutstanding(swaps, script)) return;
|
|
209
|
-
try {
|
|
210
|
-
await manager.setContractWatchState(script, "retained");
|
|
211
|
-
} catch (err) {
|
|
212
|
-
console.warn(`[swap] could not retire offer contract ${script}`, err);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
async function retireSettledOfferContracts(manager, swaps) {
|
|
217
|
-
const settled = new Set(
|
|
218
|
-
swaps.filter((s) => RETIRABLE.includes(s.status)).map((s) => s.swapPkScript)
|
|
219
|
-
);
|
|
220
|
-
for (const script of settled) await retireOfferContract(manager, swaps, script);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// src/store.ts
|
|
224
|
-
import { hex } from "@scure/base";
|
|
225
|
-
import { sha256 } from "@noble/hashes/sha2.js";
|
|
226
|
-
import { contractPreimage } from "@arkade-os/sdk";
|
|
227
|
-
var BTC_ASSET_ID = "btc";
|
|
228
|
-
var byNewest = (a, b) => b.createdAt - a.createdAt;
|
|
229
|
-
var getAssetSwapsOrThrow = async (repository) => {
|
|
230
|
-
return (await repository.getAllSwaps()).filter(
|
|
231
|
-
(s) => s && typeof s.id === "string" && // offer swaps carry the TLV; onchain-corridor swaps carry
|
|
232
|
-
// the payment hash instead — either marks a valid record
|
|
233
|
-
(typeof s.offerHex === "string" || typeof s.paymentHash === "string")
|
|
234
|
-
).sort(byNewest);
|
|
235
|
-
};
|
|
236
|
-
var getAssetSwaps = async (repository) => {
|
|
237
|
-
try {
|
|
238
|
-
return await getAssetSwapsOrThrow(repository);
|
|
239
|
-
} catch {
|
|
240
|
-
return [];
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
var saveSwapOrThrow = async (repository, swap) => {
|
|
244
|
-
try {
|
|
245
|
-
await repository.saveSwap(swap);
|
|
246
|
-
} catch (error) {
|
|
247
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
248
|
-
throw new Error(`failed to save swap ${swap.id}: ${reason}`);
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
var addAssetSwap = async (repository, swap) => {
|
|
252
|
-
const swaps = await getAssetSwapsOrThrow(repository);
|
|
253
|
-
if (swaps.some((s) => s.id === swap.id)) return swaps;
|
|
254
|
-
await saveSwapOrThrow(repository, swap);
|
|
255
|
-
const at = swaps.findIndex((s) => byNewest(swap, s) <= 0);
|
|
256
|
-
const merged = [...swaps];
|
|
257
|
-
merged.splice(at === -1 ? merged.length : at, 0, swap);
|
|
258
|
-
return merged;
|
|
259
|
-
};
|
|
260
|
-
var updateAssetSwap = async (repository, id, changes) => {
|
|
261
|
-
const swaps = (await getAssetSwapsOrThrow(repository)).map(
|
|
262
|
-
(s) => s.id === id ? { ...s, ...changes } : s
|
|
263
|
-
);
|
|
264
|
-
const updated = swaps.find((s) => s.id === id);
|
|
265
|
-
if (updated) await saveSwapOrThrow(repository, updated);
|
|
266
|
-
return swaps;
|
|
267
|
-
};
|
|
268
|
-
var updateAssetSwapBestEffort = async (repository, id, changes) => {
|
|
269
|
-
try {
|
|
270
|
-
return { swaps: await updateAssetSwap(repository, id, changes), persisted: true };
|
|
271
|
-
} catch (error) {
|
|
272
|
-
console.warn(`[swap] failed to persist update for swap ${id}`, error);
|
|
273
|
-
const swaps = (await getAssetSwaps(repository)).map(
|
|
274
|
-
(s) => s.id === id ? { ...s, ...changes } : s
|
|
275
|
-
);
|
|
276
|
-
return { swaps, persisted: false };
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
var swapSecretsToRecord = (secrets) => ({
|
|
280
|
-
signingDescriptor: secrets.descriptor,
|
|
281
|
-
..."mustPersistPreimage" in secrets && secrets.mustPersistPreimage ? { preimageHex: hex.encode(secrets.preimage) } : {},
|
|
282
|
-
..."preimageSalt" in secrets && secrets.preimageSalt ? { preimageSaltHex: hex.encode(secrets.preimageSalt) } : {}
|
|
283
|
-
});
|
|
284
|
-
var decodeHex32 = (value, field) => {
|
|
285
|
-
const bytes = hex.decode(value);
|
|
286
|
-
if (bytes.length !== 32) {
|
|
287
|
-
throw new Error(`${field} must be 32 bytes, got ${bytes.length}`);
|
|
288
|
-
}
|
|
289
|
-
return bytes;
|
|
290
|
-
};
|
|
291
|
-
var PreimageNotRecoverableError = class extends Error {
|
|
292
|
-
constructor(reason, message, options) {
|
|
293
|
-
super(message, options);
|
|
294
|
-
this.reason = reason;
|
|
295
|
-
}
|
|
296
|
-
reason;
|
|
297
|
-
name = "PreimageNotRecoverableError";
|
|
298
|
-
};
|
|
299
|
-
var preimageForSwapRecord = async (wallet, record) => {
|
|
300
|
-
if (!record.signingDescriptor) {
|
|
301
|
-
throw new PreimageNotRecoverableError(
|
|
302
|
-
"no-secrets",
|
|
303
|
-
"this swap record carries no signing descriptor"
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
let stored;
|
|
307
|
-
let salt;
|
|
308
|
-
try {
|
|
309
|
-
stored = record.preimageHex ? decodeHex32(record.preimageHex, "preimageHex") : void 0;
|
|
310
|
-
salt = record.preimageSaltHex ? decodeHex32(record.preimageSaltHex, "preimageSaltHex") : void 0;
|
|
311
|
-
} catch (cause) {
|
|
312
|
-
throw new PreimageNotRecoverableError(
|
|
313
|
-
"malformed-record",
|
|
314
|
-
`this swap record's secrets projection is unreadable: ${String(cause)}`,
|
|
315
|
-
{ cause }
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
let preimage;
|
|
319
|
-
try {
|
|
320
|
-
preimage = await contractPreimage(wallet, record.signingDescriptor, { stored, salt });
|
|
321
|
-
} catch (cause) {
|
|
322
|
-
throw new PreimageNotRecoverableError(
|
|
323
|
-
"not-derivable",
|
|
324
|
-
`this wallet cannot produce the preimage for ${record.signingDescriptor}`,
|
|
325
|
-
{ cause }
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
if (record.paymentHash && hex.encode(sha256(preimage)) !== record.paymentHash.toLowerCase()) {
|
|
329
|
-
throw new PreimageNotRecoverableError(
|
|
330
|
-
"hash-mismatch",
|
|
331
|
-
"the derived preimage does not match this swap's payment hash: wrong wallet, or a tampered salt"
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
return preimage;
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
// src/offer.ts
|
|
338
|
-
var swapPrograms = {
|
|
339
|
-
wantAsset: arkade.parseArtifact(swap_want_asset_program_default),
|
|
340
|
-
wantBtc: arkade.parseArtifact(swap_want_btc_program_default)
|
|
341
|
-
};
|
|
342
|
-
function withExitClosure(program, exit) {
|
|
343
|
-
if (!exit) return program;
|
|
344
|
-
return {
|
|
345
|
-
...program,
|
|
346
|
-
// typed params are authoritative: an undeclared `$exitDelay` fails
|
|
347
|
-
// validateProgram instead of compiling against an unbound value
|
|
348
|
-
params: [...program.params ?? [], { name: "exitDelay", type: "int" }],
|
|
349
|
-
functions: {
|
|
350
|
-
...program.functions,
|
|
351
|
-
exit: {
|
|
352
|
-
tapscript: { signers: ["$user"], csv: { type: exit.type, value: "$exitDelay" } }
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
function swapProgramBinding(offer, serverPubkey) {
|
|
358
|
-
if (offer.makerPkScript.length !== FIELDS.makerPkScript.width) {
|
|
359
|
-
throw new Error("makerPkScript is not a 34-byte taproot scriptPubKey");
|
|
360
|
-
}
|
|
361
|
-
return {
|
|
362
|
-
program: withExitClosure(
|
|
363
|
-
offer.wantAsset ? swapPrograms.wantAsset : swapPrograms.wantBtc,
|
|
364
|
-
offer.exitDelay
|
|
365
|
-
),
|
|
366
|
-
args: {
|
|
367
|
-
makerWP: offer.makerPkScript.subarray(2),
|
|
368
|
-
wantAmount: offer.wantAmount,
|
|
369
|
-
server: serverPubkey,
|
|
370
|
-
user: offer.makerPublicKey,
|
|
371
|
-
// internal byte order
|
|
372
|
-
...offer.wantAsset && {
|
|
373
|
-
wantAssetTxid: offer.wantAsset.txid.slice().reverse(),
|
|
374
|
-
wantAssetGroupIndex: offer.wantAsset.groupIndex
|
|
375
|
-
},
|
|
376
|
-
...offer.exitDelay && { exitDelay: offer.exitDelay.value }
|
|
377
|
-
},
|
|
378
|
-
keys: {
|
|
379
|
-
serverKey: serverPubkey,
|
|
380
|
-
userKey: offer.makerPublicKey,
|
|
381
|
-
emulatorKey: offer.emulatorPubkey
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
function offerVtxoScript(offer, serverPubkey) {
|
|
386
|
-
const { program, args, keys } = swapProgramBinding(offer, serverPubkey);
|
|
387
|
-
return new arkade.ArkadeProgramScript(program, args, keys);
|
|
388
|
-
}
|
|
389
|
-
var OFFER_PACKET_TYPE = 3;
|
|
390
|
-
var FIELDS = {
|
|
391
|
-
swapPkScript: { tag: 1, width: 34 },
|
|
392
|
-
wantAmount: { tag: 2, width: 8 },
|
|
393
|
-
wantAsset: { tag: 3, width: void 0 },
|
|
394
|
-
makerPkScript: { tag: 5, width: 34 },
|
|
395
|
-
makerPublicKey: { tag: 7, width: 32 },
|
|
396
|
-
emulatorPubkey: { tag: 8, width: 32 },
|
|
397
|
-
ratioNum: { tag: 9, width: 8 },
|
|
398
|
-
ratioDen: { tag: 10, width: 8 },
|
|
399
|
-
offerAsset: { tag: 11, width: void 0 },
|
|
400
|
-
exitTimelock: { tag: 12, width: 9 }
|
|
401
|
-
};
|
|
402
|
-
var EXIT_TYPES = ["blocks", "seconds"];
|
|
403
|
-
var NAMES = Object.fromEntries(Object.entries(FIELDS).map(([k, f]) => [f.tag, k]));
|
|
404
|
-
function u64(name, value) {
|
|
405
|
-
if (value < BigInt(0) || value >> BigInt(64) > BigInt(0)) {
|
|
406
|
-
throw new Error(`${name} does not fit the offer wire format (u64)`);
|
|
407
|
-
}
|
|
408
|
-
const out = new Uint8Array(8);
|
|
409
|
-
new DataView(out.buffer).setBigUint64(0, value, false);
|
|
410
|
-
return out;
|
|
411
|
-
}
|
|
412
|
-
var readU64 = (value) => new DataView(value.buffer, value.byteOffset).getBigUint64(0, false);
|
|
413
|
-
var setRatio = (name, value) => {
|
|
414
|
-
if (value === void 0 || value === BigInt(0)) return void 0;
|
|
415
|
-
if (value < BigInt(0)) throw new Error(`${name} does not fit the offer wire format (u64)`);
|
|
416
|
-
return value;
|
|
417
|
-
};
|
|
418
|
-
function tlv(type, value) {
|
|
419
|
-
if (value.length > 65535) throw new Error("TLV value exceeds the u16 length field");
|
|
420
|
-
return concatBytes(Uint8Array.of(type, value.length >> 8 & 255, value.length & 255), value);
|
|
421
|
-
}
|
|
422
|
-
function encodeOffer(offer) {
|
|
423
|
-
if (Boolean(offer.wantAsset) === Boolean(offer.offerAsset)) {
|
|
424
|
-
throw new Error("offer must carry exactly one of wantAsset or offerAsset");
|
|
425
|
-
}
|
|
426
|
-
for (const name of [
|
|
427
|
-
"swapPkScript",
|
|
428
|
-
"makerPkScript",
|
|
429
|
-
"makerPublicKey",
|
|
430
|
-
"emulatorPubkey"
|
|
431
|
-
]) {
|
|
432
|
-
if (offer[name].length !== FIELDS[name].width) {
|
|
433
|
-
throw new Error(`${name} must be ${FIELDS[name].width} bytes`);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
const ratioNum = setRatio("ratioNum", offer.ratioNum);
|
|
437
|
-
const ratioDen = setRatio("ratioDen", offer.ratioDen);
|
|
438
|
-
if (ratioNum === void 0 !== (ratioDen === void 0)) {
|
|
439
|
-
throw new Error("offer must carry both ratioNum and ratioDen, or neither");
|
|
440
|
-
}
|
|
441
|
-
const recs = [
|
|
442
|
-
tlv(FIELDS.swapPkScript.tag, offer.swapPkScript),
|
|
443
|
-
tlv(FIELDS.wantAmount.tag, u64("wantAmount", offer.wantAmount))
|
|
444
|
-
];
|
|
445
|
-
if (offer.wantAsset) recs.push(tlv(FIELDS.wantAsset.tag, offer.wantAsset.serialize()));
|
|
446
|
-
if (ratioNum !== void 0) recs.push(tlv(FIELDS.ratioNum.tag, u64("ratioNum", ratioNum)));
|
|
447
|
-
if (ratioDen !== void 0) recs.push(tlv(FIELDS.ratioDen.tag, u64("ratioDen", ratioDen)));
|
|
448
|
-
if (offer.offerAsset) recs.push(tlv(FIELDS.offerAsset.tag, offer.offerAsset.serialize()));
|
|
449
|
-
recs.push(
|
|
450
|
-
tlv(FIELDS.makerPkScript.tag, offer.makerPkScript),
|
|
451
|
-
tlv(FIELDS.makerPublicKey.tag, offer.makerPublicKey),
|
|
452
|
-
tlv(FIELDS.emulatorPubkey.tag, offer.emulatorPubkey)
|
|
453
|
-
);
|
|
454
|
-
if (offer.exitDelay) recs.push(tlv(FIELDS.exitTimelock.tag, encodeExitDelay(offer.exitDelay)));
|
|
455
|
-
return concatBytes(...recs);
|
|
456
|
-
}
|
|
457
|
-
function encodeExitDelay(exit) {
|
|
458
|
-
return concatBytes(
|
|
459
|
-
Uint8Array.of(EXIT_TYPES.indexOf(assertExitDelay(exit).type)),
|
|
460
|
-
u64("exitDelay", exit.value)
|
|
461
|
-
);
|
|
462
|
-
}
|
|
463
|
-
function assertExitDelay(exit) {
|
|
464
|
-
if (EXIT_TYPES.indexOf(exit.type) < 0) {
|
|
465
|
-
throw new Error(`unknown exitDelay locktime type: ${exit.type}`);
|
|
466
|
-
}
|
|
467
|
-
if (exit.value <= BigInt(0)) {
|
|
468
|
-
throw new Error("exitDelay must be a positive relative locktime");
|
|
469
|
-
}
|
|
470
|
-
if (exit.value >> BigInt(32) > BigInt(0)) {
|
|
471
|
-
throw new Error("exitDelay does not fit the locktime field (u32)");
|
|
472
|
-
}
|
|
473
|
-
return exit;
|
|
474
|
-
}
|
|
475
|
-
function decodeOffer(data) {
|
|
476
|
-
const fields = {};
|
|
477
|
-
let off = 0;
|
|
478
|
-
while (off < data.length) {
|
|
479
|
-
if (off + 3 > data.length) throw new Error("truncated TLV header");
|
|
480
|
-
const type = data[off];
|
|
481
|
-
const length = data[off + 1] << 8 | data[off + 2];
|
|
482
|
-
off += 3;
|
|
483
|
-
if (off + length > data.length)
|
|
484
|
-
throw new Error(`truncated TLV value for type 0x${type.toString(16)}`);
|
|
485
|
-
const name = NAMES[type];
|
|
486
|
-
if (!name) throw new Error(`unknown TLV type: 0x${type.toString(16)}`);
|
|
487
|
-
if (fields[name] !== void 0) throw new Error(`duplicate TLV record: ${name}`);
|
|
488
|
-
fields[name] = data.slice(off, off + length);
|
|
489
|
-
off += length;
|
|
490
|
-
}
|
|
491
|
-
for (const name of ["wantAsset", "offerAsset"]) {
|
|
492
|
-
if (fields[name]?.length === 0) throw new Error(`missing/invalid ${name}`);
|
|
493
|
-
}
|
|
494
|
-
for (const [name, value] of Object.entries(fields)) {
|
|
495
|
-
const width = FIELDS[name].width;
|
|
496
|
-
if (width !== void 0 && value.length !== width) {
|
|
497
|
-
throw new Error(`missing/invalid ${name}`);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
const need = (name) => {
|
|
501
|
-
const v = fields[name];
|
|
502
|
-
const len = FIELDS[name].width;
|
|
503
|
-
if (!v || len !== void 0 && v.length !== len)
|
|
504
|
-
throw new Error(`missing/invalid ${name}`);
|
|
505
|
-
return v;
|
|
506
|
-
};
|
|
507
|
-
const amount = need("wantAmount");
|
|
508
|
-
if (Boolean(fields.wantAsset) === Boolean(fields.offerAsset)) {
|
|
509
|
-
throw new Error("offer must carry exactly one of wantAsset or offerAsset");
|
|
510
|
-
}
|
|
511
|
-
const readRatio = (name) => {
|
|
512
|
-
const raw = fields[name];
|
|
513
|
-
if (!raw) return void 0;
|
|
514
|
-
const value = readU64(raw);
|
|
515
|
-
if (value === BigInt(0)) throw new Error(`missing/invalid ${name}`);
|
|
516
|
-
return value;
|
|
517
|
-
};
|
|
518
|
-
const ratioNum = readRatio("ratioNum");
|
|
519
|
-
const ratioDen = readRatio("ratioDen");
|
|
520
|
-
if (ratioNum === void 0 !== (ratioDen === void 0)) {
|
|
521
|
-
throw new Error("offer must carry both ratioNum and ratioDen, or neither");
|
|
522
|
-
}
|
|
523
|
-
return {
|
|
524
|
-
swapPkScript: need("swapPkScript"),
|
|
525
|
-
wantAmount: readU64(amount),
|
|
526
|
-
...fields.wantAsset && { wantAsset: asset.AssetId.fromBytes(fields.wantAsset) },
|
|
527
|
-
...fields.offerAsset && { offerAsset: asset.AssetId.fromBytes(fields.offerAsset) },
|
|
528
|
-
makerPkScript: need("makerPkScript"),
|
|
529
|
-
makerPublicKey: need("makerPublicKey"),
|
|
530
|
-
emulatorPubkey: need("emulatorPubkey"),
|
|
531
|
-
...ratioNum !== void 0 && { ratioNum },
|
|
532
|
-
...ratioDen !== void 0 && { ratioDen },
|
|
533
|
-
...fields.exitTimelock && { exitDelay: decodeExitDelay(fields.exitTimelock) }
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
function decodeExitDelay(value) {
|
|
537
|
-
const type = EXIT_TYPES[value[0]];
|
|
538
|
-
if (!type) throw new Error(`unknown exitDelay locktime type: 0x${value[0].toString(16)}`);
|
|
539
|
-
return { type, value: readU64(value.subarray(1)) };
|
|
540
|
-
}
|
|
541
|
-
var OFFER_CONTRACT_LABEL = "Arkade swap offer";
|
|
542
|
-
var OFFER_CONTRACT_KIND = "asset-swap-offer";
|
|
543
|
-
async function registerOfferContract(wallet, arkServerUrl, network, binding, serverPubkey, expectedPkScript, opts = {}) {
|
|
544
|
-
const contractManager = opts.contractManager ?? await wallet.getContractManager();
|
|
545
|
-
const client = opts.client ?? await arkade.Arkade.connect({
|
|
546
|
-
arkade: new RestArkProvider(arkServerUrl),
|
|
547
|
-
indexer: new RestIndexerProvider(arkServerUrl),
|
|
548
|
-
identity: wallet.identity,
|
|
549
|
-
// without this the row's `address` would be derived against the SDK's
|
|
550
|
-
// default network while its script is right — a row that disagrees with
|
|
551
|
-
// the address the user is about to fund
|
|
552
|
-
network: getNetwork(network),
|
|
553
|
-
contractManager
|
|
554
|
-
});
|
|
555
|
-
await contractManager.createContract(
|
|
556
|
-
offerContractParams(client, binding, serverPubkey, expectedPkScript)
|
|
557
|
-
);
|
|
558
|
-
await promoteOfferContract(contractManager, hex2.encode(expectedPkScript), opts.issued);
|
|
559
|
-
}
|
|
560
|
-
function offerContractParams(client, binding, serverPubkey, expectedPkScript) {
|
|
561
|
-
const { program, args, keys } = swapProgramBinding(binding, serverPubkey);
|
|
562
|
-
const contract = new arkade.ArkadeContract(client, program, args, keys);
|
|
563
|
-
if (hex2.encode(contract.pkScript) !== hex2.encode(expectedPkScript)) {
|
|
564
|
-
throw new Error("derived covenant does not match the offer's swapPkScript");
|
|
565
|
-
}
|
|
566
|
-
return {
|
|
567
|
-
...contract.toContractParams(),
|
|
568
|
-
label: OFFER_CONTRACT_LABEL,
|
|
569
|
-
metadata: { genericallySpendable: false, kind: OFFER_CONTRACT_KIND }
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
async function restoreOfferCoverage(wallet, arkServerUrl, swaps) {
|
|
573
|
-
const live = swaps.filter((s) => !RETIRABLE.includes(s.status));
|
|
574
|
-
if (live.length === 0) return;
|
|
575
|
-
const arkProvider = new RestArkProvider(arkServerUrl);
|
|
576
|
-
const info = await arkProvider.getInfo();
|
|
577
|
-
const serverPubKey = hex2.decode(toXOnlySignerHex(info.signerPubkey));
|
|
578
|
-
const contractManager = await wallet.getContractManager();
|
|
579
|
-
const client = await arkade.Arkade.connect({
|
|
580
|
-
arkade: arkProvider,
|
|
581
|
-
indexer: new RestIndexerProvider(arkServerUrl),
|
|
582
|
-
identity: wallet.identity,
|
|
583
|
-
network: getNetwork(info.network),
|
|
584
|
-
contractManager
|
|
585
|
-
});
|
|
586
|
-
const done = /* @__PURE__ */ new Set();
|
|
587
|
-
const covenants = [];
|
|
588
|
-
for (const swap of live) {
|
|
589
|
-
if (done.has(swap.swapPkScript)) continue;
|
|
590
|
-
done.add(swap.swapPkScript);
|
|
591
|
-
try {
|
|
592
|
-
const { swapPkScript: _, ...binding } = decodeOffer(hex2.decode(swap.offerHex));
|
|
593
|
-
covenants.push({
|
|
594
|
-
script: swap.swapPkScript,
|
|
595
|
-
issued: swap.createdAt,
|
|
596
|
-
params: offerContractParams(
|
|
597
|
-
client,
|
|
598
|
-
binding,
|
|
599
|
-
serverPubKey,
|
|
600
|
-
hex2.decode(swap.swapPkScript)
|
|
601
|
-
)
|
|
602
|
-
});
|
|
603
|
-
} catch (err) {
|
|
604
|
-
console.warn(`[swap] could not restore coverage for ${swap.swapPkScript}`, err);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
if (covenants.length === 0) return;
|
|
608
|
-
const paramsList = covenants.map((c) => c.params);
|
|
609
|
-
if (contractManager.createContracts) {
|
|
610
|
-
await contractManager.createContracts(paramsList);
|
|
611
|
-
} else {
|
|
612
|
-
for (const params of paramsList) await contractManager.createContract(params);
|
|
613
|
-
}
|
|
614
|
-
for (const c of covenants) await promoteOfferContract(contractManager, c.script, c.issued);
|
|
615
|
-
}
|
|
616
|
-
function serverExitDelay(delay) {
|
|
617
|
-
if (typeof delay !== "bigint" || delay <= BigInt(0)) {
|
|
618
|
-
throw new Error(
|
|
619
|
-
"the server reports no usable unilateralExitDelay; pass `exitDelay` to set the offer's exit closure explicitly, or `noExit: true` to publish without one"
|
|
620
|
-
);
|
|
621
|
-
}
|
|
622
|
-
return assertExitDelay({ value: delay, type: delay < BigInt(512) ? "blocks" : "seconds" });
|
|
623
|
-
}
|
|
624
|
-
async function createOffer(wallet, arkServerUrl, params) {
|
|
625
|
-
if (Boolean(params.wantAsset) === Boolean(params.offerAsset)) {
|
|
626
|
-
throw new Error("set exactly one of wantAsset (BTC->asset) or offerAsset (asset->BTC)");
|
|
627
|
-
}
|
|
628
|
-
const [info, makerAddress, makerPublicKey] = await Promise.all([
|
|
629
|
-
new RestArkProvider(arkServerUrl).getInfo(),
|
|
630
|
-
wallet.getAddress(),
|
|
631
|
-
wallet.identity.xOnlyPublicKey()
|
|
632
|
-
]);
|
|
633
|
-
const serverPubKey = hex2.decode(toXOnlySignerHex(info.signerPubkey));
|
|
634
|
-
const network = getNetwork(info.network);
|
|
635
|
-
const emuKey = hex2.decode(
|
|
636
|
-
toXOnlySignerHex(resolveEmulatorPubkey(network, params.emulatorPubkey))
|
|
637
|
-
);
|
|
638
|
-
const binding = {
|
|
639
|
-
wantAmount: params.wantAmount,
|
|
640
|
-
wantAsset: params.wantAsset,
|
|
641
|
-
offerAsset: params.offerAsset,
|
|
642
|
-
makerPkScript: ArkAddress.decode(makerAddress).pkScript,
|
|
643
|
-
makerPublicKey,
|
|
644
|
-
emulatorPubkey: emuKey,
|
|
645
|
-
// checked HERE, before the covenant is derived and registered below:
|
|
646
|
-
// deferring it to `encodeOffer` leaves a registered contract behind for
|
|
647
|
-
// an offer that then fails to encode. @see assertExitDelay
|
|
648
|
-
exitDelay: params.noExit ? void 0 : params.exitDelay ? assertExitDelay(params.exitDelay) : serverExitDelay(info.unilateralExitDelay)
|
|
649
|
-
};
|
|
650
|
-
const script = offerVtxoScript(binding, serverPubKey);
|
|
651
|
-
const offer = { ...binding, swapPkScript: script.pkScript };
|
|
652
|
-
await registerOfferContract(
|
|
653
|
-
wallet,
|
|
654
|
-
arkServerUrl,
|
|
655
|
-
info.network,
|
|
656
|
-
binding,
|
|
657
|
-
serverPubKey,
|
|
658
|
-
script.pkScript
|
|
659
|
-
);
|
|
660
|
-
const payload = encodeOffer(offer);
|
|
661
|
-
return {
|
|
662
|
-
offerHex: hex2.encode(payload),
|
|
663
|
-
extension: { type: OFFER_PACKET_TYPE, payload },
|
|
664
|
-
// VtxoScript.address owns address construction; assembling an ArkAddress
|
|
665
|
-
// from tweakedPublicKey here would silently miss any future step it gains
|
|
666
|
-
address: script.address(network.hrp, serverPubKey).encode(),
|
|
667
|
-
swapPkScript: script.pkScript
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
async function cancelOffer(wallet, arkServerUrl, offerHex, opts) {
|
|
671
|
-
const { repository, fundingTxid, swapAddress } = opts;
|
|
672
|
-
const offer = decodeOffer(hex2.decode(offerHex));
|
|
673
|
-
const contractManager = await wallet.getContractManager();
|
|
674
|
-
const client = await arkade.Arkade.connect({
|
|
675
|
-
arkade: new RestArkProvider(arkServerUrl),
|
|
676
|
-
indexer: new RestIndexerProvider(arkServerUrl),
|
|
677
|
-
identity: wallet.identity,
|
|
678
|
-
// registered offers resolve their VTXOs from the contract repository
|
|
679
|
-
// instead of a direct indexer query; the indexer above stays as the
|
|
680
|
-
// fallback for offers created before registration existed
|
|
681
|
-
contractManager
|
|
682
|
-
// no `network`, unlike registerOfferContract: the row lookup is by
|
|
683
|
-
// script and the payout script comes from wallet.getAddress(), so the
|
|
684
|
-
// client's network (which only shapes address derivation) is unused here
|
|
685
|
-
});
|
|
686
|
-
const operatorPubkey = swapAddress ? ArkAddress.decode(swapAddress).serverPubKey : client.serverKey;
|
|
687
|
-
const { program, args, keys } = swapProgramBinding(offer, operatorPubkey);
|
|
688
|
-
const rebuilt = new arkade.ArkadeProgramScript(program, args, keys);
|
|
689
|
-
if (hex2.encode(rebuilt.pkScript) !== hex2.encode(offer.swapPkScript)) {
|
|
690
|
-
throw new Error(
|
|
691
|
-
"rebuilt covenant does not match the offer's swapPkScript \u2014 the server signing key has likely rotated since funding; pass swapAddress (the funded address) to pin the original key"
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
const contract = new arkade.ArkadeContract(client, program, args, keys);
|
|
695
|
-
const [vtxos, makerAddress] = await Promise.all([contract.getUtxos(), wallet.getAddress()]);
|
|
696
|
-
if (!fundingTxid && vtxos.length > 1) {
|
|
697
|
-
throw new Error(
|
|
698
|
-
"multiple spendable deposits at the swap address \u2014 pass fundingTxid to select one"
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
const vtxo = fundingTxid ? vtxos.find((v) => v.txid === fundingTxid) : vtxos[0];
|
|
702
|
-
if (!vtxo) throw new Error("no spendable VTXO at the swap address");
|
|
703
|
-
const makerPkScript = ArkAddress.decode(makerAddress).pkScript;
|
|
704
|
-
const cancel = contract.functions.cancel().from({ txid: vtxo.txid, vout: vtxo.vout, value: vtxo.value }).to(makerPkScript, BigInt(vtxo.value));
|
|
705
|
-
for (const a of vtxo.assets ?? []) {
|
|
706
|
-
cancel.withAsset({
|
|
707
|
-
assetId: a.assetId,
|
|
708
|
-
inputs: [{ vin: 0, amount: BigInt(a.amount) }],
|
|
709
|
-
outputs: [{ vout: 0, amount: BigInt(a.amount) }]
|
|
710
|
-
});
|
|
711
|
-
}
|
|
712
|
-
const swapId = fundingTxid ?? vtxo.txid;
|
|
713
|
-
const hasLocalRecord = (await getAssetSwapsOrThrow(repository)).some((s) => s.id === swapId);
|
|
714
|
-
if (hasLocalRecord) await updateAssetSwap(repository, swapId, { status: "cancelling" });
|
|
715
|
-
const { txid } = await cancel.send();
|
|
716
|
-
if (hasLocalRecord) {
|
|
717
|
-
const { persisted, swaps } = await updateAssetSwapBestEffort(repository, swapId, {
|
|
718
|
-
status: "cancelled",
|
|
719
|
-
spentTxid: txid
|
|
720
|
-
});
|
|
721
|
-
if (persisted) {
|
|
722
|
-
await retireOfferContract(contractManager, swaps, hex2.encode(offer.swapPkScript));
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
return txid;
|
|
726
|
-
}
|
|
727
|
-
var ASSET_CARRIER_SATS = BigInt(SDK_ASSET_CARRIER_SATS);
|
|
728
|
-
async function fillOffer(wallet, arkServerUrl, offerHex, opts) {
|
|
729
|
-
const {
|
|
730
|
-
fund,
|
|
731
|
-
payoutScript,
|
|
732
|
-
fundingTxid,
|
|
733
|
-
swapAddress,
|
|
734
|
-
assetCarrierSats = ASSET_CARRIER_SATS,
|
|
735
|
-
emulator,
|
|
736
|
-
emulatorPubkey
|
|
737
|
-
} = opts;
|
|
738
|
-
const offer = decodeOffer(hex2.decode(offerHex));
|
|
739
|
-
const wantedAssetId = offer.wantAsset?.toString();
|
|
740
|
-
if (fund.length === 0) {
|
|
741
|
-
throw new Error("fillOffer needs coins to pay wantAmount with \u2014 `fund` is empty");
|
|
742
|
-
}
|
|
743
|
-
if (wantedAssetId !== void 0) {
|
|
744
|
-
const supplied = fund.reduce(
|
|
745
|
-
(sum, coin) => sum + amountOfAsset(coin.assets, wantedAssetId),
|
|
746
|
-
BigInt(0)
|
|
747
|
-
);
|
|
748
|
-
if (supplied < offer.wantAmount) {
|
|
749
|
-
throw new Error(
|
|
750
|
-
`fillOffer needs ${offer.wantAmount} of ${wantedAssetId} to pay the maker, but \`fund\` declares ${supplied} \u2014 pass coins carrying it, and declare their assets`
|
|
751
|
-
);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
const contractManager = await wallet.getContractManager();
|
|
755
|
-
const client = await arkade.Arkade.connect({
|
|
756
|
-
arkade: new RestArkProvider(arkServerUrl),
|
|
757
|
-
indexer: new RestIndexerProvider(arkServerUrl),
|
|
758
|
-
identity: wallet.identity,
|
|
759
|
-
contractManager,
|
|
760
|
-
// `fulfill` is a covenant path, so the emulator executes the arkade
|
|
761
|
-
// script and finalizes with arkd. Without it the spend is built and then
|
|
762
|
-
// refused at submission — there is no default to fall back on.
|
|
763
|
-
emulator: typeof emulator === "string" ? new RestEmulatorProvider(emulator) : emulator,
|
|
764
|
-
// Only reached for the client's own emulatorKey, which this fill never
|
|
765
|
-
// derives against — the offer's own key is bound below. It still has to
|
|
766
|
-
// resolve, and on a network with no pinned key it throws without this.
|
|
767
|
-
...emulatorPubkey ? { emulatorPubkey } : {}
|
|
768
|
-
});
|
|
769
|
-
const serverKey = swapAddress ? ArkAddress.decode(swapAddress).serverPubKey : client.serverKey;
|
|
770
|
-
const { program, args, keys } = swapProgramBinding(offer, serverKey);
|
|
771
|
-
const rebuilt = new arkade.ArkadeProgramScript(program, args, keys);
|
|
772
|
-
if (hex2.encode(rebuilt.pkScript) !== hex2.encode(offer.swapPkScript)) {
|
|
773
|
-
throw new Error(
|
|
774
|
-
"rebuilt covenant does not match the offer's swapPkScript \u2014 the server signing key has likely rotated since funding; pass swapAddress (the funded address) to pin the original key"
|
|
775
|
-
);
|
|
776
|
-
}
|
|
777
|
-
const contract = new arkade.ArkadeContract(client, program, args, keys);
|
|
778
|
-
const [vtxos, takerAddress] = await Promise.all([contract.getUtxos(), wallet.getAddress()]);
|
|
779
|
-
if (!fundingTxid && vtxos.length > 1) {
|
|
780
|
-
throw new Error(
|
|
781
|
-
"multiple spendable deposits at the swap address \u2014 pass fundingTxid to select one"
|
|
782
|
-
);
|
|
783
|
-
}
|
|
784
|
-
const vtxo = fundingTxid ? vtxos.find((v) => v.txid === fundingTxid) : vtxos[0];
|
|
785
|
-
if (!vtxo) throw new Error("no spendable VTXO at the swap address");
|
|
786
|
-
if (offer.offerAsset) {
|
|
787
|
-
const offered = offer.offerAsset.toString();
|
|
788
|
-
const deposited = amountOfAsset(vtxo.assets, offered);
|
|
789
|
-
if (deposited <= BigInt(0)) {
|
|
790
|
-
throw new Error(
|
|
791
|
-
`the deposit at the swap address carries no ${offered}, which this offer sells \u2014 filling it would pay wantAmount for nothing. The indexer may be behind, or the offer may not be backed by what it advertises`
|
|
792
|
-
);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
const payout = payoutScript ?? ArkAddress.decode(takerAddress).pkScript;
|
|
796
|
-
const makerSats = wantedAssetId === void 0 ? offer.wantAmount : assetCarrierSats;
|
|
797
|
-
const fill = contract.functions.fulfill().from({ txid: vtxo.txid, vout: vtxo.vout, value: vtxo.value }).fund(fund).to(offer.makerPkScript, makerSats).change(payout);
|
|
798
|
-
const held = /* @__PURE__ */ new Map();
|
|
799
|
-
const hold = (vin, assets) => {
|
|
800
|
-
for (const a of assets ?? []) {
|
|
801
|
-
const amount = BigInt(a.amount);
|
|
802
|
-
if (amount <= BigInt(0)) continue;
|
|
803
|
-
held.set(a.assetId, [...held.get(a.assetId) ?? [], { vin, amount }]);
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
hold(0, vtxo.assets);
|
|
807
|
-
fund.forEach((coin, i) => hold(i + 1, coin.assets));
|
|
808
|
-
const outputsSum = makerSats;
|
|
809
|
-
const inputsSum = fund.reduce((s, c) => s + BigInt(c.value), BigInt(vtxo.value));
|
|
810
|
-
const hasPayoutOutput = inputsSum > outputsSum;
|
|
811
|
-
const emitWanted = () => {
|
|
812
|
-
if (wantedAssetId === void 0) return;
|
|
813
|
-
const supplying = held.get(wantedAssetId) ?? [];
|
|
814
|
-
const supplied = supplying.reduce((s, i) => s + i.amount, BigInt(0));
|
|
815
|
-
const outputs = [{ vout: 0, amount: offer.wantAmount }];
|
|
816
|
-
const surplus = supplied - offer.wantAmount;
|
|
817
|
-
if (surplus > BigInt(0)) {
|
|
818
|
-
if (!hasPayoutOutput) {
|
|
819
|
-
throw new Error(
|
|
820
|
-
`fillOffer has ${surplus} of ${wantedAssetId} to return but no payout output \u2014 fund with more sats than the maker's output takes`
|
|
821
|
-
);
|
|
822
|
-
}
|
|
823
|
-
outputs.push({ vout: 1, amount: surplus });
|
|
824
|
-
}
|
|
825
|
-
fill.withAsset({ assetId: wantedAssetId, inputs: supplying, outputs });
|
|
826
|
-
held.delete(wantedAssetId);
|
|
827
|
-
};
|
|
828
|
-
emitWanted();
|
|
829
|
-
for (const [assetId, inputs] of held) {
|
|
830
|
-
const amount = inputs.reduce((s, i) => s + i.amount, BigInt(0));
|
|
831
|
-
if (!hasPayoutOutput) {
|
|
832
|
-
throw new Error(
|
|
833
|
-
`fillOffer has ${amount} of ${assetId} to return but no payout output \u2014 fund with more sats than the maker's output takes`
|
|
834
|
-
);
|
|
835
|
-
}
|
|
836
|
-
fill.withAsset({ assetId, inputs, outputs: [{ vout: 1, amount }] });
|
|
837
|
-
}
|
|
838
|
-
const { txid } = await fill.send();
|
|
839
|
-
return txid;
|
|
840
|
-
}
|
|
841
|
-
var amountOfAsset = (assets, assetId) => {
|
|
842
|
-
let total = BigInt(0);
|
|
843
|
-
for (const a of assets ?? []) {
|
|
844
|
-
if (a.assetId === assetId) total += BigInt(a.amount);
|
|
845
|
-
}
|
|
846
|
-
return total;
|
|
847
|
-
};
|
|
848
|
-
|
|
849
109
|
// src/markets.ts
|
|
850
110
|
import {
|
|
851
111
|
bestMarket,
|
|
852
112
|
discover,
|
|
853
113
|
isNetwork,
|
|
854
114
|
marketLegKey,
|
|
115
|
+
registryIndexUrl,
|
|
855
116
|
sideLimits
|
|
856
117
|
} from "@arkade-os/solver-discovery";
|
|
857
118
|
import { isSubdust } from "@arkade-os/sdk";
|
|
@@ -866,9 +127,9 @@ var LEGACY_CORRIDORS = {
|
|
|
866
127
|
};
|
|
867
128
|
var assetOf = (market, side) => side === "base" ? market.base_asset : market.quote_asset;
|
|
868
129
|
var marketAssetId = (market, side) => {
|
|
869
|
-
const
|
|
870
|
-
if (typeof
|
|
871
|
-
return typeof
|
|
130
|
+
const asset = assetOf(market, side);
|
|
131
|
+
if (typeof asset?.caip19_id === "string") return asset.caip19_id;
|
|
132
|
+
return typeof asset?.id === "string" ? asset.id : void 0;
|
|
872
133
|
};
|
|
873
134
|
var chainNamespaceOf = (id) => {
|
|
874
135
|
if (id === void 0) return void 0;
|
|
@@ -967,12 +228,14 @@ var discoverMarkets = async (options) => {
|
|
|
967
228
|
fetchImpl,
|
|
968
229
|
useCache = true
|
|
969
230
|
} = options;
|
|
970
|
-
if (!
|
|
971
|
-
const
|
|
231
|
+
if (!isNetwork(network)) return [];
|
|
232
|
+
const registries = registry ? [registry] : void 0;
|
|
233
|
+
const registryKey = registries?.[0] ?? registryIndexUrl(network);
|
|
234
|
+
const cached = repository && await readMarketsCache(repository, network, registryKey);
|
|
972
235
|
if (useCache && cached && Date.now() - cached.fetchedAt < MARKETS_CACHE_TTL_MS)
|
|
973
236
|
return cached.markets;
|
|
974
237
|
const { markets, sources, warnings } = await discover({
|
|
975
|
-
registries
|
|
238
|
+
registries,
|
|
976
239
|
localCards,
|
|
977
240
|
network,
|
|
978
241
|
fetchImpl
|
|
@@ -982,7 +245,7 @@ var discoverMarkets = async (options) => {
|
|
|
982
245
|
if (!reachable && cached) return cached.markets;
|
|
983
246
|
if (reachable && repository) {
|
|
984
247
|
try {
|
|
985
|
-
await repository.saveCachedMarkets(network,
|
|
248
|
+
await repository.saveCachedMarkets(network, registryKey, {
|
|
986
249
|
markets,
|
|
987
250
|
fetchedAt: Date.now()
|
|
988
251
|
});
|
|
@@ -996,8 +259,8 @@ var findMarket = (markets, fromId, toId) => {
|
|
|
996
259
|
const resolveMarketId = (id) => {
|
|
997
260
|
for (const market of markets) {
|
|
998
261
|
for (const side of ["base", "quote"]) {
|
|
999
|
-
const
|
|
1000
|
-
if (
|
|
262
|
+
const asset = side === "base" ? market.base_asset : market.quote_asset;
|
|
263
|
+
if (asset.id === id) return marketLegKey(market, side);
|
|
1001
264
|
const canonical = marketAssetId(market, side) ?? "";
|
|
1002
265
|
const matchesBtc = id === BTC_ASSET_ID && /^arkade:[^/]+\/slip44:(?:0|1)$/.test(canonical);
|
|
1003
266
|
const matchesAsset = /^[0-9a-f]{68}$/.test(id) && canonical.endsWith(`/asset:${id}`);
|
|
@@ -1147,7 +410,7 @@ var IndexedDbAssetSwapRepository = class {
|
|
|
1147
410
|
};
|
|
1148
411
|
|
|
1149
412
|
// src/restore.ts
|
|
1150
|
-
import { base64, hex
|
|
413
|
+
import { base64, hex } from "@scure/base";
|
|
1151
414
|
import {
|
|
1152
415
|
Extension,
|
|
1153
416
|
Transaction,
|
|
@@ -1183,7 +446,7 @@ function classifySpend(offer, serverPubkey, spendTx, deposit) {
|
|
|
1183
446
|
let leaves;
|
|
1184
447
|
try {
|
|
1185
448
|
const script = offerVtxoScript(offer, serverPubkey);
|
|
1186
|
-
if (
|
|
449
|
+
if (hex.encode(script.pkScript) !== hex.encode(offer.swapPkScript)) return "indeterminate";
|
|
1187
450
|
leaves = {
|
|
1188
451
|
// both routes that hand the deposit back; `exit` is absent on an
|
|
1189
452
|
// offer that carries no exit closure, and drops out here
|
|
@@ -1196,11 +459,11 @@ function classifySpend(offer, serverPubkey, spendTx, deposit) {
|
|
|
1196
459
|
for (let i = 0; i < spendTx.inputsLength; i++) {
|
|
1197
460
|
const input = spendTx.getInput(i);
|
|
1198
461
|
if (!input.txid || input.index !== deposit.vout) continue;
|
|
1199
|
-
if (
|
|
462
|
+
if (hex.encode(input.txid) !== deposit.txid) continue;
|
|
1200
463
|
for (const leaf of input.tapLeafScript ?? []) {
|
|
1201
|
-
const spent =
|
|
1202
|
-
if (leaves.returned.some((back) => spent ===
|
|
1203
|
-
if (leaves.fulfill && spent ===
|
|
464
|
+
const spent = hex.encode(scriptFromTapLeafScript(leaf));
|
|
465
|
+
if (leaves.returned.some((back) => spent === hex.encode(back))) return "cancelled";
|
|
466
|
+
if (leaves.fulfill && spent === hex.encode(leaves.fulfill)) return "fulfilled";
|
|
1204
467
|
}
|
|
1205
468
|
}
|
|
1206
469
|
return "indeterminate";
|
|
@@ -1220,7 +483,7 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
|
|
|
1220
483
|
try {
|
|
1221
484
|
reopened.push({
|
|
1222
485
|
fundingTx: { type: "sent", redeemTxid: swap.fundingTxid },
|
|
1223
|
-
offer: decodeOffer(
|
|
486
|
+
offer: decodeOffer(hex.decode(swap.offerHex)),
|
|
1224
487
|
offerHex: swap.offerHex,
|
|
1225
488
|
existing: swap
|
|
1226
489
|
});
|
|
@@ -1252,13 +515,13 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
|
|
|
1252
515
|
found.push({
|
|
1253
516
|
fundingTx,
|
|
1254
517
|
offer: decodeOffer(payload),
|
|
1255
|
-
offerHex:
|
|
518
|
+
offerHex: hex.encode(payload)
|
|
1256
519
|
});
|
|
1257
520
|
} catch {
|
|
1258
521
|
}
|
|
1259
522
|
}
|
|
1260
523
|
if (found.length === 0) return { restored: [], scannedTxids: fetchedTxids };
|
|
1261
|
-
const scripts = [...new Set(found.map((f) =>
|
|
524
|
+
const scripts = [...new Set(found.map((f) => hex.encode(f.offer.swapPkScript)))];
|
|
1262
525
|
const { vtxos } = await indexer.getVtxos({ scripts });
|
|
1263
526
|
const vtxoByScriptAndTxid = new Map(vtxos.map((v) => [`${v.script}:${v.txid}`, v]));
|
|
1264
527
|
const txByAnyId = /* @__PURE__ */ new Map();
|
|
@@ -1270,7 +533,7 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
|
|
|
1270
533
|
const spendTxids = /* @__PURE__ */ new Set();
|
|
1271
534
|
for (const { fundingTx, offer } of found) {
|
|
1272
535
|
const vtxo = vtxoByScriptAndTxid.get(
|
|
1273
|
-
`${
|
|
536
|
+
`${hex.encode(offer.swapPkScript)}:${fundingTx.redeemTxid}`
|
|
1274
537
|
);
|
|
1275
538
|
if (vtxo?.virtualStatus.state !== "spent") continue;
|
|
1276
539
|
for (const txid of spendTxidsOf(vtxo)) spendTxids.add(txid);
|
|
@@ -1279,7 +542,7 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
|
|
|
1279
542
|
const restored = [];
|
|
1280
543
|
const unresolved = /* @__PURE__ */ new Set();
|
|
1281
544
|
for (const { fundingTx, offer, offerHex, existing } of found) {
|
|
1282
|
-
const swapPkScript =
|
|
545
|
+
const swapPkScript = hex.encode(offer.swapPkScript);
|
|
1283
546
|
const vtxo = vtxoByScriptAndTxid.get(`${swapPkScript}:${fundingTx.redeemTxid}`);
|
|
1284
547
|
if (!vtxo) {
|
|
1285
548
|
unresolved.add(fundingTx.redeemTxid);
|
|
@@ -1459,7 +722,7 @@ function registerAssetSwapRestore(wallet, options) {
|
|
|
1459
722
|
}
|
|
1460
723
|
|
|
1461
724
|
// src/rfqCorridors.ts
|
|
1462
|
-
import { hex as
|
|
725
|
+
import { hex as hex2 } from "@scure/base";
|
|
1463
726
|
|
|
1464
727
|
// src/rfqCorridor.ts
|
|
1465
728
|
var RfqCorridorRegistry = class {
|
|
@@ -1615,13 +878,14 @@ var LightningReceiveCorridor = {
|
|
|
1615
878
|
};
|
|
1616
879
|
function onchainSendProfile(result) {
|
|
1617
880
|
return {
|
|
1618
|
-
claimKey:
|
|
1619
|
-
refundKey:
|
|
881
|
+
claimKey: hex2.encode(result.htlcParams.claimKey),
|
|
882
|
+
refundKey: hex2.encode(result.htlcParams.refundKey),
|
|
1620
883
|
htlcLocktime: result.htlcParams.refundLocktime,
|
|
1621
884
|
network: result.l1Network,
|
|
1622
885
|
htlcAddress: result.htlc.address,
|
|
1623
886
|
minConfirmations: result.minConfirmations,
|
|
1624
|
-
|
|
887
|
+
expectedAmount: result.expectedAmount,
|
|
888
|
+
payoutPkScript: hex2.encode(result.payoutPkScript)
|
|
1625
889
|
};
|
|
1626
890
|
}
|
|
1627
891
|
var OnchainSendCorridor = {
|
|
@@ -1632,6 +896,7 @@ var OnchainSendCorridor = {
|
|
|
1632
896
|
project: (swap) => {
|
|
1633
897
|
const send = swap;
|
|
1634
898
|
return {
|
|
899
|
+
expectedAmount: send.expectedAmount,
|
|
1635
900
|
...send.funding ? { funding: send.funding } : {},
|
|
1636
901
|
...send.claimTxid ? { claimTxid: send.claimTxid } : {}
|
|
1637
902
|
};
|
|
@@ -1648,6 +913,11 @@ var OnchainSendCorridor = {
|
|
|
1648
913
|
`onchain_send record carries no usable minConfirmations (${String(profile.minConfirmations)}); the confirmation gate cannot be checked \u2014 refusing to restore a swap that would claim an unconfirmed fill`
|
|
1649
914
|
);
|
|
1650
915
|
}
|
|
916
|
+
if (!Number.isSafeInteger(profile.expectedAmount) || profile.expectedAmount <= 0) {
|
|
917
|
+
throw new Error(
|
|
918
|
+
`onchain_send record carries no usable expectedAmount (${String(profile.expectedAmount)}); the funded value cannot be checked \u2014 refusing to restore a swap that would claim an underfunded fill`
|
|
919
|
+
);
|
|
920
|
+
}
|
|
1651
921
|
const htlc = onchainHtlcScript(
|
|
1652
922
|
{
|
|
1653
923
|
// From the profile, not the covenant: the lockup commits to
|
|
@@ -1655,8 +925,8 @@ var OnchainSendCorridor = {
|
|
|
1655
925
|
// unlocks both legs, but only one of the two hashes of it
|
|
1656
926
|
// is recoverable here.
|
|
1657
927
|
paymentHash,
|
|
1658
|
-
claimKey:
|
|
1659
|
-
refundKey:
|
|
928
|
+
claimKey: hex2.decode(profile.claimKey),
|
|
929
|
+
refundKey: hex2.decode(profile.refundKey),
|
|
1660
930
|
refundLocktime: profile.htlcLocktime
|
|
1661
931
|
},
|
|
1662
932
|
profile.network
|
|
@@ -1670,9 +940,10 @@ var OnchainSendCorridor = {
|
|
|
1670
940
|
paymentHash,
|
|
1671
941
|
htlc,
|
|
1672
942
|
minConfirmations: profile.minConfirmations,
|
|
943
|
+
expectedAmount: profile.expectedAmount,
|
|
1673
944
|
// Optional here, required at the write: throwing on an older
|
|
1674
945
|
// record would strand the refund it is still owed.
|
|
1675
|
-
...profile.payoutPkScript ? { payoutPkScript:
|
|
946
|
+
...profile.payoutPkScript ? { payoutPkScript: hex2.decode(profile.payoutPkScript) } : {},
|
|
1676
947
|
...profile.funding ? { funding: profile.funding } : {},
|
|
1677
948
|
...profile.claimTxid ? { claimTxid: profile.claimTxid } : {}
|
|
1678
949
|
};
|
|
@@ -1690,8 +961,8 @@ rfqCorridorHandlers.register(LightningReceiveCorridor);
|
|
|
1690
961
|
rfqCorridorHandlers.register(OnchainSendCorridor);
|
|
1691
962
|
|
|
1692
963
|
// src/rfqRecord.ts
|
|
1693
|
-
import { ArkAddress
|
|
1694
|
-
import { hex as
|
|
964
|
+
import { ArkAddress, VHTLCV2ContractHandler } from "@arkade-os/sdk";
|
|
965
|
+
import { hex as hex3 } from "@scure/base";
|
|
1695
966
|
|
|
1696
967
|
// src/rfqSwapState.ts
|
|
1697
968
|
var RFQ_SWAP_TERMINAL_STATES = ["settled", "refunded", "failed"];
|
|
@@ -1715,8 +986,8 @@ function assertSameSwap(origin, swap) {
|
|
|
1715
986
|
`rfq swap record is a ${origin.kind} origin paired with a ${swap.kind} swap`
|
|
1716
987
|
);
|
|
1717
988
|
}
|
|
1718
|
-
const funded =
|
|
1719
|
-
const watched =
|
|
989
|
+
const funded = hex3.encode(ArkAddress.decode(origin.lockupAddress).pkScript);
|
|
990
|
+
const watched = hex3.encode(swap.lockupPkScript);
|
|
1720
991
|
if (funded !== watched) {
|
|
1721
992
|
throw new Error(
|
|
1722
993
|
`rfq swap record's lockup address holds ${funded}, but the swap watches ${watched} \u2014 these are not the same swap`
|
|
@@ -1761,10 +1032,10 @@ function rfqSwapOriginOf(record) {
|
|
|
1761
1032
|
}
|
|
1762
1033
|
function lockupScript(params, lockupAddress) {
|
|
1763
1034
|
const script = VHTLCV2ContractHandler.createScript(params);
|
|
1764
|
-
const funded =
|
|
1765
|
-
if (
|
|
1035
|
+
const funded = ArkAddress.decode(lockupAddress).pkScript;
|
|
1036
|
+
if (hex3.encode(funded) !== hex3.encode(script.pkScript)) {
|
|
1766
1037
|
throw new Error(
|
|
1767
|
-
`rfq swap covenant params derive ${
|
|
1038
|
+
`rfq swap covenant params derive ${hex3.encode(script.pkScript)}, but the record's lockup address holds ${hex3.encode(funded)} \u2014 these params are not this swap's`
|
|
1768
1039
|
);
|
|
1769
1040
|
}
|
|
1770
1041
|
return script;
|
|
@@ -1799,11 +1070,11 @@ function shouldRetainRfqSwap(record, now) {
|
|
|
1799
1070
|
}
|
|
1800
1071
|
|
|
1801
1072
|
// src/watch.ts
|
|
1802
|
-
import { base64 as base642, hex as
|
|
1073
|
+
import { base64 as base642, hex as hex4 } from "@scure/base";
|
|
1803
1074
|
import {
|
|
1804
|
-
ArkAddress as
|
|
1075
|
+
ArkAddress as ArkAddress2,
|
|
1805
1076
|
isContractVtxoEvent,
|
|
1806
|
-
RestIndexerProvider as
|
|
1077
|
+
RestIndexerProvider as RestIndexerProvider2,
|
|
1807
1078
|
Transaction as Transaction2
|
|
1808
1079
|
} from "@arkade-os/sdk";
|
|
1809
1080
|
var TERMINAL = ["fulfilled", "cancelled", "recoverable"];
|
|
@@ -1825,8 +1096,8 @@ async function watchOfferSwaps({
|
|
|
1825
1096
|
onUpdate
|
|
1826
1097
|
}) {
|
|
1827
1098
|
const manager = await wallet.getContractManager();
|
|
1828
|
-
const serverPubkey =
|
|
1829
|
-
const indexer = new
|
|
1099
|
+
const serverPubkey = ArkAddress2.decode(await wallet.getAddress()).serverPubKey;
|
|
1100
|
+
const indexer = new RestIndexerProvider2(arkServerUrl);
|
|
1830
1101
|
let queue = Promise.resolve();
|
|
1831
1102
|
const enqueue = (task) => {
|
|
1832
1103
|
queue = queue.then(task).catch(() => {
|
|
@@ -1839,7 +1110,7 @@ async function watchOfferSwaps({
|
|
|
1839
1110
|
if (candidates.length === 0) return "indeterminate";
|
|
1840
1111
|
const { txs } = await indexer.getVirtualTxs(candidates);
|
|
1841
1112
|
return classifyDepositSpend(
|
|
1842
|
-
decodeOffer(
|
|
1113
|
+
decodeOffer(hex4.decode(swap.offerHex)),
|
|
1843
1114
|
serverPubkey,
|
|
1844
1115
|
txs.map((psbt) => Transaction2.fromPSBT(base642.decode(psbt))),
|
|
1845
1116
|
{ txid: vtxo.txid, vout: vtxo.vout }
|
|
@@ -1904,7 +1175,7 @@ async function watchOfferSwaps({
|
|
|
1904
1175
|
|
|
1905
1176
|
// src/chainSource.ts
|
|
1906
1177
|
import * as btc from "@scure/btc-signer";
|
|
1907
|
-
import { hex as
|
|
1178
|
+
import { hex as hex5 } from "@scure/base";
|
|
1908
1179
|
var addressOf = (pkScript, network) => btc.Address(L1_NETWORKS[network]).encode(btc.OutScript.decode(pkScript));
|
|
1909
1180
|
var chainSourceFrom = (provider, network) => {
|
|
1910
1181
|
const spenderFromHistory = async (txid, vout, pkScript) => {
|
|
@@ -1934,7 +1205,7 @@ var chainSourceFrom = (provider, network) => {
|
|
|
1934
1205
|
const spender = outspend.txid || await spenderFromHistory(txid, vout, pkScript);
|
|
1935
1206
|
if (!spender) return null;
|
|
1936
1207
|
const raw = await provider.getRawTransaction(spender);
|
|
1937
|
-
return { txHex:
|
|
1208
|
+
return { txHex: hex5.encode(raw) };
|
|
1938
1209
|
},
|
|
1939
1210
|
broadcast(txHex) {
|
|
1940
1211
|
return provider.broadcastTransaction(txHex);
|
|
@@ -1947,7 +1218,7 @@ var chainSourceFrom = (provider, network) => {
|
|
|
1947
1218
|
|
|
1948
1219
|
// src/payment/rendezvous.ts
|
|
1949
1220
|
import { sideLimits as sideLimits2 } from "@arkade-os/solver-discovery";
|
|
1950
|
-
import { hex as
|
|
1221
|
+
import { hex as hex6 } from "@scure/base";
|
|
1951
1222
|
var XONLY_HEX = /^[0-9a-f]{64}$/;
|
|
1952
1223
|
var rendezvousOf = (market, pinned) => {
|
|
1953
1224
|
const transports = { nostr: { relays: market.transports?.nostr?.relays ?? [] } };
|
|
@@ -1966,7 +1237,7 @@ var rendezvousOf = (market, pinned) => {
|
|
|
1966
1237
|
};
|
|
1967
1238
|
};
|
|
1968
1239
|
var solverRendezvous = (markets, payoutCorridor, amountSats, fallbackEmulatorPubkey) => {
|
|
1969
|
-
const encoded = fallbackEmulatorPubkey ?
|
|
1240
|
+
const encoded = fallbackEmulatorPubkey ? hex6.encode(fallbackEmulatorPubkey) : void 0;
|
|
1970
1241
|
if (encoded !== void 0 && !XONLY_HEX.test(encoded)) return void 0;
|
|
1971
1242
|
const pinned = encoded;
|
|
1972
1243
|
const quoteCorridor = payoutCorridor === "lightning" ? "bolt11" : "bitcoin";
|
|
@@ -2221,12 +1492,12 @@ var parseAssetId = (assetId) => {
|
|
|
2221
1492
|
};
|
|
2222
1493
|
var marketLabel = (market) => `${market.base_asset.ticker || market.base_asset.id}/${market.quote_asset.ticker || market.quote_asset.id}`;
|
|
2223
1494
|
function crossAssetRail(deps) {
|
|
2224
|
-
const planFor = async (
|
|
1495
|
+
const planFor = async (asset) => {
|
|
2225
1496
|
const markets = await deps.discover();
|
|
2226
|
-
const found = findMarket(markets, BTC_ASSET_ID,
|
|
1497
|
+
const found = findMarket(markets, BTC_ASSET_ID, asset.assetId);
|
|
2227
1498
|
const market = found?.market;
|
|
2228
1499
|
if (!market) return void 0;
|
|
2229
|
-
const plan = await deps.quote(market, found.give,
|
|
1500
|
+
const plan = await deps.quote(market, found.give, asset.amount);
|
|
2230
1501
|
const [balance, dust] = await Promise.all([deps.btcBalance(), deps.dust()]);
|
|
2231
1502
|
return { plan, error: validatePlan(plan, balance, dust) };
|
|
2232
1503
|
};
|
|
@@ -2236,29 +1507,29 @@ function crossAssetRail(deps) {
|
|
|
2236
1507
|
available: async (req) => {
|
|
2237
1508
|
const assets = assetsOf3(req);
|
|
2238
1509
|
if (assets.length !== 1) return false;
|
|
2239
|
-
const [
|
|
2240
|
-
if (
|
|
2241
|
-
if (typeof
|
|
2242
|
-
if (!parseAssetId(
|
|
2243
|
-
const planned = await planFor(
|
|
1510
|
+
const [asset] = assets;
|
|
1511
|
+
if (asset.assetId === BTC_ASSET_ID) return false;
|
|
1512
|
+
if (typeof asset.amount !== "bigint" || asset.amount <= 0n) return false;
|
|
1513
|
+
if (!parseAssetId(asset.assetId)) return false;
|
|
1514
|
+
const planned = await planFor(asset);
|
|
2244
1515
|
return planned !== void 0 && planned.error === void 0;
|
|
2245
1516
|
},
|
|
2246
1517
|
quote: async (req, ctx) => {
|
|
2247
1518
|
const payTo = arkTarget(req.raw);
|
|
2248
|
-
const
|
|
2249
|
-
const wantAsset = parseAssetId(
|
|
1519
|
+
const asset = resolveAssetAmount(CROSS_ASSET_RAIL, req);
|
|
1520
|
+
const wantAsset = parseAssetId(asset.assetId);
|
|
2250
1521
|
if (!wantAsset) {
|
|
2251
1522
|
throw new Error(
|
|
2252
|
-
`${CROSS_ASSET_RAIL}: ${
|
|
1523
|
+
`${CROSS_ASSET_RAIL}: ${asset.assetId} is not an asset id (expected 34 bytes of hex)`
|
|
2253
1524
|
);
|
|
2254
1525
|
}
|
|
2255
|
-
const planned = await planFor(
|
|
1526
|
+
const planned = await planFor(asset);
|
|
2256
1527
|
if (!planned) {
|
|
2257
|
-
throw new Error(`${CROSS_ASSET_RAIL}: no market swaps BTC into ${
|
|
1528
|
+
throw new Error(`${CROSS_ASSET_RAIL}: no market swaps BTC into ${asset.assetId}`);
|
|
2258
1529
|
}
|
|
2259
1530
|
if (planned.error) {
|
|
2260
1531
|
throw new Error(
|
|
2261
|
-
`${CROSS_ASSET_RAIL}: cannot swap into ${
|
|
1532
|
+
`${CROSS_ASSET_RAIL}: cannot swap into ${asset.assetId} \u2014 ${planned.error}`
|
|
2262
1533
|
);
|
|
2263
1534
|
}
|
|
2264
1535
|
const { plan } = planned;
|
|
@@ -2278,7 +1549,7 @@ function crossAssetRail(deps) {
|
|
|
2278
1549
|
fee: depositSats,
|
|
2279
1550
|
total: depositSats + carrier,
|
|
2280
1551
|
assets: {
|
|
2281
|
-
delivered:
|
|
1552
|
+
delivered: asset,
|
|
2282
1553
|
spent: { assetId: BTC_ASSET_ID, amount: plan.deposit.atomic }
|
|
2283
1554
|
},
|
|
2284
1555
|
meta: {
|
|
@@ -2288,7 +1559,7 @@ function crossAssetRail(deps) {
|
|
|
2288
1559
|
},
|
|
2289
1560
|
send: async () => makeHandle3(CROSS_ASSET_RAIL, async (emit) => {
|
|
2290
1561
|
const offer = await createOffer(ctx.wallet, deps.arkServerUrl, {
|
|
2291
|
-
wantAmount:
|
|
1562
|
+
wantAmount: asset.amount,
|
|
2292
1563
|
wantAsset,
|
|
2293
1564
|
...deps.emulatorPubkey ? { emulatorPubkey: deps.emulatorPubkey } : {}
|
|
2294
1565
|
});
|
|
@@ -2298,7 +1569,7 @@ function crossAssetRail(deps) {
|
|
|
2298
1569
|
address: offer.address,
|
|
2299
1570
|
swapPkScript: offer.swapPkScript,
|
|
2300
1571
|
depositSats,
|
|
2301
|
-
asset
|
|
1572
|
+
asset,
|
|
2302
1573
|
payTo,
|
|
2303
1574
|
plan
|
|
2304
1575
|
};
|
|
@@ -2315,7 +1586,7 @@ function crossAssetRail(deps) {
|
|
|
2315
1586
|
const txid = await ctx.wallet.send({
|
|
2316
1587
|
address: payTo,
|
|
2317
1588
|
amount: carrier,
|
|
2318
|
-
assets: [
|
|
1589
|
+
assets: [asset]
|
|
2319
1590
|
});
|
|
2320
1591
|
await deps.persist({ ...swap, phase: "settled", txid });
|
|
2321
1592
|
const result = {
|
|
@@ -2332,10 +1603,10 @@ function crossAssetRail(deps) {
|
|
|
2332
1603
|
}
|
|
2333
1604
|
|
|
2334
1605
|
// src/reveal.ts
|
|
2335
|
-
import { base64 as base643, hex as
|
|
1606
|
+
import { base64 as base643, hex as hex7 } from "@scure/base";
|
|
2336
1607
|
import { ripemd160 } from "@noble/hashes/legacy.js";
|
|
2337
|
-
import { sha256
|
|
2338
|
-
import { ArkAddress as
|
|
1608
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
1609
|
+
import { ArkAddress as ArkAddress3 } from "@arkade-os/sdk";
|
|
2339
1610
|
var CovclaimdRevealError = class extends Error {
|
|
2340
1611
|
name = "CovclaimdRevealError";
|
|
2341
1612
|
status;
|
|
@@ -2352,7 +1623,7 @@ var decodeKey = (value, field) => {
|
|
|
2352
1623
|
if (typeof value !== "string") throw new Error(`covclaimd info: ${field} is not a string`);
|
|
2353
1624
|
let bytes;
|
|
2354
1625
|
try {
|
|
2355
|
-
bytes =
|
|
1626
|
+
bytes = hex7.decode(value);
|
|
2356
1627
|
} catch {
|
|
2357
1628
|
throw new Error(`covclaimd info: ${field} is not hex`);
|
|
2358
1629
|
}
|
|
@@ -2412,7 +1683,7 @@ var covclaimdClient = (baseUrl, options = {}) => {
|
|
|
2412
1683
|
const body = await readJson("info", response);
|
|
2413
1684
|
return {
|
|
2414
1685
|
covclaimdPubkey: decodeKey(body?.covclaimd_pub_key, "covclaimd_pub_key"),
|
|
2415
|
-
emulatorPubkey:
|
|
1686
|
+
emulatorPubkey: hex7.encode(decodeKey(body?.emulator_pub_key, "emulator_pub_key"))
|
|
2416
1687
|
};
|
|
2417
1688
|
},
|
|
2418
1689
|
async reveal(params) {
|
|
@@ -2434,15 +1705,15 @@ function revealFieldsFromScript(script) {
|
|
|
2434
1705
|
}
|
|
2435
1706
|
return {
|
|
2436
1707
|
arkadeScript: base643.encode(script.nonInteractiveClaimArkadeScript),
|
|
2437
|
-
taptree:
|
|
1708
|
+
taptree: hex7.encode(script.encode())
|
|
2438
1709
|
};
|
|
2439
1710
|
}
|
|
2440
1711
|
async function revealClaimPacket(client, input) {
|
|
2441
1712
|
const fields = revealFieldsFromScript(input.script);
|
|
2442
|
-
if (
|
|
1713
|
+
if (hex7.encode(ArkAddress3.decode(input.address).pkScript) !== hex7.encode(input.script.pkScript)) {
|
|
2443
1714
|
throw new Error("address does not belong to this covenant");
|
|
2444
1715
|
}
|
|
2445
|
-
if (
|
|
1716
|
+
if (hex7.encode(ripemd160(sha256(input.preimage))) !== hex7.encode(input.script.options.preimageHash)) {
|
|
2446
1717
|
throw new Error("preimage does not match the covenant's payment hash");
|
|
2447
1718
|
}
|
|
2448
1719
|
const { ciphertext } = await sealClaimPacket({
|
|
@@ -2454,9 +1725,9 @@ async function revealClaimPacket(client, input) {
|
|
|
2454
1725
|
}
|
|
2455
1726
|
|
|
2456
1727
|
// src/claim.ts
|
|
2457
|
-
import { hex as
|
|
1728
|
+
import { hex as hex9 } from "@scure/base";
|
|
2458
1729
|
import { ripemd160 as ripemd1602 } from "@noble/hashes/legacy.js";
|
|
2459
|
-
import { sha256 as
|
|
1730
|
+
import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
|
|
2460
1731
|
import {
|
|
2461
1732
|
CSVMultisigTapscript as CSVMultisigTapscript2,
|
|
2462
1733
|
claimWithPreimageIdentity,
|
|
@@ -2464,8 +1735,8 @@ import {
|
|
|
2464
1735
|
} from "@arkade-os/sdk";
|
|
2465
1736
|
|
|
2466
1737
|
// src/refund.ts
|
|
2467
|
-
import { base64 as base644, hex as
|
|
2468
|
-
import { sha256 as
|
|
1738
|
+
import { base64 as base644, hex as hex8 } from "@scure/base";
|
|
1739
|
+
import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
|
|
2469
1740
|
import {
|
|
2470
1741
|
CSVMultisigTapscript,
|
|
2471
1742
|
ConditionWitness,
|
|
@@ -2522,7 +1793,7 @@ var LockupNeedsRecoveryError = class extends Error {
|
|
|
2522
1793
|
}
|
|
2523
1794
|
};
|
|
2524
1795
|
async function findLockupVtxos(indexer, swapPkScript) {
|
|
2525
|
-
const scripts = [
|
|
1796
|
+
const scripts = [hex8.encode(swapPkScript)];
|
|
2526
1797
|
const [spendable, recoverable] = await Promise.all([
|
|
2527
1798
|
indexer.getVtxos({ scripts, spendableOnly: true }),
|
|
2528
1799
|
indexer.getVtxos({ scripts, recoverableOnly: true })
|
|
@@ -2548,13 +1819,13 @@ async function findLockupVtxos(indexer, swapPkScript) {
|
|
|
2548
1819
|
}
|
|
2549
1820
|
return out;
|
|
2550
1821
|
}
|
|
2551
|
-
var hashesTo = (candidate, paymentHash) =>
|
|
1822
|
+
var hashesTo = (candidate, paymentHash) => hex8.encode(sha2562(candidate)) === paymentHash;
|
|
2552
1823
|
var candidateWitnessItems = (tx, inputIndex) => [
|
|
2553
1824
|
...getArkPsbtFields(tx, inputIndex, ConditionWitness).flat(),
|
|
2554
1825
|
...tx.getInput(inputIndex).finalScriptWitness ?? []
|
|
2555
1826
|
];
|
|
2556
1827
|
async function readLockupFate(indexer, input) {
|
|
2557
|
-
const { vtxos } = await indexer.getVtxos({ scripts: [
|
|
1828
|
+
const { vtxos } = await indexer.getVtxos({ scripts: [hex8.encode(input.swapPkScript)] });
|
|
2558
1829
|
const all = vtxos ?? [];
|
|
2559
1830
|
if (all.length === 0) return { fate: "unknown" };
|
|
2560
1831
|
const exited = all.filter((vtxo) => vtxo.isUnrolled && !hasTerminalSpend(vtxo));
|
|
@@ -2589,7 +1860,7 @@ async function readLockupFate(indexer, input) {
|
|
|
2589
1860
|
for (let i = 0; i < tx.inputsLength; i++) {
|
|
2590
1861
|
const spent = tx.getInput(i);
|
|
2591
1862
|
if (!spent.txid) continue;
|
|
2592
|
-
const txid =
|
|
1863
|
+
const txid = hex8.encode(spent.txid);
|
|
2593
1864
|
if (!all.some((vtxo) => vtxo.txid === txid && vtxo.vout === spent.index)) continue;
|
|
2594
1865
|
for (const candidate of candidateWitnessItems(tx, i)) {
|
|
2595
1866
|
if (hashesTo(candidate, input.paymentHash)) {
|
|
@@ -2618,7 +1889,7 @@ async function pushRefundWithoutReceiver(ark, input) {
|
|
|
2618
1889
|
const info = await ark.getInfo();
|
|
2619
1890
|
let serverUnrollScript;
|
|
2620
1891
|
try {
|
|
2621
|
-
serverUnrollScript = CSVMultisigTapscript.decode(
|
|
1892
|
+
serverUnrollScript = CSVMultisigTapscript.decode(hex8.decode(info.checkpointTapscript));
|
|
2622
1893
|
} catch {
|
|
2623
1894
|
throw new Error("invalid checkpointTapscript from the Arkade server");
|
|
2624
1895
|
}
|
|
@@ -2744,13 +2015,13 @@ async function pushClaim(ark, input) {
|
|
|
2744
2015
|
}
|
|
2745
2016
|
}
|
|
2746
2017
|
const committed = input.script.options.preimageHash;
|
|
2747
|
-
if (
|
|
2018
|
+
if (hex9.encode(ripemd1602(sha2563(input.preimage))) !== hex9.encode(committed)) {
|
|
2748
2019
|
throw new Error("preimage does not match the covenant's payment hash");
|
|
2749
2020
|
}
|
|
2750
2021
|
const info = await ark.getInfo();
|
|
2751
2022
|
let serverUnrollScript;
|
|
2752
2023
|
try {
|
|
2753
|
-
serverUnrollScript = CSVMultisigTapscript2.decode(
|
|
2024
|
+
serverUnrollScript = CSVMultisigTapscript2.decode(hex9.decode(info.checkpointTapscript));
|
|
2754
2025
|
} catch {
|
|
2755
2026
|
throw new Error("invalid checkpointTapscript from the Arkade server");
|
|
2756
2027
|
}
|
|
@@ -2869,7 +2140,7 @@ function arkadeRefunder(deps) {
|
|
|
2869
2140
|
}
|
|
2870
2141
|
|
|
2871
2142
|
// src/swapManager.ts
|
|
2872
|
-
import { hex as
|
|
2143
|
+
import { hex as hex10 } from "@scure/base";
|
|
2873
2144
|
import {
|
|
2874
2145
|
isContractVtxoEvent as isContractVtxoEvent2
|
|
2875
2146
|
} from "@arkade-os/sdk";
|
|
@@ -3320,7 +2591,7 @@ var RfqSwapManager = class {
|
|
|
3320
2591
|
// ── internals ────────────────────────────────────────────────────────────
|
|
3321
2592
|
track(swap) {
|
|
3322
2593
|
this.monitored.set(swap.rfqId, swap);
|
|
3323
|
-
this.byLockupScript.set(
|
|
2594
|
+
this.byLockupScript.set(hex10.encode(swap.lockupPkScript), swap);
|
|
3324
2595
|
}
|
|
3325
2596
|
/** Drops the swap from BOTH indexes. The event index is the one that stops
|
|
3326
2597
|
* a late event finding a swap that is gone; `pollSwap`'s own
|
|
@@ -3329,7 +2600,7 @@ var RfqSwapManager = class {
|
|
|
3329
2600
|
* them from silently re-driving a cancelled swap. */
|
|
3330
2601
|
untrack(rfqId) {
|
|
3331
2602
|
const swap = this.monitored.get(rfqId);
|
|
3332
|
-
if (swap) this.byLockupScript.delete(
|
|
2603
|
+
if (swap) this.byLockupScript.delete(hex10.encode(swap.lockupPkScript));
|
|
3333
2604
|
this.monitored.delete(rfqId);
|
|
3334
2605
|
this.refundRefused.delete(rfqId);
|
|
3335
2606
|
this.lastClaimError.delete(rfqId);
|
|
@@ -3392,7 +2663,7 @@ var RfqSwapManager = class {
|
|
|
3392
2663
|
if (!lockup) {
|
|
3393
2664
|
try {
|
|
3394
2665
|
const [existing] = await contracts.getContracts({
|
|
3395
|
-
script:
|
|
2666
|
+
script: hex10.encode(swap.lockupPkScript)
|
|
3396
2667
|
});
|
|
3397
2668
|
if (existing) {
|
|
3398
2669
|
this.registered.set(swap.rfqId, true);
|
|
@@ -3411,13 +2682,13 @@ var RfqSwapManager = class {
|
|
|
3411
2682
|
);
|
|
3412
2683
|
return;
|
|
3413
2684
|
}
|
|
3414
|
-
const script =
|
|
3415
|
-
if (script !==
|
|
2685
|
+
const script = hex10.encode(lockup.script.pkScript);
|
|
2686
|
+
if (script !== hex10.encode(swap.lockupPkScript)) {
|
|
3416
2687
|
this.registered.set(swap.rfqId, false);
|
|
3417
2688
|
this.emitFailed(
|
|
3418
2689
|
swap,
|
|
3419
2690
|
new Error(
|
|
3420
|
-
`swap ${swap.rfqId} lockup script ${script} does not match its lockupPkScript ${
|
|
2691
|
+
`swap ${swap.rfqId} lockup script ${script} does not match its lockupPkScript ${hex10.encode(swap.lockupPkScript)}`
|
|
3421
2692
|
)
|
|
3422
2693
|
);
|
|
3423
2694
|
return;
|
|
@@ -3436,7 +2707,7 @@ var RfqSwapManager = class {
|
|
|
3436
2707
|
* script for its whole life. Best-effort — the swap is over either way. */
|
|
3437
2708
|
retireContract(swap) {
|
|
3438
2709
|
if (!this.deps.contracts || !this.registered.get(swap.rfqId)) return;
|
|
3439
|
-
void this.deps.contracts.setContractWatchState(
|
|
2710
|
+
void this.deps.contracts.setContractWatchState(hex10.encode(swap.lockupPkScript), "retained").catch((error) => this.emitFailed(swap, error));
|
|
3440
2711
|
}
|
|
3441
2712
|
arm() {
|
|
3442
2713
|
if (!this.running) return;
|
|
@@ -3636,6 +2907,21 @@ var RfqSwapManager = class {
|
|
|
3636
2907
|
});
|
|
3637
2908
|
if (action === "claim" && phase.phase === "claimable") {
|
|
3638
2909
|
if (swap.claimTxid) return "continue";
|
|
2910
|
+
if (!Number.isSafeInteger(swap.expectedAmount) || swap.expectedAmount <= 0) {
|
|
2911
|
+
this.block(
|
|
2912
|
+
swap,
|
|
2913
|
+
`expectedAmount is not a positive number of sats (${String(swap.expectedAmount)}), so the funded value cannot be checked \u2014 refusing to publish the preimage`
|
|
2914
|
+
);
|
|
2915
|
+
return "handled";
|
|
2916
|
+
}
|
|
2917
|
+
const filled = Number(phase.utxo.amount);
|
|
2918
|
+
if (!Number.isFinite(filled) || filled < swap.expectedAmount) {
|
|
2919
|
+
this.block(
|
|
2920
|
+
swap,
|
|
2921
|
+
`fill holds ${filled} sats, below the agreed ${swap.expectedAmount} \u2014 refusing to publish the preimage`
|
|
2922
|
+
);
|
|
2923
|
+
return "handled";
|
|
2924
|
+
}
|
|
3639
2925
|
if (this.callbacks && !this.callbacks.claimOnchain) {
|
|
3640
2926
|
this.block(
|
|
3641
2927
|
swap,
|
|
@@ -3689,6 +2975,8 @@ var RfqSwapManager = class {
|
|
|
3689
2975
|
if (pushed) {
|
|
3690
2976
|
swap.refundArkTxid = pushed.arkTxid;
|
|
3691
2977
|
this.touch(swap);
|
|
2978
|
+
} else if (now < swap.refundLocktime + REFUND_MTP_LAG_SECONDS) {
|
|
2979
|
+
return;
|
|
3692
2980
|
}
|
|
3693
2981
|
this.setState(swap, "refunded");
|
|
3694
2982
|
this.emitAction(swap, "refundArkade");
|
|
@@ -3910,9 +3198,9 @@ var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
|
|
|
3910
3198
|
|
|
3911
3199
|
// src/activity.ts
|
|
3912
3200
|
import {
|
|
3913
|
-
ArkAddress as
|
|
3201
|
+
ArkAddress as ArkAddress4
|
|
3914
3202
|
} from "@arkade-os/sdk";
|
|
3915
|
-
import { hex as
|
|
3203
|
+
import { hex as hex11 } from "@scure/base";
|
|
3916
3204
|
var LABELS = {
|
|
3917
3205
|
lightning_send: "Lightning send",
|
|
3918
3206
|
lightning_receive: "Lightning receive",
|
|
@@ -3987,7 +3275,7 @@ async function activityInputOf(record, indexer) {
|
|
|
3987
3275
|
async function lockupTxids(indexer, record, wantFunding) {
|
|
3988
3276
|
let script;
|
|
3989
3277
|
try {
|
|
3990
|
-
script =
|
|
3278
|
+
script = hex11.encode(ArkAddress4.decode(record.lockupAddress).pkScript);
|
|
3991
3279
|
} catch {
|
|
3992
3280
|
return [];
|
|
3993
3281
|
}
|
|
@@ -4058,6 +3346,7 @@ export {
|
|
|
4058
3346
|
arkadeAssetLeg,
|
|
4059
3347
|
arkadeRefunder,
|
|
4060
3348
|
arkadeSwapRequest,
|
|
3349
|
+
assertArkadeFundable,
|
|
4061
3350
|
assertFundable,
|
|
4062
3351
|
assertReceivable,
|
|
4063
3352
|
awaitLockupFunding,
|
|
@@ -4066,6 +3355,7 @@ export {
|
|
|
4066
3355
|
buildHtlcClaim,
|
|
4067
3356
|
buildHtlcRefund,
|
|
4068
3357
|
cancelOffer,
|
|
3358
|
+
canonicalAssetAmount,
|
|
4069
3359
|
chainSourceFrom,
|
|
4070
3360
|
claimOnchainFill,
|
|
4071
3361
|
claimPacketShape,
|
|
@@ -4106,6 +3396,8 @@ export {
|
|
|
4106
3396
|
newPreimage,
|
|
4107
3397
|
newRfqId,
|
|
4108
3398
|
nextOnchainAction,
|
|
3399
|
+
normalizeMakerPkScript,
|
|
3400
|
+
normalizeMakerPublicKey,
|
|
4109
3401
|
offerTermsFromQuote,
|
|
4110
3402
|
offerVtxoScript,
|
|
4111
3403
|
onchainHtlcScript,
|
|
@@ -4123,6 +3415,7 @@ export {
|
|
|
4123
3415
|
registerAssetSwapRestore,
|
|
4124
3416
|
registerLockupContract,
|
|
4125
3417
|
relayTransport,
|
|
3418
|
+
requestArkadeSwap,
|
|
4126
3419
|
requestLightningReceive,
|
|
4127
3420
|
requestLightningSend,
|
|
4128
3421
|
requestOnchainReceive,
|
|
@@ -4160,6 +3453,7 @@ export {
|
|
|
4160
3453
|
updateRfqSwapRecord,
|
|
4161
3454
|
validatePlan,
|
|
4162
3455
|
verifyLockupAddress,
|
|
3456
|
+
verifyOfferAddress,
|
|
4163
3457
|
verifyReceiveInvoice,
|
|
4164
3458
|
watchOfferSwaps
|
|
4165
3459
|
};
|