@arkade-os/swap 0.0.3 → 0.0.5
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 +189 -54
- package/dist/{chunk-HLMMA3LJ.js → chunk-Q4FAYBXS.js} +60 -247
- package/dist/index.cjs +212 -301
- package/dist/index.d.cts +283 -10
- package/dist/index.d.ts +283 -10
- package/dist/index.js +150 -69
- package/dist/nostr.cjs +21 -14
- package/dist/nostr.d.cts +14 -3
- package/dist/nostr.d.ts +14 -3
- package/dist/nostr.js +10 -2
- package/dist/{rfq-CCAFoFtR.d.cts → rfq-BH2yvo3O.d.cts} +50 -335
- package/dist/{rfq-CCAFoFtR.d.ts → rfq-BH2yvo3O.d.ts} +50 -335
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -16,31 +16,25 @@ import {
|
|
|
16
16
|
ONCHAIN_RECEIVE_PAIR,
|
|
17
17
|
ONCHAIN_SECONDS_PER_BLOCK,
|
|
18
18
|
ONCHAIN_SEND_PAIR,
|
|
19
|
-
RFQ_PREIMAGE_TAG,
|
|
20
19
|
RFQ_TERMINAL_STATES,
|
|
21
|
-
|
|
20
|
+
SOLO_REFUND_HEADROOM_SECONDS,
|
|
22
21
|
SWAP_LOCKUP_CONTRACT_KIND,
|
|
23
22
|
SWAP_LOCKUP_CONTRACT_LABEL,
|
|
24
23
|
SWAP_LOCKUP_CONTRACT_TYPE,
|
|
25
24
|
SwapRefusal,
|
|
26
|
-
adoptSwapDescriptor,
|
|
27
25
|
arkadeSwapRequest,
|
|
28
26
|
assertFundable,
|
|
29
27
|
assertReceivable,
|
|
30
28
|
awaitOnchainFill,
|
|
31
29
|
buildHtlcClaim,
|
|
32
30
|
buildHtlcRefund,
|
|
33
|
-
buildPreimageMessage,
|
|
34
31
|
claimOnchainFill,
|
|
35
32
|
classifyOnchainHtlc,
|
|
36
33
|
deriveLightningReceive,
|
|
37
34
|
deriveOnchainReceive,
|
|
38
35
|
deriveOnchainSend,
|
|
39
|
-
derivePreimage,
|
|
40
|
-
deriveSwapSecrets,
|
|
41
36
|
extractPreimage,
|
|
42
37
|
httpTransport,
|
|
43
|
-
isDeterministicSigner,
|
|
44
38
|
lightningReceiveRequest,
|
|
45
39
|
lightningSendRequest,
|
|
46
40
|
lightningSendVtxoScript,
|
|
@@ -51,8 +45,6 @@ import {
|
|
|
51
45
|
onchainReceiveRequest,
|
|
52
46
|
onchainSendRequest,
|
|
53
47
|
paymentHashOf,
|
|
54
|
-
preimageForRfqSecrets,
|
|
55
|
-
randomSwapSecrets,
|
|
56
48
|
receiveVtxoScript,
|
|
57
49
|
registerLockupContract,
|
|
58
50
|
relayTransport,
|
|
@@ -61,21 +53,16 @@ import {
|
|
|
61
53
|
requestOnchainReceive,
|
|
62
54
|
requestOnchainSend,
|
|
63
55
|
rfqPair,
|
|
64
|
-
rfqSecretsOfRecord,
|
|
65
|
-
rfqSecretsToRecord,
|
|
66
56
|
sealClaimPacket,
|
|
67
|
-
senderIdentityForRfqSecrets,
|
|
68
|
-
senderIdentityForSwapRecord,
|
|
69
|
-
senderPubkeyForRfqSecrets,
|
|
70
57
|
unilateralClaimDelay,
|
|
71
58
|
unilateralRefundDelay,
|
|
72
59
|
unilateralRefundWithoutReceiverDelay,
|
|
73
60
|
verifyLockupAddress,
|
|
74
61
|
verifyReceiveInvoice
|
|
75
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-Q4FAYBXS.js";
|
|
76
63
|
|
|
77
64
|
// src/offer.ts
|
|
78
|
-
import { hex } from "@scure/base";
|
|
65
|
+
import { hex as hex2 } from "@scure/base";
|
|
79
66
|
import { concatBytes } from "@scure/btc-signer/utils.js";
|
|
80
67
|
import {
|
|
81
68
|
ArkAddress,
|
|
@@ -216,6 +203,9 @@ async function retireSettledOfferContracts(manager, swaps) {
|
|
|
216
203
|
}
|
|
217
204
|
|
|
218
205
|
// src/store.ts
|
|
206
|
+
import { hex } from "@scure/base";
|
|
207
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
208
|
+
import { contractPreimage } from "@arkade-os/sdk";
|
|
219
209
|
var BTC_ASSET_ID = "btc";
|
|
220
210
|
var byNewest = (a, b) => b.createdAt - a.createdAt;
|
|
221
211
|
var getAssetSwapsOrThrow = async (repository) => {
|
|
@@ -268,6 +258,63 @@ var updateAssetSwapBestEffort = async (repository, id, changes) => {
|
|
|
268
258
|
return { swaps, persisted: false };
|
|
269
259
|
}
|
|
270
260
|
};
|
|
261
|
+
var swapSecretsToRecord = (secrets) => ({
|
|
262
|
+
signingDescriptor: secrets.descriptor,
|
|
263
|
+
..."mustPersistPreimage" in secrets && secrets.mustPersistPreimage ? { preimageHex: hex.encode(secrets.preimage) } : {},
|
|
264
|
+
..."preimageSalt" in secrets && secrets.preimageSalt ? { preimageSaltHex: hex.encode(secrets.preimageSalt) } : {}
|
|
265
|
+
});
|
|
266
|
+
var decodeHex32 = (value, field) => {
|
|
267
|
+
const bytes = hex.decode(value);
|
|
268
|
+
if (bytes.length !== 32) {
|
|
269
|
+
throw new Error(`${field} must be 32 bytes, got ${bytes.length}`);
|
|
270
|
+
}
|
|
271
|
+
return bytes;
|
|
272
|
+
};
|
|
273
|
+
var PreimageNotRecoverableError = class extends Error {
|
|
274
|
+
constructor(reason, message, options) {
|
|
275
|
+
super(message, options);
|
|
276
|
+
this.reason = reason;
|
|
277
|
+
}
|
|
278
|
+
reason;
|
|
279
|
+
name = "PreimageNotRecoverableError";
|
|
280
|
+
};
|
|
281
|
+
var preimageForSwapRecord = async (wallet, record) => {
|
|
282
|
+
if (!record.signingDescriptor) {
|
|
283
|
+
throw new PreimageNotRecoverableError(
|
|
284
|
+
"no-secrets",
|
|
285
|
+
"this swap record carries no signing descriptor"
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
let stored;
|
|
289
|
+
let salt;
|
|
290
|
+
try {
|
|
291
|
+
stored = record.preimageHex ? decodeHex32(record.preimageHex, "preimageHex") : void 0;
|
|
292
|
+
salt = record.preimageSaltHex ? decodeHex32(record.preimageSaltHex, "preimageSaltHex") : void 0;
|
|
293
|
+
} catch (cause) {
|
|
294
|
+
throw new PreimageNotRecoverableError(
|
|
295
|
+
"malformed-record",
|
|
296
|
+
`this swap record's secrets projection is unreadable: ${String(cause)}`,
|
|
297
|
+
{ cause }
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
let preimage;
|
|
301
|
+
try {
|
|
302
|
+
preimage = await contractPreimage(wallet, record.signingDescriptor, { stored, salt });
|
|
303
|
+
} catch (cause) {
|
|
304
|
+
throw new PreimageNotRecoverableError(
|
|
305
|
+
"not-derivable",
|
|
306
|
+
`this wallet cannot produce the preimage for ${record.signingDescriptor}`,
|
|
307
|
+
{ cause }
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
if (record.paymentHash && hex.encode(sha256(preimage)) !== record.paymentHash.toLowerCase()) {
|
|
311
|
+
throw new PreimageNotRecoverableError(
|
|
312
|
+
"hash-mismatch",
|
|
313
|
+
"the derived preimage does not match this swap's payment hash: wrong wallet, or a tampered salt"
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
return preimage;
|
|
317
|
+
};
|
|
271
318
|
|
|
272
319
|
// src/offer.ts
|
|
273
320
|
var swapPrograms = {
|
|
@@ -405,14 +452,14 @@ async function registerOfferContract(wallet, arkServerUrl, network, binding, ser
|
|
|
405
452
|
contractManager
|
|
406
453
|
});
|
|
407
454
|
const contract = new arkade.ArkadeContract(client, program, args, keys);
|
|
408
|
-
if (
|
|
455
|
+
if (hex2.encode(contract.pkScript) !== hex2.encode(expectedPkScript)) {
|
|
409
456
|
throw new Error("derived covenant does not match the offer's swapPkScript");
|
|
410
457
|
}
|
|
411
458
|
await contract.register({
|
|
412
459
|
label: OFFER_CONTRACT_LABEL,
|
|
413
460
|
metadata: { genericallySpendable: false, kind: OFFER_CONTRACT_KIND }
|
|
414
461
|
});
|
|
415
|
-
await promoteOfferContract(contractManager,
|
|
462
|
+
await promoteOfferContract(contractManager, hex2.encode(expectedPkScript));
|
|
416
463
|
}
|
|
417
464
|
async function createOffer(wallet, arkServerUrl, params) {
|
|
418
465
|
if (Boolean(params.wantAsset) === Boolean(params.offerAsset)) {
|
|
@@ -423,9 +470,9 @@ async function createOffer(wallet, arkServerUrl, params) {
|
|
|
423
470
|
wallet.getAddress(),
|
|
424
471
|
wallet.identity.xOnlyPublicKey()
|
|
425
472
|
]);
|
|
426
|
-
const serverPubKey =
|
|
473
|
+
const serverPubKey = hex2.decode(toXOnlySignerHex(info.signerPubkey));
|
|
427
474
|
const network = getNetwork(info.network);
|
|
428
|
-
const emuKey =
|
|
475
|
+
const emuKey = hex2.decode(
|
|
429
476
|
toXOnlySignerHex(resolveEmulatorPubkey(network, params.emulatorPubkey))
|
|
430
477
|
);
|
|
431
478
|
const binding = {
|
|
@@ -448,7 +495,7 @@ async function createOffer(wallet, arkServerUrl, params) {
|
|
|
448
495
|
);
|
|
449
496
|
const payload = encodeOffer(offer);
|
|
450
497
|
return {
|
|
451
|
-
offerHex:
|
|
498
|
+
offerHex: hex2.encode(payload),
|
|
452
499
|
extension: { type: OFFER_PACKET_TYPE, payload },
|
|
453
500
|
// VtxoScript.address owns address construction; assembling an ArkAddress
|
|
454
501
|
// from tweakedPublicKey here would silently miss any future step it gains
|
|
@@ -458,7 +505,7 @@ async function createOffer(wallet, arkServerUrl, params) {
|
|
|
458
505
|
}
|
|
459
506
|
async function cancelOffer(wallet, arkServerUrl, offerHex, opts) {
|
|
460
507
|
const { repository, fundingTxid, swapAddress } = opts;
|
|
461
|
-
const offer = decodeOffer(
|
|
508
|
+
const offer = decodeOffer(hex2.decode(offerHex));
|
|
462
509
|
const contractManager = await wallet.getContractManager();
|
|
463
510
|
const client = await arkade.Arkade.connect({
|
|
464
511
|
arkade: new RestArkProvider(arkServerUrl),
|
|
@@ -475,7 +522,7 @@ async function cancelOffer(wallet, arkServerUrl, offerHex, opts) {
|
|
|
475
522
|
const serverKey = swapAddress ? ArkAddress.decode(swapAddress).serverPubKey : client.serverKey;
|
|
476
523
|
const { program, args, keys } = swapProgramBinding(offer, serverKey);
|
|
477
524
|
const rebuilt = new arkade.ArkadeProgramScript(program, args, keys);
|
|
478
|
-
if (
|
|
525
|
+
if (hex2.encode(rebuilt.pkScript) !== hex2.encode(offer.swapPkScript)) {
|
|
479
526
|
throw new Error(
|
|
480
527
|
"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"
|
|
481
528
|
);
|
|
@@ -508,7 +555,7 @@ async function cancelOffer(wallet, arkServerUrl, offerHex, opts) {
|
|
|
508
555
|
spentTxid: txid
|
|
509
556
|
});
|
|
510
557
|
if (persisted) {
|
|
511
|
-
await retireOfferContract(contractManager, swaps,
|
|
558
|
+
await retireOfferContract(contractManager, swaps, hex2.encode(offer.swapPkScript));
|
|
512
559
|
}
|
|
513
560
|
}
|
|
514
561
|
return txid;
|
|
@@ -646,7 +693,7 @@ var validatePlan = (plan, giveBalance, dust) => {
|
|
|
646
693
|
// src/repository.ts
|
|
647
694
|
var marketsCacheKey = (network, registry) => `arkade-intents-markets-${network}-${registry}`;
|
|
648
695
|
var InMemoryAssetSwapRepository = class {
|
|
649
|
-
version =
|
|
696
|
+
version = 2;
|
|
650
697
|
swaps = /* @__PURE__ */ new Map();
|
|
651
698
|
scanned = /* @__PURE__ */ new Set();
|
|
652
699
|
markets = /* @__PURE__ */ new Map();
|
|
@@ -708,7 +755,7 @@ var IndexedDbAssetSwapRepository = class {
|
|
|
708
755
|
this.dbName = dbName;
|
|
709
756
|
}
|
|
710
757
|
dbName;
|
|
711
|
-
version =
|
|
758
|
+
version = 2;
|
|
712
759
|
// the promise, not the resolved database: openDatabase bumps a refcount on
|
|
713
760
|
// every call including cache hits, while dispose closes once, so two
|
|
714
761
|
// concurrent first calls would strand the refcount above zero and leak the
|
|
@@ -779,7 +826,7 @@ var IndexedDbAssetSwapRepository = class {
|
|
|
779
826
|
};
|
|
780
827
|
|
|
781
828
|
// src/restore.ts
|
|
782
|
-
import { base64, hex as
|
|
829
|
+
import { base64, hex as hex3 } from "@scure/base";
|
|
783
830
|
import {
|
|
784
831
|
Extension,
|
|
785
832
|
Transaction,
|
|
@@ -815,7 +862,7 @@ function classifySpend(offer, serverPubkey, spendTx, deposit) {
|
|
|
815
862
|
let leaves;
|
|
816
863
|
try {
|
|
817
864
|
const script = offerVtxoScript(offer, serverPubkey);
|
|
818
|
-
if (
|
|
865
|
+
if (hex3.encode(script.pkScript) !== hex3.encode(offer.swapPkScript)) return "indeterminate";
|
|
819
866
|
leaves = {
|
|
820
867
|
cancel: script.functionByName("cancel")?.leafScript,
|
|
821
868
|
fulfill: script.functionByName("fulfill")?.leafScript
|
|
@@ -826,11 +873,11 @@ function classifySpend(offer, serverPubkey, spendTx, deposit) {
|
|
|
826
873
|
for (let i = 0; i < spendTx.inputsLength; i++) {
|
|
827
874
|
const input = spendTx.getInput(i);
|
|
828
875
|
if (!input.txid || input.index !== deposit.vout) continue;
|
|
829
|
-
if (
|
|
876
|
+
if (hex3.encode(input.txid) !== deposit.txid) continue;
|
|
830
877
|
for (const leaf of input.tapLeafScript ?? []) {
|
|
831
|
-
const spent =
|
|
832
|
-
if (leaves.cancel && spent ===
|
|
833
|
-
if (leaves.fulfill && spent ===
|
|
878
|
+
const spent = hex3.encode(scriptFromTapLeafScript(leaf));
|
|
879
|
+
if (leaves.cancel && spent === hex3.encode(leaves.cancel)) return "cancelled";
|
|
880
|
+
if (leaves.fulfill && spent === hex3.encode(leaves.fulfill)) return "fulfilled";
|
|
834
881
|
}
|
|
835
882
|
}
|
|
836
883
|
return "indeterminate";
|
|
@@ -865,13 +912,13 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
|
|
|
865
912
|
found.push({
|
|
866
913
|
fundingTx,
|
|
867
914
|
offer: decodeOffer(payload),
|
|
868
|
-
offerHex:
|
|
915
|
+
offerHex: hex3.encode(payload)
|
|
869
916
|
});
|
|
870
917
|
} catch {
|
|
871
918
|
}
|
|
872
919
|
}
|
|
873
920
|
if (found.length === 0) return { restored: [], scannedTxids: fetchedTxids };
|
|
874
|
-
const scripts = [...new Set(found.map((f) =>
|
|
921
|
+
const scripts = [...new Set(found.map((f) => hex3.encode(f.offer.swapPkScript)))];
|
|
875
922
|
const { vtxos } = await indexer.getVtxos({ scripts });
|
|
876
923
|
const vtxoByScriptAndTxid = new Map(vtxos.map((v) => [`${v.script}:${v.txid}`, v]));
|
|
877
924
|
const txByAnyId = /* @__PURE__ */ new Map();
|
|
@@ -883,7 +930,7 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
|
|
|
883
930
|
const spendTxids = /* @__PURE__ */ new Set();
|
|
884
931
|
for (const { fundingTx, offer } of found) {
|
|
885
932
|
const vtxo = vtxoByScriptAndTxid.get(
|
|
886
|
-
`${
|
|
933
|
+
`${hex3.encode(offer.swapPkScript)}:${fundingTx.redeemTxid}`
|
|
887
934
|
);
|
|
888
935
|
if (vtxo?.virtualStatus.state !== "spent") continue;
|
|
889
936
|
for (const txid of spendTxidsOf(vtxo)) spendTxids.add(txid);
|
|
@@ -892,7 +939,7 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
|
|
|
892
939
|
const restored = [];
|
|
893
940
|
const unresolved = /* @__PURE__ */ new Set();
|
|
894
941
|
for (const { fundingTx, offer, offerHex } of found) {
|
|
895
|
-
const swapPkScript =
|
|
942
|
+
const swapPkScript = hex3.encode(offer.swapPkScript);
|
|
896
943
|
const vtxo = vtxoByScriptAndTxid.get(`${swapPkScript}:${fundingTx.redeemTxid}`);
|
|
897
944
|
if (!vtxo) {
|
|
898
945
|
unresolved.add(fundingTx.redeemTxid);
|
|
@@ -949,7 +996,7 @@ async function restoreAssetSwaps(indexer, txs, existingIds, opts) {
|
|
|
949
996
|
}
|
|
950
997
|
|
|
951
998
|
// src/watch.ts
|
|
952
|
-
import { base64 as base642, hex as
|
|
999
|
+
import { base64 as base642, hex as hex4 } from "@scure/base";
|
|
953
1000
|
import {
|
|
954
1001
|
ArkAddress as ArkAddress2,
|
|
955
1002
|
RestIndexerProvider as RestIndexerProvider3,
|
|
@@ -988,7 +1035,7 @@ async function watchOfferSwaps({
|
|
|
988
1035
|
if (candidates.length === 0) return "indeterminate";
|
|
989
1036
|
const { txs } = await indexer.getVirtualTxs(candidates);
|
|
990
1037
|
return classifyDepositSpend(
|
|
991
|
-
decodeOffer(
|
|
1038
|
+
decodeOffer(hex4.decode(swap.offerHex)),
|
|
992
1039
|
serverPubkey,
|
|
993
1040
|
txs.map((psbt) => Transaction2.fromPSBT(base642.decode(psbt))),
|
|
994
1041
|
{ txid: vtxo.txid, vout: vtxo.vout }
|
|
@@ -1032,9 +1079,9 @@ async function watchOfferSwaps({
|
|
|
1032
1079
|
}
|
|
1033
1080
|
|
|
1034
1081
|
// src/claim.ts
|
|
1035
|
-
import { hex as
|
|
1082
|
+
import { hex as hex6 } from "@scure/base";
|
|
1036
1083
|
import { ripemd160 } from "@noble/hashes/legacy.js";
|
|
1037
|
-
import { sha256 as
|
|
1084
|
+
import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
|
|
1038
1085
|
import {
|
|
1039
1086
|
CSVMultisigTapscript as CSVMultisigTapscript2,
|
|
1040
1087
|
claimWithPreimageIdentity,
|
|
@@ -1042,8 +1089,8 @@ import {
|
|
|
1042
1089
|
} from "@arkade-os/sdk";
|
|
1043
1090
|
|
|
1044
1091
|
// src/refund.ts
|
|
1045
|
-
import { base64 as base643, hex as
|
|
1046
|
-
import { sha256 } from "@noble/hashes/sha2.js";
|
|
1092
|
+
import { base64 as base643, hex as hex5 } from "@scure/base";
|
|
1093
|
+
import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
|
|
1047
1094
|
import {
|
|
1048
1095
|
CSVMultisigTapscript,
|
|
1049
1096
|
ConditionWitness,
|
|
@@ -1099,7 +1146,7 @@ var LockupNeedsRecoveryError = class extends Error {
|
|
|
1099
1146
|
}
|
|
1100
1147
|
};
|
|
1101
1148
|
async function findLockupVtxos(indexer, swapPkScript) {
|
|
1102
|
-
const scripts = [
|
|
1149
|
+
const scripts = [hex5.encode(swapPkScript)];
|
|
1103
1150
|
const [spendable, recoverable] = await Promise.all([
|
|
1104
1151
|
indexer.getVtxos({ scripts, spendableOnly: true }),
|
|
1105
1152
|
indexer.getVtxos({ scripts, recoverableOnly: true })
|
|
@@ -1124,13 +1171,13 @@ async function findLockupVtxos(indexer, swapPkScript) {
|
|
|
1124
1171
|
}
|
|
1125
1172
|
return out;
|
|
1126
1173
|
}
|
|
1127
|
-
var hashesTo = (candidate, paymentHash) =>
|
|
1174
|
+
var hashesTo = (candidate, paymentHash) => hex5.encode(sha2562(candidate)) === paymentHash;
|
|
1128
1175
|
var candidateWitnessItems = (tx, inputIndex) => [
|
|
1129
1176
|
...getArkPsbtFields(tx, inputIndex, ConditionWitness).flat(),
|
|
1130
1177
|
...tx.getInput(inputIndex).finalScriptWitness ?? []
|
|
1131
1178
|
];
|
|
1132
1179
|
async function readLockupFate(indexer, input) {
|
|
1133
|
-
const { vtxos } = await indexer.getVtxos({ scripts: [
|
|
1180
|
+
const { vtxos } = await indexer.getVtxos({ scripts: [hex5.encode(input.swapPkScript)] });
|
|
1134
1181
|
const all = vtxos ?? [];
|
|
1135
1182
|
if (all.length === 0) return { fate: "unknown" };
|
|
1136
1183
|
const spentBy = /* @__PURE__ */ new Set();
|
|
@@ -1153,7 +1200,7 @@ async function readLockupFate(indexer, input) {
|
|
|
1153
1200
|
for (let i = 0; i < tx.inputsLength; i++) {
|
|
1154
1201
|
const spent = tx.getInput(i);
|
|
1155
1202
|
if (!spent.txid) continue;
|
|
1156
|
-
const txid =
|
|
1203
|
+
const txid = hex5.encode(spent.txid);
|
|
1157
1204
|
if (!all.some((vtxo) => vtxo.txid === txid && vtxo.vout === spent.index)) continue;
|
|
1158
1205
|
for (const candidate of candidateWitnessItems(tx, i)) {
|
|
1159
1206
|
if (hashesTo(candidate, input.paymentHash)) {
|
|
@@ -1182,7 +1229,7 @@ async function pushRefundWithoutReceiver(ark, input) {
|
|
|
1182
1229
|
const info = await ark.getInfo();
|
|
1183
1230
|
let serverUnrollScript;
|
|
1184
1231
|
try {
|
|
1185
|
-
serverUnrollScript = CSVMultisigTapscript.decode(
|
|
1232
|
+
serverUnrollScript = CSVMultisigTapscript.decode(hex5.decode(info.checkpointTapscript));
|
|
1186
1233
|
} catch {
|
|
1187
1234
|
throw new Error("invalid checkpointTapscript from the Arkade server");
|
|
1188
1235
|
}
|
|
@@ -1293,13 +1340,13 @@ async function pushClaim(ark, input) {
|
|
|
1293
1340
|
}
|
|
1294
1341
|
}
|
|
1295
1342
|
const committed = input.script.options.preimageHash;
|
|
1296
|
-
if (
|
|
1343
|
+
if (hex6.encode(ripemd160(sha2563(input.preimage))) !== hex6.encode(committed)) {
|
|
1297
1344
|
throw new Error("preimage does not match the covenant's payment hash");
|
|
1298
1345
|
}
|
|
1299
1346
|
const info = await ark.getInfo();
|
|
1300
1347
|
let serverUnrollScript;
|
|
1301
1348
|
try {
|
|
1302
|
-
serverUnrollScript = CSVMultisigTapscript2.decode(
|
|
1349
|
+
serverUnrollScript = CSVMultisigTapscript2.decode(hex6.decode(info.checkpointTapscript));
|
|
1303
1350
|
} catch {
|
|
1304
1351
|
throw new Error("invalid checkpointTapscript from the Arkade server");
|
|
1305
1352
|
}
|
|
@@ -1352,8 +1399,50 @@ async function claimReceiveLockup(indexer, ark, input) {
|
|
|
1352
1399
|
});
|
|
1353
1400
|
}
|
|
1354
1401
|
|
|
1402
|
+
// src/refundBlocked.ts
|
|
1403
|
+
import {
|
|
1404
|
+
ForeignDescriptorError,
|
|
1405
|
+
WalletCannotSignError,
|
|
1406
|
+
contractSigner
|
|
1407
|
+
} from "@arkade-os/sdk";
|
|
1408
|
+
var RefundNotLocallyPossibleError = class extends Error {
|
|
1409
|
+
constructor(reason, message, options) {
|
|
1410
|
+
super(message, options);
|
|
1411
|
+
this.reason = reason;
|
|
1412
|
+
}
|
|
1413
|
+
reason;
|
|
1414
|
+
name = "RefundNotLocallyPossibleError";
|
|
1415
|
+
};
|
|
1416
|
+
async function senderIdentityForSwapRecord(wallet, record) {
|
|
1417
|
+
if (!record.signingDescriptor) {
|
|
1418
|
+
throw new RefundNotLocallyPossibleError(
|
|
1419
|
+
"no-secrets",
|
|
1420
|
+
"this swap record carries no signing descriptor"
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
try {
|
|
1424
|
+
return await contractSigner(wallet, record.signingDescriptor);
|
|
1425
|
+
} catch (cause) {
|
|
1426
|
+
if (cause instanceof WalletCannotSignError) {
|
|
1427
|
+
throw new RefundNotLocallyPossibleError(
|
|
1428
|
+
"unsignable-wallet",
|
|
1429
|
+
`this wallet holds ${record.signingDescriptor} but cannot sign with it; attach its signer`,
|
|
1430
|
+
{ cause }
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
if (cause instanceof ForeignDescriptorError) {
|
|
1434
|
+
throw new RefundNotLocallyPossibleError(
|
|
1435
|
+
"foreign-descriptor",
|
|
1436
|
+
`this wallet cannot derive ${record.signingDescriptor}; the swap was created on another wallet`,
|
|
1437
|
+
{ cause }
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
throw cause;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1355
1444
|
// src/swapManager.ts
|
|
1356
|
-
import { hex as
|
|
1445
|
+
import { hex as hex7 } from "@scure/base";
|
|
1357
1446
|
var RFQ_SWAP_TERMINAL_STATES = ["settled", "refunded", "failed"];
|
|
1358
1447
|
var isRfqSwapTerminal = (state) => RFQ_SWAP_TERMINAL_STATES.includes(state);
|
|
1359
1448
|
function nextOnchainAction(input) {
|
|
@@ -1621,7 +1710,7 @@ var RfqSwapManager = class {
|
|
|
1621
1710
|
// ── internals ────────────────────────────────────────────────────────────
|
|
1622
1711
|
track(swap) {
|
|
1623
1712
|
this.monitored.set(swap.rfqId, swap);
|
|
1624
|
-
this.byLockupScript.set(
|
|
1713
|
+
this.byLockupScript.set(hex7.encode(swap.lockupPkScript), swap);
|
|
1625
1714
|
}
|
|
1626
1715
|
/** Drops the swap from BOTH indexes. The event index is the one that stops
|
|
1627
1716
|
* a late event finding a swap that is gone; `pollSwap`'s own
|
|
@@ -1630,7 +1719,7 @@ var RfqSwapManager = class {
|
|
|
1630
1719
|
* them from silently re-driving a cancelled swap. */
|
|
1631
1720
|
untrack(rfqId) {
|
|
1632
1721
|
const swap = this.monitored.get(rfqId);
|
|
1633
|
-
if (swap) this.byLockupScript.delete(
|
|
1722
|
+
if (swap) this.byLockupScript.delete(hex7.encode(swap.lockupPkScript));
|
|
1634
1723
|
this.monitored.delete(rfqId);
|
|
1635
1724
|
this.refundRefused.delete(rfqId);
|
|
1636
1725
|
this.lastClaimError.delete(rfqId);
|
|
@@ -1692,7 +1781,7 @@ var RfqSwapManager = class {
|
|
|
1692
1781
|
if (!lockup) {
|
|
1693
1782
|
try {
|
|
1694
1783
|
const [existing] = await contracts.getContracts({
|
|
1695
|
-
script:
|
|
1784
|
+
script: hex7.encode(swap.lockupPkScript)
|
|
1696
1785
|
});
|
|
1697
1786
|
if (existing) {
|
|
1698
1787
|
this.registered.set(swap.rfqId, true);
|
|
@@ -1711,13 +1800,13 @@ var RfqSwapManager = class {
|
|
|
1711
1800
|
);
|
|
1712
1801
|
return;
|
|
1713
1802
|
}
|
|
1714
|
-
const script =
|
|
1715
|
-
if (script !==
|
|
1803
|
+
const script = hex7.encode(lockup.script.pkScript);
|
|
1804
|
+
if (script !== hex7.encode(swap.lockupPkScript)) {
|
|
1716
1805
|
this.registered.set(swap.rfqId, false);
|
|
1717
1806
|
this.emitFailed(
|
|
1718
1807
|
swap,
|
|
1719
1808
|
new Error(
|
|
1720
|
-
`swap ${swap.rfqId} lockup script ${script} does not match its lockupPkScript ${
|
|
1809
|
+
`swap ${swap.rfqId} lockup script ${script} does not match its lockupPkScript ${hex7.encode(swap.lockupPkScript)}`
|
|
1721
1810
|
)
|
|
1722
1811
|
);
|
|
1723
1812
|
return;
|
|
@@ -1736,7 +1825,7 @@ var RfqSwapManager = class {
|
|
|
1736
1825
|
* script for its whole life. Best-effort — the swap is over either way. */
|
|
1737
1826
|
retireContract(swap) {
|
|
1738
1827
|
if (!this.deps.contracts || !this.registered.get(swap.rfqId)) return;
|
|
1739
|
-
void this.deps.contracts.setContractWatchState(
|
|
1828
|
+
void this.deps.contracts.setContractWatchState(hex7.encode(swap.lockupPkScript), "retained").catch((error) => this.emitFailed(swap, error));
|
|
1740
1829
|
}
|
|
1741
1830
|
arm() {
|
|
1742
1831
|
if (!this.running) return;
|
|
@@ -2142,20 +2231,20 @@ export {
|
|
|
2142
2231
|
ONCHAIN_RECEIVE_PAIR,
|
|
2143
2232
|
ONCHAIN_SECONDS_PER_BLOCK,
|
|
2144
2233
|
ONCHAIN_SEND_PAIR,
|
|
2234
|
+
PreimageNotRecoverableError,
|
|
2145
2235
|
QUOTE_OPTIONS,
|
|
2146
2236
|
REFUND_MTP_LAG_SECONDS,
|
|
2147
|
-
RFQ_PREIMAGE_TAG,
|
|
2148
2237
|
RFQ_RESOLVED_STATES,
|
|
2149
2238
|
RFQ_SWAP_TERMINAL_STATES,
|
|
2150
2239
|
RFQ_TERMINAL_STATES,
|
|
2151
2240
|
RefundNotLocallyPossibleError,
|
|
2152
2241
|
RfqSwapManager,
|
|
2242
|
+
SOLO_REFUND_HEADROOM_SECONDS,
|
|
2153
2243
|
SWAP_LOCKUP_CONTRACT_KIND,
|
|
2154
2244
|
SWAP_LOCKUP_CONTRACT_LABEL,
|
|
2155
2245
|
SWAP_LOCKUP_CONTRACT_TYPE,
|
|
2156
2246
|
SwapRefusal,
|
|
2157
2247
|
addAssetSwap,
|
|
2158
|
-
adoptSwapDescriptor,
|
|
2159
2248
|
arkadeSwapRequest,
|
|
2160
2249
|
assertFundable,
|
|
2161
2250
|
assertReceivable,
|
|
@@ -2164,7 +2253,6 @@ export {
|
|
|
2164
2253
|
awaitRfqResolution,
|
|
2165
2254
|
buildHtlcClaim,
|
|
2166
2255
|
buildHtlcRefund,
|
|
2167
|
-
buildPreimageMessage,
|
|
2168
2256
|
cancelOffer,
|
|
2169
2257
|
claimOnchainFill,
|
|
2170
2258
|
claimReceiveLockup,
|
|
@@ -2176,8 +2264,6 @@ export {
|
|
|
2176
2264
|
deriveLightningReceive,
|
|
2177
2265
|
deriveOnchainReceive,
|
|
2178
2266
|
deriveOnchainSend,
|
|
2179
|
-
derivePreimage,
|
|
2180
|
-
deriveSwapSecrets,
|
|
2181
2267
|
discoverMarkets,
|
|
2182
2268
|
encodeOffer,
|
|
2183
2269
|
extractPreimage,
|
|
@@ -2186,7 +2272,6 @@ export {
|
|
|
2186
2272
|
getAssetSwaps,
|
|
2187
2273
|
getAssetSwapsOrThrow,
|
|
2188
2274
|
httpTransport,
|
|
2189
|
-
isDeterministicSigner,
|
|
2190
2275
|
isRfqSwapTerminal,
|
|
2191
2276
|
isRfqTerminal,
|
|
2192
2277
|
lightningReceiveRequest,
|
|
@@ -2202,10 +2287,9 @@ export {
|
|
|
2202
2287
|
onchainReceiveRequest,
|
|
2203
2288
|
onchainSendRequest,
|
|
2204
2289
|
paymentHashOf,
|
|
2205
|
-
|
|
2290
|
+
preimageForSwapRecord,
|
|
2206
2291
|
pushClaim,
|
|
2207
2292
|
pushRefundWithoutReceiver,
|
|
2208
|
-
randomSwapSecrets,
|
|
2209
2293
|
readLockupFate,
|
|
2210
2294
|
receiveVtxoScript,
|
|
2211
2295
|
refundIfUnresolved,
|
|
@@ -2218,15 +2302,12 @@ export {
|
|
|
2218
2302
|
restoreAssetSwaps,
|
|
2219
2303
|
retireSettledOfferContracts,
|
|
2220
2304
|
rfqPair,
|
|
2221
|
-
rfqSecretsOfRecord,
|
|
2222
|
-
rfqSecretsToRecord,
|
|
2223
2305
|
sealClaimPacket,
|
|
2224
|
-
senderIdentityForRfqSecrets,
|
|
2225
2306
|
senderIdentityForSwapRecord,
|
|
2226
|
-
senderPubkeyForRfqSecrets,
|
|
2227
2307
|
spendTxidsOf,
|
|
2228
2308
|
spendUpdate,
|
|
2229
2309
|
swapPrograms,
|
|
2310
|
+
swapSecretsToRecord,
|
|
2230
2311
|
unilateralClaimDelay,
|
|
2231
2312
|
unilateralRefundDelay,
|
|
2232
2313
|
unilateralRefundWithoutReceiverDelay,
|
package/dist/nostr.cjs
CHANGED
|
@@ -33,14 +33,15 @@ __export(nostr_exports, {
|
|
|
33
33
|
RFQ_AD_KIND: () => RFQ_AD_KIND,
|
|
34
34
|
RFQ_DIRECTED_KIND: () => RFQ_DIRECTED_KIND,
|
|
35
35
|
RelayUnavailable: () => RelayUnavailable,
|
|
36
|
+
TransportClosed: () => TransportClosed,
|
|
36
37
|
nostrRfqTransport: () => nostrRfqTransport
|
|
37
38
|
});
|
|
38
39
|
module.exports = __toCommonJS(nostr_exports);
|
|
39
40
|
|
|
40
41
|
// src/rfq.ts
|
|
41
|
-
var
|
|
42
|
+
var import_base4 = require("@scure/base");
|
|
42
43
|
var import_legacy2 = require("@noble/hashes/legacy.js");
|
|
43
|
-
var
|
|
44
|
+
var import_sdk2 = require("@arkade-os/sdk");
|
|
44
45
|
|
|
45
46
|
// src/onchainHtlc.ts
|
|
46
47
|
var import_base = require("@scure/base");
|
|
@@ -56,23 +57,19 @@ var L1_NETWORKS = {
|
|
|
56
57
|
regtest: { ...btc.TEST_NETWORK, bech32: "bcrt" }
|
|
57
58
|
};
|
|
58
59
|
|
|
59
|
-
// src/
|
|
60
|
-
var
|
|
61
|
-
var import_sha22 = require("@noble/hashes/sha2.js");
|
|
62
|
-
var import_utils = require("@noble/hashes/utils.js");
|
|
63
|
-
var import_secp256k1 = require("@noble/curves/secp256k1.js");
|
|
64
|
-
var import_sdk = require("@arkade-os/sdk");
|
|
60
|
+
// src/rfq.ts
|
|
61
|
+
var import_sdk3 = require("@arkade-os/sdk");
|
|
65
62
|
|
|
66
63
|
// src/claimPacket.ts
|
|
67
|
-
var
|
|
68
|
-
var
|
|
64
|
+
var import_base2 = require("@scure/base");
|
|
65
|
+
var import_secp256k1 = require("@noble/curves/secp256k1.js");
|
|
69
66
|
var import_hkdf = require("@noble/hashes/hkdf.js");
|
|
70
|
-
var
|
|
67
|
+
var import_sha22 = require("@noble/hashes/sha2.js");
|
|
71
68
|
var HKDF_INFO = new TextEncoder().encode("covclaimd/preimage/v1");
|
|
72
69
|
|
|
73
70
|
// src/lockupContract.ts
|
|
74
|
-
var
|
|
75
|
-
var
|
|
71
|
+
var import_base3 = require("@scure/base");
|
|
72
|
+
var import_sdk = require("@arkade-os/sdk");
|
|
76
73
|
|
|
77
74
|
// src/rfq.ts
|
|
78
75
|
var ARKADE_BTC = "arkade:BTC";
|
|
@@ -94,6 +91,8 @@ var SwapRefusal = class extends Error {
|
|
|
94
91
|
}
|
|
95
92
|
};
|
|
96
93
|
var MIN_HEADROOM_SECONDS = 90 * 60;
|
|
94
|
+
var SEQUENCE_GRANULARITY_SECONDS = 512;
|
|
95
|
+
var SOLO_REFUND_HEADROOM_SECONDS = 8 * SEQUENCE_GRANULARITY_SECONDS;
|
|
97
96
|
var MIN_CLAIM_WINDOW_SECONDS = 30 * 60;
|
|
98
97
|
|
|
99
98
|
// src/nostr.ts
|
|
@@ -109,6 +108,12 @@ var RelayUnavailable = class extends Error {
|
|
|
109
108
|
this.reasons = reasons;
|
|
110
109
|
}
|
|
111
110
|
};
|
|
111
|
+
var TransportClosed = class extends Error {
|
|
112
|
+
constructor() {
|
|
113
|
+
super("transport closed before the solver replied");
|
|
114
|
+
this.name = "TransportClosed";
|
|
115
|
+
}
|
|
116
|
+
};
|
|
112
117
|
var closeReasons = (raw) => raw.map((entry) => {
|
|
113
118
|
if (typeof entry === "string") return entry;
|
|
114
119
|
const { url, reason } = entry ?? {};
|
|
@@ -208,9 +213,10 @@ var nostrRfqTransport = (options) => {
|
|
|
208
213
|
},
|
|
209
214
|
async close() {
|
|
210
215
|
closed = true;
|
|
211
|
-
|
|
216
|
+
for (const waiter of waiters.values()) waiter.reject(new TransportClosed());
|
|
212
217
|
waiters.clear();
|
|
213
218
|
if (ownsPool) pool.close(relays);
|
|
219
|
+
else subscription.close();
|
|
214
220
|
}
|
|
215
221
|
};
|
|
216
222
|
};
|
|
@@ -219,5 +225,6 @@ var nostrRfqTransport = (options) => {
|
|
|
219
225
|
RFQ_AD_KIND,
|
|
220
226
|
RFQ_DIRECTED_KIND,
|
|
221
227
|
RelayUnavailable,
|
|
228
|
+
TransportClosed,
|
|
222
229
|
nostrRfqTransport
|
|
223
230
|
});
|
package/dist/nostr.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as RfqTransport } from './rfq-BH2yvo3O.cjs';
|
|
2
2
|
import { SimplePool } from 'nostr-tools';
|
|
3
3
|
import '@arkade-os/sdk';
|
|
4
|
-
import '@arkade-os/solver-discovery';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* The Nostr RFQ transport — the PRODUCTION one (docs/rfq-protocol.md § 3.1 in
|
|
@@ -71,6 +70,18 @@ declare class RelayUnavailable extends Error {
|
|
|
71
70
|
readonly reasons: string[];
|
|
72
71
|
constructor(reasons: string[]);
|
|
73
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* `close()` was called while a negotiation was still waiting on a reply.
|
|
75
|
+
*
|
|
76
|
+
* Distinct from both a timeout and {@link RelayUnavailable}, which describe the
|
|
77
|
+
* wire; this describes a decision on our own side of it. A caller that closed
|
|
78
|
+
* deliberately — a user leaving the screen, a flow abandoning its request — can
|
|
79
|
+
* match on this and stay quiet, rather than reporting a solver failure that
|
|
80
|
+
* never happened.
|
|
81
|
+
*/
|
|
82
|
+
declare class TransportClosed extends Error {
|
|
83
|
+
constructor();
|
|
84
|
+
}
|
|
74
85
|
interface NostrRfqOptions {
|
|
75
86
|
/** Relay URLs from the solver's card. The rendezvous, not solver endpoints. */
|
|
76
87
|
relays: string[];
|
|
@@ -96,4 +107,4 @@ interface NostrRfqOptions {
|
|
|
96
107
|
*/
|
|
97
108
|
declare const nostrRfqTransport: (options: NostrRfqOptions) => RfqTransport;
|
|
98
109
|
|
|
99
|
-
export { type NostrRfqOptions, RFQ_AD_KIND, RFQ_DIRECTED_KIND, RelayUnavailable, nostrRfqTransport };
|
|
110
|
+
export { type NostrRfqOptions, RFQ_AD_KIND, RFQ_DIRECTED_KIND, RelayUnavailable, TransportClosed, nostrRfqTransport };
|