@arkade-os/swap 0.0.10 → 0.0.12
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 +17 -0
- package/dist/{chunk-5NPYNQ5V.js → chunk-XXWOODUE.js} +114 -29
- package/dist/index.cjs +587 -83
- package/dist/index.d.cts +171 -16
- package/dist/index.d.ts +171 -16
- package/dist/index.js +450 -36
- 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-C8FXlmHY.d.cts → repository-DaK1RzRq.d.cts} +45 -5
- package/dist/{repository-Cw2EvvZG.d.ts → repository-DlLvj_y6.d.ts} +45 -5
- package/dist/{rfq-DglvHMNC.d.ts → rfq-DzsmhXX3.d.cts} +75 -20
- package/dist/{rfq-DglvHMNC.d.cts → rfq-DzsmhXX3.d.ts} +75 -20
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -66,6 +66,8 @@ __export(index_exports, {
|
|
|
66
66
|
RfqSwapManager: () => RfqSwapManager,
|
|
67
67
|
RfqSwapOriginRequired: () => RfqSwapOriginRequired,
|
|
68
68
|
SOLO_REFUND_HEADROOM_SECONDS: () => SOLO_REFUND_HEADROOM_SECONDS,
|
|
69
|
+
SOLVER_LIGHTNING_RAIL: () => SOLVER_LIGHTNING_RAIL,
|
|
70
|
+
SOLVER_ONCHAIN_RAIL: () => SOLVER_ONCHAIN_RAIL,
|
|
69
71
|
SWAP_LOCKUP_CONTRACT_KIND: () => SWAP_LOCKUP_CONTRACT_KIND,
|
|
70
72
|
SWAP_LOCKUP_CONTRACT_LABEL: () => SWAP_LOCKUP_CONTRACT_LABEL,
|
|
71
73
|
SWAP_LOCKUP_CONTRACT_TYPE: () => SWAP_LOCKUP_CONTRACT_TYPE,
|
|
@@ -82,6 +84,7 @@ __export(index_exports, {
|
|
|
82
84
|
buildHtlcClaim: () => buildHtlcClaim,
|
|
83
85
|
buildHtlcRefund: () => buildHtlcRefund,
|
|
84
86
|
cancelOffer: () => cancelOffer,
|
|
87
|
+
chainSourceFrom: () => chainSourceFrom,
|
|
85
88
|
claimOnchainFill: () => claimOnchainFill,
|
|
86
89
|
claimReceiveLockup: () => claimReceiveLockup,
|
|
87
90
|
classifyDepositSpend: () => classifyDepositSpend,
|
|
@@ -103,6 +106,7 @@ __export(index_exports, {
|
|
|
103
106
|
httpTransport: () => httpTransport,
|
|
104
107
|
isRfqSwapTerminal: () => isRfqSwapTerminal,
|
|
105
108
|
isRfqTerminal: () => isRfqTerminal,
|
|
109
|
+
l1ScriptForAddress: () => l1ScriptForAddress,
|
|
106
110
|
lightningReceiveRequest: () => lightningReceiveRequest,
|
|
107
111
|
lightningSendRequest: () => lightningSendRequest,
|
|
108
112
|
lightningSendVtxoScript: () => lightningSendVtxoScript,
|
|
@@ -142,6 +146,11 @@ __export(index_exports, {
|
|
|
142
146
|
sealClaimPacket: () => sealClaimPacket,
|
|
143
147
|
senderIdentityForSwapRecord: () => senderIdentityForSwapRecord,
|
|
144
148
|
shouldRetainRfqSwap: () => shouldRetainRfqSwap,
|
|
149
|
+
solverLightningRail: () => solverLightningRail,
|
|
150
|
+
solverLightningRendezvous: () => solverLightningRendezvous,
|
|
151
|
+
solverOnchainRail: () => solverOnchainRail,
|
|
152
|
+
solverOnchainRendezvous: () => solverOnchainRendezvous,
|
|
153
|
+
solverRendezvous: () => solverRendezvous,
|
|
145
154
|
spendTxidsOf: () => spendTxidsOf,
|
|
146
155
|
spendUpdate: () => spendUpdate,
|
|
147
156
|
swapActivityResolver: () => swapActivityResolver,
|
|
@@ -411,12 +420,30 @@ var swapPrograms = {
|
|
|
411
420
|
wantAsset: import_sdk2.arkade.parseArtifact(swap_want_asset_program_default),
|
|
412
421
|
wantBtc: import_sdk2.arkade.parseArtifact(swap_want_btc_program_default)
|
|
413
422
|
};
|
|
423
|
+
function withExitClosure(program, exit) {
|
|
424
|
+
if (!exit) return program;
|
|
425
|
+
return {
|
|
426
|
+
...program,
|
|
427
|
+
// typed params are authoritative: an undeclared `$exitDelay` fails
|
|
428
|
+
// validateProgram instead of compiling against an unbound value
|
|
429
|
+
params: [...program.params ?? [], { name: "exitDelay", type: "int" }],
|
|
430
|
+
functions: {
|
|
431
|
+
...program.functions,
|
|
432
|
+
exit: {
|
|
433
|
+
tapscript: { signers: ["$user"], csv: { type: exit.type, value: "$exitDelay" } }
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
414
438
|
function swapProgramBinding(offer, serverPubkey) {
|
|
415
439
|
if (offer.makerPkScript.length !== FIELDS.makerPkScript.width) {
|
|
416
440
|
throw new Error("makerPkScript is not a 34-byte taproot scriptPubKey");
|
|
417
441
|
}
|
|
418
442
|
return {
|
|
419
|
-
program:
|
|
443
|
+
program: withExitClosure(
|
|
444
|
+
offer.wantAsset ? swapPrograms.wantAsset : swapPrograms.wantBtc,
|
|
445
|
+
offer.exitDelay
|
|
446
|
+
),
|
|
420
447
|
args: {
|
|
421
448
|
makerWP: offer.makerPkScript.subarray(2),
|
|
422
449
|
wantAmount: offer.wantAmount,
|
|
@@ -426,7 +453,8 @@ function swapProgramBinding(offer, serverPubkey) {
|
|
|
426
453
|
...offer.wantAsset && {
|
|
427
454
|
wantAssetTxid: offer.wantAsset.txid.slice().reverse(),
|
|
428
455
|
wantAssetGroupIndex: offer.wantAsset.groupIndex
|
|
429
|
-
}
|
|
456
|
+
},
|
|
457
|
+
...offer.exitDelay && { exitDelay: offer.exitDelay.value }
|
|
430
458
|
},
|
|
431
459
|
keys: {
|
|
432
460
|
serverKey: serverPubkey,
|
|
@@ -447,9 +475,27 @@ var FIELDS = {
|
|
|
447
475
|
makerPkScript: { tag: 5, width: 34 },
|
|
448
476
|
makerPublicKey: { tag: 7, width: 32 },
|
|
449
477
|
emulatorPubkey: { tag: 8, width: 32 },
|
|
450
|
-
|
|
478
|
+
ratioNum: { tag: 9, width: 8 },
|
|
479
|
+
ratioDen: { tag: 10, width: 8 },
|
|
480
|
+
offerAsset: { tag: 11, width: void 0 },
|
|
481
|
+
exitTimelock: { tag: 12, width: 9 }
|
|
451
482
|
};
|
|
483
|
+
var EXIT_TYPES = ["blocks", "seconds"];
|
|
452
484
|
var NAMES = Object.fromEntries(Object.entries(FIELDS).map(([k, f]) => [f.tag, k]));
|
|
485
|
+
function u64(name, value) {
|
|
486
|
+
if (value < BigInt(0) || value >> BigInt(64) > BigInt(0)) {
|
|
487
|
+
throw new Error(`${name} does not fit the offer wire format (u64)`);
|
|
488
|
+
}
|
|
489
|
+
const out = new Uint8Array(FIELDS.wantAmount.width);
|
|
490
|
+
new DataView(out.buffer).setBigUint64(0, value, false);
|
|
491
|
+
return out;
|
|
492
|
+
}
|
|
493
|
+
var readU64 = (value) => new DataView(value.buffer, value.byteOffset).getBigUint64(0, false);
|
|
494
|
+
var setRatio = (name, value) => {
|
|
495
|
+
if (value === void 0 || value === BigInt(0)) return void 0;
|
|
496
|
+
if (value < BigInt(0)) throw new Error(`${name} does not fit the offer wire format (u64)`);
|
|
497
|
+
return value;
|
|
498
|
+
};
|
|
453
499
|
function tlv(type, value) {
|
|
454
500
|
if (value.length > 65535) throw new Error("TLV value exceeds the u16 length field");
|
|
455
501
|
return (0, import_utils.concatBytes)(Uint8Array.of(type, value.length >> 8 & 255, value.length & 255), value);
|
|
@@ -468,24 +514,45 @@ function encodeOffer(offer) {
|
|
|
468
514
|
throw new Error(`${name} must be ${FIELDS[name].width} bytes`);
|
|
469
515
|
}
|
|
470
516
|
}
|
|
471
|
-
|
|
472
|
-
|
|
517
|
+
const ratioNum = setRatio("ratioNum", offer.ratioNum);
|
|
518
|
+
const ratioDen = setRatio("ratioDen", offer.ratioDen);
|
|
519
|
+
if (ratioNum === void 0 !== (ratioDen === void 0)) {
|
|
520
|
+
throw new Error("offer must carry both ratioNum and ratioDen, or neither");
|
|
473
521
|
}
|
|
474
|
-
const amount = new Uint8Array(FIELDS.wantAmount.width);
|
|
475
|
-
new DataView(amount.buffer).setBigUint64(0, offer.wantAmount, false);
|
|
476
522
|
const recs = [
|
|
477
523
|
tlv(FIELDS.swapPkScript.tag, offer.swapPkScript),
|
|
478
|
-
tlv(FIELDS.wantAmount.tag,
|
|
524
|
+
tlv(FIELDS.wantAmount.tag, u64("wantAmount", offer.wantAmount))
|
|
479
525
|
];
|
|
480
526
|
if (offer.wantAsset) recs.push(tlv(FIELDS.wantAsset.tag, offer.wantAsset.serialize()));
|
|
527
|
+
if (ratioNum !== void 0) recs.push(tlv(FIELDS.ratioNum.tag, u64("ratioNum", ratioNum)));
|
|
528
|
+
if (ratioDen !== void 0) recs.push(tlv(FIELDS.ratioDen.tag, u64("ratioDen", ratioDen)));
|
|
481
529
|
if (offer.offerAsset) recs.push(tlv(FIELDS.offerAsset.tag, offer.offerAsset.serialize()));
|
|
482
530
|
recs.push(
|
|
483
531
|
tlv(FIELDS.makerPkScript.tag, offer.makerPkScript),
|
|
484
532
|
tlv(FIELDS.makerPublicKey.tag, offer.makerPublicKey),
|
|
485
533
|
tlv(FIELDS.emulatorPubkey.tag, offer.emulatorPubkey)
|
|
486
534
|
);
|
|
535
|
+
if (offer.exitDelay) recs.push(tlv(FIELDS.exitTimelock.tag, encodeExitDelay(offer.exitDelay)));
|
|
487
536
|
return (0, import_utils.concatBytes)(...recs);
|
|
488
537
|
}
|
|
538
|
+
function encodeExitDelay(exit) {
|
|
539
|
+
return (0, import_utils.concatBytes)(
|
|
540
|
+
Uint8Array.of(EXIT_TYPES.indexOf(assertExitDelay(exit).type)),
|
|
541
|
+
u64("exitDelay", exit.value)
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
function assertExitDelay(exit) {
|
|
545
|
+
if (EXIT_TYPES.indexOf(exit.type) < 0) {
|
|
546
|
+
throw new Error(`unknown exitDelay locktime type: ${exit.type}`);
|
|
547
|
+
}
|
|
548
|
+
if (exit.value <= BigInt(0)) {
|
|
549
|
+
throw new Error("exitDelay must be a positive relative locktime");
|
|
550
|
+
}
|
|
551
|
+
if (exit.value >> BigInt(32) > BigInt(0)) {
|
|
552
|
+
throw new Error("exitDelay does not fit the locktime field (u32)");
|
|
553
|
+
}
|
|
554
|
+
return exit;
|
|
555
|
+
}
|
|
489
556
|
function decodeOffer(data) {
|
|
490
557
|
const fields = {};
|
|
491
558
|
let off = 0;
|
|
@@ -505,6 +572,12 @@ function decodeOffer(data) {
|
|
|
505
572
|
for (const name of ["wantAsset", "offerAsset"]) {
|
|
506
573
|
if (fields[name]?.length === 0) throw new Error(`missing/invalid ${name}`);
|
|
507
574
|
}
|
|
575
|
+
for (const [name, value] of Object.entries(fields)) {
|
|
576
|
+
const width = FIELDS[name].width;
|
|
577
|
+
if (width !== void 0 && value.length !== width) {
|
|
578
|
+
throw new Error(`missing/invalid ${name}`);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
508
581
|
const need = (name) => {
|
|
509
582
|
const v = fields[name];
|
|
510
583
|
const len = FIELDS[name].width;
|
|
@@ -516,16 +589,36 @@ function decodeOffer(data) {
|
|
|
516
589
|
if (Boolean(fields.wantAsset) === Boolean(fields.offerAsset)) {
|
|
517
590
|
throw new Error("offer must carry exactly one of wantAsset or offerAsset");
|
|
518
591
|
}
|
|
592
|
+
const readRatio = (name) => {
|
|
593
|
+
const raw = fields[name];
|
|
594
|
+
if (!raw) return void 0;
|
|
595
|
+
const value = readU64(raw);
|
|
596
|
+
if (value === BigInt(0)) throw new Error(`missing/invalid ${name}`);
|
|
597
|
+
return value;
|
|
598
|
+
};
|
|
599
|
+
const ratioNum = readRatio("ratioNum");
|
|
600
|
+
const ratioDen = readRatio("ratioDen");
|
|
601
|
+
if (ratioNum === void 0 !== (ratioDen === void 0)) {
|
|
602
|
+
throw new Error("offer must carry both ratioNum and ratioDen, or neither");
|
|
603
|
+
}
|
|
519
604
|
return {
|
|
520
605
|
swapPkScript: need("swapPkScript"),
|
|
521
|
-
wantAmount:
|
|
606
|
+
wantAmount: readU64(amount),
|
|
522
607
|
...fields.wantAsset && { wantAsset: import_sdk2.asset.AssetId.fromBytes(fields.wantAsset) },
|
|
523
608
|
...fields.offerAsset && { offerAsset: import_sdk2.asset.AssetId.fromBytes(fields.offerAsset) },
|
|
524
609
|
makerPkScript: need("makerPkScript"),
|
|
525
610
|
makerPublicKey: need("makerPublicKey"),
|
|
526
|
-
emulatorPubkey: need("emulatorPubkey")
|
|
611
|
+
emulatorPubkey: need("emulatorPubkey"),
|
|
612
|
+
...ratioNum !== void 0 && { ratioNum },
|
|
613
|
+
...ratioDen !== void 0 && { ratioDen },
|
|
614
|
+
...fields.exitTimelock && { exitDelay: decodeExitDelay(fields.exitTimelock) }
|
|
527
615
|
};
|
|
528
616
|
}
|
|
617
|
+
function decodeExitDelay(value) {
|
|
618
|
+
const type = EXIT_TYPES[value[0]];
|
|
619
|
+
if (!type) throw new Error(`unknown exitDelay locktime type: 0x${value[0].toString(16)}`);
|
|
620
|
+
return { type, value: readU64(value.subarray(1)) };
|
|
621
|
+
}
|
|
529
622
|
var OFFER_CONTRACT_LABEL = "Arkade swap offer";
|
|
530
623
|
var OFFER_CONTRACT_KIND = "asset-swap-offer";
|
|
531
624
|
async function registerOfferContract(wallet, arkServerUrl, network, binding, serverPubkey, expectedPkScript) {
|
|
@@ -551,6 +644,14 @@ async function registerOfferContract(wallet, arkServerUrl, network, binding, ser
|
|
|
551
644
|
});
|
|
552
645
|
await promoteOfferContract(contractManager, import_base2.hex.encode(expectedPkScript));
|
|
553
646
|
}
|
|
647
|
+
function serverExitDelay(delay) {
|
|
648
|
+
if (typeof delay !== "bigint" || delay <= BigInt(0)) {
|
|
649
|
+
throw new Error(
|
|
650
|
+
"the server reports no usable unilateralExitDelay; pass `exitDelay` to set the offer's exit closure explicitly, or `noExit: true` to publish without one"
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
return assertExitDelay({ value: delay, type: delay < BigInt(512) ? "blocks" : "seconds" });
|
|
654
|
+
}
|
|
554
655
|
async function createOffer(wallet, arkServerUrl, params) {
|
|
555
656
|
if (Boolean(params.wantAsset) === Boolean(params.offerAsset)) {
|
|
556
657
|
throw new Error("set exactly one of wantAsset (BTC->asset) or offerAsset (asset->BTC)");
|
|
@@ -571,7 +672,11 @@ async function createOffer(wallet, arkServerUrl, params) {
|
|
|
571
672
|
offerAsset: params.offerAsset,
|
|
572
673
|
makerPkScript: import_sdk2.ArkAddress.decode(makerAddress).pkScript,
|
|
573
674
|
makerPublicKey,
|
|
574
|
-
emulatorPubkey: emuKey
|
|
675
|
+
emulatorPubkey: emuKey,
|
|
676
|
+
// checked HERE, before the covenant is derived and registered below:
|
|
677
|
+
// deferring it to `encodeOffer` leaves a registered contract behind for
|
|
678
|
+
// an offer that then fails to encode. @see assertExitDelay
|
|
679
|
+
exitDelay: params.noExit ? void 0 : params.exitDelay ? assertExitDelay(params.exitDelay) : serverExitDelay(info.unilateralExitDelay)
|
|
575
680
|
};
|
|
576
681
|
const script = offerVtxoScript(binding, serverPubKey);
|
|
577
682
|
const offer = { ...binding, swapPkScript: script.pkScript };
|
|
@@ -1062,6 +1167,7 @@ var L1_NETWORKS = {
|
|
|
1062
1167
|
testnet: btc.TEST_NETWORK,
|
|
1063
1168
|
regtest: { ...btc.TEST_NETWORK, bech32: "bcrt" }
|
|
1064
1169
|
};
|
|
1170
|
+
var l1ScriptForAddress = (address, network) => btc.OutScript.encode(btc.Address(L1_NETWORKS[network]).decode(address));
|
|
1065
1171
|
function onchainHtlcScript(params, network) {
|
|
1066
1172
|
if (params.claimKey.length !== 32 || params.refundKey.length !== 32) {
|
|
1067
1173
|
throw new Error("claimKey and refundKey must be 32-byte x-only keys");
|
|
@@ -1244,7 +1350,11 @@ async function classifyOnchainHtlc(chain, input) {
|
|
|
1244
1350
|
const best = utxos.sort((a, b) => b.amount > a.amount ? 1 : -1)[0];
|
|
1245
1351
|
if (!best) {
|
|
1246
1352
|
if (!input.funding) return { phase: "unfunded" };
|
|
1247
|
-
const spend = await chain.getSpendingTx(
|
|
1353
|
+
const spend = await chain.getSpendingTx(
|
|
1354
|
+
input.funding.txid,
|
|
1355
|
+
input.funding.vout,
|
|
1356
|
+
input.htlc.pkScript
|
|
1357
|
+
);
|
|
1248
1358
|
if (!spend) return { phase: "unfunded" };
|
|
1249
1359
|
const preimage = extractPreimage(spend.txHex, input.htlc.paymentHash);
|
|
1250
1360
|
const txid = btc.Transaction.fromRaw(import_base3.hex.decode(spend.txHex), {
|
|
@@ -1303,7 +1413,8 @@ function onchainSendProfile(result) {
|
|
|
1303
1413
|
htlcLocktime: result.htlcParams.refundLocktime,
|
|
1304
1414
|
network: result.l1Network,
|
|
1305
1415
|
htlcAddress: result.htlc.address,
|
|
1306
|
-
minConfirmations: result.minConfirmations
|
|
1416
|
+
minConfirmations: result.minConfirmations,
|
|
1417
|
+
payoutPkScript: import_base4.hex.encode(result.payoutPkScript)
|
|
1307
1418
|
};
|
|
1308
1419
|
}
|
|
1309
1420
|
var OnchainSendCorridor = {
|
|
@@ -1352,6 +1463,9 @@ var OnchainSendCorridor = {
|
|
|
1352
1463
|
paymentHash,
|
|
1353
1464
|
htlc,
|
|
1354
1465
|
minConfirmations: profile.minConfirmations,
|
|
1466
|
+
// Optional here, required at the write: throwing on an older
|
|
1467
|
+
// record would strand the refund it is still owed.
|
|
1468
|
+
...profile.payoutPkScript ? { payoutPkScript: import_base4.hex.decode(profile.payoutPkScript) } : {},
|
|
1355
1469
|
...profile.funding ? { funding: profile.funding } : {},
|
|
1356
1470
|
...profile.claimTxid ? { claimTxid: profile.claimTxid } : {}
|
|
1357
1471
|
};
|
|
@@ -1512,7 +1626,9 @@ function classifySpend(offer, serverPubkey, spendTx, deposit) {
|
|
|
1512
1626
|
const script = offerVtxoScript(offer, serverPubkey);
|
|
1513
1627
|
if (import_base6.hex.encode(script.pkScript) !== import_base6.hex.encode(offer.swapPkScript)) return "indeterminate";
|
|
1514
1628
|
leaves = {
|
|
1515
|
-
|
|
1629
|
+
// both routes that hand the deposit back; `exit` is absent on an
|
|
1630
|
+
// offer that carries no exit closure, and drops out here
|
|
1631
|
+
returned: ["cancel", "exit"].map((name) => script.functionByName(name)?.leafScript).filter((leaf) => leaf !== void 0),
|
|
1516
1632
|
fulfill: script.functionByName("fulfill")?.leafScript
|
|
1517
1633
|
};
|
|
1518
1634
|
} catch {
|
|
@@ -1524,7 +1640,7 @@ function classifySpend(offer, serverPubkey, spendTx, deposit) {
|
|
|
1524
1640
|
if (import_base6.hex.encode(input.txid) !== deposit.txid) continue;
|
|
1525
1641
|
for (const leaf of input.tapLeafScript ?? []) {
|
|
1526
1642
|
const spent = import_base6.hex.encode((0, import_sdk6.scriptFromTapLeafScript)(leaf));
|
|
1527
|
-
if (leaves.
|
|
1643
|
+
if (leaves.returned.some((back) => spent === import_base6.hex.encode(back))) return "cancelled";
|
|
1528
1644
|
if (leaves.fulfill && spent === import_base6.hex.encode(leaves.fulfill)) return "fulfilled";
|
|
1529
1645
|
}
|
|
1530
1646
|
}
|
|
@@ -1918,8 +2034,22 @@ var assertPairLength = (pair) => {
|
|
|
1918
2034
|
};
|
|
1919
2035
|
var verifyLockupAddress = (quote, derivedAddress) => {
|
|
1920
2036
|
const quoted = quote.profile?.lockup_address;
|
|
1921
|
-
|
|
1922
|
-
|
|
2037
|
+
const candidates = Array.isArray(derivedAddress) ? derivedAddress : [derivedAddress];
|
|
2038
|
+
const matched = candidates.find((address) => address === quoted);
|
|
2039
|
+
if (matched === void 0) throw new AddressMismatch(candidates, quoted);
|
|
2040
|
+
return matched;
|
|
2041
|
+
};
|
|
2042
|
+
var LOCKUP_SHAPE_VARIANTS = [void 0, "preTimelockedRefund"];
|
|
2043
|
+
var matchQuotedLockup = (quote, hrp, serverPubkey, build) => {
|
|
2044
|
+
const candidates = LOCKUP_SHAPE_VARIANTS.map((legacy) => {
|
|
2045
|
+
const script = build(legacy);
|
|
2046
|
+
return { script, address: script.address(hrp, serverPubkey).encode(), legacy };
|
|
2047
|
+
});
|
|
2048
|
+
const matchedAddress = verifyLockupAddress(
|
|
2049
|
+
quote,
|
|
2050
|
+
candidates.map((candidate) => candidate.address)
|
|
2051
|
+
);
|
|
2052
|
+
return candidates.find((candidate) => candidate.address === matchedAddress);
|
|
1923
2053
|
};
|
|
1924
2054
|
var assertFundable = (input) => {
|
|
1925
2055
|
const fail = (reason, message) => {
|
|
@@ -1933,6 +2063,43 @@ var assertFundable = (input) => {
|
|
|
1933
2063
|
if (input.quote.refund_locktime !== void 0 && input.quote.refund_locktime - input.now < MIN_HEADROOM_SECONDS) {
|
|
1934
2064
|
fail("insufficient_headroom", "refund deadline headroom below 90 minutes");
|
|
1935
2065
|
}
|
|
2066
|
+
if (input.maxFee) {
|
|
2067
|
+
const { bps, sats, referenceRate } = input.maxFee;
|
|
2068
|
+
if (bps === void 0 && sats === void 0) {
|
|
2069
|
+
fail("max_fee_unbounded", "maxFee names neither bps nor sats");
|
|
2070
|
+
}
|
|
2071
|
+
if (bps !== void 0 && (!Number.isInteger(bps) || bps < 0 || bps > 1e4)) {
|
|
2072
|
+
fail("max_fee_out_of_range", `maxFee.bps must be an integer in 0..10000, got ${bps}`);
|
|
2073
|
+
}
|
|
2074
|
+
if (sats !== void 0 && (!Number.isInteger(sats) || sats < 0)) {
|
|
2075
|
+
fail("max_fee_out_of_range", `maxFee.sats must be a non-negative integer, got ${sats}`);
|
|
2076
|
+
}
|
|
2077
|
+
const legs = input.quote.pair.split("->");
|
|
2078
|
+
const assetOf = (leg) => leg.slice(leg.indexOf(":") + 1);
|
|
2079
|
+
const sameAsset = legs.length === 2 && assetOf(legs[0]) === assetOf(legs[1]);
|
|
2080
|
+
if (!sameAsset && referenceRate === void 0) {
|
|
2081
|
+
fail(
|
|
2082
|
+
"fee_gate_unavailable",
|
|
2083
|
+
`maxFee cannot gate ${input.quote.pair}: its legs name different assets, so from_amount - to_amount is not a fee. Supply maxFee.referenceRate (to-units per from-unit) from a source of your OWN \u2014 reading it off the solver's published feed would check the solver against its own number`
|
|
2084
|
+
);
|
|
2085
|
+
}
|
|
2086
|
+
if (!sameAsset && (!Number.isFinite(referenceRate) || referenceRate <= 0)) {
|
|
2087
|
+
fail(
|
|
2088
|
+
"max_fee_out_of_range",
|
|
2089
|
+
`maxFee.referenceRate must be a positive finite number, got ${referenceRate}`
|
|
2090
|
+
);
|
|
2091
|
+
}
|
|
2092
|
+
const fee = sameAsset ? input.quote.from_amount - input.quote.to_amount : Math.ceil(
|
|
2093
|
+
(input.quote.from_amount * referenceRate - input.quote.to_amount) / referenceRate
|
|
2094
|
+
);
|
|
2095
|
+
const allowed = Math.max(
|
|
2096
|
+
sats ?? 0,
|
|
2097
|
+
Math.floor(input.quote.from_amount * (bps ?? 0) / 1e4)
|
|
2098
|
+
);
|
|
2099
|
+
if (fee > allowed) {
|
|
2100
|
+
fail("fee_too_high", `fee ${fee} exceeds the ${allowed} this client allows`);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
1936
2103
|
if (input.onchain) {
|
|
1937
2104
|
const { htlcLocktime, minConfirmations, direction } = input.onchain;
|
|
1938
2105
|
if (!Number.isInteger(minConfirmations) || minConfirmations < 1 || minConfirmations > MAX_MIN_CONFIRMATIONS) {
|
|
@@ -2121,13 +2288,11 @@ function lightningSendVtxoScript(params) {
|
|
|
2121
2288
|
unilateralRefundWithoutReceiverDelay: seconds(
|
|
2122
2289
|
unilateralRefundWithoutReceiverDelay(params.claimDelay)
|
|
2123
2290
|
),
|
|
2124
|
-
|
|
2291
|
+
nonInteractiveParameters: {
|
|
2125
2292
|
receiverPkScript: params.receiverPkScript,
|
|
2126
|
-
emulatorPubkey: params.emulatorPubkey
|
|
2127
|
-
},
|
|
2128
|
-
nonInteractiveRefund: {
|
|
2129
2293
|
senderPkScript: params.refundPkScript,
|
|
2130
|
-
emulatorPubkey: params.emulatorPubkey
|
|
2294
|
+
emulatorPubkey: params.emulatorPubkey,
|
|
2295
|
+
...params.legacy !== void 0 && { legacy: params.legacy }
|
|
2131
2296
|
}
|
|
2132
2297
|
});
|
|
2133
2298
|
}
|
|
@@ -2173,9 +2338,18 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
|
|
|
2173
2338
|
receiverPkScript: solverHex(receiverPkScriptHex, "profile.receiver_pk_script"),
|
|
2174
2339
|
refundPkScript: secrets.pkScript
|
|
2175
2340
|
};
|
|
2176
|
-
const
|
|
2177
|
-
|
|
2178
|
-
|
|
2341
|
+
const matched = matchQuotedLockup(
|
|
2342
|
+
quote,
|
|
2343
|
+
network.hrp,
|
|
2344
|
+
serverPubkey,
|
|
2345
|
+
(legacy) => lightningSendVtxoScript({ ...treeParams, ...legacy !== void 0 && { legacy } })
|
|
2346
|
+
);
|
|
2347
|
+
const script = matched.script;
|
|
2348
|
+
const address = matched.address;
|
|
2349
|
+
const matchedTreeParams = {
|
|
2350
|
+
...treeParams,
|
|
2351
|
+
...matched.legacy !== void 0 && { legacy: matched.legacy }
|
|
2352
|
+
};
|
|
2179
2353
|
assertFundable({
|
|
2180
2354
|
quote,
|
|
2181
2355
|
invoiceExpiresAt: params.invoice.expiresAt,
|
|
@@ -2194,7 +2368,7 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
|
|
|
2194
2368
|
refundAddress,
|
|
2195
2369
|
senderPubkey,
|
|
2196
2370
|
secrets,
|
|
2197
|
-
treeParams
|
|
2371
|
+
treeParams: matchedTreeParams
|
|
2198
2372
|
};
|
|
2199
2373
|
}
|
|
2200
2374
|
var offerTermsFromQuote = (quote, assets) => {
|
|
@@ -2259,7 +2433,7 @@ function deriveOnchainSend(input) {
|
|
|
2259
2433
|
if (refundLocktime === void 0 || htlcPubkey === void 0 || htlcLocktime === void 0 || minConfirmations === void 0 || receiverPkScriptHex === void 0) {
|
|
2260
2434
|
throw new Error("onchain-send quote is missing a binding field");
|
|
2261
2435
|
}
|
|
2262
|
-
const
|
|
2436
|
+
const treeParams = {
|
|
2263
2437
|
solverPubkey: (0, import_sdk9.toXOnly)(import_base10.hex.decode(quote.solver_pubkey), "solver key"),
|
|
2264
2438
|
refundLocktime,
|
|
2265
2439
|
serverPubkey: input.serverPubkey,
|
|
@@ -2269,9 +2443,13 @@ function deriveOnchainSend(input) {
|
|
|
2269
2443
|
senderPubkey: input.senderPubkey,
|
|
2270
2444
|
receiverPkScript: solverHex(receiverPkScriptHex, "profile.receiver_pk_script"),
|
|
2271
2445
|
refundPkScript: import_sdk9.ArkAddress.decode(input.refundAddress).pkScript
|
|
2272
|
-
}
|
|
2273
|
-
const address =
|
|
2274
|
-
|
|
2446
|
+
};
|
|
2447
|
+
const { script, address } = matchQuotedLockup(
|
|
2448
|
+
quote,
|
|
2449
|
+
input.hrp,
|
|
2450
|
+
input.serverPubkey,
|
|
2451
|
+
(legacy) => lightningSendVtxoScript({ ...treeParams, ...legacy !== void 0 && { legacy } })
|
|
2452
|
+
);
|
|
2275
2453
|
const htlcParams = {
|
|
2276
2454
|
paymentHash: input.paymentHash,
|
|
2277
2455
|
claimKey: input.payoutPubkey,
|
|
@@ -2317,6 +2495,7 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
|
|
|
2317
2495
|
amountSide: params.amountSide
|
|
2318
2496
|
})
|
|
2319
2497
|
);
|
|
2498
|
+
assertQuotedAmount(quote, params.amountSide, params.amount);
|
|
2320
2499
|
const network = (0, import_sdk9.getNetwork)(info.network);
|
|
2321
2500
|
const derived = deriveOnchainSend({
|
|
2322
2501
|
quote,
|
|
@@ -2446,13 +2625,11 @@ function receiveVtxoScript(params) {
|
|
|
2446
2625
|
unilateralRefundWithoutReceiverDelay: seconds(
|
|
2447
2626
|
unilateralRefundWithoutReceiverDelay(params.claimDelay)
|
|
2448
2627
|
),
|
|
2449
|
-
|
|
2628
|
+
nonInteractiveParameters: {
|
|
2450
2629
|
receiverPkScript: params.payoutPkScript,
|
|
2451
|
-
emulatorPubkey: params.emulatorPubkey
|
|
2452
|
-
},
|
|
2453
|
-
nonInteractiveRefund: {
|
|
2454
2630
|
senderPkScript: params.solverRefundPkScript,
|
|
2455
|
-
emulatorPubkey: params.emulatorPubkey
|
|
2631
|
+
emulatorPubkey: params.emulatorPubkey,
|
|
2632
|
+
...params.legacy !== void 0 && { legacy: params.legacy }
|
|
2456
2633
|
}
|
|
2457
2634
|
});
|
|
2458
2635
|
}
|
|
@@ -2476,10 +2653,23 @@ function deriveLightningReceive(input) {
|
|
|
2476
2653
|
payoutPubkey: input.payoutPubkey,
|
|
2477
2654
|
payoutPkScript: import_sdk9.ArkAddress.decode(input.payoutAddress).pkScript
|
|
2478
2655
|
};
|
|
2479
|
-
const
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2656
|
+
const matched = matchQuotedLockup(
|
|
2657
|
+
quote,
|
|
2658
|
+
input.hrp,
|
|
2659
|
+
input.serverPubkey,
|
|
2660
|
+
(legacy) => receiveVtxoScript({ ...treeParams, ...legacy !== void 0 && { legacy } })
|
|
2661
|
+
);
|
|
2662
|
+
return {
|
|
2663
|
+
address: matched.address,
|
|
2664
|
+
swapPkScript: matched.script.pkScript,
|
|
2665
|
+
script: matched.script,
|
|
2666
|
+
invoice,
|
|
2667
|
+
refundLocktime,
|
|
2668
|
+
treeParams: {
|
|
2669
|
+
...treeParams,
|
|
2670
|
+
...matched.legacy !== void 0 && { legacy: matched.legacy }
|
|
2671
|
+
}
|
|
2672
|
+
};
|
|
2483
2673
|
}
|
|
2484
2674
|
async function requestLightningReceive(wallet, arkServerUrl, transport, params) {
|
|
2485
2675
|
const rfqId = params.rfqId ?? newRfqId();
|
|
@@ -2567,7 +2757,7 @@ function deriveOnchainReceive(input) {
|
|
|
2567
2757
|
if (refundLocktime === void 0 || claimPubkey === void 0 || htlcLocktime === void 0 || minConfirmations === void 0 || solverRefundPkScriptHex === void 0) {
|
|
2568
2758
|
throw new Error("onchain-receive quote is missing a binding field");
|
|
2569
2759
|
}
|
|
2570
|
-
const
|
|
2760
|
+
const treeParams = {
|
|
2571
2761
|
solverPubkey: (0, import_sdk9.toXOnly)(import_base10.hex.decode(quote.solver_pubkey), "solver key"),
|
|
2572
2762
|
refundLocktime,
|
|
2573
2763
|
serverPubkey: input.serverPubkey,
|
|
@@ -2577,9 +2767,13 @@ function deriveOnchainReceive(input) {
|
|
|
2577
2767
|
solverRefundPkScript: solverHex(solverRefundPkScriptHex, "profile.solver_refund_pk_script"),
|
|
2578
2768
|
payoutPubkey: input.payoutPubkey,
|
|
2579
2769
|
payoutPkScript: import_sdk9.ArkAddress.decode(input.payoutAddress).pkScript
|
|
2580
|
-
}
|
|
2581
|
-
const address =
|
|
2582
|
-
|
|
2770
|
+
};
|
|
2771
|
+
const { script, address } = matchQuotedLockup(
|
|
2772
|
+
quote,
|
|
2773
|
+
input.hrp,
|
|
2774
|
+
input.serverPubkey,
|
|
2775
|
+
(legacy) => receiveVtxoScript({ ...treeParams, ...legacy !== void 0 && { legacy } })
|
|
2776
|
+
);
|
|
2583
2777
|
const htlc = onchainHtlcScript(
|
|
2584
2778
|
{
|
|
2585
2779
|
paymentHash: input.paymentHash,
|
|
@@ -2677,16 +2871,302 @@ async function requestOnchainReceive(wallet, arkServerUrl, transport, params) {
|
|
|
2677
2871
|
};
|
|
2678
2872
|
}
|
|
2679
2873
|
|
|
2680
|
-
// src/
|
|
2874
|
+
// src/chainSource.ts
|
|
2875
|
+
var btc2 = __toESM(require("@scure/btc-signer"), 1);
|
|
2876
|
+
var import_base11 = require("@scure/base");
|
|
2877
|
+
var addressOf = (pkScript, network) => btc2.Address(L1_NETWORKS[network]).encode(btc2.OutScript.decode(pkScript));
|
|
2878
|
+
var chainSourceFrom = (provider, network) => {
|
|
2879
|
+
const spenderFromHistory = async (txid, vout, pkScript) => {
|
|
2880
|
+
const txs = await provider.getTransactions(addressOf(pkScript, network));
|
|
2881
|
+
return txs.find((tx) => tx.vin?.some((i) => i.txid === txid && i.vout === vout))?.txid;
|
|
2882
|
+
};
|
|
2883
|
+
return {
|
|
2884
|
+
async getScriptUtxos(pkScript) {
|
|
2885
|
+
const address = addressOf(pkScript, network);
|
|
2886
|
+
const [coins, tip] = await Promise.all([
|
|
2887
|
+
provider.getCoins(address),
|
|
2888
|
+
provider.getChainTip()
|
|
2889
|
+
]);
|
|
2890
|
+
return coins.map((coin) => ({
|
|
2891
|
+
txid: coin.txid,
|
|
2892
|
+
vout: coin.vout,
|
|
2893
|
+
amount: BigInt(coin.value),
|
|
2894
|
+
// Zero, not one: calling a mempool output "1 deep" would let a
|
|
2895
|
+
// 1-confirmation policy claim against a replaceable transaction.
|
|
2896
|
+
confirmations: coin.status.confirmed && typeof coin.status.block_height === "number" ? Math.max(0, tip.height - coin.status.block_height + 1) : 0
|
|
2897
|
+
}));
|
|
2898
|
+
},
|
|
2899
|
+
async getSpendingTx(txid, vout, pkScript) {
|
|
2900
|
+
const outspends = await provider.getTxOutspends(txid);
|
|
2901
|
+
const outspend = outspends[vout];
|
|
2902
|
+
if (!outspend?.spent) return null;
|
|
2903
|
+
const spender = outspend.txid || await spenderFromHistory(txid, vout, pkScript);
|
|
2904
|
+
if (!spender) return null;
|
|
2905
|
+
const raw = await provider.getRawTransaction(spender);
|
|
2906
|
+
return { txHex: import_base11.hex.encode(raw) };
|
|
2907
|
+
},
|
|
2908
|
+
broadcast(txHex) {
|
|
2909
|
+
return provider.broadcastTransaction(txHex);
|
|
2910
|
+
},
|
|
2911
|
+
async getMtp() {
|
|
2912
|
+
return (await provider.getChainTip()).time;
|
|
2913
|
+
}
|
|
2914
|
+
};
|
|
2915
|
+
};
|
|
2916
|
+
|
|
2917
|
+
// src/payment/rendezvous.ts
|
|
2918
|
+
var import_solver_discovery2 = require("@arkade-os/solver-discovery");
|
|
2681
2919
|
var import_base12 = require("@scure/base");
|
|
2920
|
+
var XONLY_HEX = /^[0-9a-f]{64}$/;
|
|
2921
|
+
var rendezvousOf = (market, pinned) => {
|
|
2922
|
+
const transports = { nostr: { relays: market.transports?.nostr?.relays ?? [] } };
|
|
2923
|
+
if (!market.discovery_pubkey || !transports.nostr.relays.length) return void 0;
|
|
2924
|
+
const advertised = market.emulator_pubkey;
|
|
2925
|
+
const emulatorPubkey = advertised === void 0 || advertised === null || advertised === "" ? pinned : typeof advertised === "string" && XONLY_HEX.test(advertised) ? advertised : void 0;
|
|
2926
|
+
if (!emulatorPubkey) return void 0;
|
|
2927
|
+
if (pinned && emulatorPubkey !== pinned) return void 0;
|
|
2928
|
+
const bounds = (0, import_solver_discovery2.sideLimits)(market, "quote");
|
|
2929
|
+
if (!bounds) return void 0;
|
|
2930
|
+
return {
|
|
2931
|
+
solverPubkey: market.discovery_pubkey,
|
|
2932
|
+
transports,
|
|
2933
|
+
minSats: Number(bounds.min),
|
|
2934
|
+
maxSats: Number(bounds.max)
|
|
2935
|
+
};
|
|
2936
|
+
};
|
|
2937
|
+
var solverRendezvous = (markets, payoutCorridor, amountSats, fallbackEmulatorPubkey) => {
|
|
2938
|
+
const encoded = fallbackEmulatorPubkey ? import_base12.hex.encode(fallbackEmulatorPubkey) : void 0;
|
|
2939
|
+
if (encoded !== void 0 && !XONLY_HEX.test(encoded)) return void 0;
|
|
2940
|
+
const pinned = encoded;
|
|
2941
|
+
const candidates = (0, import_solver_discovery2.selectMarkets)(markets, {
|
|
2942
|
+
baseId: BTC_ASSET_ID,
|
|
2943
|
+
quoteId: BTC_ASSET_ID,
|
|
2944
|
+
baseCorridor: "arkade",
|
|
2945
|
+
quoteCorridor: payoutCorridor
|
|
2946
|
+
});
|
|
2947
|
+
for (const market of candidates) {
|
|
2948
|
+
const rendezvous = rendezvousOf(market, pinned);
|
|
2949
|
+
if (!rendezvous) continue;
|
|
2950
|
+
if (amountSats >= rendezvous.minSats && amountSats <= rendezvous.maxSats) {
|
|
2951
|
+
return rendezvous;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return void 0;
|
|
2955
|
+
};
|
|
2956
|
+
|
|
2957
|
+
// src/payment/solverOnchain.ts
|
|
2958
|
+
var import_sdk11 = require("@arkade-os/sdk");
|
|
2959
|
+
var SOLVER_ONCHAIN_RAIL = "solver-onchain";
|
|
2960
|
+
var solverOnchainRendezvous = (markets, amountSats, fallbackEmulatorPubkey) => solverRendezvous(markets, "onchain", amountSats, fallbackEmulatorPubkey);
|
|
2961
|
+
function solverOnchainRail(deps) {
|
|
2962
|
+
const rendezvousFor = async (amount) => {
|
|
2963
|
+
if (amount === void 0) return void 0;
|
|
2964
|
+
const markets = await deps.discover();
|
|
2965
|
+
return solverOnchainRendezvous(markets, amount, deps.fallbackEmulatorPubkey);
|
|
2966
|
+
};
|
|
2967
|
+
return {
|
|
2968
|
+
id: SOLVER_ONCHAIN_RAIL,
|
|
2969
|
+
match: (req) => (0, import_sdk11.btcTarget)(req.raw) !== void 0,
|
|
2970
|
+
available: async (req) => {
|
|
2971
|
+
const address = (0, import_sdk11.btcTarget)(req.raw);
|
|
2972
|
+
if (!address) return false;
|
|
2973
|
+
try {
|
|
2974
|
+
l1ScriptForAddress(address, deps.l1Network);
|
|
2975
|
+
} catch {
|
|
2976
|
+
return false;
|
|
2977
|
+
}
|
|
2978
|
+
const amount = (0, import_sdk11.tryResolveSendAmount)(req.raw, req.amount);
|
|
2979
|
+
if (amount === void 0) return false;
|
|
2980
|
+
return await rendezvousFor(amount) !== void 0;
|
|
2981
|
+
},
|
|
2982
|
+
quote: async (req, ctx) => {
|
|
2983
|
+
const address = (0, import_sdk11.btcTarget)(req.raw);
|
|
2984
|
+
const amount = (0, import_sdk11.resolveSendAmount)(SOLVER_ONCHAIN_RAIL, req.raw, req.amount);
|
|
2985
|
+
const payoutPkScript = l1ScriptForAddress(address, deps.l1Network);
|
|
2986
|
+
const rendezvous = await rendezvousFor(amount);
|
|
2987
|
+
if (!rendezvous) {
|
|
2988
|
+
throw new Error(
|
|
2989
|
+
`${SOLVER_ONCHAIN_RAIL}: no solver serves arkade:BTC -> onchain:BTC at ${amount} sats`
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
const negotiated = await deps.connect(
|
|
2993
|
+
rendezvous,
|
|
2994
|
+
(transport) => requestOnchainSend(ctx.wallet, deps.arkServerUrl, transport, {
|
|
2995
|
+
amount,
|
|
2996
|
+
amountSide: "to",
|
|
2997
|
+
payoutPubkey: deps.payoutPubkey,
|
|
2998
|
+
...deps.emulatorPubkey ? { emulatorPubkey: deps.emulatorPubkey } : {}
|
|
2999
|
+
})
|
|
3000
|
+
);
|
|
3001
|
+
if (negotiated.l1Network !== deps.l1Network) {
|
|
3002
|
+
throw new Error(
|
|
3003
|
+
`${SOLVER_ONCHAIN_RAIL}: rail built for ${deps.l1Network} but the swap was negotiated on ${negotiated.l1Network}`
|
|
3004
|
+
);
|
|
3005
|
+
}
|
|
3006
|
+
const swap = { ...negotiated, rendezvous, payoutPkScript };
|
|
3007
|
+
return {
|
|
3008
|
+
railId: SOLVER_ONCHAIN_RAIL,
|
|
3009
|
+
amount,
|
|
3010
|
+
fee: swap.fundAmount - amount,
|
|
3011
|
+
total: swap.fundAmount,
|
|
3012
|
+
meta: {
|
|
3013
|
+
rfqId: swap.rfqId,
|
|
3014
|
+
validUntil: swap.quote.valid_until,
|
|
3015
|
+
htlcAddress: swap.htlc.address,
|
|
3016
|
+
minConfirmations: swap.minConfirmations,
|
|
3017
|
+
solverPubkey: rendezvous.solverPubkey,
|
|
3018
|
+
// The claim tx's fee comes out of the HTLC output at a rate
|
|
3019
|
+
// not knowable now, so `amount` is the payout, not the net.
|
|
3020
|
+
claimFeeDeductedFromPayout: true
|
|
3021
|
+
},
|
|
3022
|
+
send: async () => (0, import_sdk11.makeHandle)(SOLVER_ONCHAIN_RAIL, async (emit) => {
|
|
3023
|
+
assertFundable({
|
|
3024
|
+
quote: swap.quote,
|
|
3025
|
+
now: Math.floor(Date.now() / 1e3),
|
|
3026
|
+
onchain: {
|
|
3027
|
+
htlcLocktime: swap.htlcParams.refundLocktime,
|
|
3028
|
+
minConfirmations: swap.minConfirmations,
|
|
3029
|
+
direction: "send"
|
|
3030
|
+
}
|
|
3031
|
+
});
|
|
3032
|
+
await deps.persist(swap);
|
|
3033
|
+
await ctx.wallet.send({
|
|
3034
|
+
address: swap.address,
|
|
3035
|
+
amount: swap.fundAmount
|
|
3036
|
+
});
|
|
3037
|
+
emit({ status: "sent" });
|
|
3038
|
+
const result = { railId: SOLVER_ONCHAIN_RAIL, swapId: swap.rfqId };
|
|
3039
|
+
if (!deps.awaitSettlement) return result;
|
|
3040
|
+
let txid;
|
|
3041
|
+
try {
|
|
3042
|
+
({ txid } = await deps.awaitSettlement(swap));
|
|
3043
|
+
} catch (e) {
|
|
3044
|
+
console.warn(
|
|
3045
|
+
`${SOLVER_ONCHAIN_RAIL}: settlement watch failed; the payment is sent`,
|
|
3046
|
+
e
|
|
3047
|
+
);
|
|
3048
|
+
return result;
|
|
3049
|
+
}
|
|
3050
|
+
const settled = { ...result, txid };
|
|
3051
|
+
emit({ status: "settled", result: settled });
|
|
3052
|
+
return settled;
|
|
3053
|
+
})
|
|
3054
|
+
};
|
|
3055
|
+
}
|
|
3056
|
+
};
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
// src/payment/solverLightning.ts
|
|
3060
|
+
var import_sdk12 = require("@arkade-os/sdk");
|
|
3061
|
+
var SOLVER_LIGHTNING_RAIL = "solver-lightning";
|
|
3062
|
+
var factsOf = (raw, decode, now) => {
|
|
3063
|
+
const invoice = (0, import_sdk12.invoiceTarget)(raw);
|
|
3064
|
+
if (!invoice) return void 0;
|
|
3065
|
+
let facts;
|
|
3066
|
+
try {
|
|
3067
|
+
facts = decode(invoice);
|
|
3068
|
+
} catch {
|
|
3069
|
+
return void 0;
|
|
3070
|
+
}
|
|
3071
|
+
if (!Number.isInteger(facts.amountSats) || facts.amountSats <= 0) return void 0;
|
|
3072
|
+
if (facts.expiresAt <= now) return void 0;
|
|
3073
|
+
return facts;
|
|
3074
|
+
};
|
|
3075
|
+
var solverLightningRendezvous = (markets, amountSats, fallbackEmulatorPubkey) => solverRendezvous(markets, "lightning", amountSats, fallbackEmulatorPubkey);
|
|
3076
|
+
function solverLightningRail(deps) {
|
|
3077
|
+
const rendezvousFor = async (amountSats) => solverLightningRendezvous(await deps.discover(), amountSats, deps.fallbackEmulatorPubkey);
|
|
3078
|
+
return {
|
|
3079
|
+
id: SOLVER_LIGHTNING_RAIL,
|
|
3080
|
+
match: (req) => (0, import_sdk12.invoiceTarget)(req.raw) !== void 0,
|
|
3081
|
+
available: async (req) => {
|
|
3082
|
+
const facts = factsOf(req.raw, deps.decodeInvoice, Math.floor(Date.now() / 1e3));
|
|
3083
|
+
if (!facts) return false;
|
|
3084
|
+
if (req.amount !== void 0 && req.amount !== facts.amountSats) return false;
|
|
3085
|
+
return await rendezvousFor(facts.amountSats) !== void 0;
|
|
3086
|
+
},
|
|
3087
|
+
quote: async (req, ctx) => {
|
|
3088
|
+
const facts = factsOf(req.raw, deps.decodeInvoice, Math.floor(Date.now() / 1e3));
|
|
3089
|
+
if (!facts) {
|
|
3090
|
+
throw new Error(
|
|
3091
|
+
`${SOLVER_LIGHTNING_RAIL}: the request carries no payable BOLT11 invoice (amountless, expired, or undecodable)`
|
|
3092
|
+
);
|
|
3093
|
+
}
|
|
3094
|
+
if (req.amount !== void 0 && req.amount !== facts.amountSats) {
|
|
3095
|
+
throw new Error(
|
|
3096
|
+
`${SOLVER_LIGHTNING_RAIL}: the request names ${req.amount} sats but the invoice is for ${facts.amountSats} \u2014 the payee is paid the invoice`
|
|
3097
|
+
);
|
|
3098
|
+
}
|
|
3099
|
+
const rendezvous = await rendezvousFor(facts.amountSats);
|
|
3100
|
+
if (!rendezvous) {
|
|
3101
|
+
throw new Error(
|
|
3102
|
+
`${SOLVER_LIGHTNING_RAIL}: no solver serves arkade:BTC -> lightning:BTC at ${facts.amountSats} sats`
|
|
3103
|
+
);
|
|
3104
|
+
}
|
|
3105
|
+
const negotiated = await deps.connect(
|
|
3106
|
+
rendezvous,
|
|
3107
|
+
(transport) => requestLightningSend(ctx.wallet, deps.arkServerUrl, transport, {
|
|
3108
|
+
invoice: facts,
|
|
3109
|
+
...deps.emulatorPubkey ? { emulatorPubkey: deps.emulatorPubkey } : {}
|
|
3110
|
+
})
|
|
3111
|
+
);
|
|
3112
|
+
const swap = { ...negotiated, invoice: facts, rendezvous };
|
|
3113
|
+
return {
|
|
3114
|
+
railId: SOLVER_LIGHTNING_RAIL,
|
|
3115
|
+
// `requestLightningSend` refuses a quote that reprices the
|
|
3116
|
+
// invoice, so the spread is a fee on top.
|
|
3117
|
+
amount: facts.amountSats,
|
|
3118
|
+
fee: swap.fundAmount - facts.amountSats,
|
|
3119
|
+
total: swap.fundAmount,
|
|
3120
|
+
meta: {
|
|
3121
|
+
rfqId: swap.rfqId,
|
|
3122
|
+
validUntil: swap.quote.valid_until,
|
|
3123
|
+
paymentHash: facts.paymentHash,
|
|
3124
|
+
invoiceExpiresAt: facts.expiresAt,
|
|
3125
|
+
solverPubkey: rendezvous.solverPubkey
|
|
3126
|
+
},
|
|
3127
|
+
send: async () => (0, import_sdk12.makeHandle)(SOLVER_LIGHTNING_RAIL, async (emit) => {
|
|
3128
|
+
assertFundable({
|
|
3129
|
+
quote: swap.quote,
|
|
3130
|
+
invoiceExpiresAt: facts.expiresAt,
|
|
3131
|
+
now: Math.floor(Date.now() / 1e3)
|
|
3132
|
+
});
|
|
3133
|
+
await deps.persist(swap);
|
|
3134
|
+
await ctx.wallet.send({
|
|
3135
|
+
address: swap.address,
|
|
3136
|
+
amount: swap.fundAmount
|
|
3137
|
+
});
|
|
3138
|
+
emit({ status: "sent" });
|
|
3139
|
+
const result = { railId: SOLVER_LIGHTNING_RAIL, swapId: swap.rfqId };
|
|
3140
|
+
if (!deps.awaitSettlement) return result;
|
|
3141
|
+
let preimage;
|
|
3142
|
+
try {
|
|
3143
|
+
({ preimage } = await deps.awaitSettlement(swap));
|
|
3144
|
+
} catch (e) {
|
|
3145
|
+
console.warn(
|
|
3146
|
+
`${SOLVER_LIGHTNING_RAIL}: settlement watch failed; the payment is sent`,
|
|
3147
|
+
e
|
|
3148
|
+
);
|
|
3149
|
+
return result;
|
|
3150
|
+
}
|
|
3151
|
+
const settled = { ...result, ...preimage !== void 0 && { preimage } };
|
|
3152
|
+
emit({ status: "settled", result: settled });
|
|
3153
|
+
return settled;
|
|
3154
|
+
})
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3157
|
+
};
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
// src/claim.ts
|
|
3161
|
+
var import_base14 = require("@scure/base");
|
|
2682
3162
|
var import_legacy3 = require("@noble/hashes/legacy.js");
|
|
2683
3163
|
var import_sha25 = require("@noble/hashes/sha2.js");
|
|
2684
|
-
var
|
|
3164
|
+
var import_sdk14 = require("@arkade-os/sdk");
|
|
2685
3165
|
|
|
2686
3166
|
// src/refund.ts
|
|
2687
|
-
var
|
|
3167
|
+
var import_base13 = require("@scure/base");
|
|
2688
3168
|
var import_sha24 = require("@noble/hashes/sha2.js");
|
|
2689
|
-
var
|
|
3169
|
+
var import_sdk13 = require("@arkade-os/sdk");
|
|
2690
3170
|
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2691
3171
|
var isRfqTerminal = (state) => RFQ_TERMINAL_STATES.includes(state);
|
|
2692
3172
|
var RFQ_RESOLVED_STATES = ["settled", "refunded"];
|
|
@@ -2733,7 +3213,7 @@ var LockupNeedsRecoveryError = class extends Error {
|
|
|
2733
3213
|
}
|
|
2734
3214
|
};
|
|
2735
3215
|
async function findLockupVtxos(indexer, swapPkScript) {
|
|
2736
|
-
const scripts = [
|
|
3216
|
+
const scripts = [import_base13.hex.encode(swapPkScript)];
|
|
2737
3217
|
const [spendable, recoverable] = await Promise.all([
|
|
2738
3218
|
indexer.getVtxos({ scripts, spendableOnly: true }),
|
|
2739
3219
|
indexer.getVtxos({ scripts, recoverableOnly: true })
|
|
@@ -2759,16 +3239,16 @@ async function findLockupVtxos(indexer, swapPkScript) {
|
|
|
2759
3239
|
}
|
|
2760
3240
|
return out;
|
|
2761
3241
|
}
|
|
2762
|
-
var hashesTo = (candidate, paymentHash) =>
|
|
3242
|
+
var hashesTo = (candidate, paymentHash) => import_base13.hex.encode((0, import_sha24.sha256)(candidate)) === paymentHash;
|
|
2763
3243
|
var candidateWitnessItems = (tx, inputIndex) => [
|
|
2764
|
-
...(0,
|
|
3244
|
+
...(0, import_sdk13.getArkPsbtFields)(tx, inputIndex, import_sdk13.ConditionWitness).flat(),
|
|
2765
3245
|
...tx.getInput(inputIndex).finalScriptWitness ?? []
|
|
2766
3246
|
];
|
|
2767
3247
|
async function readLockupFate(indexer, input) {
|
|
2768
|
-
const { vtxos } = await indexer.getVtxos({ scripts: [
|
|
3248
|
+
const { vtxos } = await indexer.getVtxos({ scripts: [import_base13.hex.encode(input.swapPkScript)] });
|
|
2769
3249
|
const all = vtxos ?? [];
|
|
2770
3250
|
if (all.length === 0) return { fate: "unknown" };
|
|
2771
|
-
const exited = all.filter((vtxo) => vtxo.isUnrolled && !(0,
|
|
3251
|
+
const exited = all.filter((vtxo) => vtxo.isUnrolled && !(0, import_sdk13.hasTerminalSpend)(vtxo));
|
|
2772
3252
|
if (exited.length > 0) {
|
|
2773
3253
|
return {
|
|
2774
3254
|
fate: "exited",
|
|
@@ -2778,7 +3258,7 @@ async function readLockupFate(indexer, input) {
|
|
|
2778
3258
|
const spentBy = /* @__PURE__ */ new Map();
|
|
2779
3259
|
let everySpendNamed = true;
|
|
2780
3260
|
for (const vtxo of all) {
|
|
2781
|
-
if (!(0,
|
|
3261
|
+
if (!(0, import_sdk13.hasTerminalSpend)(vtxo)) return { fate: "open" };
|
|
2782
3262
|
if (vtxo.spentBy)
|
|
2783
3263
|
spentBy.set(vtxo.spentBy, {
|
|
2784
3264
|
checkpointTxid: vtxo.spentBy,
|
|
@@ -2792,7 +3272,7 @@ async function readLockupFate(indexer, input) {
|
|
|
2792
3272
|
for (const raw of txs) {
|
|
2793
3273
|
let tx;
|
|
2794
3274
|
try {
|
|
2795
|
-
tx =
|
|
3275
|
+
tx = import_sdk13.Transaction.fromPSBT(import_base13.base64.decode(raw));
|
|
2796
3276
|
} catch {
|
|
2797
3277
|
continue;
|
|
2798
3278
|
}
|
|
@@ -2800,7 +3280,7 @@ async function readLockupFate(indexer, input) {
|
|
|
2800
3280
|
for (let i = 0; i < tx.inputsLength; i++) {
|
|
2801
3281
|
const spent = tx.getInput(i);
|
|
2802
3282
|
if (!spent.txid) continue;
|
|
2803
|
-
const txid =
|
|
3283
|
+
const txid = import_base13.hex.encode(spent.txid);
|
|
2804
3284
|
if (!all.some((vtxo) => vtxo.txid === txid && vtxo.vout === spent.index)) continue;
|
|
2805
3285
|
for (const candidate of candidateWitnessItems(tx, i)) {
|
|
2806
3286
|
if (hashesTo(candidate, input.paymentHash)) {
|
|
@@ -2820,23 +3300,23 @@ async function pushRefundWithoutReceiver(ark, input) {
|
|
|
2820
3300
|
input.script.options.refundLocktime
|
|
2821
3301
|
);
|
|
2822
3302
|
}
|
|
2823
|
-
const refundPkScript = input.refundPkScript ?? input.script.options.
|
|
3303
|
+
const refundPkScript = input.refundPkScript ?? input.script.options.nonInteractiveParameters?.senderPkScript;
|
|
2824
3304
|
if (!refundPkScript) {
|
|
2825
3305
|
throw new Error(
|
|
2826
|
-
"no refund destination: the contract carries no
|
|
3306
|
+
"no refund destination: the contract carries no emulator covenant suite, so pass refundPkScript explicitly"
|
|
2827
3307
|
);
|
|
2828
3308
|
}
|
|
2829
3309
|
const info = await ark.getInfo();
|
|
2830
3310
|
let serverUnrollScript;
|
|
2831
3311
|
try {
|
|
2832
|
-
serverUnrollScript =
|
|
3312
|
+
serverUnrollScript = import_sdk13.CSVMultisigTapscript.decode(import_base13.hex.decode(info.checkpointTapscript));
|
|
2833
3313
|
} catch {
|
|
2834
3314
|
throw new Error("invalid checkpointTapscript from the Arkade server");
|
|
2835
3315
|
}
|
|
2836
3316
|
const leaf = input.script.refundWithoutReceiver();
|
|
2837
3317
|
const tapTree = input.script.encode();
|
|
2838
3318
|
const amount = input.vtxos.reduce((sum, vtxo) => sum + vtxo.value, 0);
|
|
2839
|
-
const { arkTx, checkpoints } = (0,
|
|
3319
|
+
const { arkTx, checkpoints } = (0, import_sdk13.buildOffchainTx)(
|
|
2840
3320
|
input.vtxos.map((vtxo) => ({
|
|
2841
3321
|
txid: vtxo.txid,
|
|
2842
3322
|
vout: vtxo.vout,
|
|
@@ -2849,18 +3329,18 @@ async function pushRefundWithoutReceiver(ark, input) {
|
|
|
2849
3329
|
);
|
|
2850
3330
|
const signedArkTx = await input.sender.sign(arkTx);
|
|
2851
3331
|
const submitted = await ark.submitTx(
|
|
2852
|
-
|
|
2853
|
-
checkpoints.map((c) =>
|
|
3332
|
+
import_base13.base64.encode(signedArkTx.toPSBT()),
|
|
3333
|
+
checkpoints.map((c) => import_base13.base64.encode(c.toPSBT()))
|
|
2854
3334
|
);
|
|
2855
|
-
(0,
|
|
2856
|
-
const matched = (0,
|
|
3335
|
+
(0, import_sdk13.assertSubmittedArkTxid)(submitted, signedArkTx, "refundWithoutReceiver");
|
|
3336
|
+
const matched = (0, import_sdk13.matchServerCheckpoints)(
|
|
2857
3337
|
submitted.signedCheckpointTxs,
|
|
2858
3338
|
checkpoints,
|
|
2859
3339
|
"refundWithoutReceiver"
|
|
2860
3340
|
);
|
|
2861
3341
|
const finalCheckpoints = await Promise.all(
|
|
2862
3342
|
matched.map(
|
|
2863
|
-
async ({ server }) =>
|
|
3343
|
+
async ({ server }) => import_base13.base64.encode((await input.sender.sign(server, [0])).toPSBT())
|
|
2864
3344
|
)
|
|
2865
3345
|
);
|
|
2866
3346
|
await ark.finalizeTx(submitted.arkTxid, finalCheckpoints);
|
|
@@ -2875,6 +3355,21 @@ async function refundIfUnresolved(transport, ark, indexer, input) {
|
|
|
2875
3355
|
const status = await transport.status(input.rfqId);
|
|
2876
3356
|
if (status && isResolved(status.state)) return { outcome: "resolved", status };
|
|
2877
3357
|
if (now() >= input.refundLocktime) {
|
|
3358
|
+
let fate = { fate: "unknown" };
|
|
3359
|
+
try {
|
|
3360
|
+
fate = await readLockupFate(indexer, {
|
|
3361
|
+
swapPkScript: input.script.pkScript,
|
|
3362
|
+
paymentHash: input.paymentHash
|
|
3363
|
+
});
|
|
3364
|
+
} catch {
|
|
3365
|
+
}
|
|
3366
|
+
if (fate.fate === "exited") {
|
|
3367
|
+
return {
|
|
3368
|
+
outcome: "exited",
|
|
3369
|
+
outpoints: fate.outpoints.map((o) => `${o.txid}:${o.vout}`),
|
|
3370
|
+
status
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
2878
3373
|
const vtxos = await findLockupVtxos(indexer, input.script.pkScript);
|
|
2879
3374
|
if (vtxos.length === 0) return { outcome: "nothing_to_refund", status };
|
|
2880
3375
|
try {
|
|
@@ -2940,20 +3435,20 @@ async function pushClaim(ark, input) {
|
|
|
2940
3435
|
}
|
|
2941
3436
|
}
|
|
2942
3437
|
const committed = input.script.options.preimageHash;
|
|
2943
|
-
if (
|
|
3438
|
+
if (import_base14.hex.encode((0, import_legacy3.ripemd160)((0, import_sha25.sha256)(input.preimage))) !== import_base14.hex.encode(committed)) {
|
|
2944
3439
|
throw new Error("preimage does not match the covenant's payment hash");
|
|
2945
3440
|
}
|
|
2946
3441
|
const info = await ark.getInfo();
|
|
2947
3442
|
let serverUnrollScript;
|
|
2948
3443
|
try {
|
|
2949
|
-
serverUnrollScript =
|
|
3444
|
+
serverUnrollScript = import_sdk14.CSVMultisigTapscript.decode(import_base14.hex.decode(info.checkpointTapscript));
|
|
2950
3445
|
} catch {
|
|
2951
3446
|
throw new Error("invalid checkpointTapscript from the Arkade server");
|
|
2952
3447
|
}
|
|
2953
3448
|
const leaf = input.script.claim();
|
|
2954
3449
|
const tapTree = input.script.encode();
|
|
2955
|
-
const arkTxid = await (0,
|
|
2956
|
-
identity: (0,
|
|
3450
|
+
const arkTxid = await (0, import_sdk14.signAndSubmitOffchainTx)({
|
|
3451
|
+
identity: (0, import_sdk14.claimWithPreimageIdentity)(input.receiver, input.preimage),
|
|
2957
3452
|
provider: ark,
|
|
2958
3453
|
inputs: input.vtxos.map((vtxo) => ({
|
|
2959
3454
|
txid: vtxo.txid,
|
|
@@ -3000,7 +3495,7 @@ async function claimReceiveLockup(indexer, ark, input) {
|
|
|
3000
3495
|
}
|
|
3001
3496
|
|
|
3002
3497
|
// src/refundBlocked.ts
|
|
3003
|
-
var
|
|
3498
|
+
var import_sdk15 = require("@arkade-os/sdk");
|
|
3004
3499
|
var RefundNotLocallyPossibleError = class extends Error {
|
|
3005
3500
|
constructor(reason, message, options) {
|
|
3006
3501
|
super(message, options);
|
|
@@ -3017,16 +3512,16 @@ async function senderIdentityForSwapRecord(wallet, record) {
|
|
|
3017
3512
|
);
|
|
3018
3513
|
}
|
|
3019
3514
|
try {
|
|
3020
|
-
return await (0,
|
|
3515
|
+
return await (0, import_sdk15.contractSigner)(wallet, record.signingDescriptor);
|
|
3021
3516
|
} catch (cause) {
|
|
3022
|
-
if (cause instanceof
|
|
3517
|
+
if (cause instanceof import_sdk15.WalletCannotSignError) {
|
|
3023
3518
|
throw new RefundNotLocallyPossibleError(
|
|
3024
3519
|
"unsignable-wallet",
|
|
3025
3520
|
`this wallet holds ${record.signingDescriptor} but cannot sign with it; attach its signer`,
|
|
3026
3521
|
{ cause }
|
|
3027
3522
|
);
|
|
3028
3523
|
}
|
|
3029
|
-
if (cause instanceof
|
|
3524
|
+
if (cause instanceof import_sdk15.ForeignDescriptorError) {
|
|
3030
3525
|
throw new RefundNotLocallyPossibleError(
|
|
3031
3526
|
"foreign-descriptor",
|
|
3032
3527
|
`this wallet cannot derive ${record.signingDescriptor}; the swap was created on another wallet`,
|
|
@@ -3061,7 +3556,7 @@ function arkadeRefunder(deps) {
|
|
|
3061
3556
|
}
|
|
3062
3557
|
|
|
3063
3558
|
// src/swapManager.ts
|
|
3064
|
-
var
|
|
3559
|
+
var import_base15 = require("@scure/base");
|
|
3065
3560
|
function nextOnchainAction(input) {
|
|
3066
3561
|
switch (input.phase.phase) {
|
|
3067
3562
|
case "unfunded":
|
|
@@ -3509,7 +4004,7 @@ var RfqSwapManager = class {
|
|
|
3509
4004
|
// ── internals ────────────────────────────────────────────────────────────
|
|
3510
4005
|
track(swap) {
|
|
3511
4006
|
this.monitored.set(swap.rfqId, swap);
|
|
3512
|
-
this.byLockupScript.set(
|
|
4007
|
+
this.byLockupScript.set(import_base15.hex.encode(swap.lockupPkScript), swap);
|
|
3513
4008
|
}
|
|
3514
4009
|
/** Drops the swap from BOTH indexes. The event index is the one that stops
|
|
3515
4010
|
* a late event finding a swap that is gone; `pollSwap`'s own
|
|
@@ -3518,7 +4013,7 @@ var RfqSwapManager = class {
|
|
|
3518
4013
|
* them from silently re-driving a cancelled swap. */
|
|
3519
4014
|
untrack(rfqId) {
|
|
3520
4015
|
const swap = this.monitored.get(rfqId);
|
|
3521
|
-
if (swap) this.byLockupScript.delete(
|
|
4016
|
+
if (swap) this.byLockupScript.delete(import_base15.hex.encode(swap.lockupPkScript));
|
|
3522
4017
|
this.monitored.delete(rfqId);
|
|
3523
4018
|
this.refundRefused.delete(rfqId);
|
|
3524
4019
|
this.lastClaimError.delete(rfqId);
|
|
@@ -3580,7 +4075,7 @@ var RfqSwapManager = class {
|
|
|
3580
4075
|
if (!lockup) {
|
|
3581
4076
|
try {
|
|
3582
4077
|
const [existing] = await contracts.getContracts({
|
|
3583
|
-
script:
|
|
4078
|
+
script: import_base15.hex.encode(swap.lockupPkScript)
|
|
3584
4079
|
});
|
|
3585
4080
|
if (existing) {
|
|
3586
4081
|
this.registered.set(swap.rfqId, true);
|
|
@@ -3599,13 +4094,13 @@ var RfqSwapManager = class {
|
|
|
3599
4094
|
);
|
|
3600
4095
|
return;
|
|
3601
4096
|
}
|
|
3602
|
-
const script =
|
|
3603
|
-
if (script !==
|
|
4097
|
+
const script = import_base15.hex.encode(lockup.script.pkScript);
|
|
4098
|
+
if (script !== import_base15.hex.encode(swap.lockupPkScript)) {
|
|
3604
4099
|
this.registered.set(swap.rfqId, false);
|
|
3605
4100
|
this.emitFailed(
|
|
3606
4101
|
swap,
|
|
3607
4102
|
new Error(
|
|
3608
|
-
`swap ${swap.rfqId} lockup script ${script} does not match its lockupPkScript ${
|
|
4103
|
+
`swap ${swap.rfqId} lockup script ${script} does not match its lockupPkScript ${import_base15.hex.encode(swap.lockupPkScript)}`
|
|
3609
4104
|
)
|
|
3610
4105
|
);
|
|
3611
4106
|
return;
|
|
@@ -3624,7 +4119,7 @@ var RfqSwapManager = class {
|
|
|
3624
4119
|
* script for its whole life. Best-effort — the swap is over either way. */
|
|
3625
4120
|
retireContract(swap) {
|
|
3626
4121
|
if (!this.deps.contracts || !this.registered.get(swap.rfqId)) return;
|
|
3627
|
-
void this.deps.contracts.setContractWatchState(
|
|
4122
|
+
void this.deps.contracts.setContractWatchState(import_base15.hex.encode(swap.lockupPkScript), "retained").catch((error) => this.emitFailed(swap, error));
|
|
3628
4123
|
}
|
|
3629
4124
|
arm() {
|
|
3630
4125
|
if (!this.running) return;
|
|
@@ -4097,8 +4592,8 @@ var errorMessage = (error) => error instanceof Error ? error.message : String(er
|
|
|
4097
4592
|
var outpointKey = (vtxo) => `${vtxo.txid}:${vtxo.vout}`;
|
|
4098
4593
|
|
|
4099
4594
|
// src/activity.ts
|
|
4100
|
-
var
|
|
4101
|
-
var
|
|
4595
|
+
var import_sdk16 = require("@arkade-os/sdk");
|
|
4596
|
+
var import_base16 = require("@scure/base");
|
|
4102
4597
|
var LABELS = {
|
|
4103
4598
|
lightning_send: "Lightning send",
|
|
4104
4599
|
lightning_receive: "Lightning receive",
|
|
@@ -4173,7 +4668,7 @@ async function activityInputOf(record, indexer) {
|
|
|
4173
4668
|
async function lockupTxids(indexer, record, wantFunding) {
|
|
4174
4669
|
let script;
|
|
4175
4670
|
try {
|
|
4176
|
-
script =
|
|
4671
|
+
script = import_base16.hex.encode(import_sdk16.ArkAddress.decode(record.lockupAddress).pkScript);
|
|
4177
4672
|
} catch {
|
|
4178
4673
|
return [];
|
|
4179
4674
|
}
|
|
@@ -4227,6 +4722,8 @@ async function lockupTxids(indexer, record, wantFunding) {
|
|
|
4227
4722
|
RfqSwapManager,
|
|
4228
4723
|
RfqSwapOriginRequired,
|
|
4229
4724
|
SOLO_REFUND_HEADROOM_SECONDS,
|
|
4725
|
+
SOLVER_LIGHTNING_RAIL,
|
|
4726
|
+
SOLVER_ONCHAIN_RAIL,
|
|
4230
4727
|
SWAP_LOCKUP_CONTRACT_KIND,
|
|
4231
4728
|
SWAP_LOCKUP_CONTRACT_LABEL,
|
|
4232
4729
|
SWAP_LOCKUP_CONTRACT_TYPE,
|
|
@@ -4243,6 +4740,7 @@ async function lockupTxids(indexer, record, wantFunding) {
|
|
|
4243
4740
|
buildHtlcClaim,
|
|
4244
4741
|
buildHtlcRefund,
|
|
4245
4742
|
cancelOffer,
|
|
4743
|
+
chainSourceFrom,
|
|
4246
4744
|
claimOnchainFill,
|
|
4247
4745
|
claimReceiveLockup,
|
|
4248
4746
|
classifyDepositSpend,
|
|
@@ -4264,6 +4762,7 @@ async function lockupTxids(indexer, record, wantFunding) {
|
|
|
4264
4762
|
httpTransport,
|
|
4265
4763
|
isRfqSwapTerminal,
|
|
4266
4764
|
isRfqTerminal,
|
|
4765
|
+
l1ScriptForAddress,
|
|
4267
4766
|
lightningReceiveRequest,
|
|
4268
4767
|
lightningSendRequest,
|
|
4269
4768
|
lightningSendVtxoScript,
|
|
@@ -4303,6 +4802,11 @@ async function lockupTxids(indexer, record, wantFunding) {
|
|
|
4303
4802
|
sealClaimPacket,
|
|
4304
4803
|
senderIdentityForSwapRecord,
|
|
4305
4804
|
shouldRetainRfqSwap,
|
|
4805
|
+
solverLightningRail,
|
|
4806
|
+
solverLightningRendezvous,
|
|
4807
|
+
solverOnchainRail,
|
|
4808
|
+
solverOnchainRendezvous,
|
|
4809
|
+
solverRendezvous,
|
|
4306
4810
|
spendTxidsOf,
|
|
4307
4811
|
spendUpdate,
|
|
4308
4812
|
swapActivityResolver,
|