@arkade-os/swap 0.0.6 → 0.0.8
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 +274 -13
- package/dist/{chunk-WGRU2DBF.js → chunk-6ZUS47GA.js} +15 -1
- package/dist/{chunk-Q4FAYBXS.js → chunk-TU4NGZDP.js} +108 -37
- package/dist/index.cjs +1205 -449
- package/dist/index.d.cts +292 -1139
- package/dist/index.d.ts +292 -1139
- package/dist/index.js +761 -87
- package/dist/nostr.cjs +15 -9
- package/dist/nostr.d.cts +1 -1
- package/dist/nostr.d.ts +1 -1
- package/dist/nostr.js +4 -11
- package/dist/repositories/realm/index.cjs +49 -5
- package/dist/repositories/realm/index.d.cts +28 -6
- package/dist/repositories/realm/index.d.ts +28 -6
- package/dist/repositories/realm/index.js +50 -6
- package/dist/repositories/sqlite/index.cjs +45 -4
- package/dist/repositories/sqlite/index.d.cts +20 -7
- package/dist/repositories/sqlite/index.d.ts +20 -7
- package/dist/repositories/sqlite/index.js +46 -5
- package/dist/repository-BcZ9LXRP.d.ts +1862 -0
- package/dist/repository-Dso34L4D.d.cts +1862 -0
- package/dist/{rfq-3jWha5xA.d.cts → rfq-C-aq5LDJ.d.cts} +82 -9
- package/dist/{rfq-3jWha5xA.d.ts → rfq-C-aq5LDJ.d.ts} +82 -9
- package/package.json +2 -2
- package/dist/repository-BwnZ8N62.d.cts +0 -236
- package/dist/repository-BwnZ8N62.d.ts +0 -236
|
@@ -25,6 +25,11 @@ function onchainHtlcScript(params, network) {
|
|
|
25
25
|
`refundLocktime must be a positive unix timestamp, got ${params.refundLocktime}`
|
|
26
26
|
);
|
|
27
27
|
}
|
|
28
|
+
if (!Object.hasOwn(L1_NETWORKS, network)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`unknown L1 network '${String(network)}' \u2014 expected one of ${Object.keys(L1_NETWORKS).join(", ")}`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
28
33
|
const h160 = h160FromPaymentHash(params.paymentHash);
|
|
29
34
|
const claim = btc.Script.encode([
|
|
30
35
|
"SIZE",
|
|
@@ -249,10 +254,27 @@ async function sealWithEntropy(input, ephemeralKey, nonce) {
|
|
|
249
254
|
|
|
250
255
|
// src/lockupContract.ts
|
|
251
256
|
import { hex as hex2 } from "@scure/base";
|
|
252
|
-
import {
|
|
257
|
+
import {
|
|
258
|
+
ArkAddress,
|
|
259
|
+
VHTLCV2ContractHandler
|
|
260
|
+
} from "@arkade-os/sdk";
|
|
253
261
|
var SWAP_LOCKUP_CONTRACT_TYPE = "vhtlc-v2";
|
|
254
262
|
var SWAP_LOCKUP_CONTRACT_LABEL = "Arkade RFQ swap lockup";
|
|
255
263
|
var SWAP_LOCKUP_CONTRACT_KIND = "rfq-swap-lockup";
|
|
264
|
+
var LockupContractMissing = class extends Error {
|
|
265
|
+
/** The lockup whose row is absent. */
|
|
266
|
+
address;
|
|
267
|
+
/** Its pkScript hex — the key the row would have been under. */
|
|
268
|
+
script;
|
|
269
|
+
constructor(address, script) {
|
|
270
|
+
super(
|
|
271
|
+
`no contract row for lockup ${address} (script ${script}); its covenant cannot be rebuilt from this wallet's contract store`
|
|
272
|
+
);
|
|
273
|
+
this.name = "LockupContractMissing";
|
|
274
|
+
this.address = address;
|
|
275
|
+
this.script = script;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
256
278
|
var LockupRegistrationFailed = class extends Error {
|
|
257
279
|
/** The lockup address that was never registered — never fund it: nothing
|
|
258
280
|
* is watching it. */
|
|
@@ -281,12 +303,18 @@ async function registerLockupContract(contracts, script, address) {
|
|
|
281
303
|
throw new LockupRegistrationFailed(script, address, error);
|
|
282
304
|
}
|
|
283
305
|
}
|
|
306
|
+
async function lockupContractParams(contracts, lockupAddress) {
|
|
307
|
+
const script = hex2.encode(ArkAddress.decode(lockupAddress).pkScript);
|
|
308
|
+
const [row] = await contracts.getContracts({ script });
|
|
309
|
+
if (!row) throw new LockupContractMissing(lockupAddress, script);
|
|
310
|
+
return row.params;
|
|
311
|
+
}
|
|
284
312
|
|
|
285
313
|
// src/rfq.ts
|
|
286
314
|
import { hex as hex3 } from "@scure/base";
|
|
287
315
|
import { ripemd160 as ripemd1602 } from "@noble/hashes/legacy.js";
|
|
288
316
|
import {
|
|
289
|
-
ArkAddress,
|
|
317
|
+
ArkAddress as ArkAddress2,
|
|
290
318
|
RestArkProvider,
|
|
291
319
|
VHTLC,
|
|
292
320
|
getNetwork,
|
|
@@ -311,9 +339,10 @@ var solverHex = (value, field) => {
|
|
|
311
339
|
}
|
|
312
340
|
};
|
|
313
341
|
var ARKADE_BTC = "arkade:BTC";
|
|
314
|
-
var ARKADE_ASSET = "arkade:ASSET";
|
|
315
342
|
var LIGHTNING_BTC = "lightning:BTC";
|
|
316
343
|
var ONCHAIN_BTC = "onchain:BTC";
|
|
344
|
+
var arkadeAssetLeg = (id) => `arkade:${id.toString()}`;
|
|
345
|
+
var ARKADE_ASSET = "arkade:ASSET";
|
|
317
346
|
var rfqPair = (from, to) => `${from}->${to}`;
|
|
318
347
|
var LIGHTNING_SEND_PAIR = rfqPair(ARKADE_BTC, LIGHTNING_BTC);
|
|
319
348
|
var LIGHTNING_RECEIVE_PAIR = rfqPair(LIGHTNING_BTC, ARKADE_BTC);
|
|
@@ -354,23 +383,34 @@ var lightningSendRequest = (input) => ({
|
|
|
354
383
|
}
|
|
355
384
|
});
|
|
356
385
|
var arkadeSwapRequest = (input) => {
|
|
357
|
-
if (
|
|
358
|
-
throw new Error(
|
|
386
|
+
if (!input.wantAsset && !input.offerAsset) {
|
|
387
|
+
throw new Error(
|
|
388
|
+
"set exactly one of wantAsset (BTC->asset) or offerAsset (asset->BTC) \u2014 with neither set both legs are BTC, which is not a swap"
|
|
389
|
+
);
|
|
359
390
|
}
|
|
391
|
+
if (input.wantAsset && input.offerAsset) {
|
|
392
|
+
throw new Error(
|
|
393
|
+
"set exactly one of wantAsset (BTC->asset) or offerAsset (asset->BTC) \u2014 asset->asset is nameable on the wire but no solver quotes it yet"
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
const pair = rfqPair(
|
|
397
|
+
input.offerAsset ? arkadeAssetLeg(input.offerAsset) : ARKADE_BTC,
|
|
398
|
+
input.wantAsset ? arkadeAssetLeg(input.wantAsset) : ARKADE_BTC
|
|
399
|
+
);
|
|
400
|
+
assertPairLength(pair);
|
|
360
401
|
return {
|
|
361
402
|
v: 1,
|
|
362
403
|
type: "rfq_request",
|
|
363
404
|
rfq_id: input.rfqId,
|
|
364
|
-
pair
|
|
365
|
-
input.offerAsset ? ARKADE_ASSET : ARKADE_BTC,
|
|
366
|
-
input.wantAsset ? ARKADE_ASSET : ARKADE_BTC
|
|
367
|
-
),
|
|
405
|
+
pair,
|
|
368
406
|
amount_side: input.amountSide,
|
|
369
407
|
amount: input.amount,
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
408
|
+
// The pair is the only place the asset ids appear. Repeating them here
|
|
409
|
+
// would be a key the solver's `.strict()` profile schema does not
|
|
410
|
+
// declare, and an undeclared key is `unsupported_payload` — a refusal,
|
|
411
|
+
// not an ignored extra. Empty, not absent: `profile` is required on
|
|
412
|
+
// every other request shape this wire carries.
|
|
413
|
+
profile: {}
|
|
374
414
|
};
|
|
375
415
|
};
|
|
376
416
|
var MIN_HEADROOM_SECONDS = 90 * 60;
|
|
@@ -384,6 +424,14 @@ var assertFinite = (value, reason, label) => {
|
|
|
384
424
|
throw gateError(reason, `${label} is not a finite number (${String(value)})`);
|
|
385
425
|
}
|
|
386
426
|
};
|
|
427
|
+
var MAX_PAIR_LENGTH = 158;
|
|
428
|
+
var assertPairLength = (pair) => {
|
|
429
|
+
if (pair.length > MAX_PAIR_LENGTH) {
|
|
430
|
+
throw new Error(
|
|
431
|
+
`pair is ${pair.length} characters, over the wire's ${MAX_PAIR_LENGTH}-character limit`
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
};
|
|
387
435
|
var verifyLockupAddress = (quote, derivedAddress) => {
|
|
388
436
|
const quoted = quote.profile?.lockup_address;
|
|
389
437
|
if (derivedAddress !== quoted) throw new AddressMismatch(derivedAddress, quoted);
|
|
@@ -423,14 +471,20 @@ var assertFundable = (input) => {
|
|
|
423
471
|
}
|
|
424
472
|
}
|
|
425
473
|
};
|
|
426
|
-
var expectQuote = (payload, rfqId) => {
|
|
474
|
+
var expectQuote = (payload, rfqId, requestedPair) => {
|
|
427
475
|
const p = payload;
|
|
428
476
|
if (p?.type === "rfq_refusal") throw new SwapRefusal(p.reason ?? "unknown", p.rfq_id ?? rfqId);
|
|
429
477
|
if (p?.type !== "rfq_quote" || p.rfq_id !== rfqId) {
|
|
430
478
|
throw new Error(`unexpected reply: ${p?.type ?? "no payload"}`);
|
|
431
479
|
}
|
|
480
|
+
if (requestedPair !== void 0 && p.pair !== requestedPair) {
|
|
481
|
+
throw new Error(
|
|
482
|
+
`solver quoted ${JSON.stringify(p.pair)}, not the requested ${requestedPair}`
|
|
483
|
+
);
|
|
484
|
+
}
|
|
432
485
|
return payload;
|
|
433
486
|
};
|
|
487
|
+
var pairOf = (payload) => typeof payload.pair === "string" ? payload.pair : void 0;
|
|
434
488
|
var httpTransport = (baseUrl, options = {}) => {
|
|
435
489
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
436
490
|
const readJson = async (response, what) => {
|
|
@@ -450,7 +504,11 @@ var httpTransport = (baseUrl, options = {}) => {
|
|
|
450
504
|
headers: { "content-type": "application/json" },
|
|
451
505
|
body: JSON.stringify(payload)
|
|
452
506
|
});
|
|
453
|
-
return expectQuote(
|
|
507
|
+
return expectQuote(
|
|
508
|
+
await readJson(response, "quote request"),
|
|
509
|
+
String(payload.rfq_id),
|
|
510
|
+
pairOf(payload)
|
|
511
|
+
);
|
|
454
512
|
},
|
|
455
513
|
async status(rfqId) {
|
|
456
514
|
const response = await fetchImpl(`${baseUrl}/v1/rfq/${rfqId}`, { method: "GET" });
|
|
@@ -527,7 +585,8 @@ var relayTransport = (relayUrl, options) => {
|
|
|
527
585
|
async requestQuote(payload) {
|
|
528
586
|
return expectQuote(
|
|
529
587
|
await roundTrip(payload, String(payload.rfq_id)),
|
|
530
|
-
String(payload.rfq_id)
|
|
588
|
+
String(payload.rfq_id),
|
|
589
|
+
pairOf(payload)
|
|
531
590
|
);
|
|
532
591
|
},
|
|
533
592
|
async status(rfqId) {
|
|
@@ -618,7 +677,7 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
|
|
|
618
677
|
}
|
|
619
678
|
const serverPubkey = xOnly(hex3.decode(info.signerPubkey), "ark signer key");
|
|
620
679
|
const network = getNetwork(info.network);
|
|
621
|
-
const
|
|
680
|
+
const treeParams = {
|
|
622
681
|
solverPubkey: xOnly(hex3.decode(quote.solver_pubkey), "solver key"),
|
|
623
682
|
refundLocktime: quote.refund_locktime,
|
|
624
683
|
serverPubkey,
|
|
@@ -630,8 +689,9 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
|
|
|
630
689
|
),
|
|
631
690
|
senderPubkey,
|
|
632
691
|
receiverPkScript: solverHex(receiverPkScriptHex, "profile.receiver_pk_script"),
|
|
633
|
-
refundPkScript:
|
|
634
|
-
}
|
|
692
|
+
refundPkScript: ArkAddress2.decode(refundAddress).pkScript
|
|
693
|
+
};
|
|
694
|
+
const script = lightningSendVtxoScript(treeParams);
|
|
635
695
|
const address = script.address(network.hrp, serverPubkey).encode();
|
|
636
696
|
verifyLockupAddress(quote, address);
|
|
637
697
|
assertFundable({
|
|
@@ -651,7 +711,8 @@ async function requestLightningSend(wallet, arkServerUrl, transport, params) {
|
|
|
651
711
|
script,
|
|
652
712
|
refundAddress,
|
|
653
713
|
senderPubkey,
|
|
654
|
-
secrets
|
|
714
|
+
secrets,
|
|
715
|
+
treeParams
|
|
655
716
|
};
|
|
656
717
|
}
|
|
657
718
|
var offerTermsFromQuote = (quote, assets) => {
|
|
@@ -725,25 +786,25 @@ function deriveOnchainSend(input) {
|
|
|
725
786
|
emulatorPubkey: input.emulatorPubkey,
|
|
726
787
|
senderPubkey: input.senderPubkey,
|
|
727
788
|
receiverPkScript: solverHex(receiverPkScriptHex, "profile.receiver_pk_script"),
|
|
728
|
-
refundPkScript:
|
|
789
|
+
refundPkScript: ArkAddress2.decode(input.refundAddress).pkScript
|
|
729
790
|
});
|
|
730
791
|
const address = script.address(input.hrp, input.serverPubkey).encode();
|
|
731
792
|
verifyLockupAddress(quote, address);
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
input.l1Network
|
|
740
|
-
);
|
|
793
|
+
const htlcParams = {
|
|
794
|
+
paymentHash: input.paymentHash,
|
|
795
|
+
claimKey: input.payoutPubkey,
|
|
796
|
+
refundKey: xOnly(hex3.decode(htlcPubkey), "solver L1 htlc key"),
|
|
797
|
+
refundLocktime: htlcLocktime
|
|
798
|
+
};
|
|
799
|
+
const htlc = onchainHtlcScript(htlcParams, input.l1Network);
|
|
741
800
|
if (htlc.address !== htlcAddress) throw new AddressMismatch(htlc.address, htlcAddress);
|
|
742
801
|
return {
|
|
743
802
|
address,
|
|
744
803
|
swapPkScript: script.pkScript,
|
|
745
804
|
script,
|
|
746
805
|
htlc,
|
|
806
|
+
htlcParams,
|
|
807
|
+
l1Network: input.l1Network,
|
|
747
808
|
refundLocktime,
|
|
748
809
|
htlcLocktime,
|
|
749
810
|
minConfirmations
|
|
@@ -813,6 +874,9 @@ async function requestOnchainSend(wallet, arkServerUrl, transport, params) {
|
|
|
813
874
|
script: derived.script,
|
|
814
875
|
refundAddress,
|
|
815
876
|
htlc: derived.htlc,
|
|
877
|
+
htlcParams: derived.htlcParams,
|
|
878
|
+
l1Network: derived.l1Network,
|
|
879
|
+
minConfirmations: derived.minConfirmations,
|
|
816
880
|
senderPubkey,
|
|
817
881
|
secrets
|
|
818
882
|
};
|
|
@@ -919,7 +983,7 @@ function deriveLightningReceive(input) {
|
|
|
919
983
|
if (refundLocktime === void 0 || invoice === void 0 || solverRefundPkScriptHex === void 0) {
|
|
920
984
|
throw new Error("lightning-receive quote is missing a binding field");
|
|
921
985
|
}
|
|
922
|
-
const
|
|
986
|
+
const treeParams = {
|
|
923
987
|
solverPubkey: xOnly(hex3.decode(quote.solver_pubkey), "solver key"),
|
|
924
988
|
refundLocktime,
|
|
925
989
|
serverPubkey: input.serverPubkey,
|
|
@@ -928,11 +992,12 @@ function deriveLightningReceive(input) {
|
|
|
928
992
|
emulatorPubkey: input.emulatorPubkey,
|
|
929
993
|
solverRefundPkScript: solverHex(solverRefundPkScriptHex, "profile.solver_refund_pk_script"),
|
|
930
994
|
payoutPubkey: input.payoutPubkey,
|
|
931
|
-
payoutPkScript:
|
|
932
|
-
}
|
|
995
|
+
payoutPkScript: ArkAddress2.decode(input.payoutAddress).pkScript
|
|
996
|
+
};
|
|
997
|
+
const script = receiveVtxoScript(treeParams);
|
|
933
998
|
const address = script.address(input.hrp, input.serverPubkey).encode();
|
|
934
999
|
verifyLockupAddress(quote, address);
|
|
935
|
-
return { address, swapPkScript: script.pkScript, script, invoice, refundLocktime };
|
|
1000
|
+
return { address, swapPkScript: script.pkScript, script, invoice, refundLocktime, treeParams };
|
|
936
1001
|
}
|
|
937
1002
|
async function requestLightningReceive(wallet, arkServerUrl, transport, params) {
|
|
938
1003
|
const rfqId = params.rfqId ?? newRfqId();
|
|
@@ -1004,7 +1069,8 @@ async function requestLightningReceive(wallet, arkServerUrl, transport, params)
|
|
|
1004
1069
|
script: derived.script,
|
|
1005
1070
|
payoutAddress,
|
|
1006
1071
|
payoutPubkey,
|
|
1007
|
-
secrets
|
|
1072
|
+
secrets,
|
|
1073
|
+
treeParams: derived.treeParams
|
|
1008
1074
|
};
|
|
1009
1075
|
}
|
|
1010
1076
|
function deriveOnchainReceive(input) {
|
|
@@ -1028,7 +1094,7 @@ function deriveOnchainReceive(input) {
|
|
|
1028
1094
|
emulatorPubkey: input.emulatorPubkey,
|
|
1029
1095
|
solverRefundPkScript: solverHex(solverRefundPkScriptHex, "profile.solver_refund_pk_script"),
|
|
1030
1096
|
payoutPubkey: input.payoutPubkey,
|
|
1031
|
-
payoutPkScript:
|
|
1097
|
+
payoutPkScript: ArkAddress2.decode(input.payoutAddress).pkScript
|
|
1032
1098
|
});
|
|
1033
1099
|
const address = script.address(input.hrp, input.serverPubkey).encode();
|
|
1034
1100
|
verifyLockupAddress(quote, address);
|
|
@@ -1148,12 +1214,15 @@ export {
|
|
|
1148
1214
|
SWAP_LOCKUP_CONTRACT_TYPE,
|
|
1149
1215
|
SWAP_LOCKUP_CONTRACT_LABEL,
|
|
1150
1216
|
SWAP_LOCKUP_CONTRACT_KIND,
|
|
1217
|
+
LockupContractMissing,
|
|
1151
1218
|
LockupRegistrationFailed,
|
|
1152
1219
|
registerLockupContract,
|
|
1220
|
+
lockupContractParams,
|
|
1153
1221
|
ARKADE_BTC,
|
|
1154
|
-
ARKADE_ASSET,
|
|
1155
1222
|
LIGHTNING_BTC,
|
|
1156
1223
|
ONCHAIN_BTC,
|
|
1224
|
+
arkadeAssetLeg,
|
|
1225
|
+
ARKADE_ASSET,
|
|
1157
1226
|
rfqPair,
|
|
1158
1227
|
LIGHTNING_SEND_PAIR,
|
|
1159
1228
|
LIGHTNING_RECEIVE_PAIR,
|
|
@@ -1168,6 +1237,8 @@ export {
|
|
|
1168
1237
|
MIN_HEADROOM_SECONDS,
|
|
1169
1238
|
verifyLockupAddress,
|
|
1170
1239
|
assertFundable,
|
|
1240
|
+
expectQuote,
|
|
1241
|
+
pairOf,
|
|
1171
1242
|
httpTransport,
|
|
1172
1243
|
relayTransport,
|
|
1173
1244
|
SOLO_REFUND_HEADROOM_SECONDS,
|